]> Pileus Git - ~andy/fetchmail/blobdiff - sink.c
Added warning to differs.
[~andy/fetchmail] / sink.c
diff --git a/sink.c b/sink.c
index 6ec32d87d73820920f8952ef6889e0c3975301bf..e3cb4a8fc21e4e2bade12f005b21e0b0c54c4043 100644 (file)
--- a/sink.c
+++ b/sink.c
@@ -30,6 +30,7 @@
 #else
 #include  <varargs.h>
 #endif
+#include  <ctype.h>
 
 #include  "fetchmail.h"
 #include  "socket.h"
 #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);
        ctl->smtp_socket = -1;
@@ -94,11 +95,11 @@ static int smtp_open(struct query *ctl)
        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);
 
@@ -108,11 +109,11 @@ static int smtp_open(struct query *ctl)
            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,
@@ -161,7 +162,7 @@ static int smtp_open(struct query *ctl)
     ctl->destaddr = ctl->smtpaddress ? ctl->smtpaddress : ( ctl->smtphost ? ctl->smtphost : "localhost");
 
     if (outlevel >= O_DEBUG && ctl->smtp_socket != -1)
-       error(0, 0, _("forwarding to %s"), ctl->smtphost);
+       report(stdout, _("forwarding to %s\n"), ctl->smtphost);
 
     return(ctl->smtp_socket);
 }
@@ -203,6 +204,7 @@ int stuffline(struct query *ctl, char *buf)
      * decorated any . lines it sends back up.
      */
     if (*buf == '.')
+    {
        if (ctl->server.base_protocol->delimited)       /* server has already byte-stuffed */
        {
            if (ctl->mda)
@@ -217,6 +219,7 @@ int stuffline(struct query *ctl, char *buf)
            else
                /* leave it alone */;
        }
+    }
 
     /* we may need to strip carriage returns */
     if (ctl->stripcr)
@@ -251,18 +254,21 @@ static void sanitize(char *s)
        *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];
-    int i, sock;
+    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);
@@ -272,25 +278,25 @@ static int send_bouncemail(struct msgblk *msg, int userclass,
                || 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);
 
     sprintf(boundary, 
            "om-mani-padme-hum-%d-%d-%ld", 
-           getpid(), getppid(), time((time_t *)NULL));
+           (int)getpid(), (int)getppid(), time((time_t *)NULL));
+
+    ts = rfc822timestamp();
 
     if (outlevel >= O_VERBOSE)
-       error(0, 0, "SMTP: (bounce-message body)");
+       report(stdout, "SMTP: (bounce-message body)\n");
 
     /* bouncemail headers */
-    SockPrintf(sock, "Return-Path: <>");
-    SockPrintf(sock, "From: FETCHMAIL-DAEMON@%s\r\n", fetchmailhost);
-    SockPrintf(sock, "To: %s\n", msg->return_path);
+    SockPrintf(sock, "Return-Path: <>\r\n");
+    SockPrintf(sock, "From: %s\r\n", daemon_name);
+    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; boundary=\"%s\"\r\n", boundary);
-    SockPrintf(sock, "\r\n");
-    SockPrintf(sock, "Content-Transfer-Encoding: 7bit\r\n");
+    SockPrintf(sock, "Content-Type: multipart/report; report-type=delivery-status;\r\n\tboundary=\"%s\"\r\n", boundary);
     SockPrintf(sock, "\r\n");
 
     /* RFC1892 part 1 -- human-readable message */
@@ -320,6 +326,7 @@ static int send_bouncemail(struct msgblk *msg, int userclass,
                /* Minimum RFC1894 compliance + Diagnostic-Code field */
                SockPrintf(sock, "\r\n");
                SockPrintf(sock, "Final-Recipient: rfc822; %s\r\n", idp->id);
+               SockPrintf(sock, "Last-Attempt-Date: %s\r\n", ts);
                SockPrintf(sock, "Action: failed\r\n");
 
                if (nerrors == 1)
@@ -363,16 +370,15 @@ static int send_bouncemail(struct msgblk *msg, int userclass,
     return(TRUE);
 }
 
-static int handle_smtp_error(struct query *ctl, struct msgblk *msg)
+static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
 /* handle SMTP errors based on the content of SMTP_response */
+/* Mail is deleted from the server if this function returns PS_REFUSED. */
 {
     int smtperr = atoi(smtp_response);
     char *responses[1];
 
-    responses[0] = smtp_response;
-
-    /* required by RFC1870; sets us up to be able to send bouncemail */
-    SMTP_rset(ctl->smtp_socket);
+    xalloca(responses[0], char *, strlen(smtp_response)+1);
+    strcpy(responses[0], smtp_response);
 
     /*
      * Note: send_bouncemail message strings are not made subject
@@ -389,15 +395,16 @@ static int handle_smtp_error(struct query *ctl, struct msgblk *msg)
         * 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,
+       SMTP_rset(ctl->smtp_socket);    /* stay on the safe site */
+       send_bouncemail(ctl, msg, XMIT_ACCEPT,
                        "Our spam filter rejected this transaction.\r\n", 
                        1, responses);
        return(PS_REFUSED);
@@ -409,7 +416,7 @@ static int handle_smtp_error(struct query *ctl, struct msgblk *msg)
      * an error when the return code is less specific.
      */
     if (smtperr >= 400)
-       error(0, -1, _("%cMTP error: %s"), 
+       report(stderr, _("%cMTP error: %s\n"), 
              ctl->listener,
              smtp_response);
 
@@ -423,10 +430,13 @@ static int handle_smtp_error(struct query *ctl, struct msgblk *msg)
         * a future retrieval cycle. 
         *
         * Bouncemail *might* be appropriate here as a delay
-        * notification.  But it's not really necessary because
+        * 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.
         */
+       SMTP_rset(ctl->smtp_socket);    /* required by RFC1870 */
        return(PS_TRANSIENT);
 
     case 552: /* message exceeds fixed maximum message size */
@@ -435,26 +445,34 @@ static int handle_smtp_error(struct query *ctl, struct msgblk *msg)
         * ESMTP server.  Don't try to ship the message, 
         * and allow it to be deleted.
         */
-       send_bouncemail(msg, XMIT_ACCEPT,
+       SMTP_rset(ctl->smtp_socket);    /* required by RFC1870 */
+       send_bouncemail(ctl, msg, XMIT_ACCEPT,
                        "This message was too large.\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,
+       SMTP_rset(ctl->smtp_socket);    /* stay on the safe side */
+       send_bouncemail(ctl, msg, XMIT_ACCEPT,
                        "Invalid address.\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 */
+       SMTP_rset(ctl->smtp_socket);    /* stay on the safe side */
+       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);
+       return(PS_TRANSIENT);
     }
 }
 
@@ -510,7 +528,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
 
        if (ferror(sinkfp))
        {
-           error(0, -1, _("BSMTP file open or preamble write failed"));
+           report(stderr, _("BSMTP file open or preamble write failed\n"));
            return(PS_BSMTP);
        }
     }
@@ -631,7 +649,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
 
 
        if (outlevel >= O_DEBUG)
-           error(0, 0, _("about to deliver with: %s"), before);
+           report(stdout, _("about to deliver with: %s\n"), before);
 
 #ifdef HAVE_SETEUID
        /*
@@ -654,7 +672,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
 
        if (!sinkfp)
        {
-           error(0, 0, _("MDA open failed"));
+           report(stderr, _("MDA open failed\n"));
            return(PS_IOERR);
        }
 
@@ -671,7 +689,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
        /* build a connection to the SMTP listener */
        if ((smtp_open(ctl) == -1))
        {
-           error(0, errno, _("%cMTP connect to %s failed"),
+           report(stderr, _("%cMTP connect to %s failed\n"),
                  ctl->listener,
                  ctl->smtphost ? ctl->smtphost : "localhost");
            return(PS_SMTP);
@@ -710,7 +728,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
         */
        ap = (msg->return_path[0]) ? msg->return_path : user;
        if (SMTP_from(ctl->smtp_socket, ap, options) != SM_OK)
-           return(handle_smtp_error(ctl, msg));
+           return(handle_smtp_report(ctl, msg));
 
        /*
         * Now list the recipient addressees
@@ -749,13 +767,13 @@ int open_sink(struct query *ctl, struct msgblk *msg,
                    (*bad_addresses)++;
                    idp->val.status.mark = XMIT_RCPTBAD;
                    if (outlevel >= O_VERBOSE)
-                       error(0, 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);
        /*
@@ -780,13 +798,13 @@ int open_sink(struct query *ctl, struct msgblk *msg,
 
            if (SMTP_rcpt(ctl->smtp_socket, addr) != SM_OK)
            {
-               error(0, 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)
-               error(0, 0, _("no address matches; forwarding to %s."), run.postmaster);
+               report(stderr, _("no address matches; forwarding to %s.\n"), run.postmaster);
        }
 
        /* 
@@ -794,7 +812,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,
         * Some listeners (like zmailer) may return antispam errors here.
         */
        if (SMTP_data(ctl->smtp_socket) != SM_OK)
-           return(handle_smtp_error(ctl, msg));
+           return(handle_smtp_report(ctl, msg));
     }
 
     /*
@@ -840,7 +858,8 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
        signal(SIGCHLD, sigchld);
        if (rc)
        {
-           error(0, -1, _("MDA exited abnormally or returned nonzero status"));
+           report(stderr, 
+                  _("MDA exited abnormally or returned nonzero status\n"));
            return(FALSE);
        }
     }
@@ -852,7 +871,8 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
            fclose(sinkfp);
        if (ferror(sinkfp))
        {
-           error(0, -1, _("Message termination or close of BSMTP file failed"));
+           report(stderr, 
+                  _("Message termination or close of BSMTP file failed\n"));
            return(FALSE);
        }
     }
@@ -861,11 +881,11 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
        /* write message terminator */
        if (SMTP_eom(ctl->smtp_socket) != SM_OK)
        {
-           if (handle_smtp_error(ctl, msg) != PS_REFUSED)
+           if (handle_smtp_report(ctl, msg) != PS_REFUSED)
                return(FALSE);
            else
            {
-               error(0, -1, _("SMTP listener refused delivery"));
+               report(stderr, _("SMTP listener refused delivery\n"));
                return(TRUE);
            }
        }
@@ -881,6 +901,7 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
         * to people who got it the first time.
         */
        if (ctl->listener == LMTP_MODE)
+       {
            if (lmtp_responses == 0)
            {
                SMTP_ok(ctl->smtp_socket); 
@@ -893,10 +914,10 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
                 * comply.
                 */
                if (atoi(smtp_response) == 503)
-                   error(0, -1, _("LMTP delivery error on EOM"));
+                   report(stderr, _("LMTP delivery error on EOM\n"));
                else
-                   error(0, -1,
-                         _("Unexpected non-503 response to LMTP EOM: %s"),
+                   report(stderr,
+                         _("Unexpected non-503 response to LMTP EOM: %s\n"),
                          smtp_response);
 
                /*
@@ -933,14 +954,17 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
                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);
@@ -971,7 +995,9 @@ int open_warning_by_mail(struct query *ctl, struct msgblk *msg)
      * option to ESMTP; the message length would be more trouble than
      * it's worth to compute.
      */
-    struct msgblk reply = {NULL, NULL, "FETCHMAIL-DAEMON", 0};
+    struct msgblk reply = {NULL, NULL, "FETCHMAIL-DAEMON@", 0};
+
+    strcat(reply.return_path, fetchmailhost);
 
     if (!MULTIDROP(ctl))               /* send to calling user */
     {