From 7c60d18df5a87e9de3ac2baa058308587deee242 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 9 May 2018 03:38:07 +0200 Subject: Improve wording. --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index f2fee83..bb355e0 100644 --- a/Changelog +++ b/Changelog @@ -1,7 +1,7 @@ interimap (0.4) UNRELEASED * pullimap: replace non RFC 5321-compliant envelope sender addresses - (received by the IMAP FETCH ENVELOPE command) by the null sender address + (received by the IMAP FETCH ENVELOPE command) with the null address <>. + Library: new API idle_start() and idle_stop(). + Add support for untagged ESEARCH responses from RFC 4731. -- cgit v1.2.3 From 567fc180a3e76716816ea9af5a066bad2ea8c01a Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 May 2018 03:35:13 +0200 Subject: Improve ESEARCH response parsing for full RFC 4466 compatibility. --- lib/Net/IMAP/InterIMAP.pm | 51 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 3270108..67b3ce5 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -597,7 +597,7 @@ sub incapable($@) { # Issue an UID SEARCH command with the given $criterion. For the "normal" # UID SEARCH command from RFC 3501, return the list of matching UIDs; # for the extended UID SEARCH command from RFC 4731 (ensuring ESEARCH -# capability is the caller's responsibility), return an "UID" +# capability is the caller's responsibility), return an optional "UID" # indicator followed by a hash containing search data pairs. sub search($$) { my ($self, $crit) = @_; @@ -1946,7 +1946,7 @@ sub _send($$;&) { } else { my $set = $$command =~ /\AUID (?:FETCH|STORE) ([0-9:,*]+)/ ? $1 - : $$command =~ /\AUID SEARCH / ? "\"$tag\"" # RFC 4466's tag-string + : $$command =~ /\AUID SEARCH / ? $tag # for RFC 4466's tag-string : undef; $self->_recv($tag, $callback, $cmd, $set); } @@ -2234,6 +2234,21 @@ sub _envelope($$) { return \@envelope; } +# Parse and consume an RFC 4466 tagged-ext-comp plus a trailing parenthesis +sub _tagged_ext_comp($$$) { + my ($self, $stream, $ret) = @_; + my $v = $$stream =~ s/\A\(// ? $self->_tagged_ext_comp(\$_, []) + : $self->_astring(\$_); + push @$ret, $v; + if ($$stream =~ s/\A\)//) { + return $ret; + } elsif ($$stream =~ s/\A //) { + $self->_tagged_ext_comp(\$_, $ret) + } else { + $self->panic($$stream); + } +} + # $self->_resp($buf, [$callback, $cmd, $set] ) # Parse an untagged response line or a continuation request line. # (The trailing CRLF must be removed.) The internal cache is @@ -2288,16 +2303,30 @@ sub _resp($$;&$$) { elsif (/\ASEARCH((?: [0-9]+)*)\z/) { $callback->(split(/ /, ($1 =~ s/^ //r))) if defined $callback and $cmd eq 'SEARCH'; } - elsif (defined $set and s/\AESEARCH \(TAG \Q$set\E\)( UID)?//) { - my $uid = $1; - my %ret; # RFC 4731 + elsif (s/\AESEARCH( |\z)/$1/) { + my $tag = $1 if s/\A \(TAG \"($RE_ASTRING_CHAR+)\"\)//; + my $uid = s/\A UID// ? "UID" : undef; + my @ret; while ($_ ne '') { - $self->fail("RFC 4731 violation in ESEARCH response") - # XXX RFC 4466's tagged-ext-comp unsupported - unless s/\A ($RE_ATOM_CHAR+) ([0-9,:]+)//; - $ret{uc $1} = $2; + # RFC 4466 "tagged-ext-label" is a valid RFC 3501 "atom" + s/\A ($RE_ATOM_CHAR+) // or $self->panic(); + my $label = uc($1); + my $value; + if (s/\A([0-9,:]+)//) { + # RFC 4466 tagged-ext-simple + $value = $1; + } elsif (s/\A\(//) { + # RFC 4466 "(" [tagged-ext-comp] ")" + $value = s/\A\)// ? [] : $self->_tagged_ext_comp(\$_, []); + } else { + $self->panic(); + } + # don't use a hash since some extensions might give more + # than one response for a same key + push @ret, $label => $value; } - $callback->($uid, %ret) if defined $callback and $cmd eq 'SEARCH'; + $callback->($uid, @ret) if defined $callback and $cmd eq 'SEARCH' + and defined $set and $set eq $tag; } elsif (s/\ALIST \((\\?$RE_ATOM_CHAR+(?: \\?$RE_ATOM_CHAR+)*)?\) ("(?:\\[\x22\x5C]|[\x01-\x09\x0B\x0C\x0E-\x21\x23-\x5B\x5D-\x7F])"|NIL) //) { my ($delim, $attrs) = ($2, $1); @@ -2306,7 +2335,7 @@ sub _resp($$;&$$) { $self->panic($_) unless $_ eq ''; $mailbox = 'INBOX' if uc $mailbox eq 'INBOX'; # INBOX is case-insensitive undef $delim if uc $delim eq 'NIL'; - $delim =~ s/\A"(.*)"\Z/$1/ if defined $delim; + $delim =~ s/\A"(.*)"\z/$1/ if defined $delim; $self->_update_cache_for($mailbox, DELIMITER => $delim); $self->_update_cache_for($mailbox, LIST_ATTRIBUTES => \@attrs); $callback->($mailbox, $delim, @attrs) if defined $callback and $cmd eq 'LIST'; -- cgit v1.2.3 From 15cd4f6ea5074ffb33de8670bd1877603e504289 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 May 2018 17:56:45 +0200 Subject: pullimap: use extended SEARCH (RFC 4731) when available --- Changelog | 2 ++ pullimap | 18 ++++++++++++++---- pullimap.md | 11 ++++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Changelog b/Changelog index bb355e0..7939fa2 100644 --- a/Changelog +++ b/Changelog @@ -5,6 +5,8 @@ interimap (0.4) UNRELEASED <>. + Library: new API idle_start() and idle_stop(). + Add support for untagged ESEARCH responses from RFC 4731. + + pullimap: Use extended SEARCH commands (RFC 4731) if supported by + the server to search old mail and EXPUNGE them. - Ensure the lower bound of UID ranges is at least 1. - Fix manpage generation with pandoc >=2.1. diff --git a/pullimap b/pullimap index dca8c49..736bbff 100755 --- a/pullimap +++ b/pullimap @@ -249,10 +249,20 @@ sub purge() { my @now = gmtime($now - $days*86400); my @m = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; # RFC 3501's date-month my $date = sprintf("%02d-%s-%04d", $now[3], $m[$now[4]], $now[5]+1900); - my @uid = $IMAP->search("UID $set BEFORE $date"); - - $set = @uid ? compact_set(@uid) : undef; - $IMAP->log("Removing ".($#uid+1)." UID(s) $set") if defined $set and !$CONFIG{quiet}; + my $ext = $IMAP->incapable('ESEARCH') ? undef : [qw/COUNT ALL/]; + my @uid = $IMAP->search((defined $ext ? "RETURN (".join(' ', @$ext).') ' : '') + ."UID $set BEFORE $date"); + my $count; + if (defined $ext) { + my ($uid_indicator, %resp) = @uid; + $IMAP->panic() unless defined $uid_indicator and $uid_indicator = 'UID'; + $count = $resp{COUNT} // $IMAP->panic(); + $set = $resp{ALL}; # MUST NOT be present if there are no matches + } else { + $count = $#uid+1; + $set = $count == 0 ? undef : compact_set(@uid); + } + $IMAP->log("Removing $count UID(s) $set") if $count > 0 and !$CONFIG{quiet}; } if (defined $set) { diff --git a/pullimap.md b/pullimap.md index cb2a07a..3e8aae3 100644 --- a/pullimap.md +++ b/pullimap.md @@ -121,9 +121,10 @@ Valid options are: the IMAP server. (The value is at best 24h accurate due to the IMAP `SEARCH` criterion ignoring time and timezone.) If *purge-after* is set to `0` then messages are deleted immediately - after delivery. Otherwise `pullimap` issues an IMAP `SEARCH` - command to list old messages; if `--idle` is set then the `SEARCH` - command is issued again every 12 hours. + after delivery. Otherwise `pullimap` issues an IMAP `SEARCH` (or + extended `SEARCH` on server advertizing the [`ESEARCH`][RFC 4731] + capability) command to list old messages; if `--idle` is set then + the `SEARCH` command is issued again every 12 hours. *type* @@ -339,6 +340,9 @@ Standards [RFC 4315], December 2005. * A. Gulbrandsen, _The IMAP `COMPRESS` Extension_, [RFC 4978], August 2007. + * A. Melnikov and D. Cridland, _IMAP4 Extension to SEARCH Command for + Controlling What Kind of Information Is Returned_, + [RFC 4731], November 2006. * R. Siemborski and A. Gulbrandsen, _IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response_, [RFC 4959], September 2007. @@ -358,6 +362,7 @@ Standards [RFC 4978]: https://tools.ietf.org/html/rfc4978 [RFC 1928]: https://tools.ietf.org/html/rfc1928 [RFC 1929]: https://tools.ietf.org/html/rfc1929 +[RFC 4731]: https://tools.ietf.org/html/rfc4731 [INI file]: https://en.wikipedia.org/wiki/INI_file [`fetchmail`(1)]: http://www.fetchmail.info/ -- cgit v1.2.3 From 5358f99ab2a67120de7dc67159e6d2fa26d5f026 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 May 2018 23:54:01 +0200 Subject: typofix --- pullimap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pullimap.md b/pullimap.md index 3e8aae3..06035ac 100644 --- a/pullimap.md +++ b/pullimap.md @@ -122,7 +122,7 @@ Valid options are: `SEARCH` criterion ignoring time and timezone.) If *purge-after* is set to `0` then messages are deleted immediately after delivery. Otherwise `pullimap` issues an IMAP `SEARCH` (or - extended `SEARCH` on server advertizing the [`ESEARCH`][RFC 4731] + extended `SEARCH` on servers advertizing the [`ESEARCH`][RFC 4731] capability) command to list old messages; if `--idle` is set then the `SEARCH` command is issued again every 12 hours. -- cgit v1.2.3 From dd7edb8eac0c11fb8168f5028c8b6d8706cc8fdb Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 19:55:33 +0100 Subject: pullimap, interimap: Use $XDG_CONFIG_HOME/$NAME/config as config file. --- Changelog | 4 ++++ README | 2 +- interimap | 35 +++++++++++++++-------------------- interimap.md | 10 +++++----- lib/Net/IMAP/InterIMAP.pm | 36 +++++++++++++++++++++++++++++++----- pullimap | 35 +++++++++++++++-------------------- pullimap.md | 10 +++++----- 7 files changed, 76 insertions(+), 56 deletions(-) diff --git a/Changelog b/Changelog index 7939fa2..7f033c8 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,10 @@ interimap (0.4) UNRELEASED * pullimap: replace non RFC 5321-compliant envelope sender addresses (received by the IMAP FETCH ENVELOPE command) with the null address <>. + * pullimap, interimap: take configuration filename (default: "config") + relative to $XDG_CONFIG_HOME/$NAME (or ~/.config/$NAME), to comply + with the XDG specification. Thus the previous default config file + $XDG_CONFIG_HOME/$NAME should become $XDG_CONFIG_HOME/$NAME/config. + Library: new API idle_start() and idle_stop(). + Add support for untagged ESEARCH responses from RFC 4731. + pullimap: Use extended SEARCH commands (RFC 4731) if supported by diff --git a/README b/README index 6c3ae34..70bf1df 100644 --- a/README +++ b/README @@ -52,7 +52,7 @@ the AUTHENTICATE command. For instance the following configuration snippet saves bandwidth and brings a significant speed gain compared to type=imaps. - local: $XDG_CONFIG_HOME/interimap: + local: $XDG_CONFIG_HOME/interimap/config: [remote] type = tunnel command = /usr/bin/ssh user@imap.example.net diff --git a/interimap b/interimap index 049b564..0f34bf9 100755 --- a/interimap +++ b/interimap @@ -30,7 +30,7 @@ use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/; use List::Util 'first'; use lib 'lib'; -use Net::IMAP::InterIMAP qw/read_config compact_set/; +use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/; # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; @@ -63,20 +63,24 @@ my $COMMAND = do { }; 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} or defined $CONFIG{notify}); -usage(1) if $CONFIG{target} and !(defined $COMMAND and ($COMMAND eq 'delete'or $COMMAND eq 'rename')); +usage(1) if $CONFIG{target} and !(defined $COMMAND and ($COMMAND eq 'delete' or $COMMAND eq 'rename')); $CONFIG{watch} = $CONFIG{notify} ? 900 : 60 if (defined $CONFIG{watch} or $CONFIG{notify}) and !$CONFIG{watch}; @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/ - ); +my $CONF = do { + my $conffile = delete($CONFIG{config}) // "config"; + $conffile = xdg_basedir( XDG_CONFIG_HOME => ".config", $NAME, $conffile ); + read_config( $conffile + , [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/ + ); +}; my ($DBFILE, $LOGGER_FD); { @@ -84,16 +88,7 @@ my ($DBFILE, $LOGGER_FD); $DBFILE //= $CONF->{remote}->{host}.'.db' if defined $CONF->{remote}; $DBFILE //= $CONF->{local}->{host}. '.db' if defined $CONF->{local}; die "Missing option database" unless defined $DBFILE; - - unless ($DBFILE =~ /\A\//) { - my $dir = ($ENV{XDG_DATA_HOME} // "$ENV{HOME}/.local/share") .'/'. $NAME; - $dir =~ /\A(\/\p{Print}+)\z/ or die "Insecure $dir"; - $dir = $1; - $DBFILE = $dir .'/'. $DBFILE; - unless (-d $dir) { - mkdir $dir, 0700 or die "Can't mkdir $dir: $!\n"; - } - } + $DBFILE = xdg_basedir( XDG_DATA_HOME => ".local/share", $NAME, $DBFILE ); if (defined $CONF->{_} and defined $CONF->{_}->{logfile}) { require 'POSIX.pm'; diff --git a/interimap.md b/interimap.md index 4a321f1..ca83dba 100644 --- a/interimap.md +++ b/interimap.md @@ -140,8 +140,8 @@ Options `--config=`*FILE* : Specify an alternate [configuration file](#configuration-file). - Relative paths start from *$XDG_CONFIG_HOME*, or *~/.config* if the - `XDG_CONFIG_HOME` environment variable is unset. + Relative paths start from *$XDG_CONFIG_HOME/interimap*, or *~/.config/interimap* + if the `XDG_CONFIG_HOME` environment variable is unset. `--target={local,remote,database}` @@ -192,9 +192,9 @@ Configuration file ================== Unless told otherwise by the `--config=FILE` command-line option, -`interimap` reads its configuration from *$XDG_CONFIG_HOME/interimap* -(or *~/.config/interimap* if the `XDG_CONFIG_HOME` environment variable -is unset) as an [INI file]. +`interimap` reads its configuration from *$XDG_CONFIG_HOME/interimap/config* +(or *~/.config/interimap/config* if the `XDG_CONFIG_HOME` environment +variable is unset) as an [INI file]. The syntax of the configuration file is a series of `OPTION=VALUE` lines organized under some `[SECTION]`; lines starting with a ‘#’ or ‘;’ character are ignored as comments. diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 67b3ce5..7b0a2be 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -22,7 +22,7 @@ use strict; use Compress::Raw::Zlib qw/Z_OK Z_FULL_FLUSH Z_SYNC_FLUSH MAX_WBITS/; use Config::Tiny (); -use Errno 'EINTR'; +use Errno qw/EEXIST EINTR/; use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/; use Net::SSLeay (); use List::Util qw/all first/; @@ -35,7 +35,7 @@ BEGIN { Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); - our @EXPORT_OK = qw/read_config compact_set $IMAP_text $IMAP_cond + our @EXPORT_OK = qw/xdg_basedir read_config compact_set $IMAP_text $IMAP_cond slurp is_dirty has_new_mails/; } @@ -76,6 +76,35 @@ my $CRLF = "\x0D\x0A"; ############################################################################# # Utilities +# xdg_basedir($xdg_variable, $default, $subdir, $path) +# Return $path if $path is absolute. Otherwise, return +# "$ENV{$xdg_variable}/$subdir/$path" (resp. "~/$default/$subdir/path" +# if the "$xdg_variable" environment variable is not set). +# An error is raised if "$ENV{$xdg_variable}" (resp. "~/$default") is +# not an existing absolute directory. +# If "$ENV{$xdg_variable}/$subdir" doesn't exist, it is created with +# mode 0700. +sub xdg_basedir($$$$) { + my ($xdg_variable, $default, $subdir, $path) = @_; + $path =~ /\A(\p{Print}+)\z/ or die "Insecure $path"; + $path = $1; + return $path if $path =~ /\A\//; + + my $basedir = $ENV{$xdg_variable}; + unless (defined $basedir) { + my @getent = getpwuid($>); + $basedir = $getent[7] ."/". $default; + } + die "No such directory: ", $basedir unless -d $basedir; + $basedir .= "/".$subdir; + $basedir =~ /\A(\/\p{Print}+)\z/ or die "Insecure $basedir"; + $basedir = $1; + unless (mkdir ($basedir, 0700)) { + die "Couldn't create $basedir: $!\n" unless $! == EEXIST; + } + return $basedir ."/". $path; +} + # read_config($conffile, $sections, %opts) # Read $conffile's default section, then each section in the array # reference $section (which takes precedence). %opts extends %OPTIONS @@ -85,9 +114,6 @@ sub read_config($$%) { my $sections = shift; my %opts = (%OPTIONS, @_); - $conffile = ($ENV{XDG_CONFIG_HOME} // "$ENV{HOME}/.config") .'/'. $conffile - unless $conffile =~ /\A\//; # relative path - die "No such config file $conffile\n" unless defined $conffile and -f $conffile and -r $conffile; diff --git a/pullimap b/pullimap index 736bbff..e666114 100755 --- a/pullimap +++ b/pullimap @@ -31,7 +31,7 @@ use List::Util 'first'; use Socket qw/PF_INET PF_INET6 SOCK_STREAM/; use lib 'lib'; -use Net::IMAP::InterIMAP qw/read_config compact_set/; +use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/; # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; @@ -59,15 +59,19 @@ usage(1) unless $#ARGV == 0 and $ARGV[0] ne '_'; ####################################################################### # Read and validate configuration # -my $CONF = read_config( delete $CONFIG{config} // $NAME, - , [$ARGV[0]] - , statefile => qr/\A(\P{Control}+)\z/ - , mailbox => qr/\A([\x01-\x7F]+)\z/ - , 'deliver-method' => qr/\A([ls]mtp:\[.*\]:\d+)\z/ - , 'deliver-ehlo' => qr/\A(\P{Control}+)\z/ - , 'deliver-rcpt' => qr/\A(\P{Control}+)\z/ - , 'purge-after' => qr/\A(\d*)\z/ - )->{$ARGV[0]}; +my $CONF = do { + my $conffile = delete($CONFIG{config}) // "config"; + $conffile = xdg_basedir( XDG_CONFIG_HOME => ".config", $NAME, $conffile ); + read_config( $conffile + , [$ARGV[0]] + , statefile => qr/\A(\P{Control}+)\z/ + , mailbox => qr/\A([\x01-\x7F]+)\z/ + , 'deliver-method' => qr/\A([ls]mtp:\[.*\]:\d+)\z/ + , 'deliver-ehlo' => qr/\A(\P{Control}+)\z/ + , 'deliver-rcpt' => qr/\A(\P{Control}+)\z/ + , 'purge-after' => qr/\A(\d*)\z/ + )->{$ARGV[0]}; +}; my ($MAILBOX, $STATE); do { @@ -75,16 +79,7 @@ do { my $statefile = $CONF->{statefile} // $ARGV[0]; die "Missing option statefile" unless defined $statefile; - $statefile = $statefile =~ /\A(\p{Print}+)\z/ ? $1 : die "Insecure $statefile"; - - unless ($statefile =~ /\A\//) { - my $dir = ($ENV{XDG_DATA_HOME} // "$ENV{HOME}/.local/share") .'/'. $NAME; - $dir = $dir =~ /\A(\/\p{Print}+)\z/ ? $1 : die "Insecure $dir"; - $statefile = $dir .'/'. $statefile; - unless (-d $dir) { - mkdir $dir, 0700 or die "Can't mkdir $dir: $!\n"; - } - } + $statefile = xdg_basedir( XDG_DATA_HOME => ".local/share", $NAME, $statefile ); sysopen($STATE, $statefile, O_CREAT|O_RDWR|O_DSYNC, 0600) or die "Can't open $statefile: $!"; # XXX we need to pack the struct flock manually: not portable! diff --git a/pullimap.md b/pullimap.md index 06035ac..eac8efa 100644 --- a/pullimap.md +++ b/pullimap.md @@ -32,8 +32,8 @@ Options `--config=`*FILE* : Specify an alternate [configuration file](#configuration-file). - Relative paths start from *$XDG_CONFIG_HOME*, or *~/.config* if the - `XDG_CONFIG_HOME` environment variable is unset. + Relative paths start from *$XDG_CONFIG_HOME/pullimap*, or *~/.config/pullimap* + if the `XDG_CONFIG_HOME` environment variable is unset. `--idle`[`=`*seconds*] @@ -74,9 +74,9 @@ Configuration file ================== Unless told otherwise by the `--config=FILE` command-line option, -`pullimap` reads its configuration from *$XDG_CONFIG_HOME/pullimap* (or -*~/.config/pullimap* if the `XDG_CONFIG_HOME` environment variable is -unset) as an [INI file]. +`pullimap` reads its configuration from *$XDG_CONFIG_HOME/pullimap/config* +(or *~/.config/pullimap/config* if the `XDG_CONFIG_HOME` environment variable +is unset) as an [INI file]. The syntax of the configuration file is a series of `OPTION=VALUE` lines organized under some `[SECTION]`; lines starting with a ‘#’ or ‘;’ character are ignored as comments. -- cgit v1.2.3 From 452ae91f06bec4c21e7ba7b7bc4309b089f7afe3 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 19:55:58 +0100 Subject: Specify minimum Perl and Net::SSLeay versions. --- Changelog | 1 + INSTALL | 4 ++-- interimap | 1 + lib/Net/IMAP/InterIMAP.pm | 2 +- pullimap | 1 + 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Changelog b/Changelog index 7f033c8..c8a0d51 100644 --- a/Changelog +++ b/Changelog @@ -13,6 +13,7 @@ interimap (0.4) UNRELEASED the server to search old mail and EXPUNGE them. - Ensure the lower bound of UID ranges is at least 1. - Fix manpage generation with pandoc >=2.1. + - Specify minimum Perl and Net::SSLeay versions. -- Guilhem Moulin Tue, 06 Dec 2016 17:37:01 +0100 diff --git a/INSTALL b/INSTALL index 458b7c2..69afb26 100644 --- a/INSTALL +++ b/INSTALL @@ -1,4 +1,4 @@ -InterIMAP depends on the following Perl modules: +InterIMAP depends on Perl >=5.20 and the following Perl modules: - Compress::Raw::Zlib (core module) - Config::Tiny @@ -8,7 +8,7 @@ InterIMAP depends on the following Perl modules: - Getopt::Long (core module) - MIME::Base64 (core module) if authentication is required - List::Util (core module) - - Net::SSLeay + - Net::SSLeay >=1.73 - POSIX (core module) - Socket (core module) - Time::HiRes (core module) if 'logfile' is set diff --git a/interimap b/interimap index 0f34bf9..41a0cb9 100755 --- a/interimap +++ b/interimap @@ -18,6 +18,7 @@ # along with this program. If not, see . #---------------------------------------------------------------------- +use v5.14.2; use strict; use warnings; diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 7b0a2be..f783ea7 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -24,7 +24,7 @@ use Compress::Raw::Zlib qw/Z_OK Z_FULL_FLUSH Z_SYNC_FLUSH MAX_WBITS/; use Config::Tiny (); use Errno qw/EEXIST EINTR/; use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/; -use Net::SSLeay (); +use Net::SSLeay 1.73 (); use List::Util qw/all first/; use POSIX ':signal_h'; use Socket qw/SOCK_STREAM IPPROTO_TCP AF_INET AF_INET6 SOCK_RAW :addrinfo/; diff --git a/pullimap b/pullimap index e666114..e044df5 100755 --- a/pullimap +++ b/pullimap @@ -21,6 +21,7 @@ use strict; use warnings; +use v5.20.2; our $VERSION = '0.3'; my $NAME = 'pullimap'; -- cgit v1.2.3 From 8c0f59f3f3add18a9ee8be767f54f43996ddbd78 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:16:53 +0100 Subject: Note on migrations. --- README | 2 +- interimap | 4 ++++ interimap.md | 4 +++- pullimap | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README b/README index 70bf1df..cb34ef1 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ InterIMAP is a fast bidirectional synchronization program for QRESYNC-capable IMAP4rev1 servers. PullIMAP retrieves messages a remote IMAP mailbox and -deliver them to a SMTP session. Consult the manuals for more information. +deliver them to an SMTP session. Consult the manuals for more information. https://guilhem.org/man/interimap.1.html https://guilhem.org/man/pullimap.1.html diff --git a/interimap b/interimap index 41a0cb9..0fb6c1c 100755 --- a/interimap +++ b/interimap @@ -162,6 +162,10 @@ $DBH->do('PRAGMA foreign_keys = ON'); # also, lUID < local.UIDNEXT and rUID < remote.UIDNEXT (except for interrupted syncs) # mapping.idx must be found among local.idx (and remote.idx) ], + + # We have no version number in the schema, but if we ever need a + # migration, we'll add a new table, and assume version 1.0 if + # the table is missing. ); # Invariants: diff --git a/interimap.md b/interimap.md index ca83dba..1831d39 100644 --- a/interimap.md +++ b/interimap.md @@ -398,7 +398,9 @@ Known bugs and limitations * Using `interimap` on two identical servers with a non-existent or empty *database* will duplicate each message due to the absence of - local ↔ remote UID association. + local ↔ remote UID association. Hence one needs to manually empty + the mail store on one end when migrating to `interimap` from another + synchronisation solution. * `interimap` is single threaded and doesn't use IMAP command pipelining. Synchronization could be boosted up by sending diff --git a/pullimap b/pullimap index e044df5..bd9b1cf 100755 --- a/pullimap +++ b/pullimap @@ -88,6 +88,11 @@ do { fcntl($STATE, F_SETLK, $struct_flock) or die "Can't lock $statefile: $!"; my $flags = fcntl($STATE, F_GETFD, 0) or die "fcntl F_GETFD: $!"; fcntl($STATE, F_SETFD, $flags | FD_CLOEXEC) or die "fcntl F_SETFD: $!"; + + # We have no version number in the statefile, but if we ever need a + # migration, we'll add a 1-byte header for the version number, and + # assume version 1.0 if the size of the file is a multiple of 4 + # bytes. (We can also use the fact that bytes 5 to 8 are never all 0.) }; -- cgit v1.2.3 From d19ba0a20d0d7a7ec288c93df329210b31bf3c51 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:23:20 +0100 Subject: interimap.service: use --watch=60 rather than --notify. --- Changelog | 2 ++ interimap.sample | 2 +- interimap.service | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index c8a0d51..eb187b6 100644 --- a/Changelog +++ b/Changelog @@ -14,6 +14,8 @@ interimap (0.4) UNRELEASED - Ensure the lower bound of UID ranges is at least 1. - Fix manpage generation with pandoc >=2.1. - Specify minimum Perl and Net::SSLeay versions. + - interimap.service: use --watch=60 rather than --notify, because + dovecot's NOTIFY extension doesn't seem to work so well as of 2.2.27. -- Guilhem Moulin Tue, 06 Dec 2016 17:37:01 +0100 diff --git a/interimap.sample b/interimap.sample index 8cd0a29..f771e54 100644 --- a/interimap.sample +++ b/interimap.sample @@ -1,4 +1,4 @@ -#database = imap.guilhem.org.db +#database = imap.example.org.db #list-mailbox = "*" list-select-opts = SUBSCRIBED ignore-mailbox = ^virtual/ diff --git a/interimap.service b/interimap.service index 6e487d4..8e9915f 100644 --- a/interimap.service +++ b/interimap.service @@ -4,7 +4,7 @@ Wants=network-online.target After=network-online.target [Service] -ExecStart=/usr/bin/interimap --notify +ExecStart=/usr/bin/interimap --watch=60 RestartSec=10min Restart=on-failure -- cgit v1.2.3 From 1492f504316eb506e72f7a84ecd23207bb07e226 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 21 Jan 2019 17:54:53 +0100 Subject: pullimap, interimap: don't autocreate statefile or database in long-lived mode. --- Changelog | 3 +++ interimap | 27 +++++++++++++++++---------- pullimap | 6 +++++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Changelog b/Changelog index eb187b6..b4fb931 100644 --- a/Changelog +++ b/Changelog @@ -11,6 +11,9 @@ interimap (0.4) UNRELEASED + Add support for untagged ESEARCH responses from RFC 4731. + pullimap: Use extended SEARCH commands (RFC 4731) if supported by the server to search old mail and EXPUNGE them. + + pullimap, interimap: don't autocreate statefile or database in + long-lived mode (when --watch or --idle is set). Instead, an error + is raised if the statefile or database doesn't exist. - Ensure the lower bound of UID ranges is at least 1. - Fix manpage generation with pandoc >=2.1. - Specify minimum Perl and Net::SSLeay versions. diff --git a/interimap b/interimap index 0fb6c1c..7ac6b05 100755 --- a/interimap +++ b/interimap @@ -27,6 +27,7 @@ my $NAME = 'interimap'; use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat bundling auto_version/; use DBI (); +use DBD::SQLite::Constants ':file_open'; use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/; use List::Util 'first'; @@ -121,18 +122,24 @@ $SIG{TERM} = sub { cleanup(); exit 0; }; ############################################################################# # Open the database and create tables -$DBH = DBI::->connect("dbi:SQLite:dbname=$DBFILE", undef, undef, { - AutoCommit => 0, - RaiseError => 1, - sqlite_see_if_its_a_number => 1, # see if the bind values are numbers or not - sqlite_use_immediate_transaction => 1, -}); -$DBH->sqlite_busy_timeout(250); -$DBH->do('PRAGMA locking_mode = EXCLUSIVE'); -$DBH->do('PRAGMA foreign_keys = ON'); - { + my $dbi_data_source = "dbi:SQLite:dbname=".$DBFILE; + my %dbi_attrs = ( + AutoCommit => 0, + RaiseError => 1, + sqlite_see_if_its_a_number => 1, # see if the bind values are numbers or not + sqlite_use_immediate_transaction => 1, + sqlite_open_flags => SQLITE_OPEN_READWRITE + ); + # don't auto-create in long-lived mode + $dbi_attrs{sqlite_open_flags} |= SQLITE_OPEN_CREATE unless defined $CONFIG{watch}; + + $DBH = DBI::->connect($dbi_data_source, undef, undef, \%dbi_attrs); + $DBH->sqlite_busy_timeout(250); + $DBH->do('PRAGMA locking_mode = EXCLUSIVE'); + $DBH->do('PRAGMA foreign_keys = ON'); + my @schema = ( mailboxes => [ q{idx INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT}, diff --git a/pullimap b/pullimap index bd9b1cf..b752b14 100755 --- a/pullimap +++ b/pullimap @@ -82,7 +82,11 @@ do { die "Missing option statefile" unless defined $statefile; $statefile = xdg_basedir( XDG_DATA_HOME => ".local/share", $NAME, $statefile ); - sysopen($STATE, $statefile, O_CREAT|O_RDWR|O_DSYNC, 0600) or die "Can't open $statefile: $!"; + my $mode = O_RDWR | O_DSYNC; + # don't auto-create in long-lived mode + $mode |= O_CREAT unless defined $CONFIG{idle}; + + sysopen($STATE, $statefile, $mode, 0600) or die "Can't open $statefile: $!"; # XXX we need to pack the struct flock manually: not portable! my $struct_flock = pack('s!s!l!l!i!', F_WRLCK, SEEK_SET, 0, 0, 0); fcntl($STATE, F_SETLK, $struct_flock) or die "Can't lock $statefile: $!"; -- cgit v1.2.3 From 522666e8e2556d89c25013ce17d6db49e75443ef Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 21 Jan 2019 23:59:55 +0100 Subject: Net::IMAP::InterIMAP: add support for TLSv1.3 (on recent enough Net::SSLeay). Also, change "SSL_protocols" default value from "!SSLv2 !SSLv3" to "!SSLv2 !SSLv3 !TLSv1 !TLSv1.1". I.e., only enable TLSv1.2 and later, which is the default in Debian's OpenSSL as of 1.1.1-2, cf. https://tracker.debian.org/news/998835/accepted-openssl-111-2-source-into-unstable/ . --- Changelog | 3 +++ interimap.md | 7 ++++--- lib/Net/IMAP/InterIMAP.pm | 27 +++++++++++++++++---------- pullimap.md | 7 ++++--- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Changelog b/Changelog index b4fb931..9fa46dd 100644 --- a/Changelog +++ b/Changelog @@ -7,6 +7,9 @@ interimap (0.4) UNRELEASED relative to $XDG_CONFIG_HOME/$NAME (or ~/.config/$NAME), to comply with the XDG specification. Thus the previous default config file $XDG_CONFIG_HOME/$NAME should become $XDG_CONFIG_HOME/$NAME/config. + * Library: add support for TLSv1.3 (on recent enough Net::SSLeay), and + change "SSL_protocols" default value from "!SSLv2 !SSLv3" to "!SSLv2 + !SSLv3 !TLSv1 !TLSv1.1". + Library: new API idle_start() and idle_stop(). + Add support for untagged ESEARCH responses from RFC 4731. + pullimap: Use extended SEARCH commands (RFC 4731) if supported by diff --git a/interimap.md b/interimap.md index 1831d39..4d85eaf 100644 --- a/interimap.md +++ b/interimap.md @@ -332,9 +332,10 @@ Valid options are: : A space-separated list of SSL protocols to enable or disable (if prefixed with an exclamation mark `!`. Known protocols are `SSLv2`, - `SSLv3`, `TLSv1`, `TLSv1.1`, and `TLSv1.2`. Enabling a protocol is - a short-hand for disabling all other protocols. - (Default: `!SSLv2 !SSLv3`, i.e., only enable TLSv1 and above.) + `SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`. Enabling a + protocol is a short-hand for disabling all other protocols. + (Default: `!SSLv2 !SSLv3 !TLSv1 !TLSv1.1`, i.e., only enable TLSv1.2 + and above.) *SSL_cipher_list* diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index f783ea7..3d8bd97 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -45,7 +45,7 @@ my $RE_ATOM_CHAR = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]/; my $RE_ASTRING_CHAR = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5D-\x7A\x7C-\x7E]/; my $RE_TEXT_CHAR = qr/[\x01-\x09\x0B\x0C\x0E-\x7F]/; -my $RE_SSL_PROTO = qr/(?:SSLv[23]|TLSv1|TLSv1\.[0-2])/; +my $RE_SSL_PROTO = qr/(?:SSLv[23]|TLSv1|TLSv1\.[0-3])/; # Map each option to a regexp validating its values. my %OPTIONS = ( @@ -1599,13 +1599,19 @@ sub _ssl_verify($$$) { return $ok; # 1=accept cert, 0=reject } -my %SSL_proto = ( - 'SSLv2' => Net::SSLeay::OP_NO_SSLv2(), - 'SSLv3' => Net::SSLeay::OP_NO_SSLv3(), - 'TLSv1' => Net::SSLeay::OP_NO_TLSv1(), - 'TLSv1.1' => Net::SSLeay::OP_NO_TLSv1_1(), - 'TLSv1.2' => Net::SSLeay::OP_NO_TLSv1_2() -); +my %SSL_proto; +BEGIN { + sub _append_ssl_proto($$) { + my ($k, $v) = @_; + $SSL_proto{$k} = $v if defined $v; + } + _append_ssl_proto( "SSLv2", eval { Net::SSLeay::OP_NO_SSLv2() } ); + _append_ssl_proto( "SSLv3", eval { Net::SSLeay::OP_NO_SSLv3() } ); + _append_ssl_proto( "TLSv1", eval { Net::SSLeay::OP_NO_TLSv1() } ); + _append_ssl_proto( "TLSv1.1", eval { Net::SSLeay::OP_NO_TLSv1_1() } ); + _append_ssl_proto( "TLSv1.2", eval { Net::SSLeay::OP_NO_TLSv1_2() } ); + _append_ssl_proto( "TLSv1.3", eval { Net::SSLeay::OP_NO_TLSv1_3() } ); +} # $self->_start_ssl($socket) # Upgrade the $socket to SSL/TLS. @@ -1614,7 +1620,7 @@ sub _start_ssl($$) { my $ctx = Net::SSLeay::CTX_new() or $self->panic("Failed to create SSL_CTX $!"); my $ssl_options = Net::SSLeay::OP_SINGLE_DH_USE() | Net::SSLeay::OP_SINGLE_ECDH_USE(); - $self->{SSL_protocols} //= q{!SSLv2 !SSLv3}; + $self->{SSL_protocols} //= q{!SSLv2 !SSLv3 !TLSv1 !TLSv1.1}; my ($proto_include, $proto_exclude) = (0, 0); foreach (split /\s+/, $self->{SSL_protocols}) { my $neg = s/^!// ? 1 : 0; @@ -1629,7 +1635,7 @@ sub _start_ssl($$) { $proto_exclude |= $x; } my @proto_exclude = grep { ($proto_exclude & $SSL_proto{$_}) != 0 } keys %SSL_proto; - $self->log("Disabling SSL protocol: ".join(', ', sort @proto_exclude)) if $self->{debug}; + $self->log("Disabling SSL protocols: ".join(', ', sort @proto_exclude)) if $self->{debug}; $ssl_options |= $SSL_proto{$_} foreach @proto_exclude; $ssl_options |= Net::SSLeay::OP_NO_COMPRESSION(); @@ -1675,6 +1681,7 @@ sub _start_ssl($$) { $v == 0x0301 ? 'TLSv1' : $v == 0x0302 ? 'TLSv1.1' : $v == 0x0303 ? 'TLSv1.2' : + $v == 0x0304 ? 'TLSv1.3' : '??'), $v)); $self->log(sprintf('SSL cipher: %s (%d bits)' diff --git a/pullimap.md b/pullimap.md index eac8efa..a367dd1 100644 --- a/pullimap.md +++ b/pullimap.md @@ -198,9 +198,10 @@ Valid options are: : A space-separated list of SSL protocols to enable or disable (if prefixed with an exclamation mark `!`. Known protocols are `SSLv2`, - `SSLv3`, `TLSv1`, `TLSv1.1`, and `TLSv1.2`. Enabling a protocol is - a short-hand for disabling all other protocols. - (Default: `!SSLv2 !SSLv3`, i.e., only enable TLSv1 and above.) + `SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`. Enabling a + protocol is a short-hand for disabling all other protocols. + (Default: `!SSLv2 !SSLv3 !TLSv1 !TLSv1.1`, i.e., only enable TLSv1.2 + and above.) *SSL_cipher_list* -- cgit v1.2.3 From 24362150d8ff8245885f47b8cd8362c2d2e39b46 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:26:01 +0100 Subject: Change e-mail address. --- Changelog | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog b/Changelog index 9fa46dd..c3aa444 100644 --- a/Changelog +++ b/Changelog @@ -23,7 +23,7 @@ interimap (0.4) UNRELEASED - interimap.service: use --watch=60 rather than --notify, because dovecot's NOTIFY extension doesn't seem to work so well as of 2.2.27. - -- Guilhem Moulin Tue, 06 Dec 2016 17:37:01 +0100 + -- Guilhem Moulin Tue, 06 Dec 2016 17:37:01 +0100 interimap (0.3) upstream; @@ -46,7 +46,7 @@ interimap (0.3) upstream; - interimap: when resuming a sync, only consider UIDs greater than a known UIDNEXT. - -- Guilhem Moulin Thu, 01 Dec 2016 14:37:50 +0100 + -- Guilhem Moulin Thu, 01 Dec 2016 14:37:50 +0100 interimap (0.2) upstream; @@ -80,10 +80,10 @@ interimap (0.2) upstream; * Display source UIDs upon APPEND. Previously only target UIDs where displayed in non-debug mode. - -- Guilhem Moulin Wed, 09 Sep 2015 00:44:35 +0200 + -- Guilhem Moulin Wed, 09 Sep 2015 00:44:35 +0200 interimap (0.1) upstream; * Initial public release. Development was started in July 2015. - -- Guilhem Moulin Mon, 07 Sep 2015 17:14:42 +0200 + -- Guilhem Moulin Mon, 07 Sep 2015 17:14:42 +0200 -- cgit v1.2.3 From e85db0b140eb2c56fa46231829a1bfe6f98ede7d Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:29:58 +0100 Subject: Bump version number. --- interimap | 4 ++-- lib/Net/IMAP/InterIMAP.pm | 2 +- pullimap | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interimap b/interimap index 7ac6b05..deb0949 100755 --- a/interimap +++ b/interimap @@ -22,7 +22,7 @@ use v5.14.2; use strict; use warnings; -our $VERSION = '0.3'; +our $VERSION = '0.4'; my $NAME = 'interimap'; use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat bundling auto_version/; @@ -32,7 +32,7 @@ use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/; use List::Util 'first'; use lib 'lib'; -use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/; +use Net::IMAP::InterIMAP 0.0.4 qw/xdg_basedir read_config compact_set/; # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 3d8bd97..8280fb2 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -16,7 +16,7 @@ # along with this program. If not, see . #---------------------------------------------------------------------- -package Net::IMAP::InterIMAP v0.0.3; +package Net::IMAP::InterIMAP v0.0.4; use warnings; use strict; diff --git a/pullimap b/pullimap index b752b14..233172f 100755 --- a/pullimap +++ b/pullimap @@ -22,7 +22,7 @@ use strict; use warnings; use v5.20.2; -our $VERSION = '0.3'; +our $VERSION = '0.4'; my $NAME = 'pullimap'; use Errno 'EINTR'; @@ -32,7 +32,7 @@ use List::Util 'first'; use Socket qw/PF_INET PF_INET6 SOCK_STREAM/; use lib 'lib'; -use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/; +use Net::IMAP::InterIMAP 0.0.4 qw/xdg_basedir read_config compact_set/; # Clean up PATH $ENV{PATH} = join ':', qw{/usr/bin /bin}; -- cgit v1.2.3 From 64dd58fb503c5ee31da3bcbe52313c723730a122 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:42:56 +0100 Subject: Bump copyright years. --- README | 4 ++-- interimap | 2 +- lib/Net/IMAP/InterIMAP.pm | 2 +- pullimap | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README b/README index cb34ef1..06f328f 100644 --- a/README +++ b/README @@ -82,6 +82,6 @@ usage with regular INET sockets (type=imaps or type=imap). _______________________________________________________________________ -InterIMAP is Copyright© 2015 Guilhem Moulin ⟨guilhem@fripost.org⟩, and -licensed for use under the GNU General Public License version 3 or +InterIMAP is Copyright© 2015-2018 Guilhem Moulin ⟨guilhem@fripost.org⟩, +and licensed for use under the GNU General Public License version 3 or later. See ‘COPYING’ for specific terms and distribution information. diff --git a/interimap b/interimap index deb0949..454d311 100755 --- a/interimap +++ b/interimap @@ -2,7 +2,7 @@ #---------------------------------------------------------------------- # Fast bidirectional synchronization for QRESYNC-capable IMAP servers -# Copyright © 2015,2016 Guilhem Moulin +# Copyright © 2015-2018 Guilhem Moulin # # 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 diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 8280fb2..a773f08 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -1,6 +1,6 @@ #---------------------------------------------------------------------- # A minimal IMAP4 client for QRESYNC-capable servers -# Copyright © 2015 Guilhem Moulin +# Copyright © 2015-2018 Guilhem Moulin # # 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 diff --git a/pullimap b/pullimap index 233172f..495b99e 100755 --- a/pullimap +++ b/pullimap @@ -2,7 +2,7 @@ #---------------------------------------------------------------------- # Pull mails from an IMAP mailbox and deliver them to a SMTP session -# Copyright © 2016 Guilhem Moulin +# Copyright © 2016-2018 Guilhem Moulin # # 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 -- cgit v1.2.3 From 41a6694c6d0582c7fffd682926e964ff3fa39b7b Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Jan 2019 20:31:14 +0100 Subject: Prepare new release. --- Changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index c3aa444..5a9074a 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -interimap (0.4) UNRELEASED +interimap (0.4) upstream; * pullimap: replace non RFC 5321-compliant envelope sender addresses (received by the IMAP FETCH ENVELOPE command) with the null address @@ -23,7 +23,7 @@ interimap (0.4) UNRELEASED - interimap.service: use --watch=60 rather than --notify, because dovecot's NOTIFY extension doesn't seem to work so well as of 2.2.27. - -- Guilhem Moulin Tue, 06 Dec 2016 17:37:01 +0100 + -- Guilhem Moulin Sun, 20 Jan 2019 20:30:08 +0100 interimap (0.3) upstream; -- cgit v1.2.3