]> Pileus Git - ~andy/fetchmail/blobdiff - transact.c
Remove unused code.
[~andy/fetchmail] / transact.c
index 0d40d1bfe2725065f5e1e12da81188c7e6a574f0..55d251684179f0ea1dbbb381ad91760c2efd754b 100644 (file)
@@ -3,14 +3,12 @@
  *
  * Copyright 2001 by Eric S. Raymond
  * For license terms, see the file COPYING in this directory.
- *
- * 
  */
 
 #include  "config.h"
 #include  <stdio.h>
 #include  <string.h>
-#include  <ctype.h> /* isspace() */
+#include  <ctype.h>
 #ifdef HAVE_MEMORY_H
 #include  <memory.h>
 #endif /* HAVE_MEMORY_H */
 #else
 #include  <varargs.h>
 #endif
+#include <limits.h>
 
 #ifdef HAVE_NET_SOCKET_H
 #include <net/socket.h>
 #endif
-#include "md5.h"
+#include <sys/socket.h>
+#include <netdb.h>
+#include "fm_md5.h"
 
 #include "i18n.h"
 #include "socket.h"
 #include "fetchmail.h"
 
-int mytimeout;         /* value of nonreponse timeout */
-int suppress_tags;     /* emit tags? */
-char shroud[PASSWORDLEN*2+3];  /* string to shroud in debug output */
-struct msgblk msgblk;
+#define _FIX_INT_MIN(x) ((x) < INT_MIN ? INT_MIN : (x))
+#define _FIX_INT_MAX(x) ((x) > INT_MAX ? INT_MAX : (x))
+#define CAST_TO_INT(x) ((int)(_FIX_INT_MIN(_FIX_INT_MAX(x))))
+#define UCAST_TO_INT(x) ((int)(_FIX_INT_MAX(x)))
+
+/* global variables: please reinitialize them explicitly for proper
+ * working in daemon mode */
 
+/* session variables initialized in init_transact() */
+int suppress_tags = FALSE;     /* emit tags? */
 char tag[TAGLEN];
 static int tagnum;
 #define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag)
+static const struct method *protocol;
+char shroud[PASSWORDLEN*2+3];  /* string to shroud in debug output */
 
+/* session variables initialized in do_session() */
+int mytimeout;         /* value of nonreponse timeout */
+
+/* mail variables initialized in readheaders() */
+struct msgblk msgblk;
 static int accept_count, reject_count;
-static struct method *protocol;
 
+/** add given address to xmit_names if it exactly matches a full address
+ * \returns nonzero if matched */
+static int map_address(const char *addr, struct query *ctl, struct idlist **xmit_names)
+{
+    const char *lname;
+
+    lname = idpair_find(&ctl->localnames, addr);
+    if (lname) {
+       if (outlevel >= O_DEBUG)
+           report(stdout, GT_("mapped address %s to local %s\n"), addr, lname);
+       save_str(xmit_names, lname, XMIT_ACCEPT);
+       accept_count++;
+    }
+    return lname != NULL;
+}
+
+/** add given name to xmit_names if it matches declared localnames */
 static void map_name(const char *name, struct query *ctl, struct idlist **xmit_names)
-/* add given name to xmit_names if it matches declared localnames */
 /*   name:      name to map */
 /*   ctl:       list of permissible aliases */
 /*   xmit_names: list of recipient names parsed out */
 {
     const char *lname;
-    int off = 0;
-    
-    lname = idpair_find(&ctl->localnames, name+off);
+
+    lname = idpair_find(&ctl->localnames, name);
     if (!lname && ctl->wildcard)
-       lname = name+off;
+       lname = name;
 
     if (lname != (char *)NULL)
     {
@@ -83,9 +110,7 @@ static void find_server_names(const char *hdr,
     {
        char    *cp;
 
-       for (cp = nxtaddr((const unsigned char *)hdr);
-            cp != NULL;
-            cp = nxtaddr(NULL))
+       for (cp = nxtaddr(hdr); cp != NULL; cp = nxtaddr(NULL))
        {
            char        *atsign;
 
@@ -113,6 +138,11 @@ static void find_server_names(const char *hdr,
            if ((atsign = strchr((char *)cp, '@'))) {
                struct idlist   *idp;
 
+               /* try to match full address first, this takes
+                * precedence over localdomains and alias mappings */
+               if (map_address(cp, ctl, xmit_names))
+                   goto nomap;
+
                /*
                 * Does a trailing segment of the hostname match something
                 * on the localdomains list?  If so, save the whole name
@@ -121,10 +151,10 @@ static void find_server_names(const char *hdr,
                for (idp = ctl->server.localdomains; idp; idp = idp->next) {
                    char        *rhs;
 
-                   rhs = atsign + (strlen(atsign) - strlen((char *)idp->id));
+                   rhs = atsign + (strlen(atsign) - strlen(idp->id));
                    if (rhs > atsign &&
                        (rhs[-1] == '.' || rhs[-1] == '@') &&
-                       strcasecmp(rhs, (char *)idp->id) == 0)
+                       strcasecmp(rhs, idp->id) == 0)
                    {
                        if (outlevel >= O_DEBUG)
                            report(stdout, GT_("passed through %s matching %s\n"), 
@@ -144,7 +174,7 @@ static void find_server_names(const char *hdr,
                     * not, skip this name.  If it is, we'll keep
                     * going and try to find a mapping to a client name.
                     */
-                   if (!is_host_alias(atsign+1, ctl))
+                   if (!is_host_alias(atsign+1, ctl, &ai0))
                    {
                        save_str(xmit_names, cp, XMIT_REJECT);
                        reject_count++;
@@ -184,6 +214,7 @@ static char *parse_received(struct query *ctl, char *bufp)
 {
     char *base, *ok = (char *)NULL;
     static char rbuf[HOSTLEN + USERNAMELEN + 4]; 
+    struct addrinfo *ai0;
 
 #define RBUF_WRITE(value) if (tp < rbuf+sizeof(rbuf)-1) *tp++=value
 
@@ -231,7 +262,7 @@ static char *parse_received(struct query *ctl, char *bufp)
         * recipient name after a following "for".  Otherwise
         * punt.
         */
-       if (is_host_alias(rbuf, ctl))
+       if (is_host_alias(rbuf, ctl, &ai0))
        {
            if (outlevel >= O_DEBUG)
                report(stdout, 
@@ -334,6 +365,23 @@ static char *parse_received(struct query *ctl, char *bufp)
 /* shared by readheaders and readbody */
 static int sizeticker;
 
+/** Print ticker based on a amount of data transferred of \a bytes.
+ * Increments \a *tickervar by \a bytes, and if it exceeds
+ * \a SIZETICKER, print a dot and reduce *tickervar by \a SIZETICKER. */
+static void print_ticker(int *tickervar, int bytes)
+{
+    *tickervar += bytes;
+    while (*tickervar >= SIZETICKER)
+    {
+       if (want_progress())
+       {
+           fputc('.', stdout);
+           fflush(stdout);
+       }
+       *tickervar -= SIZETICKER;
+    }
+}
+
 #define EMPTYLINE(s)   (((s)[0] == '\r' && (s)[1] == '\n' && (s)[2] == '\0') \
                        || ((s)[0] == '\n' && (s)[1] == '\0'))
 
@@ -377,6 +425,7 @@ int readheaders(int sock,
     static char                *delivered_to = NULL;
     int                n, oldlen, ch, remaining, skipcount;
     size_t             linelen;
+    int                        delivered_to_count;
     struct idlist      *idp;
     flag               no_local_matches = FALSE;
     flag               has_nuls;
@@ -398,29 +447,27 @@ int readheaders(int sock,
      * condition the code for sending bouncemail will actually look
      * at the freed storage and coredump...
      */
-    if (msgblk.headers)
-       free(msgblk.headers);
+    xfree(msgblk.headers);
     free_str_list(&msgblk.recipients);
-    if (delivered_to)
-       free(delivered_to);
+    xfree(delivered_to);
 
     /* initially, no message digest */
     memset(ctl->digest, '\0', sizeof(ctl->digest));
 
-    msgblk.headers = received_for = delivered_to = NULL;
+    received_for = NULL;
     from_offs = reply_to_offs = resent_from_offs = app_from_offs = 
        sender_offs = resent_sender_offs = env_offs = -1;
     oldlen = 0;
     msgblk.msglen = 0;
     skipcount = 0;
+    delivered_to_count = 0;
     ctl->mimemsg = 0;
 
     for (remaining = fetchlen; remaining > 0 || protocol->delimited; )
     {
        char *line, *rline;
-       int overlong = FALSE; /* XXX FIXME: this is unused */
 
-       line = xmalloc(sizeof(buf));
+       line = (char *)xmalloc(sizeof(buf));
        linelen = 0;
        line[0] = '\0';
        do {
@@ -431,8 +478,6 @@ int readheaders(int sock,
                if ((n = SockRead(sock, buf, sizeof(buf)-1)) == -1) {
                    set_timeout(0);
                    free(line);
-                   free(msgblk.headers);
-                   msgblk.headers = NULL;
                    return(PS_SOCKET);
                }
                set_timeout(0);
@@ -461,7 +506,6 @@ int readheaders(int sock,
             */
            if (n && buf[n-1] != '\n') 
            {
-               overlong = TRUE;
                rline = (char *) realloc(line, linelen + 1);
                if (rline == NULL)
                {
@@ -491,8 +535,8 @@ int readheaders(int sock,
                tcp = line + linelen - 1;
                *tcp++ = '\r';
                *tcp++ = '\n';
-               *tcp++ = '\0';
-               n++;
+               *tcp = '\0';
+               /* n++; - not used later on */
                linelen++;
            }
            else
@@ -510,6 +554,7 @@ int readheaders(int sock,
            /* check for end of headers */
            if (end_of_header(line))
            {
+eoh:
                if (linelen != strlen (line))
                    has_nuls = TRUE;
                free(line);
@@ -523,15 +568,9 @@ int readheaders(int sock,
             */
            if (protocol->delimited && line[0] == '.' && EMPTYLINE(line+1))
            {
-               if (outlevel > O_SILENT)
-                   report(stdout,
-                          GT_("message delimiter found while scanning headers\n"));
                if (suppress_readbody)
                    *suppress_readbody = TRUE;
-               if (linelen != strlen (line))
-                   has_nuls = TRUE;
-               free(line);
-               goto process_headers;
+               goto eoh; /* above */
            }
 
            /*
@@ -539,14 +578,27 @@ int readheaders(int sock,
             * send out robotmail that's missing the RFC822 delimiter blank
             * line before the body! Without this check fetchmail segfaults.
             * With it, we treat such messages as spam and refuse them.
+            *
+            * Frederic Marchal reported in February 2006 that hotmail
+            * or something improperly wrapped a very long TO header
+            * (wrapped without inserting whitespace in the continuation
+            * line) and found that this code thus refused a message
+            * that should have been delivered.
+            *
+            * XXX FIXME: we should probably wrap the message up as
+            * message/rfc822 attachment and forward to postmaster (Rob
+            * MacGregor)
             */
-           if (!refuse_mail && !isspace((unsigned char)line[0]) && !strchr(line, ':'))
+           if (!refuse_mail
+               && !ctl->server.badheader == BHACCEPT
+               && !isspace((unsigned char)line[0])
+               && !strchr(line, ':'))
            {
                if (linelen != strlen (line))
                    has_nuls = TRUE;
                if (outlevel > O_SILENT)
                    report(stdout,
-                          GT_("incorrect header line found while scanning headers\n"));
+                          GT_("incorrect header line found - see manpage for bad-header option\n"));
                if (outlevel >= O_VERBOSE)
                    report (stdout, GT_("line: %s"), line);
                refuse_mail = 1;
@@ -562,32 +614,25 @@ int readheaders(int sock,
        /* write the message size dots */
        if ((outlevel > O_SILENT && outlevel < O_VERBOSE) && linelen > 0)
        {
-           sizeticker += linelen;
-           while (sizeticker >= SIZETICKER)
-           {
-               if (outlevel > O_SILENT && run.showdots && !run.use_syslog)
-               {
-                   fputc('.', stdout);
-                   fflush(stdout);
-               }
-               sizeticker -= SIZETICKER;
-           }
+           print_ticker(&sizeticker, linelen);
+       }
+
+       /*
+        * Decode MIME encoded headers. We MUST do this before
+        * looking at the Content-Type / Content-Transfer-Encoding
+        * headers (RFC 2046).
+        */
+       if ( ctl->mimedecode )
+       {
+           char *tcp;
+           UnMimeHeader(line);
+           /* the line is now shorter. So we retrace back till we find
+            * our terminating combination \n\0, we move backwards to
+            * make sure that we don't catch some \n\0 stored in the
+            * decoded part of the message */
+           for (tcp = line + linelen - 1; tcp > line && (*tcp != 0 || tcp[-1] != '\n'); tcp--) { }
+           if  (tcp > line) linelen = tcp - line;
        }
-               /*
-                * Decode MIME encoded headers. We MUST do this before
-                * looking at the Content-Type / Content-Transfer-Encoding
-                * headers (RFC 2046).
-                */
-               if ( ctl->mimedecode )
-               {
-                   char *tcp;
-                   UnMimeHeader(line);
-                   /* the line is now shorter. So we retrace back till we find our terminating
-                    * combination \n\0, we move backwards to make sure that we don't catch som
-                    * \n\0 stored in the decoded part of the message */
-                   for(tcp = line + linelen - 1; tcp > line && (*tcp != 0 || tcp[-1] != '\n'); tcp--);
-                   if(tcp > line) linelen = tcp - line;
-               }
 
 
        /* skip processing if we are going to retain or refuse this mail */
@@ -597,7 +642,7 @@ int readheaders(int sock,
            continue;
        }
 
-       /* we see an ordinary (non-header, non-message-delimiter line */
+       /* we see an ordinary (non-header, non-message-delimiter) line */
        if (linelen != strlen (line))
            has_nuls = TRUE;
 
@@ -607,19 +652,38 @@ int readheaders(int sock,
         * on being able to keep base-UID information in a special
         * message at the head of the mailbox.  This message should
         * neither be deleted nor forwarded.
+        *
+        * An example for such a message is (keep this in so people
+        * find it when looking where the special code is to handle the
+        * data):
+        *
+        *   From MAILER-DAEMON Wed Nov 23 11:38:42 2005
+        *   Date: 23 Nov 2005 11:38:42 +0100
+        *   From: Mail System Internal Data <MAILER-DAEMON@mail.example.org>
+        *   Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+        *   Message-ID: <1132742322@mail.example.org>
+        *   X-IMAP: 1132742306 0000000001
+        *   Status: RO
+        *
+        *   This text is part of the internal format of your mail folder, and is not
+        *   a real message.  It is created automatically by the mail system software.
+        *   If deleted, important folder data will be lost, and it will be re-created
+        *   with the data reset to initial values.
+        *
+        * This message is only visible if a POP3 server that is unaware
+        * of these UWIMAP messages is used besides UWIMAP or PINE.
+        *
+        * We will just check if the first message in the mailbox has an
+        * X-IMAP: header.
         */
 #ifdef POP2_ENABLE
        /*
         * We disable this check under POP2 because there's no way to
-        * prevent deletion of the message.  So at least we ought to 
+        * prevent deletion of the message.  So at least we ought to
         * 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 */
+       if (servport("pop2") != servport(protocol->service))
 #endif /* POP2_ENABLE */
            if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
                free(line);
@@ -655,17 +719,24 @@ int readheaders(int sock,
        }
 
        /*
-        * 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. 
+        * We remove all Delivered-To: headers if dropdelivered is set
+        * - special care must be taken if Delivered-To: is also used
+        * as envelope at the same time.
+        *
+        * This is to avoid false mail loops errors when delivering
+        * local messages to and from a Postfix or qmail mailserver.
         */
        if (ctl->dropdelivered && !strncasecmp(line, "Delivered-To:", 13)) 
        {
-           if (delivered_to)
+           if (delivered_to ||
+               ctl->server.envelope == STRING_DISABLED ||
+               !ctl->server.envelope ||
+               strcasecmp(ctl->server.envelope, "Delivered-To") ||
+               delivered_to_count != ctl->server.envskip)
                free(line);
            else 
                delivered_to = line;
+           delivered_to_count++;
            continue;
        }
 
@@ -684,17 +755,17 @@ int readheaders(int sock,
         * turns on the dropstatus flag.
         */
        {
-           char        *cp;
+           char        *tcp;
 
            if (!strncasecmp(line, "Status:", 7))
-               cp = line + 7;
+               tcp = line + 7;
            else if (!strncasecmp(line, "X-Mozilla-Status:", 17))
-               cp = line + 17;
+               tcp = line + 17;
            else
-               cp = NULL;
-           if (cp) {
-               while (*cp && isspace((unsigned char)*cp)) cp++;
-               if (!*cp || ctl->dropstatus)
+               tcp = NULL;
+           if (tcp) {
+               while (*tcp && isspace((unsigned char)*tcp)) tcp++;
+               if (!*tcp || ctl->dropstatus)
                {
                    free(line);
                    continue;
@@ -734,9 +805,10 @@ int readheaders(int sock,
         */
        if ((already_has_return_path==FALSE) && !strncasecmp("Return-Path:", line, 12) && (cp = nxtaddr(line)))
        {
+           char nulladdr[] = "<>";
            already_has_return_path = TRUE;
            if (cp[0]=='\0')    /* nxtaddr() strips the brackets... */
-               cp="<>";
+               cp=nulladdr;
            strncpy(msgblk.return_path, cp, sizeof(msgblk.return_path));
            msgblk.return_path[sizeof(msgblk.return_path)-1] = '\0';
            if (!ctl->mda) {
@@ -748,7 +820,7 @@ int readheaders(int sock,
        if (!msgblk.headers)
        {
            oldlen = linelen;
-           msgblk.headers = xmalloc(oldlen + 1);
+           msgblk.headers = (char *)xmalloc(oldlen + 1);
            (void) memcpy(msgblk.headers, line, linelen);
            msgblk.headers[oldlen] = '\0';
            free(line);
@@ -816,8 +888,8 @@ int readheaders(int sock,
                sscanf(line+12, "%s", id);
                if (!str_find( &ctl->newsaved, num))
                {
-                   struct idlist *new = save_str(&ctl->newsaved,id,UID_SEEN);
-                   new->val.status.num = num;
+                   struct idlist *newl = save_str(&ctl->newsaved,id,UID_SEEN);
+                   newl->val.status.num = num;
                }
            }
        }
@@ -831,7 +903,7 @@ int readheaders(int sock,
                || !strncasecmp("Bcc:", line, 4)
                || !strncasecmp("Apparently-To:", line, 14))
            {
-               *to_chainptr = xmalloc(sizeof(struct addrblk));
+               *to_chainptr = (struct addrblk *)xmalloc(sizeof(struct addrblk));
                (*to_chainptr)->offset = (line - msgblk.headers);
                to_chainptr = &(*to_chainptr)->next; 
                *to_chainptr = NULL;
@@ -841,7 +913,7 @@ int readheaders(int sock,
                     || !strncasecmp("Resent-Cc:", line, 10)
                     || !strncasecmp("Resent-Bcc:", line, 11))
            {
-               *resent_to_chainptr = xmalloc(sizeof(struct addrblk));
+               *resent_to_chainptr = (struct addrblk *)xmalloc(sizeof(struct addrblk));
                (*resent_to_chainptr)->offset = (line - msgblk.headers);
                resent_to_chainptr = &(*resent_to_chainptr)->next; 
                *resent_to_chainptr = NULL;
@@ -871,14 +943,12 @@ int readheaders(int sock,
        }
     }
 
- process_headers:    
+process_headers:
 
-    if (retain_mail)
-    {
-       free(msgblk.headers);
-       msgblk.headers = NULL;
+    if (retain_mail) {
        return(PS_RETAINED);
     }
+
     if (refuse_mail)
        return(PS_REFUSED);
     /*
@@ -908,13 +978,18 @@ int readheaders(int sock,
      * 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...
+     *
+     * Matthias Andree:
+     * The real fix however is to insist on Delivered-To: or similar
+     * headers and require that one copy per recipient be dropped.
+     * Everything else breaks sooner or later.
      */
-    if (MULTIDROP(ctl))
+    if (MULTIDROP(ctl) && msgblk.headers)
     {
        MD5_CTX context;
 
        MD5Init(&context);
-       MD5Update(&context, msgblk.headers, strlen(msgblk.headers));
+       MD5Update(&context, (unsigned char *)msgblk.headers, strlen(msgblk.headers));
        MD5Final(ctl->digest, &context);
 
        if (!received_for && env_offs == -1 && !delivered_to)
@@ -989,7 +1064,7 @@ int readheaders(int sock,
        else if (resent_from_offs >= 0 && (ap = nxtaddr(msgblk.headers + resent_from_offs)));
        else if (from_offs >= 0 && (ap = nxtaddr(msgblk.headers + from_offs)));
        else if (reply_to_offs >= 0 && (ap = nxtaddr(msgblk.headers + reply_to_offs)));
-       else if (app_from_offs >= 0 && (ap = nxtaddr(msgblk.headers + app_from_offs)));
+       else if (app_from_offs >= 0 && (ap = nxtaddr(msgblk.headers + app_from_offs))) {}
        /* multi-line MAIL FROM addresses confuse SMTP terribly */
        if (ap && !strchr(ap, '\n')) {
            strncpy(msgblk.return_path, ap, sizeof(msgblk.return_path));
@@ -1013,16 +1088,25 @@ int readheaders(int sock,
            free(sdps_envto);
        } else
 #endif /* SDPS_ENABLE */ 
-       if (env_offs > -1)          /* We have the actual envelope addressee */
-           find_server_names(msgblk.headers + env_offs, ctl, &msgblk.recipients);
+           if (env_offs > -1) {            /* We have the actual envelope addressee */
+               if (outlevel >= O_DEBUG) {
+                   const char *tmps = msgblk.headers + env_offs;
+                   size_t l = strcspn(tmps, "\r\n");
+                   report(stdout, GT_("Parsing envelope \"%s\" names \"%-.*s\"\n"), ctl->server.envelope, UCAST_TO_INT(l), tmps);
+               }
+               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"))
-   {
+               ctl->server.envelope && !strcasecmp(ctl->server.envelope, "Delivered-To"))
+       {
+           if (outlevel >= O_DEBUG) {
+               const char *tmps = delivered_to + 2 + strlen(ctl->server.envelope);
+               size_t l = strcspn(tmps, "\r\n");
+               report(stdout, GT_("Parsing envelope \"%s\" names \"%-.*s\"\n"), ctl->server.envelope, UCAST_TO_INT(l), tmps);
+           }
            find_server_names(delivered_to, ctl, &msgblk.recipients);
-       free(delivered_to);
-       delivered_to = NULL;
-   }
-       else if (received_for)
+           xfree(delivered_to);
+       } else if (received_for) {
            /*
             * We have the Received for addressee.  
             * It has to be a mailserver address, or we
@@ -1030,9 +1114,13 @@ int readheaders(int sock,
             * We use find_server_names() to let local 
             * hostnames go through.
             */
+           if (outlevel >= O_DEBUG) {
+               const char *tmps = received_for + 2;
+               size_t l = strcspn(tmps, "\r\n");
+               report(stdout, GT_("Parsing Received names \"%-.*s\"\n"), UCAST_TO_INT(l), tmps);
+           }
            find_server_names(received_for, ctl, &msgblk.recipients);
-       else
-       {
+       } else {
            /*
             * We haven't extracted the envelope address.
             * So check all the "Resent-To" header addresses if 
@@ -1040,6 +1128,8 @@ int readheaders(int sock,
             * the "To" addresses.
             */
            register struct addrblk *nextptr;
+          if (outlevel >= O_DEBUG)
+                  report(stdout, GT_("No envelope recipient found, resorting to header guessing.\n"));
            if (resent_to_addrchain) {
                /* delete the "To" chain and substitute it 
                 * with the "Resent-To" list 
@@ -1054,6 +1144,12 @@ int readheaders(int sock,
            }
            /* now look for remaining adresses */
            while (to_addrchain) {
+               if (outlevel >= O_DEBUG) {
+                   const char *tmps = msgblk.headers+to_addrchain->offset;
+                   size_t l = strcspn(tmps, "\r\n");
+                   report(stdout, GT_("Guessing from header \"%-.*s\".\n"), UCAST_TO_INT(l), tmps);
+               }
+
                find_server_names(msgblk.headers+to_addrchain->offset, ctl, &msgblk.recipients);
                nextptr = to_addrchain->next;
                free(to_addrchain);
@@ -1082,9 +1178,6 @@ int readheaders(int sock,
        if (outlevel >= O_DEBUG)
            report(stdout,
                   GT_("forwarding and deletion suppressed due to DNS errors\n"));
-       free(msgblk.headers);
-       msgblk.headers = NULL;
-       free_str_list(&msgblk.recipients);
        return(PS_TRANSIENT);
     }
     else
@@ -1093,9 +1186,6 @@ int readheaders(int sock,
        if ((n = open_sink(ctl, &msgblk,
                           &good_addresses, &bad_addresses)) != PS_SUCCESS)
        {
-           free(msgblk.headers);
-           msgblk.headers = NULL;
-           free_str_list(&msgblk.recipients);
            return(n);
        }
     }
@@ -1123,23 +1213,27 @@ int readheaders(int sock,
     {
        /* utter any per-message Received information we need here */
         if (ctl->server.trueaddr) {
+           char saddr[50];
+           int e;
+
+           e = getnameinfo(ctl->server.trueaddr, ctl->server.trueaddr_len,
+                   saddr, sizeof(saddr), NULL, 0,
+                   NI_NUMERICHOST);
+           if (e)
+               snprintf(saddr, sizeof(saddr), "(%-.*s)", (int)(sizeof(saddr) - 3), gai_strerror(e));
            snprintf(buf, sizeof(buf),
-                   "Received: from %s [%u.%u.%u.%u]\r\n", 
-                   ctl->server.truename,
-                   (unsigned int)(unsigned char)ctl->server.trueaddr[0],
-                   (unsigned int)(unsigned char)ctl->server.trueaddr[1],
-                   (unsigned int)(unsigned char)ctl->server.trueaddr[2],
-                   (unsigned int)(unsigned char)ctl->server.trueaddr[3]);
+                   "Received: from %s [%s]\r\n", 
+                   ctl->server.truename, saddr);
        } else {
-         snprintf(buf, sizeof(buf),
+           snprintf(buf, sizeof(buf),
                  "Received: from %s\r\n", ctl->server.truename);
        }
        n = stuffline(ctl, buf);
        if (n != -1)
        {
            /*
-            * This header is technically invalid under RFC822.
-            * POP3, IMAP, etc. are not legal mail-parameter values.
+            * We SHOULD (RFC-2821 sec. 4.4/p. 53) make sure to only use
+            * IANA registered protocol names here.
             */
            snprintf(buf, sizeof(buf),
                    "\tby %s with %s (fetchmail-%s",
@@ -1152,6 +1246,10 @@ int readheaders(int sock,
                        " polling %s account %s",
                        ctl->server.pollname,
                        ctl->remotename);
+               if (ctl->folder)
+                   snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                           " folder %s",
+                           ctl->folder);
            }
            snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ")\r\n");
            n = stuffline(ctl, buf);
@@ -1160,7 +1258,7 @@ int readheaders(int sock,
                buf[0] = '\t';
                if (good_addresses == 0)
                {
-                   snprintf(buf+1, sizeof(buf)-1, "for %s (by default); ",
+                   snprintf(buf+1, sizeof(buf)-1, "for <%s> (by default); ",
                            rcpt_address (ctl, run.postmaster, 0));
                }
                else if (good_addresses == 1)
@@ -1168,8 +1266,9 @@ int readheaders(int sock,
                    for (idp = msgblk.recipients; idp; idp = idp->next)
                        if (idp->val.status.mark == XMIT_ACCEPT)
                            break;      /* only report first address */
-                   snprintf(buf+1, sizeof(buf)-1,
-                           "for %s", rcpt_address (ctl, idp->id, 1));
+                   if (idp)
+                       snprintf(buf+1, sizeof(buf)-1,
+                               "for <%s>", rcpt_address (ctl, idp->id, 1));
                    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf)-1,
                            " (%s); ",
                            MULTIDROP(ctl) ? "multi-drop" : "single-drop");
@@ -1191,13 +1290,11 @@ int readheaders(int sock,
     {
        report(stdout, GT_("writing RFC822 msgblk.headers\n"));
        release_sink(ctl);
-       free(msgblk.headers);
-       msgblk.headers = NULL;
-       free_str_list(&msgblk.recipients);
        return(PS_IOERR);
     }
-    else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !isafile(2))
-       fputs("#", stdout);
+    
+    if (want_progress())
+       fputc('#', stdout);
 
     /* write error notifications */
     if (no_local_matches || has_nuls || bad_addresses)
@@ -1240,7 +1337,7 @@ int readheaders(int sock,
                if (idp->val.status.mark == XMIT_RCPTBAD)
                    errlen += strlen(idp->id) + 2;
 
-           errmsg = xmalloc(errlen + 3);
+           errmsg = (char *)xmalloc(errlen + 3);
            strcpy(errmsg, errhd);
            for (idp = msgblk.recipients; idp; idp = idp->next)
                if (idp->val.status.mark == XMIT_RCPTBAD)
@@ -1265,10 +1362,13 @@ int readheaders(int sock,
     cp = buf;
     *cp++ = '\r';
     *cp++ = '\n';
-    *cp++ = '\0';
-    stuffline(ctl, buf);
+    *cp = '\0';
+    n = stuffline(ctl, buf);
 
-    return(PS_SUCCESS);
+    if ((size_t)n == strlen(buf))
+       return PS_SUCCESS;
+    else
+       return PS_SOCKET;
 }
 
 int readbody(int sock, struct query *ctl, flag forward, int len)
@@ -1279,8 +1379,8 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
 /*   forward:          TRUE to forward */
 {
     int        linelen;
-    unsigned char buf[MSGBUFSIZE+4];
-    unsigned char *inbufp = buf;
+    char buf[MSGBUFSIZE+4];
+    char *inbufp = buf;
     flag issoftline = FALSE;
 
     /*
@@ -1296,6 +1396,9 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
     while (protocol->delimited || len > 0)
     {
        set_timeout(mytimeout);
+       /* XXX FIXME: for undelimited protocols that ship the size, such
+        * as IMAP, we might want to use the count of remaining characters
+        * instead of the buffer size -- not for fetchmail 6.3.X though */
        if ((linelen = SockRead(sock, inbufp, sizeof(buf)-4-(inbufp-buf)))==-1)
        {
            set_timeout(0);
@@ -1307,17 +1410,22 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
        /* write the message size dots */
        if (linelen > 0)
        {
-           sizeticker += linelen;
-           while (sizeticker >= SIZETICKER)
-           {
-               if (outlevel > O_SILENT && run.showdots && !run.use_syslog)
-               {
-                   fputc('.', stdout);
-                   fflush(stdout);
-               }
-               sizeticker -= SIZETICKER;
-           }
+           print_ticker(&sizeticker, linelen);
        }
+
+       /* Mike Jones, Manchester University, 2006:
+        * "To fix IMAP MIME Messages in which fetchmail adds the remainder of
+        * the IMAP packet including the ')' character (part of the IMAP)
+        * Protocol causing the addition of an extra MIME boundary locally."
+        *
+        * However, we shouldn't do this for delimited protocols:
+        * many POP3 servers (Microsoft, qmail) goof up message sizes
+        * so we might end truncating messages prematurely.
+        */
+       if (!protocol->delimited && linelen > len) {
+           inbufp[len] = '\0';
+       }
+
        len -= linelen;
 
        /* check for end of message */
@@ -1362,11 +1470,11 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
 
            if (n < 0)
            {
-               report(stdout, GT_("writing message text\n"));
+               report(stdout, GT_("error writing message text\n"));
                release_sink(ctl);
                return(PS_IOERR);
            }
-           else if (outlevel >= O_VERBOSE && !isafile(1))
+           else if (want_progress())
            {
                fputc('*', stdout);
                fflush(stdout);
@@ -1380,9 +1488,11 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
 void init_transact(const struct method *proto)
 /* initialize state for the send and receive functions */
 {
+    suppress_tags = FALSE;
     tagnum = 0;
     tag[0] = '\0';     /* nuke any tag hanging out from previous query */
-    protocol = (struct method *)proto;
+    protocol = proto;
+    shroud[0] = '\0';
 }
 
 static void enshroud(char *buf)
@@ -1439,11 +1549,10 @@ va_dcl
     }
 }
 
-int gen_recv(sock, buf, size)
-/* get one line of input from the server */
-int sock;      /* socket to which server is connected */
-char *buf;     /* buffer to receive input */
-int size;      /* length of buffer */
+/** get one line of input from the server */
+int gen_recv(int sock  /** socket to which server is connected */,
+            char *buf /* buffer to receive input */,
+            int size  /* length of buffer */)
 {
     int oldphase = phase;      /* we don't have to be re-entrant */
 
@@ -1453,7 +1562,7 @@ int size; /* length of buffer */
     {
        set_timeout(0);
        phase = oldphase;
-       if(isidletimeout())
+       if(is_idletimeout())
        {
          resetidletimeout();
          return(PS_IDLETIMEOUT);
@@ -1506,7 +1615,8 @@ va_dcl
     va_end(ap);
 
     snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\r\n");
-    if (SockWrite(sock, buf, strlen(buf)) < strlen(buf)) {
+    ok = SockWrite(sock, buf, strlen(buf));
+    if (ok == -1 || (size_t)ok != strlen(buf)) {
        /* short write, bail out */
        return PS_SOCKET;
     }