From 29b5824a2871fd539f46e84eecca8e55080895c8 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 Mar 2016 01:34:13 +0100 Subject: pullimap: check all print statements to the SMTP socket. --- pullimap | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pullimap b/pullimap index 02aedf6..c8d4018 100755 --- a/pullimap +++ b/pullimap @@ -159,7 +159,7 @@ sub sendmail($$) { if ($$rfc822 eq '') { # RFC 5321 section 4.1.1.4: if there was no mail data, the first # "\r\n" ends the DATA command itself - $SMTP->printflush("\r\n.\r\n"); + $SMTP->printflush("\r\n.\r\n") or die; } else { my $offset = 0; my $length = length($$rfc822); @@ -167,7 +167,7 @@ sub sendmail($$) { my $line = substr($$rfc822, $offset, $end-$offset); # RFC 5321 section 4.5.2: the character sequence "\r\n.\r\n" # ends the mail text and cannot be sent by the user - $SMTP->print($line eq ".\r\n" ? "..\r\n" : $line); + $SMTP->print($line eq ".\r\n" ? "..\r\n" : $line) or die; $offset = $end; } if ($offset < $length) { @@ -175,9 +175,9 @@ sub sendmail($$) { # have the receiving SMTP server recognize the "end of data" # condition. See RFC 5321 section 4.1.1.4 my $line = substr($$rfc822, $offset); - $SMTP->print(($line eq "." ? ".." : $line), "\r\n"); + $SMTP->print(($line eq "." ? ".." : $line), "\r\n") or die; } - $SMTP->printflush(".\r\n"); + $SMTP->printflush(".\r\n") or die; } smtp_resp('250'); } @@ -200,14 +200,14 @@ sub smtp_send(@) { push @code, shift // die; } if ($SMTP_PIPELINING) { # SMTP pipelining (RFC 2920) - print STDERR join('', map {"C: $_\n"} @cmd) if $CONFIG{debug}; - $SMTP->printflush(join('', map {"$_\r\n"} @cmd)); + print STDERR (map {"C: $_\n"} @cmd) if $CONFIG{debug}; + $SMTP->printflush(map {"$_\r\n"} @cmd) or die; @r = smtp_resp($_) foreach @code; } else { foreach (@cmd) { print STDERR "C: $_\n" if $CONFIG{debug}; - $SMTP->printflush("$_\r\n"); + $SMTP->printflush("$_\r\n") or die; @r = smtp_resp(shift(@code)); } } -- cgit v1.2.3 From 2432afb0df49d86ee4d3dff8d19a2d3094ecec28 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 Mar 2016 23:38:33 +0100 Subject: Net::IMAP::InterIMAP: change argument order in slurp and _resp. --- lib/Net/IMAP/InterIMAP.pm | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 73f55e8..33ad4ee 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -910,7 +910,7 @@ sub append($$@) { # $self->fetch($set, $flags, [$callback]) # Issue an UID FETCH command with the given UID $set, $flags, and # optional $callback. -sub fetch($$$$) { +sub fetch($$$;&) { my ($self, $set, $flags, $callback) = @_; $self->_send("UID FETCH $set $flags", $callback); } @@ -938,14 +938,14 @@ sub notify($@) { } -# $self->slurp([$cmd, $callback]) +# $self->slurp([$callback, $cmd]) # See if the server has sent some unprocessed data; try to as many # lines as possible, process them, and return the number of lines # read. # This is mostly useful when waiting for notifications while no # command is progress, cf. RFC 2177 (IDLE) or RFC 5465 (NOTIFY). -sub slurp($;$$) { - my ($self, $cmd, $callback) = @_; +sub slurp($;&$) { + my ($self, $callback, $cmd) = @_; my $ssl = $self->{_SSL}; my $read = 0; @@ -962,7 +962,7 @@ sub slurp($;$$) { return $read if $r == 0; # nothing more to read } my $x = $self->_getline(); - $self->_resp($x, $cmd, undef, $callback); + $self->_resp($x, $callback, $cmd); $read++; } } @@ -973,9 +973,10 @@ sub slurp($;$$) { # when the callback $stopwhen returns true. # Return false if the timeout was reached, and true if IDLE was # stopped due the callback. -sub idle($$$) { +sub idle($;$&) { my ($self, $timeout, $stopwhen) = @_; $timeout //= 1740; # 29 mins + my $callback = sub() {$timeout = -1 if $stopwhen->()}; $self->fail("Server did not advertise IDLE (RFC 2177) capability.") unless $self->_capable('IDLE'); @@ -984,7 +985,7 @@ sub idle($$$) { $self->_cmd_flush(); for (; $timeout > 0; $timeout--) { - $self->slurp('IDLE', sub() {$timeout = -1 if $stopwhen->()}); + $self->slurp($callback, 'IDLE'); sleep 1 if $timeout > 0; } @@ -1161,7 +1162,7 @@ sub pull_updates($;$) { } -# $self->pull_new_messages($callback, $attrs, @ignore) +# $self->pull_new_messages($attrs, $callback, @ignore) # FETCH new messages since the UIDNEXT found in the persistent cache # (or 1 in no such UIDNEXT is found), and process each response on the # fly with the callback. @@ -1171,7 +1172,7 @@ sub pull_updates($;$) { # Finally, update the UIDNEXT from the persistent cache to the value # found in the internal cache. # /!\ Use pull_updates afterwards to udpate the HIGHESTMODSEQ! -sub pull_new_messages($$$@) { +sub pull_new_messages($$&@) { my $self = shift; my $attrs = shift; my $callback = shift; @@ -1921,7 +1922,7 @@ sub _send($$;&) { # provided, is used to process each untagged response. $command and # $set can further limit the set of responses to apply the callback # to. -sub _recv($$;$&$) { +sub _recv($$;&$$) { my ($self, $tag, $callback, $cmd, $set) = @_; my $r; @@ -1937,7 +1938,7 @@ sub _recv($$;$&$) { last; } else { - $self->_resp($x, $cmd, $set, $callback); + $self->_resp($x, $callback, $cmd, $set); } } @@ -2196,18 +2197,18 @@ sub _envelope($$) { return \@envelope; } -# $self->_resp($buf, [$cmd, $set, $callback] ) +# $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 # automatically updated when needed. # If a command and callback are given, the callback is be executed # for each (parsed) responses associated with the command. -sub _resp($$;$$$) { +sub _resp($$;&$$) { my $self = shift; local $_ = shift; + my $callback = shift; my $cmd = shift; my $set = shift; - my $callback = shift; my $cache = $self->{_CACHE}->{$self->{_SELECTED}} if defined $self->{_SELECTED}; if (s/\A\* //) { -- cgit v1.2.3 From 7fa59faf5b62eb108555fb8185eacbd1df13d44a Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 10 Mar 2016 23:46:50 +0100 Subject: IDLE: fix race condition when an untagged response is received after the DONE --- lib/Net/IMAP/InterIMAP.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 33ad4ee..45253c1 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -992,7 +992,9 @@ sub idle($;$&) { # done idling $self->_cmd_extend('DONE'); $self->_cmd_flush(); - $self->_recv($tag); + # run the callback again to update the return value if we received + # untagged responses between the DONE and the tagged response + $self->_recv($tag, $callback, 'IDLE'); return $timeout < 0 ? 1 : 0; } -- cgit v1.2.3 From 95787b188cc324fbdb6c6022d9750ba552591559 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Fri, 11 Mar 2016 03:08:21 +0100 Subject: Convert pullimap's manpage to pandoc. --- Makefile | 29 +++++ pullimap.1 | 320 ------------------------------------------------------ pullimap.md | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+), 320 deletions(-) create mode 100644 Makefile delete mode 100644 pullimap.1 create mode 100644 pullimap.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a56a47 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +all: pullimap.1 + +# upper case the headers and remove the links +%.1: %.md + @pandoc -S -f markdown -t json "$<" | \ + jq ".[1][] |= if .t == \"Header\" then .c[2][] |= (if .t == \"Str\" then .c |= ascii_upcase else . end) else . end" | \ + jq " \ + def fixit: \ + if type == \"object\" then \ + if .t == \"Link\" then \ + if .c[2][0][0:7] == \"mailto:\" then . else .c[1][] end \ + else \ + map_values(fixit) \ + end \ + else if type == \"array\" then \ + map(fixit) \ + else \ + . \ + end \ + end; \ + map(fixit)" | \ + pandoc -sS -f json -t man -o "$@" + +install: + +clean: + rm -f pullimap.1 + +.PHONY: all install clean diff --git a/pullimap.1 b/pullimap.1 deleted file mode 100644 index 2d92e9d..0000000 --- a/pullimap.1 +++ /dev/null @@ -1,320 +0,0 @@ -.TH PULLIMAP "1" "MARCH 2016" "PullIMAP" "User Commands" - -.SH NAME -PullIMAP \- Pull mails from an IMAP mailbox and deliver them to a SMTP session - -.SH SYNOPSIS -.B pullimap\fR [\fB--config=\fIFILE\fR] [\fB--idle\fR[\fB=\fISECONDS\fR]] -[\fB--no-delivery\fR] [\fB--quiet\fR] \fISECTION\fR - - -.SH DESCRIPTION -.PP -.B PullIMAP\fR retrieves messages from an IMAP mailbox and deliver them -to a SMTP or LMTP transmission channel. -It can also remove old messages after a configurable retention period. - -.PP -A \fIstatefile\fR is used to keep track of the mailbox's UIDVALIDITY and -UIDNEXT values. While \fBPullIMAP\fR is running, the \fIstatefile\fR is -also used to keep track of UIDs being delivered, which avoids duplicate -deliveries if the process is interrupted. -See the \fBCONTROL FLOW\fR section below. - -.SH OPTIONS -.TP -.B \-\-config=\fR\fIFILE\fR -Specify an alternate configuration file. Relative paths start from -\fI$XDG_CONFIG_HOME\fR, or \fI~/.config\fR if the XDG_CONFIG_HOME -environment variable is unset. - -.TP -.B \fB\-\-idle\fR[\fB=\fR\fIseconds\fR] -Don't exit after a successful poll; instead, keep the connection open -and issue IDLE commands (requires an IMAP server supporting RFC 2177) to -watch for updates in the mailbox. -This also sets SO_KEEPALIVE on the socket. -Each IDLE is terminated after at most \fIseconds\fR (29 minutes by -default) to avoid being logged out for inactivity. - -.TP -.B \fB\-\-no\-delivery -Update the \fIstatefile\fR, but skip SMTP/LMTP delivery. This is mostly -useful for initializing the \fIstatefile\fR when migrating to -\fBPullIMAP\fR from another equivalent program such as \fIgetmail\fR(1) -or \fIfetchmail\fR(1). - -.TP -.B \-q\fR, \fB\-\-quiet\fR -Try to be quiet. - -.TP -.B \-\-debug -Turn on debug mode. Debug messages are written to the error output. -Note that this include all IMAP traffic (except literals). Depending on -the chosen authentication mechanism, this might include authentication -credentials. - -.TP -.B \-h\fR, \fB\-\-help\fR -Output a brief help and exit. - -.TP -.B \-\-version -Show the version number and exit. - -.SH CONFIGURATION FILE - -Unless told otherwise by the \fB\-\-config=\fR\fIFILE\fR option, -\fBPullIMAP\fR reads its configuration from -\fI$XDG_CONFIG_HOME/pullimap\fR (or \fI~/.config/pullimap\fR if the -XDG_CONFIG_HOME environment variable is unset) as an INI file. -The syntax of the configuration file is a series of -\fIOPTION\fR=\fIVALUE\fR lines organized under some \fI[SECTION]\fR; -lines starting with a \(oq#\(cq or \(oq;\(cq character are ignored as -comments. -Valid options are: - -.TP -.I statefile -State file to use to keep track of the \fImailbox\fR's UIDVALIDITY and -UIDNEXT values. -Relative paths start from \fI$XDG_DATA_HOME/pullimap\fR, or -\fI~/.local/share/pullimap\fR if the XDG_DATA_HOME environment variable -is unset. -(Default: \(lq\fISECTION\fR\)\(rq, where \fISECTION\fR is the section -name of the option.) - -.TP -.I mailbox -The IMAP mailbox to pull messages from. -Support for persistent message Unique Identifiers (UID) is required. -(Default: \(lqINBOX\)\(rq.) - -.TP -.I deliver\-method -\fR\fIprotocol\fR:\fI[address]\fI\fR:\fIport\fR where to deliver -messages. Both SMTP [RFC 5321] and LMTP [RFC 2030] are supported. -(Default: \(lqsmtp:[127.0.0.1]:25\)\(rq.) - -.TP -.I deliver\-ehlo -Hostname to use in EHLO or LHLO commands. -(Default: \(lq\fIlocalhost.localdomain\fR\)\(rq.) - - -.TP -.I deliver\-rcpt -Message recipient. -(Default: the username associated with the effective uid of the -\fBpullimap\fR process.) - -.TP -.I purge\-after -Retention period (in days), after which messages are removed from the -IMAP server. (The value is at best 24h accurate due to IMAP SEARCH -criterion ignoring time and timezone.) -If \fIpurge\-after\fR is set to \(lq0\(rq then messages are deleted -immediately after delivery. Otherwise \fBPullIMAP\fR issues an IMAP -SEARCH command to list old messages; if \fB\-\-idle\fR is set then the -SEARCH command is issued again every 12 hours. - -.TP -.I type -One of \(lqimap\(rq, \(lqimaps\(rq or \(lqtunnel\(rq. -\fItype\fR=imap and \fItype\fR=imaps are respectively used for IMAP and -IMAP over SSL/TLS connections over a INET socket. -\fItype\fR=tunnel causes \fBPullIMAP\fR to open a pipe to a -\fIcommand\fR instead of a raw socket. -Note that specifying \fItype\fR=tunnel in the \(lq[remote]\(rq section -makes the default \fIdatabase\fR to be \(lqlocalhost.db\(rq. -(Default: \(lqimaps\(rq.) - -.TP -.I host -Server hostname, for \fItype\fR=imap and \fItype\fR=imaps. -(Default: \(lqlocalhost\(rq.) - -.TP -.I port -Server port. -(Default: \(lq143\(rq for \fItype\fR=imap, \(lq993\(rq for -\fItype\fR=imaps.) - -.TP -.I proxy -An optional SOCKS proxy to use for TCP connections to the IMAP server -(\fItype\fR=imap and \fItype\fR=imaps only), formatted as -\(lq\fIprotocol\fR://[\fIuser\fR:\fIpassword\fR@]\fIproxyhost\fR[:\fIproxyport\fR]\(rq. -If \fIproxyport\fR is omitted, it is assumed at port 1080. -Only SOCKSv5 is supported, in two flavors: \(lqsocks5://\(rq to resolve -\fIhostname\fR locally, and \(lqsocks5h://\(rq to let the proxy resolve -\fIhostname\fR. - -.TP -.I command -Command to use for \fItype\fR=tunnel. Must speak the IMAP4rev1 protocol -on its standard output, and understand it on its standard input. - -.TP -.I STARTTLS -Whether to use the \(lqSTARTTLS\(rq directive to upgrade to a secure -connection. Setting this to \(lqYES\(rq for a server not advertising -the \(lqSTARTTLS\(rq capability causes \fBPullIMAP\fR to immediately -abort the connection. -(Ignored for \fItype\fRs other than \(lqimap\(rq. Default: \(lqYES\(rq.) - -.TP -.I auth -Space\-separated list of preferred authentication mechanisms. -\fBPullIMAP\fR uses the first mechanism in that list that is also -advertised (prefixed with \(lqAUTH=\(rq) in the server's capability list. -Supported authentication mechanisms are \(lqPLAIN\(rq and \(lqLOGIN\(rq. -(Default: \(lqPLAIN LOGIN\(rq.) - -.TP -.I username\fR, \fIpassword\fR -Username and password to authenticate with. Can be required for non -pre\-authenticated connections, depending on the chosen authentication -mechanism. - -.TP -.I compress -Whether to use the IMAP COMPRESS extension [RFC4978] for servers -advertising it. -(Default: \(lqYES\(rq.) - -.TP -.I null\-stderr -Whether to redirect \fIcommand\fR's standard error to \(lq/dev/null\(rq -for type \fItype\fR=tunnel. -(Default: \(lqNO\(rq.) - -.TP -.I SSL_protocols -A space-separated list of SSL protocols to enable or disable (if -prefixed with an exclamation mark \(oq!\(cq). Known protocols are -\(lqSSLv2\(rq, \(lqSSLv3\(rq, \(lqTLSv1\(rq, \(lqTLSv1.1\(rq, and -\(lqTLSv1.2\(rq. Enabling a protocol is a short-hand for disabling all -other protocols. -(Default: \(lq!SSLv2 !SSLv3\(rq, i.e., only enable TLSv1 and above.) - -.TP -.I SSL_cipher_list -The cipher list to send to the server. Although the server determines -which cipher suite is used, it should take the first supported cipher in -the list sent by the client. See \fBciphers\fR(1ssl) for more -information. - -.TP -.I SSL_fingerprint -Fingerprint of the server certificate (or its public key) in the form -\fIALGO\fR$\fIDIGEST_HEX\fR, where \fIALGO\fR is the used algorithm -(default \(lqsha256\(rq). -Attempting to connect to a server with a non-matching certificate -fingerprint causes \fBPullIMAP\fR to abort the connection during the -SSL/TLS handshake. - -.TP -.I SSL_verify -Whether to verify the server certificate chain. -Note that using \fISSL_fingerprint\fR to specify the fingerprint of the -server certificate is an orthogonal authentication measure as it ignores -the CA chain. -(Default: \(lqYES\(rq.) - -.TP -.I SSL_CApath -Directory to use for server certificate verification if -\(lq\fISSL_verify\fR=YES\(rq. -This directory must be in \(lqhash format\(rq, see \fBverify\fR(1ssl) -for more information. - -.TP -.I SSL_CAfile -File containing trusted certificates to use during server certificate -authentication if \(lq\fISSL_verify\fR=YES\(rq. - -.SH CONTROL FLOW -\fBPullIMAP\fR opens the \fIstatefile\fR corresponding to a given -configuration \fISECTION\fR with O_DSYNC to ensure that written data is -flushed to the underlying hardware by the time \fIwrite\fR(2) returns. -Moreover an exclusive lock is placed on the file descriptor immediately -after opening to prevent multiple \fBpullimap\fR processes from -accessing the \fIstatefile\fR concurrently. - -Each \fIstatefile\fR consists of a series of 32-bits big-endian -integers. Usually there are only two integers: -the first is the \fImailbox\fR's UIDVALIDITY value, and the second is -the \fImailbox\fR's last seen UIDNEXT value (\fBPullIMAP\fR then assumes -that all messages with UID smaller than this UIDNEXT value have already -been retrieved and delivered). -The IMAP4rev1 specification [RFC 3501] does not guaranty that untagged -FETCH responses are sent ordered by UID in response of an UID FETCH -command. Thus it would be unsafe for \fBPullIMAP\fR to update the -UIDNEXT value in the \fIstatefile\fR while the UID FETCH command is -progress. -Instead, for each untagged FETCH response received while while the UID -FETCH command is in progress, \fBPullIMAP\fR delivers the message BODY -to the SMTP or LMTP server specified with \fIdeliver\-method\fR then -appends the message UID to the \fIstatefile\fR. When the UID FETCH -command eventually terminates, \fBPullIMAP\fR updates the UIDNEXT value -in the \fIstatefile\fR and truncate the file down to 8 bytes. -Keeping track of message UIDs as they are received avoids duplicate in -the even of a crash or session loss while the UID FETCH command is in -progress. - -In more details, \fBPullIMAP\fR works as follows: - -.nr step 1 1 -.IP \n[step]. 4 -Issue an UID FETCH command to retrieve message ENVELOPE and BODY (and -UID) with UID bigger or equal than the UIDNEXT value found in the -\fIstatefile\fR. -While the UID FETCH command is in progress, perform the following -for each untagged FETCH response sent by the server: -.RS -\(bu -if no SMTP/LMTP transmission channel was opened, open one to the server -specified with \fIdeliver\-method\fR and send an EHLO (or LHO) command -with the domain given by \fIdeliver\-ehlo\fR; -.br -\(bu -perform a mail transaction (using SMTP pipelining [RFC 2920] if -possible) to send the retrieved message BODY to the SMTP or LMTP -session; and -.br -\(bu -append the message UID to the \fIstatefile\fR. -.RE - -.IP \n+[step]. -If a SMTP/LMTP transmission channel was opened, send a QUIT command to -close it gracefully. - -.IP \n+[step]. -Issue an UID STORE command to mark all retrieved messages (and stalled -UIDs found in the \fIstatefile\fR after the UIDNEXT value) as \\Seen. - -.IP \n+[step]. -Update the \fIstatefile\fR with the new UIDNEXT value. - -.IP \n+[step]. -Truncate the \fIstatefile\fR down to 8 bytes (so that it contains only -two 32-bits integers, respectively the \fImailbox\fR's UIDVALIDITY and -UIDNEXT values). - -.IP \n+[step]. -If \fB\-\-idle\fR was set, issue an IDLE command; stop idling and go -back to step 1. whenever a new message is received. - -.SH AUTHOR -.ie \n[www-html] \{\ - Written by -. MTO guilhem@fripost.org "Guilhem Moulin" . -\} -.el \{\ - Written by Guilhem Moulin -. MT guilhem@fripost.org -. ME . -\} diff --git a/pullimap.md b/pullimap.md new file mode 100644 index 0000000..54c6ce5 --- /dev/null +++ b/pullimap.md @@ -0,0 +1,355 @@ +% pullimap(1) +% [Guilhem Moulin](mailto:guilhem@fripost.org) +% March 2016 + +Name +==== + +PullIMAP - Pull mails from an IMAP mailbox and deliver them to a SMTP +session + +Synopsis +======== + +`pullimap` [**\-\-config=***FILE*] [**\-\-idle**[**=***SECONDS*]] +[**\-\-no-delivery**] [**\-\-quiet**] *SECTION* + +Description +=========== + +`pullimap` retrieves messages from an IMAP mailbox and deliver them to a +SMTP or LMTP transmission channel. It can also remove old messages +after a configurable retention period. + +A *statefile* is used to keep track of the mailbox's `UIDVALIDITY` and +`UIDNEXT` values. While `pullimap` is running, the *statefile* is also +used to keep track of UIDs being delivered, which avoids duplicate +deliveries in case the process is interrupted. +See the [**Control flow**](#control-flow) section below for details. + +Options +======= + +`--config=`*FILE* + +: Specify an alternate configuration file. Relative paths start from + *$XDG_CONFIG_HOME*, or *~/.config* if the `XDG_CONFIG_HOME` + environment variable is unset. + +`--idle`[`=`*seconds*] + +: Don't exit after a successful poll. Instead, keep the connection open + and issue `IDLE` commands (require an IMAP server supporting [RFC + 2177]) to watch for updates in the mailbox. This also enables + `SO_KEEPALIVE` on the socket. Each `IDLE` command is terminated after + at most *seconds* (29 minutes by default) to avoid being logged out + for inactivity. + +`--no-delivery` + +: Update the *statefile*, but skip SMTP/LMTP delivery. This is mostly + useful for initializing the *statefile* when migrating to + `pullimap` from another similar program such as [`fetchmail`(1)] or + [`getmail`(1)]. + +`-q`, `--quiet` + +: Try to be quiet. + +`--debug` + +: Turn on debug mode. Debug messages are written to the error output. + Note that this include all IMAP traffic (except literals). Depending + on the chosen authentication mechanism, this might include + authentication credentials. + +`-h`, `--help` + +: Output a brief help and exit. + +`--version` + +: Show the version number and exit. + + +Configuration file +================== + +Unless told otherwise by the `--config=FILE` 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]. +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. +Valid options are: + +*statefile* + +: State file to use to keep track of the *mailbox*'s `UIDVALIDITY` and + `UIDNEXT` values. Relative paths start from + *$XDG_DATA_HOME/pullimap*, or *~/.local/share/pullimap* if the + `XDG_DATA_HOME` environment variable is unset. + (Default: the parent section name of the option.) + +*mailbox* + +: The IMAP mailbox to pull messages from. + Support for persistent message Unique Identifiers (UID) is required. + (Default: `INBOX`.) + +*deliver-method* + +: `PROTOCOL:[ADDRESS]:PORT` where to deliver messages. Both [SMTP][RFC + 5321] and [LMTP][RFC 2033] servers are supported, and [SMTP + pipelining][RFC 2920] is used when possible. + (Default: `smtp:[127.0.0.1]:25`.) + +*deliver-ehlo* + +: Hostname to use in `EHLO` or `LHLO` commands. + (Default: `localhost.localdomain`.) + +*deliver-rcpt* + +: Message recipient. + (Default: the username associated with the effective uid of the + `pullimap` process.) + +*purge-after* + +: Retention period (in days), after which messages are removed from 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. + +*type* + +: One of `imap`, `imaps` or `tunnel`. + `type=imap` and `type=imaps` are respectively used for IMAP and IMAP + over SSL/TLS connections over a INET socket. + `type=tunnel` causes `pullimap` to open a pipe to a *command* instead + of a raw socket. + (Default: `imaps`.) + +*host* + +: Server hostname, for `type=imap` and `type=imaps`. + (Default: `localhost`.) + +*port* + +: Server port. + (Default: `143` for `type=imap`, `993` for + `type=imaps`.) + +*proxy* + +: An optional SOCKS proxy to use for TCP connections to the IMAP server + (`type=imap` and `type=imaps` only), formatted as + `PROTOCOL://[USER:PASSWORD@]PROXYHOST[:PROXYPORT]`. + If `PROXYPORT` is omitted, it is assumed at port 1080. + Only [SOCKSv5][RFC 1928] is supported (with optional + [username/password authentication][RFC 1929]), in two flavors: + `socks5://` to resolve *hostname* locally, and `socks5h://` to let the + proxy resolve *hostname*. + +*command* + +: Command to use for `type=tunnel`. Must speak the [IMAP4rev1 + protocol][RFC 3501] on its standard output, and understand it on its + standard input. + +*STARTTLS* + +: Whether to use the [`STARTTLS`][RFC 2595] directive to upgrade to a + secure connection. Setting this to `YES` for a server not advertising + the `STARTTLS` capability causes `pullimap` to immediately abort the + connection. + (Ignored for *type*s other than `imap`. Default: `YES`.) + +*auth* + +: Space-separated list of preferred authentication mechanisms. + `pullimap` uses the first mechanism in that list that is also + advertised (prefixed with `AUTH=`) in the server's capability list. + Supported authentication mechanisms are `PLAIN` and `LOGIN`. + (Default: `PLAIN LOGIN`.) + +*username*, *password* + +: Username and password to authenticate with. Can be required for non + pre-authenticated connections, depending on the chosen authentication + mechanism. + +*compress* + +: Whether to use the [`IMAP COMPRESS` extension][RFC 4978] for servers + advertising it. (Default: `YES`.) + +*null-stderr* + +: Whether to redirect *command*'s standard error to `/dev/null` for type + `type=tunnel`. (Default: `NO`.) + +*SSL_protocols* + +: 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.) + +*SSL_cipher_list* + +: The cipher list to send to the server. Although the server determines + which cipher suite is used, it should take the first supported cipher + in the list sent by the client. See [`ciphers`(1ssl)] for more + information. + +*SSL_fingerprint* + +: Fingerprint of the server certificate (or its public key) in the form + `ALGO$DIGEST_HEX`, where `ALGO` is the used algorithm (default + `sha256`). + Attempting to connect to a server with a non-matching certificate + fingerprint causes `pullimap` to abort the connection during the + SSL/TLS handshake. + +*SSL_verify* + +: Whether to verify the server certificate chain. + Note that using *SSL_fingerprint* to specify the fingerprint of the + server certificate is an orthogonal authentication measure as it + ignores the CA chain. + (Default: `YES`.) + +*SSL_CApath* + +: Directory to use for server certificate verification if + `SSL_verify=YES`. + This directory must be in “hash format”, see [`verify`(1ssl)] for + more information. + +*SSL_CAfile* + +: File containing trusted certificates to use during server certificate + authentication if `SSL_verify=YES`. + +Control flow +============ + +`pullimap` opens the *statefile* corresponding to a given configuration +*SECTION* with `O_DSYNC` to ensure that written data is flushed to the +underlying hardware by the time [`write`(2)] returns. Moreover an +exclusive lock is placed on the file descriptor immediately after +opening to prevent multiple `pullimap` processes from accessing the +*statefile* concurrently. + +Each *statefile* consists of a series of 32-bits big-endian integers. +Usually there are only two integers: the first is the *mailbox*'s +`UIDVALIDITY` value, and the second is the *mailbox*'s last seen +`UIDNEXT` value (`pullimap` then assumes that all messages with UID +smaller than this `UIDNEXT` value have already been retrieved and +delivered). +The [IMAP4rev1 specification][RFC 3501] does not guaranty that untagged +`FETCH` responses are sent ordered by UID in response to an `UID FETCH` +command. Thus it would be unsafe for `pullimap` to update the `UIDNEXT` +value in its *statefile* while the `UID FETCH` command is progress. +Instead, for each untagged `FETCH` response received while the `UID +FETCH` command is in progress, `pullimap` delivers the message `BODY` to +the SMTP or LMTP server (specified with *deliver-method*) then appends +the message UID to the *statefile*. +When the `UID FETCH` command eventually terminates, `pullimap` updates +the `UIDNEXT` value in the *statefile* and truncate the file down to 8 +bytes. Keeping track of message UIDs as they are received avoids +duplicate in the event of a crash or connection loss while the `UID +FETCH` command is in progress. + +In more details, `pullimap` works as follows: + + 1. Issue an `UID FETCH` command to retrieve message `ENVELOPE` and + `BODY` (and `UID`) with UID bigger or equal than the `UIDNEXT` value + found in the *statefile*. + While the `UID FETCH` command is in progress, perform the following + for each untagged `FETCH` response sent by the server: + + i) if no SMTP/LMTP transmission channel was opened, open one to the + server specified with *deliver-method* and send an `EHLO` (or + `LHO`) command with the domain specified by *deliver-ehlo*; + + ii) perform a mail transaction (using [SMTP pipelining][RFC 2920] if + possible) to send the retrieved message BODY to the SMTP or LMTP + session; and + + ii) append the message UID to the *statefile*. + + 2. If a SMTP/LMTP transmission channel was opened, send a `QUIT` command + to terminate it gracefully. + + 3. Issue an `UID STORE` command to mark all retrieved messages (and + stalled UIDs found in the *statefile* after the eigth byte) as + `\Seen`. + + 4. Update the *statefile* with the new UIDNEXT value (bytes 5-8). + + 5. Truncate the *statefile* down to 8 bytes (so that it contains only + two 32-bits integers, respectively the *mailbox*'s current + `UIDVALIDITY` and `UIDNEXT` values). + + 6. If `--idle` was set, issue an `IDLE` command; stop idling and go + back to step 1. whenever a new message is received. + +Standards +========= + + * M. Leech, M. Ganis, Y. Lee, R. Kuris, D. Koblas and L. Jones, + _SOCKS Protocol Version 5_, + [RFC 1928], March 1996 + * M. Leech, _Username/Password Authentication for SOCKS V5_, + [RFC 1929], March 1996 + * J. Myers, _Local Mail Transfer Protocol_, + [RFC 2033], October 1996 + * J. Myers, _IMAP4 non-synchronizing literals_, + [RFC 2088], January 1997 + * B. Leiba, _IMAP4 IDLE command_, + [RFC 2177], June 1997 + * C. Newman, _Using TLS with IMAP, POP3 and ACAP_, + [RFC 2595], June 1999 + * N. Freed, _SMTP Service Extension for Command Pipelining_, + [RFC 2920], September 2000 + * M. Crispin, _Internet Message Access Protocol - Version 4rev1_, + [RFC 3501], March 2003 + * M. Crispin, + _Internet Message Access Protocol (IMAP) - UIDPLUS extension_, + [RFC 4315], December 2005 + * A. Gulbrandsen, _The IMAP COMPRESS Extension_, + [RFC 4978], August 2007 + * R. Siemborski and A. Gulbrandsen, _IMAP Extension for Simple + Authentication and Security Layer (SASL) Initial Client Response_, + [RFC 4959], September 2007 + * J. Klensin, _Simple Mail Transfer Protocol_, + [RFC 5321], October 2008 + +[RFC 4315]: https://tools.ietf.org/html/rfc4315 +[RFC 2177]: https://tools.ietf.org/html/rfc2177 +[RFC 2595]: https://tools.ietf.org/html/rfc2595 +[RFC 4959]: https://tools.ietf.org/html/rfc4959 +[RFC 2088]: https://tools.ietf.org/html/rfc2088 +[RFC 5321]: https://tools.ietf.org/html/rfc5321 +[RFC 2033]: https://tools.ietf.org/html/rfc2033 +[RFC 2920]: https://tools.ietf.org/html/rfc2920 +[RFC 3501]: https://tools.ietf.org/html/rfc3501 +[RFC 4978]: https://tools.ietf.org/html/rfc4978 +[RFC 1928]: https://tools.ietf.org/html/rfc1928 +[RFC 1929]: https://tools.ietf.org/html/rfc1929 + +[INI file]: https://en.wikipedia.org/wiki/INI_file +[`fetchmail`(1)]: http://www.fetchmail.info/ +[`getmail`(1)]: http://pyropus.ca/software/getmail/ +[`write`(2)]: http://man7.org/linux/man-pages/man2/write.2.html +[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/ciphers.html +[`verify`(1ssl)]: https://www.openssl.org/docs/manmaster/apps/verify.html -- cgit v1.2.3