aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-ximapsync200
-rw-r--r--imapsync.119
-rw-r--r--imapsync.service2
-rw-r--r--lib/Net/IMAP/Sync.pm5
4 files changed, 105 insertions, 121 deletions
diff --git a/imapsync b/imapsync
index fec37f0..339979c 100755
--- a/imapsync
+++ b/imapsync
@@ -47,7 +47,6 @@ sub usage(;$) {
print STDERR "Synchronize the given MAILBOXes between two QRESYNC-capable IMAP4rev1 servers.\n"
."Options:\n"
." --config=FILE Specify an alternate configuration file\n"
- ." -1, --oneshot Exit as soon as all mailboxes are synchronized\n"
." --repair List the database anomalies and try to repair them\n"
." -q, --quiet Try to be quiet\n"
." --debug Turn on debug mode\n"
@@ -55,7 +54,7 @@ sub usage(;$) {
}
exit $rv;
}
-usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q oneshot|1 repair debug help|h/);
+usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q repair debug help|h/);
usage(0) if $CONFIG{help};
@@ -93,13 +92,13 @@ my $DBH;
# Clean after us
sub cleanup() {
- logger("Cleaning up...") if $CONFIG{debug};
+ logger(undef, "Cleaning up...") if $CONFIG{debug};
unlink $LOCKFILE if defined $LOCKFILE and -f $LOCKFILE;
close $LOGGER_FD if defined $LOGGER_FD;
$DBH->disconnect() if defined $DBH;
}
-$SIG{$_} = sub { cleanup(); msg($!); exit 1; } foreach qw/INT TERM/;
-$SIG{$_} = sub { cleanup(); msg($!); exit 0; } foreach qw/HUP/;
+$SIG{$_} = sub { msg(undef, $!); cleanup(); exit 1; } foreach qw/INT TERM/;
+$SIG{$_} = sub { msg(undef, $!); cleanup(); exit 0; } foreach qw/HUP/;
#############################################################################
@@ -224,7 +223,9 @@ foreach my $name (qw/local remote/) {
#my $mailboxes = $client->list((uc $config{'subscribed-only'} eq 'TRUE' ? '(SUBSCRIBED)' : '' )
# .$config{mailboxes}, 'SUBSCRIBED');
# $client->notify('SELECTED', 'MAILBOXES ('.join(' ', keys %$mailboxes).')');
- $client->notify(qw/SELECTED SUBSCRIBED/) unless $CONFIG{oneshot};
+ # XXX NOTIFY doesn't work as expected for INBOX
+ # http://dovecot.org/pipermail/dovecot/2015-July/101514.html
+ #$client->notify(qw/SELECTED SUBSCRIBED/) if $CONFIG{watch};
# XXX We shouldn't need to ask for STATUS responses here, and use
# NOTIFY's STATUS indicator instead. However Dovecot violates RFC
# 5464: http://dovecot.org/pipermail/dovecot/2015-July/101474.html
@@ -337,7 +338,8 @@ sub sync_tree($$%) {
# $missing's $mbx2, which is not in the database and
# doesn't exist on $exists
msg($exists, "Rename mailbox $mbx to $mbx2[0]");
- $sth->{rename}->execute($mbx2[0],$idx);
+ $sth->{rename}->execute($mbx2[0],$idx) or
+ msg('database', "WARNING: Can't rename $mbx to $mbx2[0]");
$IMAP->{$exists}->{client}->rename($mbx, $mbx2[0]);
$DBH->commit();
mv_tree($IMAP->{$exists}->{mailboxes}, $mbx, $mbx2[0], %children);
@@ -375,7 +377,8 @@ sub sync_tree($$%) {
# database as associated with $mbx2, which exists on
# $missing but not on $exists
msg($missing, "Rename mailbox $mbx2 to $mbx");
- $sth->{rename}->execute($mbx,$idx);
+ $sth->{rename}->execute($mbx,$idx) or
+ msg('database', "WARNING: Can't rename $mbx2 to $mbx2");
$IMAP->{$missing}->{client}->rename($mbx2, $mbx);
$DBH->commit();
mv_tree($IMAP->{$missing}->{mailboxes}, $mbx2, $mbx, %children);
@@ -419,7 +422,7 @@ sub sync_tree($$%) {
foreach qw/local remote/;
$sth{$_.'_by_uidvalidity'} = $DBH->prepare("SELECT idx,mailbox FROM mailboxes NATURAL JOIN $_ WHERE UIDVALIDITY = ?")
foreach qw/local remote/;
- $sth{rename} = $DBH->prepare("UPDATE mailboxes SET mailbox = ? WHERE idx = ?");
+ $sth{rename} = $DBH->prepare(q{UPDATE mailboxes SET mailbox = ? WHERE idx = ?});
my $updated = 0;
while (my ($mbx,$children) = each %$tree) {
@@ -442,78 +445,72 @@ sub sync_tree($$%) {
}
}
-# Syncronize subscription list
+# Synchronize subscription list
my @SUBSCRIPTIONS;
{
- my $sth_search = $DBH->prepare("SELECT idx,subscribed FROM mailboxes WHERE mailbox = ?");
- my $sth_subscribe = $DBH->prepare("UPDATE mailboxes SET subscribed = ? WHERE idx = ?");
+ my $sth_search = $DBH->prepare(q{SELECT idx,subscribed FROM mailboxes WHERE mailbox = ?});
+ my $sth_subscribe = $DBH->prepare(q{UPDATE mailboxes SET subscribed = ? WHERE idx = ?});
my %mailboxes;
$mailboxes{$_} = 1 foreach (keys %{$IMAP->{local}->{mailboxes}}, keys %{$IMAP->{remote}->{mailboxes}});
foreach my $mbx (keys %mailboxes) {
- if (subscribed_mbx('local',$mbx) xor subscribed_mbx('remote',$mbx)) {
- my ($subscribed,$unsubscribed) = subscribed_mbx('local',$mbx) ? ('local','remote') : ('remote','local');
-
- $sth_search->execute($mbx);
- my $row = $sth_search->fetch();
- die if defined $sth_search->fetch(); # sanity check
+ $sth_search->execute($mbx);
+ my $row = $sth_search->fetch();
+ die if defined $sth_search->fetch(); # sanity check
+ my ($lSubscribed,$rSubscribed) = map {subscribed_mbx($_,$mbx)} qw/local remote/;
+ if ($lSubscribed == $rSubscribed) {
+ if (defined $row) {
+ my ($idx,$status) = @$row;
+ if (defined $status and $status != $lSubscribed) {
+ $sth_subscribe->execute($lSubscribed, $idx) or
+ msg('database', "WARNING: Can't (un)subscribe $mbx");
+ $DBH->commit();
+ }
+ }
+ }
+ else {
+ my ($subscribed,$unsubscribed) = $lSubscribed ? qw/local remote/ : qw/remote local/;
if (defined $row) {
my ($idx,$status) = @$row;
if ($status) {
# $mbx was SUBSCRIBEd before, UNSUBSCRIBE it now
msg($subscribed, "Unsubscribe to mailbox $mbx");
- $sth_subscribe->execute(0,$idx);
+ $sth_subscribe->execute(0,$idx) or
+ msg('database', "WARNING: Can't unsubscribe $mbx");
$IMAP->{$subscribed}->{client}->unsubscribe($mbx);
$DBH->commit();
- $IMAP->{$subscribed}->{mailboxes}->{$mbx} =
- grep {lc $_ ne lc '\Subscribed'} @{$IMAP->{$subscribed}->{mailboxes}->{$mbx} // []};
+ $lSubscribed = $rSubscribed = 0;
}
else {
# $mbx was UNSUBSCRIBEd before, SUBSCRIBE it now
msg($unsubscribed, "Subscribe to mailbox $mbx");
- $sth_subscribe->execute(1,$idx);
+ $sth_subscribe->execute(1,$idx) or
+ msg('database', "WARNING: Can't subscribe $mbx");
$IMAP->{$unsubscribed}->{client}->subscribe($mbx);
$DBH->commit();
- $IMAP->{$unsubscribed}->{mailboxes}->{$mbx} //= [];
- push @{$IMAP->{$unsubscribed}->{mailboxes}->{$mbx}}, '\Subscribed';
+ $lSubscribed = $rSubscribed = 1;
}
}
else {
# $mbx is unknown; assume the user wants to SUBSCRIBE
msg($unsubscribed, "Subscribe to mailbox $mbx");
$IMAP->{$unsubscribed}->{client}->subscribe($mbx);
- $IMAP->{$unsubscribed}->{mailboxes}->{$mbx} //= [];
- push @{$IMAP->{$unsubscribed}->{mailboxes}->{$mbx}}, '\Subscribed';
+ $lSubscribed = $rSubscribed = 1;
}
}
- else {
- $sth_search->execute($mbx);
- my $row = $sth_search->fetch();
- die if defined $sth_search->fetch(); # sanity check
-
- if (defined $row) {
- my ($idx,$status) = @$row;
- unless (defined $status and $status != 0) {
- my $subscribed = subscribed_mbx('local',$mbx) ? 1 : 0;
- $sth_subscribe->execute($subscribed, $idx);
- $DBH->commit();
- }
- }
- }
- push @SUBSCRIPTIONS, $mbx if subscribed_mbx('local', $mbx) and
- subscribed_mbx('remote',$mbx);
+ push @SUBSCRIPTIONS, $mbx if $lSubscribed;
}
}
# Clean database: remove mailboxes that no longer exist
{
- my $sth = $DBH->prepare("SELECT idx,mailbox,subscribed FROM mailboxes");
- my $sth_delete_mailboxes = $DBH->prepare("DELETE FROM mailboxes WHERE idx = ?");
- my $sth_delete_local = $DBH->prepare("DELETE FROM local WHERE idx = ?");
- my $sth_delete_remote = $DBH->prepare("DELETE FROM remote WHERE idx = ?");
- my $sth_delete_mapping = $DBH->prepare("DELETE FROM mapping WHERE idx = ?");
+ my $sth = $DBH->prepare(q{SELECT idx,mailbox,subscribed FROM mailboxes});
+ my $sth_delete_mailboxes = $DBH->prepare(q{DELETE FROM mailboxes WHERE idx = ?});
+ my $sth_delete_local = $DBH->prepare(q{DELETE FROM local WHERE idx = ?});
+ my $sth_delete_remote = $DBH->prepare(q{DELETE FROM remote WHERE idx = ?});
+ my $sth_delete_mapping = $DBH->prepare(q{DELETE FROM mapping WHERE idx = ?});
my @idx;
$sth->execute();
@@ -620,7 +617,7 @@ sub download_missing($$$@) {
my $uid = $mail->{UID};
my $from = first { defined $_ and @$_ } @{$mail->{ENVELOPE}}[2,3,4];
$from = (defined $from and @$from) ? $from->[0]->[2].'@'.$from->[0]->[3] : '';
- msg("$source($mailbox): UID $uid from <$from> ($mail->{INTERNALDATE})") unless $CONFIG{quiet};
+ msg(undef, "$source($mailbox): UID $uid from <$from> ($mail->{INTERNALDATE})") unless $CONFIG{quiet};
callback_new_message($idx, $mailbox, $source, $mail, \@uids, $buff, \$bufflen)
});
@@ -631,12 +628,12 @@ sub download_missing($$$@) {
# Solve a flag update conflict (by taking the union of the two flag lists).
sub flag_conflict($$$$$) {
- my ($mailbox, $lUID, $lFlags, $rUID, $rFlags);
+ my ($mailbox, $lUID, $lFlags, $rUID, $rFlags) = @_;
my %flags = map {$_ => 1} (split(/ /, $lFlags), split(/ /, $rFlags));
my $flags = join ' ', sort(keys %flags);
- msg("WARNING: Conflicting flag update in $mailbox for local UID $lUID ($lFlags) ".
- "and remote UID $rUID ($rFlags). Setting both to the union ($flags).");
+ msg(undef, "WARNING: Conflicting flag update in $mailbox for local UID $lUID ($lFlags) ".
+ "and remote UID $rUID ($rFlags). Setting both to the union ($flags).");
return $flags
}
@@ -647,7 +644,7 @@ sub delete_mapping($$) {
my ($idx, $lUID) = @_;
my $r = $STH_DELETE_MAPPING->execute($idx, $lUID);
die if $r > 1; # sanity check
- msg("WARNING: Can't delete (idx,lUID) = ($idx,$lUID) from the database") if $r == 0;
+ msg('database', "WARNING: Can't delete (idx,lUID) = ($idx,$lUID)") if $r == 0;
}
@@ -699,7 +696,7 @@ sub repair($$) {
}
else {
# conflict
- msg("WARNING: Missed flag update in $mailbox for (lUID,rUID) = ($lUID,$rUID). Repairing.")
+ msg(undef, "WARNING: Missed flag update in $mailbox for (lUID,rUID) = ($lUID,$rUID). Repairing.")
if $lModified->{$lUID}->[0] <= $cache->{lHIGHESTMODSEQ} and
$rModified->{$rUID}->[0] <= $cache->{rHIGHESTMODSEQ};
# set both $lUID and $rUID to the union of $lFlags and $rFlags
@@ -712,7 +709,7 @@ sub repair($$) {
}
elsif (!defined $lModified->{$lUID} and !defined $rModified->{$rUID}) {
unless ($lVanished{$lUID} and $rVanished{$rUID}) {
- msg("WARNING: Pair (lUID,rUID) = ($lUID,$rUID) vanished from $mailbox. Repairing.");
+ msg(undef, "WARNING: Pair (lUID,rUID) = ($lUID,$rUID) vanished from $mailbox. Repairing.");
push @delete_mapping, $lUID;
}
}
@@ -721,7 +718,7 @@ sub repair($$) {
if ($lVanished{$lUID}) {
push @rToRemove, $rUID;
} else {
- msg("local($mailbox): WARNING: UID $lUID disappeared. Downloading remote UID $rUID again.");
+ msg("local($mailbox)", "WARNING: UID $lUID disappeared. Downloading remote UID $rUID again.");
push @rMissing, $rUID;
}
}
@@ -730,7 +727,7 @@ sub repair($$) {
if ($rVanished{$rUID}) {
push @lToRemove, $lUID;
} else {
- msg("remote($mailbox): WARNING: UID $rUID disappeared. Downloading local UID $lUID again.");
+ msg("remote($mailbox)", "WARNING: UID $rUID disappeared. Downloading local UID $lUID again.");
push @lMissing, $lUID;
}
}
@@ -759,15 +756,15 @@ sub repair($$) {
# Process UID found in IMAP but not in the mapping table.
- msg("remote($mailbox): WARNING: No match for vanished local UID $_. Ignoring.") foreach keys %lVanished;
- msg("local($mailbox): WARNING: No match for vanished remote UID $_. Ignoring.") foreach keys %rVanished;
+ msg("remote($mailbox)", "WARNING: No match for vanished local UID $_. Ignoring.") foreach keys %lVanished;
+ msg("local($mailbox)", "WARNING: No match for vanished remote UID $_. Ignoring.") foreach keys %rVanished;
foreach my $lUID (keys %$lModified) {
- msg("remote($mailbox): WARNING: No match for modified local UID $lUID. Downloading again.");
+ msg("remote($mailbox)", "WARNING: No match for modified local UID $lUID. Downloading again.");
push @lMissing, $lUID;
}
foreach my $rUID (keys %$rModified) {
- msg("local($mailbox): WARNING: No match for modified remote UID $rUID. Downloading again.");
+ msg("local($mailbox)", "WARNING: No match for modified remote UID $rUID. Downloading again.");
push @rMissing, $rUID;
}
@@ -815,7 +812,7 @@ sub sync_known_messages($$) {
my ($rUID) = $STH_GET_REMOTE_UID->fetchrow_array();
die if defined $STH_GET_REMOTE_UID->fetchrow_arrayref(); # sanity check
if (!defined $rUID) {
- msg("remote($mailbox): WARNING: No match for vanished local UID $lUID. Ignoring.");
+ msg("remote($mailbox)", "WARNING: No match for vanished local UID $lUID. Ignoring.");
}
elsif (!exists $rVanished{$rUID}) {
push @rToRemove, $rUID;
@@ -826,7 +823,7 @@ sub sync_known_messages($$) {
my ($lUID) = $STH_GET_LOCAL_UID->fetchrow_array();
die if defined $STH_GET_LOCAL_UID->fetchrow_arrayref(); # sanity check
if (!defined $lUID) {
- msg("local($mailbox): WARNING: No match for vanished remote UID $rUID. Ignoring.");
+ msg("local($mailbox)", "WARNING: No match for vanished remote UID $rUID. Ignoring.");
}
elsif (!exists $lVanished{$lUID}) {
push @lToRemove, $lUID;
@@ -861,7 +858,7 @@ sub sync_known_messages($$) {
my ($rUID) = $STH_GET_REMOTE_UID->fetchrow_array();
die if defined $STH_GET_REMOTE_UID->fetchrow_arrayref(); # sanity check
if (!defined $rUID) {
- msg("remote($mailbox): WARNING: No match for modified local UID $lUID. Try '--repair'.");
+ msg("remote($mailbox)", "WARNING: No match for modified local UID $lUID. Try '--repair'.");
}
elsif (defined (my $rFlags = $rModified->{$rUID})) {
unless ($lFlags eq $rFlags) {
@@ -882,7 +879,7 @@ sub sync_known_messages($$) {
my ($lUID) = $STH_GET_LOCAL_UID->fetchrow_array();
die if defined $STH_GET_LOCAL_UID->fetchrow_arrayref(); # sanity check
if (!defined $lUID) {
- msg("local($mailbox): WARNING: No match for modified remote UID $rUID. Try '--repair'.");
+ msg("local($mailbox)", "WARNING: No match for modified remote UID $rUID. Try '--repair'.");
}
elsif (!exists $lModified->{$lUID}) {
# conflicts are taken care of above
@@ -915,7 +912,7 @@ sub callback_new_message($$$$;$$$) {
my $length = length $mail->{RFC822};
if ($length == 0) {
- msg("$name($mailbox): WARNING: Ignoring new 0-length message (UID $mail->{UID})");
+ msg("$name($mailbox)", "WARNING: Ignoring new 0-length message (UID $mail->{UID})");
return;
}
@@ -951,7 +948,8 @@ sub callback_new_message_flush($$$@) {
my ($lUIDs, $rUIDs) = $name eq 'local' ? (\@sUID,\@tUID) : (\@tUID,\@sUID);
for (my $k=0; $k<=$#messages; $k++) {
- logger("Adding mapping (lUID,rUID) = ($lUIDs->[$k],$rUIDs->[$k]) for $mailbox") if $CONFIG{debug};
+ logger(undef, "Adding mapping (lUID,rUID) = ($lUIDs->[$k],$rUIDs->[$k]) for $mailbox")
+ if $CONFIG{debug};
$STH_INSERT_MAPPING->execute($idx, $lUIDs->[$k], $rUIDs->[$k]);
}
$DBH->commit(); # commit only once per batch
@@ -965,26 +963,32 @@ sub callback_new_message_flush($$$@) {
# the given UIDs.
sub sync_messages($$;$$) {
my ($idx, $mailbox, $lIgnore, $rIgnore) = @_;
- my ($buff, $bufflen, @lUIDs);
-
- # get new messages from remote (except @$rIgnore) and APPEND them to local
- ($buff, $bufflen) = ([], 0);
- undef $buff if $lIMAP->incapable('MULTIAPPEND');
- $rIMAP->pull_new_messages(sub($) {
- callback_new_message($idx, $mailbox, 'remote', shift, \@lUIDs, $buff, \$bufflen)
- }, @{$rIgnore // []});
- push @lUIDs, callback_new_message_flush($idx, $mailbox, 'remote', @$buff)
- if defined $buff and @$buff;
-
- # get new messages from local (except @$lIgnore and the newly allocated local
- # UIDs @lUIDs) and APPEND them to remote
- ($buff, $bufflen) = ([], 0);
- undef $buff if $rIMAP->incapable('MULTIAPPEND');
- $lIMAP->pull_new_messages(sub($) {
- callback_new_message($idx, $mailbox, 'local', shift, undef, $buff, \$bufflen)
- }, @{$lIgnore // []}, @lUIDs);
- callback_new_message_flush($idx, $mailbox, 'local', @$buff)
- if defined $buff and @$buff;
+
+ my %ignore = (local => ($lIgnore // []), remote => ($rIgnore // []));
+ my $loop;
+ do {
+ # get new messages from $source (except @{$ignore{$source}}) and APPEND them to $target
+ foreach my $source (qw/remote local/) { # pull remote mails first
+ my $target = $source eq 'remote' ? 'local' : 'remote';
+ my $buff = [] unless ($target eq 'local' ? $lIMAP : $rIMAP)->incapable('MULTIAPPEND');
+ my $bufflen = 0;
+ my @tUIDs;
+
+ ($source eq 'remote' ? $rIMAP : $lIMAP)->pull_new_messages(sub($) {
+ callback_new_message($idx, $mailbox, $source, shift, \@tUIDs, $buff, \$bufflen)
+ }, @{$ignore{$source}});
+
+ push @tUIDs, callback_new_message_flush($idx, $mailbox, $source, @$buff)
+ if defined $buff and @$buff;
+ push @{$ignore{$target}}, @tUIDs;
+
+ $loop = @tUIDs ? 1 : 0;
+ }
+ # since $source modifies $target's UIDNEXT upon new mails, we
+ # need to check again the first $source (remote) whenever the
+ # last one (local) added new messages to it
+ }
+ while ($loop);
# both local and remote UIDNEXT are now up to date; proceed with
# pending flag updates and vanished messages
@@ -992,8 +996,10 @@ sub sync_messages($$;$$) {
# don't store the new UIDNEXTs before to avoid downloading these
# mails again in the event of a crash
- $STH_UPDATE_LOCAL->execute($lIMAP->get_cache( qw/UIDNEXT HIGHESTMODSEQ/), $idx);
- $STH_UPDATE_REMOTE->execute($rIMAP->get_cache(qw/UIDNEXT HIGHESTMODSEQ/), $idx);
+ $STH_UPDATE_LOCAL->execute($lIMAP->get_cache( qw/UIDNEXT HIGHESTMODSEQ/), $idx) or
+ msg('database', "WARNING: Can't update remote UIDNEXT/HIGHESTMODSEQ for $mailbox");
+ $STH_UPDATE_REMOTE->execute($rIMAP->get_cache(qw/UIDNEXT HIGHESTMODSEQ/), $idx) or
+ msg('database', "WARNING: Can't update remote UIDNEXT/HIGHESTMODSEQ for $mailbox");
$DBH->commit();
}
@@ -1023,7 +1029,7 @@ my ($MAILBOX, $IDX);
$STH_LIST_INTERRUPTED->execute();
while (defined (my $row = $STH_LIST_INTERRUPTED->fetchrow_arrayref())) {
($IDX, $MAILBOX) = @$row;
- msg("Resuming interrupted sync for $MAILBOX");
+ msg(undef, "Resuming interrupted sync for $MAILBOX");
my %lUIDs;
$STH_GET_INTERRUPTED_BY_IDX->execute($IDX);
@@ -1098,10 +1104,7 @@ while (@REPAIR) {
$rIMAP->select($MAILBOX);
repair($IDX, $MAILBOX);
}
-if ($CONFIG{repair}) {
- cleanup();
- exit 0;
-}
+exit 0 if $CONFIG{repair};
while(1) {
@@ -1139,18 +1142,17 @@ while(1) {
elsif (sync_known_messages($IDX, $MAILBOX)) {
# sync updates to known messages before fetching new messages
# get_cache is safe after pull_update
- $STH_UPDATE_LOCAL_HIGHESTMODSEQ->execute( $lIMAP->get_cache('HIGHESTMODSEQ'), $IDX);
- $STH_UPDATE_REMOTE_HIGHESTMODSEQ->execute($rIMAP->get_cache('HIGHESTMODSEQ'), $IDX);
+ $STH_UPDATE_LOCAL_HIGHESTMODSEQ->execute( $lIMAP->get_cache('HIGHESTMODSEQ'), $IDX) or
+ msg('database', "WARNING: Can't update local HIGHESTMODSEQ for $MAILBOX");
+ $STH_UPDATE_REMOTE_HIGHESTMODSEQ->execute($rIMAP->get_cache('HIGHESTMODSEQ'), $IDX) or
+ msg('database', "WARNING: Can't update remote HIGHESTMODSEQ for $MAILBOX");
$DBH->commit();
}
sync_messages($IDX, $MAILBOX);
}
}
# clean state!
- if ($CONFIG{oneshot}) {
- cleanup();
- exit 0;
- }
+ exit 0 unless $CONFIG{watch};
wait_notifications(900);
}
diff --git a/imapsync.1 b/imapsync.1
index f4f6965..8c22222 100644
--- a/imapsync.1
+++ b/imapsync.1
@@ -10,8 +10,7 @@ imapsync \- IMAP-to-IMAP synchronization program for QRESYNC-capable servers
.SH DESCRIPTION
.PP
.B imapsync\fR performs stateful synchronization between two IMAP4rev1
-servers, then (unless the flag \fB\-\-oneshot\fR is set) keeps both
-connection open and wait for new changes to arrive.
+servers.
Such synchronization is made possible by the QRESYNC extension from
[RFC7162]; for convenience reasons support for LIST\-EXTENDED [RFC5258],
LIST\-STATUS [RFC5819] and UIDPLUS [RFC4315] is also required.
@@ -77,15 +76,6 @@ providing extra arguments limits the synchronization to the given
\fIMAILBOX\fRes only.
.PP
-In its default mode (unless the flag \fB\-\-oneshot\fR or
-\fB\-\-repair\fR is set), \fBimapsync\fR does not exit once all
-mailboxes have been synchronized. Instead, it keeps both connection
-open and uses the NOTIFY command from [RFC5465] to be notified of new
-changes (on any mailbox) as soon as they arrive. If no update is sent
-in 15 minutes, a NOOP command is issued in order not to trigger the
-servers' inactivity timeout and be logged out.
-
-.PP
If the synchronization was interrupted during a previous run while some
messages were being replicated (but before the UIDNEXT or HIGHESTMODSEQ
values have been updated), \fBimapsync\fR performs a \(lqfull
@@ -104,13 +94,6 @@ Specify an alternate configuration file. Relative paths start from
environment variable is unset.
.TP
-.B \-1\fR, \fB\-\-oneshot\fR
-Exit as soon as all mailboxes are synchronized, instead of passively
-waiting for updates from the open connections.
-Using \fB\-\-oneshot\fR removes the requirement that IMAP servers must
-advertise support the NOTIFY extension [RFC5465].
-
-.TP
.B \-\-repair
List the database anomalies and try to repair them.
This is done by performing a so\-called \(lqfull synchronization\(rq,
diff --git a/imapsync.service b/imapsync.service
index e3a47e4..af63425 100644
--- a/imapsync.service
+++ b/imapsync.service
@@ -6,4 +6,4 @@ After=network.target
ExecStart=/usr/bin/imapsync
[Install]
-WantedBy=multi-user.target
+WantedBy=default.target
diff --git a/lib/Net/IMAP/Sync.pm b/lib/Net/IMAP/Sync.pm
index 26303a6..3216483 100644
--- a/lib/Net/IMAP/Sync.pm
+++ b/lib/Net/IMAP/Sync.pm
@@ -657,12 +657,12 @@ sub append($$@) {
my @uids;
foreach (split /,/, $uidset) {
if (/\A([0-9]+)\z/) {
- $UIDNEXT = $1 + 1 if $UIDNEXT <= $1;
+ $UIDNEXT = $1 + 1 if defined $UIDNEXT and $UIDNEXT <= $1;
push @uids, $1;
} elsif (/\A([0-9]+):([0-9]+)\z/) {
my ($min, $max) = $1 <= $2 ? ($1,$2) : ($2,$1);
push @uids, ($min .. $max);
- $UIDNEXT = $max + 1 if $UIDNEXT <= $max;
+ $UIDNEXT = $max + 1 if defined $UIDNEXT and $UIDNEXT <= $max;
} else {
$self->panic($_);
}
@@ -736,7 +736,6 @@ sub slurp($) {
# select(2) to block/timeout due to the raw socket not being
# ready.
unless (ref $stdout eq 'IO::Socket::SSL' and $stdout->pending() > 0) {
- my $sel = IO::Select::->new($stdout);
my ($ok) = $self->{_SEL_OUT}->can_read(0);
return $read unless defined $ok;
}