aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Net/IMAP/Sync.pm
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2015-07-25 01:03:25 +0200
committerGuilhem Moulin <guilhem@fripost.org>2015-07-25 01:03:25 +0200
commit7d981939c61bc9b94dcb027884d5a81cbc69fbcf (patch)
tree6f4acb697f6ce38c18c30a91a1f78b2ddf17a82d /lib/Net/IMAP/Sync.pm
parentf64d7df93fa4d21ec2ea8cfa08ed9f58af23df9b (diff)
Detect and handle interrupted syncs to avoid message duplicates.
Diffstat (limited to 'lib/Net/IMAP/Sync.pm')
-rw-r--r--lib/Net/IMAP/Sync.pm72
1 files changed, 32 insertions, 40 deletions
diff --git a/lib/Net/IMAP/Sync.pm b/lib/Net/IMAP/Sync.pm
index 5945746..9882d75 100644
--- a/lib/Net/IMAP/Sync.pm
+++ b/lib/Net/IMAP/Sync.pm
@@ -573,8 +573,8 @@ sub list($$@) {
# "UIDPLUS" (RFC 4315) in its CAPABILITY list.
# Successfully EXPUNGEd UIDs are removed from the pending VANISHED and
# MODIFIED lists.
-# Return the list of UIDs that could not be EXPUNGEd.
-sub remove($@) {
+# Return the list of UIDs that couldn't be EXPUNGEd.
+sub remove_message($@) {
my $self = shift;
my @set = @_;
$self->fail("Server did not advertize UIDPLUS (RFC 4315) capability.")
@@ -671,7 +671,7 @@ sub append($$@) {
delete $vanished2{$_} foreach keys %vanished;
my $VANISHED = scalar(keys %vanished2); # number of messages VANISHED meanwhile
$cache->{EXISTS} += $#appends+1 if defined $cache->{EXISTS} and $cache->{EXISTS} + $VANISHED == $EXISTS;
- $cache->{UIDNEXT} = $UIDNEXT if ($cache->{UIDNEXT} // 0) < $UIDNEXT;
+ $cache->{UIDNEXT} = $UIDNEXT if ($cache->{UIDNEXT} // 1) < $UIDNEXT;
}
$self->log("Added ".($#appends+1)." message(s) to $mailbox, got new UID ".compact_set(@uids))
@@ -852,42 +852,36 @@ sub pull_updates($;$) {
my $mailbox = $self->{_SELECTED} // $self->panic();
my $pcache = $self->{_PCACHE}->{$mailbox};
- my (@vanished, %modified);
- unless (defined $pcache->{UIDNEXT} and defined $pcache->{HIGHESTMODSEQ}) {
- $self->{_MODIFIED} = {};
- $self->{_VANISHED} = [];
- }
- else {
- $self->_send("UID FETCH 1:".($pcache->{UIDNEXT}-1)." (MODSEQ FLAGS)")
- if $full and $pcache->{UIDNEXT} > 1;
-
- my @missing;
- while (%{$self->{_MODIFIED}}) {
- while (my ($uid,$v) = each %{$self->{_MODIFIED}}) {
- # don't filter on the fly (during FETCH responses) because
- # FLAG updates can arrive while processing pull_new_messages
- # for instance
- if (defined $v->[1] and $v->[0] > 0) { # setting the MODSEQ to 0 forces a FETCH
- next unless $uid < $pcache->{UIDNEXT} # out of bounds
- and ($full or $v->[0] > $pcache->{HIGHESTMODSEQ}); # already seen
- $modified{$uid} = $full ? $v : $v->[1];
- } else {
- push @missing, $uid;
- }
+ my %modified;
+ $self->_send("UID FETCH 1:".($pcache->{UIDNEXT}-1)." (MODSEQ FLAGS)")
+ if $full and ($pcache->{UIDNEXT} // 1) > 1;
+
+ my @missing;
+ while (%{$self->{_MODIFIED}}) {
+ while (my ($uid,$v) = each %{$self->{_MODIFIED}}) {
+ # don't filter on the fly (during FETCH responses) because
+ # FLAG updates can arrive while processing pull_new_messages
+ # for instance
+ if (defined $v->[1] and $v->[0] > 0) { # setting the MODSEQ to 0 forces a FETCH
+ next unless $uid < ($pcache->{UIDNEXT} // 1) # out of bounds
+ and ($full or $v->[0] > ($pcache->{HIGHESTMODSEQ} // 0)); # already seen
+ $modified{$uid} = $full ? $v : $v->[1];
+ } else {
+ push @missing, $uid;
}
- $self->{_MODIFIED} = {};
- $self->_send("UID FETCH ".compact_set(@missing)." (MODSEQ FLAGS)") if @missing;
- @missing = ();
}
+ $self->{_MODIFIED} = {};
+ $self->_send("UID FETCH ".compact_set(@missing)." (MODSEQ FLAGS)") if @missing;
+ @missing = ();
+ }
- # do that afterwards since the UID FETCH command above can produce VANISHED responses
- my %vanished = map {$_ => 1} @{$self->{_VANISHED}};
- @vanished = keys %vanished;
- $self->{_VANISHED} = [];
+ # do that afterwards since the UID FETCH command above can produce VANISHED responses
+ my %vanished = map {$_ => 1} grep { $_ < ($pcache->{UIDNEXT} // 1) } @{$self->{_VANISHED}};
+ my @vanished = keys %vanished;
+ $self->{_VANISHED} = [];
- # ignore FLAG updates on VANISHED messages
- delete @modified{@vanished};
- }
+ # ignore FLAG updates on VANISHED messages
+ delete @modified{@vanished};
# update the persistent cache for HIGHESTMODSEQ (not for UIDNEXT
# since there might be new messages)
@@ -1219,7 +1213,7 @@ sub _select_or_examine($$$) {
$command .= " (QRESYNC ($pcache->{UIDVALIDITY} $pcache->{HIGHESTMODSEQ} "
."1:".($pcache->{UIDNEXT}-1)."))"
if $self->_enabled('QRESYNC') and
- ($pcache->{HIGHESTMODSEQ} // 0) > 0 and ($pcache->{UIDNEXT} // 0) > 1;
+ ($pcache->{HIGHESTMODSEQ} // 0) > 0 and ($pcache->{UIDNEXT} // 1) > 1;
if ($self->{_STATE} eq 'SELECTED' and ($self->_capable('CONDSTORE') or $self->_capable('QRESYNC'))) {
# A mailbox is currently selected and the server advertizes
@@ -1513,15 +1507,13 @@ sub _resp($$;$$$) {
if (/\A([0-9]+)\z/) {
$cache->{EXISTS}-- unless $earlier; # explicit EXISTS responses are optional
$cache->{UIDNEXT} = $1+1 if $cache->{UIDNEXT} <= $1; # the actual UIDNEXT is *at least* that
- push @{$self->{_VANISHED}}, $1
- if defined $pcache->{UIDNEXT} and $1 < $pcache->{UIDNEXT};
+ push @{$self->{_VANISHED}}, $1;
}
elsif (/\A([0-9]+):([0-9]+)\z/) {
my ($min, $max) = $1 < $2 ? ($1,$2) : ($2,$1);
$cache->{EXISTS} -= $max-$min+1 unless $earlier; # explicit EXISTS responses are optional
$cache->{UIDNEXT} = $max+1 if $cache->{UIDNEXT} <= $max; # the actual UIDNEXT is *at least* that
- push @{$self->{_VANISHED}}, grep {$_ < $pcache->{UIDNEXT}} ($min .. $max)
- if defined $pcache->{UIDNEXT};
+ push @{$self->{_VANISHED}}, ($min .. $max);
}
}
}