unmime.c
上传用户:xxcykj
上传日期:2007-01-04
资源大小:727k
文件大小:19k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. /*
  2.  * MIME mail decoding.
  3.  *
  4.  * This module contains decoding routines for converting
  5.  * quoted-printable data into pure 8-bit data, in MIME
  6.  * formatted messages.
  7.  *
  8.  * By Henrik Storner <storner@image.dk>
  9.  *
  10.  * Configuration file support for fetchmail 4.3.8 by 
  11.  * Frank Damgaard <frda@post3.tele.dk>
  12.  * 
  13.  */
  14. #include "config.h"
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include "fetchmail.h"
  20. static unsigned char unhex(unsigned char c)
  21. {
  22.   if ((c >= '0') && (c <= '9'))
  23.     return (c - '0');
  24.   else if ((c >= 'A') && (c <= 'F'))
  25.     return (c - 'A' + 10);
  26.   else if ((c >= 'a') && (c <= 'f'))
  27.     return (c - 'a' + 10);
  28.   else
  29.     return c;
  30. }
  31. static int qp_char(unsigned char c1, unsigned char c2, unsigned char *c_out)
  32. {
  33.   c1 = unhex(c1);
  34.   c2 = unhex(c2);
  35.   if ((c1 > 15) || (c2 > 15)) 
  36.     return 1;
  37.   else {
  38.     *c_out = 16*c1+c2;
  39.     return 0;
  40.   }
  41. }
  42. /*
  43.  * Routines to decode MIME QP-encoded headers, as per RFC 2047.
  44.  */
  45. /* States of the decoding state machine */
  46. #define S_COPY_PLAIN        0 /* Just copy, but watch for the QP flag */
  47. #define S_SKIP_MIMEINIT     1 /* Get the encoding, and skip header */
  48. #define S_COPY_MIME         2 /* Decode a sequence of coded characters */
  49. static const char MIMEHDR_INIT[]  = "=?"; /* Start of coded sequence */
  50. static const char MIMEHDR_END[]   = "?="; /* End of coded sequence */
  51. void UnMimeHeader(unsigned char *hdr)
  52. {
  53.   /* Decode a buffer containing data encoded according to RFC
  54.    * 2047. This only handles content-transfer-encoding; conversion
  55.    * between character sets is not implemented.  In other words: We
  56.    * assume the charsets used can be displayed by your mail program
  57.    * without problems. 
  58.    */
  59.   /* Note: Decoding is done "in-situ", i.e. without using an
  60.    * additional buffer for temp. storage. This is possible, since the
  61.    * decoded string will always be shorter than the encoded string,
  62.    * due to the en- coding scheme.
  63.    */
  64.   int  state = S_COPY_PLAIN;
  65.   unsigned char *p_in, *p_out, *p;
  66.   unsigned char enc = ''; /* initialization pacifies -Wall */
  67.   int  i;
  68.   /* Speed up in case this is not a MIME-encoded header */
  69.   p = strstr(hdr, MIMEHDR_INIT);
  70.   if (p == NULL)
  71.     return;   /* No MIME header */
  72.   /* Loop through the buffer.
  73.    *  p_in : Next char to be processed.
  74.    *  p_out: Where to put the next processed char
  75.    *  enc  : Encoding used (usually, 'q' = quoted-printable)
  76.    */
  77.   for (p_out = p_in = hdr; (*p_in); ) {
  78.     switch (state) {
  79.     case S_COPY_PLAIN:
  80.       p = strstr(p_in, MIMEHDR_INIT);
  81.       if (p == NULL) {
  82. /* 
  83.  * No more coded data in buffer, 
  84.          * just move remainder into place. 
  85.  */
  86.         i = strlen(p_in);   /* How much left */
  87. memmove(p_out, p_in, i);
  88. p_in += i; p_out += i;
  89.       }
  90.       else {
  91. /* MIME header init found at location p */
  92. if (p > p_in) {
  93.           /* There are some uncoded chars at the beginning. */
  94.           i = (p - p_in);
  95.   memmove(p_out, p_in, i);
  96.   p_out += i;
  97. }
  98. p_in = (p + 2);
  99. state = S_SKIP_MIMEINIT;
  100.       }
  101.       break;
  102.     case S_SKIP_MIMEINIT:
  103.       /* Mime type definition: "charset?encoding?" */
  104.       p = strchr(p_in, '?');
  105.       if (p != NULL) {
  106. /* p_in .. (p-1) holds the charset */
  107. /* *(p+1) is the transfer encoding, *(p+2) must be a '?' */
  108. if (*(p+2) == '?') {
  109.   enc = tolower(*(p+1));
  110.   p_in = p+3;
  111.   state = S_COPY_MIME;
  112. }
  113. else
  114.   state = S_COPY_PLAIN;
  115.       }
  116.       else
  117. state = S_COPY_PLAIN;   /* Invalid data */
  118.       break;
  119.     case S_COPY_MIME:
  120.       p = strstr(p_in, MIMEHDR_END);  /* Find end of coded data */
  121.       if (p == NULL) p = p_in + strlen(p_in);
  122.       for (; (p_in < p); ) {
  123. /* Decode all encoded data */
  124. if (enc == 'q') {
  125.   if (*p_in == '=') {
  126.     /* Decode one char qp-coded at (p_in+1) and (p_in+2) */
  127.     if (qp_char(*(p_in+1), *(p_in+2), p_out) == 0)
  128.       p_in += 3;
  129.     else {
  130.       /* Invalid QP data - pass through unchanged. */
  131.       *p_out = *p_in;
  132.       p_in++;
  133.     }
  134.   }
  135.   else if (*p_in == '_') {
  136.     /* 
  137.              * RFC 2047: '_' inside encoded word represents 0x20.
  138.              * NOT a space - always the value 0x20.
  139.              */
  140.     *p_out = 0x20;
  141.     p_in++;
  142.   }
  143.   else {
  144.     /* Copy unchanged */
  145.     *p_out = *p_in;
  146.     p_in++;
  147.   }
  148.   p_out++;
  149. }
  150. else if (enc == 'b') {
  151.   /* Decode base64 encoded data */
  152.   char delimsave;
  153.   int decoded_count;
  154.   delimsave = *p; *p = 'r';
  155.   decoded_count = from64tobits(p_out, p_in);
  156.   *p = delimsave;
  157.   if (decoded_count > 0) 
  158.     p_out += decoded_count;            
  159.   p_in = p;
  160. }
  161. else {
  162.   /* Copy unchanged */
  163.   *p_out = *p_in;
  164.   p_in++;
  165.   p_out++;
  166. }
  167.       }
  168.       if (*p_in)
  169. p_in += 2;   /* Skip the MIMEHDR_END delimiter */
  170.       /* 
  171.        * We've completed decoding one encoded sequence. But another
  172.        * may follow immediately, in which case whitespace before the
  173.        * new MIMEHDR_INIT delimiter must be discarded.
  174.        * See if that is the case 
  175.        */
  176.       p = strstr(p_in, MIMEHDR_INIT);
  177.       state = S_COPY_PLAIN;
  178.       if (p != NULL) {
  179. /*
  180.  * There is more MIME data later on. Is there
  181.          * whitespace  only before the delimiter? 
  182.  */
  183.         unsigned char *q;
  184.         int  wsp_only = 1;
  185.         for (q=p_in; (wsp_only && (q < p)); q++)
  186.           wsp_only = isspace(*q);
  187.         if (wsp_only) {
  188.   /* 
  189.    * Whitespace-only before the MIME delimiter. OK,
  190.            * just advance p_in to past the new MIMEHDR_INIT,
  191.            * and prepare to process the new MIME charset/encoding
  192.    * header.
  193.    */
  194.   p_in = p + sizeof(MIMEHDR_INIT) - 1;
  195.   state = S_SKIP_MIMEINIT;
  196.         }
  197.       }
  198.       break;
  199.     }
  200.   }
  201.   *p_out = '';
  202. }
  203. /*
  204.  * Routines for decoding body-parts of a message.
  205.  *
  206.  * Since the "fetch" part of fetchmail gets a message body
  207.  * one line at a time, we need to maintain some state variables
  208.  * across multiple invokations of the UnMimeBodyline() routine.
  209.  * The driver routine should call MimeBodyType() when all
  210.  * headers have been received, and then UnMimeBodyline() for
  211.  * every line in the message body.
  212.  *
  213.  */
  214. #define S_BODY_DATA 0
  215. #define S_BODY_HDR  1
  216. /* 
  217.  * Flag indicating if we are currently processing 
  218.  * the headers or the body of a (multipart) message.
  219.  */
  220. static int  BodyState = S_BODY_DATA;
  221. /* 
  222.  * Flag indicating if we are in the process of decoding
  223.  * a quoted-printable body part.
  224.  */
  225. static int  CurrEncodingIsQP = 0;
  226. static int  CurrTypeNeedsDecode = 0;
  227. /* 
  228.  * Delimiter for multipart messages. RFC 2046 states that this must
  229.  * NEVER be longer than 70 characters. Add 3 for the two hyphens
  230.  * at the beginning, and a terminating null.
  231.  */
  232. #define MAX_DELIM_LEN 70
  233. static unsigned char MultipartDelimiter[MAX_DELIM_LEN+3];
  234. /* This string replaces the "Content-Transfer-Encoding: quoted-printable"
  235.  * string in all headers, including those in body-parts. The replacement
  236.  * must be no longer than the original string.
  237.  */
  238. static const char ENC8BIT[] = "Content-Transfer-Encoding: 8bit";
  239. static void SetEncoding8bit(unsigned char *XferEncOfs)
  240. {
  241.   unsigned char *p;
  242.   if (XferEncOfs != NULL) {
  243.      memcpy(XferEncOfs, ENC8BIT, sizeof(ENC8BIT) - 1);
  244.      /* If anything left, in this header, replace with whitespace */
  245.      for (p=XferEncOfs+sizeof(ENC8BIT)-1; (*p >= ' '); p++) *p=' ';
  246.   }
  247. }
  248. static char *GetBoundary(char *CntType)
  249. {
  250.   char *p1, *p2;
  251.   int flag;
  252.   /* Find the "boundary" delimiter. It must be preceded with a ';'
  253.    * and optionally some whitespace.
  254.    */
  255.   p1 = CntType;
  256.   do {
  257.     p2 = strchr(p1, ';'); 
  258.     if (p2)
  259.       for (p2++; isspace(*p2); p2++);
  260.     p1 = p2;
  261.   } while ((p1) && (strncasecmp(p1, "boundary", 8) != 0));
  262.   if (p1 == NULL)
  263.     /* No boundary delimiter */
  264.     return NULL;
  265.   /* Skip "boundary", whitespace and '='; check that we do have a '=' */
  266.   for (p1+=8, flag=0; (isspace(*p1) || (*p1 == '=')); p1++)
  267.     flag |= (*p1 == '=');
  268.   if (!flag)
  269.     return NULL;
  270.   /* Find end of boundary delimiter string */
  271.   if (*p1 == '"') {
  272.     /* The delimiter is inside quotes */
  273.     p1++;
  274.     p2 = strchr(p1, '"');
  275.     if (p2 == NULL)
  276.       return NULL;  /* No closing '"' !?! */
  277.   }
  278.   else {
  279.     /* There might be more text after the "boundary" string. */
  280.     p2 = strchr(p1, ';');  /* Safe - delimiter with ';' must be in quotes */
  281.   }
  282.   /* Zero-terminate the boundary string */
  283.   if (p2 != NULL)
  284.     *p2 = '';
  285.   return (p1 && strlen(p1)) ? p1 : NULL;
  286. }
  287. int CheckContentType(char *CntType)
  288. {
  289.   /*
  290.    * Static array of Content-Type's for which we will do
  291.    * quoted-printable decoding, if requested. 
  292.    * It is probably wise to do this only on known text-only types;
  293.    * be really careful if you change this.
  294.    */
  295.   static char *DecodedTypes[] = {
  296.     "text/",        /* Will match ALL content-type's starting with 'text/' */
  297.     "message/rfc822", 
  298.     NULL
  299.   };
  300.   char *p = CntType;
  301.   int i;
  302.   /* If no Content-Type header, it isn't MIME - don't touch it */
  303.   if (CntType == NULL) return 0;
  304.   /* Skip whitespace, if any */
  305.   for (; isspace(*p); p++) ;
  306.   for (i=0; 
  307.        (DecodedTypes[i] && 
  308. (strncasecmp(p, DecodedTypes[i], strlen(DecodedTypes[i])))); 
  309.        i++) ;
  310.   return (DecodedTypes[i] != NULL);
  311. }
  312. /*
  313.  * This routine does three things:
  314.  * 1) It determines - based on the message headers - whether the
  315.  *    message body is a MIME message that may hold 8 bit data.
  316.  *    - A message that has a "quoted-printable" or "8bit" transfer 
  317.  *      encoding is assumed to contain 8-bit data (when decoded).
  318.  *    - A multipart message is assumed to contain 8-bit data
  319.  *      when decoded (there might be quoted-printable body-parts).
  320.  *    - All other messages are assumed NOT to include 8-bit data.
  321.  * 2) It determines the delimiter-string used in multi-part message
  322.  *    bodies.
  323.  * 3) It sets the initial values of the CurrEncodingIsQP, 
  324.  *    CurrTypeNeedsDecode, and BodyState variables, from the header 
  325.  *    contents.
  326.  *
  327.  * The return value is a bitmask.
  328.  */
  329. int MimeBodyType(unsigned char *hdrs, int WantDecode)
  330. {
  331.   unsigned char *NxtHdr = hdrs;
  332.   unsigned char *XferEnc, *XferEncOfs, *CntType, *MimeVer, *p;
  333.   int  HdrsFound = 0;     /* We only look for three headers */
  334.   int  BodyType;          /* Return value */ 
  335.   /* Setup for a standard (no MIME, no QP, 7-bit US-ASCII) message */
  336.   MultipartDelimiter[0] = '';
  337.   CurrEncodingIsQP = CurrTypeNeedsDecode = 0;
  338.   BodyState = S_BODY_DATA;
  339.   BodyType = 0;
  340.   /* Just in case ... */
  341.   if (hdrs == NULL)
  342.     return BodyType;
  343.   XferEnc = XferEncOfs = CntType = MimeVer = NULL;
  344.   do {
  345.     if (strncasecmp("Content-Transfer-Encoding:", NxtHdr, 26) == 0) {
  346.       XferEncOfs = NxtHdr;
  347.       p = nxtaddr(NxtHdr);
  348.       if (p != NULL) {
  349. xalloca(XferEnc, char *, strlen(p) + 1);
  350. strcpy(XferEnc, p);
  351. HdrsFound++;
  352.       }
  353.     }
  354.     else if (strncasecmp("Content-Type:", NxtHdr, 13) == 0) {
  355.       /*
  356.        * This one is difficult. We cannot use the standard
  357.        * nxtaddr() routine, since the boundary-delimiter is
  358.        * (probably) enclosed in quotes - and thus appears
  359.        * as an rfc822 comment, and nxtaddr() "eats" up any
  360.        * spaces in the delimiter. So, we have to do this
  361.        * by hand.
  362.        */
  363.       /* Skip the "Content-Type:" part and whitespace after it */
  364.       for (NxtHdr += 13; ((*NxtHdr == ' ') || (*NxtHdr == 't')); NxtHdr++);
  365.       /* 
  366.        * Get the full value of the Content-Type header;
  367.        * it might span multiple lines. So search for
  368.        * a newline char, but ignore those that have a
  369.        * have a TAB or space just after the NL (continued
  370.        * lines).
  371.        */
  372.       p = NxtHdr-1;
  373.       do {
  374.         p=strchr((p+1),'n'); 
  375.       } while ( (p != NULL) && ((*(p+1) == 't') || (*(p+1) == ' ')) );
  376.       if (p == NULL) p = NxtHdr + strlen(NxtHdr);
  377.       xalloca(CntType, char *, p-NxtHdr+2);
  378.       strncpy(CntType, NxtHdr, (p-NxtHdr));
  379.       *(CntType+(p-NxtHdr)) = '';
  380.       HdrsFound++;
  381.     }
  382.     else if (strncasecmp("MIME-Version:", NxtHdr, 13) == 0) {
  383.       p = nxtaddr(NxtHdr);
  384.       if (p != NULL) {
  385. xalloca(MimeVer, char *, strlen(p) + 1);
  386. strcpy(MimeVer, p);
  387. HdrsFound++;
  388.       }
  389.     }
  390.     NxtHdr = (strchr(NxtHdr, 'n'));
  391.     if (NxtHdr != NULL) NxtHdr++;
  392.   } while ((NxtHdr != NULL) && (*NxtHdr) && (HdrsFound != 3));
  393.   /* Done looking through the headers, now check what they say */
  394.   if ((MimeVer != NULL) && (strcmp(MimeVer, "1.0") == 0)) {
  395.     CurrTypeNeedsDecode = CheckContentType(CntType);
  396.     /* Check Content-Type to see if this is a multipart message */
  397.     if ( (CntType != NULL) &&
  398.          ((strncasecmp(CntType, "multipart/mixed", 16) == 0) ||
  399.   (strncasecmp(CntType, "message/", 8) == 0)) ) {
  400.       char *p1 = GetBoundary(CntType);
  401.       if (p1 != NULL) {
  402. /* The actual delimiter is "--" followed by 
  403.    the boundary string */
  404. strcpy(MultipartDelimiter, "--");
  405. strncat(MultipartDelimiter, p1, MAX_DELIM_LEN);
  406. BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
  407.       }
  408.     }
  409.     /* 
  410.      * Check Content-Transfer-Encoding, but
  411.      * ONLY for non-multipart messages (BodyType == 0).
  412.      */
  413.     if ((XferEnc != NULL) && (BodyType == 0)) {
  414.       if (strcasecmp(XferEnc, "quoted-printable") == 0) {
  415. CurrEncodingIsQP = 1;
  416. BodyType = (MSG_IS_8BIT | MSG_NEEDS_DECODE);
  417. if (WantDecode && CurrTypeNeedsDecode) {
  418.            SetEncoding8bit(XferEncOfs);
  419.         }
  420.       }
  421.       else if (strcasecmp(XferEnc, "7bit") == 0) {
  422. CurrEncodingIsQP = 0;
  423. BodyType = (MSG_IS_7BIT);
  424.       }
  425.       else if (strcasecmp(XferEnc, "8bit") == 0) {
  426. CurrEncodingIsQP = 0;
  427. BodyType = (MSG_IS_8BIT);
  428.       }
  429.     }
  430.   }
  431.   return BodyType;
  432. }
  433. /*
  434.  * Decode one line of data containing QP data.
  435.  * Return flag set if this line ends with a soft line-break.
  436.  * 'bufp' is modified to point to the end of the output buffer.
  437.  */
  438. static int DoOneQPLine(unsigned char **bufp, flag delimited, flag issoftline)
  439. {
  440.   unsigned char *buf = *bufp;
  441.   unsigned char *p_in, *p_out, *p;
  442.   int n;
  443.   int ret = 0;
  444.   /*
  445.    * Special case: line consists of a single =2E and messages are 
  446.    * dot-terminated.  Line has to be dot-stuffed after decoding.
  447.    */
  448.   if (delimited && !issoftline && buf[0]=='=' && !strncmp(*bufp, "=2Ern", 5))
  449.   {
  450.       strcpy(buf, "..rn");
  451.       *bufp += 5;
  452.       return(FALSE);
  453.   }
  454.   p_in = buf;
  455.   if (delimited && issoftline && (strncmp(buf, "..", 2) == 0))
  456.     p_in++;
  457.   for (p_out = buf; (*p_in); ) {
  458.     p = strchr(p_in, '=');
  459.     if (p == NULL) {
  460.       /* No more QP data, just move remainder into place */
  461.       n = strlen(p_in);
  462.       memmove(p_out, p_in, n);
  463.       p_in += n; p_out += n;
  464.     }
  465.     else {
  466.       if (p > p_in) {
  467. /* There are some uncoded chars at the beginning. */
  468. n = (p - p_in);
  469. memmove(p_out, p_in, n);
  470. p_out += n;
  471.       }
  472.               
  473.       switch (*(p+1)) {
  474.       case '': case 'r': case 'n':
  475. /* Soft line break, skip '=' */
  476. p_in = p+1; 
  477. if (*p_in == 'r') p_in++;
  478. if (*p_in == 'n') p_in++;
  479.         ret = 1;
  480. break;
  481.       default:
  482. /* There is a QP encoded byte */
  483. if (qp_char(*(p+1), *(p+2), p_out) == 0) {
  484.   p_in = p+3;
  485. }
  486. else {
  487.   /* Invalid QP data - pass through unchanged. */
  488.   *p_out = '=';
  489.   p_in = p+1;
  490. }
  491. p_out++;
  492. break;
  493.       }
  494.     }
  495.   }
  496.   *p_out = '';
  497.   *bufp = p_out;
  498.   return ret;
  499. }
  500. /* This is called once per line in the message body.  We need to scan
  501.  * all lines in the message body for the multipart delimiter string,
  502.  * and handle any body-part headers in such messages (these can toggle
  503.  * qp-decoding on and off).
  504.  *
  505.  * Note: Messages that are NOT multipart-messages go through this
  506.  * routine quickly, since BodyState will always be S_BODY_DATA,
  507.  * and MultipartDelimiter is NULL.
  508.  *
  509.  * Return flag set if this line ends with a soft line-break.
  510.  * 'bufp' is modified to point to the end of the output buffer.
  511.  */
  512. int UnMimeBodyline(unsigned char **bufp, flag delimited, flag softline)
  513. {
  514.   unsigned char *buf = *bufp;
  515.   int ret = 0;
  516.   switch (BodyState) {
  517.   case S_BODY_HDR:
  518.     UnMimeHeader(buf);   /* Headers in body-parts can be encoded, too! */
  519.     if ((*buf == '') || (*buf == 'n') || (strcmp(buf, "rn") == 0)) {
  520.       BodyState = S_BODY_DATA;
  521.     } 
  522.     else if (strncasecmp("Content-Transfer-Encoding:", buf, 26) == 0) {
  523.       char *XferEnc;
  524.       XferEnc = nxtaddr(buf);
  525.       if ((XferEnc != NULL) && (strcasecmp(XferEnc, "quoted-printable") == 0)) {
  526. CurrEncodingIsQP = 1;
  527.         /*
  528.  * Hmm ... we cannot be really sure that CurrTypeNeedsDecode
  529.          * has been set - we may not have seen the Content-Type header
  530.          * yet. But *usually* the Content-Type header comes first, so
  531.          * this will work. And there is really no way of doing it 
  532.          * "right" as long as we stick with the line-by-line processing.
  533.  */
  534. if (CurrTypeNeedsDecode)
  535.     SetEncoding8bit(buf);
  536.       }
  537.     }
  538.     else if (strncasecmp("Content-Type:", buf, 13) == 0) {
  539.       CurrTypeNeedsDecode = CheckContentType(nxtaddr(buf));
  540.     }
  541.     *bufp = (buf + strlen(buf));
  542.     break;
  543.   case S_BODY_DATA:
  544.     if ((*MultipartDelimiter) && 
  545. (strncmp(buf, MultipartDelimiter, strlen(MultipartDelimiter)) == 0)) {
  546.       BodyState = S_BODY_HDR;
  547.       CurrEncodingIsQP = CurrTypeNeedsDecode = 0;
  548.     }
  549.     if (CurrEncodingIsQP && CurrTypeNeedsDecode) 
  550.       ret = DoOneQPLine(bufp, delimited, softline);
  551.     else
  552.      *bufp = (buf + strlen(buf));
  553.     break;
  554.   }
  555.   return ret;
  556. }
  557. #ifdef STANDALONE
  558. #include <stdio.h>
  559. #include <unistd.h>
  560. char *program_name = "unmime";
  561. int outlevel = 0;
  562. #define BUFSIZE_INCREMENT 4096
  563. #ifdef DEBUG
  564. #define DBG_FWRITE(B,L,BS,FD) fwrite(B, L, BS, FD)
  565. #else
  566. #define DBG_FWRITE(B,L,BS,FD)
  567. #endif
  568. int main(int argc, char *argv[])
  569. {
  570.   unsigned int BufSize;
  571.   unsigned char *buffer, *buf_p;
  572.   int nl_count, i, bodytype;
  573. #ifdef DEBUG
  574.   pid_t pid;
  575.   FILE *fd_orig, *fd_conv;
  576.   char fnam[100];
  577.   pid = getpid();
  578.   sprintf(fnam, "/tmp/i_unmime.%x", pid);
  579.   fd_orig = fopen(fnam, "w");
  580.   sprintf(fnam, "/tmp/o_unmime.%x", pid);
  581.   fd_conv = fopen(fnam, "w");
  582. #endif
  583.   BufSize = BUFSIZE_INCREMENT;    /* Initial size of buffer */
  584.   buf_p = buffer = (unsigned char *) xmalloc(BufSize);
  585.   nl_count = 0;
  586.   do {
  587.     i = fread(buf_p, 1, 1, stdin);
  588.     switch (*buf_p) {
  589.      case 'n':
  590.        nl_count++;
  591.        break;
  592.      case 'r':
  593.        break;
  594.      default:
  595.        nl_count = 0;
  596.        break;
  597.     }
  598.     buf_p++;
  599.     if ((buf_p - buffer) == BufSize) {
  600.        /* Buffer is full! Get more room. */
  601.        buffer = xrealloc(buffer, BufSize+BUFSIZE_INCREMENT);
  602.        buf_p = buffer + BufSize;
  603.        BufSize += BUFSIZE_INCREMENT;
  604.     }
  605.   } while ((i > 0) && (nl_count < 2));
  606.   *buf_p = '';
  607.   DBG_FWRITE(buffer, strlen(buffer), 1, fd_orig);
  608.   UnMimeHeader(buffer);
  609.   bodytype = MimeBodyType(buffer, 1);
  610.   i = strlen(buffer);
  611.   fwrite(buffer, i, 1, stdout);
  612.   DBG_FWRITE(buffer, i, 1, fd_conv);
  613.   
  614.   do {
  615.      buf_p = (buffer - 1);
  616.      do {
  617.         buf_p++;
  618.         i = fread(buf_p, 1, 1, stdin);
  619.      } while ((i == 1) && (*buf_p != 'n'));
  620.      if (i == 1) buf_p++;
  621.      *buf_p = '';
  622.      DBG_FWRITE(buf, (buf_p - buffer), 1, fd_orig);
  623.      if (buf_p > buffer) {
  624.         if (bodytype & MSG_NEEDS_DECODE) {
  625.            buf_p = buffer;
  626.            UnMimeBodyline(&buf_p, 0);
  627.         }
  628.         fwrite(buffer, (buf_p - buffer), 1, stdout);
  629.         DBG_FWRITE(buffer, (buf_p - buffer), 1, fd_conv);
  630.      }
  631.   } while (buf_p > buffer);
  632.   free(buffer);
  633.   fflush(stdout);
  634. #ifdef DEBUG
  635.   fclose(fd_orig);
  636.   fclose(fd_conv);
  637. #endif
  638.   return 0;
  639. }
  640. #endif