]> Pileus Git - ~andy/fetchmail/blobdiff - unmime.c
Minor bug fixes for socket.c
[~andy/fetchmail] / unmime.c
index cedc67371b47a1c0c4d01c0158556a0849cfa762..f799ff92f51b5a22836a9135c43f9b4df8878bfb 100644 (file)
--- a/unmime.c
+++ b/unmime.c
@@ -10,6 +10,7 @@
  * 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"
@@ -18,6 +19,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include "fetchmail.h"
+#include "i18n.h"
 
 static unsigned char unhex(unsigned char c)
 {
@@ -28,10 +30,10 @@ static unsigned char unhex(unsigned char c)
   else if ((c >= 'a') && (c <= 'f'))
     return (c - 'a' + 10);
   else
-    return c;
+      return 16;       /* invalid hex character */
 }
 
-static int qp_char(unsigned char c1, unsigned char c2, unsigned char *c_out)
+static int qp_char(unsigned char c1, unsigned char c2, char *c_out)
 {
   c1 = unhex(c1);
   c2 = unhex(c2);
@@ -45,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.
  */
@@ -58,7 +59,7 @@ static int qp_char(unsigned char c1, unsigned char c2, unsigned char *c_out)
 static const char MIMEHDR_INIT[]  = "=?";      /* Start of coded sequence */
 static const char MIMEHDR_END[]   = "?=";      /* End of coded sequence */
 
-void UnMimeHeader(unsigned char *hdr)
+void UnMimeHeader(char *hdr)
 {
   /* Decode a buffer containing data encoded according to RFC
    * 2047. This only handles content-transfer-encoding; conversion
@@ -70,12 +71,12 @@ void UnMimeHeader(unsigned char *hdr)
   /* Note: Decoding is done "in-situ", i.e. without using an
    * additional buffer for temp. storage. This is possible, since the
    * decoded string will always be shorter than the encoded string,
-   * due to the encoding scheme.
+   * due to the encoding scheme.
    */
 
   int  state = S_COPY_PLAIN;
-  unsigned char *p_in, *p_out, *p;
-  unsigned char enc = '\0';            /* initialization pacifies -Wall */
+  char *p_in, *p_out, *p;
+  char enc = '\0';             /* initialization pacifies -Wall */
   int  i;
 
   /* Speed up in case this is not a MIME-encoded header */
@@ -122,7 +123,7 @@ void UnMimeHeader(unsigned char *hdr)
 
        /* *(p+1) is the transfer encoding, *(p+2) must be a '?' */
        if (*(p+2) == '?') {
-         enc = tolower(*(p+1));
+         enc = tolower((unsigned char)*(p+1));
          p_in = p+3;
          state = S_COPY_MIME;
        }
@@ -170,7 +171,7 @@ void UnMimeHeader(unsigned char *hdr)
          int decoded_count;
 
          delimsave = *p; *p = '\r';
-         decoded_count = from64tobits(p_out, p_in);
+         decoded_count = from64tobits(p_out, p_in, 0);
          *p = delimsave;
          if (decoded_count > 0) 
            p_out += decoded_count;            
@@ -199,11 +200,11 @@ void UnMimeHeader(unsigned char *hdr)
         * There is more MIME data later on. Is there
          * whitespace  only before the delimiter? 
         */
-        unsigned char *q;
+        char *q;
         int  wsp_only = 1;
 
         for (q=p_in; (wsp_only && (q < p)); q++)
-          wsp_only = isspace(*q);
+          wsp_only = isspace((unsigned char)*q);
 
         if (wsp_only) {
          /* 
@@ -212,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;
         }
       }
@@ -250,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
@@ -257,7 +259,7 @@ static int  CurrEncodingIsQP = 0;
  * at the beginning, and a terminating null.
  */
 #define MAX_DELIM_LEN 70
-static unsigned char MultipartDelimiter[MAX_DELIM_LEN+3];
+static char MultipartDelimiter[MAX_DELIM_LEN+3];
 
 
 /* This string replaces the "Content-Transfer-Encoding: quoted-printable"
@@ -265,15 +267,16 @@ static unsigned char MultipartDelimiter[MAX_DELIM_LEN+3];
  * must be no longer than the original string.
  */
 static const char ENC8BIT[] = "Content-Transfer-Encoding: 8bit";
-static void SetEncoding8bit(unsigned char *XferEncOfs)
+static void SetEncoding8bit(char *XferEncOfs)
 {
-  unsigned char *p;
+  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; ((unsigned char)*p >= ' '); p++)
+       *p=' ';
   }
 }
 
@@ -289,7 +292,7 @@ static char *GetBoundary(char *CntType)
   do {
     p2 = strchr(p1, ';'); 
     if (p2)
-      for (p2++; isspace(*p2); p2++);
+      for (p2++; isspace((unsigned char)*p2); p2++) { }
 
     p1 = p2;
   } while ((p1) && (strncasecmp(p1, "boundary", 8) != 0));
@@ -299,7 +302,7 @@ static char *GetBoundary(char *CntType)
     return NULL;
 
   /* Skip "boundary", whitespace and '='; check that we do have a '=' */
-  for (p1+=8, flag=0; (isspace(*p1) || (*p1 == '=')); p1++)
+  for (p1+=8, flag=0; (isspace((unsigned char)*p1) || (*p1 == '=')); p1++)
     flag |= (*p1 == '=');
   if (!flag)
     return NULL;
@@ -325,6 +328,39 @@ static char *GetBoundary(char *CntType)
 }
 
 
+static 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 const 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((unsigned char)*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
@@ -336,129 +372,137 @@ 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.
  */
-int MimeBodyType(unsigned char *hdrs, int WantDecode)
+int MimeBodyType(char *hdrs, int WantDecode)
 {
-  unsigned char *NxtHdr = hdrs;
-  unsigned char *XferEnc, *XferEncOfs, *CntType, *MimeVer, *p;
-  int  HdrsFound = 0;     /* We only look for three headers */
-  int  BodyType;          /* Return value */ 
-
-  /* Setup for a standard (no MIME, no QP, 7-bit US-ASCII) message */
-  MultipartDelimiter[0] = '\0';
-  CurrEncodingIsQP = 0;
-  BodyState = S_BODY_DATA;
-  BodyType = 0;
-
-  /* Just in case ... */
-  if (hdrs == NULL)
-    return BodyType;
-
-  XferEnc = XferEncOfs = CntType = MimeVer = NULL;
+    char *NxtHdr = hdrs;
+    char *XferEnc, *XferEncOfs, *CntType, *MimeVer, *p;
+    int  HdrsFound = 0;     /* We only look for three headers */
+    int  BodyType;          /* Return value */ 
+
+    /* Setup for a standard (no MIME, no QP, 7-bit US-ASCII) message */
+    MultipartDelimiter[0] = '\0';
+    CurrEncodingIsQP = CurrTypeNeedsDecode = 0;
+    BodyState = S_BODY_DATA;
+    BodyType = 0;
+
+    /* Just in case ... */
+    if (hdrs == NULL)
+       return BodyType;
+
+    XferEnc = XferEncOfs = CntType = MimeVer = NULL;
+
+    do {
+       if (strncasecmp("Content-Transfer-Encoding:", NxtHdr, 26) == 0) {
+           XferEncOfs = NxtHdr;
+           p = nxtaddr(NxtHdr);
+           if (p != NULL) {
+               xfree(XferEnc);
+               XferEnc = xstrdup(p);
+               HdrsFound++;
+           }
+       }
+       else if (strncasecmp("Content-Type:", NxtHdr, 13) == 0) {
+           /*
+            * This one is difficult. We cannot use the standard
+            * nxtaddr() routine, since the boundary-delimiter is
+            * (probably) enclosed in quotes - and thus appears
+            * as an rfc822 comment, and nxtaddr() "eats" up any
+            * spaces in the delimiter. So, we have to do this
+            * by hand.
+            */
+
+           /* Skip the "Content-Type:" part and whitespace after it */
+           for (NxtHdr += 13; ((*NxtHdr == ' ') || (*NxtHdr == '\t')); NxtHdr++) { }
 
-  do {
-    if (strncasecmp("Content-Transfer-Encoding:", NxtHdr, 26) == 0) {
-      XferEncOfs = NxtHdr;
-      p = nxtaddr(NxtHdr);
-      if (p != NULL) {
-       xalloca(XferEnc, char *, strlen(p) + 1);
-       strcpy(XferEnc, p);
-       HdrsFound++;
-      }
-    }
-    else if (strncasecmp("Content-Type:", NxtHdr, 13) == 0) {
-      /*
-       * This one is difficult. We cannot use the standard
-       * nxtaddr() routine, since the boundary-delimiter is
-       * (probably) enclosed in quotes - and thus appears
-       * as an rfc822 comment, and nxtaddr() "eats" up any
-       * spaces in the delimiter. So, we have to do this
-       * by hand.
-       */
+           /* 
+            * Get the full value of the Content-Type header;
+            * it might span multiple lines. So search for
+            * a newline char, but ignore those that have a
+            * have a TAB or space just after the NL (continued
+            * lines).
+            */
+           p = NxtHdr-1;
+           do {
+               p=strchr((p+1),'\n'); 
+           } while ( (p != NULL) && ((*(p+1) == '\t') || (*(p+1) == ' ')) );
+           if (p == NULL) p = NxtHdr + strlen(NxtHdr);
+
+           xfree(CntType);
+           CntType = (char *)xmalloc(p-NxtHdr+1);
+           strlcpy(CntType, NxtHdr, p-NxtHdr+1);
+           HdrsFound++;
+       }
+       else if (strncasecmp("MIME-Version:", NxtHdr, 13) == 0) {
+           p = nxtaddr(NxtHdr);
+           if (p != NULL) {
+               xfree(MimeVer);
+               MimeVer = xstrdup(p);
+               HdrsFound++;
+           }
+       }
 
-      /* Skip the "Content-Type:" part and whitespace after it */
-      for (NxtHdr += 13; ((*NxtHdr == ' ') || (*NxtHdr == '\t')); NxtHdr++);
+       NxtHdr = (strchr(NxtHdr, '\n'));
+       if (NxtHdr != NULL) NxtHdr++;
+    } while ((NxtHdr != NULL) && (*NxtHdr) && (HdrsFound != 3));
 
-      /* 
-       * Get the full value of the Content-Type header;
-       * it might span multiple lines. So search for
-       * a newline char, but ignore those that have a
-       * have a TAB or space just after the NL (continued
-       * lines).
-       */
-      p = NxtHdr-1;
-      do {
-        p=strchr((p+1),'\n'); 
-      } while ( (p != NULL) && ((*(p+1) == '\t') || (*(p+1) == ' ')) );
-      if (p == NULL) p = NxtHdr + strlen(NxtHdr);
-
-      xalloca(CntType, char *, p-NxtHdr+2);
-      strncpy(CntType, NxtHdr, (p-NxtHdr));
-      *(CntType+(p-NxtHdr)) = '\0';
-      HdrsFound++;
-    }
-    else if (strncasecmp("MIME-Version:", NxtHdr, 13) == 0) {
-      p = nxtaddr(NxtHdr);
-      if (p != NULL) {
-       xalloca(MimeVer, char *, strlen(p) + 1);
-       strcpy(MimeVer, p);
-       HdrsFound++;
-      }
-    }
 
-    NxtHdr = (strchr(NxtHdr, '\n'));
-    if (NxtHdr != NULL) NxtHdr++;
-  } while ((NxtHdr != NULL) && (*NxtHdr) && (HdrsFound != 3));
+    /* Done looking through the headers, now check what they say */
+    if ((MimeVer != NULL) && (strcmp(MimeVer, "1.0") == 0)) {
 
+       CurrTypeNeedsDecode = CheckContentType(CntType);
 
-  /* Done looking through the headers, now check what they say */
-  if ((MimeVer != NULL) && (strcmp(MimeVer, "1.0") == 0)) {
+       /* Check Content-Type to see if this is a multipart message */
+       if ( (CntType != NULL) &&
+               ((strncasecmp(CntType, "multipart/mixed", 16) == 0) ||
+                (strncasecmp(CntType, "message/", 8) == 0)) ) {
 
-    /* Check Content-Type to see if this is a multipart message */
-    if ( (CntType != NULL) &&
-         ((strncasecmp(CntType, "multipart/", 10) == 0) ||
-         (strncasecmp(CntType, "message/", 8) == 0)) ) {
+           char *p1 = GetBoundary(CntType);
 
-      char *p1 = GetBoundary(CntType);
+           if (p1 != NULL) {
+               /* The actual delimiter is "--" followed by 
+                  the boundary string */
+               strcpy(MultipartDelimiter, "--");
+               strlcat(MultipartDelimiter, p1, sizeof(MultipartDelimiter));
+               MultipartDelimiter[sizeof(MultipartDelimiter)-1] = '\0';
+               BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
+           }
+       }
 
-      if (p1 != NULL) {
-       /* The actual delimiter is "--" followed by 
-          the boundary string */
-       strcpy(MultipartDelimiter, "--");
-       strncat(MultipartDelimiter, p1, MAX_DELIM_LEN);
-       BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
-      }
-    }
+       /* 
+        * Check Content-Transfer-Encoding, but
+        * ONLY for non-multipart messages (BodyType == 0).
+        */
+       if ((XferEnc != NULL) && (BodyType == 0)) {
+           if (strcasecmp(XferEnc, "quoted-printable") == 0) {
+               CurrEncodingIsQP = 1;
+               BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
+               if (WantDecode && CurrTypeNeedsDecode) {
+                   SetEncoding8bit(XferEncOfs);
+               }
+           }
+           else if (strcasecmp(XferEnc, "7bit") == 0) {
+               CurrEncodingIsQP = 0;
+               BodyType = (MSG_IS_7BIT);
+           }
+           else if (strcasecmp(XferEnc, "8bit") == 0) {
+               CurrEncodingIsQP = 0;
+               BodyType = (MSG_IS_8BIT);
+           }
+       }
 
-    /* 
-     * Check Content-Transfer-Encoding, but
-     * ONLY for non-multipart messages (BodyType == 0).
-     */
-    if ((XferEnc != NULL) && (BodyType == 0)) {
-      if (strcasecmp(XferEnc, "quoted-printable") == 0) {
-       CurrEncodingIsQP = 1;
-       BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
-       if (WantDecode) {
-           SetEncoding8bit(XferEncOfs);
-        }
-      }
-      else if (strcasecmp(XferEnc, "7bit") == 0) {
-       CurrEncodingIsQP = 0;
-       BodyType = (MSG_IS_7BIT);
-      }
-      else if (strcasecmp(XferEnc, "8bit") == 0) {
-       CurrEncodingIsQP = 0;
-       BodyType = (MSG_IS_8BIT);
-      }
     }
 
-  }
+    xfree(XferEnc);
+    xfree(CntType);
+    xfree(MimeVer);
 
-  return BodyType;
+    return BodyType;
 }
 
 
@@ -467,15 +511,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(char **bufp, flag delimited, flag issoftline)
 {
-  unsigned char *buf = *bufp;
-  unsigned char *p_in, *p_out, *p;
+  char *buf = *bufp;
+  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); ) {
@@ -538,25 +593,38 @@ 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(char **bufp, flag delimited, flag softline)
 {
-  unsigned char *buf = *bufp;
+  char *buf = *bufp;
   int ret = 0;
 
   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;
@@ -565,11 +633,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;
@@ -583,12 +651,13 @@ int UnMimeBodyline(unsigned char **bufp, int collapsedoubledot)
 #include <stdio.h>
 #include <unistd.h>
 
-char *program_name = "unmime";
+const char *program_name = "unmime";
+int outlevel = 0;
 
 #define BUFSIZE_INCREMENT 4096
 
 #ifdef DEBUG
-#define DBG_FWRITE(B,L,BS,FD) fwrite(B, L, BS, FD)
+#define DBG_FWRITE(B,L,BS,FD) do { if (fwrite((B), (L), (BS), (FD))) { } } while(0)
 #else
 #define DBG_FWRITE(B,L,BS,FD)
 #endif
@@ -596,23 +665,28 @@ char *program_name = "unmime";
 int main(int argc, char *argv[])
 {
   unsigned int BufSize;
-  unsigned char *buffer, *buf_p;
+  char *buffer, *buf_p;
   int nl_count, i, bodytype;
 
+  /* quench warnings about unused arguments */
+  (void)argc;
+  (void)argv;
+
 #ifdef DEBUG
   pid_t pid;
   FILE *fd_orig, *fd_conv;
   char fnam[100];
 
+  /* we don't need snprintf here, but for consistency, we'll use it */
   pid = getpid();
-  sprintf(fnam, "/tmp/i_unmime.%x", pid);
+  snprintf(fnam, sizeof(fnam), "/tmp/i_unmime.%lx", (long)pid);
   fd_orig = fopen(fnam, "w");
-  sprintf(fnam, "/tmp/o_unmime.%x", pid);
+  snprintf(fnam, sizeof(fnam), "/tmp/o_unmime.%lx", (long)pid);
   fd_conv = fopen(fnam, "w");
 #endif
 
   BufSize = BUFSIZE_INCREMENT;    /* Initial size of buffer */
-  buf_p = buffer = (unsigned char *) xmalloc(BufSize);
+  buf_p = buffer = (char *) xmalloc(BufSize);
   nl_count = 0;
 
   do {
@@ -631,9 +705,9 @@ int main(int argc, char *argv[])
     }
 
     buf_p++;
-    if ((buf_p - buffer) == BufSize) {
+    if ((unsigned)(buf_p - buffer) == BufSize) {
        /* Buffer is full! Get more room. */
-       buffer = xrealloc(buffer, BufSize+BUFSIZE_INCREMENT);
+       buffer = (char *)xrealloc(buffer, BufSize+BUFSIZE_INCREMENT);
        buf_p = buffer + BufSize;
        BufSize += BUFSIZE_INCREMENT;
     }
@@ -646,8 +720,11 @@ int main(int argc, char *argv[])
   bodytype = MimeBodyType(buffer, 1);
 
   i = strlen(buffer);
-  fwrite(buffer, i, 1, stdout);
   DBG_FWRITE(buffer, i, 1, fd_conv);
+  if (fwrite(buffer, i, 1, stdout) < 1) {
+      perror("fwrite");
+      goto barf;
+  }
   
   do {
      buf_p = (buffer - 1);
@@ -662,15 +739,19 @@ int main(int argc, char *argv[])
      if (buf_p > buffer) {
         if (bodytype & MSG_NEEDS_DECODE) {
            buf_p = buffer;
-           UnMimeBodyline(&buf_p, 0);
+           UnMimeBodyline(&buf_p, 0, 0);
         }
-        fwrite(buffer, (buf_p - buffer), 1, stdout);
         DBG_FWRITE(buffer, (buf_p - buffer), 1, fd_conv);
+        if (fwrite(buffer, (buf_p - buffer), 1, stdout) < 1) {
+           perror("fwrite");
+           goto barf;
+       }
      }
   } while (buf_p > buffer);
 
+barf:
   free(buffer);
-  fflush(stdout);
+  if (EOF == fflush(stdout)) perror("fflush");
 
 #ifdef DEBUG
   fclose(fd_orig);
@@ -680,4 +761,3 @@ int main(int argc, char *argv[])
   return 0;
 }
 #endif
-