mirror of
https://github.com/imapsync/imapsync.git
synced 2024-11-17 08:12:48 +01:00
463 lines
15 KiB
Diff
463 lines
15 KiB
Diff
--- imapsync.ORIG 2009-05-21 02:30:56.828125000 -0400
|
|
+++ imapsync 2009-05-21 02:35:55.984375000 -0400
|
|
@@ -72,13 +72,14 @@
|
|
[--useheader <string>] [--useheader <string>]
|
|
[--skipsize] [--allowsizemismatch]
|
|
[--delete] [--delete2]
|
|
- [--expunge] [--expunge1] [--expunge2]
|
|
+ [--expunge] [--expunge1] [--expunge2] [--uidexpunge2]
|
|
[--subscribed] [--subscribe]
|
|
[--nofoldersizes]
|
|
[--dry]
|
|
[--debug] [--debugimap]
|
|
[--timeout <int>] [--fast]
|
|
[--split1] [--split2]
|
|
+ [--reconnectretry1 <int>] [--reconnectretry2 <int>]
|
|
[--version] [--help]
|
|
|
|
=cut
|
|
@@ -358,6 +359,7 @@
|
|
--expunge
|
|
--expunge1
|
|
--expunge2
|
|
+--uidexpunge2
|
|
--maxage
|
|
--minage
|
|
--maxsize
|
|
@@ -456,7 +458,7 @@
|
|
$skipheader, @useheader,
|
|
$skipsize, $allowsizemismatch, $foldersizes, $buffersize,
|
|
$delete, $delete2,
|
|
- $expunge, $expunge1, $expunge2, $dry,
|
|
+ $expunge, $expunge1, $expunge2, $uidexpunge2, $dry,
|
|
$justfoldersizes,
|
|
$authmd5,
|
|
$subscribed, $subscribe,
|
|
@@ -474,6 +476,7 @@
|
|
$authuser1, $authuser2,
|
|
$authmech1, $authmech2,
|
|
$split1, $split2,
|
|
+ $reconnectretry1, $reconnectretry2,
|
|
$tests, $test_builder,
|
|
$allow3xx, $justlogin,
|
|
);
|
|
@@ -735,12 +738,12 @@
|
|
$debugimap and print "From connection\n";
|
|
$from = login_imap($host1, $port1, $user1, $password1,
|
|
$debugimap, $timeout, $fastio1, $ssl1,
|
|
- $authmech1, $authuser1);
|
|
+ $authmech1, $authuser1, $reconnectretry1);
|
|
|
|
$debugimap and print "To connection\n";
|
|
$to = login_imap($host2, $port2, $user2, $password2,
|
|
$debugimap, $timeout, $fastio2, $ssl2,
|
|
- $authmech2, $authuser2);
|
|
+ $authmech2, $authuser2, $reconnectretry2);
|
|
|
|
# history
|
|
|
|
@@ -751,13 +754,13 @@
|
|
sub login_imap {
|
|
my($host, $port, $user, $password,
|
|
$debugimap, $timeout, $fastio,
|
|
- $ssl, $authmech, $authuser) = @_;
|
|
+ $ssl, $authmech, $authuser, $reconnectretry) = @_;
|
|
my ($imap);
|
|
|
|
$imap = Mail::IMAPClient->new();
|
|
|
|
$imap->Ssl($ssl) if ($ssl);
|
|
- $imap->Clear(20);
|
|
+ $imap->Clear(5);
|
|
$imap->Server($host);
|
|
$imap->Port($port);
|
|
$imap->Fast_io($fastio);
|
|
@@ -766,7 +769,12 @@
|
|
$imap->Peek(1);
|
|
$imap->Debug($debugimap);
|
|
$timeout and $imap->Timeout($timeout);
|
|
-
|
|
+
|
|
+ ( Mail::IMAPClient->VERSION =~ /^2/ or !$imap->can("Reconnectretry"))
|
|
+ ? warn("--reconnectretry* requires IMAPClient >= 3.17\n")
|
|
+ : $imap->Reconnectretry($reconnectretry)
|
|
+ if ($reconnectretry);
|
|
+
|
|
#$imap->connect()
|
|
myconnect($imap)
|
|
or die "Can not open imap connection on [$host] with user [$user]: $@\n";
|
|
@@ -831,13 +839,13 @@
|
|
|
|
|
|
|
|
-#print "From capability: ", join(" ", $from->capability()), "\n";
|
|
-#print "To capability: ", join(" ", $to->capability()), "\n";
|
|
+$debug and print "From capability: ", join(" ", $from->capability()), "\n";
|
|
+$debug and print "To capability: ", join(" ", $to->capability()), "\n";
|
|
|
|
die unless $from->IsAuthenticated();
|
|
-print "host1 :state Authenticated\n";
|
|
+print "host1: state Authenticated\n";
|
|
die unless $to->IsAuthenticated();
|
|
-print "host2 :state Authenticated\n";
|
|
+print "host2: state Authenticated\n";
|
|
|
|
exit(0) if ($justlogin);
|
|
|
|
@@ -1100,13 +1108,17 @@
|
|
$debug and print "Calling namespace capability\n";
|
|
if ($imap->has_capability("namespace")) {
|
|
$sep_out = $imap->separator();
|
|
- return($sep_out);
|
|
+ return($sep_out) if defined $sep_out;
|
|
+ warn
|
|
+ "NAMESPACE request failed for ",
|
|
+ $imap->Server(), ": ", $imap->LastError, "\n";
|
|
+ exit(1);
|
|
}
|
|
else{
|
|
- print
|
|
+ warn
|
|
"No NAMESPACE capability in imap server ",
|
|
$imap->Server(),"\n",
|
|
- "Give the separator caracter with the $sep_opt option\n";
|
|
+ "Give the separator character with the $sep_opt option\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
@@ -1133,9 +1145,9 @@
|
|
}
|
|
unless ($imap->select($folder)) {
|
|
warn
|
|
- "$side Folder $folder: Could not select ",
|
|
+ "$side Folder $folder: Could not select: ",
|
|
$imap->LastError, "\n";
|
|
- #$error++;
|
|
+ $error++;
|
|
next;
|
|
}
|
|
if (defined($maxage) or defined($minage)) {
|
|
@@ -1319,9 +1331,9 @@
|
|
|
|
unless ($from->select($f_fold)) {
|
|
warn
|
|
- "From Folder $f_fold: Could not select ",
|
|
+ "From Folder $f_fold: Could not select: ",
|
|
$from->LastError, "\n";
|
|
- #$error++;
|
|
+ $error++;
|
|
next FOLDER;
|
|
}
|
|
if ( ! exists($t_folders_list{$t_fold})) {
|
|
@@ -1329,7 +1341,7 @@
|
|
print "Creating folder [$t_fold]\n";
|
|
unless ($dry){
|
|
unless ($to->create($t_fold)){
|
|
- warn "Couldn't create [$t_fold]",
|
|
+ warn "Couldn't create [$t_fold]: ",
|
|
$to->LastError,"\n";
|
|
$error++;
|
|
next FOLDER;
|
|
@@ -1344,9 +1356,9 @@
|
|
|
|
unless ($to->select($t_fold)) {
|
|
warn
|
|
- "To Folder $t_fold: Could not select ",
|
|
+ "To Folder $t_fold: Could not select: ",
|
|
$to->LastError, "\n";
|
|
- #$error++;
|
|
+ $error++;
|
|
next FOLDER;
|
|
}
|
|
|
|
@@ -1384,17 +1396,22 @@
|
|
last FOLDER if $from->IsUnconnected();
|
|
last FOLDER if $to->IsUnconnected();
|
|
|
|
- my $f_heads = $from->parse_headers([@f_msgs],
|
|
- @useheader)if (@f_msgs) ;
|
|
+ my ($f_heads, $f_fir) = ({}, {});
|
|
+ $f_heads = $from->parse_headers([@f_msgs], @useheader) if (@f_msgs);
|
|
$debug and print "Time headers: ", timenext(), " s\n";
|
|
last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
- my $f_fir = $from->fetch_hash("FLAGS",
|
|
- "INTERNALDATE",
|
|
- "RFC822.SIZE") if (@f_msgs);
|
|
+
|
|
+ $f_fir = $from->fetch_hash("FLAGS", "INTERNALDATE", "RFC822.SIZE")
|
|
+ if (@f_msgs);
|
|
$debug and print "Time fir: ", timenext(), " s\n";
|
|
+ unless ($f_fir) {
|
|
+ warn
|
|
+ "From Folder $f_fold: Could not fetch_hash ",
|
|
+ scalar(@f_msgs), " msgs: ", $from->LastError, "\n";
|
|
+ $error++;
|
|
+ next FOLDER;
|
|
+ }
|
|
last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
|
|
foreach my $m (@f_msgs) {
|
|
unless (parse_header_msg1($from, $m, $f_heads, $f_fir, "F", \%f_hash)) {
|
|
@@ -1407,25 +1424,21 @@
|
|
$debug and print "Time headers: ", timenext(), " s\n";
|
|
|
|
print "++++ To [$t_fold] Parse 1 ++++\n";
|
|
- last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
|
|
- my $t_heads = $to->parse_headers([@t_msgs],
|
|
- @useheader) if (@t_msgs);
|
|
+ my ($t_heads, $t_fir) = ({}, {});
|
|
+ $t_heads = $to->parse_headers([@t_msgs], @useheader) if (@t_msgs);
|
|
$debug and print "Time headers: ", timenext(), " s\n";
|
|
- last FOLDER if $from->IsUnconnected();
|
|
last FOLDER if $to->IsUnconnected();
|
|
- my $t_fir = $to->fetch_hash("FLAGS",
|
|
- "INTERNALDATE",
|
|
- "RFC822.SIZE") if (@t_msgs);
|
|
+
|
|
+ $t_fir = $to->fetch_hash("FLAGS", "INTERNALDATE", "RFC822.SIZE")
|
|
+ if (@t_msgs);
|
|
$debug and print "Time fir: ", timenext(), " s\n";
|
|
- last FOLDER if $from->IsUnconnected();
|
|
last FOLDER if $to->IsUnconnected();
|
|
foreach my $m (@t_msgs) {
|
|
parse_header_msg1($to, $m, $t_heads, $t_fir, "T", \%t_hash);
|
|
}
|
|
$debug and print "Time headers: ", timenext(), " s\n";
|
|
-
|
|
+
|
|
print "++++ Verifying [$f_fold] -> [$t_fold] ++++\n";
|
|
# messages in "from" that are not good in "to"
|
|
|
|
@@ -1439,18 +1452,31 @@
|
|
|
|
|
|
if($delete2) {
|
|
+ my @expunge;
|
|
foreach my $m_id (@t_hash_keys_sorted_by_uid) {
|
|
#print "$m_id ";
|
|
unless (exists($f_hash{$m_id})) {
|
|
my $t_msg = $t_hash{$m_id}{'m'};
|
|
- print "deleting message $m_id $t_msg\n";
|
|
- unless ($dry) {
|
|
- last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
+ my $flags = $t_hash{$m_id}{'F'} || "";
|
|
+ my $isdel = $flags =~ /\B\\Deleted\b/ ? 1 : 0;
|
|
+ print "deleting message $m_id $t_msg\n"
|
|
+ if ! $isdel;
|
|
+ push(@expunge,$t_msg) if $uidexpunge2;
|
|
+ unless ($dry or $isdel) {
|
|
$to->delete_message($t_msg);
|
|
+ last FOLDER if $to->IsUnconnected();
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+ my $cnt = scalar @expunge;
|
|
+ if(@expunge and !$to->can("uidexpunge")) {
|
|
+ warn "uidexpunge not supported (< IMAPClient 3.17)\n";
|
|
+ }
|
|
+ elsif(@expunge) {
|
|
+ print "uidexpunge $cnt message(s)\n";
|
|
+ $to->uidexpunge(\@expunge) if !$dry;
|
|
+ }
|
|
}
|
|
|
|
MESS: foreach my $m_id (@f_hash_keys_sorted_by_uid) {
|
|
@@ -1475,7 +1501,7 @@
|
|
$string = $from->message_string($f_msg);
|
|
unless (defined($string)) {
|
|
warn
|
|
- "Could not fetch message #$f_msg from $f_fold ",
|
|
+ "Could not fetch message #$f_msg from $f_fold: ",
|
|
$from->LastError, "\n";
|
|
$error++;
|
|
$mess_size_total_error += $f_size;
|
|
@@ -1591,10 +1617,10 @@
|
|
if($delete) {
|
|
print "Deleting msg #$f_msg in folder $f_fold\n";
|
|
unless($dry) {
|
|
- last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
$from->delete_message($f_msg);
|
|
+ last FOLDER if $from->IsUnconnected();
|
|
$from->expunge() if ($expunge);
|
|
+ last FOLDER if $from->IsUnconnected();
|
|
}
|
|
}
|
|
}
|
|
@@ -1612,44 +1638,31 @@
|
|
}
|
|
|
|
$fast and next MESS;
|
|
- #$debug and print "MESSAGE $m_id\n";
|
|
+ #$debug and print "MESSAGE $m_id\n";
|
|
my $t_size = $t_hash{$m_id}{'s'};
|
|
my $t_msg = $t_hash{$m_id}{'m'};
|
|
-
|
|
-
|
|
- $debug and print "Setting flags\n";
|
|
- last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
|
|
+ # used cached flag values for efficiency
|
|
+ my $flags_f = $f_hash{$m_id}{'F'} || "";
|
|
+ my $flags_t = $t_hash{$m_id}{'F'} || "";
|
|
|
|
- my (@flags_f,@flags_t);
|
|
- my $flags_f_rv = $from->flags($f_msg);
|
|
- @flags_f = @{$flags_f_rv} if ref($flags_f_rv);
|
|
-
|
|
# No flag \Recent here, no ?
|
|
- my $flags_f = join(" ", @flags_f);
|
|
-
|
|
$flags_f = flags_regex($flags_f) if @regexflag;
|
|
-
|
|
- # This add or change flags but no flag are removed with this
|
|
- $to->store($t_msg,
|
|
- "+FLAGS.SILENT (" . $flags_f . ")"
|
|
- ) unless ($dry);
|
|
-
|
|
- # I think one IsUnconnected() is enough before all flags operations.
|
|
- #last FOLDER if $to->IsUnconnected();
|
|
-
|
|
- my $flags_t_rv = $to->flags($t_msg);
|
|
- #last FOLDER if $to->IsUnconnected();
|
|
|
|
- @flags_t = @{$flags_t_rv} if ref($flags_t_rv);
|
|
- my $flags_t = join(" ", @flags_t);
|
|
- $debug and print
|
|
- "flags from: $flags_f\n",
|
|
- "flags to : $flags_t\n";
|
|
-
|
|
+ $debug and print "Setting flags from($flags_f) to($flags_t)\n";
|
|
+
|
|
+ # This add or change flags but no flag are removed with this
|
|
+ $to->store($t_msg, "+FLAGS.SILENT ($flags_f)" )
|
|
+ if (!$dry and $flags_f ne $flags_t);
|
|
+ last FOLDER if $to->IsUnconnected();
|
|
|
|
$debug and do {
|
|
+ my @flags_t = @{ $to->flags($t_msg) || [] };
|
|
+ last FOLDER if $to->IsUnconnected();
|
|
+
|
|
+ print "flags from: $flags_f\n",
|
|
+ "flags to : @flags_t\n";
|
|
+
|
|
print "Looking dates\n";
|
|
#my $d_f = $from->internaldate($f_msg);
|
|
#my $d_t = $to->internaldate($t_msg);
|
|
@@ -1657,7 +1670,7 @@
|
|
my $d_t = $t_hash{$m_id}{'D'};
|
|
print
|
|
"idate from: $d_f\n",
|
|
- "idate to : $d_t\n";
|
|
+ "idate to : $d_t\n";
|
|
|
|
#unless ($d_f eq $d_t) {
|
|
# print "!!! Dates differ !!!\n";
|
|
@@ -1673,8 +1686,7 @@
|
|
if ($opt_G){
|
|
print "Deleting msg f:#$t_msg in folder $t_fold\n";
|
|
$to->delete_message($t_msg) unless ($dry);
|
|
- # $opt_G is fake
|
|
- #last FOLDER if $to->IsUnconnected();
|
|
+ last FOLDER if $to->IsUnconnected();
|
|
}
|
|
}
|
|
else {
|
|
@@ -1684,10 +1696,10 @@
|
|
if($delete) {
|
|
print "Deleting msg #$f_msg in folder $f_fold\n";
|
|
unless($dry) {
|
|
- last FOLDER if $from->IsUnconnected();
|
|
- last FOLDER if $to->IsUnconnected();
|
|
$from->delete_message($f_msg);
|
|
+ last FOLDER if $from->IsUnconnected();
|
|
$from->expunge() if ($expunge);
|
|
+ last FOLDER if $from->IsUnconnected();
|
|
}
|
|
}
|
|
}
|
|
@@ -1713,21 +1725,31 @@
|
|
#
|
|
# can be tested with a "killall /usr/bin/imapd" (or equivalent) in command line.
|
|
#
|
|
+sub _filter {
|
|
+ my $str = shift or return "";
|
|
+ my $sz = 64;
|
|
+ my $len = length($str);
|
|
+ if ( ! $debug and $len > $sz*2 ) {
|
|
+ my $beg = substr($str, 0, $sz);
|
|
+ my $end = substr($str, -$sz, $sz);
|
|
+ $str = $beg . "..." . $end;
|
|
+ }
|
|
+ $str =~ s/\012?\015$//;
|
|
+ return "(len=$len) " . $str;
|
|
+}
|
|
+
|
|
sub lost_connection {
|
|
my($imap, $error_message) = @_;
|
|
if ( $imap->IsUnconnected() ) {
|
|
$error++;
|
|
+ my $lcomm = $imap->LastIMAPCommand || "";
|
|
my $einfo = $imap->LastError || @{$imap->History}[-1] || "";
|
|
- my $sz = 64;
|
|
- # if einfo is long try reduce to a more reasonable size
|
|
- if ( ! $debug and length($einfo) > $sz*2 ) {
|
|
- my $beg = substr($einfo, 0, $sz);
|
|
- my $end = substr($einfo, -$sz, $sz);
|
|
- $einfo = $beg . "..." . $end;
|
|
- }
|
|
- chomp($einfo);
|
|
- $einfo = ": $einfo" if $einfo;
|
|
- warn("error: lost connection $error_message $einfo\n");
|
|
+
|
|
+ # if string is long try reduce to a more reasonable size
|
|
+ $lcomm = _filter($lcomm);
|
|
+ $einfo = _filter($einfo);
|
|
+ warn("error: last command: $lcomm\n") if ($debug && $lcomm);
|
|
+ warn("error: lost connection $error_message", $einfo, "\n");
|
|
return(1);
|
|
}else{
|
|
return(0);
|
|
@@ -1847,6 +1869,7 @@
|
|
"expunge!" => \$expunge,
|
|
"expunge1!" => \$expunge1,
|
|
"expunge2!" => \$expunge2,
|
|
+ "uidexpunge2!" => \$uidexpunge2,
|
|
"subscribed!" => \$subscribed,
|
|
"subscribe!" => \$subscribe,
|
|
"justbanner!" => \$justbanner,
|
|
@@ -1871,6 +1894,8 @@
|
|
"authuser2=s" => \$authuser2,
|
|
"split1=i" => \$split1,
|
|
"split2=i" => \$split2,
|
|
+ "reconnectretry1=i" => \$reconnectretry1,
|
|
+ "reconnectretry2=i" => \$reconnectretry2,
|
|
"tests" => \$tests,
|
|
"allow3xx!" => \$allow3xx,
|
|
"justlogin!" => \$justlogin,
|
|
@@ -2088,6 +2113,8 @@
|
|
it will change in future releases.
|
|
--expunge1 : expunge messages on source account.
|
|
--expunge2 : expunge messages on target account.
|
|
+--uidexpunge2 : uidexpunge messages on the destination imap server
|
|
+ that are not on the source server, requires --delete2
|
|
--syncinternaldates : sets the internal dates on host2 same as host1.
|
|
Turned on by default.
|
|
--idatefromheader : sets the internal dates on host2 same as the
|
|
@@ -2132,6 +2159,8 @@
|
|
and exit.
|
|
--justfolders : just do things about folders (ignore messages).
|
|
--fast : be faster (just does not sync flags).
|
|
+--reconnectretry1 <int>: reconnect if connection is lost up to <int> times
|
|
+--reconnectretry2 <int>: reconnect if connection is lost up to <int> times
|
|
--split1 <int> : split the requests in several parts on source server.
|
|
<int> is the number of messages handled per request.
|
|
default is like --split1 1000
|