]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Before showdots,
[~andy/fetchmail] / driver.c
index 38da2d87afc5e38d176302229ae9d878ea1f53ee..e0b86d6cd056d1c106611599f5258db85782e2b5 100644 (file)
--- a/driver.c
+++ b/driver.c
 #include  <sys/time.h>
 #include  <signal.h>
 
+#ifdef HAVE_NET_SOCKET_H
+#include <net/socket.h>
+#endif
+
 #ifdef HAVE_RES_SEARCH
 #include <netdb.h>
 #include "mx.h"
@@ -80,7 +84,9 @@ extern char *strstr();        /* needed on sysV68 R3V7.1. */
 int batchcount;                /* count of messages sent in current batch */
 flag peek_capable;     /* can we peek for better error recovery? */
 int pass;              /* how many times have we re-polled? */
+int stage;             /* where are we? */
 int phase;             /* where are we, for error-logging purposes? */
+int mytimeout;         /* value of nonreponse timeout */
 
 static const struct method *protocol;
 static jmp_buf restart;
@@ -90,14 +96,13 @@ static int tagnum;
 #define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag)
 
 static char shroud[PASSWORDLEN];       /* string to shroud in debug output */
-static int mytimeout;                  /* value of nonreponse timeout */
 static int timeoutcount;               /* count consecutive timeouts */
 static int msglen;                     /* actual message length */
 
 void set_timeout(int timeleft)
 /* reset the nonresponse-timeout */
 {
-#ifndef __EMX__
+#if !defined(__EMX__) && !defined(__BEOS__) 
     struct itimerval ntimeout;
 
     if (timeleft == 0)
@@ -230,6 +235,19 @@ static void find_server_names(const char *hdr,
     }
 }
 
+/*
+ * Return zero on a syntactically invalid address, nz on a valid one.
+ *
+ * This used to be strchr(a, '.'), but it turns out that lines like this
+ *
+ * Received: from punt-1.mail.demon.net by mailstore for markb@ordern.com
+ *          id 938765929:10:27223:2; Fri, 01 Oct 99 08:18:49 GMT
+ *
+ * are not uncommon.  So now we just check that the following token is
+ * not itself an email address.
+ */
+#define VALID_ADDRESS(a)       !strchr(a, '@')
+
 static char *parse_received(struct query *ctl, char *bufp)
 /* try to extract real address from the Received line */
 /* If a valid Received: line is found, we return the full address in
@@ -254,7 +272,7 @@ static char *parse_received(struct query *ctl, char *bufp)
     if (outlevel >= O_DEBUG)
        report(stdout, _("analyzing Received line:\n%s"), bufp);
 
-    /* search for whitepace-surrounded "by" followed by xxxx.yyyy */
+    /* search for whitepace-surrounded "by" followed by valid address */
     for (base = bufp;  ; base = ok + 2)
     {
        if (!(ok = strstr(base, "by")))
@@ -273,8 +291,8 @@ static char *parse_received(struct query *ctl, char *bufp)
                *tp++ = *sp;
            *tp = '\0';
 
-           /* look for embedded periods */
-           if (strchr(rbuf, '.'))
+           /* look for valid address */
+           if (VALID_ADDRESS(rbuf))
                break;
            else
                ok = sp - 1;    /* arrange to skip this token */
@@ -416,7 +434,7 @@ static int readheaders(int sock,
     int                        from_offs, reply_to_offs, resent_from_offs;
     int                        app_from_offs, sender_offs, resent_sender_offs;
     int                        env_offs;
-    char               *received_for, *rcv, *cp;
+    char               *received_for, *rcv, *cp, *delivered_to;
     int                n, linelen, oldlen, ch, remaining, skipcount;
     struct idlist      *idp;
     flag               no_local_matches = FALSE;
@@ -430,7 +448,17 @@ static int readheaders(int sock,
 
     /* read message headers */
     msgblk.reallen = reallen;
-    msgblk.headers = received_for = NULL;
+
+    /*
+     * We used to free the header block unconditionally at the end of 
+     * readheaders, but it turns out that if close_sink() hits an error
+     * condition the code for sending bouncemail will actually look
+     * at the freed storage and coredump...
+     */
+    if (msgblk.headers)
+       free(msgblk.headers);
+
+    msgblk.headers = received_for = delivered_to = NULL;
     from_offs = reply_to_offs = resent_from_offs = app_from_offs = 
        sender_offs = resent_sender_offs = env_offs = -1;
     oldlen = 0;
@@ -515,7 +543,7 @@ static int readheaders(int sock,
            sizeticker += linelen;
            while (sizeticker >= SIZETICKER)
            {
-               if (!run.use_syslog)
+               if (!run.use_syslog && !isafile(1))
                {
                    fputc('.', stdout);
                    fflush(stdout);
@@ -529,19 +557,35 @@ static int readheaders(int sock,
 
        /*
         * When mail delivered to a multidrop mailbox on the server is
-        * addressed to multiple people, there will be one copy left
-        * in the box for each recipient.  Thus, if the mail is addressed
-        * to N people, each recipient would get N copies.
+        * addressed to multiple people on the client machine, there
+        * will be one copy left in the box for each recipient.  Thus,
+        * if the mail is addressed to N people, each recipient will
+        * get N copies.  This is bad when N > 1.
         *
         * Foil this by suppressing all but one copy of a message with
-        * a given Message-ID.  Note: This implementation only catches
-        * runs of successive identical messages, but that should be
-        * good enough.
+        * a given Message-ID.  The accept_count test ensures that
+        * multiple pieces of email with the same Message-ID, each
+        * with a *single* addressee (the N == 1 case), won't be 
+        * suppressed.
+        *
+        * Note: This implementation only catches runs of successive
+        * messages with the same ID, but that should be good
+        * enough. A more general implementation would have to store
+        * ever-growing lists of seen message-IDs; in a long-running
+        * daemon this would turn into a memory leak even if the 
+        * implementation were perfect.
+        * 
+        * Don't mess with this code casually.  It would be way too easy
+        * to break it in a way that blackholed mail.  Better to pass
+        * the occasional duplicate than to do that...
         */
        if (MULTIDROP(ctl) && !strncasecmp(line, "Message-ID:", 11))
        {
            if (ctl->lastid && !strcasecmp(ctl->lastid, line))
-               return(PS_REFUSED);
+           {
+               if (accept_count > 1)
+                   return(PS_REFUSED);
+           }
            else
            {
                if (ctl->lastid)
@@ -564,7 +608,11 @@ static int readheaders(int sock,
         * forward it to the user so he or she will have some clue
         * that things have gone awry.
         */
+#if INET6_ENABLE
+       if (strncmp(protocol->service, "pop2", 4))
+#else /* INET6_ENABLE */
        if (protocol->port != 109)
+#endif /* INET6_ENABLE */
 #endif /* POP2_ENABLE */
            if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
                free(line);
@@ -599,6 +647,20 @@ static int readheaders(int sock,
            continue;
        }
 
+       /*
+        * We remove all Delivered-To: headers.
+        * 
+        * This is to avoid false mail loops messages when delivering
+        * local messages to and from a Postfix/qmail mailserver. 
+        * 
+        * Should be controlled by an option
+        */
+       if (ctl->dropdelivered && !strncasecmp(line, "Delivered-To:", 13)) {
+      if (delivered_to) free(line);
+      else delivered_to = line;
+         continue;
+       }
+
        /*
         * If we see a Status line, it may have been inserted by an MUA
         * on the mail host, or it may have been inserted by the server
@@ -703,14 +765,15 @@ static int readheaders(int sock,
        else if (!strncasecmp("Resent-Sender:", line, 14))
            resent_sender_offs = (line - msgblk.headers);
 
-       else if (!strncasecmp("Message-Id:", buf, 11))
+#ifdef __UNUSED__
+       else if (!strncasecmp("Message-Id:", line, 11))
        {
            if (ctl->server.uidl)
            {
                char id[IDLEN+1];
 
-               buf[IDLEN+12] = 0;              /* prevent stack overflow */
-               sscanf(buf+12, "%s", id);
+               line[IDLEN+12] = 0;             /* prevent stack overflow */
+               sscanf(line+12, "%s", id);
                if (!str_find( &ctl->newsaved, num))
                {
                    struct idlist *new = save_str(&ctl->newsaved,id,UID_SEEN);
@@ -718,6 +781,7 @@ static int readheaders(int sock,
                }
            }
        }
+#endif /* __UNUSED__ */
 
        else if (!MULTIDROP(ctl))
            continue;
@@ -861,6 +925,12 @@ static int readheaders(int sock,
 #endif /* SDPS_ENABLE */ 
        if (env_offs > -1)          /* We have the actual envelope addressee */
            find_server_names(msgblk.headers + env_offs, ctl, &msgblk.recipients);
+       else if (delivered_to && ctl->server.envelope != STRING_DISABLED &&
+      ctl->server.envelope && !strcasecmp(ctl->server.envelope, "Delivered-To"))
+   {
+           find_server_names(delivered_to, ctl, &msgblk.recipients);
+       free(delivered_to);
+   }
        else if (received_for)
            /*
             * We have the Received for addressee.  
@@ -876,7 +946,7 @@ static int readheaders(int sock,
             * We haven't extracted the envelope address.
             * So check all the "Resent-To" header addresses if 
             * they exist.  If and only if they don't, consider
-            * the "To" adresses.
+            * the "To" addresses.
             */
            register struct addrblk *nextptr;
            if (resent_to_addrchain) {
@@ -959,7 +1029,16 @@ static int readheaders(int sock,
     if (!run.invisible && n != -1)
     {
        /* utter any per-message Received information we need here */
-       sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+        if (ctl->server.trueaddr) {
+           sprintf(buf, "Received: from %s [%u.%u.%u.%u]\r\n", 
+                   ctl->server.truename,
+                   (unsigned char)ctl->server.trueaddr[0],
+                   (unsigned char)ctl->server.trueaddr[1],
+                   (unsigned char)ctl->server.trueaddr[2],
+                   (unsigned char)ctl->server.trueaddr[3]);
+       } else {
+         sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+       }
        n = stuffline(ctl, buf);
        if (n != -1)
        {
@@ -1023,7 +1102,7 @@ static int readheaders(int sock,
        free_str_list(&msgblk.recipients);
        return(PS_IOERR);
     }
-    else if (!run.use_syslog && outlevel >= O_VERBOSE)
+    else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !isafile(2))
        fputs("#", stderr);
 
     /* write error notifications */
@@ -1089,7 +1168,7 @@ static int readheaders(int sock,
     *cp++ = '\0';
     stuffline(ctl, buf);
 
-    free(msgblk.headers);
+/*    free(msgblk.headers); */
     free_str_list(&msgblk.recipients);
     return(headers_ok ? PS_SUCCESS : PS_TRUNCATED);
 }
@@ -1133,7 +1212,7 @@ static int readbody(int sock, struct query *ctl, flag forward, int len)
            sizeticker += linelen;
            while (sizeticker >= SIZETICKER)
            {
-               if (!run.use_syslog && outlevel > O_SILENT)
+               if ((run.poll_interval == 0 || nodetach) && outlevel > O_SILENT && !isafile(1))
                {
                    fputc('.', stdout);
                    fflush(stdout);
@@ -1145,12 +1224,14 @@ static int readbody(int sock, struct query *ctl, flag forward, int len)
 
        /* check for end of message */
        if (protocol->delimited && *inbufp == '.')
+       {
            if (inbufp[1] == '\r' && inbufp[2] == '\n' && inbufp[3] == '\0')
                break;
            else if (inbufp[1] == '\n' && inbufp[2] == '\0')
                break;
            else
                msglen--;       /* subtract the size of the dot escape */
+       }
 
        msglen += linelen;
 
@@ -1189,8 +1270,11 @@ static int readbody(int sock, struct query *ctl, flag forward, int len)
                release_sink(ctl);
                return(PS_IOERR);
            }
-           else if (outlevel >= O_VERBOSE)
-               fputc('*', stderr);
+           else if (outlevel >= O_VERBOSE && !isafile(1))
+           {
+               fputc('*', stdout);
+               fflush(stdout);
+           }
        }
     }
 
@@ -1353,7 +1437,6 @@ static void send_size_warnings(struct query *ctl)
     int msg_to_send = FALSE;
     struct idlist *head=NULL, *current=NULL;
     int max_warning_poll_count;
-#define OVERHD "Subject: Fetchmail oversized-messages warning.\r\n\r\nThe following oversized messages remain on the mail server %s:"
 
     head = ctl->skipped;
     if (!head)
@@ -1373,7 +1456,11 @@ static void send_size_warnings(struct query *ctl)
      */
     if (open_warning_by_mail(ctl, (struct msgblk *)NULL))
        return;
-    stuff_warning(ctl, OVERHD, ctl->server.pollname);
+    stuff_warning(ctl,
+          _("Subject: Fetchmail oversized-messages warning.\r\n"
+            "\r\n"
+            "The following oversized messages remain on the mail server %s:"),
+                 ctl->server.pollname);
  
     if (run.poll_interval == 0)
        max_warning_poll_count = 0;
@@ -1388,7 +1475,7 @@ static void send_size_warnings(struct query *ctl)
            nbr = current->val.status.mark;
            size = atoi(current->id);
            stuff_warning(ctl, 
-                   _("\t%d msg %d octets long skipped by fetchmail.\n"),
+                   _("\t%d msg %d octets long skipped by fetchmail.\r\n"),
                    nbr, size);
        }
        current->val.status.num++;
@@ -1399,7 +1486,6 @@ static void send_size_warnings(struct query *ctl)
     }
 
     close_warning_by_mail(ctl, (struct msgblk *)NULL);
-#undef OVERHD
 }
 
 static int do_session(ctl, proto, maxfetch)
@@ -1408,11 +1494,11 @@ struct query *ctl;              /* parsed options with merged-in defaults */
 const struct method *proto;    /* protocol method table */
 const int maxfetch;            /* maximum number of messages to fetch */
 {
-    int ok, js;
+    int js;
 #ifdef HAVE_VOLATILE
-    volatile int mailserver_socket = -1;       /* pacifies -Wall */
+    volatile int ok, mailserver_socket = -1;   /* pacifies -Wall */
 #else
-    int mailserver_socket = -1;
+    int ok, mailserver_socket = -1;
 #endif /* HAVE_VOLATILE */
     const char *msg;
     void (*pipesave)(int);
@@ -1454,9 +1540,11 @@ const int maxfetch;              /* maximum number of messages to fetch */
 
        if (js == THROW_SIGPIPE)
        {
+           signal(SIGPIPE, SIG_IGN);
            report(stdout,
-                  _("SIGPIPE thrown from an MDA or a stream socket error"));
+                  _("SIGPIPE thrown from an MDA or a stream socket error\n"));
            ok = PS_SOCKET;
+           goto cleanUp;
        }
        else if (js == THROW_TIMEOUT)
        {
@@ -1475,7 +1563,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                       ctl->mda ? "MDA" : "SMTP");
            else if (phase == LISTENER_WAIT)
                report(stdout,
-                      _("timeout after %d seconds waiting for listener to respond.\n"));
+                      _("timeout after %d seconds waiting for listener to respond.\n"), ctl->server.timeout);
            else
                report(stdout, 
                       _("timeout after %d seconds.\n"), ctl->server.timeout);
@@ -1483,6 +1571,8 @@ const int maxfetch;               /* maximum number of messages to fetch */
            /*
             * If we've exceeded our threshold for consecutive timeouts, 
             * try to notify the user, then mark the connection wedged.
+            * Don't do this if the connection can idle, though; idle
+            * timeouts just mean the frequency of mail is low.
             */
            if (timeoutcount > MAX_TIMEOUTS 
                && !open_warning_by_mail(ctl, (struct msgblk *)NULL))
@@ -1490,18 +1580,16 @@ const int maxfetch;             /* maximum number of messages to fetch */
                stuff_warning(ctl,
                              _("Subject: fetchmail sees repeated timeouts\r\n"));
                stuff_warning(ctl,
-                             _("Fetchmail saw more than %d timouts while attempting to get mail from %s@%s.\n"), 
+                             _("Fetchmail saw more than %d timeouts while attempting to get mail from %s@%s.\r\n"), 
                              MAX_TIMEOUTS,
                              ctl->remotename,
                              ctl->server.truename);
                stuff_warning(ctl, 
-                             _("This could mean that your mailserver is stuck, or that your SMTP listener"));
-               stuff_warning(ctl, 
-                             _("is wedged, or that your mailbox file on the server has been corrupted by"));
-               stuff_warning(ctl, 
-                             _("a server error.  You can run `fetchmail -v -v' to diagnose the problem."));
-               stuff_warning(ctl,
-                             _("Fetchmail won't poll this mailbox again until you restart it."));
+    _("This could mean that your mailserver is stuck, or that your SMTP\r\n" \
+    "server is wedged, or that your mailbox file on the server has been\r\n" \
+    "corrupted by a server error.  You can run `fetchmail -v -v' to\r\n" \
+    "diagnose the problem.\r\n\r\n" \
+    "Fetchmail won't poll this mailbox again until you restart it.\r\n"));
                close_warning_by_mail(ctl, (struct msgblk *)NULL);
                ctl->wedged = TRUE;
            }
@@ -1512,19 +1600,19 @@ const int maxfetch;             /* maximum number of messages to fetch */
        /* try to clean up all streams */
        release_sink(ctl);
        if (ctl->smtp_socket != -1)
-           close(ctl->smtp_socket);
+           SockClose(ctl->smtp_socket);
        if (mailserver_socket != -1)
            SockClose(mailserver_socket);
     }
     else
     {
-       char buf[POPBUFSIZE+1], *realhost;
+       char buf[MSGBUFSIZE+1], *realhost;
        int len, num, count, new, bytes, deletions = 0, *msgsizes = NULL;
-#if INET6
+#if INET6_ENABLE
        int fetches, dispatches, oldphase;
-#else /* INET6 */
+#else /* INET6_ENABLE */
        int port, fetches, dispatches, oldphase;
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
        struct idlist *idp;
 
        /* execute pre-initialization command, if any */
@@ -1540,23 +1628,28 @@ const int maxfetch;             /* maximum number of messages to fetch */
        oldphase = phase;
        phase = OPEN_WAIT;
        set_timeout(mytimeout);
-#if !INET6
+#if !INET6_ENABLE
+#ifdef SSL_ENABLE
+       port = ctl->server.port ? ctl->server.port : ( ctl->use_ssl ? protocol->sslport : protocol->port );
+#else
        port = ctl->server.port ? ctl->server.port : protocol->port;
-#endif /* !INET6 */
+#endif
+#endif /* !INET6_ENABLE */
        realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
 
        /* allow time for the port to be set up if we have a plugin */
        if (ctl->server.plugin)
            (void)sleep(1);
-#if INET6
+#if INET6_ENABLE
        if ((mailserver_socket = SockOpen(realhost, 
-                            ctl->server.service ? ctl->server.service : protocol->service,
+                            ctl->server.service ? ctl->server.service : ( ctl->use_ssl ? protocol->sslservice : protocol->service ),
                             ctl->server.netsec, ctl->server.plugin)) == -1)
-#else /* INET6 */
+#else /* INET6_ENABLE */
        if ((mailserver_socket = SockOpen(realhost, port, NULL, ctl->server.plugin)) == -1)
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
        {
-#if !INET6
+           char        errbuf[BUFSIZ];
+#if !INET6_ENABLE
            int err_no = errno;
 #ifdef HAVE_RES_SEARCH
            if (err_no != 0 && h_errno != 0)
@@ -1567,31 +1660,53 @@ const int maxfetch;             /* maximum number of messages to fetch */
             * in daemon mode but the connection to the outside world
             * is down.
             */
-           if (err_no == EHOSTUNREACH && run.poll_interval)
-               goto ehostunreach;
-
-           report_build(stderr, _("fetchmail: %s connection to %s failed"), 
-                        protocol->name, ctl->server.pollname);
-#ifdef HAVE_RES_SEARCH
-           if (h_errno != 0)
+           if (!((err_no == EHOSTUNREACH || err_no == ENETUNREACH) 
+                 && run.poll_interval))
            {
-               if (h_errno == HOST_NOT_FOUND)
-                   report_complete(stderr, _(": host is unknown\n"));
-               else if (h_errno == NO_ADDRESS)
-                   report_complete(stderr, _(": name is valid but has no IP address\n"));
-               else if (h_errno == NO_RECOVERY)
-                   report_complete(stderr, _(": unrecoverable name server error\n"));
-               else if (h_errno == TRY_AGAIN)
-                   report_complete(stderr, _(": temporary name server error\n"));
+               report_build(stderr, _("fetchmail: %s connection to %s failed"), 
+                            protocol->name, ctl->server.pollname);
+#ifdef HAVE_RES_SEARCH
+               if (h_errno != 0)
+               {
+                   if (h_errno == HOST_NOT_FOUND)
+                       strcpy(errbuf, _("host is unknown."));
+#ifndef __BEOS__
+                   else if (h_errno == NO_ADDRESS)
+                       strcpy(errbuf, _("name is valid but has no IP address."));
+#endif
+                   else if (h_errno == NO_RECOVERY)
+                       strcpy(errbuf, _("unrecoverable name server error."));
+                   else if (h_errno == TRY_AGAIN)
+                       strcpy(errbuf, _("temporary name server error."));
+                   else
+                       sprintf(errbuf, _("unknown DNS error %d."), h_errno);
+               }
                else
-                   report_complete(stderr, _(": unknown DNS error %d\n"), h_errno);
-           }
-           else
 #endif /* HAVE_RES_SEARCH */
-               report_complete(stderr, ": %s\n", strerror(err_no));
-
-       ehostunreach:
-#endif /* INET6 */
+                   strcpy(errbuf, strerror(err_no));
+               report_complete(stderr, ": %s\n", errbuf);
+
+#ifdef __UNUSED
+               /* 
+                * Don't use this.  It was an attempt to address Debian bug
+                * #47143 (Notify user by mail when pop server nonexistent).
+                * Trouble is, that doesn't work; you trip over the case 
+                * where your SLIP or PPP link is down...
+                */
+               /* warn the system administrator */
+               if (open_warning_by_mail(ctl, (struct msgblk *)NULL) == 0)
+               {
+                   stuff_warning(ctl,
+                        _("Subject: Fetchmail unreachable-server warning.\r\n"
+                          "\r\n"
+                          "Fetchmail could not reach the mail server %s:")
+                                 ctl->server.pollname);
+                   stuff_warning(ctl, errbuf, ctl->server.pollname);
+                   close_warning_by_mail(ctl, (struct msgblk *)NULL);
+               }
+#endif
+           }
+#endif /* INET6_ENABLE */
            ok = PS_SOCKET;
            set_timeout(0);
            phase = oldphase;
@@ -1600,6 +1715,17 @@ const int maxfetch;              /* maximum number of messages to fetch */
        set_timeout(0);
        phase = oldphase;
 
+#ifdef SSL_ENABLE
+       /* perform initial SSL handshake on open connection */
+       /* Note:  We pass the realhost name over for certificate
+               verification.  We may want to make this configurable */
+       if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslkey,ctl->sslcert,realhost) == -1) 
+       {
+           report(stderr, _("SSL connection failed.\n"));
+           goto closeUp;
+       }
+#endif
+
 #ifdef KERBEROS_V4
        if (ctl->server.preauthenticate == A_KERBEROS_V4)
        {
@@ -1628,6 +1754,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
            goto cleanUp;
 
        /* try to get authorized to fetch mail */
+       stage = STAGE_GETAUTH;
        if (protocol->getauth)
        {
            if (protocol->password_canonify)
@@ -1642,10 +1769,8 @@ const int maxfetch;              /* maximum number of messages to fetch */
                    report(stderr, _("Lock-busy error on %s@%s\n"),
                          ctl->remotename,
                          ctl->server.truename);
-               else
+               else if (ok == PS_AUTHFAIL)
                {
-                   if (ok == PS_ERROR)
-                       ok = PS_AUTHFAIL;
                    report(stderr, _("Authorization failure on %s@%s\n"), 
                          ctl->remotename,
                          ctl->server.truename);
@@ -1661,19 +1786,25 @@ const int maxfetch;             /* maximum number of messages to fetch */
                        && !open_warning_by_mail(ctl, (struct msgblk *)NULL))
                    {
                        stuff_warning(ctl,
-                              _("Subject: fetchmail authentication failed\r\n"));
+                           _("Subject: fetchmail authentication failed\r\n"));
                        stuff_warning(ctl,
-                               _("Fetchmail could not get mail from %s@%s."), 
-                               ctl->remotename,
-                               ctl->server.truename);
+                           _("Fetchmail could not get mail from %s@%s.\r\n"), 
+                           ctl->remotename,
+                           ctl->server.truename);
                        stuff_warning(ctl, 
-                              _("The attempt to get authorization failed."));
-                       stuff_warning(ctl, 
-                              _("This probably means your password is invalid."));
+    _("The attempt to get authorization failed.\r\n" \
+    "This probably means your password is invalid, but POP3 servers have\r\n" \
+    "other failure modes that fetchmail cannot distinguish from this\r\n" \
+    "because they don't send useful error messages on login failure.\r\n"));
                        close_warning_by_mail(ctl, (struct msgblk *)NULL);
                        ctl->wedged = TRUE;
                    }
                }
+               else
+                   report(stderr, _("Unknown login or authentication error on %s@%s\n"),
+                         ctl->remotename,
+                         ctl->server.truename);
+                   
                goto cleanUp;
            }
        }
@@ -1688,13 +1819,19 @@ const int maxfetch;             /* maximum number of messages to fetch */
                dispatches = 0;
                ++pass;
 
+               /* reset timeout, in case we did an IDLE */
+               mytimeout = ctl->server.timeout;
+
                if (outlevel >= O_DEBUG)
+               {
                    if (idp->id)
                        report(stdout, _("selecting or re-polling folder %s\n"), idp->id);
                    else
                        report(stdout, _("selecting or re-polling default folder\n"));
+               }
 
                /* compute # of messages and number of new messages waiting */
+               stage = STAGE_GETRANGE;
                ok = (protocol->getrange)(mailserver_socket, ctl, idp->id, &count, &new, &bytes);
                if (ok != 0)
                    goto cleanUp;
@@ -1707,6 +1844,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                    (void) sprintf(buf, _("%s at %s"),
                                   ctl->remotename, ctl->server.truename);
                if (outlevel > O_SILENT)
+               {
                    if (count == -1)            /* only used for ETRN */
                        report(stdout, _("Polling %s\n"), ctl->server.truename);
                    else if (count != 0)
@@ -1731,6 +1869,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                        if (pass == 1 && (run.poll_interval == 0 || outlevel >= O_VERBOSE))
                            report(stdout, _("No mail for %s\n"), buf); 
                    }
+               }
 
                /* very important, this is where we leave the do loop */ 
                if (count == 0)
@@ -1741,7 +1880,12 @@ const int maxfetch;              /* maximum number of messages to fetch */
                    if (new == -1 || ctl->fetchall)
                        new = count;
                    fetches = new;      /* set error status ccorrectly */
-                   goto no_error;
+                   /*
+                    * There used to be a `got noerror' here, but this
+                    * prevneted checking of multiple folders.  This
+                    * comment is a reminder in case I introduced some
+                    * subtle bug by removing it...
+                    */
                }
                else if (count > 0)
                {    
@@ -1791,6 +1935,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                        for (i = 0; i < count; i++)
                            msgsizes[i] = -1;
 
+                       stage = STAGE_GETSIZES;
                        ok = (proto->getsizes)(mailserver_socket, count, msgsizes);
                        if (ok != 0)
                            goto cleanUp;
@@ -1804,6 +1949,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                    }
 
                    /* read, forward, and delete messages */
+                   stage = STAGE_FETCH;
                    for (num = 1; num <= count; num++)
                    {
                        flag toolarge = NUM_NONZERO(ctl->limit)
@@ -1843,7 +1989,9 @@ const int maxfetch;               /* maximum number of messages to fetch */
                        {
                            if (outlevel > O_SILENT)
                            {
-                               report_build(stdout, _("skipping message %d"), num);
+                               report_build(stdout, 
+                                    _("skipping message %d (%d octets)"),
+                                    num, msgsizes[num-1]);
                                if (toolarge && !check_only) 
                                {
                                    char size[32];
@@ -1950,7 +2098,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                             */
                            if (protocol->fetch_body && !suppress_readbody) 
                            {
-                               if (outlevel >= O_VERBOSE)
+                               if (outlevel >= O_VERBOSE && !isafile(1))
                                {
                                    fputc('\n', stdout);
                                    fflush(stdout);
@@ -2004,7 +2152,7 @@ const int maxfetch;               /* maximum number of messages to fetch */
                                /* tell server we got it OK and resynchronize */
                                if (protocol->trail)
                                {
-                                   if (outlevel >= O_VERBOSE)
+                                   if (outlevel >= O_VERBOSE && !isafile(1))
                                    {
                                        fputc('\n', stdout);
                                        fflush(stdout);
@@ -2076,7 +2224,8 @@ const int maxfetch;               /* maximum number of messages to fetch */
                            struct idlist       *sdp;
 
                            for (sdp = ctl->newsaved; sdp; sdp = sdp->next)
-                               if (sdp->val.status.num == num)
+                               if ((sdp->val.status.num == num)
+                                               && (!toolarge || oldmsg))
                                    sdp->val.status.mark = UID_SEEN;
                        }
 
@@ -2122,14 +2271,14 @@ const int maxfetch;             /* maximum number of messages to fetch */
                }
            } while
                  /*
-                  * Only re-poll if we had some actual forwards, allowed
-                  * deletions and had no errors.
+                  * Only re-poll if we either had some actual forwards and 
+                  * either allowed deletions and had no errors.
                   * Otherwise it is far too easy to get into infinite loops.
                   */
                  (dispatches && protocol->retry && !ctl->keep && !ctl->errcount);
        }
 
-   no_error:
+    /* no_error: */
        /* ordinary termination with no errors -- officially log out */
        ok = (protocol->logout_cmd)(mailserver_socket, ctl);
        /*
@@ -2144,7 +2293,10 @@ const int maxfetch;              /* maximum number of messages to fetch */
     cleanUp:
        /* we only get here on error */
        if (ok != 0 && ok != PS_SOCKET)
+       {
+           stage = STAGE_LOGOUT;
            (protocol->logout_cmd)(mailserver_socket, ctl);
+       }
        SockClose(mailserver_socket);
     }