#----------------------------------------------------------------------
# Fast bidirectional synchronization for QRESYNC-capable IMAP servers
-# Copyright © 2015 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2015,2016 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
usage(1) if $CONFIG{target} and !(defined $COMMAND and ($COMMAND eq 'delete'or $COMMAND eq 'rename'));
$CONFIG{watch} = 60 if defined $CONFIG{watch} and $CONFIG{watch} == 0;
@ARGV = map {uc $_ eq 'INBOX' ? 'INBOX' : $_ } @ARGV; # INBOX is case-insensitive
+die "Invalid mailbox name $_" foreach grep !/\A([\x01-\x7F]+)\z/, @ARGV;
my $CONF = read_config( delete $CONFIG{config} // $NAME
, [qw/_ local remote/]
, database => qr/\A(\P{Control}+)\z/
- , logfile => qr/\A(\/\P{Control}+)\z/
, 'list-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
, 'list-select-opts' => qr/\A([\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]+)\z/
, 'ignore-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
command => qr/\A(\P{Control}+)\z/,
'null-stderr' => qr/\A(YES|NO)\z/i,
compress => qr/\A($RE_ATOM_CHAR+(?: $RE_ATOM_CHAR+)*)\z/,
+ logfile => qr/\A(\/\P{Control}+)\z/,
SSL_protocols => qr/\A(!?$RE_SSL_PROTO(?: !?$RE_SSL_PROTO)*)\z/,
SSL_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+)\z/,
SSL_cipherlist => qr/\A(\P{Control}+)\z/,
if (defined $self->{'logger-fd'}->fileno and defined $self->{'logger-fd'}->fileno
and $self->{'logger-fd'}->fileno != fileno STDERR) {
my ($s, $us) = Time::HiRes::gettimeofday();
- $prefix = POSIX::strftime("%b %e %H:%M:%S", localtime($s)).".$us ";
+ $prefix = POSIX::strftime("%b %e %H:%M:%S", localtime($s)).".$us";
+ $prefix .= ' ' if defined $self->{name} or $self->{_STATE} eq 'SELECTED';
}
- $prefix .= $self->{name} // '';
+ $prefix .= $self->{name} if defined $self->{name};
$prefix .= "($self->{_SELECTED})" if $self->{_STATE} eq 'SELECTED';
$prefix .= ': ' unless $prefix eq '';
$self->{'logger-fd'}->say($prefix, @_);
}
+# $self->silent_store($set, $mod, @flags)
+# Set / Add / Update the flags list on the UID $set.
+# /!\ there is no check that messages flags been set!
+sub silent_store($$$@) {
+ my $self = shift;
+ my $set = shift;
+ my $mod = shift;
+ $self->_send("UID STORE $set ${mod}FLAGS.SILENT (".join(' ', @_).")");
+}
+
+
#############################################################################
# Private methods
use List::Util 'first';
use lib 'lib';
-use Net::IMAP::InterIMAP 'read_config';
+use Net::IMAP::InterIMAP qw/read_config compact_set/;
my %CONFIG;
sub usage(;$) {
writeUID(1);
}
else {
+ # put the remaining UIDs in the @ignore list: these messages
+ # have already been delivered, but the process exited before the
+ # statefile was updated
while (defined (my $uid = readUID())) {
push @ignore, $uid;
}
}
+ # use BODY.PEEK[] so if something gets wrong, unpulled messages
+ # won't be marked as \Seen in the mailbox
my $attrs = join ' ', qw/ENVELOPE INTERNALDATE BODY.PEEK[]/;
my @uid;
writeUID($uid);
}, @ignore);
- # TODO mark (@ignore, @uid) as seen
+ # now that everything has been deliverd, mark @ignore and @uid as \Seen
+ $IMAP->silent_store(compact_set(@ignore, @uid), '+', '\Seen') if @ignore or @uid;
# update the statefile
sysseek($STATE, 4, SEEK_SET) // die "Can't seek: $!";