]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Ready for the UIDL patch.
[~andy/fetchmail] / driver.c
index 6ef3041961cd474fcd045ec666eec771846c1a98..73c439023c4c57be92d6ff777765f73fa1515795 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -26,6 +26,9 @@
 #if defined(HAVE_ALLOCA_H)
 #include <alloca.h>
 #endif
+#if defined(HAVE_SYS_ITIMER_H)
+#include <sys/itimer.h>
+#endif
 #include  <sys/time.h>
 #include  <signal.h>
 
@@ -54,7 +57,6 @@
 
 extern char *strstr(); /* needed on sysV68 R3V7.1. */
 
-int batchlimit;                /* how often to tear down the delivery connection */
 int fetchlimit;                /* how often to tear down the server connection */
 int batchcount;                /* count of messages sent in current batch */
 int peek_capable;      /* can we peek for better error recovery? */
@@ -147,7 +149,6 @@ static int is_host_alias(const char *name, struct query *ctl)
                "nameserver failure while looking for `%s' during poll of %s.",
                name, ctl->server.names->id);
            ctl->errcount++;
-           longjmp(restart, 2);        /* try again next poll cycle */
            break;
        }
 
@@ -173,7 +174,6 @@ static int is_host_alias(const char *name, struct query *ctl)
                "nameserver failure while looking for `%s' during poll of %s.",
                name, ctl->server.names->id);
            ctl->errcount++;
-           longjmp(restart, 2);        /* try again next poll cycle */
            break;
        }
     }
@@ -270,7 +270,7 @@ struct idlist **xmit_names; /* list of recipient names parsed out */
 }
 
 char *parse_received(struct query *ctl, char *bufp)
-/* try to extract */
+/* try to extract real addressee from the Received line */
 {
     char *ok;
     static char rbuf[HOSTLEN + USERNAMELEN + 4]; 
@@ -342,62 +342,75 @@ char *parse_received(struct query *ctl, char *bufp)
 static FILE *smtp_open(struct query *ctl)
 /* try to open a socket to the appropriate SMTP server for this query */ 
 {
-    struct query *lead;
-
-    lead = ctl->lead_smtp; /* go to the SMTP leader for this query */
+    struct idlist      *idp;
 
     /* maybe it's time to close the socket in order to force delivery */
-    if (batchlimit && lead->smtp_sockfp && batchcount++ == batchlimit)
+    if (ctl->batchlimit && ctl->smtp_sockfp && batchcount++ == ctl->batchlimit)
     {
-       fclose(lead->smtp_sockfp);
-       lead->smtp_sockfp = (FILE *)NULL;
+       fclose(ctl->smtp_sockfp);
+       ctl->smtp_sockfp = (FILE *)NULL;
        batchcount = 0;
     }
 
-    /* 
-     * RFC 1123 requires that the domain name in HELO address is a
-     * "valid principal domain name" for the client host.  We
-     * violate this with malice aforethought in order to make the
-     * Received headers and logging look right.
-     *
-     * In fact this code relies on the RFC1123 requirement that the
-     * SMTP listener must accept messages even if verification of the
-     * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
-     */
-
-    /* if no socket to this host is already set up, try to open ESMTP */
-    if (lead->smtp_sockfp == (FILE *)NULL)
+    /* run down the SMTP hunt list looking for a server that's up */
+    for (idp = ctl->smtphunt; idp; idp = idp->next)
     {
-       if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
-           return((FILE *)NULL);
-       else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
-                || SMTP_ehlo(lead->smtp_sockfp, 
-                             ctl->server.names->id,
-                             &lead->server.esmtp_options) != SM_OK)
+
+       /* 
+        * RFC 1123 requires that the domain name in HELO address is a
+        * "valid principal domain name" for the client host.  We
+        * violate this with malice aforethought in order to make the
+        * Received headers and logging look right.
+        *
+        * In fact this code relies on the RFC1123 requirement that the
+        * SMTP listener must accept messages even if verification of the
+        * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
+        */
+
+       /* if no socket to this host is already set up, try to open ESMTP */
+       if (ctl->smtp_sockfp == (FILE *)NULL)
        {
-           /*
-            * RFC 1869 warns that some listeners hang up on a failed EHLO,
-            * so it's safest not to assume the socket will still be good.
-            */
-           fclose(lead->smtp_sockfp);
-           lead->smtp_sockfp = (FILE *)NULL;
+           if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+               return((FILE *)NULL);
+           else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+                    || SMTP_ehlo(ctl->smtp_sockfp, 
+                                 ctl->server.names->id,
+                                 &ctl->server.esmtp_options) != SM_OK)
+           {
+               /*
+                * RFC 1869 warns that some listeners hang up on a failed EHLO,
+                * so it's safest not to assume the socket will still be good.
+                */
+               fclose(ctl->smtp_sockfp);
+               ctl->smtp_sockfp = (FILE *)NULL;
+           }
+           else
+           {
+               ctl->smtphost = idp->id;
+               break;
+           }
        }
-    }
 
-    /* if opening for ESMTP failed, try SMTP */
-    if (lead->smtp_sockfp == (FILE *)NULL)
-    {
-       if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
-           return((FILE *)NULL);
-       else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
-                || SMTP_helo(lead->smtp_sockfp, ctl->server.names->id) != SM_OK)
+       /* if opening for ESMTP failed, try SMTP */
+       if (ctl->smtp_sockfp == (FILE *)NULL)
        {
-           fclose(lead->smtp_sockfp);
-           lead->smtp_sockfp = (FILE *)NULL;
+           if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+               return((FILE *)NULL);
+           else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+                    || SMTP_helo(ctl->smtp_sockfp, ctl->server.names->id) != SM_OK)
+           {
+               fclose(ctl->smtp_sockfp);
+               ctl->smtp_sockfp = (FILE *)NULL;
+           }
+           else
+           {
+               ctl->smtphost = idp->id;
+               break;
+           }
        }
     }
 
-    return(lead->smtp_sockfp);
+    return(ctl->smtp_sockfp);
 }
 
 static int gen_readmsg(sockfp, len, delimited, ctl, realname)
@@ -410,8 +423,8 @@ char *realname;             /* real name of host */
 {
     char buf [MSGBUFSIZE+1]; 
     int        from_offs, to_offs, cc_offs, bcc_offs, ctt_offs, env_offs;
-    char *headers, *received_for;
-    int n, oldlen, ch, sizeticker, delete_ok;
+    char *headers, *received_for, *return_path;
+    int n, oldlen, ch, sizeticker, delete_ok, remaining;
     FILE *sinkfp;
     RETSIGTYPE (*sigchld)();
 #ifdef HAVE_GETHOSTBYNAME
@@ -423,12 +436,15 @@ char *realname;           /* real name of host */
 #ifdef HAVE_RES_SEARCH
     int                        no_local_matches = FALSE;
 #endif /* HAVE_RES_SEARCH */
+    int                        olderrs;
 
     sizeticker = 0;
     delete_ok = TRUE;
+    remaining = len;
+    olderrs = ctl->errcount;
 
     /* read message headers */
-    headers = received_for = NULL;
+    headers = received_for = return_path = NULL;
     from_offs = to_offs = cc_offs = bcc_offs = ctt_offs = env_offs = -1;
     oldlen = 0;
     for (;;)
@@ -451,7 +467,8 @@ char *realname;             /* real name of host */
            ((ch = SockPeek(sockfp)) == ' ' || ch == '\t');
 
        /* write the message size dots */
-       if ((outlevel>O_SILENT && outlevel<O_VERBOSE) && (n=strlen(line)) > 0)
+       n = strlen(line);
+       if ((outlevel > O_SILENT && outlevel < O_VERBOSE) && n > 0)
        {
            sizeticker += n;
            while (sizeticker >= SIZETICKER)
@@ -460,7 +477,7 @@ char *realname;             /* real name of host */
                sizeticker -= SIZETICKER;
            }
        }
-       len -= n;
+       remaining -= n;
 
        /* check for end of headers; don't save terminating line */
        if (line[0] == '\r' && line[1] == '\n')
@@ -469,6 +486,29 @@ char *realname;            /* real name of host */
            break;
        }
      
+
+       /*
+        * OK, this is messy.  If we're forwarding by SMTP, it's the
+        * SMTP-receiver's job (according to RFC821, page 22, section
+        * 4.1.1) to generate a Return-Path line on final delivery.
+        * The trouble is, we've already got one because the
+        * mailserver's SMTP thought *it* was responsible for final
+        * delivery.
+        *
+        * Stash away the contents of Return-Path for use in generating
+        * MAIL FROM later on, then prevent the header from being saved
+        * with the others.  In effect, we strip it off here.
+        *
+        * If the SMTP server conforms to the standards, and fetchmail gets the
+        * envelope sender from the Return-Path, the new Return-Path should be
+        * exactly the same as the original one.
+        */
+       if (!ctl->mda && !strncasecmp("Return-Path:", line, 12))
+       {
+           return_path = xstrdup(nxtaddr(line));
+           continue;
+       }
+
        if (ctl->rewrite)
            reply_hack(line, realname);
 
@@ -504,7 +544,8 @@ char *realname;             /* real name of host */
        else if (!strncasecmp("To:", line, 3))
            to_offs = (line - headers);
 
-       else if (env_offs == -1 && !strncasecmp(ctl->server.envelope,
+       else if (ctl->server.envelope != STRING_DISABLED && env_offs == -1
+                && !strncasecmp(ctl->server.envelope,
                                                line,
                                                strlen(ctl->server.envelope)))
            env_offs = (line - headers);
@@ -519,11 +560,28 @@ char *realname;           /* real name of host */
            ctt_offs = (line - headers);
 
 #ifdef HAVE_RES_SEARCH
-       else if (MULTIDROP(ctl) && !received_for && !strncasecmp("Received:", line, 9))
+       else if (ctl->server.envelope != STRING_DISABLED && MULTIDROP(ctl) && !received_for && !strncasecmp("Received:", line, 9))
            received_for = parse_received(ctl, line);
 #endif /* HAVE_RES_SEARCH */
     }
 
+    /*
+     * Hack time.  If the first line of the message was blank, with no headers
+     * (this happens occasionally due to bad gatewaying software) cons up
+     * a set of fake headers.  
+     *
+     * If you modify the fake header template below, be sure you don't
+     * make either From or To address @-less, otherwise the reply_hack
+     * logic will do bad things.
+     */
+    if (headers == (char *)NULL)
+    {
+       sprintf(buf, 
+       "From: <FETCHMAIL-DAEMON@%s>\r\nTo: %s@localhost\r\nSubject: Headerless mail from %s's mailbox on %s\r\n",
+               fetchmailhost, user, ctl->remotename, realname);
+       headers = xstrdup(buf);
+    }
+
     /*
      * We can now process message headers before reading the text.
      * In fact we have to, as this will tell us where to forward to.
@@ -572,8 +630,18 @@ char *realname;            /* real name of host */
 #endif /* HAVE_RES_SEARCH */
        save_str(&xmit_names, -1, ctl->localnames->id);
 
-    /* time to address the message */
-    if (ctl->mda)      /* we have a declared MDA */
+
+    /*
+     * Time to either address the message or decide we can't deliver it yet.
+     */
+    if (ctl->errcount > olderrs)       /* there were DNS errors above */
+    {
+       delete_ok = FALSE;
+       sinkfp = (FILE *)NULL;
+       if (outlevel == O_VERBOSE)
+           error(0,0, "forwarding and deletion suppressed due to DNS errors");
+    }
+    else if (ctl->mda)         /* we have a declared MDA */
     {
        int     length = 0;
        char    *names, *cmd;
@@ -630,7 +698,9 @@ char *realname;             /* real name of host */
        if (!ctl->mda && ((sinkfp = smtp_open(ctl)) == NULL))
        {
            free_str_list(&xmit_names);
-           error(0, 0, "SMTP connect failed");
+           error(0, 0, "SMTP connect to %s failed", ctl->smtphost);
+           if (return_path)
+               free(return_path);
            return(PS_SMTP);
        }
 
@@ -652,11 +722,16 @@ char *realname;           /* real name of host */
            sprintf(options + strlen(options), " SIZE=%d", len);
 
        /*
-        * Try to get the SMTP listener to take the header
-        * From address as MAIL FROM (this makes the logging
-        * nicer).  If it won't, fall back on the calling-user
-        * ID.  This won't affect replies, which use the header
-        * From address anyway.
+        * If there is a Return-Path address on the message, this was
+        * almost certainly the MAIL FROM address given the originating
+        * sendmail.  This is the best thing to use for logging the
+        * message origin (it sets up the right behavior for bounces and
+        * mailing lists).  Otherwise, take the From address.
+        *
+        * Try to get the SMTP listener to take the Return-Path or
+        * From 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.
         *
         * RFC 1123 requires that the domain name part of the
         * MAIL FROM address be "canonicalized", that is a
@@ -665,11 +740,14 @@ char *realname;           /* real name of host */
         * 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 lines, *and*
-        * the local SMTP listener insists on them.
+        * 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 (from_offs == -1 || !(ap = nxtaddr(headers + from_offs)))
+       ap = (char *)NULL;
+       if (return_path)
+           ap = return_path;
+       else if (from_offs == -1 || !(ap = nxtaddr(headers + from_offs)))
            ap = user;
        if (SMTP_from(sinkfp, ap, options) != SM_OK)
        {
@@ -679,7 +757,7 @@ char *realname;             /* real name of host */
                error(0, 0, "SMTP error: %s", smtp_response);
 
            /*
-            * There'a one problem with this flow of control;
+            * There's one problem with this flow of control;
             * there's no way to avoid reading the whole message
             * off the server, even if the MAIL FROM response 
             * tells us that it's just to be discarded.  We could
@@ -727,6 +805,8 @@ char *realname;             /* real name of host */
                if (SMTP_from(sinkfp, user, options) != SM_OK)
                {
                    error(0,0,"SMTP error: %s", smtp_response);
+                   if (return_path)
+                       free(return_path);
                    return(PS_SMTP);    /* should never happen */
                }
            }
@@ -756,6 +836,8 @@ char *realname;             /* real name of host */
        {
            error(0, 0, 
                  "can't even send to calling user!");
+           if (return_path)
+               free(return_path);
            return(PS_SMTP);
        }
 
@@ -763,6 +845,8 @@ char *realname;             /* real name of host */
        SMTP_data(sinkfp);
 
     skiptext:;
+       if (return_path)
+           free(return_path);
     }
 
     /* we may need to strip carriage returns */
@@ -882,7 +966,7 @@ char *realname;             /* real name of host */
      */
 
     /* pass through the text lines */
-    while (delimited || len > 0)
+    while (delimited || remaining > 0)
     {
        if (!SockGets(buf, sizeof(buf)-1, sockfp))
            return(PS_SOCKET);
@@ -899,7 +983,7 @@ char *realname;             /* real name of host */
                sizeticker -= SIZETICKER;
            }
        }
-       len -= n;
+       remaining -= n;
 
        /* check for end of message */
        if (delimited && *buf == '.')
@@ -974,6 +1058,7 @@ char *realname;            /* real name of host */
        if (SMTP_eom(sinkfp) != SM_OK)
        {
            error(0, 0, "SMTP listener refused delivery");
+           ctl->errcount++;
            return(PS_TRANSIENT);
        }
     }
@@ -1075,11 +1160,6 @@ const struct method *proto;      /* protocol method table */
                ctl->server.timeout, ctl->server.names->id);
        ok = PS_ERROR;
     }
-    else if (js == 2)
-    {
-       /* error message printed at point of longjmp */
-       ok = PS_ERROR;
-    }
     else
     {
        char buf [POPBUFSIZE+1];
@@ -1101,14 +1181,14 @@ const struct method *proto;     /* protocol method table */
 #ifndef EHOSTUNREACH
 #define EHOSTUNREACH (-1)
 #endif
-           if (errno != EHOSTUNREACH)
+           if (outlevel == O_VERBOSE || errno != EHOSTUNREACH)
                error(0, errno, "connecting to host");
            ok = PS_SOCKET;
            goto closeUp;
        }
 
 #ifdef KERBEROS_V4
-       if (ctl->authenticate == A_KERBEROS)
+       if (ctl->server.authenticate == A_KERBEROS)
        {
            ok = kerberos_auth(fileno(sockfp), ctl->server.canonical_name);
            if (ok != 0)
@@ -1267,6 +1347,8 @@ const struct method *proto;       /* protocol method table */
        }
        else if (count > 0)
        {    
+           int force_retrieval, fetches;
+
            /*
             * What forces this code is that in POP3 and IMAP2BIS you can't
             * fetch a message without having it marked `seen'.  In IMAP4,
@@ -1289,9 +1371,9 @@ const struct method *proto;       /* protocol method table */
             * previous pass and forcing all messages to be considered new
             * if it's nonzero.
             */
-           int force_retrieval = !peek_capable && (ctl->errcount > 0);
+           force_retrieval = !peek_capable && (ctl->errcount > 0);
 
-           ctl->errcount = 0;
+           ctl->errcount = fetches = 0;
 
            /* read, forward, and delete messages */
            for (num = 1; num <= count; num++)
@@ -1350,6 +1432,8 @@ const struct method *proto;       /* protocol method table */
                            goto cleanUp;
                        vtalarm(ctl->server.timeout);
                    }
+
+                   fetches++;
                }
 
                /*
@@ -1379,7 +1463,7 @@ const struct method *proto;       /* protocol method table */
                    error_complete(0, 0, " not flushed");
 
                /* perhaps this as many as we're ready to handle */
-               if (ctl->fetchlimit && ctl->fetchlimit <= num)
+               if (ctl->fetchlimit && ctl->fetchlimit <= fetches)
                    break;
            }
 
@@ -1477,7 +1561,7 @@ va_dcl
     {
        char *cp;
 
-       if (shroud && (cp = strstr(buf, shroud)))
+       if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
        {
            char        *sp;
 
@@ -1547,7 +1631,7 @@ va_dcl
     {
        char *cp;
 
-       if (shroud && (cp = strstr(buf, shroud)))
+       if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
        {
            char        *sp;
 
@@ -1555,7 +1639,7 @@ va_dcl
            *cp++ = '*';
            while (*sp)
                *cp++ = *sp++;
-           *sp = '\0';
+           *cp = '\0';
        }
        buf[strlen(buf)-1] = '\0';
        error(0, 0, "%s> %s", protocol->name, buf);