]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Ready for the UIDL patch.
[~andy/fetchmail] / driver.c
index 35bb3e8c19c281bc4816fbeeb1800b4ec6ad6495..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? */
@@ -115,7 +117,7 @@ static int is_host_alias(const char *name, struct query *ctl)
        return(TRUE);
     else if (strcmp(name, ctl->server.canonical_name) == 0)
        return(TRUE);
-    else if (ctl->server.no_dns)
+    else if (!ctl->server.dns)
        return(FALSE);
 
     /*
@@ -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]; 
@@ -290,8 +290,10 @@ char *parse_received(struct query *ctl, char *bufp)
        char    *sp, *tp;
 
        /* extract space-delimited token after "by " */
+       for (sp = ok + 3; isspace(*sp); sp++)
+           continue;
        tp = rbuf;
-       for (sp = ok + 3; !isspace(*sp); sp++)
+       for (; !isspace(*sp); sp++)
            *tp++ = *sp;
        *tp = '\0';
 
@@ -340,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)
@@ -408,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
@@ -421,17 +436,20 @@ 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 (;;)
     {
-       char *bufp, *line;
+       char *line;
 
        line = xmalloc(sizeof(buf));
        line[0] = '\0';
@@ -442,22 +460,24 @@ char *realname;           /* real name of host */
            /* leave extra room for reply_hack to play with */
            line = realloc(line, strlen(line) + strlen(buf) + HOSTLEN + 1);
            strcat(line, buf);
+           if (line[0] == '\r' && line[1] == '\n')
+               break;
        } while
            /* we may need to grab RFC822 continuations */
            ((ch = SockPeek(sockfp)) == ' ' || ch == '\t');
 
        /* write the message size dots */
-       if ((n = strlen(line)) > 0)
+       n = strlen(line);
+       if ((outlevel > O_SILENT && outlevel < O_VERBOSE) && n > 0)
        {
            sizeticker += n;
            while (sizeticker >= SIZETICKER)
            {
-               if (outlevel > O_SILENT)
-                   error_build(".");
+               error_build(".");
                sizeticker -= SIZETICKER;
            }
        }
-       len -= n;
+       remaining -= n;
 
        /* check for end of headers; don't save terminating line */
        if (line[0] == '\r' && line[1] == '\n')
@@ -466,61 +486,102 @@ char *realname;          /* real name of host */
            break;
        }
      
-       if (!ctl->no_rewrite)
+
+       /*
+        * 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);
 
-       bufp = line;
        if (!headers)
        {
-           oldlen = strlen(bufp);
+           oldlen = strlen(line);
            headers = xmalloc(oldlen + 1);
-           (void) strcpy(headers, bufp);
-           bufp = headers;
+           (void) strcpy(headers, line);
+           free(line);
+           line = headers;
        }
        else
        {
            int newlen;
 
-           newlen = oldlen + strlen(bufp);
+           newlen = oldlen + strlen(line);
            headers = realloc(headers, newlen + 1);
            if (headers == NULL)
                return(PS_IOERR);
-           strcpy(headers + oldlen, bufp);
-           bufp = headers + oldlen;
+           strcpy(headers + oldlen, line);
+           free(line);
+           line = headers + oldlen;
            oldlen = newlen;
        }
-       free(line);
 
-       if (from_offs == -1 && !strncasecmp("From:", bufp, 5))
-           from_offs = (bufp - headers);
-       else if (from_offs == -1 && !strncasecmp("Resent-From:", bufp, 12))
-           from_offs = (bufp - headers);
-       else if (from_offs == -1 && !strncasecmp("Apparently-From:", bufp, 16))
-           from_offs = (bufp - headers);
+       if (from_offs == -1 && !strncasecmp("From:", line, 5))
+           from_offs = (line - headers);
+       else if (from_offs == -1 && !strncasecmp("Resent-From:", line, 12))
+           from_offs = (line - headers);
+       else if (from_offs == -1 && !strncasecmp("Apparently-From:", line, 16))
+           from_offs = (line - headers);
 
-       else if (!strncasecmp("To:", bufp, 3))
-           to_offs = (bufp - headers);
+       else if (!strncasecmp("To:", line, 3))
+           to_offs = (line - headers);
 
-       else if (env_offs == -1 && !strncasecmp(ctl->server.envelope,
-                                               bufp,
+       else if (ctl->server.envelope != STRING_DISABLED && env_offs == -1
+                && !strncasecmp(ctl->server.envelope,
+                                               line,
                                                strlen(ctl->server.envelope)))
-           env_offs = (bufp - headers);
+           env_offs = (line - headers);
 
-       else if (!strncasecmp("Cc:", bufp, 3))
-           cc_offs = (bufp - headers);
+       else if (!strncasecmp("Cc:", line, 3))
+           cc_offs = (line - headers);
 
-       else if (!strncasecmp("Bcc:", bufp, 4))
-           bcc_offs = (bufp - headers);
+       else if (!strncasecmp("Bcc:", line, 4))
+           bcc_offs = (line - headers);
 
-       else if (!strncasecmp("Content-Transfer-Encoding:", bufp, 26))
-           ctt_offs = (bufp - headers);
+       else if (!strncasecmp("Content-Transfer-Encoding:", line, 26))
+           ctt_offs = (line - headers);
 
 #ifdef HAVE_RES_SEARCH
-       else if (MULTIDROP(ctl) && !strncasecmp("Received:", bufp, 9))
-           received_for = parse_received(ctl, bufp);
+       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.
@@ -569,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;
@@ -627,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);
        }
 
@@ -649,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
@@ -662,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)
        {
@@ -676,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
@@ -724,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 */
                }
            }
@@ -753,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);
        }
 
@@ -760,28 +845,45 @@ char *realname;           /* real name of host */
        SMTP_data(sinkfp);
 
     skiptext:;
+       if (return_path)
+           free(return_path);
     }
 
-    /* write all the headers */
-    if (ctl->mda)
-       n = fwrite(headers, 1, oldlen, sinkfp);
-    else if (sinkfp)
-       n = SockWrite(headers, 1, oldlen, sinkfp);
+    /* we may need to strip carriage returns */
+    if (ctl->stripcr)
+    {
+       char    *sp, *tp;
+
+       for (sp = tp = headers; *sp; sp++)
+           if (*sp != '\r')
+               *tp++ =  *sp;
+       *tp = '\0';
+
+    }
 
-    if (n < 0)
+    /* write all the headers */
+    if (sinkfp)
     {
-       free(headers);
-       headers = NULL;
-       error(0, errno, "writing RFC822 headers");
        if (ctl->mda)
+           n = fwrite(headers, 1, strlen(headers), sinkfp);
+       else
+           n = SockWrite(headers, 1, strlen(headers), sinkfp);
+
+       if (n < 0)
        {
-           pclose(sinkfp);
-           signal(SIGCHLD, sigchld);
+           free(headers);
+           headers = NULL;
+           error(0, errno, "writing RFC822 headers");
+           if (ctl->mda)
+           {
+               pclose(sinkfp);
+               signal(SIGCHLD, sigchld);
+           }
+           return(PS_IOERR);
        }
-       return(PS_IOERR);
+       else if (outlevel == O_VERBOSE)
+           fputs("#", stderr);
     }
-    else if (outlevel == O_VERBOSE)
-       fputs("#", stderr);
     free(headers);
     headers = NULL;
 
@@ -827,27 +929,44 @@ char *realname;           /* real name of host */
 
        strcat(errmsg, "\n");
 
+       /* we may need to strip carriage returns */
+       if (ctl->stripcr)
+       {
+           char        *sp, *tp;
+
+           for (sp = tp = errmsg; *sp; sp++)
+               if (*sp != '\r')
+                   *tp++ =  *sp;
+           *tp = '\0';
+       }
+
        /* ship out the error line */
-       if (ctl->mda)
-           fwrite(errmsg, 1, strlen(errmsg), sinkfp);
-       else if (sinkfp)
-           SockWrite(errmsg, 1, strlen(errmsg), sinkfp);
+       if (sinkfp)
+       {
+           if (ctl->mda)
+               fwrite(errmsg, 1, strlen(errmsg), sinkfp);
+           else
+               SockWrite(errmsg, 1, strlen(errmsg), sinkfp);
+       }
     }
 
     free_str_list(&xmit_names);
 
     /* issue the delimiter line */
-    if (ctl->mda)
-       fputs("\r\n", sinkfp);
-    else if (sinkfp)
-       SockWrite("\r\n", 1, 2, sinkfp);
+    if (sinkfp)
+    {
+       if (ctl->mda)
+           fputc('\n', sinkfp);
+       else
+           SockWrite("\r\n", 1, 2, sinkfp);
+    }
 
     /*
      *  Body processing starts here
      */
 
     /* pass through the text lines */
-    while (delimited || len > 0)
+    while (delimited || remaining > 0)
     {
        if (!SockGets(buf, sizeof(buf)-1, sockfp))
            return(PS_SOCKET);
@@ -864,38 +983,53 @@ char *realname;           /* real name of host */
                sizeticker -= SIZETICKER;
            }
        }
-       len -= n;
+       remaining -= n;
 
        /* check for end of message */
        if (delimited && *buf == '.')
            if (buf[1] == '\r' && buf[2] == '\n')
                break;
 
-       /* SMTP byte-stuffing */
-       if (*buf == '.')
-           if (ctl->mda)
-               fputs(".", sinkfp);
-           else if (sinkfp)
-               SockWrite(buf, 1, 1, sinkfp);
-
        /* ship out the text line */
-       if (ctl->mda)
-           n = fwrite(buf, 1, strlen(buf), sinkfp);
-       else if (sinkfp)
-           n = SockWrite(buf, 1, strlen(buf), sinkfp);
-
-       if (n < 0)
+       if (sinkfp)
        {
-           error(0, errno, "writing message text");
+           /* SMTP byte-stuffing */
+           if (*buf == '.')
+               if (ctl->mda)
+                   fputs(".", sinkfp);
+               else
+                   SockWrite(buf, 1, 1, sinkfp);
+
+           /* we may need to strip carriage returns */
+           if (ctl->stripcr)
+           {
+               char    *sp, *tp;
+
+               for (sp = tp = buf; *sp; sp++)
+                   if (*sp != '\r')
+                       *tp++ =  *sp;
+               *tp = '\0';
+           }
+
+           /* ship the text line */
            if (ctl->mda)
+               n = fwrite(buf, 1, strlen(buf), sinkfp);
+           else if (sinkfp)
+               n = SockWrite(buf, 1, strlen(buf), sinkfp);
+
+           if (n < 0)
            {
-               pclose(sinkfp);
-               signal(SIGCHLD, sigchld);
+               error(0, errno, "writing message text");
+               if (ctl->mda)
+               {
+                   pclose(sinkfp);
+                   signal(SIGCHLD, sigchld);
+               }
+               return(PS_IOERR);
            }
-           return(PS_IOERR);
+           else if (outlevel == O_VERBOSE)
+               fputc('*', stderr);
        }
-       else if (outlevel == O_VERBOSE)
-           fputc('*', stderr);
     }
 
     /*
@@ -924,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);
        }
     }
@@ -1025,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];
@@ -1051,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)
@@ -1149,19 +1279,22 @@ const struct method *proto;     /* protocol method table */
            strcpy(realname, ctl->server.names->id);
 
        /* try to get authorized to fetch mail */
-       shroud = ctl->password;
-       ok = (protocol->getauth)(sockfp, ctl, buf);
-       shroud = (char *)NULL;
-       if (ok == PS_ERROR)
-           ok = PS_AUTHFAIL;
-       if (ok != 0)
+       if (protocol->getauth)
        {
-           error(0, 0, "Authorization failure on %s@%s", 
-                 ctl->remotename,
-                 realname);
-           goto cleanUp;
+           shroud = ctl->password;
+           ok = (protocol->getauth)(sockfp, ctl, buf);
+           shroud = (char *)NULL;
+           if (ok == PS_ERROR)
+               ok = PS_AUTHFAIL;
+           if (ok != 0)
+           {
+               error(0, 0, "Authorization failure on %s@%s", 
+                     ctl->remotename,
+                     realname);
+               goto cleanUp;
+           }
+           vtalarm(ctl->server.timeout);
        }
-       vtalarm(ctl->server.timeout);
 
        /* compute number of messages and number of new messages waiting */
        ok = (protocol->getrange)(sockfp, ctl, &count, &new);
@@ -1171,19 +1304,24 @@ const struct method *proto;     /* protocol method table */
 
        /* show user how many messages we downloaded */
        if (outlevel > O_SILENT)
-           if (count == 0)
-               error(0, 0, "No mail from %s@%s", 
+           if (count == -1)                    /* only used for ETRN */
+               error(0, 0, "Polling %s@%s", 
+                       ctl->remotename,
+                       realname);
+           else if (count == 0)
+               error(0, 0, "No mail at %s@%s", 
                        ctl->remotename,
                        realname);
            else
            {
                if (new != -1 && (count - new) > 0)
-                   error(0, 0, "%d message%s (%d seen) from %s@%s.",
+                   error(0, 0, "%d message%s (%d seen) at %s@%s.",
                                count, count > 1 ? "s" : "", count-new,
                                ctl->remotename,
                                realname);
                else
-                   error(0, 0, "%d message%s from %s@%s.", count, count > 1 ? "s" : "",
+                   error(0, 0, "%d message%s at %s@%s.", 
+                               count, count > 1 ? "s" : "",
                                ctl->remotename,
                                realname);
            }
@@ -1209,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,
@@ -1231,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++)
@@ -1292,6 +1432,8 @@ const struct method *proto;       /* protocol method table */
                            goto cleanUp;
                        vtalarm(ctl->server.timeout);
                    }
+
+                   fetches++;
                }
 
                /*
@@ -1321,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;
            }
 
@@ -1419,9 +1561,17 @@ va_dcl
     {
        char *cp;
 
-       if (shroud && (cp = strstr(buf, shroud)))
-           memset(cp, '*', strlen(shroud));
-       buf[strlen(buf)-1] = '\0';
+       if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
+       {
+           char        *sp;
+
+           sp = cp + strlen(shroud);
+           *cp++ = '*';
+           while (*sp)
+               *cp++ = *sp++;
+           *cp = '\0';
+       }
+       buf[strlen(buf)-2] = '\0';
        error(0, 0, "%s> %s", protocol->name, buf);
     }
 }
@@ -1481,8 +1631,16 @@ va_dcl
     {
        char *cp;
 
-       if (shroud && (cp = strstr(buf, shroud)))
-           memset(cp, '*', strlen(shroud));
+       if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
+       {
+           char        *sp;
+
+           sp = cp + strlen(shroud);
+           *cp++ = '*';
+           while (*sp)
+               *cp++ = *sp++;
+           *cp = '\0';
+       }
        buf[strlen(buf)-1] = '\0';
        error(0, 0, "%s> %s", protocol->name, buf);
     }
@@ -1495,3 +1653,4 @@ va_dcl
 }
 
 /* driver.c ends here */
+