aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>1998-11-07 21:16:32 +0000
committerEric S. Raymond <esr@thyrsus.com>1998-11-07 21:16:32 +0000
commitb1b6ddf9079453caa64484f31887cb4273c045bc (patch)
tree1b373f96732a206faa0017de5dc82532f46a08cc
parentad27c0a72aac927955335303e6cfab5f3ee58ad0 (diff)
downloadfetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.tar.gz
fetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.tar.bz2
fetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.zip
Added LMTP support.
svn path=/trunk/; revision=2177
-rw-r--r--NEWS1
-rw-r--r--conf.c7
-rw-r--r--design-notes.html6
-rw-r--r--driver.c12
-rw-r--r--fetchmail.c11
-rw-r--r--fetchmail.h18
-rw-r--r--fetchmail.man9
-rwxr-xr-xfetchmailconf8
-rw-r--r--options.c20
-rw-r--r--rcfile_l.l1
-rw-r--r--rcfile_y.y67
-rw-r--r--sample.rcfile1
-rw-r--r--sink.c171
-rw-r--r--smtp.c37
-rw-r--r--smtp.h1
15 files changed, 292 insertions, 78 deletions
diff --git a/NEWS b/NEWS
index 9d4b4b2c..b9b93020 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ fetchmail-4.6.6 ():
* Added plugin/plugout features from Felix von Leitner.
* Dave Bodenstab's errno fix.
* You can make fetchmail read its config from stdin with the option `-f -'.
+* Experimental, UNTESTED support of LSMTP (RFC2033).
There are 247 people on fetchmail-friends and 308 on fetchmail-announce.
diff --git a/conf.c b/conf.c
index 7625516b..757a0e66 100644
--- a/conf.c
+++ b/conf.c
@@ -57,6 +57,7 @@ static void indent(char ic)
indent_level++;
}
+
static void stringdump(const char *name, const char *member)
/* dump a string member with current indent */
{
@@ -314,6 +315,12 @@ void dump_config(struct runctl *runp, struct query *querylist)
stringdump("mda", ctl->mda);
stringdump("bsmtp", ctl->bsmtp);
+ indent('\0');
+ if (ctl->listener == LMTP_MODE)
+ fputs("'lmtp':TRUE,", stdout);
+ else
+ fputs("'lmtp':FALSE,", stdout);
+
#ifdef INET6
stringdump("netsec", ctl->netsec);
#endif /* INET6 */
diff --git a/design-notes.html b/design-notes.html
index f10d969c..ec607664 100644
--- a/design-notes.html
+++ b/design-notes.html
@@ -11,7 +11,7 @@
<table width="100%" cellpadding=0><tr>
<td width="30%">Back to <a href="//localhost/~esr/index.html">Fetchmail Home Page</a>
<td width="30%" align=center>To <a href="//localhost/~esr/sitemap.html">Site Map</a>
-<td width="30%" align=right>$Date: 1998/10/29 17:22:14 $
+<td width="30%" align=right>$Date: 1998/11/07 21:16:30 $
</table>
<HR>
<H1 ALIGN=CENTER>Design Notes On Fetchmail</H1>
@@ -503,6 +503,8 @@ all shaped the design in one way or another.<P>
<DD> Post Office Protocol - Version 3
<DT><A HREF="rfc1985.txt">RFC1985</A>
<DD> SMTP Service Extension for Remote Message Queue Starting
+<DT><A HREF="rfc2033.txt">RFC2033</A>
+<DD> Local Mail Transfer Protocol
<DT><A HREF="rfc2060.txt">RFC2060</A>
<DD> Internet Message Access Protocol - Version 4rev1
<DT><A HREF="rfc2061.txt">RFC2061</A>
@@ -515,7 +517,7 @@ all shaped the design in one way or another.<P>
<table width="100%" cellpadding=0><tr>
<td width="30%">Back to <a href="index.html">Fetchmail Home Page</a>
<td width="30%" align=center>To <a href="/~esr/sitemap.html">Site Map</a>
-<td width="30%" align=right>$Date: 1998/10/29 17:22:14 $
+<td width="30%" align=right>$Date: 1998/11/07 21:16:30 $
</table>
<P><ADDRESS>Eric S. Raymond <A HREF="mailto:esr@thyrsus.com">&lt;esr@snark.thyrsus.com&gt;</A></ADDRESS>
diff --git a/driver.c b/driver.c
index 39b48c80..b1a9bbff 100644
--- a/driver.c
+++ b/driver.c
@@ -1234,7 +1234,7 @@ static void send_size_warnings(struct query *ctl)
*/
if (open_warning_by_mail(ctl))
return;
- stuff_warning_line(ctl, OVERHD);
+ stuff_warning(ctl, OVERHD);
if (run.poll_interval == 0)
max_warning_poll_count = 0;
@@ -1248,7 +1248,7 @@ static void send_size_warnings(struct query *ctl)
{
nbr = current->val.status.mark;
size = atoi(current->id);
- stuff_warning_line(ctl,
+ stuff_warning(ctl,
"\t%d msg %d octets long skipped by fetchmail.",
nbr, size);
}
@@ -1499,15 +1499,15 @@ const struct method *proto; /* protocol method table */
if (run.poll_interval
&& !ctl->authfailcount && !open_warning_by_mail(ctl))
{
- stuff_warning_line(ctl,
+ stuff_warning(ctl,
"Subject: fetchmail authentication failed\r\n");
- stuff_warning_line(ctl,
+ stuff_warning(ctl,
"Fetchmail could not get mail from %s@%s.",
ctl->remotename,
ctl->server.truename);
- stuff_warning_line(ctl,
+ stuff_warning(ctl,
"The attempt to get authorization failed.");
- stuff_warning_line(ctl,
+ stuff_warning(ctl,
"This probably means your password is invalid.");
close_warning_by_mail(ctl);
ctl->authfailcount++;
diff --git a/fetchmail.c b/fetchmail.c
index ef40f0b9..be0818b9 100644
--- a/fetchmail.c
+++ b/fetchmail.c
@@ -766,6 +766,7 @@ static void optmerge(struct query *h2, struct query *h1, int force)
FLAG_MERGE(password);
FLAG_MERGE(mda);
FLAG_MERGE(bsmtp);
+ FLAG_MERGE(listener);
FLAG_MERGE(smtpaddress);
FLAG_MERGE(preconnect);
FLAG_MERGE(postconnect);
@@ -807,6 +808,7 @@ static int load_params(int argc, char **argv, int optind)
def_opts.warnings = WARNING_INTERVAL;
def_opts.remotename = user;
def_opts.expunge = 1;
+ def_opts.listener = SMTP_MODE;
/* this builds the host list */
if (prc_parse_file(rcfile, !versioninfo) != 0)
@@ -1019,6 +1021,13 @@ static int load_params(int argc, char **argv, int optind)
ctl->server.pollname);
exit(PS_SYNTAX);
}
+ if (ctl->server.port == 25 && ctl->listener == LMTP_MODE)
+ {
+ (void) fprintf(stderr,
+ "%s configuration invalid, LMTP can't use SMTP port",
+ ctl->server.pollname);
+ exit(PS_SYNTAX);
+ }
#endif /* !INET6 */
}
}
@@ -1369,7 +1378,7 @@ void dump_params (struct runctl *runp, struct query *querylist, flag implicit)
{
struct idlist *idp;
- printf(" Messages will be SMTP-forwarded to:");
+ printf(" Messages will be %cMTP-forwarded to:", ctl->listener);
for (idp = ctl->smtphunt; idp; idp = idp->next)
{
printf(" %s", idp->id);
diff --git a/fetchmail.h b/fetchmail.h
index 79138551..7edca267 100644
--- a/fetchmail.h
+++ b/fetchmail.h
@@ -221,12 +221,16 @@ struct query
char *remotename; /* remote login name to use */
char *password; /* remote password to use */
struct idlist *mailboxes; /* list of mailboxes to check */
+
+ /* per-forwarding-target data */
struct idlist *smtphunt; /* list of SMTP hosts to try forwarding to */
- char *smtphost; /* actual SMTP host to point to */
- char *smtpaddress; /* address we want to force in the delivery messages */
+ char *smtpaddress; /* address to force in RCPT TO */
struct idlist *antispam; /* list of listener's antispam response */
char *mda; /* local MDA to pass mail to */
char *bsmtp; /* BSMTP output file */
+ char listener; /* what's the listener's wire protocol? */
+#define SMTP_MODE 'S'
+#define LMTP_MODE 'L'
char *preconnect; /* pre-connection command to execute */
char *postconnect; /* post-connection command to execute */
@@ -239,7 +243,7 @@ struct query
flag forcecr; /* if TRUE, force CRs before LFs in text */
flag pass8bits; /* if TRUE, ignore Content-Transfer-Encoding */
flag dropstatus; /* if TRUE, drop Status lines in mail */
- flag mimedecode; /* if TRUE, decode MIME-coded headers/coded printable*/
+ flag mimedecode; /* if TRUE, decode MIME-armored messages */
int limit; /* limit size of retrieved messages */
int warnings; /* size warning interval */
int fetchlimit; /* max # msgs to get in single poll */
@@ -247,16 +251,16 @@ struct query
int expunge; /* max # msgs to pass between expunges */
char *properties; /* passthrough properties for extensions */
- struct idlist *oldsaved, *newsaved;
-
/* internal use -- per-poll state */
flag active; /* should we actually poll this server? */
const char *destaddr; /* destination host for this query */
int errcount; /* count transient errors in last pass */
int authfailcount; /* count authentication failures this run */
+ char *smtphost; /* actual SMTP host we connected to */
int smtp_socket; /* socket descriptor for SMTP connection */
unsigned int uid; /* UID of user to deliver to */
struct idlist *skipped; /* messages skipped on the mail server */
+ struct idlist *oldsaved, *newsaved;
/* internal use -- per-message state */
int mimemsg; /* bitmask indicating MIME body-type */
@@ -375,9 +379,9 @@ void release_sink(struct query *);
int close_sink(struct query *, flag);
int open_warning_by_mail(struct query *);
#if defined(HAVE_STDARG_H)
-void stuff_warning_line(struct query *, const char *, ... );
+void stuff_warning(struct query *, const char *, ... );
#else
-void stuff_warning_line();
+void stuff_warning();
#endif
void close_warning_by_mail(struct query *);
diff --git a/fetchmail.man b/fetchmail.man
index 255853cb..2d9dba3f 100644
--- a/fetchmail.man
+++ b/fetchmail.man
@@ -285,6 +285,12 @@ mail message's From address will be inserted where you place an %F. Do
"sendmail -oem -t" that dispatches on the contents of To/Cc/Bcc, it
will create mail loops and bring the just wrath of many postmasters
down upon your head.
+.TP
+.B \--lmtp
+(Keyword: lmtp)
+Cause delivery via LMTP (Local Mail Transfer Protocol). A service port
+\fdImust\fR be explicitly specified if this option is selected; port
+25 will (in accordance with RFC 2033) not be accepted.
.TP
.B \--bsmtp
(keyword: bsmtp)
@@ -1775,3 +1781,6 @@ RFC 1985
.TP 5
OTP:
RFC 1938
+.TP 5
+LMTP:
+RFC 2033
diff --git a/fetchmailconf b/fetchmailconf
index 52d01c95..2a684343 100755
--- a/fetchmailconf
+++ b/fetchmailconf
@@ -7,7 +7,7 @@
#
# TO DO: Arrange for save and quit buttons to clean up all frames dependent
# on the current ones.
-version = "1.8"
+version = "1.9"
from Tkinter import *
from Dialog import *
@@ -188,6 +188,7 @@ class User:
self.postconnect = None # Connection wrapup
self.mda = None # Mail Delivery Agent
self.bsmtp = None # BSMTP output file
+ self.lmtp = FALSE # Use LMTP rather than SMTP?
self.antispam = "571 550 501" # Listener's spam-block code
self.keep = FALSE # Keep messages
self.flush = FALSE # Flush messages
@@ -214,6 +215,7 @@ class User:
('postconnect', 'String'),
('mda', 'String'),
('bsmtp', 'String'),
+ ('lmtp', 'Boolean'),
('antispam', 'String'),
('keep', 'Boolean'),
('flush', 'Boolean'),
@@ -298,6 +300,8 @@ class User:
for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
if getattr(self, fld):
str = str + " %s %s\n" % (fld, `getattr(self, fld)`)
+ if self.lmtp != UserDefaults.lmtp:
+ str = str + flag2str(self.lmtp, 'lmtp')
if self.antispam != UserDefaults.antispam:
str = str + " antispam " + self.antispam + "\n"
return str;
@@ -1202,6 +1206,8 @@ class UserEdit(Frame, MyWidget):
self.antispam, '26').pack(side=TOP, fill=X)
LabeledEntry(targwin, 'Pass-through properties:',
self.properties, '26').pack(side=TOP, fill=X)
+ Checkbutton(targwin, text="Use LMTP?",
+ variable=self.lmtp).pack(side=TOP, fill=X)
targwin.pack(fill=X, anchor=N)
if mode != 'novice':
diff --git a/options.c b/options.c
index 5a8d2c07..7a8c86f3 100644
--- a/options.c
+++ b/options.c
@@ -59,13 +59,14 @@
#define LA_EXPUNGE 37
#define LA_MDA 38
#define LA_BSMTP 39
-#define LA_PLUGIN 40
-#define LA_PLUGOUT 41
-#define LA_NETSEC 42
-#define LA_INTERFACE 43
-#define LA_MONITOR 44
-#define LA_CONFIGDUMP 45
-#define LA_YYDEBUG 46
+#define LA_LMTP 40
+#define LA_PLUGIN 41
+#define LA_PLUGOUT 42
+#define LA_NETSEC 43
+#define LA_INTERFACE 44
+#define LA_MONITOR 45
+#define LA_CONFIGDUMP 46
+#define LA_YYDEBUG 47
/* options still left: CDgGhHjJoORwWxXYz */
static const char *shortoptions =
@@ -119,6 +120,7 @@ static const struct option longoptions[] = {
{"expunge", required_argument, (int *) 0, LA_EXPUNGE },
{"mda", required_argument, (int *) 0, LA_MDA },
{"bsmtp", required_argument, (int *) 0, LA_BSMTP },
+ {"lmtp", required_argument, (int *) 0, LA_LMTP },
#ifdef INET6
{"netsec", required_argument, (int *) 0, LA_NETSEC },
@@ -478,6 +480,9 @@ struct query *ctl; /* option record to be initialized */
ctl->bsmtp = xstrdup(optarg);
ocount++;
break;
+ case LA_LMTP:
+ ctl->listener = LMTP_MODE;
+ break;
case 'T':
case LA_NETSEC:
@@ -591,6 +596,7 @@ struct query *ctl; /* option record to be initialized */
P(" -e, --expunge set max deletions between expunges\n");
P(" --mda set MDA to use for forwarding\n");
P(" --bsmtp set output BSMTP file\n");
+ P(" --lmtp use LMTP (RFC2033) for delivery\n");
P(" -r, --folder specify remote folder name\n");
#undef P
return(-1);
diff --git a/rcfile_l.l b/rcfile_l.l
index f573c716..d1f1c36f 100644
--- a/rcfile_l.l
+++ b/rcfile_l.l
@@ -57,6 +57,7 @@ smtpaddress { return SMTPADDRESS; }
antispam { return SPAMRESPONSE; }
mda { return MDA; }
bsmtp { return BSMTP; }
+lmtp { return LMTP; }
pre(connect)? { return PRECONNECT; }
post(connect)? { return POSTCONNECT; }
netsec { return NETSEC; }
diff --git a/rcfile_y.y b/rcfile_y.y
index 3170ccca..7f36b3dd 100644
--- a/rcfile_y.y
+++ b/rcfile_y.y
@@ -59,7 +59,7 @@ extern char * yytext;
%token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
%token AUTHENTICATE TIMEOUT KPOP SDPS KERBEROS4 KERBEROS5 KERBEROS
-%token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP
+%token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP LMTP
%token SMTPADDRESS SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT
%token NETSEC INTERFACE MONITOR PLUGIN PLUGOUT
%token IS HERE THERE TO MAP WILDCARD
@@ -292,44 +292,45 @@ user_option : TO localnames HERE
| IS localnames HERE
| IS localnames
- | IS STRING THERE {current.remotename = xstrdup($2);}
- | PASSWORD STRING {current.password = xstrdup($2);}
+ | IS STRING THERE {current.remotename = xstrdup($2);}
+ | PASSWORD STRING {current.password = xstrdup($2);}
| FOLDER folder_list
| SMTPHOST smtp_list
| SMTPADDRESS STRING {current.smtpaddress = xstrdup($2);}
| SPAMRESPONSE num_list
- | MDA STRING {current.mda = xstrdup($2);}
- | BSMTP STRING {current.bsmtp = xstrdup($2);}
- | PRECONNECT STRING {current.preconnect = xstrdup($2);}
+ | MDA STRING {current.mda = xstrdup($2);}
+ | BSMTP STRING {current.bsmtp = xstrdup($2);}
+ | LMTP {current.listener = LMTP_MODE;}
+ | PRECONNECT STRING {current.preconnect = xstrdup($2);}
| POSTCONNECT STRING {current.postconnect = xstrdup($2);}
- | KEEP {current.keep = FLAG_TRUE;}
- | FLUSH {current.flush = FLAG_TRUE;}
- | FETCHALL {current.fetchall = FLAG_TRUE;}
- | REWRITE {current.rewrite = FLAG_TRUE;}
- | FORCECR {current.forcecr = FLAG_TRUE;}
- | STRIPCR {current.stripcr = FLAG_TRUE;}
- | PASS8BITS {current.pass8bits = FLAG_TRUE;}
- | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
- | MIMEDECODE {current.mimedecode = FLAG_TRUE;}
-
- | NO KEEP {current.keep = FLAG_FALSE;}
- | NO FLUSH {current.flush = FLAG_FALSE;}
- | NO FETCHALL {current.fetchall = FLAG_FALSE;}
- | NO REWRITE {current.rewrite = FLAG_FALSE;}
- | NO FORCECR {current.forcecr = FLAG_FALSE;}
- | NO STRIPCR {current.stripcr = FLAG_FALSE;}
- | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
- | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
- | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
-
- | LIMIT NUMBER {current.limit = NUM_VALUE($2);}
- | WARNINGS NUMBER {current.warnings = NUM_VALUE($2);}
- | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE($2);}
- | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE($2);}
- | EXPUNGE NUMBER {current.expunge = NUM_VALUE($2);}
-
- | PROPERTIES STRING {current.properties = xstrdup($2);}
+ | KEEP {current.keep = FLAG_TRUE;}
+ | FLUSH {current.flush = FLAG_TRUE;}
+ | FETCHALL {current.fetchall = FLAG_TRUE;}
+ | REWRITE {current.rewrite = FLAG_TRUE;}
+ | FORCECR {current.forcecr = FLAG_TRUE;}
+ | STRIPCR {current.stripcr = FLAG_TRUE;}
+ | PASS8BITS {current.pass8bits = FLAG_TRUE;}
+ | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
+ | MIMEDECODE {current.mimedecode = FLAG_TRUE;}
+
+ | NO KEEP {current.keep = FLAG_FALSE;}
+ | NO FLUSH {current.flush = FLAG_FALSE;}
+ | NO FETCHALL {current.fetchall = FLAG_FALSE;}
+ | NO REWRITE {current.rewrite = FLAG_FALSE;}
+ | NO FORCECR {current.forcecr = FLAG_FALSE;}
+ | NO STRIPCR {current.stripcr = FLAG_FALSE;}
+ | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
+ | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
+ | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
+
+ | LIMIT NUMBER {current.limit = NUM_VALUE($2);}
+ | WARNINGS NUMBER {current.warnings = NUM_VALUE($2);}
+ | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE($2);}
+ | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE($2);}
+ | EXPUNGE NUMBER {current.expunge = NUM_VALUE($2);}
+
+ | PROPERTIES STRING {current.properties = xstrdup($2);}
;
%%
diff --git a/sample.rcfile b/sample.rcfile
index 5840292d..3e49f83c 100644
--- a/sample.rcfile
+++ b/sample.rcfile
@@ -52,6 +52,7 @@
# antispam -- must be followed by a numeric response value
# mda -- must be followed by an MDA command string
# bsmtp -- must be followed by a filename or -
+# lmtp
# preconnect (or pre) -- must be followed by an executable command
# postconnect (or post) -- must be followed by an executable command
#
diff --git a/sink.c b/sink.c
index 133ea56b..44ab2378 100644
--- a/sink.c
+++ b/sink.c
@@ -3,7 +3,7 @@
*
* The interface of this module (open_sink(), stuff_line(), close_sink(),
* release_sink()) seals off the delivery logic from the protocol machine,
- * so the latter won't have to care whether it's shipping to an SMTP
+ * so the latter won't have to care whether it's shipping to an [SL]MTP
* listener daemon or an MDA pipe.
*
* Copyright 1998 by Eric S. Raymond
@@ -45,6 +45,9 @@
#define SMTP_PORT 25 /* standard SMTP service port */
#endif /* INET6 */
+static int lmtp_responses; /* how many should we expect? */
+static struct msgblk msgcopy; /* copies of various message internals */
+
static int smtp_open(struct query *ctl)
/* try to open a socket to the appropriate SMTP server for this query */
{
@@ -120,6 +123,9 @@ static int smtp_open(struct query *ctl)
ctl->server.plugout)) == -1)
continue;
+ /* are we doing SMTP or LMTP? */
+ SMTP_setmode(ctl->listener);
+
/* first, probe for ESMTP */
if (SMTP_ok(ctl->smtp_socket) == SM_OK &&
SMTP_ehlo(ctl->smtp_socket, id_me,
@@ -459,12 +465,19 @@ int open_sink(struct query *ctl, struct msgblk *msg,
/* build a connection to the SMTP listener */
if ((smtp_open(ctl) == -1))
{
- error(0, errno, "SMTP connect to %s failed",
+ error(0, errno, "%cMTP connect to %s failed",
+ ctl->listener,
ctl->smtphost ? ctl->smtphost : "localhost");
return(PS_SMTP);
}
/*
+ * Stash a copy of the parsed message block
+ * for use by close_sink().
+ */
+ memcpy(&msgcopy, msg, sizeof(struct msgblk));
+
+ /*
* Compute ESMTP options.
*/
options[0] = '\0';
@@ -531,7 +544,9 @@ int open_sink(struct query *ctl, struct msgblk *msg,
* an error when the return code is less specific.
*/
if (smtperr >= 400)
- error(0, -1, "SMTP error: %s", smtp_response);
+ error(0, -1, "%cMTP error: %s",
+ ctl->listener,
+ smtp_response);
switch (smtperr)
{
@@ -568,7 +583,9 @@ int open_sink(struct query *ctl, struct msgblk *msg,
default: /* retry with postmaster's address */
if (SMTP_from(ctl->smtp_socket,run.postmaster,options)!=SM_OK)
{
- error(0, -1, "SMTP error: %s", smtp_response);
+ error(0, -1, "%cMTP error: %s",
+ ctl->listener,
+ smtp_response);
return(PS_SMTP); /* should never happen */
}
}
@@ -596,8 +613,8 @@ int open_sink(struct query *ctl, struct msgblk *msg,
(*bad_addresses)++;
idp->val.status.mark = XMIT_ANTISPAM;
error(0, 0,
- "SMTP listener doesn't like recipient address `%s'",
- addr);
+ "%cMTP listener doesn't like recipient address `%s'",
+ ctl->listener, addr);
}
}
if (!(*good_addresses))
@@ -620,6 +637,12 @@ int open_sink(struct query *ctl, struct msgblk *msg,
SMTP_data(ctl->smtp_socket);
}
+ /*
+ * We need to stash this away in order to know how many
+ * response lines to expect after the LMTP end-of-message.
+ */
+ lmtp_responses = *good_addresses;
+
return(PS_SUCCESS);
}
@@ -675,12 +698,140 @@ int close_sink(struct query *ctl, flag forward)
}
else if (forward)
{
- /* write message terminator */
+ /* write message terminator */
if (SMTP_eom(ctl->smtp_socket) != SM_OK)
{
error(0, -1, "SMTP listener refused delivery");
return(FALSE);
}
+
+ /*
+ * If this is an SMTP connection, SMTP_eom() ate the response.
+ * But could be this is an LMTP connection, in which case we have to
+ * interpret either (a) a single 503 response meaning there
+ * were no successful RCPT TOs, or (b) a variable number of
+ * responses, one for each successful RCPT TO. We need to send
+ * bouncemail on each failed response and then return TRUE anyway,
+ * otherwise the message will get left in the queue and resent
+ * to people who got it the first time.
+ */
+ if (ctl->listener == LMTP_MODE)
+ if (lmtp_responses == 0)
+ {
+ SMTP_ok(ctl->smtp_socket);
+
+ /*
+ * According to RFC2033, 503 is the only legal response
+ * if no RCPT TO commands succeeded. No error recovery
+ * is really possible here, as we have no idea what
+ * insane thing the listener might be doing if it doesn't
+ * comply.
+ */
+ if (atoi(smtp_response) == 503)
+ error(0, -1, "LMTP delivery error on EOM");
+ else
+ error(0, -1,
+ "Unexpected non-503 response to LMTP EOM: %s",
+ smtp_response);
+
+ /*
+ * It's not completely clear what to do here. We choose to
+ * interpret delivery failure here as a transient error,
+ * the same way SMTP delivery failure is handled. If we're
+ * wrong, an undead message will get stuck in the queue.
+ */
+ return(FALSE);
+ }
+ else
+ {
+ int i, errors;
+ char **responses;
+
+ /* eat the RFC2033-required responses, saving errors */
+ xalloca(responses, char **, sizeof(char *) * lmtp_responses);
+ for (errors = i = 0; i < lmtp_responses; i++)
+ {
+ if (SMTP_ok(ctl->smtp_socket) == SM_OK)
+ responses[i] = (char *)NULL;
+ else
+ {
+ xalloca(responses[errors],
+ char *,
+ strlen(smtp_response)+1);
+ strcpy(responses[errors], smtp_response);
+ errors++;
+ }
+ }
+
+ if (errors == 0)
+ return(TRUE); /* all deliveries succeeded */
+ else if (errors == lmtp_responses)
+ return(FALSE); /* all deliveries failed */
+ else
+ {
+ /*
+ * Now life gets messy. There are multiple recipients,
+ * and one or more (but not all) deliveries failed.
+ *
+ * What we'd really like to do is bounce a
+ * failures list back to the sender and return
+ * TRUE, deleting the message from the server so
+ * it won't be re-forwarded on subsequent poll
+ * cycles.
+ *
+ * We can't do that yet, so instead we report
+ * failures to the calling-user/postmaster. If we can't,
+ * there's a transient error in local delivery; leave
+ * the message on the server.
+ */
+ char buf[MSGBUFSIZE];
+
+ if (open_warning_by_mail(ctl))
+ return(FALSE);
+
+ /* generate an error report a la RFC 1892 */
+ stuff_warning(ctl, "MIME-Version: 1.0");
+ stuff_warning(ctl, "Content-Type: multipart/report report-type=text/plain boundary=\"om-mani-padme-hum\"");
+ stuff_warning(ctl, "Content-Transfer-Encoding: 7bit");
+ stuff_warning(ctl, "");
+
+
+ /* RFC1892 part 1 -- human-readable message */
+ stuff_warning(ctl, "-- om-mani-padme-hum");
+ stuff_warning(ctl, "");
+
+ /* RFC1892 part 2 -- machine-readable responses */
+ stuff_warning(ctl, "-- om-mani-padme-hum");
+ stuff_warning(ctl,"Content-Type: message/delivery-status");
+ stuff_warning(ctl, "");
+ for (i = 0; i < errors; i++)
+ stuff_warning(ctl, responses[i]);
+
+ /* RFC1892 part 3 -- headers of undelivered message */
+ stuff_warning(ctl, "-- om-mani-padme-hum");
+ stuff_warning(ctl, "Content-Type: text/rfc822-headers");
+ stuff_warning(ctl, "");
+ stuffline(ctl, msgcopy.headers);
+
+ stuff_warning(ctl, "-- om-mani-padme-hum --");
+
+ close_warning_by_mail(ctl);
+
+ /*
+ * It's not completely clear what to do here,
+ * either. We choose to interpret delivery
+ * failure here as a permanent error, so the
+ * people who got successful deliveries won't see
+ * endless repetitions of the same message on
+ * subsequent poll messages. If we're wrong, a
+ * transient error will cause someone to lose
+ * mail. This could only happen with
+ * multi-recipient messages coming from a remote
+ * mailserver to two or more local users...
+ */
+ return(TRUE);
+ }
+ }
}
return(TRUE);
@@ -702,9 +853,9 @@ int open_warning_by_mail(struct query *ctl)
}
#if defined(HAVE_STDARG_H)
-void stuff_warning_line(struct query *ctl, const char *fmt, ... )
+void stuff_warning(struct query *ctl, const char *fmt, ... )
#else
-void stuff_warning_line(struct query *ctl, fmt, va_alist)
+void stuff_warning(struct query *ctl, fmt, va_alist)
struct query *ctl;
const char *fmt; /* printf-style format */
va_dcl
@@ -740,7 +891,7 @@ va_dcl
void close_warning_by_mail(struct query *ctl)
/* sign and send mailed warnings */
{
- stuff_warning_line(ctl, "--\r\n\t\t\t\tThe Fetchmail Daemon\r\n");
+ stuff_warning(ctl, "--\r\n\t\t\t\tThe Fetchmail Daemon\r\n");
close_sink(ctl, TRUE);
}
diff --git a/smtp.c b/smtp.c
index 0caf0bb7..31aba1a6 100644
--- a/smtp.c
+++ b/smtp.c
@@ -32,6 +32,14 @@ static struct opt extensions[] =
char smtp_response[MSGBUFSIZE];
+static char smtp_mode = 'S';
+
+int SMTP_setmode(char sl)
+/* set whether we are speaking SMTP or LMTP */
+{
+ smtp_mode = sl;
+}
+
int SMTP_helo(int sock,const char *host)
/* send a "HELO" message to the SMTP listener */
{
@@ -49,9 +57,10 @@ int SMTP_ehlo(int sock, const char *host, int *opt)
{
struct opt *hp;
- SockPrintf(sock,"EHLO %s\r\n", host);
+ SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> EHLO %s", host);
+ error(0, 0, "%cMTP> %cHLO %s",
+ smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
*opt = 0;
while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
@@ -92,7 +101,7 @@ int SMTP_from(int sock, const char *from, const char *opts)
strcat(buf, opts);
SockPrintf(sock,"%s\r\n", buf);
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> %s", buf);
+ error(0, 0, "%cMTP> %s", smtp_mode, buf);
ok = SMTP_ok(sock);
return ok;
}
@@ -104,7 +113,7 @@ int SMTP_rcpt(int sock, const char *to)
SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> RCPT TO:<%s>", to);
+ error(0, 0, "%cMTP> RCPT TO:<%s>", smtp_mode, to);
ok = SMTP_ok(sock);
return ok;
}
@@ -116,7 +125,7 @@ int SMTP_data(int sock)
SockPrintf(sock,"DATA\r\n");
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> DATA");
+ error(0, 0, "%cMTP> DATA", smtp_mode);
ok = SMTP_ok(sock);
return ok;
}
@@ -128,7 +137,7 @@ int SMTP_rset(int sock)
SockPrintf(sock,"RSET\r\n");
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> RSET");
+ error(0, 0, "%cMTP> RSET", smtp_mode);
ok = SMTP_ok(sock);
return ok;
}
@@ -140,7 +149,7 @@ int SMTP_quit(int sock)
SockPrintf(sock,"QUIT\r\n");
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP> QUIT");
+ error(0, 0, "%cMTP> QUIT", smtp_mode);
ok = SMTP_ok(sock);
return ok;
}
@@ -152,8 +161,14 @@ int SMTP_eom(int sock)
SockPrintf(sock,".\r\n");
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP>. (EOM)");
- ok = SMTP_ok(sock);
+ error(0, 0, "%cMTP>. (EOM)", smtp_mode);
+
+ /*
+ * When doing LMTP, must process many of these at the outer level.
+ */
+ if (smtp_mode == 'S')
+ ok = SMTP_ok(sock);
+
return ok;
}
@@ -167,12 +182,12 @@ int SMTP_ok(int sock)
if (smtp_response[strlen(smtp_response)-1] == '\n')
smtp_response[strlen(smtp_response)-1] = '\0';
if (smtp_response[strlen(smtp_response)-1] == '\r')
- smtp_response[strlen(smtp_response)-1] = '\r';
+ smtp_response[strlen(smtp_response)-1] = '\0';
if (n < 4)
return SM_ERROR;
smtp_response[n] = '\0';
if (outlevel >= O_MONITOR)
- error(0, 0, "SMTP< %s", smtp_response);
+ error(0, 0, "%cMTP< %s", smtp_mode, smtp_response);
if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ')
return SM_OK;
else if (smtp_response[3] != '-')
diff --git a/smtp.h b/smtp.h
index 9a39f5dc..8563343c 100644
--- a/smtp.h
+++ b/smtp.h
@@ -19,6 +19,7 @@
#define ESMTP_SIZE 0x02
#define ESMTP_ETRN 0x04
+int SMTP_setmode(char);
int SMTP_helo(int socket,const char *host);
int SMTP_ehlo(int socket,const char *host,int *opt);
int SMTP_from(int socket,const char *from,const char *opts);