....................................../////.===Shadow-Here===./////................................................ > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < ------------------------------------------------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// RIFF¤ WEBPVP8 ˜ ðÑ *ôô>‘HŸK¥¤"§£±¨àð enü¹%½_F‘åè¿2ºQú³íªú`N¿­3ÿƒügµJžaÿ¯ÿ°~¼ÎùnúîÞÖô•òíôÁÉß®Sm¥Ü/ ‡ó˜f£Ùà<˜„xëJ¢Ù€SO3x<ªÔ©4¿+ç¶A`q@Ì“Úñè™ÍÿJÌ´ª-˜ÆtÊÛL]Ïq*‘Ý”ì#ŸÌÏãY]@ê`¿ /ªfkØB4·®£ó z—Üw¥Pxù–ÞLШKÇN¾AkÙTf½è'‰g gÆv›Øuh~ a˜Z— ïj*á¥t d£“uÒ ¨`K˜¹ßþ]b>˜]_ÏÔ6W—è2r4x•íÖ…"ƒÖNîä!¦å Ú}ýxGøÌ —@ ;ÆÚŠ=ɾ1ý8lªË¥ô ^yf®Œ¢u&2©nÙÇ›ñÂñŒ³ aPo['½»øFùà­+4ê“$!lövlüÞ=;N®3ð‚õ›DÉKòÞ>ÄÍ ¥ˆuߤ#ˆ$6ù™¥îŠ‡y’ÍB¼ çxÛ;X"WL£R÷͝*ó-¶Zu}º.s¸sšXqù–DþÿvªhüïwyŸ ¯é³lÀ:KCûÄ£Ëá\…­ ~—ýóî ¼ûûÜTÓüÇy…ŽÆvc»¾×U ñ¸žþоP÷¦ó:Ò¨¨5;Ð#&#ÖúñläÿÁœ GxÉ­/ñ‡áQðìYÉtÒwŽ¼GÔ´zàÒò ð*ëzƒ•4~H]Ø‹f ñÓÈñ`NåWçs'ÆÏW^ø¹!XžµmQ5ÃËoLœÎ: ÞËÍ¥J ù…î èo£ßPÎñ¶ž8.Œ]ʵ~5›ÙË-ù*8ÙÖß±~ ©¹rÓê‚j¶d¸{^Q'˜±Crß ÚH—#¥¥QlÀ×ëã‡DÜ«èî þ&Çæžî;ŽÏºò6ÒLÃXy&ZŒ'j‚¢Ù€IßÚù+–MGi‰*jE€‘JcÜ ÓÌ EÏÚj]o˜ Þr <¾U ûŪæÍ/šÝH¥˜b”¼ ÁñßX GP›ï2›4WŠÏà×£…íÓk†¦H·ÅíMh–*nó÷à]ÁjCº€b7<ب‹¨5車bp2:Á[UªM„QŒçiNMa#<5›áËó¸HýÊ"…×Éw¹¦ì2º–x<›»a±¸3Weü®FÝ⑱ö–î–³|LPÈ~çð~Çå‡|º kD¢µÏàÆAI %1À% ¹Ò – ”ϝS¦‰4&¶£°à Öý”û_Ò Áw°A«Å€?mÇÛgHÉ/8)á¾ÛìáöŽP í¨PŸNÙµº¦‡§Ùš"ÿ«>+ªÕ`Ê÷‡‚ß Õû˜þãÇ-PÍ.¾XV‘€ dÜ"þ4¹ ±Oú‘©t¥¦FªÄÃÄ•b‚znýu½—#cDs˜ÃiÑOˆñ×QO=*IAÊ,¶ŽZƒ;‡wøXè%EÐk:F±Ú” .Ѽ+Áu&Ç`."pÈÉw o&¿dE6‘’EqTuK@Ì¥ã™À(Êk(h‰,H}RÀIXÛš3µ1©_OqÚÒJAñ$ÊÙÜ;D3çŒ[þùœh¬Ã³™ö6ç†NY".Ú‰ï[ªŸŒ '²Ð öø_¨ÂÉ9u鶳ÒŠõTàîMØ#û¯gN‡bÙ놚X„ö …ÉeüÌ^J ‹€.œ$Æ)βÄeæW#óüßĺŸ€ ÀzwV 9oä»f4V*uB «Ë†¹ì¯žR霓æHXa=&“I4K;¯ç‹h×·"UŠ~<•â•ªVêª&ÍSÃÆÅ?ÔqÎ*mTM ˜›µwêd#[C¡©§‘D<©àb†–ÁœøvH/,í:¯( ²£|4-„Æövv„Yͼ™^Á$ˆ„¢Û[6yB.åH*V¨æ?$=˜Ñ€•î¦ñ·­(VlŸ‘ nÀt8W÷´Bûba?q9ú¶Xƒl«ÿ\ù¶’þòUÐj/õ¢Ìµ³g$ƒÎR!¸»|Oߍë’BhîÚÑ¢ñåŒJ„®„£2Ð3•ô02Nt…!£Í]Ïc½Qÿ?ˆ<&ÃA¾Ú,JˆijÌ#5yz„‰Î|ÊŽ5QÏ:‹ÐaóVÔxW—CpeÏzÐïíçôÿÅ_[hãsÐ_/ŽTÝ?BîˆííV$<¿i>²F¬_Eß¿ †bÊŒº­ÿ®Z H“C}”¬,Mp ý/Bá£w>˜YV°aƒúh+cŠ- r/[%|üUMHäQ°X»|û/@|°¥Ð !BÔ Ç¢Ä©š+Õì D«7ìN¶ŽðÔ " ƶ’ÖçtA‰Û×}{tþz­¾GÍ›k¹OEJR$ Â׃ «ëÁ"oÉôž$oUK(Ä)Ãz³Ê-‹êN[Ò3Œñbï8P 4ƒ×q¢bo|?<ÛX¬òÄÍ°L–±›(™ûG?ýË©ÚÄ–ÂDØÐ_Ç¡ô ¾–ÄÏø ×e8Ë©$ÄF¹Å‹ì[©óìl:F¾f´‹‹Xì²ï®\¬ôùƒ ÿat¥óèÒùHß0äe‚;ü×h:ÆWðHž=Ã8骣"kœ'Y?³}Tûè€>?0l›e1Lòñ„aæKÆw…hÖŠùW…ÈÆÄ0ši·›[pcwËþñiêíY/~-Á5˜!¿†A›™Mÿþ(±“t@â“ö2­´TG5yé]çå僳 .·ÍïçÝ7UÚ±Ð/Nè»,_Ï ùdj7\ï Wì4›„»c¸àešg#ÒÊ⥭áØo5‘?ÌdÝô¯ ¹kzsƒ=´#ëÉK›Ø´±-¥eW?‡çßtòTã…$Ý+qÿ±ƒ÷_3Ô¥í÷:æ–ž<·Ö‡‰Å¢ š‡%Ô—utÌÈìðžgÖÀz²À—ï÷Óîäõ{K'´È÷³yaÏÁjƒô}ž§®æÊydÕÈë5¯èˆõvÕ©ã*çD„ “z„Ó‡^^xÂ3M§A´JG‚öï 3W'ˆ.OvXè¡ÊÕª?5º7†˜(˜Ç¶#çê’¶!ÌdZK§æ 0fãaN]òY³RV ™î$®K2R¨`W!1Ôó\;Ý ýB%qæK•&ÓÈe9È0êI±žeŸß -ú@žQr¦ ö4»M¼Áè¹µmw 9 EÆE_°2ó„ŸXKWÁ×Hóì^´²GѝF©óäR†¦‰ç"V»eØ<3ùd3ÿÚ¤Žú“Gi" —‘_ÙËÎ~Üö¯¥½Î»üŸEÚŽåmÞþí ;ÞólËΦMzA"Âf(´òá;Éï(/7½ûñÌ­cïÕçлþÝz¾-ÍvÑ“pH­–ðÓj$¸Äû¤‚‘ãUBË-n“2åPkS5&‹Â|+g^œ®Ì͆d!OïäîU«c;{Û!ÅŽ«ëZ9Ókóˆ]¯ƒ›né `ÇÒ+tÆš (ØKá¾—=3œ®•vuMñg²\ï Ec€ 05±d™‡×iÇ×›UúvÌ¢£Èþ¡ÕØô¶ßÎA"ß±#Ö²ˆÊŸ¦*Ä~ij|àø.-¼'»Ú¥£h ofº¦‡VsR=N½„Î v˜Z*SÌ{=jÑB‹tê…;’HžH¯8–îDù8ñ¢|Q•bÛçš–‹m³“ê¨ åÏ^m¬Žãþ©ïêO‡½6] µÆ„Ooòü ²x}N¦Ë3ïé¿»€›HA˜m%çÞ/¿í7Fø“‹léUk)É°Œµ8Q8›:ÀŠeT*šõ~ôڝG6 ¢}`ùH­–”¡k ‰P1>š†®9z11!X wKfmÁ¦xÑ,N1Q”–æB¶M…ÒÃv6SMˆhU¬ÊPŽï‘öj=·CŒ¯u¹ƒVIЃsx4’ömÛýc塶7ßŠß 57^\wÒÐÆ k§h,Œý î«q^R½3]J¸ÇðN ‚çU¬ôº^Áì} ³f©Õœ§ˆã:FÄÈ‚é(€™?àýÓüè1Gô£¼éj‚OÅñ  #>×—ßtà 0G¥Åa뀐kßhc™À_ÉñÞ#±)GD" YîäË-ÿÙ̪ ¹™a¯´¢E\ÝÒö‚;™„ë]_ p8‰o¡ñ+^÷ 3‘'dT4œŽ ðVë½° :¬víÑ«£tßÚS-3¶“þ2 †üüʨòrš¹M{É_¤`Û¨0ìjœøJ‡:÷ÃáZ˜†@GP&œÑDGÏs¡þ¦þDGú‘1Yá9Ôþ¼ ûø…§÷8&–ÜÑnÄ_m®^üÆ`;ÉVÁJ£?â€-ßê}suÍ2sõA NÌúA磸‘îÿÚ»ƒìö·á¿±tÑÐ"Tÿü˜[@/äj¬€uüªìù¥Ý˜á8Ý´sõj 8@rˆð äþZÇD®ÿUÏ2ùôõrBzÆÏÞž>Ì™xœ“ wiÎ×7_… ¸ \#€MɁV¶¥üÕÿPÔ9Z‡ø§É8#H:ƒ5ÀÝå9ÍIŒ5åKÙŠ÷qÄ>1AÈøžj"µÂд/ªnÀ qªã}"iŸBå˜ÓÛŽ¦…&ݧ;G@—³b¯“•"´4í¨ôM¨åñC‹ïùÉó¯ÓsSH2Ý@ßáM‡ˆKÀªÛUeø/4\gnm¥‹ŸŒ qÄ b9ÞwÒNÏ_4Ég³ú=܆‚´ •â¥õeíþkjz>éÚyU«Íӝ݃6"8/ø{=Ô¢»G¥ äUw°W«,ô—¿ãㆅү¢³xŠUû™yŒ (øSópÐ 9\åTâ»—*oG$/×ÍT†Y¿1¤Þ¢_‡ ¼ „±ÍçèSaÓ 3ÛMÁBkxs‰’R/¡¤ˆÙçª(*õ„üXÌ´ƒ E§´¬EF"Ù”R/ÐNyÆÂ^°?™6¡œïJ·±$§?º>ÖüœcNÌù¯G ‹ñ2ŠBB„^·úìaz¨k:#¨Æ¨8LÎõލ£^§S&cŒÐU€ü(‡F±Š¼&P>8ÙÁ ‰ p5?0ÊƃZl¸aô š¼¡}gÿ¶zÆC²¹¬ÎÖG*HB¡O<º2#ñŒAƒ–¡B˜´É$¥›É:FÀÔx¾u?XÜÏÓvN©RS{2ʈãk9rmP¼Qq̳ è¼ÐFׄ^¡Öì fE“F4A…!ì/…¦Lƒ… … $%´¾yã@CI¬ á—3PþBÏNÿ<ý°4Ü ËÃ#ØÍ~âW«rEñw‹eùMMHß²`¬Öó½íf³:‹k˜¯÷}Z!ã¿<¥,\#öµÀ¯aÒNÆIé,Ћ–lŽ#Àæ9ÀÒS·I’½-Ïp Äz¤Š Â* ­íÄ9­< h>׍3ZkËU¹§˜ŒŠ±f­’¤º³Q ÏB?‹#µíÃ¥®@(Gs«†vI¥Mµ‹Á©e~2ú³ÁP4ìÕi‚²Ê^ö@-DþÓàlÜOÍ]n"µã:žpsŽ¢:! Aõ.ç~ÓBûH÷JCÌ]õVƒd «ú´QÙEA–¯¯Œ!.ˆˆëQ±ù œ·Ì!Õâ )ùL„ÅÀlÚè5@B…o´Æ¸XÓ&Û…O«˜”_#‡ƒ„ûÈt!¤ÁÏ›ÎÝŠ?c9 â\>lÓÁVÄÑ™£eØY]:fÝ–—ù+p{™ðè û³”g±OƒÚSù£áÁÊ„ä,ï7š²G ÕÌBk)~ÑiCµ|h#u¤¶îK¨² #²vݯGãeÖ϶ú…¾múÀ¶þÔñ‚Š9'^($¤§ò “š½{éúp÷J›ušS¹áªCÂubÃH9™D™/ZöØÁ‡¦ÝÙŸ·kð*_”.C‹{áXó€‡c¡c€§/šò/&éš÷,àéJþ‰X›fµ“C¨œ®r¬"kL‰Â_q…Z–.ÉL~O µ›zn‚¹À¦Öª7\àHµšÖ %»ÇníV[¥*Õ;ƒ#½¾HK-ÖIÊdÏEÚ#=o÷Óò³´Š: Ç?{¾+9›–‘OEáU·S€˜j"ÄaÜ ŒÛWt› á–c#a»pÔZÞdŽtWê=9éöÊ¢µ~ ë ;Öe‡Œ®:bî3±ýê¢wà¼îpêñ¹¾4 zc¾ðÖÿzdêŒÑÒŝÀ‰s6¤í³ÎÙB¿OZ”+F¤á‡3@Ñëäg©·Ž ˆèª<ù@É{&S„œÕúÀA)‰h:YÀ5^ÂÓŒ°õäU\ ùËÍû#²?Xe¬tu‰^zÒÔãë¼ÛWtEtû …‚g¶Úüâî*moGè¨7%u!]PhÏd™Ý%Îx: VÒ¦ôÊD3ÀŽKÛËãvÆî…N¯ä>Eró–ð`5 Œ%u5XkñÌ*NU%¶áœÊ:Qÿú»“úzyÏ6å-၇¾ ´ ÒÊ]y žO‘w2Äøæ…H’²f±ÎÇ.ª|¥'gîV•Ü .̘¯€šòü¤U~Ù†*¢!?ò wý,}´°ÔÞnïoKq5µb!áÓ3"vAßH¡³¡·G(ÐÎ0Îò¼MG!/ài®@—¬04*`…«é8ªøøló“ˆÊ”èù¤…ßÊoÿé'ËuÌÖ5×È¡§ˆˆfŽë9}hìâ_!!¯  B&Ëö¶‰ÀAÙNVŸ Wh›¸®XÑJì¨ú“¿÷3uj²˜¨ÍÎìë±aúŠÝå¯ð*Ó¨ôJ“yºØ)m°WýOè68†ŸÏ2—‰Ïüꪫٚ¥‹l1 ø ÏÄFjêµvÌbü¦èÝx:X±¢H=MÐß—,ˆÉÇ´(9ú¾^ÅÚ4¿m‡$âX‘å%(AlZo@½¨UOÌÕ”1ø¸jÎÀÃÃ_ µ‘Ü.œº¦Ut: Æï’!=¯uwû#,“pþÇúŒø(é@?³ü¥‘Mo §—s@Œ#)§ŒùkL}NOÆêA›¸~r½¼ÙA—HJ«eˆÖ´*¡ÓpÌŸö.m<-"³ûÈ$¬_6­åf£ïÚâj1y§ÕJ½@dÞÁr&Í\Z%D£Íñ·AZ Û³øüd/ªAi†/Й~  ‡âĮҮÏh§°b—›Û«mJžòG'[ÈYýŒ¦9psl ýÁ ®±f¦x,‰½tN ‚Xª9 ÙÖH.«Lo0×?͹m¡å†Ѽ+›2ƒF ±Ê8 7Hցϓ²Æ–m9…òŸï]Â1äN†VLâCˆU .ÿ‰Ts +ÅÎx(%¦u]6AF Š ØF鈄‘ |¢¶c±soŒ/t[a¾–û:s·`i햍ê›ËchÈ…8ßÀUÜewŒðNOƒõD%q#éû\9¤x¹&UE×G¥ Í—™$ð E6-‡¼!ýpãÔM˜ Âsìe¯ñµK¢Ç¡ùôléœ4Ö£”À Š®Ðc ^¨À}ÙËŸ§›ºê{ÊuÉC ×Sr€¤’fÉ*j!úÓ’Gsùìoîßîn%ò· àc Wp÷$¨˜)û»H ×8ŽÒ€Zj¤3ÀÙºY'Ql¦py{-6íÔCeiØp‘‡XÊîÆUߢ܂ž£Xé¼Y8þ©ëgñß}é.ÎógÒ„ÃØËø¯»™§Xýy M%@NŠ À(~áÐvu7&•,Ù˜ó€uP‡^^®=_E„jt’ 403WebShell
403Webshell
Server IP : 66.235.200.170  /  Your IP : 18.188.103.74
Web Server : Apache
System : Linux gator4410.hostgator.com 5.14.0-162.23.1.9991722448259.nf.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jul 31 18:11:45 UTC 2024 x86_64
User : bmgxafte ( 1214)
PHP Version : 8.2.28
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /scripts/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /scripts/convert_roundcube_mysql2sqlite
#!/usr/local/cpanel/3rdparty/bin/perl

# cpanel - scripts/convert_roundcube_mysql2sqlite  Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package Script::RCube::Mysql2Sqlite;

use strict;
## no critic (RequireUseWarnings)
use Try::Tiny;

use Cpanel::AccessIds::ReducedPrivileges ();
use Cpanel::Config::LoadCpConf           ();
use Cpanel::MysqlUtils                   ();
use Cpanel::DbUtils                      ();
use Cpanel::MysqlUtils::Connect          ();
use Cpanel::MysqlUtils::Command          ();
use Cpanel::MysqlRun                     ();
use Cpanel::Config::LoadUserDomains      ();
use Cpanel::Email::RoundCube             ();
use Cpanel::Email::RoundCube::DBI        ();
use Cpanel::Quota::Temp                  ();

use Cpanel::PwCache       ();
use Cpanel::Logger        ();
use Cpanel::Filesys::Home ();

use File::Basename ();
use File::Copy     ();
use File::Path     ();
use File::Slurper  ();
use DBI;
use XML::Simple ();
use IPC::Open3  ();

$XML::Simple::PREFERRED_PARSER = "XML::SAX::PurePerl";

my $sqlite_table_file = '/usr/local/cpanel/base/3rdparty/roundcube/SQL/sqlite.initial.sql';

my $log_file = '/usr/local/cpanel/logs/roundcube_sqlite_convert_log';
my %opts     = ( 'alternate_logfile' => $log_file );
my $logger   = Cpanel::Logger->new( \%opts );

my $mysql_dbname = 'roundcube';
my $dbh;

# If this updates, then do_all_rcube_xml_to_db() needs to be updated to match
my @dumptables = qw(users identities contacts contactgroups contactgroupmembers calendars caldav_calendars events caldav_events attachments caldav_attachments itipinvitations responses);

my $time = time();

## if invoked as a script, there is nothing in the call stack
my $invoked_as_script = !caller();
__PACKAGE__->script(@ARGV) if ($invoked_as_script);

my $mysql_plus_sqlite;

sub script {
    my ( $package, $opt_user, $opt_dbname ) = @_;
    my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf();

    my $db_type = 'mysql';
    if ( exists $cpconf->{'roundcube_db'} ) {
        $db_type           = 'sqlite' if $cpconf->{'roundcube_db'} eq 'sqlite';
        $mysql_plus_sqlite = $cpconf->{'roundcube_db'} eq 'mysql_plus_sqlite';
    }

    if ( defined $opt_user ) {
        ## the optional user arg feature is currently *only* called as a script;
        ##   otherwise, all these exits would check $invoked_as_script

        ## Transfers.pm is the only user of $opt_user, which it uses in conjunction with $opt_user;
        ##   this changes the global variable $opt_dbname just in time to be used in &init_check
        if ( defined $opt_dbname ) {
            $mysql_dbname = $opt_dbname;
        }

        if ( $db_type eq 'sqlite' && init_check($cpconf) ) {
            my $wasSuccess = convert_mysql_roundcube_to_sqlite($opt_user);
            if ($wasSuccess) {

                ## Transfers.pm operates over a different dbname, and is responsible for the
                ##   removal of the temporary database
                unless ( defined $opt_dbname ) {
                    Cpanel::Email::RoundCube::archive_and_drop_mysql_roundcube($logger);
                }
                return 1;
            }
            die "Conversion for user '$opt_user' was not successful.\n";
        }

        ## SOMEDAY @GOLIVE: update-roundcube-sqlite-db gets moved to update-roundcube-db. Update this message and the conditional @GOIVE.
        die "The optional user argument is to be used only when Roundcube has been converted to sqlite. Use bin/update-roundcube-sqlite-db Exiting.\n";
    }

    ## Has the roundcube conversion already happened?
    ## note: this check is here, and not in init_check, because Transfers.pm calls into this
    ##   from a different context. The src server uses MySQL but the dest machine has done
    ##   the sqlite conversion.
    if ( $db_type eq 'sqlite' ) {
        $logger->info("Roundcube conversion already occurred, bailing out.");
        if ($invoked_as_script) {
            exit(0);
        }
        return 1;
    }

    unless ( init_check($cpconf) ) {
        if ($invoked_as_script) {
            exit(0);
        }
        return 1;
    }

    my %TRUEDOMAINS;
    Cpanel::Config::LoadUserDomains::loadtrueuserdomains( \%TRUEDOMAINS );
    my @users          = sort values %TRUEDOMAINS;
    my $success_cnt    = 0;
    my $success_verify = scalar @users;

    for my $user (@users) {
        $success_cnt += convert_mysql_roundcube_to_sqlite($user);
    }

    unless ( $success_cnt == $success_verify ) {
        $logger->warn("Roundcube Mysql to sqlite conversion was not completely successful. Please check $log_file for details.");

        if ($invoked_as_script) {
            exit(1);
        }
        return;
    }

    ## Conversion is a success, so switch Roundcube configuration to use SQLite before dropping the database.
    my $DIR = '/usr/local/cpanel/base/3rdparty';
    Cpanel::Email::RoundCube::generate_roundcube_config_sqlite( $DIR, $logger );

    ## if the conversion was a complete success (as checked above), we can safely archive and
    ##   delete the MySQL roundcube database. This will prevent future problems with transfers,
    ##   and in fact is the final solution for the original Roundcube case 12162!
    ## already asserted via &init_check
    my $archive_success = Cpanel::Email::RoundCube::archive_and_drop_mysql_roundcube($logger);

    require Cpanel::Config::CpConfGuard;
    my $cpconf_guard = Cpanel::Config::CpConfGuard->new();
    $cpconf->{'roundcube_db'} = $cpconf_guard->{'data'}->{'roundcube_db'} = 'sqlite';
    $cpconf_guard->save();

    if ($invoked_as_script) {
        Cpanel::Email::RoundCube::restart_cpsrvd();
        exit(0);
    }

    return 1;
}

sub convert_mysql_roundcube_to_sqlite {
    my ($user) = @_;
    my @domains = Cpanel::Email::RoundCube::collect_domains($user);

    my @uid_name_pairs = collect_roundcube_user_info( \@domains, $user );

    ## returning 1: no conversion necessary
    return 1 unless ( scalar @uid_name_pairs );

    ## e.g. '/home'
    my $best_mnt_point   = Cpanel::Filesys::Home::get_homematch_with_most_free_space();
    my $tmpdir_root_base = "$best_mnt_point/roundcube_convert";

    if ( -d $tmpdir_root_base ) {
        rename( $tmpdir_root_base, "$tmpdir_root_base.$time" );
    }
    mkdir($tmpdir_root_base);

    my $tmpdir_root_sys = "$tmpdir_root_base/$user";
    mkdir($tmpdir_root_sys);

    my @pwinfo = Cpanel::PwCache::getpwnam($user);
    my ( $mmuid, $mmgid, $homedir ) = @pwinfo[ 2, 3, 7 ];
    my $tmpdir_emailuser_base = "$homedir/tmp/roundcube_convert";

    my $success_cnt = 0;
    my %has_sqlite_trapper_keeper;

    for my $email_user_info (@uid_name_pairs) {
        my ( $uid, $email_user ) = @$email_user_info;

        if ($mysql_plus_sqlite) {
            my $db_path = _roundcube_db_path( $user, $homedir, $email_user );
            $has_sqlite_trapper_keeper{$db_path} = -f $db_path;
            next if $has_sqlite_trapper_keeper{$db_path};
        }

        my $tmpdir_root_sys_emailuser = "$tmpdir_root_sys/$email_user";
        mkdir($tmpdir_root_sys_emailuser);

        do_mysqldumps_for_user( $uid, $tmpdir_root_sys_emailuser );
    }

    ## ensures that $best/rcube/$sysuser/$emailuser is readable by $sysuser,
    ##   so that $sysuser can move it to their $homedir after the setuid
    my $tempquota = Cpanel::Quota::Temp->new( user => $user, log => 1 );
    $tempquota->disable();

    my $rv_chmod = system("chmod -R 700 $tmpdir_root_sys");
    my $rv_chown = system("chown -Rf $mmuid:$mmgid $tmpdir_root_sys");

    for my $email_user_info (@uid_name_pairs) {
        my ( $uid, $email_user ) = @$email_user_info;

        if ($mysql_plus_sqlite) {
            my $db_path = _roundcube_db_path( $user, $homedir, $email_user );
            if ( $has_sqlite_trapper_keeper{$db_path} ) {
                $logger->info("$email_user already has an SQLite DB, skipping...");
                $success_cnt++;
                next;
            }
        }

        chdir($tmpdir_root_base);

        my $tmpdir_root_sys_emailuser = "$tmpdir_root_sys/$email_user";
        my $rv_convert                = xml_to_sqlite( $user, $tmpdir_root_sys_emailuser, $tmpdir_emailuser_base, $email_user, $homedir );

        unless ($rv_convert) {
            $logger->warn("Conversion was not successful for user '$user'.");
        }
        $success_cnt += $rv_convert;

        ## note: no need to "rmdir($tmpdir_root_sys_emailuser)", as this dir was moved
        ##   to $homedir/tmp/rcube/$emailuser (during the setuid)

        ## SOMEDAY @GOLIVE: remove the mysql roundcube database (steal mysql/roundcube backup/archive
        ##   clauses from update-roundcube-db, the mysql version)
    }

    $tempquota->restore();

    rmdir($tmpdir_root_sys);
    rmdir($tmpdir_root_base);

    return $success_cnt == scalar(@uid_name_pairs);
}

sub collect_roundcube_user_info {
    my ( $ar_domains, $user ) = @_;
    my $regexp = '@(' . join( '|', map { s/\./\\./gr } @$ar_domains ) . ')$';
    my @ids;

    try {
        @ids = $dbh->selectall_array( "SELECT user_id, username FROM users WHERE username REGEXP ? or username = ?", {}, $regexp, $user );
    };

    return @ids;
}

## Stolen from pkgacct; needs to be modularized!
## SOMEDAY: this version has converged; move pkgacct version to module, and get rid of below
sub mysqldumpdb {
    my ($args) = @_;

    my @options   = @{ $args->{'options'} };
    my $db        = $args->{'db'};
    my $table     = $args->{'table'};
    my $file      = $args->{'file'};
    my $file_mode = $args->{'append'} ? '>>' : '>';

    # for testing
    my @extra_options = _get_extra_options();

    my $mysqldump = Cpanel::DbUtils::find_mysqldump();
    my @db        = ($db);
    if ($table) {
        push @db, $table;
    }

    my $pid = IPC::Open3::open3( my $w, my $r, '', $mysqldump, @extra_options, @options, @db );

    my $first_line = 1;
    if ( open( my $fh, $file_mode, $file ) ) {
        while (<$r>) {
            if ( $first_line && ( !$_ || m/^mysqldump:/ ) ) {
                warn join( '.', @db ) . ': ' . $_;
                close $w;
                close $r;
                waitpid( $pid, 0 );
                $first_line = 0;
                my $mysqlcheck = Cpanel::DbUtils::find_mysqlcheck();
                system( $mysqlcheck, '--repair', @extra_options, @db );
                $pid = IPC::Open3::open3( $w, $r, '', $mysqldump, @extra_options, @options, @db );
            }
            else {
                print {$fh} $_;
            }
        }
    }
    close $w;
    close $r;
    waitpid( $pid, 0 );
    return;
}

sub do_mysqldumps_for_user {
    my ( $uid, $tmp_convertdir ) = @_;

    for my $tbl (@dumptables) {
        ## these two tables do not key off user_id
        next if grep { $tbl eq $_ } qw{contactgroupmembers events ical_events caldav_events attachments ical_attachments caldav_attachments caldav_calendars};

        ## XML output, not suppressing table structure, with a where clause on user_id list
        my @opts = ( '--xml', '-w', qq{user_id = $uid} );
        mysqldumpdb(
            {
                'options' => [@opts],
                'db'      => $mysql_dbname,
                'file'    => "$tmp_convertdir/rcube.$tbl.xml",
                'table'   => $tbl
            }
        );
    }

    ## contactgroupmembers is gathered in two steps, otherwise would be a LEFT JOIN that
    ##   &mysqldumpdb can not currently support

    my $contacts = _get_ids_from_col( 'contactgroups', 'contactgroup_id', $uid, 'user_id' );
    _dump_stuff_in( 'contactgroupmembers', 'contactgroup_id', $contacts, $tmp_convertdir ) if scalar(@$contacts);

    # Handle events & attachments for calendaring, as those are identified by
    # calendar & event respectively.
    foreach my $type (qw{default ical caldav}) {
        my $prefix = $type eq 'default' ? '' : "${type}_";

        my $cals = _get_ids_from_col( "${prefix}calendars", 'calendar_id', $uid, 'user_id' );
        next if !scalar(@$cals);
        my @events2export;
        my @attachments2export;
        foreach my $cal (@$cals) {
            my $events = _get_ids_from_col( "${prefix}events", 'event_id', $cal, 'calendar_id' );

            # Can't have attachments if no events exist to attach em to, so
            # next here is appropriate.
            next if !scalar(@$events);
            push @events2export, @$events;

            my $attachments = _get_ids_from_col( "${prefix}attachments", 'attachment_id', $events, 'event_id' );
            next if !scalar(@$attachments);
            push @attachments2export, @$attachments;
        }
        _dump_stuff_in( "${prefix}events",      'event_id',      \@events2export,      $tmp_convertdir ) if @events2export;
        _dump_stuff_in( "${prefix}attachments", 'attachment_id', \@attachments2export, $tmp_convertdir ) if @attachments2export;
    }

    return undef;
}

# Only here so that I don't "repeat myself".
sub _get_ids_from_col {
    my ( $tbl, $col, $search_term, $filter_on, $filter_mode ) = @_;
    my @ids;
    try {

        # Quoting anything other than values here is actually harmful.
        # Don't do it as such. Same for IN statement here.
        my $where_clause = "WHERE $filter_on = ?";
        if ( ref $search_term eq 'ARRAY' ) {
            my $count = scalar(@$search_term);
            die "Bad call to _get_ids_from_col: no search term specified!" if !$count;
            my $qs = ( '?,' x ( $count - 1 ) ) . '?';
            $where_clause = "WHERE $filter_on IN ($qs)";
            @ids          = map { $_->[0] } $dbh->selectall_array( "SELECT $col FROM $tbl $where_clause", {}, @$search_term );
        }
        else {
            @ids = map { $_->[0] } $dbh->selectall_array( "SELECT $col FROM $tbl $where_clause", {}, $search_term );
        }
    }
    catch {
        $logger->warn( "Attempt to find necessary data from $col in $tbl failed: " . $dbh->errstr() );
    };
    return \@ids;

}

# Only here so that I don't "repeat myself".
sub _dump_stuff_in {
    my ( $tbl, $col, $in, $tmp_convertdir ) = @_;

    # It is possible that we have a very large number of ID's.
    # If so, we need to do this in chunks.
    my $chunk_size = 1000;
    my @chunks;

    while ( $in->@* ) {
        push @chunks, [ splice( $in->@*, 0, $chunk_size ) ];
    }

    my $file_num = 0;
    foreach my $chunk (@chunks) {

        my $f_name = $file_num ? "$tmp_convertdir/rcube.$tbl.$file_num.xml" : "$tmp_convertdir/rcube.$tbl.xml";
        my $csv    = join( ',', $chunk->@* );
        mysqldumpdb(
            {
                'options' => [ '--xml', '-w', qq{$col IN ($csv)} ],
                'db'      => $mysql_dbname,
                'file'    => $f_name,
                'table'   => $tbl,
            }
        );
        $file_num++;
    }

    return;
}

sub create_rcube_sqlite_tables {
    my ($dbh) = @_;
    Cpanel::Email::RoundCube::DBI::ensure_schema_update( $dbh, 'sqlite' );
    return undef;
}

# This used to do all sorts of things like telling rcube_xml_to_db what needed
# quoting, what needed to be set to NULL, etc. -- thankfully all that is
# absolutely unnecessary with sqlite3.
sub do_all_rcube_xml_to_db {
    my ( $dbh, $xmldir ) = @_;

    my $success_cnt = 0;
    ## verify the number of XML docs to convert to SQL
    my $success_verify = scalar(@dumptables);
    {
        ## note: there is a disparity in the sqlite.initial.sql, and the "table_structure" for "users";
        ##   preferences is not NULLABLE.
        my @_cols = qw(user_id username mail_host created last_login language preferences);
        $success_cnt += rcube_xml_to_db( $xmldir, 'users', $dbh, \@_cols );
    }

    {
        my @_cols = qw( identity_id user_id changed del standard name organization email
          reply-to bcc signature html_signature);
        $success_cnt += rcube_xml_to_db( $xmldir, 'identities', $dbh, \@_cols );
    }

    {
        my @_cols = qw(contact_id user_id changed del name email firstname surname vcard);
        $success_cnt += rcube_xml_to_db( $xmldir, 'contacts', $dbh, \@_cols );
    }

    ## new contactgroups table
    {
        my @_cols = qw( contactgroup_id user_id changed del name );
        $success_cnt += rcube_xml_to_db( $xmldir, 'contactgroups', $dbh, \@_cols );
    }

    ## new contactgroupmembers table
    ## guard clause: contactgroupmembers is gathered conditionally, so it is the only file which
    ##   may not exist
    if ( -e _xml_file( $xmldir, 'contactgroupmembers' ) ) {
        my @_cols = qw( contactgroup_id contact_id created );
        $success_cnt += rcube_xml_to_db( $xmldir, 'contactgroupmembers', $dbh, \@_cols );
    }
    else {
        $success_cnt++;
    }

    # Restore calendar data
    foreach my $cal_tbl (qw{calendars caldav_calendars}) {
        unless ( -e _xml_file( $xmldir, $cal_tbl ) ) {
            $success_cnt++;
            next;
        }
        my @_cols = qw( calendar_id user_id name color showalarms );
        push( @_cols, qw{ical_url ical_user ical_pass ical_last_change} )                                                   if $cal_tbl eq 'ical_calendars';
        push( @_cols, qw{readonly caldav_url caldav_tag caldav_user caldav_pass caldav_oauth_provider caldav_last_change} ) if $cal_tbl eq 'caldav_calendars';
        $success_cnt += rcube_xml_to_db( $xmldir, $cal_tbl, $dbh, \@_cols );
    }
    foreach my $evt_tbl (qw{events caldav_events}) {
        unless ( -e _xml_file( $xmldir, $evt_tbl ) ) {
            $success_cnt++;
            next;
        }
        my @_cols = qw( event_id calendar_id recurrence_id uid instance isexception created changed sequence start end recurrence title description location categories url all_day free_busy priority sensitivity status alarms attendees notifyat );
        push( @_cols, qw{ical_url ical_last_change} )                if $evt_tbl eq 'ical_events';
        push( @_cols, qw{caldav_url caldav_tag caldav_last_change} ) if $evt_tbl eq 'caldav_events';

        $success_cnt += rcube_xml_to_db( $xmldir, $evt_tbl, $dbh, \@_cols );
    }
    foreach my $atc_tbl (qw{attachments caldav_attachments}) {
        unless ( -e _xml_file( $xmldir, $atc_tbl ) ) {
            $success_cnt++;
            next;
        }
        my @_cols = qw( attachment_id event_id filename mimetype size data );
        $success_cnt += rcube_xml_to_db( $xmldir, $atc_tbl, $dbh, \@_cols );
    }
    if ( -e _xml_file( $xmldir, 'itipinvitations' ) ) {
        my @_cols = qw(token event_uid user_id event expires cancelled);
        $success_cnt += rcube_xml_to_db( $xmldir, 'itipinvitations', $dbh, \@_cols );
    }
    else {
        $success_cnt++;
    }

    ## Restore responses table, if available
    if ( -e _xml_file( $xmldir, 'responses' ) ) {
        my @_cols = qw( response_id user_id name data is_html changed del );
        $success_cnt += rcube_xml_to_db( $xmldir, 'responses', $dbh, \@_cols );
    }
    else {
        $success_cnt++;
    }

    return $success_cnt == $success_verify;
}

sub _xml_file {
    my ( $xmldir, $tbl ) = @_;
    return "$xmldir/rcube.$tbl.xml";
}

sub _get_all_xml_files {
    my ( $xmldir, $tbl ) = @_;

    opendir( my $dh, $xmldir ) or die "Cannot open directory $xmldir: $!";
    my @files = map { "$xmldir/$_" } grep { /^rcube\.$tbl\.(?:[0-9]+\.)?xml$/ } readdir($dh);
    return @files;
}

sub rcube_xml_to_db {
    my ( $xmldir, $tbl, $dbh, $ar_cols ) = @_;

    my @xml_fnames  = _get_all_xml_files( $xmldir, $tbl );
    my %common_opts = ( ForceArray => 1, KeyAttr => [], ContentKey => '__content' );

    foreach my $xml_fname (@xml_fnames) {
        ## $ref->{database}->[0]->{table_data}->[0]->{row}->[$x]->{field}->[$x]->{__content}
        my $ref;
        my $err;
        try {
            $ref = XML::Simple::XMLin( $xml_fname, %common_opts );
        }
        catch {
            $err = $_;
        };

        if ($err) {
            $logger->warn("Failed to load xml for $tbl: $err");
            return;
        }

        my $rows = $ref->{'database'}->[0]->{'table_data'}->[0]->{'row'};

        ## FWIW, Perl does not complain if $rows is undef, when called in a for loop context.
        for my $row (@$rows) {

            # XXX Placeholders don't work on columns, but stuff with dashes
            # still needs quoting. As such, quote all col names.
            # Sorry, couldn't find a way around it.
            my $update_hr = { map { $dbh->quote_identifier( $_->{'name'} ) => $_->{'__content'} || '' } @{ $row->{'field'} } };
            my $rv        = $dbh->do( rcube_make_sql( $tbl, $update_hr ) );
            unless ($rv) {
                $logger->warn( "Conversion was not successful: " . $dbh->errstr() );
                return;
            }
        }
    }
    return 1;
}

sub rcube_make_sql {
    my ( $table, $update_hr ) = @_;

    # Lets hope these actually are nonzero
    my @values = values(%$update_hr);
    my $qs     = ( '?,' x ( scalar(@values) - 1 ) ) . '?';
    return (
        "INSERT OR REPLACE INTO $table (" . join( ",", keys(%$update_hr) ) . ") VALUES ($qs)",
        undef,
        @values,
    );
}

sub check_mysqlup {
    ## note: this call is cached with a ttl of 600
    my $isrunning = Cpanel::MysqlRun::running();
    if ($isrunning) {
        return 1;
    }

    return undef;
}

sub ensure_roundcube_tables {
    my ($dbh) = @_;

    my %tables = map { $_ => undef } $dbh->tables();

    # system table is created during a migration
    my @expected = qw(session cache system);
    push( @expected, @dumptables );

    for my $exp (@expected) {
        if ( !exists $tables{$exp} && !exists $tables{qq{"main"."$exp"}} ) {
            $logger->warn("ERROR: missing $exp table");
            return;
        }
    }

    return 1;
}

sub init_check {
    my ($cpconf) = @_;
    ## Ensure root
    return unless ( 0 == $> );

    unless ( -e $sqlite_table_file ) {
        $logger->info("Roundcube is not installed; conversion is irrelevant.");
        return;
    }

    if ( exists $cpconf->{'skiproundcube'} and $cpconf->{'skiproundcube'} ) {
        $logger->info("Roundcube should be skipped, bailing out.");
        return;
    }

    my $mysqlup = check_mysqlup();
    unless ($mysqlup) {
        $logger->warn("Mysql not currently running, bailing out.");
        return;
    }

    ## Does this installation use Roundcube/MySQL?
    unless ( Cpanel::MysqlUtils::Command::db_exists($mysql_dbname) ) {
        $logger->warn("mysql database $mysql_dbname is missing");
        return;
    }

    my $error;

    try {
        my $dbc = Cpanel::MysqlUtils::Connect->new( database => $mysql_dbname );
        $dbh = $dbc->db_handle();
    }
    catch {
        $error = $_;
    };

    if ($error) {
        $logger->warn("Failed to connect to MySQL database '$mysql_dbname'.");
        return;
    }

    return 1;
}

##############################################################
# These functions allow tests to use a temporary MySQL server

sub _get_extra_options_string {

    if ( scalar _get_extra_options() ) {
        return join( ' ', _get_extra_options() );
    }

    return;
}

sub _get_extra_options {
    return @Cpanel::MysqlUtils::_EXTRA_MYSQL_ARGS;
}

#
##############################################################

sub xml_to_sqlite {
    my ( $user, $tmpdir_root, $tmpdir_convert_base, $email_user, $homedir ) = @_;
    my $code_ref = _closure_maker( $user, $tmpdir_root, $tmpdir_convert_base, $email_user, $homedir );

    #fixup a user who's purposefully deleted their "etc" dir
    Cpanel::AccessIds::ReducedPrivileges::call_as_user( sub { File::Path::make_path( File::Basename::dirname( _roundcube_db_path( $user, $homedir, $email_user ) ), { chmod => 0750 } ); }, $user, 'mail' );
    my $rv = Cpanel::AccessIds::ReducedPrivileges::call_as_user( $code_ref, $user );
    return $rv;
}

sub _roundcube_db_path {
    my ( $system_user, $homedir, $mail_account ) = @_;
    my ( $mail_user, $mail_domain ) = split( '@', $mail_account );
    my $db_path = ( defined $mail_domain ) ? "$homedir/etc/$mail_domain/$mail_user.rcube.db" : "$homedir/etc/$mail_user.rcube.db";
    return $db_path;
}

## defining as a closure, as ::run_as_user does not currently handle sub args
sub _closure_maker {
    my ( $system_user, $tmpdir_root, $tmpdir_convert_base, $email_user, $homedir ) = @_;

    my $code_ref = sub {
        my $tmp_convertdir = "$tmpdir_convert_base/$email_user";

        if ( -d $tmp_convertdir ) {
            rename( $tmp_convertdir, "$tmp_convertdir.$time" );
        }

        # Copy the XML files over to the user's tempdir.
        # Hopefully these are simple renames. But directories may be on different
        # mount points requiring a copy.

        File::Path::make_path($tmp_convertdir);

        for my $table (@dumptables) {
            my @all_src = _get_all_xml_files( $tmpdir_root, $table );
            foreach my $src (@all_src) {
                if ( -e $src ) {
                    my $filename = File::Basename::basename($src);
                    my $dest     = "$tmp_convertdir/$filename";
                    File::Copy::move( $src, $dest );
                }
            }
        }

        # Clean up empty source directory.
        # This will fail if the above moves failed.

        rmdir($tmpdir_root);

        ## note: similar clause in cpsrvd to set up $ENV{'_RCUBE'}
        my ( $mail_user, $mail_domain ) = split( '@', $email_user );

        my $rcube_sqlite_loc8 = _roundcube_db_path( $system_user, $homedir, $email_user );

        if ( -e $rcube_sqlite_loc8 ) {
            rename( $rcube_sqlite_loc8, "$rcube_sqlite_loc8.$time" );
        }

        my $dbh = DBI->connect( "dbi:SQLite:dbname=$rcube_sqlite_loc8", "", "" ) or do {
            $logger->info("Connection error to $rcube_sqlite_loc8: $!");
            return;
        };
        ## note: $dbh->{sqlite_version} is confirmed 2.8.15
        create_rcube_sqlite_tables($dbh);
        return unless ensure_roundcube_tables($dbh);

        unless ( do_all_rcube_xml_to_db( $dbh, $tmp_convertdir ) ) {
            $logger->info("Conversion of roundcube XML dump to sqlite database failed");
            return;
        }

        $dbh->disconnect();

        chmod( 0600, $rcube_sqlite_loc8 );

        ## the return values on these rm calls are not critical
        ## only remove the /tmp directory if the XML converion has been successful thus far

        try {
            File::Path::remove_tree($tmp_convertdir);
        };

        ## attempt to rmdir, which will intentionally fail if any of the converts did not succeed
        rmdir($tmpdir_convert_base);

        return 1;
    };
    return $code_ref;
}

1;

Youez - 2016 - github.com/yon3zu
LinuXploit