]> Pileus Git - ~andy/fetchmail/blobdiff - unmime.c
Add GPL license.
[~andy/fetchmail] / unmime.c
index 32a4ed720dc7eb2a2bc1cfac3c0fd2f1b495c485..35e5a96b274028e409976f98f183e61d714c048a 100644 (file)
--- a/unmime.c
+++ b/unmime.c
  * Configuration file support for fetchmail 4.3.8 by 
  * Frank Damgaard <frda@post3.tele.dk>
  * 
+ * For license terms, see the file COPYING in this directory.
  */
 
+#include "config.h"
 #include <string.h>
 #include <stdlib.h>
-#if defined(HAVE_ALLOCA_H)
-#include <alloca.h>
-#else
-#ifdef _AIX
- #pragma alloca
-#endif
-#endif
+#include <stdio.h>
 #include <ctype.h>
 #include "fetchmail.h"
+#include "i18n.h"
 
 static unsigned char unhex(unsigned char c)
 {
@@ -50,7 +47,6 @@ static int qp_char(unsigned char c1, unsigned char c2, unsigned char *c_out)
 }
 
 
-
 /*
  * Routines to decode MIME QP-encoded headers, as per RFC 2047.
  */
@@ -80,7 +76,7 @@ void UnMimeHeader(unsigned char *hdr)
 
   int  state = S_COPY_PLAIN;
   unsigned char *p_in, *p_out, *p;
-  unsigned char enc;
+  unsigned char enc = '\0';            /* initialization pacifies -Wall */
   int  i;
 
   /* Speed up in case this is not a MIME-encoded header */
@@ -217,7 +213,7 @@ void UnMimeHeader(unsigned char *hdr)
            * and prepare to process the new MIME charset/encoding
           * header.
           */
-         p_in = p + strlen(MIMEHDR_INIT);
+         p_in = p + sizeof(MIMEHDR_INIT) - 1;
          state = S_SKIP_MIMEINIT;
         }
       }
@@ -255,6 +251,7 @@ static int  BodyState = S_BODY_DATA;
  * a quoted-printable body part.
  */
 static int  CurrEncodingIsQP = 0;
+static int  CurrTypeNeedsDecode = 0;
 
 /* 
  * Delimiter for multipart messages. RFC 2046 states that this must
@@ -275,10 +272,10 @@ static void SetEncoding8bit(unsigned char *XferEncOfs)
   unsigned char *p;
 
   if (XferEncOfs != NULL) {
-     memcpy(XferEncOfs, ENC8BIT, strlen(ENC8BIT));
+     memcpy(XferEncOfs, ENC8BIT, sizeof(ENC8BIT) - 1);
 
      /* If anything left, in this header, replace with whitespace */
-     for (p=XferEncOfs+strlen(ENC8BIT); (*p >= ' '); p++) *p=' ';
+     for (p=XferEncOfs+sizeof(ENC8BIT)-1; (*p >= ' '); p++) *p=' ';
   }
 }
 
@@ -330,6 +327,39 @@ static char *GetBoundary(char *CntType)
 }
 
 
+int CheckContentType(char *CntType)
+{
+  /*
+   * Static array of Content-Type's for which we will do
+   * quoted-printable decoding, if requested. 
+   * It is probably wise to do this only on known text-only types;
+   * be really careful if you change this.
+   */
+
+  static char *DecodedTypes[] = {
+    "text/",        /* Will match ALL content-type's starting with 'text/' */
+    "message/rfc822", 
+    NULL
+  };
+
+  char *p = CntType;
+  int i;
+
+  /* If no Content-Type header, it isn't MIME - don't touch it */
+  if (CntType == NULL) return 0;
+
+  /* Skip whitespace, if any */
+  for (; isspace(*p); p++) ;
+
+  for (i=0; 
+       (DecodedTypes[i] && 
+       (strncasecmp(p, DecodedTypes[i], strlen(DecodedTypes[i])))); 
+       i++) ;
+
+  return (DecodedTypes[i] != NULL);
+}
+
+
 /*
  * This routine does three things:
  * 1) It determines - based on the message headers - whether the
@@ -341,8 +371,9 @@ static char *GetBoundary(char *CntType)
  *    - All other messages are assumed NOT to include 8-bit data.
  * 2) It determines the delimiter-string used in multi-part message
  *    bodies.
- * 3) It sets the initial values of the CurrEncodingIsQP and BodyState
- *    variables, from the header contents.
+ * 3) It sets the initial values of the CurrEncodingIsQP, 
+ *    CurrTypeNeedsDecode, and BodyState variables, from the header 
+ *    contents.
  *
  * The return value is a bitmask.
  */
@@ -355,7 +386,7 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
 
   /* Setup for a standard (no MIME, no QP, 7-bit US-ASCII) message */
   MultipartDelimiter[0] = '\0';
-  CurrEncodingIsQP = 0;
+  CurrEncodingIsQP = CurrTypeNeedsDecode = 0;
   BodyState = S_BODY_DATA;
   BodyType = 0;
 
@@ -370,7 +401,7 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
       XferEncOfs = NxtHdr;
       p = nxtaddr(NxtHdr);
       if (p != NULL) {
-       XferEnc = (char *)alloca(strlen(p) + 1);
+       xalloca(XferEnc, char *, strlen(p) + 1);
        strcpy(XferEnc, p);
        HdrsFound++;
       }
@@ -401,7 +432,7 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
       } while ( (p != NULL) && ((*(p+1) == '\t') || (*(p+1) == ' ')) );
       if (p == NULL) p = NxtHdr + strlen(NxtHdr);
 
-      CntType = (char *)alloca(p-NxtHdr+2);
+      xalloca(CntType, char *, p-NxtHdr+2);
       strncpy(CntType, NxtHdr, (p-NxtHdr));
       *(CntType+(p-NxtHdr)) = '\0';
       HdrsFound++;
@@ -409,7 +440,7 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
     else if (strncasecmp("MIME-Version:", NxtHdr, 13) == 0) {
       p = nxtaddr(NxtHdr);
       if (p != NULL) {
-       MimeVer = (char *)alloca(strlen(p) + 1);
+       xalloca(MimeVer, char *, strlen(p) + 1);
        strcpy(MimeVer, p);
        HdrsFound++;
       }
@@ -423,9 +454,11 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
   /* Done looking through the headers, now check what they say */
   if ((MimeVer != NULL) && (strcmp(MimeVer, "1.0") == 0)) {
 
+    CurrTypeNeedsDecode = CheckContentType(CntType);
+
     /* Check Content-Type to see if this is a multipart message */
     if ( (CntType != NULL) &&
-         ((strncasecmp(CntType, "multipart/", 10) == 0) ||
+         ((strncasecmp(CntType, "multipart/mixed", 16) == 0) ||
          (strncasecmp(CntType, "message/", 8) == 0)) ) {
 
       char *p1 = GetBoundary(CntType);
@@ -447,7 +480,7 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
       if (strcasecmp(XferEnc, "quoted-printable") == 0) {
        CurrEncodingIsQP = 1;
        BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
-       if (WantDecode) {
+       if (WantDecode && CurrTypeNeedsDecode) {
            SetEncoding8bit(XferEncOfs);
         }
       }
@@ -472,15 +505,26 @@ int MimeBodyType(unsigned char *hdrs, int WantDecode)
  * Return flag set if this line ends with a soft line-break.
  * 'bufp' is modified to point to the end of the output buffer.
  */
-static int DoOneQPLine(unsigned char **bufp, int collapsedoubledot)
+static int DoOneQPLine(unsigned char **bufp, flag delimited, flag issoftline)
 {
   unsigned char *buf = *bufp;
   unsigned char *p_in, *p_out, *p;
   int n;
   int ret = 0;
 
+  /*
+   * Special case: line consists of a single =2E and messages are 
+   * dot-terminated.  Line has to be dot-stuffed after decoding.
+   */
+  if (delimited && !issoftline && buf[0]=='=' && !strncmp(*bufp, "=2E\r\n", 5))
+  {
+      strcpy(buf, "..\r\n");
+      *bufp += 5;
+      return(FALSE);
+  }
+
   p_in = buf;
-  if (collapsedoubledot && (strncmp(buf, "..", 2) == 0))
+  if (delimited && issoftline && (strncmp(buf, "..", 2) == 0))
     p_in++;
 
   for (p_out = buf; (*p_in); ) {
@@ -543,7 +587,7 @@ static int DoOneQPLine(unsigned char **bufp, int collapsedoubledot)
  * 'bufp' is modified to point to the end of the output buffer.
  */
 
-int UnMimeBodyline(unsigned char **bufp, int collapsedoubledot)
+int UnMimeBodyline(unsigned char **bufp, flag delimited, flag softline)
 {
   unsigned char *buf = *bufp;
   int ret = 0;
@@ -551,17 +595,30 @@ int UnMimeBodyline(unsigned char **bufp, int collapsedoubledot)
   switch (BodyState) {
   case S_BODY_HDR:
     UnMimeHeader(buf);   /* Headers in body-parts can be encoded, too! */
-    if (strncasecmp("Content-Transfer-Encoding:", buf, 26) == 0) {
+    if ((*buf == '\0') || (*buf == '\n') || (strcmp(buf, "\r\n") == 0)) {
+      BodyState = S_BODY_DATA;
+    } 
+    else if (strncasecmp("Content-Transfer-Encoding:", buf, 26) == 0) {
       char *XferEnc;
 
       XferEnc = nxtaddr(buf);
       if ((XferEnc != NULL) && (strcasecmp(XferEnc, "quoted-printable") == 0)) {
        CurrEncodingIsQP = 1;
-       SetEncoding8bit(buf);
+
+        /*
+        * Hmm ... we cannot be really sure that CurrTypeNeedsDecode
+         * has been set - we may not have seen the Content-Type header
+         * yet. But *usually* the Content-Type header comes first, so
+         * this will work. And there is really no way of doing it 
+         * "right" as long as we stick with the line-by-line processing.
+        */
+       if (CurrTypeNeedsDecode)
+           SetEncoding8bit(buf);
       }
     }
-    else if ((*buf == '\0') || (*buf == '\n') || (strcmp(buf, "\r\n") == 0))
-      BodyState = S_BODY_DATA;
+    else if (strncasecmp("Content-Type:", buf, 13) == 0) {
+      CurrTypeNeedsDecode = CheckContentType(nxtaddr(buf));
+    }
 
     *bufp = (buf + strlen(buf));
     break;
@@ -570,11 +627,11 @@ int UnMimeBodyline(unsigned char **bufp, int collapsedoubledot)
     if ((*MultipartDelimiter) && 
        (strncmp(buf, MultipartDelimiter, strlen(MultipartDelimiter)) == 0)) {
       BodyState = S_BODY_HDR;
-      CurrEncodingIsQP = 0;
+      CurrEncodingIsQP = CurrTypeNeedsDecode = 0;
     }
 
-    if (CurrEncodingIsQP) 
-      ret = DoOneQPLine(bufp, collapsedoubledot);
+    if (CurrEncodingIsQP && CurrTypeNeedsDecode
+      ret = DoOneQPLine(bufp, delimited, softline);
     else
      *bufp = (buf + strlen(buf));
     break;
@@ -589,6 +646,7 @@ int UnMimeBodyline(unsigned char **bufp, int collapsedoubledot)
 #include <unistd.h>
 
 char *program_name = "unmime";
+int outlevel = 0;
 
 #define BUFSIZE_INCREMENT 4096