]> Pileus Git - ~andy/fetchmail/blobdiff - transact.c
Revised dup-killer code.
[~andy/fetchmail] / transact.c
index 73c133f707527bff04762f675b3dea5ef02bec49..c6387e3ca32e1134417d9dbdcd40194494e95cc1 100644 (file)
@@ -10,6 +10,7 @@
 #include  "config.h"
 #include  <stdio.h>
 #include  <string.h>
+#include  <ctype.h> /* isspace() */
 #ifdef HAVE_MEMORY_H
 #include  <memory.h>
 #endif /* HAVE_MEMORY_H */
@@ -65,7 +66,7 @@ static void map_name(const char *name, struct query *ctl, struct idlist **xmit_n
     if (lname != (char *)NULL)
     {
        if (outlevel >= O_DEBUG)
-           report(stdout, _("mapped %s to local %s\n"), name, lname);
+           report(stdout, GT_("mapped %s to local %s\n"), name, lname);
        save_str(xmit_names, lname, XMIT_ACCEPT);
        accept_count++;
     }
@@ -122,7 +123,7 @@ static void find_server_names(const char *hdr,
                        strcasecmp(rhs, idp->id) == 0)
                    {
                        if (outlevel >= O_DEBUG)
-                           report(stdout, _("passed through %s matching %s\n"), 
+                           report(stdout, GT_("passed through %s matching %s\n"), 
                                  cp, idp->id);
                        save_str(xmit_names, cp, XMIT_ACCEPT);
                        accept_count++;
@@ -189,7 +190,7 @@ static char *parse_received(struct query *ctl, char *bufp)
      * does this when the mail has a single recipient.
      */
     if (outlevel >= O_DEBUG)
-       report(stdout, _("analyzing Received line:\n%s"), bufp);
+       report(stdout, GT_("analyzing Received line:\n%s"), bufp);
 
     /* search for whitepace-surrounded "by" followed by valid address */
     for (base = bufp;  ; base = ok + 2)
@@ -228,13 +229,13 @@ static char *parse_received(struct query *ctl, char *bufp)
        {
            if (outlevel >= O_DEBUG)
                report(stdout, 
-                     _("line accepted, %s is an alias of the mailserver\n"), rbuf);
+                     GT_("line accepted, %s is an alias of the mailserver\n"), rbuf);
        }
        else
        {
            if (outlevel >= O_DEBUG)
                report(stdout, 
-                     _("line rejected, %s is not an alias of the mailserver\n"), 
+                     GT_("line rejected, %s is not an alias of the mailserver\n"), 
                      rbuf);
            return(NULL);
        }
@@ -305,7 +306,7 @@ static char *parse_received(struct query *ctl, char *bufp)
     if (!ok)
     {
        if (outlevel >= O_DEBUG)
-           report(stdout, _("no Received address found\n"));
+           report(stdout, GT_("no Received address found\n"));
        return(NULL);
     }
     else
@@ -314,7 +315,7 @@ static char *parse_received(struct query *ctl, char *bufp)
            char *lf = rbuf + strlen(rbuf)-1;
            *lf = '\0';
            if (outlevel >= O_DEBUG)
-               report(stdout, _("found Received address `%s'\n"), rbuf+2);
+               report(stdout, GT_("found Received address `%s'\n"), rbuf+2);
            *lf = '\n';
        }
        return(rbuf);
@@ -376,6 +377,11 @@ int readheaders(int sock,
     if (msgblk.headers)
        free(msgblk.headers);
 
+    /* initially, no message ID */
+    if (ctl->thisid)
+       free(ctl->thisid);
+    ctl->thisid = NULL;
+
     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;
@@ -387,6 +393,7 @@ int readheaders(int sock,
     for (remaining = fetchlen; remaining > 0 || protocol->delimited; remaining -= linelen)
     {
        char *line;
+       int overlong = FALSE;
 
        line = xmalloc(sizeof(buf));
        linelen = 0;
@@ -404,6 +411,19 @@ int readheaders(int sock,
            linelen += n;
            msgblk.msglen += n;
 
+               /*
+                * Try to gracefully handle the case, where the length of a
+                * line exceeds MSGBUFSIZE.
+                */
+               if ( n && buf[n-1] != '\n' ) {
+                       unsigned int llen = strlen(line);
+                       overlong = TRUE;
+                       line = realloc(line, llen + n + 1);
+                       strcpy(line + llen, buf);
+                       ch = ' '; /* So the next iteration starts */
+                       continue;
+               }
+
            /* lines may not be properly CRLF terminated; fix this for qmail */
            if (ctl->forcecr)
            {
@@ -416,17 +436,27 @@ int readheaders(int sock,
                }
            }
 
-           /*
-            * Decode MIME encoded headers. We MUST do this before
-            * looking at the Content-Type / Content-Transfer-Encoding
-            * headers (RFC 2046).
-            */
-           if (ctl->mimedecode)
-               UnMimeHeader(buf);
-
-           line = (char *) realloc(line, strlen(line) + strlen(buf) +1);
+               /*
+                * Decode MIME encoded headers. We MUST do this before
+                * looking at the Content-Type / Content-Transfer-Encoding
+                * headers (RFC 2046).
+                */
+               if ( ctl->mimedecode && overlong ) {
+                       /*
+                        * If we received an overlong line, we have to decode the
+                        * whole line at once.
+                        */
+                       line = (char *) realloc(line, strlen(line) + strlen(buf) +1);
+                       strcat(line, buf);
+                       UnMimeHeader(line);
+               }
+               else {
+                       if ( ctl->mimedecode )
+                               UnMimeHeader(buf);
 
-           strcat(line, buf);
+                       line = (char *) realloc(line, strlen(line) + strlen(buf) +1);
+                       strcat(line, buf);
+               }
 
            /* check for end of headers */
            if (EMPTYLINE(line))
@@ -444,8 +474,8 @@ int readheaders(int sock,
             */
            if (protocol->delimited && line[0] == '.' && EMPTYLINE(line+1))
            {
-               free(line);
                has_nuls = (linelen != strlen(line));
+               free(line);
                goto process_headers;
            }
 
@@ -458,7 +488,7 @@ int readheaders(int sock,
             */
            if (!isspace(line[0]) && !strchr(line, ':'))
            {
-               headers_ok = TRUE;
+               headers_ok = FALSE;
                has_nuls = (linelen != strlen(line));
                free(line);
                goto process_headers;
@@ -489,44 +519,9 @@ int readheaders(int sock,
        /* we see an ordinary (non-header, non-message-delimiter line */
        has_nuls = (linelen != strlen(line));
 
-       /*
-        * When mail delivered to a multidrop mailbox on the server is
-        * 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.  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...
-        */
+       /* save the message's ID, we may use it for killing duplicates later */
        if (MULTIDROP(ctl) && !strncasecmp(line, "Message-ID:", 11))
-       {
-           if (ctl->lastid && !strcasecmp(ctl->lastid, line))
-           {
-               if (accept_count > 1)
-                   return(PS_REFUSED);
-           }
-           else
-           {
-               if (ctl->lastid)
-                   free(ctl->lastid);
-               ctl->lastid = strdup(line);
-           }
-       }
+           ctl->thisid = xstrdup(line);
 
        /*
         * The University of Washington IMAP server (the reference
@@ -716,9 +711,9 @@ int readheaders(int sock,
         * (RFC2822 says the condents of Sender must be a valid mailbox
         * address, which is also what RFC822 4.4.4 implies.)
         */
-       else if (!strncasecmp("Sender:", line, 7) && strchr(line, '@'))
+       else if (!strncasecmp("Sender:", line, 7) && (strchr(line, '@') || strchr(line, '!')))
            sender_offs = (line - msgblk.headers);
-       else if (!strncasecmp("Resent-Sender:", line, 14) && strchr(line, '@'))
+       else if (!strncasecmp("Resent-Sender:", line, 14) && (strchr(line, '@') || strchr(line, '!')))
            resent_sender_offs = (line - msgblk.headers);
 
 #ifdef __UNUSED__
@@ -772,21 +767,66 @@ int readheaders(int sock,
                                                line,
                                                strlen(ctl->server.envelope)))
                {                               
-                   if (skipcount++ != ctl->server.envskip)
+                   if (skipcount++ < ctl->server.envskip)
                        continue;
                    env_offs = (line - msgblk.headers);
                }    
            }
            else if (!received_for && !strncasecmp("Received:", line, 9))
            {
-               if (skipcount++ != ctl->server.envskip)
+               if (skipcount++ < ctl->server.envskip)
                    continue;
                received_for = parse_received(ctl, line);
            }
        }
     }
 
- process_headers:
+ process_headers:    
+    /*
+     * When mail delivered to a multidrop mailbox on the server is
+     * addressed to multiple people on the client machine, there will
+     * be one copy left in the box for each recipient.  This is not a
+     * problem if we have the actual recipient address to dispatch on
+     * (e.g. because we've mined it out of sendmail trace headers, or
+     * a qmail Delivered-To line, or a declared sender envelope line).
+     *
+     * But if we're mining addressees out of the To/Cc/Bcc fields, and
+     * 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.  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 (!received_for && env_offs == -1 && !delivered_to)
+    {
+       if (ctl->lastid && ctl->thisid && !strcasecmp(ctl->lastid, ctl->thisid))
+       {
+           if (accept_count > 1)
+               return(PS_REFUSED);
+       }
+       else
+       {
+           if (ctl->lastid)
+               free(ctl->lastid);
+           ctl->lastid = ctl->thisid;
+           ctl->thisid = NULL;
+       }
+    }
+
     /*
      * We want to detect this early in case there are so few headers that the
      * dispatch logic barfs.
@@ -795,7 +835,7 @@ int readheaders(int sock,
     {
        if (outlevel > O_SILENT)
            report(stdout,
-                  _("message delimiter found while scanning headers\n"));
+                  GT_("message delimiter found while scanning headers\n"));
     }
 
     /*
@@ -933,7 +973,7 @@ int readheaders(int sock,
            save_str(&msgblk.recipients, run.postmaster, XMIT_ACCEPT);
            if (outlevel >= O_DEBUG)
                report(stdout,
-                     _("no local matches, forwarding to %s\n"),
+                     GT_("no local matches, forwarding to %s\n"),
                      run.postmaster);
        }
     }
@@ -948,7 +988,7 @@ int readheaders(int sock,
     {
        if (outlevel >= O_DEBUG)
            report(stdout,
-                  _("forwarding and deletion suppressed due to DNS errors\n"));
+                  GT_("forwarding and deletion suppressed due to DNS errors\n"));
        free(msgblk.headers);
        msgblk.headers = NULL;
        free_str_list(&msgblk.recipients);
@@ -990,14 +1030,24 @@ int readheaders(int sock,
     {
        /* utter any per-message Received information we need here */
         if (ctl->server.trueaddr) {
-           sprintf(buf, "Received: from %s [%u.%u.%u.%u]\r\n", 
+#ifdef HAVE_SNPRINTF
+           snprintf(buf, sizeof(buf),
+#else
+           sprintf(buf, 
+#endif /* HAVE_SNPRINTF */
+                   "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);
+#ifdef HAVE_SNPRINTF
+         snprintf(buf, sizeof(buf),
+#else                       
+         sprintf(buf,
+#endif /* HAVE_SNPRINTF */
+                 "Received: from %s\r\n", ctl->server.truename);
        }
        n = stuffline(ctl, buf);
        if (n != -1)
@@ -1006,7 +1056,12 @@ int readheaders(int sock,
             * This header is technically invalid under RFC822.
             * POP3, IMAP, etc. are not legal mail-parameter values.
             */
-           sprintf(buf, "\tby %s with %s (fetchmail-%s)",
+#ifdef HAVE_SNPRINTF
+           snprintf(buf, sizeof(buf),
+#else
+           sprintf(buf,
+#endif /* HAVE_SNPRINTF */
+                   "\tby %s with %s (fetchmail-%s",
                    fetchmailhost,
                    protocol->name,
                    VERSION);
@@ -1016,14 +1071,22 @@ int readheaders(int sock,
                        ctl->server.pollname, 
                        ctl->remotename);
            }
-           strcat(buf, "\r\n");
+#ifdef HAVE_SNPRINTF
+           snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ")\r\n");
+#else
+           strcat(buf, ")\r\n");
+#endif /* HAVE_SNPRINTF */
            n = stuffline(ctl, buf);
            if (n != -1)
            {
                buf[0] = '\t';
                if (good_addresses == 0)
                {
-                   sprintf(buf+1, 
+#ifdef HAVE_SNPRINTF
+                   snprintf(buf+1, sizeof(buf)-1,
+#else
+                   sprintf(buf+1,
+#endif /* HAVE_SNPRINTF */
                            "for %s@%s (by default); ",
                            user, ctl->destaddr);
                }
@@ -1033,22 +1096,37 @@ int readheaders(int sock,
                        if (idp->val.status.mark == XMIT_ACCEPT)
                            break;      /* only report first address */
                    if (strchr(idp->id, '@'))
-                       sprintf(buf+1, "for %s", idp->id);
+#ifdef HAVE_SNPRINTF
+                   snprintf(buf+1, sizeof(buf)-1,
+#else                       
+                   sprintf(buf+1,
+#endif /* HAVE_SNPRINTF */
+                           "for %s", idp->id);
                    else
                        /*
                         * This could be a bit misleading, as destaddr is
                         * the forwarding host rather than the actual 
                         * destination.  Most of the time they coincide.
                         */
-                       sprintf(buf+1, "for %s@%s", idp->id, ctl->destaddr);
+#ifdef HAVE_SNPRINTF
+                       snprintf(buf+1, sizeof(buf)-1,
+#else                       
+                       sprintf(buf+1,
+#endif /* HAVE_SNPRINTF */
+                               "for %s@%s", idp->id, ctl->destaddr);
                    sprintf(buf+strlen(buf), " (%s); ",
                            MULTIDROP(ctl) ? "multi-drop" : "single-drop");
                }
                else
                    buf[1] = '\0';
 
+#ifdef HAVE_SNPRINTF
+               snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s\r\n",
+                       rfc822timestamp());
+#else
                strcat(buf, rfc822timestamp());
                strcat(buf, "\r\n");
+#endif /* HAVE_SNPRINTF */
                n = stuffline(ctl, buf);
            }
        }
@@ -1059,7 +1137,7 @@ int readheaders(int sock,
 
     if (n == -1)
     {
-       report(stdout, _("writing RFC822 msgblk.headers\n"));
+       report(stdout, GT_("writing RFC822 msgblk.headers\n"));
        release_sink(ctl);
        free(msgblk.headers);
        msgblk.headers = NULL;
@@ -1067,7 +1145,7 @@ int readheaders(int sock,
        return(PS_IOERR);
     }
     else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !isafile(2))
-       fputs("#", stderr);
+       fputs("#", stdout);
 
     /* write error notifications */
     if (no_local_matches || has_nuls || bad_addresses)
@@ -1080,13 +1158,13 @@ int readheaders(int sock,
        if (no_local_matches)
        {
            if (reject_count != 1)
-               strcat(errhd, _("no recipient addresses matched declared local names"));
+               strcat(errhd, GT_("no recipient addresses matched declared local names"));
            else
            {
                for (idp = msgblk.recipients; idp; idp = idp->next)
                    if (idp->val.status.mark == XMIT_REJECT)
                        break;
-               sprintf(errhd+strlen(errhd), _("recipient address %s didn't match any local name"), idp->id);
+               sprintf(errhd+strlen(errhd), GT_("recipient address %s didn't match any local name"), idp->id);
            }
        }
 
@@ -1094,14 +1172,14 @@ int readheaders(int sock,
        {
            if (errhd[sizeof("X-Fetchmail-Warning: ")])
                strcat(errhd, "; ");
-           strcat(errhd, _("message has embedded NULs"));
+           strcat(errhd, GT_("message has embedded NULs"));
        }
 
        if (bad_addresses)
        {
            if (errhd[sizeof("X-Fetchmail-Warning: ")])
                strcat(errhd, "; ");
-           strcat(errhd, _("SMTP listener rejected local recipient addresses: "));
+           strcat(errhd, GT_("SMTP listener rejected local recipient addresses: "));
            errlen = strlen(errhd);
            for (idp = msgblk.recipients; idp; idp = idp->next)
                if (idp->val.status.mark == XMIT_RCPTBAD)
@@ -1230,7 +1308,7 @@ int readbody(int sock, struct query *ctl, flag forward, int len)
 
            if (n < 0)
            {
-               report(stdout, _("writing message text\n"));
+               report(stdout, GT_("writing message text\n"));
                release_sink(ctl);
                return(PS_IOERR);
            }
@@ -1283,7 +1361,11 @@ va_dcl
 #endif
     va_end(ap);
 
+#ifdef HAVE_SNPRINTF
+    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\r\n");
+#else
     strcat(buf, "\r\n");
+#endif /* HAVE_SNPRINTF */
     SockWrite(sock, buf, strlen(buf));
 
     if (outlevel >= O_MONITOR)
@@ -1369,7 +1451,11 @@ va_dcl
 #endif
     va_end(ap);
 
+#ifdef HAVE_SNPRINTF
+    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\r\n");
+#else
     strcat(buf, "\r\n");
+#endif /* HAVE_SNPRINTF */
     SockWrite(sock, buf, strlen(buf));
 
     if (outlevel >= O_MONITOR)