From 1d08afaba6526c2052955c914f5a43eac293daa0 Mon Sep 17 00:00:00 2001
From: Nick Bebout Help the author to maintain imapsync and its online services:
+This donate button will ask your email address and your name but don't hesitate
+to give fake ones, like "Smith" and smith@example.com if you prefer to be anonymous.
+ Thanks in advance!
+
-
-
-
diff --git a/S/paypal.shtml b/S/donate.shtml
similarity index 88%
rename from S/paypal.shtml
rename to S/donate.shtml
index 1b2bc58..45416a6 100644
--- a/S/paypal.shtml
+++ b/S/donate.shtml
@@ -29,9 +29,12 @@
-
imapsync donation
+Imapsync donation
Donate via Paypal
+
+Donate with Crypto
+
@@ -64,7 +83,7 @@ value="-----BEGIN PKCS7-----MIIHRwYJKoZIhvcNAQcEoIIHODCCBzQCAQExggEwMIIBLAIBADCB
This document last modified on
-($Id: paypal.shtml,v 1.13 2018/08/21 12:07:50 gilles Exp gilles $)
+($Id: donate.shtml,v 1.16 2019/07/31 22:49:12 gilles Exp gilles $)
+
+
+
External online IMAP migration services (back to menu)
@@ -74,21 +81,23 @@ Prices are given par mailbox and may be outdated
(Last checked on Thu Apr 11, 2019).
To know whether your IMAP server is a widespread choice,
-take a look at http://openemailsurvey.org/.
-The quick answer is: if you're not using Dovecot then you're in a niche!
+take a look at http://openemailsurvey.org/
+The short answer is that if you're not using the famous Dovecot then you're in a niche!
Let's start with the long reported success stories list: @@ -55,10 +55,12 @@ Example: Host2 banner:* OK Courier-IMAP ready -
You can use option --justconnect to get those lines. -Example:
+You can use the following command to get those lines +Examples:
-imapsync --host1 test1.lamiral.info --host2 test2.lamiral.info --justconnect +imapsync --host1 test1.lamiral.info + +imapsync --host1 imap.gmail.com@@ -74,6 +76,9 @@ imapsync --host1 test1.lamiral.info --host2 test2.lamiral.info --justconnect
Effective date: November 1, 2010
+ + ++Gilles LAMIRAL (I) operates the + +Imapsync Online Service (the "Service"). +
+ ++This page informs you of our policies regarding the collection, use, +and disclosure of personal data when you use our Service +and the choices you have associated with that data. + +
+ ++We use your data to provide and improve the Service. +By using the Service, you agree to the collection +and use of information in accordance with this policy. + +Unless otherwise defined in this Privacy Policy, +terms used in this Privacy Policy have the same meanings +as in our Terms and Conditions, +accessible from https://imapsync.lamiral.info
+ + +We collect several different types of information +for various purposes to provide and improve our Service to you.
+ +While using our Service, we may ask you to provide us with +certain personally identifiable information that can be used to +contact or identify you ("Personal Data"). +Personally identifiable information may include, but is not limited to:
+ ++We may also collect information how the Service is +accessed and used ("Usage Data"). +This Usage Data may include information such as your +computer's Internet Protocol address (e.g. IP address), +browser type, browser version, +the pages of our Service that you visit, the time and date of your visit, +the time spent on those pages, unique device identifiers +and other diagnostic data.
+ ++We use cookies and similar tracking technologies to track the activity +on our Service and hold certain information. +
+ ++Cookies are files with small amount of data which may include an +anonymous unique identifier. +Cookies are sent to your browser from a website and stored on your device. +Tracking technologies also used are beacons, tags, and scripts to collect +and track information and to improve and analyze our Service. +
+ ++You can instruct your browser to refuse all cookies or to indicate +when a cookie is being sent. +If you do not accept cookies, it should be ok to use our Service. +
+ +Examples of Cookies we use:
+imapsync uses the collected data for various purposes: +
++Your information, including Personal Data, may be transferred to +— and maintained on — computers located outside of your state, +province, country or other governmental jurisdiction where the +data protection laws may differ than those from your jurisdiction. +
+ ++If you are located outside France and choose to provide information to us, +please note that we transfer the data, including Personal Data, +to France and process it there. +
+ ++Your consent to this Privacy Policy followed by your submission of +such information represents your agreement to that transfer. +
+ ++imapsync will take all steps reasonably necessary to ensure that your +data is treated securely and in accordance with this Privacy Policy and +no transfer of your Personal Data will take place to an organization or +a country unless there are adequate controls in place including the +security of your data and other personal information. +
+ +imapsync may disclose your Personal Data in the +good faith belief that such action is necessary to: +
+ +The security of your data is important to us, but remember that +no method of transmission over the Internet, +or method of electronic storage is 100% secure. + +While we strive to use commercially acceptable means +to protect your Personal Data, we cannot guarantee +its absolute security. +
+ +We may employ third party companies and individuals +to facilitate our Service ("Service Providers"), +to provide the Service on our behalf, +to perform Service-related services +or to assist us in analyzing how our Service is used. +
+ ++These third parties have access to your Personal Data only +to perform these tasks on our behalf and are obligated +not to disclose or use it for any other purpose. +
+ + + ++Our Service may contain links to other sites that are not operated by us. +If you click on a third party link, you will be directed to +that third party's site. +We strongly advise you to review the Privacy Policy of every site you visit. +
+ ++We have no control over and assume no responsibility for the content, +privacy policies or practices of any third party sites or services. +
+ + +Our Service does not address anyone under the age of 18 ("Children"). +
+ ++We do not knowingly collect personally identifiable information +from anyone under the age of 18. +If you are a parent or guardian and you are aware +that your Children has provided us with Personal Data, +please contact us. + +If we become aware that we have collected Personal Data +from children without verification of parental consent, +we take steps to remove that information from our servers. +
+ + ++We may update our Privacy Policy from time to time. +We will notify you of any changes by posting the +new Privacy Policy on this page. +
+ + ++We will let you know via email and/or a prominent notice +on our Service, prior to the change becoming effective and +update the "effective date" at the top of this Privacy Policy. +
+ ++You are advised to review this Privacy Policy periodically +for any changes. +Changes to this Privacy Policy are effective when +they are posted on this page. +
+ + +If you have any questions about this Privacy Policy, please contact us: +
+ ++Our Privacy Policy for imapsync was created with the help of the + +Free Privacy Policy Generator. +
+ + +Please read these terms and conditions carefully before using Our Service. +
+ ++The words of which the initial letter is capitalized +have meanings defined under the following conditions. +
+ ++The following definitions shall have the same meaning +regardless of whether they appear in singular or in plural. +
+ +For the purposes of these Terms and Conditions: +
+ ++These are the Terms and Conditions governing the use of this Service +and the agreement that operates between You and the Company. +These Terms and Conditions set out the rights and obligations +of all users regarding the use of the Service. +
+ +Your access to and use of the Service is conditioned on +Your acceptance of and compliance with these Terms and Conditions. +These Terms and Conditions apply to all visitors, +users and others who access or use the Service. +
+ ++By accessing or using the Service +You agree to be bound by these Terms and Conditions. +If You disagree with any part of these Terms and Conditions then +You may not access the Service. +
+ ++Your access to and use of the Service is also conditioned on Your +acceptance of and compliance with the Privacy Policy of the Company. +Our Privacy Policy describes Our policies and procedures on the collection, +use and disclosure of Your personal information when You +use the Application or the Website and tells You +about Your privacy rights and how the law protects You. +Please read Our Privacy Policy +carefully before using Our Service. +
+ + + ++Our Service may contain links to third-party web sites or services +that are not owned or controlled by the Company. +
+ ++The Company has no control over, and assumes no responsibility for, +the content, privacy policies, or practices of any third +party web sites or services. +You further acknowledge and agree that the Company shall not +be responsible or liable, directly or indirectly, +for any damage or loss caused or alleged to be caused by or +in connection with the use of or reliance on any such content, +goods or services available on or through any such web sites or services. +
+ ++We strongly advise You to read the terms and conditions and +privacy policies of any third-party web sites or services that You visit. +
+ ++We may terminate or suspend Your access immediately, +without prior notice or liability, +for any reason whatsoever, +including without limitation if You breach these Terms and Conditions. +
+ ++Upon termination, Your right to use the Service will cease immediately. +
+ ++Notwithstanding any damages that You might incur, +the entire liability of the Company and any of its suppliers under +any provision of this Terms and Your exclusive remedy for all of +the foregoing shall be limited to the amount actually paid by You through +the Service or nothing if You haven't purchased anything through the Service. +
+ ++To the maximum extent permitted by applicable law, +in no event shall the Company or its suppliers be liable for any +special, incidental, indirect, or consequential +damages whatsoever (including, but not limited to, +damages for loss of profits, loss of data or other information, +for business interruption, for personal injury, +loss of privacy arising out of or in any way related +to the use of or inability to use the Service, +third-party software and/or third-party hardware used with +the Service, or otherwise in connection with any +provision of this Terms), even if the Company or any supplier has been +advised of the possibility of such damages and +even if the remedy fails of its essential purpose. +
+ ++Some states do not allow the exclusion of implied warranties or +limitation of liability for incidental or consequential damages, which +means that some of the above limitations may not apply. +In these states, each party's liability will be limited to +the greatest extent permitted by law. +
+ +The Service is provided to You "AS IS" and "AS AVAILABLE" and with +all faults and defects without warranty of any kind. +To the maximum extent permitted under applicable law, the Company, on its +own behalf and on behalf of its Affiliates and its and their +respective licensors and service providers, expressly disclaims +all warranties, whether express, implied, statutory or otherwise, +with respect to the Service, including all implied warranties of +merchantability, fitness for a particular purpose, title and +non-infringement, and warranties that may arise out of course of +dealing, course of performance, usage or trade practice. + +Without limitation to the foregoing, the Company provides no +warranty or undertaking, and makes no representation of any +kind that the Service will meet Your requirements, achieve any +intended results, be compatible or work with any other +software, applications, systems or services, operate without +interruption, meet any performance or reliability standards or +be error free or that any errors or defects can or will be corrected. +
+ ++Without limiting the foregoing, neither the Company nor any of +the company's provider makes any representation or warranty of any +kind, express or implied: +(i) as to the operation or availability of the Service, +or the information, content, and materials +or products included thereon; +(ii) that the Service will be uninterrupted or error-free; +(iii) as to the accuracy, reliability, or currency of any information or +content provided through the Service; or +(iv) that the Service, its servers, the content, or e-mails sent from or +on behalf of the Company are free of viruses, scripts, trojan horses, +worms, malware, timebombs or other harmful components. +
+ ++Some jurisdictions do not allow the exclusion of certain types of +warranties or limitations on applicable statutory rights of +a consumer, so some or all of the above exclusions and +limitations may not apply to You. +But in such a case the exclusions and limitations set forth in +this section shall be applied to the greatest extent enforceable +under applicable law. +
+ ++The laws of the Country, excluding its conflicts of law rules, shall +govern this Terms and Your use of the Service. +Your use of the Application may also be subject to other +local, state, national, or international laws. +
+ ++If You have any concern or dispute about the Service, You +agree to first try to resolve the dispute informally by +contacting the Company. +
+ + ++If You are a European Union consumer, you will benefit from +any mandatory provisions of the law of the country in which you +are resident in. +
+ + ++You represent and warrant that +(i) You are not located in a country that is subject to the +United States government embargo, or that has been designated by +the United States government as a “terrorist supporting” country, and +(ii) You are not listed on any United States government list of +prohibited or restricted parties. +
+ ++If any provision of these Terms is held to be unenforceable or invalid, +such provision will be changed and interpreted to accomplish the +objectives of such provision to the greatest extent possible under +applicable law and the remaining provisions will continue in full +force and effect. +
+ ++Except as provided herein, the failure to exercise a right or +to require performance of an obligation under this Terms shall not +effect a party's ability to exercise such right or require such +performance at any time thereafter nor shall be the waiver of +a breach constitute a waiver of any subsequent breach. +
+ + ++These Terms and Conditions may have been translated +if We have made them available to You on our Service. +
+ ++You agree that the original English text shall prevail +in the case of a dispute. +
+ ++We reserve the right, at Our sole discretion, +to modify or replace these Terms at any time. +If a revision is material We will make reasonable efforts +to provide at least 30 days' notice prior to any new terms taking effect. +What constitutes a material change will be determined at Our sole discretion. +
+ ++By continuing to access or use Our Service +after those revisions become effective, +You agree to be bound by the revised terms. +If You do not agree to the new terms, +in whole or in part, +please stop using the website and the Service. +
+ + ++If you have any questions about these Terms and Conditions, +You can contact us: +
+ ++Our Terms and Conditions agreement was created with the help of the + +Free Terms and Conditions Generator +. +
+ + +++What’s your favorite IMAP migration/sync tool? I made the best experiences with +@imapsync. +Thanks a lot, you saved my day!
— petRockBlog (@petrockblog) +September 24, 2019 +
++@imapsync is awesome !
— Eusener Castilho (@eusener) +September 13, 2019 +
For me, this is still the most useful tool anyone has ever created
@@ -49,6 +62,21 @@ I'm not sure I deserve such great quotes for my work on Imapsync, anyway here th
++You rocks! I'm using IMAPsync since the early days and it resolved soo much problems +that some "Enterprise solutions" didn't manage :)))
— Manuel Serrenti (@meksone) +July 11, 2019 +
+merci beaucoup for the fantastic +@imapsync +- well documented, feature filled, great bit of work, thanks loads. donated. +
— George Wright (@georgie) +August 21, 2019 +
Right next to chocolate, imapsync is humankind’s best invention.
— Mathias Maul (@dontcallmedarth) @@ -56,6 +84,15 @@ I'm not sure I deserve such great quotes for my work on Imapsync, anyway here th
++Finally shifting my mail archives from my scratch-built +emails server in Singapore to my new MailCow server in Sydney... +imapsync is crucial! And +#PerlLives! +
— Dave Lane (@lightweight) +August 11, 2019 +
Imapsync was the best investment I've ever done.
— Uwe Keim @@ -63,6 +100,15 @@ I'm not sure I deserve such great quotes for my work on Imapsync, anyway here th
++Bonjour, on recommande d'utiliser l'excellent +@imapsync +pour ça, il existe même une version en ligne : +https://t.co/ZCNAwjRzby +
— o2switch - Alexis 🐯 (@o2switch_alexis) +July 25, 2019 +
My life right now. Would be 10x worse without @imapsync
— G.C. (@its_gc) @@ -81,7 +127,7 @@ I'm not sure I deserve such great quotes for my work on Imapsync, anyway here thHi Gilles! If every tool just fracking worked like this, my life would be sooo much easier. Thanks a ton for this simple to use yet extremely powerfull tool! I'm glad you made it easy to (get) support as well ;)
— Jeroen (@Planet_Jeroen) December 27, 2017
- + @@ -111,7 +157,7 @@ alt="Viewable With Any Browser" > This document was last modified on -($Id: testimonial.shtml,v 1.5 2018/06/10 20:03:54 gilles Exp gilles $)
+($Id: testimonial.shtml,v 1.8 2019/11/15 11:35:16 gilles Exp gilles $)
Top of the page diff --git a/TODO b/TODO index 4da7409..0ffe9da 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ #!/bin/cat -# $Id: TODO,v 1.215 2019/06/20 10:12:00 gilles Exp gilles $ +# $Id: TODO,v 1.230 2020/01/03 22:39:22 gilles Exp gilles $ This documentation is also at http://imapsync.lamiral.info/#doc @@ -8,6 +8,141 @@ TODO file for imapsync ---------------------- +SUGGESTED 2020_01_03 by Gilles +Bugfix. When running ./imapsync --tests --testslive +Host Nb folders are 0, which is false. + + +SUGGESTED 2020_01_03 by Gilles +https://lamiral.info/~gilles/imapsync/cover_db/imapsync.html +is result of ./imapsync --tests --testslive +Increase the subroutine coverage to 100% by +removing unused ones or add a test call for the remaining. + +SUGGESTED 2019_12_26 by Gilles +For /X Look at +https://en.wikipedia.org/wiki/ModSecurity + + +SUGGESTED 2019_12_26 by Gilles +Look at +https://metacpan.org/pod/Test::Warnings + + +SUGGESTED 2019_12_26 by https://github.com/masbaehr +Add the "throttle" word in the README about +--maxmessagespersecond --maxbytespersecond +See https://github.com/imapsync/imapsync/issues/204 + + +SUGGESTED 2016_07_12 by Fronik +With --automap apply the mapping to subfolders of mapped folders. +If +Sent => Envoyes +then +Sent.Foo => Envoyes.Foo +Sent.Foo.Bar => Envoyes.Foo.bar + + +SUGGESTED 2019_10_22 by Gilles +Add the download link in imapsync itself, with --releasecheck + +SUGGESTED 2019_10_20 by Olav Seyfarth +One suggestion/request regarding INSTALL.OnlineUI.txt: Please add a +typical NGINX setup since NGINX is used very often now and requires a +quite different setup: one has to set up a fcgi service. + +SUGGESTED 2019_10_02 by Joel from Blue Net Box +Fix this issue: +imapsync_bin_Darwin cant be opened because its integrity cannot be verified. +This software needs to be updated. Contact the developer for more information. +MacOS 10.15 is the new macOS Catalina (in the last beta phase) +This error message happens in a shell window. +Same imapsync_bin_Darwin in macOS 10.14 works fine. + +SUGGESTED 2019_10_02 by Gilles +Look at Traefik to automatic ssl services (webserver). + + +RE_SUGGESTED 2019_09_26 by Andre Adrian (rotlaus) +Allow @ in subfolder names #195 +https://github.com/imapsync/imapsync/issues/195 +Allow also & (imap_utf7 character) +Or add a --nosanetizesubfolder option. + +SUGGESTED 2019_07_01 by Jeffrey Thatplayer +https://github.com/imapsync/imapsync/issues/181 +Make sanitizing an option for --subfolder1 --subfolder2 +Two options? --nosanitize1 --nosanitize2 +Reflecting more I wonder if sanitizing for --subfolder1 is just stupid. +It's not, it's good from the usage point of view, same string to point out +the destination backup/restore folder + + +SUGGESTED 2019_09_23 by Celko Michal. +My proposal is to create a status file. +it would be a command line option, ex. + imapsync ... --status-file /var/www/hosting/tmp/xyz.json +When I start imapsync process with this option, it would generate +and update this status file every few seconds, so any application +can access it and see what's going on with this process without +parsing logs or keeping up with the log format when it changes. +Example of such status file would be: +In progress +{ +"status": "In progress", +"progress": "245/5000", +"Message": "", +"Debug": "", +"Process ID": "pid123456" +} +On error example +{ +"status": "Fail", +"progress": "245/5000", +"Message": "ERRCODE:123", +"Debug": "LOG_imapsync/2019_09_22_09_01_42_058_abc@example.com_JDoe@example.com.txt", +"Process ID": "pid123456" +} +On complete example* +{ +"status": "Completed", +"progress": "5000/5000", +"Message": "", +"Debug": "LOG_imapsync/2019_09_22_09_01_42_058_abc@example.com_JDoe@example.com.txt", +"Process ID": "" +} + + +SUGGESTED 2019_07_30 by Gilles +Only since imapsync 1.774 the release check is done with +\r\n to end lines, instead of the not RFC compliant \n. +If I upgrade Apache on imapsync.lamiral.info +ii apache2-mpm-worker 2.2.22-13+deb7u7 amd64 Apache HTTP Server - high speed threaded model +then any imapsync prior to 1.774 will fail to GET the VERSION file. +Release check life with names: + +PeerAddr "linux-france.org" in release 1.350 +"Host: www.linux-france.org\n\n" in release 1.350 to 1.466 + +PeerAddr "imapsync.lamiral.info" in release 1.374 and upper +"Host: www.linux-france.org\n\n" in release 1.464 but not after +"Host: ks.lamiral.info\n\n" in release 1.467 and upper + +"Host: ks.lamiral.info\r\n\r\n" in release 1.836 and upper + +Solution to test: Use the directive +HttpProtocolOptions Unsafe # available from 2.2.32 or 2.4.24 +http://httpd.apache.org/docs/2.4/mod/core.html#httpprotocoloptions +ks5 should be ok: Server Version: Apache/2.4.33 (FreeBSD) + + + +SUGGESTED on 31 Jan 2017 by tibbsbrookside +Try this code XOAUTH2 +https://github.com/imapsync/imapsync/issues/87#issuecomment-276298841 + + SUGGESTED 2019_06_18 by Konrad Wawryn Add NTLMv2 support It shouldn't be that hard to support NTLMv2 because @@ -63,11 +198,6 @@ Take a look at Test::NoWarnings Test::Warn -SUGGESTED 2018_05_15 by Massimo Maggi -https://github.com/imapsync/imapsync/issues/141 -Document --skipcrossduplicates and --debugcrossduplicates in imapsync --help -Document all other useful missing options. - SUGGESTED 2018_05_08 by James B. Byrne Sync also the quota with SETQUOTA https://tools.ietf.org/html/rfc2087 @@ -76,10 +206,6 @@ SUGGESTED 2017_12_19 by Gilles In foldersizes, add info about the oldest message found: INTERNALDATE EPOCH DAYS_FROM_NOW See draft in sub info_date_from_uid -SUGGESTED 2017_09_04 by Gilles -STDOUT instead of STDERR with Mail::IMAPClient output. -Shoul be easy using $imap->Debug_fh($fileHandle); - SUGGESTED 2017_08_31 by Gilles Makefile. Add a "make docker" goal that build, test and publish the @@ -108,16 +234,6 @@ Review the "make install" in Makefile SUGGESTED 2017_04_26 by Franco Fassio --usecache with trailing dots foo... in folder name breaks on NTFS. -SUGGESTED 2017_04_26 by Franco Fassio -Check that --maxbyteperseconds does not interfer with --timeout -by sleeping too long. - -DONE by Gilles revision 1.795 date: 2017/04/22 -SUGGESTED 2016_12_01 by Gilles -When working under with webmin and virtualmin imapsync -acts as a cgi, it should not. Find a way to avoid cgi mode -even under a web server. Solution: edit the sub under_cgi_context() - SUGGESTED 2016_11_16 by Flvio Zarur Lucarelli LucaNet Sistemas Getting subjects of messages for duplicates and print them. @@ -158,29 +274,12 @@ Exchange Online errors that need a relogin: SUGGESTED 2016_08_31 by Gilles Rename $expungeaftereach $expungeaftereach1 or $expunge1aftereach -SUGGESTED 2016_08_31 by Gilles & David Carter -Detect equivalent folders by upper/lower case on host1 -and add a way to not merge them when host2 is case insensitive. + SUGGESTED 2016_08_19 by Gilles Go back to SSL_VERIFY_PEER but include SSL_ca_file inside imapsync or near. -SUGGESTED 2016_08_07 by Gilles -Add a meaningful exit value to all die: - * fatal software dependency, Perl modules - * fatal parameter issue - * fatal connect issue - * fatal login issue - * fatal permission file issue (open) - * fatal IMAP issue - * fatal IMAP disconnection -Maybe replace all die by exit -Add a meaningful exit value to all exit - * exit at end but with errors - * exit at middle because of errormax - * exit by signal - SUGGESTED 2016_07_29 by Gilles from CSS3 Coursera course Take a look at @@ -190,14 +289,6 @@ SUGGESTED 2016_07_29 by Gilles Move website to HTML5 https://about.validator.nu/ -SUGGESTED 2016_07_12 by Fronik -With --automap apply the mapping to subfolders of mapped folders. -If -Sent => Envoyes -then -Sent.Foo => Envoyes.Foo -Sent.Foo.Bar => Envoyes.Foo.bar - SUGGESTED 2016_07_07 by Jean-Dominique Delyon. Add a way to know easily which account transfers went wrong. Gnrer un fichier des comptes qui ont rencontr des problmes @@ -245,10 +336,6 @@ no header found so adding our own [Message-Id: <151648@imapsync>] SUGGESTED 2016_04_17 by Gilles Add a --passfile to allow user=>password style file. -WANTED 2016_03_11 -Add a FAQ about Authentication failures and quoting. - - WANTED 2016_02_10 Add stats about "Messages found crossduplicate on host1" @@ -449,6 +536,41 @@ http://asg.web.cmu.edu/cyrus/download/imapd/altnamespace.html Now the TODO done! (or not) =========================================================================== +DONE 2019/12/16 revision 1.974 by Gilles +Use Debug_fh to set where go the --debugimap outputs. +SUGGESTED 2017_09_04 by Gilles +STDOUT + logfile instead of STDERR with Mail::IMAPClient output. +Should be easy using $imap->Debug_fh( $fileHandle ) ; + +DONE 2019_12_11 by Gilles +SUGGESTED 2019_11_28 by Gilles +Find a place to add this advice: +https://github.com/imapsync/imapsync/issues/201#issuecomment-559500077 +Place: https://imapsync.lamiral.info/FAQ.d/FAQ.Docker.txt + + +DONE 2019_11_27 by Gilles +SUGGESTED 2019_09_30 by Gilles +Update the docker build +Mail::IMAPClient 3.38 to latest +Ok Mail::IMAPClient 3.42 in docker gilleslamiral/imapsync 1.967 a82b5d5ff2f0 + +DONE 2019_11_19 by Gilles revision 1.961 +SUGGESTED 2019_11_13 by Flavio +Are --delete2foldersonly or --delete2folders or --delete2foldersbutnot +compatible with --subfolder2 ? +Yes but fix this +--delete2foldersonly SUB --subfolder SUB deletes SUB +while +--delete2foldersonly SUB. --subfolder SUB does not delete SUB +(seen the dot?) + + +DONE 2019_07_30 by Gilles release 1.955 +SUGGESTED 2018_05_15 by Massimo Maggi +https://github.com/imapsync/imapsync/issues/141 +Document --skipcrossduplicates and --debugcrossduplicates in imapsync --help + DONE 2019_04_28 by Gilles SUGGESTED 2016_10_03 by Gilles Do like tail -F on logfile when @@ -461,6 +583,43 @@ SUGGESTED 2019_04_25 by Gilles Add length of $cache_dir to Local cache directory: $cache_dir + +DONE 2019/04/11 by Gilles revision 1.930 +SUGGESTED 2016_08_07 by Gilles +Add a meaningful exit value to all die: + * fatal software dependency, Perl modules + * fatal parameter issue + * fatal connect issue + * fatal login issue + * fatal permission file issue (open) + * fatal IMAP issue + * fatal IMAP disconnection +Maybe replace all die by exit +Add a meaningful exit value to all exit + * exit at end but with errors + * exit at middle because of errormax + * exit by signal + + +DONE 2017_07_13 +SUGGESTED 2016_03_11 +Add a FAQ about Authentication failures and quoting. +FAQ.d/FAQ.Authentication_failure.txt +FAQ.d/FAQ.Passwords_on_Unix.txt +FAQ.d/FAQ.Passwords_on_Windows.txt + +DONE 2017/05/24 by Gilles revision 1.813 +SUGGESTED 2017_04_26 by Franco Fassio +Check that --maxbyteperseconds does not interfer with --timeout +by sleeping too long. + + +DONE by Gilles revision 1.795 date: 2017/04/22 +SUGGESTED 2016_12_01 by Gilles +When working under with webmin and virtualmin imapsync +acts as a cgi, it should not. Find a way to avoid cgi mode +even under a web server. Solution: edit the sub under_cgi_context() + DONE 2019_04_08 by Gilles SUGGESTED 2019_03_06 by Gilles when exit by signal, exit by sending a signal to itself: @@ -584,6 +743,12 @@ DONE 2015_08_03. WANTED 2015_07_21 Fix W/learn/imap_utf7_encode --regextrans2 "s,El&AOk-ments,&AMk-l&AOk-ments," +DONE 2014/10/06 by Gilles revision 1.597 +SUGGESTED 2016_08_31 by Gilles & David Carter +(I don't know why the DONE date older than the SUGGESTED date) +Detect equivalent folders by upper/lower case on host1 +and add a way to not merge them when host2 is case insensitive. + DONE. Add a FAQ entry about long path over than 260 character on Win32. DONE. Build and distribute a standalone Darwin Mac OS X binary. diff --git a/VERSION b/VERSION index b2f91a4..93df0bc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.945 +1.977 diff --git a/W/.BUILD_EXE_TIME b/W/.BUILD_EXE_TIME index f70f7c1..8192618 100644 --- a/W/.BUILD_EXE_TIME +++ b/W/.BUILD_EXE_TIME @@ -697,3 +697,63 @@ 1561592446 END 1.945 : jeudi 27 juin 2019, 01:40:46 (UTC+0200) 1561592446 BEGIN 64bit 1.945 : jeudi 27 juin 2019, 01:40:46 (UTC+0200) 1561592761 END 64bit 1.945 : jeudi 27 juin 2019, 01:46:01 (UTC+0200) +1563247231 BEGIN 1.947 : mardi 16 juillet 2019, 05:20:31 (UTC+0200) +1563271510 BEGIN 1.947 : mardi 16 juillet 2019, 12:05:10 (UTC+0200) +1563272943 END 1.947 : mardi 16 juillet 2019, 12:29:03 (UTC+0200) +1564388334 BEGIN 1.953 : lundi 29 juillet 2019, 10:18:54 (UTC+0200) +1564627935 BEGIN 1.955 : jeudi 1 août 2019, 04:52:15 (UTC+0200) +1564629209 END 1.955 : jeudi 1 août 2019, 05:13:29 (UTC+0200) +1564629209 BEGIN 64bit 1.955 : jeudi 1 août 2019, 05:13:29 (UTC+0200) +1564659229 BEGIN 64bit 1.955 : jeudi 1 août 2019, 13:33:49 (UTC+0200) +1564659589 END 64bit 1.955 : jeudi 1 août 2019, 13:39:49 (UTC+0200) +1564795411 BEGIN 1.956 : samedi 3 août 2019, 03:23:31 (UTC+0200) +1564796657 END 1.956 : samedi 3 août 2019, 03:44:17 (UTC+0200) +1564796657 BEGIN 64bit 1.956 : samedi 3 août 2019, 03:44:17 (UTC+0200) +1564823546 BEGIN 64bit 1.956 : samedi 3 août 2019, 11:12:26 (UTC+0200) +1564823894 END 64bit 1.956 : samedi 3 août 2019, 11:18:14 (UTC+0200) +1574416593 BEGIN 1.962 : vendredi 22 novembre 2019, 10:56:34 (UTC+0100) +1574419847 BEGIN 1.962 : vendredi 22 novembre 2019, 11:50:47 (UTC+0100) +1574429786 BEGIN 64bit 1.962 : vendredi 22 novembre 2019, 14:36:26 (UTC+0100) +1574433224 BEGIN 64bit 1.962 : vendredi 22 novembre 2019, 15:33:44 (UTC+0100) +1574433927 BEGIN 1.962 : vendredi 22 novembre 2019, 15:45:27 (UTC+0100) +1574434278 BEGIN 64bit 1.962 : vendredi 22 novembre 2019, 15:51:18 (UTC+0100) +1574434980 BEGIN 64bit 1.962 : vendredi 22 novembre 2019, 16:03:00 (UTC+0100) +1574435270 BEGIN 64bit 1.962 : vendredi 22 novembre 2019, 16:07:50 (UTC+0100) +1574450028 BEGIN 1.962 : vendredi 22 novembre 2019, 20:13:48 (UTC+0100) +1574450399 END 1.962 : vendredi 22 novembre 2019, 20:19:59 (UTC+0100) +1574452231 BEGIN 1.962 : vendredi 22 novembre 2019, 20:50:32 (UTC+0100) +1574452286 BEGIN 1.962 : vendredi 22 novembre 2019, 20:51:26 (UTC+0100) +1574452715 END 1.962 : vendredi 22 novembre 2019, 20:58:36 (UTC+0100) +1574681848 BEGIN 64bit 1.962 : lundi 25 novembre 2019, 12:37:28 (UTC+0100) +1574692336 BEGIN 1.963 : lundi 25 novembre 2019, 15:32:16 (UTC+0100) +1574693629 END 1.963 : lundi 25 novembre 2019, 15:53:49 (UTC+0100) +1574693629 BEGIN 64bit 1.963 : lundi 25 novembre 2019, 15:53:49 (UTC+0100) +1574693945 END 64bit 1.963 : lundi 25 novembre 2019, 15:59:05 (UTC+0100) +1574953419 BEGIN 1.967 : jeudi 28 novembre 2019, 16:03:39 (UTC+0100) +1574954698 END 1.967 : jeudi 28 novembre 2019, 16:24:58 (UTC+0100) +1574954698 BEGIN 64bit 1.967 : jeudi 28 novembre 2019, 16:24:58 (UTC+0100) +1574955031 END 64bit 1.967 : jeudi 28 novembre 2019, 16:30:31 (UTC+0100) +1575033978 BEGIN 1.969 : vendredi 29 novembre 2019, 14:26:18 (UTC+0100) +1575035212 END 1.969 : vendredi 29 novembre 2019, 14:46:52 (UTC+0100) +1575035212 BEGIN 64bit 1.969 : vendredi 29 novembre 2019, 14:46:52 (UTC+0100) +1575035732 END 64bit 1.969 : vendredi 29 novembre 2019, 14:55:32 (UTC+0100) +1575911925 BEGIN 1.969 : lundi 9 décembre 2019, 18:18:45 (UTC+0100) +1575916737 BEGIN 1.969 : lundi 9 décembre 2019, 19:38:57 (UTC+0100) +1575924912 END 1.969 : lundi 9 décembre 2019, 21:55:12 (UTC+0100) +1575931824 BEGIN 64bit 1.969 : lundi 9 décembre 2019, 23:50:24 (UTC+0100) +1575932149 END 64bit 1.969 : lundi 9 décembre 2019, 23:55:49 (UTC+0100) +1575933203 BEGIN 1.970 : mardi 10 décembre 2019, 00:13:23 (UTC+0100) +1575973632 BEGIN 1.970 : mardi 10 décembre 2019, 11:27:12 (UTC+0100) +1575973801 BEGIN 1.970 : mardi 10 décembre 2019, 11:30:01 (UTC+0100) +1575973972 BEGIN 1.970 : mardi 10 décembre 2019, 11:32:52 (UTC+0100) +1575974574 BEGIN 1.970 : mardi 10 décembre 2019, 11:42:54 (UTC+0100) +1575975909 END 1.970 : mardi 10 décembre 2019, 12:05:09 (UTC+0100) +1575977906 BEGIN 64bit 1.970 : mardi 10 décembre 2019, 12:38:26 (UTC+0100) +1575978279 END 64bit 1.970 : mardi 10 décembre 2019, 12:44:39 (UTC+0100) +1575978283 BEGIN 32bit 1.970 : mardi 10 décembre 2019, 12:44:43 (UTC+0100) +1575979537 END 32bit 1.970 : mardi 10 décembre 2019, 13:05:37 (UTC+0100) +1577132428 BEGIN 64bit 1.977 : lundi 23 décembre 2019, 21:20:28 (UTC+0100) +1577133568 BEGIN 64bit 1.977 : lundi 23 décembre 2019, 21:39:28 (UTC+0100) +1577134322 END 64bit 1.977 : lundi 23 décembre 2019, 21:52:02 (UTC+0100) +1577134327 BEGIN 32bit 1.977 : lundi 23 décembre 2019, 21:52:07 (UTC+0100) +1577135607 END 32bit 1.977 : lundi 23 décembre 2019, 22:13:27 (UTC+0100) diff --git a/W/.compok b/W/.compok index 445108a..a599f60 100644 --- a/W/.compok +++ b/W/.compok @@ -114,3 +114,24 @@ mardi 25 juin 2019, 10:46:03 (UTC+0200) mardi 25 juin 2019, 18:52:01 (UTC+0200) mercredi 26 juin 2019, 17:34:23 (UTC+0200) mercredi 26 juin 2019, 21:32:34 (UTC+0200) +mardi 16 juillet 2019, 03:57:32 (UTC+0200) +mercredi 17 juillet 2019, 17:45:33 (UTC+0200) +mercredi 17 juillet 2019, 18:35:58 (UTC+0200) +mercredi 17 juillet 2019, 18:40:36 (UTC+0200) +mercredi 17 juillet 2019, 18:49:36 (UTC+0200) +dimanche 21 juillet 2019, 02:47:49 (UTC+0200) +jeudi 25 juillet 2019, 19:10:43 (UTC+0200) +jeudi 25 juillet 2019, 20:30:32 (UTC+0200) +jeudi 25 juillet 2019, 22:46:56 (UTC+0200) +vendredi 26 juillet 2019, 12:13:40 (UTC+0200) +lundi 29 juillet 2019, 09:26:31 (UTC+0200) +jeudi 1 août 2019, 04:03:35 (UTC+0200) +samedi 3 août 2019, 02:31:37 (UTC+0200) +mercredi 20 novembre 2019, 14:59:11 (UTC+0100) +lundi 25 novembre 2019, 15:18:38 (UTC+0100) +lundi 2 décembre 2019, 15:59:44 (UTC+0100) +mercredi 4 décembre 2019, 19:41:38 (UTC+0100) +mercredi 11 décembre 2019, 22:12:49 (UTC+0100) +jeudi 19 décembre 2019, 10:52:31 (UTC+0100) +vendredi 20 décembre 2019, 18:00:28 (UTC+0100) +mardi 24 décembre 2019, 22:25:29 (UTC+0100) diff --git a/W/.tests.errors.txt b/W/.tests.errors.txt index f46c458..7b92c76 100644 --- a/W/.tests.errors.txt +++ b/W/.tests.errors.txt @@ -3942,3 +3942,994 @@ 2019_06_27_00_22_07 : ALL 1 TESTS SUCCESSFUL 2019_06_27_00_35_49 : ALL 1 TESTS SUCCESSFUL 2019_06_27_01_15_17 : ALL 157 TESTS SUCCESSFUL +2019_06_28_20_24_14 : FAILED 1/1 TESTS: perl_syntax +2019_06_28_20_24_38 : ALL 1 TESTS SUCCESSFUL +2019_06_28_20_25_22 : FAILED 1/2 TESTS: ll_with_errors +2019_06_28_21_11_02 : ALL 1 TESTS SUCCESSFUL +2019_06_28_21_11_12 : FAILED 1/2 TESTS: ll_with_errors +2019_06_30_21_14_13 : ALL 1 TESTS SUCCESSFUL +2019_06_30_21_15_23 : ALL 1 TESTS SUCCESSFUL +2019_06_30_21_15_31 : ALL 2 TESTS SUCCESSFUL +2019_06_30_21_18_02 : ALL 1 TESTS SUCCESSFUL +2019_06_30_21_18_09 : ALL 2 TESTS SUCCESSFUL +2019_06_30_21_26_15 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_03_07 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_32_55 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_33_02 : ALL 2 TESTS SUCCESSFUL +2019_06_30_23_34_34 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_34_41 : ALL 2 TESTS SUCCESSFUL +2019_06_30_23_41_17 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_41_24 : ALL 2 TESTS SUCCESSFUL +2019_06_30_23_52_35 : ALL 1 TESTS SUCCESSFUL +2019_06_30_23_52_42 : ALL 2 TESTS SUCCESSFUL +2019_07_01_00_12_45 : ALL 1 TESTS SUCCESSFUL +2019_07_01_00_12_53 : ALL 2 TESTS SUCCESSFUL +2019_07_01_00_41_17 : ALL 1 TESTS SUCCESSFUL +2019_07_01_00_41_26 : ALL 2 TESTS SUCCESSFUL +2019_07_01_15_17_23 : ALL 1 TESTS SUCCESSFUL +2019_07_01_15_17_34 : ALL 2 TESTS SUCCESSFUL +2019_07_01_16_14_26 : ALL 1 TESTS SUCCESSFUL +2019_07_01_16_14_33 : ALL 2 TESTS SUCCESSFUL +2019_07_01_16_14_51 : ALL 1 TESTS SUCCESSFUL +2019_07_01_16_15_31 : ALL 2 TESTS SUCCESSFUL +2019_07_01_16_30_47 : ALL 1 TESTS SUCCESSFUL +2019_07_01_16_31_53 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_12_37 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_13_43 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_16_24 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_16_40 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_17_10 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_17_19 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_32_44 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_32_52 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_41_58 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_43_09 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_43_33 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_43_50 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_44_22 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_45_02 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_45_21 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_45_29 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_45_38 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_46_20 : ALL 2 TESTS SUCCESSFUL +2019_07_01_18_48_42 : ALL 1 TESTS SUCCESSFUL +2019_07_01_18_49_32 : ALL 2 TESTS SUCCESSFUL +2019_07_01_20_18_11 : ALL 1 TESTS SUCCESSFUL +2019_07_01_20_18_53 : ALL 2 TESTS SUCCESSFUL +2019_07_01_22_01_40 : ALL 1 TESTS SUCCESSFUL +2019_07_01_22_01_48 : ALL 2 TESTS SUCCESSFUL +2019_07_01_22_05_59 : ALL 1 TESTS SUCCESSFUL +2019_07_01_22_06_07 : ALL 2 TESTS SUCCESSFUL +2019_07_02_21_25_57 : ALL 1 TESTS SUCCESSFUL +2019_07_02_21_26_13 : ALL 2 TESTS SUCCESSFUL +2019_07_02_21_28_46 : ALL 1 TESTS SUCCESSFUL +2019_07_02_21_28_51 : ALL 2 TESTS SUCCESSFUL +2019_07_02_21_30_00 : ALL 1 TESTS SUCCESSFUL +2019_07_02_21_30_08 : ALL 2 TESTS SUCCESSFUL +2019_07_02_21_30_24 : ALL 1 TESTS SUCCESSFUL +2019_07_02_21_30_29 : ALL 2 TESTS SUCCESSFUL +2019_07_03_14_53_16 : ALL 1 TESTS SUCCESSFUL +2019_07_03_14_53_16 : FAILED 1/2 TESTS: ll_empty_test2 +2019_07_03_14_55_17 : ALL 1 TESTS SUCCESSFUL +2019_07_03_14_55_30 : ALL 2 TESTS SUCCESSFUL +2019_07_04_01_07_34 : ALL 1 TESTS SUCCESSFUL +2019_07_04_01_07_45 : ALL 2 TESTS SUCCESSFUL +2019_07_04_04_26_45 : ALL 1 TESTS SUCCESSFUL +2019_07_04_04_26_53 : ALL 2 TESTS SUCCESSFUL +2019_07_04_04_32_26 : ALL 1 TESTS SUCCESSFUL +2019_07_04_04_32_34 : ALL 2 TESTS SUCCESSFUL +2019_07_04_04_43_15 : ALL 1 TESTS SUCCESSFUL +2019_07_04_04_43_23 : ALL 2 TESTS SUCCESSFUL +2019_07_04_13_45_18 : ALL 1 TESTS SUCCESSFUL +2019_07_04_13_45_29 : ALL 2 TESTS SUCCESSFUL +2019_07_04_13_58_57 : ALL 1 TESTS SUCCESSFUL +2019_07_04_13_59_06 : ALL 2 TESTS SUCCESSFUL +2019_07_04_14_08_34 : ALL 1 TESTS SUCCESSFUL +2019_07_04_14_08_42 : ALL 2 TESTS SUCCESSFUL +2019_07_04_14_44_20 : ALL 1 TESTS SUCCESSFUL +2019_07_04_14_44_28 : ALL 2 TESTS SUCCESSFUL +2019_07_04_17_21_37 : ALL 1 TESTS SUCCESSFUL +2019_07_04_17_21_45 : ALL 2 TESTS SUCCESSFUL +2019_07_05_14_26_43 : ALL 1 TESTS SUCCESSFUL +2019_07_05_14_26_53 : ALL 2 TESTS SUCCESSFUL +2019_07_05_14_27_44 : ALL 1 TESTS SUCCESSFUL +2019_07_05_14_29_16 : ALL 2 TESTS SUCCESSFUL +2019_07_05_14_42_18 : ALL 1 TESTS SUCCESSFUL +2019_07_05_14_42_27 : ALL 2 TESTS SUCCESSFUL +2019_07_05_14_46_50 : ALL 1 TESTS SUCCESSFUL +2019_07_05_14_46_59 : ALL 2 TESTS SUCCESSFUL +2019_07_05_15_45_23 : ALL 1 TESTS SUCCESSFUL +2019_07_05_15_45_31 : ALL 2 TESTS SUCCESSFUL +2019_07_05_15_51_25 : ALL 1 TESTS SUCCESSFUL +2019_07_05_15_52_08 : ALL 2 TESTS SUCCESSFUL +2019_07_05_15_52_20 : ALL 1 TESTS SUCCESSFUL +2019_07_05_15_52_27 : ALL 2 TESTS SUCCESSFUL +2019_07_05_16_03_13 : ALL 1 TESTS SUCCESSFUL +2019_07_05_16_03_22 : ALL 2 TESTS SUCCESSFUL +2019_07_05_16_19_18 : ALL 1 TESTS SUCCESSFUL +2019_07_05_16_19_27 : ALL 2 TESTS SUCCESSFUL +2019_07_05_16_24_15 : ALL 1 TESTS SUCCESSFUL +2019_07_05_16_24_23 : ALL 2 TESTS SUCCESSFUL +2019_07_05_23_12_34 : ALL 1 TESTS SUCCESSFUL +2019_07_05_23_12_43 : ALL 2 TESTS SUCCESSFUL +2019_07_05_23_28_24 : ALL 1 TESTS SUCCESSFUL +2019_07_05_23_28_33 : ALL 2 TESTS SUCCESSFUL +2019_07_05_23_35_41 : ALL 1 TESTS SUCCESSFUL +2019_07_05_23_35_51 : ALL 2 TESTS SUCCESSFUL +2019_07_06_13_09_49 : ALL 1 TESTS SUCCESSFUL +2019_07_06_13_09_58 : ALL 2 TESTS SUCCESSFUL +2019_07_06_17_00_33 : ALL 1 TESTS SUCCESSFUL +2019_07_06_17_00_42 : ALL 2 TESTS SUCCESSFUL +2019_07_07_23_09_55 : ALL 1 TESTS SUCCESSFUL +2019_07_07_23_10_06 : ALL 2 TESTS SUCCESSFUL +2019_07_07_23_34_14 : ALL 1 TESTS SUCCESSFUL +2019_07_07_23_34_22 : ALL 2 TESTS SUCCESSFUL +2019_07_07_23_39_28 : ALL 1 TESTS SUCCESSFUL +2019_07_07_23_39_36 : ALL 2 TESTS SUCCESSFUL +2019_07_07_23_41_44 : ALL 1 TESTS SUCCESSFUL +2019_07_07_23_41_50 : ALL 2 TESTS SUCCESSFUL +2019_07_09_16_01_49 : ALL 1 TESTS SUCCESSFUL +2019_07_09_16_02_10 : ALL 2 TESTS SUCCESSFUL +2019_07_09_19_34_54 : ALL 1 TESTS SUCCESSFUL +2019_07_09_19_35_34 : ALL 2 TESTS SUCCESSFUL +2019_07_09_20_11_00 : ALL 1 TESTS SUCCESSFUL +2019_07_09_20_11_08 : ALL 2 TESTS SUCCESSFUL +2019_07_09_20_23_41 : ALL 1 TESTS SUCCESSFUL +2019_07_09_20_23_48 : ALL 2 TESTS SUCCESSFUL +2019_07_09_22_46_24 : ALL 1 TESTS SUCCESSFUL +2019_07_09_22_46_32 : ALL 2 TESTS SUCCESSFUL +2019_07_10_22_02_28 : ALL 1 TESTS SUCCESSFUL +2019_07_10_22_02_37 : ALL 2 TESTS SUCCESSFUL +2019_07_11_00_52_03 : ALL 1 TESTS SUCCESSFUL +2019_07_11_00_53_46 : ALL 2 TESTS SUCCESSFUL +2019_07_14_19_55_10 : FAILED 1/1 TESTS: perl_syntax +2019_07_14_19_55_55 : ALL 1 TESTS SUCCESSFUL +2019_07_14_19_56_35 : ALL 1 TESTS SUCCESSFUL +2019_07_14_19_56_44 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_11_52 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_11_57 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_12_57 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_13_00 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_13_34 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_13_38 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_19_14 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_19_18 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_23_12 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_23_16 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_35_20 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_35_25 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_39_09 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_39_20 : ALL 2 TESTS SUCCESSFUL +2019_07_15_00_41_37 : ALL 1 TESTS SUCCESSFUL +2019_07_15_00_41_42 : ALL 2 TESTS SUCCESSFUL +2019_07_15_01_59_07 : ALL 1 TESTS SUCCESSFUL +2019_07_15_01_59_12 : ALL 2 TESTS SUCCESSFUL +2019_07_15_01_59_24 : ALL 1 TESTS SUCCESSFUL +2019_07_15_01_59_28 : ALL 2 TESTS SUCCESSFUL +2019_07_15_02_06_29 : ALL 1 TESTS SUCCESSFUL +2019_07_15_02_14_29 : ALL 1 TESTS SUCCESSFUL +2019_07_15_02_14_33 : FAILED 1/2 TESTS: ll_authmech_X_MASTERAUTH +2019_07_15_02_47_31 : ALL 157 TESTS SUCCESSFUL +2019_07_15_03_06_00 : ALL 1 TESTS SUCCESSFUL +2019_07_15_03_06_16 : ALL 2 TESTS SUCCESSFUL +2019_07_15_03_07_37 : ALL 1 TESTS SUCCESSFUL +2019_07_15_03_07_41 : ALL 2 TESTS SUCCESSFUL +2019_07_15_03_12_22 : ALL 1 TESTS SUCCESSFUL +2019_07_15_03_57_01 : ALL 157 TESTS SUCCESSFUL +2019_07_16_05_12_39 : ALL 1 TESTS SUCCESSFUL +2019_07_16_05_15_56 : ALL 2 TESTS SUCCESSFUL +2019_07_17_02_48_07 : ALL 1 TESTS SUCCESSFUL +2019_07_17_02_49_25 : ALL 2 TESTS SUCCESSFUL +2019_07_17_02_49_31 : ALL 1 TESTS SUCCESSFUL +2019_07_17_02_49_59 : ALL 2 TESTS SUCCESSFUL +2019_07_17_02_51_34 : ALL 1 TESTS SUCCESSFUL +2019_07_17_02_51_42 : ALL 2 TESTS SUCCESSFUL +2019_07_17_02_55_58 : ALL 1 TESTS SUCCESSFUL +2019_07_17_02_56_06 : ALL 2 TESTS SUCCESSFUL +2019_07_17_05_57_51 : ALL 1 TESTS SUCCESSFUL +2019_07_17_06_38_11 : FAILED 1/157 TESTS: ll_subscribed +2019_07_17_17_05_15 : ALL 1 TESTS SUCCESSFUL +2019_07_17_17_07_11 : ALL 2 TESTS SUCCESSFUL +2019_07_17_17_44_05 : ALL 1 TESTS SUCCESSFUL +2019_07_17_17_44_10 : ALL 2 TESTS SUCCESSFUL +2019_07_17_19_56_23 : ALL 1 TESTS SUCCESSFUL +2019_07_17_20_10_34 : ALL 1 TESTS SUCCESSFUL +2019_07_17_20_11_31 : ALL 2 TESTS SUCCESSFUL +2019_07_17_20_17_00 : ALL 1 TESTS SUCCESSFUL +2019_07_17_20_17_59 : ALL 2 TESTS SUCCESSFUL +2019_07_17_20_19_49 : ALL 1 TESTS SUCCESSFUL +2019_07_17_20_20_46 : ALL 2 TESTS SUCCESSFUL +2019_07_17_20_28_08 : ALL 1 TESTS SUCCESSFUL +2019_07_17_20_29_14 : ALL 2 TESTS SUCCESSFUL +2019_07_18_05_05_08 : ALL 1 TESTS SUCCESSFUL +2019_07_18_05_05_18 : ALL 2 TESTS SUCCESSFUL +2019_07_18_05_08_08 : ALL 1 TESTS SUCCESSFUL +2019_07_18_05_08_16 : ALL 2 TESTS SUCCESSFUL +2019_07_18_05_37_25 : ALL 1 TESTS SUCCESSFUL +2019_07_18_05_37_33 : ALL 2 TESTS SUCCESSFUL +2019_07_18_05_46_37 : ALL 1 TESTS SUCCESSFUL +2019_07_18_17_20_24 : ALL 1 TESTS SUCCESSFUL +2019_07_18_17_20_33 : ALL 2 TESTS SUCCESSFUL +2019_07_21_02_06_10 : ALL 1 TESTS SUCCESSFUL +2019_07_21_02_47_37 : ALL 157 TESTS SUCCESSFUL +2019_07_21_03_24_36 : ALL 1 TESTS SUCCESSFUL +2019_07_21_03_30_23 : ALL 2 TESTS SUCCESSFUL +2019_07_21_10_57_49 : ALL 1 TESTS SUCCESSFUL +2019_07_21_10_58_56 : ALL 2 TESTS SUCCESSFUL +2019_07_21_10_59_15 : ALL 1 TESTS SUCCESSFUL +2019_07_21_10_59_29 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_00_47 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_00_54 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_07_41 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_07_48 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_13_51 : FAILED 1/1 TESTS: perl_syntax +2019_07_21_11_16_19 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_16_26 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_40_16 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_40_24 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_40_45 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_40_52 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_41_16 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_41_26 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_49_34 : FAILED 1/1 TESTS: perl_syntax +2019_07_21_11_50_36 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_50_46 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_51_56 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_52_06 : ALL 2 TESTS SUCCESSFUL +2019_07_21_11_53_28 : FAILED 1/1 TESTS: perl_syntax +2019_07_21_11_54_28 : ALL 1 TESTS SUCCESSFUL +2019_07_21_11_54_35 : ALL 2 TESTS SUCCESSFUL +2019_07_21_12_00_04 : ALL 1 TESTS SUCCESSFUL +2019_07_21_12_00_11 : ALL 2 TESTS SUCCESSFUL +2019_07_21_12_01_37 : ALL 1 TESTS SUCCESSFUL +2019_07_21_12_01_44 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_33_24 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_33_39 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_34_05 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_34_11 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_36_35 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_36_43 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_37_43 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_37_48 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_40_45 : FAILED 1/1 TESTS: perl_syntax +2019_07_22_15_43_00 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_43_06 : ALL 2 TESTS SUCCESSFUL +2019_07_22_15_44_17 : ALL 1 TESTS SUCCESSFUL +2019_07_22_15_44_25 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_03_28 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_03_33 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_05_45 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_05_50 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_10_19 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_10_24 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_16_30 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_16_35 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_17_50 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_17_55 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_28_22 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_28_27 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_28_46 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_29_36 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_29_40 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_32_38 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_32_47 : ALL 2 TESTS SUCCESSFUL +2019_07_22_21_32_53 : ALL 1 TESTS SUCCESSFUL +2019_07_22_21_33_58 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_38_24 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_39_08 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_39_45 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_39_52 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_39_58 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_41_08 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_44_44 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_44_51 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_47_16 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_47_25 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_48_31 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_49_19 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_49_19 : FAILED 1/2 TESTS: ll_search2_NOT_OR_OR_UID +2019_07_24_20_49_53 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_51_17 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_51_34 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_51_49 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_52_03 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_52_22 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_52_36 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_53_19 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_53_34 : ALL 2 TESTS SUCCESSFUL +2019_07_24_20_53_50 : ALL 1 TESTS SUCCESSFUL +2019_07_24_20_54_05 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_09_40 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_09_54 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_12_21 : FAILED 1/1 TESTS: perl_syntax +2019_07_24_21_12_41 : FAILED 1/1 TESTS: perl_syntax +2019_07_24_21_12_54 : FAILED 1/1 TESTS: perl_syntax +2019_07_24_21_13_19 : FAILED 1/1 TESTS: perl_syntax +2019_07_24_21_13_34 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_13_48 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_13_53 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_14_08 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_14_46 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_15_01 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_24_54 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_25_15 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_32_32 : FAILED 1/1 TESTS: perl_syntax +2019_07_24_21_33_02 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_33_15 : ALL 2 TESTS SUCCESSFUL +2019_07_24_21_42_22 : ALL 1 TESTS SUCCESSFUL +2019_07_24_21_42_34 : ALL 2 TESTS SUCCESSFUL +2019_07_25_00_23_03 : ALL 1 TESTS SUCCESSFUL +2019_07_25_00_23_16 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_27_42 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_29_23 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_38_17 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_38_19 : FAILED 1/3 TESTS: titi +2019_07_25_14_38_23 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_38_23 : FAILED 1/3 TESTS: titi +2019_07_25_14_40_21 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_40_21 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_40_26 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_40_26 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_40_29 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_40_30 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_40_37 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_41_21 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_45_43 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_46_24 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_50_51 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_51_33 : ALL 2 TESTS SUCCESSFUL +2019_07_25_14_58_33 : ALL 1 TESTS SUCCESSFUL +2019_07_25_14_58_49 : ALL 2 TESTS SUCCESSFUL +2019_07_25_15_01_29 : ALL 1 TESTS SUCCESSFUL +2019_07_25_15_01_34 : ALL 2 TESTS SUCCESSFUL +2019_07_25_15_03_07 : ALL 1 TESTS SUCCESSFUL +2019_07_25_15_03_49 : ALL 2 TESTS SUCCESSFUL +2019_07_25_15_04_32 : ALL 1 TESTS SUCCESSFUL +2019_07_25_15_04_33 : ALL 2 TESTS SUCCESSFUL +2019_07_25_15_04_36 : ALL 1 TESTS SUCCESSFUL +2019_07_25_15_04_36 : ALL 2 TESTS SUCCESSFUL +2019_07_25_15_17_41 : ALL 1 TESTS SUCCESSFUL +2019_07_25_15_18_23 : ALL 2 TESTS SUCCESSFUL +2019_07_25_16_59_13 : ALL 1 TESTS SUCCESSFUL +2019_07_25_17_00_02 : ALL 2 TESTS SUCCESSFUL +2019_07_25_17_51_42 : ALL 1 TESTS SUCCESSFUL +2019_07_25_17_51_57 : ALL 2 TESTS SUCCESSFUL +2019_07_25_18_03_44 : ALL 1 TESTS SUCCESSFUL +2019_07_25_18_04_55 : ALL 1 TESTS SUCCESSFUL +2019_07_25_18_05_20 : ALL 2 TESTS SUCCESSFUL +2019_07_25_18_29_33 : ALL 1 TESTS SUCCESSFUL +2019_07_25_19_10_24 : FAILED 3/157 TESTS: option_tests option_tests_in_var_tmp option_tests_in_var_tmp_sub +2019_07_26_00_53_21 : ALL 1 TESTS SUCCESSFUL +2019_07_26_00_54_27 : ALL 1 TESTS SUCCESSFUL +2019_07_26_00_54_56 : ALL 2 TESTS SUCCESSFUL +2019_07_26_00_56_07 : ALL 1 TESTS SUCCESSFUL +2019_07_26_00_56_14 : ALL 2 TESTS SUCCESSFUL +2019_07_26_00_57_35 : ALL 1 TESTS SUCCESSFUL +2019_07_26_00_57_42 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_02_18 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_02_25 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_03_16 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_03_21 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_05_40 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_05_44 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_08_05 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_08_10 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_16_36 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_17_00 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_19_59 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_20_18 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_22_14 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_22_27 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_27_55 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_28_09 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_31_29 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_31_42 : ALL 2 TESTS SUCCESSFUL +2019_07_26_01_34_01 : ALL 1 TESTS SUCCESSFUL +2019_07_26_01_34_14 : ALL 2 TESTS SUCCESSFUL +2019_07_26_09_29_41 : ALL 1 TESTS SUCCESSFUL +2019_07_26_09_30_48 : ALL 2 TESTS SUCCESSFUL +2019_07_26_09_55_25 : ALL 1 TESTS SUCCESSFUL +2019_07_26_09_55_30 : ALL 2 TESTS SUCCESSFUL +2019_07_26_10_34_48 : FAILED 1/1 TESTS: perl_syntax +2019_07_26_10_36_46 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_36_51 : ALL 2 TESTS SUCCESSFUL +2019_07_26_10_38_35 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_40_19 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_40_26 : ALL 2 TESTS SUCCESSFUL +2019_07_26_10_41_38 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_43_20 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_44_17 : ALL 1 TESTS SUCCESSFUL +2019_07_26_10_45_20 : ALL 1 TESTS SUCCESSFUL +2019_07_26_11_24_51 : ALL 1 TESTS SUCCESSFUL +2019_07_26_11_27_32 : ALL 1 TESTS SUCCESSFUL +2019_07_26_11_27_43 : ALL 2 TESTS SUCCESSFUL +2019_07_26_11_30_21 : ALL 1 TESTS SUCCESSFUL +2019_07_26_11_30_33 : ALL 2 TESTS SUCCESSFUL +2019_07_26_11_32_28 : ALL 1 TESTS SUCCESSFUL +2019_07_26_12_13_31 : FAILED 5/157 TESTS: first_sync_dry first_sync yahoo_all ll_justfolders_delete1emptyfolders ll_sep2 +2019_07_26_12_54_45 : ALL 1 TESTS SUCCESSFUL +2019_07_26_13_01_33 : ALL 2 TESTS SUCCESSFUL +2019_07_26_21_21_20 : ALL 1 TESTS SUCCESSFUL +2019_07_26_21_21_32 : ALL 2 TESTS SUCCESSFUL +2019_07_27_05_27_08 : FAILED 1/1 TESTS: perl_syntax +2019_07_27_05_52_36 : FAILED 1/1 TESTS: perl_syntax +2019_07_27_06_01_38 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_01_49 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_03_37 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_03_49 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_05_23 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_05_35 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_06_31 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_06_41 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_09_44 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_10_02 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_10_57 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_11_14 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_11_49 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_12_12 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_12_40 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_12_54 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_29_57 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_30_07 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_31_06 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_31_16 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_33_18 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_33_25 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_34_21 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_41_25 : ALL 2 TESTS SUCCESSFUL +2019_07_27_06_43_55 : ALL 1 TESTS SUCCESSFUL +2019_07_27_06_44_03 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_02_44 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_03_23 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_08_07 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_08_14 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_09_30 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_09_30 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist! +2019_07_27_10_09_45 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_09_52 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_10_55 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_11_03 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_13_59 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_14_06 : ALL 2 TESTS SUCCESSFUL +2019_07_27_10_15_05 : ALL 1 TESTS SUCCESSFUL +2019_07_27_10_15_16 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_08_43 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_08_52 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_19_02 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_19_09 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_21_26 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_21_33 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_34_40 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_34_47 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_35_32 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_36_10 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_36_23 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_36_37 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_36_47 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_36_57 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_37_06 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_37_17 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_37_26 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_37_42 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_38_20 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_39_03 : ALL 2 TESTS SUCCESSFUL +2019_07_27_11_41_04 : ALL 1 TESTS SUCCESSFUL +2019_07_27_11_41_15 : ALL 2 TESTS SUCCESSFUL +2019_07_28_02_51_54 : ALL 1 TESTS SUCCESSFUL +2019_07_28_02_52_19 : ALL 2 TESTS SUCCESSFUL +2019_07_28_02_53_54 : ALL 1 TESTS SUCCESSFUL +2019_07_28_02_54_15 : ALL 2 TESTS SUCCESSFUL +2019_07_28_02_57_40 : ALL 1 TESTS SUCCESSFUL +2019_07_28_02_58_20 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_00_34 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_01_18 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_07_42 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_08_24 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_10_55 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_11_37 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_14_08 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_14_17 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_18_39 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_18_46 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_18_54 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_19_26 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_24_14 : FAILED 1/1 TESTS: perl_syntax +2019_07_28_03_25_17 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_25_52 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_27_22 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_27_52 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_32_34 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_32_35 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_32_38 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_32_39 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_32_55 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_33_07 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_35_24 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_35_24 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_35_36 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_36_07 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_37_57 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_37_57 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_38_01 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_38_01 : ALL 2 TESTS SUCCESSFUL +2019_07_28_03_38_08 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_38_09 : ALL 4 TESTS SUCCESSFUL +2019_07_28_03_38_16 : ALL 1 TESTS SUCCESSFUL +2019_07_28_03_38_28 : ALL 2 TESTS SUCCESSFUL +2019_07_28_04_17_19 : ALL 1 TESTS SUCCESSFUL +2019_07_28_04_17_20 : ALL 4 TESTS SUCCESSFUL +2019_07_28_04_17_23 : ALL 1 TESTS SUCCESSFUL +2019_07_28_04_17_23 : ALL 4 TESTS SUCCESSFUL +2019_07_28_04_17_27 : ALL 1 TESTS SUCCESSFUL +2019_07_28_04_17_42 : ALL 2 TESTS SUCCESSFUL +2019_07_29_08_42_09 : ALL 1 TESTS SUCCESSFUL +2019_07_29_09_26_18 : FAILED 7/157 TESTS: first_sync_dry first_sync yahoo_all ll_justfolders_delete1emptyfolders ll_sep2 ll_domino1_domino2 ll_domino2 +2019_07_29_10_11_07 : ALL 1 TESTS SUCCESSFUL +2019_07_29_10_14_23 : ALL 2 TESTS SUCCESSFUL +2019_07_30_00_39_42 : ALL 1 TESTS SUCCESSFUL +2019_07_30_00_39_51 : ALL 2 TESTS SUCCESSFUL +2019_07_30_11_17_17 : ALL 1 TESTS SUCCESSFUL +2019_07_31_15_02_48 : ALL 1 TESTS SUCCESSFUL +2019_07_31_15_02_55 : ALL 2 TESTS SUCCESSFUL +2019_07_31_17_57_02 : ALL 1 TESTS SUCCESSFUL +2019_07_31_17_57_09 : ALL 2 TESTS SUCCESSFUL +2019_07_31_19_05_39 : ALL 1 TESTS SUCCESSFUL +2019_08_01_03_19_29 : ALL 1 TESTS SUCCESSFUL +2019_08_01_04_03_21 : FAILED 7/157 TESTS: first_sync_dry first_sync yahoo_all ll_justfolders_delete1emptyfolders ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_01_04_43_05 : ALL 1 TESTS SUCCESSFUL +2019_08_01_04_47_48 : ALL 2 TESTS SUCCESSFUL +2019_08_01_18_46_50 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_47_21 : FAILED 1/2 TESTS: ll_regexmess_add_header +2019_08_01_18_47_40 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_48_02 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_49_31 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_54_01 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_54_07 : FAILED 1/2 TESTS: ll_regexmess_add_header +2019_08_01_18_54_37 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_54_43 : FAILED 1/2 TESTS: ll_regexmess_add_header +2019_08_01_18_55_35 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_55_41 : FAILED 1/2 TESTS: ll_regexmess_add_header +2019_08_01_18_59_33 : ALL 1 TESTS SUCCESSFUL +2019_08_01_18_59_38 : FAILED 1/2 TESTS: ll_regexmess_add_header +2019_08_01_19_00_46 : ALL 1 TESTS SUCCESSFUL +2019_08_01_19_00_52 : ALL 2 TESTS SUCCESSFUL +2019_08_01_19_04_07 : ALL 1 TESTS SUCCESSFUL +2019_08_01_19_04_12 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_19_07_02 : ALL 1 TESTS SUCCESSFUL +2019_08_01_19_07_06 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_20_35_48 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_35_54 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_20_40_38 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_40_44 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_20_44_20 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_44_29 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_45_23 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_45_27 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_46_51 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_46_55 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_47_02 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_47_08 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_20_49_16 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_49_19 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_51_11 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_51_15 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_52_35 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_52_40 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_53_17 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_53_21 : ALL 2 TESTS SUCCESSFUL +2019_08_01_20_53_44 : ALL 1 TESTS SUCCESSFUL +2019_08_01_20_53_48 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_16_30 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_16_35 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_23_45 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_23_49 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_31_26 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_31_30 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_31_41 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_31_46 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_21_34_26 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_34_31 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_37_45 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_37_51 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_39_00 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_39_07 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_01_21_39_35 : ALL 1 TESTS SUCCESSFUL +2019_08_01_21_39_41 : ALL 2 TESTS SUCCESSFUL +2019_08_01_21_41_42 : ALL 1 TESTS SUCCESSFUL +2019_08_01_22_24_04 : FAILED 7/159 TESTS: first_sync_dry first_sync yahoo_all ll_justfolders_delete1emptyfolders ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_01_22_36_10 : ALL 1 TESTS SUCCESSFUL +2019_08_01_22_39_03 : FAILED 7/8 TESTS: first_sync_dry first_sync yahoo_all ll_justfolders_delete1emptyfolders ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_01_22_39_18 : ALL 1 TESTS SUCCESSFUL +2019_08_01_22_39_24 : FAILED 1/2 TESTS: ll_domino2 +2019_08_02_13_58_44 : ALL 1 TESTS SUCCESSFUL +2019_08_02_13_59_03 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_00_38 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_00_44 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_01_40 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_01_45 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_03_56 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_04_02 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_04_33 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_04_40 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_06_14 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_06_27 : ALL 2 TESTS SUCCESSFUL +2019_08_02_14_17_30 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_17_36 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_02_14_25_00 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_25_16 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_02_14_25_57 : ALL 1 TESTS SUCCESSFUL +2019_08_02_14_26_05 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_02_15_51_14 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_51_28 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_52_54 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_53_04 : FAILED 1/2 TESTS: ll_regexmess_add_header_path +2019_08_02_15_54_40 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_54_52 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_55_09 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_55_29 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_55_49 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_55_56 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_57_55 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_58_03 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_59_16 : ALL 1 TESTS SUCCESSFUL +2019_08_02_15_59_22 : ALL 2 TESTS SUCCESSFUL +2019_08_02_15_59_56 : ALL 1 TESTS SUCCESSFUL +2019_08_02_16_00_12 : ALL 2 TESTS SUCCESSFUL +2019_08_02_16_02_10 : ALL 1 TESTS SUCCESSFUL +2019_08_02_16_02_21 : ALL 2 TESTS SUCCESSFUL +2019_08_02_20_59_29 : ALL 1 TESTS SUCCESSFUL +2019_08_02_20_59_55 : ALL 2 TESTS SUCCESSFUL +2019_08_02_21_01_38 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_01_42 : ALL 2 TESTS SUCCESSFUL +2019_08_02_21_03_00 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_03_05 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist +2019_08_02_21_12_13 : FAILED 1/1 TESTS: perl_syntax +2019_08_02_21_12_52 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_12_56 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist +2019_08_02_21_14_24 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_14_28 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist +2019_08_02_21_15_46 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_15_51 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist +2019_08_02_21_17_48 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_17_53 : FAILED 1/2 TESTS: ll_justfoldersizes_noexist +2019_08_02_21_18_39 : ALL 1 TESTS SUCCESSFUL +2019_08_02_21_18_44 : ALL 2 TESTS SUCCESSFUL +2019_08_02_21_19_58 : ALL 1 TESTS SUCCESSFUL +2019_08_02_22_18_53 : FAILED 4/160 TESTS: yahoo_all ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_02_22_22_43 : ALL 1 TESTS SUCCESSFUL +2019_08_02_22_22_52 : ALL 2 TESTS SUCCESSFUL +2019_08_02_22_34_41 : ALL 1 TESTS SUCCESSFUL +2019_08_02_23_18_10 : FAILED 4/160 TESTS: first_sync_dry ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_03_01_48_18 : ALL 1 TESTS SUCCESSFUL +2019_08_03_02_31_26 : FAILED 4/160 TESTS: first_sync_dry ll_sep2 ll_domino1_domino2 ll_domino2 +2019_08_03_03_13_59 : ALL 1 TESTS SUCCESSFUL +2019_08_03_03_19_02 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_36_15 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_36_24 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_37_08 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_37_15 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_38_09 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_38_17 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_39_26 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_39_30 : FAILED 1/2 TESTS: firstclass +2019_08_10_12_40_08 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_40_33 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_43_38 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_44_01 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_47_48 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_48_12 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_50_18 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_50_43 : ALL 2 TESTS SUCCESSFUL +2019_08_10_12_59_50 : ALL 1 TESTS SUCCESSFUL +2019_08_10_12_59_52 : FAILED 1/2 TESTS: firstclass_fullfill +2019_08_10_13_02_25 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_02_42 : ALL 2 TESTS SUCCESSFUL +2019_08_10_13_03_15 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_03_43 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_04_01 : ALL 2 TESTS SUCCESSFUL +2019_08_10_13_04_07 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_04_33 : ALL 2 TESTS SUCCESSFUL +2019_08_10_13_05_29 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_05_46 : ALL 2 TESTS SUCCESSFUL +2019_08_10_13_06_36 : ALL 1 TESTS SUCCESSFUL +2019_08_10_13_07_02 : ALL 2 TESTS SUCCESSFUL +2019_08_29_11_40_36 : ALL 1 TESTS SUCCESSFUL +2019_08_29_11_40_51 : ALL 2 TESTS SUCCESSFUL +2019_08_29_11_42_54 : ALL 1 TESTS SUCCESSFUL +2019_08_29_11_42_59 : ALL 2 TESTS SUCCESSFUL +2019_08_29_11_47_58 : ALL 1 TESTS SUCCESSFUL +2019_08_29_11_48_03 : ALL 2 TESTS SUCCESSFUL +2019_08_29_11_49_05 : ALL 1 TESTS SUCCESSFUL +2019_08_29_11_49_10 : FAILED 1/2 TESTS: ll_justfolders_skipemptyfolders +2019_08_29_11_55_17 : FAILED 1/1 TESTS: perl_syntax +2019_08_29_11_58_33 : ALL 1 TESTS SUCCESSFUL +2019_08_29_11_58_38 : FAILED 1/2 TESTS: ll_justfolders_skipemptyfolders +2019_08_29_19_10_36 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_10_45 : FAILED 1/2 TESTS: ll_justfolders_skipemptyfolders +2019_08_29_19_12_06 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_12_45 : FAILED 1/2 TESTS: ll_delete1_delete1emptyfolders +2019_08_29_19_15_03 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_15_26 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_21_00 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_21_17 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_21_58 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_22_03 : FAILED 1/2 TESTS: ll_justfolders_skipemptyfolders +2019_08_29_19_23_46 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_23_51 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_30_05 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_30_12 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_30_24 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_30_44 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_34_30 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_34_47 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_35_33 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_39_14 : ALL 1 TESTS SUCCESSFUL +2019_08_29_19_39_19 : ALL 2 TESTS SUCCESSFUL +2019_08_29_19_41_34 : ALL 1 TESTS SUCCESSFUL +2019_08_29_20_27_41 : FAILED 2/160 TESTS: yahoo_all ll_justfolders_delete1emptyfolders +2019_08_29_22_20_12 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_22_12 : FAILED 1/3 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_23_10 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_23_27 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_26_52 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_27_09 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_40_17 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_40_38 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_40_55 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_41_06 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_41_10 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_41_21 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_43_49 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_44_06 : FAILED 1/2 TESTS: ll_justfolders_delete1emptyfolders +2019_08_29_22_48_28 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_48_51 : ALL 2 TESTS SUCCESSFUL +2019_08_29_22_48_58 : ALL 1 TESTS SUCCESSFUL +2019_08_29_22_49_16 : ALL 2 TESTS SUCCESSFUL +2019_09_11_11_12_28 : ALL 1 TESTS SUCCESSFUL +2019_09_11_11_12_29 : FAILED 1/2 TESTS: abort_tests +2019_09_11_13_56_05 : ALL 1 TESTS SUCCESSFUL +2019_09_11_13_56_23 : ALL 2 TESTS SUCCESSFUL +2019_09_11_14_05_29 : ALL 1 TESTS SUCCESSFUL +2019_09_11_14_05_47 : ALL 2 TESTS SUCCESSFUL +2019_09_11_14_15_14 : ALL 1 TESTS SUCCESSFUL +2019_09_11_14_15_24 : ALL 2 TESTS SUCCESSFUL +2019_09_11_14_19_16 : ALL 1 TESTS SUCCESSFUL +2019_09_11_14_19_22 : ALL 2 TESTS SUCCESSFUL +2019_09_11_17_06_50 : ALL 1 TESTS SUCCESSFUL +2019_09_11_17_06_56 : ALL 2 TESTS SUCCESSFUL +2019_09_11_23_13_46 : ALL 1 TESTS SUCCESSFUL +2019_09_11_23_13_53 : ALL 2 TESTS SUCCESSFUL +2019_09_12_02_50_23 : ALL 1 TESTS SUCCESSFUL +2019_09_12_02_50_32 : ALL 2 TESTS SUCCESSFUL +2019_09_12_02_58_26 : ALL 1 TESTS SUCCESSFUL +2019_09_12_02_58_33 : ALL 2 TESTS SUCCESSFUL +2019_09_12_02_59_21 : ALL 1 TESTS SUCCESSFUL +2019_09_12_02_59_27 : ALL 2 TESTS SUCCESSFUL +2019_09_12_03_15_52 : ALL 1 TESTS SUCCESSFUL +2019_09_12_03_15_59 : ALL 2 TESTS SUCCESSFUL +2019_09_16_21_56_34 : ALL 1 TESTS SUCCESSFUL +2019_09_16_21_56_59 : ALL 2 TESTS SUCCESSFUL +2019_09_17_00_22_50 : ALL 1 TESTS SUCCESSFUL +2019_09_17_00_22_55 : FAILED 1/2 TESTS: abort_tests +2019_09_17_00_26_25 : ALL 1 TESTS SUCCESSFUL +2019_09_17_00_26_28 : FAILED 1/2 TESTS: ll_abort_noprocess +2019_09_18_01_06_12 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_06_17 : FAILED 1/2 TESTS: abort_tests +2019_09_18_01_07_13 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_07_26 : FAILED 1/2 TESTS: abort_tests +2019_09_18_01_24_21 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_24_33 : FAILED 1/2 TESTS: abort_tests +2019_09_18_01_35_25 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_35_40 : FAILED 1/2 TESTS: abort_tests +2019_09_18_01_44_20 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_44_32 : FAILED 1/2 TESTS: abort_tests +2019_09_18_01_45_58 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_46_07 : FAILED 1/2 TESTS: ll_abort +2019_09_18_01_47_41 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_47_47 : ALL 2 TESTS SUCCESSFUL +2019_09_18_01_51_02 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_51_08 : ALL 2 TESTS SUCCESSFUL +2019_09_18_01_51_20 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_51_28 : FAILED 1/2 TESTS: ll_abort +2019_09_18_01_56_26 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_56_34 : FAILED 1/2 TESTS: ll_abort_no_pidfile_option +2019_09_18_01_57_51 : ALL 1 TESTS SUCCESSFUL +2019_09_18_01_57_57 : ALL 2 TESTS SUCCESSFUL +2019_09_18_02_05_31 : ALL 1 TESTS SUCCESSFUL +2019_09_18_02_05_38 : ALL 2 TESTS SUCCESSFUL +2019_09_18_02_05_49 : ALL 1 TESTS SUCCESSFUL +2019_09_18_02_05_55 : ALL 2 TESTS SUCCESSFUL +2019_09_18_02_07_53 : ALL 1 TESTS SUCCESSFUL +2019_09_18_02_07_59 : ALL 2 TESTS SUCCESSFUL +2019_09_18_02_08_49 : ALL 1 TESTS SUCCESSFUL +2019_09_18_02_08_55 : ALL 2 TESTS SUCCESSFUL +2019_09_18_08_45_27 : ALL 1 TESTS SUCCESSFUL +2019_09_18_08_45_39 : ALL 2 TESTS SUCCESSFUL +2019_09_18_09_21_21 : ALL 1 TESTS SUCCESSFUL +2019_09_18_09_21_28 : FAILED 1/2 TESTS: ll_abort +2019_09_18_09_24_42 : ALL 1 TESTS SUCCESSFUL +2019_09_18_09_24_49 : FAILED 1/2 TESTS: ll_abort +2019_09_18_09_27_49 : ALL 1 TESTS SUCCESSFUL +2019_09_18_09_27_55 : FAILED 1/2 TESTS: ll_abort +2019_09_18_09_29_12 : ALL 1 TESTS SUCCESSFUL +2019_09_18_09_29_19 : ALL 2 TESTS SUCCESSFUL +2019_09_18_09_30_23 : ALL 1 TESTS SUCCESSFUL +2019_09_18_09_30_50 : ALL 2 TESTS SUCCESSFUL +2019_09_18_23_48_12 : ALL 1 TESTS SUCCESSFUL +2019_09_18_23_48_22 : ALL 2 TESTS SUCCESSFUL +2019_09_19_09_40_54 : ALL 1 TESTS SUCCESSFUL +2019_09_19_10_27_06 : FAILED 1/161 TESTS: option_tests +2019_09_19_10_31_06 : ALL 1 TESTS SUCCESSFUL +2019_09_19_10_31_37 : FAILED 1/2 TESTS: option_tests +2019_09_19_10_33_14 : ALL 1 TESTS SUCCESSFUL +2019_09_19_10_33_50 : FAILED 1/2 TESTS: option_tests +2019_09_19_15_37_26 : ALL 1 TESTS SUCCESSFUL +2019_09_19_15_37_53 : ALL 2 TESTS SUCCESSFUL +2019_10_18_15_29_40 : ALL 1 TESTS SUCCESSFUL +2019_10_18_15_30_11 : ALL 2 TESTS SUCCESSFUL +2019_10_18_15_30_48 : ALL 1 TESTS SUCCESSFUL +2019_10_18_15_31_05 : ALL 2 TESTS SUCCESSFUL +2019_10_22_13_27_05 : ALL 1 TESTS SUCCESSFUL +2019_10_22_13_27_22 : FAILED 1/2 TESTS: ll_regextrans2_archive_per_year_flat_hard_year +2019_10_22_13_29_36 : ALL 1 TESTS SUCCESSFUL +2019_11_13_23_47_26 : ALL 1 TESTS SUCCESSFUL +2019_11_13_23_48_08 : ALL 2 TESTS SUCCESSFUL +2019_11_13_23_49_06 : ALL 1 TESTS SUCCESSFUL +2019_11_13_23_49_22 : ALL 2 TESTS SUCCESSFUL +2019_11_13_23_50_00 : ALL 1 TESTS SUCCESSFUL +2019_11_13_23_50_14 : ALL 2 TESTS SUCCESSFUL +2019_11_13_23_51_57 : ALL 1 TESTS SUCCESSFUL +2019_11_13_23_52_05 : ALL 2 TESTS SUCCESSFUL +2019_11_14_12_33_13 : ALL 1 TESTS SUCCESSFUL +2019_11_14_12_33_54 : ALL 2 TESTS SUCCESSFUL +2019_11_14_12_34_47 : ALL 1 TESTS SUCCESSFUL +2019_11_14_12_34_55 : ALL 2 TESTS SUCCESSFUL +2019_11_14_12_38_16 : ALL 1 TESTS SUCCESSFUL +2019_11_14_12_43_50 : ALL 2 TESTS SUCCESSFUL +2019_11_15_12_39_18 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_39_19 : FAILED 5/6 TESTS: ./W/learn/create_folder localhost titi HUwtEd INBOX.NEW_2 +2019_11_15_12_39_41 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_40_35 : ALL 2 TESTS SUCCESSFUL +2019_11_15_12_40_50 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_41_07 : ALL 2 TESTS SUCCESSFUL +2019_11_15_12_41_38 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_41_49 : ALL 2 TESTS SUCCESSFUL +2019_11_15_12_44_45 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_44_54 : ALL 2 TESTS SUCCESSFUL +2019_11_15_12_57_36 : FAILED 1/1 TESTS: perl_syntax +2019_11_15_12_58_08 : FAILED 1/1 TESTS: perl_syntax +2019_11_15_12_58_45 : ALL 1 TESTS SUCCESSFUL +2019_11_15_12_58_56 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_00_44 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_00_59 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_04_21 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_04_32 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_05_40 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_05_55 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_11_51 : FAILED 1/1 TESTS: perl_syntax +2019_11_15_13_13_02 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_13_11 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_15_02 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_15_18 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_18_24 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_18_33 : ALL 2 TESTS SUCCESSFUL +2019_11_15_13_19_05 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_19_18 : FAILED 1/2 TESTS: ll_delete2foldersonly_tmp +2019_11_15_13_19_26 : ALL 1 TESTS SUCCESSFUL +2019_11_15_13_19_37 : FAILED 1/2 TESTS: ll_delete2foldersonly_tmp +2019_11_19_20_42_08 : ALL 1 TESTS SUCCESSFUL +2019_11_19_20_42_44 : FAILED 1/2 TESTS: ll_delete2foldersonly_tmp +2019_11_19_20_44_01 : ALL 1 TESTS SUCCESSFUL +2019_11_19_20_44_07 : ALL 2 TESTS SUCCESSFUL +2019_11_19_20_44_13 : ALL 1 TESTS SUCCESSFUL +2019_11_19_21_32_57 : ALL 161 TESTS SUCCESSFUL +2019_11_24_18_28_57 : ALL 1 TESTS SUCCESSFUL +2019_11_24_18_29_21 : ALL 2 TESTS SUCCESSFUL +2019_11_25_22_52_47 : ALL 1 TESTS SUCCESSFUL +2019_11_25_22_53_03 : ALL 1 TESTS SUCCESSFUL +2019_11_25_22_53_15 : ALL 2 TESTS SUCCESSFUL +2019_11_25_23_15_06 : ALL 1 TESTS SUCCESSFUL +2019_11_25_23_15_16 : ALL 2 TESTS SUCCESSFUL +2019_11_25_23_27_46 : ALL 1 TESTS SUCCESSFUL +2019_11_25_23_27_54 : ALL 2 TESTS SUCCESSFUL +2019_11_28_12_38_36 : ALL 1 TESTS SUCCESSFUL +2019_11_28_12_38_56 : ALL 2 TESTS SUCCESSFUL +2019_11_28_15_56_07 : ALL 1 TESTS SUCCESSFUL +2019_11_28_16_47_06 : FAILED 3/161 TESTS: ks_justconnect_ipv6 ks_justconnect_ipv6_nossl testslive6 +2019_11_28_16_55_34 : ALL 1 TESTS SUCCESSFUL +2019_11_28_17_05_35 : FAILED 2/4 TESTS: ks_justconnect_ipv6 ks_justconnect_ipv6_nossl +2019_11_29_10_00_15 : ALL 1 TESTS SUCCESSFUL +2019_11_29_10_00_15 : FAILED 1/2 TESTS: tests_mailimapclient_connect +2019_11_29_10_01_12 : ALL 1 TESTS SUCCESSFUL +2019_11_29_10_01_42 : ALL 4 TESTS SUCCESSFUL +2019_11_29_12_40_34 : ALL 1 TESTS SUCCESSFUL +2019_11_29_14_25_16 : ALL 1 TESTS SUCCESSFUL +2019_11_29_15_09_06 : FAILED 3/161 TESTS: yahoo_all ks_justconnect_ipv6_nossl testslive6 +2019_11_29_15_19_59 : ALL 1 TESTS SUCCESSFUL +2019_11_29_15_24_29 : ALL 4 TESTS SUCCESSFUL +2019_11_29_15_25_29 : ALL 1 TESTS SUCCESSFUL +2019_11_29_15_28_32 : ALL 2 TESTS SUCCESSFUL +2019_12_01_21_34_37 : ALL 1 TESTS SUCCESSFUL +2019_12_01_21_38_48 : FAILED 1/4 TESTS: ks_justconnect_ipv6_nossl +2019_12_01_22_25_15 : ALL 1 TESTS SUCCESSFUL +2019_12_01_22_28_34 : ALL 4 TESTS SUCCESSFUL +2019_12_02_11_28_25 : ALL 1 TESTS SUCCESSFUL +2019_12_02_11_34_33 : FAILED 2/4 TESTS: ks_justconnect_ipv6_nossl testslive6 +2019_12_02_19_23_38 : ALL 1 TESTS SUCCESSFUL +2019_12_02_20_08_53 : FAILED 3/159 TESTS: option_tests_in_var_tmp option_tests_in_var_tmp_sub ks_justconnect_ipv6 +2019_12_03_00_14_54 : ALL 1 TESTS SUCCESSFUL +2019_12_03_00_16_15 : FAILED 2/4 TESTS: option_tests_in_var_tmp_sub ks_justconnect_ipv6 +2019_12_03_00_17_36 : ALL 1 TESTS SUCCESSFUL +2019_12_03_00_18_06 : ALL 2 TESTS SUCCESSFUL +2019_12_03_00_18_15 : ALL 1 TESTS SUCCESSFUL +2019_12_03_00_18_29 : ALL 1 TESTS SUCCESSFUL +2019_12_03_00_23_53 : FAILED 3/4 TESTS: option_tests_in_var_tmp option_tests_in_var_tmp_sub ks_justconnect_ipv6 +2019_12_06_10_45_32 : ALL 1 TESTS SUCCESSFUL +2019_12_06_11_28_39 : FAILED 2/159 TESTS: option_tests_in_var_tmp ks_justconnect_ipv6 +2019_12_06_11_32_47 : ALL 1 TESTS SUCCESSFUL +2019_12_06_12_19_04 : FAILED 3/158 TESTS: option_tests option_tests_in_var_tmp option_tests_in_var_tmp_sub +2019_12_06_15_21_08 : ALL 1 TESTS SUCCESSFUL +2019_12_06_15_22_30 : ALL 4 TESTS SUCCESSFUL +2019_12_06_15_28_25 : ALL 1 TESTS SUCCESSFUL +2019_12_06_15_31_58 : FAILED 1/4 TESTS: option_tests_in_var_tmp +2019_12_11_20_06_54 : ALL 1 TESTS SUCCESSFUL +2019_12_11_21_28_55 : ALL 1 TESTS SUCCESSFUL +2019_12_11_22_10_46 : FAILED 1/158 TESTS: option_tests +2019_12_12_15_15_00 : ALL 1 TESTS SUCCESSFUL +2019_12_12_16_04_53 : FAILED 1/158 TESTS: option_tests_in_var_tmp_sub +2019_12_12_16_35_25 : ALL 1 TESTS SUCCESSFUL +2019_12_12_16_45_10 : ALL 1 TESTS SUCCESSFUL +2019_12_12_17_40_39 : FAILED 1/158 TESTS: option_tests +2019_12_12_19_55_25 : ALL 1 TESTS SUCCESSFUL +2019_12_12_19_56_04 : ALL 2 TESTS SUCCESSFUL +2019_12_12_19_56_27 : ALL 1 TESTS SUCCESSFUL +2019_12_12_20_00_08 : FAILED 1/8 TESTS: option_tests +2019_12_12_20_08_10 : ALL 1 TESTS SUCCESSFUL +2019_12_12_20_11_59 : ALL 8 TESTS SUCCESSFUL +2019_12_12_20_28_44 : ALL 1 TESTS SUCCESSFUL +2019_12_12_21_24_43 : FAILED 1/158 TESTS: yahoo_all +2019_12_12_21_29_30 : ALL 1 TESTS SUCCESSFUL +2019_12_12_22_21_51 : FAILED 2/158 TESTS: option_tests ll_abort_cgi_context_tail +2019_12_12_22_22_22 : ALL 1 TESTS SUCCESSFUL +2019_12_12_23_12_40 : ALL 158 TESTS SUCCESSFUL +2019_12_12_23_12_55 : ALL 1 TESTS SUCCESSFUL +2019_12_13_00_04_24 : ALL 158 TESTS SUCCESSFUL +2019_12_13_00_04_39 : ALL 1 TESTS SUCCESSFUL +2019_12_13_00_58_38 : ALL 158 TESTS SUCCESSFUL +2019_12_15_02_00_45 : ALL 1 TESTS SUCCESSFUL +2019_12_15_02_01_28 : ALL 1 TESTS SUCCESSFUL +2019_12_15_02_17_41 : ALL 1 TESTS SUCCESSFUL +2019_12_15_02_18_28 : ALL 1 TESTS SUCCESSFUL +2019_12_15_02_18_43 : ALL 2 TESTS SUCCESSFUL +2019_12_15_02_18_56 : ALL 1 TESTS SUCCESSFUL +2019_12_15_02_57_38 : ALL 158 TESTS SUCCESSFUL +2019_12_15_23_49_50 : ALL 1 TESTS SUCCESSFUL +2019_12_15_23_49_59 : FAILED 1/2 TESTS: ll_justlogin_dollar_char +2019_12_15_23_56_02 : ALL 1 TESTS SUCCESSFUL +2019_12_15_23_56_07 : ALL 2 TESTS SUCCESSFUL +2019_12_15_23_56_41 : ALL 1 TESTS SUCCESSFUL +2019_12_15_23_56_50 : FAILED 1/2 TESTS: ll_justlogin_dollar_char +2019_12_15_23_57_15 : ALL 1 TESTS SUCCESSFUL +2019_12_15_23_57_19 : ALL 2 TESTS SUCCESSFUL +2019_12_17_22_20_49 : ALL 1 TESTS SUCCESSFUL +2019_12_17_22_21_01 : FAILED 1/2 TESTS: ll_authmech_xoauth2_json_gmail_app +2019_12_20_17_21_15 : ALL 1 TESTS SUCCESSFUL +2019_12_20_17_59_52 : ALL 158 TESTS SUCCESSFUL +2019_12_20_18_50_27 : ALL 1 TESTS SUCCESSFUL +2019_12_20_18_55_47 : ALL 2 TESTS SUCCESSFUL +2019_12_21_12_36_50 : ALL 1 TESTS SUCCESSFUL +2019_12_21_12_42_18 : ALL 2 TESTS SUCCESSFUL +2020_01_01_22_19_08 : ALL 1 TESTS SUCCESSFUL +2020_01_01_22_59_11 : ALL 158 TESTS SUCCESSFUL +2020_01_01_23_08_57 : ALL 1 TESTS SUCCESSFUL +2020_01_01_23_18_28 : ALL 2 TESTS SUCCESSFUL +2020_01_02_12_54_08 : ALL 1 TESTS SUCCESSFUL +2020_01_02_12_58_26 : ALL 2 TESTS SUCCESSFUL +2020_01_02_13_35_45 : ALL 1 TESTS SUCCESSFUL +2020_01_02_13_38_50 : ALL 2 TESTS SUCCESSFUL +2020_01_03_00_48_36 : ALL 1 TESTS SUCCESSFUL +2020_01_03_00_53_07 : ALL 2 TESTS SUCCESSFUL +2020_01_03_14_40_29 : ALL 1 TESTS SUCCESSFUL +2020_01_03_14_45_00 : ALL 2 TESTS SUCCESSFUL +2020_01_03_23_42_20 : ALL 1 TESTS SUCCESSFUL +2020_01_04_00_20_39 : ALL 158 TESTS SUCCESSFUL diff --git a/W/LOG_bat/test_exe_tests.bat.txt b/W/LOG_bat/test_exe_tests.bat.txt new file mode 100644 index 0000000..a72f628 --- /dev/null +++ b/W/LOG_bat/test_exe_tests.bat.txt @@ -0,0 +1 @@ +Failure running .\imapsync.exe --tests diff --git a/W/build_exe.bat b/W/build_exe.bat old mode 100644 new mode 100755 index 880836a..162f478 --- a/W/build_exe.bat +++ b/W/build_exe.bat @@ -1,5 +1,5 @@ -REM $Id: build_exe.bat,v 1.54 2019/05/28 13:20:08 gilles Exp gilles $ +REM $Id: build_exe.bat,v 1.56 2019/12/11 18:57:20 gilles Exp gilles $ @SETLOCAL @ECHO Currently running through %0 %* @@ -19,9 +19,9 @@ CALL :handle_error CALL :rename_to_old CALL :handle_error CALL :pp_exe CALL :handle_error CALL :copy_with_architecture_name -@ENDLOCAL @REM Do a PAUSE if run by double-click, aka, explorer (then ). No PAUSE in a DOS window or via ssh. IF %0 EQU "%~dpnx0" IF "%SSH_CLIENT%"=="" PAUSE +@ENDLOCAL EXIT /B @@ -42,8 +42,7 @@ EXIT /B @REM 32 bits @REM Do not add command after this one since it will anihilate the %ERRORLEVEL% of pp ECHO Building 32 bits binary PROCESSOR_ARCHITECTURE = %PROCESSOR_ARCHITECTURE% - CALL pp -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event ^ - -M Test2::Event::Info ^ + CALL pp -u -x -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event ^ --link zlib1_.dll ^ --link libcrypto-1_1_.dll ^ --link libssl-1_1_.dll ^ @@ -52,8 +51,8 @@ EXIT /B @REM 64 bits @REM Do not add command after this one since it will anihilate the %ERRORLEVEL% of pp ECHO Building 64 bits binary PROCESSOR_ARCHITECTURE = %PROCESSOR_ARCHITECTURE% - CALL pp -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event ^ - -M Test2::Event::Info -M Test2::EventFacet -M Test2::Event::Pass ^ + CALL pp -u -x -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event ^ + -M Test2::EventFacet -M Test2::Event::Pass ^ -M Test2::Event::Fail -M Test2::Event::V2 ^ --link libcrypto-1_1-x64__.dll ^ --link zlib1__.dll ^ @@ -63,6 +62,8 @@ EXIT /B @ENDLOCAL EXIT /B +@REM -M Test2::Event::Info + ::------------------------------------------------------ ::--------------- Copy with architecture name ---------- @@ -84,7 +85,7 @@ EXIT /B :rename_to_old @SETLOCAL IF EXIST imapsync_old.exe DEL imapsync_old.exe -RENAME imapsync.exe imapsync_old.exe +IF EXIST imapsync.exe RENAME imapsync.exe imapsync_old.exe @ENDLOCAL EXIT /B ::------------------------------------------------------ @@ -106,6 +107,7 @@ EXIT /B :check_modules @SETLOCAL perl ^ + -mApp::cpanminus ^ -mTest::MockObject ^ -mPAR::Packer ^ -mReadonly ^ @@ -135,8 +137,47 @@ perl ^ -mCrypt::OpenSSL::RSA ^ -mEncode::Byte ^ -mFile::Tail ^ + -mEncode ^ + -mEncode::IMAPUTF7 ^ + -mMIME::Base64 ^ -e '' IF ERRORLEVEL 1 CALL .\install_modules.bat +@ECHO Calling a second time to check all modules are now installed +perl ^ + -mApp::cpanminus ^ + -mTest::MockObject ^ + -mPAR::Packer ^ + -mReadonly ^ + -mAuthen::NTLM ^ + -mData::Dumper ^ + -mData::Uniqid ^ + -mDigest::HMAC_MD5 ^ + -mDigest::HMAC_SHA1 ^ + -mDigest::MD5 ^ + -mFile::Copy::Recursive ^ + -mFile::Spec ^ + -mIO::Socket ^ + -mIO::Socket::INET ^ + -mIO::Socket::IP ^ + -mIO::Socket::SSL ^ + -mIO::Tee ^ + -mMail::IMAPClient ^ + -mRegexp::Common ^ + -mTerm::ReadKey ^ + -mTime::Local ^ + -mUnicode::String ^ + -mURI::Escape ^ + -mJSON::WebToken ^ + -mLWP::UserAgent ^ + -mHTML::Entities ^ + -mJSON ^ + -mCrypt::OpenSSL::RSA ^ + -mEncode::Byte ^ + -mFile::Tail ^ + -mEncode ^ + -mEncode::IMAPUTF7 ^ + -mMIME::Base64 ^ + -e '' @ENDLOCAL EXIT /B ::------------------------------------------------------ @@ -144,10 +185,11 @@ EXIT /B + ::------------------------------------------------------ ::--------------- Handle error ------------------------- :handle_error -SETLOCAL +@SETLOCAL ECHO IN %0 with parameters %* %* SET CMD_RETURN=%ERRORLEVEL% @@ -159,6 +201,6 @@ IF %CMD_RETURN% EQU 0 ( IF NOT EXIST LOG_bat MKDIR LOG_bat ECHO Failure calling: %* >> LOG_bat\%~nx0.txt ) -ENDLOCAL +@ENDLOCAL EXIT /B ::------------------------------------------------------ diff --git a/W/build_mac.sh b/W/build_mac.sh index dca4fc5..74027c6 100755 --- a/W/build_mac.sh +++ b/W/build_mac.sh @@ -1,6 +1,6 @@ #!/bin/sh -# $Id: build_mac.sh,v 1.12 2019/04/13 22:16:04 gilles Exp gilles $ +# $Id: build_mac.sh,v 1.15 2019/11/25 12:46:48 gilles Exp gilles $ # exit on any failure set -e @@ -23,12 +23,13 @@ VERSION=`./imapsync --version` # Update important Perl modules OPENSSL_PREFIX=/sw cpanm Mail::IMAPClient IO::Socket::SSL Net::SSLeay PAR::Packer -pp -o $BIN_NAME \ +pp -u -x -o $BIN_NAME \ -M Mail::IMAPClient -M IO::Socket -M IO::Socket::SSL \ -M Digest::MD5 -M Digest::HMAC_MD5 -M Term::ReadKey \ -M Authen::NTLM \ -M Crypt::OpenSSL::RSA -M JSON -M JSON::WebToken -M LWP -M HTML::Entities \ -M Sys::MemInfo -M Net::SSLeay \ + -M Encode -M MIME::Base64 -M Encode::IMAPUTF7 \ --link /sw/lib/libssl.1.0.0.dylib \ --link /sw/lib/libcrypto.1.0.0.dylib \ imapsync @@ -36,5 +37,6 @@ pp -o $BIN_NAME \ ./imapsync_bin_Darwin ./imapsync_bin_Darwin --tests ./imapsync_bin_Darwin --testslive -./imapsync_bin_Darwin --testslive6 +# Do not work anymore on polarhome at 2019_11_24 +#./imapsync_bin_Darwin --testslive6 diff --git a/W/checklinkext.txt b/W/checklinkext.txt index 7a9387f..b0a104c 100644 --- a/W/checklinkext.txt +++ b/W/checklinkext.txt @@ -5,6 +5,14 @@ Processing https://lamiral.info/~gilles/imapsync/S/external.shtml List of broken links and other issues: +http://www.washington.edu/imap/ +-> https://www.washington.edu/imap/ + Line: 43 + Code: 301 -> 404 Not Found + To do: The link is broken. Double-check that you have not made any typo, + or mistake in copy-pasting. If the link points to a resource that + no longer exists, you may want to remove or fix the link. + https://wiki2.dovecot.org/Tools/Doveadm/Sync Line: 38 Code: 403 FORBIDDEN @@ -17,15 +25,28 @@ Processing https://lamiral.info/~gilles/imapsync/S/imapservers.shtml List of broken links and other issues: -https://en.mail.qq.com/ - Line: 157 - Code: 405 Method Not Allowed - To do: The server does not allow HTTP HEAD requests, which prevents the - Link Checker to check the link automatically. Check the link - manually. +http://www.washington.edu/imap/ +-> https://www.washington.edu/imap/ + Line: 183 + Code: 301 -> 404 Not Found + To do: The link is broken. Double-check that you have not made any typo, + or mistake in copy-pasting. If the link points to a resource that + no longer exists, you may want to remove or fix the link. + +https://www.fusemail.com/ +-> https://global.vipre.com/ + Line: 123 + Code: 301 -> 403 Forbidden + To do: The link is forbidden! This needs fixing. Usual suspects: a missing + index.html or Overview.html, or a missing ACL. + +http://de.tobit.com/ + Line: 96 + Code: 308 Permanent Redirect + http://outlook.com/ -> https://outlook.live.com/owa/ - Line: 150 + Line: 157 Code: 301 -> 440 Login Timeout diff --git a/W/docker_pull_count b/W/docker_pull_count new file mode 100755 index 0000000..b3680c4 --- /dev/null +++ b/W/docker_pull_count @@ -0,0 +1,10 @@ +#!/bin/sh + +docker_pull_count() +{ + curl -s https://hub.docker.com/v2/repositories/gilleslamiral/imapsync/ | jq '.pull_count' +} + +docker_pull_count >> /home/gilles/public_html/imapsync/W/docker_pull_count.txt + + diff --git a/W/docker_pull_count.txt b/W/docker_pull_count.txt new file mode 100644 index 0000000..c684127 --- /dev/null +++ b/W/docker_pull_count.txt @@ -0,0 +1,33 @@ +32753 +32780 +32804 +32828 +32858 +32880 +32938 +33246 +33348 +33570 +33796 +33818 +33836 +33855 +33880 +33901 +33926 +33956 +33977 +33997 +34016 +34046 +34273 +34301 +34324 +34344 +34367 +34448 +34500 +34531 +34561 +34590 +34769 diff --git a/W/imapsync.1 b/W/imapsync.1 index c1d98a6..383fa01 100644 --- a/W/imapsync.1 +++ b/W/imapsync.1 @@ -133,18 +133,18 @@ .\" ======================================================================== .\" .IX Title "IMAPSYNC 1" -.TH IMAPSYNC 1 "2019-06-26" "perl v5.22.1" "User Contributed Perl Documentation" +.TH IMAPSYNC 1 "2019-12-23" "perl v5.22.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" -imapsync \- Email IMAP tool for syncing, copying and migrating -email mailboxes between two imap servers, one way, +imapsync \- Email IMAP tool for syncing, copying, migrating +and archiving email mailboxes between two imap servers, one way, and without duplicates. .SH "VERSION" .IX Header "VERSION" -This documentation refers to Imapsync \f(CW$Revision:\fR 1.945 $ +This documentation refers to Imapsync \f(CW$Revision:\fR 1.977 $ .SH "USAGE" .IX Header "USAGE" .Vb 5 @@ -175,8 +175,8 @@ are synced too. .PP Imapsync reduces the amount of data transferred by not transferring a given message if it already resides on the destination side. -Messages that are on the destination side but not on the -source side stay as they are (see the \-\-delete2 +Messages that are on the destination side but not on the +source side stay as they are (see the \-\-delete2 option to have a strict sync). .PP How imapsync knows a message is already on both sides? @@ -191,19 +191,19 @@ read ones will stay read, deleted will stay deleted. You can abort the transfer at any time and restart it later, imapsync works well with bad connections and interruptions, by design. On a terminal hit Ctr-c twice within two seconds -in order to abort the program. Hit Ctr-c just once makes +in order to abort the program. Hit Ctr-c just once makes imapsync reconnect to both imap servers. .PP A classical scenario is synchronizing a mailbox B from another mailbox A -in case you just want to keep a strict copy of A in B. Strict meaning +where you just want to keep a strict copy of A in B. Strict meaning all messages in A will be in B but no more. .PP For this, option \-\-delete2 has to be used, it deletes messages in host2 folder B that are not in host1 folder A. If you also need to destroy host2 folders that are not in host1 then use \-\-delete2folders. See also \&\-\-delete2foldersonly and \-\-delete2foldersbutnot to set up exceptions -on folders to destroy (\s-1INBOX\s0 will never be destroy, it's a mandatory -folder in \s-1IMAP\s0). +on folders to destroy. \s-1INBOX\s0 will never be destroy, it's a mandatory +folder in \s-1IMAP.\s0 .PP A different scenario is to delete the messages from the source mailbox after a successful transfer, it can be a good feature when migrating @@ -232,11 +232,11 @@ Michael R. Elkins) for a 2 ways synchronization. \& usage: imapsync [options] .Ve .PP -Standard options are the six values forming the credentials, -three on each sides, needed to log in into the \s-1IMAP\s0 servers, ie, -a host, a username, and a password, two times. +The standard options are the six values forming the credentials. +Three values on each side are needed in order to log in into the \s-1IMAP \s0 +servers. These six values are a host, a username, and a password, two times. .PP -Conventions used: +Conventions used in the following descriptions of the options: .PP .Vb 4 \& str means string @@ -252,8 +252,8 @@ Conventions used: .Vb 6 \& \-\-host1 str : Source or "from" imap server. \& \-\-port1 int : Port to connect on host1. -\& Optional since default ports are the -\& well known ports 143 or 993. +\& Optional since default ports are the +\& well known ports imap/143 or imaps/993. \& \-\-user1 str : User to login on host1. \& \-\-password1 str : Password for the user1. \& @@ -263,10 +263,10 @@ Conventions used: \& \-\-password2 str : Password for the user2. \& \& \-\-showpasswords : Shows passwords on output instead of "MASKED". -\& Useful to restart a complete run by just reading +\& Useful to restart a complete run by just reading \& the command line used in the log, \& or to debug passwords. -\& It\*(Aqs not a secure practice. +\& It\*(Aqs not a secure practice at all. \& \& \-\-passfile1 str : Password file for the user1. It must contain the \& password on the first line. This option avoids showing @@ -333,14 +333,14 @@ You can also pass the passwords in the environment variables \& \-\-folderrec str : Sync this folder recursively. \& \-\-folderrec str : and this one, etc. \& -\& \-\-folderfirst str : Sync this folder first. \-\-folderfirst "Work" +\& \-\-folderfirst str : Sync this folder first. Ex. \-\-folderfirst "INBOX" \& \-\-folderfirst str : then this one, etc. \& \-\-folderlast str : Sync this folder last. \-\-folderlast "[Gmail]/All Mail" \& \-\-folderlast str : then this one, etc. \& \& \-\-nomixfolders : Do not merge folders when host1 is case\-sensitive \& while host2 is not (like Exchange). Only the first -\& similar folder is synced (example: with folders +\& similar folder is synced (example: with folders \& "Sent", "SENT" and "sent" \& on host1 only "Sent" will be synced to host2). \& @@ -367,14 +367,14 @@ You can also pass the passwords in the environment variables \& \-\-regextrans2 options before all others. \& Add \-\-debug to see what\*(Aqs really going on. \& -\& \-\-subfolder1 str : Syncs the host1 folders hierarchy under str -\& to the root hierarchy of host2. +\& \-\-subfolder1 str : Syncs the host1 folders hierarchy which is under folder +\& str to the root hierarchy of host2. \& It\*(Aqs the couterpart of a sync done by \-\-subfolder2 -\& when doing it in the reverse order. +\& when doing it in the reverse order. \& Backup/Restore scenario: \& Use \-\-subfolder2 str for a backup to the folder str -\& on host2. Then use \-\-subfolder1 str for restoring -\& from the folder str, after inverting +\& on host2. Then use \-\-subfolder1 str for restoring +\& from the folder str, after inverting \& host1/host2 user1/user2 values. \& \& @@ -388,25 +388,26 @@ You can also pass the passwords in the environment variables \& usually "INBOX." or "INBOX/" or an empty string "". \& imapsync guesses the prefix if host1 imap server \& does not have NAMESPACE capability. So this option -\& should not be used, most of the time. +\& should not be used most of the time. \& \-\-prefix2 str : Add prefix to all host2 folders. See \-\-prefix1 \& -\& \-\-sep1 str : Host1 separator. This option should not be used, +\& \-\-sep1 str : Host1 separator. This option should not be used \& most of the time. \& Imapsync gets the separator from the server itself, \& by using NAMESPACE, or it tries to guess it \& from the folders listing (it counts \& characters / . \e\e \e in folder names and choose the \& more frequent, or finally / if nothing is found. -\& \-\-sep2 str : Host2 separator. +\& \-\-sep2 str : Host2 separator. See \-\-sep1 \& \& \-\-regextrans2 reg : Apply the whole regex to each destination folders. \& \-\-regextrans2 reg : and this one. etc. \& When you play with the \-\-regextrans2 option, first \& add also the safe options \-\-dry \-\-justfolders -\& Then, when happy, remove \-\-dry, remove \-\-justfolders. -\& Have in mind that \-\-regextrans2 is applied after -\& the automatic prefix and separator inversion. +\& Then, when happy, remove \-\-dry for a run, then +\& remove \-\-justfolders for the next ones. +\& Have in mind that \-\-regextrans2 is applied after +\& the automatic prefix and separator inversion. \& For examples see: \& https://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt .Ve @@ -440,6 +441,22 @@ You can also pass the passwords in the environment variables \& \-\-logfile str : Change the default log filename (can be dirname/filename). \& \-\-logdir str : Change the default log directory. Default is LOG_imapsync/ .Ve +.PP +The default logfile name is for example +.PP +.Vb 1 +\& LOG_imapsync/2019_12_22_23_57_59_532_user1_user2.txt +.Ve +.PP +where: +.PP +.Vb 3 +\& 2019_12_22_23_57_59_532 is nearly the date of the start +\& YYYY_MM_DD_HH_MM_SS_mmm +\& year_month_day_hour_minute_seconde_millisecond +.Ve +.PP +and user1 user2 are the \-\-user1 \-\-user2 values. .SS "OPTIONS/messages" .IX Subsection "OPTIONS/messages" .Vb 4 @@ -448,13 +465,22 @@ You can also pass the passwords in the environment variables \& \-\-skipmess is applied before \-\-regexmess \& \-\-skipmess reg : or this one, etc. \& +\& \-\-skipcrossduplicates : Avoid copying messages that are already copied +\& in another folder, good from Gmail to X when +\& X is not also Gmail. +\& Activated with \-\-gmail1 unless \-\-noskipcrossduplicates +\& +\& \-\-debugcrossduplicates : Prints which messages (UIDs) are skipped with +\& \-\-skipcrossduplicates (and in what other folders +\& they are). +\& \& \-\-pipemess cmd : Apply this cmd command to each message content \& before the copy. -\& \-\-pipemess cmd : and this one, etc. +\& \-\-pipemess cmd : and this one, etc. \& With several \-\-pipemess, the output of each cmd -\& command (STDOUT) is given to the input (STDIN) +\& command (STDOUT) is given to the input (STDIN) \& of the next command. -\& For example, +\& For example, \& \-\-pipemess cmd1 \-\-pipemess cmd2 \-\-pipemess cmd3 \& is like a Unix pipe: \& "cat message | cmd1 | cmd2 | cmd3" @@ -465,6 +491,24 @@ You can also pass the passwords in the environment variables \& Example: \*(Aqs/\e000/ /g\*(Aq # to replace null by space. \& \-\-regexmess reg : and this one, etc. .Ve +.SS "OPTIONS/labels" +.IX Subsection "OPTIONS/labels" +Gmail present labels as folders in imap. Imapsync can accelerate the sync +by syncing X\-GM-LABELS, it will avoid to transfer messages when they are +already on host2. +.PP +.Vb 3 +\& \-\-synclabels : Syncs also Gmail labels when a message is copied to host2. +\& Activated by default with \-\-gmail1 \-\-gmail2 unless +\& \-\-nosynclabels is added. +\& +\& \-\-resynclabels : Resyncs Gmail labels when a message is already on host2. +\& Activated by default with \-\-gmail1 \-\-gmail2 unless +\& \-\-noresynclabels is added. +.Ve +.PP +For Gmail syncs, see also: +https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt .SS "OPTIONS/flags" .IX Subsection "OPTIONS/flags" .Vb 2 @@ -491,7 +535,7 @@ You can also pass the passwords in the environment variables \& EXPUNGE IMAP command. If expunging after each message \& slows down too much the sync then use \& \-\-noexpungeaftereach to speed up, expunging will then be -\& done only twice per folder, one at the beginning and +\& done only twice per folder, one at the beginning and \& one at the end of a folder sync. \& \& \-\-expunge1 : Expunge messages on host1 just before syncing a folder. @@ -517,12 +561,15 @@ You can also pass the passwords in the environment variables \& \-\-delete2folders : Delete folders in host2 that are not in host1 server. \& For safety, first try it like this (it is safe): \& \-\-delete2folders \-\-dry \-\-justfolders \-\-nofoldersizes +\& and see what folders will be deleted. \& -\& \-\-delete2foldersonly reg : Deleted only folders matching regex. +\& \-\-delete2foldersonly reg : Delete only folders matching the regex reg. \& Example: \-\-delete2foldersonly "/^Junk$|^INBOX.Junk$/" +\& This option activates \-\-delete2folders \& -\& \-\-delete2foldersbutnot reg : Do not delete folders matching regex. +\& \-\-delete2foldersbutnot reg : Do not delete folders matching the regex rex. \& Example: \-\-delete2foldersbutnot "/Tasks$|Contacts$|Foo$/" +\& This option activates \-\-delete2folders \& \& \-\-noexpunge2 : Do not expunge messages on host2. \& \-\-nouidexpunge2 : Do not uidexpunge messages on the host2 account @@ -550,7 +597,7 @@ You can also pass the passwords in the environment variables \& see also \-\-minage \& \-\-minage int : Skip messages newer than int days. \& final stats (skipped) don\*(Aqt count newer messages -\& You can do (+ are the messages selected): +\& You can do (+ zone are the messages selected): \& past|\-\-\-\-maxage+++++++++++++++>now \& past|+++++++++++++++minage\-\-\-\->now \& past|\-\-\-\-maxage+++++minage\-\-\-\->now (intersection) @@ -558,22 +605,23 @@ You can also pass the passwords in the environment variables \& \& \-\-search str : Selects only messages returned by this IMAP SEARCH \& command. Applied on both sides. -\& For a complete of what can be search see +\& For a complete set of what can be search see \& https://imapsync.lamiral.info/FAQ.d/FAQ.Messages_Selection.txt \& \& \-\-search1 str : Same as \-\-search but for selecting host1 messages only. \& \-\-search2 str : Same as \-\-search but for selecting host2 messages only. -\& \-\-search CRIT equals \-\-search1 CRIT \-\-search2 CRIT +\& So \-\-search CRIT equals \-\-search1 CRIT \-\-search2 CRIT \& \& \-\-maxlinelength int : skip messages with a line length longer than int bytes. -\& RFC 2822 says it must be no more than 1000 bytes. +\& RFC 2822 says it must be no more than 1000 bytes but +\& real life servers and email clients do more. \& \& \& \-\-useheader str : Use this header to compare messages on both sides. \& Ex: Message\-ID or Subject or Date. \& \-\-useheader str and this one, etc. \& -\& \-\-usecache : Use cache to speed up the sync. +\& \-\-usecache : Use cache to speed up next syncs. Not set by default. \& \-\-nousecache : Do not use cache. Caveat: \-\-useuid \-\-nousecache creates \& duplicates on multiple runs. \& \-\-useuid : Use UIDs instead of headers as a criterion to recognize @@ -584,10 +632,10 @@ You can also pass the passwords in the environment variables .IX Subsection "OPTIONS/miscellaneous" .Vb 5 \& \-\-syncacls : Synchronizes acls (Access Control Lists). -\& \-\-nosyncacls : Does not synchronize acls. This is the default. \& Acls in IMAP are not standardized, be careful \& since one acl code on one side may signify something \& else on the other one. +\& \-\-nosyncacls : Does not synchronize acls. This is the default. \& \& \-\-addheader : When a message has no headers to be identified, \& \-\-addheader adds a "Message\-Id" header, @@ -617,17 +665,17 @@ You can also pass the passwords in the environment variables .SS "OPTIONS/specific" .IX Subsection "OPTIONS/specific" .Vb 2 -\& \-\-gmail1 : sets \-\-host1 to Gmail and options from FAQ.Gmail.txt -\& \-\-gmail2 : sets \-\-host2 to Gmail and options from FAQ.Gmail.txt +\& \-\-gmail1 : sets \-\-host1 to Gmail and other options. See FAQ.Gmail.txt +\& \-\-gmail2 : sets \-\-host2 to Gmail and other options. See FAQ.Gmail.txt \& -\& \-\-office1 : sets \-\-host1 to Office365 options from FAQ.Exchange.txt -\& \-\-office2 : sets \-\-host2 to Office365 options from FAQ.Exchange.txt +\& \-\-office1 : sets \-\-host1 to Office365 and other options. See FAQ.Exchange.txt +\& \-\-office2 : sets \-\-host2 to Office365 and other options. See FAQ.Exchange.txt \& -\& \-\-exchange1 : sets options from FAQ.Exchange.txt, account1 part -\& \-\-exchange2 : sets options from FAQ.Exchange.txt, account2 part +\& \-\-exchange1 : sets options for Exchange. See FAQ.Exchange.txt +\& \-\-exchange2 : sets options for Exchange. See FAQ.Exchange.txt \& -\& \-\-domino1 : sets options from FAQ.Domino.txt, account1 part -\& \-\-domino2 : sets options from FAQ.Domino.txt, account2 part +\& \-\-domino1 : sets options for Domino. See FAQ.Domino.txt +\& \-\-domino2 : sets options for Domino. See FAQ.Domino.txt .Ve .SS "OPTIONS/behavior" .IX Subsection "OPTIONS/behavior" @@ -644,11 +692,11 @@ You can also pass the passwords in the environment variables \& \-\-abort : terminates a previous call still running. \& It uses the pidfile to know what process to abort. \& -\& \-\-exitwhenover int : Stop syncing and exits when int total bytes +\& \-\-exitwhenover int : Stop syncing and exits when int total bytes \& transferred is reached. \& \& \-\-version : Print only software version. -\& \-\-noreleasecheck : Do not check for new imapsync release +\& \-\-noreleasecheck : Do not check for any new imapsync release. \& \-\-releasecheck : Check for new imapsync release. \& it\*(Aqs an http request to \& http://imapsync.lamiral.info/prj/imapsync/VERSION @@ -659,7 +707,7 @@ You can also pass the passwords in the environment variables \& information. Need only \-\-host1 and \-\-host2 options. \& Obsolete since "imapsync \-\-host1 imaphost" alone \& implies \-\-justconnect -\& +\& \& \-\-justlogin : Just login to both host1 and host2 with users \& credentials, then exit. \& @@ -696,9 +744,9 @@ normal (clear) connection on port 143 but it looks for \s-1TLS\s0 support in the \s-1CAPABILITY\s0 list of the servers. If \s-1TLS\s0 is supported then imapsync goes to encryption. .PP -If the automatic ssl/tls detection fails then imapsync will -not protect against sniffing activities on the -network, especially for passwords. +If the automatic ssl and the tls detections fail then imapsync will +not protect against sniffing activities on the network, especially +for passwords. .PP If you want to force ssl or tls just use \-\-ssl1 \-\-ssl2 or \-\-tls1 \-\-tls2 .PP @@ -707,9 +755,9 @@ or at https://imapsync.lamiral.info/FAQ.d/FAQ.Security.txt .SH "EXIT STATUS" .IX Header "EXIT STATUS" Imapsync will exit with a 0 status (return code) if everything went good. -Otherwise, it exits with a non-zero status. -Here is the list of the exit code values (an integer between 0 and 255), -the names reflects their meaning: +Otherwise, it exits with a non-zero status. That's classical Unix behavior. +Here is the list of the exit code values (an integer between 0 and 255). +The names reflect their meaning: .PP .Vb 10 \& EX_OK => 0 ; #/* successful termination */ @@ -731,7 +779,7 @@ the names reflects their meaning: .SH "LICENSE AND COPYRIGHT" .IX Header "LICENSE AND COPYRIGHT" Imapsync is free, open, public but not always gratis software -cover by the \s-1NOLIMIT\s0 Public License. +cover by the \s-1NOLIMIT\s0 Public License, now called \s-1NLPL.\s0 See the \s-1LICENSE\s0 file included in the distribution or just read this simple sentence as it \s-1IS\s0 the licence text: .PP @@ -750,7 +798,7 @@ Look at https://imapsync.lamiral.info/LICENSE .IX Header "AUTHOR" Gilles \s-1LAMIRAL\s0.PP -Good feedback good is always welcome. +Good feedback is always welcome. Bad feedback is very often welcome. .PP Gilles \s-1LAMIRAL\s0 earns his living by writing, installing, @@ -812,8 +860,8 @@ https://imapsync.lamiral.info/examples/ \& and all Server releases 2000, 2003, 2008 and R2, 2012 and R2, 2016) \& as a standalone binary software called imapsync.exe, \& usually launched from a batch file in order to avoid always typing -\& the options. -\& +\& the options. There is also a 64bit binary called imapsync_64bit.exe +\& \& Imapsync works under OS X as a standalone binary \& software called imapsync_bin_Darwin \& @@ -848,50 +896,48 @@ Feel free to hack imapsync as the \s-1NOLIMIT\s0 license permits it. \& for a better up to date list. .Ve .PP -Last updated and verified on Thu Apr 11, 2019. +Last updated and verified on Sun Dec 8, 2019. .PP .Vb 10 -\& imapsync : https://github.com/imapsync/imapsync -\& (this is an imapsync copy, sometimes delayed, -\& with \-\-noreleasecheck by default since release 1.592, 2014/05/22) -\& imap_tools : https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/ -\& The imap_tools code is now at -\& https://github.com/andrewnimmo/rick\-sanders\-imap\-tools -\& imaputils : https://github.com/mtsatsenko/imaputils (very old imap_tools fork) -\& Doveadm\-Sync : https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool ) -\& davmail : http://davmail.sourceforge.net/ -\& offlineimap : http://offlineimap.org/ -\& mbsync : http://isync.sourceforge.net/ -\& mailsync : http://mailsync.sourceforge.net/ -\& mailutil : http://www.washington.edu/imap/ part of the UW IMAP tookit. -\& imaprepl : https://bl0rg.net/software/ http://freecode.com/projects/imap\-repl/ -\& imapcopy (Pascal): http://www.ardiehl.de/imapcopy/ -\& imapcopy (Java) : https://code.google.com/archive/p/imapcopy/ -\& imapsize : http://www.broobles.com/imapsize/ -\& migrationtool : http://sourceforge.net/projects/migrationtool/ -\& imapmigrate : http://sourceforge.net/projects/cyrus\-utils/ -\& larch : https://github.com/rgrove/larch (derived from wonko_imapsync, good at Gmail) -\& wonko_imapsync : http://wonko.com/article/554 (superseded by larch) -\& pop2imap : http://www.linux\-france.org/prj/pop2imap/ (I wrote that too) -\& exchange\-away : http://exchange\-away.sourceforge.net/ -\& SyncBackPro : http://www.2brightsparks.com/syncback/sbpro.html -\& ImapSyncClient : https://github.com/ridaamirini/ImapSyncClient -\& MailStore : https://www.mailstore.com/en/products/mailstore\-home/ -\& mnIMAPSync : https://github.com/manusa/mnIMAPSync -\& imap\-upload : http://imap\-upload.sourceforge.net/ -\& (a tool for uploading a local mbox file to IMAP4 server) +\& imapsync: https://github.com/imapsync/imapsync (this is an imapsync copy, sometimes delayed, with \-\-noreleasecheck by default since release 1.592, 2014/05/22) +\& imap_tools: https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/. The imap_tools code is now at https://github.com/andrewnimmo/rick\-sanders\-imap\-tools +\& imaputils: https://github.com/mtsatsenko/imaputils (very old imap_tools fork) +\& Doveadm\-Sync: https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool ) +\& davmail: http://davmail.sourceforge.net/ +\& offlineimap: http://offlineimap.org/ +\& mbsync: http://isync.sourceforge.net/ +\& mailsync: http://mailsync.sourceforge.net/ +\& mailutil: https://www.washington.edu/imap/ part of the UW IMAP toolkit. (well, seems abandoned now) +\& imaprepl: https://bl0rg.net/software/ http://freecode.com/projects/imap\-repl/ +\& imapcopy (Pascal): http://www.ardiehl.de/imapcopy/ +\& imapcopy (Java): https://code.google.com/archive/p/imapcopy/ +\& imapsize: http://www.broobles.com/imapsize/ +\& migrationtool: http://sourceforge.net/projects/migrationtool/ +\& imapmigrate: http://sourceforge.net/projects/cyrus\-utils/ +\& larch: https://github.com/rgrove/larch (derived from wonko_imapsync, good at Gmail) +\& wonko_imapsync: http://wonko.com/article/554 (superseded by larch) +\& pop2imap: http://www.linux\-france.org/prj/pop2imap/ (I wrote that too) +\& exchange\-away: http://exchange\-away.sourceforge.net/ +\& SyncBackPro: http://www.2brightsparks.com/syncback/sbpro.html +\& ImapSyncClient: https://github.com/ridaamirini/ImapSyncClient +\& MailStore: https://www.mailstore.com/en/products/mailstore\-home/ +\& mnIMAPSync: https://github.com/manusa/mnIMAPSync +\& imap\-upload: http://imap\-upload.sourceforge.net/ (A tool for uploading a local mbox file to IMAP4 server) +\& imapbackup: https://github.com/rcarmo/imapbackup (A Python script for incremental backups of IMAP mailboxes) +\& BitRecover email\-backup 99 USD, 299 USD https://www.bitrecover.com/email\-backup/. +\& ImportExportTools: https://addons.thunderbird.net/en\-us/thunderbird/addon/importexporttools/ ImportExportTools for Mozilla Thunderbird by Paolo Kaosmos. ImportExportTools does not do IMAP. .Ve .SH "HISTORY" .IX Header "HISTORY" -I initially wrote imapsync in July 2001 because an enterprise, -basystemes, paid me to install a new imap server +I initially wrote imapsync in July 2001 because an enterprise, +called BaSystemes, paid me to install a new imap server without losing huge old mailboxes located in a far -away remote imap server, accessible by an +away remote imap server, accessible by an often broken low-bandwidth \s-1ISDN\s0 link. .PP I had to verify every mailbox was well transferred, all folders, all messages, -without wasting bandwidth or creating duplicates upon resyncs. The design was -made with the beautiful rsync command in mind. +without wasting bandwidth or creating duplicates upon resyncs. The imapsync +design was made with the beautiful rsync command in mind. .PP Imapsync started its life as a patch of the copy_folder.pl script. The script copy_folder.pl comes from the Mail\-IMAPClient\-2.1.3 perl @@ -899,5 +945,5 @@ module tarball source (more precisely in the examples/ directory of the Mail-IMAPClient tarball). .PP So many happened since then that I wonder -if it remains any lines of the original +if it remains any lines of the original copy_folder.pl in imapsync source code. diff --git a/W/imapsync_functions_tree.txt b/W/imapsync_functions_tree.txt new file mode 100644 index 0000000..42c5793 --- /dev/null +++ b/W/imapsync_functions_tree.txt @@ -0,0 +1,6871 @@ +$VAR1 = { + 'teelaunch' => [ + 'defined', + 'logfileprepa', + 'croak', + 'encoding', + 'new', + 'autoflush' + ], + 'setlogfile' => [ + 'my', + 'filter_forbidden_characters', + 'slash_to_underscore', + 'filter_forbidden_characters', + 'slash_to_underscore', + 'logfile', + 'return' + ], + 'check_capability' => [ + 'my', + 'has_capability', + 'has_capability', + 'myprintf', + 'myprintf', + 'myprint' + ], + 'tests_imapsping' => [ + 'note', + 'tests_imapsping', + 'is', + 'imapsping', + 'is', + 'imapsping', + 'is', + 'imapsping', + 'is', + 'imapsping', + 'note', + 'tests_imapsping' + ], + 'testunitsession' => [ + 'testsunit', + 'done_testing' + ], + 'tests_cache_dir_fix' => [ + 'note', + 'tests_cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'ok', + 'cache_dir_fix', + 'note', + 'tests_cache_dir_fix' + ], + 'search_dyn_lib_locale_linux' => [ + 'myprint', + 'backtick' + ], + 'foldersizesatend' => [ + 'timenext', + 'IsUnconnected', + 'IsUnconnected', + 'folders', + 'foldersizes_diff_list', + 'foldersizes_total', + 'all_defined', + 'errors_incr' + ], + 'backtick' => [ + 'open3', + 'myprint', + 'waitpid', + 'myprint', + 'return', + 'return', + 'join' + ], + 'tests_appendlimit' => [ + 'note', + 'tests_appendlimit', + 'is', + 'appendlimit', + 'is', + 'appendlimit', + 'mock_capability', + 'is', + 'appendlimit', + 'note', + 'tests_appendlimit' + ], + 'set_tls' => [ + 'Starttls' + ], + 'tests_delete1emptyfolders_unit' => [ + 'note', + 'tests_delete1emptyfolders_unit', + 'is_deeply', + 'delete1emptyfolders', + 'is_deeply', + 'note', + 'tests_delete1emptyfolders_unit' + ], + 'appendlimit_from_capability' => [ + 'myprint', + 'myprint', + 'Dump', + 'capability_of', + 'myprint', + 'is_an_integer' + ], + 'flags_regex' => [ + 'myprint', + 'myprint', + 'if', + 'myprint', + 'return', + 'return' + ], + 'tests_guess_special' => [ + 'note', + 'tests_guess_special', + 'build_possible_special', + 'ok', + 'guess_special', + 'ok', + 'guess_special', + 'ok', + 'guess_special', + 'ok', + 'guess_special', + 'note', + 'tests_guess_special' + ], + 'tests_logfile' => [ + 'note', + 'tests_logfile', + 'skip', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'is', + 'logfile', + 'note', + 'tests_logfile' + ], + 'mock_capability' => [ + 'require_ok', + 'new', + 'mock' + ], + 'hashsync' => [ + 'join', + 'hmac_sha1_hex', + 'myprint', + 'return' + ], + 'tests_memory_stress' => [ + 'note', + 'tests_memory_stress', + 'is', + 'memory_stress', + 'note', + 'tests_memory_stress' + ], + 'good_date' => [ + 'return', + 's', + 'myprint', + 'myprint', + 'length', + 'length', + 'length', + 'mysprintf', + 'mysprintf', + 'mysprintf', + 'mysprintf', + 's', + 's', + 's', + 'myprint', + 'mysprintf', + 'mysprintf', + 'mysprintf', + 'mysprintf', + 'qw', + 's', + 'myprint', + 'myprint', + 'myprint', + 's', + 'myprint', + 's', + 's', + 's', + 's', + 'mysprintf', + 's', + 's', + 's', + 's', + 'mysprintf', + 'mysprintf', + 'return', + 'qq', + 'return' + ], + 'eta' => [ + 'my', + 'return', + 'timesince', + 'time_remaining', + 'myprint', + 'localtime', + 'return', + 'mysprintf' + ], + 'output_reset_with' => [ + 'join' + ], + 'xmasterauth' => [ + 'User', + 'Password', + 'tag_and_run', + 'exit_clean', + 'myprint', + '_imap_command', + 'md5_hex', + 'exit_clean', + 'tag_and_run', + 'exit_clean', + 'State', + 'State' + ], + 'tests_output' => [ + 'note', + 'tests_output', + 'is', + 'output', + 'is', + 'output', + 'is', + 'output', + 'is', + 'output', + 'is', + 'output', + 'is', + 'output', + 'note', + 'tests_output' + ], + 'tests_list_keys_in_2_not_in_1' => [ + 'note', + 'tests_list_keys_in_2_not_in_1', + 'ok', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'ok', + 'compare_lists', + 'list_keys_in_2_not_in_1', + 'note', + 'tests_list_keys_in_2_not_in_1' + ], + 'tests_errors_log' => [ + 'note', + 'tests_errors_log', + 'is', + 'errors_log', + 'is', + 'errors_log', + 'is_deeply', + 'errors_log', + 'is_deeply', + 'errors_log', + 'is_deeply', + 'errors_log', + 'is_deeply', + 'errors_log', + 'note', + 'tests_errors_log' + ], + 'reconnect_if_needed' => [ + 'Server', + 'IsUnconnected', + 'reconnect', + 'State', + 'reconnect' + ], + 'tests_imapsync_context' => [ + 'note', + 'tests_imapsync_context', + 'like', + 'imapsync_context', + 'note', + 'tests_imapsync_context' + ], + 'tests_useheader_suggestion' => [ + 'note', + 'tests_useheader_suggestion', + 'is', + 'useheader_suggestion', + 'is', + 'useheader_suggestion', + 'is', + 'useheader_suggestion', + 'note', + 'tests_useheader_suggestion' + ], + 'cgibegin' => [ + 'under_cgi_context', + 'import', + 'qw', + 'import', + 'qw', + 'new' + ], + 'tests_remove_edging_blanks' => [ + 'note', + 'tests_remove_edging_blanks', + 'is', + 'remove_edging_blanks', + 'is', + 'remove_edging_blanks', + 'is', + 'remove_edging_blanks', + 'note', + 'tests_remove_edging_blanks' + ], + 'tests_ucsecond' => [ + 'note', + 'tests_ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'ok', + 'ucsecond', + 'note', + 'tests_ucsecond' + ], + 'install_signals' => [ + 'under_docker_context', + 'output', + 'output', + 'defined', + 'sig_install', + 'defined', + 'defined', + 'defined', + 'defined', + 'sig_install', + 'sig_install', + 'sig_install', + 'sig_install', + 'sig_install_toggle_sleep' + ], + 'message_exists' => [ + 'my', + 'return', + 'Uid', + 'search', + 'myprint', + 'return', + 'return' + ], + 'permanentflags' => [ + 'myprint', + 'return', + 'return' + ], + 'tests_quota_extract_storage_current_in_bytes' => [ + 'note', + 'tests_quota_extract_storage_current_in_bytes', + 'ok', + 'quota_extract_storage_current_in_bytes', + 'note', + 'tests_quota_extract_storage_current_in_bytes' + ], + 'remove_from_requested_folders' => [ + 'return' + ], + 'get_separator' => [ + 'my', + 'my', + 'myprint', + 'guess_separator', + 'myprint', + 'myprint', + 'has_capability', + 'separator', + 'myprint', + 'myprint', + 'return', + 'return', + 'myprint', + 'return', + 'myprint', + 'help_to_guess_sep', + 'return', + 'myprint', + 'return', + 'myprint', + 'help_to_guess_sep', + 'return' + ], + 'tests_hashsync' => [ + 'note', + 'tests_hashsync', + 'is', + 'hashsync', + 'is', + 'hashsync', + 'is', + 'hashsync', + 'is', + 'hashsync', + 'is', + 'hashsync', + 'note', + 'tests_hashsync' + ], + 'labels_remove_subfolder1' => [ + 'quotewords', + 'myprint', + 'join', + 'quotewords', + 'join', + 'uniq' + ], + 'search_dyn_lib_locale' => [ + 'search_dyn_lib_locale_darwin', + 'search_dyn_lib_locale_linux', + 'search_dyn_lib_locale_MSWin32' + ], + 'tests_split_around_equal' => [ + 'note', + 'tests_split_around_equal', + 'is', + 'split_around_equal', + 'is_deeply', + 'split_around_equal', + 'is_deeply', + 'split_around_equal', + 'is_deeply', + 'split_around_equal', + 'note', + 'tests_split_around_equal' + ], + 'errorsdump' => [ + 'my', + 'return' + ], + 'cache_map' => [ + 'myprint', + 'match_a_cache_file', + 'exists', + 'exists', + 'max', + 'return' + ], + 'build_possible_special' => [ + 'qw', + 'myprint', + 'Dump', + 'return' + ], + 'timesince' => [ + 'return', + 'max', + 'min' + ], + 'tests_loadavg' => [ + 'note', + 'tests_loadavg', + 'skip', + 'is', + 'loadavg', + 'is_deeply', + 'loadavg', + 'skip', + 'is', + 'loadavg', + 'ok', + 'loadavg', + 'is_deeply', + 'loadavg', + 'skip', + 'is_deeply', + 'loadavg', + 'note', + 'tests_loadavg' + ], + 'sync_flags' => [ + 'my', + 'myprint', + 'flags', + 'flags', + 'flags_for_host2', + 'flagscase', + 'myprint', + 'flags', + 'flags', + 'split', + 'split', + 'compare_lists', + 'myprint', + 'flags', + 'flags', + 'store', + 'errors_incr' + ], + 'usage' => [ + 'usage_footer', + 'mypod2usage', + 'backslash_caret', + 'join', + 'return' + ], + 'tests_compare_lists' => [ + 'note', + 'tests_compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'note', + 'tests_compare_lists' + ], + 'flags_for_host2' => [ + 'my', + 'defined', + 'flags_regex', + 'flagscase', + 'flags_filter', + 'return' + ], + 'delete_messages_on_any' => [ + 'my', + 'Debug', + 'myprint', + 'delete_message', + 'Range', + 'join', + 'errors_incr', + 'uidexpunge_or_expunge', + 'Debug' + ], + 'tests_version_from_rcs' => [ + 'note', + 'tests_version_from_rcs', + 'is', + 'version_from_rcs', + 'is', + 'version_from_rcs', + 'is', + 'version_from_rcs', + 'note', + 'tests_version_from_rcs' + ], + 'tests_get_options' => [ + 'note', + 'tests_get_options', + 'is', + 'get_options', + 'qw', + 'is', + 'is', + 'get_options', + 'qw', + 'is', + 'is', + 'is', + 'get_options', + 'qw', + 'is', + 'is', + 'get_options', + 'qw', + 'is', + 'is', + 'get_options', + 'is', + 'get_options', + 'is', + 'get_options', + 'is', + 'get_options', + 'qw', + 'is', + 'get_options', + 'qw', + 'is', + 'get_options', + 'qw', + 'is', + 'get_options', + 'qw', + 'is', + 'get_options', + 'qw', + 'is', + 'myprint', + 'Dump', + 'note', + 'tests_get_options' + ], + 'tests_truncmess' => [ + 'note', + 'tests_truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'is', + 'truncmess', + 'note', + 'tests_truncmess' + ], + 'bytes_display_string' => [ + 'defined', + 'return', + 'match_number', + 'return', + 'abs', + 'mysprintf', + 'abs', + 'mysprintf', + 'abs', + 'mysprintf', + 'abs', + 'mysprintf', + 'mysprintf', + 'myprint', + 'return' + ], + 'tests_nb_messages_in_2_not_in_1' => [ + 'note', + 'tests_stats_across_folders', + 'is', + 'nb_messages_in_2_not_in_1', + 'is', + 'nb_messages_in_2_not_in_1', + 'is', + 'nb_messages_in_2_not_in_1', + 'is', + 'nb_messages_in_2_not_in_1', + 'is', + 'nb_messages_in_2_not_in_1', + 'note', + 'tests_stats_across_folders' + ], + 'cache_dir_fix' => [ + 'myprint', + 'return' + ], + 'tmpdir_has_colon_bug' => [ + 'filter_forbidden_characters', + 'myprint', + 'return', + 'return' + ], + 'tests_string_to_file' => [ + 'note', + 'tests_string_to_file', + 'is', + 'string_to_file', + 'is', + 'string_to_file', + 'is', + 'string_to_file', + 'ok', + 'mkpath', + 'is', + 'string_to_file', + 'is', + 'string_to_file', + 'skip', + 'is', + 'string_to_file', + 'note', + 'tests_string_to_file' + ], + 'myGetOptions' => [ + 'under_cgi_context', + 'GetOptionsFromArray', + 'defined', + 'param', + 'multi_param', + 'myprint', + 'ref', + 'ref', + 'ref', + 'ref', + 'param' + ], + 'tests_remove_not_num' => [ + 'note', + 'tests_remove_not_num', + 'ok', + 'remove_not_num', + 'remove_not_num', + 'ok', + 'remove_not_num', + 'remove_not_num', + 'ok', + 'remove_not_num', + 'remove_not_num', + 'ok', + 'remove_not_num', + 'remove_not_num', + 'note', + 'tests_remove_not_num' + ], + 'tests_tmpdir_has_colon_bug' => [ + 'note', + 'tests_tmpdir_has_colon_bug', + 'ok', + 'tmpdir_has_colon_bug', + 'ok', + 'tmpdir_has_colon_bug', + 'ok', + 'tmpdir_has_colon_bug', + 'ok', + 'tmpdir_has_colon_bug', + 'note', + 'tests_tmpdir_has_colon_bug' + ], + 'get_prefix' => [ + 'my', + 'my', + 'myprint', + 'guess_prefix', + 'myprint', + 'myprint', + 'has_capability', + 'namespace', + 'myprint', + 'myprint', + 'return', + 'return', + 'myprint', + 'return', + 'myprint', + 'help_to_guess_prefix', + 'return' + ], + 'tests_labels_add_subfolder2' => [ + 'note', + 'tests_labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'is', + 'labels_add_subfolder2', + 'note', + 'tests_labels_add_subfolder2' + ], + 'header_construct' => [ + 'my', + 'header_line_normalize', + 'myprint', + 'myprint', + 'return' + ], + 'getpwuid_any_os' => [ + 'return', + 'return' + ], + 'synclabels' => [ + 'my', + 'all_defined', + 'Debug', + 'labels', + 'Debug', + 'myprint', + 'labels_remove_subfolder1', + 'myprint', + 'labels_add_subfolder2', + 'myprint', + 'Debug', + 'store', + 'Debug' + ], + 'remove_qq' => [ + 'return', + 'return' + ], + 'get_options' => [ + 'myprint', + 'Dump', + 'under_cgi_context', + 'get_options_cgi', + 'get_options_cmd', + 'myprint', + 'Dump', + 'ref', + 'scalar' + ], + 'delete_folders_in_2_not_in_1' => [ + 'myprint', + 'myprint', + 'unsubscribe', + 'delete', + 'myprint', + 'myprint' + ], + 'debugsleep' => [ + 'myprint' + ], + 'xoauth2' => [ + 'if', + 'new', + 'myprint', + 'open', + 'exit_clean', + 'decode', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'encode', + 'new', + 'env_proxy', + 'post', + 'encode_entities', + 'unless', + 'is_success', + 'exit_clean', + 'myprint', + 'decode_json', + 'encode_base64', + 'myprint', + 'return' + ], + 'nthline' => [ + 'all_defined', + 'file_to_array' + ], + 'tests_report_failures' => [ + 'note', + 'tests_report_failures', + 'is', + 'report_failures', + 'is', + 'report_failures', + 'is', + 'report_failures', + 'is', + 'report_failures', + 'is', + 'report_failures', + 'note', + 'tests_report_failures' + ], + 'tests_match_a_pid_number' => [ + 'note', + 'tests_match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'is', + 'match_a_pid_number', + 'note', + 'tests_match_a_pid_number' + ], + 'tcpping' => [ + 'scalar', + 'new', + 'new', + 'service_check', + 'hires', + 'ping', + 'sprintf', + 'myprint', + 'close', + 'if' + ], + 'delete1emptyfolders' => [ + 'IsUnconnected', + 'myprint', + 'is_parent', + 'myprint', + 'examine_folder_and_count', + 'scalar', + 'messages', + 'myprint', + 'myprint', + 'messages', + 'myprint', + 'myprint', + 'close', + 'delete_folder', + 'remove_deleted_folders_from_wanted_list', + 'myprint' + ], + 'xoauth' => [ + 'mysprintf', + 'mysprintf', + 'uri_escape', + 'myprint', + 'uri_escape', + 'md5_hex', + 'uniqid', + 'rand', + 'uri_escape', + 'length', + 'uri_escape', + 'myprint', + 'hmac_sha1', + 'uri_escape', + 'uri_escape', + 'encode_base64', + 'if', + 'length', + 'myprint', + 'encode_base64' + ], + 'authenticate_imap' => [ + 'my', + 'check_capability', + 'User', + 'Domain', + 'Authuser', + 'Password', + 'xmasterauth', + 'Authmechanism', + 'User', + 'Authmechanism', + 'Authcallback', + 'Authcallback', + 'Authcallback', + 'login', + 'IsUnconnected', + 'myprint', + 'IsUnconnected', + 'exit_clean', + 'myprint', + 'myprint', + 'Authmechanism', + 'login', + 'exit_clean', + 'proxyauth', + 'exit_clean' + ], + 'delete_folder' => [ + 'unsubscribe', + 'delete', + 'myprint', + 'myprint' + ], + 'lost_connection' => [ + 'my', + 'IsUnconnected', + '_filter', + '_filter', + 'myprint', + 'myprint', + 'return', + 'return' + ], + 'touch' => [ + 'open', + 'myprint', + 'return' + ], + 'tests_message_for_host2' => [ + 'note', + 'tests_message_for_host2', + 'is', + 'message_for_host2', + 'is', + 'message_for_host2', + 'require_ok', + 'new', + 'mock', + 'is', + 'message_for_host2', + 'is', + 'skip', + 'skip', + 'is', + 'message_for_host2', + 'is', + 'is', + 'message_for_host2', + 'is', + 'is', + 'message_for_host2', + 'is', + 'note', + 'tests_message_for_host2' + ], + 'is_an_integer' => [ + 'return' + ], + 'tests_imapsync_basename' => [ + 'note', + 'tests_imapsync_basename', + 'ok', + 'imapsync_basename', + 'ok', + 'imapsync_basename', + 'note', + 'tests_imapsync_basename' + ], + 'size_filtered_flag' => [ + 'return', + 'return', + 'return' + ], + 'length_ref' => [ + 'length' + ], + 'ask_for_password_new' => [ + 'get_stdin_masked' + ], + 'tests_get_options_cgi_context' => [ + 'note', + 'tests_get_options_cgi', + 'is', + 'get_options', + 'import', + 'qw', + 'is', + 'get_options', + 'new', + 'is', + 'get_options', + 'is', + 'is', + 'new', + 'is', + 'get_options', + 'is', + 'new', + 'get_options', + 'is_deeply', + 'new', + 'get_options', + 'is_deeply', + 'new', + 'get_options', + 'is_deeply', + 'new', + 'get_options', + 'is_deeply', + 'new', + 'get_options', + 'is_deeply', + 'new', + 'get_options', + 'is', + 'new', + 'get_options', + 'is', + 'myprint', + 'Dump', + 'new', + 'get_options', + 'is', + 'new', + 'get_options', + 'is', + 'new', + 'get_options', + 'is', + 'is', + 'myprint', + 'Dump', + 'new', + 'get_options', + 'is', + 'myprint', + 'Dump', + 'note', + 'tests_get_options_cgi_context' + ], + 'usage_complete' => [ + 'return' + ], + 'sync_flags_after_copy' => [ + 'my', + 'flags', + 'myprint', + 'sync_flags', + 'myprint' + ], + 'abort' => [ + 'myprint', + 'firstline', + 'myprint', + 'killpid' + ], + 'get_options_cgi' => [ + 'myGetOptions', + 'tests_get_options_cgi', + 'tests_myGetOptions', + 'output' + ], + 'output_start' => [ + 'join' + ], + 'skipmess' => [ + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'return', + 'return', + 'return' + ], + 'get_password1' => [ + 'myprint', + 'ask_for_password', + 'myprint', + 'exit_clean' + ], + 'tests_testsunit' => [ + 'note', + 'tests_testunit', + 'is', + 'testsunit', + 'is', + 'testsunit', + 'is', + 'testsunit', + 'is', + 'testsunit', + 'is', + 'testsunit', + 'note', + 'tests_testunit' + ], + 'tests_memory_consumption' => [ + 'note', + 'tests_memory_consumption', + 'like', + 'memory_consumption', + 'like', + 'memory_consumption', + 'like', + 'memory_consumption', + 'like', + 'memory_consumption_ratio', + 'like', + 'memory_consumption_ratio', + 'like', + 'memory_consumption_ratio', + 'like', + 'memory_consumption', + 'note', + 'tests_memory_consumption' + ], + 'capability_of' => [ + 'search_in_array' + ], + 'message_for_host2' => [ + 'myprint', + 'debugmemory', + 'message_to_file', + 'myprint', + 'debugmemory', + 'length_ref', + 'errors_incr', + 'skipmess', + 'myprint', + 'subject', + 'myprint', + 'regexmess', + 'myprint', + 'pipemess', + 'myprint', + 'add_header', + 'myprint', + 'is_an_integer', + 'truncmess', + 'length_ref', + 'myprint', + 'myprint', + 'debugmemory' + ], + 'tests_tail' => [ + 'note', + 'tests_tail', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'is', + 'tail', + 'is', + 'tail', + 'is', + 'tail', + 'is', + 'tail', + 'is', + 'string_to_file', + 'is', + 'tail', + 'is', + 'string_to_file', + 'is', + 'tail', + 'is', + 'tail', + 'is', + 'string_to_file', + 'is', + 'tail', + 'note', + 'tests_tail' + ], + 'tests_labels_remove_subfolder1' => [ + 'note', + 'tests_labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'is', + 'labels_remove_subfolder1', + 'note', + 'tests_labels_remove_subfolder1' + ], + 'tests_permanentflags' => [ + 'note', + 'tests_permanentflags', + 'ok', + 'permanentflags', + 'ok', + 'permanentflags', + 'ok', + 'permanentflags', + 'ok', + 'permanentflags', + 'note', + 'tests_permanentflags' + ], + 'separator_invert' => [ + 'my', + 'return', + 'all_defined', + 'if', + 'return' + ], + 'resolvrev_with_getaddrinfo' => [ + 'getaddrinfo', + 'myprint', + 'while', + 'getnameinfo', + 'NIx_NOSERV', + 'myprint', + 'myprint' + ], + 'check_lib_version' => [ + 'myprint', + 'myprint' + ], + 'select_msgs_by_fetch' => [ + 'messages', + 'myprint', + 'fetch_hash', + 'uidnext', + 'fetch_hash', + 'myprint', + 'fetch_hash', + 'return', + 'myprint', + 'search', + 'return', + 'my', + 'myprint', + 'epoch', + 'epoch', + 'msgs_from_maxmin', + 'return' + ], + 'quota' => [ + 'Debug', + 'Debug', + 'has_capability', + 'Debug', + 'myprint', + 'getquotaroot', + 'quota', + 'quota', + 'myprint', + 'Debug', + 'quota_extract_storage_limit_in_bytes', + 'quota_extract_storage_current_in_bytes', + 'mysprintf', + 'myprint', + 'errors_incr' + ], + 'search_dyn_lib_locale_darwin' => [ + 'myprint', + 'backtick' + ], + 'flagscase' => [ + 'ucsecond', + 'ucsecond', + 'return' + ], + 'firstline' => [ + 'nthline' + ], + 'remove_pidfile_not_running' => [ + 'myprint', + 'myprint', + 'myprint', + 'firstline', + 'match_a_pid_number', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint' + ], + 'match_a_pid_number' => [ + 'match', + 'abs', + 'abs' + ], + 'check_binary_embed_all_dyn_libs' => [ + 'search_dyn_lib_locale', + 'myprint', + 'myprint', + 'myprint' + ], + 'tests_setlogfile' => [ + 'note', + 'tests_setlogfile', + 'is', + 'setlogfile', + 'skip', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'is', + 'setlogfile', + 'note', + 'tests_setlogfile' + ], + 'compare_lists' => [ + 'return', + 'return', + 'return' + ], + 'tests_sanitize' => [ + 'note', + 'tests_remove_edging_blanks', + 'is', + 'sanitize', + 'is', + 'sanitize', + 'is', + 'is', + 'is', + 'note', + 'tests_remove_edging_blanks' + ], + 'get_options_cmd' => [ + 'output', + 'myGetOptions', + 'myprint', + 'Dump', + 'output', + 'myprint', + 'myprint' + ], + 'select_msgs_by_age' => [ + 'my', + 'my', + 'sentsince', + 'sentbefore', + 'msgs_from_maxmin', + 'return' + ], + 'get_password2' => [ + 'myprint', + 'ask_for_password', + 'myprint', + 'exit_clean' + ], + 'tests_toggle_sleep' => [ + 'note', + 'tests_toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'is', + 'toggle_sleep', + 'skip', + 'kill', + 'is', + 'sig_install', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'note', + 'tests_toggle_sleep' + ], + 'tmpdir_fix_colon_bug' => [ + 'myprint', + 'return', + 'tmpdir_has_colon_bug', + 'return', + 'filter_forbidden_characters', + 'myprint', + 'return', + 'myprint', + 'return', + 'myprint', + 'rmove', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'return' + ], + 'fix_Inbox_INBOX_mapping' => [ + 'my', + 'return' + ], + 'tototo' => [ + 'myprint' + ], + 'tests_capability_of' => [ + 'note', + 'tests_capability_of', + 'is', + 'capability_of', + 'is', + 'capability_of', + 'mock_capability', + 'is', + 'capability_of', + 'is', + 'capability_of', + 'note', + 'tests_capability_of' + ], + 'tests_labels' => [ + 'note', + 'tests_labels', + 'is', + 'labels', + 'is', + 'labels', + 'require_ok', + 'new', + 'mock', + 'return', + 'mock', + 'mock', + 'Unescape', + 'is', + 'labels', + 'is', + 'labels', + 'note', + 'tests_labels' + ], + 'createhashfileifneeded' => [ + 'rand32', + 'dirname', + 'myprint', + 'myprint' + ], + 'tests_decompose_regex' => [ + 'note', + 'tests_decompose_regex', + 'ok', + 'ok', + 'compare_lists', + 'decompose_regex', + 'ok', + 'compare_lists', + 'decompose_regex', + 'note', + 'tests_decompose_regex' + ], + 'tests_eta' => [ + 'note', + 'tests_eta', + 'is', + 'eta', + 'is', + 'eta', + 'is', + 'eta', + 'is', + 'localtime', + 'eta', + 'is', + 'localtime', + 'eta', + 'note', + 'tests_eta' + ], + 'tests_all_defined' => [ + 'note', + 'tests_all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'is', + 'all_defined', + 'note', + 'tests_all_defined' + ], + 'tests_sanitize_subfolder' => [ + 'note', + 'tests_sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'is', + 'sanitize_subfolder', + 'note', + 'tests_sanitize_subfolder' + ], + 'jux_utf8_old' => [ + 'imap_utf7_decode_old', + 'myprint', + 'return', + 'myprint', + 'return' + ], + 'login_imap' => [ + 'my', + 'myprint', + 'init_imap', + 'connect', + 'exit_clean', + 'myprint', + 'peerhost', + 'Results', + 'myprint', + 'myprint', + 'join', + 'capability', + 'has_capability', + 'myprint', + 'IsAuthenticated', + 'myprintf', + 'exit_clean', + 'set_tls', + 'starttls', + 'exit_clean', + 'myprint', + 'authenticate_imap', + 'myprint', + 'return' + ], + 'special_from_folders_hash' => [ + 'can', + 'errors_incr', + 'return', + 'folders_hash', + 'myprintf', + 'join', + 'myprintf', + 'join', + 'myprint', + 'return' + ], + 'umask_str' => [ + 'oct', + 'return', + 'sprintf' + ], + 'tests_clean_cache' => [ + 'note', + 'tests_clean_cache', + 'ok', + 'rmtree', + 'ok', + 'mkpath', + 'qw', + 'ok', + 'touch', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'clean_cache', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'note', + 'tests_clean_cache' + ], + 'list_folders_in_2_not_in_1' => [ + 'list_keys_in_2_not_in_1', + 'list_keys_in_2_not_in_1', + 'return' + ], + 'tests_diff_or_NA' => [ + 'note', + 'tests_diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'is', + 'diff_or_NA', + 'note', + 'tests_diff_or_NA' + ], + 'skip_macosx' => [ + 'return', + 'hostname' + ], + 'tests_secondline' => [ + 'note', + 'tests_secondline', + 'is', + 'secondline', + 'is', + 'secondline', + 'ok', + 'mkpath', + 'is', + 'string_to_file', + 'is', + 'secondline', + 'note', + 'tests_secondline' + ], + 'connect_socket' => [ + 'my', + 'peerhost', + 'peerport', + 'peerhost', + 'peerport', + 'new', + 'Socket', + 'Results', + 'myprint' + ], + 'imap2_folder_name' => [ + 'myprint', + 'return', + 'myprint', + 'return', + 'remove_last_char_if_is', + 'part_to_removed', + 'myprint', + 'prefix_seperator_invertion', + 'regextrans2', + 'return' + ], + 'tests_always_fail' => [ + 'note', + 'tests_always_fail', + 'is', + 'note', + 'tests_always_fail' + ], + 'format_for_imap_arg' => [ + 'return', + 'qw', + 'return' + ], + 'logfileprepa' => [ + 'defined', + 'myprint', + 'dirname', + 'do_valid_directory', + 'return', + 'return' + ], + 'file_to_string' => [ + 'return', + 'myprint' + ], + 'string_to_file' => [ + 'my', + 'if', + 'if', + 'dirname', + 'myprint', + 'sysopen', + 'myprint' + ], + 'tests_get_stdin_masked' => [ + 'note', + 'tests_get_stdin_masked', + 'is', + 'get_stdin_masked', + 'is', + 'get_stdin_masked', + 'note', + 'tests_get_stdin_masked' + ], + 'usage_footer' => [ + 'localhost_info', + 'homepage', + 'check_last_release' + ], + 'guess_separator' => [ + 'return', + 'myprint', + 'return' + ], + 'tests_backtick' => [ + 'note', + 'tests_backtick', + 'is', + 'backtick', + 'is', + 'backtick', + 'skip', + 'backtick', + 'ok', + 'myprint', + 'backtick', + 'ok', + 'ok', + 'myprint', + 'ok', + 'backtick', + 'ok', + 'backtick', + 'skip', + 'is', + 'backtick', + 'backtick', + 'ok', + 'myprint', + 'backtick', + 'ok', + 'ok', + 'myprint', + 'ok', + 'backtick', + 'ok', + 'backtick', + 'is', + 'backtick', + 'backtick', + 'myprint', + 'note', + 'tests_backtick' + ], + 'resolv' => [ + 'resolv_with_getaddrinfo', + 'return', + 'inet_aton', + 'inet_ntoa' + ], + 'tests_check_binary_embed_all_dyn_libs' => [ + 'note', + 'tests_check_binary_embed_all_dyn_libs', + 'is', + 'check_binary_embed_all_dyn_libs', + 'note', + 'tests_check_binary_embed_all_dyn_libs' + ], + 'loadavg_linux' => [ + 'firstline', + 'all_defined', + 'myprint' + ], + 'pidfile' => [ + 'filter_forbidden_characters', + 'slash_to_underscore', + 'filter_forbidden_characters', + 'slash_to_underscore' + ], + 'module_version_str' => [ + 'my', + 'mysprintf', + 'return' + ], + 'tests_flags_filter' => [ + 'note', + 'tests_flags_filter', + 'ok', + 'flags_filter', + 'ok', + 'flags_filter', + 'ok', + 'flags_filter', + 'ok', + 'flags_filter', + 'ok', + 'flags_filter', + 'ok', + 'flags_filter', + 'note', + 'tests_flags_filter' + ], + 'mysprintf' => [ + 'my' + ], + 'automap' => [ + 'myprint', + 'myprint', + 'special_from_folders_hash', + 'special_from_folders_hash', + 'build_possible_special', + 'build_guess_special', + 'build_automap' + ], + 'loadavg_darwin' => [ + 'myprint', + 'myprint', + 'firstline', + 'myprint' + ], + 'foldersizes_total' => [ + 'scalar', + 'scalar', + 'add', + 'add', + 'max', + 'add', + 'add', + 'max', + 'myprintf', + 'myprintf', + 'myprint', + 'myprintf', + 'myprintf', + 'myprint', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'bytes_display_string', + 'myprint', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'bytes_display_string', + 'myprint', + 'myprintf', + 'timenext' + ], + 'tests_do_valid_directory' => [ + 'note', + 'tests_do_valid_directory', + 'skip', + 'ok', + 'do_valid_directory', + 'ok', + 'do_valid_directory', + 'skip', + 'diag', + 'ok', + 'do_valid_directory', + 'diag', + 'ok', + 'do_valid_directory', + 'note', + 'tests_do_valid_directory' + ], + 'get_stdin_masked' => [ + 'prompt', + 'myprint' + ], + 'banner_imapsync' => [ + 'command_line_nopassword', + 'return' + ], + 'errors_log' => [ + 'join' + ], + 'private_folders_separators_and_prefixes' => [ + 'myprint', + 'get_separator', + 'get_separator', + 'get_prefix', + 'get_prefix', + 'myprint', + 'myprint' + ], + 'match' => [ + 'my', + 'myprint' + ], + 'sync_flags_fir' => [ + 'size_filtered_flag', + 'sync_flags' + ], + 'tests_length_ref' => [ + 'note', + 'tests_length_ref', + 'is', + 'length_ref', + 'is', + 'length_ref', + 'is', + 'length_ref', + 'is', + 'length_ref', + 'note', + 'tests_length_ref' + ], + 'justconnect' => [ + 'justconnect1', + 'justconnect2' + ], + 'catch_ignore' => [ + 'myprint', + 'getppid', + 'stats' + ], + 'comment_on_final_diff_in_1_not_in_2' => [ + 'scalar', + 'scalar', + 'myprint', + 'myprint', + 'nb_messages_in_1_not_in_2', + 'myprint', + 'myprint', + 'myprint', + 'myprint' + ], + 'logfile' => [ + 'strftime', + 'int', + 'return' + ], + 'help_to_guess_prefix' => [ + 'my', + 'folders_list_to_help', + 'return' + ], + 'tests_umask_str' => [ + 'note', + 'tests_umask_str', + 'is', + 'umask_str', + 'umask_str', + 'is', + 'umask_str', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'skip', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'umask_str', + 'is', + 'is', + 'is', + 'note', + 'tests_umask_str' + ], + 'select_msgs' => [ + 'select_msgs_by_search', + 'select_msgs_by_fetch', + 'return' + ], + 'tests_info_date_from_uid' => [ + 'note', + 'tests_info_date_from_uid', + 'note', + 'tests_info_date_from_uid' + ], + 'tests_createhashfileifneeded' => [ + 'note', + 'tests_createhashfileifneeded', + 'is', + 'createhashfileifneeded', + 'note', + 'tests_createhashfileifneeded' + ], + 'tests_sig_install' => [ + 'note', + 'tests_sig_install', + 'is', + 'sig_install', + 'is', + 'sig_install', + 'is', + 'sig_install', + 'skip', + 'kill', + 'is', + 'sig_install', + 'is', + 'kill', + 'is', + 'is', + 'sig_install', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'is', + 'sig_install', + 'is', + 'kill', + 'is', + 'is', + 'kill', + 'is', + 'note', + 'tests_sig_install' + ], + 'cgioutputenvcontext' => [ + 'qw', + 'output' + ], + 'tests_set_umask' => [ + 'note', + 'tests_set_umask', + 'is', + 'set_umask', + 'is', + 'set_umask', + 'note', + 'tests_set_umask' + ], + 'imap_id_stuff' => [ + 'imap_id', + 'myprint', + 'imap_id', + 'myprint' + ], + 'justconnect1' => [ + 'myprint', + 'connect_imap', + 'imap_id', + 'logout' + ], + 'tests_max' => [ + 'note', + 'tests_max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'is', + 'max', + 'note', + 'tests_max' + ], + 'plainauth' => [ + 'mysprintf', + 'encode_base64' + ], + 'examine_folder' => [ + 'examine', + 'errors_incr', + 'return', + 'return' + ], + 'tests_check_last_release' => [ + 'note', + 'tests_check_last_release', + 'diag', + 'check_last_release', + 'like', + 'check_last_release', + 'like', + 'check_last_release', + 'diag', + 'check_last_release', + 'like', + 'check_last_release', + 'like', + 'check_last_release', + 'like', + 'check_last_release', + 'like', + 'check_last_release', + 'like', + 'check_last_release', + 'diag', + 'check_last_release', + 'note', + 'tests_check_last_release' + ], + 'tests_operators_and_exclam_precedence' => [ + 'note', + 'tests_operators_and_exclam_precedence', + 'is', + 'is', + 'is', + 'not', + 'not', + 'is', + 'not', + 'not', + 'delete_messages_on_any', + 'is', + 'is', + 'is', + 'is', + 'is', + 'is', + 'is', + 'is', + 'is', + 'note', + 'tests_operators_and_exclam_precedence' + ], + 'tests_regexmess' => [ + 'note', + 'tests_regexmess', + 'ok', + 'regexmess', + 'ok', + 'not', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'myprint', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'A', + 'n', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'A', + 'A', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'ok', + 'regexmess', + 'is', + 'regexmess', + 'is', + 'regexmess', + 'is', + 'regexmess', + 'is', + 'regexmess', + 'A', + 'note', + 'tests_regexmess' + ], + 'tests_resolv' => [ + 'note', + 'tests_resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'is', + 'resolv', + 'note', + 'tests_resolv' + ], + 'prefix_seperator_invertion' => [ + 'myprint', + 'separator_invert', + 'myprint', + 'myprint', + 'return' + ], + 'subfolder1' => [ + 'sanitize_subfolder', + 'myprint', + 'myprint', + 'add_subfolder1_to_folderrec', + 'exit_clean' + ], + 'subject' => [ + 'extract_header', + 'if', + 'myprint', + 'return' + ], + 'foldersizesatend_old' => [ + 'timenext', + 'IsUnconnected', + 'IsUnconnected', + 'folders', + 'foldersizes', + 'foldersizes', + 'all_defined', + 'errors_incr' + ], + 'tests_abort' => [ + 'note', + 'tests_abort', + 'is', + 'abort', + 'note', + 'tests_abort' + ], + 'tests_imap2_folder_name' => [ + 'note', + 'tests_imap2_folder_name', + 'myprint', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'U', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'set_regextrans2_for_subfolder2', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'set_regextrans2_for_subfolder2', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'is', + 'imap2_folder_name', + 'note', + 'tests_imap2_folder_name' + ], + 'tests_guess_separator' => [ + 'note', + 'tests_guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'ok', + 'guess_separator', + 'note', + 'tests_guess_separator' + ], + 'uidexpunge_or_expunge' => [ + 'uidexpunge', + 'expunge' + ], + 'help_to_guess_sep' => [ + 'my', + 'folders_list_to_help', + 'return' + ], + 'isrunning' => [ + 'myprint', + 'myprint' + ], + 'tests_appendlimit_from_capability' => [ + 'note', + 'tests_appendlimit_from_capability', + 'is', + 'appendlimit_from_capability', + 'is', + 'appendlimit_from_capability', + 'mock_capability', + 'is', + 'appendlimit_from_capability', + 'mock_capability', + 'is', + 'appendlimit_from_capability', + 'note', + 'tests_appendlimit_from_capability' + ], + 'tests_pipemess' => [ + 'note', + 'tests_pipemess', + 'skip', + 'ok', + 'pipemess', + 'ok', + 'pipemess', + 'qw', + 'diag', + 'ok', + 'defined', + 'pipemess', + 'skip', + 'ok', + 'pipemess', + 'ok', + 'pipemess', + 'ok', + 'pipemess', + 'ok', + 'pipemess', + 'ok', + 'pipemess', + 'diag', + 'is', + 'pipemess', + 'is', + 'pipemess', + 'is', + 'pipemess', + 'is', + 'pipemess', + 'pipemess', + 'is', + 'is', + 'pipemess', + 'is', + 'like', + 'pipemess', + 'is', + 'like', + 'pipemess', + 'is', + 'is', + 'pipemess', + 'is', + 'like', + 'pipemess', + 'is', + 'like', + 'pipemess', + 'is', + 'like', + 'pipemess', + 'like', + 'like', + 'pipemess', + 'is', + 'like', + 'note', + 'tests_pipemess' + ], + 'tests_clean_cache_2' => [ + 'note', + 'tests_clean_cache_2', + 'ok', + 'rmtree', + 'ok', + 'mkpath', + 'qw', + 'ok', + 'touch', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'clean_cache', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'note', + 'tests_clean_cache_2' + ], + 'sleep_max_messages' => [ + 'my', + 'return', + 'return', + 'max' + ], + 'diff_or_NA' => [ + 'my', + 'match_number', + 'match_number', + 'return' + ], + 'tests_subject' => [ + 'note', + 'tests_subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'ok', + 'subject', + 'note', + 'tests_subject' + ], + 'cleanup_before_exit' => [ + 'remove_tmp_files', + 'IsConnected', + 'myprint', + 'logout', + 'IsConnected', + 'myprint', + 'logout', + 'myprint', + 'myprint' + ], + 'time_remaining' => [ + 'my', + 'return' + ], + 'check_last_release' => [ + 'not_long_imapsync_version_public', + 'myprint', + 'return', + 'return', + 'is_a_release_number', + 'return', + 'imapsync_version', + 'return', + 'return', + 'return' + ], + 'modulesversion' => [ + 'module_version_str', + 'return' + ], + 'subfolder2' => [ + 'sanitize_subfolder', + 'myprint', + 'myprint', + 'set_regextrans2_for_subfolder2' + ], + 'ucsecond' => [ + 'return', + 'substr', + 'myprint', + 'return' + ], + 'max_line_length' => [ + 'max', + 'return' + ], + 'search_dyn_lib_locale_MSWin32' => [ + 'myprint', + 'qx' + ], + 'foldersize' => [ + 'all_defined', + 'examine', + 'select', + 'errors_incr', + 'IsUnconnected', + 'select_msgs', + 'IsUnconnected', + 'fetch_hash', + 'errors_incr', + 'uidnext', + 'fetch_hash', + 'errors_incr', + 'max', + 'return' + ], + 'catch_reconnect' => [ + 'here_twice', + 'myprint', + 'catch_exit', + 'myprint', + 'getppid', + 'myprint', + 'myprint', + 'State', + 'reconnect', + 'myprint', + 'exit_clean', + 'myprint', + 'State', + 'reconnect', + 'myprint', + 'exit_clean', + 'myprint' + ], + 'after_get_options' => [ + 'myprint', + 'myprint', + 'usage' + ], + 'tests_cache_folder' => [ + 'note', + 'tests_cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'ok', + 'cache_folder', + 'note', + 'tests_cache_folder' + ], + 'sanitize' => [ + 'qw', + 'remove_edging_blanks' + ], + 'justconnect2' => [ + 'myprint', + 'connect_imap', + 'imap_id', + 'logout' + ], + 'folders_list_to_help' => [ + 'my', + 'return' + ], + 'tests_epoch' => [ + 'note', + 'tests_epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'ok', + 'epoch', + 'is', + 'epoch', + 'is', + 'epoch', + 'note', + 'tests_epoch' + ], + 'tests_sslcheck' => [ + 'note', + 'tests_sslcheck', + 'is', + 'sslcheck', + 'is', + 'sslcheck', + 'is', + 'sslcheck', + 'is', + 'sslcheck', + 'is', + 'is', + 'sslcheck', + 'is', + 'sslcheck', + 'is', + 'sslcheck', + 'note', + 'tests_sslcheck' + ], + 'tests_true' => [ + 'note', + 'tests_true', + 'is', + 'note', + 'tests_true' + ], + 'missing_option' => [ + 'exit_clean' + ], + 'timenext' => [ + 'myprint', + 'return' + ], + 'sig_install' => [ + 'myprint', + 'myprint', + 'mysub', + 'myprint', + 'output' + ], + 'simulong' => [ + 'myprint', + 'myprint', + 'sleep' + ], + 'do_valid_directory' => [ + 'return', + 'myprint', + 'return', + 'myprintf', + 'getpwuid_any_os', + 'oct', + 'getpwuid_any_os', + 'uid', + 'return', + 'myprint', + 'mkpath', + 'myprint', + 'return', + 'return' + ], + 'loadavg' => [ + 'loadavg_linux', + 'loadavg_freebsd', + 'loadavg_darwin', + 'loadavg_windows', + 'return' + ], + 'tests_is_a_release_number' => [ + 'note', + 'tests_is_a_release_number', + 'is', + 'is_a_release_number', + 'ok', + 'is_a_release_number', + 'ok', + 'is_a_release_number', + 'ok', + 'is_a_release_number', + 'imapsync_version', + 'imapsync_version', + 'ok', + 'is_a_release_number', + 'note', + 'tests_is_a_release_number' + ], + 'tests_mock_capability' => [ + 'note', + 'tests_mock_capability', + 'ok', + 'mock_capability', + 'ok', + 'isa', + 'is', + 'capability', + 'ok', + 'mock_capability', + 'is', + 'capability', + 'ok', + 'mock_capability', + 'is', + 'capability', + 'ok', + 'mock_capability', + 'is', + 'capability', + 'ok', + 'mock_capability', + 'is_deeply', + 'capability', + 'ok', + 'mock_capability', + 'is_deeply', + 'capability', + 'ok', + 'mock_capability', + 'is_deeply', + 'capability', + 'ok', + 'mock_capability', + 'is_deeply', + 'capability', + 'note', + 'tests_mock_capability' + ], + 'set_umask' => [ + 'umask_str', + 'umask_str', + 'output' + ], + 'tests_search_in_array' => [ + 'note', + 'tests_search_in_array', + 'is', + 'search_in_array', + 'is', + 'search_in_array', + 'is', + 'search_in_array', + 'is', + 'search_in_array', + 'note', + 'tests_search_in_array' + ], + 'reconnect_12_if_needed' => [ + 'reconnect_if_needed', + 'reconnect_if_needed' + ], + 'create_folder_old' => [ + 'my', + 'myprint', + 'exists', + 'myprint', + 'return', + 'create', + 'LastError', + 'errors_incr', + 'return', + 'exists', + 'return', + 'myprint', + 'return', + 'myprint', + 'return' + ], + 'cpu_number' => [ + 'myprint', + 'backtick', + 'chomp', + 'myprint', + 'myprint', + 'elsif', + 'file_to_array', + 'myprint', + 'return', + 'integer_or_1' + ], + 'tests_convert_sep_to_slash' => [ + 'note', + 'tests_convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'ok', + 'convert_sep_to_slash', + 'note', + 'tests_convert_sep_to_slash' + ], + 'tests_write_pidfile' => [ + 'note', + 'tests_write_pidfile', + 'is', + 'write_pidfile', + 'is', + 'write_pidfile', + 'is', + 'write_pidfile', + 'is', + 'write_pidfile', + 'ok', + 'mkpath', + 'is', + 'touch', + 'is', + 'write_pidfile', + 'is', + 'firstline', + 'is', + 'secondline', + 'is', + 'write_pidfile', + 'is', + 'write_pidfile', + 'is', + 'firstline', + 'is', + 'secondline', + 'note', + 'tests_write_pidfile' + ], + 'imapsync_basename' => [ + 'basename' + ], + 'testsexit' => [ + 'tests', + 'testsdebug', + 'testunitsession', + 'summary', + 'details', + 'scalar', + 'expected_tests', + 'count_0s', + 'report_failures', + 'reset', + 'myprint', + 'cleanup_mess_from_tests' + ], + 'printenv' => [ + 'myprint' + ], + 'tests_connect_socket' => [ + 'note', + 'tests_connect_socket', + 'is', + 'connect_socket', + 'hostname', + 'skip_macosx', + 'hostname', + 'skip', + 'new', + 'ok', + 'connect_socket', + 'Debug', + 'myprint', + 'capability', + 'logout', + 'new', + 'myprint', + 'ok', + 'connect_socket', + 'Debug', + 'myprint', + 'capability', + 'close', + 'close', + 'close', + 'logout', + 'myprint', + 'logout', + 'note', + 'tests_connect_socket' + ], + 'guess_special' => [ + 'my', + 'myprint', + 'return' + ], + 'tests_mkpath' => [ + 'note', + 'tests_mkpath', + 'ok', + 'mkpath', + 'skip', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'ok', + 'ok', + 'ok', + 'skip', + 'myprint', + 'myprint', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'myprint', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'note', + 'tests_mkpath' + ], + 'cgiload' => [ + 'under_cgi_context', + 'exit_clean' + ], + 'regextrans2' => [ + 'my', + 'option', + 'myprint', + 'exit_clean', + 'return' + ], + 'memory_consumption' => [ + 'return', + 'memory_consumption_of_pids' + ], + 'acls_sync' => [ + 'my', + 'getacl', + 'myprint', + 'getacl', + 'myprint', + 'myprint', + 'myprint', + 'setacl', + 'myprint' + ], + 'tests_foldersize' => [ + 'note', + 'tests_foldersize', + 'is', + 'foldersize', + 'is_deeply', + 'is_deeply', + 'note', + 'tests_foldersize' + ], + 'tests_mailimapclient_connect' => [ + 'note', + 'tests_mailimapclient_connect', + 'ok', + 'new', + 'is', + 'ref', + 'is', + 'connect', + 'is', + 'Server', + 'Server', + 'is', + 'Debug', + 'Debug', + 'is', + 'Port', + 'Port', + 'is', + 'Timeout', + 'Timout', + 'like', + 'ref', + 'connect', + 'like', + 'logout', + 'is', + 'ok', + 'new', + 'is', + 'Server', + 'Server', + 'is', + 'Debug', + 'Debug', + 'ok', + 'Ssl', + 'Ssl', + 'is', + 'Port', + 'Port', + 'like', + 'ref', + 'connect', + 'like', + 'logout', + 'is', + 'ok', + 'new', + 'is', + 'Server', + 'is', + 'Timeout', + 'Timout', + 'ok', + 'Ssl', + 'Ssl', + 'is', + 'Port', + 'Port', + 'hostname', + 'skip_macosx', + 'hostname', + 'skip', + 'is', + 'Debug', + 'Debug', + 'is', + 'resolv', + 'like', + 'ref', + 'connect', + 'like', + 'ref', + 'logout', + 'is', + 'note', + 'tests_mailimapclient_connect' + ], + 'clean_cache' => [ + 'myprint', + 'myprint', + 'myprint', + 'match_a_cache_file', + 'myprint', + 'exists', + 'myprint', + 'myprint', + 'myprint', + 'return' + ], + 'jux_utf8' => [ + 'imap_utf7_decode', + 'myprint', + 'return', + 'myprint', + 'return' + ], + 'single_sync' => [ + 'memory_consumption', + 'loadavg', + 'cpu_number', + 'load_and_delay', + 'join', + 'ram_memory_info', + 'get_options', + 'cgibegin', + 'output', + 'get_options', + 'docker_context', + 'cgibuildheader', + 'myprint', + 'output', + 'output_reset_with', + 'cgiload', + 'myprint', + 'imapsync_version' + ], + 'tests_cache_map' => [ + 'note', + 'tests_cache_map', + 'my', + 'ok', + 'cache_map', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'ok', + 'ok', + 'myprint', + 'note', + 'tests_cache_map' + ], + 'tests_flagscase' => [ + 'note', + 'tests_flagscase', + 'ok', + 'flagscase', + 'ok', + 'flagscase', + 'ok', + 'flagscase', + 'ok', + 'flagscase', + 'ok', + 'flagscase', + 'ok', + 'flagscase', + 'note', + 'tests_flagscase' + ], + 'imapsync_version' => [ + 'version_from_rcs', + 'return' + ], + 'tests_add_subfolder1_to_folderrec' => [ + 'note', + 'tests_add_subfolder1_to_folderrec', + 'is', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'is_deeply', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'is_deeply', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'is_deeply', + 'add_subfolder1_to_folderrec', + 'is_deeply', + 'note', + 'tests_add_subfolder1_to_folderrec' + ], + 'sort_requested_folders' => [ + 'remove_from_requested_folders', + 'remove_from_requested_folders', + 'add_to_requested_folders', + 'return' + ], + 'sleep_max_bytes' => [ + 'my', + 'return', + 'myprint', + 'return', + 'max' + ], + 'regexmess' => [ + 'myprint', + 'myprint', + 'myprint', + 'return', + 'myprint', + 'return' + ], + 'imapsync_version_public' => [ + 'imapsync_version', + 'imapsync_basename', + 'imapsync_context', + 'mysprintf', + 'new', + 'return', + 'return' + ], + 'memory_consumption_of_pids' => [ + 'myprint', + 'memory_consumption_of_pids_win32', + 'myprint', + 'backtick', + 'myprint', + 'myprint', + 'return' + ], + 'epoch' => [ + 'return', + 's', + 's', + 'myprint', + 'myprint', + 'myprint', + 'timegm', + 'myprint', + 'localtime', + 'return' + ], + 'tests_match' => [ + 'note', + 'tests_match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'is', + 'match', + 'note', + 'tests_match' + ], + 'tests_cache_dir_fix_win' => [ + 'note', + 'tests_cache_dir_fix_win', + 'ok', + 'cache_dir_fix_win', + 'ok', + 'cache_dir_fix_win', + 'note', + 'tests_cache_dir_fix_win' + ], + 'exchange1' => [ + 'output' + ], + 'is_a_release_number' => [ + 'return' + ], + 'tests_load_and_delay' => [ + 'note', + 'tests_load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'is', + 'load_and_delay', + 'note', + 'tests_load_and_delay' + ], + 'tests_rand32' => [ + 'note', + 'tests_rand32', + 'rand32', + 'myprint', + 'is', + 'length', + 'is', + 'length', + 'rand32', + 'note', + 'tests_rand32' + ], + 'office2' => [ + 'output', + 'output', + 'output', + 'output' + ], + 'mygetppid' => [ + 'return', + 'return', + 'getppid' + ], + 'msgs_from_maxmin' => [ + 'my', + 'my', + 'unless', + 'unless', + 'return' + ], + 'foldersize_diff_compute' => [ + 'myprint', + 'foldersize', + 'myprint', + 'foldersize', + 'diff', + 'diff', + 'diff' + ], + 'cgibuildheader' => [ + 'under_cgi_context', + 'cookie', + 'cookie', + 'header', + 'hostname', + 'elsif', + 'header', + 'hostname', + 'header', + 'hostname', + 'output_start' + ], + 'info_date_from_uid' => [ + 'epoch', + 'myprint' + ], + 'tests_timesince' => [ + 'note', + 'tests_timesince', + 'ok', + 'timesince', + 'ok', + 'timesince', + 'ok', + 'timesince', + 'note', + 'tests_timesince' + ], + 'tests_file_to_string' => [ + 'note', + 'tests_file_to_string', + 'is', + 'file_to_string', + 'is', + 'file_to_string', + 'is', + 'file_to_string', + 'ok', + 'file_to_string', + 'ok', + 'mkpath', + 'is', + 'string_to_file', + 'is', + 'file_to_string', + 'is', + 'string_to_file', + 'is', + 'file_to_string', + 'note', + 'tests_file_to_string' + ], + 'tests_synclabels' => [ + 'note', + 'tests_synclabels', + 'is', + 'synclabels', + 'is', + 'synclabels', + 'is', + 'synclabels', + 'require_ok', + 'new', + 'mock', + 'return', + 'mock', + 'mock', + 'Unescape', + 'new', + 'mock', + 'is', + 'synclabels', + 'is', + 'synclabels', + 'is', + 'synclabels', + 'note', + 'tests_synclabels' + ], + 'lastuid' => [ + 'recent', + 'myprint', + 'max', + 'return' + ], + 'max' => [ + 'return', + 'my', + 'is_number', + 'return' + ], + 'extract_header' => [ + 'return', + 'myprint', + 'return' + ], + 'tests_logfileprepa' => [ + 'note', + 'tests_logfileprepa', + 'is', + 'logfileprepa', + 'is', + 'logfileprepa', + 'note', + 'tests_logfileprepa' + ], + 'toggle_sleep' => [ + 'myprint', + 'defined', + 'defined', + 'max', + 'myprint' + ], + 'tests_match_a_cache_file' => [ + 'note', + 'tests_match_a_cache_file', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'ok', + 'match_a_cache_file', + 'ok', + 'ok', + 'note', + 'tests_match_a_cache_file' + ], + 'tests_killpid_by_parent' => [ + 'note', + 'tests_killpid_by_parent', + 'skip', + 'is', + 'killpid', + 'note', + 'is', + 'killpid', + 'fork', + 'ok', + 'defined', + 'ok', + 'is', + 'kill', + 'is', + 'killpid', + 'is', + 'waitpid', + 'myprint', + 'myprint', + 'mygetppid', + 'ok', + 'myprint', + 'note', + 'tests_killpid_by_parent' + ], + 'tests_add_header' => [ + 'note', + 'tests_add_header', + 'ok', + 'add_header', + 'ok', + 'add_header', + 'note', + 'tests_add_header' + ], + 'tests_guess_prefix' => [ + 'note', + 'tests_guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'is', + 'guess_prefix', + 'note', + 'tests_guess_prefix' + ], + 'tests_good_date' => [ + 'note', + 'tests_good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'ok', + 'good_date', + 'note', + 'tests_good_date' + ], + 'write_pidfile' => [ + 'myprint', + 'return', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'return', + 'myprint' + ], + 'tests_jux_utf8_old' => [ + 'note', + 'tests_jux_utf8_old', + 'is', + 'jux_utf8_old', + 'is', + 'jux_utf8_old', + 'is', + 'jux_utf8_old', + 'is', + 'jux_utf8_old', + 'is', + 'jux_utf8_old', + 'is', + 'jux_utf8_old', + 'note', + 'tests_jux_utf8_old' + ], + 'hashsynclocal' => [ + 'createhashfileifneeded', + 'firstline', + 'myprint', + 'hashsync', + 'return' + ], + 'build_automap' => [ + 'myprint', + 'return' + ], + 'tests_slash_to_underscore' => [ + 'note', + 'tests_slash_to_underscore', + 'is', + 'slash_to_underscore', + 'is', + 'slash_to_underscore', + 'is', + 'slash_to_underscore', + 'note', + 'tests_slash_to_underscore' + ], + 'tests_remove_from_requested_folders' => [ + 'note', + 'tests_remove_from_requested_folders', + 'is', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'is_deeply', + 'remove_from_requested_folders', + 'is_deeply', + 'note', + 'tests_remove_from_requested_folders' + ], + 'secondline' => [ + 'nthline' + ], + 'tests_min' => [ + 'note', + 'tests_min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'is', + 'min', + 'note', + 'tests_min' + ], + 'imap_utf7_decode' => [ + 'return', + 'decode' + ], + 'set_ssl' => [ + 'myprint', + 'Dump', + 'Ssl' + ], + 'tests_firstline' => [ + 'note', + 'tests_firstline', + 'is', + 'firstline', + 'ok', + 'mkpath', + 'is', + 'string_to_file', + 'is', + 'firstline', + 'is', + 'string_to_file', + 'is', + 'firstline', + 'is', + 'string_to_file', + 'is', + 'firstline', + 'is', + 'string_to_file', + 'is', + 'firstline', + 'note', + 'tests_firstline' + ], + 'stats' => [ + 'memory_consumption', + 'mysprintf', + 'useheader_suggestion', + 'myprint', + 'myprint', + 'myprint', + 'myprintf', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'useheader_suggestion', + 'myprint', + 'nb_messages_in_1_not_in_2', + 'nb_messages_in_2_not_in_1', + 'myprintf', + 'myprintf', + 'myprint', + 'myprint', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'myprintf', + 'myprint', + 'myprint', + 'myprintf', + 'myprint', + 'join', + 'loadavg', + 'myprintf', + 'bytes_display_string', + 'myprint', + 'diff_or_NA', + 'diff_or_NA', + 'myprintf', + 'bytes_display_string', + 'diff_or_NA', + 'diff_or_NA', + 'myprintf', + 'bytes_display_string', + 'comment_on_final_diff_in_1_not_in_2', + 'comment_on_final_diff_in_2_not_in_1', + 'myprint', + 'myprint', + 'myprint', + 'homepage' + ], + 'slash_to_underscore' => [ + 'return' + ], + 'jux_utf8_list' => [ + 'jux_utf8', + 'return' + ], + 'tests_reconnect_if_needed' => [ + 'note', + 'tests_reconnect_if_needed', + 'is', + 'reconnect_if_needed', + 'is', + 'reconnect_if_needed', + 'new', + 'Debug', + 'is', + 'reconnect_if_needed', + 'Server', + 'is', + 'reconnect_if_needed', + 'is', + 'note', + 'tests_reconnect_if_needed' + ], + 'filter_forbidden_characters' => [ + 'myprint', + 'return' + ], + 'tests_extract_header' => [ + 'note', + 'tests_extract_header', + 'ok', + 'extract_header', + 'note', + 'tests_extract_header' + ], + 'tests' => [ + 'note', + 'tests', + 'tests_folder_routines', + 'tests_compare_lists', + 'tests_regexmess', + 'tests_skipmess', + 'tests_flags_regex', + 'tests_ucsecond', + 'tests_permanentflags', + 'tests_flags_filter', + 'tests_separator_invert', + 'tests_imap2_folder_name', + 'tests_command_line_nopassword', + 'tests_good_date', + 'tests_max', + 'tests_remove_not_num', + 'tests_memory_consumption', + 'tests_is_a_release_number', + 'tests_imapsync_basename', + 'tests_list_keys_in_2_not_in_1', + 'tests_convert_sep_to_slash', + 'tests_match_a_cache_file', + 'tests_cache_map', + 'tests_get_cache', + 'tests_clean_cache', + 'tests_clean_cache_2', + 'tests_touch', + 'tests_flagscase', + 'tests_mkpath', + 'tests_extract_header', + 'tests_decompose_header', + 'tests_epoch', + 'tests_add_header', + 'tests_cache_dir_fix', + 'tests_cache_dir_fix_win', + 'tests_filter_forbidden_characters', + 'tests_cache_folder', + 'tests_time_remaining', + 'tests_decompose_regex', + 'tests_backtick', + 'tests_bytes_display_string', + 'tests_header_line_normalize', + 'tests_fix_Inbox_INBOX_mapping', + 'tests_max_line_length', + 'tests_subject', + 'tests_msgs_from_maxmin', + 'tests_tmpdir_has_colon_bug', + 'tests_sleep_max_messages', + 'tests_sleep_max_bytes', + 'tests_logfile', + 'tests_setlogfile', + 'tests_jux_utf8_old', + 'tests_jux_utf8', + 'tests_pipemess', + 'tests_jux_utf8_list', + 'tests_guess_prefix', + 'tests_guess_separator', + 'tests_format_for_imap_arg', + 'tests_imapsync_id', + 'tests_date_from_rcs', + 'tests_quota_extract_storage_limit_in_bytes', + 'tests_quota_extract_storage_current_in_bytes', + 'tests_guess_special', + 'tests_do_valid_directory', + 'tests_delete1emptyfolders', + 'tests_message_for_host2', + 'tests_length_ref', + 'tests_firstline', + 'tests_diff_or_NA', + 'tests_match_number', + 'tests_all_defined', + 'tests_special_from_folders_hash', + 'tests_notmatch', + 'tests_match', + 'tests_get_options', + 'tests_get_options_cgi_context', + 'tests_rand32', + 'tests_hashsynclocal', + 'tests_hashsync', + 'tests_output', + 'tests_output_reset_with', + 'tests_output_start', + 'tests_check_last_release', + 'tests_loadavg', + 'tests_cpu_number', + 'tests_load_and_delay', + 'tests_imapsping', + 'tests_tcpping', + 'tests_sslcheck', + 'tests_not_long_imapsync_version_public', + 'tests_reconnect_if_needed', + 'tests_reconnect_12_if_needed', + 'tests_sleep_if_needed', + 'tests_string_to_file', + 'tests_file_to_string', + 'tests_under_cgi_context', + 'tests_umask', + 'tests_umask_str', + 'tests_set_umask', + 'tests_createhashfileifneeded', + 'tests_slash_to_underscore', + 'tests_testsunit', + 'tests_count_0s', + 'tests_report_failures', + 'tests_min', + 'tests_connect_socket', + 'tests_resolvrev', + 'tests_usage', + 'tests_version_from_rcs', + 'tests_backslash_caret', + 'tests_mailimapclient_connect_bug', + 'tests_write_pidfile', + 'tests_remove_pidfile_not_running', + 'tests_match_a_pid_number', + 'tests_prefix_seperator_invertion', + 'tests_is_an_integer', + 'tests_integer_or_1', + 'tests_is_number', + 'tests_sig_install', + 'tests_template', + 'tests_split_around_equal', + 'tests_toggle_sleep', + 'tests_labels', + 'tests_synclabels', + 'tests_uidexpunge_or_expunge', + 'tests_appendlimit_from_capability', + 'tests_maxsize_setting', + 'tests_mock_capability', + 'tests_appendlimit', + 'tests_capability_of', + 'tests_search_in_array', + 'tests_operators_and_exclam_precedence', + 'tests_teelaunch', + 'tests_logfileprepa', + 'tests_useheader_suggestion', + 'tests_nb_messages_in_2_not_in_1', + 'tests_labels_add_subfolder2', + 'tests_labels_remove_subfolder1', + 'tests_resynclabels', + 'tests_labels_remove_special', + 'tests_uniq', + 'tests_remove_from_requested_folders', + 'tests_errors_log', + 'tests_add_subfolder1_to_folderrec', + 'tests_sanitize_subfolder', + 'tests_remove_edging_blanks', + 'tests_sanitize', + 'tests_remove_last_char_if_is', + 'tests_check_binary_embed_all_dyn_libs', + 'tests_nthline', + 'tests_secondline', + 'tests_tail', + 'tests_truncmess', + 'tests_eta', + 'tests_timesince', + 'tests_timenext', + 'tests_foldersize', + 'tests_imapsync_context', + 'tests_abort', + 'tests_probe_imapssl', + 'tests_mailimapclient_connect', + 'tests_resolv', + 'tests_killpid_by_parent', + 'tests_killpid_by_brother', + 'tests_kill_zero', + 'tests_always_fail', + 'done_testing', + 'note', + 'tests' + ], + 'imap_utf7_encode' => [ + 'return', + 'encode' + ], + 'appendlimit' => [ + 'appendlimit_from_capability', + 'myprint' + ], + 'integer_or_1' => [ + 'is_an_integer' + ], + 'flags_filter' => [ + 'my', + 'return' + ], + 'create_folder' => [ + 'my', + 'my', + 'IsUnconnected', + 'myprint', + 'return', + 'return', + 'create_folder_old', + 'myprint', + 'exists', + 'myprint', + 'return', + 'exists', + 'myprint', + 'return', + 'exists', + 'myprint', + 'return', + 'exists', + 'create_folder', + 'create', + 'LastError', + 'errors_incr', + 'return', + 'exists', + 'return', + 'myprint', + 'return', + 'myprint', + 'myprint', + 'return' + ], + 'sig_install_toggle_sleep' => [ + 'myprint', + 'sig_install', + 'sig_install', + 'myprint' + ], + 'tests_output_start' => [ + 'note', + 'tests_output_start', + 'is', + 'output_start', + 'is', + 'output_start', + 'is', + 'output_start', + 'is', + 'output_start', + 'is', + 'output_start', + 'is', + 'output_start', + 'note', + 'tests_output_start' + ], + 'ask_for_password' => [ + 'myprint', + 'ReadMode', + 'myprint', + 'ReadMode' + ], + 'connect_imap' => [ + 'my', + 'new', + 'set_ssl', + 'Server', + 'Port', + 'Debug', + 'Timeout', + 'myprint', + 'connect', + 'exit_clean', + 'myprint', + 'peerhost', + 'Results', + 'myprint', + 'myprint', + 'join', + 'capability', + 'set_tls', + 'starttls', + 'exit_clean', + 'myprint', + 'return' + ], + 'tests_umask' => [ + 'note', + 'tests_umask', + 'is', + 'is', + 'is', + 'oct', + 'is', + 'oct', + 'is', + 'oct', + 'oct', + 'skip', + 'is', + 'oct', + 'is', + 'oct', + 'ok', + 'is', + 'note', + 'tests_umask' + ], + 'size_filtered' => [ + 'my', + 'myprint', + 'return', + 'myprint', + 'return', + 'return' + ], + 'linelengthstuff' => [ + 'my', + 'max_line_length', + 'myprint', + 'subject', + 'myprint', + 'subject', + 'pipemess', + 'myprint', + 'myprint' + ], + 'tests_sleep_if_needed' => [ + 'note', + 'tests_sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'is', + 'sleep_if_needed', + 'note', + 'tests_sleep_if_needed' + ], + 'tests_output_reset_with' => [ + 'note', + 'tests_output_reset_with', + 'is', + 'output_reset_with', + 'is', + 'output_reset_with', + 'is', + 'output_reset_with', + 'is', + 'output_reset_with', + 'is', + 'output_reset_with', + 'note', + 'tests_output_reset_with' + ], + 'tests_header_line_normalize' => [ + 'note', + 'tests_header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'ok', + 'header_line_normalize', + 'note', + 'tests_header_line_normalize' + ], + 'tests_fix_Inbox_INBOX_mapping' => [ + 'note', + 'tests_fix_Inbox_INBOX_mapping', + 'my', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'ok', + 'fix_Inbox_INBOX_mapping', + 'note', + 'tests_fix_Inbox_INBOX_mapping' + ], + 'notmatch' => [ + 'my', + 'myprint' + ], + 'office1' => [ + 'output', + 'output' + ], + 'tests_jux_utf8_list' => [ + 'note', + 'tests_jux_utf8_list', + 'is', + 'jux_utf8_list', + 'is', + 'jux_utf8_list', + 'is', + 'jux_utf8_list', + 'is', + 'jux_utf8_list', + 'note', + 'tests_jux_utf8_list', + 'return' + ], + 'date_for_host2' => [ + 'my', + 'myprint', + 'good_date', + 'myprint', + 'get_header', + 'myprint', + 'good_date', + 'myprint', + 'return' + ], + 'tests_prefix_seperator_invertion' => [ + 'note', + 'tests_prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'is', + 'prefix_seperator_invertion', + 'note', + 'tests_prefix_seperator_invertion' + ], + 'tests_command_line_nopassword' => [ + 'note', + 'tests_command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'myprint', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'myprint', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'ok', + 'command_line_nopassword', + 'note', + 'tests_command_line_nopassword' + ], + 'tail' => [ + 'firstline', + 'secondline', + 'myprint', + 'myprint', + 'new', + 'myprint', + 'isrunning', + 'defined', + 'myprint', + 'sleep' + ], + 'header_line_normalize' => [ + 'my', + 'return' + ], + 'tests_cpu_number' => [ + 'note', + 'tests_cpu_number', + 'is', + 'is_an_integer', + 'cpu_number', + 'ok', + 'cpu_number', + 'is', + 'cpu_number', + 'is', + 'cpu_number', + 'is', + 'cpu_number', + 'is', + 'cpu_number', + 'note', + 'tests_cpu_number' + ], + 'tests_time_remaining' => [ + 'note', + 'tests_time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'is', + 'time_remaining', + 'note', + 'tests_time_remaining' + ], + 'tests_is_an_integer' => [ + 'note', + 'tests_is_an_integer', + 'is', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'ok', + 'is_an_integer', + 'note', + 'tests_is_an_integer' + ], + 'memory_consumption_ratio' => [ + 'memory_consumption', + 'return' + ], + 'get_cache' => [ + 'myprint', + 'return', + 'myprint', + 'cache_dir_fix', + 'cache_dir_fix_win', + 'myprint', + 'bsd_glob', + 'myprint', + 'myprint', + 'my', + 'cache_map', + 'clean_cache', + 'myprint', + 'return' + ], + 'tests_notmatch' => [ + 'note', + 'tests_notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'is', + 'notmatch', + 'note', + 'tests_notmatch' + ], + 'tests_bytes_display_string' => [ + 'note', + 'tests_bytes_display_string', + 'is', + 'bytes_display_string', + 'is', + 'bytes_display_string', + 'is', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'ok', + 'bytes_display_string', + 'myprint', + 'bytes_display_string', + 'note', + 'tests_bytes_display_string' + ], + 'add_to_requested_folders' => [ + 'return' + ], + 'cache_folder' => [ + 'my', + 'myprint', + 'convert_sep_to_slash', + 'convert_sep_to_slash', + 'filter_forbidden_characters', + 'myprint', + 'return' + ], + 'decompose_regex' => [ + 'my', + 'return', + 'return' + ], + 'catch_exit' => [ + 'myprint', + 'getppid', + 'myprint', + 'stats', + 'myprint', + 'getppid', + 'myprint', + 'myprint', + 'cleanup_before_exit', + 'kill', + 'exit_clean', + 'exit_clean' + ], + 'catch_print' => [ + 'myprint', + 'getppid' + ], + 'quota_extract_storage_current_in_bytes' => [ + 'myprint', + 'return' + ], + 'sleep_if_needed' => [ + 'my', + 'max', + 'timesince', + 'sleep_max_messages', + 'myprint', + 'myprint', + 'sleep_max_bytes', + 'min', + 'max', + 'mysprintf', + 'myprint' + ], + 'match_a_cache_file' => [ + 'return', + '_', + 'return' + ], + 'labels_add_subfolder2' => [ + 'uniq', + 'quotewords', + 'myprint', + 'join', + 'quotewords', + 'join', + 'quotewords', + 'join' + ], + 'pipemess' => [ + 'string_to_file', + 'file_to_string', + 'chomp', + 'file_to_string', + 'length', + 'myprint', + 'myprint', + 'myprint' + ], + 'tests_hashsynclocal' => [ + 'note', + 'tests_hashsynclocal', + 'is', + 'hashsynclocal', + 'is', + 'hashsynclocal', + 'is', + 'hashsynclocal', + 'skip', + 'is', + 'hashsynclocal', + 'ok', + 'mkpath', + 'ok', + 'ok', + 'is', + 'hashsynclocal', + 'is', + 'hashsynclocal', + 'note', + 'tests_hashsynclocal' + ], + 'killpid' => [ + 'myprint', + 'myprint', + 'kill', + 'myprint', + 'waitpid', + 'myprint', + 'waitpid', + 'kill', + 'myprint', + 'waitpid', + 'myprint', + 'kill', + 'myprint', + 'myprint' + ], + 'tests_special_from_folders_hash' => [ + 'note', + 'tests_special_from_folders_hash', + 'require_ok', + 'new', + 'is', + 'special_from_folders_hash', + 'is', + 'special_from_folders_hash', + 'is_deeply', + 'special_from_folders_hash', + 'mock', + 'return', + 'is_deeply', + 'special_from_folders_hash', + 'note', + 'tests_special_from_folders_hash', + 'return' + ], + 'min' => [ + 'return', + 'my', + 'is_number', + 'elsif', + 'return' + ], + 'debugmemory' => [ + 'return', + 'mysprintf', + 'memory_consumption' + ], + 'tests_remove_pidfile_not_running' => [ + 'note', + 'tests_remove_pidfile_not_running', + 'ok', + 'mkpath', + 'is', + 'remove_pidfile_not_running', + 'is', + 'remove_pidfile_not_running', + 'is', + 'remove_pidfile_not_running', + 'is', + 'touch', + 'is', + 'remove_pidfile_not_running', + 'is', + 'string_to_file', + 'is', + 'remove_pidfile_not_running', + 'is', + 'string_to_file', + 'is', + 'remove_pidfile_not_running', + 'is', + 'string_to_file', + 'is', + 'remove_pidfile_not_running', + 'note', + 'tests_remove_pidfile_not_running' + ], + 'tests_skipmess' => [ + 'note', + 'tests_skipmess', + 'ok', + 'not', + 'skipmess', + 'ok', + 'not', + 'skipmess', + 'ok', + 'not', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'A', + 'n', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'ok', + 'skipmess', + 'note', + 'tests_skipmess' + ], + 'tests_quota_extract_storage_limit_in_bytes' => [ + 'note', + 'tests_quota_extract_storage_limit_in_bytes', + 'ok', + 'quota_extract_storage_limit_in_bytes', + 'note', + 'tests_quota_extract_storage_limit_in_bytes' + ], + 'guess_prefix' => [ + 'INBOX', + 'return' + ], + 'copy_message' => [ + 'myprint', + 'eta', + 'size_filtered', + 'debugsleep', + 'myprint', + 'message_exists', + 'stats_update_skip_message', + 'myprint', + 'debugmemory', + 'message_for_host2', + 'myprint', + 'debugmemory', + 'myprint', + 'stats_update_skip_message', + 'linelengthstuff', + 'stats_update_skip_message', + 'date_for_host2', + 'myprint', + 'flags_for_host2', + 'myprint', + 'append_message_on_host2', + 'sync_flags_after_copy', + 'myprint', + 'debugmemory' + ], + 'foldersizes' => [ + 'myprint', + 'myprintf', + 'jux_utf8', + 'myprint', + 'myprint', + 'IsUnconnected', + 'foldersize', + 'myprintf', + 'myprintf', + 'myprintf', + 'max', + 'myprintf', + 'myprintf', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'bytes_display_string', + 'myprintf', + 'timenext', + 'return' + ], + 'tests_tcpping' => [ + 'note', + 'tests_tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'is', + 'tcpping', + 'note', + 'tests_tcpping' + ], + 'tests_mailimapclient_connect_bug' => [ + 'note', + 'tests_mailimapclient_connect_bug', + 'ok', + 'new', + 'is', + 'Server', + 'Server', + 'is', + 'Port', + 'Port', + 'hostname', + 'skip_macosx', + 'hostname', + 'skip', + 'like', + 'ref', + 'connect', + 'diag', + 'LastError', + 'is', + 'logout', + 'is', + 'note', + 'tests_mailimapclient_connect_bug' + ], + 'memory_stress' => [ + 'get', + 'myprintf', + 'memory_consumption', + 'myprintf', + 'memory_consumption' + ], + 'load_modules' => [ + 'import', + 'import' + ], + 'is_requested_folder' => [ + 'return' + ], + 'tests_live_result' => [ + 'note', + 'tests_live_result', + 'myprint', + 'myprint', + 'note', + 'tests_live_result' + ], + 'homepage' => [ + 'return' + ], + 'tests_uidexpunge_or_expunge' => [ + 'note', + 'tests_uidexpunge_or_expunge', + 'is', + 'uidexpunge_or_expunge', + 'is', + 'uidexpunge_or_expunge', + 'is', + 'uidexpunge_or_expunge', + 'is', + 'uidexpunge_or_expunge', + 'require_ok', + 'new', + 'is', + 'uidexpunge_or_expunge', + 'is', + 'uidexpunge_or_expunge', + 'mock', + 'mock', + 'is', + 'uidexpunge_or_expunge', + 'mock', + 'is', + 'uidexpunge_or_expunge', + 'mock', + 'is', + 'uidexpunge_or_expunge', + 'note', + 'tests_uidexpunge_or_expunge' + ], + 'add_header' => [ + 'return' + ], + 'tests_sleep_max_bytes' => [ + 'note', + 'tests_sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'ok', + 'sleep_max_bytes', + 'note', + 'tests_sleep_max_bytes' + ], + 'resolvrev' => [ + 'resolvrev_with_getaddrinfo', + 'return' + ], + 'tests_match_number' => [ + 'note', + 'tests_match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'is', + 'match_number', + 'note', + 'tests_match_number' + ], + 'tests_delete1emptyfolders' => [ + 'note', + 'tests_delete1emptyfolders', + 'is', + 'delete1emptyfolders', + 'is', + 'delete1emptyfolders', + 'is', + 'delete1emptyfolders', + 'require_ok', + 'new', + 'set_true', + 'is', + 'delete1emptyfolders', + 'set_false', + 'mock', + 'tests_delete1emptyfolders_unit', + 'set_true', + 'tests_delete1emptyfolders_unit', + 'set_false', + 'tests_delete1emptyfolders_unit', + 'set_true', + 'mock', + 'tests_delete1emptyfolders_unit', + 'messages', + 'mock', + 'mock', + 'tests_delete1emptyfolders_unit', + 'messages', + 'mock', + 'mock', + 'tests_delete1emptyfolders_unit', + 'messages', + 'messages', + 'mock', + 'mock', + 'tests_delete1emptyfolders_unit', + 'messages', + 'messages', + 'mock', + 'mock', + 'set_true', + 'tests_delete1emptyfolders_unit', + 'messages', + 'note', + 'tests_delete1emptyfolders' + ], + 'imap_id' => [ + 'has_capability', + 'myprint', + 'imapsync_id', + 'myprint', + 'Debug', + 'Debug', + 'tag_and_run', + 'tag_and_run', + 'myprint', + 'Debug', + 'Dump', + 'return' + ], + 'docker_context' => [ + 'under_docker_context', + 'myprint', + 'myprint' + ], + 'tests_timenext' => [ + 'note', + 'tests_timenext', + 'is', + 'timenext', + 'is', + 'timenext', + 'ok', + 'timenext', + 'ok', + 'timenext', + 'ok', + 'timenext', + 'note', + 'tests_timenext' + ], + 'probe_imapssl' => [ + 'new', + 'sysread', + 'close' + ], + 'tests_format_for_imap_arg' => [ + 'note', + 'tests_format_for_imap_arg', + 'ok', + 'format_for_imap_arg', + 'ok', + 'format_for_imap_arg', + 'ok', + 'format_for_imap_arg', + 'note', + 'tests_format_for_imap_arg' + ], + 'build_guess_special' => [ + 'guess_special', + 'myprint', + 'guess_special', + 'myprint' + ], + 'maxsize_setting' => [ + 'myprint', + 'appendlimit', + 'all_defined', + 'min', + 'myprint', + 'myprint' + ], + 'imap_utf7_decode_old' => [ + 'return', + 'utf7' + ], + 'foldersize_diff_present' => [ + 'foldersize_diff_compute', + 'sprintf', + 'sprintf', + 'myprintf', + 'jux_utf8', + 'myprintf', + 'jux_utf8', + 'myprintf' + ], + 'examine_folder_and_count' => [ + 'examine_folder', + 'count_from_select' + ], + 'imapsync_id' => [ + 'imapsync_version', + 'date_from_rcs', + 'imapsync_version', + 'date_from_rcs', + 'format_for_imap_arg', + 'myprint', + 'return' + ], + 'tests_imapsync_id' => [ + 'note', + 'tests_imapsync_id', + 'ok', + 'imapsync_id', + 'note', + 'tests_imapsync_id' + ], + 'uniq' => [ + 'push' + ], + 'tests_flags_regex' => [ + 'note', + 'tests_flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'defined', + 'q', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'ok', + 'flags_regex', + 'is', + 'flags_regex', + 'is', + 'flags_regex', + 'is', + 'flags_regex', + 'note', + 'tests_flags_regex' + ], + 'tests_usage' => [ + 'note', + 'tests_usage', + 'like', + 'usage', + 'myprint', + 'like', + 'like', + 'like', + 'is', + 'usage', + 'note', + 'tests_usage' + ], + 'localhost_info' => [ + 'my', + 'join', + 'imapsync_version', + 'hostname', + 'ram_memory_info', + 'mysprintf', + 'return' + ], + 'tests_kill_zero' => [ + 'note', + 'tests_kill_zero', + 'skip', + 'is', + 'kill', + 'is', + 'kill', + 'is', + 'kill', + 'is', + 'kill', + 'is', + 'kill', + 'is', + 'kill', + 'fork', + 'fork', + 'ok', + 'defined', + 'ok', + 'is', + 'kill', + 'is', + 'waitpid', + 'note', + 'is', + 'kill', + 'myprint', + 'wait', + 'note', + 'tests_kill_zero' + ], + 'exit_clean' => [ + 'myprint', + 'myprint', + 'cleanup_before_exit' + ], + 'nb_messages_in_1_not_in_2' => [ + 'scalar', + 'list_keys_in_2_not_in_1' + ], + 'tests_uniq' => [ + 'note', + 'tests_uniq', + 'is', + 'uniq', + 'is_deeply', + 'uniq', + 'is_deeply', + 'uniq', + 'is_deeply', + 'uniq', + 'note', + 'tests_uniq' + ], + 'file_to_array' => [ + 'my', + 'myprint', + 'return' + ], + 'tests_killpid_by_brother' => [ + 'note', + 'tests_killpid_by_brother', + 'skip', + 'myprint', + 'fork', + 'ok', + 'myprint', + 'fork', + 'myprint', + 'is', + 'killpid', + 'is', + 'waitpid', + '_1', + 'is', + 'waitpid', + '_2', + 'note', + 'tests_killpid_by_brother' + ], + 'tests_backslash_caret' => [ + 'note', + 'tests_backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'is', + 'backslash_caret', + 'note', + 'tests_backslash_caret' + ], + 'parse_header_msg' => [ + 'myprint', + 'myprint', + 'fetch', + 'myprint', + 'decompose_header', + 'myprint', + 'myprint', + 'Dump', + 'header_construct', + 'add_header', + 'myprint', + 'md5_base64', + 'myprint', + 'return' + ], + 'append_message_on_host2' => [ + 'my', + 'myprint', + 'debugmemory', + 'max', + 'append_string', + 'myprint', + 'debugmemory', + 'subject', + 'errors_incr', + 'lastuid', + 'synclabels', + 'timesince', + 'bytes_display_string', + 'eta', + 'bytes_display_string', + 'myprintf', + 'sleep_if_needed', + 'myprint', + 'touch', + 'croak', + 'delete_message_on_host1', + 'myprint', + 'return' + ], + 'loadavg_windows' => [ + 'myprint', + 'myprint', + 'file_to_string', + 'myprint', + 'n', + 'myprint' + ], + 'tests_folder_routines' => [ + 'note', + 'tests_folder_routines', + 'ok', + 'is_requested_folder', + 'ok', + 'add_to_requested_folders', + 'ok', + 'is_requested_folder', + 'ok', + 'is_requested_folder', + 'is_deeply', + 'remove_from_requested_folders', + 'ok', + 'is_requested_folder', + 'ok', + 'add_to_requested_folders', + 'ok', + 'is_requested_folder', + 'ok', + 'is_requested_folder', + 'ok', + 'remove_from_requested_folders', + 'ok', + 'is_requested_folder', + 'is_deeply', + 'remove_from_requested_folders', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'ok', + 'add_to_requested_folders', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'is_deeply', + 'sort_requested_folders', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'ok', + 'add_to_requested_folders', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'ok', + 'add_to_requested_folders', + 'qw', + 'qw', + 'ok', + 'compare_lists', + 'sort_requested_folders', + 'note', + 'tests_folder_routines' + ], + 'tests_probe_imapssl' => [ + 'note', + 'tests_probe_imapssl', + 'is', + 'probe_imapssl', + 'is', + 'probe_imapssl', + 'note', + 'hostname', + 'hostname', + 'skip_macosx', + 'hostname', + 'skip', + 'like', + 'probe_imapssl', + 'ok', + 'resolv', + 'like', + 'probe_imapssl', + 'like', + 'probe_imapssl', + 'note', + 'tests_probe_imapssl' + ], + 'comment_on_final_diff_in_2_not_in_1' => [ + 'scalar', + 'nb_messages_in_2_not_in_1', + 'myprint', + 'myprint' + ], + 'list_keys_in_2_not_in_1' => [ + 'return' + ], + 'tests_msgs_from_maxmin' => [ + 'note', + 'tests_msgs_from_maxmin', + 'msgs_from_maxmin', + 'ok', + 'compare_lists', + 'msgs_from_maxmin', + 'ok', + 'compare_lists', + 'msgs_from_maxmin', + 'ok', + 'compare_lists', + 'msgs_from_maxmin', + 'ok', + 'compare_lists', + 'note', + 'tests_msgs_from_maxmin' + ], + 'memory_consumption_of_pids_win32' => [ + 'backtick', + 'myprint', + 'my', + 'myprint', + 'remove_qq', + 'myprint', + 'remove_qq', + 'remove_Ko', + 'remove_not_num', + 'myprint', + 'return' + ], + 'add_subfolder1_to_folderrec' => [ + 'myprint', + 'myprint', + 'myprint' + ], + 'tests_is_number' => [ + 'note', + 'tests_is_number', + 'is', + 'is_number', + 'is', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'ok', + 'is_number', + 'note', + 'tests_is_number' + ], + 'labels' => [ + 'all_defined', + 'fetch_hash', + 'Unescape' + ], + 'tests_million_folders_baby_2' => [ + 'note', + 'tests_million_folders_baby_2', + 'myprint', + 'myprint', + 'ok', + 'myprint', + 'note', + 'tests_million_folders_baby_2' + ], + 'testsunit' => [ + 'myprint', + 'myprint', + 'myprint', + 'myprint', + 'function_ref' + ], + 'resynclabels' => [ + 'my', + 'all_defined', + 'labels_remove_subfolder1', + 'labels_add_subfolder2', + 'labels_remove_special', + 'myprint', + 'myprint', + 'myprint', + 'Debug', + 'store', + 'Debug' + ], + 'select_msgs_by_search' => [ + 'messages', + 'myprint', + 'messages', + 'messages', + 'return', + 'search', + 'return', + 'select_msgs_by_age', + 'return' + ], + 'tests_nthline' => [ + 'note', + 'tests_nthline', + 'is', + 'nthline', + 'is', + 'nthline', + 'ok', + 'mkpath', + 'is', + 'string_to_file', + 'is', + 'nthline', + 'note', + 'tests_nthline' + ], + 'tests_labels_remove_special' => [ + 'note', + 'tests_labels_remove_special', + 'is', + 'labels_remove_special', + 'is', + 'labels_remove_special', + 'is', + 'labels_remove_special', + 'is', + 'labels_remove_special', + 'is', + 'labels_remove_special', + 'is', + 'labels_remove_special', + 'note', + 'tests_labels_remove_special' + ], + 'init_imap' => [ + 'my', + 'new', + 'Debug_fh', + 'set_ssl', + 'set_tls', + 'connect', + 'Clear', + 'Server', + 'Port', + 'Fast_io', + 'Buffer', + 'Uid', + 'Peek', + 'Debug', + 'Showcredentials', + 'Timeout', + 'Reconnectretry', + 'Ignoresizeerrors', + 'Maxcommandlength', + 'return' + ], + 'tests_date_from_rcs' => [ + 'note', + 'tests_date_from_rcs', + 'ok', + 'date_from_rcs', + 'note', + 'tests_date_from_rcs' + ], + 'tests_remove_last_char_if_is' => [ + 'note', + 'tests_remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'is', + 'remove_last_char_if_is', + 'note', + 'tests_remove_last_char_if_is' + ], + 'tests_get_cache' => [ + 'note', + 'tests_get_cache', + 'ok', + 'not', + 'get_cache', + 'ok', + 'rmtree', + 'ok', + 'mkpath', + 'qw', + 'ok', + 'touch', + 'my', + 'ok', + 'get_cache', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'touch', + 'ok', + 'get_cache', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'rmtree', + 'ok', + 'mkpath', + 'qw', + 'ok', + 'touch', + 'ok', + 'get_cache', + 'ok', + 'compare_lists', + 'ok', + 'compare_lists', + 'ok', + 'ok', + 'ok', + 'ok', + 'note', + 'tests_get_cache' + ], + 'errors_incr' => [ + 'errors_log', + 'myprint', + 'myprint', + 'myprint', + 'errorsdump', + 'errors_log', + 'errorsdump', + 'myprint', + 'exit_clean' + ], + 'quota_extract_storage_limit_in_bytes' => [ + 'myprint', + 'return' + ], + 'imapsync_context' => [ + 'under_docker_context', + 'under_cgi_context', + 'under_docker_context', + 'under_cgi_context' + ], + 'easyany' => [ + 'myprint', + 'gmail12', + 'myprint', + 'gmail1', + 'myprint', + 'gmail2', + 'office1', + 'office2', + 'exchange1', + 'exchange2', + 'domino1', + 'domino2' + ], + 'tests_jux_utf8' => [ + 'note', + 'tests_jux_utf8', + 'encoding', + 'encoding', + 'ok', + 'find_encoding', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'encode', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'is', + 'jux_utf8', + 'note', + 'tests_jux_utf8' + ], + 'nb_messages_in_2_not_in_1' => [ + 'scalar', + 'list_keys_in_2_not_in_1' + ], + 'tests_template' => [ + 'note', + 'tests_template', + 'is', + 'is_deeply', + 'is_deeply', + 'note', + 'tests_template' + ], + 'remove_Ko' => [ + 'return', + 'return' + ], + 'loadavg_freebsd' => [ + 'myprint', + 'myprint', + 'firstline', + 'myprint' + ], + 'count_from_select' => [ + 'myprint', + 'return', + 'return' + ], + 'tests_teelaunch' => [ + 'note', + 'tests_teelaunch', + 'is', + 'teelaunch', + 'is', + 'teelaunch', + 'is', + 'teelaunch', + 'isa_ok', + 'teelaunch', + 'is', + 'print', + 'is', + 'file_to_string', + 'is', + 'print', + 'is', + 'file_to_string', + 'note', + 'tests_teelaunch' + ], + 'tests_not_long_imapsync_version_public' => [ + 'note', + 'tests_not_long_imapsync_version_public', + 'is', + 'is_a_release_number', + 'not_long_imapsync_version_public', + 'note', + 'tests_not_long_imapsync_version_public' + ], + 'imap_utf7_encode_old' => [ + 'utf8', + 'return' + ], + 'tests_max_line_length' => [ + 'note', + 'tests_max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'ok', + 'max_line_length', + 'note', + 'tests_max_line_length' + ], + 'total_bytes_max_reached' => [ + 'return', + 'myprint', + 'return' + ], + 'mypod2usage' => [ + 'pod2usage', + 'qw' + ], + 'version_from_rcs' => [ + 'return' + ], + 'labels_remove_special' => [ + 'quotewords', + 'myprint', + 'join' + ], + 'foldersizes_diff_list' => [ + 'imap2_folder_name', + 'foldersize_diff_present' + ], + 'tests_integer_or_1' => [ + 'note', + 'tests_integer_or_1', + 'is', + 'integer_or_1', + 'is', + 'integer_or_1', + 'is', + 'integer_or_1', + 'is', + 'integer_or_1', + 'is', + 'integer_or_1', + 'note', + 'tests_integer_or_1' + ], + 'decompose_header{' => [ + 'myprint', + 's', + 'myprint', + 'elsif', + 'myprint', + 'myprint', + 'Dump', + 'return' + ], + 'tests_reconnect_12_if_needed' => [ + 'note', + 'tests_reconnect_12_if_needed', + 'new', + 'new', + 'Server', + 'Server', + 'is', + 'reconnect_12_if_needed', + 'is', + 'is', + 'note', + 'tests_reconnect_12_if_needed' + ], + 'tests_separator_invert' => [ + 'note', + 'tests_separator_invert', + 'ok', + 'not', + 'separator_invert', + 'ok', + 'not', + 'separator_invert', + 'ok', + 'not', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'ok', + 'separator_invert', + 'note', + 'tests_separator_invert' + ], + 'tests_resolvrev' => [ + 'note', + 'tests_resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'is', + 'resolvrev', + 'note', + 'tests_resolvrev' + ], + 'not_long_imapsync_version_public' => [ + 'myprint', + 'sigaction', + 'new', + 'myprint', + 'imapsync_version_public', + 'myprint', + 'myprint', + 'myprint', + 'return', + 'return', + 'return' + ], + 'ram_memory_info' => [ + 'return', + 'sprintf', + 'get', + 'get' + ], + 'foldersizes_at_the_beggining_old' => [ + 'myprint', + 'foldersizes', + 'foldersizes', + 'all_defined', + 'errors_incr', + 'mysprintf', + 'errors_incr' + ], + 'imapsping' => [ + 'tcpping' + ], + 'foldersizes_at_the_beggining' => [ + 'myprint', + 'foldersizes_diff_list', + 'foldersizes_total', + 'all_defined', + 'errors_incr', + 'mysprintf', + 'errors_incr' + ], + 'convert_sep_to_slash' => [ + 'return' + ], + 'tests_touch' => [ + 'note', + 'tests_touch', + 'ok', + 'mkpath', + 'ok', + 'touch', + 'ok', + 'touch', + 'ok', + 'touch', + 'ok', + 'touch', + 'ok', + 'touch', + 'note', + 'tests_touch' + ], + 'eta_print' => [ + 'eta', + 'myprint' + ], + 'tests_count_0s' => [ + 'note', + 'tests_count_zeros', + 'is', + 'count_0s', + 'is', + 'count_0s', + 'is', + 'count_0s', + 'is', + 'count_0s', + 'is', + 'count_0s', + 'note', + 'tests_count_zeros' + ], + 'output' => [ + 'join' + ], + 'sslcheck' => [ + 'myprint', + 'myprint', + 'probe_imapssl', + 'myprint', + 'myprint', + 'myprint', + 'probe_imapssl', + 'myprint', + 'myprint' + ], + 'tests_resynclabels' => [ + 'note', + 'tests_resynclabels', + 'is', + 'resynclabels', + 'is', + 'resynclabels', + 'is', + 'resynclabels', + 'is', + 'resynclabels', + 'require_ok', + 'new', + 'mock', + 'mock', + 'is', + 'resynclabels', + 'note', + 'tests_resynclabels' + ], + 'date_from_rcs' => [ + 'qw', + 's', + 'myprint', + 'myprint', + 'myprint', + 'return' + ], + 'resolv_with_getaddrinfo' => [ + 'getaddrinfo', + 'myprint', + 'while', + 'getnameinfo', + 'NI_NUMERICHOST', + 'NIx_NOSERV', + 'myprint', + 'myprint', + 'getnameinfo', + 'NIx_NOSERV', + 'myprint' + ], + 'cache_dir_fix_win' => [ + 'myprint', + 'return' + ], + 'tests_filter_forbidden_characters' => [ + 'note', + 'tests_filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'skip', + 'ok', + 'filter_forbidden_characters', + 'skip', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'ok', + 'filter_forbidden_characters', + 'note', + 'tests_filter_forbidden_characters' + ], + 'tests_under_cgi_context' => [ + 'note', + 'tests_under_cgi_context', + 'is', + 'under_cgi_context', + 'is', + 'under_cgi_context', + 'is', + 'under_cgi_context', + 'is', + 'under_cgi_context', + 'note', + 'tests_under_cgi_context' + ], + 'command_line_nopassword' => [ + 'mask_password_value', + 'return', + 'return', + 'mask_password_value', + 'return' + ], + 'remove_not_num' => [ + 'myprint', + 'return' + ], + 'testsdebug' => [ + 'note', + 'testsdebug', + 'ok', + 'rmtree', + 'tests_check_binary_embed_all_dyn_libs', + 'tests_killpid_by_parent', + 'tests_killpid_by_brother', + 'tests_kill_zero', + 'tests_connect_socket', + 'tests_probe_imapssl', + 'tests_always_fail', + 'note', + 'testsdebug', + 'done_testing' + ], + 'tests_decompose_header{' => [ + 'note', + 'tests_decompose_header', + 'decompose_header', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'ok', + 'not', + 'decompose_header', + 'ok', + 'decompose_header', + 'ok', + 'decompose_header', + 'ok', + 'ok', + 'decompose_header', + 'ok', + 'ok', + 'ok', + 'note', + 'tests_decompose_header' + ], + 'tests_sleep_max_messages' => [ + 'note', + 'tests_sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'ok', + 'sleep_max_messages', + 'note', + 'tests_sleep_max_messages' + ], + 'exchange2' => [ + 'output', + 'output', + 'output' + ], + 'delete_message_on_host1' => [ + 'my', + 'delete_messages_on_any' + ], + 'set_regextrans2_for_subfolder2' => [ + 'q', + 'q', + 'q', + 'myprint' + ], + 'select_folder' => [ + 'select', + 'errors_incr', + 'return', + 'return' + ] + }; diff --git a/W/imapsync_functions_tree_ppi.txt b/W/imapsync_functions_tree_ppi.txt new file mode 100644 index 0000000..84506d7 --- /dev/null +++ b/W/imapsync_functions_tree_ppi.txt @@ -0,0 +1,5956 @@ +single_sync + timestart + rcs + memory_consumption_at_start + cpu_number + loaddelay + cpu_number + loadavg + total_bytes_transferred + total_bytes_skipped + nb_msg_transferred + nb_msg_skipped + h1_nb_msg_deleted + h1_nb_msg_noheader + h1_nb_msg_start + h1_bytes_start + h2_nb_msg_start + h2_bytes_start + h1_nb_msg_processed + h2_nb_msg_crossdup + nb_errors + Jan + cgibegin + docker_context + cgibuildheader + myprint + output + output_reset_with + exit + releasecheck + releasecheck + releasecheck + version + myprint + imapsync_version + exit + debugenv + load_modules + after_get_options + cgisetcontext + easyany + sanitize + sanitize + sanitize + sanitize + tmpdir + testsexit + testslive_init + testslive + testslive6_init + testslive6 + pidfile + install_signals + log + log + log + errorsdump + errorsdump + errorsdump + errorsmax + errorsmax + errorsmax + binmode + log + setlogfile + teelaunch + tee + tee + IO + timestart + timebefore + timestart + timestart + myprint + localhost_info + myprint + myprint + myprint + log + myprint + join + myprint + myprint + myprint + releasecheck + checkselectable + checkselectable + checkselectable + checkfoldersexist + checkfoldersexist + checkfoldersexist + expungeaftereach + expungeaftereach + expungeaftereach + abletosearch + abletosearch + abletosearch + abletosearch1 + abletosearch1 + abletosearch1 + abletosearch + abletosearch2 + abletosearch2 + abletosearch2 + abletosearch + abletosearch1 + showpasswords + showpasswords + showpasswords + fixslash2 + fixslash2 + fixslash2 + automap + automap + automap + delete2duplicates + delete2 + delete2duplicates + maxmessagespersecond + maxmessagespersecond + maxmessagespersecond + maxbytespersecond + maxbytespersecond + maxbytespersecond + sslcheck + sslcheck + sslcheck + myprint + banner_imapsync + myprint + myprint + output + output_reset_with + do_valid_directory + tmpdir + remove_pidfile_not_running + pidfile + tail + nb_errors + exit_clean + exit + myprint + exit + abort + abort + simulong + simulong + simulong + cgiload + tmpdir_fix_colon_bug + check_lib_version + exit_clean + resyncflags + resyncflags + resyncflags + resyncflags + myprint + myprint + sslcheck + port1 + ssl1 + port2 + ssl2 + debug + defined + defined + Readonly + Readonly + IO::Socket::SSL::SSL_VERIFY_NONE + debugssl + debugssl + ssl1 + ssl2 + tls1 + tls2 + myprint + ssl1 + myprint + myprint + ssl2 + myprint + myprint + id + id + id + justconnect + user1 + user2 + host1 + host2 + myprint + debugmemory + exit_clean + delete1 + expunge1 + myprint + expunge1 + myprint + uidexpunge2 + myprint + nb_errors + exit_clean + delete2 + delete2duplicates + uidexpunge2 + Mail::IMAPClient + myprint + uidexpunge2 + expunge2 + myprint + expunge2 + delete1 + delete2 + myprint + nb_errors + exit_clean + myprint + myprint + myprint + defined + defined + defined + defined + missing_option + defined + missing_option + myprint + myprint + h1 + timeout + h1 + timeout + h1 + timeout + myprint + h2 + timeout + h2 + timeout + h2 + timeout + myprint + justfolders + foldersizes + justfoldersizes + foldersizes + foldersizesatend + foldersizes + defined + foldersizes + foldersizes + foldersizesatend + defined + foldersizesatend + foldersizesatend + foldersizes + uc + myprint + myprint + get_password1 + get_password2 + dry_message + dry + dry_message + search1 + search2 + push + defined + myprint + defined + nb_errors + exit_clean + myprint + myprint + defined + nb_errors + exit_clean + myprint + myprint + defined + exit_clean + myprint + myprint + defined + nb_errors + exit_clean + myprint + myprint + defined + nb_errors + exit_clean + myprint + imap1 + host1 + port1 + user1 + password1 + h1 + timeout + ssl1 + tls1 + h1 + imap2 + host2 + port2 + user2 + password2 + h2 + timeout + ssl2 + tls2 + h2 + debug + imap1 + debug + imap2 + imap1 + nb_errors + exit_clean + myprint + imap2 + nb_errors + exit_clean + myprint + myprint + imap1 + myprint + imap2 + imap_id_stuff + quota + imap2 + maxsize_setting + justlogin + imap1 + imap2 + exit_clean + imap1 + imap2 + myprint + myprint + h2_folders_all_UPPER + uc + h1_folders_all + h2_folders_all + private_folders_separators_and_prefixes + imap1 + imap2 + defined + subfolder1 + subfolder1 + defined + subfolder2 + subfolder2 + push + regextrans2 + folder + folder + folder + folder + add_to_requested_folders + folder + add_to_requested_folders + keys + scalar + add_to_requested_folders + imap1 + myprint + add_to_requested_folders + scalar + add_to_requested_folders + myprint + scalar + remove_from_requested_folders + myprint + h1_folders_wanted + sort_requested_folders + checkfoldersexist + myprint + h1_folders_wanted + debug + debugfolders + myprint + push + h1_folders_wanted + myprint + checkselectable + myprint + h1_folders_wanted + debug + debugfolders + imap1 + myprint + push + h1_folders_wanted + debug + debugfolders + myprint + f1f2h + f1f2 + automap + h1_folders_wanted + subfolder2 + h2_prefix + subfolder2 + subfolder2 + myprint + myprint + imap1 + imap2 + myprint + sort + myprint + keys + f1f2auto + myprint + keys + f1f2auto + f1f2auto + myprintf + myprint + keys + f1f2h + myprint + keys + f1f2h + f1f2h + myprintf + myprint + exit_clean + justfolderlists + exit_clean + justautomap + debugsleep + skipemptyfolders + myprint + foldersizes + foldersizes_at_the_beggining + justfoldersizes + exit_clean + stats + delete1emptyfolders + delete1emptyfolders + delete_folders_in_2_not_in_1 + h1_folders_wanted + myprint + begin_transfer_time + h2_folders_of_md5 + h1_folders_wanted + h1_current_folder + eta_print + h2_current_folder + myprintf + myprint + debugmemory + select_folder + imap1 + debugsleep + imap1 + myprint + skipemptyfolders + myprint + skipemptyfolders + imap1 + search1 + myprint + create_folder + imap2 + acls_sync + select_folder + imap2 + create_folder + imap2 + imap2 + imap2 + myprint + myprint + expunge1 + myprint + dry + imap1 + myprint + dry + imap2 + justfolders + imap1 + search1 + abletosearch1 + myprint + debug + debug + imap2 + search2 + abletosearch2 + myprint + debug + debug + imap1 + imap2 + myprint + mkpath + myprint + debug + delete + delete + debug + imap1 + debug + undef + debug + synclabels + resynclabels + push + abletosearch1 + imap1 + imap1 + imap1 + debug + imap1 + errors_incr + imap1 + myprint + total_bytes_skipped + nb_msg_skipped + h1_nb_msg_noheader + h1_nb_msg_processed + push + nb_msg_skipped + h1_nb_msg_processed + myprint + debug + debug + imap2 + debug + debug + synclabels + resynclabels + push + abletosearch2 + imap2 + imap2 + imap2 + debug + imap2 + myprint + push + keys + h2_folders_of_md5 + keys + h1_folders_of_md5 + myprint + debug + debug + delete2duplicates + myprint + push + uidexpunge2 + dry + imap2 + expunge2 + myprint + imap2 + dry + expunge2 + myprint + imap2 + dry + delete2 + myprint + push + uidexpunge2 + dry + imap2 + myprint + push + uidexpunge2 + dry + imap2 + expunge2 + myprint + imap2 + dry + expunge2 + myprint + imap2 + dry + delete2 + myprint + debug + debug + myprint + myprint + myprint + keys + debug + exists + debug + myprint + push + uidexpunge2 + dry + imap2 + expunge2 + myprint + imap2 + dry + expunge2 + myprint + imap2 + dry + imap2 + debug + exists + h2_folders_of_md5 + delete1 + expungeaftereach + push + dry + h2_folders_of_md5 + delete2 + exists + myprint + total_bytes_max_reached + exists + debug + touch + exists + h2_folders_of_md5 + h2_folders_of_md5 + debug + h2_nb_msg_crossdup + total_bytes_skipped + nb_msg_skipped + h1_nb_msg_processed + exists + resyncflags + sync_flags_fir + debug + resynclabels + resynclabels + delete1 + push + delete_message_on_host1 + expunge1 + delete1 + resyncflags + sync_flags_fir + total_bytes_skipped + nb_msg_skipped + h1_nb_msg_processed + sort + debug + delete2 + myprint + expunge1 + myprint + dry + imap1 + expunge2 + myprint + dry + imap2 + debug + eta_print + myprint + delete1 + delete1emptyfolders + delete1emptyfolders + debug + debugfolders + foldersizesatend + myprint + foldersizesatend + imap1 + imap1 + imap2 + imap2 + stats + myprint + errorsdump + nb_errors + errorsdump + tests_live_result + nb_errors + testslive + testslive6 + nb_errors + exit_clean + exit_clean +myprint + print + tee +myprintf + printf + tee +mysprintf +output_start + output + output + output +tests_output_start + note + is + undef + is + is + is + is + is + note +tests_output + note + is + undef + is + is + is + is + is + note +output + output + output +tests_output_reset_with + note + is + undef + is + is + is + is + note +output_reset_with + output + output +pidfile + pidfilelocking + pidfilelocking + pidfilelocking + host1 + user1 + host2 + user2 + slash_to_underscore + slash_to_underscore + pidfile + pidfile + pidfile + tmpdir +tests_kill_zero + note + skip + is + is + is + is + is + is + sleep + exit + ok + defined + ok + is + is + note + is + myprint + exit + wait + note +tests_killpid_by_parent + note + skip + is + undef + note + is + undef + myprint + exit + ok + defined + ok + is + is + is + myprint + sleep + myprint + sleep + ok + myprint + exit + note +tests_killpid_by_brother + note + skip + myprint + exit + myprint + sleep + sleep + ok + myprint + exit + myprint + is + sleep + exit + is + is + note +killpid + myprint + myprint + kill + myprint + kill + sleep + waitpid + myprint + kill + myprint + kill + sleep + waitpid + myprint + kill + myprint + myprint +tests_abort + note + is + undef + note +abort + pidfile + myprint + exit + pidfile + myprint + exit + killpid + exit +under_docker_context +docker_context + debug + pidfile + log + debug + chdir +cgibegin + CGI + CGI::Carp + cgi +tests_under_cgi_context + note + do + delete + SERVER_SOFTWARE + is + undef + do + SERVER_SOFTWARE + is + do + delete + SERVER_SOFTWARE + is + undef + do + SERVER_SOFTWARE + is + note +under_cgi_context + SERVER_SOFTWARE +cgibuildheader + cgi + cgi + -name + abort + cgi + -type + loaddelay + cgi + -type + cgi + -type + output_start +cgiload + abort + loaddelay + nb_errors + exit_clean +tests_set_umask + note + is + undef + is + umask + note +set_umask + output +tests_umask_str + note + is + umask_str + is + is + is + is + is + is + is + is + skip + is + is + is + is + is + is + is + is + is + is + note +umask_str + defined + umask + sprintf +tests_umask + note + is + umask + is + is + is + oct + is + oct + skip + is + oct + is + oct + ok + defined + is + note +cgisetcontext + output + set_umask + regextrans2 + pidfilelocking + errorsmax + releasecheck + releasecheck + releasecheck + showpasswords + hashfile + tmpdir + chdir + cgioutputenvcontext + debug + debug + debug + skipemptyfolders + skipemptyfolders + skipemptyfolders + maxsize + maxsize + maxsize + tail + tail + tail + addheader + addheader + addheader +cgioutputenvcontext + output +debugsleep + defined + debugsleep + myprint + sleep + debugsleep +tests_foldersize + note + is + undef + note +foldersize + errors_incr + undef + errors_incr + errors_incr + keys +foldersizes + myprint + myprintf + h2_folders_all_UPPER + uc + myprint + myprint + myprintf + myprintf + myprintf + myprintf + myprintf + myprintf + myprintf + myprintf +foldersize_diff_present + folder1 + size + foldersize_diff_compute + defined + folder1 + size + folder1 + size + folder1 + nb_msgs + folder1 + biggest + defined + folder2 + size + folder2 + size + folder2 + nb_msgs + folder2 + biggest + myprintf + myprintf + myprintf + folder1 + size_diff + folder1 + nb_msgs_diff + folder1 + biggest_diff +foldersize_diff_compute + exists + folder1 + size + imap1 + search1 + abletosearch1 + folder1 + size + folder1 + nb_msgs + folder1 + biggest + folder1 + size + folder1 + nb_msgs + folder1 + biggest + exists + h2_folders_all_UPPER + uc + folder2 + size + imap2 + search2 + abletosearch2 + folder2 + size + folder2 + nb_msgs + folder2 + biggest + folder2 + size + folder2 + nb_msgs + folder2 + biggest + folder1 + size_diff + folder1 + nb_msgs_diff + folder1 + biggest_diff + folder2 + size_diff + folder2 + nb_msgs_diff + folder2 + biggest_diff +diff +add +foldersizes_diff_list + h1_folders_wanted + foldersize_diff_present +foldersizes_total + h1_folders_wanted + folder1 + size + folder1 + nb_msgs + folder1 + biggest + folder2 + size + folder2 + nb_msgs + folder2 + biggest + myprintf + myprintf + myprint + myprintf + myprintf + myprint + myprintf + myprintf + myprint + myprintf + myprintf + myprint + myprintf +foldersizesatend_old + timenext + imap1 + imap2 + imap2 + h2_folders_all_UPPER + uc + imap1 + search1 + abletosearch1 + h1_folders_wanted + imap2 + search2 + abletosearch2 + errors_incr +foldersizesatend + timenext + imap1 + imap2 + imap2 + h2_folders_all_UPPER + uc + foldersizes_diff_list + errors_incr +foldersizes_at_the_beggining + myprint + foldersizes_diff_list + h1_nb_msg_start + h1_bytes_start + h2_nb_msg_start + h2_bytes_start + h1_nb_msg_start + h1_bytes_start + h2_nb_msg_start + h2_bytes_start + errors_incr + foldersizes + foldersizesatend + h2 + quota_limit_bytes + h1_bytes_start + h1_bytes_start + errors_incr +foldersizes_at_the_beggining_old + myprint + h1_nb_msg_start + h1_bytes_start + imap1 + search1 + abletosearch1 + h1_folders_wanted + h2_nb_msg_start + h2_bytes_start + imap2 + search2 + abletosearch2 + h1_nb_msg_start + h1_bytes_start + h2_nb_msg_start + h2_bytes_start + errors_incr + foldersizes + foldersizesatend + h2 + quota_limit_bytes + h1_bytes_start + h1_bytes_start + errors_incr +total_bytes_max_reached + exitwhenover + total_bytes_transferred + exitwhenover + myprint +tests_mock_capability + note + ok + ok + is + undef + ok + mock_capability + is + undef + ok + mock_capability + is + ok + mock_capability + is + ok + mock_capability + is_deeply + ok + mock_capability + is_deeply + ok + mock_capability + is_deeply + ok + mock_capability + is_deeply + note +sig_install_toggle_sleep + sig_install +mock_capability + require_ok +tests_capability_of + note + is + undef + is + undef + is + undef + is + note +capability_of +tests_search_in_array + note + is + undef + is + is + is + note +search_in_array +tests_appendlimit_from_capability + note + is + undef + is + undef + is + is + undef + note +appendlimit_from_capability + myprint + is_an_integer +tests_appendlimit + note + is + undef + is + undef + imap2 + is + note +appendlimit + imap2 + defined + myprint +tests_maxsize_setting + note + is + undef + is + undef + maxsize + is + imap2 + is + is + maxsize + appendlimit + is + is + maxsize + imap2 + maxsize + is + imap2 + maxsize + is + note +maxsize_setting + defined + appendlimit + myprint + appendlimit + all_defined + appendlimit + maxsize + maxsize + appendlimit + myprint + maxsize + maxsize + defined + appendlimit + myprint + maxsize + appendlimit + maxsize + defined + maxsize + maxsize +all_defined +tests_all_defined + note + is + is + is + undef + is + undef + is + is + undef + is + is + note +tests_hashsynclocal + note + host1 + is + undef + hashfile + is + undef + hashfile + is + undef + skip + hashfile + is + undef + ok + hashfile + ok + ok + is + is + note +hashsynclocal + hashfile + myprint +tests_hashsync + note + is + host1 + is + is + host2 + is + is + note +hashsync + host1 + user1 + password1 + host2 + user2 + password2 +tests_createhashfileifneeded + note + is + undef + note +createhashfileifneeded + open + myprint + myprint + print + close +tests_rand32 + note + myprint + is + is + rand32 + note +rand32 + rand +imap_id_stuff + id + h1_imap_id + imap1 + h2_imap_id + imap2 +imap_id + id + myprint + side + myprint + myprint +imapsync_id + name + name +tests_imapsync_id + note + ok + version + note +format_for_imap_arg + push + delete + push + join +tests_format_for_imap_arg + note + ok + ok + name + ok + name + note +quota + h1 + myprint + myprint + quota_limit_bytes + quota_current_bytes + myprint + errors_incr +tests_quota_extract_storage_limit_in_bytes + note + ok + note +quota_extract_storage_limit_in_bytes + map + debug +tests_quota_extract_storage_current_in_bytes + note + ok + note +quota_extract_storage_current_in_bytes + map + debug +automap + automap + myprint + myprint + h1_special + imap1 + h2_special + imap2 + build_possible_special + build_guess_special + build_automap +build_guess_special + sort + h1_folders_all + possible_special + h1_prefix + h1_special_guessed + h1_special_guessed + myprint + h1_special_guessed + sort + h2_folders_all + possible_special + h2_prefix + h2_special_guessed + h2_special_guessed + myprint + h2_special_guessed +guess_special +tests_guess_special + note + ok + ok + ok + ok + note +build_automap + debug + h1_folders_wanted + h1_special + h1_special_guessed + h2_special + h2_special + f1f2auto + h2_special + exists + h2_special_guessed + h2_special_guessed + f1f2auto + exists + h2_special + h2_special + f1f2auto + h2_special + exists + h2_special_guessed + h2_special_guessed + f1f2auto + f1f2auto +build_possible_special + possible_special + debug + Data::Dumper +tests_special_from_folders_hash + note + require_ok + is + undef + is + undef + is_deeply + name + is_deeply + Sent + note +special_from_folders_hash + errors_incr + attrs + exists + myprintf + name + myprintf + name + name + name + myprint +errors_incr + nb_errors + errors_log + myprint + errorsmax + nb_errors + errorsmax + myprint + errorsdump + myprint + errorsdump + nb_errors + myprint + exit_clean +tests_errors_log + note + is + undef + is + undef + is_deeply + errors_log + is_deeply + errors_log + is_deeply + errors_log + is_deeply + errors_log + note +errors_log + errors_log + errors_log + push + errors_log + errors_log + errors_log +errorsdump +tests_live_result + note + myprint + myprint + note +size_filtered_flag + defined + maxsize + maxsize + defined +sync_flags_fir + sync_flags +sync_flags_after_copy + imap2 + debug + sync_flags + myprint +sync_flags + debug + debug + debug + dry + imap2 + imap2 + errors_incr +_filter + debug +lost_connection + nb_errors + myprint + debug + myprint +tests_max + note + is + is + is + is + undef + is + undef + undef + is + undef + undef + is + is + is + is + is + is + is + is + is + is + is + is + undef + is + undef + is + is + is + is + note +max + undef + is_number + push + defined + push + pop +tests_is_number + note + is + undef + is + undef + undef + ok + is_number + ok + is_number + ok + is_number + ok + is_number + ok + ok + ok + ok + ok + ok + note +is_number +tests_min + note + is + is + is + is + undef + is + is + is + is + is + is + is + is + undef + is + undef + is + undef + is + undef + is + is + is + is + note +min + undef + is_number + push + push + shift +check_lib_version + debug + myprint +module_version_str +modulesversion + sort + eval + push +tests_command_line_nopassword + note + ok + ok + ok + ok + showpasswords + ok + ok + ok + ok + note +command_line_nopassword + cmdcgi + cmdcgi + showpasswords +mask_password_value + shift + push + push +tests_get_stdin_masked + note + is + is + note +get_stdin_masked + -prompt +ask_for_password_new +ask_for_password + myprint + Term::ReadKey::ReadMode + chomp + myprint + Term::ReadKey::ReadMode +get_password1 + password1 + passfile1 + IMAPSYNC_PASSWORD1 + myprint + user1 + host1 + password1 + defined + passfile1 + passfile1 + myprint + nb_errors + exit_clean + password1 + passfile1 + IMAPSYNC_PASSWORD1 + password1 + IMAPSYNC_PASSWORD1 +get_password2 + password2 + passfile2 + IMAPSYNC_PASSWORD2 + myprint + user2 + host2 + password2 + defined + passfile2 + passfile2 + myprint + nb_errors + exit_clean + password2 + passfile2 + IMAPSYNC_PASSWORD2 + password2 + IMAPSYNC_PASSWORD2 +remove_tmp_files + pidfile + pidfile + unlink + pidfile +cleanup_before_exit + remove_tmp_files + imap1 + imap1 + myprint + imap1 + imap2 + imap2 + myprint + imap2 + log + myprint + log + logfile_handle + close + logfile_handle +exit_clean + myprint + myprint + cleanup_before_exit + exit +missing_option + nb_errors + exit_clean +catch_ignore + sigcounter + myprint + stats +catch_exit + myprint + stats + myprint + stats + myprint + myprint + myprint + cleanup_before_exit + kill + sleep + nb_errors + exit_clean + nb_errors + exit_clean +catch_print + sigcounter + myprint +here_twice + lastcatch + lastcatch +catch_reconnect + here_twice + myprint + catch_exit + myprint + myprint + imap1 + imap2 + myprint + imap1 + Mail::IMAPClient::Unconnected + imap1 + IMAPSYNC_RECONNECT_COUNT + imap1 + myprint + nb_errors + exit_clean + myprint + imap2 + Mail::IMAPClient::Unconnected + imap2 + IMAPSYNC_RECONNECT_COUNT + imap2 + myprint + nb_errors + exit_clean + myprint +install_signals + under_docker_context + output + sigexit + defined + sigexit + sigexit + sig_install + sigexit + sigexit + defined + sigexit + sigexit + sigreconnect + defined + sigreconnect + sigreconnect + sigprint + defined + sigprint + sigprint + sigignore + defined + sigignore + sigignore + sig_install + sigexit + sig_install + sigreconnect + sig_install + sigprint + sig_install + sigignore + sig_install_toggle_sleep +tests_reconnect_12_if_needed + note + imap1 + imap2 + imap1 + imap2 + is + is + imap1 + IMAPSYNC_RECONNECT_COUNT + is + imap2 + IMAPSYNC_RECONNECT_COUNT + note +reconnect_12_if_needed + imap1 + imap2 +tests_reconnect_if_needed + note + is + undef + is + undef + is + undef + is + is + IMAPSYNC_RECONNECT_COUNT + note +reconnect_if_needed + IMAPSYNC_RECONNECT_COUNT + Mail::IMAPClient::Unconnected + IMAPSYNC_RECONNECT_COUNT +justconnect +justconnect1 + host1 + myprint + imap1 + host1 + port1 + ssl1 + tls1 + h1 + timeout + h1 + imap_id + imap1 + imap1 + host1 +justconnect2 + host2 + myprint + imap2 + host2 + port2 + ssl2 + tls2 + h2 + timeout + h2 + imap_id + imap2 + imap2 + host2 +skip_macosx +tests_mailimapclient_connect + note + ok + is + is + undef + is + is + is + is + like + ref + like + is + undef + ok + is + is + ok + SSL_verify_mode + is + like + ref + like + is + undef + ok + is + is + ok + SSL_verify_mode + is + skip + is + is + like + ref + like + ref + is + undef + note +tests_mailimapclient_connect_bug + note + ok + is + is + skip + like + ref + is + undef + note +tests_connect_socket + note + is + undef + skip + PeerAddr + ok + PeerHost + ok + note +connect_socket +tests_probe_imapssl + note + is + undef + is + undef + note + skip + ok + resolv + like + probe_imapssl + like + probe_imapssl + note +probe_imapssl + debug + PeerHost + debug + debug +connect_imap + set_ssl + myprint + nb_errors + exit_clean + myprint + myprint + myprint + set_tls + nb_errors + exit_clean + myprint +login_imap + myprint + nb_errors + exit_clean + myprint + myprint + myprint + myprint + myprintf + nb_errors + exit_clean + set_tls + nb_errors + exit_clean + myprint + authenticate_imap + myprint +authenticate_imap + check_capability + defined + xmasterauth + chomp + myprint + nb_errors + exit_clean + myprint + myprint + nb_errors + exit_clean + chomp + nb_errors + exit_clean +check_capability + myprintf + myprintf + myprint +set_ssl + sslargs + SSL_verify_mode + keys + keys + delete +set_tls + sslargs + SSL_verify_mode + keys + keys + delete +init_imap + tee + tee + set_ssl + showpasswords + defined + IMAPSYNC_RECONNECT_COUNT +plainauth +xoauth2 + debug + nb_errors + exit_clean + join + close + client_id + private_key + debug + debug + debug + debug + grant_type + nb_errors + exit_clean + debug + access_token + debug +xoauth + debug + Data::Uniqid::uniqid + rand + sort + length + debug + substr + delete + sort + length + debug +xmasterauth + nb_errors + exit_clean + debug + addcrlf + nb_errors + exit_clean + nb_errors + exit_clean + Mail::IMAPClient::Authenticated +tests_do_valid_directory + note + Readonly + skip + ok + ok + Readonly + skip + diag + ok + diag + ok + note +banner_imapsync +do_valid_directory + myprint + myprintf + myprint + mkpath + myprint +tests_match_a_pid_number + note + is + undef + is + undef + is + undef + is + is + is + is + is + is + undef + is + undef + is + is + is + undef + is + undef + is + undef + is + undef + is + undef + is + undef + note +match_a_pid_number +tests_remove_pidfile_not_running + note + ok + is + undef + is + undef + is + undef + is + is + undef + is + is + undef + is + is + is + is + undef + note +remove_pidfile_not_running + myprint + myprint + myprint + myprint + myprint + myprint + unlink + myprint + myprint + myprint +tests_tail + note + ok + ok + ok + is + undef + is + undef + pidfile + is + undef + pidfilelocking + is + undef + is + pidfile + is + undef + is + is + undef + tail + is + is + pidfile + is + undef + note +tail + abort + pidfile + pidfilelocking + tail + myprint + name + myprint + isrunning + myprint + sleep +isrunning + kill + myprint +tests_write_pidfile + note + is + pidfile + is + pidfile + is + undef + pidfilelocking + is + undef + pidfile + ok + is + pidfile + pidfilelocking + is + is + is + pidfilelocking + is + undef + pidfilelocking + logfile + is + is + is + note +write_pidfile + abort + pidfile + pidfilelocking + myprint + myprint + myprint + myprint + logfile + open + myprint + print + close + myprint +fix_Inbox_INBOX_mapping + exists + INBOX + INBOX + exists + Inbox + Inbox + exists + INBOX + Inbox + exists + Inbox + INBOX +tests_fix_Inbox_INBOX_mapping + note + ok + ok + ok + ok + ok + ok + note +jux_utf8_list +tests_jux_utf8_list + note + is + is + is + is + note +tests_jux_utf8_old + note + is + is + is + is + is + is + note +jux_utf8_old +imap_utf7_decode_old + Unicode::String::utf7 +tests_jux_utf8 + note + binmode + ok + find_encoding + is + is + is + is + is + is + is + is + is + note +jux_utf8 +imap_utf7_decode + Encode::IMAPUTF7::decode +imap_utf7_encode + Encode::IMAPUTF7::encode +imap_utf7_encode_old +select_folder + errors_incr +examine_folder + errors_incr +count_from_select + undef +create_folder_old + myprint + myprint + dry + errors_incr + myprint + myprint +create_folder + myprint + create_folder_old + myprint + myprint + myprint + myprint + pop + h2_sep + create_folder + dry + errors_incr + myprint + myprint + justfolders + myprint +tests_folder_routines + note + ok + ok + add_to_requested_folders + ok + is_requested_folder + ok + is_deeply + remove_from_requested_folders + ok + ok + ok + is_requested_folder + ok + is_requested_folder + ok + remove_from_requested_folders + ok + is_deeply + remove_from_requested_folders + ok + sort_requested_folders + ok + add_to_requested_folders + ok + sort_requested_folders + ok + sort_requested_folders + is_deeply + sort_requested_folders + ok + sort_requested_folders + ok + add_to_requested_folders + ok + sort_requested_folders + ok + add_to_requested_folders + ok + sort_requested_folders + undef + undef + note +sort_requested_folders + add_to_requested_folders +is_requested_folder + defined +add_to_requested_folders + keys +tests_remove_from_requested_folders + note + is + undef + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + is_deeply + remove_from_requested_folders + is_deeply + remove_from_requested_folders + is_deeply + is_deeply + remove_from_requested_folders + is_deeply + note +remove_from_requested_folders + exists + delete + push +compare_lists +tests_compare_lists + note + ok + ok + undef + ok + undef + ok + undef + ok + undef + ok + undef + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + split + ok + sort + note +guess_prefix +tests_guess_prefix + note + is + guess_prefix + is + is + is + is + is + is + is + is + is + is + is + note +get_prefix + debug + debugfolders + myprint + debug + debugfolders + myprint + defined + myprint + defined + myprint + myprint +guess_separator + debug +tests_guess_separator + note + ok + ok + ok + ok + ok + ok + ok + ok + note +get_separator + debug + debugfolders + myprint + debug + debugfolders + defined + myprint + defined + myprint + defined + myprint + myprint + defined + myprint + myprint +help_to_guess_sep +help_to_guess_prefix +folders_list_to_help +private_folders_separators_and_prefixes + debug + debugfolders + h1_sep + imap1 + sep1 + h2_sep + imap2 + sep2 + h1_prefix + imap1 + h2_prefix + imap2 + myprint + myprint +subfolder1 + subfolder1 + myprint + automap + myprint + subfolder1 + nb_errors + exit_clean + subfolder1 +subfolder2 + subfolder2 + myprint + automap + myprint + subfolder2 + set_regextrans2_for_subfolder2 + subfolder2 +tests_sanitize_subfolder + note + is + undef + is + undef + is + undef + is + undef + is + is + is + is + note +sanitize_subfolder +tests_add_subfolder1_to_folderrec + note + is + undef + is_deeply + add_subfolder1_to_folderrec + is_deeply + add_subfolder1_to_folderrec + is_deeply + subfolder1 + h1_prefix + is_deeply + add_subfolder1_to_folderrec + is_deeply + subfolder1 + is_deeply + add_subfolder1_to_folderrec + is_deeply + is_deeply + add_subfolder1_to_folderrec + is_deeply + note +add_subfolder1_to_folderrec + subfolder1 + subfolder1 + h1_prefix + exists + myprint + push + exists + myprint + push + myprint +set_regextrans2_for_subfolder2 + unshift + regextrans2 +tests_imap2_folder_name + note + h1_prefix + h2_prefix + h1_sep + h2_sep + debug + fixslash2 + is + is + is + is + is + is + f1f2h + is + f1f2h + is + regextrans2 + is + is + is + is + is + regextrans2 + is + is + regextrans2 + is + fixslash2 + regextrans2 + is + is + is + is + is + is + h1_sep + h2_sep + is + is + is + is + is + fixslash2 + h1_prefix + is + is + h1_sep + h2_sep + h1_prefix + h2_prefix + regextrans2 + is + is + regextrans2 + is + h1_prefix + h2_prefix + h1_sep + h2_sep + is + is + is + is + is + is + h1_prefix + h2_prefix + h1_sep + h2_sep + set_regextrans2_for_subfolder2 + subfolder2 + is + is + h1_prefix + h2_prefix + h1_sep + h2_sep + set_regextrans2_for_subfolder2 + subfolder2 + is + is + is + is + h1_prefix + h2_prefix + h1_sep + h2_sep + subfolder1 + is + is + is + is + is + is + is + is + subfolder1 + is + is + is + is + is + is + is + is + h1_prefix + h2_prefix + h1_sep + h2_sep + subfolder1 + is + is + is + is + subfolder1 + is + is + is + is + note +imap2_folder_name + f1f2h + f1f2h + debug + debugfolders + f1f2auto + f1f2auto + debug + debugfolders + subfolder1 + h1_sep + subfolder1 + h1_sep + h1_prefix +tests_remove_last_char_if_is + note + is + undef + is + is + is + is + is + note +remove_last_char_if_is + chop +tests_prefix_seperator_invertion + note + is + undef + is + undef + is + undef + is + undef + is + undef + is + undef + is + undef + h1_prefix + is + is + is + is + is + is + h1_prefix + is + is + is + is + is + is + is + is + is + is + is + note +prefix_seperator_invertion + h1_prefix + h2_prefix + h1_sep + h2_sep + debug + debugfolders + debug + debugfolders + debug + debugfolders +tests_separator_invert + note + fixslash2 + ok + defined + ok + defined + ok + defined + ok + ok + ok + ok + ok + ok + ok + ok + fixslash2 + ok + note +separator_invert + undef + fixslash2 +regextrans2 + regextrans2 + debug + debugfolders + defined + nb_errors + exit_clean +tests_decompose_regex + note + ok + ok + decompose_regex + ok + decompose_regex + note +decompose_regex +tests_timenext + note + is + undef + is + undef + ok + time + ok + timenext + ok + timenext + note +timenext + timebefore + timebefore + timebefore +tests_timesince + note + ok + timesince + time + ok + timesince + time + ok + timesince + note +timesince + max +tests_flags_regex + note + ok + ok + ok + defined + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + is + is + note +flags_regex + defined + myprint + undef +acls_sync + imap1 + imap2 + keys + sort + myprint + dry + myprint + imap2 +tests_permanentflags + note + ok + ok + ok + ok + note +permanentflags + debug +tests_flags_filter + note + ok + ok + ok + ok + ok + ok + note +flags_filter + exists +flagscase + exists + ucsecond + lc + lc +tests_flagscase + note + ok + ok + ok + ok + ok + ok + note +ucsecond + substr + uc + substr +tests_ucsecond + note + ok + ok + ok + ok + ok + ok + ok + ok + note +select_msgs +select_msgs_by_search + defined + defined + defined +select_msgs_by_fetch + defined + defined + myprint + defined + defined + defined + epoch + push + defined + epoch + push +select_msgs_by_age + defined + defined +msgs_from_maxmin + defined + defined +tests_msgs_from_maxmin + note + ok + ok + ok + ok + note +tests_info_date_from_uid + note + note +info_date_from_uid +lastuid + defined +size_filtered + defined + maxsize + maxsize + myprint + total_bytes_skipped + nb_msg_skipped + defined + myprint + total_bytes_skipped + nb_msg_skipped +message_exists +stats_update_skip_message + total_bytes_skipped + nb_msg_skipped + h1_nb_msg_processed +copy_message + debug + dry + size_filtered + h1_nb_msg_processed + debugsleep + myprint + imap1 + stats_update_skip_message + myprint + debugmemory + myprint + debugmemory + myprint + stats_update_skip_message + defined + defined + stats_update_skip_message + debug + debug + sync_flags_after_copy + myprint + debugmemory +linelengthstuff + defined + defined + defined + myprint + myprint +message_for_host2 + myprint + debugmemory + imap1 + myprint + debugmemory + defined + imap1 + errors_incr + h1_nb_msg_processed + defined + myprint + myprint + defined + myprint + defined + myprint + addheader + debug + defined + truncmess + truncmess + truncmess + myprint + debugmemory +tests_truncmess + note + is + undef + is + is + is + is + is + is + note +truncmess +tests_message_for_host2 + note + is + undef + is + undef + require_ok + imap1 + is + is + Readonly + skip + Readonly + skip + is + is + is + undef + is + undef + is + undef + is + undef + note +tests_labels_remove_subfolder1 + note + is + undef + is + is + is + is + is + is + is + is + is + is + is + note +labels_remove_subfolder1 + push + push + push + push + push +tests_labels_remove_special + note + is + undef + is + is + is + is + is + note +labels_remove_special + myprint + push +tests_labels_add_subfolder2 + note + is + undef + is + is + is + is + is + is + is + is + is + is + is + is + is + note +labels_add_subfolder2 + quotewords + myprint + push + push + push + push + push +tests_labels + note + is + undef + is + undef + undef + require_ok + is + undef + is + note +labels +tests_synclabels + note + is + undef + is + undef + undef + is + undef + require_ok + imap1 + imap2 + is + undef + is + undef + is + note +synclabels + debuglabels + debuglabels + debuglabels + subfolder1 + subfolder1 + debuglabels + subfolder2 + subfolder2 + debuglabels + dry + debuglabels + debuglabels +tests_resynclabels + note + is + undef + is + undef + undef + is + undef + debuglabels + is + require_ok + imap2 + is + note +resynclabels + subfolder1 + subfolder1 + subfolder2 + subfolder2 + debuglabels + debuglabels + debuglabels + dry + debuglabels + imap2 + imap2 + debuglabels + imap2 +tests_uniq + note + is + is_deeply + uniq + is_deeply + uniq + is_deeply + uniq + note +uniq + push +length_ref +tests_length_ref + note + is + is + is + is + note +date_for_host2 + debug + debug + imap1 + debug + debug +flags_for_host2 +subject +tests_subject + note + ok + ok + ok + ok + ok + ok + ok + ok + note +append_message_on_host2 + myprint + debugmemory + dry + imap2 + myprint + debugmemory + imap2 + errors_incr + h1_nb_msg_processed + imap2 + synclabels + synclabels + total_bytes_transferred + nb_msg_transferred + h1_nb_msg_processed + begin_transfer_time + total_bytes_transferred + total_bytes_transferred + myprintf + nb_msg_transferred + sleep_if_needed + touch + delete1 + delete_message_on_host1 + expungeaftereach + h1_nb_msg_processed +tests_sleep_if_needed + note + is + undef + is + undef + maxbytespersecond + is + begin_transfer_time + is + begin_transfer_time + is + total_bytes_transferred + begin_transfer_time + is + is + maxsleep + begin_transfer_time + is + maxbytesafter + begin_transfer_time + is + note +sleep_if_needed + maxmessagespersecond + maxbytespersecond + maxsleep + maxsleep + maxsleep + maxsleep + maxsleep + begin_transfer_time + nb_msg_transferred + maxmessagespersecond + maxbytesafter + total_bytes_transferred + maxbytespersecond + maxsleep + myprint + sleep +sleep_max_messages + max +tests_sleep_max_messages + note + ok + ok + ok + ok + ok + ok + note +sleep_max_bytes + max +tests_sleep_max_bytes + note + ok + ok + ok + ok + ok + ok + ok + ok + note +delete_message_on_host1 + delete1 + delete_messages_on_any + imap1 +tests_operators_and_exclam_precedence + note + is + is + is + is + is + is + is + is + is + note +delete_messages_on_any + dry_message + myprint + dry + defined + h1_nb_msg_deleted + errors_incr + uidexpunge_or_expunge +tests_uidexpunge_or_expunge + note + is + undef + is + undef + is + undef + is + undef + require_ok + is + undef + is + undef + is + undef + is + is + note +uidexpunge_or_expunge +eta_print + myprint +tests_eta + note + is + is + undef + is + foldersizes + begin_transfer_time + h1_nb_msg_processed + is + time + h1_nb_msg_processed + h1_nb_msg_start + is + time + note +eta + foldersizes + h1_nb_msg_start + h1_nb_msg_processed + dry + h1_nb_msg_processed + nb_msg_transferred + begin_transfer_time + debug + time + mysprintf +time_remaining +tests_time_remaining + note + is + is + is + is + is + is + is + is + note +cache_map + sort + exists + defined + defined + exists + delete +tests_cache_map + note + ok + sort + sort + ok + ok + ok + ok + ok + note +cache_dir_fix +tests_cache_dir_fix + note + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +cache_dir_fix_win +tests_cache_dir_fix_win + note + ok + ok + note +get_cache + undef + clean_cache +tests_get_cache + note + ok + get_cache + ok + ok + mkpath + ok + touch + ok + sort + sort + ok + ok + ok + ok + ok + ok + ok + touch + ok + ok + ok + ok + ok + ok + ok + mkpath + ok + touch + ok + sort + sort + ok + ok + ok + ok + ok + ok + note +match_a_cache_file + undef +tests_match_a_cache_file + note + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +clean_cache + map + unlink +tests_clean_cache + note + ok + ok + mkpath + ok + touch + ok + ok + ok + ok + ok + ok + ok + clean_cache + ok + ok + ok + ok + ok + ok + note +tests_clean_cache_2 + note + ok + ok + mkpath + ok + touch + ok + ok + ok + ok + ok + ok + ok + clean_cache + ok + ok + ok + ok + ok + ok + note +tests_mkpath + note + ok + skip + ok + ok + ok + ok + ok + ok + ok + ok + eval + ok + ok + skip + myprint + ok + ok + ok + ok + ok + ok + eval + ok + ok + ok + ok + ok + ok + ok + note +tests_touch + note + ok + ok + ok + ok + ok + ok + note +touch + myprint +tests_tmpdir_has_colon_bug + note + ok + ok + ok + ok + note +tmpdir_has_colon_bug +tmpdir_fix_colon_bug + tmpdir + myprint + myprint + myprint + myprint + File::Copy::Recursive::rmove + myprint + myprint + myprint + myprint + myprint +tests_cache_folder + note + ok + ok + ok + ok + ok + ok + ok + ok + note +cache_folder + h1_sep + h2_sep +tests_filter_forbidden_characters + note + ok + ok + ok + ok + ok + skip + ok + skip + ok + ok + ok + ok + ok + ok + note +filter_forbidden_characters +tests_convert_sep_to_slash + note + ok + ok + ok + ok + ok + ok + ok + note +convert_sep_to_slash +tests_regexmess + note + ok + ok + defined + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + is + is + is + is + note +regexmess + debug + myprint + undef + debug +tests_skipmess + note + ok + defined + ok + defined + ok + defined + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +skipmess + debug + debug + myprint + undef +tests_bytes_display_string + note + is + is + undef + is + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +bytes_display_string + abs + abs + abs + abs +tests_useheader_suggestion + note + is + undef + h1_nb_msg_noheader + is + h1_nb_msg_noheader + is + note +useheader_suggestion + h1_nb_msg_noheader + h1_nb_msg_noheader +stats + stats + timestart + memory_consumption_at_start + myprint + myprint + myprint + myprintf + myprint + myprint + myprint + dry + myprint + myprint + myprint + myprint + myprint + myprint + myprint + nb_messages_in_1_not_in_2 + nb_messages_in_2_not_in_1 + myprintf + nb_messages_in_1_not_in_2 + myprintf + nb_messages_in_2_not_in_1 + myprint + myprint + myprintf + total_bytes_transferred + total_bytes_transferred + myprintf + total_bytes_skipped + total_bytes_skipped + myprintf + nb_msg_transferred + myprintf + total_bytes_transferred + myprint + myprint + myprintf + myprint + join + myprintf + myprint + foldersizesatend + foldersizes + h2_nb_msg_start + h1_nb_msg_start + h2_bytes_start + h1_bytes_start + myprintf + myprintf + comment_on_final_diff_in_1_not_in_2 + comment_on_final_diff_in_2_not_in_1 + myprint + myprint + myprint + homepage +diff_or_NA +match_number +tests_match_number + note + is + is + undef + is + is + is + is + is + note +tests_diff_or_NA + note + is + is + undef + is + undef + is + undef + is + is + is + is + is + is + is + is + is + note +homepage +load_modules + ssl1 + ssl2 + tls1 + tls2 + inet4 + IO::Socket::SSL + inet6 + IO::Socket::SSL +parse_header_msg + debug + debug + debug + addheader + debug + NO_HEADER + debug +header_construct + sort + exists + uc + sort + debug + debug +header_line_normalize +tests_header_line_normalize + note + ok + ok + ok + ok + ok + ok + ok + note +tests_firstline + note + is + ok + is + is + is + is + is + is + is + is + note +firstline +tests_secondline + note + is + is + ok + is + is + note +secondline +tests_nthline + note + is + is + ok + is + is + note +nthline + file_to_array + chomp +file_to_array + open + myprint + close +tests_file_to_string + note + is + undef + is + undef + is + undef + ok + file_to_string + ok + is + is + is + is + note +file_to_string + open + close + join + myprint +tests_string_to_file + note + is + undef + is + undef + is + undef + ok + is + is + Readonly + skip + is + undef + note +string_to_file + myprint + FILE + myprint + print + close +pipemess + string_to_file + chomp + unlink + myprint + wantarray + undef + myprint + wantarray +tests_pipemess + note + Readonly + skip + ok + ok + ok + pipemess + Readonly + skip + ok + ok + ok + ok + ok + is + undef + is + undef + is + undef + is + is + is + is + like + is + like + is + is + is + like + is + like + is + like + like + like + is + like + note +tests_is_a_release_number + note + is + undef + ok + is_a_release_number + ok + is_a_release_number + ok + is_a_release_number + imapsync_version + ok + note +is_a_release_number +imapsync_version_public + PeerAddr + print + close + chomp +not_long_imapsync_version_public + ALRM + die + POSIX::sigaction + SIGALRM + sub + croak + alarm + alarm + alarm +tests_not_long_imapsync_version_public + note + is + not_long_imapsync_version_public + note +check_last_release + debug +tests_check_last_release + note + diag + check_last_release + like + check_last_release + like + check_last_release + diag + check_last_release + like + check_last_release + like + check_last_release + like + check_last_release + like + check_last_release + like + check_last_release + diag + check_last_release + note +tests_imapsync_context + note + like + imapsync_context + note +imapsync_context + under_docker_context + under_docker_context + under_cgi_context +imapsync_version + rcs +tests_version_from_rcs + note + is + undef + is + is + note +version_from_rcs +tests_imapsync_basename + note + ok + imapsync_basename + ok + note +imapsync_basename +localhost_info +tests_cpu_number + note + is + cpu_number + ok + is + is + is + is + note +cpu_number + debug + chomp + debug + debug + debug + defined + integer_or_1 +tests_integer_or_1 + note + is + is + undef + is + is + is + note +integer_or_1 + is_an_integer +tests_is_an_integer + note + is + undef + ok + is_an_integer + ok + is_an_integer + ok + is_an_integer + ok + is_an_integer + ok + is_an_integer + ok + is_an_integer + ok + is_an_integer + ok + ok + note +is_an_integer +tests_loadavg + note + skip + is + undef + is_deeply + loadavg + skip + is + undef + ok + loadavg + is_deeply + loadavg + skip + is_deeply + loadavg + note +loadavg + loadavg_linux + loadavg_freebsd + loadavg_darwin + loadavg_windows +loadavg_linux + all_defined + debug +loadavg_freebsd + eval + myprint + debug +loadavg_darwin + eval + myprint + debug +loadavg_windows + eval + myprint + debug +tests_load_and_delay + note + is + undef + is + undef + is + undef + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + note +load_and_delay +ram_memory_info + sprintf +tests_memory_stress + note + is + undef + note +memory_stress + myprintf + myprintf +tests_memory_consumption + note + like + memory_consumption + like + memory_consumption + like + memory_consumption + like + memory_consumption_ratio + like + memory_consumption_ratio + like + memory_consumption_ratio + like + memory_consumption + note +memory_consumption + memory_consumption_of_pids +debugmemory + debugmemory + mysprintf +memory_consumption_of_pids + debug + shift + chomp + debug +memory_consumption_of_pids_win32 + map + split + remove_qq + chomp + push +tests_backtick + note + is + undef + is + undef + skip + ok + debug + ok + ok + debug + ok + ok + skip + is + undef + ok + debug + ok + ok + debug + ok + ok + is + undef + debug + note +backtick + myprint + waitpid + wantarray + join +tests_check_binary_embed_all_dyn_libs + note + is + note +check_binary_embed_all_dyn_libs + myprint + myprint + myprint +search_dyn_lib_locale +search_dyn_lib_locale_darwin + myprint +search_dyn_lib_locale_linux + myprint +search_dyn_lib_locale_MSWin32 + myprint +remove_not_num +tests_remove_not_num + note + ok + ok + ok + ok + note +remove_Ko +remove_qq +memory_consumption_ratio +date_from_rcs +tests_date_from_rcs + note + ok + note +good_date +tests_good_date + note + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +tests_list_keys_in_2_not_in_1 + note + ok + ok + list_keys_in_2_not_in_1 + ok + list_keys_in_2_not_in_1 + ok + list_keys_in_2_not_in_1 + ok + list_keys_in_2_not_in_1 + ok + list_keys_in_2_not_in_1 + ok + list_keys_in_2_not_in_1 + note +list_keys_in_2_not_in_1 + sort + exists + push +list_folders_in_2_not_in_1 + map + reverse +tests_nb_messages_in_2_not_in_1 + note + is + undef + h1_folders_of_md5 + is + h1_folders_of_md5 + h2_folders_of_md5 + is + h2_folders_of_md5 + is + h2_folders_of_md5 + is + note +nb_messages_in_2_not_in_1 + nb_messages_in_2_not_in_1 + list_keys_in_2_not_in_1 + h1_folders_of_md5 + h2_folders_of_md5 + nb_messages_in_2_not_in_1 +nb_messages_in_1_not_in_2 + nb_messages_in_1_not_in_2 + list_keys_in_2_not_in_1 + h2_folders_of_md5 + h1_folders_of_md5 + nb_messages_in_1_not_in_2 +comment_on_final_diff_in_1_not_in_2 + justfolders + useuid + keys + h1_folders_of_md5 + keys + h2_folders_of_md5 + debug + debug + nb_messages_in_1_not_in_2 + nb_messages_in_1_not_in_2 + nb_messages_in_1_not_in_2 + myprint + myprint + nb_messages_in_1_not_in_2 + h1_nb_msg_noheader + myprint + h1_nb_msg_noheader + myprint +comment_on_final_diff_in_2_not_in_1 + justfolders + useuid + keys + h2_folders_of_md5 + nb_messages_in_2_not_in_1 + nb_messages_in_2_not_in_1 + nb_messages_in_2_not_in_1 + myprint + myprint + nb_messages_in_2_not_in_1 +tests_match + note + is + undef + is + undef + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + is + undef + is + undef + is + undef + note +match + eval + myprint +tests_notmatch + note + is + undef + is + undef + is + is + is + is + is + is + is + is + is + is + is + is + is + is + note +notmatch + eval + myprint +delete_folders_in_2_not_in_1 + defined + myprint + defined + myprint + dry + imap2 + dry + imap2 + dry + myprint + myprint +delete_folder + dry + dry + myprint + dry_message + myprint +delete1emptyfolders + delete1emptyfolders + imap1 + myprint + reverse + h1_folders_wanted + defined + myprint + myprint + myprint + uc + myprint + myprint + delete_folder + remove_deleted_folders_from_wanted_list + myprint +remove_deleted_folders_from_wanted_list + h1_folders_wanted + push + h1_folders_wanted +examine_folder_and_count +tests_delete1emptyfolders + note + is + undef + is + undef + imap1 + is + undef + require_ok + imap1 + is + undef + delete1emptyfolders + tests_delete1emptyfolders_unit + delete1emptyfolders + tests_delete1emptyfolders_unit + tests_delete1emptyfolders_unit + tests_delete1emptyfolders_unit + tests_delete1emptyfolders_unit + tests_delete1emptyfolders_unit + tests_delete1emptyfolders_unit + dry_message + tests_delete1emptyfolders_unit + note +tests_delete1emptyfolders_unit + note + h1_folders_wanted + is_deeply + h1_folders_wanted + delete1emptyfolders + is_deeply + h1_folders_wanted + note +extract_header +tests_extract_header + note + chomp + ok + note +decompose_header + push +tests_decompose_header + note + ok + ok + ok + ok + ok + ok + ok + defined + ok + ok + ok + ok + ok + ok + ok + note +tests_epoch + note + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + is + is + note +epoch + myprint +tests_add_header + note + ok + ok + note +add_header +tests_max_line_length + note + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + ok + note +max_line_length +tests_setlogfile + note + logdir + logfile + is + skip + TZ + timestart + is + timestart + is + timestart + is + timestart + is + timestart + is + logdir + logfile + is + logdir + logfile + is + logdir + logfile + is + user1 + is + note +setlogfile + abort + remote + filter_forbidden_characters + slash_to_underscore + user1 + filter_forbidden_characters + slash_to_underscore + user2 + logdir + logdir + logdir + logfile + logfile + timestart + logdir + logfile +tests_logfile + note + skip + TZ + POSIX::tzset + is + is + is + is + is + is + is + is + is + is + POSIX::tzset + note +logfile +tests_slash_to_underscore + note + is + undef + is + is + note +slash_to_underscore +tests_million_folders_baby_2 + note + ok + note +tests_always_fail + note + is + note +tests_logfileprepa + note + is + undef + is + note +logfileprepa + do_valid_directory +tests_teelaunch + note + is + undef + is + undef + logfile + is + undef + logfile + isa_ok + is + is + is + is + note +teelaunch + logfile + logfileprepa + open + binmode + logfile_handle + tee +getpwuid_any_os + scalar + scalar +simulong + myprint + sleep +printenv + myprint + map +testsexit + tests + testsdebug + testsunit + tests + testsdebug + testunitsession + myprint + exit + cleanup_mess_from_tests + testslive + exit +cleanup_mess_from_tests + undef +after_get_options + debug + myprint + usage + exit +tests_remove_edging_blanks + note + is + undef + is + is + note +remove_edging_blanks +tests_sanitize + note + is + undef + host1 + user1 + password1 + is + undef + is + host1 + is + user1 + is + password1 + note +sanitize +easyany + gmail1 + gmail2 + debug + gmail12 + gmail1 + debug + gmail1 + gmail2 + debug + gmail2 + office1 + office1 + office2 + office2 + exchange1 + exchange1 + exchange2 + exchange2 + domino1 + domino1 + domino2 + domino2 +gmail12 + host1 + ssl1 + defined + ssl1 + ssl1 + host2 + ssl2 + defined + ssl2 + ssl2 + maxbytespersecond + maxbytesafter + automap + defined + automap + automap + maxsleep + defined + maxsleep + maxsleep + defined + synclabels + defined + synclabels + synclabels + resynclabels + defined + resynclabels + resynclabels + push + push +gmail1 + host1 + ssl1 + defined + ssl1 + ssl1 + maxbytespersecond + maxbytesafter + automap + defined + automap + automap + maxsleep + defined + maxsleep + maxsleep + defined + push + push + regextrans2 + push +gmail2 + host2 + ssl2 + defined + ssl2 + ssl2 + maxbytespersecond + maxbytesafter + automap + defined + automap + automap + expunge1 + defined + expunge1 + expunge1 + addheader + defined + addheader + addheader + maxsleep + defined + maxsleep + maxsleep + maxsize + defined + maxsize + maxsize + noexclude + push + push + push + regextrans2 + push + regextrans2 + push + regextrans2 + push + regextrans2 + push +office1 + output + output + host1 + ssl1 + defined + ssl1 + ssl1 + noexclude + push +office2 + output + output + output + output + host2 + ssl2 + defined + ssl2 + ssl2 + maxsize + maxmessagespersecond + defined + noregexmess + push + nof1f2 + push + f1f2 +exchange1 + output +exchange2 + output + output + output + maxsize + maxmessagespersecond + defined + noregexflag + push + noregexmess + push +domino1 + sep1 + defined +domino2 + sep2 + defined + push + regextrans2 +tests_resolv + note + is + undef + is + undef + is + undef + is + is + is + is + is + is + is + is + is + note +resolv + defined +resolv_with_getaddrinfo + socktype + myprint + addr + myprint + debug + push + addr + debug +tests_resolvrev + note + is + undef + is + undef + is + undef + is + is + is + is + is + is + is + is + is + note +resolvrev + defined +resolvrev_with_getaddrinfo + socktype + myprint + addr + myprint + debug + push +tests_imapsping + note + is + undef + is + undef + is + is + note +imapsping +tests_tcpping + note + is + undef + is + undef + is + undef + undef + is + undef + is + undef + is + is + is + is + is + is + note +tcpping + port_num + debug +tests_sslcheck + note + is + undef + sslcheck + is + sslcheck + is + sslcheck + is + is + ssl1 + sslcheck + is + undef + sslcheck + is + sslcheck + is + note +sslcheck + sslcheck + debug + port1 + tls1 + ssl1 + defined + host1 + myprint + probe_imapssl + host1 + ssl1 + myprint + myprint + port2 + tls2 + ssl2 + defined + host2 + myprint + probe_imapssl + host2 + ssl2 + myprint + myprint +testslive_init + host1 + user1 + password1 + host2 + user2 + password2 +testslive6_init + host1 + user1 + password1 + host2 + user2 + password2 +tests_backslash_caret + note + is + is + is + is + is + is + is + is + is + is + note +backslash_caret +tests_split_around_equal + note + is + undef + is_deeply + toto + split_around_equal + is_deeply + A + split_around_equal + is_deeply + A + split_around_equal + note +split_around_equal + split +tests_sig_install + note + is + undef + is + undef + is + undef + Readonly + skip + USR1 + USR2 + kill + debugsig + is + undef + is + is + tototo_calls + is + undef + is + is + tototo_calls + is + is + tototo_calls + USR1 + USR2 + is + is + tototo_calls + is + undef + is + is + tototo_calls + is + is + tototo_calls + note +sig_install + debugsig + debugsig + debugsig + output +tototo + myprint + tototo_calls +mygetppid + getppid +tests_toggle_sleep + note + is + undef + is + undef + is + undef + maxsleep + is + is + is + is + is + Readonly + skip + USR1 + kill + debugsig + is + undef + maxsleep + is + is + maxsleep + is + is + maxsleep + is + is + maxsleep + is + is + maxsleep + note +toggle_sleep + myprint + maxsleep + maxsleep + maxsleep + myprint + maxsleep + maxsleep +mypod2usage + pod2usage + -exitval +usage + open + warn + mypod2usage + close +tests_usage + note + like + myprint + like + like + like + is + undef + note +usage_footer + rcs + releasecheck +usage_complete +myGetOptions + cgi + sort + split + split + keys + keys + push + cmdcgi + ref + push + cmdcgi + push + cmdcgi +tests_get_options_cgi_context + note + SERVER_SOFTWARE + is + undef + CGI + is + undef + cgi + is + is + version + is + undef + debugenv + cgi + is + is + user1 + cgi + get_options + is_deeply + cgi + get_options + is_deeply + cgi + get_options + is_deeply + folder + cgi + get_options + is_deeply + folder + cgi + get_options + is_deeply + f1f2h + cgi + get_options + is + undef + dry + cgi + get_options + is + host1 + cgi + get_options + is + undef + simulong + cgi + get_options + is + undef + simulong + cgi + get_options + is + simulong + is + undef + folder + cgi + get_options + is + justfoldersizes + myprint + Data::Dumper + note +get_options_cgi + cgi + f1f2h + abort + host1 + host2 + user1 + user2 + password1 + password2 + dry + version + ssl1 + ssl2 + tls1 + tls2 + justlogin + justconnect + addheader + automap + justautomap + gmail1 + gmail2 + office1 + office2 + exchange1 + exchange2 + domino1 + domino2 + f1f2 + f1f2h + folder + testslive + testslive6 + releasecheck + simulong + debugsleep + subfolder1 + subfolder2 + justfolders + justfoldersizes + delete1 + delete2 + delete2duplicates + tail + debug +get_options_cmd + cgi + output + f1f2h + debug + debugsleep + debugmemory + debugfolders + debugssl + debugenv + debugsig + debuglabels + simulong + abort + host1 + host2 + port1 + port2 + inet4 + inet6 + user1 + user2 + gmail1 + gmail2 + office1 + office2 + exchange1 + exchange2 + domino1 + domino2 + password1 + password2 + passfile1 + passfile2 + sep1 + sep2 + sanitize + folder + noexclude + subfolder1 + subfolder2 + fixslash2 + regextrans2 + skipemptyfolders + noregexmess + noregexflag + resyncflags + synclabels + resynclabels + delete1 + delete2 + delete2duplicates + maxsize + appendlimit + truncmess + search1 + search2 + foldersizes + foldersizesatend + dry + expunge1 + expunge2 + uidexpunge2 + justfolders + justfoldersizes + version + h1 + timeout + h2 + timeout + sslcheck + ssl1 + ssl2 + h1 + sslargs + SSL_version + h2 + sslargs + SSL_version + h1 + sslargs + h2 + sslargs + tls1 + tls2 + tests + testsdebug + testsunit + testslive + testslive6 + justlogin + justconnect + tmpdir + pidfile + pidfilelocking + sigexit + sigreconnect + sigignore + releasecheck + addheader + exitwhenover + checkselectable + checkfoldersexist + expungeaftereach + abletosearch + abletosearch1 + abletosearch2 + showpasswords + maxmessagespersecond + maxbytespersecond + maxbytesafter + maxsleep + log + tail + logfile + logdir + errorsmax + errorsdump + automap + justautomap + id + f1f2 + nof1f2 + f1f2h + justfolderlists + delete1emptyfolders + debug + myprint +tests_get_options + note + is + undef + is + undef + noexist + is + undef + is + version + is + undef + noexist + is + is + delete2 + is + undef + is + undef + delete1 + is + undef + is + is + is + undef + is + is + undef + is + undef + is + is + host1 + note +get_options + under_cgi_context + sort + delete + delete +testunitsession + testsunit + testsunit + skip + testsunit + done_testing +tests_count_0s + note + is + is + is + is + is + note +count_0s + map +tests_report_failures + note + is + undef + is + is + is + is + note +report_failures +tests_true + note + is + note +tests_testsunit + note + is + undef + is + undef + undef + is + undef + is + undef + is + undef + note +testsunit + myprint + myprint + myprint + myprint +testsdebug + testsdebug + testsdebug + skip + note + tests_probe_imapssl + note + done_testing +tests + tests + skip + tests + note + tests_folder_routines + tests_compare_lists + tests_regexmess + tests_skipmess + tests_flags_regex + tests_ucsecond + tests_permanentflags + tests_flags_filter + tests_separator_invert + tests_imap2_folder_name + tests_command_line_nopassword + tests_good_date + tests_max + tests_remove_not_num + tests_memory_consumption + tests_is_a_release_number + tests_imapsync_basename + tests_list_keys_in_2_not_in_1 + tests_convert_sep_to_slash + tests_match_a_cache_file + tests_cache_map + tests_get_cache + tests_clean_cache + tests_clean_cache_2 + tests_touch + tests_flagscase + tests_mkpath + tests_extract_header + tests_decompose_header + tests_epoch + tests_add_header + tests_cache_dir_fix + tests_cache_dir_fix_win + tests_filter_forbidden_characters + tests_cache_folder + tests_time_remaining + tests_decompose_regex + tests_backtick + tests_bytes_display_string + tests_header_line_normalize + tests_fix_Inbox_INBOX_mapping + tests_max_line_length + tests_subject + tests_msgs_from_maxmin + tests_tmpdir_has_colon_bug + tests_sleep_max_messages + tests_sleep_max_bytes + tests_logfile + tests_setlogfile + tests_jux_utf8_old + tests_jux_utf8 + tests_pipemess + tests_jux_utf8_list + tests_guess_prefix + tests_guess_separator + tests_format_for_imap_arg + tests_imapsync_id + tests_date_from_rcs + tests_quota_extract_storage_limit_in_bytes + tests_quota_extract_storage_current_in_bytes + tests_guess_special + tests_do_valid_directory + tests_delete1emptyfolders + tests_message_for_host2 + tests_length_ref + tests_firstline + tests_diff_or_NA + tests_match_number + tests_all_defined + tests_special_from_folders_hash + tests_notmatch + tests_match + tests_get_options + tests_get_options_cgi_context + tests_rand32 + tests_hashsynclocal + tests_hashsync + tests_output + tests_output_reset_with + tests_output_start + tests_check_last_release + tests_loadavg + tests_cpu_number + tests_load_and_delay + tests_sslcheck + tests_not_long_imapsync_version_public + tests_reconnect_if_needed + tests_reconnect_12_if_needed + tests_sleep_if_needed + tests_string_to_file + tests_file_to_string + tests_under_cgi_context + tests_umask + tests_umask_str + tests_set_umask + tests_createhashfileifneeded + tests_slash_to_underscore + tests_testsunit + tests_count_0s + tests_report_failures + tests_min + tests_usage + tests_version_from_rcs + tests_backslash_caret + tests_write_pidfile + tests_remove_pidfile_not_running + tests_match_a_pid_number + tests_prefix_seperator_invertion + tests_is_an_integer + tests_integer_or_1 + tests_is_number + tests_sig_install + tests_template + tests_split_around_equal + tests_toggle_sleep + tests_labels + tests_synclabels + tests_uidexpunge_or_expunge + tests_appendlimit_from_capability + tests_maxsize_setting + tests_mock_capability + tests_appendlimit + tests_capability_of + tests_search_in_array + tests_operators_and_exclam_precedence + tests_teelaunch + tests_logfileprepa + tests_useheader_suggestion + tests_nb_messages_in_2_not_in_1 + tests_labels_add_subfolder2 + tests_labels_remove_subfolder1 + tests_resynclabels + tests_labels_remove_special + tests_uniq + tests_remove_from_requested_folders + tests_errors_log + tests_add_subfolder1_to_folderrec + tests_sanitize_subfolder + tests_remove_edging_blanks + tests_sanitize + tests_remove_last_char_if_is + tests_check_binary_embed_all_dyn_libs + tests_nthline + tests_secondline + tests_tail + tests_truncmess + tests_eta + tests_timesince + tests_timenext + tests_foldersize + tests_imapsync_context + tests_abort + tests_probe_imapssl + tests_mailimapclient_connect + done_testing + note +tests_template + note + is + undef + is_deeply + is_deeply + note diff --git a/W/install_module_one.bat b/W/install_module_one.bat old mode 100644 new mode 100755 index 3ee9ca2..6ab80f9 --- a/W/install_module_one.bat +++ b/W/install_module_one.bat @@ -1,5 +1,5 @@ -@REM $Id: install_module_one.bat,v 1.9 2019/05/28 13:20:08 gilles Exp gilles $ +@REM $Id: install_module_one.bat,v 1.11 2019/12/11 18:57:04 gilles Exp gilles $ @SETLOCAL @ECHO OFF @@ -18,11 +18,12 @@ IF ERRORLEVEL 1 ECHO Perl needed. Install Strawberry Perl. Get it at http://stra @REM PAUSE @REM EXIT FOR %%M in ( - Crypt::OpenSSL::RSA ^ + IO::Tee ^ ) DO perl -m%%M -e "print qq{Updating %%M $%%M::VERSION \n}" ^ & cpanm --force %%M -REM IO::Socket::SSL Net::SSLeay PAR::Packer IO::Tee Crypt::OpenSSL::RSA +REM & cpanm --force %%M +REM IO::Socket::SSL Net::SSLeay PAR::Packer IO::Tee Crypt::OpenSSL::RSA Encode::IMAPUTF7 IO::Socket::IP @ECHO Perl modules for imapsync installed @REM Do a PAUSE if run by double-click, aka, explorer (then ). No PAUSE in a DOS window or via ssh. diff --git a/W/install_module_ssl.bat b/W/install_module_ssl.bat old mode 100644 new mode 100755 diff --git a/W/install_modules.bat b/W/install_modules.bat old mode 100644 new mode 100755 index 4c3d65a..7ba7d14 --- a/W/install_modules.bat +++ b/W/install_modules.bat @@ -1,4 +1,4 @@ -REM $Id: install_modules.bat,v 1.37 2019/05/28 13:20:08 gilles Exp gilles $ +REM $Id: install_modules.bat,v 1.39 2019/12/11 18:56:54 gilles Exp gilles $ ::------------------------------------------------------ ::--------------- Main of install_modules.bat ---------- @@ -16,9 +16,9 @@ CD /D %~dp0 CALL :handle_error CALL :detect_perl CALL :handle_error CALL :update_modules -@ENDLOCAL @REM Do a PAUSE if run by double-click, aka, explorer (then ). No PAUSE in a DOS window or via ssh. IF %0 EQU "%~dpnx0" IF "%SSH_CLIENT%"=="" PAUSE +@ENDLOCAL EXIT /B @@ -43,6 +43,10 @@ EXIT /B :update_modules @SETLOCAL FOR %%M in ( ^ + App::cpanminus ^ + MIME::Base64 ^ + Encode ^ + Encode::IMAPUTF7 ^ File::Tail ^ Regexp::Common ^ Sys::MemInfo ^ @@ -58,6 +62,7 @@ FOR %%M in ( ^ Getopt::ArgvFile ^ Socket6 ^ Net::SSLeay ^ + IO::Socket::IP ^ IO::Socket::INET ^ IO::Socket::INET6 ^ IO::Socket::SSL ^ @@ -77,7 +82,7 @@ FOR %%M in ( ^ HTML::Entities ^ Encode::Byte ^ ) DO @perl -m%%M -e "print qq{Updating %%M $%%M::VERSION \n}" ^ - & cpanm %%M + & ECHO DOING cpanm %%M & cpanm %%M & ECHO DONE cpanm %%M ECHO Perl modules for imapsync updated REM PAUSE diff --git a/W/learn/bibmail_1GB b/W/learn/bibmail_1GB new file mode 100644 index 0000000..30cba24 --- /dev/null +++ b/W/learn/bibmail_1GB @@ -0,0 +1,7 @@ + +# A big file 714MB +dd if=/dev/zero of=pate714M bs=1M count=714 +# post it to big1 user +echo Aie aie aie | mutt -s '1 GB dans ta face !' -a pate714M -- big1 + +# ok ? diff --git a/W/learn/imap_Encode_IMAPUTF7_decode b/W/learn/imap_Encode_IMAPUTF7_decode new file mode 100755 index 0000000..4e2e1f4 --- /dev/null +++ b/W/learn/imap_Encode_IMAPUTF7_decode @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +use strict ; +use warnings ; +use Encode::IMAPUTF7 ; + +sub imap_utf7_decode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::decode("IMAP-UTF-7", $s ) ) ; +} + +sub imap_utf7_encode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::encode("IMAP-UTF-7", $s ) ) ; +} + +#use utf8 ; +while ( <> ) +{ + chomp ; + #print( Encode::IMAPUTF7::encode('IMAP-UTF-7', $_ ), "\n" ) ; + print( Encode::IMAPUTF7::decode('IMAP-UTF-7', $_ ), "\n" ) ; +} diff --git a/W/learn/imap_Encode_IMAPUTF7_encode b/W/learn/imap_Encode_IMAPUTF7_encode new file mode 100755 index 0000000..b4c596b --- /dev/null +++ b/W/learn/imap_Encode_IMAPUTF7_encode @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +use strict ; +use warnings ; +use Encode::IMAPUTF7 ; + +sub imap_utf7_decode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::decode("IMAP-UTF-7", $s ) ) ; +} + +sub imap_utf7_encode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::encode("IMAP-UTF-7", $s ) ) ; +} + + + +#use utf8 ; +no utf8 ; +while ( <> ) +{ + chomp ; + print( Encode::IMAPUTF7::encode('IMAP-UTF-7', $_ ), "\n" ) ; + #print( Encode::IMAPUTF7::decode('IMAP-UTF-7', $_ ), "\n" ) ; +} diff --git a/W/learn/imap_utf7_tests_Encode_IMAPUTF7 b/W/learn/imap_utf7_tests_Encode_IMAPUTF7 new file mode 100755 index 0000000..9e39c45 --- /dev/null +++ b/W/learn/imap_utf7_tests_Encode_IMAPUTF7 @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +use strict ; +use warnings ; +use Encode::IMAPUTF7 ; + +sub imap_utf7_decode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::decode("IMAP-UTF-7", $s ) ) ; +} + +sub imap_utf7_encode_new +{ + use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::encode("IMAP-UTF-7", $s ) ) ; +} + +use utf8 ; +#no utf8 ; +print( Encode::IMAPUTF7::encode('IMAP-UTF-7', 'Répertoire' ), "\n" ); +print( Encode::IMAPUTF7::decode('IMAP-UTF-7', 'R&AOk-pertoire' ), "\n" ); diff --git a/W/learn/xoauth2_gmail b/W/learn/xoauth2_gmail new file mode 100755 index 0000000..ee2f623 --- /dev/null +++ b/W/learn/xoauth2_gmail @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +use strict ; +use warnings ; + + +use LWP::Authen::OAuth2; + +my $dbh ; +my $token_string ; + +# Constructor +my $oauth2 = LWP::Authen::OAuth2->new( + client_id => "108687549524-86sjq07f3ch8otl9fnr56mjnniltdrvn.apps.googleusercontent.com", + client_secret => "zAJO4PLxzeJ4yOaiJRk6f69k", + service_provider => "Google", + redirect_uri => "https://imapsync.lamiral.info/", + + # Optional hook, but recommended. + #save_tokens => \&save_tokens, + #save_tokens_args => [ $dbh ], + + # This is for when you have tokens from last time. + token_string => $token_string, + scope => 'https://mail.google.com/', + login_hint => 'gilles.lamiral@gmail.com', + ); + +# URL for user to go to to start the process. +my $url = $oauth2->authorization_url(); + +print "url: $url\ntoken_string: $token_string\n" ; \ No newline at end of file diff --git a/W/learn_func.bat b/W/learn_func.bat old mode 100644 new mode 100755 diff --git a/W/memo b/W/memo index 7817fc5..1f7ea7f 100644 --- a/W/memo +++ b/W/memo @@ -1,6 +1,6 @@ #!/bin/sh -# $Id: memo,v 1.70 2019/04/28 03:11:49 gilles Exp gilles $ +# $Id: memo,v 1.73 2019/09/05 13:32:57 gilles Exp gilles $ if test -n "$1"; then echoq() { echo "$@" ; } # not quiet mode @@ -68,7 +68,7 @@ date statistics_ks_frequency() { cat < now past|+++++++++++++++minage---->now past|----maxage+++++minage---->now (intersection) @@ -336,22 +378,23 @@ Options/message selection: --search str : Selects only messages returned by this IMAP SEARCH command. Applied on both sides. - For a complete of what can be search see + For a complete set of what can be search see https://imapsync.lamiral.info/FAQ.d/FAQ.Messages_Selection.txt --search1 str : Same as --search but for selecting host1 messages only. --search2 str : Same as --search but for selecting host2 messages only. - --search CRIT equals --search1 CRIT --search2 CRIT + So --search CRIT equals --search1 CRIT --search2 CRIT --maxlinelength int : skip messages with a line length longer than int bytes. - RFC 2822 says it must be no more than 1000 bytes. + RFC 2822 says it must be no more than 1000 bytes but + real life servers and email clients do more. --useheader str : Use this header to compare messages on both sides. Ex: Message-ID or Subject or Date. --useheader str and this one, etc. - --usecache : Use cache to speed up the sync. + --usecache : Use cache to speed up next syncs. Not set by default. --nousecache : Do not use cache. Caveat: --useuid --nousecache creates duplicates on multiple runs. --useuid : Use UIDs instead of headers as a criterion to recognize @@ -361,10 +404,10 @@ Options/message selection: Options/miscellaneous: --syncacls : Synchronizes acls (Access Control Lists). - --nosyncacls : Does not synchronize acls. This is the default. Acls in IMAP are not standardized, be careful since one acl code on one side may signify something else on the other one. + --nosyncacls : Does not synchronize acls. This is the default. --addheader : When a message has no headers to be identified, --addheader adds a "Message-Id" header, @@ -392,17 +435,17 @@ Options/debugging: Options/specific: - --gmail1 : sets --host1 to Gmail and options from FAQ.Gmail.txt - --gmail2 : sets --host2 to Gmail and options from FAQ.Gmail.txt + --gmail1 : sets --host1 to Gmail and other options. See FAQ.Gmail.txt + --gmail2 : sets --host2 to Gmail and other options. See FAQ.Gmail.txt - --office1 : sets --host1 to Office365 options from FAQ.Exchange.txt - --office2 : sets --host2 to Office365 options from FAQ.Exchange.txt + --office1 : sets --host1 to Office365 and other options. See FAQ.Exchange.txt + --office2 : sets --host2 to Office365 and other options. See FAQ.Exchange.txt - --exchange1 : sets options from FAQ.Exchange.txt, account1 part - --exchange2 : sets options from FAQ.Exchange.txt, account2 part + --exchange1 : sets options for Exchange. See FAQ.Exchange.txt + --exchange2 : sets options for Exchange. See FAQ.Exchange.txt - --domino1 : sets options from FAQ.Domino.txt, account1 part - --domino2 : sets options from FAQ.Domino.txt, account2 part + --domino1 : sets options for Domino. See FAQ.Domino.txt + --domino2 : sets options for Domino. See FAQ.Domino.txt Options/behavior: @@ -418,11 +461,11 @@ Options/behavior: --abort : terminates a previous call still running. It uses the pidfile to know what process to abort. - --exitwhenover int : Stop syncing and exits when int total bytes + --exitwhenover int : Stop syncing and exits when int total bytes transferred is reached. --version : Print only software version. - --noreleasecheck : Do not check for new imapsync release + --noreleasecheck : Do not check for any new imapsync release. --releasecheck : Check for new imapsync release. it's an http request to http://imapsync.lamiral.info/prj/imapsync/VERSION @@ -433,7 +476,7 @@ Options/behavior: information. Need only --host1 and --host2 options. Obsolete since "imapsync --host1 imaphost" alone implies --justconnect - + --justlogin : Just login to both host1 and host2 with users credentials, then exit. @@ -450,271 +493,284 @@ Options/behavior: --host1 test1.lamiral.info --user1 test1 --password1 secret1 \ --host2 test2.lamiral.info --user2 test2 --password2 secret2 -Here is imapsync 1.945 on host petite, a linux system with 1.1/2.0 free GiB of RAM +Here is imapsync 1.977 on host petite, a linux system with 0.2/2.0 free GiB of RAM with Perl 5.22.1 and Mail::IMAPClient 3.38 -$Id: prereq.scandeps.Ubuntu_16.04_xenial.txt,v 1.5 2019/06/26 23:17:16 gilles Exp gilles $ -This imapsync is up to date. ( local 1.945 >= official 1.921 )( Use --noreleasecheck to avoid this release check. ) +$Id: prereq.scandeps.Ubuntu_16.04_xenial.txt,v 1.7 2020/01/03 22:44:51 gilles Exp gilles $ +This imapsync is up to date. ( local 1.977 >= official 1.945 )( Use --noreleasecheck to avoid this release check. ) Homepage: https://imapsync.lamiral.info/ -'Tie::Hash::NamedCapture' => '0.09', -'APR' => '0.009000', -'APR::XSLoader' => 'undef', -'Apache2::XSLoader' => 'undef', -'Authen::NTLM::DES' => '1.02', -'Authen::NTLM::MD4' => '1.02', -'GSSAPI' => '0.28', -'Crypt::Random::Seed' => '0.03', -'Math::Random::ISAAC' => '1.003', -'CGI::Cookie' => '4.26', -'CGI::File::Temp' => '4.26', -'APR::Pool' => '0.009000', -'Apache2::RequestIO' => '2.000009', -'Apache2::RequestRec' => '2.000009', -'Apache2::Response' => '2.000009', -'Apache2::RequestUtil' => '2.000009', -'CGI::Util' => '4.26', -'ModPerl::Util' => '2.000009', -'APR::Table' => '0.009000', -'Fh' => '4.26', -'Convert::ASN1::IO' => '0.27', -'Convert::ASN1::_decode' => '0.27', -'Convert::ASN1::_encode' => '0.27', -'Convert::ASN1::parser' => '0.27', -'Bytes::Random::Secure' => '0.28', -'Crypt::SSLeay::X509' => 'undef', -'Crypt::SSLeay::CTX' => 'undef', -'Digest::HMAC' => '1.03', -'Digest::Perl::MD5' => '1.9', -'Encode::HanExtra' => '0.23', -'HTML::Parser' => '3.72', -'HTTP::Headers' => '6.11', -'HTTP::Cookies::Netscape' => '6.00', -'IO::Compress::Bzip2' => '2.069', -'IO::Compress::Deflate' => '2.069', -'IO::Compress::Gzip' => '2.069', -'IO::HTML' => '1.001', -'IO::Uncompress::Bunzip2' => '2.069', -'IO::Uncompress::Inflate' => '2.069', -'IO::Uncompress::RawInflate' => '2.069', -'IO::Uncompress::Gunzip' => '2.069', -'HTTP::Message' => '6.11', -'Compress::Raw::Bzip2' => '2.069', -'File::GlobMapper' => '1.000', -'IO::Compress::Adapter::Bzip2' => '2.069', -'IO::Compress::Base' => '2.069', -'IO::Compress::RawDeflate' => '2.069', -'IO::Compress::Adapter::Deflate' => '2.069', -'IO::Compress::Base::Common' => '2.069', -'IO::Compress::Zlib::Constants' => '2.069', -'IO::Compress::Zlib::Extra' => '2.069', -'IO::Compress::Gzip::Constants' => '2.069', -'IO::Socket::SSL::PublicSuffix' => 'undef', -'Net::SSLeay' => '1.72', -'Socket6' => '0.25', -'IO::Uncompress::Adapter::Bunzip2' => '2.069', -'IO::Uncompress::Base' => '2.069', -'IO::Uncompress::Adapter::Inflate' => '2.069', -'JSON::WebToken::Constants' => 'undef', -'JSON::WebToken::Exception' => 'undef', -'Module::Runtime' => '0.014', -'JSON::WebToken::Crypt' => 'undef', -'Types::Serialiser' => '1.0', -'common::sense' => '3.74', -'CPAN::Config' => 'undef', -'URI::_foreign' => '1.71', -'URI::mailto' => '1.71', -'URI::data' => '1.71', -'URI::_query' => '1.71', -'URI' => '1.71', -'URI::QueryParam' => '1.71', -'URI::Split' => '1.71', -'URI::_segment' => '1.71', -'URI::file::FAT' => '1.71', -'URI::file::Mac' => '1.71', -'URI::file::OS2' => '1.71', -'URI::file::QNX' => '1.71', -'URI::ftp' => '1.71', -'URI::gopher' => '1.71', -'URI::https' => '1.71', -'URI::ldapi' => '1.71', -'URI::ldaps' => '1.71', -'URI::mms' => '1.71', -'URI::nntp' => '1.71', -'URI::pop' => '1.71', -'URI::rlogin' => '1.71', -'URI::rsync' => '1.71', -'URI::rtspu' => '1.71', -'URI::sftp' => '1.71', -'URI::sips' => '1.71', -'URI::snews' => '1.71', -'URI::telnet' => '1.71', -'URI::tn3270' => '1.71', -'URI::_punycode' => '1.71', -'URI::file::Win32' => '1.71', -'URI::file::Unix' => '1.71', -'URI::file::Base' => '1.71', -'URI::_ldap' => '1.71', -'URI::ldap' => '1.71', -'URI::rtsp' => '1.71', -'URI::ssh' => '1.71', -'URI::sip' => '1.71', -'URI::news' => '1.71', -'URI::IRI' => '1.71', -'URI::Heuristic' => '4.20', -'URI::_generic' => '1.71', -'URI::_login' => '1.71', -'URI::_idna' => '1.71', -'URI::_userpass' => '1.71', -'LWP::MediaTypes' => '6.02', -'File::Listing' => '6.04', -'HTTP::Negotiate' => '6.00', -'Net::HTTP' => '6.09', -'HTTP::Status' => '6.11', -'Net::HTTPS' => '6.09', -'Net::SSL' => '2.88', -'Net::LDAP::DSML' => '0.16', -'Net::LDAP' => '0.65', -'Net::LDAP::LDIF' => '0.26', -'Mail::Internet' => '2.13', -'HTML::HeadParser' => '3.71', -'HTTP::Config' => '6.11', -'HTTP::Request::Common' => '6.11', -'LWP::ConnCache' => '6.15', -'HTTP::Cookies' => '6.01', -'Encode::Locale' => '1.05', -'HTTP::Headers::Util' => '6.11', -'LWP::MemberMixin' => 'undef', -'LWP' => '6.15', -'HTTP::Date' => '6.02', -'HTTP::Request' => '6.11', -'LWP::Protocol' => '6.15', -'HTTP::Response' => '6.11', -'Mail::IMAPClient::MessageSet' => 'undef', -'Digest::HMAC_MD5' => '1.01', -'Authen::NTLM' => '1.09', -'Authen::SASL' => '2.16', -'Authen::SASL::CRAM_MD5' => '2.14', -'Authen::SASL::EXTERNAL' => '2.14', -'Authen::SASL::Perl' => '2.14', -'Authen::SASL::Perl::ANONYMOUS' => '2.14', -'Authen::SASL::Perl::CRAM_MD5' => '2.14', -'Authen::SASL::Perl::DIGEST_MD5' => '2.14', -'Authen::SASL::Perl::EXTERNAL' => '2.14', -'Authen::SASL::Perl::GSSAPI' => '0.05', -'Authen::SASL::Perl::LOGIN' => '2.14', -'Authen::SASL::Perl::PLAIN' => '2.14', -'Mail::Address' => '2.13', -'Mail::Header' => '2.13', -'Mail::Mailer' => '2.13', -'Mail::Util' => '2.13', -'Math::Random::ISAAC::PP' => '1.003', -'Math::Random::ISAAC::XS' => '1.004', -'Net::HTTP::Methods' => '6.09', -'Compress::Raw::Zlib' => '2.069', -'Net::LDAP::Bind' => '1.05', -'Net::LDAP::Extension' => '1.04', -'Net::LDAP::RootDSE' => '0.02', -'Net::LDAP::Search' => '0.14', -'Convert::ASN1::Debug' => '0.27', -'Convert::ASN1' => '0.27', -'Net::LDAP::Message' => '1.12', -'Net::LDAP::ASN' => '0.12', -'Net::LDAP::Constant' => '0.23', -'Net::LDAP::Filter' => '0.20', -'XML::SAX::Base' => '1.07', -'Net::LDAP::Schema' => '0.9908', -'Net::LDAP::Entry' => '0.27', -'Net::LDAP::Control' => '0.18', -'Net::LDAP::Util' => '0.19', -'Net::LDAP::Intermediate' => '0.04', -'Crypt::SSLeay::MainContext' => 'undef', -'Crypt::SSLeay' => '0.73_04', -'Email::Address' => '1.908', -'Test::Builder::IO::Scalar' => '2.113', -'UNIVERSAL::can' => '1.20140328', -'UNIVERSAL::isa' => '1.20150614', -'Test::Builder' => '1.001014', -'Test::Builder::Module' => '1.001014', -'URI::WithBase' => '2.20', -'URI::file' => '4.21', -'URI::_server' => '1.71', -'Unicode::CharName' => '1.07', -'XML::SAX::Exception' => '1.07', -'CGI::Carp' => '4.26', -'Data::Uniqid' => '0.12', -'Digest::HMAC_SHA1' => '1.03', -'File::Copy::Recursive' => '0.38', -'File::Tail' => '1.3', -'IO::Tee' => '0.64', -'JSON::WebToken' => '0.10', -'JSON::WebToken::Crypt::RSA' => 'undef', -'Mail::IMAPClient' => '3.38', -'Readonly' => '2.00', -'Sys::MemInfo' => '0.98', -'Term::ReadKey' => '2.33', -'Test::MockObject' => '1.20150527', -'Test::More' => '1.001014', -'Unicode::String' => '2.09', -'HTML::Entities' => '3.69', -'CGI' => '4.26', -'LWP::Authen::Digest' => 'undef', -'LWP::Authen::Ntlm' => '6.15', -'LWP::Protocol::GHTTP' => 'undef', -'LWP::Protocol::cpan' => 'undef', -'LWP::Protocol::data' => 'undef', -'LWP::Protocol::file' => 'undef', -'LWP::Protocol::ftp' => 'undef', -'LWP::Protocol::gopher' => 'undef', -'LWP::Protocol::https' => '6.06', -'LWP::Protocol::ldapi' => 'undef', -'LWP::Protocol::ldaps' => 'undef', -'LWP::Protocol::loopback' => 'undef', -'LWP::Protocol::mailto' => 'undef', -'LWP::Protocol::nntp' => 'undef', -'LWP::UserAgent' => '6.15', -'JSON::XS' => '3.01', -'JSON' => '2.90', -'JSON::XS::Boolean' => 'undef', -'Crypt::OpenSSL::RSA' => '0.28', -'LWP::Authen::Basic' => 'undef', -'URI::URL' => '5.04', -'URI::http' => '1.71', -'URI::Escape' => '3.31', -'LWP::Protocol::http' => 'undef', -'IO::Socket::SSL' => '2.047', -'LWP::Protocol::ldap' => '1.25', -'LWP::Protocol::nogo' => 'undef', -'IO::Socket::INET6' => '2.72', -'Regexp::Common::comment' => '2016020301', -'Regexp::Common::lingua' => '2016020301', -'Regexp::Common::zip' => '2016020301', -'Regexp::Common::net' => '2016020301', -'Regexp::Common::list' => '2016020301', -'Regexp::Common::URI::news' => '2016020301', -'Regexp::Common::URI::pop' => '2016020301', -'Regexp::Common::URI::tel' => '2016020301', -'Regexp::Common::profanity' => '2016020301', -'Regexp::Common::URI::ftp' => '2016020301', -'Regexp::Common::URI::fax' => '2016020301', -'Regexp::Common::URI::wais' => '2016020301', -'Regexp::Common::URI::RFC2396' => '2016020301', -'Regexp::Common::URI::prospero' => '2016020301', -'Regexp::Common::URI::tv' => '2016020301', -'Regexp::Common::URI::telnet' => '2016020301', -'Regexp::Common::Email::Address' => '1.01', -'Regexp::Common' => '2016020301', -'Regexp::Common::URI::RFC1808' => '2016020301', -'Regexp::Common::URI::RFC2806' => '2016020301', -'Regexp::Common::_support' => '2016020301', -'Regexp::Common::URI::gopher' => '2016020301', -'Regexp::Common::URI::RFC2384' => '2016020301', -'Regexp::Common::URI::RFC1738' => '2016020301', -'Regexp::Common::URI::RFC1035' => '2016020301', -'Regexp::Common::URI::file' => '2016020301', -'Regexp::Common::URI::http' => '2016020301', -'Regexp::Common::delimited' => '2016020301', -'Regexp::Common::SEN' => '2016020301', -'Regexp::Common::whitespace' => '2016020301', -'Regexp::Common::number' => '2016020301', -'Regexp::Common::balanced' => '2016020301', -'Regexp::Common::CC' => '2016020301', -'Regexp::Common::URI' => '2016020301', +'Encode::Unicode' => '2.09', +'Tie::Hash::NamedCapture' => '0.09', +'APR' => '0.009000', +'APR::XSLoader' => 'undef', +'Apache2::XSLoader' => 'undef', +'Authen::NTLM::DES' => '1.02', +'Authen::NTLM::MD4' => '1.02', +'GSSAPI' => '0.28', +'Crypt::Random::Seed' => '0.03', +'Math::Random::ISAAC' => '1.003', +'CGI::Cookie' => '4.26', +'CGI::File::Temp' => '4.26', +'APR::Pool' => '0.009000', +'Apache2::RequestIO' => '2.000009', +'Apache2::RequestRec' => '2.000009', +'Apache2::Response' => '2.000009', +'Apache2::RequestUtil' => '2.000009', +'CGI::Util' => '4.26', +'ModPerl::Util' => '2.000009', +'APR::Table' => '0.009000', +'Fh' => '4.26', +'Convert::ASN1::IO' => '0.27', +'Convert::ASN1::_decode' => '0.27', +'Convert::ASN1::_encode' => '0.27', +'Convert::ASN1::parser' => '0.27', +'Bytes::Random::Secure' => '0.28', +'Crypt::SSLeay::X509' => 'undef', +'Crypt::SSLeay::CTX' => 'undef', +'Digest::HMAC' => '1.03', +'Digest::Perl::MD5' => '1.9', +'Encode::HanExtra' => '0.23', +'HTML::Parser' => '3.72', +'HTTP::Cookies::Netscape' => '6.00', +'IO::Compress::Bzip2' => '2.069', +'IO::Compress::Deflate' => '2.069', +'IO::Compress::Gzip' => '2.069', +'IO::HTML' => '1.001', +'IO::Uncompress::Bunzip2' => '2.069', +'IO::Uncompress::Inflate' => '2.069', +'IO::Uncompress::RawInflate' => '2.069', +'IO::Uncompress::Gunzip' => '2.069', +'HTTP::Headers' => '6.11', +'HTTP::Message' => '6.11', +'Compress::Raw::Bzip2' => '2.069', +'File::GlobMapper' => '1.000', +'IO::Compress::Adapter::Bzip2' => '2.069', +'IO::Compress::Base' => '2.069', +'IO::Compress::RawDeflate' => '2.069', +'IO::Compress::Adapter::Deflate' => '2.069', +'IO::Compress::Zlib::Constants' => '2.069', +'Socket6' => '0.25', +'IO::Socket::SSL::PublicSuffix' => 'undef', +'Net::SSLeay' => '1.72', +'IO::Uncompress::Adapter::Bunzip2' => '2.069', +'IO::Compress::Zlib::Extra' => '2.069', +'IO::Compress::Gzip::Constants' => '2.069', +'IO::Compress::Base::Common' => '2.069', +'IO::Uncompress::Adapter::Inflate' => '2.069', +'IO::Uncompress::Base' => '2.069', +'JSON::WebToken::Constants' => 'undef', +'JSON::WebToken::Exception' => 'undef', +'Module::Runtime' => '0.014', +'JSON::WebToken::Crypt' => 'undef', +'Types::Serialiser' => '1.0', +'common::sense' => '3.74', +'Authen::NTLM' => '1.09', +'HTTP::Request::Common' => '6.11', +'URI::data' => '1.71', +'URI::_idna' => '1.71', +'URI::_foreign' => '1.71', +'URI::mailto' => '1.71', +'URI::_query' => '1.71', +'URI' => '1.71', +'URI::QueryParam' => '1.71', +'URI::Split' => '1.71', +'URI::_segment' => '1.71', +'URI::file::FAT' => '1.71', +'URI::file::Mac' => '1.71', +'URI::file::OS2' => '1.71', +'URI::file::QNX' => '1.71', +'URI::ftp' => '1.71', +'URI::gopher' => '1.71', +'URI::https' => '1.71', +'URI::ldapi' => '1.71', +'URI::ldaps' => '1.71', +'URI::mms' => '1.71', +'URI::nntp' => '1.71', +'URI::pop' => '1.71', +'URI::rlogin' => '1.71', +'URI::rsync' => '1.71', +'URI::rtspu' => '1.71', +'URI::sftp' => '1.71', +'URI::sips' => '1.71', +'URI::snews' => '1.71', +'URI::telnet' => '1.71', +'URI::tn3270' => '1.71', +'URI::file::Win32' => '1.71', +'URI::file::Unix' => '1.71', +'URI::file::Base' => '1.71', +'URI::_punycode' => '1.71', +'URI::IRI' => '1.71', +'URI::ldap' => '1.71', +'URI::news' => '1.71', +'URI::ssh' => '1.71', +'URI::rtsp' => '1.71', +'URI::sip' => '1.71', +'URI::_userpass' => '1.71', +'URI::_login' => '1.71', +'URI::Heuristic' => '4.20', +'URI::_ldap' => '1.71', +'URI::_generic' => '1.71', +'URI::_server' => '1.71', +'HTTP::Status' => '6.11', +'LWP::Protocol' => '6.15', +'HTTP::Response' => '6.11', +'CPAN::Config' => 'undef', +'HTTP::Date' => '6.02', +'LWP' => '6.15', +'LWP::MediaTypes' => '6.02', +'HTTP::Request' => '6.11', +'File::Listing' => '6.04', +'HTTP::Negotiate' => '6.00', +'Net::HTTP' => '6.09', +'Net::HTTPS' => '6.09', +'Net::SSL' => '2.88', +'Net::LDAP::DSML' => '0.16', +'Net::LDAP' => '0.65', +'Net::LDAP::LDIF' => '0.26', +'Mail::Internet' => '2.13', +'HTML::HeadParser' => '3.71', +'HTTP::Config' => '6.11', +'LWP::ConnCache' => '6.15', +'HTTP::Cookies' => '6.01', +'Encode::Locale' => '1.05', +'HTTP::Headers::Util' => '6.11', +'LWP::MemberMixin' => 'undef', +'Mail::IMAPClient::MessageSet' => 'undef', +'Digest::HMAC_MD5' => '1.01', +'Authen::SASL' => '2.16', +'Authen::SASL::CRAM_MD5' => '2.14', +'Authen::SASL::EXTERNAL' => '2.14', +'Authen::SASL::Perl' => '2.14', +'Authen::SASL::Perl::ANONYMOUS' => '2.14', +'Authen::SASL::Perl::CRAM_MD5' => '2.14', +'Authen::SASL::Perl::DIGEST_MD5' => '2.14', +'Authen::SASL::Perl::EXTERNAL' => '2.14', +'Authen::SASL::Perl::GSSAPI' => '0.05', +'Authen::SASL::Perl::LOGIN' => '2.14', +'Authen::SASL::Perl::PLAIN' => '2.14', +'Mail::Address' => '2.13', +'Mail::Header' => '2.13', +'Mail::Mailer' => '2.13', +'Mail::Util' => '2.13', +'Math::Random::ISAAC::PP' => '1.003', +'Math::Random::ISAAC::XS' => '1.004', +'Compress::Raw::Zlib' => '2.069', +'Net::HTTP::Methods' => '6.09', +'Net::LDAP::Bind' => '1.05', +'Net::LDAP::Extension' => '1.04', +'Net::LDAP::RootDSE' => '0.02', +'Net::LDAP::Search' => '0.14', +'Convert::ASN1::Debug' => '0.27', +'Convert::ASN1' => '0.27', +'Net::LDAP::Schema' => '0.9908', +'Net::LDAP::ASN' => '0.12', +'Net::LDAP::Entry' => '0.27', +'Net::LDAP::Filter' => '0.20', +'Net::LDAP::Message' => '1.12', +'Net::LDAP::Constant' => '0.23', +'XML::SAX::Base' => '1.07', +'Net::LDAP::Control' => '0.18', +'Net::LDAP::Util' => '0.19', +'Net::LDAP::Intermediate' => '0.04', +'Crypt::SSLeay::MainContext' => 'undef', +'Crypt::SSLeay' => '0.73_04', +'Email::Address' => '1.908', +'Test::Builder::IO::Scalar' => '2.113', +'UNIVERSAL::can' => '1.20140328', +'UNIVERSAL::isa' => '1.20150614', +'Test::Builder' => '1.001014', +'Test::Builder::Module' => '1.001014', +'URI::file' => '4.21', +'URI::WithBase' => '2.20', +'Unicode::CharName' => '1.07', +'XML::SAX::Exception' => '1.07', +'CGI::Carp' => '4.26', +'Data::Uniqid' => '0.12', +'Digest::HMAC_SHA1' => '1.03', +'Encode::IMAPUTF7' => '1.05', +'File::Copy::Recursive' => '0.38', +'File::Tail' => '1.3', +'IO::Tee' => '0.64', +'JSON::WebToken' => '0.10', +'JSON::WebToken::Crypt::RSA' => 'undef', +'Mail::IMAPClient' => '3.38', +'Readonly' => '2.00', +'Sys::MemInfo' => '0.98', +'Term::ReadKey' => '2.33', +'Test::MockObject' => '1.20150527', +'Test::More' => '1.001014', +'Unicode::String' => '2.09', +'HTML::Entities' => '3.69', +'CGI' => '4.26', +'Crypt::OpenSSL::RSA' => '0.28', +'LWP::Authen::OAuth2' => '0.16', +'LWP::Authen::Basic' => 'undef', +'LWP::Authen::Digest' => 'undef', +'LWP::Authen::Ntlm' => '6.15', +'LWP::Authen::OAuth2::ServiceProvider::Dwolla' => 'undef', +'LWP::Authen::OAuth2::ServiceProvider::Google' => '0.02', +'LWP::Authen::OAuth2::ServiceProvider::Line' => 'undef', +'LWP::Authen::OAuth2::ServiceProvider::Line::AccessToken' => 'undef', +'LWP::Authen::OAuth2::ServiceProvider::Strava' => '0.02', +'LWP::Authen::OAuth2::ServiceProvider::Yahoo' => '0.01', +'LWP::Protocol::GHTTP' => 'undef', +'LWP::Protocol::cpan' => 'undef', +'LWP::Protocol::data' => 'undef', +'LWP::Protocol::file' => 'undef', +'LWP::Protocol::ftp' => 'undef', +'LWP::Protocol::gopher' => 'undef', +'LWP::Protocol::https' => '6.06', +'LWP::Protocol::ldapi' => 'undef', +'LWP::Protocol::ldaps' => 'undef', +'LWP::Protocol::loopback' => 'undef', +'LWP::Protocol::mailto' => 'undef', +'LWP::Protocol::nntp' => 'undef', +'LWP::UserAgent' => '6.15', +'LWP::Protocol::nogo' => 'undef', +'LWP::Authen::OAuth2::AccessToken' => '0.02', +'LWP::Authen::OAuth2::Args' => 'undef', +'JSON::XS' => '3.01', +'JSON' => '2.90', +'JSON::XS::Boolean' => 'undef', +'URI::URL' => '5.04', +'URI::http' => '1.71', +'LWP::Authen::OAuth2::ServiceProvider' => '0.02', +'LWP::Authen::OAuth2::AccessToken::Bearer' => '0.02', +'LWP::Protocol::http' => 'undef', +'IO::Socket::SSL' => '2.047', +'LWP::Protocol::ldap' => '1.25', +'IO::Socket::INET6' => '2.72', +'Regexp::Common::URI::pop' => '2016020301', +'Regexp::Common::URI::tel' => '2016020301', +'Regexp::Common::URI::ftp' => '2016020301', +'Regexp::Common::URI::fax' => '2016020301', +'Regexp::Common::URI::wais' => '2016020301', +'Regexp::Common::URI::RFC2396' => '2016020301', +'Regexp::Common::URI::prospero' => '2016020301', +'Regexp::Common::URI::tv' => '2016020301', +'Regexp::Common::URI::telnet' => '2016020301', +'Regexp::Common::profanity' => '2016020301', +'Regexp::Common::comment' => '2016020301', +'Regexp::Common::lingua' => '2016020301', +'Regexp::Common::zip' => '2016020301', +'Regexp::Common::net' => '2016020301', +'Regexp::Common::list' => '2016020301', +'Regexp::Common::URI::news' => '2016020301', +'Regexp::Common::URI::http' => '2016020301', +'Regexp::Common' => '2016020301', +'Regexp::Common::URI::RFC1808' => '2016020301', +'Regexp::Common::URI::RFC2806' => '2016020301', +'Regexp::Common::_support' => '2016020301', +'Regexp::Common::delimited' => '2016020301', +'Regexp::Common::SEN' => '2016020301', +'Regexp::Common::whitespace' => '2016020301', +'Regexp::Common::number' => '2016020301', +'Regexp::Common::balanced' => '2016020301', +'Regexp::Common::CC' => '2016020301', +'Regexp::Common::URI' => '2016020301', +'Regexp::Common::Email::Address' => '1.01', +'Regexp::Common::URI::gopher' => '2016020301', +'Regexp::Common::URI::RFC2384' => '2016020301', +'Regexp::Common::URI::RFC1738' => '2016020301', +'Regexp::Common::URI::RFC1035' => '2016020301', +'Regexp::Common::URI::file' => '2016020301', +'URI::Escape' => '3.31', diff --git a/W/t/07_ll_regexmess_add_header.txt b/W/t/07_ll_regexmess_add_header.txt new file mode 100644 index 0000000..79bb626 --- /dev/null +++ b/W/t/07_ll_regexmess_add_header.txt @@ -0,0 +1,38 @@ +X-migrated-from-foo: 20100617 +Delivered-To: gilles.lamiral@gmail.com +Received: by 10.216.17.5 with SMTP id i5cs170544wei; + Fri, 4 Sep 2009 21:56:09 -0700 (PDT) +Received: by 10.220.79.140 with SMTP id p12mr1652453vck.57.1252126565558; + Fri, 04 Sep 2009 21:56:05 -0700 (PDT) +Return-Path: +Received: from mx09.roch.ny.frontiernet.net (mx09.roch.ny.frontiernet.net [66.133.183.226]) + by mx.google.com with ESMTP id 7si2214072vws.26.2009.09.04.21.55.33; + Fri, 04 Sep 2009 21:56:05 -0700 (PDT) +Received-SPF: pass (google.com: domain of nicomonkey118@frontiernet.net designates 66.133.183.226 as permitted sender) client-ip=66.133.183.226; +Authentication-Results: mx.google.com; spf=pass (google.com: domain of nicomonkey118@frontiernet.net designates 66.133.183.226 as permitted sender) smtp.mail=nicomonkey118@frontiernet.net +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: AhOoAIyLoUpChbqH/2dsb2JhbACBNIpzBIQMiSaCWoYGqDWEEIpdhBYFgVeJEQI +X-Header: [4] +Received: from relay03.roch.ny.frontiernet.net ([66.133.182.166]) + by mx09.roch.ny.frontiernet.net with ESMTP; 05 Sep 2009 04:55:13 +0000 +Received: from cl05-zms04.roch.ny.frontiernet.net (cl05-host04.roch.ny.frontiernet.net [66.133.186.135]) + by relay03.roch.ny.frontiernet.net (Postfix) with ESMTP id AC7CC10056; + Sat, 5 Sep 2009 04:55:11 +0000 (UTC) +Date: Sat, 5 Sep 2009 04:55:11 +0000 (UTC) +From: Toyota Awards +Reply-To: Toyota Awards +Message-ID: <165391284.448761252126511648.JavaMail.root@cl05-host04.roch.ny.frontiernet.net> +Subject: add_some_header_please +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit +X-Originating-IP: [65.120.56.2] +X-Mailer: Zimbra 5.0.13_GA_2791.UBUNTU8_64 (zclient/5.0.13_GA_2791.UBUNTU8_64) +X-Authenticated-User: nicomonkey118@frontiernet.net +To: undisclosed-recipients:; +X-Virus-Scanned: ClamAV 0.94.2/9777/Fri Sep 4 22:17:01 2009 on relay03.roch.ny.frontiernet.net +X-Virus-Status: Clean + + + +You were given 750,000.00 Pounds by the toyota global company, send Name,Address,Occupation diff --git a/W/t/08_ll_regexmess_add_header_path.txt b/W/t/08_ll_regexmess_add_header_path.txt new file mode 100644 index 0000000..85d5eee --- /dev/null +++ b/W/t/08_ll_regexmess_add_header_path.txt @@ -0,0 +1,38 @@ +X-ImapSync-OriginalPath-tata: INBOX.yop.blanc blanc +Delivered-To: gilles.lamiral@gmail.com +Received: by 10.216.17.5 with SMTP id i5cs170544wei; + Fri, 4 Sep 2009 21:56:09 -0700 (PDT) +Received: by 10.220.79.140 with SMTP id p12mr1652453vck.57.1252126565558; + Fri, 04 Sep 2009 21:56:05 -0700 (PDT) +Return-Path: +Received: from mx09.roch.ny.frontiernet.net (mx09.roch.ny.frontiernet.net [66.133.183.226]) + by mx.google.com with ESMTP id 7si2214072vws.26.2009.09.04.21.55.33; + Fri, 04 Sep 2009 21:56:05 -0700 (PDT) +Received-SPF: pass (google.com: domain of nicomonkey118@frontiernet.net designates 66.133.183.226 as permitted sender) client-ip=66.133.183.226; +Authentication-Results: mx.google.com; spf=pass (google.com: domain of nicomonkey118@frontiernet.net designates 66.133.183.226 as permitted sender) smtp.mail=nicomonkey118@frontiernet.net +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: AhOoAIyLoUpChbqH/2dsb2JhbACBNIpzBIQMiSaCWoYGqDWEEIpdhBYFgVeJEQI +X-Header: [4] +Received: from relay03.roch.ny.frontiernet.net ([66.133.182.166]) + by mx09.roch.ny.frontiernet.net with ESMTP; 05 Sep 2009 04:55:13 +0000 +Received: from cl05-zms04.roch.ny.frontiernet.net (cl05-host04.roch.ny.frontiernet.net [66.133.186.135]) + by relay03.roch.ny.frontiernet.net (Postfix) with ESMTP id AC7CC10056; + Sat, 5 Sep 2009 04:55:11 +0000 (UTC) +Date: Sat, 5 Sep 2009 04:55:11 +0000 (UTC) +From: Toyota Awards +Reply-To: Toyota Awards +Message-ID: <165391284.448761252126511648.JavaMail.root@cl05-host04.roch.ny.frontiernet.net> +Subject: add_some_header_please +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 7bit +X-Originating-IP: [65.120.56.2] +X-Mailer: Zimbra 5.0.13_GA_2791.UBUNTU8_64 (zclient/5.0.13_GA_2791.UBUNTU8_64) +X-Authenticated-User: nicomonkey118@frontiernet.net +To: undisclosed-recipients:; +X-Virus-Scanned: ClamAV 0.94.2/9777/Fri Sep 4 22:17:01 2009 on relay03.roch.ny.frontiernet.net +X-Virus-Status: Clean + + + +You were given 750,000.00 Pounds by the toyota global company, send Name,Address,Occupation diff --git a/W/test.bat b/W/test.bat old mode 100644 new mode 100755 diff --git a/W/test2.bat b/W/test2.bat old mode 100644 new mode 100755 diff --git a/W/test3.bat b/W/test3.bat old mode 100644 new mode 100755 diff --git a/W/test3_boxon.bat b/W/test3_boxon.bat old mode 100644 new mode 100755 diff --git a/W/test3_gmail.bat b/W/test3_gmail.bat old mode 100644 new mode 100755 diff --git a/W/test3_longdir.bat b/W/test3_longdir.bat old mode 100644 new mode 100755 diff --git a/W/test4.bat b/W/test4.bat old mode 100644 new mode 100755 diff --git a/W/test6.bat b/W/test6.bat new file mode 100755 index 0000000..f83e1b5 --- /dev/null +++ b/W/test6.bat @@ -0,0 +1,9 @@ + + +SET +CD /D %~dp0 +REM perl imapsync --testsunit tests_kill_zero +REM perl imapsync --testsunit tests_killpid + +perl imapsync --tests + diff --git a/W/test_abort.bat b/W/test_abort.bat new file mode 100755 index 0000000..e12d081 --- /dev/null +++ b/W/test_abort.bat @@ -0,0 +1,18 @@ + + +SET +CD /D %~dp0 +REM perl imapsync --testsunit tests_kill_zero +REM perl imapsync --testsunit tests_killpid + +START perl imapsync --host1 p --user1 tata --passfile1 secret.tata --host2 p --user2 titi --passfile2 secret.titi --simulong 9 +ping -n 5 127.0.0.1 >nul +perl imapsync --host1 p --user1 tata --passfile1 secret.tata --host2 p --user2 titi --passfile2 secret.titi --abort + + + + + + + + diff --git a/W/test_cook_exe.bat b/W/test_cook_exe.bat old mode 100644 new mode 100755 diff --git a/W/test_cook_src.bat b/W/test_cook_src.bat old mode 100644 new mode 100755 diff --git a/W/test_exe.bat b/W/test_exe.bat old mode 100644 new mode 100755 index 578ee6a..fd0f5f8 --- a/W/test_exe.bat +++ b/W/test_exe.bat @@ -1,4 +1,4 @@ -REM $Id: test_exe.bat,v 1.22 2019/05/28 13:20:08 gilles Exp gilles $ +REM $Id: test_exe.bat,v 1.24 2019/12/11 18:56:40 gilles Exp gilles $ @SETLOCAL @ECHO OFF @@ -39,14 +39,14 @@ CALL :handle_error CALL :launch_imapsync --ssl1 --ssl2 --delete2 CALL :handle_error CALL :launch_imapsync --ssl1 --ssl2 --delete2 --folder INBOX CALL :handle_error CALL :launch_imapsync --ssl1 --ssl2 --delete2 --folder INBOX --usecache -ENDLOCAL @REM Do a PAUSE if run by double-click, aka, explorer (then ). No PAUSE in a DOS window or via ssh. IF %0 EQU "%~dpnx0" IF "%SSH_CLIENT%"=="" PAUSE +@ENDLOCAL EXIT /B :handle_error -SETLOCAL +@SETLOCAL ECHO IN %0 %* %* SET CMD_RETURN=%ERRORLEVEL% @@ -58,11 +58,11 @@ IF %CMD_RETURN% EQU 0 ( IF NOT EXIST LOG_bat MKDIR LOG_bat ECHO Failure calling %* >> LOG_bat\%~nx0.txt ) -ENDLOCAL +@ENDLOCAL EXIT /B :handle_bad_success -SETLOCAL +@SETLOCAL ECHO IN %0 %* %* SET CMD_RETURN=%ERRORLEVEL% @@ -74,7 +74,7 @@ IF %CMD_RETURN% NEQ 0 ( IF NOT EXIST LOG_bat MKDIR LOG_bat ECHO No failure calling %* >> LOG_bat\%~nx0.txt ) -ENDLOCAL +@ENDLOCAL EXIT /B diff --git a/W/test_exe_2.bat b/W/test_exe_2.bat old mode 100644 new mode 100755 diff --git a/W/test_exe_tests.bat b/W/test_exe_tests.bat old mode 100644 new mode 100755 index 60d531a..0141c8a --- a/W/test_exe_tests.bat +++ b/W/test_exe_tests.bat @@ -1,4 +1,4 @@ -@REM $Id: test_exe_tests.bat,v 1.3 2019/05/28 13:20:08 gilles Exp gilles $ +@REM $Id: test_exe_tests.bat,v 1.4 2019/11/25 12:41:39 gilles Exp gilles $ @SETLOCAL @ECHO OFF diff --git a/W/test_ipv6.bat b/W/test_ipv6.bat new file mode 100755 index 0000000..c480dd0 --- /dev/null +++ b/W/test_ipv6.bat @@ -0,0 +1,12 @@ + + + + +@REM the following command change current directory to the dirname of the current batch pathname +CD /D %~dp0 + +perl .\imapsync --host1 imap.gmail.com --host2 ks2ipv6.lamiral.info --justconnect + +PAUSE + +.\imapsync.exe --host1 imap.gmail.com --host2 ks2ipv6.lamiral.info --justconnect diff --git a/W/test_reg.bat b/W/test_reg.bat old mode 100644 new mode 100755 diff --git a/W/test_tail.bat b/W/test_tail.bat new file mode 100755 index 0000000..7a9e743 --- /dev/null +++ b/W/test_tail.bat @@ -0,0 +1,18 @@ + + +SET +CD /D %~dp0 +REM perl imapsync --testsunit tests_kill_zero +REM perl imapsync --testsunit tests_killpid + +START perl imapsync --host1 p --user1 tata --passfile1 secret.tata --host2 p --user2 titi --passfile2 secret.titi --pidfilelocking --simulong 22 --justlogin +ping -n 5 127.0.0.1 >nul +perl imapsync --host1 p --user1 tata --passfile1 secret.tata --host2 p --user2 titi --passfile2 secret.titi --pidfilelocking --tail --justlogin + + + + + + + + diff --git a/W/test_tests.bat b/W/test_tests.bat old mode 100644 new mode 100755 index 52bc1c0..388aa85 --- a/W/test_tests.bat +++ b/W/test_tests.bat @@ -1,4 +1,4 @@ -@REM $Id: test_tests.bat,v 1.6 2019/05/28 13:20:08 gilles Exp gilles $ +@REM $Id: test_tests.bat,v 1.7 2019/11/25 12:44:43 gilles Exp gilles $ @SETLOCAL @ECHO OFF diff --git a/W/test_testsdebug.bat b/W/test_testsdebug.bat old mode 100644 new mode 100755 index b90b591..0a47431 --- a/W/test_testsdebug.bat +++ b/W/test_testsdebug.bat @@ -1,4 +1,4 @@ -@REM $Id: test_testsdebug.bat,v 1.3 2017/07/08 00:02:13 gilles Exp gilles $ +@REM $Id: test_testsdebug.bat,v 1.4 2019/11/25 12:44:59 gilles Exp gilles $ @SETLOCAL @ECHO OFF diff --git a/W/test_xoauth2.bat b/W/test_xoauth2.bat old mode 100644 new mode 100755 diff --git a/W/tools/gen_README_dist b/W/tools/gen_README_dist index cdcfe89..32e10f0 100755 --- a/W/tools/gen_README_dist +++ b/W/tools/gen_README_dist @@ -1,17 +1,16 @@ #!/bin/sh -# $Id: gen_README_dist,v 1.2 2017/09/11 02:19:34 gilles Exp gilles $ +# $Id: gen_README_dist,v 1.4 2020/01/04 09:16:08 gilles Exp gilles $ -VERSION_UNX=`cat VERSION` -#echo $VERSION_UNX -VERSION_EXE=`cat VERSION_EXE` -#echo $VERSION_EXE + +VERSION=`cat VERSION` +#echo $VERSION cat < /dev/null; then echo html5check.py -h "$1" - html5check.py -h "$1" + html5check.py -h "$1" + html5check.py -h "$1" | grep 'The document is valid HTML5' > /dev/null return $? fi if expr match "$type" '.*application/xml.*' > /dev/null; then @@ -19,17 +20,26 @@ validate_xml_html5_one() { xmllint --noout "$1" return $? fi + if expr match "$type" '.*inode/symlink.*' > /dev/null; then + echo ignore $1 since it is a symlink + return 0 + fi echo Unknown type return 1 } - +nb_failures=0 for f in "$@"; do validate_xml_html5_one "$f" cmd_status=$? echo cmd_status = $cmd_status - test 0 != $cmd_status && return $cmd_status + test 0 != $cmd_status && { + nb_failures=`expr 1 + $nb_failures` + files_failed="$files_failed $f" + } done +echo Found $nb_failures failures $files_failed +return $nb_failures : # if here then good return diff --git a/X/cgi_memo b/X/cgi_memo old mode 100755 new mode 100644 index 1def8ed..c7d7c17 --- a/X/cgi_memo +++ b/X/cgi_memo @@ -1,6 +1,6 @@ #!/bin/sh -# $Id: cgi_memo,v 1.48 2019/06/25 16:49:11 gilles Exp gilles $ +# $Id: cgi_memo,v 1.54 2020/01/04 00:13:49 gilles Exp gilles $ if test -n "$1"; then echoq() { echo "$@" ; } # not quiet mode @@ -38,18 +38,6 @@ total_volume_transferred() { bytestohuman `total_bytes_transferred` } -echoq mean_bytes_transferred -mean_bytes_transferred() { - nb_transfers_ended=`wc -l < transfers_sizes_in_bytes.txt` - total_bytes_transferred=`total_bytes_transferred` - echo "$total_bytes_transferred / $nb_transfers_ended" | bc -} - -echoq mean_volume_transferred -mean_volume_transferred() { - bytestohuman `mean_bytes_transferred` - -} echoq total_messages_transferred total_messages_transferred() { @@ -86,10 +74,13 @@ oom_immune_imapsync_running() { test -f /proc/$pid/oom_adj || continue echo -n "$pid " cat /proc/$pid/oom_* | tr '\n' ' ' - { test -f /proc/$pid/oom_adj && echo -12 > /proc/$pid/oom_adj && echo -n ">>> " && cat /proc/$pid/oom_adj ; } + { test -f /proc/$pid/oom_adj && echo ${1:-"-12"} > /proc/$pid/oom_adj && echo -n ">>> " && cat /proc/$pid/oom_adj ; } done } + + + echoq nb_migrations_launched nb_migrations_launched() { /bin/ls . | egrep [a-f0-9]{40} | wc -l @@ -169,7 +160,13 @@ stat_patterns_list() { } - +stat_useragent_X() +{ + grep -o 'HTTP_USER_AGENT.*' G_HTTP_USER_AGENT.txt \ + | tail -10000000 | sort | egrep -o -w 'Mozilla/5.0 \([^;]+' \ + | sort | egrep -o '\([a-zA-Z]+' | sort | uniq -c | sort -g \ + | grep -v KHTML | tr -d '(' +} stat_load() { @@ -182,6 +179,23 @@ stat_load() echo -n 'Load max: ' ; datamash --format=%3.1f -W max 3 max 4 max 5 < G_Load.txt } +echoq stat_exit_value +stat_exit_value() +{ + good_lines=`grep '(EX' G_Exiting_with_return_value.txt | wc -l | tr -d ' '` + grep '(EX' G_Exiting_with_return_value.txt \ + | datamash --sort groupby 6 -W count 5 \ + | awk -v good_lines=$good_lines \ + '{ printf "%.2g%% %s\n", 100*$2/good_lines, $1 }' \ + | sort -n +} + +echoq stat_exit_value_by_value +stat_exit_value_by_value() +{ + datamash --sort groupby 5 -W count 5 < G_Exiting_with_return_value.txt +} + datamash_file_op_index() { @@ -209,20 +223,39 @@ stat_any() { echo } + + echoq stat_all -stat_all() +stat_all() { stat_load ; echo + stat_useragent_X ; echo # stat_any G_REMOTE_ADDR.txt # stat_any G_REMOTE_HOST.txt # stat_any G_HTTP_COOKIE.txt - # stat_any G_HTTP_USER_AGENT.txt # stat_any G_HTTP_REFERER.txt - # stat_any G_Host__IMAP_server.txt - # stat_any G_Host__banner.txt + + # See various_usefull() + # stat_any G_Host1_IMAP_server.txt + # stat_any G_Host2_IMAP_server.txt + + # stat_any G_Host1_banner.txt + # stat_any G_Host2_banner.txt + + stat_any G_Host1_Nb_messages.txt + stat_any G_Host2_Nb_messages.txt stat_any G_Messages_transferred.txt stat_any G_Messages_skipped.txt + stat_any G_Messages_found_in_host1_not_in_host2.txt 9 + stat_any G_Messages_found_in_host2_not_in_host1.txt 9 + # stat_any G_Folders_synced.txt + egrep -o '[0-9]+/[0-9]+' G_Folders_synced.txt | egrep -o '^[0-9]+' > G_Folders_synced_.txt + egrep -o '[0-9]+/[0-9]+' G_Folders_synced.txt | egrep -o '[0-9]+$' > G_Folders_total_seen.txt + stat_any G_Folders_synced_.txt 1 + stat_any G_Folders_total_seen.txt 1 + + # stat_any G_Transfer_time.txt stat_any G_Total_bytes_transferred.txt 5 stat_any G_Message_rate.txt @@ -231,9 +264,9 @@ stat_all() stat_any G_Detected_errors.txt 2 stat_any G_Exiting_with_return_value.txt 5 # GROUP stat_any G_Memory_consumption_at_the_end.txt 7 - stat_any G_Messages_found_in_host1_not_in_host2.txt 9 - stat_any G_Messages_found_in_host2_not_in_host1.txt 9 #stat_any G_failure_Error_login.txt + stat_exit_value + echo "Data made at" `date -r grep_stats.txt` } @@ -274,12 +307,12 @@ logfiles_finished_recently() # -3 less than 3 days ago # 7 exactly 7 days ago #set -x - find . -maxdepth 1 -mtime "${1:--1}" | grep -v "385d7a4d8d428d7aa2b57c8982629e2bd67698ed" | egrep [a-f0-9]{40} | while read f; do - test -f "$f" && continue - test -f $f/imapsync.pid && continue - test -d $f/LOG_imapsync || continue + find . -maxdepth 1 -mtime "${1:--1}" | grep -v "385d7a4d8d428d7aa2b57c8982629e2bd67698ed" | egrep [a-f0-9]{40} | while read d; do + test -f "$d" && continue + test -f $d/imapsync.pid && continue + test -d $d/LOG_imapsync || continue # { ls -trb $f/LOG_imapsync/* ; } - find $f/LOG_imapsync/ -type f -mtime "${1:--1}" + ls -trb `find $d/LOG_imapsync/ -type f -mtime "${1:--1}"` done } } @@ -587,14 +620,21 @@ ratio_killed_by_TERM() { } echoq 'nb_syncs_badly_finished -1 # last 1 day' -nb_syncs_badly_finished() { - logfiles_finished_recently=`logfiles_finished_recently $1` - nb_logfiles_finished_recently=`echo $logfiles_finished_recently | wc -w` - nb_syncs_badly_finished=`echo $logfiles_finished_recently | xargs grep -i 'Exiting with return value' | grep -v 'return value 0' | wc -l ` - echo $nb_syncs_badly_finished / $nb_logfiles_finished_recently +nb_syncs_badly_finished() +{ + logfiles_finished_recently=`logfiles_finished_recently $1` + nb_logfiles_finished_recently=`echo $logfiles_finished_recently | wc -w | tr -d ' '` + nb_syncs_badly_finished=`echo $logfiles_finished_recently | xargs grep -i 'Exiting with return value' | grep -v 'return value 0' | wc -l ` + echo $nb_syncs_badly_finished / $nb_logfiles_finished_recently \ + | awk '{ printf "%s %.2g%% %s\n", "Total:", 100*$1/$3, $0 }' + + echo $logfiles_finished_recently | xargs grep -i 'Exiting with return value' \ + | grep -v 'return value 0' | grep -o 'Exiting with return value.*)' \ + | sort | uniq -c | sort -n \ + | awk -v nb_logfiles_finished_recently=$nb_logfiles_finished_recently \ + '{ printf "%.2g%% %s\n", 100*$1/nb_logfiles_finished_recently, $0 }' cat < $patterns_alone_file + cat $patterns_file | + while read imap_server pattern + do + echo "$pattern" >> $patterns_alone_file + done + +} + +count_imap_server_all() +{ + count_imap_server_all=0 + cat $patterns_file | + while read imap_server pattern + do + #echo count_imap_server "$pattern" "$banners_files" + count_imap_server=`count_expression "$pattern" "$banners_files"` + count_imap_server_all=`expr $count_imap_server_all + $count_imap_server` + echo $count_imap_server_all + done + +} + +echoq server_survey_percent +server_survey_percent() +{ + + banners_files=${1:-G_Host1_banner.txt} + patterns_file=${2:-server_survey_patterns.txt} + + patterns_alone_file=${patterns_file}.alone.txt + + patterns_alone_file_generate $patterns_file $patterns_alone_file + + banners_counted=`egrep -f $patterns_alone_file $banners_files | wc -l | tr -d ' \n'` + banners_not_counted=`egrep -v -f $patterns_alone_file $banners_files | wc -l | tr -d ' \n'` + banners_all=`cat $banners_files | wc -l | tr -d ' \n'` + banners_all_verif=`expr $banners_not_counted + $banners_counted` + cat $patterns_file | + while read imap_server pattern + do + #echo count_imap_server "$pattern" "$banners_files" + count_imap_server=`count_expression "$pattern" "$banners_files"` + percent_imap_server=`echo "scale=2; 100 * $count_imap_server/$banners_all" | bc -l` + echo $percent_imap_server% : $count_imap_server " : $imap_server : " "[$pattern]" + done | sort -n + +} + +echoq server_survey +server_survey() +{ + banners_files=${1:-G_Host1_banner.txt} + patterns_file=${2:-server_survey_patterns.txt} + + server_survey_percent $banners_files $patterns_file + + count_imap_server_all=`count_imap_server_all | tail -1` + + echo $banners_files + echo "Banners counted sum $count_imap_server_all" + echo "Banners counted $banners_counted" + echo "Banners not counted $banners_not_counted" + echo "Banners all $banners_all" + echo "Banners all verif $banners_all_verif = $banners_not_counted + $banners_counted" + if test $count_imap_server_all != $banners_counted + then echo WARNING count_imap_server_all $count_imap_server_all != $banners_counted banners_counted \ + diff `expr $count_imap_server_all - $banners_counted` + fi + echo "server_survey $banners_files # finished" +} + +echoq server_survey_next_pattern +server_survey_next_pattern() +{ + patterns_alone_file_generate server_survey_patterns.txt server_survey_patterns.txt.alone.txt + grep -h -o 'banner:.*' G_Host?_banner.txt |sort | uniq -c | sort -g > banner_counted_sorted.txt + egrep -v -f server_survey_patterns.txt.alone.txt banner_counted_sorted.txt + +} + +echoq server_survey_last_pattern +server_survey_last_pattern() +{ + banners_files1=${1:-G_Host1_banner.txt} + banners_files2=${2:-G_Host2_banner.txt} + + tail -1 server_survey_patterns.txt > pattern_alone_file.txt + server_survey $banners_files1 pattern_alone_file.txt + server_survey $banners_files2 pattern_alone_file.txt +} + +echoq server_survey_host1 +server_survey_host1() +{ + server_survey G_Host1_banner.txt +} + +echoq server_survey_host2 +server_survey_host2() +{ + server_survey G_Host2_banner.txt +} + + + + echoq summary_display summary_display() { vnstat_gen > /dev/null @@ -673,16 +833,12 @@ sync_ks2_i005() && pwd } -echoq watch_number_of_imapsync_running -watch_number_of_imapsync_running() -{ - date_space - while number_of_imapsync_running | tr -d ' \n' - do - sleep 6 - date_if_new_hour - done +date_space() +{ + date | tr -d '\n' + echo -n " " + } #echoq date_if_new_hour @@ -699,32 +855,49 @@ date_if_new_hour() fi } -date_space() +echoq watch_number_of_imapsync_running +watch_number_of_imapsync_running() { - date | tr -d '\n' - echo -n " " - + date_space + while number_of_imapsync_running | tr -d ' \n' + do + sleep 6 + date_if_new_hour + done + } echoq various_usefull various_usefull() { cat <<'EOF' -sort -k5 -n grep_Messages_transferred____.txt -sort -k5 -n grep_Memory_consumption___.txt -sort -k5 -n grep_Average_bandwidth_rate__.txt -sort -k4 -n grep_Transfer_time____.txt -strace -e trace=signal -f `pgrep apache | xargs -n1 echo -n " -p "` 2>&1 -logfiles_finished_recently -1 | xargs grep -i 'Exiting with return value' | grep -v 'return value 0' -egrep -o 'Host1: IMAP server \[[^]]+\]' G_Host__IMAP_server.txt | sort | uniq -c | sort -n | tail -15 -egrep -o 'Host2: IMAP server \[[^]]+\]' G_Host__IMAP_server.txt | sort | uniq -c | sort -n | tail -15 - -egrep -o '[0-9]+/[0-9]+' G_Folders_synced.txt | sort -n +strace -e trace=signal -f `pgrep /usr/sbin/apach | xargs -n1 echo -n " -p "` 2>&1 +egrep -o '[0-9]+/[0-9]+' G_Folders_synced.txt | sort -g +egrep -o '[0-9]+/[0-9]+' G_Folders_synced.txt | sort -t/ -g -k2 | uniq -c +egrep -o '* ID .*' G_Read___ID.txt | sort | uniq -c | sort -n +egrep -o 'imapsync_runs=[0-9]+' G_HTTP_COOKIE.txt | egrep -o '[0-9]+' | sort -n | uniq -c | sort -g -k1,2 +egrep -o 'HTTP_REFERER is .*' G_HTTP_REFERER.txt | sort -g | uniq -c | sort -g +egrep -o 'REMOTE_HOST is .*' G_REMOTE_HOST.txt | sort -g | uniq -c | sort -g +egrep -o 'REMOTE_ADDR is .*' G_REMOTE_ADDR.txt | sort -g | uniq -c | sort -g +datamash -s -W -g 4 count 4 < G_Host1_IMAP_server.txt | awk '{ print $2 " " $1 }' | sort -g | tail -22 +datamash -s -W -g 4 count 4 < G_Host2_IMAP_server.txt | awk '{ print $2 " " $1 }' | sort -g | tail -22 +egrep -o '* ID .*' G_Read___ID.txt | sort | awk '{ print $1 " " $2 " " $3 " NIL" }' | datamash -s -W -g 3 count 3 | awk '{ print $2 " " $1 }' | sort -g +locate perl.core | xargs -n 1 gdb -q -x /tmp/gdb_quit.txt -c EOF } -# hosts used and counted -# grep Host grep_success_login_on_with_user.txt | egrep -o 'on \[[^[]+]' | sort | uniq -c | sort -n -# grep Host1 grep_success_login_on_with_user.txt | egrep -o 'on \[[^[]+]' | sort | uniq -c | sort -n -# grep Host2 grep_success_login_on_with_user.txt | egrep -o 'on \[[^[]+]' | sort | uniq -c | sort -n +echoq perf_help +perf_help() { + test FreeBSD = `uname -s` && { + echo FreeBSD here + echo "nload -t 6000 em0 -u K" + echo "iftop -i em0 -f 'port imap or port imaps' -B # t p >" + } + + test Linux = `uname -s` && { + echo Linux here + echo "nload -t 6000 eth0 -u K # Linux" + echo "iftop -i eth0 -f 'port imap or port imaps' -B # t p >" + } +} diff --git a/X/imapsync_form.css b/X/imapsync_form.css old mode 100755 new mode 100644 index f6120c3..8cdd214 --- a/X/imapsync_form.css +++ b/X/imapsync_form.css @@ -1,10 +1,11 @@ -/* $Id: imapsync_form.css,v 1.5 2019/06/25 21:31:44 gilles Exp gilles $ */ +/* $Id: imapsync_form.css,v 1.8 2019/11/25 13:49:18 gilles Exp gilles $ */ #account1 { - // background-color: DodgerBlue ; - background-color: Fuchsia ; + background-color: DodgerBlue ; + /* background-color: Fuchsia ; */ + /* background-color: RoyalBlue ; */ } #account2 { @@ -12,16 +13,30 @@ } #parameters { - // background-color: GreenYellow ; - // background-color: White ; + /* background-color: GreenYellow ; */ + /* background-color: White ; */ } -#progress { +#progress-txt { text-align: center ; - font-size: 1.6em ; - margin: 0 0 0px; + font-size: 1.4em ; + margin: 0 0 0px ; + height: 50px ; } +.progress { + /* text-align: center ; */ + /* font-size: 1.4em ; */ + margin: 0 0 0px ; +} + +.progress-bar { + text-align: center ; + font-size: 1.4em ; + margin: 0 0 0px ; +} + + .padd0 { padding-left: 0px ; padding-right: 0px ; diff --git a/X/imapsync_form.html b/X/imapsync_form.html old mode 100755 new mode 100644 index a3e826b..428f98e --- a/X/imapsync_form.html +++ b/X/imapsync_form.html @@ -1,5 +1,5 @@ - + @@ -28,13 +28,16 @@ - -@@ -604,7 +685,7 @@ lists what may be coded or done in the future.-- - - - ++ + + ++ + + + +@@ -210,10 +213,10 @@ old: H2YTURNFT4XT4- - + +
@@ -252,7 +255,16 @@ old: H2YTURNFT4XT4- +diff --git a/X/imapsync_form.js b/X/imapsync_form.js old mode 100755 new mode 100644 index 8655b9f..34da540 --- a/X/imapsync_form.js +++ b/X/imapsync_form.js @@ -1,453 +1,704 @@ -// $Id: imapsync_form.js,v 1.8 2019/06/25 16:41:43 gilles Exp gilles $ +// $Id: imapsync_form.js,v 1.11 2019/07/29 22:42:50 gilles Exp gilles $ /*jslint browser: true*/ /*global $*/ $(document).ready( - function () - { - "use strict"; - // Bootstrap popover and tooltip - $("[data-toggle='tooltip']").tooltip(); + function () + { + "use strict"; + // Bootstrap popover and tooltip + $("[data-toggle='tooltip']").tooltip(); - var readyStateStr = { - "0": "Request not initialized", - "1": "Server connection established", - "2": "Response headers received", - "3": "Processing request", - "4": "Finished and response is ready" - }; + var readyStateStr = { + "0": "Request not initialized", + "1": "Server connection established", + "2": "Response headers received", + "3": "Processing request", + "4": "Finished and response is ready" + } ; - function is( expected, given, comment ) + var refresh_interval_ms = 5000 ; + var refresh_interval_s = refresh_interval_ms / 1000 ; + var test = { + counter_all : 0 , + counter_ok : 0 , + counter_nok : 0 , + failed_tests : "" + } ; + + var is = function is( expected, given, comment ) + { + test.counter_all += 1 ; + var message = test.counter_all + " - [" + + expected + + "] === [" + + given + + "] " + + comment + + "\n" ; + if ( expected === given ) { - var message = ": [" - + expected - + "] === [" - + given - + "] " - + comment - + "\n" ; - if ( expected === given ) - { - message = "ok " + message ; - } - else - { - message = "Nok" + message ; - } - $("#tests").append( message ) ; + test.counter_ok += 1 ; + message = "ok " + message ; + } + else + { + test.counter_nok += 1 ; + test.failed_tests += "nb " + message + "\n" ; + message = "not ok " + message ; + } + $("#tests").append( message ) ; + } ; + + function last_eta( string ) + { + // return the last occurrence of the substring "ETA: ...\n" + // or "ETA: unknown" or "" + var eta ; + var last_found ; + + if ( undefined === string ) + { + return "" ; } - function last_eta( string ) + var eta_re = /ETA:.*\n/g ; + + eta = string.match( eta_re ) ; + if ( eta ) { - // return the last occurrence of the substring "ETA: ...\n" - // or "" - var eta ; - var last_found ; + last_found = eta[eta.length -1 ] ; + return last_found ; + } + else + { + return "ETA: unknown" ; + } + } - if ( undefined === string ) - { - return "" ; - } + function tests_last_eta() + { + is( "", last_eta( ), "last_eta: no args => empty string" ) ; - var eta_re = /ETA:.*\n/g ; + is( + "ETA: unknown", + last_eta( "" ), + "last_eta: empty => empty string" ) ; - eta = string.match( eta_re ) ; - if ( eta ) - { - last_found = eta[eta.length -1 ] ; - return last_found ; - } - else - { - return "" ; + is( "ETA: unknown", + last_eta( "ETA" ), + "last_eta: ETA => empty string" ) ; + + is( "ETA: unknown", last_eta( "ETA: but no CR" ), + "last_eta: ETA: but no CR => empty string" ) ; + + is( + "ETA: with CR\n", + last_eta( "Blabla ETA: with CR\n" ), + "last_eta: ETA: with CR => ETA: with CR" + ) ; + + is( + "ETA: 2 with CR\n", + last_eta( "Blabla ETA: 1 with CR\nBlabla ETA: 2 with CR\n" ), + "last_eta: several ETA: with CR => ETA: 2 with CR" + ) ; + } + + var tests_decompose_eta_line = function tests_decompose_eta_line() + { + var eta_obj ; + var eta_str = "ETA: Wed Jul 3 14:55:27 2019 1234 s 123/4567 msgs left\n" ; + + eta_obj = decompose_eta_line( "" ) ; + is( + "", + eta_obj.str, + "decompose_eta_line: no match => undefined" + ) ; + + + eta_obj = decompose_eta_line( eta_str ) ; + is( + eta_str, + eta_str, + "decompose_eta_line: str is str" + ) ; + + is( + eta_str, + eta_obj.str, + "decompose_eta_line: str back" + ) ; + + is( + "Wed Jul 3 14:55:27 2019", + eta_obj.date, + "decompose_eta_line: date" + ) ; + + is( + "1234", + eta_obj.seconds_left, + "decompose_eta_line: seconds_left" + ) ; + + is( + "123", + eta_obj.msgs_left, + "decompose_eta_line: msgs_left" + ) ; + + is( + "4567", + eta_obj.msgs_total, + "decompose_eta_line: msgs_total" + ) ; + + is( + "4444", + eta_obj.msgs_done(), + "decompose_eta_line: msgs_done" + ) ; + + is( + "97.31", + eta_obj.percent_done(), + "decompose_eta_line: percent_done" + ) ; + + is( + "2.69", + eta_obj.percent_left(), + "decompose_eta_line: percent_left" + ) ; + } ; + + var decompose_eta_line = function decompose_eta_line( eta_str ) + { + var eta_obj ; + var eta_array ; + + var regex_eta = /^ETA:\s+(.*?)\s+([0-9]+)\s+s\s+([0-9]+)\/([0-9]+)\s+msgs\s+left\n?$/ ; + eta_array = regex_eta.exec( eta_str ) ; + + if ( null !== eta_array ) + { + eta_obj = { + str : eta_str, + date : eta_array[1], + seconds_left : eta_array[2], + msgs_left : eta_array[3], + msgs_total : eta_array[4], + msgs_done : function() { + var diff = eta_obj.msgs_total - eta_obj.msgs_left ; + return( diff.toString() ) ; + }, + percent_done : function() { + var percent ; + if ( 0 === eta_obj.msgs_total ) + { + return "0" ; + } + else + { + percent = ( eta_obj.msgs_total - eta_obj.msgs_left ) / eta_obj.msgs_total * 100 ; + return( percent.toFixed(2) ) ; + } + }, + percent_left : function() { + var percent ; + if ( 0 === eta_obj.msgs_total ) + { + return "0" ; + } + else + { + percent = ( eta_obj.msgs_left / eta_obj.msgs_total * 100 ) ; + return( percent.toFixed(2) ) ; + } } + } ; + } + else + { + eta_obj = { + str : "", + date : "?", + seconds_left : "?", + msgs_left : "?", + msgs_total : "?", + msgs_done : "?", + percent_done : function() { return "" ; }, + percent_left : function() { return "" ; } + } ; } - function tests_last_eta() - { - is( "", last_eta( ), "last_eta: no args => empty string" ) ; - is( "", last_eta( "" ), "last_eta: empty => empty string" ) ; - is( "", last_eta( "ETA" ), "last_eta: ETA => empty string" ) ; - is( "", last_eta( "ETA: but no CR" ), - "last_eta: ETA: but no CR => empty string" ) ; + return eta_obj ; + } ; - is( - "ETA: with CR\n", - last_eta( "Blabla ETA: with CR\n" ), - "last_eta: ETA: with CR => ETA: with CR" + var extract_eta = function extract_eta( xhr ) + { + var eta_obj ; + var slice_length ; + var slice_log ; + var eta_str ; + + if ( xhr.readyState === 4 ) + { + slice_length = -24000 ; + } + else + { + slice_length = -240 ; + } + slice_log = xhr.responseText.slice( slice_length ) ; + eta_str = last_eta( slice_log ) ; + // $("#tests").append( "extract_eta eta_str: " + eta_str + "\n" ) ; + eta_obj = decompose_eta_line( eta_str ) ; + return eta_obj ; + } ; + + var progress_bar_update = function progress_bar_update( eta_obj ) + { + if ( eta_obj.str.length ) + { + $("#progress-bar-done").css( "width", eta_obj.percent_done() + "%" ).attr( "aria-valuenow", eta_obj.percent_done() ) ; + $("#progress-bar-left").css( "width", eta_obj.percent_left() + "%" ).attr( "aria-valuenow", eta_obj.percent_left() ) ; + $("#progress-bar-done").text( eta_obj.percent_done() + "% " + "done" ) ; + $("#progress-bar-left").text( eta_obj.percent_left() + "% " + "left" ) ; + } + else + { + $("#progress-bar-done").text( "unknown % " + "done" ) ; + $("#progress-bar-left").text( "unknown % " + "left" ) ; + } + return ; + } ; + + function refreshLog( xhr ) + { + var eta_obj ; + var eta_str ; + + eta_obj = extract_eta( xhr ) ; + + progress_bar_update( eta_obj ) ; + + if ( xhr.readyState === 4 ) + { + // end of sync + $("#progress-txt").text( + "Ended. It remains " + + eta_obj.msgs_left + " messages to be synced" ) ; + } + else + { + eta_str = eta_obj.str + " (refresh every " + refresh_interval_s + " s)" ; + eta_str = eta_str.replace(/(\r\n|\n|\r)/gm, "") ; // trim newline + //$("#tests").append( "refreshLog eta_str: " + eta_str + "\n" ) ; + $("#progress-txt").text( eta_str ) ; + + } + + $( "#output" ).text( xhr.responseText ) ; + } + + + + function handleRun(xhr, timerRefreshLog) + { + + $("#console").text( + "Status: " + xhr.status + " " + xhr.statusText + "\n" + + "State: " + readyStateStr[xhr.readyState] + "\n" ) ; + + if ( xhr.readyState === 4 ) { + // var headers = xhr.getAllResponseHeaders(); + // $("#console").append(headers); + // $("#console").append("See the completed log\n"); + $("#link_to_bottom").show() ; + clearInterval( timerRefreshLog ) ; + refreshLog( xhr ) ; // a last time + // back to enable state for next run + $("#bt-sync").prop("disabled", false) ; + } + } + + function imapsync() + { + var querystring = $("#form").serialize() ; + $("#abort").text("\n\n") ; // clean abort console + $("#output").text("Here comes the log!\n\n") ; + + if ( "imap.gmail.com" === $("#host1").val() ) + { + querystring = querystring + "&gmail1=on" ; + } + if ( "imap.gmail.com" === $("#host2").val() ) + { + querystring = querystring + "&gmail2=on" ; + } + + // Same for "outlook.office365.com" + if ( "outlook.office365.com" === $("#host1").val() ) + { + querystring = querystring + "&office1=on" ; + } + if ( "outlook.office365.com" === $("#host2").val() ) + { + querystring = querystring + "&office2=on" ; + } + + + var xhr ; + xhr = new XMLHttpRequest() ; + var timerRefreshLog = setInterval( + function () + { + refreshLog( xhr ) ; + }, refresh_interval_ms ) ; + + xhr.onreadystatechange = function () + { + handleRun( xhr, timerRefreshLog ) ; + } ; + + xhr.open( "POST", "/cgi-bin/imapsync", true ) ; + xhr.setRequestHeader( "Content-type", + "application/x-www-form-urlencoded" ) ; + xhr.send( querystring ) ; + } + + + function handleAbort( xhr ) + { + + $( "#abort" ).text( + "Status: " + xhr.status + " " + xhr.statusText + "\n" + + "State: " + readyStateStr[xhr.readyState] + "\n\n" ) ; + + if ( xhr.readyState === 4 ) + { + $("#abort").append(xhr.responseText); + $("#bt-sync").prop("disabled", false); + $("#bt-abort").prop("disabled", false); // back for next abort + } + } + + function abort() + { + var querystring = $("#form").serialize() + "&abort=on"; + var xhr; + xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () + { + handleAbort(xhr); + }; + xhr.open("POST", "/cgi-bin/imapsync", true); + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhr.send(querystring); + } + + function store( id ) + { + var stored ; + //$( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + "\n" ) ; + if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) + { + localStorage.setItem( id, $(id).val() ) ; + stored = $( id ).val() ; + } + else if ( "checkbox" === $( id ).attr( "type" ) ) + { + //$( "#tests" ).append( "Eco: " + id + " checked is " + $( id )[0].checked + "\n" ) ; + localStorage.setItem( id, $( id )[0].checked ) ; + stored = $( id )[0].checked ; + } + return stored ; + } + + function retrieve( id ) + { + var retrieved ; + //$( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + " length is " + $( id ).length + "\n" ) ; + if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) + { + $( id ).val( localStorage.getItem( id ) ) ; + retrieved = $( id ).val() ; + } + else if ( "checkbox" === $( id ).attr( "type" ) ) + { + //$( "#tests" ).append( "Eco: " + id + " getItem is " + localStorage.getItem( id ) + "\n" ) ; + $( id )[0].checked = JSON.parse( localStorage.getItem( id ) ) ; + retrieved = $( id )[0].checked ; + } + return retrieved ; + } + + function tests_store_retrieve() + { + if ( $("#tests").length !== 0 ) + { + is( 1, 1, "one equals one" ) ; + // isnot( 0, 1, "zero differs one" ) ; + + // no exist + is( undefined, store( "#test_noexists" ), + "store: #test_noexists" ) ; + is( undefined, retrieve( "#test_noexists" ), + "retrieve: #test_noexists" ) ; + is( undefined, retrieve( "#test_noexists2" ), + "retrieve: #test_noexists2" ) ; + + // input text + $("#test_text" ).val( "foo" ) ; + is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; + is( "foo", store( "#test_text" ), "store: #test_text" ) ; + $("#test_text" ).val( "bar" ) ; + is( "bar", $("#test_text" ).val( ), "#test_text val = bar" ) ; + is( "foo", retrieve( "#test_text" ), "retrieve: #test_text = foo" ) ; + is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; + + + // input check button + $( "#test_checkbox" ).prop( "checked", true ); + is( true, store( "#test_checkbox" ), "store: #test_checkbox checked" ) ; + + $( "#test_checkbox" ).prop( "checked", false ); + is( true, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = true" ) ; + + $( "#test_checkbox" ).prop( "checked", false ); + is( false, store( "#test_checkbox" ), "store: #test_checkbox not checked" ) ; + $( "#test_checkbox" ).prop( "checked", true ); + is( false, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = false" ) ; + + } + } + + + function store_form() + { + if ( Storage !== "undefined") + { + // Code for localStorage. + store("#user1") ; + store("#password1") ; + store("#host1") ; + store("#subfolder1") ; + store("#showpassword1") ; + + store("#user2") ; + store("#password2") ; + store("#host2") ; + store("#subfolder2") ; + store("#showpassword2") ; + + store("#dry") ; + store("#justlogin") ; + store("#justfolders") ; + store("#justfoldersizes") ; + + localStorage.account1_background_color = $("#account1").css("background-color") ; + localStorage.account2_background_color = $("#account2").css("background-color") ; + } + } + + function show_extra_if_needed() + { + if ( $("#subfolder1").length && $("#subfolder1").val().length > 0 ) + { + $(".extra_param").show() ; + } + if ( $("#subfolder2").length && $("#subfolder2").val().length > 0 ) + { + $(".extra_param").show() ; + } + } + + function retrieve_form() + { + if ( Storage !== "undefined" ) + { + // Code for localStorage. + retrieve( "#user1" ) ; + retrieve( "#password1" ) ; + // retrieve("#showpassword1") ; + retrieve( "#host1" ) ; + retrieve( "#subfolder1" ) ; + + retrieve( "#user2" ) ; + retrieve( "#password2" ) ; + // retrieve("#showpassword2") ; + retrieve( "#host2" ) ; + retrieve( "#subfolder2" ) ; + + retrieve( "#dry" ) ; + retrieve( "#justlogin" ) ; + retrieve( "#justfolders" ) ; + retrieve( "#justfoldersizes" ) ; + + // In case, how to restore the original color from css file. + // localStorage.removeItem( "account1_background_color" ) ; + // localStorage.removeItem( "account2_background_color" ) ; + + if ( localStorage.account1_background_color ) + { + $("#account1").css("background-color", + localStorage.account1_background_color ) ; + } + if ( localStorage.account2_background_color ) + { + $("#account2").css("background-color", + localStorage.account2_background_color ) ; + } + + // Show the extra parameters if they are not empty because it would be dangerous + // to retrieve them without knowing + show_extra_if_needed() ; + } + } + + + function showpassword( id, button ) + { + var x = document.getElementById( id ); + if ( button.checked ) + { + x.type = "text"; + } else { + x.type = "password"; + } + } + + function init() + { + // in case of a manual refresh, start with + $("#bt-sync").prop("disabled", false); + $("#bt-abort").prop("disabled", false); + $("#link_to_bottom").hide(); + $("#progress-bar-left").css( "width", 100 + "%" ).attr( "aria-valuenow", 100 ) ; + + retrieve_form(); + + $("#showpassword1").click( + function ( event ) + { + var button = event.target ; + showpassword( "password1", button ) ; + } + ); + + + $("#showpassword2").click( + function ( event ) + { + //$("#tests").append( "\nthat1=" + JSON.stringify( event.target, undefined, 4 ) ) ; + var button = event.target ; + showpassword( "password2", button ) ; + } + ); + + + $("#bt-sync").click( + function () + { + $("#bt-sync").prop("disabled", true) ; + $("#bt-abort").prop("disabled", false) ; + $("#link_to_bottom").hide() ; + $("#progress-txt").text( "ETA: coming soon" ) ; + store_form() ; + imapsync() ; + } + ); + + $("#bt-abort").click( + function () + { + $("#bt-sync").prop("disabled", true); + $("#bt-abort").prop("disabled", true); + abort(); + } + ); + + + var swap = function swap( p1, p2 ) + { + var temp = $( p2 ).val( ) ; + $( p2 ).val( $( p1 ).val( ) ) ; + $( p1 ).val( temp ) ; + } ; + + + $("#swap").click( + function() + { + // swaping colors can't use swap() + var temp1 = $("#account1").css("background-color") ; + var temp2 = $("#account2").css("background-color") ; + $("#account1").css("background-color", temp2 ); + $("#account2").css("background-color", temp1 ); + + swap( $("#user1"), $("#user2") ) ; + swap( $("#password1"), $("#password2") ) ; + swap( $("#host1"), $("#host2") ) ; + swap( $("#subfolder1"), $("#subfolder2") ) ; + + var temp = $("#showpassword1")[0].checked ; + $("#showpassword1")[0].checked = $("#showpassword2")[0].checked ; + $("#showpassword2")[0].checked = temp ; + showpassword( "password1", $("#showpassword1")[0] ) ; + showpassword( "password2", $("#showpassword2")[0] ) ; + } + ) ; + } + + var tests_bilan = function tests_bilan() + { + // attended number of tests + var nb_attended_test = 29 ; + $("#tests").append( "1.." + test.counter_all + "\n" ) ; + if ( test.counter_nok > 0 ) + { + $("#tests").append( + "\nFAILED tests \n" + + test.failed_tests ) ; - - is( - "ETA: 2 with CR\n", - last_eta( "Blabla ETA: 1 with CR\nBlabla ETA: 2 with CR\n" ), - "last_eta: several ETA: with CR => ETA: 2 with CR" + $("#tests").collapse("show") ; + } + // Summary of tests: failed 0 tests, run xx tests, + // expected to run yy tests. + if ( test.counter_all !== nb_attended_test ) + { + $("#tests").append( "# Looks like you planned " + + nb_attended_test + + " tests but ran " + + test.counter_all + ".\n" ) ; - } + $("#tests").collapse("show") ; + } + } ; - function refreshLog(xhr) + function tests() { - var slice_length ; - $("#output").text(xhr.responseText) ; - if (xhr.readyState === 4) { - slice_length = -2400 ; - } - else - { - slice_length = -240 ; - } - $("#progress").text( last_eta( xhr.responseText.slice( slice_length ) ) ) ; + if ( $("#tests").length !== 0 ) + { + tests_store_retrieve( ) ; + tests_last_eta( ) ; + tests_decompose_eta_line( ) ; + //is( 0, 1, "this test always fails" ) ; + tests_bilan( ) ; + //$("#tests").collapse("show") ; + + } } + init( ) ; + tests( ) ; - - function handleRun(xhr, timerRefreshLog) - { - - $("#console").text("Status: " + xhr.status + " " + xhr.statusText + "\n" + "State: " + readyStateStr[xhr.readyState] + "\n") ; - - if (xhr.readyState === 4) { - // var headers = xhr.getAllResponseHeaders(); - // $("#console").append(headers); - // $("#console").append("See the completed log\n"); - $("#link_to_bottom").show(); - clearInterval(timerRefreshLog); - refreshLog(xhr); // a last time - $("#bt-sync").prop("disabled", false); // back to enable state for next run - } - } - - function imapsync() - { - var querystring = $("#form").serialize(); - $("#abort").text("\n\n"); // clean abort console - $("#output").text("Here comes the log!\n\n"); - - if ( "imap.gmail.com" === $("#host1").val() ) - { - querystring = querystring + "&gmail1=on"; - } - if ( "imap.gmail.com" === $("#host2").val() ) - { - querystring = querystring + "&gmail2=on"; - } - - var xhr; - xhr = new XMLHttpRequest(); - var timerRefreshLog = setInterval( - function () - { - refreshLog(xhr); - }, 5000 ) ; - xhr.onreadystatechange = function () - { - handleRun(xhr, timerRefreshLog); - }; - xhr.open("POST", "/cgi-bin/imapsync", true); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.send(querystring); - } - - - function handleAbort(xhr) - { - - $("#abort").text("Status: " + xhr.status + " " + xhr.statusText + "\n" + "State: " + readyStateStr[xhr.readyState] + "\n\n"); - - if (xhr.readyState === 4) - { - $("#abort").append(xhr.responseText); - $("#bt-sync").prop("disabled", false); - $("#bt-abort").prop("disabled", false); // back for next abort - } - } - - function abort() - { - var querystring = $("#form").serialize() + "&abort=on"; - var xhr; - xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () - { - handleAbort(xhr); - }; - xhr.open("POST", "/cgi-bin/imapsync", true); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.send(querystring); - } - - function store( id ) - { - var stored ; - $( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + "\n" ) ; - if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) - { - localStorage.setItem( id, $(id).val() ) ; - stored = $(id).val() ; - } - else if ( "checkbox" === $( id ).attr( "type" ) ) - { - $( "#tests" ).append( "Eco: " + id + " checked is " + $( id )[0].checked + "\n" ) ; - localStorage.setItem( id, $( id )[0].checked ) ; - stored = $( id )[0].checked ; - } - return stored ; - } - - function retrieve( id ) - { - var retrieved ; - $( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + " length is " + $( id ).length + "\n" ) ; - if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) - { - $( id ).val( localStorage.getItem( id ) ) ; - retrieved = $( id ).val() ; - } - else if ( "checkbox" === $( id ).attr( "type" ) ) - { - $( "#tests" ).append( "Eco: " + id + " getItem is " + localStorage.getItem( id ) + "\n" ) ; - $( id )[0].checked = JSON.parse( localStorage.getItem( id ) ) ; - retrieved = $( id )[0].checked ; - } - return retrieved ; - } - - function tests_store_retrieve() - { - if ( $("#tests").length !== 0 ) - { - is( 1, 1, "one equals one" ) ; - // isnot( 0, 1, "zero differs one" ) ; - - // no exist - is( undefined, store( "#test_noexists" ), "store: #test_noexists" ) ; - is( undefined, retrieve( "#test_noexists" ), "retrieve: #test_noexists" ) ; - is( undefined, retrieve( "#test_noexists2" ), "retrieve: #test_noexists2" ) ; - - // input text - $("#test_text" ).val( "foo" ) ; - is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; - is( "foo", store( "#test_text" ), "store: #test_text" ) ; - $("#test_text" ).val( "bar" ) ; - is( "bar", $("#test_text" ).val( ), "#test_text val = bar" ) ; - is( "foo", retrieve( "#test_text" ), "retrieve: #test_text = foo" ) ; - is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; - - - // input check button - $( "#test_checkbox" ).prop( "checked", true ); - is( true, store( "#test_checkbox" ), "store: #test_checkbox checked" ) ; - - $( "#test_checkbox" ).prop( "checked", false ); - is( true, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = true" ) ; - - $( "#test_checkbox" ).prop( "checked", false ); - is( false, store( "#test_checkbox" ), "store: #test_checkbox not checked" ) ; - $( "#test_checkbox" ).prop( "checked", true ); - is( false, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = false" ) ; - - } - } - - - function store_form() - { - if ( Storage !== "undefined") - { - // Code for localStorage. - store("#user1") ; - store("#password1") ; - store("#host1") ; - store("#subfolder1") ; - store("#showpassword1") ; - - store("#user2") ; - store("#password2") ; - store("#host2") ; - store("#subfolder2") ; - store("#showpassword2") ; - - store("#dry") ; - store("#justlogin") ; - store("#justfolders") ; - store("#justfoldersizes") ; - - localStorage.account1_background_color = $("#account1").css("background-color") ; - localStorage.account2_background_color = $("#account2").css("background-color") ; - } - } - - function show_extra_if_needed() - { - if ( $("#subfolder1").length && $("#subfolder1").val().length > 0 ) - { - $(".extra_param").show() ; - } - if ( $("#subfolder2").length && $("#subfolder2").val().length > 0 ) - { - $(".extra_param").show() ; - } - } - - function retrieve_form() - { - if ( Storage !== "undefined" ) - { - // Code for localStorage. - retrieve( "#user1" ) ; - retrieve( "#password1" ) ; - // retrieve("#showpassword1") ; - retrieve( "#host1" ) ; - retrieve( "#subfolder1" ) ; - - retrieve( "#user2" ) ; - retrieve( "#password2" ) ; - // retrieve("#showpassword2") ; - retrieve( "#host2" ) ; - retrieve( "#subfolder2" ) ; - - retrieve( "#dry" ) ; - retrieve( "#justlogin" ) ; - retrieve( "#justfolders" ) ; - retrieve( "#justfoldersizes" ) ; - - // In case - // localStorage.removeItem( "account1_background_color" ) ; - // localStorage.removeItem( "account2_background_color" ) ; - - if ( localStorage.account1_background_color ) - { - $("#account1").css("background-color", localStorage.account1_background_color ) ; - } - if ( localStorage.account2_background_color ) - { - $("#account2").css("background-color", localStorage.account2_background_color ) ; - } - - // Show the extra parameters if they are not empty because it would be dangerous - // to retrieve them without knowing - show_extra_if_needed() ; - } - } - - - function showpassword( id, button ) - { - var x = document.getElementById( id ); - if ( button.checked ) - { - x.type = "text"; - } else { - x.type = "password"; - } - } - - function init() - { - // in case of a manual refresh, start with - $("#bt-sync").prop("disabled", false); - $("#bt-abort").prop("disabled", false); - $("#link_to_bottom").hide(); - retrieve_form(); - - $("#showpassword1").click( - function () - { - // does not change jslint report... - /*jshint validthis: true */ - var button = this ; - showpassword( "password1", button ) ; - } - ); - - - $("#showpassword2").click( - function () - { - var button = this ; - showpassword( "password2", button ) ; - } - ); - - - $("#bt-sync").click( - function () - { - $("#bt-sync").prop("disabled", true); - $("#bt-abort").prop("disabled", false); - $("#link_to_bottom").hide(); - store_form(); - imapsync(); - } - ); - - $("#bt-abort").click( - function () - { - $("#bt-sync").prop("disabled", true); - $("#bt-abort").prop("disabled", true); - abort(); - } - ); - - - $.fn.swapWith = function(to) - { - var temp = $(to).val(); - $(to).val($(this).val()); - $(this).val(temp); - }; - - - $("#swap").click( - function() - { - // swaping colors can't use swapWith() - var temp1 = $("#account1").css("background-color") ; - var temp2 = $("#account2").css("background-color") ; - $("#account1").css("background-color", temp2 ); - $("#account2").css("background-color", temp1 ); - - $("#user1").swapWith("#user2"); - $("#password1").swapWith("#password2"); - $("#host1").swapWith("#host2"); - $("#subfolder1").swapWith("#subfolder2"); - - var temp = $("#showpassword1")[0].checked ; - $("#showpassword1")[0].checked = $("#showpassword2")[0].checked ; - $("#showpassword2")[0].checked = temp ; - showpassword( "password1", $("#showpassword1")[0] ) ; - showpassword( "password2", $("#showpassword2")[0] ) ; - } - ); - - } - - - - - function progress_bar_update( string ) - { - // - return ; - } - - - - function tests() - { - if ( $("#tests").length !== 0 ) - { - tests_store_retrieve() ; - tests_last_eta() ; - } - } - - init( ) ; - tests( ) ; - - } + } ); diff --git a/X/imapsync_form_1.3.js b/X/imapsync_form_1.3.js old mode 100755 new mode 100644 diff --git a/X/imapsync_form_1.4.js b/X/imapsync_form_1.4.js old mode 100755 new mode 100644 diff --git a/X/imapsync_form_1.69.html b/X/imapsync_form_1.69.html old mode 100755 new mode 100644 diff --git a/X/imapsync_form_extra.html b/X/imapsync_form_extra.html index e763665..5df7997 100755 --- a/X/imapsync_form_extra.html +++ b/X/imapsync_form_extra.html @@ -1,9 +1,13 @@ - + + - + + + +ETA: Estimation Time of Arrival+ +++ +Console of imapsync run
@@ -299,7 +311,7 @@ old: H2YTURNFT4XT4 - +
Local bandwidth statistics
@@ -323,7 +335,7 @@ old: H2YTURNFT4XT4 Bottom
- ($Id: imapsync_form.html,v 1.75 2019/06/27 05:27:13 gilles Exp gilles $)
+ ($Id: imapsync_form.html,v 1.85 2019/11/24 17:35:25 gilles Exp gilles $)
Terms and conditions for anything: No limits to do anything with this work and this license!
Imapsync online @@ -28,13 +32,16 @@ --- +-- - - - ++ + + ++ + + + +@@ -305,12 +312,12 @@ old: H2YTURNFT4XT4 - + - +
@@ -353,7 +360,16 @@ old: H2YTURNFT4XT4- +diff --git a/X/imapsync_form_new.js b/X/imapsync_form_new.js old mode 100755 new mode 100644 index e3243ac..9ccd7c2 --- a/X/imapsync_form_new.js +++ b/X/imapsync_form_new.js @@ -1,453 +1,704 @@ -// $Id: imapsync_form_new.js,v 1.4 2019/06/25 16:34:19 gilles Exp gilles $ +// $Id: imapsync_form_new.js,v 1.12 2019/07/29 22:42:19 gilles Exp gilles $ /*jslint browser: true*/ /*global $*/ $(document).ready( - function () - { - "use strict"; - // Bootstrap popover and tooltip - $("[data-toggle='tooltip']").tooltip(); + function () + { + "use strict"; + // Bootstrap popover and tooltip + $("[data-toggle='tooltip']").tooltip(); - var readyStateStr = { - "0": "Request not initialized", - "1": "Server connection established", - "2": "Response headers received", - "3": "Processing request", - "4": "Finished and response is ready" - }; + var readyStateStr = { + "0": "Request not initialized", + "1": "Server connection established", + "2": "Response headers received", + "3": "Processing request", + "4": "Finished and response is ready" + } ; - function is( expected, given, comment ) + var refresh_interval_ms = 5000 ; + var refresh_interval_s = refresh_interval_ms / 1000 ; + var test = { + counter_all : 0 , + counter_ok : 0 , + counter_nok : 0 , + failed_tests : "" + } ; + + var is = function is( expected, given, comment ) + { + test.counter_all += 1 ; + var message = test.counter_all + " - [" + + expected + + "] === [" + + given + + "] " + + comment + + "\n" ; + if ( expected === given ) { - var message = ": [" - + expected - + "] === [" - + given - + "] " - + comment - + "\n" ; - if ( expected === given ) - { - message = "ok " + message ; - } - else - { - message = "Nok" + message ; - } - $("#tests").append( message ) ; + test.counter_ok += 1 ; + message = "ok " + message ; + } + else + { + test.counter_nok += 1 ; + test.failed_tests += "nb " + message + "\n" ; + message = "not ok " + message ; + } + $("#tests").append( message ) ; + } ; + + function last_eta( string ) + { + // return the last occurrence of the substring "ETA: ...\n" + // or "ETA: unknown" or "" + var eta ; + var last_found ; + + if ( undefined === string ) + { + return "" ; } - function last_eta( string ) + var eta_re = /ETA:.*\n/g ; + + eta = string.match( eta_re ) ; + if ( eta ) { - // return the last occurrence of the substring "ETA: ...\n" - // or "" - var eta ; - var last_found ; + last_found = eta[eta.length -1 ] ; + return last_found ; + } + else + { + return "ETA: unknown" ; + } + } - if ( undefined === string ) - { - return "" ; - } + function tests_last_eta() + { + is( "", last_eta( ), "last_eta: no args => empty string" ) ; - var eta_re = /ETA:.*\n/g ; + is( + "ETA: unknown", + last_eta( "" ), + "last_eta: empty => empty string" ) ; - eta = string.match( eta_re ) ; - if ( eta ) - { - last_found = eta[eta.length -1 ] ; - return last_found ; - } - else - { - return "" ; + is( "ETA: unknown", + last_eta( "ETA" ), + "last_eta: ETA => empty string" ) ; + + is( "ETA: unknown", last_eta( "ETA: but no CR" ), + "last_eta: ETA: but no CR => empty string" ) ; + + is( + "ETA: with CR\n", + last_eta( "Blabla ETA: with CR\n" ), + "last_eta: ETA: with CR => ETA: with CR" + ) ; + + is( + "ETA: 2 with CR\n", + last_eta( "Blabla ETA: 1 with CR\nBlabla ETA: 2 with CR\n" ), + "last_eta: several ETA: with CR => ETA: 2 with CR" + ) ; + } + + var tests_decompose_eta_line = function tests_decompose_eta_line() + { + var eta_obj ; + var eta_str = "ETA: Wed Jul 3 14:55:27 2019 1234 s 123/4567 msgs left\n" ; + + eta_obj = decompose_eta_line( "" ) ; + is( + "", + eta_obj.str, + "decompose_eta_line: no match => undefined" + ) ; + + + eta_obj = decompose_eta_line( eta_str ) ; + is( + eta_str, + eta_str, + "decompose_eta_line: str is str" + ) ; + + is( + eta_str, + eta_obj.str, + "decompose_eta_line: str back" + ) ; + + is( + "Wed Jul 3 14:55:27 2019", + eta_obj.date, + "decompose_eta_line: date" + ) ; + + is( + "1234", + eta_obj.seconds_left, + "decompose_eta_line: seconds_left" + ) ; + + is( + "123", + eta_obj.msgs_left, + "decompose_eta_line: msgs_left" + ) ; + + is( + "4567", + eta_obj.msgs_total, + "decompose_eta_line: msgs_total" + ) ; + + is( + "4444", + eta_obj.msgs_done(), + "decompose_eta_line: msgs_done" + ) ; + + is( + "97.31", + eta_obj.percent_done(), + "decompose_eta_line: percent_done" + ) ; + + is( + "2.69", + eta_obj.percent_left(), + "decompose_eta_line: percent_left" + ) ; + } ; + + var decompose_eta_line = function decompose_eta_line( eta_str ) + { + var eta_obj ; + var eta_array ; + + var regex_eta = /^ETA:\s+(.*?)\s+([0-9]+)\s+s\s+([0-9]+)\/([0-9]+)\s+msgs\s+left\n?$/ ; + eta_array = regex_eta.exec( eta_str ) ; + + if ( null !== eta_array ) + { + eta_obj = { + str : eta_str, + date : eta_array[1], + seconds_left : eta_array[2], + msgs_left : eta_array[3], + msgs_total : eta_array[4], + msgs_done : function() { + var diff = eta_obj.msgs_total - eta_obj.msgs_left ; + return( diff.toString() ) ; + }, + percent_done : function() { + var percent ; + if ( 0 === eta_obj.msgs_total ) + { + return "0" ; + } + else + { + percent = ( eta_obj.msgs_total - eta_obj.msgs_left ) / eta_obj.msgs_total * 100 ; + return( percent.toFixed(2) ) ; + } + }, + percent_left : function() { + var percent ; + if ( 0 === eta_obj.msgs_total ) + { + return "0" ; + } + else + { + percent = ( eta_obj.msgs_left / eta_obj.msgs_total * 100 ) ; + return( percent.toFixed(2) ) ; + } } + } ; + } + else + { + eta_obj = { + str : "", + date : "?", + seconds_left : "?", + msgs_left : "?", + msgs_total : "?", + msgs_done : "?", + percent_done : function() { return "" ; }, + percent_left : function() { return "" ; } + } ; } - function tests_last_eta() - { - is( "", last_eta( ), "last_eta: no args => empty string" ) ; - is( "", last_eta( "" ), "last_eta: empty => empty string" ) ; - is( "", last_eta( "ETA" ), "last_eta: ETA => empty string" ) ; - is( "", last_eta( "ETA: but no CR" ), - "last_eta: ETA: but no CR => empty string" ) ; + return eta_obj ; + } ; - is( - "ETA: with CR\n", - last_eta( "Blabla ETA: with CR\n" ), - "last_eta: ETA: with CR => ETA: with CR" + var extract_eta = function extract_eta( xhr ) + { + var eta_obj ; + var slice_length ; + var slice_log ; + var eta_str ; + + if ( xhr.readyState === 4 ) + { + slice_length = -24000 ; + } + else + { + slice_length = -240 ; + } + slice_log = xhr.responseText.slice( slice_length ) ; + eta_str = last_eta( slice_log ) ; + // $("#tests").append( "extract_eta eta_str: " + eta_str + "\n" ) ; + eta_obj = decompose_eta_line( eta_str ) ; + return eta_obj ; + } ; + + var progress_bar_update = function progress_bar_update( eta_obj ) + { + if ( eta_obj.str.length ) + { + $("#progress-bar-done").css( "width", eta_obj.percent_done() + "%" ).attr( "aria-valuenow", eta_obj.percent_done() ) ; + $("#progress-bar-left").css( "width", eta_obj.percent_left() + "%" ).attr( "aria-valuenow", eta_obj.percent_left() ) ; + $("#progress-bar-done").text( eta_obj.percent_done() + "% " + "done" ) ; + $("#progress-bar-left").text( eta_obj.percent_left() + "% " + "left" ) ; + } + else + { + $("#progress-bar-done").text( "unknown % " + "done" ) ; + $("#progress-bar-left").text( "unknown % " + "left" ) ; + } + return ; + } ; + + function refreshLog( xhr ) + { + var eta_obj ; + var eta_str ; + + eta_obj = extract_eta( xhr ) ; + + progress_bar_update( eta_obj ) ; + + if ( xhr.readyState === 4 ) + { + // end of sync + $("#progress-txt").text( + "Ended. It remains " + + eta_obj.msgs_left + " messages to be synced" ) ; + } + else + { + eta_str = eta_obj.str + " (refresh every " + refresh_interval_s + " s)" ; + eta_str = eta_str.replace(/(\r\n|\n|\r)/gm, "") ; // trim newline + //$("#tests").append( "refreshLog eta_str: " + eta_str + "\n" ) ; + $("#progress-txt").text( eta_str ) ; + + } + + $( "#output" ).text( xhr.responseText ) ; + } + + + + function handleRun(xhr, timerRefreshLog) + { + + $("#console").text( + "Status: " + xhr.status + " " + xhr.statusText + "\n" + + "State: " + readyStateStr[xhr.readyState] + "\n" ) ; + + if ( xhr.readyState === 4 ) { + // var headers = xhr.getAllResponseHeaders(); + // $("#console").append(headers); + // $("#console").append("See the completed log\n"); + $("#link_to_bottom").show() ; + clearInterval( timerRefreshLog ) ; + refreshLog( xhr ) ; // a last time + // back to enable state for next run + $("#bt-sync").prop("disabled", false) ; + } + } + + function imapsync() + { + var querystring = $("#form").serialize() ; + $("#abort").text("\n\n") ; // clean abort console + $("#output").text("Here comes the log!\n\n") ; + + if ( "imap.gmail.com" === $("#host1").val() ) + { + querystring = querystring + "&gmail1=on" ; + } + if ( "imap.gmail.com" === $("#host2").val() ) + { + querystring = querystring + "&gmail2=on" ; + } + + // Same for "outlook.office365.com" + if ( "outlook.office365.com" === $("#host1").val() ) + { + querystring = querystring + "&office1=on" ; + } + if ( "outlook.office365.com" === $("#host2").val() ) + { + querystring = querystring + "&office2=on" ; + } + + + var xhr ; + xhr = new XMLHttpRequest() ; + var timerRefreshLog = setInterval( + function () + { + refreshLog( xhr ) ; + }, refresh_interval_ms ) ; + + xhr.onreadystatechange = function () + { + handleRun( xhr, timerRefreshLog ) ; + } ; + + xhr.open( "POST", "/cgi-bin/imapsync", true ) ; + xhr.setRequestHeader( "Content-type", + "application/x-www-form-urlencoded" ) ; + xhr.send( querystring ) ; + } + + + function handleAbort( xhr ) + { + + $( "#abort" ).text( + "Status: " + xhr.status + " " + xhr.statusText + "\n" + + "State: " + readyStateStr[xhr.readyState] + "\n\n" ) ; + + if ( xhr.readyState === 4 ) + { + $("#abort").append(xhr.responseText); + $("#bt-sync").prop("disabled", false); + $("#bt-abort").prop("disabled", false); // back for next abort + } + } + + function abort() + { + var querystring = $("#form").serialize() + "&abort=on"; + var xhr; + xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () + { + handleAbort(xhr); + }; + xhr.open("POST", "/cgi-bin/imapsync", true); + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhr.send(querystring); + } + + function store( id ) + { + var stored ; + //$( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + "\n" ) ; + if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) + { + localStorage.setItem( id, $(id).val() ) ; + stored = $( id ).val() ; + } + else if ( "checkbox" === $( id ).attr( "type" ) ) + { + //$( "#tests" ).append( "Eco: " + id + " checked is " + $( id )[0].checked + "\n" ) ; + localStorage.setItem( id, $( id )[0].checked ) ; + stored = $( id )[0].checked ; + } + return stored ; + } + + function retrieve( id ) + { + var retrieved ; + //$( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + " length is " + $( id ).length + "\n" ) ; + if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) + { + $( id ).val( localStorage.getItem( id ) ) ; + retrieved = $( id ).val() ; + } + else if ( "checkbox" === $( id ).attr( "type" ) ) + { + //$( "#tests" ).append( "Eco: " + id + " getItem is " + localStorage.getItem( id ) + "\n" ) ; + $( id )[0].checked = JSON.parse( localStorage.getItem( id ) ) ; + retrieved = $( id )[0].checked ; + } + return retrieved ; + } + + function tests_store_retrieve() + { + if ( $("#tests").length !== 0 ) + { + is( 1, 1, "one equals one" ) ; + // isnot( 0, 1, "zero differs one" ) ; + + // no exist + is( undefined, store( "#test_noexists" ), + "store: #test_noexists" ) ; + is( undefined, retrieve( "#test_noexists" ), + "retrieve: #test_noexists" ) ; + is( undefined, retrieve( "#test_noexists2" ), + "retrieve: #test_noexists2" ) ; + + // input text + $("#test_text" ).val( "foo" ) ; + is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; + is( "foo", store( "#test_text" ), "store: #test_text" ) ; + $("#test_text" ).val( "bar" ) ; + is( "bar", $("#test_text" ).val( ), "#test_text val = bar" ) ; + is( "foo", retrieve( "#test_text" ), "retrieve: #test_text = foo" ) ; + is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; + + + // input check button + $( "#test_checkbox" ).prop( "checked", true ); + is( true, store( "#test_checkbox" ), "store: #test_checkbox checked" ) ; + + $( "#test_checkbox" ).prop( "checked", false ); + is( true, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = true" ) ; + + $( "#test_checkbox" ).prop( "checked", false ); + is( false, store( "#test_checkbox" ), "store: #test_checkbox not checked" ) ; + $( "#test_checkbox" ).prop( "checked", true ); + is( false, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = false" ) ; + + } + } + + + function store_form() + { + if ( Storage !== "undefined") + { + // Code for localStorage. + store("#user1") ; + store("#password1") ; + store("#host1") ; + store("#subfolder1") ; + store("#showpassword1") ; + + store("#user2") ; + store("#password2") ; + store("#host2") ; + store("#subfolder2") ; + store("#showpassword2") ; + + store("#dry") ; + store("#justlogin") ; + store("#justfolders") ; + store("#justfoldersizes") ; + + localStorage.account1_background_color = $("#account1").css("background-color") ; + localStorage.account2_background_color = $("#account2").css("background-color") ; + } + } + + function show_extra_if_needed() + { + if ( $("#subfolder1").length && $("#subfolder1").val().length > 0 ) + { + $(".extra_param").show() ; + } + if ( $("#subfolder2").length && $("#subfolder2").val().length > 0 ) + { + $(".extra_param").show() ; + } + } + + function retrieve_form() + { + if ( Storage !== "undefined" ) + { + // Code for localStorage. + retrieve( "#user1" ) ; + retrieve( "#password1" ) ; + // retrieve("#showpassword1") ; + retrieve( "#host1" ) ; + retrieve( "#subfolder1" ) ; + + retrieve( "#user2" ) ; + retrieve( "#password2" ) ; + // retrieve("#showpassword2") ; + retrieve( "#host2" ) ; + retrieve( "#subfolder2" ) ; + + retrieve( "#dry" ) ; + retrieve( "#justlogin" ) ; + retrieve( "#justfolders" ) ; + retrieve( "#justfoldersizes" ) ; + + // In case, how to restore the original color from css file. + // localStorage.removeItem( "account1_background_color" ) ; + // localStorage.removeItem( "account2_background_color" ) ; + + if ( localStorage.account1_background_color ) + { + $("#account1").css("background-color", + localStorage.account1_background_color ) ; + } + if ( localStorage.account2_background_color ) + { + $("#account2").css("background-color", + localStorage.account2_background_color ) ; + } + + // Show the extra parameters if they are not empty because it would be dangerous + // to retrieve them without knowing + show_extra_if_needed() ; + } + } + + + function showpassword( id, button ) + { + var x = document.getElementById( id ); + if ( button.checked ) + { + x.type = "text"; + } else { + x.type = "password"; + } + } + + function init() + { + // in case of a manual refresh, start with + $("#bt-sync").prop("disabled", false); + $("#bt-abort").prop("disabled", false); + $("#link_to_bottom").hide(); + $("#progress-bar-left").css( "width", 100 + "%" ).attr( "aria-valuenow", 100 ) ; + + retrieve_form(); + + $("#showpassword1").click( + function ( event ) + { + var button = event.target ; + showpassword( "password1", button ) ; + } + ); + + + $("#showpassword2").click( + function ( event ) + { + //$("#tests").append( "\nthat1=" + JSON.stringify( event.target, undefined, 4 ) ) ; + var button = event.target ; + showpassword( "password2", button ) ; + } + ); + + + $("#bt-sync").click( + function () + { + $("#bt-sync").prop("disabled", true) ; + $("#bt-abort").prop("disabled", false) ; + $("#link_to_bottom").hide() ; + $("#progress-txt").text( "ETA: coming soon" ) ; + store_form() ; + imapsync() ; + } + ); + + $("#bt-abort").click( + function () + { + $("#bt-sync").prop("disabled", true); + $("#bt-abort").prop("disabled", true); + abort(); + } + ); + + + var swap = function swap( p1, p2 ) + { + var temp = $( p2 ).val( ) ; + $( p2 ).val( $( p1 ).val( ) ) ; + $( p1 ).val( temp ) ; + } ; + + + $("#swap").click( + function() + { + // swaping colors can't use swap() + var temp1 = $("#account1").css("background-color") ; + var temp2 = $("#account2").css("background-color") ; + $("#account1").css("background-color", temp2 ); + $("#account2").css("background-color", temp1 ); + + swap( $("#user1"), $("#user2") ) ; + swap( $("#password1"), $("#password2") ) ; + swap( $("#host1"), $("#host2") ) ; + swap( $("#subfolder1"), $("#subfolder2") ) ; + + var temp = $("#showpassword1")[0].checked ; + $("#showpassword1")[0].checked = $("#showpassword2")[0].checked ; + $("#showpassword2")[0].checked = temp ; + showpassword( "password1", $("#showpassword1")[0] ) ; + showpassword( "password2", $("#showpassword2")[0] ) ; + } + ) ; + } + + var tests_bilan = function tests_bilan() + { + // attended number of tests + var nb_attended_test = 29 ; + $("#tests").append( "1.." + test.counter_all + "\n" ) ; + if ( test.counter_nok > 0 ) + { + $("#tests").append( + "\nFAILED tests \n" + + test.failed_tests ) ; - - is( - "ETA: 2 with CR\n", - last_eta( "Blabla ETA: 1 with CR\nBlabla ETA: 2 with CR\n" ), - "last_eta: several ETA: with CR => ETA: 2 with CR" + $("#tests").collapse("show") ; + } + // Summary of tests: failed 0 tests, run xx tests, + // expected to run yy tests. + if ( test.counter_all !== nb_attended_test ) + { + $("#tests").append( "# Looks like you planned " + + nb_attended_test + + " tests but ran " + + test.counter_all + ".\n" ) ; - } + $("#tests").collapse("show") ; + } + } ; - function refreshLog(xhr) + function tests() { - var slice_length ; - $("#output").text(xhr.responseText) ; - if (xhr.readyState === 4) { - slice_length = -2400 ; - } - else - { - slice_length = -240 ; - } - $("#progress").text( last_eta( xhr.responseText.slice( slice_length ) ) ) ; + if ( $("#tests").length !== 0 ) + { + tests_store_retrieve( ) ; + tests_last_eta( ) ; + tests_decompose_eta_line( ) ; + //is( 0, 1, "this test always fails" ) ; + tests_bilan( ) ; + //$("#tests").collapse("show") ; + + } } + init( ) ; + tests( ) ; - - function handleRun(xhr, timerRefreshLog) - { - - $("#console").text("Status: " + xhr.status + " " + xhr.statusText + "\n" + "State: " + readyStateStr[xhr.readyState] + "\n") ; - - if (xhr.readyState === 4) { - // var headers = xhr.getAllResponseHeaders(); - // $("#console").append(headers); - // $("#console").append("See the completed log\n"); - $("#link_to_bottom").show(); - clearInterval(timerRefreshLog); - refreshLog(xhr); // a last time - $("#bt-sync").prop("disabled", false); // back to enable state for next run - } - } - - function imapsync() - { - var querystring = $("#form").serialize(); - $("#abort").text("\n\n"); // clean abort console - $("#output").text("Here comes the log!\n\n"); - - if ( "imap.gmail.com" === $("#host1").val() ) - { - querystring = querystring + "&gmail1=on"; - } - if ( "imap.gmail.com" === $("#host2").val() ) - { - querystring = querystring + "&gmail2=on"; - } - - var xhr; - xhr = new XMLHttpRequest(); - var timerRefreshLog = setInterval( - function () - { - refreshLog(xhr); - }, 5000 ) ; - xhr.onreadystatechange = function () - { - handleRun(xhr, timerRefreshLog); - }; - xhr.open("POST", "/cgi-bin/imapsync", true); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.send(querystring); - } - - - function handleAbort(xhr) - { - - $("#abort").text("Status: " + xhr.status + " " + xhr.statusText + "\n" + "State: " + readyStateStr[xhr.readyState] + "\n\n"); - - if (xhr.readyState === 4) - { - $("#abort").append(xhr.responseText); - $("#bt-sync").prop("disabled", false); - $("#bt-abort").prop("disabled", false); // back for next abort - } - } - - function abort() - { - var querystring = $("#form").serialize() + "&abort=on"; - var xhr; - xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () - { - handleAbort(xhr); - }; - xhr.open("POST", "/cgi-bin/imapsync", true); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.send(querystring); - } - - function store( id ) - { - var stored ; - $( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + "\n" ) ; - if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) - { - localStorage.setItem( id, $(id).val() ) ; - stored = $(id).val() ; - } - else if ( "checkbox" === $( id ).attr( "type" ) ) - { - $( "#tests" ).append( "Eco: " + id + " checked is " + $( id )[0].checked + "\n" ) ; - localStorage.setItem( id, $( id )[0].checked ) ; - stored = $( id )[0].checked ; - } - return stored ; - } - - function retrieve( id ) - { - var retrieved ; - $( "#tests" ).append( "Eco: " + id + " type is " + $( id ).attr( "type" ) + " length is " + $( id ).length + "\n" ) ; - if ( "text" === $( id ).attr( "type" ) || "password" === $( id ).attr( "type" ) ) - { - $( id ).val( localStorage.getItem( id ) ) ; - retrieved = $( id ).val() ; - } - else if ( "checkbox" === $( id ).attr( "type" ) ) - { - $( "#tests" ).append( "Eco: " + id + " getItem is " + localStorage.getItem( id ) + "\n" ) ; - $( id )[0].checked = JSON.parse( localStorage.getItem( id ) ) ; - retrieved = $( id )[0].checked ; - } - return retrieved ; - } - - function tests_store_retrieve() - { - if ( $("#tests").length !== 0 ) - { - is( 1, 1, "one equals one" ) ; - // isnot( 0, 1, "zero differs one" ) ; - - // no exist - is( undefined, store( "#test_noexists" ), "store: #test_noexists" ) ; - is( undefined, retrieve( "#test_noexists" ), "retrieve: #test_noexists" ) ; - is( undefined, retrieve( "#test_noexists2" ), "retrieve: #test_noexists2" ) ; - - // input text - $("#test_text" ).val( "foo" ) ; - is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; - is( "foo", store( "#test_text" ), "store: #test_text" ) ; - $("#test_text" ).val( "bar" ) ; - is( "bar", $("#test_text" ).val( ), "#test_text val = bar" ) ; - is( "foo", retrieve( "#test_text" ), "retrieve: #test_text = foo" ) ; - is( "foo", $("#test_text" ).val( ), "#test_text val = foo" ) ; - - - // input check button - $( "#test_checkbox" ).prop( "checked", true ); - is( true, store( "#test_checkbox" ), "store: #test_checkbox checked" ) ; - - $( "#test_checkbox" ).prop( "checked", false ); - is( true, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = true" ) ; - - $( "#test_checkbox" ).prop( "checked", false ); - is( false, store( "#test_checkbox" ), "store: #test_checkbox not checked" ) ; - $( "#test_checkbox" ).prop( "checked", true ); - is( false, retrieve( "#test_checkbox" ), "retrieve: #test_checkbox = false" ) ; - - } - } - - - function store_form() - { - if ( Storage !== "undefined") - { - // Code for localStorage. - store("#user1") ; - store("#password1") ; - store("#host1") ; - store("#subfolder1") ; - store("#showpassword1") ; - - store("#user2") ; - store("#password2") ; - store("#host2") ; - store("#subfolder2") ; - store("#showpassword2") ; - - store("#dry") ; - store("#justlogin") ; - store("#justfolders") ; - store("#justfoldersizes") ; - - localStorage.account1_background_color = $("#account1").css("background-color") ; - localStorage.account2_background_color = $("#account2").css("background-color") ; - } - } - - function show_extra_if_needed() - { - if ( $("#subfolder1").length && $("#subfolder1").val().length > 0 ) - { - $(".extra_param").show() ; - } - if ( $("#subfolder2").length && $("#subfolder2").val().length > 0 ) - { - $(".extra_param").show() ; - } - } - - function retrieve_form() - { - if ( Storage !== "undefined" ) - { - // Code for localStorage. - retrieve( "#user1" ) ; - retrieve( "#password1" ) ; - // retrieve("#showpassword1") ; - retrieve( "#host1" ) ; - retrieve( "#subfolder1" ) ; - - retrieve( "#user2" ) ; - retrieve( "#password2" ) ; - // retrieve("#showpassword2") ; - retrieve( "#host2" ) ; - retrieve( "#subfolder2" ) ; - - retrieve( "#dry" ) ; - retrieve( "#justlogin" ) ; - retrieve( "#justfolders" ) ; - retrieve( "#justfoldersizes" ) ; - - // In case - // localStorage.removeItem( "account1_background_color" ) ; - // localStorage.removeItem( "account2_background_color" ) ; - - if ( localStorage.account1_background_color ) - { - $("#account1").css("background-color", localStorage.account1_background_color ) ; - } - if ( localStorage.account2_background_color ) - { - $("#account2").css("background-color", localStorage.account2_background_color ) ; - } - - // Show the extra parameters if they are not empty because it would be dangerous - // to retrieve them without knowing - show_extra_if_needed() ; - } - } - - - function showpassword( id, button ) - { - var x = document.getElementById( id ); - if ( button.checked ) - { - x.type = "text"; - } else { - x.type = "password"; - } - } - - function init() - { - // in case of a manual refresh, start with - $("#bt-sync").prop("disabled", false); - $("#bt-abort").prop("disabled", false); - $("#link_to_bottom").hide(); - retrieve_form(); - - $("#showpassword1").click( - function () - { - // does not change jslint report... - /*jshint validthis: true */ - var button = this ; - showpassword( "password1", button ) ; - } - ); - - - $("#showpassword2").click( - function () - { - var button = this ; - showpassword( "password2", button ) ; - } - ); - - - $("#bt-sync").click( - function () - { - $("#bt-sync").prop("disabled", true); - $("#bt-abort").prop("disabled", false); - $("#link_to_bottom").hide(); - store_form(); - imapsync(); - } - ); - - $("#bt-abort").click( - function () - { - $("#bt-sync").prop("disabled", true); - $("#bt-abort").prop("disabled", true); - abort(); - } - ); - - - $.fn.swapWith = function(to) - { - var temp = $(to).val(); - $(to).val($(this).val()); - $(this).val(temp); - }; - - - $("#swap").click( - function() - { - // swaping colors can't use swapWith() - var temp1 = $("#account1").css("background-color") ; - var temp2 = $("#account2").css("background-color") ; - $("#account1").css("background-color", temp2 ); - $("#account2").css("background-color", temp1 ); - - $("#user1").swapWith("#user2"); - $("#password1").swapWith("#password2"); - $("#host1").swapWith("#host2"); - $("#subfolder1").swapWith("#subfolder2"); - - var temp = $("#showpassword1")[0].checked ; - $("#showpassword1")[0].checked = $("#showpassword2")[0].checked ; - $("#showpassword2")[0].checked = temp ; - showpassword( "password1", $("#showpassword1")[0] ) ; - showpassword( "password2", $("#showpassword2")[0] ) ; - } - ); - - } - - - - - function progress_bar_update( string ) - { - // - return ; - } - - - - function tests() - { - if ( $("#tests").length !== 0 ) - { - tests_store_retrieve() ; - tests_last_eta() ; - } - } - - init( ) ; - tests( ) ; - - } + } ); diff --git a/X/index.html b/X/index.html index 3ecb07a..981d6bf 120000 --- a/X/index.html +++ b/X/index.html @@ -1 +1 @@ -imapsync_form.html \ No newline at end of file +imapsync_form_extra.html \ No newline at end of file diff --git a/X/noscript.css b/X/noscript.css old mode 100755 new mode 100644 diff --git a/X/server_survey_patterns.txt b/X/server_survey_patterns.txt new file mode 100644 index 0000000..0a5e533 --- /dev/null +++ b/X/server_survey_patterns.txt @@ -0,0 +1,46 @@ +Dovecot Dovecot +OK_IMAP4_ready \\* OK IMAP4 ready. +Courier Courier-IMAP ready. +Exchange Microsoft Exchange +SmarterMail \\* OK IMAP4rev1 SmarterMail.$ +IMAP4rev1_proxy \\* OK IMAP4rev1 proxy server ready.$ +Zoho Zoho +C \\* OK IMAP \\(C\\) +Fenix Fenix ready\..$ +DinaPopper DinaPopper Server ready..$ +IMAP4rev1_server \\* OK IMAP4rev1 server ready.$ +IMAPrev1 \\* OK IMAPrev1.$ +ZXCS ZXCS ready..$ +Welcome \\* OK Welcome.$ +Mailcore Mailcore Mail Server.$ +Speedbone Speedbone Mailservice ready..$ +kasserver kasserver.com mailserver ready..$ +Namesco Namesco Mail Server Ready.$ +imapfront \\* OK imapfront ready..$ +Cloudez Cloudez mail auth ready..$ +Axigen AXIGEN|Axigen +Kerio \\* OK Kerio Connect +ISPConfig3 ISPConfig3 IMAP server ready +ABSOL \\* OK ABSOL Mail Server +PXX IMAP server ready \\(P.* TLS +perdition perdition ready +H IMAP server ready H +Cyrus Cyrus +Gimap OK Gimap ready +IceWarp OK IceWarp +iSCREAM iSCREAM ready +Domino OK Domino +IMAP4rev1_at OK IMAP4rev1 server ready at +InterMail InterMail +IdeaImapServer IdeaImapServer +MDaemon MDaemon +Zimbra Zimbra +Zarafa Zarafa +Postak Postak +Amazon_WorkMail Amazon WorkMail +CommuniGate CommuniGate +Outlook.com Outlook.com +Yandex Yandex +Seznam Seznam +MC-link MC-link +QQMail QQMail diff --git a/X/stat_patterns.txt b/X/stat_patterns.txt index f25f66e..27f195a 100644 --- a/X/stat_patterns.txt +++ b/X/stat_patterns.txt @@ -41,3 +41,5 @@ ^Read:.*\* *ID failure: Error login failure: can not open imap connection on +^\/usr\/local\/www\/apache24\/cgi-bin\/imapsync -- +^\/usr\/lib\/cgi-bin\/imapsync -- diff --git a/doc/GOOD_PRACTICES.html b/doc/GOOD_PRACTICES.html index 6d68138..e09146e 100644 --- a/doc/GOOD_PRACTICES.html +++ b/doc/GOOD_PRACTICES.html @@ -7,7 +7,7 @@ETA: Estimation Time of Arrival+ +++ +Console of imapsync launch
@@ -400,7 +416,7 @@ old: H2YTURNFT4XT4 - +
Local bandwidth statistics
@@ -424,7 +440,7 @@ old: H2YTURNFT4XT4 Bottom
- ($Id: imapsync_form_extra.html,v 1.2 2019/06/25 16:38:24 gilles Exp gilles $)
+ ($Id: imapsync_form_extra.html,v 1.4 2019/11/07 11:16:25 gilles Exp gilles $)
Terms and conditions for anything: No limits to do anything with this work and this license!
@@ -32,7 +32,7 @@ Good practices for imapsync
Gilles LAMIRAL gilles@lamiral.info
-% $Id: GOOD_PRACTICES.t2t,v 1.9 2018/04/10 00:15:05 gilles Exp gilles $ +% $Id: GOOD_PRACTICES.t2t,v 1.10 2019/11/25 12:50:54 gilles Exp gilles $Good practices for imapsync
-You are not supposed to have read the TUTORIAL documentation but it +You are not supposed to have read the TUTORIAL documentation but reading it should help to understand and master the following best practices.
@@ -42,9 +42,11 @@ reading it should help to understand and master the following best practices.By principle, imapsync does not change any single byte of messages, unless --regexmess or --addheader is used. -But since imapsync identifies messages with "Message-Id" and "Received" headers, -it ignores messages that lack those headers; +Imapsync identifies messages with "Message-Id" and "Received" headers, +so it ignores messages that lack those headers; most of the time it happens with the "Sent" folders. +
+A way to sync those messages is to add option --addheader. Before appending a message on host2, and only when needed, --addheader option adds a Message-Id header like "Message-Id: <123456789@imapsync>" diff --git a/doc/GOOD_PRACTICES.t2t b/doc/GOOD_PRACTICES.t2t index ea6fa06..c2b41bc 100644 --- a/doc/GOOD_PRACTICES.t2t +++ b/doc/GOOD_PRACTICES.t2t @@ -1,10 +1,10 @@ Good practices for imapsync Gilles LAMIRAL gilles@lamiral.info -% $Id: GOOD_PRACTICES.t2t,v 1.9 2018/04/10 00:15:05 gilles Exp gilles $ +% $Id: GOOD_PRACTICES.t2t,v 1.10 2019/11/25 12:50:54 gilles Exp gilles $ = Good practices for imapsync = -You are not supposed to have read the TUTORIAL documentation but it +You are not supposed to have read the TUTORIAL documentation but reading it should help to understand and master the following best practices. @@ -12,9 +12,10 @@ reading it should help to understand and master the following best practices. By principle, imapsync does not change any single byte of messages, unless --regexmess or --addheader is used. -But since imapsync identifies messages with "Message-Id" and "Received" headers, -it ignores messages that lack those headers; +Imapsync identifies messages with "Message-Id" and "Received" headers, +so it ignores messages that lack those headers; most of the time it happens with the "Sent" folders. + A way to sync those messages is to add option --addheader. Before appending a message on host2, and only when needed, --addheader option adds a Message-Id header like "Message-Id: <123456789@imapsync>" diff --git a/examples/imapsync_example.sh b/examples/imapsync_example.sh old mode 100644 new mode 100755 index 7d2bac5..eaa72d0 --- a/examples/imapsync_example.sh +++ b/examples/imapsync_example.sh @@ -1,5 +1,5 @@ #!/bin/sh -# $Id: imapsync_example.sh,v 1.6 2016/01/21 03:35:15 gilles Exp gilles $ +# $Id: imapsync_example.sh,v 1.7 2019/11/06 09:58:42 gilles Exp gilles $ # imapsync example shell for Unix users # lines beginning with # are just comments @@ -46,3 +46,14 @@ --automap --justfolders --dry "$@" +# Warning to Mac users using the binary imapsync_bin_Darwin, +# this example is not ready to play. +# You have replace the command above +# ./imapsync +# by +# ./imapsync_bin_Darwin +# +# an easier way is to use the ready to use example script named +# examples/imapsync_example_darwin.sh +# + diff --git a/examples/imapsync_example_darwin.sh b/examples/imapsync_example_darwin.sh new file mode 100755 index 0000000..78d3da1 --- /dev/null +++ b/examples/imapsync_example_darwin.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# $Id: imapsync_example_darwin.sh,v 1.1 2019/11/06 09:58:57 gilles Exp gilles $ + +# imapsync example shell for Mac OS users +# lines beginning with # are just comments + +# See http://imapsync.lamiral.info/#doc +# for more details on how to use imapsync. + +# Replace below the 6 parameters +# "test1.lamiral.info" "test1" "secret1" "test2.lamiral.info" "test2" "secret2" +# with your own values +# Double quotes are necessary if a value contain one or more blanks. + +# value for --host1 is the IMAP source server hostname or IP address +# value for --user1 is the IMAP source user login +# value for --password1 is the IMAP source user password + +# value for --host2 is the IMAP destination server hostname or IP address +# value for --user2 is the IMAP destination user login +# value for --password2 is the IMAP destination user password + +# Character \ at the end of the first line is essential and means +# "this command continues on the next line". You can add other lines +# but don't forget \ character lasting each line, except the last one. + +# Three other options are in this example because they are good to start with +# +# --dry makes imapsync doing nothing, just print what would be done without --dry. +# +# --justfolders does only things about folders (ignore messages). It is good +# to verify the folder mapping is good for you. +# +# --automap guesses folders mapping, for folders like +# "Sent", "Junk", "Drafts", "All", "Archive", "Flagged". +# +# I suggest to start with --automap --justfolders --dry. +# If the folder mapping is not good then add some --f1f2 fold1=fold2 +# to fix it. +# Then remove --dry and have a run to create folders on host2. +# If everything goes well so far then remove --justfolders to +# start syncing messages. + +./imapsync_bin_Darwin --host1 test1.lamiral.info --user1 test1 --password1 'secret1' \ + --host2 test2.lamiral.info --user2 test2 --password2 'secret2' \ + --automap --justfolders --dry "$@" + + + diff --git a/examples/sync_loop_darwin.sh b/examples/sync_loop_darwin.sh old mode 100644 new mode 100755 diff --git a/imapsync b/imapsync index 7e6e28b..85c956a 100755 --- a/imapsync +++ b/imapsync @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# $Id: imapsync,v 1.945 2019/06/26 19:30:56 gilles Exp gilles $ +# $Id: imapsync,v 1.977 2019/12/23 20:18:02 gilles Exp gilles $ # structure # pod documentation # use pragmas @@ -19,13 +19,13 @@ =head1 NAME -imapsync - Email IMAP tool for syncing, copying and migrating -email mailboxes between two imap servers, one way, +imapsync - Email IMAP tool for syncing, copying, migrating +and archiving email mailboxes between two imap servers, one way, and without duplicates. =head1 VERSION -This documentation refers to Imapsync $Revision: 1.945 $ +This documentation refers to Imapsync $Revision: 1.977 $ =head1 USAGE @@ -56,8 +56,8 @@ are synced too. Imapsync reduces the amount of data transferred by not transferring a given message if it already resides on the destination side. -Messages that are on the destination side but not on the -source side stay as they are (see the --delete2 +Messages that are on the destination side but not on the +source side stay as they are (see the --delete2 option to have a strict sync). How imapsync knows a message is already on both sides? @@ -72,19 +72,19 @@ read ones will stay read, deleted will stay deleted. You can abort the transfer at any time and restart it later, imapsync works well with bad connections and interruptions, by design. On a terminal hit Ctr-c twice within two seconds -in order to abort the program. Hit Ctr-c just once makes +in order to abort the program. Hit Ctr-c just once makes imapsync reconnect to both imap servers. A classical scenario is synchronizing a mailbox B from another mailbox A -in case you just want to keep a strict copy of A in B. Strict meaning +where you just want to keep a strict copy of A in B. Strict meaning all messages in A will be in B but no more. For this, option --delete2 has to be used, it deletes messages in host2 folder B that are not in host1 folder A. If you also need to destroy host2 folders that are not in host1 then use --delete2folders. See also --delete2foldersonly and --delete2foldersbutnot to set up exceptions -on folders to destroy (INBOX will never be destroy, it's a mandatory -folder in IMAP). +on folders to destroy. INBOX will never be destroy, it's a mandatory +folder in IMAP. A different scenario is to delete the messages from the source mailbox after a successful transfer, it can be a good feature when migrating @@ -114,11 +114,11 @@ Michael R. Elkins) for a 2 ways synchronization. usage: imapsync [options] -Standard options are the six values forming the credentials, -three on each sides, needed to log in into the IMAP servers, ie, -a host, a username, and a password, two times. +The standard options are the six values forming the credentials. +Three values on each side are needed in order to log in into the IMAP +servers. These six values are a host, a username, and a password, two times. -Conventions used: +Conventions used in the following descriptions of the options: str means string int means integer @@ -133,8 +133,8 @@ Conventions used: --host1 str : Source or "from" imap server. --port1 int : Port to connect on host1. - Optional since default ports are the - well known ports 143 or 993. + Optional since default ports are the + well known ports imap/143 or imaps/993. --user1 str : User to login on host1. --password1 str : Password for the user1. @@ -144,10 +144,10 @@ Conventions used: --password2 str : Password for the user2. --showpasswords : Shows passwords on output instead of "MASKED". - Useful to restart a complete run by just reading + Useful to restart a complete run by just reading the command line used in the log, or to debug passwords. - It's not a secure practice. + It's not a secure practice at all. --passfile1 str : Password file for the user1. It must contain the password on the first line. This option avoids showing @@ -214,14 +214,14 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --folderrec str : Sync this folder recursively. --folderrec str : and this one, etc. - --folderfirst str : Sync this folder first. --folderfirst "Work" + --folderfirst str : Sync this folder first. Ex. --folderfirst "INBOX" --folderfirst str : then this one, etc. --folderlast str : Sync this folder last. --folderlast "[Gmail]/All Mail" --folderlast str : then this one, etc. --nomixfolders : Do not merge folders when host1 is case-sensitive while host2 is not (like Exchange). Only the first - similar folder is synced (example: with folders + similar folder is synced (example: with folders "Sent", "SENT" and "sent" on host1 only "Sent" will be synced to host2). @@ -248,14 +248,14 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --regextrans2 options before all others. Add --debug to see what's really going on. - --subfolder1 str : Syncs the host1 folders hierarchy under str - to the root hierarchy of host2. + --subfolder1 str : Syncs the host1 folders hierarchy which is under folder + str to the root hierarchy of host2. It's the couterpart of a sync done by --subfolder2 - when doing it in the reverse order. + when doing it in the reverse order. Backup/Restore scenario: Use --subfolder2 str for a backup to the folder str - on host2. Then use --subfolder1 str for restoring - from the folder str, after inverting + on host2. Then use --subfolder1 str for restoring + from the folder str, after inverting host1/host2 user1/user2 values. @@ -269,25 +269,26 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 usually "INBOX." or "INBOX/" or an empty string "". imapsync guesses the prefix if host1 imap server does not have NAMESPACE capability. So this option - should not be used, most of the time. + should not be used most of the time. --prefix2 str : Add prefix to all host2 folders. See --prefix1 - --sep1 str : Host1 separator. This option should not be used, + --sep1 str : Host1 separator. This option should not be used most of the time. Imapsync gets the separator from the server itself, by using NAMESPACE, or it tries to guess it from the folders listing (it counts characters / . \\ \ in folder names and choose the more frequent, or finally / if nothing is found. - --sep2 str : Host2 separator. + --sep2 str : Host2 separator. See --sep1 --regextrans2 reg : Apply the whole regex to each destination folders. --regextrans2 reg : and this one. etc. When you play with the --regextrans2 option, first add also the safe options --dry --justfolders - Then, when happy, remove --dry, remove --justfolders. - Have in mind that --regextrans2 is applied after - the automatic prefix and separator inversion. + Then, when happy, remove --dry for a run, then + remove --justfolders for the next ones. + Have in mind that --regextrans2 is applied after + the automatic prefix and separator inversion. For examples see: https://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt @@ -321,6 +322,17 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --logfile str : Change the default log filename (can be dirname/filename). --logdir str : Change the default log directory. Default is LOG_imapsync/ +The default logfile name is for example + + LOG_imapsync/2019_12_22_23_57_59_532_user1_user2.txt + +where: + + 2019_12_22_23_57_59_532 is nearly the date of the start + YYYY_MM_DD_HH_MM_SS_mmm + year_month_day_hour_minute_seconde_millisecond + +and user1 user2 are the --user1 --user2 values. =head2 OPTIONS/messages @@ -329,13 +341,22 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --skipmess is applied before --regexmess --skipmess reg : or this one, etc. + --skipcrossduplicates : Avoid copying messages that are already copied + in another folder, good from Gmail to X when + X is not also Gmail. + Activated with --gmail1 unless --noskipcrossduplicates + + --debugcrossduplicates : Prints which messages (UIDs) are skipped with + --skipcrossduplicates (and in what other folders + they are). + --pipemess cmd : Apply this cmd command to each message content before the copy. - --pipemess cmd : and this one, etc. + --pipemess cmd : and this one, etc. With several --pipemess, the output of each cmd - command (STDOUT) is given to the input (STDIN) + command (STDOUT) is given to the input (STDIN) of the next command. - For example, + For example, --pipemess cmd1 --pipemess cmd2 --pipemess cmd3 is like a Unix pipe: "cat message | cmd1 | cmd2 | cmd3" @@ -346,6 +367,23 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 Example: 's/\000/ /g' # to replace null by space. --regexmess reg : and this one, etc. +=head2 OPTIONS/labels + +Gmail present labels as folders in imap. Imapsync can accelerate the sync +by syncing X-GM-LABELS, it will avoid to transfer messages when they are +already on host2. + + + --synclabels : Syncs also Gmail labels when a message is copied to host2. + Activated by default with --gmail1 --gmail2 unless + --nosynclabels is added. + + --resynclabels : Resyncs Gmail labels when a message is already on host2. + Activated by default with --gmail1 --gmail2 unless + --noresynclabels is added. + +For Gmail syncs, see also: +https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt =head2 OPTIONS/flags @@ -371,7 +409,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 EXPUNGE IMAP command. If expunging after each message slows down too much the sync then use --noexpungeaftereach to speed up, expunging will then be - done only twice per folder, one at the beginning and + done only twice per folder, one at the beginning and one at the end of a folder sync. --expunge1 : Expunge messages on host1 just before syncing a folder. @@ -397,12 +435,15 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --delete2folders : Delete folders in host2 that are not in host1 server. For safety, first try it like this (it is safe): --delete2folders --dry --justfolders --nofoldersizes + and see what folders will be deleted. - --delete2foldersonly reg : Deleted only folders matching regex. + --delete2foldersonly reg : Delete only folders matching the regex reg. Example: --delete2foldersonly "/^Junk$|^INBOX.Junk$/" + This option activates --delete2folders - --delete2foldersbutnot reg : Do not delete folders matching regex. + --delete2foldersbutnot reg : Do not delete folders matching the regex rex. Example: --delete2foldersbutnot "/Tasks$|Contacts$|Foo$/" + This option activates --delete2folders --noexpunge2 : Do not expunge messages on host2. --nouidexpunge2 : Do not uidexpunge messages on the host2 account @@ -419,7 +460,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 a message arrived on a host (Unix mtime). --idatefromheader : Sets the internal dates on host2 same as the ones in "Date:" headers. - + =head2 OPTIONS/message selection @@ -431,7 +472,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 see also --minage --minage int : Skip messages newer than int days. final stats (skipped) don't count newer messages - You can do (+ are the messages selected): + You can do (+ zone are the messages selected): past|----maxage+++++++++++++++>now past|+++++++++++++++minage---->now past|----maxage+++++minage---->now (intersection) @@ -439,22 +480,23 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --search str : Selects only messages returned by this IMAP SEARCH command. Applied on both sides. - For a complete of what can be search see + For a complete set of what can be search see https://imapsync.lamiral.info/FAQ.d/FAQ.Messages_Selection.txt --search1 str : Same as --search but for selecting host1 messages only. --search2 str : Same as --search but for selecting host2 messages only. - --search CRIT equals --search1 CRIT --search2 CRIT + So --search CRIT equals --search1 CRIT --search2 CRIT --maxlinelength int : skip messages with a line length longer than int bytes. - RFC 2822 says it must be no more than 1000 bytes. + RFC 2822 says it must be no more than 1000 bytes but + real life servers and email clients do more. --useheader str : Use this header to compare messages on both sides. Ex: Message-ID or Subject or Date. --useheader str and this one, etc. - --usecache : Use cache to speed up the sync. + --usecache : Use cache to speed up next syncs. Not set by default. --nousecache : Do not use cache. Caveat: --useuid --nousecache creates duplicates on multiple runs. --useuid : Use UIDs instead of headers as a criterion to recognize @@ -465,10 +507,10 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 =head2 OPTIONS/miscellaneous --syncacls : Synchronizes acls (Access Control Lists). - --nosyncacls : Does not synchronize acls. This is the default. Acls in IMAP are not standardized, be careful since one acl code on one side may signify something else on the other one. + --nosyncacls : Does not synchronize acls. This is the default. --addheader : When a message has no headers to be identified, --addheader adds a "Message-Id" header, @@ -498,17 +540,17 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 =head2 OPTIONS/specific - --gmail1 : sets --host1 to Gmail and options from FAQ.Gmail.txt - --gmail2 : sets --host2 to Gmail and options from FAQ.Gmail.txt + --gmail1 : sets --host1 to Gmail and other options. See FAQ.Gmail.txt + --gmail2 : sets --host2 to Gmail and other options. See FAQ.Gmail.txt - --office1 : sets --host1 to Office365 options from FAQ.Exchange.txt - --office2 : sets --host2 to Office365 options from FAQ.Exchange.txt + --office1 : sets --host1 to Office365 and other options. See FAQ.Exchange.txt + --office2 : sets --host2 to Office365 and other options. See FAQ.Exchange.txt - --exchange1 : sets options from FAQ.Exchange.txt, account1 part - --exchange2 : sets options from FAQ.Exchange.txt, account2 part + --exchange1 : sets options for Exchange. See FAQ.Exchange.txt + --exchange2 : sets options for Exchange. See FAQ.Exchange.txt - --domino1 : sets options from FAQ.Domino.txt, account1 part - --domino2 : sets options from FAQ.Domino.txt, account2 part + --domino1 : sets options for Domino. See FAQ.Domino.txt + --domino2 : sets options for Domino. See FAQ.Domino.txt =head2 OPTIONS/behavior @@ -525,11 +567,11 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --abort : terminates a previous call still running. It uses the pidfile to know what process to abort. - --exitwhenover int : Stop syncing and exits when int total bytes + --exitwhenover int : Stop syncing and exits when int total bytes transferred is reached. --version : Print only software version. - --noreleasecheck : Do not check for new imapsync release + --noreleasecheck : Do not check for any new imapsync release. --releasecheck : Check for new imapsync release. it's an http request to http://imapsync.lamiral.info/prj/imapsync/VERSION @@ -540,7 +582,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 information. Need only --host1 and --host2 options. Obsolete since "imapsync --host1 imaphost" alone implies --justconnect - + --justlogin : Just login to both host1 and host2 with users credentials, then exit. @@ -585,9 +627,9 @@ normal (clear) connection on port 143 but it looks for TLS support in the CAPABILITY list of the servers. If TLS is supported then imapsync goes to encryption. -If the automatic ssl/tls detection fails then imapsync will -not protect against sniffing activities on the -network, especially for passwords. +If the automatic ssl and the tls detections fail then imapsync will +not protect against sniffing activities on the network, especially +for passwords. If you want to force ssl or tls just use --ssl1 --ssl2 or --tls1 --tls2 @@ -597,9 +639,9 @@ or at https://imapsync.lamiral.info/FAQ.d/FAQ.Security.txt =head1 EXIT STATUS Imapsync will exit with a 0 status (return code) if everything went good. -Otherwise, it exits with a non-zero status. -Here is the list of the exit code values (an integer between 0 and 255), -the names reflects their meaning: +Otherwise, it exits with a non-zero status. That's classical Unix behavior. +Here is the list of the exit code values (an integer between 0 and 255). +The names reflect their meaning: =for comment egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _' @@ -622,10 +664,11 @@ egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _' EXIT_TESTS_FAILED => 254 ; # Like Test::More API + =head1 LICENSE AND COPYRIGHT Imapsync is free, open, public but not always gratis software -cover by the NOLIMIT Public License. +cover by the NOLIMIT Public License, now called NLPL. See the LICENSE file included in the distribution or just read this simple sentence as it IS the licence text: @@ -641,7 +684,7 @@ Look at https://imapsync.lamiral.info/LICENSE Gilles LAMIRAL
-Good feedback good is always welcome. +Good feedback is always welcome. Bad feedback is very often welcome. Gilles LAMIRAL earns his living by writing, installing, @@ -702,8 +745,8 @@ https://imapsync.lamiral.info/examples/ and all Server releases 2000, 2003, 2008 and R2, 2012 and R2, 2016) as a standalone binary software called imapsync.exe, usually launched from a batch file in order to avoid always typing - the options. - + the options. There is also a 64bit binary called imapsync_64bit.exe + Imapsync works under OS X as a standalone binary software called imapsync_bin_Darwin @@ -740,49 +783,50 @@ Feel free to hack imapsync as the NOLIMIT license permits it. See also https://imapsync.lamiral.info/S/external.shtml for a better up to date list. -Last updated and verified on Thu Apr 11, 2019. +Last updated and verified on Sun Dec 8, 2019. + + + imapsync: https://github.com/imapsync/imapsync (this is an imapsync copy, sometimes delayed, with --noreleasecheck by default since release 1.592, 2014/05/22) + imap_tools: https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/. The imap_tools code is now at https://github.com/andrewnimmo/rick-sanders-imap-tools + imaputils: https://github.com/mtsatsenko/imaputils (very old imap_tools fork) + Doveadm-Sync: https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool ) + davmail: http://davmail.sourceforge.net/ + offlineimap: http://offlineimap.org/ + mbsync: http://isync.sourceforge.net/ + mailsync: http://mailsync.sourceforge.net/ + mailutil: https://www.washington.edu/imap/ part of the UW IMAP toolkit. (well, seems abandoned now) + imaprepl: https://bl0rg.net/software/ http://freecode.com/projects/imap-repl/ + imapcopy (Pascal): http://www.ardiehl.de/imapcopy/ + imapcopy (Java): https://code.google.com/archive/p/imapcopy/ + imapsize: http://www.broobles.com/imapsize/ + migrationtool: http://sourceforge.net/projects/migrationtool/ + imapmigrate: http://sourceforge.net/projects/cyrus-utils/ + larch: https://github.com/rgrove/larch (derived from wonko_imapsync, good at Gmail) + wonko_imapsync: http://wonko.com/article/554 (superseded by larch) + pop2imap: http://www.linux-france.org/prj/pop2imap/ (I wrote that too) + exchange-away: http://exchange-away.sourceforge.net/ + SyncBackPro: http://www.2brightsparks.com/syncback/sbpro.html + ImapSyncClient: https://github.com/ridaamirini/ImapSyncClient + MailStore: https://www.mailstore.com/en/products/mailstore-home/ + mnIMAPSync: https://github.com/manusa/mnIMAPSync + imap-upload: http://imap-upload.sourceforge.net/ (A tool for uploading a local mbox file to IMAP4 server) + imapbackup: https://github.com/rcarmo/imapbackup (A Python script for incremental backups of IMAP mailboxes) + BitRecover email-backup 99 USD, 299 USD https://www.bitrecover.com/email-backup/. + ImportExportTools: https://addons.thunderbird.net/en-us/thunderbird/addon/importexporttools/ ImportExportTools for Mozilla Thunderbird by Paolo Kaosmos. ImportExportTools does not do IMAP. + - imapsync : https://github.com/imapsync/imapsync - (this is an imapsync copy, sometimes delayed, - with --noreleasecheck by default since release 1.592, 2014/05/22) - imap_tools : https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/ - The imap_tools code is now at - https://github.com/andrewnimmo/rick-sanders-imap-tools - imaputils : https://github.com/mtsatsenko/imaputils (very old imap_tools fork) - Doveadm-Sync : https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool ) - davmail : http://davmail.sourceforge.net/ - offlineimap : http://offlineimap.org/ - mbsync : http://isync.sourceforge.net/ - mailsync : http://mailsync.sourceforge.net/ - mailutil : http://www.washington.edu/imap/ part of the UW IMAP tookit. - imaprepl : https://bl0rg.net/software/ http://freecode.com/projects/imap-repl/ - imapcopy (Pascal): http://www.ardiehl.de/imapcopy/ - imapcopy (Java) : https://code.google.com/archive/p/imapcopy/ - imapsize : http://www.broobles.com/imapsize/ - migrationtool : http://sourceforge.net/projects/migrationtool/ - imapmigrate : http://sourceforge.net/projects/cyrus-utils/ - larch : https://github.com/rgrove/larch (derived from wonko_imapsync, good at Gmail) - wonko_imapsync : http://wonko.com/article/554 (superseded by larch) - pop2imap : http://www.linux-france.org/prj/pop2imap/ (I wrote that too) - exchange-away : http://exchange-away.sourceforge.net/ - SyncBackPro : http://www.2brightsparks.com/syncback/sbpro.html - ImapSyncClient : https://github.com/ridaamirini/ImapSyncClient - MailStore : https://www.mailstore.com/en/products/mailstore-home/ - mnIMAPSync : https://github.com/manusa/mnIMAPSync - imap-upload : http://imap-upload.sourceforge.net/ - (a tool for uploading a local mbox file to IMAP4 server) =head1 HISTORY -I initially wrote imapsync in July 2001 because an enterprise, -basystemes, paid me to install a new imap server +I initially wrote imapsync in July 2001 because an enterprise, +called BaSystemes, paid me to install a new imap server without losing huge old mailboxes located in a far -away remote imap server, accessible by an +away remote imap server, accessible by an often broken low-bandwidth ISDN link. I had to verify every mailbox was well transferred, all folders, all messages, -without wasting bandwidth or creating duplicates upon resyncs. The design was -made with the beautiful rsync command in mind. +without wasting bandwidth or creating duplicates upon resyncs. The imapsync +design was made with the beautiful rsync command in mind. Imapsync started its life as a patch of the copy_folder.pl script. The script copy_folder.pl comes from the Mail-IMAPClient-2.1.3 perl @@ -790,7 +834,7 @@ module tarball source (more precisely in the examples/ directory of the Mail-IMAPClient tarball). So many happened since then that I wonder -if it remains any lines of the original +if it remains any lines of the original copy_folder.pl in imapsync source code. @@ -798,7 +842,7 @@ copy_folder.pl in imapsync source code. # use pragmas -# +# use strict ; use warnings ; @@ -825,7 +869,7 @@ use IPC::Open3 'open3' ; use Mail::IMAPClient 3.30 ; use MIME::Base64 ; use Pod::Usage qw(pod2usage) ; -use POSIX qw(uname SIGALRM) ; +use POSIX qw(uname SIGALRM :sys_wait_h) ; use Sys::Hostname ; use Term::ReadKey ; use Test::More ; @@ -836,9 +880,13 @@ use Cwd ; use Readonly ; use Sys::MemInfo ; use Regexp::Common ; -use Text::ParseWords; +use Text::ParseWords ; # for quotewords() use File::Tail ; +use Encode ; +use Encode::IMAPUTF7 ; + + local $OUTPUT_AUTOFLUSH = 1 ; # constants @@ -888,7 +936,7 @@ Readonly my %EXIT_TXT => ( $EX_NOINPUT => 'EX_NOINPUT: cannot open input', $EX_UNAVAILABLE => 'EX_UNAVAILABLE: service unavailable', $EX_SOFTWARE => 'EX_SOFTWARE: internal software error', - + $EXIT_CATCH_ALL => 'EXIT_CATCH_ALL', $EXIT_BY_SIGNAL => 'EXIT_BY_SIGNAL', $EXIT_PID_FILE_ERROR => 'EXIT_PID_FILE_ERROR' , @@ -902,8 +950,6 @@ Readonly my %EXIT_TXT => ( ) ; - - Readonly my $DEFAULT_LOGDIR => 'LOG_imapsync' ; Readonly my $ERRORS_MAX => 50 ; # exit after 50 errors. @@ -946,8 +992,8 @@ Readonly my $NUMBER_42 => 42 ; Readonly my $NUMBER_100 => 100 ; Readonly my $NUMBER_200 => 200 ; Readonly my $NUMBER_300 => 300 ; -Readonly my $NUMBER_123456 => 123456 ; -Readonly my $NUMBER_654321 => 654321 ; +Readonly my $NUMBER_123456 => 123_456 ; +Readonly my $NUMBER_654321 => 654_321 ; Readonly my $NUMBER_20_000 => 20_000 ; @@ -974,6 +1020,7 @@ Readonly my $STR_use_releasecheck => q{Check if a new imapsync release is availa Readonly my $GMAIL_MAXSIZE => 35_651_584 ; +Readonly my $FORCE => 1 ; # if ( 'MSWin32' eq $OSNAME ) # if ( 'darwin' eq $OSNAME ) @@ -987,11 +1034,23 @@ Readonly my $GMAIL_MAXSIZE => 35_651_584 ; my( $sync, + $timestart_str, $debugimap, $debugimap1, $debugimap2, $debugcontent, $debugflags, $debuglist, $debugdev, $debugmaxlinelength, $debugcgi, $domain1, $domain2, + @include, @exclude, @folderrec, @folderfirst, @folderlast, + @h1_folders_all, %h1_folders_all, + @h2_folders_all, %h2_folders_all, + @h2_folders_from_1_wanted, %h2_folders_from_1_all, + %requested_folder, + $h1_folders_wanted_nb, $h1_folders_wanted_ct, + @h2_folders_not_in_1, + %h1_subscribed_folder, %h2_subscribed_folder, + %h2_folders_from_1_wanted, + %h2_folders_from_1_several, + $prefix1, $prefix2, @regexmess, @regexflag, @skipmess, @pipemess, $pipemesscheck, $flagscase, $filterflags, $syncflagsaftercopy, @@ -1000,9 +1059,9 @@ my( $syncacls, $fastio1, $fastio2, $minsize, $maxage, $minage, - $search, $search1, $search2, - $skipheader, @useheader, - $skipsize, $allowsizemismatch, $foldersizes, $foldersizesatend, $buffersize, + $search, + $skipheader, @useheader, %useheader, + $skipsize, $allowsizemismatch, $buffersize, $authmd5, $authmd51, $authmd52, @@ -1020,14 +1079,12 @@ my( $h1_bytes_processed, - $h1_nb_msg_start, $h1_bytes_start, - $h2_nb_msg_start, $h2_bytes_start, $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end, $timeout, $timestart_int, - $timebefore, + $uid1, $uid2, $authuser1, $authuser2, $proxyauth1, $proxyauth2, @@ -1049,10 +1106,18 @@ my( $create_folder_old, $skipcrossduplicates, $debugcrossduplicates, $disarmreadreceipts, - $mixfolders, $skipemptyfolders, + $mixfolders, $fetch_hash_set, + $cgidir, + %month_abrev, + $SSL_VERIFY_POLICY, + $warn_release, ) ; +single_sync( ) ; + +sub single_sync +{ # main program # global variables initialization @@ -1062,7 +1127,7 @@ my( $sync->{timestart} = time ; # Is a float because of use Time::HiRres -$sync->{rcs} = q{$Id: imapsync,v 1.945 2019/06/26 19:30:56 gilles Exp gilles $} ; +$sync->{rcs} = q{$Id: imapsync,v 1.977 2019/12/23 20:18:02 gilles Exp gilles $} ; $sync->{ memory_consumption_at_start } = memory_consumption( ) || 0 ; @@ -1089,8 +1154,10 @@ $sync->{ h1_nb_msg_noheader } = 0 ; $h2_nb_msg_noheader = 0 ; -$h1_nb_msg_start = $h1_bytes_start = 0 ; -$h2_nb_msg_start = $h2_bytes_start = 0 ; +$sync->{ h1_nb_msg_start } = 0 ; +$sync->{ h1_bytes_start } = 0 ; +$sync->{ h2_nb_msg_start } = 0 ; +$sync->{ h2_bytes_start } = 0 ; $sync->{ h1_nb_msg_processed } = $h1_bytes_processed = 0 ; $sync->{ h2_nb_msg_crossdup } = 0 ; @@ -1101,7 +1168,7 @@ $sync->{ h2_nb_msg_crossdup } = 0 ; $sync->{nb_errors} = 0; $max_msg_size_in_bytes = 0; -my %month_abrev = ( +%month_abrev = ( Jan => '00', Feb => '01', Mar => '02', @@ -1117,7 +1184,6 @@ my %month_abrev = ( ); -my $cgidir ; # Just create a CGI object if under cgi context only. # Needed for the get_options() call @@ -1142,8 +1208,8 @@ if ( ! defined $options_good ) { exit $EX_USAGE ; } # the second line (ending with "1 ;") can then stay active or be commented, # the result will be the same: no releasecheck by default (because 0 is then the defined value). -$sync->{releasecheck} = defined $sync->{releasecheck} ? $sync->{releasecheck} : 0 ; -#$sync->{releasecheck} = defined $sync->{releasecheck} ? $sync->{releasecheck} : 1 ; +#$sync->{releasecheck} = defined $sync->{releasecheck} ? $sync->{releasecheck} : 0 ; +$sync->{releasecheck} = defined $sync->{releasecheck} ? $sync->{releasecheck} : 1 ; # just the version if ( $sync->{ version } ) { @@ -1151,6 +1217,7 @@ if ( $sync->{ version } ) { exit 0 ; } +#$sync->{debugenv} = 1 ; $sync->{debugenv} and printenv( $sync ) ; # if option --debugenv load_modules( ) ; @@ -1173,52 +1240,42 @@ $sync->{ tmpdir } ||= File::Spec->tmpdir( ) ; testsexit( $sync ) ; # init live varaiables -testslive( $sync ) if ( $sync->{testslive} ) ; -testslive6( $sync ) if ( $sync->{testslive6} ) ; +testslive_init( $sync ) if ( $sync->{testslive} ) ; +testslive6_init( $sync ) if ( $sync->{testslive6} ) ; # -$sync->{pidfile} = defined $sync->{pidfile} ? $sync->{pidfile} : $sync->{ tmpdir } . '/imapsync.pid' ; -$sync->{pidfilelocking} = defined $sync->{pidfilelocking} ? $sync->{pidfilelocking} : 0 ; +pidfile( $sync ) ; # old abort place -# Unix signals -@{ $sync->{ sigexit } } = ( defined( $sync->{ sigexit } ) ) ? @{ $sync->{ sigexit } } : ( 'QUIT', 'TERM' ) ; -@{ $sync->{ sigreconnect } } = ( defined( $sync->{ sigreconnect } ) ) ? @{ $sync->{ sigreconnect } } : ( 'INT' ) ; -@{ $sync->{ sigprint } } = ( defined( $sync->{ sigprint } ) ) ? @{ $sync->{ sigprint } } : ( 'HUP' ) ; -@{ $sync->{ sigignore } } = ( defined( $sync->{ sigignore } ) ) ? @{ $sync->{ sigignore } } : ( ) ; - -local %SIG = %SIG ; -sig_install( $sync, 'catch_exit', @{ $sync->{ sigexit } } ) ; -sig_install( $sync, 'catch_reconnect', @{ $sync->{ sigreconnect } } ) ; -sig_install( $sync, 'catch_print', @{ $sync->{ sigprint } } ) ; -# --sigignore can override sigexit, sigreconnect and sigprint (for the same signals only) -sig_install( $sync, 'catch_ignore', @{ $sync->{ sigignore } } ) ; - -sig_install_toggle_sleep( $sync ) ; - +install_signals( $sync ) ; $sync->{log} = defined $sync->{log} ? $sync->{log} : 1 ; $sync->{errorsdump} = defined $sync->{errorsdump} ? $sync->{errorsdump} : 1 ; $sync->{errorsmax} = defined $sync->{errorsmax} ? $sync->{errorsmax} : $ERRORS_MAX ; # log and output +binmode STDOUT, ":encoding(UTF-8)" ; + if ( $sync->{log} ) { setlogfile( $sync ) ; teelaunch( $sync ) ; # now $sync->{tee} is a filehandle to STDOUT and the logfile } + +#binmode STDERR, ":encoding(UTF-8)" ; # STDERR goes to the same place: LOG and STDOUT (if logging is on) +# Useful only for --debugssl $sync->{tee} and local *STDERR = *${$sync->{tee}}{IO} ; $timestart_int = int( $sync->{timestart} ) ; -$timebefore = $sync->{timestart} ; +$sync->{timebefore} = $sync->{timestart} ; -my $timestart_str = localtime( $sync->{timestart} ) ; +$timestart_str = localtime( $sync->{timestart} ) ; # The prints in the log starts here @@ -1234,7 +1291,7 @@ myprint( 'Effective user id is ' . getpwuid_any_os( $EFFECTIVE_USER_ID ). " (eui $modulesversion = defined $modulesversion ? $modulesversion : 1 ; -my $warn_release = ( $sync->{releasecheck} ) ? check_last_release( ) : $STR_use_releasecheck ; +$warn_release = ( $sync->{releasecheck} ) ? check_last_release( ) : $STR_use_releasecheck ; $wholeheaderifneeded = defined $wholeheaderifneeded ? $wholeheaderifneeded : 1; @@ -1282,15 +1339,15 @@ remove_pidfile_not_running( $sync->{pidfile} ) ; # if another imapsync is running then tail -f its logfile and exit # useful in cgi context -if ( $sync->{tail} and tail( $sync ) ) +if ( $sync->{ tail } and tail( $sync ) ) { - myprint( "Tail -f finished. Now finishing myself processus $PROCESS_ID\n" ) ; - exit_clean( $sync, $EX_OK ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EX_OK, "Tail -f finished. Now finishing myself processus $PROCESS_ID\n" ) ; exit $EX_OK ; } if ( ! write_pidfile( $sync ) ) { - myprint( "Exiting with return value $EXIT_PID_FILE_ERROR ($EXIT_TXT{$EXIT_PID_FILE_ERROR})\n" ) ; + myprint( "Exiting with return value $EXIT_PID_FILE_ERROR ($EXIT_TXT{$EXIT_PID_FILE_ERROR}) $sync->{nb_errors}/$sync->{errorsmax} nb_errors/max_errors\n" ) ; exit $EXIT_PID_FILE_ERROR ; } @@ -1380,7 +1437,6 @@ if ( defined $delete2foldersbutnot or defined $delete2foldersonly ) { } -my $SSL_VERIFY_POLICY ; my %SSL_VERIFY_STR ; Readonly $SSL_VERIFY_POLICY => IO::Socket::SSL::SSL_VERIFY_NONE( ) ; @@ -1406,17 +1462,22 @@ if ( $sync->{ssl2} ) { myprint( 'Host2: Use --sslargs2 SSL_verify_mode=' . IO::Socket::SSL::SSL_VERIFY_PEER( ) . " to have $SSL_VERIFY_STR{IO::Socket::SSL::SSL_VERIFY_PEER( )} of host2\n" ) ; } +# ID on by default since 1.832 +$sync->{id} = defined $sync->{id} ? $sync->{id} : 1 ; -if ( $sync->{justconnect} - or not $sync->{user1} +if ( $sync->{justconnect} + or not $sync->{user1} or not $sync->{user2} or not $sync->{host1} or not $sync->{host2} ) { my $justconnect = justconnect( $sync ) ; + myprint( debugmemory( $sync, " after justconnect() call" ) ) ; - exit_clean( $sync, $EX_OK, "Exiting after a justconnect on host(s): $justconnect\n" ) ; + exit_clean( $sync, $EX_OK, + "Exiting after a justconnect on host(s): $justconnect\n" + ) ; } @@ -1440,6 +1501,7 @@ if ( $sync->{ delete1 } ) { if ( $sync->{ uidexpunge2 } and not Mail::IMAPClient->can( 'uidexpunge' ) ) { myprint( "Failure: uidexpunge not supported (IMAPClient release < 3.17), use nothing or --expunge2 instead\n" ) ; + $sync->{nb_errors}++ ; exit_clean( $sync, $EX_SOFTWARE ) ; } @@ -1455,6 +1517,7 @@ if ( ( $sync->{ delete2 } or $sync->{ delete2duplicates } ) and not defined $sy if ( $sync->{ delete1 } and $sync->{ delete2 } ) { myprint( "Warning: using --delete1 and --delete2 together is almost always a bad idea, exiting imapsync\n" ) ; + $sync->{nb_errors}++ ; exit_clean( $sync, $EX_USAGE ) ; } @@ -1501,8 +1564,8 @@ if (defined $proxyauth2 && !$authuser2) { missing_option( $sync, 'With --proxyauth2, --authuser2' ) ; } -$authuser1 ||= $sync->{user1}; -$authuser2 ||= $sync->{user2}; +#$authuser1 ||= $sync->{user1}; +#$authuser2 ||= $sync->{user2}; myprint( "Host1: will try to use $authmech1 authentication on host1\n") ; myprint( "Host2: will try to use $authmech2 authentication on host2\n") ; @@ -1517,10 +1580,18 @@ myprint( "Host2: imap connection timeout is $sync->{h2}->{timeout} seconds\n" ) $syncacls = defined $syncacls ? $syncacls : 0 ; # No folders sizes if --justfolders, unless really wanted. -if ( $sync->{ justfolders } and not defined $foldersizes and not $sync->{ justfoldersizes } ) { $foldersizes = 0 ; $foldersizesatend = 1 ; } +if ( + $sync->{ justfolders } + and not defined $sync->{ foldersizes } + and not $sync->{ justfoldersizes } ) +{ + $sync->{ foldersizes } = 0 ; + $sync->{ foldersizesatend } = 1 ; +} + +$sync->{ foldersizes } = ( defined $sync->{ foldersizes } ) ? $sync->{ foldersizes } : 1 ; +$sync->{ foldersizesatend } = ( defined $sync->{ foldersizesatend } ) ? $sync->{ foldersizesatend } : $sync->{ foldersizes } ; -$foldersizes = ( defined $foldersizes ) ? $foldersizes : 1 ; -$foldersizesatend = ( defined $foldersizesatend ) ? $foldersizesatend : $foldersizes ; $fastio1 = defined $fastio1 ? $fastio1 : 0 ; $fastio2 = defined $fastio2 ? $fastio2 : 0 ; @@ -1534,8 +1605,6 @@ $uidnext_default = $DEFAULT_UIDNEXT ; if ( ! @useheader ) { @useheader = qw( Message-Id Received ) ; } -my %useheader ; - # Make a hash %useheader of each --useheader 'key' in uppercase for ( @useheader ) { $useheader{ uc $_ } = undef } ; @@ -1554,8 +1623,8 @@ if( $sync->{dry} ) { $sync->{dry_message} = "\t(not really since --dry mode)" ; } -$search1 ||= $search if ( $search ) ; -$search2 ||= $search if ( $search ) ; +$sync->{ search1 } ||= $search if ( $search ) ; +$sync->{ search2 } ||= $search if ( $search ) ; if ( $disarmreadreceipts ) { push @regexmess, q{s{\A((?:[^\n]+\r\n)+|)(^Disposition-Notification-To:[^\n]*\n)(\r?\n|.*\n\r?\n)}{$1X-$2$3}ims} ; @@ -1570,17 +1639,25 @@ if ( @pipemess and $pipemesscheck ) { my $string = pipemess( q{ }, @pipemess ) ; # string undef means something was bad. if ( not ( defined $string ) ) { - exit_clean( $sync, $EX_USAGE, "Error: one of --pipemess command is bad, check it\n" ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EX_USAGE, + "Error: one of --pipemess command is bad, check it\n" + ) ; } myprint( "Ok with each --pipemess @pipemess\n" ) ; } if ( $maxlinelengthcmd ) { - myprint( "Checking --maxlinelengthcmd command, $maxlinelengthcmd, with an space string.\n" ) ; + myprint( "Checking --maxlinelengthcmd command, + $maxlinelengthcmd, with an space string.\n" + ) ; my $string = pipemess( q{ }, $maxlinelengthcmd ) ; # string undef means something was bad. if ( not ( defined $string ) ) { - exit_clean( $sync, $EX_USAGE, "Error: --maxlinelengthcmd command is bad, check it\n" ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EX_USAGE, + "Error: --maxlinelengthcmd command is bad, check it\n" + ) ; } myprint( "Ok with --maxlinelengthcmd $maxlinelengthcmd\n" ) ; } @@ -1590,7 +1667,10 @@ if ( @regexmess ) { myprint( "Checking each --regexmess command with an space string.\n" ) ; # string undef means one of the eval regex was bad. if ( not ( defined $string ) ) { - exit_clean( $sync, $EX_USAGE, 'Error: one of --regexmess option is bad, check it' ) ; + #errors_incr( $sync, 'Warning: one of --regexmess option may be bad, check them' ) ; + exit_clean( $sync, $EX_USAGE, + "Error: one of --regexmess option is bad, check it\n" + ) ; } myprint( "Ok with each --regexmess\n" ) ; } @@ -1600,7 +1680,10 @@ if ( @skipmess ) { my $match = skipmess( q{ } ) ; # match undef means one of the eval regex was bad. if ( not ( defined $match ) ) { - exit_clean( $sync, $EX_USAGE, 'Error: one of --skipmess option is bad, check it' ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EX_USAGE, + "Error: one of --skipmess option is bad, check it\n" + ) ; } myprint( "Ok with each --skipmess\n" ) ; } @@ -1610,7 +1693,10 @@ if ( @regexflag ) { my $string = flags_regex( q{ } ) ; # string undef means one of the eval regex was bad. if ( not ( defined $string ) ) { - exit_clean( $sync, $EX_USAGE, 'Error: one of --regexflag option is bad, check it' ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EX_USAGE, + "Error: one of --regexflag option is bad, check it\n" + ) ; } myprint( "Ok with each --regexflag\n" ) ; } @@ -1630,9 +1716,18 @@ $sync->{ debug } and myprint( 'Host1 Buffer I/O: ', $sync->{imap1}->Buffer(), "\ $sync->{ debug } and myprint( 'Host2 Buffer I/O: ', $sync->{imap2}->Buffer(), "\n" ) ; -if ( ! $sync->{imap1}->IsAuthenticated( ) ) { exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, 'Not authenticated on host1' ) ; } +if ( ! $sync->{imap1}->IsAuthenticated( ) ) +{ + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Not authenticated on host1\n" ) ; +} myprint( "Host1: state Authenticated\n" ) ; -if ( ! $sync->{imap2}->IsAuthenticated( ) ) { exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, 'Not authenticated on host2' ) ; } + +if ( ! $sync->{imap2}->IsAuthenticated( ) ) +{ + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Not authenticated on host2\n" ) ; +} myprint( "Host2: state Authenticated\n" ) ; myprint( 'Host1 capability once authenticated: ', join(q{ }, @{ $sync->{imap1}->capability() || [] }), "\n" ) ; @@ -1642,9 +1737,6 @@ myprint( 'Host1 capability once authenticated: ', join(q{ }, @{ $sync->{imap1}-> myprint( 'Host2 capability once authenticated: ', join(q{ }, @{ $sync->{imap2}->capability() || [] }), "\n" ) ; - -# ID on by default since 1.832 -$sync->{id} = defined $sync->{id} ? $sync->{id} : 1 ; imap_id_stuff( $sync ) ; #quota( $sync, $sync->{imap1}, 'h1' ) ; # quota on host1 is useless and pollute host2 output. @@ -1655,8 +1747,7 @@ maxsize_setting( $sync ) ; if ( $sync->{ justlogin } ) { $sync->{imap1}->logout( ) ; $sync->{imap2}->logout( ) ; - myprint( "Exiting because of --justlogin\n" ) ; - exit_clean( $sync, $EX_OK ) ; + exit_clean( $sync, $EX_OK, "Exiting because of --justlogin\n" ) ; } @@ -1664,17 +1755,8 @@ if ( $sync->{ justlogin } ) { # Folder stuff # -my ( - @h1_folders_all , %h1_folders_all , @h1_folders_wanted , %requested_folder , - %h1_subscribed_folder , %h2_subscribed_folder , - @h2_folders_all , %h2_folders_all , %h2_folders_all_UPPER , - @h2_folders_from_1_wanted , %h2_folders_from_1_wanted , - %h2_folders_from_1_several , - %h2_folders_from_1_all , -) ; - -my $h1_folders_wanted_nb = 0 ; -my $h1_folders_wanted_ct = 0 ; # counter of folders done. +$h1_folders_wanted_nb = 0 ; # counter of folders to be done. +$h1_folders_wanted_ct = 0 ; # counter of folders done. # All folders on host1 and host2 @@ -1684,17 +1766,20 @@ my $h1_folders_wanted_ct = 0 ; # counter of folders done. myprint( 'Host1: found ', scalar @h1_folders_all , " folders.\n" ) ; myprint( 'Host2: found ', scalar @h2_folders_all , " folders.\n" ) ; -foreach my $f ( @h1_folders_all ) { - $h1_folders_all{ $f } = 1 +foreach my $f ( @h1_folders_all ) +{ + $h1_folders_all{ $f } = 1 } -foreach my $f ( @h2_folders_all ) { + +foreach my $f ( @h2_folders_all ) +{ $h2_folders_all{ $f } = 1 ; - $h2_folders_all_UPPER{ uc $f } = 1 ; + $sync->{h2_folders_all_UPPER}{ uc $f } = 1 ; } $sync->{h1_folders_all} = \%h1_folders_all ; $sync->{h2_folders_all} = \%h2_folders_all ; -$sync->{h2_folders_all_UPPER} = \%h2_folders_all_UPPER ; + private_folders_separators_and_prefixes( ) ; @@ -1756,6 +1841,8 @@ else # consider (optional) includes and excludes if ( scalar @include ) { foreach my $include ( @include ) { + # No, do not add /x after the regex, never. + # Users would kill you! my @included_folders = grep { /$include/ } @h1_folders_all ; add_to_requested_folders( @included_folders ) ; myprint( "Including folders matching pattern $include\n" . jux_utf8_list( @included_folders ) . "\n" ) ; @@ -1765,6 +1852,8 @@ if ( scalar @include ) { if ( scalar @exclude ) { foreach my $exclude ( @exclude ) { my @requested_folder = sort keys %requested_folder ; + # No, do not add /x after the regex, never. + # Users would kill you! my @excluded_folders = grep { /$exclude/ } @requested_folder ; remove_from_requested_folders( @excluded_folders ) ; myprint( "Excluding folders matching pattern $exclude\n" . jux_utf8_list( @excluded_folders ) . "\n" ) ; @@ -1774,7 +1863,8 @@ if ( scalar @exclude ) { # sort before is not very powerful # it adds --folderfirst and --folderlast even if they don't exist on host1 -@h1_folders_wanted = sort_requested_folders( ) ; +#@h1_folders_wanted = sort_requested_folders( ) ; +$sync->{h1_folders_wanted} = [ sort_requested_folders( ) ] ; # Remove no selectable folders @@ -1782,7 +1872,7 @@ if ( scalar @exclude ) { if ( $sync->{ checkfoldersexist } ) { my @h1_folders_wanted_exist ; myprint( "Host1: Checking wanted folders exist. Use --nocheckfoldersexist to avoid this check (shared of public namespace targeted).\n" ) ; - foreach my $folder ( @h1_folders_wanted ) { + foreach my $folder ( @{ $sync->{h1_folders_wanted} } ) { ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Checking $folder exists on host1\n" ) ; if ( ! exists $h1_folders_all{ $folder } ) { myprint( "Host1: warning! ignoring folder $folder because it is not in host1 whole folders list.\n" ) ; @@ -1791,7 +1881,7 @@ if ( $sync->{ checkfoldersexist } ) { push @h1_folders_wanted_exist, $folder ; } } - @h1_folders_wanted = @h1_folders_wanted_exist ; + @{ $sync->{h1_folders_wanted} } = @h1_folders_wanted_exist ; }else{ myprint( "Host1: Not checking that wanted folders exist. Remove --nocheckfoldersexist to get this check.\n" ) ; } @@ -1800,7 +1890,7 @@ if ( $sync->{ checkfoldersexist } ) { if ( $sync->{ checkselectable } ) { my @h1_folders_wanted_selectable ; myprint( "Host1: Checking wanted folders are selectable. Use --nocheckselectable to avoid this check.\n" ) ; - foreach my $folder ( @h1_folders_wanted ) { + foreach my $folder ( @{ $sync->{h1_folders_wanted} } ) { ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Checking $folder is selectable on host1\n" ) ; # It does an imap command LIST "" $folder and then search for no \Noselect if ( ! $sync->{imap1}->selectable( $folder ) ) { @@ -1809,13 +1899,12 @@ if ( $sync->{ checkselectable } ) { push @h1_folders_wanted_selectable, $folder ; } } - @h1_folders_wanted = @h1_folders_wanted_selectable ; - ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext( ), " s\n" ) ; + @{ $sync->{h1_folders_wanted} } = @h1_folders_wanted_selectable ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext( $sync ), " s\n" ) ; }else{ myprint( "Host1: Not checking that wanted folders are selectable. Remove --nocheckselectable to get this check.\n" ) ; } -$sync->{h1_folders_wanted} = \@h1_folders_wanted ; # Old place of private_folders_separators_and_prefixes( ) call. @@ -1829,7 +1918,7 @@ $sync->{h1_folders_wanted} = \@h1_folders_wanted ; automap( $sync ) ; -foreach my $h1_fold ( @h1_folders_wanted ) { +foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } ) { my $h2_fold ; $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; $h2_folders_from_1_wanted{ $h2_fold }++ ; @@ -1837,12 +1926,21 @@ foreach my $h1_fold ( @h1_folders_wanted ) { $h2_folders_from_1_several{ $h2_fold }++ ; } } + @h2_folders_from_1_wanted = sort keys %h2_folders_from_1_wanted; + foreach my $h1_fold ( @h1_folders_all ) { my $h2_fold ; $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; $h2_folders_from_1_all{ $h2_fold }++ ; + # Follows a fix to avoid deleting folder $sync->{ subfolder2 } + # because it usually does not exist on host1. + if ( $sync->{ subfolder2 } ) + { + $h2_folders_from_1_all{ $sync->{ h2_prefix } . $sync->{ subfolder2 } }++ ; + $h2_folders_from_1_all{ $sync->{ subfolder2 } }++ ; + } } @@ -1880,7 +1978,7 @@ if ( $subscribed ) { } -my @h2_folders_not_in_1; + @h2_folders_not_in_1 = list_folders_in_2_not_in_1( ) ; if ( @h2_folders_not_in_1 ) { @@ -1913,21 +2011,28 @@ if ( keys %{ $sync->{f1f2h} } ) { myprint( "\n" ) ; } -exit_clean( $sync, $EX_OK ) if ( $sync->{justfolderlists} ) ; -exit_clean( $sync, $EX_OK ) if ( $sync->{justautomap} ) ; +exit_clean( $sync, $EX_OK, "Exiting because of --justfolderlists\n" ) if ( $sync->{ justfolderlists } ) ; +exit_clean( $sync, $EX_OK, "Exiting because of --justautomap\n" ) if ( $sync->{ justautomap } ) ; debugsleep( $sync ) ; -if ( $foldersizes ) { - foldersizes_on_h1h2( $sync ) ; +if ( $sync->{ skipemptyfolders } ) +{ + myprint( "Host1: will not syncing empty folders on host1. Use --noskipemptyfolders to create them anyway on host2\n") ; +} + + +if ( $sync->{ foldersizes } ) { + + foldersizes_at_the_beggining( $sync ) ; + #foldersizes_at_the_beggining_old( $sync ) ; } if ( $sync->{ justfoldersizes } ) { - myprint( "Exiting because of --justfoldersizes\n" ) ; - exit_clean( $sync, $EX_OK ) ; + exit_clean( $sync, $EX_OK, "Exiting because of --justfoldersizes\n" ) ; } $sync->{stats} = 1 ; @@ -1939,7 +2044,7 @@ if ( $sync->{ delete1emptyfolders } ) { delete_folders_in_2_not_in_1( ) if $delete2folders ; # folder loop -$h1_folders_wanted_nb = scalar @h1_folders_wanted ; +$h1_folders_wanted_nb = scalar @{ $sync->{h1_folders_wanted} } ; myprint( "++++ Looping on each one of $h1_folders_wanted_nb folders to sync\n" ) ; @@ -1950,11 +2055,15 @@ my %uid_candidate_no_deletion ; $sync->{ h2_folders_of_md5 } = { } ; -FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { +FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } ) +{ + $sync->{ h1_current_folder } = $h1_fold ; + eta_print( $sync ) ; if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } my $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; + $sync->{ h2_current_folder } = $h2_fold ; $h1_folders_wanted_ct++ ; myprintf( "Folder %7s %-35s -> %-35s\n", "$h1_folders_wanted_ct/$h1_folders_wanted_nb", @@ -1969,16 +2078,19 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my $h1_fold_nb_messages = count_from_select( $sync->{imap1}->History ) ; myprint( "Host1: folder [$h1_fold] has $h1_fold_nb_messages messages in total (mentioned by SELECT)\n" ) ; - if ( $skipemptyfolders and 0 == $h1_fold_nb_messages ) { + if ( $sync->{ skipemptyfolders } and 0 == $h1_fold_nb_messages ) { myprint( "Host1: skipping empty host1 folder [$h1_fold]\n" ) ; next FOLDER ; } # Code added from https://github.com/imapsync/imapsync/issues/95 # Thanks jh1995 - if ( $skipemptyfolders ) { + # Goal: do not create folder if --search or --max/minage return 0 message. + # even if there are messages by SELECT (no not real empty, empty for the user point of vue). + if ( $sync->{ skipemptyfolders } ) + { my $h1_msgs_all_hash_ref_tmp = { } ; - my @h1_msgs_tmp = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref_tmp, $search1, $h1_fold ) ; + my @h1_msgs_tmp = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref_tmp, $sync->{ search1 }, $h1_fold ) ; my $h1_fold_nb_messages_tmp = scalar( @h1_msgs_tmp ) ; if ( 0 == $h1_fold_nb_messages_tmp ) { myprint( "Host1: skipping empty host1 folder [$h1_fold] (0 message found by SEARCH)\n" ) ; @@ -2009,7 +2121,10 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( $sync->{ expunge1 } ) { myprint( "Host1: Expunging $h1_fold $sync->{dry_message}\n" ) ; - if ( ! $sync->{dry} ) { $sync->{imap1}->expunge( ) } ; + if ( ! $sync->{dry} ) + { + $sync->{imap1}->expunge( ) ; + } } if ( ( ( $subscribe and exists $h1_subscribed_folder{ $h1_fold } ) or $subscribeall ) @@ -2024,7 +2139,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } my $h1_msgs_all_hash_ref = { } ; - my @h1_msgs = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref, $search1, $sync->{abletosearch1}, $h1_fold ); + my @h1_msgs = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref, $sync->{ search1 }, $sync->{abletosearch1}, $h1_fold ); if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } @@ -2032,10 +2147,10 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages\n" ) ; ( $sync->{ debug } or $debuglist ) and myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages, LIST gives: @h1_msgs\n" ) ; - $sync->{ debug } and myprint( "Host1: selecting messages of folder [$h1_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host1: selecting messages of folder [$h1_fold] took ", timenext( $sync ), " s\n" ) ; my $h2_msgs_all_hash_ref = { } ; - my @h2_msgs = select_msgs( $sync->{imap2}, $h2_msgs_all_hash_ref, $search2, $sync->{abletosearch2}, $h2_fold ) ; + my @h2_msgs = select_msgs( $sync->{imap2}, $h2_msgs_all_hash_ref, $sync->{ search2 }, $sync->{abletosearch2}, $h2_fold ) ; if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } @@ -2043,10 +2158,11 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host2: folder [$h2_fold] considering $h2_msgs_nb messages\n" ) ; ( $sync->{ debug } or $debuglist ) and myprint( "Host2: folder [$h2_fold] considering $h2_msgs_nb messages, LIST gives: @h2_msgs\n" ) ; - $sync->{ debug } and myprint( "Host2: selecting messages of folder [$h2_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host2: selecting messages of folder [$h2_fold] took ", timenext( $sync ), " s\n" ) ; my $cache_base = "$sync->{ tmpdir }/imapsync_cache/" ; - my $cache_dir = cache_folder( $cache_base, "$sync->{host1}/$sync->{user1}/$sync->{host2}/$sync->{user2}", $h1_fold, $h2_fold ) ; + my $cache_dir = cache_folder( $cache_base, + "$sync->{host1}/$sync->{user1}/$sync->{host2}/$sync->{user2}", $h1_fold, $h2_fold ) ; my ( $cache_1_2_ref, $cache_2_1_ref ) = ( {}, {} ) ; my $h1_uidvalidity = $sync->{imap1}->uidvalidity( ) || q{} ; @@ -2101,7 +2217,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my ($h1_heads_ref, $h1_fir_ref) = ({}, {}); $h1_heads_ref = $sync->{imap1}->parse_headers([@h1_msgs_not_in_cache], @useheader) if (@h1_msgs_not_in_cache); - $sync->{ debug } and myprint( "Host1: parsing headers of folder [$h1_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host1: parsing headers of folder [$h1_fold] took ", timenext( $sync ), " s\n" ) ; @{ $h1_fir_ref }{@h1_msgs} = ( undef ) ; @@ -2123,7 +2239,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( @h1_msgs ) ; } - $sync->{ debug } and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold] took ", timenext( $sync ), " s\n" ) ; if ( ! $h1_fir_ref ) { my $error = join( q{}, "Host1: folder $h1_fold : Could not fetch_hash ", @@ -2159,7 +2275,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host1: folder [$h1_fold] selected $h1_msgs_nb messages, duplicates $h1_msgs_duplicate_nb\n" ) ; - $sync->{ debug } and myprint( 'Host1: whole time parsing headers took ', timenext(), " s\n" ) ; + $sync->{ debug } and myprint( 'Host1: whole time parsing headers took ', timenext( $sync ), " s\n" ) ; # Getting headers and metada can be so long that host2 might be disconnected here if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } @@ -2168,7 +2284,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my ($h2_heads_ref, $h2_fir_ref) = ( {}, {} ); $h2_heads_ref = $sync->{imap2}->parse_headers([@h2_msgs_not_in_cache], @useheader) if (@h2_msgs_not_in_cache); - $sync->{ debug } and myprint( "Host2: parsing headers of folder [$h2_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host2: parsing headers of folder [$h2_fold] took ", timenext( $sync ), " s\n" ) ; $sync->{ debug } and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold]\n" ) ; @{ $h2_fir_ref }{@h2_msgs} = ( ); # fetch_hash can select by uid with last arg as ref @@ -2186,7 +2302,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( @h2_msgs ) ; } - $sync->{ debug } and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold] took ", timenext( $sync ), " s\n" ) ; my @h2_msgs_duplicate; foreach my $m (@h2_msgs_not_in_cache) { @@ -2216,7 +2332,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host2: folder [$h2_fold] selected $h2_msgs_nb messages, duplicates $h2_msgs_duplicate_nb\n" ) ; - $sync->{ debug } and myprint( 'Host2 whole time parsing headers took ', timenext( ), " s\n" ) ; + $sync->{ debug } and myprint( 'Host2 whole time parsing headers took ', timenext( $sync ), " s\n" ) ; $sync->{ debug } and myprint( "++++ Verifying [$h1_fold] -> [$h2_fold]\n" ) ; # messages in host1 that are not in host2 @@ -2498,9 +2614,10 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host2: Expunging folder $h2_fold $sync->{dry_message}\n" ) ; if ( ! $sync->{dry} ) { $sync->{imap2}->expunge( ) } ; } - $sync->{ debug } and myprint( 'Time: ', timenext( ), " s\n" ) ; + $sync->{ debug } and myprint( 'Time: ', timenext( $sync ), " s\n" ) ; } +eta_print( $sync ) ; myprint( "++++ End looping on each folder\n" ) ; @@ -2508,10 +2625,10 @@ if ( $sync->{ delete1 } and $sync->{ delete1emptyfolders } ) { delete1emptyfolders( $sync ) ; } -( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Time: ', timenext( ), " s\n" ) ; +( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Time: ', timenext( $sync ), " s\n" ) ; -if ( $foldersizesatend ) { +if ( $sync->{ foldersizesatend } ) { myprint( << 'END_SIZE' ) ; Folders sizes after the synchronization. @@ -2529,10 +2646,20 @@ myprint( errorsdump( $sync->{nb_errors}, errors_log( $sync ) ) ) if ( $sync->{er tests_live_result( $sync->{nb_errors} ) if ( $sync->{testslive} or $sync->{testslive6} ) ; -exit_clean( $sync, $EXIT_WITH_ERRORS ) if ( $sync->{nb_errors} ) ; -exit_clean( $sync, $EX_OK ) ; -# END of main program +if ( $sync->{nb_errors} ) +{ + exit_clean( $sync, $EXIT_WITH_ERRORS ) ; +} +else +{ + exit_clean( $sync, $EX_OK ) ; +} + +return ; +} + +# END of sub single_sync # subroutines @@ -2641,64 +2768,331 @@ sub output_reset_with return $mysync->{ output } ; } +sub pidfile +{ + my $mysync = shift ; + + $mysync->{ pidfilelocking } = defined $mysync->{ pidfilelocking } ? $mysync->{ pidfilelocking } : 0 ; + + my $host1 = $mysync->{ host1 } || q{} ; + my $user1 = $mysync->{ user1 } || q{} ; + my $host2 = $mysync->{ host2 } || q{} ; + my $user2 = $mysync->{ user2 } || q{} ; + + my $account1_filtered = filter_forbidden_characters( slash_to_underscore( $host1 . '_' . $user1 ) ) || q{} ; + my $account2_filtered = filter_forbidden_characters( slash_to_underscore( $host2 . '_' . $user2 ) ) || q{} ; + + my $pidfile_basename ; + + if ( $ENV{ 'NET_SERVER_SOFTWARE' } and ( $ENV{ 'NET_SERVER_SOFTWARE' } =~ /Net::Server::HTTP/ ) ) + { + # under local webserver + $pidfile_basename = 'imapsync' . '_' . $account1_filtered . '_' . $account2_filtered . '.pid' ; + } + else + { + $pidfile_basename = 'imapsync.pid' ; + } + + $mysync->{ pidfile } = defined $mysync->{ pidfile } ? $mysync-> { pidfile } : $mysync->{ tmpdir } . "/$pidfile_basename" ; + return ; +} + + +sub tests_kill_zero +{ + note( 'Entering tests_kill_zero()' ) ; + + + + SKIP: { + if ( 'MSWin32' eq $OSNAME ) { skip( 'Tests tests_kill_zero avoided on Windows', 8 ) ; } + + + is( 1, kill( 'ZERO', $PROCESS_ID ), "kill ZERO : myself $PROCESS_ID => 1" ) ; + is( 2, kill( 'ZERO', $PROCESS_ID, $PROCESS_ID ), "kill ZERO : myself $PROCESS_ID $PROCESS_ID => 2" ) ; + + if ( (-e '/.dockerenv' ) or ( 0 == $EFFECTIVE_USER_ID) ) + { + is( 1, kill( 'ZERO', 1 ), "kill ZERO : pid 1 => 1 (docker context or root)" ) ; + is( 2, kill( 'ZERO', $PROCESS_ID, 1 ), "kill ZERO : myself + pid 1, $PROCESS_ID 1 => 2 (docker context or root)" ) ; + } + else + { + is( 0, kill( 'ZERO', 1 ), "kill ZERO : pid 1 => 0 (non root)" ) ; + is( 1, kill( 'ZERO', $PROCESS_ID, 1 ), "kill ZERO : myself + pid 1, $PROCESS_ID 1 => 1 (one is non root)" ) ; + + } + + + my $pid_1 = fork( ) ; + if ( $pid_1 ) + { + # parent + } + else + { + # child + sleep 3 ; + exit ; + } + + my $pid_2 ; + $pid_2 = fork( ) ; + if ( $pid_2 ) + { + # I am the parent + ok( defined( $pid_2 ), "kill_zero: initial fork ok. I am the parent $PROCESS_ID" ) ; + ok( $pid_2 , "kill_zero: initial fork ok, child pid is $pid_2" ) ; + is( 3, kill( 'ZERO', $PROCESS_ID, $pid_2, $pid_1 ), "kill ZERO : myself $PROCESS_ID and child $pid_2 and brother $pid_1 => 3" ) ; + + is( $pid_2, waitpid( $pid_2, 0 ), "kill_zero: child $pid_2 no more there => waitpid return $pid_2" ) ; + } + else + { + # I am the child + note( 'This one fails under Windows, kill ZERO returns 0 instead of 2' ) ; + is( 2, kill( 'ZERO', $PROCESS_ID, $pid_1 ), "kill ZERO : myself child $PROCESS_ID brother $pid_1 => 2" ) ; + myprint( "I am the child pid $PROCESS_ID, Exiting\n" ) ; + exit ; + } + wait( ) ; + + # End of SKIP block + } + + note( 'Leaving tests_kill_zero()' ) ; + return ; +} + + + + +sub tests_killpid_by_parent +{ + note( 'Entering tests_killpid_by_parent()' ) ; + + SKIP: { + if ( 'MSWin32' eq $OSNAME ) { skip( 'Tests tests_killpid_by_parent avoided on Windows', 7 ) ; } + + is( undef, killpid( ), 'killpid: no args => undef' ) ; + note( "killpid: trying to kill myself pid $PROCESS_ID, hope I will not succeed" ) ; + is( undef, killpid( $PROCESS_ID ), 'killpid: myself => undef' ) ; + + local $SIG{'QUIT'} = sub { myprint "GOT SIG QUIT! I am PID $PROCESS_ID. Exiting\n" ; exit ; } ; + + my $pid ; + $pid = fork( ) ; + if ( $pid ) + { + # I am the parent + ok( defined( $pid ), "killpid: initial fork ok. I am the parent $PROCESS_ID" ) ; + ok( $pid , "killpid: initial fork ok, child pid is $pid" ) ; + + is( 2, kill( 'ZERO', $PROCESS_ID, $pid ), "kill ZERO : myself $PROCESS_ID and child $pid => 2" ) ; + is( 1, killpid( $pid ), "killpid: child $pid killed => 1" ) ; + is( -1, waitpid( $pid, 0 ), "killpid: child $pid no more there => waitpid return -1" ) ; + } + else + { + # I am the child + myprint( "I am the child pid $PROCESS_ID, sleeping 1 + 3 seconds then kill myself\n" ) ; + sleep 1 ; + myprint( "I am the child pid $PROCESS_ID, slept 1 second, should be killed by my parent now, PPID " . mygetppid( ) . "\n" ) ; + sleep 3 ; + # this test should not be run. If it happens => failure. + ok( 0 == 1, "killpid: child pid $PROCESS_ID not dead => failure" ) ; + myprint( "I am the child pid $PROCESS_ID, killing myself failure... Exiting\n" ) ; + exit ; + } + + # End of SKIP block + } + note( 'Leaving tests_killpid_by_parent()' ) ; + return ; +} + +sub tests_killpid_by_brother +{ + note( 'Entering tests_killpid_by_brother()' ) ; + + + SKIP: { + if ( 'MSWin32' eq $OSNAME ) { skip( 'Tests tests_killpid_by_brother avoided on Windows', 2 ) ; } + + local $SIG{'QUIT'} = sub { myprint "GOT SIG QUIT! I am PID $PROCESS_ID. Exiting\n" ; exit ; } ; + + my $pid_parent = $PROCESS_ID ; + myprint( "I am the parent pid $pid_parent\n" ) ; + my $pid_1 = fork( ) ; + if ( $pid_1 ) + { + # parent + } + else + { + # child + #while ( 1 ) { } ; + sleep 2 ; + sleep 2 ; + # this test should not be run. If it happens => failure. + # Well under Windows this always fails, shit! + ok( 0 == 1 or ( 'MSWin32' eq $OSNAME ) , "killpid: child pid $PROCESS_ID killing by brother but not dead => failure" ) ; + myprint( "I am the child pid $PROCESS_ID, killing by brother failed... Exiting\n" ) ; + exit ; + } + + my $pid_2 ; + $pid_2 = fork( ) ; + if ( $pid_2 ) + { + # parent + } + else + { + # I am the child + myprint( "I am the child pid $PROCESS_ID, my brother has pid $pid_1\n" ) ; + is( 1, killpid( $pid_1 ), "killpid: brother $pid_1 killed => 1" ) ; + sleep 2 ; + exit ; + } + + #sleep 1 ; + is( $pid_1, waitpid( $pid_1, 0), "I am the parent $PROCESS_ID waitpid _1( $pid_1 )" ) ; + is( $pid_2, waitpid( $pid_2, 0 ), "I am the parent $PROCESS_ID waitpid _2( $pid_2 )" ) ; + + + # End of SKIP block + } + + note( 'Leaving tests_killpid_by_brother()' ) ; + return ; +} + + +sub killpid +{ + my $pidtokill = shift ; + + if ( ! $pidtokill ) { + myprint( "No process to abort.\n" ) ; + return ; + } + + if ( $PROCESS_ID == $pidtokill ) { + myprint( "I will not kill myself pid $PROCESS_ID via killpid. Sractch it!\n" ) ; + return ; + } + + + # First ask for suicide + if ( kill( 'ZERO', $pidtokill ) or ( 'MSWin32' eq $OSNAME ) ) { + myprint( "Sending signal QUIT to PID $pidtokill \n" ) ; + kill 'QUIT', $pidtokill ; + sleep 2 ; + waitpid( $pidtokill, WNOHANG) ; + }else{ + myprint( "Can not send signal kill ZERO to PID $pidtokill.\n" ) ; + return ; + } + + #while ( waitpid( $pidtokill, WNOHANG) > 0 ) { } ; + + # Then murder + if ( kill( 'ZERO', $pidtokill ) or ( 'MSWin32' eq $OSNAME ) ) { + myprint( "Sending signal KILL to PID $pidtokill \n" ) ; + kill 'KILL', $pidtokill ; + sleep 1 ; + waitpid( $pidtokill, WNOHANG) ; + }else{ + myprint( "Process PID $pidtokill ended.\n" ) ; + return 1; + } + # Well ... + if ( kill( 'ZERO', $pidtokill ) or ( 'xMSWin32' eq $OSNAME ) ) { + myprint( "Process PID $pidtokill seems still there. Can not do much.\n" ) ; + return ; + }else{ + myprint( "Process PID $pidtokill ended.\n" ) ; + return 1; + } + + return ; +} + +sub tests_abort +{ + note( 'Entering tests_abort()' ) ; + + is( undef, abort( ), 'abort: no args => undef' ) ; + note( 'Leaving tests_abort()' ) ; + return ; +} + + sub abort { - my $mysync = shift @ARG ; - if ( ! -r $mysync->{pidfile} ) { - myprint( "Can not read pidfile $mysync->{pidfile}. Exiting.\n" ) ; - exit $EX_OK ; - } - my $pidtokill = firstline( $mysync->{pidfile} ) ; - if ( ! $pidtokill ) { - myprint( "No process to abort. Exiting.\n" ) ; - exit $EX_OK ; - } - # First ask for suicide - if ( kill 'ZERO', $pidtokill ) { - myprint( "Sending signal QUIT to PID $pidtokill \n" ) ; - kill 'QUIT', $pidtokill ; - sleep 1 ; - }else{ - myprint( "Can not send signal to PID $pidtokill. Exiting.\n" ) ; - exit $EX_OK ; - } - # Then murder - if ( kill 'ZERO', $pidtokill ) { - myprint( "Sending signal KILL to PID $pidtokill \n" ) ; - kill 'KILL', $pidtokill ; - sleep 1 ; - }else{ - myprint( "Process PID $pidtokill ended. Exiting.\n" ) ; - exit $EX_OK ; - } - # Well ... - if ( kill 'ZERO', $pidtokill ) { - myprint( "Process PID $pidtokill still there. Can not do much. Exiting.\n" ) ; - exit $EX_OK ; - }else{ - myprint( "Process PID $pidtokill ended. Exiting.\n" ) ; - exit $EX_OK ; - } - # well abort job done anyway + my $mysync = shift @ARG ; + + if ( not $mysync ) { return ; } + + if ( ! -r $mysync->{pidfile} ) { + myprint( "Can not read pidfile $mysync->{pidfile}. Exiting.\n" ) ; + exit $EX_OK ; + } + my $pidtokill = firstline( $mysync->{pidfile} ) ; + if ( ! $pidtokill ) { + myprint( "No process to abort. Exiting.\n" ) ; + exit $EX_OK ; + } + + killpid( $pidtokill ) ; + + # well, the abort job is done anyway, because even when not succeeded + # in aborting another run, this run has to end without doing any + # thing else exit $EX_OK ; } +sub under_docker_context +{ + my $mysync = shift ; + + if ( -e '/.dockerenv' ) + { + return 1 ; + } + else + { + return 0 ; + } + + return ; +} + sub docker_context { my $mysync = shift ; - -e '/.dockerenv' || return ; - myprint( "Docker context detected with /.dockerenv\n" ) ; + + #-e '/.dockerenv' || return ; + + if ( ! under_docker_context( $mysync ) ) + { + return ; + } + + $mysync->{ debug } and myprint( "Docker context detected with /.dockerenv\n" ) ; # No pidfile $mysync->{pidfile} = q{} ; # No log $mysync->{log} = 0 ; # In case - myprint( "Changing current directory to /var/tmp/\n" ) ; + $mysync->{ debug } and myprint( "Changing current directory to /var/tmp/\n" ) ; chdir '/var/tmp/' ; return ; @@ -2709,7 +3103,7 @@ sub cgibegin my $mysync = shift ; if ( ! under_cgi_context( $mysync ) ) { return ; } require CGI ; - CGI->import( qw( -no_debug ) ) ; + CGI->import( qw( -no_debug -utf8 ) ) ; require CGI::Carp ; CGI::Carp->import( qw( fatalsToBrowser ) ) ; $mysync->{cgi} = CGI->new( ) ; @@ -2749,12 +3143,12 @@ sub tests_under_cgi_context sub under_cgi_context { my $mysync = shift ; - # Under cgi context - if ( $ENV{SERVER_SOFTWARE} ) { - return 1 ; - } - # Not in cgi context - return ; + # Under cgi context + if ( $ENV{SERVER_SOFTWARE} ) { + return 1 ; + } + # Not in cgi context + return ; } sub cgibuildheader @@ -2785,7 +3179,7 @@ sub cgibuildheader ) ; }else{ $httpheader = $mysync->{cgi}->header( - -type => 'text/plain', + -type => 'text/plain; charset=UTF-8', -status => '200 OK to sync IMAP boxes' . ". Load on " . hostname() . " is $mysync->{ loadavg }", -cookie => $cookie, ) ; @@ -2803,8 +3197,10 @@ sub cgiload if ( $mysync->{ abort } ) { return ; } # keep going to abort since some ressources will be free soon if ( $mysync->{ loaddelay } ) { - myprint( "Server is on heavy load. Be back in $mysync->{ loaddelay } min. Load is $mysync->{ loadavg }\n") ; - exit_clean( $mysync, $EX_UNAVAILABLE ) ; + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EX_UNAVAILABLE, + "Server is on heavy load. Be back in $mysync->{ loaddelay } min. Load is $mysync->{ loadavg }\n" + ) ; } return ; } @@ -2930,7 +3326,8 @@ sub cgisetcontext # Set safe default values (I hope...) - $mysync->{pidfile} = 'imapsync.pid' ; + + #$mysync->{pidfile} = 'imapsync.pid' ; $mysync->{pidfilelocking} = 1 ; $mysync->{errorsmax} = $ERRORS_MAX_CGI ; $modulesversion = 0 ; @@ -2943,30 +3340,39 @@ sub cgisetcontext $mysync->{hashfile} = $CGI_HASHFILE ; my $hashsynclocal = hashsynclocal( $mysync ) || die "Can not get hashsynclocal. Exiting\n" ; - $cgidir = $CGI_TMPDIR_TOP . '/' . $hashsynclocal ; + if ( $ENV{ 'NET_SERVER_SOFTWARE' } and ( $ENV{ 'NET_SERVER_SOFTWARE' } =~ /Net::Server::HTTP/ ) ) + { + # under local webserver + $cgidir = q{.} ; + } + else + { + $cgidir = $CGI_TMPDIR_TOP . '/' . $hashsynclocal ; + } -d $cgidir or mkpath $cgidir or die "Can not create $cgidir: $OS_ERROR\n" ; + $mysync->{ tmpdir } = $cgidir ; + chdir $cgidir or die "Can not cd to $cgidir: $OS_ERROR\n" ; - $mysync->{ tmpdir } = $cgidir ; - cgioutputenvcontext( $mysync ) ; + cgioutputenvcontext( $mysync ) ; $mysync->{ debug } and output( $mysync, 'Current directory is ' . getcwd( ) . "\n" ) ; $mysync->{ debug } and output( $mysync, 'Real user id is ' . getpwuid_any_os( $REAL_USER_ID ) . " (uid $REAL_USER_ID)\n" ) ; $mysync->{ debug } and output( $mysync, 'Effective user id is ' . getpwuid_any_os( $EFFECTIVE_USER_ID ). " (euid $EFFECTIVE_USER_ID)\n" ) ; - $skipemptyfolders = defined $skipemptyfolders ? $skipemptyfolders : 1 ; + $mysync->{ skipemptyfolders } = defined $mysync->{ skipemptyfolders } ? $mysync->{ skipemptyfolders } : 1 ; # Out of memory with messages over 1 GB ? $mysync->{ maxsize } = defined $mysync->{ maxsize } ? $mysync->{ maxsize } : 1_000_000_000 ; # tail -f behaviour on by default $mysync->{ tail } = defined $mysync->{ tail } ? $mysync->{ tail } : 1 ; - + # not sure it's for good @useheader = qw( Message-Id ) ; # addheader on by default $mysync->{ addheader } = defined $mysync->{ addheader } ? $mysync->{ addheader } : 1 ; - + return ; } @@ -2984,9 +3390,6 @@ sub cgioutputenvcontext } - - - sub debugsleep { my $mysync = shift @ARG ; @@ -2997,7 +3400,440 @@ sub debugsleep return ; } -sub foldersizes_on_h1h2 +sub tests_foldersize +{ + note( 'Entering tests_foldersize()' ) ; + + is( undef, foldersize( ), 'foldersize: no args => undef' ) ; + + + #is_deeply( {}, {}, 'foldersize: a hash is a hash' ) ; + #is_deeply( [], [], 'foldersize: an array is an array' ) ; + note( 'Leaving tests_foldersize()' ) ; + return ; +} + + + +# Globals: +# $uidnext_default +# $fetch_hash_set +# +sub foldersize +{ + # size of one folder + my ( $mysync, $side, $imap, $search_cmd, $abletosearch, $folder ) = @ARG ; + + if ( ! all_defined( $mysync, $side, $imap, $folder ) ) + { + return ; + } + + # FTGate is RFC buggy with EXAMINE it does not act as SELECT + #if ( ! $imap->examine( $folder ) ) { + if ( ! $imap->select( $folder ) ) { + my $error = join q{}, + "$side Folder $folder: Could not select: ", + $imap->LastError, "\n" ; + errors_incr( $mysync, $error ) ; + return ; + } + + if ( $imap->IsUnconnected( ) ) + { + return ; + } + + my $hash_ref = { } ; + my @msgs = select_msgs( $imap, undef, $search_cmd, $abletosearch, $folder ) ; + my $nb_msgs = scalar @msgs ; + my $biggest_in_folder = 0 ; + @{ $hash_ref }{ @msgs } = ( undef ) if @msgs ; + + my $stot = 0 ; + + if ( $imap->IsUnconnected( ) ) + { + return ; + } + + if ( $nb_msgs > 0 and @msgs ) { + if ( $abletosearch ) { + if ( ! $imap->fetch_hash( \@msgs, 'RFC822.SIZE', $hash_ref) ) { + my $error = "$side failure with fetch_hash: $EVAL_ERROR\n" ; + errors_incr( $mysync, $error ) ; + return ; + } + } + else + { + my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ; + my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ; + if ( ! $imap->fetch_hash( $fetch_hash_uids, 'RFC822.SIZE', $hash_ref ) ) { + my $error = "$side failure with fetch_hash: $EVAL_ERROR\n" ; + errors_incr( $mysync, $error ) ; + return ; + } + } + for ( keys %{ $hash_ref } ) { + my $size = $hash_ref->{ $_ }->{ 'RFC822.SIZE' } ; + $stot += $size ; + $biggest_in_folder = max( $biggest_in_folder, $size ) ; + } + } + return( $stot, $nb_msgs, $biggest_in_folder ) ; + +} + + +# The old subroutine that performed just one side at a time. +# Still here for a while, until confident with sub foldersize_diff_compute() +sub foldersizes +{ + my ( $mysync, $side, $imap, $search_cmd, $abletosearch, @folders ) = @_ ; + my $total_size = 0 ; + my $total_nb = 0 ; + my $biggest_in_all = 0 ; + + my $nb_folders = scalar @folders ; + my $ct_folders = 0 ; # folder counter. + myprint( "++++ Calculating sizes of $nb_folders folders on $side\n" ) ; + foreach my $folder ( @folders ) { + my $stot = 0 ; + my $nb_msgs = 0 ; + my $biggest_in_folder = 0 ; + + $ct_folders++ ; + myprintf( "$side folder %7s %-35s", "$ct_folders/$nb_folders", jux_utf8( $folder ) ) ; + if ( 'Host2' eq $side and not exists $mysync->{h2_folders_all_UPPER}{ uc $folder } ) { + myprint( " does not exist yet\n") ; + next ; + } + if ( 'Host1' eq $side and not exists $h1_folders_all{ $folder } ) { + myprint( " does not exist\n" ) ; + next ; + } + + last if $imap->IsUnconnected( ) ; + + ( $stot, $nb_msgs, $biggest_in_folder ) = foldersize( $mysync, $side, $imap, $search_cmd, $abletosearch, $folder ) ; + + myprintf( ' Size: %9s', $stot ) ; + myprintf( ' Messages: %5s', $nb_msgs ) ; + myprintf( " Biggest: %9s\n", $biggest_in_folder ) ; + $total_size += $stot ; + $total_nb += $nb_msgs ; + $biggest_in_all = max( $biggest_in_all, $biggest_in_folder ) ; + } + myprintf( "%s Nb folders: %11s folders\n", $side, $nb_folders ) ; + myprintf( "%s Nb messages: %11s messages\n", $side, $total_nb ) ; + myprintf( "%s Total size: %11s bytes (%s)\n", $side, $total_size, bytes_display_string( $total_size ) ) ; + myprintf( "%s Biggest message: %11s bytes (%s)\n", $side, $biggest_in_all, bytes_display_string( $biggest_in_all ) ) ; + myprintf( "%s Time spent on sizing: %11.1f seconds\n", $side, timenext( $mysync ) ) ; + return( $total_nb, $total_size ) ; +} + + +sub foldersize_diff_present +{ + my $mysync = shift ; + my $folder1 = shift ; + my $folder2 = shift ; + my $counter_str = shift ; + my $force = shift ; + + my $values1_str ; + my $values2_str ; + + if ( ! defined $mysync->{ folder1 }->{ $folder1 }->{ size } || $force ) + { + foldersize_diff_compute( $mysync, $folder1, $folder2, $force ) ; + } + + # again, but this time it means no availaible data. + if ( defined $mysync->{ folder1 }->{ $folder1 }->{ size } ) + { + $values1_str = sprintf( "Size: %9s Messages: %5s Biggest: %9s\n", + $mysync->{ folder1 }->{ $folder1 }->{ size }, + $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs }, + $mysync->{ folder1 }->{ $folder1 }->{ biggest }, + ) ; + } + else + { + $values1_str = " does not exist\n" ; + } + + if ( defined $mysync->{ folder2 }->{ $folder2 }->{ size } ) + { + $values2_str = sprintf( "Size: %9s Messages: %5s Biggest: %9s\n", + $mysync->{ folder2 }->{ $folder2 }->{ size }, + $mysync->{ folder2 }->{ $folder2 }->{ nb_msgs }, + $mysync->{ folder2 }->{ $folder2 }->{ biggest }, + ) ; + } + else + { + $values2_str = " does not exist yet\n" ; + } + + myprintf( "Host1 folder %7s %-35s %s", + "$counter_str", + jux_utf8( $folder1 ), + $values1_str + ) ; + + myprintf( "Host2 folder %7s %-35s %s", + "$counter_str", + jux_utf8( $folder2 ), + $values2_str + ) ; + + myprintf( "Host2-Host1 %7s %-35s %9s %5s %9s\n\n", + "", + "", + $mysync->{ folder1 }->{ $folder1 }->{ size_diff }, + $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs_diff }, + $mysync->{ folder1 }->{ $folder1 }->{ biggest_diff }, + + ) ; + + + + + return ; +} + +sub foldersize_diff_compute +{ + my $mysync = shift ; + my $folder1 = shift ; + my $folder2 = shift ; + my $force = shift ; + + + + my ( $size_1, $nb_msgs_1, $biggest_1 ) ; + # memoization + if ( + exists $h1_folders_all{ $folder1 } + && + ( + ! defined $mysync->{ folder1 }->{ $folder1 }->{ size } + || $force + ) + ) + { + #myprint( "foldersize folder1 $h1_folders_all{ $folder1 }\n" ) ; + ( $size_1, $nb_msgs_1, $biggest_1 ) = + foldersize( $mysync, + 'Host1', + $mysync->{ imap1 }, + $mysync->{ search1 }, + $mysync->{ abletosearch1 }, + $folder1 + ) ; + $mysync->{ folder1 }->{ $folder1 }->{ size } = $size_1 ; + $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs } = $nb_msgs_1 ; + $mysync->{ folder1 }->{ $folder1 }->{ biggest } = $biggest_1 ; + } + else + { + $size_1 = $mysync->{ folder1 }->{ $folder1 }->{ size } ; + $nb_msgs_1 = $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs } ; + $biggest_1 = $mysync->{ folder1 }->{ $folder1 }->{ biggest } ; + + } + + + my ( $size_2, $nb_msgs_2, $biggest_2 ) ; + if ( + exists $mysync->{ h2_folders_all_UPPER }{ uc $folder2 } + && + ( + ! defined $mysync->{ folder2 }->{ $folder2 }->{ size } + || $force + ) + ) + { + #myprint( "foldersize folder2\n" ) ; + ( $size_2, $nb_msgs_2, $biggest_2 ) = + foldersize( $mysync, + 'Host2', + $mysync->{ imap2 }, + $mysync->{ search2 }, + $mysync->{ abletosearch2 }, + $folder2 + ) ; + + $mysync->{ folder2 }->{ $folder2 }->{ size } = $size_2 ; + $mysync->{ folder2 }->{ $folder2 }->{ nb_msgs } = $nb_msgs_2 ; + $mysync->{ folder2 }->{ $folder2 }->{ biggest } = $biggest_2 ; + } + else + { + $size_2 = $mysync->{ folder2 }->{ $folder2 }->{ size } ; + $nb_msgs_2 = $mysync->{ folder2 }->{ $folder2 }->{ nb_msgs } ; + $biggest_2 = $mysync->{ folder2 }->{ $folder2 }->{ biggest } ; + + } + + + my $size_diff = diff( $size_2, $size_1 ) ; + my $nb_msgs_diff = diff( $nb_msgs_2, $nb_msgs_1 ) ; + my $biggest_diff = diff( $biggest_2, $biggest_1 ) ; + + $mysync->{ folder1 }->{ $folder1 }->{ size_diff } = $size_diff ; + $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs_diff } = $nb_msgs_diff ; + $mysync->{ folder1 }->{ $folder1 }->{ biggest_diff } = $biggest_diff ; + + # It's redundant but easier to access later + $mysync->{ folder2 }->{ $folder2 }->{ size_diff } = $size_diff ; + $mysync->{ folder2 }->{ $folder2 }->{ nb_msgs_diff } = $nb_msgs_diff ; + $mysync->{ folder2 }->{ $folder2 }->{ biggest_diff } = $biggest_diff ; + + return ; +} + +sub diff +{ + my $x = shift ; + my $y = shift ; + + $x ||= 0 ; + $y ||= 0 ; + + return $x - $y ; +} + +sub add +{ + my $x = shift ; + my $y = shift ; + + $x ||= 0 ; + $y ||= 0 ; + + return $x + $y ; +} + + +sub foldersizes_diff_list +{ + my $mysync = shift ; + my $force = shift ; + + my @folders = @{ $mysync->{h1_folders_wanted} } ; + my $nb_folders = scalar @folders ; + my $ct_folders = 0 ; # folder counter. + + foreach my $folder1 ( @folders ) + { + $ct_folders++ ; + my $counter_str = "$ct_folders/$nb_folders" ; + my $folder2 = imap2_folder_name( $mysync, $folder1 ) ; + foldersize_diff_present( $mysync, $folder1, $folder2, $counter_str, $force ) ; + } + + return ; +} + +sub foldersizes_total +{ + my $mysync = shift ; + + my @folders_1 = @{ $mysync->{h1_folders_wanted} } ; + my @folders_2 = @h2_folders_from_1_wanted ; + + my $nb_folders_1 = scalar( @folders_1 ) ; + my $nb_folders_2 = scalar( @folders_2 ) ; + + my ( $total_size_1, $total_nb_1, $biggest_in_all_1 ) = ( 0, 0, 0 ) ; + my ( $total_size_2, $total_nb_2, $biggest_in_all_2 ) = ( 0, 0, 0 ) ; + + foreach my $folder1 ( @folders_1 ) + { + $total_size_1 = add( $total_size_1, $mysync->{ folder1 }->{ $folder1 }->{ size } ) ; + $total_nb_1 = add( $total_nb_1, $mysync->{ folder1 }->{ $folder1 }->{ nb_msgs } ) ; + $biggest_in_all_1 = max( $biggest_in_all_1 , $mysync->{ folder1 }->{ $folder1 }->{ biggest } ) ; + } + + foreach my $folder2 ( @folders_2 ) + { + $total_size_2 = add( $total_size_2, $mysync->{ folder2 }->{ $folder2 }->{ size } ) ; + $total_nb_2 = add( $total_nb_2, $mysync->{ folder2 }->{ $folder2 }->{ nb_msgs } ) ; + $biggest_in_all_2 = max( $biggest_in_all_2 , $mysync->{ folder2 }->{ $folder2 }->{ biggest } ) ; + + } + + myprintf( "Host1 Nb folders: %11s folders\n", $nb_folders_1 ) ; + myprintf( "Host2 Nb folders: %11s folders\n", $nb_folders_2 ) ; + myprint( "\n" ) ; + myprintf( "Host1 Nb messages: %11s messages\n", $total_nb_1 ) ; + myprintf( "Host2 Nb messages: %11s messages\n", $total_nb_2 ) ; + myprint( "\n" ) ; + myprintf( "Host1 Total size: %11s bytes (%s)\n", $total_size_1, bytes_display_string( $total_size_1 ) ) ; + myprintf( "Host2 Total size: %11s bytes (%s)\n", $total_size_2, bytes_display_string( $total_size_2 ) ) ; + myprint( "\n" ) ; + myprintf( "Host1 Biggest message: %11s bytes (%s)\n", $biggest_in_all_1, bytes_display_string( $biggest_in_all_1 ) ) ; + myprintf( "Host2 Biggest message: %11s bytes (%s)\n", $biggest_in_all_2, bytes_display_string( $biggest_in_all_2 ) ) ; + myprint( "\n" ) ; + myprintf( "Time spent on sizing: %11.1f seconds\n", timenext( $mysync ) ) ; + + my @total_1_2 = ( $total_nb_1, $total_size_1, $total_nb_2, $total_size_2 ) ; + return @total_1_2 ; +} + +sub foldersizesatend_old +{ + my $mysync = shift ; + timenext( $mysync ) ; + return if ( $mysync->{imap1}->IsUnconnected( ) ) ; + return if ( $mysync->{imap2}->IsUnconnected( ) ) ; + # Get all folders on host2 again since new were created + @h2_folders_all = sort $mysync->{imap2}->folders(); + for ( @h2_folders_all ) { + $h2_folders_all{ $_ } = 1 ; + $mysync->{h2_folders_all_UPPER}{ uc $_ } = 1 ; + } ; + ( $h1_nb_msg_end, $h1_bytes_end ) = foldersizes( $mysync, 'Host1', $mysync->{imap1}, $mysync->{ search1 }, $mysync->{abletosearch1}, @{ $mysync->{h1_folders_wanted} } ) ; + ( $h2_nb_msg_end, $h2_bytes_end ) = foldersizes( $mysync, 'Host2', $mysync->{imap2}, $mysync->{ search2 }, $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; + if ( not all_defined( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) ) { + my $error = "Failure getting foldersizes, final differences will not be calculated\n" ; + errors_incr( $mysync, $error ) ; + } + return ; +} + +sub foldersizesatend +{ + my $mysync = shift ; + timenext( $mysync ) ; + return if ( $mysync->{imap1}->IsUnconnected( ) ) ; + return if ( $mysync->{imap2}->IsUnconnected( ) ) ; + # Get all folders on host2 again since new were created + @h2_folders_all = sort $mysync->{imap2}->folders(); + for ( @h2_folders_all ) { + $h2_folders_all{ $_ } = 1 ; + $mysync->{h2_folders_all_UPPER}{ uc $_ } = 1 ; + } ; + + + foldersizes_diff_list( $mysync, $FORCE ) ; + + ( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) + = foldersizes_total( $mysync ) ; + + + if ( not all_defined( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) ) { + my $error = "Failure getting foldersizes, final differences will not be calculated\n" ; + errors_incr( $mysync, $error ) ; + } + return ; +} + + +sub foldersizes_at_the_beggining { my $mysync = shift ; @@ -3008,21 +3844,73 @@ You can remove foldersizes listings by using "--nofoldersizes" and "--nofoldersi but then you will also lose the ETA (Estimation Time of Arrival) given after each message copy. END_SIZE - ( $h1_nb_msg_start, $h1_bytes_start ) = foldersizes( 'Host1', $mysync->{imap1}, $search1, $mysync->{abletosearch1}, @h1_folders_wanted ) ; - ( $h2_nb_msg_start, $h2_bytes_start ) = foldersizes( 'Host2', $mysync->{imap2}, $search2, $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; + foldersizes_diff_list( $mysync ) ; - if ( not all_defined( $h1_nb_msg_start, $h1_bytes_start, $h2_nb_msg_start, $h2_bytes_start ) ) { + ( $mysync->{ h1_nb_msg_start }, $mysync->{ h1_bytes_start }, + $mysync->{ h2_nb_msg_start }, $mysync->{ h2_bytes_start } ) + = foldersizes_total( $mysync ) ; + + + if ( not all_defined( + $mysync->{ h1_nb_msg_start }, + $mysync->{ h1_bytes_start }, + $mysync->{ h2_nb_msg_start }, + $mysync->{ h2_bytes_start } ) ) + { my $error = "Failure getting foldersizes, ETA and final diff will not be displayed\n" ; errors_incr( $mysync, $error ) ; - $foldersizes = 0 ; - $foldersizesatend = 0 ; + $mysync->{ foldersizes } = 0 ; + $mysync->{ foldersizesatend } = 0 ; return ; } my $h2_bytes_limit = $mysync->{h2}->{quota_limit_bytes} || 0 ; - if ( $h2_bytes_limit and ( $h2_bytes_limit < $h1_bytes_start ) ) { - my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $h1_bytes_start / $h2_bytes_limit ) ; - my $error = "Host2: Quota limit will be exceeded! Over $quota_percent % ( $h1_bytes_start bytes / $h2_bytes_limit bytes )\n" ; + if ( $h2_bytes_limit and ( $h2_bytes_limit < $mysync->{ h1_bytes_start } ) ) + { + my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $mysync->{ h1_bytes_start } / $h2_bytes_limit ) ; + my $error = "Host2: Quota limit will be exceeded! Over $quota_percent % ( $mysync->{ h1_bytes_start } bytes / $h2_bytes_limit bytes )\n" ; + errors_incr( $mysync, $error ) ; + } + return ; +} + + +# Globals: +# @h2_folders_from_1_wanted + +sub foldersizes_at_the_beggining_old +{ + my $mysync = shift ; + + myprint( << 'END_SIZE' ) ; + +Folders sizes before the synchronization. +You can remove foldersizes listings by using "--nofoldersizes" and "--nofoldersizesatend" +but then you will also lose the ETA (Estimation Time of Arrival) given after each message copy. +END_SIZE + + ( $mysync->{ h1_nb_msg_start }, $mysync->{ h1_bytes_start } ) = + foldersizes( $mysync, 'Host1', $mysync->{imap1}, $mysync->{ search1 }, + $mysync->{abletosearch1}, @{ $mysync->{h1_folders_wanted} } ) ; + ( $mysync->{ h2_nb_msg_start }, $mysync->{ h2_bytes_start } ) = + foldersizes( $mysync, 'Host2', $mysync->{imap2}, $mysync->{ search2 }, + $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; + + if ( not all_defined( $mysync->{ h1_nb_msg_start }, + $mysync->{ h1_bytes_start }, $mysync->{ h2_nb_msg_start }, $mysync->{ h2_bytes_start } ) ) + { + my $error = "Failure getting foldersizes, ETA and final diff will not be displayed\n" ; + errors_incr( $mysync, $error ) ; + $mysync->{ foldersizes } = 0 ; + $mysync->{ foldersizesatend } = 0 ; + return ; + } + + my $h2_bytes_limit = $mysync->{h2}->{quota_limit_bytes} || 0 ; + if ( $h2_bytes_limit and ( $h2_bytes_limit < $mysync->{ h1_bytes_start } ) ) + { + my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $mysync->{ h1_bytes_start } / $h2_bytes_limit ) ; + my $error = "Host2: Quota limit will be exceeded! Over $quota_percent % ( $mysync->{ h1_bytes_start } bytes / $h2_bytes_limit bytes )\n" ; errors_incr( $mysync, $error ) ; } return ; @@ -3369,7 +4257,7 @@ sub tests_maxsize_setting # APPENDLIMIT with --appendlimit => --appendlimit wins $mysync->{ appendlimit } = $NUMBER_123456 ; - + is( $NUMBER_123456, maxsize_setting( $mysync ), 'maxsize_setting: APPENDLIMIT 654321 + --appendlimit 123456 => 123456' ) ; @@ -3381,7 +4269,7 @@ sub tests_maxsize_setting # Fresh $mysync = { } ; $mysync->{ imap2 } = $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=654321' ) ; - + # Case: "APPENDLIMIT >= --maxsize" => maxsize. $mysync->{ maxsize } = $NUMBER_123456 ; @@ -3390,8 +4278,8 @@ sub tests_maxsize_setting ) ; # Case: "APPENDLIMIT < --maxsize" => APPENDLIMIT. - - + + # Fresh $mysync = { } ; $mysync->{ imap2 } = $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=123456' ) ; @@ -3402,9 +4290,9 @@ sub tests_maxsize_setting ) ; # Now --truncmess stuff - - - + + + note( 'Leaving tests_maxsize_setting()' ) ; return ; @@ -3489,17 +4377,17 @@ sub tests_hashsynclocal note( 'Entering tests_hashsynclocal()' ) ; my $mysync = { - host1 => '', - user1 => '', - password1 => '', - host2 => '', - user2 => '', - password2 => '', + host1 => q{}, + user1 => q{}, + password1 => q{}, + host2 => q{}, + user2 => q{}, + password2 => q{}, } ; is( undef, hashsynclocal( $mysync ), 'hashsynclocal: no hashfile name' ) ; - $mysync->{ hashfile } = '' ; + $mysync->{ hashfile } = q{} ; is( undef, hashsynclocal( $mysync ), 'hashsynclocal: empty hashfile name' ) ; $mysync->{ hashfile } = './noexist/rrr' ; @@ -3662,6 +4550,8 @@ sub imap_id { my ( $mysync, $imap, $Side ) = @_ ; + if ( not $mysync->{id} ) { return q{} ; } ; + $Side ||= q{} ; my $imap_id_response = q{} ; @@ -3676,7 +4566,7 @@ sub imap_id $imap->Debug( 1 ) ; my $id_out = $imap->tag_and_run( 'ID ' . $id_inp ) ; #my $id_out = $imap->tag_and_run( 'ID NIL' ) ; - myprint( "\n" ) ; + myprint( "\n" ) ; $imap->Debug( $debug_before ) ; #$imap_id_response = Data::Dumper->Dump( [ $id_out ], [ 'IMAP_ID' ] ) ; } @@ -3698,7 +4588,7 @@ sub imapsync_id vendor => 'Gilles LAMIRAL', 'support-url' => 'https://imapsync.lamiral.info/', # Example of date-time: 19-Sep-2015 08:56:07 - date => date_from_rcs( q{$Date: 2019/06/26 19:30:56 $ } ), + date => date_from_rcs( q{$Date: 2019/12/23 20:18:02 $ } ), } ; my $imapsync_id_github = { @@ -3707,7 +4597,7 @@ sub imapsync_id os => $OSNAME, vendor => 'github', 'support-url' => 'https://github.com/imapsync/imapsync', - date => date_from_rcs( q{$Date: 2019/06/26 19:30:56 $ } ), + date => date_from_rcs( q{$Date: 2019/12/23 20:18:02 $ } ), } ; $imapsync_id = $imapsync_id_lamiral ; @@ -4020,7 +4910,7 @@ sub build_possible_special $possible_special->{'\Archive'} = [ 'Archive', 'Archives', '&BBAEQARFBDgEMg-' ] ; $possible_special->{'\Drafts'} = [ 'Drafts', 'DRAFTS', '&BCcENQRABD0EPgQyBDgEOgQ4-', 'Szkice', 'Wersje robocze' ] ; $possible_special->{'\Flagged'} = [ 'Flagged', 'Starred', '&BB8EPgQ8BDUERwQ1BD0EPQRLBDU-' ] ; - $possible_special->{'\Junk'} = [ 'Junk', 'junk', 'Spam', 'SPAM', '&BCEEPwQwBDw-', + $possible_special->{'\Junk'} = [ 'Junk', 'junk', 'Spam', 'SPAM', '&BCEEPwQwBDw-', 'Potwierdzony spam', 'Wiadomo&AVs-ci-&AVs-mieci', 'Junk E-Mail', 'Junk Email'] ; $possible_special->{'\Sent'} = [ 'Sent', 'Sent Messages', 'Sent Items', @@ -4031,7 +4921,7 @@ sub build_possible_special '&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-', 'Elementy wys&AUI-ane'] ; $possible_special->{'\Trash'} = [ 'Trash', 'TRASH', - '&BCMENAQwBDsENQQ9BD0ESwQ1-', '&BBoEPgRABDcEOAQ9BDA-', + '&BCMENAQwBDsENQQ9BD0ESwQ1-', '&BBoEPgRABDcEOAQ9BDA-', 'Kosz', 'Deleted Items', 'Deleted Messages' ] ; @@ -4188,26 +5078,6 @@ sub tests_live_result return ; } -sub foldersizesatend -{ - my $mysync = shift ; - timenext( ) ; - return if ( $mysync->{imap1}->IsUnconnected( ) ) ; - return if ( $mysync->{imap2}->IsUnconnected( ) ) ; - # Get all folders on host2 again since new were created - @h2_folders_all = sort $mysync->{imap2}->folders(); - for ( @h2_folders_all ) { - $h2_folders_all{ $_ } = 1 ; - $h2_folders_all_UPPER{ uc $_ } = 1 ; - } ; - ( $h1_nb_msg_end, $h1_bytes_end ) = foldersizes( 'Host1', $mysync->{imap1}, $search1, $mysync->{abletosearch1}, @h1_folders_wanted ) ; - ( $h2_nb_msg_end, $h2_bytes_end ) = foldersizes( 'Host2', $mysync->{imap2}, $search2, $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; - if ( not all_defined( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) ) { - my $error = "Failure getting foldersizes, final differences will not be calculated\n" ; - errors_incr( $mysync, $error ) ; - } - return ; -} sub size_filtered_flag { @@ -4347,6 +5217,9 @@ sub tests_max is( 1, max( 1 ), 'max 1 => 1' ) ; is( $MINUS_ONE, max( $MINUS_ONE ), 'max -1 => -1') ; is( undef, max( ), 'max no arg => undef' ) ; + is( undef, max( undef ), 'undef => undef' ) ; + is( undef, max( undef, undef ), 'undef, undef => undef' ) ; + is( $NUMBER_100, max( 1, $NUMBER_100 ), 'max 1 100 => 100' ) ; is( $NUMBER_100, max( $NUMBER_100, 1 ), 'max 100 1 => 100' ) ; is( $NUMBER_100, max( $NUMBER_100, $NUMBER_42, 1 ), 'max 100 42 1 => 100' ) ; @@ -4374,20 +5247,30 @@ sub max return( undef ) if ( 0 == scalar @list ) ; my( @numbers, @notnumbers ) ; - foreach my $item ( @list ) { - if ( is_number( $item ) ) { + foreach my $item ( @list ) + { + if ( is_number( $item ) ) + { push @numbers, $item ; - }else{ + } + elsif ( defined $item ) + { push @notnumbers, $item ; } } my @sorted ; - if ( @numbers ) { + + if ( @numbers ) + { @sorted = sort { $a <=> $b } @numbers ; - }elsif( @notnumbers ) { + } + elsif ( @notnumbers ) + { @sorted = sort { $a cmp $b } @notnumbers ; - }else{ + } + else + { return ; } @@ -4398,7 +5281,8 @@ sub tests_is_number { note( 'Entering tests_is_number()' ) ; - ok( ! is_number( ), 'is_number: no args => undef ' ) ; + is( undef, is_number( ), 'is_number: no args => undef ' ) ; + is( undef, is_number( undef ), 'is_number: undef => undef ' ) ; ok( is_number( 1 ), 'is_number: 1 => 1' ) ; ok( is_number( 1.1 ), 'is_number: 1.1 => 1' ) ; ok( is_number( 0 ), 'is_number: 0 => 1' ) ; @@ -4523,6 +5407,8 @@ sub modulesversion 'Digest::HMAC_MD5' => sub { $Digest::HMAC_MD5::VERSION }, 'Digest::HMAC_SHA1' => sub { $Digest::HMAC_SHA1::VERSION }, 'Digest::MD5' => sub { $Digest::MD5::VERSION }, + 'Encode' => sub { $Encode::VERSION }, + 'Encode::IMAPUTF7' => sub { $Encode::IMAPUTF7::VERSION }, 'File::Copy::Recursive' => sub { $File::Copy::Recursive::VERSION }, 'File::Spec' => sub { $File::Spec::VERSION }, 'Getopt::Long' => sub { $Getopt::Long::VERSION }, @@ -4537,6 +5423,7 @@ sub modulesversion 'JSON::WebToken' => sub { $JSON::WebToken::VERSION }, 'LWP' => sub { $LWP::VERSION }, 'Mail::IMAPClient' => sub { $Mail::IMAPClient::VERSION }, + 'MIME::Base64' => sub { $MIME::Base64::VERSION }, 'Net::Ping' => sub { $Net::Ping::VERSION }, 'Net::SSLeay' => sub { $Net::SSLeay::VERSION }, 'Term::ReadKey' => sub { $Term::ReadKey::VERSION }, @@ -4705,6 +5592,7 @@ FIN_PASSFILE if ( defined $mysync->{ passfile1 } ) { if ( ! -e -r $mysync->{ passfile1 } ) { myprint( "Failure: file from parameter --passfile1 $mysync->{ passfile1 } does not exist or is not readable\n" ) ; + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EX_NOINPUT ) ; } # passfile1 readable @@ -4747,6 +5635,7 @@ FIN_PASSFILE if ( defined $mysync->{ passfile2 } ) { if ( ! -e -r $mysync->{ passfile2 } ) { myprint( "Failure: file from parameter --passfile2 $mysync->{ passfile2 } does not exist or is not readable\n" ) ; + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EX_NOINPUT ) ; } # passfile2 readable @@ -4808,7 +5697,7 @@ sub exit_clean { myprint( @messages ) ; } - myprint( "Exiting with return value $status ($EXIT_TXT{$status})\n" ) ; + myprint( "Exiting with return value $status ($EXIT_TXT{$status}) $mysync->{nb_errors}/$mysync->{errorsmax} nb_errors/max_errors\n" ) ; cleanup_before_exit( $mysync ) ; exit $status ; @@ -4818,6 +5707,7 @@ sub missing_option { my $mysync = shift ; my $option = shift ; + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EX_USAGE, "$option option is mandatory, for help run $PROGRAM_NAME --help\n" ) ; return ; } @@ -4858,10 +5748,16 @@ sub catch_exit myprint( "Killing myself with signal $signame\n" ) ; cleanup_before_exit( $mysync ) ; kill( $signame, $PROCESS_ID ) ; + sleep 1 ; + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_BY_SIGNAL, + "Still there after killing myself with signal $signame...\n" + ) ; } else { - exit_clean( $mysync, $EXIT_BY_SIGNAL ) ; + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_BY_SIGNAL, "Exiting in catch_exit with no signal...\n" ) ; } return ; } @@ -4878,6 +5774,20 @@ sub catch_print return ; } +sub here_twice +{ + my $mysync = shift ; + my $now = time ; + my $previous = $mysync->{lastcatch} || 0 ; + $mysync->{lastcatch} = $now ; + + if ( $INTERVAL_TO_EXIT >= $now - $previous ) { + return $TRUE ; + }else{ + return $FALSE ; + } +} + sub catch_reconnect { @@ -4905,6 +5815,7 @@ sub catch_reconnect } else { + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EXIT_CONNECTION_FAILURE ) ; } myprint( "Info: reconnecting to host2 imap server\n" ) ; @@ -4916,6 +5827,7 @@ sub catch_reconnect } else { + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EXIT_CONNECTION_FAILURE ) ; } myprint( "Info: reconnected to both imap servers\n" ) ; @@ -4923,6 +5835,40 @@ sub catch_reconnect return ; } +sub install_signals +{ + my $mysync = shift ; + + if ( under_docker_context( $mysync ) ) + { + # output( $mysync, "Under docker context so leaving signals as they are\n" ) ; + output( $mysync, "Under docker context so installing only signals to exit\n" ) ; + @{ $mysync->{ sigexit } } = ( defined( $mysync->{ sigexit } ) ) ? @{ $mysync->{ sigexit } } : ( 'INT', 'QUIT', 'TERM' ) ; + sig_install( $mysync, 'catch_exit', @{ $mysync->{ sigexit } } ) ; + } + else + { + # Unix signals + @{ $mysync->{ sigexit } } = ( defined( $mysync->{ sigexit } ) ) ? @{ $mysync->{ sigexit } } : ( 'QUIT', 'TERM' ) ; + @{ $mysync->{ sigreconnect } } = ( defined( $mysync->{ sigreconnect } ) ) ? @{ $mysync->{ sigreconnect } } : ( 'INT' ) ; + @{ $mysync->{ sigprint } } = ( defined( $mysync->{ sigprint } ) ) ? @{ $mysync->{ sigprint } } : ( 'HUP' ) ; + @{ $mysync->{ sigignore } } = ( defined( $mysync->{ sigignore } ) ) ? @{ $mysync->{ sigignore } } : ( ) ; + + #local %SIG = %SIG ; + sig_install( $mysync, 'catch_exit', @{ $mysync->{ sigexit } } ) ; + sig_install( $mysync, 'catch_reconnect', @{ $mysync->{ sigreconnect } } ) ; + sig_install( $mysync, 'catch_print', @{ $mysync->{ sigprint } } ) ; + # --sigignore can override sigexit, sigreconnect and sigprint (for the same signals only) + sig_install( $mysync, 'catch_ignore', @{ $mysync->{ sigignore } } ) ; + + sig_install_toggle_sleep( $mysync ) ; + } + + return ; +} + + + sub tests_reconnect_12_if_needed { note( 'Entering tests_reconnect_12_if_needed()' ) ; @@ -5010,22 +5956,10 @@ sub reconnect_if_needed -sub here_twice -{ - my $mysync = shift ; - my $now = time ; - my $previous = $mysync->{lastcatch} || 0 ; - $mysync->{lastcatch} = $now ; +# $sync->{id} = defined $sync->{id} ? $sync->{id} : 1 ; +# imap_id_stuff( $sync ) ; - if ( $INTERVAL_TO_EXIT >= $now - $previous ) { - return $TRUE ; - }else{ - return $FALSE ; - } -} - - -sub justconnect +sub justconnect { my $mysync = shift ; my $justconnect1 = justconnect1( $sync ) ; @@ -5036,19 +5970,19 @@ sub justconnect sub justconnect1 { my $mysync = shift ; - if ( $mysync->{host1} ) + if ( $mysync->{host1} ) { myprint( "Host1: Will just connect to $mysync->{host1} without login\n" ) ; $mysync->{imap1} = connect_imap( $mysync->{host1}, $mysync->{port1}, $debugimap1, - $mysync->{ssl1}, $mysync->{tls1}, 'Host1', + $mysync->{ssl1}, $mysync->{tls1}, 'Host1', $mysync->{h1}->{timeout}, $mysync->{h1} ) ; - + imap_id( $mysync, $mysync->{imap1}, 'Host1' ) ; $mysync->{imap1}->logout( ) ; return $mysync->{host1} ; } - - return '' ; + + return q{} ; } sub justconnect2 @@ -5057,22 +5991,22 @@ sub justconnect2 if ( $mysync->{host2} ) { myprint( "Host2: Will just connect to $mysync->{host2} without login\n" ) ; - $mysync->{imap2} = connect_imap( + $mysync->{imap2} = connect_imap( $mysync->{host2}, $mysync->{port2}, $debugimap2, $mysync->{ssl2}, $mysync->{tls2}, 'Host2', $mysync->{h2}->{timeout}, $mysync->{h2} ) ; - + imap_id( $mysync, $mysync->{imap2}, 'Host2' ) ; $mysync->{imap2}->logout( ) ; return $mysync->{host2} ; } - - return '' ; + + return q{} ; } sub skip_macosx { - return ; - # return( 'macosx.polarhome.com' eq hostname() ) ; + #return ; + return( 'macosx.polarhome.com' eq hostname() ) ; } sub tests_mailimapclient_connect @@ -5092,7 +6026,7 @@ sub tests_mailimapclient_connect is( 'test.lamiral.info', $imap->Server( 'test.lamiral.info' ), 'mailimapclient_connect ipv4: setting Server(test.lamiral.info)' ) ; is( 1, $imap->Debug( 1 ), 'mailimapclient_connect ipv4: setting Debug( 1 )' ) ; is( 143, $imap->Port( 143 ), 'mailimapclient_connect ipv4: setting Port( 143 )' ) ; - is( 3, $imap->Timeout( 3 ), 'mailimapclient_connect ipv4: setting Timout( 30 )' ) ; + is( 3, $imap->Timeout( 3 ), 'mailimapclient_connect ipv4: setting Timout( 3 )' ) ; like( ref( $imap->connect( ) ), qr/IO::Socket::INET|IO::Socket::IP/, 'mailimapclient_connect ipv4: connect to test.lamiral.info' ) ; like( $imap->logout( ), qr/Mail::IMAPClient/, 'mailimapclient_connect ipv4: logout' ) ; is( undef, undef $imap, 'mailimapclient_connect ipv4: free variable' ) ; @@ -5101,16 +6035,19 @@ sub tests_mailimapclient_connect ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect ipv4 + ssl: new' ) ; is( 'test.lamiral.info', $imap->Server( 'test.lamiral.info' ), 'mailimapclient_connect ipv4 + ssl: setting Server(test.lamiral.info)' ) ; is( 1, $imap->Debug( 1 ), 'mailimapclient_connect ipv4 + ssl: setting Debug( 1 )' ) ; - ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE ] ), 'mailimapclient_connect ipv4 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ; + ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE, SSL_cipher_list => 'DEFAULT:!DH' ] ), 'mailimapclient_connect ipv4 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ; is( 993, $imap->Port( 993 ), 'mailimapclient_connect ipv4 + ssl: setting Port( 993 )' ) ; like( ref( $imap->connect( ) ), qr/IO::Socket::SSL/, 'mailimapclient_connect ipv4 + ssl: connect to test.lamiral.info' ) ; - is( $imap->logout( ), undef, 'mailimapclient_connect ipv4 + ssl: logout in ssl causes failure' ) ; + like( $imap->logout( ), qr/Mail::IMAPClient/, 'mailimapclient_connect ipv4 + ssl: logout in ssl does not cause failure' ) ; is( undef, undef $imap, 'mailimapclient_connect ipv4 + ssl: free variable' ) ; # ipv6 + ssl + # Fails often on ks2ipv6.lamiral.info + ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect ipv6 + ssl: new' ) ; - is( 'ks2ipv6.lamiral.info', $imap->Server( 'ks2ipv6.lamiral.info' ), 'mailimapclient_connect ipv6 + ssl: setting Server(ks2ipv6.lamiral.info)' ) ; - ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE ] ), 'mailimapclient_connect ipv6 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ; + is( 'petiteipv6.lamiral.info', $imap->Server( 'petiteipv6.lamiral.info' ), 'mailimapclient_connect ipv6 + ssl: setting Server petiteipv6.lamiral.info' ) ; + is( 3, $imap->Timeout( 3 ), 'mailimapclient_connect ipv4: setting Timout( 3 )' ) ; + ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE, SSL_cipher_list => 'DEFAULT:!DH' ] ), 'mailimapclient_connect ipv6 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ; is( 993, $imap->Port( 993 ), 'mailimapclient_connect ipv6 + ssl: setting Port( 993 )' ) ; SKIP: { if ( @@ -5119,13 +6056,23 @@ sub tests_mailimapclient_connect skip_macosx() or -e '/.dockerenv' + or + 'pcHPDV7-HP' eq hostname() ) { - skip( 'Tests avoided on CUILLERE can not do ipv6', 2 ) ; + skip( 'Tests avoided on CUILLERE/pcHPDV7-HP/macosx.polarhome.com/docker cannot do ipv6', 4 ) ; } - like( ref( $imap->connect( ) ), qr/IO::Socket::SSL/, 'mailimapclient_connect ipv6 + ssl: connect to ks2ipv6.lamiral.info' ) ; - is( $imap->logout( ), undef, 'mailimapclient_connect ipv6 + ssl: logout in ssl causes failure' ) ; + + is( 1, $imap->Debug( 1 ), 'mailimapclient_connect ipv4 + ssl: setting Debug( 1 )' ) ; + + # It sounds stupid but it avoids failures on the next test about $imap->connect + is( '2a01:e34:ecde:70d0:223:54ff:fec2:36d7', resolv( 'petiteipv6.lamiral.info' ), 'resolv: petiteipv6.lamiral.info => 2001:41d0:8:bebd::1' ) ; + + like( ref( $imap->connect( ) ), qr/IO::Socket::SSL/, 'mailimapclient_connect ipv6 + ssl: connect to petiteipv6.lamiral.info' ) ; + # This one is ok on petite, not on ks2, do not know why, so commented. + like( ref( $imap->logout( ) ), qr/Mail::IMAPClient/, 'mailimapclient_connect ipv6 + ssl: logout in ssl is ok on petiteipv6.lamiral.info' ) ; } + is( undef, undef $imap, 'mailimapclient_connect ipv6 + ssl: free variable' ) ; @@ -5152,9 +6099,11 @@ sub tests_mailimapclient_connect_bug skip_macosx() or -e '/.dockerenv' + or + 'pcHPDV7-HP' eq hostname() ) { - skip( 'Tests avoided on CUILLERE can not do ipv6', 1 ) ; + skip( 'Tests avoided on CUILLERE/pcHPDV7-HP/macosx.polarhome.com/docker cannot do ipv6', 1 ) ; } like( ref( $imap->connect( ) ), qr/IO::Socket::INET/, 'mailimapclient_connect_bug ipv6: connect to ks2ipv6.lamiral.info' ) or diag( 'mailimapclient_connect_bug ipv6: ', $imap->LastError( ), $!, ) ; @@ -5183,9 +6132,11 @@ sub tests_connect_socket skip_macosx() or -e '/.dockerenv' + or + 'pcHPDV7-HP' eq hostname() ) { - skip( 'Tests avoided on CUILLERE/macosx.polarhome.com/docker cannot do ipv6', 2 ) ; + skip( 'Tests avoided on CUILLERE/pcHPDV7-HP/macosx.polarhome.com/docker cannot do ipv6', 2 ) ; } $socket = IO::Socket::INET6->new( @@ -5201,11 +6152,12 @@ sub tests_connect_socket $imap->logout( ) ; } - #$IO::Socket::SSL::DEBUG = 4 ; + $IO::Socket::SSL::DEBUG = 4 ; $socket = IO::Socket::SSL->new( PeerHost => 'ks2ipv6.lamiral.info', PeerPort => 993, SSL_verify_mode => SSL_VERIFY_NONE, + SSL_cipher_list => 'DEFAULT:!DH', ) ; # myprint( $socket ) ; ok( $imap = connect_socket( $socket ), 'connect_socket: ks2ipv6.lamiral.info port 993 IO::Socket::SSL' ) ; @@ -5249,44 +6201,55 @@ sub tests_probe_imapssl is( undef, probe_imapssl( ), 'probe_imapssl: no args => undef' ) ; is( undef, probe_imapssl( 'unknown' ), 'probe_imapssl: unknown => undef' ) ; - SKIP: { + note( "hostname is: ", hostname() ) ; + SKIP: { if ( 'CUILLERE' eq hostname() or skip_macosx() or -e '/.dockerenv' + or + 'pcHPDV7-HP' eq hostname() ) { - skip( 'Tests avoided on CUILLERE/macosx.polarhome.com/docker cannot do ipv6', 2 ) ; + skip( 'Tests avoided on CUILLERE or pcHPDV7-HP or Mac or docker: cannot do ipv6', 0 ) ; } - like( probe_imapssl( 'ks2ipv6.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: ks2ipv6.lamiral.info matches "* OK"' ) ; - like( probe_imapssl( 'imap.gmail.com' ), qr/^\* OK/, 'probe_imapssl: imap.gmail.com matches "* OK"' ) ; + # fed up with this one + #like( probe_imapssl( 'ks2ipv6.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: ks2ipv6.lamiral.info matches "* OK"' ) ; } ; + + # It sounds stupid but it avoids failures on the next test about $imap->connect + ok( resolv( 'imap.gmail.com' ), 'resolv: imap.gmail.com => something' ) ; + like( probe_imapssl( 'imap.gmail.com' ), qr/^\* OK/, 'probe_imapssl: imap.gmail.com matches "* OK"' ) ; + like( probe_imapssl( 'test1.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: test1.lamiral.info matches "* OK"' ) ; note( 'Leaving tests_probe_imapssl()' ) ; return ; } + sub probe_imapssl { my $host = shift ; if ( ! $host ) { return ; } - + $sync->{ debug } and $IO::Socket::SSL::DEBUG = 4 ; my $socket = IO::Socket::SSL->new( PeerHost => $host, PeerPort => $IMAP_SSL_PORT, - SSL_verify_mode => SSL_VERIFY_NONE, + SSL_verifycn_scheme => 'imap', + SSL_verify_mode => $SSL_VERIFY_POLICY, + SSL_cipher_list => 'DEFAULT:!DH', ) ; - #print "$socket\n" ; if ( ! $socket ) { return ; } + $sync->{ debug } and print "socket: $socket\n" ; my $banner ; $socket->sysread( $banner, 65_536 ) ; - #print "$banner" ; + $sync->{ debug } and print "banner: $banner" ; $socket->close( ) ; return $banner ; @@ -5296,6 +6259,7 @@ sub connect_imap { my( $host, $port, $mydebugimap, $ssl, $tls, $Side, $mytimeout, $h ) = @_ ; my $imap = Mail::IMAPClient->new( ) ; + if ( $ssl ) { set_ssl( $imap, $h ) } $imap->Server( $host ) ; $imap->Port( $port ) ; @@ -5305,9 +6269,17 @@ sub connect_imap my $side = lc $Side ; myprint( "$Side: connecting on $side [$host] port [$port]\n" ) ; - $imap->connect( ) - or exit_clean( $sync, $EXIT_CONNECTION_FAILURE, "$Side: Can not open imap connection on [$host]: " . $imap->LastError . " $OS_ERROR\n" ) ; - myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; + if ( ! $imap->connect( ) ) + { + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_CONNECTION_FAILURE, + "$Side: Can not open imap connection on [$host]: ", + $imap->LastError, + " $OS_ERROR\n" + ) ; + } + myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; + my $banner = $imap->Results()->[0] ; myprint( "$Side banner: $banner" ) ; @@ -5315,8 +6287,14 @@ sub connect_imap if ( $tls ) { set_tls( $imap, $h ) ; - $imap->starttls( ) - or exit_clean( $sync, $EXIT_TLS_FAILURE, "$Side: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; + if ( ! $imap->starttls( ) ) + { + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_TLS_FAILURE, + "$Side: Can not go to tls encryption on $side [$host]:", + $imap->LastError, "\n" + ) ; + } myprint( "$Side: Socket successfuly converted to SSL\n" ) ; } return( $imap ) ; @@ -5338,9 +6316,15 @@ sub login_imap my $imap = init_imap( @allargs ) ; - $imap->connect() - or exit_clean( $mysync, $EXIT_CONNECTION_FAILURE, "$Side failure: can not open imap connection on $side [$host] with user [$user]: " . $imap->LastError . " $OS_ERROR\n" ) ; - myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; + if ( ! $imap->connect() ) + { + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_CONNECTION_FAILURE, + "$Side failure: can not open imap connection on $side [$host] with user [$user]: ", + $imap->LastError . " $OS_ERROR\n" + ) ; + } + myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; my $banner = $imap->Results()->[0] ; myprint( "$Side banner: $banner" ) ; @@ -5356,14 +6340,24 @@ sub login_imap $imap->Socket ; myprintf("%s: Assuming PREAUTH for %s\n", $Side, $imap->Server ) ; }else{ - exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]" ) ; + $mysync->{nb_errors}++ ; + exit_clean( + $mysync, $EXIT_AUTHENTICATION_FAILURE, + "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]\n" + ) ; } } if ( $tls ) { set_tls( $imap, $h ) ; - $imap->starttls( ) - or exit_clean( $mysync, $EXIT_TLS_FAILURE, "$Side failure: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; + if ( ! $imap->starttls( ) ) + { + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_TLS_FAILURE, + "$Side failure: Can not go to tls encryption on $side [$host]:", + $imap->LastError, "\n" + ) ; + } myprint( "$Side: Socket successfuly converted to SSL\n" ) ; } @@ -5406,20 +6400,32 @@ sub authenticate_imap $imap->Authcallback(\&plainauth) if ( ( 'PLAIN' eq $authmech ) or ( 'EXTERNAL' eq $authmech ) ) ; - unless ( $authmech eq 'PREAUTH' or $authmech eq 'X-MASTERAUTH' or $imap->login( ) ) { + unless ( $authmech eq 'PREAUTH' or $imap->login( ) ) { my $info = "$Side failure: Error login on [$host] with user [$user] auth" ; my $einfo = $imap->LastError || @{$imap->History}[$LAST] ; chomp $einfo ; my $error = "$info [$authmech]: $einfo\n" ; - if ( $authmech eq 'LOGIN' or $imap->IsUnconnected( ) or $authuser ) { + if ( ( $authmech eq 'LOGIN' ) or $imap->IsUnconnected( ) or $authuser ) { + $authuser ||= "" ; + myprint( "$Side info: authmech [$authmech] user [$user] authuser [$authuser] IsUnconnected [", $imap->IsUnconnected( ), "]\n" ) ; + $mysync->{nb_errors}++ ; exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, $error ) ; }else{ myprint( $error ) ; } + # It is not secure to try plain text LOGIN when another authmech failed + # but I do it. + # I shell remove this code one day. myprint( "$Side info: trying LOGIN Auth mechanism on [$host] with user [$user]\n" ) ; $imap->Authmechanism(q{}) ; - $imap->login() or - exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$info [LOGIN]: ", $imap->LastError, "\n") ; + if ( ! $imap->login( ) ) + { + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, + "$info [LOGIN]: ", + $imap->LastError, "\n" + ) ; + } } if ( $proxyauth ) { @@ -5427,7 +6433,11 @@ sub authenticate_imap my $info = "$Side failure: Error doing proxyauth as user [$user] on [$host] using proxy-login as [$authuser]" ; my $einfo = $imap->LastError || @{$imap->History}[$LAST] ; chomp $einfo ; - exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$info: $einfo\n" ) ; + $mysync->{nb_errors}++ ; + exit_clean( $mysync, + $EXIT_AUTHENTICATION_FAILURE, + "$info: $einfo\n" + ) ; } } @@ -5526,7 +6536,7 @@ sub set_tls -sub init_imap +sub init_imap { my( $host, $port, $user, $domain, $password, @@ -5537,7 +6547,14 @@ sub init_imap my ( $imap ) ; $imap = Mail::IMAPClient->new() ; - + + if ( $mysync->{ tee } ) + { + # Well, it does not change anything, does it? + # It does when suppressing the hack with *STDERR + $imap->Debug_fh( $mysync->{ tee } ) ; + } + if ( $ssl ) { set_ssl( $imap, $h ) } if ( $tls ) { } # can not do set_tls() here because connect() will directly do a STARTTLS $imap->Clear(1); @@ -5618,20 +6635,29 @@ sub xoauth2 my ($iss,$key); - if( $imap->Password =~ /^(.*\.json)$/x ) { - my $json = JSON->new( ) ; - my $filename = $1; - $sync->{ debug } and myprint( "XOAUTH2 json file: $filename\n" ) ; - open( my $FILE, '<', $filename ) or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "error [$filename]: $OS_ERROR " ) ; - my $jsonfile = $json->decode( join q{}, <$FILE> ) ; - close $FILE ; + if( $imap->Password =~ /^(.*\.json)$/x ) + { + my $json = JSON->new( ) ; + my $filename = $1; + $sync->{ debug } and myprint( "XOAUTH2 json file: $filename\n" ) ; + my $FILE ; + if ( ! open( $FILE, '<', $filename ) ) + { + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, + "error [$filename]: $OS_ERROR\n" + ) ; + } + my $jsonfile = $json->decode( join q{}, <$FILE> ) ; + close $FILE ; - $iss = $jsonfile->{client_id}; - $key = $jsonfile->{private_key}; - $sync->{ debug } and myprint( "Service account: $iss\n"); - $sync->{ debug } and myprint( "Private key:\n$key\n"); + $iss = $jsonfile->{client_id}; + $key = $jsonfile->{private_key}; + $sync->{ debug } and myprint( "Service account: $iss\n"); + $sync->{ debug } and myprint( "Private key:\n$key\n"); } - else { + else + { # Get iss (service account address), keyfile name, and keypassword if necessary ( $iss, my $keyfile, my $keypass ) = $imap->Password =~ /([\-\d\w\@\.]+);([a-zA-Z0-9 \_\-\.\/]+);?(.*)?/x ; @@ -5667,7 +6693,10 @@ sub xoauth2 assertion => $jwt } ) ; unless( $response->is_success( ) ) { - exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, $response->code, "\n", $response->content, "\n" ) ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, + $response->code, "\n", $response->content, "\n" + ) ; }else{ $sync->{ debug } and myprint( $response->content ) ; } @@ -5774,19 +6803,35 @@ sub xmasterauth my @challenge = $imap->tag_and_run( $authmech, "+" ) ; if ( not defined $challenge[0] ) { - exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", $imap->LastError, "\n") ; + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, + "Failure authenticate with $authmech: ", + $imap->LastError, "\n" + ) ; return ; # hahaha! } $sync->{ debug } and myprint( "X-MASTERAUTH challenge: [@challenge]\n" ) ; $challenge[1] =~ s/^\+ |^\s+|\s+$//g ; - $imap->_imap_command( { addcrlf => 1, addtag => 0, tag => $imap->Count }, md5_hex( $challenge[1] . $password ) ) - or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", $imap->LastError, "\n") ; + if ( ! $imap->_imap_command( { addcrlf => 1, addtag => 0, tag => $imap->Count }, md5_hex( $challenge[1] . $password ) ) ) + { + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, + "Failure authenticate with $authmech: ", + $imap->LastError, "\n" + ) ; + } - $imap->tag_and_run( 'X-SETUSER ' . $user ) - or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", "X-SETUSER ", $imap->LastError, "\n") ; + if ( ! $imap->tag_and_run( 'X-SETUSER ' . $user ) ) + { + $sync->{nb_errors}++ ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, + "Failure authenticate with $authmech: ", + "X-SETUSER ", $imap->LastError, "\n" + ) ; + } - $imap->State( Mail::IMAPClient::Authenticated ) ; + $imap->State( Mail::IMAPClient::Authenticated ) ; # I comment this state because "Selected" state is usually done by SELECT or EXAMINE imap commands # $imap->State( Mail::IMAPClient::Selected ) ; @@ -5825,8 +6870,8 @@ sub banner_imapsync my $banner_imapsync = join q{}, q{$RCSfile: imapsync,v $ }, - q{$Revision: 1.945 $ }, - q{$Date: 2019/06/26 19:30:56 $ }, + q{$Revision: 1.977 $ }, + q{$Date: 2019/12/23 20:18:02 $ }, "\n", "Command line used, run by $EXECUTABLE_NAME:\n", "$PROGRAM_NAME ", command_line_nopassword( $mysync, @argv ), "\n" ; @@ -5867,16 +6912,23 @@ sub tests_match_a_pid_number note( 'Entering tests_match_a_pid_number()' ) ; is( undef, match_a_pid_number( ), 'match_a_pid_number: no args => undef' ) ; - is( undef, match_a_pid_number( '' ), 'match_a_pid_number: "" => undef' ) ; + is( undef, match_a_pid_number( q{} ), 'match_a_pid_number: "" => undef' ) ; is( undef, match_a_pid_number( 'lalala' ), 'match_a_pid_number: lalala => undef' ) ; is( 1, match_a_pid_number( 1 ), 'match_a_pid_number: 1 => 1' ) ; is( 1, match_a_pid_number( 123 ), 'match_a_pid_number: 123 => 1' ) ; + is( 1, match_a_pid_number( -123 ), 'match_a_pid_number: -123 => 1' ) ; is( 1, match_a_pid_number( '123' ), 'match_a_pid_number: "123" => 1' ) ; + is( 1, match_a_pid_number( '-123' ), 'match_a_pid_number: "-123" => 1' ) ; is( undef, match_a_pid_number( 'a123' ), 'match_a_pid_number: a123 => undef' ) ; + is( undef, match_a_pid_number( '-a123' ), 'match_a_pid_number: -a123 => undef' ) ; is( 1, match_a_pid_number( 99999 ), 'match_a_pid_number: 99999 => 1' ) ; + is( 1, match_a_pid_number( -99999 ), 'match_a_pid_number: -99999 => 1' ) ; is( undef, match_a_pid_number( 0 ), 'match_a_pid_number: 0 => undef' ) ; is( undef, match_a_pid_number( 100000 ), 'match_a_pid_number: 100000 => undef' ) ; is( undef, match_a_pid_number( 123456 ), 'match_a_pid_number: 123456 => undef' ) ; + is( undef, match_a_pid_number( '-0' ), 'match_a_pid_number: "-0" => undef' ) ; + is( undef, match_a_pid_number( -100000 ), 'match_a_pid_number: -100000 => undef' ) ; + is( undef, match_a_pid_number( -123456 ), 'match_a_pid_number: -123456 => undef' ) ; note( 'Leaving tests_match_a_pid_number()' ) ; return ; @@ -5885,11 +6937,15 @@ sub tests_match_a_pid_number sub match_a_pid_number { my $pid = shift @ARG ; - if ( ! $pid ) { return ; } - if ( ! match( $pid, '^\d+$' ) ) { return ; } - if ( 0 > $pid ) { return ; } + if ( ! defined $pid ) { return ; } + #print "$pid\n" ; + if ( ! match( $pid, '^-?\d+$' ) ) { return ; } + #print "$pid\n" ; + # can be negative on Windows + #if ( 0 > $pid ) { return ; } #if ( 65535 < $pid ) { return ; } - if ( 99999 < $pid ) { return ; } + if ( 99999 < abs( $pid ) ) { return ; } + if ( 0 == abs( $pid ) ) { return ; } return 1 ; } @@ -6231,22 +7287,42 @@ sub jux_utf8_list sub tests_jux_utf8_list { - note( 'Entering tests_jux_utf8_list()' ) ; + note( 'Entering tests_jux_utf8_list()' ) ; - ok( q{} eq jux_utf8_list( ), 'jux_utf8_list: void' ) ; - ok( "[]\n" eq jux_utf8_list( q{} ), 'jux_utf8_list: empty string' ) ; - ok( "[INBOX]\n" eq jux_utf8_list( 'INBOX' ), 'jux_utf8_list: INBOX' ) ; - ok( "[&ANY-] = [Ö]\n" eq jux_utf8_list( '&ANY-' ), 'jux_utf8_list: &ANY-' ) ; + use utf8 ; + is( q{}, jux_utf8_list( ), 'jux_utf8_list: void' ) ; + is( "[]\n", jux_utf8_list( q{} ), 'jux_utf8_list: empty string' ) ; + is( "[INBOX]\n", jux_utf8_list( 'INBOX' ), 'jux_utf8_list: INBOX' ) ; + is( "[&ANY-] = [Ö]\n", jux_utf8_list( '&ANY-' ), 'jux_utf8_list: [&ANY-] = [Ö]' ) ; - note( 'Leaving tests_jux_utf8_list()' ) ; + note( 'Leaving tests_jux_utf8_list()' ) ; return( 0 ) ; } -sub jux_utf8 +# editing utf8 can be tricky without an utf8 editor +sub tests_jux_utf8_old +{ + note( 'Entering tests_jux_utf8_old()' ) ; + + no utf8 ; + + is( '[]', jux_utf8_old( q{} ), 'jux_utf8_old: void => []' ) ; + is( '[INBOX]', jux_utf8_old( 'INBOX'), 'jux_utf8_old: INBOX => [INBOX]' ) ; + is( '[&ZTZO9nux-] = [收件箱]', jux_utf8_old( '&ZTZO9nux-'), 'jux_utf8_old: => [&ZTZO9nux-] = [收件箱]' ) ; + is( '[&ANY-] = [Ö]', jux_utf8_old( '&ANY-'), 'jux_utf8_old: &ANY- => [&ANY-] = [Ö]' ) ; + # +BD8EQAQ1BDQEOwQ+BDM- SHOULD stay as is! + is( '[+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]', jux_utf8_old( '+BD8EQAQ1BDQEOwQ+BDM-' ), 'jux_utf8_old: => [+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]' ) ; + is( '[&BB8EQAQ+BDUEOgRC-] = [Проект]', jux_utf8_old( '&BB8EQAQ+BDUEOgRC-' ), 'jux_utf8_old: => [&BB8EQAQ+BDUEOgRC-] = [Проект]' ) ; + + note( 'Leaving tests_jux_utf8_old()' ) ; + return ; +} + +sub jux_utf8_old { # juxtapose utf8 at the right if different my ( $s_utf7 ) = shift ; - my ( $s_utf8 ) = imap_utf7_decode( $s_utf7 ) ; + my ( $s_utf8 ) = imap_utf7_decode_old( $s_utf7 ) ; if ( $s_utf7 eq $s_utf8 ) { #myprint( "[$s_utf7]\n" ) ; @@ -6257,26 +7333,10 @@ sub jux_utf8 } } -# editing utf8 can be tricky without an utf8 editor -sub tests_jux_utf8 -{ - note( 'Entering tests_jux_utf8()' ) ; - - ok( '[INBOX]' eq jux_utf8( 'INBOX'), 'jux_utf8: INBOX => [INBOX]' ) ; - ok( '[&ZTZO9nux-] = [收件箱]' eq jux_utf8( '&ZTZO9nux-'), 'jux_utf8: => [&ZTZO9nux-] = [收件箱]' ) ; - ok( '[&ANY-] = [Ö]' eq jux_utf8( '&ANY-'), 'jux_utf8: &ANY- => [&ANY-] = [Ö]' ) ; - ok( '[]' eq jux_utf8( q{} ), 'jux_utf8: void => []' ) ; - ok( '[+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]' eq jux_utf8( '+BD8EQAQ1BDQEOwQ+BDM-' ), 'jux_utf8: => [+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]' ) ; - ok( '[&BB8EQAQ+BDUEOgRC-] = [Проект]' eq jux_utf8( '&BB8EQAQ+BDUEOgRC-' ), 'jux_utf8: => [&BB8EQAQ+BDUEOgRC-] = [Проект]' ) ; - - note( 'Leaving tests_jux_utf8()' ) ; - return ; -} - # Copied from http://cpansearch.perl.org/src/FABPOT/Unicode-IMAPUtf7-2.01/lib/Unicode/IMAPUtf7.pm # and then fixed with # https://rt.cpan.org/Public/Bug/Display.html?id=11172 -sub imap_utf7_decode +sub imap_utf7_decode_old { my ( $s ) = shift ; @@ -6290,7 +7350,77 @@ sub imap_utf7_decode return( Unicode::String::utf7( $s )->utf8 ) ; } + + + + +sub tests_jux_utf8 +{ + note( 'Entering tests_jux_utf8()' ) ; + #no utf8 ; + use utf8 ; + + #binmode STDOUT, ":encoding(UTF-8)" ; + binmode STDERR, ":encoding(UTF-8)" ; + + # This test is because the binary can fail on it, a PAR.pm issue. + # The failure was with the underlying Encode::IMAPUTF7 module line 66 release 1.05 + # Was solved by including Encode in imapsync and using "pp -x". + ok( find_encoding( "UTF-16BE"), 'jux_utf8: Encode::find_encoding: UTF-16BE' ) ; + + # + is( '[]', jux_utf8( q{} ), 'jux_utf8: void => []' ) ; + is( '[INBOX]', jux_utf8( 'INBOX'), 'jux_utf8: INBOX => [INBOX]' ) ; + is( '[&ANY-] = [Ö]', jux_utf8( '&ANY-'), 'jux_utf8: &ANY- => [&ANY-] = [Ö]' ) ; + # +BD8EQAQ1BDQEOwQ+BDM- must stay as is + is( '[+BD8EQAQ1BDQEOwQ+BDM-]', jux_utf8( '+BD8EQAQ1BDQEOwQ+BDM-' ), 'jux_utf8: => [+BD8EQAQ1BDQEOwQ+BDM-] = [+BD8EQAQ1BDQEOwQ+BDM-]' ) ; + is( '[&BB8EQAQ+BDUEOgRC-] = [Проект]', jux_utf8( '&BB8EQAQ+BDUEOgRC-' ), 'jux_utf8: => [&BB8EQAQ+BDUEOgRC-] = [Проект]' ) ; + + is( '[R&AOk-ponses 1200+1201+1202] = [Réponses 1200+1201+1202]', jux_utf8( q{R&AOk-ponses 1200+1201+1202} ), 'jux_utf8: [R&AOk-ponses 1200+1201+1202] = [Réponses 1200+1201+1202]' ) ; + my $str = Encode::IMAPUTF7::encode("IMAP-UTF-7", 'Réponses 1200+1201+1202' ) ; + is( '[R&AOk-ponses 1200+1201+1202] = [Réponses 1200+1201+1202]', jux_utf8( $str ), "jux_utf8: [$str] = [Réponses 1200+1201+1202]" ) ; + + is( '[INBOX.&AOkA4ADnAPk-&-*] = [INBOX.éàçù&*]', jux_utf8( 'INBOX.&AOkA4ADnAPk-&-*' ), "jux_utf8: [INBOX.&AOkA4ADnAPk-&-*] = [INBOX.éàçù&*]" ) ; + + is( '[&ZTZO9nux-] = [收件箱]', jux_utf8( '&ZTZO9nux-'), 'jux_utf8: => [&ZTZO9nux-] = [收件箱]' ) ; + # + note( 'Leaving tests_jux_utf8()' ) ; + return ; +} + +sub jux_utf8 +{ + #use utf8 ; + # juxtapose utf8 at the right if different + my ( $s_utf7 ) = shift ; + my ( $s_utf8 ) = imap_utf7_decode( $s_utf7 ) ; + + if ( $s_utf7 eq $s_utf8 ) { + #myprint( "[$s_utf7]\n" ) ; + return( "[$s_utf7]" ) ; + }else{ + #myprint( "[$s_utf7] = [$s_utf8]\n" ) ; + return( "[$s_utf7] = [$s_utf8]" ) ; + } +} + +sub imap_utf7_decode +{ + #use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::decode("IMAP-UTF-7", $s ) ) ; +} + sub imap_utf7_encode +{ + #use utf8 ; + my ( $s ) = shift ; + return( Encode::IMAPUTF7::encode("IMAP-UTF-7", $s ) ) ; +} + + + +sub imap_utf7_encode_old { my ( $s ) = @_ ; @@ -6915,7 +8045,11 @@ sub subfolder1 $mysync->{ automap } = undef ; myprint( "Sanitizing subfolder1: [$mysync->{ subfolder1 }] => [$subfolder1]\n" ) ; $mysync->{ subfolder1 } = $subfolder1 ; - add_subfolder1_to_folderrec( $mysync ) || exit_clean( $mysync, $EXIT_SUBFOLDER1_NO_EXISTS ) ; + if ( ! add_subfolder1_to_folderrec( $mysync ) ) + { + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EXIT_SUBFOLDER1_NO_EXISTS, "subfolder1 $subfolder1 does not exist\n" ) ; + } } else { @@ -6951,7 +8085,7 @@ sub tests_sanitize_subfolder note( 'Entering tests_sanitize_subfolder()' ) ; is( undef, sanitize_subfolder( ), 'sanitize_subfolder: no args => undef' ) ; - is( undef, sanitize_subfolder( '' ), 'sanitize_subfolder: empty => undef' ) ; + is( undef, sanitize_subfolder( q{} ), 'sanitize_subfolder: empty => undef' ) ; is( undef, sanitize_subfolder( ' ' ), 'sanitize_subfolder: blank => undef' ) ; is( undef, sanitize_subfolder( ' ' ), 'sanitize_subfolder: blanks => undef' ) ; is( 'abcd', sanitize_subfolder( 'abcd' ), 'sanitize_subfolder: abcd => abcd' ) ; @@ -7256,7 +8390,7 @@ EOS # Global variables to remove: -# +# None? sub imap2_folder_name @@ -7285,7 +8419,7 @@ sub imap2_folder_name #myprint( "h1_fold=$h1_fold\n" ) ; } - if ( ( '' eq $h1_fold ) or ( $mysync->{ h1_prefix } eq $h1_fold ) ) + if ( ( q{} eq $h1_fold ) or ( $mysync->{ h1_prefix } eq $h1_fold ) ) { $h1_fold = 'INBOX' ; } @@ -7301,9 +8435,9 @@ sub tests_remove_last_char_if_is note( 'Entering tests_remove_last_char_if_is()' ) ; is( undef, remove_last_char_if_is( ), 'remove_last_char_if_is: no args => undef' ) ; - is( '', remove_last_char_if_is( '' ), 'remove_last_char_if_is: empty => empty' ) ; - is( '', remove_last_char_if_is( '', 'Z' ), 'remove_last_char_if_is: empty Z => empty' ) ; - is( '', remove_last_char_if_is( 'Z', 'Z' ), 'remove_last_char_if_is: Z Z => empty' ) ; + is( q{}, remove_last_char_if_is( q{} ), 'remove_last_char_if_is: empty => empty' ) ; + is( q{}, remove_last_char_if_is( q{}, 'Z' ), 'remove_last_char_if_is: empty Z => empty' ) ; + is( q{}, remove_last_char_if_is( 'Z', 'Z' ), 'remove_last_char_if_is: Z Z => empty' ) ; is( 'abc', remove_last_char_if_is( 'abcZ', 'Z' ), 'remove_last_char_if_is: abcZ Z => abc' ) ; is( 'abcY', remove_last_char_if_is( 'abcY', 'Z' ), 'remove_last_char_if_is: abcY Z => abcY' ) ; note( 'Leaving tests_remove_last_char_if_is()' ) ; @@ -7353,8 +8487,8 @@ sub tests_prefix_seperator_invertion is( '.....', prefix_seperator_invertion( undef, '.....' ), 'prefix_seperator_invertion: ..... => .....' ) ; my $mysync = { - h1_prefix => '', - h2_prefix => '', + h1_prefix => q{}, + h2_prefix => q{}, h1_sep => '/', h2_sep => '/', } ; @@ -7477,7 +8611,10 @@ sub regextrans2 my $ret = eval "\$h2_fold =~ $regextrans2 ; 1 " ; ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "[$h2_fold_before] -> [$h2_fold] using regextrans2 [$regextrans2]\n" ) ; if ( not ( defined $ret ) or $EVAL_ERROR ) { - exit_clean( $mysync, $EX_USAGE, "error: eval regextrans2 '$regextrans2': $EVAL_ERROR\n" ) ; + $mysync->{nb_errors}++ ; + exit_clean( $mysync, $EX_USAGE, + "error: eval regextrans2 '$regextrans2': $EVAL_ERROR\n" + ) ; } } return( $h2_fold ) ; @@ -7507,106 +8644,64 @@ sub decompose_regex } -sub foldersizes + +sub tests_timenext { + note( 'Entering tests_timenext()' ) ; - my ( $side, $imap, $search_cmd, $abletosearch, @folders ) = @_ ; - my $total_size = 0 ; - my $total_nb = 0 ; - my $biggest_in_all = 0 ; + is( undef, timenext( ), 'timenext: no args => undef' ) ; + my $mysync ; + is( undef, timenext( $mysync ), 'timenext: undef => undef' ) ; + $mysync = {} ; + ok( time - timenext( $mysync ) <= 1e-02, 'timenext: defined first time => ~ time' ) ; + ok( timenext( $mysync ) <= 1e-02, 'timenext: second time => less than 1e-02' ) ; + ok( timenext( $mysync ) <= 1e-02, 'timenext: third time => less than 1e-02' ) ; - my $nb_folders = scalar @folders ; - my $ct_folders = 0 ; # folder counter. - myprint( "++++ Calculating sizes of $nb_folders folders on $side\n" ) ; - foreach my $folder ( @folders ) { - my $stot = 0 ; - my $nb_msgs = 0 ; - $ct_folders++ ; - myprintf( "$side folder %7s %-35s", "$ct_folders/$nb_folders", jux_utf8( $folder ) ) ; - if ( 'Host2' eq $side and not exists $h2_folders_all_UPPER{ uc $folder } ) { - myprint( " does not exist yet\n") ; - next ; - } - if ( 'Host1' eq $side and not exists $h1_folders_all{ $folder } ) { - myprint( " does not exist\n" ) ; - next ; - } - - last if $imap->IsUnconnected( ) ; - # FTGate is RFC buggy with EXAMINE it does not act as SELECT - #unless ( $imap->examine( $folder ) ) { - unless ( $imap->select( $folder ) ) { - my $error = join q{}, - "$side Folder $folder: Could not select: ", - $imap->LastError, "\n" ; - errors_incr( $sync, $error ) ; - next ; - } - last if $imap->IsUnconnected( ) ; - - my $hash_ref = { } ; - my @msgs = select_msgs( $imap, undef, $search_cmd, $abletosearch, $folder ) ; - $nb_msgs = scalar @msgs ; - my $biggest_in_folder = 0 ; - @{ $hash_ref }{ @msgs } = ( undef ) if @msgs ; - - last if $imap->IsUnconnected( ) ; - if ( $nb_msgs > 0 and @msgs ) { - if ( $abletosearch ) { - if ( ! $imap->fetch_hash( \@msgs, 'RFC822.SIZE', $hash_ref) ) { - my $error = "$side failure with fetch_hash: $EVAL_ERROR\n" ; - errors_incr( $sync, $error ) ; - return ; - } - }else{ - my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ; - my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ; - if ( ! $imap->fetch_hash( $fetch_hash_uids, 'RFC822.SIZE', $hash_ref ) ) { - my $error = "$side failure with fetch_hash: $EVAL_ERROR\n" ; - errors_incr( $sync, $error ) ; - return ; - } - } - for ( keys %{ $hash_ref } ) { - my $size = $hash_ref->{ $_ }->{ 'RFC822.SIZE' } ; - $stot += $size ; - $biggest_in_folder = max( $biggest_in_folder, $size ) ; - } - } - - myprintf( ' Size: %9s', $stot ) ; - myprintf( ' Messages: %5s', $nb_msgs ) ; - myprintf( " Biggest: %9s\n", $biggest_in_folder ) ; - $total_size += $stot ; - $total_nb += $nb_msgs ; - $biggest_in_all = max( $biggest_in_all, $biggest_in_folder ) ; - } - myprintf( "%s Nb folders: %11s folders\n", $side, $nb_folders ) ; - myprintf( "%s Nb messages: %11s messages\n", $side, $total_nb ) ; - myprintf( "%s Total size: %11s bytes (%s)\n", $side, $total_size, bytes_display_string( $total_size ) ) ; - myprintf( "%s Biggest message: %11s bytes (%s)\n", $side, $biggest_in_all, bytes_display_string( $biggest_in_all ) ) ; - myprintf( "%s Time spent: %11.1f seconds\n", $side, timenext( ) ) ; - return( $total_nb, $total_size ) ; + note( 'Leaving tests_timenext()' ) ; + return ; } + sub timenext { + my $mysync = shift ; + + if ( ! defined $mysync ) + { + return ; + } my ( $timenow, $timediff ) ; - # $timebefore is global, beurk ! + + $mysync->{ timebefore } ||= 0; # epoch... $timenow = time ; - $timediff = $timenow - $timebefore ; - $timebefore = $timenow ; + $timediff = $timenow - $mysync->{ timebefore } ; + $mysync->{ timebefore } = $timenow ; + # myprint( "timenext: $timediff\n" ) ; return( $timediff ) ; } + +sub tests_timesince +{ + note( 'Entering tests_timesince()' ) ; + + ok( timesince( time - 1 ) - 1 <= 1e-02, 'timesince: time - 1 => <= 1 + 1e-02' ) ; + ok( timesince( time ) <= 1e-02, 'timesince: time => <= 1e-02' ) ; + ok( timesince( ) - time <= 1e-02, 'timesince: no args => <= time + 1e-02' ) ; + note( 'Leaving tests_timesince()' ) ; + return ; +} + + + sub timesince { my $timeinit = shift || 0 ; my ( $timenow, $timediff ) ; $timenow = time ; $timediff = $timenow - $timeinit ; - # Often used in a division so no 0 - return( max( 1, $timediff) ) ; + # Often used in a division so no 0 but a nano seconde. + return( max( $timediff, min( 1e-09, $timediff ) ) ) ; } @@ -8151,7 +9246,8 @@ sub copy_message # copy my ( $mysync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) = @_ ; - ( $mysync->{ debug } or $mysync->{dry}) and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $mysync->{dry_message}\n" ) ; + ( $mysync->{ debug } or $mysync->{dry} ) + and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $mysync->{dry_message} " . eta( $mysync ) . "\n" ) ; my $h1_size = $h1_fir_ref->{$h1_msg}->{'RFC822.SIZE'} || 0 ; my $h1_flags = $h1_fir_ref->{$h1_msg}->{'FLAGS'} || q{} ; @@ -8350,7 +9446,7 @@ sub message_for_host2 $mysync->{ debug } and myprint( "msg $h1_fold/$h1_msg adding custom header [$header]\n" ) ; ${ $string_ref } = $header . "\r\n" . ${ $string_ref } ; } - + if ( ( defined $mysync->{ truncmess } ) and is_an_integer( $mysync->{ truncmess } ) ) { ${ $string_ref } = truncmess( ${ $string_ref }, $mysync->{ truncmess } ) ; @@ -8388,10 +9484,10 @@ sub truncmess { my $string = shift ; my $length = shift ; - + if ( not defined $string ) { return ; } if ( not defined $length ) { return $string ; } - + $string = substr $string, 0, $length ; return $string ; } @@ -8414,7 +9510,7 @@ sub tests_message_for_host2 $h1_msg = 1 ; $h1_fold = 'FoldFoo'; $h1_size = 9 ; - $h1_flags = '' ; + $h1_flags = q{} ; $h1_idate = '10-Jul-2015 09:00:00 +0200' ; $h1_fir_ref = {} ; $string_ref = \$string ; @@ -8466,7 +9562,7 @@ sub tests_message_for_host2 is( undef, $string, q{message_for_host2: --pipemess 'true', value} ) ; } - note( 'Leaving tests_message_for_host2()' ) ; + note( 'Leaving tests_message_for_host2()' ) ; return ; } @@ -8531,7 +9627,7 @@ sub labels_remove_subfolder1 else { # Remove surrounding quotes if any, to add them again in case of space - $label = join( '', quotewords('\s+', 0, $label ) ) ; + $label = join( q{}, quotewords('\s+', 0, $label ) ) ; $label =~ s{$subfolder1/?}{} ; if ( 'INBOX' eq $label ) { @@ -8562,9 +9658,9 @@ sub tests_labels_remove_special note( 'Entering tests_labels_remove_special()' ) ; is( undef, labels_remove_special( ), 'labels_remove_special: no parameters => undef' ) ; - is( '', labels_remove_special( '' ), 'labels_remove_special: empty string => empty string' ) ; - is( '', labels_remove_special( '"\\\\Inbox"' ), 'labels_remove_special:"\\\\Inbox" => empty string' ) ; - is( '', labels_remove_special( '"\\\\Inbox" "\\\\Starred"' ), 'labels_remove_special:"\\\\Inbox" "\\\\Starred" => empty string' ) ; + is( q{}, labels_remove_special( q{} ), 'labels_remove_special: empty string => empty string' ) ; + is( q{}, labels_remove_special( '"\\\\Inbox"' ), 'labels_remove_special:"\\\\Inbox" => empty string' ) ; + is( q{}, labels_remove_special( '"\\\\Inbox" "\\\\Starred"' ), 'labels_remove_special:"\\\\Inbox" "\\\\Starred" => empty string' ) ; is( 'Bar Foo', labels_remove_special( 'Foo Bar' ), 'labels_remove_special:Foo Bar => Bar Foo' ) ; is( 'Bar Foo', labels_remove_special( 'Foo Bar "\\\\Inbox"' ), 'labels_remove_special:Foo Bar "\\\\Inbox" => Bar Foo' ) ; note( 'Leaving tests_labels_remove_special()' ) ; @@ -8686,14 +9782,14 @@ sub labels_add_subfolder2 # \Seen \Deleted ... stay the same #push @labels_subfolder2, $label ; # Remove surrounding quotes if any, to add them again - $label = join( '', quotewords('\s+', 0, $label ) ) ; + $label = join( q{}, quotewords('\s+', 0, $label ) ) ; push @labels_subfolder2, qq{"$subfolder2/\\$label"} ; } else { # Remove surrounding quotes if any, to add them again in case of space - $label = join( '', quotewords('\s+', 0, $label ) ) ; + $label = join( q{}, quotewords('\s+', 0, $label ) ) ; if ( $label =~ m{ } ) { push @labels_subfolder2, qq{"$subfolder2/$label"} ; @@ -9111,14 +10207,13 @@ sub append_message_on_host2 } if ( $mysync->{ synclabels } ) { synclabels( $mysync, $h1_msg, $new_id ) } $h2_uidguess += 1 ; - $mysync->{total_bytes_transferred} += $string_len ; - $mysync->{nb_msg_transferred} += 1 ; + $mysync->{ total_bytes_transferred } += $string_len ; + $mysync->{ nb_msg_transferred } += 1 ; $mysync->{ h1_nb_msg_processed } +=1 ; my $time_spent = timesince( $mysync->{begin_transfer_time} ) ; my $rate = bytes_display_string( $mysync->{total_bytes_transferred} / $time_spent ) ; - my $eta = eta( $time_spent, - $mysync->{ h1_nb_msg_processed }, $h1_nb_msg_start, $mysync->{nb_msg_transferred} ) ; + my $eta = eta( $mysync ) ; my $amount_transferred = bytes_display_string( $mysync->{total_bytes_transferred} ) ; myprintf( "msg %s/%-19s copied to %s/%-10s %.2f msgs/s %s/s %s copied %s\n", $h1_fold, "$h1_msg {$string_len}", $h2_fold, $new_id, $mysync->{nb_msg_transferred}/$time_spent, $rate, @@ -9140,12 +10235,13 @@ sub append_message_on_host2 } else{ $nb_msg_skipped_dry_mode += 1 ; - $mysync->{ h1_nb_msg_processed } +=1 ; + $mysync->{ h1_nb_msg_processed } += 1 ; } return ; } + sub tests_sleep_if_needed { note( 'Entering tests_sleep_if_needed()' ) ; @@ -9426,38 +10522,106 @@ sub uidexpunge_or_expunge return ; } +sub eta_print +{ + my $mysync = shift ; + if ( my $eta = eta( $mysync ) ) + { + myprint( "$eta\n" ) ; + } + return ; +} + +sub tests_eta +{ + note( 'Entering tests_eta()' ) ; + + is( q{}, eta( ), 'eta: no args => ""' ) ; + is( q{}, eta( undef ), 'eta: undef => ""' ) ; + my $mysync = {} ; + # No foldersizes + is( q{}, eta( $mysync ), 'eta: No foldersizes => ""' ) ; + + $mysync->{ foldersizes } = 1 ; + + $mysync->{ begin_transfer_time } = time ; # Now + $mysync->{ h1_nb_msg_processed } = 0 ; + + is( "ETA: " . localtime( time ) . " 0 s 0/0 msgs left", + eta( $mysync ), + 'eta: no args => ETA: "Now" 0 s 0/0 msgs left' ) ; + + $mysync->{ h1_nb_msg_processed } = 1 ; + $mysync->{ h1_nb_msg_start } = 2 ; + is( "ETA: " . localtime( time ) . " 0 s 1/2 msgs left", + eta( $mysync ), + 'eta: 1, 1, 2 => ETA: "Now" 0 s 1/2 msgs left' ) ; + + note( 'Leaving tests_eta()' ) ; + return ; +} + sub eta { - my( $my_time_spent, $h1_nb_processed, $my_h1_nb_msg_start, $nb_transferred ) = @_ ; - return( q{} ) if not $foldersizes ; + my( $mysync ) = shift ; - my $time_remaining = time_remaining( $my_time_spent, $h1_nb_processed, $my_h1_nb_msg_start, $nb_transferred ) ; - my $nb_msg_remaining = $my_h1_nb_msg_start - $h1_nb_processed ; + if ( ! $mysync ) + { + return q{} ; + } + + return( q{} ) if not $mysync->{ foldersizes } ; + + my $h1_nb_msg_start = $mysync->{ h1_nb_msg_start } ; + my $h1_nb_processed = $mysync->{ h1_nb_msg_processed } ; + my $nb_msg_transferred = ( $mysync->{dry} ) ? $mysync->{ h1_nb_msg_processed } : $mysync->{ nb_msg_transferred } ; + my $time_spent = timesince( $mysync->{ begin_transfer_time } ) ; + $h1_nb_processed ||= 0 ; + $h1_nb_msg_start ||= 0 ; + $time_spent ||= 0 ; + + my $time_remaining = time_remaining( $time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_msg_transferred ) ; + $mysync->{ debug } and myprint( "time_spent: $time_spent time_remaining: $time_remaining\n" ) ; + my $nb_msg_remaining = $h1_nb_msg_start - $h1_nb_processed ; my $eta_date = localtime( time + $time_remaining ) ; - return( mysprintf( 'ETA: %s %1.0f s %s/%s msgs left', $eta_date, $time_remaining, $nb_msg_remaining, $my_h1_nb_msg_start ) ) ; + return( mysprintf( 'ETA: %s %1.0f s %s/%s msgs left', + $eta_date, $time_remaining, $nb_msg_remaining, $h1_nb_msg_start ) ) ; } + + + sub time_remaining { - my( $my_time_spent, $h1_nb_processed, $my_h1_nb_msg_start, $nb_transferred ) = @_ ; + my( $my_time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_transferred ) = @_ ; - my $time_remaining = ( $my_time_spent / $nb_transferred ) * ( $my_h1_nb_msg_start - $h1_nb_processed ) ; + $nb_transferred ||= 1 ; # At least one is done (no division by zero) + $h1_nb_processed ||= 0 ; + $h1_nb_msg_start ||= $h1_nb_processed ; + $my_time_spent ||= 0 ; + + my $time_remaining = ( $my_time_spent / $nb_transferred ) * ( $h1_nb_msg_start - $h1_nb_processed ) ; return( $time_remaining ) ; } sub tests_time_remaining { - note( 'Entering tests_time_remaining()' ) ; + note( 'Entering tests_time_remaining()' ) ; + # time_spent, nb_processed, nb_to_do_total, nb_transferred + is( 0, time_remaining( ), 'time_remaining: no args -> 0' ) ; + is( 0, time_remaining( 0, 0, 0, 0 ), 'time_remaining: 0, 0, 0, 0 -> 0' ) ; + is( 1, time_remaining( 1, 1, 2, 1 ), 'time_remaining: 1, 1, 2, 1 -> 1' ) ; + is( 1, time_remaining( 9, 9, 10, 9 ), 'time_remaining: 9, 9, 10, 9 -> 1' ) ; + is( 9, time_remaining( 1, 1, 10, 1 ), 'time_remaining: 1, 1, 10, 1 -> 9' ) ; + is( 5, time_remaining( 5, 5, 10, 5 ), 'time_remaining: 5, 5, 10, 5 -> 5' ) ; + is( 25, time_remaining( 5, 5, 10, 0 ), 'time_remaining: 5, 5, 10, 0 -> ( 5 / 1 ) * ( 10 - 5) = 25' ) ; + is( 25, time_remaining( 5, 5, 10, 1 ), 'time_remaining: 5, 5, 10, 1 -> ( 5 / 1 ) * ( 10 - 5) = 25' ) ; - ok( 1 == time_remaining( 1, 1, 2, 1 ), 'time_remaining: 1, 1, 2, 1 -> 1' ) ; - ok( 1 == time_remaining( 9, 9, 10, 9 ), 'time_remaining: 9, 9, 10, 9 -> 1' ) ; - ok( 9 == time_remaining( 1, 1, 10, 1 ), 'time_remaining: 1, 1, 10, 1 -> 1' ) ; - - note( 'Leaving tests_time_remaining()' ) ; + note( 'Leaving tests_time_remaining()' ) ; return ; } @@ -10669,11 +11833,52 @@ EOM @regexmess = ( 's/.{10000}\K.*//gs' ) ; is( "123456789\n" x 1000, regexmess( "123456789\n" x 100_000 ), 'regexmess, truncate whole message after 10000 characters ~ 1MB' ) ; +@regexmess = ( 's/^(X-Ham-Report.*?\n)^X-/X-/sm' ) ; + +is( +<<'EOM' +X-Spam-Score: -1 +X-Spam-Bar: / +X-Spam-Flag: NO +Date: Sat, 10 Jul 2010 05:34:45 -0700 +From: + +Hello, + +Bye. +EOM +, +regexmess( +<<'EOM' +X-Spam-Score: -1 +X-Spam-Bar: / +X-Ham-Report: =?utf-8?Q?Spam_detection_software=2C_running?= + =?utf-8?Q?_on_the_system_=22ohp-ag006.int200?= +_has_NOT_identified_thi?= + =?utf-8?Q?s_incoming_email_as_spam.__The_o?= +_message_has_been_attac?= + =?utf-8?Q?hed_to_this_so_you_can_view_it_o?= +___________________________?= + =?utf-8?Q?__author's_domain +X-Spam-Flag: NO +Date: Sat, 10 Jul 2010 05:34:45 -0700 +From: + +Hello, + +Bye. +EOM +), + 'regexmess: 1 Delete header X-Ham-Report:'); # regex to play with Date: from the FAQ #@regexmess = 's{\A(.*?(?! ^$))^Date:(.*?)$}{$1Date:$2\nX-Date:$2}gxms' + + + + note( 'Leaving tests_regexmess()' ) ; return ; @@ -11076,7 +12281,7 @@ sub stats myprint( "Transfer ended on : $timeend_str\n" ) ; myprintf( "Transfer time : %.1f sec\n", $timediff ) ; myprint( "Folders synced : $h1_folders_wanted_ct/$h1_folders_wanted_nb synced\n" ) ; - myprint( "Messages transferred : $mysync->{nb_msg_transferred} " ) ; + myprint( "Messages transferred : $mysync->{ nb_msg_transferred } " ) ; myprint( "(could be $nb_msg_skipped_dry_mode without dry mode)" ) if ( $mysync->{dry} ) ; myprint( "\n" ) ; myprint( "Messages skipped : $mysync->{ nb_msg_skipped }\n" ) ; @@ -11111,11 +12316,11 @@ sub stats $max_msg_size_in_bytes, bytes_display_string( $max_msg_size_in_bytes) ) ; myprint( "Memory/biggest message ratio : $memory_ratio\n" ) ; - if ( $foldersizesatend and $foldersizes ) { + if ( $mysync->{ foldersizesatend } and $mysync->{ foldersizes } ) { - my $nb_msg_start_diff = diff_or_NA( $h2_nb_msg_start, $h1_nb_msg_start ) ; - my $bytes_start_diff = diff_or_NA( $h2_bytes_start, $h1_bytes_start ) ; + my $nb_msg_start_diff = diff_or_NA( $mysync->{ h2_nb_msg_start }, $mysync->{ h1_nb_msg_start } ) ; + my $bytes_start_diff = diff_or_NA( $mysync->{ h2_bytes_start }, $mysync->{ h1_bytes_start } ) ; myprintf("Start difference host2 - host1 : %s messages, %s bytes (%s)\n", $nb_msg_start_diff, $bytes_start_diff, @@ -11769,10 +12974,12 @@ sub imapsync_version_public my $local_version = imapsync_version( $sync ) ; my $imapsync_basename = imapsync_basename( ) ; + my $context = imapsync_context( ) ; my $agent_info = "$OSNAME system, perl " . mysprintf( '%vd', $PERL_VERSION) . ", Mail::IMAPClient $Mail::IMAPClient::VERSION" - . " $imapsync_basename" ; + . " $imapsync_basename" + . " $context" ; my $sock = IO::Socket::INET->new( PeerAddr => 'imapsync.lamiral.info', PeerPort => 80, @@ -11899,6 +13106,42 @@ sub tests_check_last_release return ; } +sub tests_imapsync_context +{ + note( 'Entering tests_imapsync_context()' ) ; + + like( imapsync_context( ), qr/^CGI|^Docker|^DockerCGI|^Standard/, 'imapsync_context: CGI or Docker or DockerCGI or Standard' ) ; + note( 'Leaving tests_imapsync_context()' ) ; + return ; +} + +sub imapsync_context +{ + my $mysync = shift ; + + my $context = q{} ; + + if ( under_docker_context( $mysync ) && under_cgi_context( $mysync ) ) + { + $context = 'DockerCGI' ; + } + elsif ( under_docker_context( $mysync ) ) + { + $context = 'Docker' ; + } + elsif ( under_cgi_context( $mysync ) ) + { + $context = 'CGI' ; + } + else + { + $context = 'Standard' ; + } + + return $context ; + +} + sub imapsync_version { my $mysync = shift ; @@ -12571,6 +13814,7 @@ sub search_dyn_lib_locale { return search_dyn_lib_locale_MSWin32( ) ; } + } sub search_dyn_lib_locale_darwin @@ -12873,11 +14117,15 @@ sub list_keys_in_2_not_in_1 my @list; foreach my $key ( sort keys %{ $hash_2_ref } ) { - #$debug and print "$folder\n" ; - next if exists $hash_1_ref->{$key} ; + #$sync->{ debug } and print "$key\n" ; + if ( exists $hash_1_ref->{$key} ) + { + next ; + } + #$sync->{ debug } and print "list_keys_in_2_not_in_1: $key\n" ; push @list, $key ; } - #$debug and print "@list\n" ; + #$sync->{ debug } and print "@list\n" ; return( @list ) ; } @@ -12889,7 +14137,7 @@ sub list_folders_in_2_not_in_1 @h2_folders_not_in_h1 = list_keys_in_2_not_in_1( \%h1_folders_all, \%h2_folders_all ) ; map { $h2_folders_not_in_h1{$_} = 1} @h2_folders_not_in_h1 ; @h2_folders_not_in_h1 = list_keys_in_2_not_in_1( \%h2_folders_from_1_all, \%h2_folders_not_in_h1 ) ; - + #$sync->{ debug } and print "h2_folders_not_in_h1: @h2_folders_not_in_h1\n" ; return( reverse @h2_folders_not_in_h1 ) ; } @@ -12973,16 +14221,28 @@ sub comment_on_final_diff_in_1_not_in_2 if ( 0 == $mysync->{ nb_messages_in_1_not_in_2 } ) { - myprint( "The sync looks good, all $nb_identified_h1_messages identified messages in host1 are on host2.\n" ) ; + myprint( "The sync looks good, all ", + $nb_identified_h1_messages, + " identified messages in host1 are on host2.\n" ) ; } else { - myprint( "The sync is not finished, there are $mysync->{ nb_messages_in_1_not_in_2 } identified messages in host1 that are not on host2.\n" ) ; + myprint( "The sync is not finished, there are ", + $mysync->{ nb_messages_in_1_not_in_2 }, + " identified messages in host1 that are not on host2.\n" ) ; } + if ( 1 <= $mysync->{ h1_nb_msg_noheader } ) { - myprint( "There are $mysync->{ h1_nb_msg_noheader } unidentified messages (usually Sent or Draft messages). To sync them add option --addheader\n" ) ; + myprint( "There are ", + $mysync->{ h1_nb_msg_noheader }, + " unidentified messages (usually Sent or Draft messages).", + " To sync them add option --addheader\n" ) ; + } + else + { + myprint( "There is no unidentified message\n" ) ; } return ; @@ -13001,7 +14261,7 @@ sub comment_on_final_diff_in_2_not_in_1 } my $nb_identified_h2_messages = scalar( keys %{ $mysync->{ h2_folders_of_md5 } } ) ; - # Calculate if not yet done + # Calculate if not done yet if ( not defined $mysync->{ nb_messages_in_2_not_in_1 } ) { nb_messages_in_2_not_in_1( $mysync ) ; @@ -13009,14 +14269,17 @@ sub comment_on_final_diff_in_2_not_in_1 if ( 0 == $mysync->{ nb_messages_in_2_not_in_1 } ) { - myprint( "The sync is strict, all $nb_identified_h2_messages identified messages in host2 are on host1.\n" ) ; + myprint( "The sync is strict, all ", + $nb_identified_h2_messages, + " identified messages in host2 are on host1.\n" ) ; } else { myprint( "The sync is not strict, there are ", $mysync->{ nb_messages_in_2_not_in_1 }, " messages in host2 that are not on host1.", - " Use --delete2 to delete them and have a strict sync.\n" ) ; + " Use --delete2 to delete them and have a strict sync.", + " ($nb_identified_h2_messages identified messages in host2)\n" ) ; } return ; } @@ -13142,7 +14405,7 @@ sub notmatch sub delete_folders_in_2_not_in_1 { - foreach my $folder (@h2_folders_not_in_1) { + foreach my $folder ( @h2_folders_not_in_1 ) { if ( defined $delete2foldersonly and eval "\$folder !~ $delete2foldersonly" ) { myprint( "Not deleting $folder because of --delete2foldersonly $delete2foldersonly\n" ) ; next ; @@ -13795,10 +15058,11 @@ sub setlogfile # proxy mode is not done yet my $remote_suffix = ( $mysync->{remote} ) ? '_remote' : q{} ; - my $suffix = ( filter_forbidden_characters( move_slash( $mysync->{user1} ) ) || q{} ) - . '_' - . ( filter_forbidden_characters( move_slash( $mysync->{user2} ) ) || q{} ) - . $remote_suffix . $abort_suffix ; + my $suffix = ( + filter_forbidden_characters( slash_to_underscore( $mysync->{user1} ) ) || q{} ) + . '_' + . ( filter_forbidden_characters( slash_to_underscore( $mysync->{user2} ) ) || q{} ) + . $remote_suffix . $abort_suffix ; $mysync->{logdir} = defined $mysync->{logdir} ? $mysync->{logdir} : $DEFAULT_LOGDIR ; @@ -13857,7 +15121,7 @@ sub logfile my $sep_dir = ( $dir ) ? '/' : q{} ; my $date_str = POSIX::strftime( '%Y_%m_%d_%H_%M_%S', localtime $time ) ; - # Because of ab tests or web access, more than one sync withing one second is possible + # Because of ab tests or web accesses, more than one sync withing one second is possible # so we add also milliseconds $date_str .= sprintf "_%03d", ($time - int( $time ) ) * 1000 ; # without rounding my $logfile = "${dir}${sep_dir}${date_str}${sep_suffix}${suffix}.txt" ; @@ -13866,18 +15130,18 @@ sub logfile -sub tests_move_slash +sub tests_slash_to_underscore { - note( 'Entering tests_move_slash()' ) ; + note( 'Entering tests_slash_to_underscore()' ) ; - is( undef, move_slash( ), 'move_slash: no parameters => undef' ) ; - is( '_', move_slash( '/' ), 'move_slash: / => _' ) ; - is( '_abc_def_', move_slash( '/abc/def/' ), 'move_slash: /abc/def/ => _abc_def_' ) ; - note( 'Leaving tests_move_slash()' ) ; + is( undef, slash_to_underscore( ), 'slash_to_underscore: no parameters => undef' ) ; + is( '_', slash_to_underscore( '/' ), 'slash_to_underscore: / => _' ) ; + is( '_abc_def_', slash_to_underscore( '/abc/def/' ), 'slash_to_underscore: /abc/def/ => _abc_def_' ) ; + note( 'Leaving tests_slash_to_underscore()' ) ; return ; } -sub move_slash +sub slash_to_underscore { my $string = shift ; @@ -13961,7 +15225,7 @@ sub tests_teelaunch is( undef, teelaunch( ), 'teelaunch: no args => undef' ) ; my $mysync = {} ; is( undef, teelaunch( $mysync ), 'teelaunch: arg empty {} => undef' ) ; - $mysync->{logfile} = '' ; + $mysync->{logfile} = q{} ; is( undef, teelaunch( $mysync ), 'teelaunch: logfile empty string => undef' ) ; $mysync->{logfile} = 'W/tmp/tests/tests_teelaunch.txt' ; isa_ok( my $tee = teelaunch( $mysync ), 'IO::Tee' , 'teelaunch: logfile W/tmp/tests/tests_teelaunch.txt' ) ; @@ -13996,6 +15260,7 @@ sub teelaunch ## no critic (InputOutput::RequireBriefOpen) open my $logfile_handle, '>', $logfile or croak( "Can not open $logfile for write: $OS_ERROR" ) ; + binmode $logfile_handle, ":encoding(UTF-8)" ; my $tee = IO::Tee->new( $logfile_handle, \*STDOUT ) ; $tee->autoflush( 1 ) ; $mysync->{logfile_handle} = $logfile_handle ; @@ -14039,27 +15304,27 @@ sub printenv sub testsexit { - my $mysync = shift ; + my $mysync = shift ; if ( ! ( $mysync->{ tests } or $mysync->{ testsdebug } or $mysync->{ testsunit } ) ) { - return ; - } + return ; + } my $test_builder = Test::More->builder ; tests( $mysync ) ; testsdebug( $mysync ) ; - testunitsession( $mysync ) ; + testunitsession( $mysync ) ; - my @summary = $test_builder->summary() ; - my @details = $test_builder->details() ; - my $nb_tests_run = scalar( @summary ) ; - my $nb_tests_expected = $test_builder->expected_tests() ; - my $nb_tests_failed = count_0s( @summary ) ; - my $tests_failed = report_failures( @details ) ; - if ( $nb_tests_failed or ( $nb_tests_run != $nb_tests_expected ) ) { - #$test_builder->reset( ) ; - myprint( "Summary of tests: failed $nb_tests_failed tests, run $nb_tests_run tests, expected to run $nb_tests_expected tests.\n", + my @summary = $test_builder->summary() ; + my @details = $test_builder->details() ; + my $nb_tests_run = scalar( @summary ) ; + my $nb_tests_expected = $test_builder->expected_tests() ; + my $nb_tests_failed = count_0s( @summary ) ; + my $tests_failed = report_failures( @details ) ; + if ( $nb_tests_failed or ( $nb_tests_run != $nb_tests_expected ) ) { + #$test_builder->reset( ) ; + myprint( "Summary of tests: failed $nb_tests_failed tests, run $nb_tests_run tests, expected to run $nb_tests_expected tests.\n", "List of failed tests:\n", $tests_failed ) ; - exit $EXIT_TESTS_FAILED ; - } + exit $EXIT_TESTS_FAILED ; + } cleanup_mess_from_tests( ) ; # Cover is larger with --tests --testslive @@ -14067,8 +15332,7 @@ sub testsexit { exit ; } - # $eeee ; - return ; + return ; } sub cleanup_mess_from_tests @@ -14203,7 +15467,7 @@ sub easyany return ; } -# From https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt +# From and for https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt sub gmail12 { my $mysync = shift ; @@ -14218,7 +15482,7 @@ sub gmail12 $mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ; $skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 0 ; $mysync->{ synclabels } = ( defined $mysync->{ synclabels } ) ? $mysync->{ synclabels } : 1 ; - $mysync->{ reynclabels } = ( defined $mysync->{ reynclabels } ) ? $mysync->{ reynclabels } : 1 ; + $mysync->{ resynclabels } = ( defined $mysync->{ resynclabels } ) ? $mysync->{ resynclabels } : 1 ; push @exclude, '\[Gmail\]$' ; push @folderlast, '[Gmail]/All Mail' ; return ; @@ -14251,10 +15515,10 @@ sub gmail2 $mysync->{ssl2} = ( defined $mysync->{ssl2} ) ? $mysync->{ssl2} : 1 ; $mysync->{maxbytespersecond} ||= 20_000 ; # should be 10_000 computed from by Gmail documentation $mysync->{maxbytesafter} ||= 1_000_000_000 ; # In fact it is documented as half: 500_000_000 - #$mysync->{ maxsize } ||= 25_000_000 ; + $mysync->{automap} = ( defined $mysync->{automap} ) ? $mysync->{automap} : 1 ; #$skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 1 ; - $mysync->{ expunge1 } = ( defined $mysync->{ expunge1 } ) ? $mysync->{ expunge1 } : 1 ; + $mysync->{ expunge1 } = ( defined $mysync->{ expunge1 } ) ? $mysync->{ expunge1 } : 1 ; $mysync->{addheader} = ( defined $mysync->{addheader} ) ? $mysync->{addheader} : 1 ; $mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ; @@ -14281,17 +15545,17 @@ sub gmail2 # From https://imapsync.lamiral.info/FAQ.d/FAQ.Exchange.txt sub office1 { - # Office 365 at host1 - my $mysync = shift ; + # Office 365 at host1 + my $mysync = shift ; - output( $mysync, q{Option --office1 is like: --host1 outlook.office365.com --ssl1 --exclude "^Files$"} . "\n" ) ; + output( $mysync, q{Option --office1 is like: --host1 outlook.office365.com --ssl1 --exclude "^Files$"} . "\n" ) ; output( $mysync, "Option --office1 (cont) : unless overrided with --host1 otherhost --nossl1 --noexclude\n" ) ; - $mysync->{host1} ||= 'outlook.office365.com' ; - $mysync->{ssl1} = ( defined $mysync->{ssl1} ) ? $mysync->{ssl1} : 1 ; + $mysync->{host1} ||= 'outlook.office365.com' ; + $mysync->{ssl1} = ( defined $mysync->{ssl1} ) ? $mysync->{ssl1} : 1 ; if ( ! $mysync->{noexclude} ) { push @exclude, '^Files$' ; } - return ; + return ; } @@ -14381,7 +15645,7 @@ sub tests_resolv # is( , resolv( ), 'resolv: => ' ) ; is( undef, resolv( ), 'resolv: no args => undef' ) ; - is( undef, resolv( '' ), 'resolv: empty string => undef' ) ; + is( undef, resolv( q{} ), 'resolv: empty string => undef' ) ; is( undef, resolv( 'hostnotexist' ), 'resolv: hostnotexist => undef' ) ; is( '127.0.0.1', resolv( '127.0.0.1' ), 'resolv: 127.0.0.1 => 127.0.0.1' ) ; is( '127.0.0.1', resolv( 'localhost' ), 'resolv: localhost => 127.0.0.1' ) ; @@ -14459,7 +15723,7 @@ sub tests_resolvrev # is( , resolvrev( ), 'resolvrev: => ' ) ; is( undef, resolvrev( ), 'resolvrev: no args => undef' ) ; - is( undef, resolvrev( '' ), 'resolvrev: empty string => undef' ) ; + is( undef, resolvrev( q{} ), 'resolvrev: empty string => undef' ) ; is( undef, resolvrev( 'hostnotexist' ), 'resolvrev: hostnotexist => undef' ) ; is( 'localhost', resolvrev( '127.0.0.1' ), 'resolvrev: 127.0.0.1 => localhost' ) ; is( 'localhost', resolvrev( 'localhost' ), 'resolvrev: localhost => localhost' ) ; @@ -14698,7 +15962,7 @@ sub sslcheck } -sub testslive +sub testslive_init { my $mysync = shift ; $mysync->{host1} ||= 'test1.lamiral.info' ; @@ -14710,7 +15974,7 @@ sub testslive return ; } -sub testslive6 +sub testslive6_init { my $mysync = shift ; $mysync->{host1} ||= 'ks2ipv6.lamiral.info' ; @@ -14773,7 +16037,7 @@ sub split_around_equal -sub tests_sig_install +sub tests_sig_install { note( 'Entering tests_sig_install()' ) ; @@ -14794,7 +16058,7 @@ sub tests_sig_install # Assign USR1 to call sub tototo # Surely a better value than undef should be returned when doing real signal stuff is( undef, sig_install( $mysync, 'tototo', 'USR1' ), 'sig_install: USR1 tototo' ) ; - + is( 1, kill( 'USR1', $PROCESS_ID ), 'sig_install: kill USR1 myself 1' ) ; is( 1, $mysync->{ tototo_calls }, 'sig_install: tototo call nb 1' ) ; @@ -14829,7 +16093,7 @@ sub tests_sig_install # -sub sig_install +sub sig_install { my $mysync = shift ; if ( ! $mysync ) { return ; } @@ -14837,9 +16101,9 @@ sub sig_install if ( ! $mysubname ) { return ; } if ( ! @ARG ) { return ; } - + my @signals = @ARG ; - + my $mysub = \&$mysubname ; #$mysync->{ debugsig } = 1 ; $mysync->{ debugsig } and myprint( "In sig_install with sub $mysubname and signal @ARG\n" ) ; @@ -15191,7 +16455,7 @@ sub tests_get_options_cgi_context is( undef, get_options( $mysync ), 'get_options cgi context: no CGI module => undef' ) ; require CGI ; - CGI->import( qw( -no_debug ) ) ; + CGI->import( qw( -no_debug -utf8 ) ) ; is( undef, get_options( $mysync ), 'get_options cgi context: no CGI param => undef' ) ; # Testing boolean @@ -15282,10 +16546,10 @@ sub get_options_cgi $mysync, \@arguments, 'abort' => \$mysync->{abort}, - 'host1=s' => \$mysync->{host1}, - 'host2=s' => \$mysync->{host2}, - 'user1=s' => \$mysync->{user1}, - 'user2=s' => \$mysync->{user2}, + 'host1=s' => \$mysync->{ host1 }, + 'host2=s' => \$mysync->{ host2 }, + 'user1=s' => \$mysync->{ user1 }, + 'user2=s' => \$mysync->{ user2 }, 'password1=s' => \$mysync->{password1}, 'password2=s' => \$mysync->{password2}, 'dry!' => \$mysync->{dry}, @@ -15373,14 +16637,14 @@ sub get_options_cmd 'debuglabels!' => \$mysync->{debuglabels}, 'simulong=i' => \$mysync->{simulong}, 'abort' => \$mysync->{abort}, - 'host1=s' => \$mysync->{host1}, - 'host2=s' => \$mysync->{host2}, + 'host1=s' => \$mysync->{ host1 }, + 'host2=s' => \$mysync->{ host2 }, 'port1=i' => \$mysync->{port1}, 'port2=i' => \$mysync->{port2}, 'inet4|ipv4' => \$mysync->{inet4}, 'inet6|ipv6' => \$mysync->{inet6}, - 'user1=s' => \$mysync->{user1}, - 'user2=s' => \$mysync->{user2}, + 'user1=s' => \$mysync->{ user1 }, + 'user2=s' => \$mysync->{ user2 }, 'gmail1' => \$mysync->{gmail1}, 'gmail2' => \$mysync->{gmail2}, 'office1' => \$mysync->{office1}, @@ -15416,7 +16680,7 @@ sub get_options_cmd 'fixInboxINBOX!' => \$fixInboxINBOX, 'regextrans2=s@' => \$mysync->{ regextrans2 }, 'mixfolders!' => \$mixfolders, - 'skipemptyfolders!' => \$skipemptyfolders, + 'skipemptyfolders!' => \$mysync->{ skipemptyfolders }, 'regexmess=s' => \@regexmess, 'noregexmess' => \$mysync->{noregexmess}, 'skipmess=s' => \@skipmess, @@ -15447,10 +16711,10 @@ sub get_options_cmd 'maxage=f' => \$maxage, 'minage=f' => \$minage, 'search=s' => \$search, - 'search1=s' => \$search1, - 'search2=s' => \$search2, - 'foldersizes!' => \$foldersizes, - 'foldersizesatend!' => \$foldersizesatend, + 'search1=s' => \$mysync->{ search1 }, + 'search2=s' => \$mysync->{ search2 }, + 'foldersizes!' => \$mysync->{ foldersizes }, + 'foldersizesatend!' => \$mysync->{ foldersizesatend }, 'dry!' => \$mysync->{dry}, 'expunge1|expunge!' => \$mysync->{ expunge1 }, 'expunge2!' => \$mysync->{ expunge2 }, @@ -15785,8 +17049,15 @@ sub testsdebug } note( 'Entering testsdebug()' ) ; - ok( ( ( not -d 'W/tmp/tests' ) or rmtree( 'W/tmp/tests/' ) ), 'testsdebug: rmtree W/tmp/tests' ) ; - tests_check_binary_embed_all_dyn_libs( ) ; + #ok( ( ( not -d 'W/tmp/tests' ) or rmtree( 'W/tmp/tests/' ) ), 'testsdebug: rmtree W/tmp/tests' ) ; + #tests_check_binary_embed_all_dyn_libs( ) ; + #tests_killpid_by_parent( ) ; + #tests_killpid_by_brother( ) ; + #tests_kill_zero( ) ; + #tests_connect_socket( ) ; + tests_probe_imapssl( ) ; + #tests_always_fail( ) ; + note( 'Leaving testsdebug()' ) ; done_testing( ) ; } @@ -15851,6 +17122,7 @@ sub tests tests_sleep_max_bytes( ) ; tests_logfile( ) ; tests_setlogfile( ) ; + tests_jux_utf8_old( ) ; tests_jux_utf8( ) ; tests_pipemess( ) ; tests_jux_utf8_list( ) ; @@ -15899,16 +17171,13 @@ sub tests tests_umask_str( ) ; tests_set_umask( ) ; tests_createhashfileifneeded( ) ; - tests_move_slash( ) ; + tests_slash_to_underscore( ) ; tests_testsunit( ) ; tests_count_0s( ) ; tests_report_failures( ) ; tests_min( ) ; - #tests_resolv( ) ; + #tests_connect_socket( ) ; #tests_resolvrev( ) ; - tests_connect_socket( ) ; - tests_probe_imapssl( ) ; - tests_mailimapclient_connect( ) ; tests_usage( ) ; tests_version_from_rcs( ) ; tests_backslash_caret( ) ; @@ -15955,8 +17224,24 @@ sub tests tests_secondline( ) ; tests_tail( ) ; tests_truncmess( ) ; + tests_eta( ) ; + tests_timesince( ) ; + tests_timenext( ) ; + tests_foldersize( ) ; + tests_imapsync_context( ) ; + tests_abort( ) ; + tests_probe_imapssl( ) ; + tests_mailimapclient_connect( ) ; + #tests_resolv( ) ; + + # Those three are for later use, when webserver will be inside imapsync + # or will be deleted them if I abandon the project. + #tests_killpid_by_parent( ) ; + #tests_killpid_by_brother( ) ; + #tests_kill_zero( ) ; + #tests_always_fail( ) ; - done_testing( 1454 ) ; + done_testing( 1496 ) ; note( 'Leaving tests()' ) ; } return ; @@ -15966,7 +17251,7 @@ sub tests_template { note( 'Entering tests_template()' ) ; - is( undef, undef, 'template: undef is undef' ) ; + is( undef, undef, 'template: no args => undef' ) ; is_deeply( {}, {}, 'template: a hash is a hash' ) ; is_deeply( [], [], 'template: an array is an array' ) ; note( 'Leaving tests_template()' ) ; @@ -15974,5 +17259,3 @@ sub tests_template } - - diff --git a/imapsync-1.921 b/imapsync-1.945 similarity index 79% rename from imapsync-1.921 rename to imapsync-1.945 index 4e72093..5b408bc 100755 --- a/imapsync-1.921 +++ b/imapsync-1.945 @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# $Id: imapsync,v 1.921 2019/02/18 10:21:03 gilles Exp gilles $ +# $Id: imapsync,v 1.945 2019/06/26 19:30:56 gilles Exp gilles $ # structure # pod documentation # use pragmas @@ -25,7 +25,7 @@ and without duplicates. =head1 VERSION -This documentation refers to Imapsync $Revision: 1.921 $ +This documentation refers to Imapsync $Revision: 1.945 $ =head1 USAGE @@ -46,15 +46,21 @@ one another. Imapsync command is a tool allowing incremental and recursive imap transfers from one mailbox to another. +If you don't understand the previous sentence, it's normal, +it's pedantic computer oriented jargon. -By default all folders are transferred, recursively, meaning +All folders are transferred, recursively, meaning the whole folder hierarchy is taken, all messages in them, and all messages flags (\Seen \Answered \Flagged etc.) are synced too. Imapsync reduces the amount of data transferred by not transferring -a given message if it resides already on both sides. +a given message if it already resides on the destination side. +Messages that are on the destination side but not on the +source side stay as they are (see the --delete2 +option to have a strict sync). +How imapsync knows a message is already on both sides? Same specific headers and the transfer is done only once. By default, the identification headers are "Message-Id:" and "Received:" lines @@ -63,29 +69,40 @@ but this choice can be changed with the --useheader option. All flags are preserved, unread messages will stay unread, read ones will stay read, deleted will stay deleted. -You can stop the transfer at any time and restart it later, +You can abort the transfer at any time and restart it later, imapsync works well with bad connections and interruptions, -by design. +by design. On a terminal hit Ctr-c twice within two seconds +in order to abort the program. Hit Ctr-c just once makes +imapsync reconnect to both imap servers. -You can decide to delete the messages from the source mailbox -after a successful transfer, it can be a good feature when migrating -live mailboxes since messages will be only on one side. - -In that case, use the --delete1 option. Option --delete1 implies -also option --expunge1 so all messages marked deleted on host1 -will be really deleted. - -You can also decide to remove empty folders once all of their -messages have been transferred. Add --delete1emptyfolders to -obtain this behavior. - -A different scenario is synchronizing a mailbox B from another mailbox A -in case you just want to keep a "live" copy of A in B. +A classical scenario is synchronizing a mailbox B from another mailbox A +in case you just want to keep a strict copy of A in B. Strict meaning +all messages in A will be in B but no more. For this, option --delete2 has to be used, it deletes messages in host2 folder B that are not in host1 folder A. If you also need to destroy host2 folders that are not in host1 then use --delete2folders. See also ---delete2foldersonly and --delete2foldersbutnot. +--delete2foldersonly and --delete2foldersbutnot to set up exceptions +on folders to destroy (INBOX will never be destroy, it's a mandatory +folder in IMAP). + +A different scenario is to delete the messages from the source mailbox +after a successful transfer, it can be a good feature when migrating +mailboxes since messages will be only on one side. The source account +will only have messages that are not on the destination yet, ie, +messages that arrived after a sync or that failed to be copied. + +In that case, use the --delete1 option. Option --delete1 implies also +option --expunge1 so all messages marked deleted on host1 will be really +deleted. In IMAP protocol deleting a message does not really delete it, +it marks it with the flag \Deleted, allowing an undelete. Expunging +a folder removes, definitively, all the messages marked as \Deleted +in this folder. + +You can also decide to remove empty folders once all of their messages +have been transferred. Add --delete1emptyfolders to obtain this +behavior. + Imapsync is not adequate for maintaining two active imap accounts in synchronization when the user plays independently on both sides. @@ -97,8 +114,8 @@ Michael R. Elkins) for a 2 ways synchronization. usage: imapsync [options] -Mandatory options are the six values, three on each sides, -needed to log in into the IMAP servers, ie, +Standard options are the six values forming the credentials, +three on each sides, needed to log in into the IMAP servers, ie, a host, a username, and a password, two times. Conventions used: @@ -114,28 +131,30 @@ Conventions used: =head2 OPTIONS/credentials - --host1 str : Source or "from" imap server. Mandatory. + --host1 str : Source or "from" imap server. --port1 int : Port to connect on host1. - Optional since default port is 143 or 993 if --ssl1 - --user1 str : User to login on host1. Mandatory. + Optional since default ports are the + well known ports 143 or 993. + --user1 str : User to login on host1. --password1 str : Password for the user1. - --host2 str : "destination" imap server. Mandatory. - --port2 int : Port to connect on host2. - Optional since default port is 143 or 993 if --ssl2 - --user2 str : User to login on host2. Mandatory. + --host2 str : "destination" imap server. + --port2 int : Port to connect on host2. Optional + --user2 str : User to login on host2. --password2 str : Password for the user2. --showpasswords : Shows passwords on output instead of "MASKED". - Useful to restart a complete run by just reading the log, - or to debug passwords. It's not a secure practice. + Useful to restart a complete run by just reading + the command line used in the log, + or to debug passwords. + It's not a secure practice. --passfile1 str : Password file for the user1. It must contain the - password on the first line. This option avoids to show + password on the first line. This option avoids showing the password on the command line like --password1 does. - --passfile2 str : Password file for the user2. Contains the password. + --passfile2 str : Password file for the user2. -You can also pass the passwords in the environment variables +You can also pass the passwords in the environment variables IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 =head2 OPTIONS/encryption @@ -202,8 +221,9 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --nomixfolders : Do not merge folders when host1 is case-sensitive while host2 is not (like Exchange). Only the first - similar folder is synced (ex: with Sent SENT sent - on host1 only Sent will be synced to host2). + similar folder is synced (example: with folders + "Sent", "SENT" and "sent" + on host1 only "Sent" will be synced to host2). --skipemptyfolders : Empty host1 folders are not created on host2. @@ -224,9 +244,20 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --subfolder2 str : Syncs the whole host1 folders hierarchy under the host2 folder named str. - (It does it internally by adding two + It does it internally by adding three --regextrans2 options before all others. - Add --debug to see what's really going on.) + Add --debug to see what's really going on. + + --subfolder1 str : Syncs the host1 folders hierarchy under str + to the root hierarchy of host2. + It's the couterpart of a sync done by --subfolder2 + when doing it in the reverse order. + Backup/Restore scenario: + Use --subfolder2 str for a backup to the folder str + on host2. Then use --subfolder1 str for restoring + from the folder str, after inverting + host1/host2 user1/user2 values. + --subscribed : Transfers subscribed folders. --subscribe : Subscribe to the folders transferred on the @@ -235,21 +266,29 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 host2 even if they are not subscribed on host1. --prefix1 str : Remove prefix str to all destination folders, - usually INBOX. or INBOX/ or an empty string "". + usually "INBOX." or "INBOX/" or an empty string "". imapsync guesses the prefix if host1 imap server - does not have NAMESPACE capability. This option + does not have NAMESPACE capability. So this option should not be used, most of the time. --prefix2 str : Add prefix to all host2 folders. See --prefix1 - --sep1 str : Host1 separator in case NAMESPACE is not supported. - --sep2 str : Host2 separator in case NAMESPACE is not supported. + + --sep1 str : Host1 separator. This option should not be used, + most of the time. + Imapsync gets the separator from the server itself, + by using NAMESPACE, or it tries to guess it + from the folders listing (it counts + characters / . \\ \ in folder names and choose the + more frequent, or finally / if nothing is found. + --sep2 str : Host2 separator. --regextrans2 reg : Apply the whole regex to each destination folders. --regextrans2 reg : and this one. etc. When you play with the --regextrans2 option, first add also the safe options --dry --justfolders Then, when happy, remove --dry, remove --justfolders. - Have in mind that --regextrans2 is applied after prefix - and separator inversion. For examples see + Have in mind that --regextrans2 is applied after + the automatic prefix and separator inversion. + For examples see: https://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt =head2 OPTIONS/folders sizes @@ -292,7 +331,14 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --pipemess cmd : Apply this cmd command to each message content before the copy. - --pipemess cmd : and this one, etc. + --pipemess cmd : and this one, etc. + With several --pipemess, the output of each cmd + command (STDOUT) is given to the input (STDIN) + of the next command. + For example, + --pipemess cmd1 --pipemess cmd2 --pipemess cmd3 + is like a Unix pipe: + "cat message | cmd1 | cmd2 | cmd3" --disarmreadreceipts : Disarms read receipts (host2 Exchange issue) @@ -303,6 +349,9 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 =head2 OPTIONS/flags + If you encounter flag problems see also: + https://imapsync.lamiral.info/FAQ.d/FAQ.Flags.txt + --regexflag reg : Apply the whole regex to each flags list. Example: 's/"Junk"//g' # to remove "Junk" flag. --regexflag reg : then this one, etc. @@ -321,19 +370,26 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 \Deleted, then messages are really deleted with an EXPUNGE IMAP command. If expunging after each message slows down too much the sync then use - --noexpungeaftereach to speed up. + --noexpungeaftereach to speed up, expunging will then be + done only twice per folder, one at the beginning and + one at the end of a folder sync. + --expunge1 : Expunge messages on host1 just before syncing a folder. Expunge is done per folder. Expunge aims is to really delete messages marked deleted. An expunge is also done after each message copied - if option --delete1 is set. + if option --delete1 is set (unless --noexpungeaftereach). + --noexpunge1 : Do not expunge messages on host1. + --delete1emptyfolders : Deletes empty folders on host1, INBOX excepted. Useful with --delete1 since what remains on host1 is only what failed to be synced. --delete2 : Delete messages in host2 that are not in host1 server. Useful for backup or pre-sync. + --delete2 implies --uidexpunge2 + --delete2duplicates : Delete messages in host2 that are duplicates. Works only without --useuid since duplicates are detected with an header part of each message. @@ -341,32 +397,36 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --delete2folders : Delete folders in host2 that are not in host1 server. For safety, first try it like this (it is safe): --delete2folders --dry --justfolders --nofoldersizes + --delete2foldersonly reg : Deleted only folders matching regex. Example: --delete2foldersonly "/^Junk$|^INBOX.Junk$/" + --delete2foldersbutnot reg : Do not delete folders matching regex. Example: --delete2foldersbutnot "/Tasks$|Contacts$|Foo$/" - --expunge2 : Expunge messages on host2 after messages transfer. - --uidexpunge2 : uidexpunge messages on the host2 account - that are not on the host1 account, requires --delete2 + --noexpunge2 : Do not expunge messages on host2. + --nouidexpunge2 : Do not uidexpunge messages on the host2 account + that are not on the host1 account. =head2 OPTIONS/dates + If you encounter problems with dates, see also: + https://imapsync.lamiral.info/FAQ.d/FAQ.Dates.txt + --syncinternaldates : Sets the internal dates on host2 same as host1. Turned on by default. Internal date is the date - a message arrived on a host (mtime). + a message arrived on a host (Unix mtime). --idatefromheader : Sets the internal dates on host2 same as the - "Date:" headers. - If you encounter problems with dates see also - https://imapsync.lamiral.info/FAQ.d/FAQ.Dates.txt + ones in "Date:" headers. + =head2 OPTIONS/message selection --maxsize int : Skip messages larger (or equal) than int bytes --minsize int : Skip messages smaller (or equal) than int bytes - --maxage int : Skip messages older than int days. + --maxage int : Skip messages older than int days. final stats (skipped) don't count older messages see also --minage --minage int : Skip messages newer than int days. @@ -397,7 +457,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --usecache : Use cache to speed up the sync. --nousecache : Do not use cache. Caveat: --useuid --nousecache creates duplicates on multiple runs. - --useuid : Use UIDs instead of headers as a criterium to recognize + --useuid : Use UIDs instead of headers as a criterion to recognize messages. Option --usecache is then implied unless --nousecache is used. @@ -406,8 +466,11 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --syncacls : Synchronizes acls (Access Control Lists). --nosyncacls : Does not synchronize acls. This is the default. - Acls in IMAP are not standardized, be careful. - --addheader : When a message has no headers to be identified, + Acls in IMAP are not standardized, be careful + since one acl code on one side may signify something + else on the other one. + + --addheader : When a message has no headers to be identified, --addheader adds a "Message-Id" header, like "Message-Id: 12345@imapsync", where 12345 is the imap UID of the message on the host1 folder. @@ -448,8 +511,6 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --domino2 : sets options from FAQ.Domino.txt, account2 part - - =head2 OPTIONS/behavior --maxmessagespersecond int : limits the number of messages transferred per second. @@ -464,16 +525,25 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 --abort : terminates a previous call still running. It uses the pidfile to know what process to abort. - --exitwhenover int : Stop syncing when total bytes transferred reached. + --exitwhenover int : Stop syncing and exits when int total bytes + transferred is reached. --version : Print only software version. - --noreleasecheck : Do not check for new imapsync release (a http request). - --releasecheck : Check for new imapsync release (a http request). + --noreleasecheck : Do not check for new imapsync release + --releasecheck : Check for new imapsync release. + it's an http request to + http://imapsync.lamiral.info/prj/imapsync/VERSION + --noid : Do not send/receive ID command to imap servers. + --justconnect : Just connect to both servers and print useful information. Need only --host1 and --host2 options. + Obsolete since "imapsync --host1 imaphost" alone + implies --justconnect + --justlogin : Just login to both host1 and host2 with users credentials, then exit. + --justfolders : Do only things about folders (ignore messages). --help : print this help. @@ -498,15 +568,17 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2 =head1 SECURITY You can use --passfile1 instead of --password1 to give the -password since it is safer. With --password1 option, any user -on your host can see the password by using the 'ps auxwwww' -command. Using a variable (like $PASSWORD1) is also +password since it is safer. With --password1 option, on Linux, +any user on your host can see the password by using the 'ps auxwwww' +command. Using a variable (like IMAPSYNC_PASSWORD1) is also dangerous because of the 'ps auxwwwwe' command. So, saving the password in a well protected file (600 or rw-------) is the best solution. Imapsync activates ssl or tls encryption by default, if possible. + What detailed behavior is under this "if possible"? + Imapsync activates ssl if the well known port imaps port (993) is open on the imap servers. If the imaps port is closed then it open a normal (clear) connection on port 143 but it looks for TLS support @@ -526,7 +598,28 @@ or at https://imapsync.lamiral.info/FAQ.d/FAQ.Security.txt Imapsync will exit with a 0 status (return code) if everything went good. Otherwise, it exits with a non-zero status. +Here is the list of the exit code values (an integer between 0 and 255), +the names reflects their meaning: +=for comment +egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _' + + + EX_OK => 0 ; #/* successful termination */ + EX_USAGE => 64 ; #/* command line usage error */ + EX_NOINPUT => 66 ; #/* cannot open input */ + EX_UNAVAILABLE => 69 ; #/* service unavailable */ + EX_SOFTWARE => 70 ; #/* internal software error */ + EXIT_CATCH_ALL => 1 ; # Any other error + EXIT_BY_SIGNAL => 6 ; # Should be 128+n where n is the sig_num + EXIT_PID_FILE_ERROR => 8 ; + EXIT_CONNECTION_FAILURE => 10 ; + EXIT_TLS_FAILURE => 12 ; + EXIT_AUTHENTICATION_FAILURE => 16 ; + EXIT_SUBFOLDER1_NO_EXISTS => 21 ; + EXIT_WITH_ERRORS => 111 ; + EXIT_WITH_ERRORS_MAX => 112 ; + EXIT_TESTS_FAILED => 254 ; # Like Test::More API =head1 LICENSE AND COPYRIGHT @@ -542,13 +635,14 @@ In case it is not long enough, I repeat: "No limits to do anything with this work and this license." -https://imapsync.lamiral.info/LICENSE +Look at https://imapsync.lamiral.info/LICENSE =head1 AUTHOR Gilles LAMIRAL -Feedback good or bad is very often welcome. +Good feedback good is always welcome. +Bad feedback is very often welcome. Gilles LAMIRAL earns his living by writing, installing, configuring and teaching free, open and often gratis @@ -567,19 +661,6 @@ See https://imapsync.lamiral.info/S/imapservers.shtml =head1 HUGE MIGRATION -Pay special attention to options ---subscribed ---subscribe ---delete1 ---delete1emptyfolders ---delete2 ---delete2folders ---maxage ---minage ---maxsize ---useuid ---usecache - If you have many mailboxes to migrate think about a little shell program. Write a file called file.txt (for example) containing users and passwords. @@ -615,10 +696,10 @@ https://imapsync.lamiral.info/examples/ =head1 INSTALL - Imapsync works under any Unix with perl. + Imapsync works under any Unix with Perl. Imapsync works under most Windows (2000, XP, Vista, Seven, Eight, Ten - and all Server releases 2000, 2003, 2008 and R2, 2012 and R2) + and all Server releases 2000, 2003, 2008 and R2, 2012 and R2, 2016) as a standalone binary software called imapsync.exe, usually launched from a batch file in order to avoid always typing the options. @@ -659,45 +740,65 @@ Feel free to hack imapsync as the NOLIMIT license permits it. See also https://imapsync.lamiral.info/S/external.shtml for a better up to date list. - imap_tools : https://github.com/andrewnimmo/rick-sanders-imap-tools - offlineimap : https://github.com/nicolas33/offlineimap - Doveadm-Sync : http://wiki2.dovecot.org/Tools/Doveadm/Sync - ( Dovecot sync tool ) - mbsync : http://isync.sourceforge.net/ - mailsync : http://mailsync.sourceforge.net/ - mailutil : http://www.washington.edu/imap/ - part of the UW IMAP tookit. - imaprepl : http://www.bl0rg.net/software/ - http://freecode.com/projects/imap-repl/ - imapcopy : http://www.ardiehl.de/imapcopy/ - migrationtool : http://sourceforge.net/projects/migrationtool/ - imapmigrate : http://sourceforge.net/projects/cyrus-utils/ - wonko_imapsync: http://wonko.com/article/554 - see also file W/tools/wonko_ruby_imapsync - exchange-away : http://exchange-away.sourceforge.net/ - pop2imap : http://www.linux-france.org/prj/pop2imap/ +Last updated and verified on Thu Apr 11, 2019. - -Feedback (good or bad) will often be welcome. + imapsync : https://github.com/imapsync/imapsync + (this is an imapsync copy, sometimes delayed, + with --noreleasecheck by default since release 1.592, 2014/05/22) + imap_tools : https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/ + The imap_tools code is now at + https://github.com/andrewnimmo/rick-sanders-imap-tools + imaputils : https://github.com/mtsatsenko/imaputils (very old imap_tools fork) + Doveadm-Sync : https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool ) + davmail : http://davmail.sourceforge.net/ + offlineimap : http://offlineimap.org/ + mbsync : http://isync.sourceforge.net/ + mailsync : http://mailsync.sourceforge.net/ + mailutil : http://www.washington.edu/imap/ part of the UW IMAP tookit. + imaprepl : https://bl0rg.net/software/ http://freecode.com/projects/imap-repl/ + imapcopy (Pascal): http://www.ardiehl.de/imapcopy/ + imapcopy (Java) : https://code.google.com/archive/p/imapcopy/ + imapsize : http://www.broobles.com/imapsize/ + migrationtool : http://sourceforge.net/projects/migrationtool/ + imapmigrate : http://sourceforge.net/projects/cyrus-utils/ + larch : https://github.com/rgrove/larch (derived from wonko_imapsync, good at Gmail) + wonko_imapsync : http://wonko.com/article/554 (superseded by larch) + pop2imap : http://www.linux-france.org/prj/pop2imap/ (I wrote that too) + exchange-away : http://exchange-away.sourceforge.net/ + SyncBackPro : http://www.2brightsparks.com/syncback/sbpro.html + ImapSyncClient : https://github.com/ridaamirini/ImapSyncClient + MailStore : https://www.mailstore.com/en/products/mailstore-home/ + mnIMAPSync : https://github.com/manusa/mnIMAPSync + imap-upload : http://imap-upload.sourceforge.net/ + (a tool for uploading a local mbox file to IMAP4 server) =head1 HISTORY -I wrote imapsync because an enterprise (basystemes) paid me to install -a new imap server without losing huge old mailboxes located in a far -away remote imap server, accessible by a low-bandwidth often broken link. -The tool imapcp (written in python) could not help me because I had to verify -every mailbox was well transferred, and then delete it after a good -transfer. Imapsync started its life as a patch of the copy_folder.pl +I initially wrote imapsync in July 2001 because an enterprise, +basystemes, paid me to install a new imap server +without losing huge old mailboxes located in a far +away remote imap server, accessible by an +often broken low-bandwidth ISDN link. + +I had to verify every mailbox was well transferred, all folders, all messages, +without wasting bandwidth or creating duplicates upon resyncs. The design was +made with the beautiful rsync command in mind. + +Imapsync started its life as a patch of the copy_folder.pl script. The script copy_folder.pl comes from the Mail-IMAPClient-2.1.3 perl module tarball source (more precisely in the examples/ directory of the -Mail-IMAPClient tarball). So many happened since then that I wonder -if it remains any lines of the original copy_folder.pl in imapsync source code. +Mail-IMAPClient tarball). + +So many happened since then that I wonder +if it remains any lines of the original +copy_folder.pl in imapsync source code. =cut # use pragmas +# use strict ; use warnings ; @@ -735,6 +836,8 @@ use Cwd ; use Readonly ; use Sys::MemInfo ; use Regexp::Common ; +use Text::ParseWords; +use File::Tail ; local $OUTPUT_AUTOFLUSH = 1 ; @@ -742,6 +845,10 @@ local $OUTPUT_AUTOFLUSH = 1 ; # Let us do like sysexits.h # /usr/include/sysexits.h +# and https://www.tldp.org/LDP/abs/html/exitcodes.html + +# Should avoid 2 126 127 128..128+64=192 255 +# Should use 0 1 3..125 193..254 Readonly my $EX_OK => 0 ; #/* successful termination */ Readonly my $EX_USAGE => 64 ; #/* command line usage error */ @@ -761,21 +868,49 @@ Readonly my $EX_SOFTWARE => 70 ; #/* internal software error */ #Readonly my $EX_CONFIG => 78 ; #/* configuration error */ # Mine -Readonly my $EXIT_BY_SIGNAL => 6 ; +Readonly my $EXIT_CATCH_ALL => 1 ; # Any other error +Readonly my $EXIT_BY_SIGNAL => 6 ; # Should be 128+n where n is the sig_num Readonly my $EXIT_PID_FILE_ERROR => 8 ; - +Readonly my $EXIT_CONNECTION_FAILURE => 10 ; +Readonly my $EXIT_TLS_FAILURE => 12 ; +Readonly my $EXIT_AUTHENTICATION_FAILURE => 16 ; +Readonly my $EXIT_SUBFOLDER1_NO_EXISTS => 21 ; Readonly my $EXIT_WITH_ERRORS => 111 ; Readonly my $EXIT_WITH_ERRORS_MAX => 112 ; -Readonly my $EXIT_UNKNOWN => 126 ; + Readonly my $EXIT_TESTS_FAILED => 254 ; # Like Test::More API + +Readonly my %EXIT_TXT => ( + $EX_OK => 'EX_OK: successful termination', + $EX_USAGE => 'EX_USAGE: command line usage error', + $EX_NOINPUT => 'EX_NOINPUT: cannot open input', + $EX_UNAVAILABLE => 'EX_UNAVAILABLE: service unavailable', + $EX_SOFTWARE => 'EX_SOFTWARE: internal software error', + + $EXIT_CATCH_ALL => 'EXIT_CATCH_ALL', + $EXIT_BY_SIGNAL => 'EXIT_BY_SIGNAL', + $EXIT_PID_FILE_ERROR => 'EXIT_PID_FILE_ERROR' , + $EXIT_CONNECTION_FAILURE => 'EXIT_CONNECTION_FAILURE', + $EXIT_TLS_FAILURE => 'EXIT_TLS_FAILURE', + $EXIT_AUTHENTICATION_FAILURE => 'EXIT_AUTHENTICATION_FAILURE', + $EXIT_SUBFOLDER1_NO_EXISTS => 'EXIT_SUBFOLDER1_NO_EXISTS', + $EXIT_WITH_ERRORS => 'EXIT_WITH_ERRORS', + $EXIT_WITH_ERRORS_MAX => 'EXIT_WITH_ERRORS_MAX', + $EXIT_TESTS_FAILED => 'EXIT_TESTS_FAILED', +) ; + + + + Readonly my $DEFAULT_LOGDIR => 'LOG_imapsync' ; Readonly my $ERRORS_MAX => 50 ; # exit after 50 errors. Readonly my $ERRORS_MAX_CGI => 20 ; # exit after 20 errors in CGI context. + Readonly my $INTERVAL_TO_EXIT => 2 ; # interval max to exit instead of reconnect Readonly my $SPLIT => 100 ; # By default, 100 at a time, not more. @@ -839,22 +974,27 @@ Readonly my $STR_use_releasecheck => q{Check if a new imapsync release is availa Readonly my $GMAIL_MAXSIZE => 35_651_584 ; + +# if ( 'MSWin32' eq $OSNAME ) +# if ( 'darwin' eq $OSNAME ) +# if ( 'linux' eq $OSNAME ) + + + # global variables # Currently working to finish with only $sync # Not finished yet... my( $sync, - $debug, $debugimap, $debugimap1, $debugimap2, $debugcontent, $debugflags, + $debugimap, $debugimap1, $debugimap2, $debugcontent, $debugflags, $debuglist, $debugdev, $debugmaxlinelength, $debugcgi, $domain1, $domain2, @include, @exclude, @folderrec, @folderfirst, @folderlast, $prefix1, $prefix2, - $subfolder2, - @regextrans2, @regexmess, @regexflag, @skipmess, @pipemess, $pipemesscheck, + @regexmess, @regexflag, @skipmess, @pipemess, $pipemesscheck, $flagscase, $filterflags, $syncflagsaftercopy, - $sep1, $sep2, $syncinternaldates, $idatefromheader, $syncacls, @@ -865,7 +1005,6 @@ my( $skipsize, $allowsizemismatch, $foldersizes, $foldersizesatend, $buffersize, - $justfoldersizes, $authmd5, $authmd51, $authmd52, $subscribed, $subscribe, $subscribeall, $help, @@ -900,10 +1039,7 @@ my( $delete2folders, $delete2foldersonly, $delete2foldersbutnot, $usecache, $debugcache, $cacheaftercopy, $wholeheaderifneeded, %h1_msgs_copy_by_uid, $useuid, $h2_uidguess, - %h1, %h2, $checkmessageexists, - - $fixslash2, $messageidnodomain, $fixInboxINBOX, $maxlinelength, $maxlinelengthcmd, @@ -915,7 +1051,7 @@ my( $disarmreadreceipts, $mixfolders, $skipemptyfolders, $fetch_hash_set, -); +) ; # main program @@ -926,7 +1062,7 @@ my( $sync->{timestart} = time ; # Is a float because of use Time::HiRres -$sync->{rcs} = q{$Id: imapsync,v 1.921 2019/02/18 10:21:03 gilles Exp gilles $} ; +$sync->{rcs} = q{$Id: imapsync,v 1.945 2019/06/26 19:30:56 gilles Exp gilles $} ; $sync->{ memory_consumption_at_start } = memory_consumption( ) || 0 ; @@ -942,11 +1078,13 @@ $sync->{loadavg} = join( q{ }, $loadavg[ 0 ] ) $sync->{ total_bytes_transferred } = 0 ; -$sync->{ total_bytes_skipped } = 0; -$sync->{ nb_msg_transferred } = 0; -$sync->{ nb_msg_skipped } = $nb_msg_skipped_dry_mode = 0; -$sync->{ h1_nb_msg_deleted } = $h2_nb_msg_deleted = 0; -$h1_nb_msg_duplicate = $h2_nb_msg_duplicate = 0; +$sync->{ total_bytes_skipped } = 0 ; +$sync->{ nb_msg_transferred } = 0 ; +$sync->{ nb_msg_skipped } = $nb_msg_skipped_dry_mode = 0 ; +$sync->{ h1_nb_msg_deleted } = 0 ; +$h2_nb_msg_deleted = 0 ; +$h1_nb_msg_duplicate = 0 ; +$h2_nb_msg_duplicate = 0 ; $sync->{ h1_nb_msg_noheader } = 0 ; $h2_nb_msg_noheader = 0 ; @@ -987,15 +1125,14 @@ cgibegin( $sync ) ; # In cgi context, printing must start by the header so we delay other prints by using output() storage my $options_good = get_options( $sync, @ARGV ) ; -# Is it The first myprint? +# Is it the first myprint? docker_context( $sync ) ; cgibuildheader( $sync ) ; myprint( output( $sync ) ) ; output_reset_with( $sync ) ; -# Can break here if load is too heavy -cgiload( $sync ) ; +# Old place for cgiload( $sync ) ; # don't go on if options are not all known. if ( ! defined $options_good ) { exit $EX_USAGE ; } @@ -1024,9 +1161,12 @@ after_get_options( $sync, $options_good ) ; # Under CGI environment, fix caveat emptor potential issues cgisetcontext( $sync ) ; - +# --gmail --gmail --exchange --office etc. easyany( $sync ) ; +$sync->{ sanitize } = defined $sync->{ sanitize } ? $sync->{ sanitize } : 1 ; +sanitize( $sync ) ; + $sync->{ tmpdir } ||= File::Spec->tmpdir( ) ; # Unit tests @@ -1037,6 +1177,7 @@ testslive( $sync ) if ( $sync->{testslive} ) ; testslive6( $sync ) if ( $sync->{testslive6} ) ; # + $sync->{pidfile} = defined $sync->{pidfile} ? $sync->{pidfile} : $sync->{ tmpdir } . '/imapsync.pid' ; $sync->{pidfilelocking} = defined $sync->{pidfilelocking} ? $sync->{pidfilelocking} : 0 ; @@ -1049,11 +1190,11 @@ $sync->{pidfilelocking} = defined $sync->{pidfilelocking} ? $sync->{pidfileloc @{ $sync->{ sigignore } } = ( defined( $sync->{ sigignore } ) ) ? @{ $sync->{ sigignore } } : ( ) ; local %SIG = %SIG ; -sig_install( $sync, \&catch_exit, @{ $sync->{ sigexit } } ) ; -sig_install( $sync, \&catch_reconnect, @{ $sync->{ sigreconnect } } ) ; -sig_install( $sync, \&catch_print, @{ $sync->{ sigprint } } ) ; +sig_install( $sync, 'catch_exit', @{ $sync->{ sigexit } } ) ; +sig_install( $sync, 'catch_reconnect', @{ $sync->{ sigreconnect } } ) ; +sig_install( $sync, 'catch_print', @{ $sync->{ sigprint } } ) ; # --sigignore can override sigexit, sigreconnect and sigprint (for the same signals only) -sig_install( $sync, \&catch_ignore, @{ $sync->{ sigignore } } ) ; +sig_install( $sync, 'catch_ignore', @{ $sync->{ sigignore } } ) ; sig_install_toggle_sleep( $sync ) ; @@ -1115,7 +1256,7 @@ $checkmessageexists = 0 if ( not $sync->{abletosearch1} ) ; $sync->{showpasswords} = defined $sync->{showpasswords} ? $sync->{showpasswords} : 0 ; -$fixslash2 = defined $fixslash2 ? $fixslash2 : 1 ; +$sync->{ fixslash2 } = defined $sync->{ fixslash2 } ? $sync->{ fixslash2 } : 1 ; $fixInboxINBOX = defined $fixInboxINBOX ? $fixInboxINBOX : 1 ; $create_folder_old = defined $create_folder_old ? $create_folder_old : 0 ; $mixfolders = defined $mixfolders ? $mixfolders : 1 ; @@ -1128,7 +1269,7 @@ $sync->{maxbytespersecond} = defined $sync->{maxbytespersecond} ? $sync- $sync->{sslcheck} = defined $sync->{sslcheck} ? $sync->{sslcheck} : 1 ; -myprint( banner_imapsync( @ARGV ) ) ; +myprint( banner_imapsync( $sync, @ARGV ) ) ; myprint( "Temp directory is $sync->{ tmpdir } ( to change it use --tmpdir dirpath )\n" ) ; @@ -1139,21 +1280,41 @@ do_valid_directory( $sync->{ tmpdir } ) || croak "Error creating tmpdir $sync->{ remove_pidfile_not_running( $sync->{pidfile} ) ; +# if another imapsync is running then tail -f its logfile and exit +# useful in cgi context +if ( $sync->{tail} and tail( $sync ) ) +{ + myprint( "Tail -f finished. Now finishing myself processus $PROCESS_ID\n" ) ; + exit_clean( $sync, $EX_OK ) ; + exit $EX_OK ; +} if ( ! write_pidfile( $sync ) ) { + myprint( "Exiting with return value $EXIT_PID_FILE_ERROR ($EXIT_TXT{$EXIT_PID_FILE_ERROR})\n" ) ; exit $EXIT_PID_FILE_ERROR ; } -# simulong is just a loop printing some lines for xx seconds with option "--simulong xx". -if ( $sync->{simulong} ) { simulong( $sync->{simulong} ) ; } - - -# New place to abort -if ( $sync->{abort} ) { - abort( $sync ) ; +# New place for abort +# abort before simulong in order to be able to abort a simulong sync +if ( $sync->{ abort } ) +{ + abort( $sync ) ; } +# simulong is just a loop printing some lines for xx seconds with option "--simulong xx". +if ( $sync->{ simulong } ) +{ + simulong( $sync->{ simulong } ) ; +} + + +# New place for cgiload 2019_03_03 +# because I want to log it +# Can break here if load is too heavy +cgiload( $sync ) ; + + $fixcolonbug = defined $fixcolonbug ? $fixcolonbug : 1 ; if ( $usecache and $fixcolonbug ) { tmpdir_fix_colon_bug( $sync ) } ; @@ -1161,7 +1322,7 @@ if ( $usecache and $fixcolonbug ) { tmpdir_fix_colon_bug( $sync ) } ; $modulesversion and myprint( "Modules version list:\n", modulesversion(), "( use --no-modulesversion to turn off printing this Perl modules list )\n" ) ; -check_lib_version( ) or +check_lib_version( $sync ) or croak "imapsync needs perl lib Mail::IMAPClient release 3.30 or superior.\n"; @@ -1193,14 +1354,14 @@ sslcheck( $sync ) ; $split1 ||= $SPLIT ; $split2 ||= $SPLIT ; -$sync->{host1} || missing_option( '--host1' ) ; +#$sync->{host1} || missing_option( $sync, '--host1' ) ; $sync->{port1} ||= ( $sync->{ssl1} ) ? $IMAP_SSL_PORT : $IMAP_PORT ; -$sync->{host2} || missing_option( '--host2' ) ; +#$sync->{host2} || missing_option( $sync, '--host2' ) ; $sync->{port2} ||= ( $sync->{ssl2} ) ? $IMAP_SSL_PORT : $IMAP_PORT ; $debugimap1 = $debugimap2 = 1 if ( $debugimap ) ; -$debug = 1 if ( $debugimap1 or $debugimap2 ) ; +$sync->{ debug } = 1 if ( $debugimap1 or $debugimap2 ) ; # By default, don't take size to compare $skipsize = (defined $skipsize) ? $skipsize : 1; @@ -1246,15 +1407,21 @@ if ( $sync->{ssl2} ) { } -if ( $sync->{justconnect} ) { - justconnect( ) ; +if ( $sync->{justconnect} + or not $sync->{user1} + or not $sync->{user2} + or not $sync->{host1} + or not $sync->{host2} + ) +{ + my $justconnect = justconnect( $sync ) ; myprint( debugmemory( $sync, " after justconnect() call" ) ) ; - exit_clean( $sync, $EX_OK ) ; + exit_clean( $sync, $EX_OK, "Exiting after a justconnect on host(s): $justconnect\n" ) ; } -$sync->{user1} || missing_option( '--user1' ) ; -$sync->{user2} || missing_option( '--user2' ) ; +#$sync->{user1} || missing_option( $sync, '--user1' ) ; +#$sync->{user2} || missing_option( $sync, '--user2' ) ; $syncinternaldates = defined $syncinternaldates ? $syncinternaldates : 1; @@ -1327,11 +1494,11 @@ $authmech1 = uc $authmech1; $authmech2 = uc $authmech2; if (defined $proxyauth1 && !$authuser1) { - missing_option( 'With --proxyauth1, --authuser1' ) ; + missing_option( $sync, 'With --proxyauth1, --authuser1' ) ; } if (defined $proxyauth2 && !$authuser2) { - missing_option( 'With --proxyauth2, --authuser2' ) ; + missing_option( $sync, 'With --proxyauth2, --authuser2' ) ; } $authuser1 ||= $sync->{user1}; @@ -1350,7 +1517,7 @@ myprint( "Host2: imap connection timeout is $sync->{h2}->{timeout} seconds\n" ) $syncacls = defined $syncacls ? $syncacls : 0 ; # No folders sizes if --justfolders, unless really wanted. -if ( $sync->{ justfolders } and not defined $foldersizes ) { $foldersizes = 0 ; } +if ( $sync->{ justfolders } and not defined $foldersizes and not $sync->{ justfoldersizes } ) { $foldersizes = 0 ; $foldersizesatend = 1 ; } $foldersizes = ( defined $foldersizes ) ? $foldersizes : 1 ; $foldersizesatend = ( defined $foldersizesatend ) ? $foldersizesatend : $foldersizes ; @@ -1382,18 +1549,14 @@ get_password1( $sync ) ; get_password2( $sync ) ; - $sync->{dry_message} = q{} ; if( $sync->{dry} ) { $sync->{dry_message} = "\t(not really since --dry mode)" ; } - $search1 ||= $search if ( $search ) ; $search2 ||= $search if ( $search ) ; - - if ( $disarmreadreceipts ) { push @regexmess, q{s{\A((?:[^\n]+\r\n)+|)(^Disposition-Notification-To:[^\n]*\n)(\r?\n|.*\n\r?\n)}{$1X-$2$3}ims} ; } @@ -1407,7 +1570,7 @@ if ( @pipemess and $pipemesscheck ) { my $string = pipemess( q{ }, @pipemess ) ; # string undef means something was bad. if ( not ( defined $string ) ) { - die_clean( "Error: one of --pipemess command is bad, check it\n" ) ; + exit_clean( $sync, $EX_USAGE, "Error: one of --pipemess command is bad, check it\n" ) ; } myprint( "Ok with each --pipemess @pipemess\n" ) ; } @@ -1417,7 +1580,7 @@ if ( $maxlinelengthcmd ) { my $string = pipemess( q{ }, $maxlinelengthcmd ) ; # string undef means something was bad. if ( not ( defined $string ) ) { - die_clean( "Error: --maxlinelengthcmd command is bad, check it\n" ) ; + exit_clean( $sync, $EX_USAGE, "Error: --maxlinelengthcmd command is bad, check it\n" ) ; } myprint( "Ok with --maxlinelengthcmd $maxlinelengthcmd\n" ) ; } @@ -1427,7 +1590,7 @@ if ( @regexmess ) { myprint( "Checking each --regexmess command with an space string.\n" ) ; # string undef means one of the eval regex was bad. if ( not ( defined $string ) ) { - die_clean( 'Error: one of --regexmess option is bad, check it' ) ; + exit_clean( $sync, $EX_USAGE, 'Error: one of --regexmess option is bad, check it' ) ; } myprint( "Ok with each --regexmess\n" ) ; } @@ -1437,7 +1600,7 @@ if ( @skipmess ) { my $match = skipmess( q{ } ) ; # match undef means one of the eval regex was bad. if ( not ( defined $match ) ) { - die_clean( 'Error: one of --skipmess option is bad, check it' ) ; + exit_clean( $sync, $EX_USAGE, 'Error: one of --skipmess option is bad, check it' ) ; } myprint( "Ok with each --skipmess\n" ) ; } @@ -1447,7 +1610,7 @@ if ( @regexflag ) { my $string = flags_regex( q{ } ) ; # string undef means one of the eval regex was bad. if ( not ( defined $string ) ) { - die_clean( 'Error: one of --regexflag option is bad, check it' ) ; + exit_clean( $sync, $EX_USAGE, 'Error: one of --regexflag option is bad, check it' ) ; } myprint( "Ok with each --regexflag\n" ) ; } @@ -1463,30 +1626,36 @@ $sync->{imap2} = login_imap( $sync->{host2}, $sync->{port2}, $sync->{user2}, $do $proxyauth2, $uid2, $split2, 'Host2', $sync->{h2}, $sync ) ; -$debug and myprint( 'Host1 Buffer I/O: ', $sync->{imap1}->Buffer(), "\n" ) ; -$debug and myprint( 'Host2 Buffer I/O: ', $sync->{imap2}->Buffer(), "\n" ) ; +$sync->{ debug } and myprint( 'Host1 Buffer I/O: ', $sync->{imap1}->Buffer(), "\n" ) ; +$sync->{ debug } and myprint( 'Host2 Buffer I/O: ', $sync->{imap2}->Buffer(), "\n" ) ; -if ( ! $sync->{imap1}->IsAuthenticated( ) ) { die_clean( 'Not authenticated on host1' ) ; } +if ( ! $sync->{imap1}->IsAuthenticated( ) ) { exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, 'Not authenticated on host1' ) ; } myprint( "Host1: state Authenticated\n" ) ; -if ( ! $sync->{imap2}->IsAuthenticated( ) ) { die_clean( 'Not authenticated on host2' ) ; } +if ( ! $sync->{imap2}->IsAuthenticated( ) ) { exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, 'Not authenticated on host2' ) ; } myprint( "Host2: state Authenticated\n" ) ; myprint( 'Host1 capability once authenticated: ', join(q{ }, @{ $sync->{imap1}->capability() || [] }), "\n" ) ; + +#myprint( Data::Dumper->Dump( [ $sync->{imap1} ] ) ) ; +#myprint( "imap4rev1: " . $sync->{imap1}->imap4rev1() . "\n" ) ; + myprint( 'Host2 capability once authenticated: ', join(q{ }, @{ $sync->{imap2}->capability() || [] }), "\n" ) ; + # ID on by default since 1.832 $sync->{id} = defined $sync->{id} ? $sync->{id} : 1 ; imap_id_stuff( $sync ) ; -#quota( $sync->{imap1}, 'h1', $sync ) ; # quota on host1 is useless and pollute host2 output. -quota( $sync->{imap2}, 'h2', $sync ) ; +#quota( $sync, $sync->{imap1}, 'h1' ) ; # quota on host1 is useless and pollute host2 output. +quota( $sync, $sync->{imap2}, 'h2' ) ; maxsize_setting( $sync ) ; -if ( $sync->{justlogin} ) { +if ( $sync->{ justlogin } ) { $sync->{imap1}->logout( ) ; $sync->{imap2}->logout( ) ; + myprint( "Exiting because of --justlogin\n" ) ; exit_clean( $sync, $EX_OK ) ; } @@ -1527,30 +1696,39 @@ $sync->{h1_folders_all} = \%h1_folders_all ; $sync->{h2_folders_all} = \%h2_folders_all ; $sync->{h2_folders_all_UPPER} = \%h2_folders_all_UPPER ; +private_folders_separators_and_prefixes( ) ; + + # Make a hash of subscribed folders in both servers. for ( $sync->{imap1}->subscribed( ) ) { $h1_subscribed_folder{ $_ } = 1 } ; for ( $sync->{imap2}->subscribed( ) ) { $h2_subscribed_folder{ $_ } = 1 } ; -if ( defined $subfolder2 ) { - unshift @regextrans2, - q(s,^$sync->{h2_prefix}(.*),$sync->{h2_prefix}${subfolder2}${h2_sep}$1,), - q(s,^INBOX$,$sync->{h2_prefix}${subfolder2}${h2_sep}INBOX,) ; +if ( defined $sync->{ subfolder1 } ) { + subfolder1( $sync ) ; +} + + + +if ( defined $sync->{ subfolder2 } ) { + subfolder2( $sync ) ; } if ( $fixInboxINBOX and ( my $reg = fix_Inbox_INBOX_mapping( \%h1_folders_all, \%h2_folders_all ) ) ) { - push @regextrans2, $reg ; + push @{ $sync->{ regextrans2 } }, $reg ; } -if ( ( $sync->{folder} and scalar @{ $sync->{folder} } ) + +if ( ( $sync->{ folder } and scalar @{ $sync->{ folder } } ) or $subscribed - or scalar @folderrec ) { + or scalar @folderrec ) +{ # folders given by option --folder - if ( $sync->{folder} and scalar @{ $sync->{folder} } ) { - add_to_requested_folders( @{ $sync->{folder} } ); + if ( $sync->{ folder } and scalar @{ $sync->{ folder } } ) { + add_to_requested_folders( @{ $sync->{ folder } } ) ; } # option --subscribed @@ -1559,16 +1737,18 @@ if ( ( $sync->{folder} and scalar @{ $sync->{folder} } ) } # option --folderrec - if (scalar @folderrec) { - foreach my $folderrec (@folderrec) { - add_to_requested_folders($sync->{imap1}->folders($folderrec)); + if ( scalar @folderrec ) { + foreach my $folderrec ( @folderrec ) { + add_to_requested_folders( $sync->{imap1}->folders( $folderrec ) ) ; } } -} else { +} +else +{ # no include, no folder/subscribed/folderrec options => all folders if ( not scalar @include ) { myprint( "Including all folders found by default. Use --subscribed or --folder or --folderrec or --include to select specific folders. Use --exclude to unselect specific folders.\n" ) ; - add_to_requested_folders(@h1_folders_all); + add_to_requested_folders( @h1_folders_all ) ; } } @@ -1603,7 +1783,7 @@ if ( $sync->{ checkfoldersexist } ) { my @h1_folders_wanted_exist ; myprint( "Host1: Checking wanted folders exist. Use --nocheckfoldersexist to avoid this check (shared of public namespace targeted).\n" ) ; foreach my $folder ( @h1_folders_wanted ) { - ( $debug or $sync->{debugfolders} ) and myprint( "Checking $folder exists on host1\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Checking $folder exists on host1\n" ) ; if ( ! exists $h1_folders_all{ $folder } ) { myprint( "Host1: warning! ignoring folder $folder because it is not in host1 whole folders list.\n" ) ; next ; @@ -1621,7 +1801,7 @@ if ( $sync->{ checkselectable } ) { my @h1_folders_wanted_selectable ; myprint( "Host1: Checking wanted folders are selectable. Use --nocheckselectable to avoid this check.\n" ) ; foreach my $folder ( @h1_folders_wanted ) { - ( $debug or $sync->{debugfolders} ) and myprint( "Checking $folder is selectable on host1\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Checking $folder is selectable on host1\n" ) ; # It does an imap command LIST "" $folder and then search for no \Noselect if ( ! $sync->{imap1}->selectable( $folder ) ) { myprint( "Host1: warning! ignoring folder $folder because it is not selectable\n" ) ; @@ -1630,7 +1810,7 @@ if ( $sync->{ checkselectable } ) { } } @h1_folders_wanted = @h1_folders_wanted_selectable ; - ( $debug or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext( ), " s\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext( ), " s\n" ) ; }else{ myprint( "Host1: Not checking that wanted folders are selectable. Remove --nocheckselectable to get this check.\n" ) ; } @@ -1638,20 +1818,9 @@ if ( $sync->{ checkselectable } ) { $sync->{h1_folders_wanted} = \@h1_folders_wanted ; -my( $h1_sep, $h2_sep ) ; -# what are the private folders separators for each server ? +# Old place of private_folders_separators_and_prefixes( ) call. +#private_folders_separators_and_prefixes( ) ; -( $debug or $sync->{debugfolders} ) and myprint( "Getting separators\n" ) ; -$h1_sep = get_separator( $sync->{imap1}, $sep1, '--sep1', 'Host1', \@h1_folders_all ) ; -$h2_sep = get_separator( $sync->{imap2}, $sep2, '--sep2', 'Host2', \@h2_folders_all ) ; - - -$sync->{ h1_prefix } = get_prefix( $sync->{imap1}, $prefix1, '--prefix1', 'Host1', \@h1_folders_all ) ; -$sync->{ h2_prefix } = get_prefix( $sync->{imap2}, $prefix2, '--prefix2', 'Host2', \@h2_folders_all ) ; - - -myprint( "Host1: separator and prefix: [$h1_sep][$sync->{ h1_prefix }]\n" ) ; -myprint( "Host2: separator and prefix: [$h2_sep][$sync->{ h2_prefix }]\n" ) ; # this hack is because LWP post does not pass well a hash in the $form parameter # but it does pass well an array @@ -1662,7 +1831,7 @@ automap( $sync ) ; foreach my $h1_fold ( @h1_folders_wanted ) { my $h2_fold ; - $h2_fold = imap2_folder_name( $h1_fold ) ; + $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; $h2_folders_from_1_wanted{ $h2_fold }++ ; if ( 1 < $h2_folders_from_1_wanted{ $h2_fold } ) { $h2_folders_from_1_several{ $h2_fold }++ ; @@ -1672,7 +1841,7 @@ foreach my $h1_fold ( @h1_folders_wanted ) { foreach my $h1_fold ( @h1_folders_all ) { my $h2_fold ; - $h2_fold = imap2_folder_name( $h1_fold ) ; + $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; $h2_folders_from_1_all{ $h2_fold }++ ; } @@ -1685,7 +1854,7 @@ All foldernames are presented between brackets like [X] where X is the foldernam When a foldername contains non-ASCII characters it is presented in the form [X] = [Y] where X is the imap foldername you have to use in command line options and -Y is the uft8 output just printed for convenience, to recognize it. +Y is the utf8 output just printed for convenience, to recognize it. END_LISTING @@ -1750,15 +1919,20 @@ exit_clean( $sync, $EX_OK ) if ( $sync->{justautomap} ) ; debugsleep( $sync ) ; if ( $foldersizes ) { - foldersizes_on_h1h2( ) ; + foldersizes_on_h1h2( $sync ) ; } -exit_clean( $sync, $EX_OK ) if ( $justfoldersizes ) ; + +if ( $sync->{ justfoldersizes } ) +{ + myprint( "Exiting because of --justfoldersizes\n" ) ; + exit_clean( $sync, $EX_OK ) ; +} $sync->{stats} = 1 ; -if ( $sync->{'delete1emptyfolders'} ) { +if ( $sync->{ delete1emptyfolders } ) { delete1emptyfolders( $sync ) ; } @@ -1780,7 +1954,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } - my $h2_fold = imap2_folder_name( $h1_fold ) ; + my $h2_fold = imap2_folder_name( $sync, $h1_fold ) ; $h1_folders_wanted_ct++ ; myprintf( "Folder %7s %-35s -> %-35s\n", "$h1_folders_wanted_ct/$h1_folders_wanted_nb", @@ -1788,7 +1962,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( debugmemory( $sync, " at folder loop" ) ) ; # host1 can not be fetched read only, select is needed because of expunge. - select_folder( $sync->{imap1}, $h1_fold, 'Host1' ) or next FOLDER ; + select_folder( $sync, $sync->{imap1}, $h1_fold, 'Host1' ) or next FOLDER ; debugsleep( $sync ) ; @@ -1813,16 +1987,16 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { } if ( ! exists $h2_folders_all{ $h2_fold } ) { - create_folder( $sync->{imap2}, $h2_fold, $h1_fold ) or next FOLDER ; + create_folder( $sync, $sync->{imap2}, $h2_fold, $h1_fold ) or next FOLDER ; } acls_sync( $h1_fold, $h2_fold ) ; # Sometimes the folder on host2 is listed (it exists) but is # not selectable but becomes selectable by a create (Gmail) - select_folder( $sync->{imap2}, $h2_fold, 'Host2' ) - or ( create_folder( $sync->{imap2}, $h2_fold, $h1_fold ) - and select_folder( $sync->{imap2}, $h2_fold, 'Host2' ) ) + select_folder( $sync, $sync->{imap2}, $h2_fold, 'Host2' ) + or ( create_folder( $sync, $sync->{imap2}, $h2_fold, $h1_fold ) + and select_folder( $sync, $sync->{imap2}, $h2_fold, 'Host2' ) ) or next FOLDER ; my @select_results = $sync->{imap2}->Results( ) ; @@ -1857,8 +2031,8 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my $h1_msgs_nb = scalar @h1_msgs ; myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages\n" ) ; - ( $debug or $debuglist ) and myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages, LIST gives: @h1_msgs\n" ) ; - $debug and myprint( "Host1: selecting messages of folder [$h1_fold] took ", timenext(), " s\n" ) ; + ( $sync->{ debug } or $debuglist ) and myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages, LIST gives: @h1_msgs\n" ) ; + $sync->{ debug } and myprint( "Host1: selecting messages of folder [$h1_fold] took ", timenext(), " s\n" ) ; my $h2_msgs_all_hash_ref = { } ; my @h2_msgs = select_msgs( $sync->{imap2}, $h2_msgs_all_hash_ref, $search2, $sync->{abletosearch2}, $h2_fold ) ; @@ -1868,8 +2042,8 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my $h2_msgs_nb = scalar @h2_msgs ; myprint( "Host2: folder [$h2_fold] considering $h2_msgs_nb messages\n" ) ; - ( $debug or $debuglist ) and myprint( "Host2: folder [$h2_fold] considering $h2_msgs_nb messages, LIST gives: @h2_msgs\n" ) ; - $debug and myprint( "Host2: selecting messages of folder [$h2_fold] took ", timenext(), " s\n" ) ; + ( $sync->{ debug } or $debuglist ) and myprint( "Host2: folder [$h2_fold] considering $h2_msgs_nb messages, LIST gives: @h2_msgs\n" ) ; + $sync->{ debug } and myprint( "Host2: selecting messages of folder [$h2_fold] took ", timenext(), " s\n" ) ; my $cache_base = "$sync->{ tmpdir }/imapsync_cache/" ; my $cache_dir = cache_folder( $cache_base, "$sync->{host1}/$sync->{user1}/$sync->{host2}/$sync->{user2}", $h1_fold, $h2_fold ) ; @@ -1881,12 +2055,12 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } if ( $usecache ) { - myprint( "Local cache directory: $cache_dir\n" ) ; + myprint( "Local cache directory: $cache_dir ( " . length( $cache_dir ) . " characters long )\n" ) ; mkpath( "$cache_dir" ) ; ( $cache_1_2_ref, $cache_2_1_ref ) = get_cache( $cache_dir, \@h1_msgs, \@h2_msgs, $h1_msgs_all_hash_ref, $h2_msgs_all_hash_ref ) ; myprint( 'CACHE h1 h2: ', scalar keys %{ $cache_1_2_ref } , " files\n" ) ; - $debug and myprint( '[', + $sync->{ debug } and myprint( '[', map ( { "$_->$cache_1_2_ref->{$_} " } keys %{ $cache_1_2_ref } ), " ]\n" ) ; } @@ -1923,29 +2097,33 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { #myprint( "delete2: @h2_msgs_delete2_not_in_cache\n" ) ; } - $debug and myprint( "Host1: parsing headers of folder [$h1_fold]\n" ) ; + $sync->{ debug } and myprint( "Host1: parsing headers of folder [$h1_fold]\n" ) ; my ($h1_heads_ref, $h1_fir_ref) = ({}, {}); $h1_heads_ref = $sync->{imap1}->parse_headers([@h1_msgs_not_in_cache], @useheader) if (@h1_msgs_not_in_cache); - $debug and myprint( "Host1: parsing headers of folder [$h1_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host1: parsing headers of folder [$h1_fold] took ", timenext(), " s\n" ) ; @{ $h1_fir_ref }{@h1_msgs} = ( undef ) ; - $debug and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold]\n" ) ; + $sync->{ debug } and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold]\n" ) ; + + my @h1_common_fetch_param = ( 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE' ) ; + if ( $sync->{ synclabels } or $sync->{ resynclabels } ) { push @h1_common_fetch_param, 'X-GM-LABELS' ; } + if ( $sync->{abletosearch1} ) { - $h1_fir_ref = $sync->{imap1}->fetch_hash( \@h1_msgs, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h1_fir_ref ) + $h1_fir_ref = $sync->{imap1}->fetch_hash( \@h1_msgs, @h1_common_fetch_param, $h1_fir_ref ) if ( @h1_msgs ) ; } else { my $uidnext = $sync->{imap1}->uidnext( $h1_fold ) || $uidnext_default ; my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ; - $h1_fir_ref = $sync->{imap1}->fetch_hash( $fetch_hash_uids, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h1_fir_ref ) + $h1_fir_ref = $sync->{imap1}->fetch_hash( $fetch_hash_uids, @h1_common_fetch_param, $h1_fir_ref ) if ( @h1_msgs ) ; } - $debug and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host1: getting flags idate and sizes of folder [$h1_fold] took ", timenext(), " s\n" ) ; if ( ! $h1_fir_ref ) { my $error = join( q{}, "Host1: folder $h1_fold : Could not fetch_hash ", @@ -1981,31 +2159,34 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host1: folder [$h1_fold] selected $h1_msgs_nb messages, duplicates $h1_msgs_duplicate_nb\n" ) ; - $debug and myprint( 'Host1: whole time parsing headers took ', timenext(), " s\n" ) ; + $sync->{ debug } and myprint( 'Host1: whole time parsing headers took ', timenext(), " s\n" ) ; # Getting headers and metada can be so long that host2 might be disconnected here if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } - $debug and myprint( "Host2: parsing headers of folder [$h2_fold]\n" ) ; + $sync->{ debug } and myprint( "Host2: parsing headers of folder [$h2_fold]\n" ) ; my ($h2_heads_ref, $h2_fir_ref) = ( {}, {} ); $h2_heads_ref = $sync->{imap2}->parse_headers([@h2_msgs_not_in_cache], @useheader) if (@h2_msgs_not_in_cache); - $debug and myprint( "Host2: parsing headers of folder [$h2_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host2: parsing headers of folder [$h2_fold] took ", timenext(), " s\n" ) ; - $debug and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold]\n" ) ; + $sync->{ debug } and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold]\n" ) ; @{ $h2_fir_ref }{@h2_msgs} = ( ); # fetch_hash can select by uid with last arg as ref + my @h2_common_fetch_param = ( 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE' ) ; + if ( $sync->{ synclabels } or $sync->{ resynclabels } ) { push @h2_common_fetch_param, 'X-GM-LABELS' ; } + if ( $sync->{abletosearch2} and scalar( @h2_msgs ) ) { - $h2_fir_ref = $sync->{imap2}->fetch_hash( \@h2_msgs, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h2_fir_ref) ; + $h2_fir_ref = $sync->{imap2}->fetch_hash( \@h2_msgs, @h2_common_fetch_param, $h2_fir_ref) ; }else{ my $uidnext = $sync->{imap2}->uidnext( $h2_fold ) || $uidnext_default ; my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ; - $h2_fir_ref = $sync->{imap2}->fetch_hash( $fetch_hash_uids, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h2_fir_ref ) + $h2_fir_ref = $sync->{imap2}->fetch_hash( $fetch_hash_uids, @h2_common_fetch_param, $h2_fir_ref ) if ( @h2_msgs ) ; } - $debug and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold] took ", timenext(), " s\n" ) ; + $sync->{ debug } and myprint( "Host2: getting flags idate and sizes of folder [$h2_fold] took ", timenext(), " s\n" ) ; my @h2_msgs_duplicate; foreach my $m (@h2_msgs_not_in_cache) { @@ -2035,9 +2216,9 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host2: folder [$h2_fold] selected $h2_msgs_nb messages, duplicates $h2_msgs_duplicate_nb\n" ) ; - $debug and myprint( 'Host2 whole time parsing headers took ', timenext( ), " s\n" ) ; + $sync->{ debug } and myprint( 'Host2 whole time parsing headers took ', timenext( ), " s\n" ) ; - $debug and myprint( "++++ Verifying [$h1_fold] -> [$h2_fold]\n" ) ; + $sync->{ debug } and myprint( "++++ Verifying [$h1_fold] -> [$h2_fold]\n" ) ; # messages in host1 that are not in host2 my @h1_hash_keys_sorted_by_uid @@ -2119,11 +2300,11 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { my $h2_flags = $h2_hash{ $m_id }{ 'F' } || q{} ; my $isdel = $h2_flags =~ /\B\\Deleted\b/x ? 1 : 0 ; if ( ! $isdel ) { - $debug and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion [$m_id]\n" ) ; + $sync->{ debug } and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion [$m_id]\n" ) ; $uid_candidate_for_deletion{ $h2_fold }{ $h2_msg }++ ; } }else{ - $debug and myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [$m_id]\n" ) ; + $sync->{ debug } and myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [$m_id]\n" ) ; $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ; } } @@ -2142,9 +2323,9 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { # last host1 folder going to $h2_fold myprint( "Last host1 folder going to $h2_fold\n" ) ; foreach my $h2_msg ( keys %{ $uid_candidate_for_deletion{ $h2_fold } } ) { - $debug and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion\n" ) ; + $sync->{ debug } and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion\n" ) ; if ( exists $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg } ) { - $debug and myprint( "Host2: msg $h2_fold/$h2_msg canceled deletion\n" ) ; + $sync->{ debug } and myprint( "Host2: msg $h2_fold/$h2_msg canceled deletion\n" ) ; }else{ myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted $sync->{dry_message}\n" ) ; push @h2_expunge, $h2_msg if $sync->{ uidexpunge2 } ; @@ -2170,7 +2351,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { } my $h2_uidnext = $sync->{imap2}->uidnext( $h2_fold ) ; - $debug and myprint( "Host2: uidnext is $h2_uidnext\n" ) ; + $sync->{ debug } and myprint( "Host2: uidnext is $h2_uidnext\n" ) ; $h2_uidguess = $h2_uidnext ; # Getting host2 headers, metada and delete2 stuff can be so long that host1 might be disconnected here @@ -2202,11 +2383,11 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { # A bug here with imapsync 1.920, fixed in 1.921 # Added $h2_msg in the condition. Errors of APPEND were not counted as missing messages on host2! if ( $h2_msg and not $sync->{ dry } ) - { + { $sync->{ h2_folders_of_md5 }->{ $m_id }->{ $h2_fold } ++ ; } - - # + + # if( $sync->{ delete2 } and ( exists $h2_folders_from_1_several{ $h2_fold } ) and $h2_msg ) { myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [fresh copy] on host2\n" ) ; $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ; @@ -2225,7 +2406,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { if ( exists $h2_hash{ $m_id } ) { my $h2_msg = $h2_hash{$m_id}{'m'} ; - $debug and myprint( "Host1: found that msg $h1_fold/$h1_msg equals Host2 $h2_fold/$h2_msg\n" ) ; + $sync->{ debug } and myprint( "Host1: found that msg $h1_fold/$h1_msg equals Host2 $h2_fold/$h2_msg\n" ) ; if ( $usecache ) { $debugcache and myprint( "touch $cache_dir/${h1_msg}_$h2_msg\n" ) ; @@ -2236,7 +2417,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { elsif( exists $sync->{ h2_folders_of_md5 }->{ $m_id } ) { my @folders_dup = keys %{ $sync->{ h2_folders_of_md5 }->{ $m_id } } ; - ( $debug or $debugcrossduplicates ) and myprint( "Host1: found that msg $h1_fold/$h1_msg is also in Host2 folders @folders_dup\n" ) ; + ( $sync->{ debug } or $debugcrossduplicates ) and myprint( "Host1: found that msg $h1_fold/$h1_msg is also in Host2 folders @folders_dup\n" ) ; $sync->{ h2_nb_msg_crossdup } +=1 ; } $sync->{ total_bytes_skipped } += $h1_size ; @@ -2252,8 +2433,13 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { } # Good my $h2_size = $h2_hash{$m_id}{'s'}; - $debug and myprint( + $sync->{ debug } and myprint( "Host1: size msg $h1_fold/$h1_msg = $h1_size <> $h2_size = Host2 $h2_fold/$h2_msg\n" ) ; + + if ( $sync->{ resynclabels } ) + { + resynclabels( $sync, $h1_msg, $h2_msg, $h1_fir_ref, $h2_fir_ref, $h1_fold ) + } } if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } @@ -2267,7 +2453,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { delete_message_on_host1( $sync, $h1_fold, $sync->{ expunge1 }, @h1_msgs_to_delete, @h1_msgs_in_cache ) ; if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } - + # MESS_IN_CACHE: if ( ! $sync->{ delete1 } ) { @@ -2275,7 +2461,7 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { { my $h2_msg = $cache_1_2_ref->{ $h1_msg } ; $debugcache and myprint( "cache messages update flags $h1_msg->$h2_msg\n" ) ; - if ( $sync->{resyncflags} ) + if ( $sync->{resyncflags} ) { sync_flags_fir( $sync, $h1_fold, $h1_msg, $h2_fold, $h2_msg, $permanentflags2, $h1_fir_ref, $h2_fir_ref ) ; } @@ -2285,15 +2471,15 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { $sync->{ h1_nb_msg_processed } +=1 ; } } - + if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } @h1_msgs_to_delete = ( ) ; #myprint( "Messages by uid: ", map { "$_ " } keys %h1_msgs_copy_by_uid, "\n" ) ; - # MESS_BY_UID: - foreach my $h1_msg ( sort { $a <=> $b } keys %h1_msgs_copy_by_uid ) + # MESS_BY_UID: + foreach my $h1_msg ( sort { $a <=> $b } keys %h1_msgs_copy_by_uid ) { - $debug and myprint( "Copy by uid $h1_fold/$h1_msg\n" ) ; + $sync->{ debug } and myprint( "Copy by uid $h1_fold/$h1_msg\n" ) ; if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; } my $h2_msg = copy_message( $sync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) ; @@ -2312,17 +2498,17 @@ FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) { myprint( "Host2: Expunging folder $h2_fold $sync->{dry_message}\n" ) ; if ( ! $sync->{dry} ) { $sync->{imap2}->expunge( ) } ; } - $debug and myprint( 'Time: ', timenext( ), " s\n" ) ; + $sync->{ debug } and myprint( 'Time: ', timenext( ), " s\n" ) ; } myprint( "++++ End looping on each folder\n" ) ; -if ( $sync->{ delete1 } and $sync->{'delete1emptyfolders'} ) { +if ( $sync->{ delete1 } and $sync->{ delete1emptyfolders } ) { delete1emptyfolders( $sync ) ; } -( $debug or $sync->{debugfolders} ) and myprint( 'Time: ', timenext( ), " s\n" ) ; +( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Time: ', timenext( ), " s\n" ) ; if ( $foldersizesatend ) { @@ -2332,11 +2518,11 @@ Folders sizes after the synchronization. You can remove this foldersizes listing by using "--nofoldersizesatend" END_SIZE - foldersizesatend( ) ; + foldersizesatend( $sync ) ; } -if ( ! lost_connection( $sync->{imap1}, "for host1 [$sync->{host1}]" ) ) { $sync->{imap1}->logout( ) ; } -if ( ! lost_connection( $sync->{imap2}, "for host2 [$sync->{host2}]" ) ) { $sync->{imap2}->logout( ) ; } +if ( ! lost_connection( $sync, $sync->{imap1}, "for host1 [$sync->{host1}]" ) ) { $sync->{imap1}->logout( ) ; } +if ( ! lost_connection( $sync, $sync->{imap2}, "for host2 [$sync->{host2}]" ) ) { $sync->{imap2}->logout( ) ; } stats( $sync ) ; myprint( errorsdump( $sync->{nb_errors}, errors_log( $sync ) ) ) if ( $sync->{errorsdump} ) ; @@ -2460,11 +2646,11 @@ sub output_reset_with sub abort { my $mysync = shift @ARG ; - if ( ! -r $sync->{pidfile} ) { - myprint( "Can not read pidfile $sync->{pidfile}. Exiting.\n" ) ; + if ( ! -r $mysync->{pidfile} ) { + myprint( "Can not read pidfile $mysync->{pidfile}. Exiting.\n" ) ; exit $EX_OK ; } - my $pidtokill = firstline( $sync->{pidfile} ) ; + my $pidtokill = firstline( $mysync->{pidfile} ) ; if ( ! $pidtokill ) { myprint( "No process to abort. Exiting.\n" ) ; exit $EX_OK ; @@ -2491,9 +2677,13 @@ sub abort if ( kill 'ZERO', $pidtokill ) { myprint( "Process PID $pidtokill still there. Can not do much. Exiting.\n" ) ; exit $EX_OK ; + }else{ + myprint( "Process PID $pidtokill ended. Exiting.\n" ) ; + exit $EX_OK ; } + # well abort job done anyway - return ; + exit $EX_OK ; } @@ -2607,14 +2797,16 @@ sub cgibuildheader sub cgiload { - my $mysync = shift ; - if ( ! under_cgi_context( $mysync ) ) { return ; } - if ( $mysync->{ abort } ) { return ; } # keep going to abort - if ( $mysync->{ loaddelay } ) { - myprint( "Server is on heavy load. Be back in $mysync->{ loaddelay } min. Load is $mysync->{ loadavg }\n") ; - exit_clean( $mysync, $EX_UNAVAILABLE ) ; - } - return ; + # Exit on heavy load in CGI context + my $mysync = shift ; + if ( ! under_cgi_context( $mysync ) ) { return ; } + if ( $mysync->{ abort } ) { return ; } # keep going to abort since some ressources will be free soon + if ( $mysync->{ loaddelay } ) + { + myprint( "Server is on heavy load. Be back in $mysync->{ loaddelay } min. Load is $mysync->{ loadavg }\n") ; + exit_clean( $mysync, $EX_UNAVAILABLE ) ; + } + return ; } sub tests_set_umask @@ -2718,7 +2910,7 @@ sub tests_umask return ; } -sub cgisetcontext +sub cgisetcontext { my $mysync = shift ; if ( ! under_cgi_context( $mysync ) ) { return ; } @@ -2727,7 +2919,7 @@ sub cgisetcontext set_umask( $mysync ) ; # Remove all content in unsafe evaled options - @regextrans2 = ( ) ; + @{ $mysync->{ regextrans2 } } = ( ) ; @regexflag = ( ) ; @regexmess = ( ) ; @skipmess = ( ) ; @@ -2757,12 +2949,25 @@ sub cgisetcontext chdir $cgidir or die "Can not cd to $cgidir: $OS_ERROR\n" ; $mysync->{ tmpdir } = $cgidir ; cgioutputenvcontext( $mysync ) ; - $debug and output( $mysync, 'Current directory is ' . getcwd( ) . "\n" ) ; - $debug and output( $mysync, 'Real user id is ' . getpwuid_any_os( $REAL_USER_ID ) . " (uid $REAL_USER_ID)\n" ) ; - $debug and output( $mysync, 'Effective user id is ' . getpwuid_any_os( $EFFECTIVE_USER_ID ). " (euid $EFFECTIVE_USER_ID)\n" ) ; - # @{ $mysync->{ sigexit } } = ( 'QUIT' ) ; - # output( $mysync, "Setting the QUIT signal to exit properly\n" ) ; - return ; + $mysync->{ debug } and output( $mysync, 'Current directory is ' . getcwd( ) . "\n" ) ; + $mysync->{ debug } and output( $mysync, 'Real user id is ' . getpwuid_any_os( $REAL_USER_ID ) . " (uid $REAL_USER_ID)\n" ) ; + $mysync->{ debug } and output( $mysync, 'Effective user id is ' . getpwuid_any_os( $EFFECTIVE_USER_ID ). " (euid $EFFECTIVE_USER_ID)\n" ) ; + + $skipemptyfolders = defined $skipemptyfolders ? $skipemptyfolders : 1 ; + + # Out of memory with messages over 1 GB ? + $mysync->{ maxsize } = defined $mysync->{ maxsize } ? $mysync->{ maxsize } : 1_000_000_000 ; + + # tail -f behaviour on by default + $mysync->{ tail } = defined $mysync->{ tail } ? $mysync->{ tail } : 1 ; + + # not sure it's for good + @useheader = qw( Message-Id ) ; + + # addheader on by default + $mysync->{ addheader } = defined $mysync->{ addheader } ? $mysync->{ addheader } : 1 ; + + return ; } sub cgioutputenvcontext @@ -2794,6 +2999,8 @@ sub debugsleep sub foldersizes_on_h1h2 { + my $mysync = shift ; + myprint( << 'END_SIZE' ) ; Folders sizes before the synchronization. @@ -2801,22 +3008,22 @@ You can remove foldersizes listings by using "--nofoldersizes" and "--nofoldersi but then you will also lose the ETA (Estimation Time of Arrival) given after each message copy. END_SIZE - ( $h1_nb_msg_start, $h1_bytes_start ) = foldersizes( 'Host1', $sync->{imap1}, $search1, $sync->{abletosearch1}, @h1_folders_wanted ) ; - ( $h2_nb_msg_start, $h2_bytes_start ) = foldersizes( 'Host2', $sync->{imap2}, $search2, $sync->{abletosearch2}, @h2_folders_from_1_wanted ) ; + ( $h1_nb_msg_start, $h1_bytes_start ) = foldersizes( 'Host1', $mysync->{imap1}, $search1, $mysync->{abletosearch1}, @h1_folders_wanted ) ; + ( $h2_nb_msg_start, $h2_bytes_start ) = foldersizes( 'Host2', $mysync->{imap2}, $search2, $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; if ( not all_defined( $h1_nb_msg_start, $h1_bytes_start, $h2_nb_msg_start, $h2_bytes_start ) ) { my $error = "Failure getting foldersizes, ETA and final diff will not be displayed\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; $foldersizes = 0 ; $foldersizesatend = 0 ; return ; } - my $h2_bytes_limit = $sync->{h2}->{quota_limit_bytes} || 0 ; + my $h2_bytes_limit = $mysync->{h2}->{quota_limit_bytes} || 0 ; if ( $h2_bytes_limit and ( $h2_bytes_limit < $h1_bytes_start ) ) { my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $h1_bytes_start / $h2_bytes_limit ) ; my $error = "Host2: Quota limit will be exceeded! Over $quota_percent % ( $h1_bytes_start bytes / $h2_bytes_limit bytes )\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; } return ; } @@ -2911,9 +3118,11 @@ sub tests_mock_capability sub sig_install_toggle_sleep { my $mysync = shift ; - if ( ! 'MSWin32' eq $OSNAME ) { - sig_install( $mysync, \&toggle_sleep, 'USR1' ) + if ( 'MSWin32' ne $OSNAME ) { + #myprint( "sig_install( $mysync, \&toggle_sleep, 'USR1' )\n" ) ; + sig_install( $mysync, 'toggle_sleep', 'USR1' ) ; } + #myprint( "Leaving sig_install_toggle_sleep\n" ) ; return ; } @@ -3112,7 +3321,7 @@ sub appendlimit my $appendlimit = appendlimit_from_capability( $myimap ) ; if ( defined $appendlimit ) { - myprint( "Host2: found APPENDLIMIT=$appendlimit in CAPABILITY\n" ) ; + myprint( "Host2: found APPENDLIMIT=$appendlimit in CAPABILITY (use --appendlimit xxxx to override this automatic setting)\n" ) ; return $appendlimit ; } return ; @@ -3120,7 +3329,7 @@ sub appendlimit } -sub tests_maxsize_setting +sub tests_maxsize_setting { note( 'Entering tests_maxsize_setting()' ) ; @@ -3134,132 +3343,114 @@ sub tests_maxsize_setting 'maxsize_setting: undef arg => undef' ) ; + $mysync = { } ; + $mysync->{ maxsize } = $NUMBER_123456 ; + + # --maxsize alone + is( $NUMBER_123456, maxsize_setting( $mysync ), + 'maxsize_setting: --maxsize 123456 alone => 123456' + ) ; + + $mysync = { } ; my $myimap ; $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=654321' ) ; $mysync->{ imap2 } = $myimap ; + # APPENDLIMIT alone is( $NUMBER_654321, maxsize_setting( $mysync ), - 'maxsize_setting: APPENDLIMIT 654321 => maxsize 654321' + 'maxsize_setting: APPENDLIMIT 654321 alone => 654321' ) ; - # Case: "APPENDLIMIT >= --maxsize" => Ok. + is( $NUMBER_654321, $mysync->{ maxsize }, + 'maxsize_setting: APPENDLIMIT 654321 alone => maxsize 654321' + ) ; + + # APPENDLIMIT with --appendlimit => --appendlimit wins + $mysync->{ appendlimit } = $NUMBER_123456 ; + + is( $NUMBER_123456, maxsize_setting( $mysync ), + 'maxsize_setting: APPENDLIMIT 654321 + --appendlimit 123456 => 123456' + ) ; + + is( $NUMBER_123456, $mysync->{ maxsize }, + 'maxsize_setting: APPENDLIMIT 654321 + --appendlimit 123456 => maxsize 123456' + ) ; + + # Fresh + $mysync = { } ; + $mysync->{ imap2 } = $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=654321' ) ; + + # Case: "APPENDLIMIT >= --maxsize" => maxsize. $mysync->{ maxsize } = $NUMBER_123456 ; is( $NUMBER_123456, maxsize_setting( $mysync ), - 'maxsize_setting: --maxsize 123456 => 123456' + 'maxsize_setting: APPENDLIMIT 654321 --maxsize 123456 => 123456' ) ; - # Case: "APPENDLIMIT < --maxsize" => Warning. - - $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=123456' ) ; + # Case: "APPENDLIMIT < --maxsize" => APPENDLIMIT. + + + # Fresh + $mysync = { } ; + $mysync->{ imap2 } = $myimap = mock_capability( $myimap, 'IMAP4rev1', 'APPENDLIMIT=123456' ) ; $mysync->{ maxsize } = $NUMBER_654321 ; - is( $NUMBER_654321, maxsize_setting( $mysync ), - 'maxsize_setting: --maxsize 654321 APPENDLIMIT 123456 => 654321 ' + is( $NUMBER_123456, maxsize_setting( $mysync ), + 'maxsize_setting: APPENDLIMIT 123456 --maxsize 654321 => 123456 ' ) ; + # Now --truncmess stuff + + + note( 'Leaving tests_maxsize_setting()' ) ; return ; } +# Three variables to take account of +# appendlimit (given by --appendlimit or CAPABILITY...) +# maxsize +# truncmess + sub maxsize_setting { my $mysync = shift || return ; - $mysync->{ appendlimit } = appendlimit( $mysync ) ; - - my $maxsize ; - $maxsize = maxsize_setting_when_no_maxsize_but_appendlimit( $mysync ) ; - if ( defined $maxsize ) { return $maxsize ; } - - $maxsize = maxsize_setting_when_maxsize_but_no_appendlimit( $mysync ) ; - if ( defined $maxsize ) { return $maxsize ; } - - $maxsize = maxsize_setting_when_maxsize_over_appendlimit( $mysync ) ; - if ( defined $maxsize ) { return $maxsize ; } - - return $mysync->{ maxsize } ; -} - -sub maxsize_setting_when_maxsize_but_no_appendlimit -{ - my $mysync = shift || return ; - if ( defined $mysync->{ appendlimit } ) { - return ; + myprint( "Host2: Getting appendlimit from --appendlimit $mysync->{ appendlimit }\n" ) ; + } + else + { + $mysync->{ appendlimit } = appendlimit( $mysync ) ; } - if ( ! defined( $mysync->{ maxsize } ) ) + + if ( all_defined( $mysync->{ appendlimit }, $mysync->{ maxsize } ) ) + { + my $min_maxsize_appendlimit = min( $mysync->{ maxsize }, $mysync->{ appendlimit } ) ; + myprint( "Host2: Setting maxsize to $min_maxsize_appendlimit (min of --maxsize $mysync->{ maxsize } and appendlimit $mysync->{ appendlimit }\n" ) ; + $mysync->{ maxsize } = $min_maxsize_appendlimit ; + return $mysync->{ maxsize } ; + } + elsif ( defined $mysync->{ appendlimit } ) + { + myprint( "Host2: Setting maxsize to appendlimit $mysync->{ appendlimit }\n" ) ; + $mysync->{ maxsize } = $mysync->{ appendlimit } ; + return $mysync->{ maxsize } ; + }elsif ( defined $mysync->{ maxsize } ) + { + return $mysync->{ maxsize } ; + }else { return ; } - - - return $mysync->{ maxsize } ; } -sub maxsize_setting_when_no_maxsize_but_appendlimit -{ - my $mysync = shift || return ; - - if ( ! defined $mysync->{ appendlimit } ) - { - return ; - } - - if ( defined( $mysync->{ maxsize } ) ) - { - return ; - } - - my $limit = $mysync->{ maxsize } = $mysync->{ appendlimit } ; - myprint( "Setting --maxsize from APPENDLIMIT=$limit\n" ) ; - return $mysync->{ maxsize } ; -} - - -sub maxsize_setting_when_maxsize_over_appendlimit -{ - my $mysync = shift || return ; - - if ( ! defined $mysync->{ appendlimit } ) - { - return ; - } - - if ( ! defined( $mysync->{ maxsize } ) ) - { - return ; - } - - my $limit = $mysync->{ appendlimit } ; - my $maxsize = $mysync->{ maxsize } ; - - if ( $mysync->{ maxsize } > $mysync->{ appendlimit } ) - { - myprint( "Warn: --maxsize $maxsize is > APPENDLIMIT=$limit " - . "found in CAPABILITY." - . " Big messages might not pass, ie, APPEND errors might occur.\n" ) ; - } - - return ; -} - -sub maxsize_setting_when_maxsize_under_appendlimit -{ - my $mysync = shift ; - - - return ; -} - - - sub all_defined @@ -3507,7 +3698,7 @@ sub imapsync_id vendor => 'Gilles LAMIRAL', 'support-url' => 'https://imapsync.lamiral.info/', # Example of date-time: 19-Sep-2015 08:56:07 - date => date_from_rcs( q{$Date: 2019/02/18 10:21:03 $ } ), + date => date_from_rcs( q{$Date: 2019/06/26 19:30:56 $ } ), } ; my $imapsync_id_github = { @@ -3516,7 +3707,7 @@ sub imapsync_id os => $OSNAME, vendor => 'github', 'support-url' => 'https://github.com/imapsync/imapsync', - date => date_from_rcs( q{$Date: 2019/02/18 10:21:03 $ } ), + date => date_from_rcs( q{$Date: 2019/06/26 19:30:56 $ } ), } ; $imapsync_id = $imapsync_id_lamiral ; @@ -3584,7 +3775,7 @@ sub tests_format_for_imap_arg sub quota { - my ( $imap, $side, $mysync ) = @_ ; + my ( $mysync, $imap, $side ) = @_ ; my %side = ( h1 => 'Host1', @@ -3604,8 +3795,8 @@ sub quota #$imap->quota( '""' ) ; myprint( "\n" ) ; $imap->Debug( $debug_before ) ; - my $quota_limit_bytes = quota_extract_storage_limit_in_bytes( $getquotaroot ) ; - my $quota_current_bytes = quota_extract_storage_current_in_bytes( $getquotaroot ) ; + my $quota_limit_bytes = quota_extract_storage_limit_in_bytes( $mysync, $getquotaroot ) ; + my $quota_current_bytes = quota_extract_storage_current_in_bytes( $mysync, $getquotaroot ) ; $mysync->{$side}->{quota_limit_bytes} = $quota_limit_bytes ; $mysync->{$side}->{quota_current_bytes} = $quota_current_bytes ; my $quota_percent ; @@ -3624,28 +3815,30 @@ sub quota sub tests_quota_extract_storage_limit_in_bytes { - note( 'Entering tests_quota_extract_storage_limit_in_bytes()' ) ; + note( 'Entering tests_quota_extract_storage_limit_in_bytes()' ) ; + my $mysync = {} ; my $imap_output = [ '* QUOTAROOT "INBOX" "Storage quota" "Messages quota"', '* QUOTA "Storage quota" (STORAGE 1 104857600)', '* QUOTA "Messages quota" (MESSAGE 2 100000)', '5 OK Getquotaroot completed.' ] ; - ok( $NUMBER_104_857_600 * $KIBI == quota_extract_storage_limit_in_bytes( $imap_output ), 'quota_extract_storage_limit_in_bytes ') ; + ok( $NUMBER_104_857_600 * $KIBI == quota_extract_storage_limit_in_bytes( $mysync, $imap_output ), 'quota_extract_storage_limit_in_bytes ') ; - note( 'Leaving tests_quota_extract_storage_limit_in_bytes()' ) ; + note( 'Leaving tests_quota_extract_storage_limit_in_bytes()' ) ; return ; } sub quota_extract_storage_limit_in_bytes { + my $mysync = shift ; my $imap_output = shift ; my $limit_kb ; $limit_kb = ( map { /.*\(\s*STORAGE\s+\d+\s+(\d+)\s*\)/x ? $1 : () } @{ $imap_output } )[0] ; $limit_kb ||= 0 ; - $debug and myprint( "storage_limit_kb = $limit_kb\n" ) ; + $mysync->{ debug } and myprint( "storage_limit_kb = $limit_kb\n" ) ; return( $KIBI * $limit_kb ) ; } @@ -3654,14 +3847,14 @@ sub tests_quota_extract_storage_current_in_bytes { note( 'Entering tests_quota_extract_storage_current_in_bytes()' ) ; - + my $mysync = {} ; my $imap_output = [ '* QUOTAROOT "INBOX" "Storage quota" "Messages quota"', '* QUOTA "Storage quota" (STORAGE 1 104857600)', '* QUOTA "Messages quota" (MESSAGE 2 100000)', '5 OK Getquotaroot completed.' ] ; - ok( 1*$KIBI == quota_extract_storage_current_in_bytes( $imap_output ), 'quota_extract_storage_current_in_bytes: 1 => 1024 ') ; + ok( 1*$KIBI == quota_extract_storage_current_in_bytes( $mysync, $imap_output ), 'quota_extract_storage_current_in_bytes: 1 => 1024 ') ; note( 'Leaving tests_quota_extract_storage_current_in_bytes()' ) ; return ; @@ -3669,12 +3862,13 @@ sub tests_quota_extract_storage_current_in_bytes sub quota_extract_storage_current_in_bytes { + my $mysync = shift ; my $imap_output = shift ; my $current_kb ; $current_kb = ( map { /.*\(\s*STORAGE\s+(\d+)\s+\d+\s*\)/x ? $1 : () } @{ $imap_output } )[0] ; $current_kb ||= 0 ; - $debug and myprint( "storage_current_kb = $current_kb\n" ) ; + $mysync->{ debug } and myprint( "storage_current_kb = $current_kb\n" ) ; return( $KIBI * $current_kb ) ; } @@ -3691,8 +3885,8 @@ sub automap return ; } - $mysync->{h1_special} = special_from_folders_hash( $mysync->{imap1}, 'Host1' ) ; - $mysync->{h2_special} = special_from_folders_hash( $mysync->{imap2}, 'Host2' ) ; + $mysync->{h1_special} = special_from_folders_hash( $mysync, $mysync->{imap1}, 'Host1' ) ; + $mysync->{h2_special} = special_from_folders_hash( $mysync, $mysync->{imap2}, 'Host2' ) ; build_possible_special( $mysync ) ; build_guess_special( $mysync ) ; @@ -3767,7 +3961,7 @@ sub tests_guess_special sub build_automap { my $mysync = shift ; - $debug and myprint( "Entering build_automap\n" ) ; + $mysync->{ debug } and myprint( "Entering build_automap\n" ) ; foreach my $h1_fold ( @{ $mysync->{h1_folders_wanted} } ) { my $h2_fold ; my $h1_special = $mysync->{h1_special}{$h1_fold} ; @@ -3826,7 +4020,9 @@ sub build_possible_special $possible_special->{'\Archive'} = [ 'Archive', 'Archives', '&BBAEQARFBDgEMg-' ] ; $possible_special->{'\Drafts'} = [ 'Drafts', 'DRAFTS', '&BCcENQRABD0EPgQyBDgEOgQ4-', 'Szkice', 'Wersje robocze' ] ; $possible_special->{'\Flagged'} = [ 'Flagged', 'Starred', '&BB8EPgQ8BDUERwQ1BD0EPQRLBDU-' ] ; - $possible_special->{'\Junk'} = [ 'Junk', 'Spam', 'SPAM', '&BCEEPwQwBDw-', 'Potwierdzony spam', 'Wiadomo&AVs-ci-&AVs-mieci' ] ; + $possible_special->{'\Junk'} = [ 'Junk', 'junk', 'Spam', 'SPAM', '&BCEEPwQwBDw-', + 'Potwierdzony spam', 'Wiadomo&AVs-ci-&AVs-mieci', + 'Junk E-Mail', 'Junk Email'] ; $possible_special->{'\Sent'} = [ 'Sent', 'Sent Messages', 'Sent Items', 'Gesendete Elemente', 'Gesendete Objekte', '&AMk-l&AOk-ments envoy&AOk-s', 'Envoy&AOk-', 'Objets envoy&AOk-s', @@ -3834,7 +4030,10 @@ sub build_possible_special '&kAFP4W4IMH8wojCkMMYw4A-', '&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-', 'Elementy wys&AUI-ane'] ; - $possible_special->{'\Trash'} = [ 'Trash', 'TRASH', '&BCMENAQwBDsENQQ9BD0ESwQ1-', '&BBoEPgRABDcEOAQ9BDA-', 'Kosz', 'Deleted Items' ] ; + $possible_special->{'\Trash'} = [ 'Trash', 'TRASH', + '&BCMENAQwBDsENQQ9BD0ESwQ1-', '&BBoEPgRABDcEOAQ9BDA-', + 'Kosz', + 'Deleted Items', 'Deleted Messages' ] ; foreach my $special ( qw( \All \Archive \Drafts \Flagged \Junk \Sent \Trash ) ){ @@ -3843,13 +4042,34 @@ sub build_possible_special } ; } $mysync->{possible_special} = $possible_special ; - $debug and myprint( Data::Dumper->Dump( [ $possible_special ], [ 'possible_special' ] ) ) ; + $mysync->{ debug } and myprint( Data::Dumper->Dump( [ $possible_special ], [ 'possible_special' ] ) ) ; return( $possible_special ) ; } +sub tests_special_from_folders_hash +{ + note( 'Entering tests_special_from_folders_hash()' ) ; + + my $mysync = {} ; + require_ok( "Test::MockObject" ) ; + my $imapT = Test::MockObject->new( ) ; + + is( undef, special_from_folders_hash( ), 'special_from_folders_hash: no args' ) ; + is( undef, special_from_folders_hash( $mysync ), 'special_from_folders_hash: undef args' ) ; + is_deeply( {}, special_from_folders_hash( $mysync, $imapT ), 'special_from_folders_hash: $imap void' ) ; + + $imapT->mock( 'folders_hash', sub { return( [ { name => 'Sent', attrs => [ '\Sent' ] } ] ) } ) ; + + is_deeply( { Sent => '\Sent', '\Sent' => 'Sent' }, + special_from_folders_hash( $mysync, $imapT ), 'special_from_folders_hash: $imap \Sent' ) ; + + note( 'Leaving tests_special_from_folders_hash()' ) ; + return( ) ; +} + sub special_from_folders_hash { - my ( $imap, $side ) = @_ ; + my ( $mysync, $imap, $side ) = @_ ; my %special = ( ) ; if ( ! defined $imap ) { return ; } @@ -3857,7 +4077,7 @@ sub special_from_folders_hash if ( ! $imap->can( 'folders_hash' ) ) { my $error = "$side: To have automagic rfc6154 folder mapping, upgrade Mail::IMAPClient >= 3.34\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; return( \%special ) ; # empty hash ref } my $folders_hash = $imap->folders_hash( ) ; @@ -3880,27 +4100,10 @@ sub special_from_folders_hash return( \%special ) ; } -sub tests_special_from_folders_hash -{ - note( 'Entering tests_special_from_folders_hash()' ) ; - - require_ok( "Test::MockObject" ) ; - my $imapT = Test::MockObject->new( ) ; - - is( undef, special_from_folders_hash( ), 'special_from_folders_hash: no args' ) ; - is_deeply( {}, special_from_folders_hash( $imapT ), 'special_from_folders_hash: $imap void' ) ; - - $imapT->mock( 'folders_hash', sub { return( [ { name => 'Sent', attrs => [ '\Sent' ] } ] ) } ) ; - is_deeply( { Sent => '\Sent', '\Sent' => 'Sent' }, special_from_folders_hash( $imapT ), 'special_from_folders_hash: $imap \Sent' ) ; - - note( 'Leaving tests_special_from_folders_hash()' ) ; - return( ) ; -} - sub errors_incr { my ( $mysync, @error ) = @ARG ; - $sync->{nb_errors}++ ; + $mysync->{nb_errors}++ ; if ( @error ) { errors_log( $mysync, @error ) ; @@ -3908,10 +4111,10 @@ sub errors_incr } $mysync->{errorsmax} ||= $ERRORS_MAX ; - if ( $sync->{nb_errors} >= $mysync->{errorsmax} ) { + if ( $mysync->{nb_errors} >= $mysync->{errorsmax} ) { myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to any value, for example 100 with --errorsmax 100 ). Exiting.\n" ) ; if ( $mysync->{errorsdump} ) { - myprint( errorsdump( $sync->{nb_errors}, errors_log( $mysync ) ) ) ; + myprint( errorsdump( $mysync->{nb_errors}, errors_log( $mysync ) ) ) ; # again since errorsdump( ) can be very verbose and masquerade previous warning myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to any value, for example 100 with --errorsmax 100 ). Exiting.\n" ) ; } @@ -3920,6 +4123,21 @@ sub errors_incr return ; } +sub tests_errors_log +{ + note( 'Entering tests_errors_log()' ) ; + is( undef, errors_log( ), 'errors_log: no args => undef' ) ; + my $mysync = {} ; + is( undef, errors_log( $mysync ), 'errors_log: empty => undef' ) ; + is_deeply( [ 'aieaie' ], [ errors_log( $mysync, 'aieaie' ) ], 'errors_log: aieaie => aieaie' ) ; + # cumulative + is_deeply( [ 'aieaie' ], [ errors_log( $mysync ) ], 'errors_log: nothing more => aieaie' ) ; + is_deeply( [ 'aieaie', 'ouille' ], [ errors_log( $mysync, 'ouille' ) ], 'errors_log: ouille => aieaie ouille' ) ; + is_deeply( [ 'aieaie', 'ouille' ], [ errors_log( $mysync ) ], 'errors_log: nothing more => aieaie ouille' ) ; + note( 'Leaving tests_errors_log()' ) ; + return ; +} + sub errors_log { my ( $mysync, @error ) = @ARG ; @@ -3939,15 +4157,6 @@ sub errors_log } } -sub tests_errors_log -{ - note( 'Entering tests_errors_log()' ) ; - - - note( 'Leaving tests_errors_log()' ) ; - return ; -} - sub errorsdump { @@ -3981,32 +4190,34 @@ sub tests_live_result sub foldersizesatend { + my $mysync = shift ; timenext( ) ; - return if ( $sync->{imap1}->IsUnconnected( ) ) ; - return if ( $sync->{imap2}->IsUnconnected( ) ) ; + return if ( $mysync->{imap1}->IsUnconnected( ) ) ; + return if ( $mysync->{imap2}->IsUnconnected( ) ) ; # Get all folders on host2 again since new were created - @h2_folders_all = sort $sync->{imap2}->folders(); + @h2_folders_all = sort $mysync->{imap2}->folders(); for ( @h2_folders_all ) { $h2_folders_all{ $_ } = 1 ; $h2_folders_all_UPPER{ uc $_ } = 1 ; } ; - ( $h1_nb_msg_end, $h1_bytes_end ) = foldersizes( 'Host1', $sync->{imap1}, $search1, $sync->{abletosearch1}, @h1_folders_wanted ) ; - ( $h2_nb_msg_end, $h2_bytes_end ) = foldersizes( 'Host2', $sync->{imap2}, $search2, $sync->{abletosearch2}, @h2_folders_from_1_wanted ) ; + ( $h1_nb_msg_end, $h1_bytes_end ) = foldersizes( 'Host1', $mysync->{imap1}, $search1, $mysync->{abletosearch1}, @h1_folders_wanted ) ; + ( $h2_nb_msg_end, $h2_bytes_end ) = foldersizes( 'Host2', $mysync->{imap2}, $search2, $mysync->{abletosearch2}, @h2_folders_from_1_wanted ) ; if ( not all_defined( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) ) { my $error = "Failure getting foldersizes, final differences will not be calculated\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; } return ; } sub size_filtered_flag { + my $mysync = shift ; my $h1_size = shift ; - if (defined $sync->{ maxsize } and $h1_size >= $sync->{ maxsize }) { + if ( defined $mysync->{ maxsize } and $h1_size >= $mysync->{ maxsize } ) { return( 1 ) ; } - if (defined $minsize and $h1_size <= $minsize) { + if ( defined $minsize and $h1_size <= $minsize ) { return( 1 ) ; } return( 0 ) ; @@ -4020,7 +4231,7 @@ sub sync_flags_fir if ( not defined $h2_msg ) { return } ; my $h1_size = $h1_fir_ref->{$h1_msg}->{'RFC822.SIZE'} ; - return if size_filtered_flag( $h1_size ) ; + return if size_filtered_flag( $mysync, $h1_size ) ; # used cached flag values for efficiency my $h1_flags = $h1_fir_ref->{ $h1_msg }->{ 'FLAGS' } || q{} ; @@ -4038,7 +4249,7 @@ sub sync_flags_after_copy if ( my @h2_flags = $mysync->{imap2}->flags( $h2_msg ) ) { my $h2_flags = "@h2_flags" ; - ( $debug or $debugflags ) and myprint( "Host2: msg $h2_fold/$h2_msg flags before sync flags after copy ( $h2_flags )\n" ) ; + ( $mysync->{ debug } or $debugflags ) and myprint( "Host2: msg $h2_fold/$h2_msg flags before sync flags after copy ( $h2_flags )\n" ) ; sync_flags( $mysync, $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $h2_flags, $permanentflags2 ) ; }else{ myprint( "Host2: msg $h2_fold/$h2_msg could not get its flags for sync flags after copy\n" ) ; @@ -4056,14 +4267,14 @@ sub sync_flags { my( $mysync, $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $h2_flags, $permanentflags2 ) = @_ ; - ( $debug or $debugflags ) and + ( $mysync->{ debug } or $debugflags ) and myprint( "Host1: flags init msg $h1_fold/$h1_msg flags( $h1_flags ) Host2 msg $h2_fold/$h2_msg flags( $h2_flags )\n" ) ; $h1_flags = flags_for_host2( $h1_flags, $permanentflags2 ) ; $h2_flags = flagscase( $h2_flags ) ; - ( $debug or $debugflags ) and + ( $mysync->{ debug } or $debugflags ) and myprint( "Host1: flags filt msg $h1_fold/$h1_msg flags( $h1_flags ) Host2 msg $h2_fold/$h2_msg flags( $h2_flags )\n" ) ; @@ -4072,7 +4283,7 @@ sub sync_flags my @h2_flags = sort split(q{ }, $h2_flags ); my $diff = compare_lists( \@h1_flags, \@h2_flags ); - $diff and ( $debug or $debugflags ) + $diff and ( $mysync->{ debug } or $debugflags ) and myprint( "Host2: flags msg $h2_fold/$h2_msg replacing h2 flags( $h2_flags ) with h1 flags( $h1_flags )\n" ) ; # This sets flags exactly. So flags can be removed with this. @@ -4093,10 +4304,11 @@ sub sync_flags sub _filter { + my $mysync = shift ; my $str = shift or return q{} ; my $sz = $SIZE_MAX_STR ; my $len = length $str ; - if ( not $debug and $len > $sz*2 ) { + if ( not $mysync->{ debug } and $len > $sz*2 ) { my $beg = substr $str, 0, $sz ; my $end = substr $str, -$sz, $sz ; $str = $beg . '...' . $end ; @@ -4109,16 +4321,16 @@ sub _filter sub lost_connection { - my( $imap, $error_message ) = @_; + my( $mysync, $imap, $error_message ) = @_; if ( $imap->IsUnconnected( ) ) { - $sync->{nb_errors}++ ; + $mysync->{nb_errors}++ ; my $lcomm = $imap->LastIMAPCommand || q{} ; my $einfo = $imap->LastError || @{$imap->History}[$LAST] || q{} ; # if string is long try reduce to a more reasonable size - $lcomm = _filter( $lcomm ) ; - $einfo = _filter( $einfo ) ; - myprint( "Failure: last command: $lcomm\n") if ($debug && $lcomm) ; + $lcomm = _filter( $mysync, $lcomm ) ; + $einfo = _filter( $mysync, $einfo ) ; + myprint( "Failure: last command: $lcomm\n") if ( $mysync->{ debug } && $lcomm) ; myprint( "Failure: lost connection $error_message: ", $einfo, "\n") ; return( 1 ) ; } @@ -4276,7 +4488,8 @@ sub min sub check_lib_version { - $debug and myprint( "IMAPClient $Mail::IMAPClient::VERSION\n" ) ; + my $mysync = shift ; + $mysync->{ debug } and myprint( "IMAPClient $Mail::IMAPClient::VERSION\n" ) ; if ( '2.2.9' eq $Mail::IMAPClient::VERSION ) { myprint( "imapsync no longer supports Mail::IMAPClient 2.2.9, upgrade it\n" ) ; return 0 ; @@ -4350,13 +4563,54 @@ sub modulesversion } +sub tests_command_line_nopassword +{ + note( 'Entering tests_command_line_nopassword()' ) ; + + ok( q{} eq command_line_nopassword(), 'command_line_nopassword void' ); + my $mysync = {} ; + ok( '--blabla' eq command_line_nopassword( $mysync, '--blabla' ), 'command_line_nopassword --blabla' ); + #myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ; + ok( '--password1 MASKED' eq command_line_nopassword( $mysync, qw{ --password1 secret1}), 'command_line_nopassword --password1' ); + ok( '--blabla --password1 MASKED --blibli' + eq command_line_nopassword( $mysync, qw{ --blabla --password1 secret1 --blibli } ), 'command_line_nopassword --password1 --blibli' ); + $mysync->{showpasswords} = 1 ; + ok( q{} eq command_line_nopassword(), 'command_line_nopassword void' ); + ok( '--blabla' eq command_line_nopassword( $mysync, '--blabla'), 'command_line_nopassword --blabla' ); + #myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ; + ok( '--password1 secret1' eq command_line_nopassword( $mysync, qw{ --password1 secret1} ), 'command_line_nopassword --password1' ); + ok( '--blabla --password1 secret1 --blibli' + eq command_line_nopassword( $mysync, qw{ --blabla --password1 secret1 --blibli } ), 'command_line_nopassword --password1 --blibli' ); + + note( 'Leaving tests_command_line_nopassword()' ) ; + return ; +} + # Construct a command line copy with passwords replaced by MASKED. sub command_line_nopassword { - my @argv = @_ ; + my $mysync = shift @ARG ; + my @argv = @ARG ; my @argv_nopassword ; - return( "@argv" ) if $sync->{showpasswords} ; + if ( $mysync->{ cmdcgi } ) { + @argv_nopassword = mask_password_value( @{ $mysync->{ cmdcgi } } ) ; + return( "@argv_nopassword" ) ; + } + + if ( $mysync->{showpasswords} ) + { + return( "@argv" ) ; + } + + @argv_nopassword = mask_password_value( @argv ) ; + return("@argv_nopassword") ; +} + +sub mask_password_value +{ + my @argv = @ARG ; + my @argv_nopassword ; while ( @argv ) { my $arg = shift @argv ; # option name or value if ( $arg =~ m/-password[12]/x ) { @@ -4366,30 +4620,9 @@ sub command_line_nopassword push @argv_nopassword, $arg ; # same option or value } } - return("@argv_nopassword") ; + return @argv_nopassword ; } -sub tests_command_line_nopassword -{ - note( 'Entering tests_command_line_nopassword()' ) ; - - ok(q{} eq command_line_nopassword(), 'command_line_nopassword void'); - ok('--blabla' eq command_line_nopassword('--blabla'), 'command_line_nopassword --blabla'); - #myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ; - ok('--password1 MASKED' eq command_line_nopassword(qw{ --password1 secret1}), 'command_line_nopassword --password1'); - ok('--blabla --password1 MASKED --blibli' - eq command_line_nopassword(qw{ --blabla --password1 secret1 --blibli }), 'command_line_nopassword --password1 --blibli'); - $sync->{showpasswords} = 1 ; - ok(q{} eq command_line_nopassword(), 'command_line_nopassword void'); - ok('--blabla' eq command_line_nopassword('--blabla'), 'command_line_nopassword --blabla'); - #myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ; - ok('--password1 secret1' eq command_line_nopassword(qw{ --password1 secret1}), 'command_line_nopassword --password1'); - ok('--blabla --password1 secret1 --blibli' - eq command_line_nopassword(qw{ --blabla --password1 secret1 --blibli }), 'command_line_nopassword --password1 --blibli'); - - note( 'Leaving tests_command_line_nopassword()' ) ; - return ; -} sub tests_get_stdin_masked { @@ -4529,6 +4762,67 @@ FIN_PASSFILE + +sub remove_tmp_files +{ + my $mysync = shift or return ; + $mysync->{pidfile} or return ; + if ( -e $mysync->{pidfile} ) { + unlink $mysync->{pidfile} ; + } + return ; +} + +sub cleanup_before_exit +{ + my $mysync = shift ; + remove_tmp_files( $mysync ) ; + if ( $mysync->{imap1} and $mysync->{imap1}->IsConnected() ) + { + myprint( "Disconnecting from host1 $mysync->{ host1 } user1 $mysync->{ user1 }\n" ) ; + $mysync->{imap1}->logout( ) ; + } + if ( $mysync->{imap2} and $mysync->{imap2}->IsConnected() ) + { + myprint( "Disconnecting from host2 $mysync->{ host2 } user2 $mysync->{ user2 }\n" ) ; + $mysync->{imap2}->logout( ) ; + } + if ( $mysync->{log} ) { + myprint( "Log file is $mysync->{logfile} ( to change it, use --logfile filepath ; or use --nolog to turn off logging )\n" ) ; + } + if ( $mysync->{log} and $mysync->{logfile_handle} ) { + #myprint( "Closing $mysync->{ logfile }\n" ) ; + close $mysync->{logfile_handle} ; + } + return ; +} + + + +sub exit_clean +{ + my $mysync = shift @ARG ; + my $status = shift @ARG ; + my @messages = @ARG ; + if ( @messages ) + { + myprint( @messages ) ; + } + myprint( "Exiting with return value $status ($EXIT_TXT{$status})\n" ) ; + cleanup_before_exit( $mysync ) ; + + exit $status ; +} + +sub missing_option +{ + my $mysync = shift ; + my $option = shift ; + exit_clean( $mysync, $EX_USAGE, "$option option is mandatory, for help run $PROGRAM_NAME --help\n" ) ; + return ; +} + + sub catch_ignore { my $mysync = shift ; @@ -4549,14 +4843,26 @@ sub catch_exit if ( $signame ) { myprint( "\nGot a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ), "). Asked to terminate\n" ) ; + if ( $mysync->{stats} ) { + myprint( "Here are the final stats of this sync not completely finished so far\n" ) ; + stats( $mysync ) ; + myprint( "Ended by a signal $signame (my PID is $PROCESS_ID my PPID is ", + getppid( ), "). I am asked to terminate immediately.\n" ) ; + myprint( "You should resynchronize those accounts by running a sync again,\n", + "since some messages and entire folders might still be missing on host2.\n" ) ; + } + ## no critic (RequireLocalizedPunctuationVars) + $SIG{ $signame } = 'DEFAULT'; # restore default action + # kill myself with $signame + # https://www.cons.org/cracauer/sigint.html + myprint( "Killing myself with signal $signame\n" ) ; + cleanup_before_exit( $mysync ) ; + kill( $signame, $PROCESS_ID ) ; + } + else + { + exit_clean( $mysync, $EXIT_BY_SIGNAL ) ; } - myprint( "Here are the final stats of this sync not completely finished so far\n" ) ; - stats( $mysync ) ; - myprint( "Ended by a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ), - "). I am asked to terminate immediately.\n" ) ; - myprint( "You should resynchronize those accounts by running a sync again,\n", - "since some messages and entire folders might still be missing on host2.\n" ) ; - exit_clean( $mysync, $EXIT_BY_SIGNAL ) ; return ; } @@ -4577,30 +4883,43 @@ sub catch_reconnect { my $mysync = shift ; my $signame = shift ; - myprint( "\nGot a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ), ")\n", - "Hit 2 ctr-c within 2 seconds to exit the program\n", - "Hit only 1 ctr-c to reconnect to both imap servers\n", - ) ; if ( here_twice( $mysync ) ) { myprint( "Got two signals $signame within $INTERVAL_TO_EXIT seconds. Exiting...\n" ) ; catch_exit( $mysync, $signame ) ; }else{ + myprint( "\nGot a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ), ")\n", + "Hit 2 ctr-c within 2 seconds to exit the program\n", + "Hit only 1 ctr-c to reconnect to both imap servers\n", + ) ; myprint( "For now only one signal $signame within $INTERVAL_TO_EXIT seconds.\n" ) ; + + if ( ! defined $mysync->{imap1} ) { return ; } + if ( ! defined $mysync->{imap2} ) { return ; } + + myprint( "Info: reconnecting to host1 imap server $mysync->{host1}\n" ) ; + $mysync->{imap1}->State( Mail::IMAPClient::Unconnected ) ; + $mysync->{imap1}->{IMAPSYNC_RECONNECT_COUNT} += 1 ; + if ( $mysync->{imap1}->reconnect( ) ) + { + myprint( "Info: reconnected to host1 imap server $mysync->{host1}\n" ) ; + } + else + { + exit_clean( $mysync, $EXIT_CONNECTION_FAILURE ) ; + } + myprint( "Info: reconnecting to host2 imap server\n" ) ; + $mysync->{imap2}->State( Mail::IMAPClient::Unconnected ) ; + $mysync->{imap2}->{IMAPSYNC_RECONNECT_COUNT} += 1 ; + if ( $mysync->{imap2}->reconnect( ) ) + { + myprint( "Info: reconnected to host2 imap server $mysync->{host2}\n" ) ; + } + else + { + exit_clean( $mysync, $EXIT_CONNECTION_FAILURE ) ; + } + myprint( "Info: reconnected to both imap servers\n" ) ; } - - if ( ! defined $mysync->{imap1} ) { return ; } - if ( ! defined $mysync->{imap2} ) { return ; } - - - myprint( "Info: reconnecting to host1 imap server\n" ) ; - $mysync->{imap1}->State( Mail::IMAPClient::Unconnected ) ; - $mysync->{imap1}->{IMAPSYNC_RECONNECT_COUNT} += 1 ; - $mysync->{imap1}->reconnect( ) ; - myprint( "Info: reconnecting to host2 imap server\n" ) ; - $mysync->{imap2}->State( Mail::IMAPClient::Unconnected ) ; - $mysync->{imap2}->{IMAPSYNC_RECONNECT_COUNT} += 1 ; - $mysync->{imap2}->reconnect( ) ; - myprint( "Info: reconnected to both imap servers\n" ) ; return ; } @@ -4706,16 +5025,55 @@ sub here_twice } -sub justconnect +sub justconnect { - - $sync->{imap1} = connect_imap( $sync->{host1}, $sync->{port1}, $debugimap1, $sync->{ssl1}, $sync->{tls1}, 'Host1', $sync->{h1}->{timeout}, $sync->{h1} ) ; - $sync->{imap2} = connect_imap( $sync->{host2}, $sync->{port2}, $debugimap2, $sync->{ssl2}, $sync->{tls2}, 'Host2', $sync->{h2}->{timeout}, $sync->{h2} ) ; - $sync->{imap1}->logout( ) ; - $sync->{imap2}->logout( ) ; - return ; + my $mysync = shift ; + my $justconnect1 = justconnect1( $sync ) ; + my $justconnect2 = justconnect2( $sync ) ; + return "$justconnect1 $justconnect2"; } +sub justconnect1 +{ + my $mysync = shift ; + if ( $mysync->{host1} ) + { + myprint( "Host1: Will just connect to $mysync->{host1} without login\n" ) ; + $mysync->{imap1} = connect_imap( + $mysync->{host1}, $mysync->{port1}, $debugimap1, + $mysync->{ssl1}, $mysync->{tls1}, 'Host1', + $mysync->{h1}->{timeout}, $mysync->{h1} ) ; + + $mysync->{imap1}->logout( ) ; + return $mysync->{host1} ; + } + + return '' ; +} + +sub justconnect2 +{ + my $mysync = shift ; + if ( $mysync->{host2} ) + { + myprint( "Host2: Will just connect to $mysync->{host2} without login\n" ) ; + $mysync->{imap2} = connect_imap( + $mysync->{host2}, $mysync->{port2}, $debugimap2, + $mysync->{ssl2}, $mysync->{tls2}, 'Host2', + $mysync->{h2}->{timeout}, $mysync->{h2} ) ; + + $mysync->{imap2}->logout( ) ; + return $mysync->{host2} ; + } + + return '' ; +} + +sub skip_macosx +{ + return ; + # return( 'macosx.polarhome.com' eq hostname() ) ; +} sub tests_mailimapclient_connect { @@ -4727,7 +5085,8 @@ sub tests_mailimapclient_connect is( 'Mail::IMAPClient', ref( $imap ), 'mailimapclient_connect ipv4: ref is Mail::IMAPClient' ) ; # Mail::IMAPClient 3.40 die on this... So we skip it, thanks to "mature" IO::Socket::IP - # is( undef, $imap->connect( ), 'mailimapclient_connect ipv4: connect with no server => failure' ) ; + # Mail::IMAPClient 3.42 is ok so this test is back. + is( undef, $imap->connect( ), 'mailimapclient_connect ipv4: connect with no server => failure' ) ; is( 'test.lamiral.info', $imap->Server( 'test.lamiral.info' ), 'mailimapclient_connect ipv4: setting Server(test.lamiral.info)' ) ; @@ -4754,10 +5113,13 @@ sub tests_mailimapclient_connect ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE ] ), 'mailimapclient_connect ipv6 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ; is( 993, $imap->Port( 993 ), 'mailimapclient_connect ipv6 + ssl: setting Port( 993 )' ) ; SKIP: { - if ( - 'CUILLERE' eq hostname() - or - 'macosx.polarhome.com' eq hostname() ) + if ( + 'CUILLERE' eq hostname() + or + skip_macosx() + or + -e '/.dockerenv' + ) { skip( 'Tests avoided on CUILLERE can not do ipv6', 2 ) ; } @@ -4771,6 +5133,7 @@ sub tests_mailimapclient_connect return ; } + sub tests_mailimapclient_connect_bug { note( 'Entering tests_mailimapclient_connect_bug()' ) ; @@ -4778,35 +5141,31 @@ sub tests_mailimapclient_connect_bug my $imap ; # ipv6 - ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect ipv6: new' ) ; - is( 'ks2ipv6.lamiral.info', $imap->Server( 'ks2ipv6.lamiral.info' ), 'mailimapclient_connect ipv6: setting Server(ks2ipv6.lamiral.info)' ) ; - is( 143, $imap->Port( 143 ), 'mailimapclient_connect ipv6: setting Port( 993 )' ) ; + ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect_bug ipv6: new' ) ; + is( 'ks2ipv6.lamiral.info', $imap->Server( 'ks2ipv6.lamiral.info' ), 'mailimapclient_connect_bug ipv6: setting Server(ks2ipv6.lamiral.info)' ) ; + is( 143, $imap->Port( 143 ), 'mailimapclient_connect_bug ipv6: setting Port( 993 )' ) ; SKIP: { - if ( - 'CUILLERE' eq hostname() - or - 'macosx.polarhome.com' eq hostname() ) + if ( + 'CUILLERE' eq hostname() + or + skip_macosx() + or + -e '/.dockerenv' + ) { skip( 'Tests avoided on CUILLERE can not do ipv6', 1 ) ; } - like( ref( $imap->connect( ) ), qr/IO::Socket::INET/, 'mailimapclient_connect ipv6: connect to ks2ipv6.lamiral.info' ) - or diag( 'mailimapclient_connect ipv6: ', $imap->LastError( ), $!, ) ; + like( ref( $imap->connect( ) ), qr/IO::Socket::INET/, 'mailimapclient_connect_bug ipv6: connect to ks2ipv6.lamiral.info' ) + or diag( 'mailimapclient_connect_bug ipv6: ', $imap->LastError( ), $!, ) ; } - #is( $imap->logout( ), undef, 'mailimapclient_connect ipv6: logout in ssl causes failure' ) ; - is( undef, undef $imap, 'mailimapclient_connect ipv6: free variable' ) ; + #is( $imap->logout( ), undef, 'mailimapclient_connect_bug ipv6: logout in ssl causes failure' ) ; + is( undef, undef $imap, 'mailimapclient_connect_bug ipv6: free variable' ) ; note( 'Leaving tests_mailimapclient_connect_bug()' ) ; return ; } -sub mailimapclient_connect -{ - - - return ; -} - sub tests_connect_socket @@ -4818,12 +5177,15 @@ sub tests_connect_socket my $socket ; my $imap ; SKIP: { - if ( - 'CUILLERE' eq hostname() - or - 'macosx.polarhome.com' eq hostname() ) + if ( + 'CUILLERE' eq hostname() + or + skip_macosx() + or + -e '/.dockerenv' + ) { - skip( 'Tests avoided on CUILLERE cannot do ipv6', 2 ) ; + skip( 'Tests avoided on CUILLERE/macosx.polarhome.com/docker cannot do ipv6', 2 ) ; } $socket = IO::Socket::INET6->new( @@ -4845,17 +5207,17 @@ sub tests_connect_socket PeerPort => 993, SSL_verify_mode => SSL_VERIFY_NONE, ) ; - # myprint $socket ; + # myprint( $socket ) ; ok( $imap = connect_socket( $socket ), 'connect_socket: ks2ipv6.lamiral.info port 993 IO::Socket::SSL' ) ; #$imap->Debug( 1 ) ; - # myprint $imap->capability( ) ; - $socket->close( ) ; + # myprint( $imap->capability( ) ) ; + # $socket->close( ) ; if ( $imap ) { $socket->close( ) ; } #$socket->close(SSL_no_shutdown => 1) ; #$imap->logout( ) ; - #myprint "\n" ; + #myprint( "\n" ) ; #$imap->logout( ) ; } note( 'Leaving tests_connect_socket()' ) ; @@ -4888,12 +5250,15 @@ sub tests_probe_imapssl is( undef, probe_imapssl( 'unknown' ), 'probe_imapssl: unknown => undef' ) ; SKIP: { - if ( - 'CUILLERE' eq hostname() - or - 'macosx.polarhome.com' eq hostname() ) - { - skip( 'Tests avoided on CUILLERE cannot do ipv6', 2 ) ; + if ( + 'CUILLERE' eq hostname() + or + skip_macosx() + or + -e '/.dockerenv' + ) + { + skip( 'Tests avoided on CUILLERE/macosx.polarhome.com/docker cannot do ipv6', 2 ) ; } like( probe_imapssl( 'ks2ipv6.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: ks2ipv6.lamiral.info matches "* OK"' ) ; like( probe_imapssl( 'imap.gmail.com' ), qr/^\* OK/, 'probe_imapssl: imap.gmail.com matches "* OK"' ) ; @@ -4941,7 +5306,7 @@ sub connect_imap myprint( "$Side: connecting on $side [$host] port [$port]\n" ) ; $imap->connect( ) - or die_clean( "$Side: Can not open imap connection on [$host]: " . $imap->LastError . " $OS_ERROR\n" ) ; + or exit_clean( $sync, $EXIT_CONNECTION_FAILURE, "$Side: Can not open imap connection on [$host]: " . $imap->LastError . " $OS_ERROR\n" ) ; myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; my $banner = $imap->Results()->[0] ; @@ -4951,7 +5316,7 @@ sub connect_imap if ( $tls ) { set_tls( $imap, $h ) ; $imap->starttls( ) - or die_clean("$Side: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; + or exit_clean( $sync, $EXIT_TLS_FAILURE, "$Side: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; myprint( "$Side: Socket successfuly converted to SSL\n" ) ; } return( $imap ) ; @@ -4974,7 +5339,7 @@ sub login_imap my $imap = init_imap( @allargs ) ; $imap->connect() - or die_clean("$Side failure: can not open imap connection on $side [$host] with user [$user]: " . $imap->LastError . " $OS_ERROR\n" ) ; + or exit_clean( $mysync, $EXIT_CONNECTION_FAILURE, "$Side failure: can not open imap connection on $side [$host] with user [$user]: " . $imap->LastError . " $OS_ERROR\n" ) ; myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ; my $banner = $imap->Results()->[0] ; @@ -4991,14 +5356,14 @@ sub login_imap $imap->Socket ; myprintf("%s: Assuming PREAUTH for %s\n", $Side, $imap->Server ) ; }else{ - die_clean( "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]" ) ; + exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]" ) ; } } if ( $tls ) { set_tls( $imap, $h ) ; $imap->starttls( ) - or die_clean("$Side failure: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; + or exit_clean( $mysync, $EXIT_TLS_FAILURE, "$Side failure: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ; myprint( "$Side: Socket successfuly converted to SSL\n" ) ; } @@ -5009,7 +5374,7 @@ sub login_imap } -sub authenticate_imap +sub authenticate_imap { my( $imap, $host, $port, $user, $domain, $password, @@ -5022,13 +5387,13 @@ sub authenticate_imap $imap->Domain( $domain ) if ( defined $domain ) ; $imap->Authuser( $authuser ) ; $imap->Password( $password ) ; - + if ( 'X-MASTERAUTH' eq $authmech ) { xmasterauth( $imap ) ; return ; } - + if ( $proxyauth ) { $imap->Authmechanism(q{}) ; $imap->User( $authuser ) ; @@ -5047,14 +5412,14 @@ sub authenticate_imap chomp $einfo ; my $error = "$info [$authmech]: $einfo\n" ; if ( $authmech eq 'LOGIN' or $imap->IsUnconnected( ) or $authuser ) { - die_clean( $error ) ; + exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, $error ) ; }else{ myprint( $error ) ; } myprint( "$Side info: trying LOGIN Auth mechanism on [$host] with user [$user]\n" ) ; $imap->Authmechanism(q{}) ; $imap->login() or - die_clean("$info [LOGIN]: ", $imap->LastError, "\n") ; + exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$info [LOGIN]: ", $imap->LastError, "\n") ; } if ( $proxyauth ) { @@ -5062,7 +5427,7 @@ sub authenticate_imap my $info = "$Side failure: Error doing proxyauth as user [$user] on [$host] using proxy-login as [$authuser]" ; my $einfo = $imap->LastError || @{$imap->History}[$LAST] ; chomp $einfo ; - die_clean( "$info: $einfo\n" ) ; + exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, "$info: $einfo\n" ) ; } } @@ -5256,15 +5621,15 @@ sub xoauth2 if( $imap->Password =~ /^(.*\.json)$/x ) { my $json = JSON->new( ) ; my $filename = $1; - $debug and myprint( "XOAUTH2 json file: $filename\n" ) ; - open( my $FILE, '<', $filename ) or die_clean( "error [$filename]: $OS_ERROR " ) ; + $sync->{ debug } and myprint( "XOAUTH2 json file: $filename\n" ) ; + open( my $FILE, '<', $filename ) or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "error [$filename]: $OS_ERROR " ) ; my $jsonfile = $json->decode( join q{}, <$FILE> ) ; close $FILE ; $iss = $jsonfile->{client_id}; $key = $jsonfile->{private_key}; - $debug and myprint( "Service account: $iss\n"); - $debug and myprint( "Private key:\n$key\n"); + $sync->{ debug } and myprint( "Service account: $iss\n"); + $sync->{ debug } and myprint( "Private key:\n$key\n"); } else { # Get iss (service account address), keyfile name, and keypassword if necessary @@ -5273,12 +5638,12 @@ sub xoauth2 # Assume key password is google default if not provided $keypass = 'notasecret' if not $keypass; - $debug and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n"); + $sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n"); # Get private key from p12 file (would be better in perl...) $key = `openssl pkcs12 -in "$keyfile" -nodes -nocerts -passin pass:$keypass -nomacver`; - $debug and myprint( "Private key:\n$key\n"); + $sync->{ debug } and myprint( "Private key:\n$key\n"); } # Create jwt of oauth2 request @@ -5302,9 +5667,9 @@ sub xoauth2 assertion => $jwt } ) ; unless( $response->is_success( ) ) { - die_clean( $response->code, "\n", $response->content, "\n" ) ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, $response->code, "\n", $response->content, "\n" ) ; }else{ - $debug and myprint( $response->content ) ; + $sync->{ debug } and myprint( $response->content ) ; } # access_token in response is what we need @@ -5313,7 +5678,7 @@ sub xoauth2 # format as oauth2 auth data my $xoauth2_string = encode_base64( 'user=' . $imap->User . "\1auth=Bearer " . $data->{access_token} . "\1\1", q{} ) ; - $debug and myprint( "XOAUTH2 String: $xoauth2_string\n"); + $sync->{ debug } and myprint( "XOAUTH2 String: $xoauth2_string\n"); return($xoauth2_string); } @@ -5337,7 +5702,7 @@ sub xoauth # For Google Apps, the consumer key is the primary domain # TODO: create a command line argument to define the consumer key my @user_parts = split /@/x, $imap->User ; - $debug and myprint( "XOAUTH: consumer key: $user_parts[1]\n" ) ; + $sync->{ debug } and myprint( "XOAUTH: consumer key: $user_parts[1]\n" ) ; # All the parameters needed to be signed on the XOAUTH my %hash = (); @@ -5362,7 +5727,7 @@ sub xoauth } $base .= URI::Escape::uri_escape($baseparms); - $debug and myprint( "XOAUTH: base request to sign: $base\n" ) ; + $sync->{ debug } and myprint( "XOAUTH: base request to sign: $base\n" ) ; # Sign it with the consumer secret, informed on the command line (password) my $digest = hmac_sha1( $base, URI::Escape::uri_escape( $imap->Password ) . q{&} ) ; @@ -5387,7 +5752,7 @@ sub xoauth $string .= $baseparms; - $debug and myprint( "XOAUTH: authentication string: $string\n" ) ; + $sync->{ debug } and myprint( "XOAUTH: authentication string: $string\n" ) ; # It must be base64 encoded return encode_base64("$string", q{}); @@ -5397,29 +5762,29 @@ sub xoauth sub xmasterauth { # This is Kerio auth admin - # This code comes from + # This code comes from # https://github.com/imapsync/imapsync/pull/53/files - + my $imap = shift ; - + my $user = $imap->User( ) ; my $password = $imap->Password( ) ; my $authmech = 'X-MASTERAUTH' ; - + my @challenge = $imap->tag_and_run( $authmech, "+" ) ; if ( not defined $challenge[0] ) { - die_clean("Failure authenticate with $authmech: ", $imap->LastError, "\n") ; + exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", $imap->LastError, "\n") ; return ; # hahaha! } - $debug and myprint "X-MASTERAUTH challenge: [@challenge]\n" ; + $sync->{ debug } and myprint( "X-MASTERAUTH challenge: [@challenge]\n" ) ; $challenge[1] =~ s/^\+ |^\s+|\s+$//g ; $imap->_imap_command( { addcrlf => 1, addtag => 0, tag => $imap->Count }, md5_hex( $challenge[1] . $password ) ) - or die_clean("Failure authenticate with $authmech: ", $imap->LastError, "\n") ; - + or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", $imap->LastError, "\n") ; + $imap->tag_and_run( 'X-SETUSER ' . $user ) - or die_clean("Failure authenticate with $authmech: ", "X-SETUSER ", $imap->LastError, "\n") ; + or exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Failure authenticate with $authmech: ", "X-SETUSER ", $imap->LastError, "\n") ; $imap->State( Mail::IMAPClient::Authenticated ) ; # I comment this state because "Selected" state is usually done by SELECT or EXAMINE imap commands @@ -5455,23 +5820,23 @@ sub tests_do_valid_directory sub banner_imapsync { - - my @argv = @_ ; + my $mysync = shift @ARG ; + my @argv = @ARG ; my $banner_imapsync = join q{}, q{$RCSfile: imapsync,v $ }, - q{$Revision: 1.921 $ }, - q{$Date: 2019/02/18 10:21:03 $ }, + q{$Revision: 1.945 $ }, + q{$Date: 2019/06/26 19:30:56 $ }, "\n", - "Command line used:\n", - "$PROGRAM_NAME ", command_line_nopassword( @argv ), "\n" ; + "Command line used, run by $EXECUTABLE_NAME:\n", + "$PROGRAM_NAME ", command_line_nopassword( $mysync, @argv ), "\n" ; return( $banner_imapsync ) ; } sub do_valid_directory { - my $dir = shift; + my $dir = shift @ARG ; # all good => return ok. return( 1 ) if ( -d $dir and -r _ and -w _ ) ; @@ -5519,7 +5884,7 @@ sub tests_match_a_pid_number sub match_a_pid_number { - my $pid = shift ; + my $pid = shift @ARG ; if ( ! $pid ) { return ; } if ( ! match( $pid, '^\d+$' ) ) { return ; } if ( 0 > $pid ) { return ; } @@ -5532,6 +5897,7 @@ sub tests_remove_pidfile_not_running { note( 'Entering tests_remove_pidfile_not_running()' ) ; + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'remove_pidfile_not_running: mkpath W/tmp/tests/' ) ; is( undef, remove_pidfile_not_running( ), 'remove_pidfile_not_running: no args => undef' ) ; is( undef, remove_pidfile_not_running( './W' ), 'remove_pidfile_not_running: a dir => undef' ) ; is( undef, remove_pidfile_not_running( 'noexists' ), 'remove_pidfile_not_running: noexists => undef' ) ; @@ -5551,7 +5917,7 @@ sub tests_remove_pidfile_not_running sub remove_pidfile_not_running { # - my $pid_filename = shift ; + my $pid_filename = shift @ARG ; if ( ! $pid_filename ) { myprint( "No variable pid_filename\n" ) ; return } ; if ( ! -e $pid_filename ) { myprint( "File $pid_filename does not exist\n" ) ; return } ; @@ -5577,6 +5943,131 @@ sub remove_pidfile_not_running return ; } + +sub tests_tail +{ + note( 'Entering tests_tail()' ) ; + + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'tail: mkpath W/tmp/tests/' ) ; + ok( ( ! -e 'W/tmp/tests/tail.pid' || unlink 'W/tmp/tests/tail.pid' ), 'tail: unlink W/tmp/tests/tail.pid' ) ; + ok( ( ! -e 'W/tmp/tests/tail.txt' || unlink 'W/tmp/tests/tail.txt' ), 'tail: unlink W/tmp/tests/tail.txt' ) ; + + is( undef, tail( ), 'tail: no args => undef' ) ; + my $mysync ; + is( undef, tail( $mysync ), 'tail: no pidfile => undef' ) ; + + $mysync->{pidfile} = 'W/tmp/tests/tail.pid' ; + is( undef, tail( $mysync ), 'tail: no pidfilelocking => undef' ) ; + + $mysync->{pidfilelocking} = 1 ; + is( undef, tail( $mysync ), 'tail: pidfile no exists => undef' ) ; + + + my $pidandlog = "33333\nW/tmp/tests/tail.txt\n" ; + is( $pidandlog, string_to_file( $pidandlog, $mysync->{pidfile} ), 'tail: put pid 33333 and tail.txt in pidfile' ) ; + is( undef, tail( $mysync ), 'tail: logfile to tail no exists => undef' ) ; + + my $tailcontent = "L1\nL2\nL3\nL4\nL5\n" ; + is( $tailcontent, string_to_file( $tailcontent, 'W/tmp/tests/tail.txt' ), + 'tail: put L1\nL2\nL3\nL4\nL5\n in W/tmp/tests/tail.txt' ) ; + + is( undef, tail( $mysync ), 'tail: fake pid in pidfile + tail off => 1' ) ; + + $mysync->{ tail } = 1 ; + is( 1, tail( $mysync ), 'tail: fake pid in pidfile + tail on=> 1' ) ; + + # put my own pid, won't do tail + $pidandlog = "$PROCESS_ID\nW/tmp/tests/tail.txt\n" ; + is( $pidandlog, string_to_file( $pidandlog, $mysync->{pidfile} ), 'tail: put my own PID in pidfile' ) ; + is( undef, tail( $mysync ), 'tail: my own pid in pidfile => undef' ) ; + + note( 'Leaving tests_tail()' ) ; + return ; +} + + + +sub tail +{ + # return undef on failures + # return 1 on success + + my $mysync = shift ; + + # no tail when aborting! + if ( $mysync->{ abort } ) { return ; } + + my $pidfile = $mysync->{pidfile} ; + my $lock = $mysync->{pidfilelocking} ; + my $tail = $mysync->{tail} ; + + if ( ! $pidfile ) { return ; } + if ( ! $lock ) { return ; } + if ( ! $tail ) { return ; } + + my $pidtotail = firstline( $pidfile ) ; + if ( ! $pidtotail ) { return ; } + + + + # It should not happen but who knows... + if ( $pidtotail eq $PROCESS_ID ) { return ; } + + + my $filetotail = secondline( $pidfile ) ; + if ( ! $filetotail ) { return ; } + + if ( ! -r $filetotail ) + { + #myprint( "Error: can not read $filetotail\n" ) ; + return ; + } + + myprint( "Doing a tail -f on $filetotail for processus pid $pidtotail until it is finished.\n" ) ; + my $file = File::Tail->new( + name => $filetotail, + nowait => 1, + interval => 1, + tail => 1, + adjustafter => 2 + ); + + my $moretimes = 200 ; + # print one line at least + my $line = $file->read ; + myprint( $line ) ; + while ( isrunning( $pidtotail, \$moretimes ) and defined( $line = $file->read ) ) + { + myprint( $line ); + sleep( 0.02 ) ; + } + + return 1 ; +} + +sub isrunning +{ + my $pidtocheck = shift ; + my $moretimes_ref = shift ; + + if ( kill 'ZERO', $pidtocheck ) + { + #myprint( "$pidtocheck running\n" ) ; + return 1 ; + } + elsif ( $$moretimes_ref >= 0 ) + { + # continue to consider it running + $$moretimes_ref-- ; + return 1 ; + } + else + { + myprint( "Tailed processus $pidtocheck ended\n" ) ; + return ; + } +} + sub tests_write_pidfile { note( 'Entering tests_write_pidfile()' ) ; @@ -5585,8 +6076,14 @@ sub tests_write_pidfile is( 1, write_pidfile( ), 'write_pidfile: no args => 1' ) ; + # no pidfile => ok + $mysync->{pidfile} = q{} ; + is( 1, write_pidfile( $mysync ), 'write_pidfile: no pidfile => undef' ) ; + + # The pidfile path is bad => failure $mysync->{pidfile} = '/no/no/no.pid' ; - is( 1, write_pidfile( $mysync ), 'write_pidfile: no permission for /no/no/no.pid, no lock => 1' ) ; + is( undef, write_pidfile( $mysync ), 'write_pidfile: no permission for /no/no/no.pid, no lock => undef' ) ; + $mysync->{pidfilelocking} = 1 ; is( undef, write_pidfile( $mysync ), 'write_pidfile: no permission for /no/no/no.pid + lock => undef' ) ; @@ -5595,16 +6092,27 @@ sub tests_write_pidfile is( 1, touch( $mysync->{pidfile} ), 'write_pidfile: lock prepa' ) ; $mysync->{pidfilelocking} = 0 ; - is( 1, write_pidfile( $mysync ), 'write_pidfile: W/tmp/tests/test.pid => 1' ) ; + is( 1, write_pidfile( $mysync ), 'write_pidfile: W/tmp/tests/test.pid + no lock => 1' ) ; is( $PROCESS_ID, firstline( 'W/tmp/tests/test.pid' ), "write_pidfile: W/tmp/tests/test.pid contains $PROCESS_ID" ) ; + is( q{}, secondline( 'W/tmp/tests/test.pid' ), "write_pidfile: W/tmp/tests/test.pid contains no second line" ) ; $mysync->{pidfilelocking} = 1 ; is( undef, write_pidfile( $mysync ), 'write_pidfile: W/tmp/tests/test.pid + lock => undef' ) ; + + $mysync->{pidfilelocking} = 0 ; + $mysync->{ logfile } = 'rrrr.txt' ; + is( 1, write_pidfile( $mysync ), 'write_pidfile: W/tmp/tests/test.pid + no lock + logfile => 1' ) ; + is( $PROCESS_ID, firstline( 'W/tmp/tests/test.pid' ), "write_pidfile: + no lock + logfile W/tmp/tests/test.pid contains $PROCESS_ID" ) ; + is( q{rrrr.txt}, secondline( 'W/tmp/tests/test.pid' ), "write_pidfile: + no lock + logfile W/tmp/tests/test.pid contains rrrr.txt" ) ; + + note( 'Leaving tests_write_pidfile()' ) ; return ; } + + sub write_pidfile { # returns undef if something is considered fatal @@ -5612,80 +6120,52 @@ sub write_pidfile if ( ! @ARG ) { return 1 ; } - my $mysync = shift ; + my $mysync = shift @ARG ; # Do not write the pid file if this process goal is to abort the process designed by the pid file if ( $mysync->{abort} ) { return 1 ; } # - my $pid_filename = $mysync->{pidfile} ; - my $lock = $mysync->{pidfilelocking} ; + my $pid_filename = $mysync->{ pidfile } ; + my $lock = $mysync->{ pidfilelocking } ; - myprint( "PID file is $pid_filename ( to change it use --pidfile filepath ; to avoid it use --pidfile \"\" )\n" ) ; + if ( ! $pid_filename ) + { + myprint( "PID file is unset ( to set it, use --pidfile filepath ; to avoid it use --pidfile \"\" )\n" ) ; + return( 1 ) ; + } + + myprint( "PID file is $pid_filename ( to change it, use --pidfile filepath ; to avoid it use --pidfile \"\" )\n" ) ; if ( -e $pid_filename and $lock ) { myprint( "$pid_filename already exists, another imapsync may be curently running. Aborting imapsync.\n" ) ; return ; } + if ( -e $pid_filename ) { myprint( "$pid_filename already exists, overwriting it ( use --pidfilelocking to avoid concurrent runs )\n" ) ; } + my $pid_string = "$PROCESS_ID\n" ; + my $pid_message = "Writing my PID $PROCESS_ID in $pid_filename\n" ; + + if ( $mysync->{ logfile } ) + { + $pid_string .= "$mysync->{ logfile }\n" ; + $pid_message .= "Writing also my logfile name in $pid_filename : $mysync->{ logfile }\n" ; + } + if ( open my $FILE_HANDLE, '>', $pid_filename ) { - myprint( "Writing my PID $PROCESS_ID in $pid_filename\n" ) ; - print $FILE_HANDLE $PROCESS_ID ; + myprint( $pid_message ) ; + print $FILE_HANDLE $pid_string ; close $FILE_HANDLE ; return( 1 ) ; - } else { - myprint( "Could not open $pid_filename for writing. Check permissions or disk space.\n" ) ; - if ( $lock ) { - return ; - }else{ - return( 1 ) ; - } } -} - - - -sub remove_tmp_files -{ - my $mysync = shift or return ; - $mysync->{pidfile} or return ; - if ( -e $mysync->{pidfile} ) { - unlink $mysync->{pidfile} ; - } - return ; -} - - -sub exit_clean -{ - my $mysync = shift ; - my $status = shift ; - $status = defined $status ? $status : $EXIT_UNKNOWN ; - remove_tmp_files( $mysync ) ; - myprint( "Exiting with return value $status\n" ) ; - if ( $mysync->{log} ) { - myprint( "Log file is $mysync->{logfile} ( to change it, use --logfile filepath ; or use --nolog to turn off logging )\n" ) ; - close $mysync->{logfile_handle} ; + else + { + myprint( "Could not open $pid_filename for writing. Check permissions or disk space: $OS_ERROR\n" ) ; + return ; } - exit $status ; -} - -sub die_clean -{ - my @messages = @_ ; - remove_tmp_files( $sync ) ; - myprint( @messages ) ; - exit 255 ; -} - -sub missing_option -{ - my ( $option ) = @_ ; - die_clean( "$option option is mandatory, for help run $PROGRAM_NAME --help\n" ) ; - return ; } @@ -5827,12 +6307,12 @@ sub imap_utf7_encode sub select_folder { - my ( $imap, $folder, $hostside ) = @_ ; + my ( $mysync, $imap, $folder, $hostside ) = @_ ; if ( ! $imap->select( $folder ) ) { my $error = join q{}, "$hostside folder $folder: Could not select: ", $imap->LastError, "\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; return( 0 ) ; }else{ # ok select succeeded @@ -5842,12 +6322,12 @@ sub select_folder sub examine_folder { - my ( $imap, $folder, $hostside ) = @_ ; + my ( $mysync, $imap, $folder, $hostside ) = @_ ; if ( ! $imap->examine( $folder ) ) { my $error = join q{}, "$hostside folder $folder: Could not examine: ", $imap->LastError, "\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; return( 0 ) ; }else{ # ok select succeeded @@ -5856,11 +6336,9 @@ sub examine_folder } - - sub count_from_select { - my @lines = @_ ; + my @lines = @ARG ; my $count ; foreach my $line ( @lines ) { #myprint( "line = [$line]\n" ) ; @@ -5874,24 +6352,10 @@ sub count_from_select - - - - - - - - - - - - - - - sub create_folder_old { - my( $imap, $h2_fold, $h1_fold ) = @_ ; + my $mysync = shift @ARG ; + my( $imap, $h2_fold, $h1_fold ) = @ARG ; myprint( "Creating (old way) folder [$h2_fold] on host2\n" ) ; if ( ( 'INBOX' eq uc $h2_fold ) @@ -5899,12 +6363,12 @@ sub create_folder_old myprint( "Folder [$h2_fold] already exists\n" ) ; return( 1 ) ; } - if ( ! $sync->{dry} ){ + if ( ! $mysync->{dry} ){ if ( ! $imap->create( $h2_fold ) ) { my $error = join q{}, "Could not create folder [$h2_fold] from [$h1_fold]: ", $imap->LastError( ), "\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; # success if folder exists ("already exists" error) return( 1 ) if $imap->exists( $h2_fold ) ; # failure since create failed @@ -5916,7 +6380,7 @@ sub create_folder_old } }else{ # dry mode, no folder so many imap will fail, assuming failure - myprint( "Created ( the old way ) folder [$h2_fold] on host2 $sync->{dry_message}\n" ) ; + myprint( "Created ( the old way ) folder [$h2_fold] on host2 $mysync->{dry_message}\n" ) ; return( 0 ) ; } } @@ -5924,7 +6388,8 @@ sub create_folder_old sub create_folder { - my( $myimap2 , $h2_fold , $h1_fold ) = @_ ; + my $mysync = shift @ARG ; + my( $myimap2 , $h2_fold , $h1_fold ) = @ARG ; my( @parts , $parent ) ; if ( $myimap2->IsUnconnected( ) ) { @@ -5933,7 +6398,7 @@ sub create_folder } if ( $create_folder_old ) { - return( create_folder_old( $myimap2 , $h2_fold , $h1_fold ) ) ; + return( create_folder_old( $mysync, $myimap2 , $h2_fold , $h1_fold ) ) ; } myprint( "Creating folder [$h2_fold] on host2\n" ) ; if ( ( 'INBOX' eq uc $h2_fold ) @@ -5953,20 +6418,20 @@ sub create_folder return( 0 ) ; } - @parts = split /\Q$h2_sep\E/x, $h2_fold ; + @parts = split /\Q$mysync->{ h2_sep }\E/x, $h2_fold ; pop @parts ; - $parent = join $h2_sep, @parts ; + $parent = join $mysync->{ h2_sep }, @parts ; $parent =~ s/^\s+|\s+$//xg ; if ( ( $parent ne q{} ) and ( ! $myimap2->exists( $parent ) ) ) { - create_folder( $myimap2 , $parent , $h1_fold ) ; + create_folder( $mysync, $myimap2 , $parent , $h1_fold ) ; } - if ( ! $sync->{dry} ) { + if ( ! $mysync->{dry} ) { if ( ! $myimap2->create( $h2_fold ) ) { my $error = join q{}, "Could not create folder [$h2_fold] from [$h1_fold]: " , $myimap2->LastError( ), "\n" ; - errors_incr( $sync, $error ) ; + errors_incr( $mysync, $error ) ; # success if folder exists ("already exists" error) return( 1 ) if $myimap2->exists( $h2_fold ) ; # failure since create failed @@ -5978,8 +6443,8 @@ sub create_folder } }else{ # dry mode, no folder so many imap will fail, assuming failure - myprint( "Created folder [$h2_fold] on host2 $sync->{dry_message}\n" ) ; - if ( ! $sync->{ justfolders } ) { + myprint( "Created folder [$h2_fold] on host2 $mysync->{dry_message}\n" ) ; + if ( ! $mysync->{ justfolders } ) { myprint( "Since --dry mode is on and folder [$h2_fold] on host2 does not exist yet, syncing messages will not be simulated.\n" . "To simulate message syncing, use --justfolders without --dry to first create the missing folders then rerun the --dry sync.\n" ) ; } @@ -5997,7 +6462,8 @@ sub tests_folder_routines ok( add_to_requested_folders('folder_foo'), 'add_to_requested_folders folder_foo' ); ok( is_requested_folder('folder_foo'), 'is_requested_folder folder_foo 2' ); ok( !is_requested_folder('folder_NO_EXIST'), 'is_requested_folder folder_NO_EXIST' ); - ok( !remove_from_requested_folders('folder_foo'), 'removed folder_foo' ); + + is_deeply( [ 'folder_foo' ], [ remove_from_requested_folders( 'folder_foo' ) ], 'removed folder_foo => folder_foo' ) ; ok( !is_requested_folder('folder_foo'), 'is_requested_folder folder_foo 3' ); my @f ; ok( @f = add_to_requested_folders('folder_bar', 'folder_toto'), "add result: @f" ); @@ -6005,21 +6471,34 @@ sub tests_folder_routines ok( is_requested_folder('folder_toto'), 'is_requested_folder 5' ); ok( remove_from_requested_folders('folder_toto'), 'remove_from_requested_folders: ' ); ok( !is_requested_folder('folder_toto'), 'is_requested_folder 6' ); - ok( !remove_from_requested_folders('folder_bar'), 'remove_from_requested_folders: empty' ) ; + + is_deeply( [ 'folder_bar' ], [ remove_from_requested_folders('folder_bar') ], 'remove_from_requested_folders: empty' ) ; ok( 0 == compare_lists( [ sort_requested_folders( ) ], [] ), 'sort_requested_folders: all empty' ) ; - ok( add_to_requested_folders('M_55'), 'add_to_requested_folders M_55' ); - ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'M_55' ] ), 'sort_requested_folders: middle' ) ; + ok( add_to_requested_folders( 'A_99', 'M_55', 'Z_11' ), 'add_to_requested_folders M_55 Z_11' ); + ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'A_99', 'M_55', 'Z_11' ] ), 'sort_requested_folders: middle' ) ; + + @folderfirst = ( 'Z_11' ) ; - ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_11', 'M_55' ] ), 'sort_requested_folders: first+middle' ) ; + + ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_11', 'A_99', 'M_55' ] ), 'sort_requested_folders: first+middle' ) ; + + is_deeply( [ 'Z_11', 'A_99', 'M_55' ], [ sort_requested_folders( ) ], 'sort_requested_folders: first+middle is_deeply' ) ; + @folderlast = ( 'A_99' ) ; ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_11', 'M_55', 'A_99' ] ), 'sort_requested_folders: first+middle+last 1' ) ; - ok( add_to_requested_folders('M_55', 'M_44',), 'add_to_requested_folders M_55 M_44' ); - ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_11', 'M_44', 'M_55', 'A_99' ] ), 'sort_requested_folders: first+middle+last 2' ) ; + ok( add_to_requested_folders('M_55', 'M_44',), 'add_to_requested_folders M_55 M_44' ) ; + + ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_11', 'M_44', 'M_55', 'A_99'] ), 'sort_requested_folders: first+middle+last 2' ) ; + + + ok( add_to_requested_folders('A_88', 'Z_22',), 'add_to_requested_folders A_88 Z_22' ) ; @folderfirst = qw( Z_22 Z_11 ) ; @folderlast = qw( A_99 A_88 ) ; ok( 0 == compare_lists( [ sort_requested_folders( ) ], [ 'Z_22', 'Z_11', 'M_44', 'M_55', 'A_99', 'A_88' ] ), 'sort_requested_folders: first+middle+last 3' ) ; + undef @folderfirst ; + undef @folderlast ; note( 'Leaving tests_folder_routines()' ) ; return ; @@ -6030,17 +6509,17 @@ sub sort_requested_folders { my @requested_folders_sorted = () ; - foreach my $folder ( @folderfirst ) { - remove_from_requested_folders( $folder ) ; - } + #myprint "folderfirst: @folderfirst\n" ; + my @folderfirst_requested = remove_from_requested_folders( @folderfirst ) ; + #myprint "folderfirst_requested: @folderfirst_requested\n" ; - foreach my $folder ( @folderlast ) { - remove_from_requested_folders( $folder ) ; - } + my @folderlast_requested = remove_from_requested_folders( @folderlast ) ; my @middle = sort keys %requested_folder ; - @requested_folders_sorted = ( @folderfirst, @middle, @folderlast ) ; + @requested_folders_sorted = ( @folderfirst_requested, @middle, @folderlast_requested ) ; + #myprint "requested_folders_sorted: @requested_folders_sorted\n" ; + add_to_requested_folders( @requested_folders_sorted ) ; return( @requested_folders_sorted ) ; } @@ -6049,7 +6528,7 @@ sub is_requested_folder { my ( $folder ) = @_; - return( defined $requested_folder{ $folder } ) ; + return( defined $requested_folder{ $folder } ) ; } @@ -6063,14 +6542,61 @@ sub add_to_requested_folders return( keys %requested_folder ) ; } +sub tests_remove_from_requested_folders +{ + note( 'Entering tests_remove_from_requested_folders()' ) ; + + is( undef, undef, 'remove_from_requested_folders: undef is undef' ) ; + is_deeply( [], [ remove_from_requested_folders( ) ], 'remove_from_requested_folders: no args' ) ; + %requested_folder = ( + 'F1' => 1, + ) ; + is_deeply( [], [ remove_from_requested_folders( ) ], 'remove_from_requested_folders: remove nothing among F1 => nothing' ) ; + is_deeply( [], [ remove_from_requested_folders( 'Fno' ) ], 'remove_from_requested_folders: remove Fno among F1 => nothing' ) ; + is_deeply( [ 'F1' ], [ remove_from_requested_folders( 'F1' ) ], 'remove_from_requested_folders: remove F1 among F1 => F1' ) ; + is_deeply( { }, { %requested_folder }, 'remove_from_requested_folders: remove F1 among F1 => %requested_folder emptied' ) ; + + %requested_folder = ( + 'F1' => 1, + 'F2' => 1, + ) ; + is_deeply( [], [ remove_from_requested_folders( ) ], 'remove_from_requested_folders: remove nothing among F1 F2 => nothing' ) ; + is_deeply( [], [ remove_from_requested_folders( 'Fno' ) ], 'remove_from_requested_folders: remove Fno among F1 F2 => nothing' ) ; + is_deeply( [ 'F1' ], [ remove_from_requested_folders( 'F1' ) ], 'remove_from_requested_folders: remove F1 among F1 F2 => F1' ) ; + is_deeply( { 'F2' => 1 }, { %requested_folder }, 'remove_from_requested_folders: remove F1 among F1 F2 => %requested_folder F2' ) ; + + is_deeply( [], [ remove_from_requested_folders( 'F1' ) ], 'remove_from_requested_folders: remove F1 among F2 => nothing' ) ; + is_deeply( [ 'F2' ], [ remove_from_requested_folders( 'F1', 'F2' ) ], 'remove_from_requested_folders: remove F1 F2 among F2 => F2' ) ; + is_deeply( {}, { %requested_folder }, 'remove_from_requested_folders: remove F1 among F1 F2 => %requested_folder F2' ) ; + + %requested_folder = ( + 'F1' => 1, + 'F2' => 1, + 'F3' => 1, + ) ; + is_deeply( [ 'F1', 'F2' ], [ remove_from_requested_folders( 'F1', 'F2' ) ], 'remove_from_requested_folders: remove F1 F2 among F1 F2 F3 => F1 F2' ) ; + is_deeply( { 'F3' => 1 }, { %requested_folder }, 'remove_from_requested_folders: remove F1 F2 among F1 F2 F3 => %requested_folder F3' ) ; + + + + note( 'Leaving tests_remove_from_requested_folders()' ) ; + return ; +} + + sub remove_from_requested_folders { - my @wanted_folders = @_ ; + my @unwanted_folders = @_ ; - foreach my $folder ( @wanted_folders ) { - delete $requested_folder{ $folder } ; + my @removed_folders = () ; + foreach my $folder ( @unwanted_folders ) { + if ( exists $requested_folder{ $folder } ) + { + delete $requested_folder{ $folder } ; + push @removed_folders, $folder ; + } } - return( keys %requested_folder ) ; + return( @removed_folders ) ; } sub compare_lists @@ -6206,10 +6732,10 @@ sub get_prefix my( $imap, $prefix_in, $prefix_opt, $Side, $folders_ref ) = @_ ; my( $prefix_out, $prefix_guessed ) ; - ( $debug or $sync->{debugfolders} ) and myprint( "$Side: Getting prefix\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "$Side: Getting prefix\n" ) ; $prefix_guessed = guess_prefix( @{ $folders_ref } ) ; myprint( "$Side: guessing prefix from folder listing: [$prefix_guessed]\n" ) ; - ( $debug or $sync->{debugfolders} ) and myprint( "$Side: Calling namespace capability\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "$Side: Calling namespace capability\n" ) ; if ( $imap->has_capability( 'namespace' ) ) { my $r_namespace = $imap->namespace( ) ; $prefix_out = $r_namespace->[0][0][0] ; @@ -6254,7 +6780,7 @@ sub guess_separator $counter{'\\'}++ while ( $folder =~ m{[^\\](\\){1}(?=[^\\])}xg ) ; # count \ } my @race_sorted = sort { $counter{ $b } <=> $counter{ $a } } keys %counter ; - $debug and myprint( "@foldernames\n@race_sorted\n", %counter, "\n" ) ; + $sync->{ debug } and myprint( "@foldernames\n@race_sorted\n", %counter, "\n" ) ; $sep_guessed = shift @race_sorted || $LAST_RESSORT_SEPARATOR ; # / when nothing found. return( $sep_guessed ) ; } @@ -6281,12 +6807,13 @@ sub get_separator my( $imap, $sep_in, $sep_opt, $Side, $folders_ref ) = @_ ; my( $sep_out, $sep_guessed ) ; - ( $debug or $sync->{debugfolders} ) and myprint( "$Side: Getting separator\n" ) ; + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "$Side: Getting separator\n" ) ; $sep_guessed = guess_separator( @{ $folders_ref } ) ; myprint( "$Side: guessing separator from folder listing: [$sep_guessed]\n" ) ; - ( $debug or $sync->{debugfolders} ) and myprint( "$Side: calling namespace capability\n" ) ; - if ( $imap->has_capability( 'namespace' ) ) { + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "$Side: calling namespace capability\n" ) ; + if ( $imap->has_capability( 'namespace' ) ) + { $sep_out = $imap->separator( ) ; if ( defined $sep_out ) { myprint( "$Side: separator given by NAMESPACE: [$sep_out]\n" ) ; @@ -6310,7 +6837,8 @@ sub get_separator } } } - else{ + else + { if ( defined $sep_in ) { myprint( "$Side: No NAMESPACE capability but using [$sep_in] given by $sep_opt\n" ) ; $sep_out = $sep_in ; @@ -6357,127 +6885,465 @@ sub folders_list_to_help return( $listing ) ; } - - -sub tests_imap2_folder_name +sub private_folders_separators_and_prefixes { - note( 'Entering tests_imap2_folder_name()' ) ; +# what are the private folders separators and prefixes for each server ? -$sync->{ h1_prefix } = $sync->{ h2_prefix } = q{} ; -$h1_sep = '/'; -$h2_sep = '.'; - -$debug and myprint( <<"EOS" -prefix1: [$sync->{ h1_prefix }] -prefix2: [$sync->{ h2_prefix }] -sep1:[$h1_sep] -sep2:[$h2_sep] -EOS -) ; - -$fixslash2 = 0 ; -ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string'); -ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla'); -ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam'); -ok('spam/spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam'); -ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam'); -ok('s pam.spam/sp am' eq imap2_folder_name('s pam/spam.sp am'), 'imap2_folder_name: s pam/spam.sp am'); - -$sync->{f1f2h}{ 'auto' } = 'moto' ; -ok( 'moto' eq imap2_folder_name( 'auto' ), 'imap2_folder_name: auto' ) ; -$sync->{f1f2h}{ 'auto/auto' } = 'moto x 2' ; -ok( 'moto x 2' eq imap2_folder_name( 'auto/auto' ), 'imap2_folder_name: auto/auto' ) ; - -@regextrans2 = ('s,/,X,g'); -ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string [s,/,X,g]'); -ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla [s,/,X,g]'); -ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam [s,/,X,g]'); -ok('spamXspam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam [s,/,X,g]'); -ok('spam.spamXspam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam [s,/,X,g]'); - -@regextrans2 = ( 's, ,_,g' ) ; -ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla [s, ,_,g]'); -ok('bla_bla' eq imap2_folder_name('bla bla'), 'imap2_folder_name: blabla [s, ,_,g]'); - -@regextrans2 = ( q{s,(.*),\U$1,} ) ; -ok( 'BLABLA' eq imap2_folder_name( 'blabla' ), q{imap2_folder_name: blabla [s,\U(.*)\E,$1,]} ) ; - -$fixslash2 = 1 ; -@regextrans2 = ( ) ; -ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string'); -ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla'); -ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam'); -ok('spam_spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam -> spam_spam'); -ok('spam.spam_spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam_spam'); -ok('s pam.spam_spa m' eq imap2_folder_name('s pam/spam.spa m'), 'imap2_folder_name: s pam/spam.spa m -> s pam.spam_spa m'); - -$h1_sep = '.'; -$h2_sep = '/'; -ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string'); -ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla'); -ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam'); -ok('spam/spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam -> spam/spam'); -ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam'); + ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Getting separators\n" ) ; + $sync->{ h1_sep } = get_separator( $sync->{imap1}, $sync->{ sep1 }, '--sep1', 'Host1', \@h1_folders_all ) ; + $sync->{ h2_sep } = get_separator( $sync->{imap2}, $sync->{ sep2 }, '--sep2', 'Host2', \@h2_folders_all ) ; + $sync->{ h1_prefix } = get_prefix( $sync->{imap1}, $prefix1, '--prefix1', 'Host1', \@h1_folders_all ) ; + $sync->{ h2_prefix } = get_prefix( $sync->{imap2}, $prefix2, '--prefix2', 'Host2', \@h2_folders_all ) ; -$fixslash2 = 0 ; -$sync->{ h1_prefix } = q{ }; - -ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam'); -ok('spam.spam/spam' eq imap2_folder_name(' spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam'); - -$h1_sep = '.' ; -$h2_sep = '/' ; -$sync->{ h1_prefix } = 'INBOX.' ; -$sync->{ h2_prefix } = q{} ; -@regextrans2 = ( q{s,(.*),\U$1,} ) ; -ok( 'BLABLA' eq imap2_folder_name( 'blabla' ), 'imap2_folder_name: blabla' ) ; -ok( 'TEST/TEST/TEST/TEST' eq imap2_folder_name( 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ; -@regextrans2 = ( q{s,(.*),\L$1,} ) ; -ok( 'test/test/test/test' eq imap2_folder_name( 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ; + myprint( "Host1: separator and prefix: [$sync->{ h1_sep }][$sync->{ h1_prefix }]\n" ) ; + myprint( "Host2: separator and prefix: [$sync->{ h2_sep }][$sync->{ h2_prefix }]\n" ) ; + return ; +} - note( 'Leaving tests_imap2_folder_name()' ) ; - return ; +sub subfolder1 +{ + my $mysync = shift ; + my $subfolder1 = sanitize_subfolder( $mysync->{ subfolder1 } ) ; + if ( $subfolder1 ) + { + # turns off automap + myprint( "Turning off automapping folders because of --subfolder1\n" ) ; + $mysync->{ automap } = undef ; + myprint( "Sanitizing subfolder1: [$mysync->{ subfolder1 }] => [$subfolder1]\n" ) ; + $mysync->{ subfolder1 } = $subfolder1 ; + add_subfolder1_to_folderrec( $mysync ) || exit_clean( $mysync, $EXIT_SUBFOLDER1_NO_EXISTS ) ; + } + else + { + $mysync->{ subfolder1 } = undef ; + } + + return ; +} + +sub subfolder2 +{ + my $mysync = shift ; + my $subfolder2 = sanitize_subfolder( $mysync->{ subfolder2 } ) ; + if ( $subfolder2 ) + { + # turns off automap + myprint( "Turning off automapping folders because of --subfolder2\n" ) ; + $mysync->{ automap } = undef ; + myprint( "Sanitizing subfolder2: [$mysync->{ subfolder2 }] => [$subfolder2]\n" ) ; + $mysync->{ subfolder2 } = $subfolder2 ; + set_regextrans2_for_subfolder2( $mysync ) ; + } + else + { + $mysync->{ subfolder2 } = undef ; + } + + return ; +} + +sub tests_sanitize_subfolder +{ + note( 'Entering tests_sanitize_subfolder()' ) ; + + is( undef, sanitize_subfolder( ), 'sanitize_subfolder: no args => undef' ) ; + is( undef, sanitize_subfolder( '' ), 'sanitize_subfolder: empty => undef' ) ; + is( undef, sanitize_subfolder( ' ' ), 'sanitize_subfolder: blank => undef' ) ; + is( undef, sanitize_subfolder( ' ' ), 'sanitize_subfolder: blanks => undef' ) ; + is( 'abcd', sanitize_subfolder( 'abcd' ), 'sanitize_subfolder: abcd => abcd' ) ; + is( 'ab cd', sanitize_subfolder( ' ab cd ' ), 'sanitize_subfolder: " ab cd " => "ab cd"' ) ; + is( 'abcd', sanitize_subfolder( q{a&~b#\\c[]=d;} ), 'sanitize_subfolder: "a&~b#\\c[]=d;" => "abcd"' ) ; + is( 'aA.b-_ 8c/dD', sanitize_subfolder( 'aA.b-_ 8c/dD' ), 'sanitize_subfolder: aA.b-_ 8c/dD => aA.b-_ 8c/dD' ) ; + note( 'Leaving tests_sanitize_subfolder()' ) ; + return ; +} + + +sub sanitize_subfolder +{ + my $subfolder = shift ; + + if ( ! $subfolder ) + { + return ; + } + # Remove edging blanks + $subfolder =~ s,^ +| +$,,g ; + # Keep only abcd...ABCD...0123... and -_./ + $subfolder =~ tr,-_a-zA-Z0-9./ ,,cd ; + + # A blank subfolder is not a subfolder + if ( ! $subfolder ) + { + return ; + } + else + { + return $subfolder ; + } } + + +sub tests_add_subfolder1_to_folderrec +{ + note( 'Entering tests_add_subfolder1_to_folderrec()' ) ; + + is( undef, add_subfolder1_to_folderrec( ), 'add_subfolder1_to_folderrec: undef => undef' ) ; + is_deeply( [], [ add_subfolder1_to_folderrec( ) ], 'add_subfolder1_to_folderrec: no args => empty array' ) ; + @folderrec = () ; + my $mysync = {} ; + is_deeply( [ ], [ add_subfolder1_to_folderrec( $mysync ) ], 'add_subfolder1_to_folderrec: empty => empty array' ) ; + is_deeply( [ ], [ @folderrec ], 'add_subfolder1_to_folderrec: empty => empty folderrec' ) ; + $mysync->{ subfolder1 } = 'SUBI' ; + $h1_folders_all{ 'SUBI' } = 1 ; + $mysync->{ h1_prefix } = 'INBOX/' ; + is_deeply( [ 'SUBI' ], [ add_subfolder1_to_folderrec( $mysync ) ], 'add_subfolder1_to_folderrec: SUBI => SUBI' ) ; + is_deeply( [ 'SUBI' ], [ @folderrec ], 'add_subfolder1_to_folderrec: SUBI => folderrec SUBI ' ) ; + + @folderrec = () ; + $mysync->{ subfolder1 } = 'SUBO' ; + is_deeply( [ ], [ add_subfolder1_to_folderrec( $mysync ) ], 'add_subfolder1_to_folderrec: SUBO no exists => empty array' ) ; + is_deeply( [ ], [ @folderrec ], 'add_subfolder1_to_folderrec: SUBO no exists => empty folderrec' ) ; + $h1_folders_all{ 'INBOX/SUBO' } = 1 ; + is_deeply( [ 'INBOX/SUBO' ], [ add_subfolder1_to_folderrec( $mysync ) ], 'add_subfolder1_to_folderrec: SUBO + INBOX/SUBO exists => INBOX/SUBO' ) ; + is_deeply( [ 'INBOX/SUBO' ], [ @folderrec ], 'add_subfolder1_to_folderrec: SUBO + INBOX/SUBO exists => INBOX/SUBO folderrec' ) ; + + note( 'Leaving tests_add_subfolder1_to_folderrec()' ) ; + return ; +} + + +sub add_subfolder1_to_folderrec +{ + my $mysync = shift ; + if ( ! $mysync || ! $mysync->{ subfolder1 } ) + { + return ; + } + + my $subfolder1 = $mysync->{ subfolder1 } ; + my $subfolder1_extended = $mysync->{ h1_prefix } . $subfolder1 ; + + if ( exists $h1_folders_all{ $subfolder1 } ) + { + myprint( qq{Acting like --folderrec "$subfolder1"\n} ) ; + push @folderrec, $subfolder1 ; + } + elsif ( exists $h1_folders_all{ $subfolder1_extended } ) + { + myprint( qq{Acting like --folderrec "$subfolder1_extended"\n} ) ; + push @folderrec, $subfolder1_extended ; + } + else + { + myprint( qq{Nor folder "$subfolder1" nor "$subfolder1_extended" exists on host1\n} ) ; + } + return @folderrec ; +} + +sub set_regextrans2_for_subfolder2 +{ + my $mysync = shift ; + + + unshift @{ $mysync->{ regextrans2 } }, + q(s,^$mysync->{ h2_prefix }(.*),$mysync->{ h2_prefix }$mysync->{ subfolder2 }$mysync->{ h2_sep }$1,), + q(s,^INBOX$,$mysync->{ h2_prefix }$mysync->{ subfolder2 }$mysync->{ h2_sep }INBOX,), + q(s,^($mysync->{ h2_prefix }){2},$mysync->{ h2_prefix },); + + #myprint( "@{ $mysync->{ regextrans2 } }\n" ) ; + return ; +} + + + +# Looks like no globals here + +sub tests_imap2_folder_name +{ + note( 'Entering tests_imap2_folder_name()' ) ; + + my $mysync = {} ; + $mysync->{ h1_prefix } = q{} ; + $mysync->{ h2_prefix } = q{} ; + $mysync->{ h1_sep } = '/'; + $mysync->{ h2_sep } = '.'; + + $mysync->{ debug } and myprint( <<"EOS" +prefix1: [$mysync->{ h1_prefix }] +prefix2: [$mysync->{ h2_prefix }] +sep1: [$sync->{ h1_sep }] +sep2: [$sync->{ h2_sep }] +EOS +) ; + + $mysync->{ fixslash2 } = 0 ; + is( q{INBOX}, imap2_folder_name( $mysync, q{} ), 'imap2_folder_name: empty string' ) ; + is( 'blabla', imap2_folder_name( $mysync, 'blabla' ), 'imap2_folder_name: blabla' ) ; + is('spam.spam', imap2_folder_name( $mysync, 'spam/spam' ), 'imap2_folder_name: spam/spam' ) ; + + is( 'spam/spam', imap2_folder_name( $mysync, 'spam.spam' ), 'imap2_folder_name: spam.spam') ; + is( 'spam.spam/spam', imap2_folder_name( $mysync, 'spam/spam.spam' ), 'imap2_folder_name: spam/spam.spam' ) ; + is( 's pam.spam/sp am', imap2_folder_name( $mysync, 's pam/spam.sp am' ), 'imap2_folder_name: s pam/spam.sp am' ) ; + + $mysync->{f1f2h}{ 'auto' } = 'moto' ; + is( 'moto', imap2_folder_name( $mysync, 'auto' ), 'imap2_folder_name: auto' ) ; + $mysync->{f1f2h}{ 'auto/auto' } = 'moto x 2' ; + is( 'moto x 2', imap2_folder_name( $mysync, 'auto/auto' ), 'imap2_folder_name: auto/auto' ) ; + + @{ $mysync->{ regextrans2 } } = ( 's,/,X,g' ) ; + is( q{INBOX}, imap2_folder_name( $mysync, q{} ), 'imap2_folder_name: empty string [s,/,X,g]' ) ; + is( 'blabla', imap2_folder_name( $mysync, 'blabla' ), 'imap2_folder_name: blabla [s,/,X,g]' ) ; + is('spam.spam', imap2_folder_name( $mysync, 'spam/spam'), 'imap2_folder_name: spam/spam [s,/,X,g]'); + is('spamXspam', imap2_folder_name( $mysync, 'spam.spam'), 'imap2_folder_name: spam.spam [s,/,X,g]'); + is('spam.spamXspam', imap2_folder_name( $mysync, 'spam/spam.spam'), 'imap2_folder_name: spam/spam.spam [s,/,X,g]'); + + @{ $mysync->{ regextrans2 } } = ( 's, ,_,g' ) ; + is('blabla', imap2_folder_name( $mysync, 'blabla'), 'imap2_folder_name: blabla [s, ,_,g]'); + is('bla_bla', imap2_folder_name( $mysync, 'bla bla'), 'imap2_folder_name: blabla [s, ,_,g]'); + + @{ $mysync->{ regextrans2 } } = ( q{s,(.*),\U$1,} ) ; + is( 'BLABLA', imap2_folder_name( $mysync, 'blabla' ), q{imap2_folder_name: blabla [s,\U(.*)\E,$1,]} ) ; + + $mysync->{ fixslash2 } = 1 ; + @{ $mysync->{ regextrans2 } } = ( ) ; + is(q{INBOX}, imap2_folder_name( $mysync, q{}), 'imap2_folder_name: empty string'); + is('blabla', imap2_folder_name( $mysync, 'blabla'), 'imap2_folder_name: blabla'); + is('spam.spam', imap2_folder_name( $mysync, 'spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam'); + is('spam_spam', imap2_folder_name( $mysync, 'spam.spam'), 'imap2_folder_name: spam.spam -> spam_spam'); + is('spam.spam_spam', imap2_folder_name( $mysync, 'spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam_spam'); + is('s pam.spam_spa m', imap2_folder_name( $mysync, 's pam/spam.spa m'), 'imap2_folder_name: s pam/spam.spa m -> s pam.spam_spa m'); + + $mysync->{ h1_sep } = '.'; + $mysync->{ h2_sep } = '/'; + is( q{INBOX}, imap2_folder_name( $mysync, q{}), 'imap2_folder_name: empty string'); + is('blabla', imap2_folder_name( $mysync, 'blabla'), 'imap2_folder_name: blabla'); + is('spam.spam', imap2_folder_name( $mysync, 'spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam'); + is('spam/spam', imap2_folder_name( $mysync, 'spam.spam'), 'imap2_folder_name: spam.spam -> spam/spam'); + is('spam.spam/spam', imap2_folder_name( $mysync, 'spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam'); + + + + $mysync->{ fixslash2 } = 0 ; + $mysync->{ h1_prefix } = q{ }; + + is( 'spam.spam/spam', imap2_folder_name( $mysync, 'spam/spam.spam' ), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam' ) ; + is( 'spam.spam/spam', imap2_folder_name( $mysync, ' spam/spam.spam' ), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam' ) ; + + $mysync->{ h1_sep } = '.' ; + $mysync->{ h2_sep } = '/' ; + $mysync->{ h1_prefix } = 'INBOX.' ; + $mysync->{ h2_prefix } = q{} ; + @{ $mysync->{ regextrans2 } } = ( q{s,(.*),\U$1,} ) ; + is( 'BLABLA', imap2_folder_name( $mysync, 'blabla' ), 'imap2_folder_name: blabla' ) ; + is( 'TEST/TEST/TEST/TEST', imap2_folder_name( $mysync, 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ; + @{ $mysync->{ regextrans2 } } = ( q{s,(.*),\L$1,} ) ; + is( 'test/test/test/test', imap2_folder_name( $mysync, 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ; + + # INBOX + $mysync = {} ; + $mysync->{ h1_prefix } = q{Pf1.} ; + $mysync->{ h2_prefix } = q{Pf2/} ; + $mysync->{ h1_sep } = '.'; + $mysync->{ h2_sep } = '/'; + + # + #$mysync->{ debug } = 1 ; + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'F1.F2.F3' ), 'imap2_folder_name: F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + is( 'Pf2/F1/INBOX', imap2_folder_name( $mysync, 'F1.INBOX' ), 'imap2_folder_name: F1.INBOX -> Pf2/F1/INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'INBOX' ), 'imap2_folder_name: INBOX -> INBOX' ) ; + + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'Pf1.F1.F2.F3' ), 'imap2_folder_name: Pf1.F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + is( 'Pf2/F1/INBOX', imap2_folder_name( $mysync, 'Pf1.F1.INBOX' ), 'imap2_folder_name: Pf1.F1.INBOX -> Pf2/F1/INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.INBOX' ), 'imap2_folder_name: Pf1.INBOX -> INBOX' ) ; # not Pf2/INBOX: Yes I can! + + + + # subfolder2 + $mysync = {} ; + $mysync->{ h1_prefix } = q{} ; + $mysync->{ h2_prefix } = q{} ; + $mysync->{ h1_sep } = '/'; + $mysync->{ h2_sep } = '.'; + + + set_regextrans2_for_subfolder2( $mysync ) ; + $mysync->{ subfolder2 } = 'S1.S2' ; + is( 'S1.S2.F1.F2.F3', imap2_folder_name( $mysync, 'F1/F2/F3' ), 'imap2_folder_name: F1/F2/F3 -> S1.S2.F1.F2.F3' ) ; + is( 'S1.S2.INBOX', imap2_folder_name( $mysync, 'INBOX' ), 'imap2_folder_name: F1/F2/F3 -> S1.S2.INBOX' ) ; + + $mysync = {} ; + $mysync->{ h1_prefix } = q{Pf1/} ; + $mysync->{ h2_prefix } = q{Pf2.} ; + $mysync->{ h1_sep } = '/'; + $mysync->{ h2_sep } = '.'; + #$mysync->{ debug } = 1 ; + + set_regextrans2_for_subfolder2( $mysync ) ; + $mysync->{ subfolder2 } = 'Pf2.S1.S2' ; + is( 'Pf2.S1.S2.F1.F2.F3', imap2_folder_name( $mysync, 'F1/F2/F3' ), 'imap2_folder_name: F1/F2/F3 -> Pf2.S1.S2.F1.F2.F3' ) ; + is( 'Pf2.S1.S2.INBOX', imap2_folder_name( $mysync, 'INBOX' ), 'imap2_folder_name: INBOX -> Pf2.S1.S2.INBOX' ) ; + is( 'Pf2.S1.S2.F1.F2.F3', imap2_folder_name( $mysync, 'Pf1/F1/F2/F3' ), 'imap2_folder_name: F1/F2/F3 -> Pf2.S1.S2.F1.F2.F3' ) ; + is( 'Pf2.S1.S2.INBOX', imap2_folder_name( $mysync, 'Pf1/INBOX' ), 'imap2_folder_name: INBOX -> Pf2.S1.S2.INBOX' ) ; + + # subfolder1 + # scenario as the reverse of the previous tests, separators point of vue + $mysync = {} ; + $mysync->{ h1_prefix } = q{Pf1.} ; + $mysync->{ h2_prefix } = q{Pf2/} ; + $mysync->{ h1_sep } = '.'; + $mysync->{ h2_sep } = '/'; + #$mysync->{ debug } = 1 ; + + $mysync->{ subfolder1 } = 'S1.S2' ; + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'S1.S2.F1.F2.F3' ), 'imap2_folder_name: S1.S2.F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'Pf1.S1.S2.F1.F2.F3' ), 'imap2_folder_name: Pf1.S1.S2.F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2.INBOX' ), 'imap2_folder_name: S1.S2.INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2' ), 'imap2_folder_name: S1.S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2.' ), 'imap2_folder_name: S1.S2. -> INBOX' ) ; + + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2.INBOX' ), 'imap2_folder_name: Pf1.S1.S2.INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2' ), 'imap2_folder_name: Pf1.S1.S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2.' ), 'imap2_folder_name: Pf1.S1.S2. -> INBOX' ) ; + + + $mysync->{ subfolder1 } = 'S1.S2.' ; + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'S1.S2.F1.F2.F3' ), 'imap2_folder_name: S1.S2.F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + is( 'Pf2/F1/F2/F3', imap2_folder_name( $mysync, 'Pf1.S1.S2.F1.F2.F3' ), 'imap2_folder_name: Pf1.S1.S2.F1.F2.F3 -> Pf2/F1/F2/F3' ) ; + + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2.INBOX' ), 'imap2_folder_name: S1.S2.INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2' ), 'imap2_folder_name: S1.S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1.S2.' ), 'imap2_folder_name: S1.S2. -> INBOX' ) ; + + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2.INBOX' ), 'imap2_folder_name: Pf1.S1.S2.INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2' ), 'imap2_folder_name: Pf1.S1.S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'Pf1.S1.S2.' ), 'imap2_folder_name: Pf1.S1.S2. -> INBOX' ) ; + + + # subfolder1 + # scenario as Gmail + $mysync = {} ; + $mysync->{ h1_prefix } = q{} ; + $mysync->{ h2_prefix } = q{} ; + $mysync->{ h1_sep } = '/'; + $mysync->{ h2_sep } = '/'; + #$mysync->{ debug } = 1 ; + + $mysync->{ subfolder1 } = 'S1/S2' ; + is( 'F1/F2/F3', imap2_folder_name( $mysync, 'S1/S2/F1/F2/F3' ), 'imap2_folder_name: S1/S2/F1/F2/F3 -> F1/F2/F3' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2/INBOX' ), 'imap2_folder_name: S1/S2/INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2' ), 'imap2_folder_name: S1/S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2/' ), 'imap2_folder_name: S1/S2/ -> INBOX' ) ; + + $mysync->{ subfolder1 } = 'S1/S2/' ; + is( 'F1/F2/F3', imap2_folder_name( $mysync, 'S1/S2/F1/F2/F3' ), 'imap2_folder_name: S1/S2/F1/F2/F3 -> F1/F2/F3' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2/INBOX' ), 'imap2_folder_name: S1/S2/INBOX -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2' ), 'imap2_folder_name: S1/S2 -> INBOX' ) ; + is( 'INBOX', imap2_folder_name( $mysync, 'S1/S2/' ), 'imap2_folder_name: S1/S2/ -> INBOX' ) ; + + + note( 'Leaving tests_imap2_folder_name()' ) ; + return ; +} + + # Global variables to remove: -# $debug -# $sync +# + sub imap2_folder_name { - my $mysync = $sync ; # will be soon next line - #my $mysync = shift ; + my $mysync = shift ; my ( $h1_fold ) = shift ; my ( $h2_fold ) ; if ( $mysync->{f1f2h}{ $h1_fold } ) { $h2_fold = $mysync->{f1f2h}{ $h1_fold } ; - ( $debug or $mysync->{debugfolders} ) and myprint( "f1f2 [$h1_fold] -> [$h2_fold]\n" ) ; + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "f1f2 [$h1_fold] -> [$h2_fold]\n" ) ; return( $h2_fold ) ; } if ( $mysync->{f1f2auto}{ $h1_fold } ) { $h2_fold = $mysync->{f1f2auto}{ $h1_fold } ; - ( $debug or $mysync->{debugfolders} ) and myprint( "automap [$h1_fold] -> [$h2_fold]\n" ) ; + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "automap [$h1_fold] -> [$h2_fold]\n" ) ; return( $h2_fold ) ; } + if ( $mysync->{ subfolder1 } ) + { + my $esc_h1_sep = "\\" . $mysync->{ h1_sep } ; + # case where subfolder1 has the sep1 at the end, then remove it + my $part_to_removed = remove_last_char_if_is( $mysync->{ subfolder1 }, $mysync->{ h1_sep } ) ; + # remove the subfolder1 part and the sep1 if present after + $h1_fold =~ s{$part_to_removed($esc_h1_sep)?}{} ; + #myprint( "h1_fold=$h1_fold\n" ) ; + } + + if ( ( '' eq $h1_fold ) or ( $mysync->{ h1_prefix } eq $h1_fold ) ) + { + $h1_fold = 'INBOX' ; + } + $h2_fold = prefix_seperator_invertion( $mysync, $h1_fold ) ; - $h2_fold = regextrans2( $h2_fold ) ; + $h2_fold = regextrans2( $mysync, $h2_fold ) ; return( $h2_fold ) ; } + +sub tests_remove_last_char_if_is +{ + note( 'Entering tests_remove_last_char_if_is()' ) ; + + is( undef, remove_last_char_if_is( ), 'remove_last_char_if_is: no args => undef' ) ; + is( '', remove_last_char_if_is( '' ), 'remove_last_char_if_is: empty => empty' ) ; + is( '', remove_last_char_if_is( '', 'Z' ), 'remove_last_char_if_is: empty Z => empty' ) ; + is( '', remove_last_char_if_is( 'Z', 'Z' ), 'remove_last_char_if_is: Z Z => empty' ) ; + is( 'abc', remove_last_char_if_is( 'abcZ', 'Z' ), 'remove_last_char_if_is: abcZ Z => abc' ) ; + is( 'abcY', remove_last_char_if_is( 'abcY', 'Z' ), 'remove_last_char_if_is: abcY Z => abcY' ) ; + note( 'Leaving tests_remove_last_char_if_is()' ) ; + return ; +} + + + + +sub remove_last_char_if_is +{ + my $string = shift ; + my $char = shift ; + + if ( ! defined $string ) + { + return ; + } + + if ( ! defined $char ) + { + return $string ; + } + + my $last_char = substr $string, -1 ; + if ( $char eq $last_char ) + { + chop $string ; + return $string ; + } + else + { + return $string ; + } +} + sub tests_prefix_seperator_invertion { note( 'Entering tests_prefix_seperator_invertion()' ) ; - undef $h1_sep; - undef $h2_sep ; - is( undef, prefix_seperator_invertion( ), 'prefix_seperator_invertion: no args => undef' ) ; is( q{}, prefix_seperator_invertion( undef, q{} ), 'prefix_seperator_invertion: empty string => empty string' ) ; is( 'lalala', prefix_seperator_invertion( undef, 'lalala' ), 'prefix_seperator_invertion: lalala => lalala' ) ; @@ -6525,9 +7391,7 @@ sub tests_prefix_seperator_invertion } # Global variables to remove: -# $h1_sep -# $h2_sep -# $debug + sub prefix_seperator_invertion { @@ -6539,18 +7403,23 @@ sub prefix_seperator_invertion my $my_h1_prefix = $mysync->{ h1_prefix } || q{} ; my $my_h2_prefix = $mysync->{ h2_prefix } || q{} ; - my $my_h1_sep = $h1_sep || $mysync->{ h1_sep } || '/' ; - my $my_h2_sep = $h2_sep || $mysync->{ h2_sep } || '/' ; + my $my_h1_sep = $mysync->{ h1_sep } || '/' ; + my $my_h2_sep = $mysync->{ h2_sep } || '/' ; # first we remove the prefix $h1_fold =~ s/^\Q$my_h1_prefix\E//x ; - ( $debug or $mysync->{debugfolders} ) and myprint( "removed host1 prefix: [$h1_fold]\n" ) ; - $h2_fold = separator_invert( $h1_fold, $my_h1_sep, $my_h2_sep ) ; - ( $debug or $mysync->{debugfolders} ) and myprint( "inverted separators: [$h2_fold]\n" ) ; + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "removed host1 prefix: [$h1_fold]\n" ) ; + $h2_fold = separator_invert( $mysync, $h1_fold, $my_h1_sep, $my_h2_sep ) ; + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "inverted separators: [$h2_fold]\n" ) ; + # Adding the prefix supplied by namespace or the --prefix2 option - $h2_fold = $my_h2_prefix . $h2_fold - unless( ( $my_h2_prefix eq 'INBOX' . $my_h2_sep ) and ( $h2_fold =~ m/^INBOX$/xi ) ) ; - ( $debug or $mysync->{debugfolders} ) and myprint( "added host2 prefix: [$h2_fold]\n" ) ; + # except for INBOX or Inbox + if ( $h2_fold !~ m/^INBOX$/xi ) + { + $h2_fold = $my_h2_prefix . $h2_fold ; + } + + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "added host2 prefix: [$h2_fold]\n" ) ; return( $h2_fold ) ; } @@ -6558,34 +7427,35 @@ sub tests_separator_invert { note( 'Entering tests_separator_invert()' ) ; - $fixslash2 = 0 ; + my $mysync = {} ; + $mysync->{ fixslash2 } = 0 ; ok( not( defined separator_invert( ) ), 'separator_invert: no args' ) ; ok( not( defined separator_invert( q{} ) ), 'separator_invert: not enough args' ) ; ok( not( defined separator_invert( q{}, q{} ) ), 'separator_invert: not enough args' ) ; - ok( q{} eq separator_invert( q{}, q{}, q{} ), 'separator_invert: 3 empty strings' ) ; - ok( 'lalala' eq separator_invert( 'lalala', q{}, q{} ), 'separator_invert: empty separator' ) ; - ok( 'lalala' eq separator_invert( 'lalala', '/', '/' ), 'separator_invert: same separator /' ) ; - ok( 'lal/ala' eq separator_invert( 'lal/ala', '/', '/' ), 'separator_invert: same separator / 2' ) ; - ok( 'lal.ala' eq separator_invert( 'lal/ala', '/', '.' ), 'separator_invert: separators /.' ) ; - ok( 'lal/ala' eq separator_invert( 'lal.ala', '.', '/' ), 'separator_invert: separators ./' ) ; - ok( 'la.l/ala' eq separator_invert( 'la/l.ala', '.', '/' ), 'separator_invert: separators ./' ) ; + ok( q{} eq separator_invert( $mysync, q{}, q{}, q{} ), 'separator_invert: 3 empty strings' ) ; + ok( 'lalala' eq separator_invert( $mysync, 'lalala', q{}, q{} ), 'separator_invert: empty separator' ) ; + ok( 'lalala' eq separator_invert( $mysync, 'lalala', '/', '/' ), 'separator_invert: same separator /' ) ; + ok( 'lal/ala' eq separator_invert( $mysync, 'lal/ala', '/', '/' ), 'separator_invert: same separator / 2' ) ; + ok( 'lal.ala' eq separator_invert( $mysync, 'lal/ala', '/', '.' ), 'separator_invert: separators /.' ) ; + ok( 'lal/ala' eq separator_invert( $mysync, 'lal.ala', '.', '/' ), 'separator_invert: separators ./' ) ; + ok( 'la.l/ala' eq separator_invert( $mysync, 'la/l.ala', '.', '/' ), 'separator_invert: separators ./' ) ; - ok( 'l/al.ala' eq separator_invert( 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ; - $fixslash2 = 1 ; - ok( 'l_al.ala' eq separator_invert( 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ; + ok( 'l/al.ala' eq separator_invert( $mysync, 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ; + $mysync->{ fixslash2 } = 1 ; + ok( 'l_al.ala' eq separator_invert( $mysync, 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ; note( 'Leaving tests_separator_invert()' ) ; return ; } # Global variables to remove: -# $fixslash2 +# sub separator_invert { - my( $h1_fold, $h1_separator, $h2_separator ) = @_ ; + my( $mysync, $h1_fold, $h1_separator, $h2_separator ) = @_ ; - return( undef ) if ( not defined $h1_fold or not defined $h1_separator or not defined $h2_separator ) ; + return( undef ) if ( not all_defined( $mysync, $h1_fold, $h1_separator, $h2_separator ) ) ; # The separator we hope we'll never encounter: 00000000 == 0x00 my $o_sep = "\000" ; @@ -6593,21 +7463,21 @@ sub separator_invert $h2_fold =~ s,\Q$h2_separator,$o_sep,xg ; $h2_fold =~ s,\Q$h1_separator,$h2_separator,xg ; $h2_fold =~ s,\Q$o_sep,$h1_separator,xg ; - $h2_fold =~ s,/,_,xg if( $fixslash2 and '/' ne $h2_separator and '/' eq $h1_separator ) ; + $h2_fold =~ s,/,_,xg if( $mysync->{ fixslash2 } and '/' ne $h2_separator and '/' eq $h1_separator ) ; return( $h2_fold ) ; } sub regextrans2 { - my( $h2_fold ) = @_ ; + my( $mysync, $h2_fold ) = @_ ; # Transforming the folder name by the --regextrans2 option(s) - foreach my $regextrans2 ( @regextrans2 ) { + foreach my $regextrans2 ( @{ $mysync->{ regextrans2 } } ) { my $h2_fold_before = $h2_fold ; my $ret = eval "\$h2_fold =~ $regextrans2 ; 1 " ; - ( $debug or $sync->{debugfolders} ) and myprint( "[$h2_fold_before] -> [$h2_fold] using regextrans2 [$regextrans2]\n" ) ; + ( $mysync->{ debug } or $mysync->{debugfolders} ) and myprint( "[$h2_fold_before] -> [$h2_fold] using regextrans2 [$regextrans2]\n" ) ; if ( not ( defined $ret ) or $EVAL_ERROR ) { - die_clean( "error: eval regextrans2 '$regextrans2': $EVAL_ERROR\n" ) ; + exit_clean( $mysync, $EX_USAGE, "error: eval regextrans2 '$regextrans2': $EVAL_ERROR\n" ) ; } } return( $h2_fold ) ; @@ -6754,7 +7624,7 @@ sub tests_flags_regex @regexflag = ( 's/NonJunk//g' ) ; ok( q{\Seen $Spam} eq flags_regex( q{\Seen NonJunk $Spam} ), q{flags_regex, remove NonJunk: 's/NonJunk//g'} ) ; - @regexflag = ( q's/\$Spam//g' ) ; + @regexflag = ( q{s/\$Spam//g} ) ; ok( q{\Seen NonJunk } eq flags_regex( q{\Seen NonJunk $Spam} ), q{flags_regex, remove $Spam: 's/\$Spam//g'} ) ; @regexflag = ( 's/\\\\Seen//g' ) ; @@ -6907,7 +7777,7 @@ sub permanentflags foreach my $line (@lines) { if ( $line =~ m{\[PERMANENTFLAGS\s\(([^)]+?)\)\]}x ) { - ( $debugflags or $debug ) and myprint( "permanentflags: $line" ) ; + ( $debugflags or $sync->{ debug } ) and myprint( "permanentflags: $line" ) ; my $permanentflags = $1 ; if ( $permanentflags =~ m{\\\*}x ) { $permanentflags = q{} ; @@ -7233,13 +8103,13 @@ sub size_filtered my( $h1_size, $h1_msg, $h1_fold, $h2_fold ) = @_ ; $h1_size = 0 if ( ! $h1_size ) ; # null if empty or undef - if (defined $sync->{ maxsize } and $h1_size > $sync->{ maxsize }) { + if ( defined $sync->{ maxsize } and $h1_size > $sync->{ maxsize } ) { myprint( "msg $h1_fold/$h1_msg skipped ($h1_size exceeds maxsize limit $sync->{ maxsize } bytes)\n" ) ; $sync->{ total_bytes_skipped } += $h1_size; $sync->{ nb_msg_skipped } += 1; return( 1 ) ; } - if (defined $minsize and $h1_size <= $minsize) { + if ( defined $minsize and $h1_size <= $minsize ) { myprint( "msg $h1_fold/$h1_msg skipped ($h1_size smaller than minsize $minsize bytes)\n" ) ; $sync->{ total_bytes_skipped } += $h1_size; $sync->{ nb_msg_skipped } += 1; @@ -7270,7 +8140,7 @@ sub stats_update_skip_message my $mysync = shift ; # to be used my $h1_size = shift ; - $sync->{ total_bytes_skipped } += $h1_size ; + $mysync->{ total_bytes_skipped } += $h1_size ; $mysync->{ nb_msg_skipped } += 1 ; $mysync->{ h1_nb_msg_processed } +=1 ; return ; @@ -7281,7 +8151,7 @@ sub copy_message # copy my ( $mysync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) = @_ ; - ( $debug or $mysync->{dry}) and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $mysync->{dry_message}\n" ) ; + ( $mysync->{ debug } or $mysync->{dry}) and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $mysync->{dry_message}\n" ) ; my $h1_size = $h1_fir_ref->{$h1_msg}->{'RFC822.SIZE'} || 0 ; my $h1_flags = $h1_fir_ref->{$h1_msg}->{'FLAGS'} || q{} ; @@ -7325,12 +8195,12 @@ sub copy_message my $h1_date = date_for_host2( $h1_msg, $h1_idate ) ; - ( $debug or $debugflags ) and + ( $mysync->{ debug } or $debugflags ) and myprint( "Host1: flags init msg $h1_fold/$h1_msg date [$h1_date] flags [$h1_flags] size [$h1_size]\n" ) ; $h1_flags = flags_for_host2( $h1_flags, $permanentflags2 ) ; - ( $debug or $debugflags ) and + ( $mysync->{ debug } or $debugflags ) and myprint( "Host1: flags filt msg $h1_fold/$h1_msg date [$h1_date] flags [$h1_flags] size [$h1_size]\n" ) ; $h1_date = undef if ( $h1_date eq q{} ) ; @@ -7409,16 +8279,19 @@ sub message_for_host2 my ( $mysync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ) = @_ ; # abort when missing a parameter - if ( (!$sync) or (!$h1_msg) or (!$h1_fold) or (!$h1_size) or (!defined $h1_flags) or (!defined $h1_idate) or (!$h1_fir_ref) or (!$string_ref) ) { + if ( ( ! $mysync ) or ( ! $h1_msg ) or ( ! $h1_fold ) or ( ! defined $h1_size ) + or ( ! defined $h1_flags) or ( ! defined $h1_idate ) + or ( ! $h1_fir_ref) or ( ! $string_ref ) ) + { return ; } - myprint( debugmemory( $sync, " at M1" ) ) ; + myprint( debugmemory( $mysync, " at M1" ) ) ; my $string_ok = $mysync->{imap1}->message_to_file( $string_ref, $h1_msg ) ; - myprint( debugmemory( $sync, " at M2" ) ) ; + myprint( debugmemory( $mysync, " at M2" ) ) ; my $string_len = length_ref( $string_ref ) ; @@ -7474,9 +8347,14 @@ sub message_for_host2 if ( $mysync->{addheader} and defined $h1_fir_ref->{$h1_msg}->{'NO_HEADER'} ) { my $header = add_header( $h1_msg ) ; - $debug and myprint( "msg $h1_fold/$h1_msg adding custom header [$header]\n" ) ; + $mysync->{ debug } and myprint( "msg $h1_fold/$h1_msg adding custom header [$header]\n" ) ; ${ $string_ref } = $header . "\r\n" . ${ $string_ref } ; } + + if ( ( defined $mysync->{ truncmess } ) and is_an_integer( $mysync->{ truncmess } ) ) + { + ${ $string_ref } = truncmess( ${ $string_ref }, $mysync->{ truncmess } ) ; + } $string_len = length_ref( $string_ref ) ; @@ -7484,14 +8362,39 @@ sub message_for_host2 q{=} x $STD_CHAR_PER_LINE, "\n", "F message content begin next line ($string_len characters long)\n", ${ $string_ref }, - "F message content ended on previous line\n", q{=} x $STD_CHAR_PER_LINE, "\n" ) ; + "\nF message content ended on previous line\n", q{=} x $STD_CHAR_PER_LINE, "\n" ) ; - myprint( debugmemory( $sync, " at M3" ) ) ; + myprint( debugmemory( $mysync, " at M3" ) ) ; return $string_len ; } +sub tests_truncmess +{ + note( 'Entering tests_truncmess()' ) ; + is( undef, truncmess( ), 'truncmess: no args => undef' ) ; + is( 'abc', truncmess( 'abc' ), 'truncmess: abc => abc' ) ; + is( 'ab', truncmess( 'abc', 2 ), 'truncmess: abc 2 => ab' ) ; + is( 'abc', truncmess( 'abc', 3 ), 'truncmess: abc 3 => abc' ) ; + is( 'abc', truncmess( 'abc', 4 ), 'truncmess: abc 4 => abc' ) ; + is( '12345', truncmess( "123456789\n", 5 ), 'truncmess: "123456789\n", 5 => 12345' ) ; + is( "123456789\n" x 5000, truncmess( "123456789\n" x 100000, 50000 ), 'truncmess: "123456789\n" x 100000, 50000 => "123456789\n" x 5000' ) ; + note( 'Leaving tests_truncmess()' ) ; + return ; +} + +sub truncmess +{ + my $string = shift ; + my $length = shift ; + + if ( not defined $string ) { return ; } + if ( not defined $length ) { return $string ; } + + $string = substr $string, 0, $length ; + return $string ; +} sub tests_message_for_host2 { @@ -7567,28 +8470,252 @@ sub tests_message_for_host2 return ; } - -sub labels +sub tests_labels_remove_subfolder1 { - my ( $myimap, $uid ) = @ARG ; + note( 'Entering tests_labels_remove_subfolder1()' ) ; + is( undef, labels_remove_subfolder1( ), 'labels_remove_subfolder1: no parameters => undef' ) ; + is( 'Blabla', labels_remove_subfolder1( 'Blabla' ), 'labels_remove_subfolder1: one parameter Blabla => Blabla' ) ; + is( 'Blan blue', labels_remove_subfolder1( 'Blan blue' ), 'labels_remove_subfolder1: one parameter Blan blue => Blan blue' ) ; + is( '\Bla "Blan blan" Blabla', labels_remove_subfolder1( '\Bla "Blan blan" Blabla' ), + 'labels_remove_subfolder1: one parameter \Bla "Blan blan" Blabla => \Bla "Blan blan" Blabla' ) ; - if ( not all_defined( $myimap, $uid ) ) { - return ; + is( 'Bla', labels_remove_subfolder1( 'Subf/Bla', 'Subf' ), 'labels_remove_subfolder1: Subf/Bla Subf => "Bla"' ) ; + + + is( '"\\\\Bla"', labels_remove_subfolder1( '"\\\\Bla"', 'Subf' ), 'labels_remove_subfolder1: "\\\\Bla" Subf => "\\\\Bla"' ) ; + + is( 'Bla Kii', labels_remove_subfolder1( 'Subf/Bla Subf/Kii', 'Subf' ), + 'labels_remove_subfolder1: Subf/Bla Subf/Kii, Subf => "Bla" "Kii"' ) ; + + is( '"\\\\Bla" Kii', labels_remove_subfolder1( '"\\\\Bla" Subf/Kii', 'Subf' ), + 'labels_remove_subfolder1: "\\\\Bla" Subf/Kii Subf => "\\\\Bla" Kii' ) ; + + is( '"Blan blan"', labels_remove_subfolder1( '"Subf/Blan blan"', 'Subf' ), + 'labels_remove_subfolder1: "Subf/Blan blan" Subf => "Blan blan"' ) ; + + is( '"\\\\Loo" "Blan blan" Kii', labels_remove_subfolder1( '"\\\\Loo" "Subf/Blan blan" Subf/Kii', 'Subf' ), + 'labels_remove_subfolder1: "\\\\Loo" "Subf/Blan blan" Subf/Kii + Subf => "\\\\Loo" "Blan blan" Kii' ) ; + + is( '"\\\\Inbox"', labels_remove_subfolder1( 'Subf/INBOX', 'Subf' ), + 'labels_remove_subfolder1: Subf/INBOX + Subf => "\\\\Inbox"' ) ; + + is( '"\\\\Loo" "Blan blan" Kii "\\\\Inbox"', labels_remove_subfolder1( '"\\\\Loo" "Subf/Blan blan" Subf/Kii Subf/INBOX', 'Subf' ), + 'labels_remove_subfolder1: "\\\\Loo" "Subf/Blan blan" Subf/Kii Subf/INBOX + Subf => "\\\\Loo" "Blan blan" Kii "\\\\Inbox"' ) ; + + + note( 'Leaving tests_labels_remove_subfolder1()' ) ; + return ; +} + + + +sub labels_remove_subfolder1 +{ + my $labels = shift ; + my $subfolder1 = shift ; + + if ( not defined $labels ) { return ; } + if ( not defined $subfolder1 ) { return $labels ; } + + my @labels = quotewords('\s+', 1, $labels ) ; + #myprint( "@labels\n" ) ; + my @labels_subfolder2 ; + + foreach my $label ( @labels ) + { + if ( $label =~ m{zzzzzzzzzz} ) + { + # \Seen \Deleted ... stay the same + push @labels_subfolder2, $label ; + } + else + { + # Remove surrounding quotes if any, to add them again in case of space + $label = join( '', quotewords('\s+', 0, $label ) ) ; + $label =~ s{$subfolder1/?}{} ; + if ( 'INBOX' eq $label ) + { + push @labels_subfolder2, q{"\\\\Inbox"} ; + } + elsif ( $label =~ m{\\} ) + { + push @labels_subfolder2, qq{"\\$label"} ; + } + elsif ( $label =~ m{ } ) + { + push @labels_subfolder2, qq{"$label"} ; + } + else + { + push @labels_subfolder2, $label ; + } + } } - #$myimap->Debug( 1 ) ; - my $hash = $myimap->fetch_hash( [ $uid ], 'X-GM-LABELS' ) ; - #$myimap->Debug( 0 ) ; - my $labels = $hash->{ $uid }->{ 'X-GM-LABELS' } ; - $labels = $myimap->Unescape( $labels ) ; - return $labels ; + + my $labels_subfolder2 = join( ' ', sort uniq( @labels_subfolder2 ) ) ; + + return $labels_subfolder2 ; +} + +sub tests_labels_remove_special +{ + note( 'Entering tests_labels_remove_special()' ) ; + + is( undef, labels_remove_special( ), 'labels_remove_special: no parameters => undef' ) ; + is( '', labels_remove_special( '' ), 'labels_remove_special: empty string => empty string' ) ; + is( '', labels_remove_special( '"\\\\Inbox"' ), 'labels_remove_special:"\\\\Inbox" => empty string' ) ; + is( '', labels_remove_special( '"\\\\Inbox" "\\\\Starred"' ), 'labels_remove_special:"\\\\Inbox" "\\\\Starred" => empty string' ) ; + is( 'Bar Foo', labels_remove_special( 'Foo Bar' ), 'labels_remove_special:Foo Bar => Bar Foo' ) ; + is( 'Bar Foo', labels_remove_special( 'Foo Bar "\\\\Inbox"' ), 'labels_remove_special:Foo Bar "\\\\Inbox" => Bar Foo' ) ; + note( 'Leaving tests_labels_remove_special()' ) ; + return ; +} + + + + +sub labels_remove_special +{ + my $labels = shift ; + + if ( not defined $labels ) { return ; } + + my @labels = quotewords('\s+', 1, $labels ) ; + myprint( "labels before remove_non_folded: @labels\n" ) ; + my @labels_remove_special ; + + foreach my $label ( @labels ) + { + if ( $label =~ m{^\"\\\\} ) + { + # not kept + } + else + { + push @labels_remove_special, $label ; + } + } + + my $labels_remove_special = join( ' ', sort @labels_remove_special ) ; + + return $labels_remove_special ; +} + + +sub tests_labels_add_subfolder2 +{ + note( 'Entering tests_labels_add_subfolder2()' ) ; + is( undef, labels_add_subfolder2( ), 'labels_add_subfolder2: no parameters => undef' ) ; + is( 'Blabla', labels_add_subfolder2( 'Blabla' ), 'labels_add_subfolder2: one parameter Blabla => Blabla' ) ; + is( 'Blan blue', labels_add_subfolder2( 'Blan blue' ), 'labels_add_subfolder2: one parameter Blan blue => Blan blue' ) ; + is( '\Bla "Blan blan" Blabla', labels_add_subfolder2( '\Bla "Blan blan" Blabla' ), + 'labels_add_subfolder2: one parameter \Bla "Blan blan" Blabla => \Bla "Blan blan" Blabla' ) ; + + is( 'Subf/Bla', labels_add_subfolder2( 'Bla', 'Subf' ), 'labels_add_subfolder2: Bla Subf => "Subf/Bla"' ) ; + + + is( 'Subf/\Bla', labels_add_subfolder2( '\\\\Bla', 'Subf' ), 'labels_add_subfolder2: \Bla Subf => \Bla' ) ; + + is( 'Subf/Bla Subf/Kii', labels_add_subfolder2( 'Bla Kii', 'Subf' ), + 'labels_add_subfolder2: Bla Kii Subf => "Subf/Bla" "Subf/Kii"' ) ; + + is( 'Subf/Kii Subf/\Bla', labels_add_subfolder2( '\\\\Bla Kii', 'Subf' ), + 'labels_add_subfolder2: \Bla Kii Subf => \Bla Subf/Kii' ) ; + + is( '"Subf/Blan blan"', labels_add_subfolder2( '"Blan blan"', 'Subf' ), + 'labels_add_subfolder2: "Blan blan" Subf => "Subf/Blan blan"' ) ; + + is( '"Subf/Blan blan" Subf/Kii Subf/\Loo', labels_add_subfolder2( '\\\\Loo "Blan blan" Kii', 'Subf' ), + 'labels_add_subfolder2: \Loo "Blan blan" Kii + Subf => "Subf/Blan blan" Subf/Kii Subf/\Loo' ) ; + + # "\\Inbox" is special, add to subfolder INBOX also because Gmail will but ... + is( '"Subf/\\\\Inbox" Subf/INBOX', labels_add_subfolder2( '"\\\\Inbox"', 'Subf' ), + 'labels_add_subfolder2: "\\\\Inbox" Subf => "Subf/\\\\Inbox" Subf/INBOX' ) ; + + # but not with INBOX folder + is( '"Subf/\\\\Inbox"', labels_add_subfolder2( '"\\\\Inbox"', 'Subf', 'INBOX' ), + 'labels_add_subfolder2: "\\\\Inbox" Subf INBOX => "Subf/\\\\Inbox"' ) ; + + # two times => one time + is( '"Subf/\\\\Inbox" Subf/INBOX', labels_add_subfolder2( '"\\\\Inbox" "\\\\Inbox"', 'Subf' ), + 'labels_add_subfolder2: "\\\\Inbox" "\\\\Inbox" Subf => "Subf/\\\\Inbox"' ) ; + + is( '"Subf/\\\\Starred"', labels_add_subfolder2( '"\\\\Starred"', 'Subf' ), + 'labels_add_subfolder2: "\\\\Starred" Subf => "Subf/\\\\Starred"' ) ; + + note( 'Leaving tests_labels_add_subfolder2()' ) ; + return ; +} + +sub labels_add_subfolder2 +{ + my $labels = shift ; + my $subfolder2 = shift ; + my $h1_folder = shift || q{} ; + + if ( not defined $labels ) { return ; } + if ( not defined $subfolder2 ) { return $labels ; } + + # Isn't it messy? + if ( 'INBOX' eq $h1_folder ) + { + $labels .= ' "\\\\Inbox"' ; + } + + my @labels = uniq( quotewords('\s+', 1, $labels ) ) ; + myprint( "labels before subfolder2: @labels\n" ) ; + my @labels_subfolder2 ; + + + foreach my $label ( @labels ) + { + # Isn't it more messy? + if ( ( q{"\\\\Inbox"} eq $label ) and ( 'INBOX' ne $h1_folder ) ) + { + if ( $subfolder2 =~ m{ } ) + { + push @labels_subfolder2, qq{"$subfolder2/INBOX"} ; + } + else + { + push @labels_subfolder2, "$subfolder2/INBOX" ; + } + } + if ( $label =~ m{^\"\\\\} ) + { + # \Seen \Deleted ... stay the same + #push @labels_subfolder2, $label ; + # Remove surrounding quotes if any, to add them again + $label = join( '', quotewords('\s+', 0, $label ) ) ; + push @labels_subfolder2, qq{"$subfolder2/\\$label"} ; + + } + else + { + # Remove surrounding quotes if any, to add them again in case of space + $label = join( '', quotewords('\s+', 0, $label ) ) ; + if ( $label =~ m{ } ) + { + push @labels_subfolder2, qq{"$subfolder2/$label"} ; + } + else + { + push @labels_subfolder2, "$subfolder2/$label" ; + } + } + } + + my $labels_subfolder2 = join( ' ', sort @labels_subfolder2 ) ; + + return $labels_subfolder2 ; } sub tests_labels { note( 'Entering tests_labels()' ) ; - is( undef, labels( ), 'labels no parameters => undef' ) ; - is( undef, labels( undef ), 'labels undef => undef' ) ; + is( undef, labels( ), 'labels: no parameters => undef' ) ; + is( undef, labels( undef ), 'labels: undef => undef' ) ; require_ok( "Test::MockObject" ) ; my $myimap = Test::MockObject->new( ) ; @@ -7605,27 +8732,26 @@ sub tests_labels $myimap->mock( 'Debug' , sub { } ) ; $myimap->mock( 'Unescape', sub { return Mail::IMAPClient::Unescape( @_ ) } ) ; # real one - is( undef, labels( $myimap ), 'labels one parameter => undef' ) ; - is( '\Seen Blabla', labels( $myimap, '1' ), 'labels $mysync UID_1 => \Seen Blabla' ) ; + is( undef, labels( $myimap ), 'labels: one parameter => undef' ) ; + is( '\Seen Blabla', labels( $myimap, '1' ), 'labels: $mysync UID_1 => \Seen Blabla' ) ; note( 'Leaving tests_labels()' ) ; return ; } -sub synclabels +sub labels { - my( $mysync, $uid1, $uid2 ) = @ARG ; + my ( $myimap, $uid ) = @ARG ; - if ( not all_defined( $mysync, $uid1, $uid2 ) ) { + if ( not all_defined( $myimap, $uid ) ) { return ; } - my $myimap1 = $mysync->{ 'imap1' } || return ; - my $myimap2 = $mysync->{ 'imap2' } || return ; - my $labels1 = labels( $myimap1, $uid1 ) ; - if ( $labels1 ) { - return $myimap2->store( $uid2, "+X-GM-LABELS ($labels1)" ) ; - } + my $hash = $myimap->fetch_hash( [ $uid ], 'X-GM-LABELS' ) ; + + my $labels = $hash->{ $uid }->{ 'X-GM-LABELS' } ; + #$labels = $myimap->Unescape( $labels ) ; + return $labels ; } sub tests_synclabels @@ -7676,6 +8802,154 @@ sub tests_synclabels } +sub synclabels +{ + my( $mysync, $uid1, $uid2 ) = @ARG ; + + if ( not all_defined( $mysync, $uid1, $uid2 ) ) { + return ; + } + my $myimap1 = $mysync->{ 'imap1' } || return ; + my $myimap2 = $mysync->{ 'imap2' } || return ; + + $mysync->{debuglabels} and $myimap1->Debug( 1 ) ; + my $labels1 = labels( $myimap1, $uid1 ) ; + $mysync->{debuglabels} and $myimap1->Debug( 0 ) ; + $mysync->{debuglabels} and myprint( "Host1 labels: $labels1\n" ) ; + + + + if ( $mysync->{ subfolder1 } and $labels1 ) + { + $labels1 = labels_remove_subfolder1( $labels1, $mysync->{ subfolder1 } ) ; + $mysync->{debuglabels} and myprint( "Host1 labels with subfolder1: $labels1\n" ) ; + } + + if ( $mysync->{ subfolder2 } and $labels1 ) + { + $labels1 = labels_add_subfolder2( $labels1, $mysync->{ subfolder2 } ) ; + $mysync->{debuglabels} and myprint( "Host1 labels with subfolder2: $labels1\n" ) ; + } + + my $store ; + if ( $labels1 and not $mysync->{ dry } ) + { + $mysync->{ debuglabels } and $myimap2->Debug( 1 ) ; + $store = $myimap2->store( $uid2, "X-GM-LABELS ($labels1)" ) ; + $mysync->{ debuglabels } and $myimap2->Debug( 0 ) ; + } + return $store ; +} + + +sub tests_resynclabels +{ + note( 'Entering tests_resynclabels()' ) ; + + is( undef, resynclabels( ), 'resynclabels: no parameters => undef' ) ; + is( undef, resynclabels( undef ), 'resynclabels: undef => undef' ) ; + my $mysync ; + is( undef, resynclabels( $mysync ), 'resynclabels: var undef => undef' ) ; + + my ( $h1_fir_ref, $h2_fir_ref ) ; + + $mysync->{ debuglabels } = 1 ; + $h1_fir_ref->{ 11 }->{ 'X-GM-LABELS' } = '\Seen Baa Kii' ; + $h2_fir_ref->{ 22 }->{ 'X-GM-LABELS' } = '\Seen Baa Kii' ; + + # labels are equal + is( 1, resynclabels( $mysync, 11, 22, $h1_fir_ref, $h2_fir_ref ), + 'resynclabels: $mysync UID_1 UID_2 labels are equal => 1' ) ; + + # labels are different + $h2_fir_ref->{ 22 }->{ 'X-GM-LABELS' } = '\Seen Zuu' ; + require_ok( "Test::MockObject" ) ; + my $myimap2 = Test::MockObject->new( ) ; + $myimap2->mock( 'store', + sub { + return 1 ; + } + ) ; + $myimap2->mock( 'Debug', sub { } ) ; + $mysync->{imap2} = $myimap2 ; + + is( 1, resynclabels( $mysync, 11, 22, $h1_fir_ref, $h2_fir_ref ), + 'resynclabels: $mysync UID_1 UID_2 labels are not equal => store => 1' ) ; + + note( 'Leaving tests_resynclabels()' ) ; + return ; +} + + + +sub resynclabels +{ + my( $mysync, $uid1, $uid2, $h1_fir_ref, $h2_fir_ref, $h1_folder ) = @ARG ; + + if ( not all_defined( $mysync, $uid1, $uid2, $h1_fir_ref, $h2_fir_ref ) ) { + return ; + } + + my $labels1 = $h1_fir_ref->{ $uid1 }->{ 'X-GM-LABELS' } || q{} ; + my $labels2 = $h2_fir_ref->{ $uid2 }->{ 'X-GM-LABELS' } || q{} ; + + if ( $mysync->{ subfolder1 } and $labels1 ) + { + $labels1 = labels_remove_subfolder1( $labels1, $mysync->{ subfolder1 } ) ; + } + + if ( $mysync->{ subfolder2 } and $labels1 ) + { + $labels1 = labels_add_subfolder2( $labels1, $mysync->{ subfolder2 }, $h1_folder ) ; + $labels2 = labels_remove_special( $labels2 ) ; + } + $mysync->{ debuglabels } and myprint( "Host1 labels fixed: $labels1\n" ) ; + $mysync->{ debuglabels } and myprint( "Host2 labels : $labels2\n" ) ; + + my $store ; + if ( $labels1 eq $labels2 ) + { + # no sync needed + $mysync->{ debuglabels } and myprint( "Labels are already equal\n" ) ; + return 1 ; + } + elsif ( not $mysync->{ dry } ) + { + # sync needed + $mysync->{debuglabels} and $mysync->{imap2}->Debug( 1 ) ; + $store = $mysync->{imap2}->store( $uid2, "X-GM-LABELS ($labels1)" ) ; + $mysync->{debuglabels} and $mysync->{imap2}->Debug( 0 ) ; + } + + return $store ; +} + +sub tests_uniq +{ + note( 'Entering tests_uniq()' ) ; + + is( 0, uniq( ), 'uniq: undef => 0' ) ; + is_deeply( [ 'one' ], [ uniq( 'one' ) ], 'uniq: one => one' ) ; + is_deeply( [ 'one' ], [ uniq( 'one', 'one' ) ], 'uniq: one one => one' ) ; + is_deeply( [ 'one', 'two' ], [ uniq( 'one', 'one', 'two', 'one', 'two' ) ], 'uniq: one one two one two => one two' ) ; + note( 'Leaving tests_uniq()' ) ; + return ; +} + +sub uniq +{ + my @list = @ARG ; + my %seen = ( ) ; + my @uniq = ( ) ; + foreach my $item ( @list ) { + if ( ! $seen{ $item } ) { + $seen{ $item } = 1 ; + push( @uniq, $item ) ; + } + } + return @uniq ; +} + sub length_ref { @@ -7709,16 +8983,16 @@ sub date_for_host2 if ( $syncinternaldates ) { $h1_date = $h1_idate ; - $debug and myprint( "internal date from host1: [$h1_date]\n" ) ; + $sync->{ debug } and myprint( "internal date from host1: [$h1_date]\n" ) ; $h1_date = good_date( $h1_date ) ; - $debug and myprint( "internal date from host1: [$h1_date] (fixed)\n" ) ; + $sync->{ debug } and myprint( "internal date from host1: [$h1_date] (fixed)\n" ) ; } if ( $idatefromheader ) { $h1_date = $sync->{imap1}->get_header( $h1_msg, 'Date' ) ; - $debug and myprint( "header date from host1: [$h1_date]\n" ) ; + $sync->{ debug } and myprint( "header date from host1: [$h1_date]\n" ) ; $h1_date = good_date( $h1_date ) ; - $debug and myprint( "header date from host1: [$h1_date] (fixed)\n" ) ; + $sync->{ debug } and myprint( "header date from host1: [$h1_date] (fixed)\n" ) ; } return( $h1_date ) ; @@ -7817,7 +9091,7 @@ sub append_message_on_host2 my $new_id ; if ( ! $mysync->{dry} ) { - $max_msg_size_in_bytes = max( $h1_size, $max_msg_size_in_bytes ) ; + $max_msg_size_in_bytes = max( $string_len, $max_msg_size_in_bytes ) ; $new_id = $mysync->{imap2}->append_string( $h2_fold, ${ $string_ref }, $h1_flags, $h1_date ) ; myprint( debugmemory( $mysync, " at A2" ) ) ; if ( ! $new_id){ @@ -7837,7 +9111,7 @@ sub append_message_on_host2 } if ( $mysync->{ synclabels } ) { synclabels( $mysync, $h1_msg, $new_id ) } $h2_uidguess += 1 ; - $mysync->{total_bytes_transferred} += $h1_size ; + $mysync->{total_bytes_transferred} += $string_len ; $mysync->{nb_msg_transferred} += 1 ; $mysync->{ h1_nb_msg_processed } +=1 ; @@ -7860,7 +9134,7 @@ sub append_message_on_host2 delete_message_on_host1( $mysync, $h1_fold, $mysync->{ expungeaftereach }, $h1_msg ) ; } #myprint( "PRESS ENTER" ) and my $a = <> ; - + return( $new_id ) ; } } @@ -8069,9 +9343,10 @@ sub delete_messages_on_any if ( ! $mysync->{dry} && @messages_part ) { my $nb_deleted = $imap->delete_message( $imap->Range( @messages_part ) ) ; - if ( defined $nb_deleted ) + if ( defined $nb_deleted ) { - $mysync->{ h1_nb_msg_deleted } += $nb_deleted ; + # $nb_deleted is not accurate + $mysync->{ h1_nb_msg_deleted } += scalar @messages_part ; } else { @@ -8827,8 +10102,8 @@ sub cache_folder { my( $cache_base, $cache_dir, $h1_fold, $h2_fold ) = @_ ; - my $sep_1 = $h1_sep || '/'; - my $sep_2 = $h2_sep || '/'; + my $sep_1 = $sync->{ h1_sep } || '/'; + my $sep_2 = $sync->{ h2_sep } || '/'; #myprint( "$cache_dir h1_fold $h1_fold sep1 $sep_1 h2_fold $h2_fold sep2 $sep_2\n" ) ; $h1_fold = convert_sep_to_slash( $h1_fold, $sep_1 ) ; @@ -8839,21 +10114,6 @@ sub cache_folder return( $cache_folder ) ; } -sub filter_forbidden_characters -{ - my $string = shift ; - - if ( ! defined $string ) { return ; } - - if ( 'MSWin32' eq $OSNAME ) { - # Move trailing whitespace to _ " a b /c d " -> " a b_/c d_" - $string =~ s{\ (/|$)}{_$1}xg ; - } - $string =~ s{[\Q*|?:"<>\E\t\r\n\\]}{_}xg ; - #myprint( "[$string]\n" ) ; - return( $string ) ; -} - sub tests_filter_forbidden_characters { note( 'Entering tests_filter_forbidden_characters()' ) ; @@ -8884,12 +10144,19 @@ sub tests_filter_forbidden_characters return ; } -sub convert_sep_to_slash +sub filter_forbidden_characters { - my ( $folder, $sep ) = @_ ; + my $string = shift ; - $folder =~ s{\Q$sep\E}{/}xg ; - return( $folder ) ; + if ( ! defined $string ) { return ; } + + if ( 'MSWin32' eq $OSNAME ) { + # Move trailing whitespace to _ " a b /c d " -> " a b_/c d_" + $string =~ s{\ (/|$)}{_$1}xg ; + } + $string =~ s{[\Q*|?:"<>\E\t\r\n\\]}{_}xg ; + #myprint( "[$string]\n" ) ; + return( $string ) ; } sub tests_convert_sep_to_slash @@ -8909,6 +10176,14 @@ sub tests_convert_sep_to_slash return ; } +sub convert_sep_to_slash +{ + my ( $folder, $sep ) = @_ ; + + $folder =~ s{\Q$sep\E}{/}xg ; + return( $folder ) ; +} + sub tests_regexmess { @@ -9387,6 +10662,13 @@ EOM ), 'regexmess: 17 Delete header Disposition-Notification-To:'); + @regexmess = ( 's/.{11}\K.*//gs' ) ; + is( "0123456789\n", regexmess( "0123456789\n" x 100 ), 'regexmess, truncate whole message after 11 characters' ) ; + is( "0123456789\n", regexmess( "0123456789\n" x 100_000 ), 'regexmess, truncate whole message after 11 characters ~ 1MB' ) ; + + @regexmess = ( 's/.{10000}\K.*//gs' ) ; + is( "123456789\n" x 1000, regexmess( "123456789\n" x 100_000 ), 'regexmess, truncate whole message after 10000 characters ~ 1MB' ) ; + # regex to play with Date: from the FAQ @@ -9401,7 +10683,7 @@ sub regexmess { my ( $string ) = @_ ; foreach my $regexmess ( @regexmess ) { - $debug and myprint( "eval \$string =~ $regexmess\n" ) ; + $sync->{ debug } and myprint( "eval \$string =~ $regexmess\n" ) ; my $ret = eval "\$string =~ $regexmess ; 1" ; #myprint( "eval [$ret]\n" ) ; if ( ( not $ret ) or $EVAL_ERROR ) { @@ -9409,7 +10691,7 @@ sub regexmess return( undef ) ; } } - $debug and myprint( "$string\n" ) ; + $sync->{ debug } and myprint( "$string\n" ) ; return( $string ) ; } @@ -9642,10 +10924,10 @@ sub skipmess my $match ; #myprint( "$string\n" ) ; foreach my $skipmess ( @skipmess ) { - $debug and myprint( "eval \$match = \$string =~ $skipmess\n" ) ; + $sync->{ debug } and myprint( "eval \$match = \$string =~ $skipmess\n" ) ; my $ret = eval "\$match = \$string =~ $skipmess ; 1" ; #myprint( "eval [$ret]\n" ) ; - $debug and myprint( "match [$match]\n" ) ; + $sync->{ debug } and myprint( "match [$match]\n" ) ; if ( ( not $ret ) or $EVAL_ERROR ) { myprint( "Error: eval skipmess '$skipmess': $EVAL_ERROR" ) ; return( undef ) ; @@ -9797,7 +11079,7 @@ sub stats myprint( "Messages transferred : $mysync->{nb_msg_transferred} " ) ; myprint( "(could be $nb_msg_skipped_dry_mode without dry mode)" ) if ( $mysync->{dry} ) ; myprint( "\n" ) ; - myprint( "Messages skipped : $sync->{ nb_msg_skipped }\n" ) ; + myprint( "Messages skipped : $mysync->{ nb_msg_skipped }\n" ) ; myprint( "Messages found duplicate on host1 : $h1_nb_msg_duplicate\n" ) ; myprint( "Messages found duplicate on host2 : $h2_nb_msg_duplicate\n" ) ; myprint( "Messages found crossduplicate on host2 : $mysync->{ h2_nb_msg_crossdup }\n" ) ; @@ -9813,8 +11095,8 @@ sub stats $mysync->{total_bytes_transferred}, bytes_display_string( $mysync->{total_bytes_transferred} ) ) ; myprintf( "Total bytes skipped : %s (%s)\n", - $sync->{ total_bytes_skipped }, - bytes_display_string( $sync->{ total_bytes_skipped } ) ) ; + $mysync->{ total_bytes_skipped }, + bytes_display_string( $mysync->{ total_bytes_skipped } ) ) ; $timediff ||= 1 ; # No division per 0 myprintf("Message rate : %.1f messages/s\n", $mysync->{nb_msg_transferred} / $timediff ) ; myprintf("Average bandwidth rate : %.1f KiB/s\n", $mysync->{total_bytes_transferred} / $KIBI / $timediff ) ; @@ -9823,7 +11105,7 @@ sub stats myprintf("Memory consumption at the end : %.1f MiB (started with %.1f MiB)\n", $memory_consumption_at_end / $KIBI / $KIBI, $memory_consumption_at_start / $KIBI / $KIBI ) ; - myprint( "Load end is : " . ( join( q{ }, loadavg( ) ) || 'unknown' ), " on $sync->{cpu_number} cores\n" ) ; + myprint( "Load end is : " . ( join( q{ }, loadavg( ) ) || 'unknown' ), " on $mysync->{cpu_number} cores\n" ) ; myprintf("Biggest message : %s bytes (%s)\n", $max_msg_size_in_bytes, @@ -9846,7 +11128,7 @@ sub stats $bytes_end_diff, bytes_display_string( $bytes_end_diff ) ) ; } - + comment_on_final_diff_in_1_not_in_2( $mysync ) ; comment_on_final_diff_in_2_not_in_1( $mysync ) ; myprint( "Detected $mysync->{nb_errors} errors\n\n" ) ; @@ -9960,10 +11242,10 @@ sub parse_header_msg my $head = $s_heads->{$m_uid} ; my $headnum = scalar keys %{ $head } ; - $debug and myprint( "$side: uid $m_uid number of headers, pass one: ", $headnum, "\n" ) ; + $mysync->{ debug } and myprint( "$side: uid $m_uid number of headers, pass one: ", $headnum, "\n" ) ; if ( ( ! $headnum ) and ( $wholeheaderifneeded ) ){ - myprint( "$side: uid $m_uid no header by parse_headers so taking whole header with BODY.PEEK[HEADER]\n" ) ; + $mysync->{ debug } and myprint( "$side: uid $m_uid no header by parse_headers so taking whole header with BODY.PEEK[HEADER]\n" ) ; $imap->fetch($m_uid, 'BODY.PEEK[HEADER]' ) ; my $whole_header = $imap->_transaction_literals ; @@ -9971,7 +11253,7 @@ sub parse_header_msg $head = decompose_header( $whole_header ) ; $headnum = scalar keys %{ $head } ; - $debug and myprint( "$side: uid $m_uid number of headers, pass two: ", $headnum, "\n" ) ; + $mysync->{ debug } and myprint( "$side: uid $m_uid number of headers, pass two: ", $headnum, "\n" ) ; } #myprint( Data::Dumper->Dump( [ $head, \%useheader ] ) ) ; @@ -9982,7 +11264,7 @@ sub parse_header_msg if ( ( ! $headstr ) and ( $mysync->{addheader} ) and ( $side eq 'Host1' ) ) { my $header = add_header( $m_uid ) ; - myprint( "$side: uid $m_uid no header found so adding our own [$header]\n" ) ; + $mysync->{ debug } and myprint( "$side: uid $m_uid no header found so adding our own [$header]\n" ) ; $headstr .= uc $header ; $s_fir->{$m_uid}->{NO_HEADER} = 1; } @@ -9994,7 +11276,7 @@ sub parse_header_msg my $idate = $s_fir->{$m_uid}->{'INTERNALDATE'} ; $size = length $headstr unless ( $size ) ; my $m_md5 = md5_base64( $headstr ) ; - $debug and myprint( "$side: uid $m_uid sig $m_md5 size $size idate $idate\n" ) ; + $mysync->{ debug } and myprint( "$side: uid $m_uid sig $m_md5 size $size idate $idate\n" ) ; my $key ; if ($skipsize) { $key = "$m_md5"; @@ -10028,10 +11310,10 @@ sub header_construct my $H = header_line_normalize( $h, $val ) ; # show stuff in debug mode - $debug and myprint( "$side uid $m_uid header [$H]", "\n" ) ; + $sync->{ debug } and myprint( "$side uid $m_uid header [$H]", "\n" ) ; if ($skipheader and $H =~ m/$skipheader/xi) { - $debug and myprint( "$side uid $m_uid skipping header [$H]\n" ) ; + $sync->{ debug } and myprint( "$side uid $m_uid skipping header [$H]\n" ) ; next ; } $headstr .= "$H" ; @@ -10089,44 +11371,118 @@ sub tests_header_line_normalize } -sub firstline -{ - # extract the first line of a file (without \n) - - my( $file ) = @_ ; - my $line = q{} ; - - if ( ! -e $file ) { - myprint( "Cannot open file $file since it does not exist\n" ) ; - return ; - } - - open my $FILE, '<', $file or do { - myprint( "Error opening file $file : $OS_ERROR\n" ) ; - return ; - } ; - $line = <$FILE> || q{} ; - close $FILE ; - chomp $line ; - return $line ; -} - sub tests_firstline { - note( 'Entering tests_firstline()' ) ; + note( 'Entering tests_firstline()' ) ; - is( undef , firstline( 'W/tmp/tests/noexist.txt' ), 'tests_firstline: not getting blabla from W/tmp/tests/noexist.txt' ) ; - is( "blabla\n" , string_to_file( "blabla\n", 'W/tmp/tests/firstline.txt' ), 'tests_firstline: put blabla in W/tmp/tests/firstline.txt' ) ; - is( 'blabla' , firstline( 'W/tmp/tests/firstline.txt' ), 'tests_firstline: get blabla from W/tmp/tests/firstline.txt' ) ; - is( q{} , string_to_file( q{}, 'W/tmp/tests/firstline2.txt' ), 'tests_firstline: put empty string in W/tmp/tests/firstline2.txt' ) ; - is( q{} , firstline( 'W/tmp/tests/firstline2.txt' ), 'tests_firstline: get empty string from W/tmp/tests/firstline2.txt' ) ; - is( "\n" , string_to_file( "\n", 'W/tmp/tests/firstline3.txt' ), 'tests_firstline: put CR in W/tmp/tests/firstline3.txt' ) ; - is( q{} , firstline( 'W/tmp/tests/firstline3.txt' ), 'tests_firstline: get empty string from W/tmp/tests/firstline3.txt' ) ; + is( q{}, firstline( 'W/tmp/tests/noexist.txt' ), 'firstline: getting empty string from inexisting W/tmp/tests/noexist.txt' ) ; - note( 'Leaving tests_firstline()' ) ; + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'firstline: mkpath W/tmp/tests/' ) ; + + is( "blabla\n" , string_to_file( "blabla\n", 'W/tmp/tests/firstline.txt' ), 'firstline: put blabla in W/tmp/tests/firstline.txt' ) ; + is( 'blabla' , firstline( 'W/tmp/tests/firstline.txt' ), 'firstline: get blabla from W/tmp/tests/firstline.txt' ) ; + + is( q{} , string_to_file( q{}, 'W/tmp/tests/firstline2.txt' ), 'firstline: put empty string in W/tmp/tests/firstline2.txt' ) ; + is( q{} , firstline( 'W/tmp/tests/firstline2.txt' ), 'firstline: get empty string from W/tmp/tests/firstline2.txt' ) ; + + is( "\n" , string_to_file( "\n", 'W/tmp/tests/firstline3.txt' ), 'firstline: put CR in W/tmp/tests/firstline3.txt' ) ; + is( q{} , firstline( 'W/tmp/tests/firstline3.txt' ), 'firstline: get empty string from W/tmp/tests/firstline3.txt' ) ; + + is( "blabla\nTiti\n" , string_to_file( "blabla\nTiti\n", 'W/tmp/tests/firstline4.txt' ), 'firstline: put blabla\nTiti\n in W/tmp/tests/firstline4.txt' ) ; + is( 'blabla' , firstline( 'W/tmp/tests/firstline4.txt' ), 'firstline: get blabla from W/tmp/tests/firstline4.txt' ) ; + + note( 'Leaving tests_firstline()' ) ; return ; } +sub firstline +{ + # extract the first line of a file (without \n) + # return empty string if error or empty string + + my $file = shift ; + my $line ; + + $line = nthline( $file, 1 ) ; + return $line ; +} + + + +sub tests_secondline +{ + note( 'Entering tests_secondline()' ) ; + + is( q{}, secondline( 'W/tmp/tests/noexist.txt' ), 'secondline: getting empty string from inexisting W/tmp/tests/noexist.txt' ) ; + is( q{}, secondline( 'W/tmp/tests/noexist.txt', 2 ), 'secondline: 2nd getting empty string from inexisting W/tmp/tests/noexist.txt' ) ; + + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'secondline: mkpath W/tmp/tests/' ) ; + + is( "L1\nL2\nL3\nL4\n" , string_to_file( "L1\nL2\nL3\nL4\n", 'W/tmp/tests/secondline.txt' ), 'secondline: put L1\nL2\nL3\nL4\n in W/tmp/tests/secondline.txt' ) ; + is( 'L2' , secondline( 'W/tmp/tests/secondline.txt' ), 'secondline: get L2 from W/tmp/tests/secondline.txt' ) ; + + + note( 'Leaving tests_secondline()' ) ; + return ; +} + + +sub secondline +{ + # extract the second line of a file (without \n) + # return empty string if error or empty string + + my $file = shift ; + my $line ; + + $line = nthline( $file, 2 ) ; + return $line ; +} + + + + +sub tests_nthline +{ + note( 'Entering tests_nthline()' ) ; + + is( q{}, nthline( 'W/tmp/tests/noexist.txt' ), 'nthline: getting empty string from inexisting W/tmp/tests/noexist.txt' ) ; + is( q{}, nthline( 'W/tmp/tests/noexist.txt', 2 ), 'nthline: 2nd getting empty string from inexisting W/tmp/tests/noexist.txt' ) ; + + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'nthline: mkpath W/tmp/tests/' ) ; + + is( "L1\nL2\nL3\nL4\n" , string_to_file( "L1\nL2\nL3\nL4\n", 'W/tmp/tests/nthline.txt' ), 'nthline: put L1\nL2\nL3\nL4\n in W/tmp/tests/nthline.txt' ) ; + is( 'L3' , nthline( 'W/tmp/tests/nthline.txt', 3 ), 'nthline: get L3 from W/tmp/tests/nthline.txt' ) ; + + + note( 'Leaving tests_nthline()' ) ; + return ; +} + + +sub nthline +{ + # extract the nth line of a file (without \n) + # return empty string if error or empty string + + my $file = shift ; + my $num = shift ; + + if ( ! all_defined( $file, $num ) ) { return q{} ; } + + my $line ; + + $line = ( file_to_array( $file ) )[$num - 1] ; + if ( ! defined $line ) + { + return q{} ; + } + else + { + chomp $line ; + return $line ; + } +} # Should be unit tested and then be used by file_to_string, refactoring file_to_string @@ -10137,7 +11493,7 @@ sub file_to_array my @string ; open my $FILE, '<', $file or do { - myprint( "Error reading file $file : $OS_ERROR" ) ; + myprint( "Error reading file $file : $OS_ERROR\n" ) ; return ; } ; @string = <$FILE> ; @@ -10155,7 +11511,7 @@ sub tests_file_to_string is( undef, file_to_string( '/' ), 'file_to_string: reading a directory => undef' ) ; ok( file_to_string( $PROGRAM_NAME ), 'file_to_string: reading myself' ) ; - ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'file_to_string: mkpath W/tmp/tests/' ) ; + ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'file_to_string: mkpath W/tmp/tests/' ) ; is( 'lilili', string_to_file( 'lilili', 'W/tmp/tests/canbewritten' ), 'file_to_string: string_to_file filling W/tmp/tests/canbewritten with lilili' ) ; is( 'lilili', file_to_string( 'W/tmp/tests/canbewritten' ), 'file_to_string: reading W/tmp/tests/canbewritten is lilili' ) ; @@ -10497,7 +11853,7 @@ sub check_last_release { my $fake = shift ; my $public_release = not_long_imapsync_version_public( $fake ) ; - $debug and myprint( "check_last_release: [$public_release]\n" ) ; + $sync->{ debug } and myprint( "check_last_release: [$public_release]\n" ) ; my $inline_help_when_on = '( Use --noreleasecheck to avoid this release check. )' ; if ( $public_release eq 'unknown' ) { @@ -10643,17 +11999,17 @@ sub cpu_number if ( $ENV{"NUMBER_OF_PROCESSORS"} ) { # might be under a Windows system $cpu_number = $ENV{"NUMBER_OF_PROCESSORS"} ; - $debug and myprint( "Number of processors found by env var NUMBER_OF_PROCESSORS: $cpu_number\n" ) ; + $sync->{ debug } and myprint( "Number of processors found by env var NUMBER_OF_PROCESSORS: $cpu_number\n" ) ; }elsif ( 'darwin' eq $OSNAME or 'freebsd' eq $OSNAME ) { $cpu_number = backtick( "sysctl -n hw.ncpu" ) ; chomp( $cpu_number ) ; - $debug and myprint( "Number of processors found by cmd 'sysctl -n hw.ncpu': $cpu_number\n" ) ; + $sync->{ debug } and myprint( "Number of processors found by cmd 'sysctl -n hw.ncpu': $cpu_number\n" ) ; }elsif ( ! -e '/proc/cpuinfo' ) { - $debug and myprint( "Number of processors not found so I might assume there is only 1\n" ) ; + $sync->{ debug } and myprint( "Number of processors not found so I might assume there is only 1\n" ) ; $cpu_number = 1 ; }elsif( @cpuinfo = file_to_array( '/proc/cpuinfo' ) ) { $cpu_number = grep { /^processor/mxs } @cpuinfo ; - $debug and myprint( "Number of processors found via /proc/cpuinfo: $cpu_number\n" ) ; + $sync->{ debug } and myprint( "Number of processors found via /proc/cpuinfo: $cpu_number\n" ) ; } if ( defined $cpu_number_forced ) { @@ -10780,7 +12136,7 @@ sub loadavg_linux my ( $avg_1_min, $avg_5_min, $avg_15_min, $current_runs ) = split /\s/mxs, $line ; if ( all_defined( $avg_1_min, $avg_5_min, $avg_15_min ) ) { - $debug and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min $current_runs\n" ) ; + $sync->{ debug } and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min $current_runs\n" ) ; return ( $avg_1_min, $avg_5_min, $avg_15_min, $current_runs ) ; } return ; @@ -10805,7 +12161,7 @@ sub loadavg_freebsd my ( $avg_1_min, $avg_5_min, $avg_15_min ) = $loadavg =~ /vm\.loadavg\s*[:=]\s*\{?\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s+(\d+\.?\d*)/mxs ; - $debug and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min\n" ) ; + $sync->{ debug } and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min\n" ) ; return ( $avg_1_min, $avg_5_min, $avg_15_min ) ; } @@ -10828,7 +12184,7 @@ sub loadavg_darwin my ( $avg_1_min, $avg_5_min, $avg_15_min ) = $loadavg =~ /vm\.loadavg\s*[:=]\s*\{?\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s+(\d+\.?\d*)/mxs ; - $debug and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min\n" ) ; + $sync->{ debug } and myprint( "System load: $avg_1_min $avg_5_min $avg_15_min\n" ) ; return ( $avg_1_min, $avg_5_min, $avg_15_min ) ; } @@ -10855,7 +12211,7 @@ sub loadavg_windows my $num = $1 ; $num /= 100 ; - $debug and myprint( "System load: $num\n" ) ; + $sync->{ debug } and myprint( "System load: $num\n" ) ; return ( $num ) ; } @@ -10872,27 +12228,50 @@ sub tests_load_and_delay is( undef, load_and_delay( 1 ), 'load_and_delay: not 4 args => undef ' ) ; is( undef, load_and_delay( 0, 1, 1, 1 ), 'load_and_delay: division per 0 => undef ' ) ; is( 0, load_and_delay( 1, 1, 1, 1 ), 'load_and_delay: one core, loads are all 1 => ok ' ) ; + is( 0, load_and_delay( 1, 1, 1, 1, 'lalala' ), 'load_and_delay: five arguments is ok' ) ; is( 0, load_and_delay( 2, 2, 2, 2 ), 'load_and_delay: two core, loads are all 2 => ok ' ) ; is( 0, load_and_delay( 2, 2, 4, 5 ), 'load_and_delay: two core, load1m is 2 => ok ' ) ; - is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ; - is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ; - is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ; - is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ; - is( 1, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 1 ' ) ; - is( 1, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 1 ' ) ; - is( 5, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 5 ' ) ; - is( 15, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 15 ' ) ; +# Old behavior, rather strict + # is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ; + # is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ; + # is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ; + # is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ; + # is( 1, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 1 ' ) ; + # is( 1, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 1 ' ) ; + # is( 5, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 5 ' ) ; + # is( 15, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 15 ' ) ; - is( 0, load_and_delay( 4, 0, 2, 2 ), 'load_and_delay: four core, load1m=0 load5m=2 load15m=2 => 0 ' ) ; - is( 1, load_and_delay( 4, 8, 0, 0 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=0 => 1 ' ) ; - is( 1, load_and_delay( 4, 8, 0, 2 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=2 => 1 ' ) ; - is( 5, load_and_delay( 4, 8, 8, 0 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=0 => 5 ' ) ; - is( 15, load_and_delay( 4, 8, 8, 8 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=2 => 15 ' ) ; - is( 15, load_and_delay( 4, 8, 8, 8, 'lalala' ), 'load_and_delay: five arguments is ok' ) ; + # is( 0, load_and_delay( 4, 0, 2, 2 ), 'load_and_delay: four core, load1m=0 load5m=2 load15m=2 => 0 ' ) ; + # is( 1, load_and_delay( 4, 8, 0, 0 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=0 => 1 ' ) ; + # is( 1, load_and_delay( 4, 8, 0, 2 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=2 => 1 ' ) ; + # is( 5, load_and_delay( 4, 8, 8, 0 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=0 => 5 ' ) ; + # is( 15, load_and_delay( 4, 8, 8, 8 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=2 => 15 ' ) ; - note( 'Leaving tests_load_and_delay()' ) ; - return ; +# New behavior, tolerate more load + + is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ; + is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ; + is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ; + is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ; + is( 0, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 1 ' ) ; + is( 0, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 1 ' ) ; + is( 0, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 5 ' ) ; + is( 0, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 15 ' ) ; + + is( 1, load_and_delay( 1, 4, 0, 0 ), 'load_and_delay: one core, load1m=4 load5m=0 load15m=0 => 1 ' ) ; + is( 1, load_and_delay( 1, 4, 0, 4 ), 'load_and_delay: one core, load1m=4 load5m=0 load15m=4 => 1 ' ) ; + is( 5, load_and_delay( 1, 4, 4, 0 ), 'load_and_delay: one core, load1m=4 load5m=4 load15m=0 => 5 ' ) ; + is( 15, load_and_delay( 1, 4, 4, 4 ), 'load_and_delay: one core, load1m=4 load5m=4 load15m=4 => 15 ' ) ; + + is( 0, load_and_delay( 4, 0, 9, 9 ), 'load_and_delay: four core, load1m=0 load5m=9 load15m=9 => 0 ' ) ; + is( 1, load_and_delay( 4, 9, 0, 0 ), 'load_and_delay: four core, load1m=9 load5m=0 load15m=0 => 1 ' ) ; + is( 1, load_and_delay( 4, 9, 0, 9 ), 'load_and_delay: four core, load1m=9 load5m=0 load15m=9 => 1 ' ) ; + is( 5, load_and_delay( 4, 9, 9, 0 ), 'load_and_delay: four core, load1m=9 load5m=9 load15m=0 => 5 ' ) ; + is( 15, load_and_delay( 4, 9, 9, 9 ), 'load_and_delay: four core, load1m=9 load5m=9 load15m=9 => 15 ' ) ; + + note( 'Leaving tests_load_and_delay()' ) ; + return ; } sub load_and_delay @@ -10909,9 +12288,9 @@ sub load_and_delay # Let divide by number of cores ( $avg_1_min, $avg_5_min, $avg_15_min ) = map { $_ / $cpu_num } ( $avg_1_min, $avg_5_min, $avg_15_min ) ; # One of avg ok => ok, for now it is a OR - if ( $avg_1_min <= 1 ) { return 0 ; } - if ( $avg_5_min <= 1 ) { return 1 ; } # Retry in 1 minute - if ( $avg_15_min <= 1 ) { return 5 ; } # Retry in 5 minutes + if ( $avg_1_min <= 2 ) { return 0 ; } + if ( $avg_5_min <= 2 ) { return 1 ; } # Retry in 1 minute + if ( $avg_15_min <= 2 ) { return 5 ; } # Retry in 5 minutes return 15 ; # Retry in 15 minutes } @@ -10991,7 +12370,7 @@ sub memory_consumption_of_pids my @pid = @_; @pid = ( @pid ) ? @pid : ( $PROCESS_ID ) ; - $debug and myprint( "memory_consumption_of_pids PIDs: @pid\n" ) ; + $sync->{ debug } and myprint( "memory_consumption_of_pids PIDs: @pid\n" ) ; my @val ; if ( ( 'MSWin32' eq $OSNAME ) or ( 'cygwin' eq $OSNAME ) ) { @val = memory_consumption_of_pids_win32( @pid ) ; @@ -11011,7 +12390,7 @@ sub memory_consumption_of_pids @val = map { $_ * $KIBI } @ps ; } - $debug and myprint "@val\n" ; + $sync->{ debug } and myprint( "@val\n" ) ; return( @val ) ; } @@ -11061,11 +12440,11 @@ sub tests_backtick @output = backtick( 'echo Hello World!' ) ; # Add \r on Windows. ok( "Hello World!\r\n" eq $output[0], 'backtick: echo Hello World!' ) ; - $debug and myprint( "[@output]" ) ; + $sync->{ debug } and myprint( "[@output]" ) ; @output = backtick( 'echo Hello & echo World!' ) ; ok( "Hello \r\n" eq $output[0], 'backtick: echo Hello & echo World! line 1' ) ; ok( "World!\r\n" eq $output[1], 'backtick: echo Hello & echo World! line 2' ) ; - $debug and myprint( "[@output][$output[0]][$output[1]]" ) ; + $sync->{ debug } and myprint( "[@output][$output[0]][$output[1]]" ) ; # Scalar context ok( "Hello World!\r\n" eq backtick( 'echo Hello World!' ), 'backtick: echo Hello World! scalar' ) ; @@ -11079,11 +12458,11 @@ sub tests_backtick my @output ; @output = backtick( 'echo Hello World!' ) ; ok( "Hello World!\n" eq $output[0], 'backtick: echo Hello World!' ) ; - $debug and myprint( "[@output]" ) ; + $sync->{ debug } and myprint( "[@output]" ) ; @output = backtick( "echo Hello\necho World!" ) ; ok( "Hello\n" eq $output[0], 'backtick: echo Hello; echo World! line 1' ) ; ok( "World!\n" eq $output[1], 'backtick: echo Hello; echo World! line 2' ) ; - $debug and myprint( "[@output]" ) ; + $sync->{ debug } and myprint( "[@output]" ) ; # Scalar context ok( "Hello World!\n" eq backtick( 'echo Hello World!' ), 'backtick: echo Hello World! scalar' ) ; @@ -11092,7 +12471,7 @@ sub tests_backtick # Return error positive value, that's ok is( undef, backtick( 'false' ), 'backtick: false returns no output' ) ; my $mem = backtick( "ps -o vsz -p $PROCESS_ID" ) ; - $debug and myprint "MEM=$mem\n" ; + $sync->{ debug } and myprint( "MEM=$mem\n" ) ; } @@ -11135,6 +12514,89 @@ sub backtick } + +sub tests_check_binary_embed_all_dyn_libs +{ + note( 'Entering tests_check_binary_embed_all_dyn_libs()' ) ; + + is( 1, check_binary_embed_all_dyn_libs( ), 'check_binary_embed_all_dyn_libs: no args => 1' ) ; + + note( 'Leaving tests_check_binary_embed_all_dyn_libs()' ) ; + + return ; +} + + +sub check_binary_embed_all_dyn_libs +{ + my @search_dyn_lib_locale = search_dyn_lib_locale( ) ; + + if ( @search_dyn_lib_locale ) + { + myprint( "Found myself $PROGRAM_NAME pid $PROCESS_ID using locale dynamic libraries that seems out of myself:\n" ) ; + myprint( @search_dyn_lib_locale ) ; + if ( $PROGRAM_NAME =~ m{imapsync_bin_Darwin} ) + { + return 0 ; + } + elsif ( $PROGRAM_NAME =~ m{imapsync.*\.exe} ) + { + return 0 ; + } + else + { + # is always ok for non binary + return 1 ; + } + } + else + { + # Found only embedded dynamic lib + myprint( "Found nothing\n" ) ; + return 1 ; + } +} + +sub search_dyn_lib_locale +{ + if ( 'darwin' eq $OSNAME ) + { + return search_dyn_lib_locale_darwin( ) ; + } + if ( 'linux' eq $OSNAME ) + { + return search_dyn_lib_locale_linux( ) ; + } + if ( 'MSWin32' eq $OSNAME ) + { + return search_dyn_lib_locale_MSWin32( ) ; + } +} + +sub search_dyn_lib_locale_darwin +{ + my $command = qq{ lsof -p $PID | grep ' REG ' | grep .dylib | grep -v '/par-' } ; + myprint( "Search non embeded dynamic libs with the command: $command\n" ) ; + return backtick( $command ) ; +} + +sub search_dyn_lib_locale_linux +{ + my $command = qq{ lsof -p $PID | grep ' REG ' | grep -v '/tmp/par-' | grep '\.so' } ; + myprint( "Search non embeded dynamic libs with the command: $command\n" ) ; + return backtick( $command ) ; +} + +sub search_dyn_lib_locale_MSWin32 +{ + my $command = qq{ Listdlls.exe $PID|findstr Strawberry } ; + # $command = qq{ Listdlls.exe $PID|findstr Strawberry } ; + myprint( "Search non embeded dynamic libs with the command: $command\n" ) ; + return qx( $command ) ; +} + + + sub remove_not_num { @@ -11435,21 +12897,21 @@ sub tests_nb_messages_in_2_not_in_1 { note( 'Entering tests_stats_across_folders()' ) ; is( undef, nb_messages_in_2_not_in_1( ), 'nb_messages_in_2_not_in_1: no args => undef' ) ; - + my $mysync->{ h1_folders_of_md5 }->{ 'some_id_01' }->{ 'some_folder_01' } = 1 ; is( 0, nb_messages_in_2_not_in_1( $mysync ), 'nb_messages_in_2_not_in_1: no messages in 2 => 0' ) ; - + $mysync->{ h1_folders_of_md5 }->{ 'some_id_in_1_and_2' }->{ 'some_folder_01' } = 2 ; $mysync->{ h2_folders_of_md5 }->{ 'some_id_in_1_and_2' }->{ 'some_folder_02' } = 4 ; - + is( 0, nb_messages_in_2_not_in_1( $mysync ), 'nb_messages_in_2_not_in_1: a common message => 0' ) ; - + $mysync->{ h2_folders_of_md5 }->{ 'some_id_in_2_not_in_1' }->{ 'some_folder_02' } = 1 ; is( 1, nb_messages_in_2_not_in_1( $mysync ), 'nb_messages_in_2_not_in_1: one message in_2_not_in_1 => 1' ) ; - + $mysync->{ h2_folders_of_md5 }->{ 'some_other_id_in_2_not_in_1' }->{ 'some_folder_02' } = 3 ; is( 2, nb_messages_in_2_not_in_1( $mysync ), 'nb_messages_in_2_not_in_1: two messages in_2_not_in_1 => 2' ) ; - + note( 'Leaving tests_stats_across_folders()' ) ; return ; } @@ -11458,12 +12920,12 @@ sub nb_messages_in_2_not_in_1 { my $mysync = shift ; if ( not defined $mysync ) { return ; } - - $mysync->{ nb_messages_in_2_not_in_1 } = scalar( - list_keys_in_2_not_in_1( + + $mysync->{ nb_messages_in_2_not_in_1 } = scalar( + list_keys_in_2_not_in_1( $mysync->{ h1_folders_of_md5 }, $mysync->{ h2_folders_of_md5 } ) ) ; - + return $mysync->{ nb_messages_in_2_not_in_1 } ; } @@ -11472,12 +12934,12 @@ sub nb_messages_in_1_not_in_2 { my $mysync = shift ; if ( not defined $mysync ) { return ; } - - $mysync->{ nb_messages_in_1_not_in_2 } = scalar( - list_keys_in_2_not_in_1( + + $mysync->{ nb_messages_in_1_not_in_2 } = scalar( + list_keys_in_2_not_in_1( $mysync->{ h2_folders_of_md5 }, $mysync->{ h1_folders_of_md5 } ) ) ; - + return $mysync->{ nb_messages_in_1_not_in_2 } ; } @@ -11486,35 +12948,38 @@ sub nb_messages_in_1_not_in_2 sub comment_on_final_diff_in_1_not_in_2 { my $mysync = shift ; - - if ( not defined $mysync + + if ( not defined $mysync or $mysync->{ justfolders } or $mysync->{ useuid } ) - { - return ; + { + return ; } - - $debug and myprint( "nb_keys h1_folders_of_md5 " . scalar( keys %{ $mysync->{ h1_folders_of_md5 } } ) . "\n" ) ; - $debug and myprint( "nb_keys h2_folders_of_md5 " . scalar( keys %{ $mysync->{ h2_folders_of_md5 } } ) . "\n" ) ; - if ( $mysync->{ justfolders } ) { return ; } - + my $nb_identified_h1_messages = scalar( keys %{ $mysync->{ h1_folders_of_md5 } } ) ; + my $nb_identified_h2_messages = scalar( keys %{ $mysync->{ h2_folders_of_md5 } } ) ; + $mysync->{ debug } and myprint( "nb_keys h1_folders_of_md5 $nb_identified_h1_messages\n" ) ; + $mysync->{ debug } and myprint( "nb_keys h2_folders_of_md5 $nb_identified_h2_messages\n" ) ; + + if ( 0 == $nb_identified_h1_messages ) { return ; } + # Calculate if not yet done if ( not defined $mysync->{ nb_messages_in_1_not_in_2 } ) { nb_messages_in_1_not_in_2( $mysync ) ; } - + + if ( 0 == $mysync->{ nb_messages_in_1_not_in_2 } ) { - myprint( "The sync is good, all identified messages in host1 are on host2.\n" ) ; + myprint( "The sync looks good, all $nb_identified_h1_messages identified messages in host1 are on host2.\n" ) ; } else { myprint( "The sync is not finished, there are $mysync->{ nb_messages_in_1_not_in_2 } identified messages in host1 that are not on host2.\n" ) ; } - + if ( 1 <= $mysync->{ h1_nb_msg_noheader } ) { myprint( "There are $mysync->{ h1_nb_msg_noheader } unidentified messages (usually Sent or Draft messages). To sync them add option --addheader\n" ) ; @@ -11526,29 +12991,30 @@ sub comment_on_final_diff_in_1_not_in_2 sub comment_on_final_diff_in_2_not_in_1 { my $mysync = shift ; - - if ( not defined $mysync + + if ( not defined $mysync or $mysync->{ justfolders } or $mysync->{ useuid } ) - { - return ; + { + return ; } - + + my $nb_identified_h2_messages = scalar( keys %{ $mysync->{ h2_folders_of_md5 } } ) ; # Calculate if not yet done if ( not defined $mysync->{ nb_messages_in_2_not_in_1 } ) { nb_messages_in_2_not_in_1( $mysync ) ; } - + if ( 0 == $mysync->{ nb_messages_in_2_not_in_1 } ) { - myprint( "The sync is strict, all messages in host2 are on host1.\n" ) ; + myprint( "The sync is strict, all $nb_identified_h2_messages identified messages in host2 are on host1.\n" ) ; } else { - myprint( "The sync is not strict, there are ", - $mysync->{ nb_messages_in_2_not_in_1 }, + myprint( "The sync is not strict, there are ", + $mysync->{ nb_messages_in_2_not_in_1 }, " messages in host2 that are not on host1.", " Use --delete2 to delete them and have a strict sync.\n" ) ; } @@ -11558,33 +13024,36 @@ sub comment_on_final_diff_in_2_not_in_1 sub tests_match { - note( 'Entering tests_match()' ) ; + note( 'Entering tests_match()' ) ; - # undef serie - is( undef, match( ), 'match: no args => undef' ) ; - is( undef, match( 'lalala' ), 'match: one args => undef' ) ; + # undef serie + is( undef, match( ), 'match: no args => undef' ) ; + is( undef, match( 'lalala' ), 'match: one args => undef' ) ; - # This one gives 0 under a binary made by pp - # but 1 under "normal" Perl interpreter. So a PAR bug? - #is( 1, match( q{}, q{} ), 'match: q{} =~ q{} => 1' ) ; + # This one gives 0 under a binary made by pp + # but 1 under "normal" Perl interpreter. So a PAR bug? + #is( 1, match( q{}, q{} ), 'match: q{} =~ q{} => 1' ) ; - is( 1, match( 'lalala', 'lalala' ), 'match: lalala =~ lalala => 1' ) ; - is( 1, match( 'lalala', '^lalala' ), 'match: lalala =~ ^lalala => 1' ) ; - is( 1, match( 'lalala', 'lalala$' ), 'match: lalala =~ lalala$ => 1' ) ; - is( 1, match( 'lalala', '^lalala$' ), 'match: lalala =~ ^lalala$ => 1' ) ; - is( 1, match( '_lalala_', 'lalala' ), 'match: _lalala_ =~ lalala => 1' ) ; - is( 1, match( 'lalala', '.*' ), 'match: lalala =~ .* => 1' ) ; - is( 1, match( 'lalala', '.' ), 'match: lalala =~ . => 1' ) ; - is( 1, match( '/lalala/', '/lalala/' ), 'match: /lalala/ =~ /lalala/ => 1' ) ; + is( 'lalala', match( 'lalala', 'lalala' ), 'match: lalala =~ lalala => lalala' ) ; + is( 'lalala', match( 'lalala', '^lalala' ), 'match: lalala =~ ^lalala => lalala' ) ; + is( 'lalala', match( 'lalala', 'lalala$' ), 'match: lalala =~ lalala$ => lalala' ) ; + is( 'lalala', match( 'lalala', '^lalala$' ), 'match: lalala =~ ^lalala$ => lalala' ) ; + is( '_lalala_', match( '_lalala_', 'lalala' ), 'match: _lalala_ =~ lalala => _lalala_' ) ; + is( 'lalala', match( 'lalala', '.*' ), 'match: lalala =~ .* => lalala' ) ; + is( 'lalala', match( 'lalala', '.' ), 'match: lalala =~ . => lalala' ) ; + is( '/lalala/', match( '/lalala/', '/lalala/' ), 'match: /lalala/ =~ /lalala/ => /lalala/' ) ; + + is( 0, match( 'foo', 's/foo/bar/g' ), 'match: foo =~ s/foo/bar/g => 0' ) ; + is( 's/foo/bar/g', match( 's/foo/bar/g', 's/foo/bar/g' ), 'match: s/foo/bar/g =~ s/foo/bar/g => s/foo/bar/g' ) ; - is( 0, match( 'lalala', 'ooo' ), 'match: lalala =~ ooo => 0' ) ; - is( 0, match( 'lalala', 'lal_ala' ), 'match: lalala =~ lal_ala => 0' ) ; - is( 0, match( 'lalala', '\.' ), 'match: lalala =~ \. => 0' ) ; - is( 0, match( 'lalalaX', '^lalala$' ), 'match: lalalaX =~ ^lalala$ => 0' ) ; - is( 0, match( 'lalala', '/lalala/' ), 'match: lalala =~ /lalala/ => 1' ) ; + is( 0, match( 'lalala', 'ooo' ), 'match: lalala =~ ooo => 0' ) ; + is( 0, match( 'lalala', 'lal_ala' ), 'match: lalala =~ lal_ala => 0' ) ; + is( 0, match( 'lalala', '\.' ), 'match: lalala =~ \. => 0' ) ; + is( 0, match( 'lalalaX', '^lalala$' ), 'match: lalalaX =~ ^lalala$ => 0' ) ; + is( 0, match( 'lalala', '/lalala/' ), 'match: lalala =~ /lalala/ => 0' ) ; - is( 1, match( 'LALALA', '(?i:lalala)' ), 'match: LALALA =~ (?i:lalala) => 1' ) ; + is( 'LALALA', match( 'LALALA', '(?i:lalala)' ), 'match: LALALA =~ (?i:lalala) => 1' ) ; is( undef, match( 'LALALA', '(?{`ls /`})' ), 'match: LALALA =~ (?{`ls /`}) => undef' ) ; is( undef, match( 'LALALA', '(?{print "CACA"})' ), 'match: LALALA =~ (?{print "CACA"}) => undef' ) ; @@ -11603,8 +13072,8 @@ sub match if ( ( ! defined $var ) or ( ! defined $regex ) ) { return ; } # normal cases - if ( eval { $var =~ $regex } ) { - return 1 ; + if ( eval { $var =~ qr{$regex} } ) { + return $var ; }elsif ( $EVAL_ERROR ) { myprint( "Fatal regex $regex\n" ) ; return ; @@ -11734,7 +13203,7 @@ sub delete1emptyfolders $folders_kept{ $folder }++ ; next ; } - my $nb_messages_select = examine_folder_and_count( $imap, $folder, 'Host1' ) ; + my $nb_messages_select = examine_folder_and_count( $mysync, $imap, $folder, 'Host1' ) ; if ( ! defined $nb_messages_select ) { next ; } # Select failed => Neither continue nor keep this folder } my $nb_messages_search = scalar( @{ $imap->messages( ) } ) ; if ( 0 != $nb_messages_select and 0 != $nb_messages_search ) { @@ -11784,12 +13253,13 @@ sub remove_deleted_folders_from_wanted_list return ; } + sub examine_folder_and_count { - my ( $imap, $folder, $Side ) = @_ ; + my ( $mysync, $imap, $folder, $Side ) = @_ ; $Side ||= 'HostX' ; - if ( ! examine_folder( $imap, $folder, $Side ) ) { + if ( ! examine_folder( $mysync, $imap, $folder, $Side ) ) { return ; } my $nb_messages_select = count_from_select( $imap->History ) ; @@ -12547,10 +14017,10 @@ sub simulong { my $max_seconds = shift ; my $division = 5 ; - my $last = $division * $max_seconds ; - foreach my $i ( 1 .. ( $last ) ) { - myprint( "Are you still here $i/$last\n" ) ; - #myprint( "Are you still here $i/$last\n" . ( "Ah" x 40 . "\n") x 4000 ) ; + my $last_count = $division * $max_seconds ; + foreach my $i ( 1 .. ( $last_count ) ) { + myprint( "Are you still here ETA: " . ($last_count - $i) . "/$last_count msgs left\n" ) ; + #myprint( "Are you still here ETA: " . ($last_count - $i) . "/$last_count msgs left\n" . ( "Ah" x 40 . "\n") x 4000 ) ; sleep( 1 / $division ) ; } @@ -12590,10 +14060,10 @@ sub testsexit "List of failed tests:\n", $tests_failed ) ; exit $EXIT_TESTS_FAILED ; } - + cleanup_mess_from_tests( ) ; - # Cover is larger with --tests --testslive - if ( ! $mysync->{ testslive } ) + # Cover is larger with --tests --testslive + if ( ! $mysync->{ testslive } ) { exit ; } @@ -12604,6 +14074,7 @@ sub testsexit sub cleanup_mess_from_tests { undef @pipemess ; + return ; } sub after_get_options @@ -12613,7 +14084,7 @@ sub after_get_options # exit with --help option or no option at all - $debug and myprint( "numopt:$numopt\n" ) ; + $mysync->{ debug } and myprint( "numopt:$numopt\n" ) ; if ( $help or not $numopt ) { myprint( usage( $mysync ) ) ; @@ -12623,22 +14094,82 @@ sub after_get_options return ; } +sub tests_remove_edging_blanks +{ + note( 'Entering tests_remove_edging_blanks()' ) ; + + is( undef, remove_edging_blanks( ), 'remove_edging_blanks: no args => undef' ) ; + is( 'abcd', remove_edging_blanks( 'abcd' ), 'remove_edging_blanks: abcd => abcd' ) ; + is( 'ab cd', remove_edging_blanks( ' ab cd ' ), 'remove_edging_blanks: " ab cd " => "ab cd"' ) ; + + note( 'Leaving tests_remove_edging_blanks()' ) ; + return ; +} + + + +sub remove_edging_blanks +{ + my $string = shift ; + if ( ! defined $string ) + { + return ; + } + $string =~ s,^ +| +$,,g ; + return $string ; +} + + +sub tests_sanitize +{ + note( 'Entering tests_remove_edging_blanks()' ) ; + + is( undef, sanitize( ), 'sanitize: no args => undef' ) ; + my $mysync = {} ; + + $mysync->{ host1 } = ' example.com ' ; + $mysync->{ user1 } = ' to to ' ; + $mysync->{ password1 } = ' sex is good! ' ; + is( undef, sanitize( $mysync ), 'sanitize: => undef' ) ; + is( 'example.com', $mysync->{ host1 }, 'sanitize: host1 " example.com " => "example.com"' ) ; + is( 'to to', $mysync->{ user1 }, 'sanitize: user1 " to to " => "to to"' ) ; + is( 'sex is good!', $mysync->{ password1 }, 'sanitize: password1 " sex is good! " => "sex is good!"' ) ; + note( 'Leaving tests_remove_edging_blanks()' ) ; + return ; +} + + +sub sanitize +{ + my $mysync = shift ; + if ( ! defined $mysync ) + { + return ; + } + + foreach my $parameter ( qw( host1 host2 user1 user2 password1 password2 ) ) + { + $mysync->{ $parameter } = remove_edging_blanks( $mysync->{ $parameter } ) ; + } + return ; +} + sub easyany { my $mysync = shift ; # Gmail if ( $mysync->{gmail1} and $mysync->{gmail2} ) { - $debug and myprint( "gmail1 gmail2\n") ; + $mysync->{ debug } and myprint( "gmail1 gmail2\n") ; gmail12( $mysync ) ; return ; } if ( $mysync->{gmail1} ) { - $debug and myprint( "gmail1\n" ) ; + $mysync->{ debug } and myprint( "gmail1\n" ) ; gmail1( $mysync ) ; } if ( $mysync->{gmail2} ) { - $debug and myprint( "gmail2\n" ) ; + $mysync->{ debug } and myprint( "gmail2\n" ) ; gmail2( $mysync ) ; } # Office 365 @@ -12686,7 +14217,8 @@ sub gmail12 $mysync->{automap} = ( defined $mysync->{automap} ) ? $mysync->{automap} : 1 ; $mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ; $skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 0 ; - $mysync->{synclabels} = ( defined $mysync->{synclabels} ) ? $mysync->{synclabels} : 1 ; + $mysync->{ synclabels } = ( defined $mysync->{ synclabels } ) ? $mysync->{ synclabels } : 1 ; + $mysync->{ reynclabels } = ( defined $mysync->{ reynclabels } ) ? $mysync->{ reynclabels } : 1 ; push @exclude, '\[Gmail\]$' ; push @folderlast, '[Gmail]/All Mail' ; return ; @@ -12706,7 +14238,7 @@ sub gmail1 $skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 1 ; push @useheader, 'X-Gmail-Received', 'Message-Id' ; - push @regextrans2, 's,\[Gmail\].,,' ; + push @{ $mysync->{ regextrans2 } }, 's,\[Gmail\].,,' ; push @folderlast, '[Gmail]/All Mail' ; return ; } @@ -12731,10 +14263,16 @@ sub gmail2 if ( ! $mysync->{noexclude} ) { push @exclude, '\[Gmail\]$' ; } - push @useheader, 'Message-Id' ; - push @regextrans2, 's,\[Gmail\].,,' ; - push @regextrans2, 's/[ ]+/_/g' ; - push @regextrans2, q{s/['\\^"]/_/g} ; # Verified this + push @useheader, 'Message-Id' ; + push @{ $mysync->{ regextrans2 } }, 's,\[Gmail\].,,' ; + + # push @{ $mysync->{ regextrans2 } }, 's/[ ]+/_/g' ; # is now replaced + # by the two more specific following regexes, + # they remove just the beginning and trailing blanks, not all. + push @{ $mysync->{ regextrans2 } }, 's,^ +| +$,,g' ; + push @{ $mysync->{ regextrans2 } }, 's,/ +| +/,/,g' ; + # + push @{ $mysync->{ regextrans2 } }, q{s/['\\^"]/_/g} ; # Verified this push @folderlast, '[Gmail]/All Mail' ; return ; } @@ -12774,7 +14312,7 @@ sub office2 # I dislike double negation but here is one if ( ! $mysync->{noregexmess} ) { - push @regexmess, 's,(.{10500}),$1\r\n,g' ; + push @regexmess, 's,(.{10239}),$1\r\n,g' ; } # and another... if ( ! $mysync->{nof1f2} ) @@ -12808,7 +14346,7 @@ sub exchange2 push @regexflag, 's/\\\\Flagged//g' ; } if ( ! $mysync->{noregexmess} ) { - push @regexmess, 's,(.{10500}),$1\r\n,g' ; + push @regexmess, 's,(.{10239}),$1\r\n,g' ; } return ; } @@ -12818,7 +14356,7 @@ sub domino1 # Domino at host1 my $mysync = shift ; - $sep1 = q{\\} ; + $mysync->{ sep1 } = q{\\} ; $prefix1 = q{} ; $messageidnodomain = ( defined $messageidnodomain ) ? $messageidnodomain : 1 ; return ; @@ -12829,10 +14367,10 @@ sub domino2 # Domino at host1 my $mysync = shift ; - $sep2 = q{\\} ; + $mysync->{ sep2 } = q{\\} ; $prefix2 = q{} ; $messageidnodomain = ( defined $messageidnodomain ) ? $messageidnodomain : 1 ; - push @regextrans2, 's,^Inbox\\\\(.*),$1,i' ; + push @{ $mysync->{ regextrans2 } }, 's,^Inbox\\\\(.*),$1,i' ; return ; } @@ -12892,24 +14430,24 @@ sub resolv_with_getaddrinfo if ( ! $host ) { return ; } - my ( $err, @res ) = Socket::getaddrinfo( $host, "", { socktype => Socket::SOCK_RAW } ) ; - if ( $err ) { - myprint( "Cannot getaddrinfo of $host: $err\n" ) ; + my ( $err_getaddrinfo, @res ) = Socket::getaddrinfo( $host, "", { socktype => Socket::SOCK_RAW } ) ; + if ( $err_getaddrinfo ) { + myprint( "Cannot getaddrinfo of $host: $err_getaddrinfo\n" ) ; return ; } my @addr ; while( my $ai = shift @res ) { - my ( $err, $ipaddr ) = Socket::getnameinfo( $ai->{addr}, Socket::NI_NUMERICHOST(), Socket::NIx_NOSERV() ) ; - if ( $err ) { - myprint( "Cannot getnameinfo of $host: $err\n" ) ; + my ( $err_getnameinfo, $ipaddr ) = Socket::getnameinfo( $ai->{addr}, Socket::NI_NUMERICHOST(), Socket::NIx_NOSERV() ) ; + if ( $err_getnameinfo ) { + myprint( "Cannot getnameinfo of $host: $err_getnameinfo\n" ) ; return ; } - $debug and myprint( "$host => $ipaddr\n" ) ; + $sync->{ debug } and myprint( "$host => $ipaddr\n" ) ; push @addr, $ipaddr ; - - my ( $err_r, $reverse ) = Socket::getnameinfo( $ai->{addr}, 0, Socket::NIx_NOSERV() ) ; - $debug and myprint( "$host => $ipaddr => $reverse\n" ) ; + my $reverse ; + ( $err_getnameinfo, $reverse ) = Socket::getnameinfo( $ai->{addr}, 0, Socket::NIx_NOSERV() ) ; + $sync->{ debug } and myprint( "$host => $ipaddr => $reverse\n" ) ; } return $addr[0] ; @@ -12975,7 +14513,7 @@ sub resolvrev_with_getaddrinfo myprint( "Cannot getnameinfo of $host: $err\n" ) ; return ; } - $debug and myprint( "$host => $reverse\n" ) ; + $sync->{ debug } and myprint( "$host => $reverse\n" ) ; push @name, $reverse ; } @@ -13046,7 +14584,7 @@ sub tcpping my ($ping_ok, $rtt, $ip ) = $p->ping( $host, $mytimeout ) ; if ( ! defined $ping_ok ) { return ; } my $rtt_approx = sprintf( "%.3f", $rtt ) ; - $debug and myprint( "Host $host timeout $mytimeout port $port ok $ping_ok ip $ip acked in $rtt_approx s\n" ) ; + $sync->{ debug } and myprint( "Host $host timeout $mytimeout port $port ok $ping_ok ip $ip acked in $rtt_approx s\n" ) ; $p->close( ) ; if( $ping_ok ) { return 1 ; @@ -13118,7 +14656,7 @@ sub sslcheck return ; } my $nb_on = 0 ; - $debug and myprint( "sslcheck\n" ) ; + $mysync->{ debug } and myprint( "sslcheck\n" ) ; if ( ( ! defined $mysync->{port1} ) and @@ -13163,24 +14701,24 @@ sub sslcheck sub testslive { my $mysync = shift ; - $mysync->{host1} = 'test1.lamiral.info' ; - $mysync->{user1} = 'test1' ; - $mysync->{password1} = 'secret1' ; - $mysync->{host2} = 'test2.lamiral.info' ; - $mysync->{user2} = 'test2' ; - $mysync->{password2} ='secret2' ; + $mysync->{host1} ||= 'test1.lamiral.info' ; + $mysync->{user1} ||= 'test1' ; + $mysync->{password1} ||= 'secret1' ; + $mysync->{host2} ||= 'test2.lamiral.info' ; + $mysync->{user2} ||= 'test2' ; + $mysync->{password2} ||= 'secret2' ; return ; } sub testslive6 { my $mysync = shift ; - $mysync->{host1} = 'ks2ipv6.lamiral.info' ; - $mysync->{user1} = 'test1' ; - $mysync->{password1} = 'secret1' ; - $mysync->{host2} = 'ks2ipv6.lamiral.info' ; - $mysync->{user2} = 'test2' ; - $mysync->{password2} ='secret2' ; + $mysync->{host1} ||= 'ks2ipv6.lamiral.info' ; + $mysync->{user1} ||= 'test1' ; + $mysync->{password1} ||= 'secret1' ; + $mysync->{host2} ||= 'ks2ipv6.lamiral.info' ; + $mysync->{user2} ||= 'test2' ; + $mysync->{password2} ||= 'secret2' ; return ; } @@ -13234,7 +14772,8 @@ sub split_around_equal } -sub tests_sig_install + +sub tests_sig_install { note( 'Entering tests_sig_install()' ) ; @@ -13254,13 +14793,14 @@ sub tests_sig_install $mysync->{ debugsig } = 1 ; # Assign USR1 to call sub tototo # Surely a better value than undef should be returned when doing real signal stuff - is( undef, sig_install( $mysync, \&tototo, 'USR1' ), 'sig_install: USR1 tototo' ) ; - + is( undef, sig_install( $mysync, 'tototo', 'USR1' ), 'sig_install: USR1 tototo' ) ; + is( 1, kill( 'USR1', $PROCESS_ID ), 'sig_install: kill USR1 myself 1' ) ; is( 1, $mysync->{ tototo_calls }, 'sig_install: tototo call nb 1' ) ; + #return ; # Assign USR2 to call sub tototo - is( undef, sig_install( $mysync, \&tototo, 'USR2' ), 'sig_install: USR2 tototo' ) ; + is( undef, sig_install( $mysync, 'tototo', 'USR2' ), 'sig_install: USR2 tototo' ) ; is( 1, kill( 'USR2', $PROCESS_ID ), 'sig_install: kill USR2 myself 1' ) ; is( 2, $mysync->{ tototo_calls }, 'sig_install: tototo call nb 2' ) ; @@ -13274,7 +14814,7 @@ sub tests_sig_install is( 3, $mysync->{ tototo_calls }, 'sig_install: tototo call still nb 3' ) ; # Assign USR1 + USR2 to call sub tototo - is( undef, sig_install( $mysync, \&tototo, 'USR1', 'USR2' ), 'sig_install: USR1 USR2 tototo' ) ; + is( undef, sig_install( $mysync, 'tototo', 'USR1', 'USR2' ), 'sig_install: USR1 USR2 tototo' ) ; is( 1, kill( 'USR1', $PROCESS_ID ), 'sig_install: kill USR1 myself 4' ) ; is( 4, $mysync->{ tototo_calls }, 'sig_install: tototo call now nb 4' ) ; @@ -13288,27 +14828,31 @@ sub tests_sig_install } - -sub sig_install +# +sub sig_install { my $mysync = shift ; if ( ! $mysync ) { return ; } - my $mysub = shift ; - if ( ! $mysub ) { return ; } + my $mysubname = shift ; + if ( ! $mysubname ) { return ; } + if ( ! @ARG ) { return ; } + my @signals = @ARG ; - - $mysync->{ debugsig } and myprint( "In sig_install with $mysync and $mysub\n" ) ; + + my $mysub = \&$mysubname ; + #$mysync->{ debugsig } = 1 ; + $mysync->{ debugsig } and myprint( "In sig_install with sub $mysubname and signal @ARG\n" ) ; my $subsignal = sub { my $signame = shift ; - $mysync->{ debugsig } and myprint( "In subsignal with $signame and $mysync\n" ) ; + $mysync->{ debugsig } and myprint( "In subsignal with $signame and $mysubname\n" ) ; &$mysub( $mysync, $signame ) ; } ; foreach my $signal ( @signals ) { - $mysync->{ debugsig } and myprint( "Installing signal $signal for $subsignal\n") ; - output( $mysync, "kill -$signal $PROCESS_ID # special behavior\n" ) ; + $mysync->{ debugsig } and myprint( "Installing signal $signal to call sub $mysubname\n") ; + output( $mysync, "kill -$signal $PROCESS_ID # special behavior: call to sub $mysubname\n" ) ; ## no critic (RequireLocalizedPunctuationVars) $SIG{ $signal } = $subsignal ; } @@ -13327,7 +14871,7 @@ sub tototo sub mygetppid { if ( 'MSWin32' eq $OSNAME ) { - return( 'unknown' ) ; + return( 'unknown under MSWin32 (too complicated)' ) ; } else { # Unix return( getppid( ) ) ; @@ -13531,7 +15075,7 @@ sub myGetOptions } # We must be in CGI context now - if ( !defined( $mycgi ) ) { return ; } + if ( ! defined( $mycgi ) ) { return ; } my $badthings = 0 ; foreach my $key ( sort keys %options ) { @@ -13581,20 +15125,38 @@ sub myGetOptions } if ( ( $3 || q{} ) eq '@' ) { @{ ${$val} } = @values ; + my @option = map { +( "--$name", "$_" ) } @values ; + push @{ $mysync->{ cmdcgi } }, @option ; } elsif ( ref( $val ) eq 'ARRAY' ) { @{$val} = @values ; } - else { - ${$val} = $values[0] ; + elsif ( my $value = $values[0] ) + { + ${$val} = $value ; + push @{ $mysync->{ cmdcgi } }, "--$name", $value ; + } + else + { + } } } - else { + else + { # Checkbox # Considers only --name # Should consider also --no-name and --noname - ${$val} = $mycgi->param( $name ) ? 1 : undef ; + my $value = $mycgi->param( $name ) ; + if ( $value ) + { + ${$val} = 1 ; + push @{ $mysync->{ cmdcgi } }, "--$name" ; + } + else + { + ${$val} = undef ; + } } } if ( $badthings ) { @@ -13659,10 +15221,10 @@ sub tests_get_options_cgi_context # Testing s@ as ref $mysync->{cgi} = CGI->new( 'folder=fd1' ) ; get_options( $mysync ) ; - is_deeply( [ 'fd1' ], $mysync->{folder}, 'get_options cgi context: $mysync->{folder} => fd1' ) ; + is_deeply( [ 'fd1' ], $mysync->{ folder }, 'get_options cgi context: $mysync->{ folder } => fd1' ) ; $mysync->{cgi} = CGI->new( 'folder=fd1&folder=fd2' ) ; get_options( $mysync ) ; - is_deeply( [ 'fd1', 'fd2' ], $mysync->{folder}, 'get_options cgi context: $mysync->{folder} => fd1, fd2' ) ; + is_deeply( [ 'fd1', 'fd2' ], $mysync->{ folder }, 'get_options cgi context: $mysync->{ folder } => fd1, fd2' ) ; # Testing % $mysync->{cgi} = CGI->new( 'f1f2h=s1=d1&f1f2h=s2=d2&f1f2h=s3=d3' ) ; @@ -13692,9 +15254,15 @@ sub tests_get_options_cgi_context $mysync->{cgi} = CGI->new( 'simulong=4' ) ; get_options( $mysync ) ; is( 4, $mysync->{simulong}, 'get_options cgi context: --simulong=4 => $mysync->{simulong} => 4' ) ; - is( undef, $mysync->{folder}, 'get_options cgi context: --simulong=4 => $mysync->{folder} => undef' ) ; + is( undef, $mysync->{ folder }, 'get_options cgi context: --simulong=4 => $mysync->{ folder } => undef' ) ; #myprint( Data::Dumper->Dump( [ $mysync ] ) ) ; + $mysync ={} ; + $mysync->{cgi} = CGI->new( 'justfoldersizes=on' ) ; + get_options( $mysync ) ; + is( 1, $mysync->{ justfoldersizes }, 'get_options cgi context: --justfoldersizes=1 => justfoldersizes => 1' ) ; + myprint( Data::Dumper->Dump( [ $mysync ] ) ) ; + note( 'Leaving tests_get_options_cgi_context()' ) ; return ; } @@ -13741,20 +15309,28 @@ sub get_options_cgi 'domino2' => \$mysync->{domino2}, 'f1f2=s@' => \$mysync->{f1f2}, 'f1f2h=s%' => \$mysync->{f1f2h}, - 'folder=s@' => \$mysync->{folder}, + 'folder=s@' => \$mysync->{ folder }, 'blabla=s' => \@blabla, 'testslive!' => \$mysync->{testslive}, 'testslive6!' => \$mysync->{testslive6}, 'releasecheck!' => \$mysync->{releasecheck}, 'simulong=i' => \$mysync->{simulong}, 'debugsleep=f' => \$mysync->{debugsleep}, + 'subfolder1=s' => \$mysync->{ subfolder1 }, + 'subfolder2=s' => \$mysync->{ subfolder2 }, + 'justfolders!' => \$mysync->{ justfolders }, + 'justfoldersizes!' => \$mysync->{ justfoldersizes }, + 'delete1!' => \$mysync->{ delete1 }, + 'delete2!' => \$mysync->{ delete2 }, + 'delete2duplicates!' => \$mysync->{ delete2duplicates }, + 'tail!' => \$mysync->{tail}, # blabla and f1f2h=s% could be removed but # tests_get_options_cgi() should be split before # with a sub tests_myGetOptions() ) ; - $debug and output( $mysync, "get options: [$opt_ret][$numopt]\n" ) ; + $mysync->{ debug } and output( $mysync, "get options: [$opt_ret][$numopt]\n" ) ; if ( ! $opt_ret ) { return ; @@ -13762,7 +15338,7 @@ sub get_options_cgi return $numopt ; } -sub get_options_cmd +sub get_options_cmd { my $mysync = shift @ARG ; my @arguments = @ARG ; @@ -13779,7 +15355,7 @@ sub get_options_cmd my $opt_ret = myGetOptions( $mysync, \@arguments, - 'debug!' => \$debug, + 'debug!' => \$mysync->{ debug }, 'debuglist!' => \$debuglist, 'debugcontent!' => \$debugcontent, 'debugsleep=f' => \$mysync->{debugsleep}, @@ -13792,8 +15368,9 @@ sub get_options_cmd 'debugfolders!' => \$mysync->{debugfolders}, 'debugssl=i' => \$mysync->{debugssl}, 'debugcgi!' => \$debugcgi, - 'debugenv' => \$mysync->{debugenv}, - 'debugsig' => \$mysync->{debugsig}, + 'debugenv!' => \$mysync->{debugenv}, + 'debugsig!' => \$mysync->{debugsig}, + 'debuglabels!' => \$mysync->{debuglabels}, 'simulong=i' => \$mysync->{simulong}, 'abort' => \$mysync->{abort}, 'host1=s' => \$mysync->{host1}, @@ -13821,9 +15398,10 @@ sub get_options_cmd 'authmd5!' => \$authmd5, 'authmd51!' => \$authmd51, 'authmd52!' => \$authmd52, - 'sep1=s' => \$sep1, - 'sep2=s' => \$sep2, - 'folder=s@' => \$mysync->{folder}, + 'sep1=s' => \$mysync->{ sep1 }, + 'sep2=s' => \$mysync->{ sep2 }, + 'sanitize!' => \$mysync->{ sanitize }, + 'folder=s@' => \$mysync->{ folder }, 'folderrec=s' => \@folderrec, 'include=s' => \@include, 'exclude=s' => \@exclude, @@ -13832,10 +15410,11 @@ sub get_options_cmd 'folderlast=s' => \@folderlast, 'prefix1=s' => \$prefix1, 'prefix2=s' => \$prefix2, - 'subfolder2=s' => \$subfolder2, - 'fixslash2!' => \$fixslash2, + 'subfolder1=s' => \$mysync->{ subfolder1 }, + 'subfolder2=s' => \$mysync->{ subfolder2 }, + 'fixslash2!' => \$mysync->{ fixslash2 }, 'fixInboxINBOX!' => \$fixInboxINBOX, - 'regextrans2=s' => \@regextrans2, + 'regextrans2=s@' => \$mysync->{ regextrans2 }, 'mixfolders!' => \$mixfolders, 'skipemptyfolders!' => \$skipemptyfolders, 'regexmess=s' => \@regexmess, @@ -13849,8 +15428,9 @@ sub get_options_cmd 'filterflags!' => \$filterflags, 'flagscase!' => \$flagscase, 'syncflagsaftercopy!' => \$syncflagsaftercopy, - 'resyncflags!' => \$mysync->{resyncflags}, - 'synclabels!' => \$mysync->{synclabels}, + 'resyncflags!' => \$mysync->{ resyncflags }, + 'synclabels!' => \$mysync->{ synclabels }, + 'resynclabels!' => \$mysync->{ resynclabels }, 'delete|delete1!' => \$mysync->{ delete1 }, 'delete2!' => \$mysync->{ delete2 }, 'delete2duplicates!' => \$mysync->{ delete2duplicates }, @@ -13859,15 +15439,17 @@ sub get_options_cmd 'delete2foldersbutnot=s' => \$delete2foldersbutnot, 'syncinternaldates!' => \$syncinternaldates, 'idatefromheader!' => \$idatefromheader, - 'syncacls!' => \$syncacls, - 'maxsize=i' => \$mysync->{ maxsize }, - 'minsize=i' => \$minsize, - 'maxage=i' => \$maxage, - 'minage=i' => \$minage, - 'search=s' => \$search, - 'search1=s' => \$search1, - 'search2=s' => \$search2, - 'foldersizes!' => \$foldersizes, + 'syncacls!' => \$syncacls, + 'maxsize=i' => \$mysync->{ maxsize }, + 'appendlimit=i' => \$mysync->{ appendlimit }, + 'truncmess=i' => \$mysync->{ truncmess }, + 'minsize=i' => \$minsize, + 'maxage=f' => \$maxage, + 'minage=f' => \$minage, + 'search=s' => \$search, + 'search1=s' => \$search1, + 'search2=s' => \$search2, + 'foldersizes!' => \$foldersizes, 'foldersizesatend!' => \$foldersizesatend, 'dry!' => \$mysync->{dry}, 'expunge1|expunge!' => \$mysync->{ expunge1 }, @@ -13878,7 +15460,7 @@ sub get_options_cmd 'subscribeall|subscribe_all!' => \$subscribeall, 'justbanner!' => \$justbanner, 'justfolders!'=> \$mysync->{ justfolders }, - 'justfoldersizes!' => \$justfoldersizes, + 'justfoldersizes!' => \$mysync->{ justfoldersizes }, 'fast!' => \$fast, 'version' => \$mysync->{version}, 'help' => \$help, @@ -13957,6 +15539,7 @@ sub get_options_cmd 'skipcrossduplicates!' => \$skipcrossduplicates, 'debugcrossduplicates!' => \$debugcrossduplicates, 'log!' => \$mysync->{log}, + 'tail!' => \$mysync->{tail}, 'logfile=s' => \$mysync->{logfile}, 'logdir=s' => \$mysync->{logdir}, 'errorsmax=i' => \$mysync->{errorsmax}, @@ -13972,12 +15555,12 @@ sub get_options_cmd 'delete1emptyfolders' => \$mysync->{delete1emptyfolders}, ) ; #myprint( Data::Dumper->Dump( [ $mysync ] ) ) ; - $debug and output( $mysync, "get options: [$opt_ret][$numopt]\n" ) ; + $mysync->{ debug } and output( $mysync, "get options: [$opt_ret][$numopt]\n" ) ; my $numopt_after = scalar @arguments ; #myprint( "get options: [$opt_ret][$numopt][$numopt_after]\n" ) ; if ( $numopt_after ) { - myprint( - "Extra arguments found: @arguments\n", + myprint( + "Extra arguments found: @arguments\n", "It usually means a quoting issue in the command line ", "or some misspelling options.\n", ) ; @@ -14194,47 +15777,31 @@ sub testsdebug { # Now a little obsolete since there is # imapsync ... --testsunit "anyfunction" - my $mysync = shift ; - if ( ! $mysync->{ testsdebug } ) { return ; } - SKIP: { + my $mysync = shift ; + if ( ! $mysync->{ testsdebug } ) { return ; } + SKIP: { if ( ! $mysync->{ testsdebug } ) { - skip 'No test in normal run' ; - } + skip 'No test in normal run' ; + } - note( 'Entering testsdebug()' ) ; - ok( ( ( not -d 'W/tmp/tests' ) or rmtree( 'W/tmp/tests/' ) ), 'testsdebug: rmtree W/tmp/tests' ) ; - tests_appendlimit_from_capability( ) ; - tests_maxsize_setting( ) ; - tests_mock_capability( ) ; - tests_appendlimit( ) ; - tests_capability_of( ) ; - note( 'Leaving testsdebug()' ) ; + note( 'Entering testsdebug()' ) ; + ok( ( ( not -d 'W/tmp/tests' ) or rmtree( 'W/tmp/tests/' ) ), 'testsdebug: rmtree W/tmp/tests' ) ; + tests_check_binary_embed_all_dyn_libs( ) ; + note( 'Leaving testsdebug()' ) ; done_testing( ) ; } return ; } -sub tests_template -{ - note( 'Entering tests_template()' ) ; - - is( undef, undef, 'template: undef is undef' ) ; - is_deeply( {}, {}, 'template: a hash is a hash' ) ; - is_deeply( [], [], 'template: an array is an array' ) ; - note( 'Leaving tests_template()' ) ; - return ; -} - - sub tests { - my $mysync = shift ; - if ( ! $mysync->{ tests } ) { return ; } + my $mysync = shift ; + if ( ! $mysync->{ tests } ) { return ; } - SKIP: { + SKIP: { skip 'No test in normal run' if ( ! $mysync->{ tests } ) ; - note( 'Entering tests()' ) ; + note( 'Entering tests()' ) ; tests_folder_routines( ) ; tests_compare_lists( ) ; tests_regexmess( ) ; @@ -14371,12 +15938,41 @@ sub tests tests_logfileprepa( ) ; tests_useheader_suggestion( ) ; tests_nb_messages_in_2_not_in_1( ) ; - #tests_always_fail( ) ; - done_testing( 1264 ) ; - note( 'Leaving tests()' ) ; + tests_labels_add_subfolder2( ) ; + tests_labels_remove_subfolder1( ) ; + tests_resynclabels( ) ; + tests_labels_remove_special( ) ; + tests_uniq( ) ; + tests_remove_from_requested_folders( ) ; + tests_errors_log( ) ; + tests_add_subfolder1_to_folderrec( ) ; + tests_sanitize_subfolder( ) ; + tests_remove_edging_blanks( ) ; + tests_sanitize( ) ; + tests_remove_last_char_if_is( ) ; + tests_check_binary_embed_all_dyn_libs( ) ; + tests_nthline( ) ; + tests_secondline( ) ; + tests_tail( ) ; + tests_truncmess( ) ; + #tests_always_fail( ) ; + done_testing( 1454 ) ; + note( 'Leaving tests()' ) ; } return ; } +sub tests_template +{ + note( 'Entering tests_template()' ) ; + + is( undef, undef, 'template: undef is undef' ) ; + is_deeply( {}, {}, 'template: a hash is a hash' ) ; + is_deeply( [], [], 'template: an array is an array' ) ; + note( 'Leaving tests_template()' ) ; + return ; +} + + diff --git a/index.shtml b/index.shtml index a9f10ef..775293f 100644 --- a/index.shtml +++ b/index.shtml @@ -50,9 +50,11 @@ - Installation
- Documentation
- News about imapsync , previous and next releases
-- List of the 79 imap software server applications supported by imapsync
+- List of the 81 imap software server applications supported by imapsync
- Similar software tools and external services
- Discuss or search on the mailing-list
+- Imapsync Terms and Conditions
+- Imapsync Privacy Policy
- Bottom of this page
@@ -64,28 +66,34 @@-
Welcome to the imapsync web site!
+Time to copy all your emails and folders elsewhere!
- + - -Is it time to copy all your emails and folders elsewhere?
-Site last updated on +Site last updated on
+Why this website looks so old fashion? +What is imapsync? (back to menu)
++
+ +
+Imapsync presentation by Gilles +Imapsync is an IMAP transfers tool. The purpose of imapsync is to migrate IMAP accounts or to backup IMAP accounts. -IMAP is one of the three current standard protocols to access mailboxes, +IMAP is one of the three current standard protocols used to access mailboxes, the two other are POP3 and HTTP with webmails, webmails are often tied to an IMAP server.
@@ -106,7 +114,7 @@ and restart it later efficiently, without generating duplicates. "Command line" means it's not a graphical tool, on Windows you have to run imapsync from a batch file. Anyway there is a visual online service, -you can try imapsync at https://i005.lamiral.info/X/ +you can try imapsync online at https://i005.lamiral.info/X/@@ -114,7 +122,8 @@ you can try imapsync at https://i005.lam Most email systems don't set or get Contacts or Calendars via the IMAP protocol. No way via IMAP, no way via imapsync but it can be done with other tools or via export/import of csv or ics files. -Also consider using caldavsynchronizer or asking experts at Sumatra company. +Also consider using caldavsynchronizer +or asking experts at Sumatra company.
@@ -163,7 +172,8 @@ See detailed explanation and motivation here when LAMIRAL
@@ -204,45 +214,75 @@ See detailed explanation and motivation here when@@ -587,6 +665,9 @@ to understand imapsync and succeed in your migration or backup.Buy imapsync and support (back to menu)
+++ +Imapsync was the best investment I've ever done.
+— Uwe Keim +Dec 6 '16 at 12:17 +
++ Read some other nice and true testimonials from users.
+
+- Buy complete and latest imapsync for €60 EUR.
+
There is no trial version but I offer 30-day money back guarantee whatever the reason for a refund. Or drop me a gentle email and I will provide you the latest imapsync because, well, you made the effort to contact me.
- After buying and downloading imapsync, go to the installation documentation. + After downloading imapsync, go to the installation documentation.+ +Special offer: it's only €120 EUR if you buy +imapsync + support at once + (instead of 60 + 120 = 180 EUR). +
-For €60 EUR you will get: +
For €120 EUR you will get:
+
+- Imapsync full professional support (that costs also €120 EUR by itself, see why below).
+- Standalone imapsync.exe for win32, easy installation done by a zip extraction anywhere. See README_Windows.txt for details.
-- Standalone imapsync_bin_Darwin for Mac OS X.
-- Imapsync Perl source code for any operating system, Unix, Windows, OS X.
-- The online visual interface, like /X, to install the service on a Linux server (not working on Windows yet).
-- Lifetime of imapsync updates without extra payment.
-- 30-day money back guarantee! No question nor condition to get a refund, really, just request it and you'll sure get a refund!
-- No limit to do anything with imapsync and its license.
+ +- Standalone imapsync_bin_Darwin for Mac OS X. (not Catalina ready because of the new signature mechanism)
+- Imapsync Perl source code for any operating system, Unix, Windows, OS X.
+ +- The online visual interface, like /X, + to install the service on a Linux server (not working on Windows yet).
+ +- Lifetime of imapsync updates without extra payment.
+ +- 30-day money back guarantee! No question nor condition to get a refund, really, just request it and you'll sure get a refund!
+ +- No limit to do anything with imapsync and its license.
For €60 EUR you will get all the above except imapsync full professional support (the first line). +
+-For €60 EUR independently, you get imapsync full professional support, +For €120 EUR independently, you get imapsync full professional support, provided by the imapsync designer and developer, Gilles LAMIRAL, your servitor, who has been supporting imap migrations with imapsync for more than 18 years (I started in 2001). See a detailed support description below.
- -Special offer: it's only €100 EUR if you buy -imapsync + support at once - (instead of 60 + 60 = 120 EUR). +You may wonder why the support alone is the same price as imapsync+support? it's +because when you buy both at once, I'm not sure I will have to spend some time +directly with you, it's possible that everything will go smoothly for you; +but when you buy the support alone, after buying the software, +then I will surely spend time (any amount of hours) to help you. +Does it seem fair enough?
+-At the end of the payment Paypal will proposed you to go back to the site, +Instantly at the end of the payment, Paypal will proposed you to go back to the site, via a link to gilles@lamiral.info (it's my paypal account name), this link brings you to imapsync download.
You will also receive an email from gilles@lamiral.info a few minutes later -(can fall in the Spam folder sometimes).
+(it can fall in the Spam folder sometimes).
In order to get an accurate invoice, please make sure the delivery postal address you enter in Paypal suits your accounting department, since revised editing is not easy.
Your invoice will be sent within a few days by email. My company is identified by VAT number FR74429303332 or French SIRET number 42930333200051, -French APE/NAF number 6201Z (programmation informatique, aka, computer programming). +French APE/NAF number 6201Z ("programmation informatique" in French, aka, computer programming).Support (back to menu) @@ -300,11 +340,12 @@ French APE/NAF number 6201Z (programmation informatique, aka, computer programmi
For those of you who seek support, contact me, Gilles LAMIRAL, by email or phone:
-
- Email address: gilles@lamiral.info.
-- Desk phone number: +33 9 51 84 42 42 located in France.
+- Email address: gilles@lamiral.info.
+- Desk phone number: +33 9 51 84 42 42 located in France.
- Mobile phone number: +33 6 19 22 03 54 located in France (SFR operator).
-Refund for a bank transfer is far less easy than with Paypal so drop me an email to test imapsync and to be sure it will do what you need it to do, before any payment.
+
The bank account references will be given upon request.+It will surely add extra work editing manually the invoice, back and forth getting good coordinates, +so I will charge both software and support normal prices for a payment via bank transfer, +that is 120 EUR. +
Payment with crypto currencies, Bitcoins, Ethereum etc. (back to menu) @@ -373,33 +416,54 @@ The bank account references will be given upon request.
-
+- 6000 to 7000 users per month (45000 users a year).
-- 7 to 12 millions mailboxes transfers per month.
-- 108 millions transfers for 2018, -that is more than three whole mailboxes synced per second in average, -and an estimation of 38 Petabytes ( 1 PiB = 2^50 bytes = 1024^5 ~ 10^15) transferred in total, -taking a mean of 400 Mbytes per mailbox (a mean given by online /X stats). +
- 6000 to 7000 users per month (48000 users a year).
+- 11 to 16 millions mailboxes transfers per month.
+- 159 millions transfers for 2019, +that is five whole mailboxes complitely synced per second in average (159*10^6/365/24/3600), +and an estimation of 71 Petabytes ( 1 PiB = 2^50 bytes = 1024^5 ~ 10^15) transferred in total in 2019, +taking a mean of 500 Mbytes per transfer (an estimated mean given by online /X real stats). +71 Petabytes = 500*10^6*159*10^6. The global internet traffic of 2019 was 2004 Exabytes so imapsync +is a very very tiny thing (35 per million). +
+- +159 millions transfers for 2019. +That's nearly 717 millions email messages transferred by imapsync every day (1600*164/366) +taking a mean of 1600 messages per transfer (an estimated mean given by online /X real stats). + +The internet global estimated number of messages sent every day is 300 billions in 2019 +(10% being non-spam but that's another story...). +So imapsync does 0.24% of all email trafic, not that bad for a command line tool!
-- Operating systems run by imapsync users (in 2018): +
- Operating systems run by imapsync users (in 2019):
-
- Linux: 70%
-- Win32: 17%
-- Darwin: 12%
-- FreeBSD: 1%
-- Solaris: 0.04%
-- OpenBSD: 0.04%
-- Other: 0.01%
- +- Linux: 68%
+- Win32: 18%
+- Darwin: 13%
+- FreeBSD: 0.92%
+- OpenBSD: 0.05%
+- Solaris: 0.03%
+- Cygwin: 0%
+- Unknown: 0%
+- Other: 0%
- Highest use rate: about 56 millions of IMAP mailbox transfers by just one host.
- Biggest known account migrated: 2.4 millions folders (figure independently reported).
++The figures presented here do not include the github imapsync release usage. +It's because --noreleasecheck is on by default since release 1.592 (2014/05/22) +in the github release. + +Looking at the numbers before and after 2014, the figures showed here +could be doubled. +
@@ -416,7 +480,7 @@ Via the User-agent parameter it also sends:
You can remove this behavior by adding option --noreleasecheck on the command line (or by setting $releasecheck = 0 in the source code). -Check CVE-2013-4279. +Check CVE-2013-4279.
@@ -481,7 +545,19 @@ All example scripts can be found in the examples/* direInstallation (back to menu)
-Depending on the system where you'll run imapsync:
++Hardware system requirements: +
++
+ + +- 500 MB of RAM is ok. The mean value RAM used per imapsync process is 230 MB.
+- Any CPU is ok.
+- 100 MB of disk space is far enough.
+- Any network link is ok. The average bandwidth rate is 345 KiB/s ~ 2.8 Mbps.
+Depending on the system where you'll run imapsync:
+
- Windows users, read directly README_Windows.txt.
@@ -495,7 +571,7 @@ All example scripts can be found in the examples/* dire- ArchLinux users, read INSTALL.ArchLinux.txt.
- FreeBSD users, read INSTALL.FreeBSD.txt.
-- Docker users, read INSTALL.Docker.txt +
- Docker users, read FAQ.Docker.txt or directly the Imapsync Docker page.
- Online UI sysadmins, wanting their own /X, @@ -539,8 +615,10 @@ to understand imapsync and succeed in your migration or backup.
- Zimbra.
- SmarterMail.
- Kerio.
-- Other various imap software servers.
- Oracle-UCS.
+- David Tobit.
+- Yandex.
+- Other various imap software servers.
- Emptying an account.
- XOAUTH2 (Gmail).
- Online Visual Inferface (the web GUI /X).
+- Principles and design ideas.
+- GDPR (General Data Protection Regulation).
+- Using Docker.
What you're allowed to do with imapsync is "No limits to do anything with this work and this license." -like repeated in the LICENSE file (the detour is worth it). +like written in the https://imapsync.lamiral.info/LICENSE file (the detour is worth it).
@@ -638,7 +719,7 @@ like repeated in the LICENSE file (the detour is worth it This document last modified on -($Id: index.shtml,v 1.419 2019/06/26 22:25:17 gilles Exp gilles $)
+($Id: index.shtml,v 1.443 2020/01/02 23:37:20 gilles Exp gilles $)
Top of the page diff --git a/tests.sh b/tests.sh index e096172..b2b0309 100644 --- a/tests.sh +++ b/tests.sh @@ -1,6 +1,6 @@ #!/bin/sh -# $Id: tests.sh,v 1.340 2019/06/26 22:21:30 gilles Exp gilles $ +# $Id: tests.sh,v 1.353 2020/01/02 23:48:18 gilles Exp gilles $ # general tests start # general tests end @@ -147,9 +147,21 @@ sendtestmessage() { eval "$cmd" } +sendtestmessage_titi() { + email=${1:-"titi"} + rand=${2:-"`pwgen 16 1`"} + mess='test: '"$rand" + cmd="echo $mess""| mail -s '""$mess""' $email" + echo $cmd + eval "$cmd" +} + can_send() { + + # no send at all return 1 + test X`hostname` = X"petite" && return 0; test X`hostname` = X"plume" && return 0; test X`hostname` = X"vadrouille" && return 0; @@ -304,11 +316,12 @@ ll_justhost2() } - +# In mandatory_tests testslive() { $CMD_PERL ./imapsync --testslive } +# In mandatory_tests testslive6() { $CMD_PERL ./imapsync --testslive6 } @@ -349,6 +362,17 @@ ll_INBOX() { --folder INBOX } +ll_skipcrossduplicates() { + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --skipcrossduplicates --debugcrossduplicates +} + + + ll_append_debugimap() { sendtestmessage $CMD_PERL ./imapsync \ @@ -422,6 +446,7 @@ kk_simulong() { --testslive --simulong 30 } +# In mandatory_tests ll_sigreconnect_INT() { ( $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -433,13 +458,13 @@ ll_sigreconnect_INT() { echo status code when killing itself: $? # status code when killing itself? ) & echo ; sleep 2; echo ; - kill -INT `cat /tmp/imapsync_tests_ll_sigreconnect_INT.pid` + kill -INT `head -1 /tmp/imapsync_tests_ll_sigreconnect_INT.pid` echo ; sleep 3; echo ; - kill -INT `cat /tmp/imapsync_tests_ll_sigreconnect_INT.pid` + kill -INT `head -1 /tmp/imapsync_tests_ll_sigreconnect_INT.pid` echo ; sleep 3; echo ; - kill -INT `cat /tmp/imapsync_tests_ll_sigreconnect_INT.pid` + kill -INT `head -1 /tmp/imapsync_tests_ll_sigreconnect_INT.pid` sleepenh 0.1 - kill -INT `cat /tmp/imapsync_tests_ll_sigreconnect_INT.pid` + kill -INT `head -1 /tmp/imapsync_tests_ll_sigreconnect_INT.pid` wait } @@ -479,8 +504,10 @@ ll_sigignore_TERM() { --sigignore 'TERM' --simulong 10 } +# ABORT tests -ll_abort_nopidfile() { +# In mandatory_tests +ll_abort_pidfile_no_exist() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -490,17 +517,19 @@ ll_abort_nopidfile() { | grep 'Can not read pidfile /noexist. Exiting.' } +# In mandatory_tests ll_abort_noprocess() { - echo 999999 > /tmp/imapsync_fake.pid + echo 999999 > /tmp/imapsync_fake.pid $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --abort --pidfile /tmp/imapsync_fake.pid \ - | grep 'Can not send signal to PID 999999. Exiting.' + --abort --pidfile /tmp/imapsync_fake.pid \ + | grep 'Can not send signal kill ZERO to PID 999999.' } +# In mandatory_tests ll_abort() { # send QUIT signal rm -f LOG_imapsync/imapsync_abortme.log $CMD_PERL ./imapsync \ @@ -518,13 +547,19 @@ ll_abort() { # send QUIT signal --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --abort --pidfile /tmp/imapsync_abortme.pid --tail \ - | egrep 'Process PID .* ended. Exiting.' || return 1 + --abort --pidfile /tmp/imapsync_abortme.pid \ + --logfile imapsync_aborter.log \ + | egrep 'Process PID .* ended.' \ + || { echo 'Look into LOG_imapsync/imapsync_aborter.log' ; return 1 ; } + grep 'Killing myself with signal QUIT' LOG_imapsync/imapsync_abortme.log } -ll_abort_cgi_context() { + + +# In mandatory_tests +ll_abort_cgi_context_tail() { rm -f LOG_imapsync/imapsync_abortme.log $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -542,12 +577,43 @@ ll_abort_cgi_context() { --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ --abort --pidfile /tmp/imapsync_abortme_cgi_context.pid --pidfilelocking --tail \ - | egrep 'Process PID .* ended. Exiting.' || return 1 + | egrep 'Process PID .* ended.' || return 1 grep 'Killing myself with signal QUIT' LOG_imapsync/imapsync_abortme_cgi_context.log } +# In mandatory_tests +ll_abort_no_pidfile_option() { + rm -f LOG_imapsync/imapsync_abortme.log + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --logfile imapsync_abortme_no_pidfile_option.log --simulong 4 & + + sleep 2 + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --abort \ + | egrep 'Process PID .* ended.' || return 1 + + grep 'Killing myself with signal QUIT' LOG_imapsync/imapsync_abortme_no_pidfile_option.log +} + + +abort_tests() +{ + ll_abort_pidfile_no_exist \ + && ll_abort_noprocess \ + && ll_abort \ + && ll_abort_cgi_context_tail \ + && ll_abort_no_pidfile_option +} ll_nouid1() { can_send && sendtestmessage @@ -556,7 +622,7 @@ ll_nouid1() { --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --nouid1 --folder INBOX --debugimap1 + --nouid1 --folder INBOX # --debugimap1 } @@ -607,13 +673,14 @@ ll_errors() { #--pipemess 'grep lalalala' --nopipemesscheck --dry --debugcontent --debugflags } -ll_debug() { +ll_debug() +{ $CMD_PERL ./imapsync \ - --host1 $HOST1 --user1 tata \ - --passfile1 ../../var/pass/secret.tata \ - --host2 $HOST2 --user2 titi \ - --passfile2 ../../var/pass/secret.titi \ - --debug --nofoldersizes + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --debug } ll_debugcontent() { @@ -637,6 +704,17 @@ ll_debugmemory() { --debugmemory --nofoldersizes --folder INBOX } +ll_justfolderlists() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --checkselectable --justfolderlists +} + + ll_checkselectable() { $CMD_PERL ./imapsync \ @@ -666,11 +744,11 @@ ll_checkfoldersexist() --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --checkfoldersexist --debugimap1 --justfolderlists \ - | grep -i 'checking wanted folders exist' + --checkfoldersexist --debug --justfolderlists \ + | grep -i 'checking' } -ll_nocheckfoldersexist() +ll_nocheckfoldersexist() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -682,7 +760,10 @@ ll_nocheckfoldersexist() } -ll_nofoldersizes() + + + +ll_nofoldersizes() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -718,7 +799,7 @@ pidfile_bad() { test "$?" = "$EXIT_PID_FILE_ERROR" } -tail() { +test_tail() { $CMD_PERL ./imapsync \ --justbanner --simulong 15 \ --pidfile /var/tmp/imapsync_tail_tests.pid \ @@ -964,6 +1045,7 @@ ksks_init_test1() --folder INBOX.init --f1f2 INBOX.init=INBOX } +# In mandatory_tests ksks_reset_test1() { ksks_empty_test1 @@ -1005,7 +1087,9 @@ ll_folder_mixfolders() { # Way to check it each time: # sh -x tests.sh ll_folder_create ll_delete2folders -ll_folder_create() { + +# In mandatory_tests +ll_folder_create() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1015,6 +1099,7 @@ ll_folder_create() { --justfolders } +# In mandatory_tests ll_folder_create_INBOX_Inbox() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1060,6 +1145,7 @@ ll_folder_domino_sub() { --justfolders --dry --debug } +# In mandatory_tests ll_domino2() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1073,6 +1159,7 @@ ll_domino2() { } +# In mandatory_tests ll_domino1_domino2() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1194,6 +1281,7 @@ ll_size_null() { --folder INBOX.size_null } +# In mandatory_tests ll_noheader() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1203,6 +1291,7 @@ ll_noheader() { --folder INBOX.few_emails --useheader '' --debug } +# In mandatory_tests ll_noheader_force() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1313,6 +1402,16 @@ ll_automap() { --justautomap --automap } +ll_justautomap() { + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --justautomap +} + + l_ks_automap() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1366,9 +1465,11 @@ ll_justfolders_delete1emptyfolders() { --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --justfolders --delete1emptyfolders --delete1 --include Empty --folder INBOX --folderfirst INBOX.Empty.Empty --foldersizes + --justfolders --delete1emptyfolders --include Empty --folder INBOX --folderfirst INBOX.Empty.Empty --foldersizes } + + ll_delete1_delete1emptyfolders() { ./W/learn/create_folder localhost tata `cat /g/var/pass/secret.tata` INBOX.Empty INBOX.Empty.Empty INBOX.Empty.Empty.Empty $CMD_PERL ./imapsync \ @@ -1388,9 +1489,7 @@ ll_justfolders_skipemptyfolders() { --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ --justfolders --skipemptyfolders \ - --folder INBOX.empty --folder INBOX.notempty --minage 3660 - - echo "sudo rm -rf /home/vmail/titi/.new_folder/" + --folder INBOX.empty --folder INBOX.notempty } @@ -1407,7 +1506,8 @@ ll_justfolders_folderfirst_noexist() { -ll_justfolders_foldersizes() { +ll_justfolders_foldersizes() +{ $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1418,8 +1518,9 @@ ll_justfolders_foldersizes() { } - -ll_delete2foldersonly() { +# In mandatory_tests +ll_delete2foldersonly_dry() +{ $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1429,7 +1530,10 @@ ll_delete2foldersonly() { --subfolder2 NEW --delete2foldersonly NEW --dry } -ll_delete2foldersonly_tmp() { +# In mandatory_tests +ll_delete2foldersonly_subfolder2() +{ +./W/learn/create_folder localhost titi `cat /g/var/pass/secret.titi` INBOX.NEW_2 $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1437,10 +1541,14 @@ ll_delete2foldersonly_tmp() { --passfile2 ../../var/pass/secret.titi \ --justfolders --nofoldersizes \ --subfolder2 NEW_2 \ - --delete2foldersonly NEW_2 + --delete2foldersonly NEW_2 --folder INBOX --debug + # NEW_2 should be still there because of --subfolder2 NEW_2 + test -d /home/vmail/titi/.NEW_2/ || return 1 } -ll_delete2foldersbutnot() { +# In mandatory_tests +ll_delete2foldersbutnot() +{ $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1451,7 +1559,9 @@ ll_delete2foldersbutnot() { --dry } -ll_delete2foldersonly_NEW_3() { +# In mandatory_tests +ll_delete2foldersonly_NEW_3() +{ $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1491,8 +1601,9 @@ ll_delete2foldersonly_bug() { } - -ll_delete2folders() { +# In mandatory_tests +ll_delete2folders() +{ $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ @@ -1501,7 +1612,7 @@ ll_delete2folders() { --justfolders --nofoldersizes \ --delete2folders - ! test -d /home/vmail/titi/.NEW_3/ || return 1 + ! test -d /home/vmail/titi/.NEW_3/ || return 1 } @@ -1672,27 +1783,41 @@ ks_justconnect_ipv6_nossl() -ll_justfoldersizes() +ll_justfoldersizes() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --nocheckfoldersexist --nocheckselectable \ + --passfile2 ../../var/pass/secret.titi \ + --justfoldersizes # --folder INBOX +} + +ll_justfoldersizes_all_to_INBOX() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --nocheckfoldersexist --nocheckselectable \ + --passfile2 ../../var/pass/secret.titi \ + --justfoldersizes --regextrans2 's/.*/INBOX/' +} + + +ll_justfoldersizes_case_different() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --justfoldersizes + --nocheckfoldersexist --nocheckselectable \ + --justfoldersizes --folder NoExist --folder INBOX --regextrans2 's,^INBOX$,iNbOx,' } -ll_justfoldersizes_case_different() -{ - $CMD_PERL ./imapsync \ - --host1 $HOST1 --user1 tata \ - --passfile1 ../../var/pass/secret.tata \ - --host2 $HOST2 --user2 titi \ - --passfile2 ../../var/pass/secret.titi \ - --justfoldersizes --folder INBOX --regextrans2 's,^INBOX$,iNbOx,' -} - -ll_justfoldersizes_case_different_2() +ll_justfoldersizes_case_different_2() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -1711,7 +1836,8 @@ ll_justfoldersizes_noexist() --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --justfoldersizes --folder NoExist --folder INBOX + --justfoldersizes --folder NoExist --folder AnotherNoExist \ + --nocheckfoldersexist --errorsmax 2 } @@ -2004,18 +2130,37 @@ ll_search_ALL() --search 'ALL' --folder INBOX } -ll_search_UID() +ll_search1_NOT_OR_OR_UID() { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ - --search1 'NOT OR OR UID 20000 UID 20002 UID 20004' --usecache --folder INBOX - - #--search1 'OR OR UID 20000 UID 20002 UID 20004' --usecache --folder INBOX + --search1 'NOT OR OR UID 20000 UID 20002 UID 20004' --folder INBOX } +ll_search1_OR_OR_UID() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --search1 'OR OR UID 20000 UID 20002 UID 20004' --folder INBOX +} + +ll_search2_NOT_OR_OR_UID() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --search2 'NOT OR OR UID 20000 UID 20002 UID 20004' --folder INBOX +} + + ll_search_FLAGGED() { can_send && sendtestmessage @@ -2665,6 +2810,35 @@ ll_regextrans2_archive_per_month() } +ll_regextrans2_archive_per_year_flat_hard_year() +{ + year= + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --nofoldersizes \ + --search "SENTSINCE 1-1-$year SENTBEFORE 30-12-2018" \ + --sep2 _ --regextrans2 's{(.*)}{Archive_$1_2018}' --justfolders --dry +} + +ll_regextrans2_archive_per_year_flat_variable_year() +{ + year=2018 + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --nofoldersizes \ + --search "SENTSINCE 1-1-$year SENTBEFORE 30-12-$year" \ + --sep2 _ --regextrans2 's{(.*)}{Archive_$1_'"$year}" --justfolders --dry +} + + + + ll_regextrans2_ALLIN() { @@ -2906,7 +3080,7 @@ ll_regexmess_trailing_NUL() } -ll_regexmess_add_header() +ll_regexmess_add_header() { if at_home; then rm -f /home/vmail/titi/.yop.yap/cur/* @@ -2918,16 +3092,63 @@ ll_regexmess_add_header() --passfile2 ../../var/pass/secret.titi \ --folder INBOX.yop.yap \ --regexmess 's/\A/X-migrated-from-foo: 20100617\n/' \ - --search 'SUBJECT add_some_header_please' \ - --debugcontent --dry + --search 'SUBJECT add_some_header_please' \ + --debugcontent - if at_home; then - file=`ls -t /home/vmail/titi/.yop.yap/cur/* | tail -1` - diff ../../var/imapsync/tests/ll_regexmess/dest_03_add_some_header $file || return 1 + if at_home; then + file=`ls -t /home/vmail/titi/.yop.yap/cur/* | tail -1` + diff W/t/07_ll_regexmess_add_header.txt $file || return 1 echo 'sudo rm -fv /home/vmail/titi/.yop.yap/cur/*' - fi + fi } + +ll_regexmess_add_header_path() +{ + if at_home; then + rm -fv "/home/vmail/titi/.yop.blanc blanc/cur/"* + fi + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --folder "INBOX.yop.blanc blanc" \ + --regexmess 's/\A/X-ImapSync-OriginalPath-$sync->{user1}: $sync->{ h1_current_folder }\n/' \ + --search 'SUBJECT add_some_header_please' \ + --debugcontent + + if at_home; then + file=`ls -t "/home/vmail/titi/.yop.blanc blanc/cur/"* | tail -1` + diff W/t/08_ll_regexmess_add_header_path.txt "$file" || return 1 + echo 'sudo rm -fv "/home/vmail/titi/.yop.blanc blanc/cur/"*' + fi + +} + +ll_regexmess_add_header_path_verif() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 titi \ + --passfile1 ../../var/pass/secret.titi \ + --host2 $HOST2 --user2 tata \ + --passfile2 ../../var/pass/secret.tata \ + --folder "INBOX.yop.blanc blanc" \ + --search1 'HEADER X-ImapSync-OriginalPath-tata ""' \ + --debugcontent --dry --useuid --debugimap1 + + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 titi \ + --passfile1 ../../var/pass/secret.titi \ + --host2 $HOST2 --user2 tata \ + --passfile2 ../../var/pass/secret.tata \ + --folder "INBOX.yop.blanc blanc" \ + --search1 'HEADER X-ImapSync-OriginalPath-tata "INBOX.yop.blanc blanc"' \ + --debugcontent --dry --useuid --debugimap1 + +} + + ll_regexmess_change_header() { # @@ -3156,6 +3377,19 @@ ll_regex_flag() echo 'rm -f /home/vmail/titi/.yop.yap/cur/*' } +ll_regex_flag_remove() +{ + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi \ + --folder INBOX.yop.yap \ + --regexflag 's/\\Indexed//gi' --debugflags + + echo 'rm -f /home/vmail/titi/.yop.yap/cur/*' +} + ll_regex_flag_bad() { ! $CMD_PERL ./imapsync \ @@ -3547,6 +3781,17 @@ ll_authmech_XOAUTH2_json_gmail() { } ll_authmech_xoauth2_json_gmail() { ll_authmech_XOAUTH2_json_gmail; } +ll_authmech_XOAUTH2_json_gmail_app() { + ! ping -c1 imap.gmail.com || { $CMD_PERL ./imapsync \ + --host1 imap.gmail.com --ssl1 --user1 gilles.lamiral@gmail.com \ + --password1 ../../var/pass/secret.xoauth2.json \ + --host2 imap.gmail.com --ssl2 --user2 gilles.lamiral@gmail.com \ + --password2 ../../var/pass/secret.xoauth2.json \ + --justlogin \ + --authmech1 XOAUTH2 --authmech2 XOAUTH2 --debugimap ; } +} +ll_authmech_xoauth2_json_gmail_app() { ll_authmech_XOAUTH2_json_gmail_app; } + ll_authmech_XOAUTH2_gmail_proxy() { @@ -3894,36 +4139,36 @@ ll_delete2_dev() { ll_maxmessagespersecond() { - ll_delete1_reverse + ll_delete1_reverse $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ --folder INBOX \ - --maxmessagespersecond 3.3 + --maxmessagespersecond 3.3 } ll_maxbytespersecond() { - ll_delete1_reverse + ll_delete1_reverse $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ --folder INBOX \ - --maxbytespersecond 2000 --nofoldersizes + --maxbytespersecond 2000 --nofoldersizes } ll_maxbytesafter() { - ll_delete1_reverse + ll_delete1_reverse $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi \ --folder INBOX \ - --maxbytespersecond 1000 --maxbytesafter 20000 --nofoldersizes + --maxbytespersecond 1000 --maxbytesafter 20000 --nofoldersizes } @@ -3948,6 +4193,7 @@ ll_bigmail_fastio() { echo 'sudo sh -c "rm -v /home/vmail/big2/.bigmail/cur/*"' } +# In mandatory_tests memory_stress() { free $CMD_PERL ./imapsync --testsunit tests_memory_stress && free @@ -5036,7 +5282,7 @@ ll_usecache_bracket() { } - +# In mandatory_tests ll_nousecache() { if can_send; then sendtestmessage @@ -5139,6 +5385,7 @@ ll_useuid_INBOX() } +# In mandatory_tests ll_useuid() { $CMD_PERL ./imapsync \ @@ -5161,7 +5408,7 @@ ll_useuid_all() --delete2 --useuid --nofoldersizes } - +# In mandatory_tests ll_useuid_nousecache() { $CMD_PERL ./imapsync \ @@ -5524,10 +5771,12 @@ l_exchange_maxline() --minmaxlinelength 10000 --maxlinelength 11000 --debugmaxlinelength } +# In mandatory_tests fuzz_basic() { zzuf -E '^' $CMD_PERL ./imapsync } +# In mandatory_tests fuzz_network() { zzuf -E '^' -d -n $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ @@ -5583,7 +5832,24 @@ xgenplus_few() { } +firstclass() { + $CMD_PERL ./imapsync \ + --host1 mail.una.ab.ca \ + --user1 glamiral --passfile1 ../../var/pass/secret.firstclass \ + --host2 mail.una.ab.ca \ + --user2 glamiral --passfile2 ../../var/pass/secret.firstclass \ + --dry --useuid --debugcontent +} +firstclass_fullfill() { + $CMD_PERL ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 mail.una.ab.ca \ + --user2 glamiral --passfile2 ../../var/pass/secret.firstclass \ + --debugcontent \ + --folder INBOX.few_emails --f1f2 'INBOX.few_emails=INBOX' +} @@ -5818,14 +6084,25 @@ nytprof_bigmail() ll_nytprof() { date1=`date` - { $CMD_PERL -d:NYTProf ./imapsync \ + # one time without NYTProf + { $CMD_PERL ./imapsync \ --host1 $HOST1 --user1 tata \ --passfile1 ../../var/pass/secret.tata \ --host2 $HOST2 --user2 titi \ --passfile2 ../../var/pass/secret.titi } date2=`date` - echo3 "[$date1] [$date2]" + # then one time with NYTProf + { $CMD_PERL -d:NYTProf ./imapsync \ + --host1 $HOST1 --user1 tata \ + --passfile1 ../../var/pass/secret.tata \ + --host2 $HOST2 --user2 titi \ + --passfile2 ../../var/pass/secret.titi + } + date3=`date` + echo3 "begin: [$date1]" + echo3 "first: [$date2]" + echo3 "end: [$date3]" } @@ -5876,7 +6153,7 @@ ll pidfile_well_removed pidfile_bad ll_pidfilelocking -tail +test_tail justbanner nomodules_version xxxxx_gmail @@ -5916,7 +6193,8 @@ ll_idatefromheader ll_folder_rev ll_subscribed ll_nosubscribe -ll_justfoldersizes +ll_justfoldersizes +ll_justfoldersizes_noexist ll_authmd5 ll_authmd51 ll_authmd52 @@ -5937,6 +6215,8 @@ ll_useheader ll_useheader_noheader ll_regexmess ll_regexmess_bad_regex +ll_regexmess_add_header +ll_regexmess_add_header_path ll_regexmess_scwchu ll_skipmess ll_skipmess_8bits @@ -5956,8 +6236,6 @@ ll_regex_flag_keep_only ll_justconnect ll_justconnect_ipv6 ll_justconnect_ipv6_nossl -ks_justconnect_ipv6 -ks_justconnect_ipv6_nossl ll_justhost1 ll_justhost2 ll_justlogin @@ -5988,8 +6266,8 @@ ll_usecache_noheader ll_usecache_debugcache ll_nousecache ll_delete2foldersonly_NEW_3 -ll_delete2foldersonly -ll_delete2foldersonly_tmp +ll_delete2foldersonly_dry +ll_delete2foldersonly_subfolder2 ll_delete2foldersbutnot ll_folder_create ll_folder_create_INBOX_Inbox @@ -6003,16 +6281,21 @@ ll_domino2 fuzz_basic fuzz_network testslive -testslive6 -ll_abort_nopidfile +ll_abort_pidfile_no_exist ll_abort_noprocess ll_abort -ll_abort_cgi_context +ll_abort_cgi_context_tail +ll_abort_no_pidfile_option ll_sigreconnect_INT ksks_reset_test1 memory_stress ' +# 2019_12 Removed +# ks_justconnect_ipv6_nossl testslive6 +# ks_justconnect_ipv6 + + other_tests=' archiveopteryx_1 msw diff --git a/webserver b/webserver new file mode 100755 index 0000000..6cf0333 --- /dev/null +++ b/webserver @@ -0,0 +1,156 @@ +#!/usr/bin/perl + +# $Id: webserver,v 1.6 2019/11/28 14:45:09 gilles Exp gilles $ +package Imapsync; + +use base qw(Net::Server::HTTP); +use strict ; +use warnings ; +use Data::Dumper ; +use English qw( -no_match_vars ) ; + +my $server = Imapsync->new( + 'port' => [8080], + 'access_log_file' => 'STDERR', + 'log_level' => 4, + 'timeout_header' => 20, + 'timeout_idle' => 60, +) ; + + +$server->run() ; + + + + +sub default_server_type { 'Fork' } + +sub post_configure_hook +{ + my $self = shift ; + $self->log( 2, Data::Dumper->Dump( [ $self ], ['self'] ) ) ; +} + +sub output_file +{ + my ( $self, $file, $type ) = @_ ; + + $type ||= 'text/plain' ; + + my $string = file_to_string( $file ) ; + + my $output ; + my( $status, $msg, $body ) ; + if ( defined $string ) + { + $output = "Content-type: $type\r\n\r\n" ; + $output .= $string ; + # body can not be sent by send_status() because then it + # sets Content-type to text/html + $self->send_status( '200', 'OK' ) ; + print $output ; + + } + else + { + $self->send_status( '404', 'Not found', "File not found: $file " ) ; + } + + return ; +} + + +sub process_path_info +{ + my $self = shift ; + my $path_info = shift ; + + my $sitemap = + { + '/imapsync_form_extra.html' => sub { + output_file( $self, './X/imapsync_form_extra.html', 'text/html' ) + }, + '/imapsync_form.html' => sub { + output_file( $self, './X/imapsync_form.html', 'text/html' ) + }, + '/imapsync_form.css' => sub { + output_file( $self, './X/imapsync_form.css', 'text/css' ) + }, + '/imapsync_form.js' => sub { + output_file( $self, './X/imapsync_form.js', 'text/javascript' ) + }, + '/imapsync_form_new.js' => sub { + output_file( $self, './X/imapsync_form_new.js', 'text/javascript' ) + }, + '/' => sub { + output_file( $self, './X/imapsync_form_extra.html', 'text/html' ) + }, + '/vnstat/vnstati.html' => sub { + output_file( $self, './X/vnstati.html', 'text/html' ) + }, + + } ; + + if ( defined $sitemap->{ $path_info } ) + { + $sitemap->{ $path_info }->() ; + } + else + { + $self->send_status( '404', 'Not found', "Error: $path_info not found!\n" ) ; + } + return ; + +} + +sub process_http_request +{ + my $self = shift ; + + $self->log( 2, "In process_http_request PID $$\n" ) ; + local $Data::Dumper::Sortkeys = 1; + #$self->log( 2, Data::Dumper->Dump( [ $self ], ['self'] ) ) ; + + $ENV{'SERVER_SOFTWARE'} = $PROGRAM_NAME ; + + if ( '/cgi-bin/imapsync' eq $ENV{'PATH_INFO'} ) { + #$self->exec_trusted_perl( './imapsync' ) ; + $self->exec_cgi( './imapsync' ) ; + #$self->exec_cgi( './imapsync_bin_Linux_i686' ) ; + + #$self->exec_trusted_perl( '.\imapsync.pl' ); + $self->log( 2, "In process_http_request PID $$ after exec_trusted_perl\n" ) ; + #return ; + #return $self->exec_cgi( 'C:\Strawberry\perl\bin\perl.exe .\imapsync.pl' ); + #return $self->exec_cgi( 'imapsyncbat' ); + #return $self->exec_trusted_perl( 'imapsyncbat' ); + + } + else + { + process_path_info( $self, $ENV{'PATH_INFO'} ) ; + } + #$self->log( 4, Data::Dumper->Dump( [ $self ], ['self'] ) ) ; + $self->log( 2, "End of process_http_request PID $$\n" ) ; +} + +sub file_to_string +{ + my $file = shift ; + if ( ! $file ) { warn "Error, no file given\n" ; return ; } + if ( ! -e $file ) { warn "Error, $file does not exist\n" ; return ; } + if ( ! -f $file ) { warn "Error, $file is not a file\n" ; return ; } + if ( ! -r $file ) { warn "Error, $file is not readable\n" ; return ; } + my @string ; + if ( open my $FILE, '<', $file ) { + @string = <$FILE> ; + close $FILE ; + my $string = join q{}, @string ; + return $string ; + }else{ + warn "Error reading file $file : $OS_ERROR\n" ; + return ; + } +} + +