diff options
author | Eric S. Raymond <esr@thyrsus.com> | 1998-11-07 21:16:32 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 1998-11-07 21:16:32 +0000 |
commit | b1b6ddf9079453caa64484f31887cb4273c045bc (patch) | |
tree | 1b373f96732a206faa0017de5dc82532f46a08cc | |
parent | ad27c0a72aac927955335303e6cfab5f3ee58ad0 (diff) | |
download | fetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.tar.gz fetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.tar.bz2 fetchmail-b1b6ddf9079453caa64484f31887cb4273c045bc.zip |
Added LMTP support.
svn path=/trunk/; revision=2177
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | conf.c | 7 | ||||
-rw-r--r-- | design-notes.html | 6 | ||||
-rw-r--r-- | driver.c | 12 | ||||
-rw-r--r-- | fetchmail.c | 11 | ||||
-rw-r--r-- | fetchmail.h | 18 | ||||
-rw-r--r-- | fetchmail.man | 9 | ||||
-rwxr-xr-x | fetchmailconf | 8 | ||||
-rw-r--r-- | options.c | 20 | ||||
-rw-r--r-- | rcfile_l.l | 1 | ||||
-rw-r--r-- | rcfile_y.y | 67 | ||||
-rw-r--r-- | sample.rcfile | 1 | ||||
-rw-r--r-- | sink.c | 171 | ||||
-rw-r--r-- | smtp.c | 37 | ||||
-rw-r--r-- | smtp.h | 1 |
15 files changed, 292 insertions, 78 deletions
@@ -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. @@ -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"><esr@snark.thyrsus.com></A></ADDRESS> @@ -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': @@ -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); @@ -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; } @@ -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 # @@ -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); } @@ -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] != '-') @@ -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); |