]> Pileus Git - ~andy/fetchmail/blobdiff - sink.c
Better error return reporting from the MDA.
[~andy/fetchmail] / sink.c
diff --git a/sink.c b/sink.c
index 4e160206004dd7eb3ac9099db42b6dd4cbfb9ddd..a906fde711ef96c4511ecca7ad2580275ac082d4 100644 (file)
--- a/sink.c
+++ b/sink.c
 #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"
@@ -216,6 +224,37 @@ static void sanitize(char *s)
        *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[])
@@ -306,15 +345,15 @@ static int send_bouncemail(struct query *ctl, struct msgblk *msg,
                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;
@@ -491,6 +530,48 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
     }
 }
 
+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;
 
@@ -605,15 +686,9 @@ static int open_bsmtp_sink(struct query *ctl, struct msgblk *msg,
     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);
@@ -652,6 +727,7 @@ static const char *is_quad(const char *q)
     return NULL;
   /* Make sure quad is < 255 */
   if ( (r-q) == 3)
+  {
     if (*q > '2')
       return NULL;
     else if (*q == '2')
@@ -664,6 +740,7 @@ static const char *is_quad(const char *q)
           return NULL;
       }
     }
+  }
   return r;
 }
 
@@ -801,59 +878,52 @@ static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
     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
@@ -874,14 +944,6 @@ static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
      */
     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)
@@ -889,18 +951,8 @@ static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
            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 */
@@ -917,8 +969,9 @@ static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
      */
     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);
     }
 
     /*
@@ -1163,7 +1216,13 @@ void release_sink(struct query *ctl)
 /* 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)
@@ -1195,8 +1254,17 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
 
        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);
        }
     }
@@ -1208,7 +1276,10 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
        fputs(".\r\n", sinkfp);
        error = ferror(sinkfp);
        if (strcmp(ctl->bsmtp, "-"))
+       {
            if (fclose(sinkfp) == EOF) error = 1;
+           sinkfp = (FILE *)NULL;
+       }
        if (error)
        {
            report(stderr,