source: companies/celepar/doc-expressolivre/debian/arqs-conf/home/expressolivre/imapsync @ 763

Revision 763, 41.1 KB checked in by niltonneto, 15 years ago (diff)

Importação inicial do Expresso da Celepar

Line 
1#!/usr/bin/perl -w
2
3=head1 NAME
4
5imapsync - IMAP synchronization, copy or migration
6tool. Synchronize mailboxes between two imap servers. Good
7at IMAP migration.
8
9$Revision: 1.156 $
10
11=head1 INSTALL
12
13 imapsync works fine under any Unix OS.
14 imapsync works fine under Windows 2000 (at least) and ActiveState's 5.8 Perl
15
16 Get imapsync at
17 http://www.linux-france.org/prj/imapsync/dist/
18
19 You'll find a compressed tarball called imapsync-x.xx.tgz
20 where x.xx is the version number. Untar the tarball where
21 you want :
22
23 tar xzvf  imapsync-x.xx.tgz
24
25 Go into the directory imapsync-x.xx and read the INSTALL
26 file.
27
28 The freshmeat record is http://freshmeat.net/projects/imapsync/
29
30=head1 SYNOPSIS
31
32  imapsync [options]
33
34  imapsync --help
35  imapsync
36
37  imapsync [--host1 server1]  [--port1 <num>]
38           [--user1 <string>] [--passfile1 <string>]
39           [--host2 server2]  [--port2 <num>]
40           [--user2 <string>] [--passfile2 <string>]
41           [--noauthmd5]
42           [--folder <string> --folder <string> ...]
43           [--include <regex>] [--exclude <regex>]
44           [--prefix2 <string>] [--prefix1 <string>]
45           [--regextrans2 <regex> --regextrans2 <regex> ...]
46           [--sep1 <char>]
47           [--sep2 <char>]
48           [--justfolders] [--justfoldersizes] [--justconnect]
49           [--syncinternaldates]
50           [--buffersize  <int>]
51           [--syncacls]
52           [--regexmess <regex>] [--regexmess <regex>]
53           [--maxsize <int>]
54           [--maxage <int>]
55           [--minage <int>]
56           [--skipheader <regex>]
57           [--useheader <string>] [--useheader <string>]
58           [--skipsize]
59           [--delete] [--expunge] [--expunge1] [--expunge2]
60           [--subscribed] [--subscribe]
61           [--nofoldersizes]
62           [--dry]
63           [--debug] [--debugimap]
64           [--timeout <int>] [--fast]
65           [--version] [--help]
66 
67=cut
68# comment
69=pod
70
71=head1 DESCRIPTION
72
73The command imapsync is a tool allowing incremental and recursive
74imap transfer from one mailbox to another.
75
76We sometimes need to transfer mailboxes from one imap server to
77another. This is called migration.
78
79imapsync is the adequate tool because it reduces the amount
80of data transfered by not transfering a given message if it
81is already on both sides. Same headers, same message size
82and the transfert is done only once. All flags are
83preserved, unread will stay unread, read will stay read,
84deleted will stay deleted. You can stop the transfert at any
85time and restart it later, imapsync is adapted to a bad
86connection.
87
88You can decide to delete the messages from the source mailbox
89after a successful transfert (it is a good feature when migrating).
90In that case, use the --delete --expunge1 options.
91
92You can also just synchronize a mailbox A from another mailbox B
93in case you just want to keep a "live" copy of B in A.
94
95=head1 OPTIONS
96
97Invoke: imapsync --help
98
99=head1 HISTORY
100
101I wrote imapsync because an enterprise (basystemes) paid me to install
102a new imap server without loosing huge old mailboxes located on a far
103away remote imap server accessible by a low bandwith link. The tool
104imapcp (written in python) could not help me because I had to verify
105every mailbox was well transfered and delete it after a good
106transfert. imapsync started its life being a copy_folder.pl patch.
107The tool copy_folder.pl comes from the Mail-IMAPClient-2.1.3 perl
108module tarball source (in the examples/ directory of the tarball).
109
110=head1 EXAMPLE
111
112While working on imapsync parameters please run imapsync in
113dry mode (no modification induced) with the --dry
114option. Nothing bad can be done this way.
115
116To synchronize the imap account "buddy" on host
117"imap.src.fr" to the imap account "max" on host
118"imap.dest.fr" (the passwords are located in too files
119"/etc/secret1" for "buddy", "/etc/secret2" for "max") :
120
121 imapsync --host1 imap.src.fr  --user1 buddy --passfile1 /etc/secret1 \
122          --host2 imap.dest.fr --user2 max   --passfile2 /etc/secret2
123
124Then, you will have buddy's mailbox updated from max's
125mailbox.
126
127=head1 SECURITY
128
129You can use --password1 instead of --passfile1 to give the
130password but it is dangerous because any user on your host
131can see the password by using the 'ps auxwwww'
132command. Using a variable (like $PASSWORD1) is also
133dangerous because of the 'ps auxwwwwe' command. So, saving
134the password in a well protected file (600 or rw-------) is
135the best solution.
136
137imasync is not protected against sniffers on the network so
138the passwords are in plain text.
139
140=head1 EXIT STATUS
141
142imapsync will exit with a 0 status (return code) if everything went good.
143Otherwise, it exits with a non-zero status.
144
145So if you have a buggy internet connection, you can use this loop
146in a Bourne shell:
147
148        while ! imapsync ...; do
149              echo imapsync not complete
150        done
151
152=head1 AUTHOR
153
154Gilles LAMIRAL <lamiral@linux-france.org>
155
156Feedback good or bad is always welcome. The first you send
157me an email you will receive a confirmation request before I
158really read your message.
159
160The newsgroup comp.mail.imap is a good place to talk about
161imapsync. I read it when imapsync is concerned.
162
163=head1 LICENSE
164
165imapsync is free, gratis and open source software cover by
166the GNU General Public License. See the GPL file included in
167the distribution or the web site
168http://www.gnu.org/licenses/licenses.html
169
170=head1 BUGS
171
172No known serious bug.
173
174Multiple copies: Multiple copies of the emails on the
175destination server. Some IMAP servers (Domino for example)
176add some headers for each message transfered. The message is
177transfered again and again each time you run imapsync. This
178is bad of course. The explanation is that imapsync considers
179the message is not the same since headers have changed (one
180line added) and size too (the header part). You can look at
181the headers found by imapsync by using the --debug option
182(and search for the message on both part). The way to avoid
183this problem is by using options --skipheader and
184--skipsize, like this (avoid headers beginning whith X-):
185
186 imapsync ... --skipheader '^X-' --skipsize
187
188You can use --skipheader only one time; if you need to skip
189several different headers use the "or" perl regex caracter
190which is "|". Example:
191
192 imapsync ... --skipheader '^X-|^Status|^Bcc'
193
194Flags : with some IMAP servers the flags are not very well
195copied the first time.  Run imapsync twice if you want the
196flags set correctly.  (fixed since 1.28 release but wait for
197a time before removing those lines)
198
199Report any bugs to the author.
200
201=head1 IMAP SERVERS
202
203Failure stories reported with the following imap servers :
204
205 - MailEnable 1.54 (Proprietary) http://www.mailenable.com/
206 - DBMail 2.0.7 (GPL). But DBMail 1.2.1 works.
207   Patient and confident testers are welcome.
208
209Success stories reported with the following imap servers
210(softwares names are in alphabetic order) :
211
212 - BincImap 1.2.3 (GPL) (http://www.bincimap.org/)
213 - CommunicatePro server (Redhat 8.0)
214 - Courier IMAP 1.5.1, 2.2.0, 2.1.1, 2.2.1 (GPL)
215   (http://www.courier-mta.org/)
216 - Critical Path (7.0.020)
217 - Cyrus IMAP 1.5, 1.6, 2.1, 2.1.15, 2.1.16, 2.1.18
218   2.2.1, 2.2.2-BETA, 2.2.10, 2.2.12, 2.3-alpha (OSI Approved)
219   (http://asg.web.cmu.edu/cyrus/)
220 - DBMail 1.2.1 (GPL) (http://www.dbmail.org/). 2.0.7 seems buggy.
221 - Dovecot 0.99.10.4  0.99.14 (LGPL) (http://www.dovecot.org/)
222 - Domino (Notes) 6.5, 5.0.6, 5.0.7
223 - Groupwise IMAP (Novell). Buggy so see the FAQ.
224 - iPlanet Messaging server 4.15, 5.1
225 - IMail 7.15 (Ipswitch/Win2003), 8.12
226 - MDaemon 7.0.1, 8.1
227 - MS Exchange Server 5.5
228 - Netscape Mail Server 3.6 (Wintel !)
229 - Netscape Messaging Server 4.15 Patch 7
230 - OpenWave
231 - Qualcomm Worldmail (NT)
232 - Samsung Contact IMAP server 8.5.0
233 - SunONE Messaging server 5.2, 6.0 (SUN JES - Java Enterprise System)
234 - UW-imap servers (imap-2000b) rijkkramer IMAP4rev1 2000.287
235   (RedHat uses UW like 2003.338rh) (OSI Approved)
236   (http://www.washington.edu/imap/)
237 - UW - QMail v2.1
238
239Please report to the author any success or bad story with
240imapsync and don't forget to mention the IMAP server
241software names and version on both sides. This will help
242future users. To help the author maintaining this section
243report the two lines at the begining of the output if they
244are useful to know the softwares. Example:
245
246 From software :* OK louloutte Cyrus IMAP4 v1.5.19 server ready
247 To   software :* OK Courier-IMAP ready
248
249You can use option --justconnect to get those lines.
250Example :
251
252  imapsync --host1 imap.troc.org --host2 imap.trac.org --justconnect
253
254And please rate imapsync at http://freshmeat.net/projects/imapsync/
255
256=head1 HUGE MIGRATION
257
258
259Have a special attention on options
260--subscribed
261--subscribe
262--delete
263--expunge
264--expunge1
265--expunge2
266--maxage
267--minage
268--maxsize
269--useheader
270
271If you have many mailboxes to migrate think about a little
272shell program. Write a file called file.csv (for example)
273containing users and passwords.
274The separator used in this example is ';'
275
276The file.csv file content is :
277
278user0001;password0001;user0002;password0002
279user0011;password0011;user0012;password0012
280...
281
282And the shell program is just :
283
284 { while IFS=';' read  u1 p1 u2 p2; do
285        imapsync --user1 "$u1" --password1 "$p1" --user2 "$u2" --password2 "$p2" ...
286 done ; } < file.csv
287
288Welcome in shell programming !
289
290=head1 Hacking
291
292Feel free to hack imapsync as the GPL Licence permits it.
293
294=head1 Links
295
296Entries for imapsync:
297  http://www.imap.org/products/showall.php
298
299
300=head1 SIMILAR SOFTWARES
301
302  imap_tools  : http://www.athensfbc.com/imap_tools
303  offlineimap : http://gopher.quux.org:70/devel/offlineimap/
304  mailsync    : http://mailsync.sourceforge.net/
305  imapxfer    : http://www.washington.edu/imap/
306                part of the imap-utils from UW.
307  mailutil    : replace imapxfer in
308                part of the imap-utils from UW.
309                http://www.gsp.com/cgi-bin/man.cgi?topic=mailutil
310  imaprepl    : http://www.bl0rg.net/software/
311                http://freshmeat.net/projects/imap-repl/
312  imap_migrate: http://freshmeat.net/projects/imapmigration/
313  imapcopy    : http://home.arcor.de/armin.diehl/imapcopy/imapcopy.html
314  migrationtool http://sourceforge.net/projects/migrationtool/
315  pop2imap    : http://www.linux-france.org/prj/pop2imap/
316
317Feedback (good or bad) will be always welcome.
318
319=head1 AUTHOR
320
321Gilles LAMIRAL earn his living writing, installing,
322configuring and teaching free open and gratis
323softwares. Don't hesitate to pay him for that services.
324
325$Id: imapsync,v 1.156 2006/03/02 03:14:12 gilles Exp gilles $
326
327=cut
328
329
330++$|;
331use strict;
332use Getopt::Long;
333use Mail::IMAPClient;
334use Digest::MD5  qw(md5_base64);
335use Term::ReadKey;
336#use Digest::HMAC_MD5;
337
338eval { require 'usr/include/sysexits.ph' };
339
340
341my(
342        $rcs, $debug, $debugimap, $error,
343        $host1, $host2, $port1, $port2,
344        $user1, $user2, $password1, $password2, $passfile1, $passfile2,
345        @folder, $include, $exclude,
346        $prefix1, $prefix2,
347        @regextrans2, @regexmess,
348        $sep1, $sep2,
349        $syncinternaldates, $syncacls,
350        $fastio1, $fastio2,
351        $maxsize, $maxage, $minage,
352        $skipheader, @useheader,
353        $skipsize, $foldersizes, $buffersize,
354        $delete, $expunge, $expunge1, $expunge2, $dry,
355        $justfoldersizes,
356        $authmd5,
357        $subscribed, $subscribe,
358        $version, $VERSION, $help,
359        $justconnect, $justfolders,
360        $fast,
361        $mess_size_total_trans,
362        $mess_size_total_skipped,
363        $mess_size_total_error,
364        $mess_trans, $mess_skipped,
365        $timeout,   # whr (ESS/PRW)
366        $timestart, $timeend, $timediff,
367        $timesize, $timebefore,
368);
369
370use vars qw ($opt_G); # missing code for this will be option.
371
372
373$rcs = ' $Id: imapsync,v 1.156 2006/03/02 03:14:12 gilles Exp gilles $ ';
374$rcs =~ m/,v (\d+\.\d+)/;
375$VERSION = ($1) ? $1 : "UNKNOWN";
376
377my $VERSION_IMAPClient = $Mail::IMAPClient::VERSION;
378
379check_lib_version() or
380  die "Upgrade perl lib Mail::IMAPClient to release 2.2.9 at least\n";
381
382
383$mess_size_total_trans   = 0;
384$mess_size_total_skipped = 0;
385$mess_size_total_error   = 0;
386$mess_trans = $mess_skipped = 0;
387
388
389sub check_lib_version {
390        # I know this is ugly, I should write a sort function
391        if ($VERSION_IMAPClient =~ m/(\d+)\.(\d+)\.(\d+)/) {
392                $debug and print "VERSION_IMAPClient $1 $2 $3\n";
393                my($major,$minor,$sub) = ($1, $2, $3);
394               
395                return(1) if($major >=3);
396                return(0) if($major <=1);
397                return(1) if($minor >=3);
398                return(0) if($minor <=1);
399                return(1) if($sub >=8);
400                return(0) if($sub <=7);
401        }else{
402                return 0; # don't match regex => bad
403        }
404}
405
406$error=0;
407
408my $banner = join("",
409                  '$RCSfile: imapsync,v $ ',
410                  '$Revision: 1.156 $ ',
411                  '$Date: 2006/03/02 03:14:12 $ ',
412                  "\n",
413                  "Mail::IMAPClient version used here is ",
414                  $VERSION_IMAPClient,"\n"
415                 );
416
417unless(defined(&_SYSEXITS_H)) {
418        # 64 on my linux box.
419        eval 'sub EX_USAGE () {64;}' unless defined(&EX_USAGE);
420}
421
422get_options();
423print $banner;
424
425sub missing_option {
426        my ($option) = @_;
427        die "$option option must be used, run $0 --help for help\n";
428}
429
430$host1 || missing_option("--host1") ;
431$port1 = (defined($port1)) ? $port1 : 143;
432
433$host2 || missing_option("--host2") ;
434$port2 = (defined($port2)) ? $port2 : 143;
435
436sub connect_imap {
437        my($host, $port, $debugimap) = @_;
438        my $imap = Mail::IMAPClient->new();
439        $imap->Server($host);
440        $imap->Port($port);
441        $imap->Debug($debugimap);
442        $imap->connect()
443          or die "Can not open imap connection on [$host] : $@\n";     
444}
445
446if ($justconnect) {
447        my $from = ();
448        my $to = ();
449       
450        $from = connect_imap($host1, $port1);
451        print "From software : ", ($from->Report())[0];
452        print "From capability : ", join(" ", $from->capability()), "\n";
453        $to   = connect_imap($host2, $port2);
454        print "To   software : ", ($to->Report())[0];
455        print "To   capability : ", join(" ", $to->capability()), "\n";
456        $from->logout();
457        $to->logout();
458        exit(0);
459}
460       
461
462$user1 || missing_option("--user1");
463$user2 || missing_option("--user2");
464
465$authmd5 = (defined($authmd5)) ? $authmd5 : 1;
466
467$syncacls = (defined($syncacls)) ? $syncacls : 0;
468$foldersizes = (defined($foldersizes)) ? $foldersizes : 1;
469
470$fastio1 = (defined($fastio1)) ? $fastio1 : 1;
471$fastio2 = (defined($fastio2)) ? $fastio2 : 1;
472
473
474@useheader = ("ALL") unless (@useheader);
475
476print "From imap server [$host1] port [$port1] user [$user1]\n";
477print "To   imap server [$host2] port [$port2] user [$user2]\n";
478
479$password1 || $passfile1 || do {
480        print "What's the password for $user1\@$host1? ";
481        ReadMode 2;
482        $password1 = <>; chop $password1;
483        printf "\n"; ReadMode 0;
484};
485
486$password1 = (defined($passfile1)) ? firstline ($passfile1) : $password1;
487
488$password2 || $passfile2 || do {
489        print "What's the password for $user2\@$host2? ";
490        ReadMode 2;
491        $password2 = <>; chop $password2;
492        printf "\n"; ReadMode 0;
493};
494$password2 = (defined($passfile2)) ? firstline ($passfile2) : $password2;
495
496my $from = ();
497my $to = ();
498
499my $authmech = "CRAM-MD5";
500
501
502$timestart = time();
503$timebefore = $timestart;
504
505$fastio1 = 1;
506$fastio2 = 1;
507
508$debugimap and print "From connection\n";
509$from = login_imap($host1, $port1, $user1, $password1, $debugimap, $timeout, $fastio1);
510
511$debugimap and print "To  connection\n";
512$to = login_imap($host2, $port2, $user2, $password2, $debugimap, $timeout, $fastio2);
513
514# No history
515$from->Clear(2);
516$to->Clear(2);
517
518$debug and print "From Buffer I/O : ", $from->Buffer(), "\n";
519$debug and print "To   Buffer I/O : ", $to->Buffer(), "\n";
520
521       
522
523sub login_imap {
524        my($host, $port, $user, $password,
525           $debugimap, $timeout, $fastio) = @_;
526        my $imap = Mail::IMAPClient->new();
527        $imap->Server($host);
528        $imap->Port($port);
529        $imap->Fast_io($fastio);
530        $imap->Buffer($buffersize || 4096);
531        $imap->Uid(1);
532        $imap->Peek(1);
533        $imap->Debug($debugimap);
534        $imap->connect()
535          or die "Can not open imap connection on [$host] with user [$user] : $@\n";
536        if ($timeout)    # whr (ESS/PRW)
537          {
538                  $imap->Timeout($timeout);
539                  print "Setting imap timeout to $timeout\n";
540          }
541
542        $imap->User($user);
543        $imap->Password($password);
544        md5auth($imap);
545        $imap->login() or die "Error login : [$host] with user [$user] : $@";
546        return($imap);
547}
548
549
550sub md5auth() {
551        my ($imap) = @_;
552       
553        unless ($authmd5) {
554                print "$authmech not wanted by you\n";
555                return;
556        }
557        if ($imap->has_capability($authmech)
558            or $imap->has_capability("AUTH=$authmech")) {
559                print "Server [", $imap->Server,
560                  "] has capability $authmech\n";
561        }else{
562                print "Server [", $imap->Server,
563                  "] has NOT capability $authmech\n";
564                return;
565        }
566        #print "EE", $imap->Authmechanism(), "\n";
567        if ($imap->Authmechanism($authmech)) {
568                print "Using $authmech authentification\n";
569        }else{
570                $imap->Authmechanism(undef);
571                print "Can NOT use $authmech authentification, using plain\n";
572        }
573        return;
574}
575
576
577
578
579print "From software : ", ($from->Report())[0];
580print "To   software : ", ($to->Report())[0];
581
582print "From capability : ", join(" ", $from->capability()), "\n";
583print "To   capability : ", join(" ", $to->capability()), "\n";
584
585die unless $from->IsAuthenticated();
586die unless   $to->IsAuthenticated();
587
588my (@f_folders, @t_folders, %fs_folders);
589
590# Make a hash of subscribed folders in source server.
591map { $fs_folders{$_}=1 } $from->subscribed();
592
593
594
595
596if (scalar(@folder)) {
597        # folders given by option --folder
598        @f_folders = @folder;
599}elsif ($subscribed) {
600        # option --subscribed
601        @f_folders = sort keys (%fs_folders);
602}else {
603        # no option, all folders
604        @f_folders = sort $from->folders();
605        # consider (optional) includes and excludes
606        if ($include) {
607                @f_folders = grep  /$include/,@f_folders;
608                print "Only including folders matching pattern '$include'\n";
609        }
610        if ($exclude) {
611                @f_folders = grep !/$exclude/,@f_folders;
612                print "Excluding folders matching pattern '$exclude'\n";
613        }
614}
615
616@t_folders = sort @{$to->folders()};
617
618my($f_sep,$t_sep);
619# what are the private folders separators for each server ?
620
621
622$debug and print "Getting separators\n";
623$f_sep = get_separator($from, $sep1, "--sep1");
624$t_sep = get_separator($to, $sep2, "--sep2");
625
626#my $f_namespace = $from->namespace();
627#my $t_namespace = $to->namespace();
628#$debug and print "From namespace:\n", Data::Dumper->Dump([$f_namespace]);
629#$debug and print "To   namespace:\n", Data::Dumper->Dump([$t_namespace]);
630
631my($f_prefix,$t_prefix);
632$f_prefix = get_prefix($from, $prefix1, "--prefix1");
633$t_prefix = get_prefix($to, $prefix2, "--prefix2");
634
635sub get_prefix {
636        my($imap, $prefix_in, $prefix_opt) = @_;
637        my($prefix_out);
638       
639        $debug and print "Getting prefix namespace\n";
640        if (defined($prefix_in)) {
641                print "Using [$prefix_in] given by $prefix_opt\n";
642                $prefix_out = $prefix_in;
643                return($prefix_out);
644        }
645        $debug and print "Calling namespace capability\n";
646        if ($imap->has_capability("namespace")) {
647                my $r_namespace = $imap->namespace();
648                $prefix_out = $r_namespace->[0][0][0];
649                return($prefix_out);
650        }else{
651                print
652                  "No NAMESPACE capability in imap server ",
653                    $imap->Server(),"\n",
654                      "Give the prefix namespace with the $prefix_opt option\n";
655                exit(1);
656        }
657}
658
659
660sub get_separator {
661        my($imap, $sep_in, $sep_opt) = @_;
662        my($sep_out);
663       
664       
665        if ($sep_in) {
666                print "Using [$sep_in] given by $sep_opt\n";
667                $sep_out = $sep_in;
668                return($sep_out);
669        }
670        $debug and print "Calling namespace capability\n";
671        if ($imap->has_capability("namespace")) {
672                $sep_out = $imap->separator();
673                return($sep_out);
674        }else{
675                print
676                  "No NAMESPACE capability in imap server ",
677                    $imap->Server(),"\n",
678                      "Give the separator caracter with the $sep_opt option\n";
679                exit(1);
680        }
681}
682
683
684print "From separator and prefix : [$f_sep][$f_prefix]\n";
685print "To   separator and prefix : [$t_sep][$t_prefix]\n";
686
687
688sub foldersizes {
689
690        my ($side, $imap, $folders_r) = @_;
691        my $tot = 0;
692        my $tmess = 0;
693        my @folders = @{$folders_r};
694        print "++++ Calculating sizes ++++\n";
695        foreach my $folder (@folders)     {
696                my $stot = 0;
697                my $smess = 0;
698                printf("$side Folder %-35s", "[$folder]");
699                unless ($imap->select($folder)) {
700                        warn
701                          "$side Folder $folder : Could not select ",
702                            $imap->LastError,  "\n";
703                        $error++;
704                        next;
705                }
706                if (defined($maxage) or defined($minage)) {
707                        # The pb is fetch_hash() can only be applied on ALL messages
708                        my @msgs = select_msgs($imap);
709                        $smess = scalar(@msgs);
710                        foreach my $m (@msgs) {
711                                my $s = $imap->size($m)
712                                  or warn "Could not find size of message $m: $@\n";
713                                $stot += $s;
714                        }
715                }else{
716                        my $hashref = {};
717                        $smess = $imap->message_count();
718                        unless ($smess == 0) {
719                                #$imap->Ranges(1);
720                                $imap->fetch_hash("RFC822.SIZE",$hashref) or die "$@";
721                                #$imap->Ranges(0);
722                                #print map {$hashref->{$_}->{"RFC822.SIZE"}, " "} keys %$hashref;
723                                map {$stot += $hashref->{$_}->{"RFC822.SIZE"}} keys %$hashref;
724                        }
725                }
726                printf(" Size: %9s", $stot);
727                printf(" Messages: %5s\n", $smess);
728                $tot += $stot;
729                $tmess += $smess;
730        }
731        print "Total size: $tot\n";
732        print "Total messages: $tmess\n";
733        print "Time : ", timenext(), " s\n";
734}
735
736if ($foldersizes) {
737        foldersizes("From", $from, \@f_folders);
738        foldersizes("To  ", $to,   \@t_folders);
739}
740
741
742
743sub timenext {
744        my ($timenow, $timerel);
745        # $timebefore is global, beurk !
746        $timenow    = time;
747        $timerel    = $timenow - $timebefore;
748        $timebefore = $timenow;
749        return($timerel);
750}
751
752exit if ($justfoldersizes);
753
754# needed for setting flags
755my $tohasuidplus = $to->has_capability("UIDPLUS");
756
757
758
759print
760  "From folders : ", map("[$_] ",@f_folders),"\n",
761  "To   folders : ", map("[$_] ",@t_folders),"\n";
762
763print
764  "From subscribed folders : ", map("[$_] ", sort keys(%fs_folders)), "\n";
765
766sub separator_invert {
767        # The separator we hope we'll never encounter
768        my $o_sep="\000";
769
770        my($f_fold, $f_sep, $t_sep) = @_;
771
772        my $t_fold = $f_fold;
773        $t_fold =~ s@\Q$t_sep@$o_sep@g;
774        $t_fold =~ s@\Q$f_sep@$t_sep@g;
775        $t_fold =~ s@\Q$o_sep@$f_sep@g;
776        return($t_fold);
777}
778
779FOLDER: foreach my $f_fold (@f_folders) {
780        my $t_fold;
781        print "From Folder [$f_fold]\n";
782       
783        my $x_fold = $f_fold;
784        # first we remove the prefix
785        $x_fold =~ s/^$f_prefix//;
786        $debug and print "removed source prefix : [$x_fold]\n";
787        $t_fold = separator_invert($x_fold,$f_sep, $t_sep);
788        $debug and print "inverted   separators : [$t_fold]\n";
789        # Adding the prefix supplied by namespace or the --prefix2 option
790        $t_fold = $t_prefix . $t_fold unless($t_fold eq 'INBOX');
791        $debug and print "added   target prefix : [$t_fold]\n";
792
793        # Transforming the folder name by the --regextrans2 option(s)
794        foreach my $regextrans2 (@regextrans2) {
795                $debug and print "eval \$t_fold =~ $regextrans2\n";
796                eval("\$t_fold =~ $regextrans2");
797        }
798
799        print "To   Folder [$t_fold]\n";
800
801        unless ($from->select($f_fold)) {
802                warn
803                "From Folder $f_fold : Could not select ",
804                $from->LastError,  "\n";
805                $error++;
806                next FOLDER;
807        }
808
809        unless ($to->exists($t_fold) or $to->select($t_fold)) {
810                print "To   Folder $t_fold does not exist\n";
811                print "Creating folder [$t_fold]\n";
812                unless ($dry){
813                        unless ($to->create($t_fold)){
814                                warn "Couldn't create [$t_fold]",
815                                $to->LastError,"\n";
816                                $error++;
817                                next FOLDER;
818                        }
819                }else{
820                        next FOLDER;
821                }
822        }
823       
824        if ($syncacls) {
825          my $f_hash = $from->getacl($f_fold)
826            or warn "Could not getacl for $f_fold: $@\n";
827          my $t_hash = $to->getacl($t_fold)
828            or warn "Could not getacl for $t_fold: $@\n";
829          my %users = map({ ($_, 1) } (keys(%$f_hash), keys(%$t_hash)));
830          foreach my $user (sort(keys(%users))) {
831            my $acl = $f_hash->{$user} || "none";
832            print "acl $user : [$acl]\n";
833            next if ($f_hash->{$user} && $t_hash->{$user} &&
834                $f_hash->{$user} eq $t_hash->{$user});
835            unless ($dry) {
836              print "setting acl $t_fold $user $acl\n";
837              $to->setacl($t_fold, $user, $acl)
838                or warn "Could not set acl: $@\n";
839            }
840          }
841        }
842       
843        unless ($to->select($t_fold)) {
844                warn
845                "To   Folder $t_fold : Could not select ",
846                $to->LastError, "\n";
847                $error++;
848                next FOLDER;
849        }
850       
851        if ($expunge){
852                print "Expunging $f_fold and $t_fold\n";
853                unless($dry) { $from->expunge() };
854                #unless($dry) { $to->expunge() };
855        }
856       
857        if ($subscribe and exists $fs_folders{$f_fold}) {
858                print "Subscribing to folder $t_fold on destination server\n";
859                unless($dry) { $to->subscribe($t_fold) };
860        }
861       
862        next FOLDER if ($justfolders);
863
864
865        my @f_msgs = select_msgs($from);
866
867
868
869        $debug and print "LIST FROM : ", scalar(@f_msgs), " messages [@f_msgs]\n";
870        # internal dates on "TO" are after the ones on "FROM"
871        # normally...
872        my @t_msgs = select_msgs($to);
873       
874        $debug and print "LIST TO   : ", scalar(@t_msgs), " messages [@t_msgs]\n";
875
876        my %f_hash = ();
877        my %t_hash = ();
878
879        print "++++ From [$f_fold] Parse 1 ++++\n";
880       
881        my $f_heads = $from->parse_headers($from->Range([@f_msgs]),@useheader)
882          if (@f_msgs) ;
883        $debug and print "Time headers: ", timenext(), " s\n";
884        my $f_size  = $from->fetch_hash("RFC822.SIZE") if (@f_msgs);
885        $debug and print "Time sizes  : ", timenext(), " s\n";
886        #my $f_flags = $from->flags(@f_msgs) ;
887        #print "Time flags  : ", timenext(), " s\n";
888        use Data::Dumper;
889        #print Data::Dumper->Dump([$f_heads]);
890        #print Data::Dumper->Dump([$f_flags]);
891       
892        foreach my $m (@f_msgs) {
893                parse_header_msg1($from, $m, $f_heads, $f_size, "F", \%f_hash);
894        }
895        $debug and print "Time headers: ", timenext(), " s\n";
896       
897        print "++++ To   [$t_fold] Parse 1 ++++\n";
898        my $t_heads =   $to->parse_headers($to->Range([@t_msgs]),@useheader)
899          if (@t_msgs);
900        $debug and print "Time headers: ", timenext(), " s\n";
901        my $t_size  =   $to->fetch_hash("RFC822.SIZE") if (@t_msgs);
902        $debug and print "Time sizes  : ", timenext(), " s\n";
903        #my $t_flags =   $to->flags(@t_msgs) ;
904        #print "Time flags  : ", timenext(), " s\n";
905       
906        foreach my $m (@t_msgs) {
907                parse_header_msg1($to, $m, $t_heads, $t_size, "T", \%t_hash);
908        }
909        $debug and print "Time headers: ", timenext(), " s\n";
910        #exit;
911
912        print "++++ Verifying [$f_fold] -> [$t_fold] ++++\n";
913        # messages in "from" that are not good in "to"
914       
915        my @f_hash_keys_sorted_by_uid
916          = sort {$f_hash{$a}{'m'} <=> $f_hash{$b}{'m'}} keys(%f_hash);
917       
918        #print map { $f_hash{$_}{'m'} . " "} @f_hash_keys_sorted_by_uid;
919       
920        MESS: foreach my $m_id (@f_hash_keys_sorted_by_uid) {
921                my $f_size = $f_hash{$m_id}{'s'};
922                my $f_msg = $f_hash{$m_id}{'m'};
923                # print ".";
924                if (defined $maxsize and $f_size > $maxsize) {
925                        print "+ Skipping msg #$f_msg:$f_size in folder $f_fold (exceeds maxsize limit $maxsize bytes)\n";
926                        $mess_size_total_skipped += $f_msg;
927                        next MESS;
928                }
929                $debug and print "+ key     $m_id #$f_msg\n";
930                unless (exists($t_hash{$m_id})) {
931                        print "+ NO msg #$f_msg [$m_id] in $t_fold\n";
932                        # copy
933                        print "+ Copying msg #$f_msg:$f_size to folder $t_fold\n";
934                        my $string = $from->message_string($f_msg);
935                        foreach my $regexmess (@regexmess) {
936                                $debug and print "eval \$string =~ $regexmess\n";
937                                eval("\$string =~ $regexmess");
938                        }
939                        $debug and print "F message content begin next line\n",
940                          $string,
941                            "F message content ended on previous line\n";
942                        my $d = "";
943                        if ($syncinternaldates) {
944                                $d = $from->internaldate($f_msg);
945                                $d = "\"$d\"";
946                                $debug and print "internal date from 1: [$d]\n";
947                        }
948                       
949                        my $flags_f_rv = $from->flags($f_msg);
950                        my @flags_f;
951                        my $flags_f;
952                       
953                        if (ref($flags_f_rv)) {
954                                @flags_f = @{$flags_f_rv};
955                                $flags_f = join(" ", @flags_f);
956                        }else{
957                                $flags_f = ""; 
958                        }
959                       
960                        #$flags_f = join(" ", @{$from->flags($f_msg)});
961                       
962                        # RFC 2060 : This flag can not be altered by the client
963                        $flags_f =~ s@\\Recent@@g;
964                       
965                        my $new_id;
966                        print "flags from : [$flags_f][$d]\n";
967                        unless ($dry) {
968                                unless($new_id = $to->append_string($t_fold,$string, $flags_f, $d)){
969                                        warn "Couldn't append msg #$f_msg (Subject:[".$from->subject($f_msg)."]) to folder $t_fold: ",
970                                          $to->LastError, "\n";
971                                        $error++;
972                                        $mess_size_total_error += $f_size;
973                                        next MESS;
974                                       
975                                }else{
976                                                # good
977                                        # $new_id is an id if the IMAP server has the
978                                        # UIDPLUS capability else just a ref
979                                        print "Copied msg id [$f_msg] to folder $t_fold msg id [$new_id]\n";
980                                        $mess_size_total_trans += $f_size;
981                                        $mess_trans += 1;
982                                }
983                        }
984                        next MESS;
985                }else{
986                        $debug and print "Message id [$m_id] found in t:$t_fold\n";
987                        $mess_size_total_skipped += $f_size;
988                        $mess_skipped += 1;
989                }
990               
991                $fast and next MESS;
992                #$debug and print "MESSAGE $m_id\n";
993                my $t_size = $t_hash{$m_id}{'s'};
994                my $t_msg  = $t_hash{$m_id}{'m'};
995               
996                $debug and print "Setting flags\n";
997                my (@flags_f,@flags_t);
998                my $flags_f_rv = $from->flags($f_msg);
999                @flags_f = @{$flags_f_rv} if ref($flags_f_rv);
1000               
1001                # No flag \Recent here, no ?
1002               
1003                $to->store($t_msg,
1004                           "+FLAGS (" . join(" ", @flags_f) . ")"
1005                          ) unless ($dry) ;
1006               
1007                my $flags_t_rv = $to->flags($t_msg);
1008                @flags_t = @{$flags_t_rv} if ref($flags_t_rv);
1009                $debug and print
1010                  "flags from : @flags_f\n",
1011                  "flags to   : @flags_t\n";
1012               
1013
1014                $debug and do {
1015                        print "Looking dates\n";
1016                        my $d_f = $from->internaldate($f_msg);
1017                        my $d_t = $to->internaldate($t_msg);
1018                        print
1019                          "idate from : $d_f\n",
1020                            "idate to   : $d_t\n";
1021                        #unless ($d_f eq $d_t) {
1022                        #       print "!!! Dates differ !!!\n";
1023                        #}
1024                };
1025                unless (($f_size == $t_size) or $skipsize) {
1026                        # Bad size
1027                        print
1028                        "Message $m_id SZ_BAD  f:$f_msg:$f_size t:$t_msg:$t_size\n";
1029                        # delete in to and recopy ?
1030                        # NO recopy CODE HERE. to be written if needed.
1031                        $error++;
1032                        if ($opt_G){
1033                                print "Deleting msg f:#$t_msg in folder $t_fold\n";
1034                                $to->delete_message($t_msg) unless ($dry);
1035                        }
1036                }else {
1037                        # Good
1038                        $debug and print
1039                        "Message $m_id SZ_GOOD f:$f_msg:$f_size t:$t_msg:$t_size\n";
1040                        if($delete) {
1041                                print "Deleting msg #$f_msg in folder $f_fold\n";
1042                                $from->delete_message($f_msg) unless ($dry);
1043                                $from->expunge() if ($expunge and not $dry);
1044                        }
1045                }
1046        }
1047        if ($expunge1){
1048                print "Expunging source folder $f_fold\n";
1049                unless($dry) { $from->expunge() };
1050        }
1051        if ($expunge2){
1052                print "Expunging target folder $t_fold\n";
1053                unless($dry) { $to->expunge() };
1054        }
1055
1056print "Time : ", timenext(), " s\n";
1057}
1058$from->logout();
1059$to->logout();
1060
1061$timeend = time();
1062
1063$timediff = $timeend - $timestart;
1064
1065stats();
1066
1067exit(1) if($error);
1068
1069sub select_msgs {
1070        my ($imap) = @_;
1071        my (@msgs,@max,@min,@union,@inter);
1072       
1073        unless (defined($maxage) or defined($minage)) {
1074                @msgs = $imap->search("ALL");
1075                return(@msgs);
1076        }
1077        if (defined($maxage)) {
1078                @max = $imap->since(time - 86400 * $maxage);
1079        }
1080        if (defined($minage)) {
1081                @min = $imap->before(time - 86400 * $minage);
1082        }
1083      SWITCH: {
1084                unless(defined($minage)) {@msgs = @max; last SWITCH};
1085                unless(defined($maxage)) {@msgs = @min; last SWITCH};
1086                my (%union, %inter);
1087                foreach my $m (@min, @max) {$union{$m}++ && $inter{$m}++}
1088                # normal case
1089                if ($minage <= $maxage)  {@msgs = @inter; last SWITCH};
1090                # just exclude messages between
1091                if ($minage > $maxage)  {@msgs = @union; last SWITCH};
1092               
1093        }
1094        return(@msgs);
1095}
1096
1097
1098sub stats {
1099        print "++++ Statistics ++++\n";
1100        print "Time                   : $timediff sec\n";
1101        print "Messages transfered    : $mess_trans\n";
1102        print "Messages skipped       : $mess_skipped\n";
1103        print "Total bytes transfered : $mess_size_total_trans\n";
1104        print "Total bytes skipped    : $mess_size_total_skipped\n";
1105        print "Total bytes error      : $mess_size_total_error\n";
1106        print "Detected $error errors\n";
1107        print "Please, rate imapsync at http://freshmeat.net/projects/imapsync/\n";
1108
1109
1110}
1111
1112
1113sub get_options
1114{
1115        my $numopt = scalar(@ARGV);
1116        my $opt_ret = GetOptions(
1117                                   "debug!"       => \$debug,
1118                                   "debugimap!"   => \$debugimap,
1119                                   "host1=s"     => \$host1,
1120                                   "host2=s"     => \$host2,
1121                                   "port1=i"     => \$port1,
1122                                   "port2=i"     => \$port2,
1123                                   "user1=s"     => \$user1,
1124                                   "user2=s"     => \$user2,
1125                                   "password1=s" => \$password1,
1126                                   "password2=s" => \$password2,
1127                                   "passfile1=s" => \$passfile1,
1128                                   "passfile2=s" => \$passfile2,
1129                                   "authmd5!"    => \$authmd5,
1130                                   "sep1=s"      => \$sep1,
1131                                   "sep2=s"      => \$sep2,
1132                                   "folder=s"    => \@folder,
1133                                   "include=s"   => \$include,
1134                                   "exclude=s"   => \$exclude,
1135                                   "prefix1=s"   => \$prefix1,
1136                                   "prefix2=s"   => \$prefix2,
1137                                   "regextrans2=s" => \@regextrans2,
1138                                   "regexmess=s" => \@regexmess,
1139                                   "delete!"     => \$delete,
1140                                   "syncinternaldates!" => \$syncinternaldates,
1141                                   "syncacls!"   => \$syncacls,
1142                                   "maxsize=i"   => \$maxsize,
1143                                   "maxage=i"    => \$maxage,
1144                                   "minage=i"    => \$minage,
1145                                   "buffersize=i" => \$buffersize,
1146                                   "foldersizes!" => \$foldersizes,
1147                                   "dry!"        => \$dry,
1148                                   "expunge!"    => \$expunge,
1149                                   "expunge1!"    => \$expunge1,
1150                                   "expunge2!"    => \$expunge2,
1151                                   "subscribed!" => \$subscribed,
1152                                   "subscribe!"  => \$subscribe,
1153                                   "justconnect!"=> \$justconnect,
1154                                   "justfolders!"=> \$justfolders,
1155                                   "justfoldersizes!" => \$justfoldersizes,
1156                                   "fast!"       => \$fast,
1157                                   "version"     => \$version,
1158                                   "help"        => \$help,
1159                                   "timeout=i"   => \$timeout,
1160                                   "skipheader=s" => \$skipheader,
1161                                   "useheader=s" => \@useheader,
1162                                   "skipsize!"   => \$skipsize,
1163                                   "fastio1!"     => \$fastio1,
1164                                   "fastio2!"     => \$fastio2,
1165                                  );
1166       
1167        $debug and print "get options: [$opt_ret]\n";
1168
1169        # just the version
1170        print "$VERSION\n" and exit if ($version) ;
1171
1172        # exit with --help option or no option at all
1173        usage() and exit if ($help or ! $numopt) ;
1174
1175        # don't go on if options are not all known.
1176        exit(EX_USAGE()) unless ($opt_ret) ;
1177       
1178       
1179}
1180
1181
1182sub parse_header_msg1 {
1183        my ($imap, $m_uid, $s_heads, $s_size, $s, $s_hash) = @_;
1184       
1185        my $head = $s_heads->{$m_uid};
1186        my $headnum =  scalar(keys(%$head));
1187        $debug and print "Head NUM:", $headnum, "\n";
1188        unless($headnum) { print "Warning : no header used or found \n"; }
1189        my $headstr;
1190       
1191        foreach my $h (sort keys(%$head)){
1192                foreach my $val (sort @{$head->{$h}}) {
1193                        # no 8-bit data in headers !
1194                        $val =~ s/[\x80-\xff]/X/g;
1195                        # remove the first blanks (dbmail bug ?)
1196                        $val =~ s/^\s+//;
1197                        # show stuff in debug mode
1198                        $debug and print "${s}H $h:", $val, "\n";
1199                        if ($skipheader and $h =~ m/$skipheader/) {
1200                                $debug and print "Skipping header $h\n";
1201                                next;
1202                        }
1203                        $headstr .= "$h:". $val;
1204                }
1205        }
1206        #return unless ($headstr);
1207        unless ($headstr){
1208                # no header so taking everything
1209                $headstr = $imap->message_string($m_uid);
1210        }
1211        my $size = $s_size->{$m_uid}->{"RFC822.SIZE"};
1212        #return unless ($size);
1213        $size = length($headstr) unless ($size);
1214        my $m_md5 = md5_base64($headstr);       
1215        $debug and print "$s msg $m_uid:$m_md5:$size\n";
1216        my $key;
1217        if ($skipsize) {
1218                $key = "$m_md5";
1219        }else {
1220                $key = "$m_md5:$size";
1221        }
1222        $s_hash->{"$key"}{'5'} = $m_md5;
1223        $s_hash->{"$key"}{'s'} = $size;
1224        $s_hash->{"$key"}{'m'} = $m_uid;
1225}
1226
1227
1228sub  firstline {
1229        # extract the first line of a file (without \n)
1230
1231        my($file) = @_;
1232        my $line  = "";
1233       
1234        open FILE, $file or die("$! $file");
1235        chomp($line = <FILE>);
1236        close FILE;
1237        $line = ($line) ? $line : "!EMPTY! $file";
1238        return $line;   
1239}
1240
1241sub usage {
1242        print <<EOF;
1243
1244usage: $0 [options]
1245
1246Several options are mandatory.
1247
1248--host1       <string> : "from" imap server. Mandatory.
1249--port1       <int>    : port to connect. Default is 143.
1250--user1       <string> : user to login.   Mandatory.
1251--password1   <string> : password for the user1. Dangerous, use --passfile1
1252--passfile1   <string> : password file for the user1. Contains the password.
1253--host2       <string> : "destination" imap server. Mandatory.
1254--port2       <int>    : port to connect. Default is 143.
1255--user2       <string> : user to login.   Mandatory.
1256--password2   <string> : password for the user2. Dangerous, use --passfile2
1257--passfile2   <string> : password file for the user2. Contains the password.
1258--noauthmd5            : don't use MD5 authentification.
1259--authmd5              : use MD5 authentification.
1260--folder      <string> : sync only this folder.
1261--folder      <string> : and this one.
1262--folder      <string> : and this one, etc.
1263--include     <regex>  : only sync folders matching this regular expression
1264                         (only effective if neither --folder nor --subscribed
1265                          is specified).
1266--exclude     <regex>  : skips folders matching this regular expression
1267                         (only effective if neither --folder nor --subscribed
1268                          is specified). Several folders to avoid:
1269                          --exclude 'fold1|fold2|f3' skips fold1, fold2 and f3.
1270--prefix1     <string> : remove prefix to all destination folders
1271                         (usually INBOX. for cyrus imap servers)
1272                         use --prefix1 if your source imap server does not
1273                         have NAMESPACE capability.
1274--prefix2     <string> : add prefix to all destination folders
1275                         (usually INBOX. for cyrus imap servers)
1276                         use --prefix2 if your target imap server does not
1277                         have NAMESPACE capability.
1278--regextrans2 <regex>  : Apply the whole regex to each destination folders.
1279--regextrans2 <regex>  : and this one. etc.
1280                         When you play with the --regextrans2 option, first
1281                         add also the safe options --dry --justfolders
1282                         Then, when happy, remove --dry, remove --justfolders
1283--regexmess   <regex>  : Apply the whole regex to each message before transfer.
1284                         Exemple : 's/\\000/ /g' # to replace null by space.
1285--regexmess   <regex>  : and this one.
1286--regexmess   <regex>  : and this one, etc.
1287--sep1        <string> : separator in case namespace is not supported.
1288--sep2        <string> : idem.
1289--delete               : delete messages in source imap server after
1290                         a successful transfert. Useful in case you
1291                         want to migrate from one server to another one.
1292                         With imap, delete tags messages as deleted, they
1293                         are not really deleted. See expunge.
1294--expunge              : expunge messages on source account.
1295                         expunge really deletes messages marked deleted.
1296                         expunge is made at the begining on the
1297                         source server only. newly transfered messages
1298                         are expunged if option --expunge is given.
1299                         no expunge is done on destination account but
1300                         it will change in future releases.
1301--expunge1             : expunge messages on source account.
1302--expunge2             : expunge messages on target account.
1303--syncinternaldates    : sets the internal dates on host2 same as host1
1304--buffersize  <int>    : sets the size of a block of I/O.
1305--maxsize     <int>    : skip messages larger than <int> bytes
1306--maxage      <int>    : skip messages older than <int> days.
1307                         final stats (skipped) don't count older messages
1308                         see also --minage
1309--minage      <int>    : skip messages newer than <int> days.
1310                         final stats (skipped) don't count newer messages
1311                         You can do (+ are the messages selected):
1312                         past|----maxage+++++++++++++++>now
1313                         past|+++++++++++++++minage---->now
1314                         past|----maxage+++++minage---->now (intersection)
1315                         past|++++minage-----maxage++++>now (union)
1316--skipheader  <regex>  : Don't take into account header keyword
1317                         matching <string> ex: --skipheader 'X.*'
1318--useheader   <string> : Use this header to compare messages on both sides.
1319                         Ex: Message-ID or Subject or Date.
1320--useheader   <string>   and this one, etc.
1321--skipsize             : Don't take message size into account.
1322--dry                  : do nothing, just print what would be done.
1323--subscribed           : transfer only subscribed folders.
1324--subscribe            : subscribe to the folders transfered on the
1325                         "destination" server that are subscribed
1326                         on the "source" server.
1327--(no)foldersizes      : Calculate the size of each "From" folder in bytes
1328                         and message counts. Meant to be used with
1329                         --justfoldersizes. Turned on by default.
1330--justfoldersizes      : exit after printed the folder sizes.
1331--syncacls             : Synchronizes acls (Access Control Lists).
1332--nosyncacls           : Does not synchronize acls. This is the default.
1333--debug                : debug mode.
1334--debugimap            : imap debug mode.
1335--version              : print sotfware version.
1336--justconnect          : just connect to both servers and print useful
1337                         information. Need only --host1 and --host2 options.
1338--justfolders          : just do things about folders (ignore messages).
1339--fast                 : be faster (does not sync flags).
1340--nofastio1            : don't use fastio with the "from" server.
1341--nofastio2            : don't use fastio with the "destination" server.
1342--timeout     <int>    : imap connect timeout.
1343--help                 : print this.
1344
1345Example: to synchronise imap account "foo" on "imap.truc.org"
1346                     to imap account "bar" on "imap.trac.org"
1347
1348$0 \\
1349   --host1 imap.truc.org --user1 foo --passfile1 /etc/secret1 \\
1350   --host2 imap.trac.org --user2 bar --passfile2 /etc/secret2
1351
1352
1353 Mail::IMAPClient version is $Mail::IMAPClient::VERSION
1354$rcs
1355      imapsync copyleft is the GNU General Public License.
1356      See http://www.gnu.org/copyleft/gpl.html
1357EOF
1358}
Note: See TracBrowser for help on using the repository browser.