#include <ctype.h>
#include <time.h>
+/* for W* macros after pclose() */
+#define _USE_BSD
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+
#include "fetchmail.h"
#include "socket.h"
#include "smtp.h"
*cp = '_';
}
+char *rcpt_address(struct query *ctl, const char *id,
+ int usesmtpname)
+{
+ static char addr[HOSTLEN+USERNAMELEN+1];
+ if (strchr(id, '@'))
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof (addr), "%s", id);
+#else
+ sprintf(addr, "%s", id);
+#endif /* HAVE_SNPRINTF */
+ }
+ else if (usesmtpname && ctl->smtpname)
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof (addr), "%s", ctl->smtpname);
+#else
+ sprintf(addr, "%s", ctl->smtpname);
+#endif /* HAVE_SNPRINTF */
+ }
+ else
+ {
+#ifdef HAVE_SNPRINTF
+ snprintf(addr, sizeof (addr), "%s@%s", id, ctl->destaddr);
+#else
+ sprintf(addr, "%s@%s", id, ctl->destaddr);
+#endif /* HAVE_SNPRINTF */
+ }
+ return addr;
+}
+
static int send_bouncemail(struct query *ctl, struct msgblk *msg,
int userclass, char *message,
int nerrors, char *errors[])
char *error;
/* Minimum RFC1894 compliance + Diagnostic-Code field */
SockPrintf(sock, "\r\n");
- SockPrintf(sock, "Final-Recipient: rfc822; %s@%s\r\n",
- idp->id, fetchmailhost);
+ SockPrintf(sock, "Final-Recipient: rfc822; %s\r\n",
+ rcpt_address (ctl, idp->id, 1));
SockPrintf(sock, "Last-Attempt-Date: %s\r\n", rfc822timestamp());
SockPrintf(sock, "Action: failed\r\n");
if (nerrors == 1)
/* one error applies to all users */
error = errors[0];
- else if (nerrors > nusers)
+ else if (nerrors <= nusers)
{
SockPrintf(sock, "Internal error: SMTP error count doesn't match number of recipients.\r\n");
break;
}
}
+static int handle_smtp_report_without_bounce(struct query *ctl, struct msgblk *msg)
+/* handle SMTP errors based on the content of SMTP_response */
+/* atleast one PS_TRANSIENT: do not send the bounce mail, keep the mail;
+ * no PS_TRANSIENT, atleast one PS_SUCCESS: send the bounce mail, delete the mail;
+ * no PS_TRANSIENT, no PS_SUCCESS: do not send the bounce mail, delete the mail */
+{
+ int smtperr = atoi(smtp_response);
+
+ if (str_find(&ctl->antispam, smtperr))
+ {
+ if (run.spambounce)
+ return(PS_SUCCESS);
+ return(PS_REFUSED);
+ }
+
+ if (smtperr >= 400)
+ report(stderr, GT_("%cMTP error: %s\n"),
+ ctl->listener,
+ smtp_response);
+
+ switch (smtperr)
+ {
+ case 552: /* message exceeds fixed maximum message size */
+ if (run.bouncemail)
+ return(PS_SUCCESS);
+ return(PS_REFUSED);
+
+ case 553: /* invalid sending domain */
+#ifdef __DONT_FEED_THE_SPAMMERS__
+ if (run.bouncemail)
+ return(PS_SUCCESS);
+#endif /* __DONT_FEED_THE_SPAMMERS__ */
+ return(PS_REFUSED);
+
+ default:
+ /* bounce non-transient errors back to the sender */
+ if (smtperr >= 500 && smtperr <= 599)
+ return(PS_SUCCESS);
+ return(PS_TRANSIENT);
+ }
+}
+
/* these are shared by open_sink and stuffline */
static FILE *sinkfp;
for (idp = msg->recipients; idp; idp = idp->next)
if (idp->val.status.mark == XMIT_ACCEPT)
{
- 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);
- *good_addresses = 0;
+ fprintf(sinkfp, "RCPT TO: %s\r\n",
+ rcpt_address (ctl, idp->id, 1));
+ (*good_addresses)++;
}
fputs("DATA\r\n", sinkfp);
return NULL;
/* Make sure quad is < 255 */
if ( (r-q) == 3)
+ {
if (*q > '2')
return NULL;
else if (*q == '2')
return NULL;
}
}
+ }
return r;
}
for (idp = msg->recipients; idp; idp = idp->next)
if (idp->val.status.mark == XMIT_ACCEPT)
{
- if (strchr(idp->id, '@'))
- strcpy(addr, idp->id);
- else {
- if (ctl->smtpname) {
-#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr), "%s", ctl->smtpname);
-#else
- sprintf(addr, "%s", ctl->smtpname);
-#endif /* HAVE_SNPRINTF */
-
- } else {
-#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr), "%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)
+ const char *address;
+ address = rcpt_address (ctl, idp->id, 1);
+ if (SMTP_rcpt(ctl->smtp_socket, address) == SM_OK)
(*good_addresses)++;
else
{
-#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
- char errbuf[POPBUFSIZE];
-#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
- if (handle_smtp_report(ctl, msg) == PS_TRANSIENT)
+ switch (handle_smtp_report_without_bounce(ctl, msg))
+ {
+ case PS_TRANSIENT:
force_transient_error = 1;
+ break;
+ case PS_SUCCESS:
#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
-#ifdef HAVE_SNPRINTF
- snprintf(errbuf, sizeof(errbuf), "%s: %s",
- idp->id, smtp_response);
-#else
- strncpy(errbuf, idp->id, sizeof(errbuf));
- strcat(errbuf, ": ");
- strcat(errbuf, smtp_response);
-#endif /* HAVE_SNPRINTF */
-
- xalloca(from_responses[*bad_addresses],
- char *,
- strlen(errbuf)+1);
- strcpy(from_responses[*bad_addresses], errbuf);
+ xalloca(from_responses[*bad_addresses],
+ char *,
+ strlen(smtp_response)+1);
+ strcpy(from_responses[*bad_addresses], smtp_response);
#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
- (*bad_addresses)++;
- idp->val.status.mark = XMIT_RCPTBAD;
- if (outlevel >= O_VERBOSE)
- report(stderr,
- GT_("%cMTP listener doesn't like recipient address `%s'\n"),
- ctl->listener, addr);
+ (*bad_addresses)++;
+ idp->val.status.mark = XMIT_RCPTBAD;
+ if (outlevel >= O_VERBOSE)
+ report(stderr,
+ GT_("%cMTP listener doesn't like recipient address `%s'\n"),
+ ctl->listener, address);
+ break;
+
+ case PS_REFUSED:
+ if (outlevel >= O_VERBOSE)
+ report(stderr,
+ GT_("%cMTP listener doesn't really like recipient address `%s'\n"),
+ ctl->listener, address);
+ break;
+ }
}
}
+ if (force_transient_error) {
+ /* do not risk dataloss due to overengineered multidrop
+ * crap. If one of the recipients returned PS_TRANSIENT,
+ * we return exactly that.
+ */
+ SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
+ return(PS_TRANSIENT);
+ }
#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
/*
* This should not be necessary, because the SMTP listener itself
*/
if (!(*good_addresses))
{
- if (force_transient_error) {
- /* do not risk dataloss due to overengineered multidrop
- * crap. If one of the recipients returned PS_TRANSIENT,
- * we return exactly that.
- */
- SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
- return(PS_TRANSIENT);
- }
if (!run.postmaster[0])
{
if (outlevel >= O_VERBOSE)
SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
return(PS_REFUSED);
}
- if (strchr(run.postmaster, '@'))
- strncpy(addr, run.postmaster, sizeof(addr));
- else
- {
-#ifdef HAVE_SNPRINTF
- snprintf(addr, sizeof(addr), "%s@%s", run.postmaster, ctl->destaddr);
-#else
- sprintf(addr, "%s@%s", run.postmaster, ctl->destaddr);
-#endif /* HAVE_SNPRINTF */
- }
-
- if (SMTP_rcpt(ctl->smtp_socket, addr) != SM_OK)
+ if (SMTP_rcpt(ctl->smtp_socket,
+ rcpt_address (ctl, run.postmaster, 0)) != SM_OK)
{
report(stderr, GT_("can't even send to %s!\n"), run.postmaster);
SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
*/
if (SMTP_data(ctl->smtp_socket) != SM_OK)
{
+ int err = handle_smtp_report(ctl, msg);
SMTP_rset(ctl->smtp_socket); /* stay on the safe side */
- return(handle_smtp_report(ctl, msg));
+ return(err);
}
/*
/* release the per-message output sink, whether it's a pipe or SMTP socket */
{
if (ctl->bsmtp && sinkfp)
- fclose(sinkfp);
+ {
+ if (strcmp(ctl->bsmtp, "-"))
+ {
+ fclose(sinkfp);
+ sinkfp = (FILE *)NULL;
+ }
+ }
else if (ctl->mda)
{
if (sinkfp)
if (rc)
{
- report(stderr,
- GT_("MDA returned nonzero status %d\n"), rc);
+ if (WIFSIGNALED(rc)) {
+ report(stderr,
+ GT_("MDA died of signal %d\n"), WTERMSIG(rc));
+ } else if (WIFEXITED(rc)) {
+ report(stderr,
+ GT_("MDA returned nonzero status %d\n"), WEXITSTATUS(rc));
+ } else {
+ report(stderr,
+ GT_("Strange: MDA pclose returned %d, cannot handle at %s:%d\n"), rc, __FILE__, __LINE__);
+ }
+
return(FALSE);
}
}
fputs(".\r\n", sinkfp);
error = ferror(sinkfp);
if (strcmp(ctl->bsmtp, "-"))
+ {
if (fclose(sinkfp) == EOF) error = 1;
+ sinkfp = (FILE *)NULL;
+ }
if (error)
{
report(stderr,