#endif
/* makes the open_sink()/close_sink() pair non-reentrant */
-static lmtp_responses;
+static int lmtp_responses;
static int smtp_open(struct query *ctl)
/* try to open a socket to the appropriate SMTP server for this query */
{
/* maybe it's time to close the socket in order to force delivery */
- if (NUM_NONZERO(ctl->batchlimit) && (ctl->smtp_socket != -1) && batchcount++ == ctl->batchlimit)
+ if (NUM_NONZERO(ctl->batchlimit) && (ctl->smtp_socket != -1) && ++batchcount == ctl->batchlimit)
{
- close(ctl->smtp_socket);
+ SockClose(ctl->smtp_socket);
ctl->smtp_socket = -1;
batchcount = 0;
}
for (idp = ctl->smtphunt; idp; idp = idp->next)
{
char *cp, *parsed_host;
-#ifdef INET6
+#ifdef INET6_ENABLE
char *portnum = SMTP_PORT;
#else
int portnum = SMTP_PORT;
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
xalloca(parsed_host, char *, strlen(idp->id) + 1);
if ((cp = strrchr(parsed_host, '/')))
{
*cp++ = 0;
-#ifdef INET6
+#ifdef INET6_ENABLE
portnum = cp;
#else
portnum = atoi(cp);
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
}
if ((ctl->smtp_socket = SockOpen(parsed_host,portnum,NULL,
ctl->destaddr = ctl->smtpaddress ? ctl->smtpaddress : ( ctl->smtphost ? ctl->smtphost : "localhost");
if (outlevel >= O_DEBUG && ctl->smtp_socket != -1)
- report(stdout, 0, _("forwarding to %s"), ctl->smtphost);
+ report(stdout, _("forwarding to %s\n"), ctl->smtphost);
return(ctl->smtp_socket);
}
/* these are shared by open_sink and stuffline */
static FILE *sinkfp;
+#ifndef HAVE_SIGACTION
static RETSIGTYPE (*sigchld)(int);
+#else
+static struct sigaction sa_old;
+#endif /* HAVE_SIGACTION */
int stuffline(struct query *ctl, char *buf)
/* ship a line to the given control block's output sink (SMTP server or MDA) */
* decorated any . lines it sends back up.
*/
if (*buf == '.')
+ {
if (ctl->server.base_protocol->delimited) /* server has already byte-stuffed */
{
if (ctl->mda)
else
/* leave it alone */;
}
+ }
/* we may need to strip carriage returns */
if (ctl->stripcr)
*cp = '_';
}
-static int send_bouncemail(struct msgblk *msg, int userclass,
- char *message, int nerrors, char *errors[])
+static int send_bouncemail(struct query *ctl, struct msgblk *msg,
+ int userclass, char *message,
+ int nerrors, char *errors[])
/* bounce back an error report a la RFC 1892 */
{
char daemon_name[18 + HOSTLEN] = "FETCHMAIL-DAEMON@";
- char boundary[BUFSIZ], *ts;
+ char boundary[BUFSIZ], *ts, *bounce_to;
int sock;
/* don't bounce in reply to undeliverable bounces */
if (!msg->return_path[0] || strcmp(msg->return_path, "<>") == 0)
return(FALSE);
+ bounce_to = (run.bouncemail ? msg->return_path : run.postmaster);
+
SMTP_setmode(SMTP_MODE);
strcat(daemon_name, fetchmailhost);
|| SMTP_ok(sock) != SM_OK
|| SMTP_helo(sock, "localhost") != SM_OK
|| SMTP_from(sock, daemon_name, (char *)NULL) != SM_OK
- || SMTP_rcpt(sock, msg->return_path) != SM_OK
+ || SMTP_rcpt(sock, bounce_to) != SM_OK
|| SMTP_data(sock) != SM_OK)
return(FALSE);
+ /* our first duty is to keep the sacred foo counters turning... */
sprintf(boundary,
- "om-mani-padme-hum-%d-%d-%ld",
- getpid(), getppid(), time((time_t *)NULL));
+ "foo-mani-padme-hum-%d-%d-%ld",
+ (int)getpid(), (int)getppid(), time((time_t *)NULL));
ts = rfc822timestamp();
if (outlevel >= O_VERBOSE)
- report(stdout, 0, "SMTP: (bounce-message body)");
+ report(stdout, "SMTP: (bounce-message body)\n");
+ else
+ /* this will usually go to sylog... */
+ report(stderr, "mail from %s bounced to %s\n",
+ daemon_name, bounce_to);
/* bouncemail headers */
SockPrintf(sock, "Return-Path: <>\r\n");
SockPrintf(sock, "From: %s\r\n", daemon_name);
- SockPrintf(sock, "To: %s\r\n", msg->return_path);
+ SockPrintf(sock, "To: %s\r\n", bounce_to);
SockPrintf(sock, "MIME-Version: 1.0\r\n");
SockPrintf(sock, "Content-Type: multipart/report; report-type=delivery-status;\r\n\tboundary=\"%s\"\r\n", boundary);
SockPrintf(sock, "\r\n");
static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
/* handle SMTP errors based on the content of SMTP_response */
+/* return of PS_REFUSED deletes mail from the server; PS_TRANSIENT keeps it */
{
int smtperr = atoi(smtp_response);
char *responses[1];
- responses[0] = smtp_response;
+ xalloca(responses[0], char *, strlen(smtp_response)+1);
+ strcpy(responses[0], smtp_response);
- /* required by RFC1870; sets us up to be able to send bouncemail */
- SMTP_rset(ctl->smtp_socket);
+ SMTP_rset(ctl->smtp_socket); /* stay on the safe site */
+
+ if (outlevel >= O_DEBUG)
+ report(stdout, "Saved error is still %d\n", smtperr);
/*
* Note: send_bouncemail message strings are not made subject
* coming from this address, probably due to an
* anti-spam domain exclusion. Respect this. Don't
* try to ship the message, and don't prevent it from
- * being deleted. Typical values:
+ * being deleted. Default values:
*
- * 501 = exim's old antispam response
- * 550 = exim's new antispam response (temporary)
- * 553 = sendmail 8.8.7's generic REJECT
* 571 = sendmail's "unsolicited email refused"
+ * 550 = exim's new antispam response (temporary)
+ * 501 = exim's old antispam response
+ * 554 = Postfix antispam response.
*
*/
- send_bouncemail(msg, XMIT_ACCEPT,
+ send_bouncemail(ctl, msg, XMIT_ACCEPT,
"Our spam filter rejected this transaction.\r\n",
1, responses);
return(PS_REFUSED);
* an error when the return code is less specific.
*/
if (smtperr >= 400)
- report(stderr, 0, _("%cMTP error: %s"),
+ report(stderr, _("%cMTP error: %s\n"),
ctl->listener,
smtp_response);
switch (smtperr)
{
- case 452: /* insufficient system storage */
- /*
- * Temporary out-of-queue-space condition on the
- * ESMTP server. Don't try to ship the message,
- * and suppress deletion so it can be retried on
- * a future retrieval cycle.
- *
- * Bouncemail *might* be appropriate here as a delay
- * notification (note; if we ever add this, we must make
- * sure the RFC1894 Action field is "delayed" rather thwn
- * "failed"). But it's not really necessary because
- * this is not an actual failure, we're very likely to be
- * able to recover on the next cycle.
- */
- return(PS_TRANSIENT);
-
case 552: /* message exceeds fixed maximum message size */
/*
* Permanent no-go condition on the
* ESMTP server. Don't try to ship the message,
* and allow it to be deleted.
*/
- send_bouncemail(msg, XMIT_ACCEPT,
- "This message was too large.\r\n",
+ send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ "This message was too large (SMTP error 552).\r\n",
1, responses);
- return(PS_REFUSED);
-
+ return(run.bouncemail ? PS_REFUSED : PS_TRANSIENT);
+
case 553: /* invalid sending domain */
/*
* These latter days 553 usually means a spammer is trying to
- * cover his tracks.
+ * cover his tracks. We never bouncemail on these, because
+ * (a) the return address is invalid by definition, and
+ * (b) we wouldn't want spammers to get confirmation that
+ * this address is live, anyway.
*/
- send_bouncemail(msg, XMIT_ACCEPT,
- "Invalid address.\r\n",
+ send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ "Invalid address in MAIL FROM (SMTP error 553).\r\n",
1, responses);
return(PS_REFUSED);
- default: /* bounce the error back to the sender */
- send_bouncemail(msg, XMIT_ACCEPT,
- "General SMTP/ESMTP error.\r\n",
- 1, responses);
- return(PS_REFUSED);
+ default:
+ /* bounce non-transient errors back to the sender */
+ if (smtperr >= 500 && smtperr <= 599)
+ if (send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ "General SMTP/ESMTP error.\r\n",
+ 1, responses))
+ return(run.bouncemail ? PS_REFUSED : PS_TRANSIENT);
+ /*
+ * We're going to end up here on 4xx errors, like:
+ *
+ * 451: temporarily unable to identify sender (exim)
+ * 452: temporary out-of-queue-space condition on the ESMTP server.
+ *
+ * These are temporary errors. Don't try to ship the message,
+ * and suppress deletion so it can be retried on a future
+ * retrieval cycle.
+ *
+ * Bouncemail *might* be appropriate here as a delay
+ * notification (note; if we ever add this, we must make
+ * sure the RFC1894 Action field is "delayed" rather thwn
+ * "failed"). But it's not really necessary because
+ * these are not actual failures, we're very likely to be
+ * able to recover on the next cycle.
+ */
+ return(PS_TRANSIENT);
}
}
/* set up sinkfp to be an input sink we can ship a message to */
{
struct idlist *idp;
+#ifdef HAVE_SIGACTION
+ struct sigaction sa_new;
+#endif /* HAVE_SIGACTION */
*bad_addresses = *good_addresses = 0;
else if (ctl->mimemsg & MSG_IS_7BIT)
fputs(" BODY=7BIT", sinkfp);
- fprintf(sinkfp, " SIZE=%d\r\n", msg->reallen);
+ /* exim's BSMTP processor does not handle SIZE */
+ /* fprintf(sinkfp, " SIZE=%d", msg->reallen); */
+
+ fprintf(sinkfp, "\r\n");
/*
* RFC 1123 requires that the domain name part of the
for (idp = msg->recipients; idp; idp = idp->next)
if (idp->val.status.mark == XMIT_ACCEPT)
{
- if (strchr(idp->id, '@'))
- fprintf(sinkfp,
- "RCPT TO: %s\r\n", idp->id);
+ if (ctl->smtpname)
+ fprintf(sinkfp, "RCPT TO: %s\r\n", ctl->smtpname);
+ else if (strchr(idp->id, '@'))
+ fprintf(sinkfp,
+ "RCPT TO: %s\r\n", idp->id);
else
fprintf(sinkfp,
- "RCPT TO: %s@%s\r\n", idp->id, ctl->destaddr);
+ "RCPT TO: %s@%s\r\n", idp->id, ctl->destaddr);
*good_addresses = 0;
}
if (ferror(sinkfp))
{
- report(stderr, 0, _("BSMTP file open or preamble write failed"));
+ report(stderr, _("BSMTP file open or preamble write failed\n"));
return(PS_BSMTP);
}
}
if (outlevel >= O_DEBUG)
- report(stdout, 0, _("about to deliver with: %s"), before);
+ report(stdout, _("about to deliver with: %s\n"), before);
#ifdef HAVE_SETEUID
/*
if (!sinkfp)
{
- report(stderr, 0, _("MDA open failed"));
+ report(stderr, _("MDA open failed\n"));
return(PS_IOERR);
}
+#ifndef HAVE_SIGACTION
sigchld = signal(SIGCHLD, SIG_DFL);
+#else
+ memset (&sa_new, 0, sizeof sa_new);
+ sigemptyset (&sa_new.sa_mask);
+ sa_new.sa_handler = SIG_DFL;
+ sigaction (SIGCHLD, &sa_new, &sa_old);
+#endif /* HAVE_SIGACTION */
}
else /* forward to an SMTP or LMTP listener */
{
/* build a connection to the SMTP listener */
if ((smtp_open(ctl) == -1))
{
- report(stderr, errno, _("%cMTP connect to %s failed"),
+ report(stderr, _("%cMTP connect to %s failed\n"),
ctl->listener,
ctl->smtphost ? ctl->smtphost : "localhost");
return(PS_SMTP);
/*
* Try to get the SMTP listener to take the Return-Path
- * address as MAIL FROM . If it won't, fall back on the
- * calling-user ID. This won't affect replies, which use the
- * header From address anyway.
+ * address as MAIL FROM. If it won't, fall back on the
+ * remotename and mailserver host. This won't affect replies,
+ * which use the header From address anyway; the MAIL FROM
+ * address is a place for the SMTP listener to send
+ * bouncemail. The point is to guarantee a FQDN in the MAIL
+ * FROM line -- some SMTP listeners, like smail, become
+ * unhappy otherwise.
*
* RFC 1123 requires that the domain name part of the
* MAIL FROM address be "canonicalized", that is a
- * FQDN or MX but not a CNAME. We'll assume the From
+ * FQDN or MX but not a CNAME. We'll assume the Return-Path
* header is already in this form here (it certainly
* is if rewrite is on). RFC 1123 is silent on whether
* a nonexistent hostname part is considered canonical.
*
* This is a potential problem if the MTAs further upstream
* didn't pass canonicalized From/Return-Path lines, *and* the
- * local SMTP listener insists on them.
+ * local SMTP listener insists on them.
*/
- ap = (msg->return_path[0]) ? msg->return_path : user;
+ if (!msg->return_path[0])
+ {
+ sprintf(addr, "%s@%s", ctl->remotename, ctl->server.truename);
+ ap = addr;
+ }
+ else if (strchr(msg->return_path, '@'))
+ ap = msg->return_path;
+ else /* in case Return-Path existed but was local */
+ {
+ sprintf(addr, "%s@%s", msg->return_path, ctl->server.truename);
+ ap = addr;
+ }
+
if (SMTP_from(ctl->smtp_socket, ap, options) != SM_OK)
return(handle_smtp_report(ctl, msg));
{
if (strchr(idp->id, '@'))
strcpy(addr, idp->id);
- else
+ else {
+ if (ctl->smtpname) {
#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr)-1, "%s@%s", idp->id, ctl->destaddr);
+ snprintf(addr, sizeof(addr)-1, "%s", ctl->smtpname);
#else
- sprintf(addr, "%s@%s", idp->id, ctl->destaddr);
+ sprintf(addr, "%s", ctl->smtpname);
#endif /* HAVE_SNPRINTF */
+ } else {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof(addr)-1, "%s@%s", idp->id, ctl->destaddr);
+#else
+ sprintf(addr, "%s@%s", idp->id, ctl->destaddr);
+#endif /* HAVE_SNPRINTF */
+ }
+ }
if (SMTP_rcpt(ctl->smtp_socket, addr) == SM_OK)
(*good_addresses)++;
else
(*bad_addresses)++;
idp->val.status.mark = XMIT_RCPTBAD;
if (outlevel >= O_VERBOSE)
- report(stderr, 0,
- _("%cMTP listener doesn't like recipient address `%s'"),
+ report(stderr,
+ _("%cMTP listener doesn't like recipient address `%s'\n"),
ctl->listener, addr);
}
}
if (*bad_addresses)
- send_bouncemail(msg, XMIT_RCPTBAD,
+ send_bouncemail(ctl, msg, XMIT_RCPTBAD,
"Some addresses were rejected by the MDA fetchmail forwards to.\r\n",
*bad_addresses, from_responses);
/*
if (SMTP_rcpt(ctl->smtp_socket, addr) != SM_OK)
{
- report(stderr, 0, _("can't even send to %s!"), run.postmaster);
+ report(stderr, _("can't even send to %s!\n"), run.postmaster);
SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
return(PS_SMTP);
}
if (outlevel >= O_VERBOSE)
- report(stderr, 0, _("no address matches; forwarding to %s."), run.postmaster);
+ report(stderr, _("no address matches; forwarding to %s.\n"), run.postmaster);
}
/*
pclose(sinkfp);
sinkfp = (FILE *)NULL;
}
+#ifndef HAVE_SIGACTION
signal(SIGCHLD, sigchld);
+#else
+ sigaction (SIGCHLD, &sa_old, NULL);
+#endif /* HAVE_SIGACTION */
}
}
}
else
rc = 0;
+#ifndef HAVE_SIGACTION
signal(SIGCHLD, sigchld);
+#else
+ sigaction (SIGCHLD, &sa_old, NULL);
+#endif /* HAVE_SIGACTION */
if (rc)
{
- report(stderr, 0, _("MDA exited abnormally or returned nonzero status"));
+ report(stderr,
+ _("MDA exited abnormally or returned nonzero status\n"));
return(FALSE);
}
}
else if (ctl->bsmtp)
{
+ int error;
+
/* implicit disk-full check here... */
- fputs("..\r\n", sinkfp);
+ fputs(".\r\n", sinkfp);
+ error = ferror(sinkfp);
if (strcmp(ctl->bsmtp, "-"))
- fclose(sinkfp);
- if (ferror(sinkfp))
+ if (fclose(sinkfp) == EOF) error = 1;
+ if (error)
{
- report(stderr, 0, _("Message termination or close of BSMTP file failed"));
+ report(stderr,
+ _("Message termination or close of BSMTP file failed\n"));
return(FALSE);
}
}
return(FALSE);
else
{
- report(stderr, 0, _("SMTP listener refused delivery"));
+ report(stderr, _("SMTP listener refused delivery\n"));
return(TRUE);
}
}
* to people who got it the first time.
*/
if (ctl->listener == LMTP_MODE)
+ {
if (lmtp_responses == 0)
{
SMTP_ok(ctl->smtp_socket);
* comply.
*/
if (atoi(smtp_response) == 503)
- report(stderr, 0, _("LMTP delivery error on EOM"));
+ report(stderr, _("LMTP delivery error on EOM\n"));
else
- report(stderr, 0,
- _("Unexpected non-503 response to LMTP EOM: %s"),
+ report(stderr,
+ _("Unexpected non-503 response to LMTP EOM: %s\n"),
smtp_response);
/*
else
/*
* One or more deliveries failed.
- * If we can bounce a failures list back to the sender,
- * return TRUE, deleting the message from the server so
- * it won't be re-forwarded on subsequent poll cycles.
+ * If we can bounce a failures list back to the
+ * sender, and the postmaster does not want to
+ * deal with the bounces return TRUE, deleting the
+ * message from the server so it won't be
+ * re-forwarded on subsequent poll cycles.
*/
- return(send_bouncemail(msg, XMIT_ACCEPT,
- "LSMTP partial delivery failure.\r\n",
- errors, responses));
+ return(send_bouncemail(ctl, msg, XMIT_ACCEPT,
+ "LSMTP partial delivery failure.\r\n",
+ errors, responses));
}
+ }
}
return(TRUE);