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