aboutsummaryrefslogtreecommitdiffstats
path: root/imapsync
diff options
context:
space:
mode:
Diffstat (limited to 'imapsync')
-rwxr-xr-ximapsync73
1 files changed, 68 insertions, 5 deletions
diff --git a/imapsync b/imapsync
index f7c5234..ac63577 100755
--- a/imapsync
+++ b/imapsync
@@ -559,6 +559,13 @@ my $STH_GET_INTERRUPTED_BY_IDX = $DBH->prepare(q{
WHERE m.idx = ? AND (lUID >= l.UIDNEXT OR rUID >= r.UIDNEXT)
});
+# Count messages
+my $STH_COUNT_MESSAGES = $DBH->prepare(q{SELECT COUNT(*) FROM mapping WHERE idx = ?});
+
+# List last 1024 messages UIDs
+my $STH_LASTUIDs_LOCAL = $DBH->prepare(q{SELECT rUID FROM mapping WHERE idx = ? ORDER BY rUID DESC LIMIT 1024});
+my $STH_LASTUIDs_REMOTE = $DBH->prepare(q{SELECT lUID FROM mapping WHERE idx = ? ORDER BY lUID DESC LIMIT 1024});
+
# Download some missing UIDs from $source; returns the thew allocated UIDs
sub download_missing($$$@) {
@@ -612,6 +619,64 @@ sub delete_mapping($$) {
}
+# Create a sample (UIDs, sequence numbers) to use as 3rd and 4th
+# argument of the QRESYNC parameters to the SELECT command.
+# QRESYNC [RFC7162] doesn't force the server to remember the MODSEQs of
+# EXPUNGEd messages. By passing a sample of known UIDs/sequence numbers
+# we let the server know that the messages have been EXPUNGEd [RFC7162,
+# section 3.2.5.2].
+# The UID set is the largest set of higest UIDs with at most 1024 UIDs,
+# of length (after compacting) at most 64.
+# The reason why we sample with the highest UIDs is that lowest UIDs are
+# less likely to be deleted.
+sub sample($$$) {
+ my ($idx, $count, $sth) = @_;
+ return unless $count > 0;
+
+ my ($n, $uids, $min, $max);
+ $sth->execute($idx);
+ while (defined (my $row = $sth->fetchrow_arrayref())) {
+ my $k = $row->[0];
+ if (!defined $min and !defined $max) {
+ $n = 0;
+ $min = $max = $k;
+ }
+ elsif ($k == $min - 1) {
+ $min--;
+ }
+ else {
+ $n += $max - $min + 1;
+ $uids = ($min == $max ? $min : "$min:$max")
+ .(defined $uids ? ','.$uids : '');
+ $min = $max = $k;
+ if (length($uids) > 64) {
+ $sth->finish(); # done with the statement
+ last;
+ }
+ }
+ }
+ if (!defined $uids or length($uids) <= 64) {
+ $n += $max - $min + 1;
+ $uids = ($min == $max ? $min : "$min:$max")
+ .(defined $uids ? ','.$uids : '');
+ }
+ return ( $uids, ($count - $n + 1).':'.$count );
+}
+
+
+# Issue a SELECT command with the given $mailbox.
+sub select_mbx($$) {
+ my ($idx, $mailbox) = @_;
+
+ $STH_COUNT_MESSAGES->execute($idx);
+ my ($count) = $STH_COUNT_MESSAGES->fetchrow_array();
+ die if defined $STH_COUNT_MESSAGES->fetch(); # sanity check
+
+ $lIMAP->select($mailbox, sample($idx, $count, $STH_LASTUIDs_LOCAL));
+ $rIMAP->select($mailbox, sample($idx, $count, $STH_LASTUIDs_REMOTE));
+}
+
+
# Check and repair synchronization of a mailbox between the two servers
# (in a very crude way, by downloading all existing UID with their flags)
sub repair($) {
@@ -622,8 +687,7 @@ sub repair($) {
die if defined $STH_GET_INDEX->fetch(); # sanity check
return unless defined $idx; # not in the database
- $lIMAP->select($mailbox);
- $rIMAP->select($mailbox);
+ select_mbx($idx, $mailbox);
$STH_GET_CACHE_BY_IDX->execute($idx);
my $cache = $STH_GET_CACHE_BY_IDX->fetchrow_hashref() // return; # no cache
@@ -1005,7 +1069,7 @@ sub wait_notifications(;$) {
#############################################################################
-# Resume interrupted mailbox syncs.
+# Resume interrupted mailbox syncs (before initializing the cache).
#
my ($MAILBOX, $IDX);
$STH_LIST_INTERRUPTED->execute();
@@ -1101,8 +1165,7 @@ while(1) {
die if defined $STH_GET_INDEX->fetch(); # sanity check
die unless defined $IDX; # sanity check;
- $lIMAP->select($MAILBOX);
- $rIMAP->select($MAILBOX);
+ select_mbx($IDX, $MAILBOX);
if (!$KNOWN_INDEXES{$IDX}) {
$STH_INSERT_LOCAL->execute( $IDX, $lIMAP->uidvalidity($MAILBOX));