]> Pileus Git - ~andy/fetchmail/blobdiff - sink.c
More from HH's patch.
[~andy/fetchmail] / sink.c
diff --git a/sink.c b/sink.c
index 419e0d33baea369f991089ea2aa6615db1fb78a3..1f54b2f87cad5382eb2f1a5bd59640b4a875fab0 100644 (file)
--- a/sink.c
+++ b/sink.c
@@ -104,6 +104,8 @@ static int smtp_open(struct query *ctl)
            xalloca(parsed_host, char *, strlen(idp->id) + 1);
 
            ctl->smtphost = idp->id;  /* remember last host tried. */
+           if(ctl->smtphost[0]=='/')
+               ctl->listener = LMTP_MODE;
 
            strcpy(parsed_host, idp->id);
            if ((cp = strrchr(parsed_host, '/')))
@@ -116,6 +118,10 @@ static int smtp_open(struct query *ctl)
 #endif /* INET6_ENABLE */
            }
 
+           if (ctl->smtphost[0]=='/'){
+               if((ctl->smtp_socket = UnixOpen(ctl->smtphost))==-1)
+                   continue;
+           } else
            if ((ctl->smtp_socket = SockOpen(parsed_host,portnum,NULL,
                                             ctl->server.plugout)) == -1)
                continue;
@@ -159,7 +165,7 @@ static int smtp_open(struct query *ctl)
      * enforce this.  Now that we have the actual hostname,
      * compute what we should canonicalize with.
      */
-    ctl->destaddr = ctl->smtpaddress ? ctl->smtpaddress : ( ctl->smtphost ? ctl->smtphost : "localhost");
+    ctl->destaddr = ctl->smtpaddress ? ctl->smtpaddress : ( ctl->smtphost && ctl->smtphost[0] != '/' ? ctl->smtphost : "localhost");
 
     if (outlevel >= O_DEBUG && ctl->smtp_socket != -1)
        report(stdout, _("forwarding to %s\n"), ctl->smtphost);
@@ -169,7 +175,11 @@ static int smtp_open(struct query *ctl)
 
 /* 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) */
@@ -260,7 +270,7 @@ static int send_bouncemail(struct query *ctl, struct msgblk *msg,
 /* bounce back an error report a la RFC 1892 */
 {
     char daemon_name[18 + HOSTLEN] = "FETCHMAIL-DAEMON@";
-    char boundary[BUFSIZ], *ts, *bounce_to;
+    char boundary[BUFSIZ], *bounce_to;
     int sock;
 
     /* don't bounce  in reply to undeliverable bounces */
@@ -287,13 +297,11 @@ static int send_bouncemail(struct query *ctl, struct msgblk *msg,
            "foo-mani-padme-hum-%d-%d-%ld", 
            (int)getpid(), (int)getppid(), time((time_t *)NULL));
 
-    ts = rfc822timestamp();
-
     if (outlevel >= O_VERBOSE)
-       report(stdout, "SMTP: (bounce-message body)\n");
+       report(stdout, _("SMTP: (bounce-message body)\n"));
     else
        /* this will usually go to sylog... */
-       report(stderr, "mail from %s bounced to %s\n",
+       report(stderr, _("mail from %s bounced to %s\n"),
               daemon_name, bounce_to);
 
     /* bouncemail headers */
@@ -331,7 +339,7 @@ static int send_bouncemail(struct query *ctl, struct msgblk *msg,
                /* 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, "Last-Attempt-Date: %s\r\n", rfc822timestamp());
                SockPrintf(sock, "Action: failed\r\n");
 
                if (nerrors == 1)
@@ -385,10 +393,10 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
     xalloca(responses[0], char *, strlen(smtp_response)+1);
     strcpy(responses[0], smtp_response);
 
-    SMTP_rset(ctl->smtp_socket);    /* stay on the safe site */
+    SMTP_rset(ctl->smtp_socket);    /* stay on the safe side */
 
     if (outlevel >= O_DEBUG)
-       report(stdout, "Saved error is still %d\n", smtperr);
+       report(stdout, _("Saved error is still %d\n"), smtperr);
 
     /*
      * Note: send_bouncemail message strings are not made subject
@@ -405,7 +413,13 @@ static int handle_smtp_report(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.  Default values:
+        * being deleted.  There's no point in bouncing the
+        * email either since most spammers don't put their
+        * real return email address anywhere in the headers
+        * (unless the user insists with the SET SPAMBOUNCE
+        * config option).
+        *
+        * Default values:
         *
         * 571 = sendmail's "unsolicited email refused"
         * 550 = exim's new antispam response (temporary)
@@ -413,7 +427,8 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
         * 554 = Postfix antispam response.
         *
         */
-       send_bouncemail(ctl, msg, XMIT_ACCEPT,
+       if (run.spambounce)
+               send_bouncemail(ctl, msg, XMIT_ACCEPT,
                        "Our spam filter rejected this transaction.\r\n", 
                        1, responses);
        return(PS_REFUSED);
@@ -427,7 +442,7 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)
     if (smtperr >= 400)
        report(stderr, _("%cMTP error: %s\n"), 
              ctl->listener,
-             smtp_response);
+             responses[0]);
 
     switch (smtperr)
     {
@@ -488,6 +503,9 @@ int open_sink(struct query *ctl, struct msgblk *msg,
 /* 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;
 
@@ -525,12 +543,14 @@ int open_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, '@'))
-                   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;
            }
 
@@ -542,7 +562,202 @@ int open_sink(struct query *ctl, struct msgblk *msg,
            return(PS_BSMTP);
        }
     }
-    else if (ctl->mda)         /* we have a declared MDA */
+
+    /* 
+     * Try to forward to an SMTP or LMTP listener.  If the attempt to 
+     * open a socket fails, fall through to attempt delivery via
+     * local MDA.
+     */
+    else if (!ctl->mda && smtp_open(ctl) != -1)
+    {
+       const char      *ap;
+       char            options[MSGBUFSIZE]; 
+       char            addr[HOSTLEN+USERNAMELEN+1];
+       char            **from_responses;
+       int             total_addresses;
+
+       /*
+        * Compute ESMTP options.
+        */
+       options[0] = '\0';
+       if (ctl->server.esmtp_options & ESMTP_8BITMIME) {
+             if (ctl->pass8bits || (ctl->mimemsg & MSG_IS_8BIT))
+               strcpy(options, " BODY=8BITMIME");
+             else if (ctl->mimemsg & MSG_IS_7BIT)
+               strcpy(options, " BODY=7BIT");
+        }
+
+       if ((ctl->server.esmtp_options & ESMTP_SIZE) && msg->reallen > 0)
+           sprintf(options + strlen(options), " SIZE=%d", msg->reallen);
+
+       /*
+        * Try to get the SMTP listener to take the Return-Path
+        * 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 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. 
+        */
+       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));
+
+       /*
+        * Now list the recipient addressees
+        */
+       total_addresses = 0;
+       for (idp = msg->recipients; idp; idp = idp->next)
+           total_addresses++;
+       xalloca(from_responses, char **, sizeof(char *) * total_addresses);
+       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)-1, "%s", ctl->smtpname);
+#else
+                       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
+               {
+                   char        errbuf[POPBUFSIZE];
+                   int res;
+
+                   if ((res = handle_smtp_report(ctl, msg))==PS_REFUSED)
+                       return(PS_REFUSED);
+
+                   strcpy(errbuf, idp->id);
+                   strcat(errbuf, ": ");
+                   strcat(errbuf, smtp_response);
+
+                   xalloca(from_responses[*bad_addresses], 
+                           char *, 
+                           strlen(errbuf)+1);
+                   strcpy(from_responses[*bad_addresses], errbuf);
+
+                   (*bad_addresses)++;
+                   idp->val.status.mark = XMIT_RCPTBAD;
+                   if (outlevel >= O_VERBOSE)
+                       report(stderr, 
+                             _("%cMTP listener doesn't like recipient address `%s'\n"),
+                             ctl->listener, addr);
+               }
+           }
+       if (*bad_addresses)
+           send_bouncemail(ctl, msg, XMIT_RCPTBAD,
+                            "Some addresses were rejected by the MDA fetchmail forwards to.\r\n",
+                            *bad_addresses, from_responses);
+       /*
+        * It's tempting to do local notification only if bouncemail was
+        * insufficient -- that is, to add && total_addresses > *bad_addresses
+        * to the test here.  The problem with this theory is that it would
+        * make initial diagnosis of a broken multidrop configuration very
+        * hard -- most single-recipient messages would just invisibly bounce.
+        */
+       if (!(*good_addresses)) 
+       {
+           if (strchr(run.postmaster, '@'))
+               strcpy(addr, run.postmaster);
+           else
+           {
+#ifdef HAVE_SNPRINTF
+               snprintf(addr, sizeof(addr)-1, "%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)
+           {
+               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, _("no address matches; forwarding to %s.\n"), run.postmaster);
+       }
+
+       /* 
+        * Tell the listener we're ready to send data.
+        * Some listeners (like zmailer) may return antispam errors here.
+        */
+       if (SMTP_data(ctl->smtp_socket) != SM_OK)
+           return(handle_smtp_report(ctl, msg));
+    }
+
+    /*
+     * Awkward case.  User didn't specify an MDA.  Our attempt to get a
+     * listener socket failed.  Try to cope anyway -- initial configuration
+     * may have found procmail.
+     */
+    else if (!ctl->mda)
+    {
+       report(stderr, _("%cMTP connect to %s failed\n"),
+              ctl->listener,
+              ctl->smtphost ? ctl->smtphost : "localhost");
+
+#ifndef FALLBACK_MDA
+       /* No fallback MDA declared.  Bail out. */
+       return(PS_SMTP);
+#else
+       /*
+        * If user had things set up to forward offsite, no way
+        * we want to deliver locally!
+        */
+       if (ctl->smtphost && strcmp(ctl->smtphost, "localhost"))
+           return(PS_SMTP);
+
+       /* 
+        * User was delivering locally.  We have a fallback MDA.
+        * Latch it in place, logging the error, and fall through.
+        */
+       ctl->mda = FALLBACK_MDA;
+
+       report(stderr, _("can't raise the listener; falling back to " FALLBACK_MDA));
+#endif
+    }
+
+    if (ctl->mda)              /* must deliver through an MDA */
     {
        int     length = 0, fromlen = 0, nameslen = 0;
        char    *names = NULL, *before, *after, *from = NULL;
@@ -686,159 +901,14 @@ int open_sink(struct query *ctl, struct msgblk *msg,
            return(PS_IOERR);
        }
 
+#ifndef HAVE_SIGACTION
        sigchld = signal(SIGCHLD, SIG_DFL);
-    }
-    else /* forward to an SMTP or LMTP listener */
-    {
-       const char      *ap;
-       char            options[MSGBUFSIZE]; 
-       char            addr[HOSTLEN+USERNAMELEN+1];
-       char            **from_responses;
-       int             total_addresses;
-
-       /* build a connection to the SMTP listener */
-       if ((smtp_open(ctl) == -1))
-       {
-           report(stderr, _("%cMTP connect to %s failed\n"),
-                 ctl->listener,
-                 ctl->smtphost ? ctl->smtphost : "localhost");
-           return(PS_SMTP);
-       }
-
-       /*
-        * Compute ESMTP options.
-        */
-       options[0] = '\0';
-       if (ctl->server.esmtp_options & ESMTP_8BITMIME) {
-             if (ctl->pass8bits || (ctl->mimemsg & MSG_IS_8BIT))
-               strcpy(options, " BODY=8BITMIME");
-             else if (ctl->mimemsg & MSG_IS_7BIT)
-               strcpy(options, " BODY=7BIT");
-        }
-
-       if ((ctl->server.esmtp_options & ESMTP_SIZE) && msg->reallen > 0)
-           sprintf(options + strlen(options), " SIZE=%d", msg->reallen);
-
-       /*
-        * Try to get the SMTP listener to take the Return-Path
-        * 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 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. 
-        */
-       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));
-
-       /*
-        * Now list the recipient addressees
-        */
-       total_addresses = 0;
-       for (idp = msg->recipients; idp; idp = idp->next)
-           total_addresses++;
-       xalloca(from_responses, char **, sizeof(char *) * total_addresses);
-       for (idp = msg->recipients; idp; idp = idp->next)
-           if (idp->val.status.mark == XMIT_ACCEPT)
-           {
-               if (strchr(idp->id, '@'))
-                   strcpy(addr, idp->id);
-               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
-               {
-                   char        errbuf[POPBUFSIZE];
-
-                   strcpy(errbuf, idp->id);
-                   strcat(errbuf, ": ");
-                   strcat(errbuf, smtp_response);
-
-                   xalloca(from_responses[*bad_addresses], 
-                           char *, 
-                           strlen(errbuf)+1);
-                   strcpy(from_responses[*bad_addresses], errbuf);
-
-                   (*bad_addresses)++;
-                   idp->val.status.mark = XMIT_RCPTBAD;
-                   if (outlevel >= O_VERBOSE)
-                       report(stderr, 
-                             _("%cMTP listener doesn't like recipient address `%s'\n"),
-                             ctl->listener, addr);
-               }
-           }
-       if (*bad_addresses)
-           send_bouncemail(ctl, msg, XMIT_RCPTBAD,
-                            "Some addresses were rejected by the MDA fetchmail forwards to.\r\n",
-                            *bad_addresses, from_responses);
-       /*
-        * It's tempting to do local notification only if bouncemail was
-        * insufficient -- that is, to add && total_addresses > *bad_addresses
-        * to the test here.  The problem with this theory is that it would
-        * make initial diagnosis of a broken multidrop configuration very
-        * hard -- most single-recipient messages would just invisibly bounce.
-        */
-       if (!(*good_addresses)) 
-       {
-           if (strchr(run.postmaster, '@'))
-               strcpy(addr, run.postmaster);
-           else
-           {
-#ifdef HAVE_SNPRINTF
-               snprintf(addr, sizeof(addr)-1, "%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)
-           {
-               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, _("no address matches; forwarding to %s.\n"), run.postmaster);
-       }
-
-       /* 
-        * Tell the listener we're ready to send data.
-        * Some listeners (like zmailer) may return antispam errors here.
-        */
-       if (SMTP_data(ctl->smtp_socket) != SM_OK)
-           return(handle_smtp_report(ctl, msg));
+       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 */
     }
 
     /*
@@ -862,7 +932,11 @@ void release_sink(struct query *ctl)
            pclose(sinkfp);
            sinkfp = (FILE *)NULL;
        }
+#ifndef HAVE_SIGACTION
        signal(SIGCHLD, sigchld);
+#else
+       sigaction (SIGCHLD, &sa_old, NULL);
+#endif /* HAVE_SIGACTION */
     }
 }
 
@@ -881,7 +955,11 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)
        }
        else
            rc = 0;
+#ifndef HAVE_SIGACTION
        signal(SIGCHLD, sigchld);
+#else
+       sigaction (SIGCHLD, &sa_old, NULL);
+#endif /* HAVE_SIGACTION */
        if (rc)
        {
            report(stderr, 
@@ -1080,7 +1158,7 @@ va_dcl
 void close_warning_by_mail(struct query *ctl, struct msgblk *msg)
 /* sign and send mailed warnings */
 {
-    stuff_warning(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, msg, TRUE);
 }