]> Pileus Git - ~andy/fetchmail/blobdiff - transact.c
Merge branch 'master' into next
[~andy/fetchmail] / transact.c
index fcacc62d8115360566de9c810c6a1ade11f9f670..1480e76417002cac68a2f67e9dcbe406a032dcf2 100644 (file)
@@ -3,40 +3,30 @@
  *
  * 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>
-#ifdef HAVE_MEMORY_H
-#include  <memory.h>
-#endif /* HAVE_MEMORY_H */
-#if defined(STDC_HEADERS)
 #include  <stdlib.h>
-#endif
-#if defined(HAVE_UNISTD_H)
 #include <unistd.h>
-#endif
-#if defined(HAVE_STDARG_H)
 #include  <stdarg.h>
-#else
-#include  <varargs.h>
-#endif
+#include <limits.h>
 
-#ifdef HAVE_NET_SOCKET_H
-#include <net/socket.h>
-#endif
 #include <sys/socket.h>
 #include <netdb.h>
-#include "md5.h"
+#include "fm_md5.h"
 
-#include "i18n.h"
+#include "gettext.h"
 #include "socket.h"
 #include "fetchmail.h"
 
+#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 */
 
@@ -45,7 +35,7 @@ int suppress_tags = FALSE;    /* emit tags? */
 char tag[TAGLEN];
 static int tagnum;
 #define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag)
-static struct method *protocol;
+static const struct method *protocol;
 char shroud[PASSWORDLEN*2+3];  /* string to shroud in debug output */
 
 /* session variables initialized in do_session() */
@@ -55,18 +45,33 @@ int mytimeout;              /* value of nonreponse timeout */
 struct msgblk msgblk;
 static int accept_count, reject_count;
 
+/** 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)
     {
@@ -91,9 +96,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;
 
@@ -121,6 +124,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
@@ -129,10 +137,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"), 
@@ -152,7 +160,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++;
@@ -192,6 +200,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
 
@@ -239,7 +248,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, 
@@ -342,6 +351,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'))
 
@@ -426,9 +452,8 @@ int readheaders(int sock,
     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 {
@@ -467,7 +492,6 @@ int readheaders(int sock,
             */
            if (n && buf[n-1] != '\n') 
            {
-               overlong = TRUE;
                rline = (char *) realloc(line, linelen + 1);
                if (rline == NULL)
                {
@@ -497,8 +521,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
@@ -516,6 +540,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);
@@ -529,15 +554,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 */
            }
 
            /*
@@ -545,14 +564,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;
@@ -568,32 +600,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 */
@@ -603,7 +628,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;
 
@@ -637,15 +662,6 @@ int readheaders(int sock,
         * 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
-        * forward it to the user so he or she will have some clue
-        * that things have gone awry.
-        */
-       if (servport("pop2") != servport(protocol->service))
-#endif /* POP2_ENABLE */
            if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
                free(line);
                retain_mail = 1;
@@ -716,17 +732,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;
@@ -766,9 +782,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) {
@@ -780,7 +797,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);
@@ -837,24 +854,6 @@ int readheaders(int sock,
        else if (!strncasecmp("Resent-Sender:", line, 14) && (strchr(line, '@') || strchr(line, '!')))
            resent_sender_offs = (line - msgblk.headers);
 
-#ifdef __UNUSED__
-       else if (!strncasecmp("Message-Id:", line, 11))
-       {
-           if (ctl->server.uidl)
-           {
-               char id[IDLEN+1];
-
-               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);
-                   new->val.status.num = num;
-               }
-           }
-       }
-#endif /* __UNUSED__ */
-
        /* if multidrop is on, gather addressee headers */
        if (MULTIDROP(ctl))
        {
@@ -863,7 +862,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;
@@ -873,7 +872,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;
@@ -903,12 +902,12 @@ int readheaders(int sock,
        }
     }
 
- process_headers:    
+process_headers:
 
-    if (retain_mail)
-    {
+    if (retain_mail) {
        return(PS_RETAINED);
     }
+
     if (refuse_mail)
        return(PS_REFUSED);
     /*
@@ -949,7 +948,7 @@ int readheaders(int sock,
        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)
@@ -1048,15 +1047,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);
            xfree(delivered_to);
-   }
-       else if (received_for)
+       } else if (received_for) {
            /*
             * We have the Received for addressee.  
             * It has to be a mailserver address, or we
@@ -1064,9 +1073,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 
@@ -1074,6 +1087,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 
@@ -1088,6 +1103,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);
@@ -1158,7 +1179,7 @@ int readheaders(int sock,
                    saddr, sizeof(saddr), NULL, 0,
                    NI_NUMERICHOST);
            if (e)
-               snprintf(saddr, sizeof(saddr), "(%-.*s)", sizeof(saddr) - 3, gai_strerror(e));
+               snprintf(saddr, sizeof(saddr), "(%-.*s)", (int)(sizeof(saddr) - 3), gai_strerror(e));
            snprintf(buf, sizeof(buf),
                    "Received: from %s [%s]\r\n", 
                    ctl->server.truename, saddr);
@@ -1170,8 +1191,8 @@ int readheaders(int sock,
        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",
@@ -1184,6 +1205,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);
@@ -1200,8 +1225,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");
@@ -1225,7 +1251,8 @@ int readheaders(int sock,
        release_sink(ctl);
        return(PS_IOERR);
     }
-    else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !is_a_file(1) && !run.use_syslog)
+    
+    if (want_progress())
        fputc('#', stdout);
 
     /* write error notifications */
@@ -1269,7 +1296,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)
@@ -1294,10 +1321,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)
@@ -1308,8 +1338,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;
 
     /*
@@ -1325,6 +1355,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);
@@ -1336,17 +1369,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 */
@@ -1391,11 +1429,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 && !is_a_file(1) && !run.use_syslog)
+           else if (want_progress())
            {
                fputc('*', stdout);
                fflush(stdout);
@@ -1412,7 +1450,7 @@ void init_transact(const struct method *proto)
     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';
 }
 
@@ -1433,14 +1471,7 @@ static void enshroud(char *buf)
     }
 }
 
-#if defined(HAVE_STDARG_H)
 void gen_send(int sock, const char *fmt, ... )
-#else
-void gen_send(sock, fmt, va_alist)
-int sock;              /* socket to which server is connected */
-const char *fmt;       /* printf-style format */
-va_dcl
-#endif
 /* assemble command in printf(3) style and send to the server */
 {
     char buf [MSGBUFSIZE+1];
@@ -1451,11 +1482,7 @@ va_dcl
     else
        buf[0] = '\0';
 
-#if defined(HAVE_STDARG_H)
     va_start(ap, fmt);
-#else
-    va_start(ap);
-#endif
     vsnprintf(buf + strlen(buf), sizeof(buf)-2-strlen(buf), fmt, ap);
     va_end(ap);
 
@@ -1470,11 +1497,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 */
 
@@ -1506,14 +1532,7 @@ int size;        /* length of buffer */
     }
 }
 
-#if defined(HAVE_STDARG_H)
 int gen_transact(int sock, const char *fmt, ... )
-#else
-int gen_transact(int sock, fmt, va_alist)
-int sock;              /* socket to which server is connected */
-const char *fmt;       /* printf-style format */
-va_dcl
-#endif
 /* assemble command in printf(3) style, send to server, accept a response */
 {
     int ok;
@@ -1528,16 +1547,13 @@ va_dcl
     else
        buf[0] = '\0';
 
-#if defined(HAVE_STDARG_H)
     va_start(ap, fmt) ;
-#else
-    va_start(ap);
-#endif
     vsnprintf(buf + strlen(buf), sizeof(buf)-2-strlen(buf), fmt, ap);
     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;
     }