}
my @COMMANDS = qw/repair delete rename/;
-usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q target=s@ debug help|h/, @COMMANDS);
+usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q target=s@ debug help|h watch:i/, @COMMANDS);
usage(0) if $CONFIG{help};
my $COMMAND = do {
my @command = grep {exists $CONFIG{$_}} @COMMANDS;
$command[0]
};
usage(1) if defined $COMMAND and (($COMMAND eq 'delete' and !@ARGV) or $COMMAND eq 'rename' and $#ARGV != 1);
+usage(1) if defined $COMMAND and defined $CONFIG{watch};
+$CONFIG{watch} = 60 if defined $CONFIG{watch} and $CONFIG{watch} == 0;
@ARGV = map {uc $_ eq 'INBOX' ? 'INBOX' : $_ } @ARGV; # INBOX is case-insensitive
#############################################################################
# Connect to the local and remote IMAP servers
+my $LIST = '"" ';
+my @LIST_PARAMS;
+if (!defined $COMMAND or $COMMAND eq 'repair') {
+ $LIST = '('.uc($CONF->{_}->{'list-select-opts'}).') '.$LIST if defined $CONF->{_}->{'list-select-opts'};
+ $LIST .= (defined $CONF->{_}->{'list-mailbox'} ? '('.$CONF->{_}->{'list-mailbox'}.')' : '*') unless @ARGV;
+ @LIST_PARAMS = ('SUBSCRIBED', 'STATUS (UIDVALIDITY UIDNEXT HIGHESTMODSEQ)');
+}
+$LIST .= $#ARGV == 0 ? Net::IMAP::InterIMAP::quote($ARGV[0])
+ : ('('.join(' ',map {Net::IMAP::InterIMAP::quote($_)} @ARGV).')') if @ARGV;
+
+
my $IMAP;
foreach my $name (qw/local remote/) {
my %config = %{$CONF->{$name}};
# 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
-
- my $list = '"" ';
- my @params;
- if (!defined $COMMAND or $COMMAND eq 'repair') {
- $list = '('.uc($CONF->{_}->{'list-select-opts'}).') '.$list if defined $CONF->{_}->{'list-select-opts'};
- $list .= (defined $CONF->{_}->{'list-mailbox'} ? '('.$CONF->{_}->{'list-mailbox'}.')' : '*') unless @ARGV;
- @params = ('SUBSCRIBED', 'STATUS (UIDVALIDITY UIDNEXT HIGHESTMODSEQ)');
- }
- $list .= $#ARGV == 0 ? Net::IMAP::InterIMAP::quote($ARGV[0])
- : ('('.join(' ',map {Net::IMAP::InterIMAP::quote($_)} @ARGV).')') if @ARGV;
- @{$IMAP->{$name}}{qw/mailboxes delims/} = $client->list($list, @params);
}
+@{$IMAP->{$_}}{qw/mailboxes delims/} = $IMAP->{$_}->{client}->list($LIST, @LIST_PARAMS) for qw/local remote/;
##############################################################################
#
# Synchronize mailbox and subscription lists
my @MAILBOXES;
-{
+sub sync_mailbox_list() {
my %mailboxes;
$mailboxes{$_} = 1 foreach keys %{$IMAP->{local}->{mailboxes}};
$mailboxes{$_} = 1 foreach keys %{$IMAP->{remote}->{mailboxes}};
}
}
}
+
+sync_mailbox_list();
my ($lIMAP, $rIMAP) = map {$IMAP->{$_}->{client}} qw/local remote/;
-undef $IMAP;
#############################################################################
}
}
# clean state!
- exit 0 unless defined $COMMAND and $COMMAND eq 'watch';
- wait_notifications(900);
+ exit 0 unless $CONFIG{watch};
+
+ # we need to issue a NOOP command or go back to AUTH state since the
+ # LIST command may not report the correct HIGHESTMODSEQ value for
+ # the mailbox currently selected.
+ if (defined $MAILBOX) {
+ # Prefer UNSELECT over NOOP commands as it requires a single command per cycle
+ if ($lIMAP->incapable('UNSELECT') or $rIMAP->incapable('UNSELECT')) {
+ $_->noop() foreach ($lIMAP, $rIMAP);
+ } else {
+ $_->unselect() foreach ($lIMAP, $rIMAP);
+ undef $MAILBOX;
+ }
+ }
+
+ sleep $CONFIG{watch};
+ # Refresh the mailbox list and status
+ @{$IMAP->{$_}}{qw/mailboxes delims/} = $IMAP->{$_}->{client}->list($LIST, @LIST_PARAMS) for qw/local remote/;
+ sync_mailbox_list();
}
END {