1 
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 
6 #include <ctype.h>
7 #include <errno.h>
8 
9 #include "main.h"
10 #include "struct.h"
11 
12 #define MAXLINE 4096
13 
14 typedef enum {
15   ENCODE_UNSET,  /* not set, act NORMAL (ho ho ho) */
16   ENCODE_NORMAL,
17   ENCODE_QP,     /* quoted printable */
18 
19   ENCODE_MULTILINED, /* this is not a real type, but just a separator showing
20                         that the types below are encoded in a way that makes
21                         one line in the indata may become one or more lines
22                         in the outdata */
23 
24   ENCODE_BASE64, /* base64 */
25   ENCODE_UUENCODE, /* well, it seems there exist some kind of semi-standard
26                       for uu-encoded attachments. */
27 
28   ENCODE_UNKNOWN /* must be the last one */
29 } EncodeType;
30 
31 typedef enum {
32     CONTENT_TEXT,   /* normal mails are text based default */
33     CONTENT_BINARY, /* this kind we store separately and href to */
34     CONTENT_HTML,   /* this is html formated text */
35     CONTENT_IGNORE, /* don't care about this content */
36 
37     CONTENT_UNKNOWN /* must be the last one */
38 } ContentType;
39 
40 
preferedcontent(char * type)41 int preferedcontent(char *type)
42 {
43   if(!strcasecmp(type, "text/plain"))
44     return 1;
45   return 0;
46 }
47 
48 /*
49 ** strcasestr() - case insensitive strstr()
50 */
51 
52 /* FreeBSD defines this function a bit differently, so rename this version */
mail2sms_strcasestr(char * haystack,char * needle)53 char *mail2sms_strcasestr(char *haystack, char *needle)
54 {
55   int nlen = strlen(needle);
56   int hlen = strlen(haystack);
57 
58   int i;
59   int max;
60 
61   max = hlen-nlen;
62 
63   for (i=0; i<=max; i++) {
64     if (!strncasecmp(haystack, needle, nlen))
65       return haystack;
66     haystack++;
67   }
68   return NULL;
69 }
70 
71 /*
72 ** RFC 2047 defines MIME extensions for mail headers.
73 **
74 ** This function decodes that into binary/8bit data.
75 **
76 ** Example:
77 **   =?iso-8859-1?q?I'm_called_?= =?iso-8859-1?q?Daniel?=
78 **
79 ** Should result in "I'm called Daniel", but:
80 **
81 **   =?iso-8859-1?q?I'm_called?= Daniel
82 **
83 ** Should result in "I'm called Daniel" too.
84 **
85 ** Returns the newly allcated string, or the previous if nothing changed
86 */
87 
mdecodeRFC2047(char * string,int length)88 static char *mdecodeRFC2047( char *string, int length )
89 {
90     char *iptr = string;
91     char *oldptr;
92     char *storage=malloc(length+1);
93 
94     char *output = storage;
95 
96     char charset[129];
97     char encoding[33];
98     char blurb[257];
99     char equal;
100     int value;
101 
102     char didanything=FALSE;
103 
104     while (*iptr) {
105       if (!strncmp(iptr, "=?", 2) &&
106 	  (3 == sscanf(iptr+2, "%128[^?]?%32[^?]?%256[^ ]",
107 		       charset, encoding, blurb)) ) {
108 	/* This is a full, valid 'encoded-word'. Decode! */
109 	char *ptr=blurb;
110 
111 	ptr = strstr(blurb, "?=");
112 	if(ptr) {
113 	  *ptr=0;
114 	}
115 	else {
116 	  *output++ = *iptr++;
117 	  /* it wasn't a real encoded-word */
118 	  continue;
119 	}
120 	ptr = blurb;
121 
122 	didanything=TRUE; /* yes, we decode something */
123 
124 	/* we could've done this with a %n in the sscanf, but we know all
125 	   sscanfs don't grok that */
126 
127 	iptr += 2+ strlen(charset) + 1 + strlen(encoding) + 1 + strlen(blurb) + 2;
128 
129 	if (!strcasecmp("q", encoding)) {
130 	  /* quoted printable decoding */
131 
132 	  for ( ; *ptr; ptr++ ) {
133 	    switch ( *ptr ) {
134 	    case '=':
135 	      sscanf( ptr+1, "%02X", &value );
136 	      *output++ = value;
137 	      ptr += 2;
138 	      break;
139 	    case '_':
140 	      *output++ = ' ';
141 	      break;
142                         default:
143                           *output++ = *ptr;
144                           break;
145 	    }
146 	  }
147 	}
148             else if (!strcasecmp("b", encoding)) {
149                 /* base64 decoding */
150                 int length;
151                 base64Decode(ptr, output, &length);
152                 output += length-1;
153             }
154             else {
155                 /* unsupported encoding type */
156                 strcpy(output, "<unknown>");
157                 output += 9;
158             }
159 
160             oldptr = iptr; /* save start position */
161 
162             while (*iptr && isspace(*iptr))
163                   iptr++; /* pass all whitespaces */
164 
165             /* if this is an encoded word here, we should skip the passed
166                whitespaces. If it isn't an encoded-word, we should include the
167                whitespaces in the output. */
168 
169             if (!strncmp(iptr, "=?", 2) &&
170                 (4 == sscanf(iptr+2, "%128[^?]?%32[^?]?%128[^?]?%c",
171                               charset, encoding, blurb, &equal)) &&
172                 ('=' == equal)) {
173                 continue; /* this IS an encoded-word, continue from here */
174             }
175             else
176               /* this IS NOT an encoded-word, move back to the first whitespace */
177               iptr = oldptr;
178         }
179         else
180             *output++ = *iptr++;
181     }
182     *output=0;
183 
184     if (didanything) {
185         /* this check prevents unneccessary strsav() calls if not needed */
186         free(string); /* free old memory */
187 
188 #if 0
189         /* debug display */
190         printf("NEW: %s\n", storage);
191 
192         {
193             unsigned char *f;
194             puts("NEW:");
195             for (f=storage; f<output; f++) {
196                 if (isgraph(*f))
197                    printf("%c", *f);
198                 else
199                    printf("%02X", (unsigned char)*f);
200             }
201             puts("");
202         }
203 #endif
204         return storage; /* return new */
205     }
206     else {
207       free (storage);
208       return string;
209     }
210 }
211 
212 /*
213 ** Decode this [virtual] Quoted-Printable line as defined by RFC2045.
214 ** Written by Daniel.Stenberg@haxx.nu
215 */
216 
mdecodeQP(FILE * file,char * input,char ** result,int * length)217 static void mdecodeQP(FILE *file, char *input, char **result, int *length)
218 {
219     int outcount=0;
220     char *buffer=input;
221     unsigned char inchar;
222     char *output;
223 
224     int len=strlen(input);
225     output=strdup(input);
226 
227     while ((inchar = *input) != '\0') {
228 
229         if (outcount>=len-1) {
230             /* we need to enlarge the destination area! */
231 	  /* double the size each time enlargement is needed */
232             char *newp = realloc(output, len*2);
233             if (newp) {
234                 output = newp;
235                 len *= 2;
236             }
237             else
238                 break;
239         }
240 
241         input++;
242         if ('=' == inchar) {
243             int value;
244             if (('\n'== *input) ||
245                 (('\r' == input[0]) && ('\n' == input[1]))) {
246               if (!fgets(buffer, MAXLINE, file))
247                 break;
248               input = buffer;
249               continue;
250             }
251             else if ('=' == *input) {
252               inchar='=';
253               input++; /* pass this */
254             }
255             else if (isxdigit(*input)) {
256               sscanf(input, "%02X", &value);
257               inchar = (unsigned char)value;
258               input+=2; /* pass the two letters */
259             }
260             else
261             inchar='=';
262         }
263         output[outcount++] = inchar;
264     }
265     output[outcount]=0; /* zero terminate */
266 
267     *result = output;
268     *length = outcount;
269 }
270 
271 /*
272 ** Parsing...
273 ** This loads in the mail from stdin or a file, adding the right
274 ** field variables to the right structures. If readone is set, it will
275 ** think anything it reads in is one article only.
276 */
277 
process(char * mbox,int use_stdin,int readone)278 struct body * process(char *mbox,    /* file name */
279 		      int use_stdin, /* read from stdin */
280 		      int readone)   /* only one mail */
281 {
282   char line[MAXLINE];
283   char *cp, *dp;
284   FILE *fp;
285   int num, isinheader;
286 
287   /* -- variables for the multipart/alternative parser -- */
288   struct body *origbp=NULL;     /* store the original bp */
289   struct body *origlp=NULL;     /* ... and the original lp */
290   char alternativeparser=FALSE; /* set when inside alternative parser mode */
291   /* -- end of alternative parser variables -- */
292 
293   struct body *bp;
294   struct body *lp=NULL; /* the last pointer, points to the last node in the
295                            body list. Initially set to NULL since we have
296                            none at the moment. */
297 
298   struct body *headp=NULL; /* stored pointer to the point where we last
299                               scanned the headers of this mail. */
300 
301   char Mime_B = FALSE;
302   char boundbuffer[128]="";
303 
304   struct boundary *boundp=NULL; /* This variable is used to store a stack
305                                    of boundary separators in cases with mimed
306                                    mails inside mimed mails */
307 
308   char multilinenoend=FALSE; /* This variable is set TRUE if we have read
309                                 a partial line off a multiline-encoded line,
310                                 and the next line we read is supposed to get
311                                 appended to the previous one */
312 
313   int bodyflags=0;           /* This variable is set to extra flags that the
314                                 addbody() calls should OR in the flag parameter */
315 
316   char *binname=NULL;        /* file name to store binary attachments in */
317   int binfile=-1;
318 
319   char *boundary;
320   char type[129]; /* for Content-Type */
321 
322 
323   EncodeType decode=ENCODE_UNSET;
324   ContentType content=CONTENT_TEXT;
325 
326   if (use_stdin || !mbox || !strcasecmp(mbox, "NONE"))
327     fp = stdin;
328   else if ((fp = fopen(mbox, "r")) == NULL) {
329     return -1; /* add error code */
330   }
331 
332   isinheader = 1;
333 
334   bp = NULL;
335 
336   while (fgets(line, MAXLINE, fp) != NULL) {
337 #if 0
338     printf("IN: %s", line);
339 #endif
340     if (isinheader) {
341       /* check for MIME */
342       if (!strncasecmp( line, "MIME-Version:", 13))
343         Mime_B = TRUE;
344       else if (isspace(line[0]) && ('\n' != line[0]) && ('\r' != line[0])) {
345         /*
346         ** since this begins with a whitespace, it means the
347         ** previous line is continued on this line, leave only
348         ** one space character and go!
349         */
350         char *ptr=line;
351         while (isspace(*ptr))
352           ptr++;
353         ptr--; /* leave one space */
354         *ptr=' '; /* make it a true space, no tabs here! */
355 #if 0
356         decodeRFC2047(ptr+1, MAXLINE-(ptr+2-line));
357 #endif
358         bp = addbody(bp, &lp, ptr, BODY_CONTINUE|BODY_HEADER|bodyflags);
359       }
360 
361       else if ((line[0] == '\n') || (line[0] == '\r')) {
362         struct body *head;
363 
364         char savealternative;
365 
366         /*
367         ** we mark this as a header-line, and we use it to
368         ** track end-of-header displays
369         */
370         bp = addbody(bp, &lp, line, BODY_HEADER|bodyflags);
371         isinheader--;
372 
373 #if 0
374         printf("HEADER status: %d\n", isinheader);
375 #endif
376 
377         /*
378         ** This signals us that we are no longer in the header,
379         ** let's fill in all those fields we are interested in.
380         ** Parse the headers up to now and copy to the target
381         ** variables
382         */
383 
384         for (head = bp; head; head=head->next) {
385           if (head->header && !head->demimed) {
386             head->line = mdecodeRFC2047(head->line, strlen(head->line));
387             head->demimed=TRUE; /* don't do this again */
388           }
389         }
390 
391         if (!headp)
392           headp=bp;
393 
394         savealternative = FALSE;
395 
396         for (head = headp; head; head=head->next) {
397           if(head->parsedheader || !head->header)
398             continue;
399 
400           if (!strncasecmp( head->line, "Content-Type:", 13)) {
401             char *ptr=head->line+13;
402 #define DISP_HREF 1
403 #define DISP_IMG  2
404 #define DISP_IGNORE 3
405             /* default is href to the attachment: */
406             char disposition=DISP_HREF;
407 
408             /* we must make sure this is not parsed more times
409                than this */
410             head->parsedheader= TRUE;
411 
412             while (isspace(*ptr))
413               ptr++;
414 
415             sscanf(ptr, "%128[^;]", type);
416             if ((cp = strchr(type, '\r')) != NULL)
417               *cp = '\0'; /* rm CR */
418             if ((cp = strchr(type, '\n')) != NULL)
419               *cp = '\0'; /* rm LF */
420 
421             if(alternativeparser) {
422               /* We are parsing alternatives... */
423 
424               if(preferedcontent(type) ) {
425                 /* ... this is a prefered type, we want to store
426                    this [instead of the earlier one]. */
427 #if 0
428                 struct body *next;
429                 printf("%s is more fun than the previous one\n",
430                        type);
431 #endif
432 #if 0
433                 /*
434                 ** Not sure why this free section is here.
435                 ** It is causing purify to barf with massive numbers of
436                 ** "FMR: Free memory reads". When I commented it out it
437                 ** cleared up the problem with no associated memory leaked
438                 ** or difference in output. It's history for now.
439                 */
440                 while(bp) {
441                   next=bp->next;
442                   if (bp->line) free(bp->line);
443                   if (bp) free(bp);
444                   bp=next;
445                 }
446 #endif
447                 headp = NULL;
448               }
449               else {
450                 /* ...and this type is not a prefered one. Thus, we
451                  * shall ignore it completely! */
452                 disposition = DISP_IGNORE;
453 #if 0
454                 printf("%s is to be ignored\n", type);
455 #endif
456               }
457             }
458             if (!strcasecmp(type, "text/plain") ||
459                 (!alternativeparser &&
460                  !strcasecmp(type, "text/html")) ) {
461               /*
462                * text or inlined html follows
463                */
464               /* default is just plain 7/8 bit */
465               if(ENCODE_UNSET == decode)
466                 decode = ENCODE_NORMAL;
467 
468               if (!strcasecmp(type, "text/html"))
469                 content = CONTENT_HTML;
470               else
471                 content = CONTENT_TEXT;
472               continue;
473             }
474             else if (!strncasecmp(type, "message/rfc822", 14)) {
475               /*
476               ** Here comes an attached mail! This can be ugly,
477               ** since the attached mail may very well itself
478               ** contain attached binaries, or why not another
479               ** attached mail? :-)
480               **
481               ** We need to store the current boundary separator
482               ** in order to get it back when we're done parsing
483               ** this particular mail, since each attached mail
484               ** will have its own boundary separator that *might*
485               ** be used.
486               */
487 #if 0
488               /* removed 2001-02-07, this is old leftovers from when I
489                * picked this code out of hypermail */
490               bp = addbody(bp, &lp,
491                            "<P><STRONG>attached mail follows:</STRONG><HR>",
492                            BODY_HTMLIZED | bodyflags);
493 #endif
494               bodyflags |= BODY_ATTACHED;
495               isinheader = 2;
496               continue;
497             }
498             else if (strncasecmp(type, "multipart/", 10)) {
499               /*
500               ** This is not a multipart and not text
501               */
502               struct body *fnamep=NULL;
503               char acomment[256];
504               char attachname[129]; /* listed attachment name */
505               char checkpath[256];  /* uniqueness path */
506               char *fname = NULL;    /* attachment filename */
507               char nameisuniq=FALSE; /* use the name included ?*/
508               char *file = NULL;
509 
510               fname = strstr(ptr, "name=");
511               if (NULL == fname) {
512                 /*
513                 ** Name of file not specified in the
514                 ** Content-Type header.  See if the
515                 ** Content-Disposition header exists and
516                 ** contains the info.
517                 */
518 
519                 for (fnamep = head;fnamep;fnamep=fnamep->next) {
520                   if(!fnamep->header)
521                     continue;
522                   if (!strncasecmp(fnamep->line,"Content-Disposition:", 20)) {
523                     if ((fname = strstr(fnamep->line, "filename=")) != NULL) {
524                       sscanf(fname+10, "%128[^\"]",attachname);
525                       fname = attachname;
526                     }
527                   }
528                 }
529               }
530               else {
531                 sscanf(fname+6, "%128[^\"]", attachname);
532                 fname = attachname;
533               }
534 #if 0
535               sprintf(line, "** %s\n", fname?fname:"attachment");
536               bp = addbody(bp,&lp,line,BODY_HTMLIZED|bodyflags);
537 #endif
538               /* don't save this */
539               disposition = DISP_IGNORE;
540               content = CONTENT_IGNORE;
541 
542               continue;
543             }
544             else {
545               /*
546               ** Find the first boundary separator
547               */
548 
549               boundary=strcasestr(ptr, "boundary=");
550 
551               if (boundary) {
552                 boundary=strchr(boundary, '=');
553                 if (boundary) {
554                   boundary++;
555                   while (isspace(*boundary))
556                     boundary++;
557                   if ('\"' == *boundary) {
558                     sscanf(++boundary, "%[^\"]", boundbuffer);
559                   }
560                   else
561                     sscanf(boundary, "%s", boundbuffer);
562                   boundary = boundbuffer;
563                 }
564 
565                 while (fgets(line, MAXLINE, fp)) {
566                   if (!strncmp(line, "--", 2) &&
567                       !strncmp(line+2, boundbuffer,
568                                strlen(boundbuffer))) {
569                     break;
570                   }
571                 }
572 
573                 /*
574                 ** This stores the boundary string in a stack
575                 ** of strings:
576                 */
577                 boundp = bound(boundp, boundbuffer);
578 
579                 /* printf("set new boundary: %s\n", boundp->line); */
580 
581                 /*
582                 ** We set ourselves, "back in header" since there is
583                 ** gonna come MIME headers now after the separator
584                 */
585                 isinheader = 1;
586 
587                 /* Daniel Stenberg started adding the
588                  * "multipart/alternative" parser 13th of July
589                  * 1998!  We check if this is a 'multipart/
590                  * alternative' header, in which case we need to
591                  * treat it very special.
592                  */
593 
594                 if(!strncasecmp(&ptr[10], "alternative", 11)) {
595                   /* It *is* an alternative session!  Alternative
596                   ** means there will be X parts with the same text
597                   ** using different content-types. We are supposed
598                   ** to take the most prefered format of the ones
599                   ** used and only output that one. MIME defines
600                   ** the order of the texts to start with pure text
601                   ** and then continue with more and more obscure
602                   ** formats. (well, it doesn't use those terms but
603                   ** that's what it means! ;-))
604                   */
605 
606                   /* How "we" are gonna deal with them:
607                   **
608                   ** We create a "spare" linked list body for the
609                   ** very first part. Since the first part is
610                   ** defined to be the most readable, we save that
611                   ** in case no content-type present is prefered!
612                   **
613                   ** We skip all parts that are not prefered. All
614                   ** prefered parts found will replace the first
615                   ** one that is saved. When we reach the end of
616                   ** the alternatives, we will use the last saved
617                   ** one as prefered.
618                   */
619 
620                   savealternative = TRUE;
621 #if 0
622                   printf("SAVEALTERNATIVE: yes\n");
623 #endif
624 
625                 }
626 
627               }
628               else
629                 boundary = NULL;
630             }
631           }
632           else if (!strncasecmp(head->line, "Content-Transfer-Encoding:", 26)) {
633             char *ptr=head->line+26;
634 
635             head->parsedheader= TRUE;
636             while (isspace(*ptr))
637               ptr++;
638             if (!strncasecmp(ptr, "QUOTED-PRINTABLE", 16)) {
639               decode = ENCODE_QP;
640             }
641             else if (!strncasecmp(ptr, "BASE64", 6)) {
642               decode = ENCODE_BASE64;
643             }
644             else if (!strncasecmp(ptr, "8BIT", 4)) {
645               decode = ENCODE_NORMAL;
646             }
647             else if (!strncasecmp(ptr, "7BIT", 4)) {
648               decode = ENCODE_NORMAL;
649             }
650             else if (!strncasecmp(ptr, "x-uue", 5)) {
651               decode = ENCODE_UUENCODE;
652 
653               if (uudecode(fp, line, line, NULL, TRUE))
654                 /*
655                 ** oh gee, we failed this is chaos */
656                 break;
657             }
658             else {
659               /* this is an unknown format, we use default decoding */
660               char code[64];
661 
662               sscanf(ptr, "%63s", code);
663               sprintf(line, " ('%s')\n", code);
664 
665               bp = addbody(bp, &lp, line, BODY_HTMLIZED|bodyflags);
666             }
667 #if 0
668             printf("DECODE set to %d\n", decode);
669 #endif
670           }
671         }
672         if (savealternative) {
673           /* let's remember 'bp' and 'lp' */
674 
675           origbp=bp;
676           origlp=lp;
677 
678           alternativeparser = TRUE;
679 
680           /* restart on a new list: */
681           lp=bp=NULL;
682         }
683         headp = lp; /* start at this point next time */
684       }
685       else {
686 #if 0
687         decodeRFC2047(line, MAXLINE);
688 #endif
689         bp = addbody(bp, &lp, line, BODY_HEADER|bodyflags);
690       }
691     }
692     else {
693       /* decode MIME complient gibberish */
694       char newbuffer[MAXLINE];
695       char *data;
696       int datalen=-1; /* -1 means use strlen to get length */
697 
698       if (Mime_B) {
699         if (boundp &&
700             !strncmp(line, "--", 2) &&
701             !strncmp(line+2, boundp->line, strlen(boundp->line))) {
702           /* right at this point, we have another part coming up */
703           isinheader = 1; /* back on a kind-of-header */
704 
705 #if 0
706           printf("hit %s\n", line);
707 #endif
708           if (!strncmp(line+2+strlen(boundp->line), "--", 2)) {
709             bp = addbody(bp,&lp,"\n",BODY_HTMLIZED|bodyflags);
710 
711 
712             isinheader = 0; /* no header, the ending boundary
713                                can't have any describing
714                                headers */
715 
716 #if 0
717             printf("End boundary %s\n", line);
718 #endif
719             boundp = bound(boundp, NULL);
720             if (!boundp) {
721               bodyflags &= ~BODY_ATTACHED;
722             }
723             if(alternativeparser) {
724               struct body *next;
725               /* we no longer have alternatives */
726               alternativeparser = FALSE;
727 #if 0
728               printf("We DUMP an old alternative\n");
729 #endif
730               while(bp) {
731                 origbp = addbody(origbp, &origlp,
732                                  bp->line,
733                                  (bp->header?BODY_HEADER:0)|
734                                  (bp->html?BODY_HTMLIZED:0)|
735                                  (bp->attached?BODY_ATTACHED:0)
736                                  );
737                 next= bp->next;
738                 free(bp->line);
739                 free(bp);
740                 bp=next;
741               }
742               bp = origbp;
743               lp = origlp;
744 
745               headp= NULL;
746             }
747 #if 0
748             if (boundp)
749               printf("back %s\n", boundp->line);
750             else
751               printf("back to NONE\n");
752 #endif
753           }
754 
755           if (-1 != binfile) {
756             close(binfile);
757             binfile=-1;
758           }
759           continue;
760         }
761       }
762 
763       switch ( decode ) {
764       case ENCODE_QP:
765         mdecodeQP(fp, line, &data, &datalen);
766         break;
767       case ENCODE_BASE64:
768         base64Decode(line, newbuffer, &datalen);
769         data = newbuffer;
770         break;
771       case ENCODE_UUENCODE:
772         uudecode(NULL, line, newbuffer, &datalen, FALSE);
773         data = newbuffer;
774         break;
775       case ENCODE_NORMAL:
776       case ENCODE_UNSET:
777         data = line;
778         break;
779       default:
780         /* we have no clue! */
781         data = NULL;
782         break;
783       }
784 #if 0
785       printf("LINE %s\n", data);
786 #endif
787       if (data) {
788         if ((content == CONTENT_TEXT) || (content==CONTENT_HTML)) {
789           if (decode > ENCODE_MULTILINED) {
790             /*
791             ** This can be more than one resulting line,
792             ** as the decoded the string may look like:
793             "#!/bin/sh\r\n\r\nhelp() {\r\n echo 'Usage: difftree"
794             */
795             char *p=data;
796             char *n;
797             char store;
798 
799 #if 0
800             printf("decode type %d\n", decode);
801 #endif
802 
803             while ((n = strchr(p, '\n'))) {
804               store = n[1];
805               n[1]=0;
806 #if 0
807               printf("UNFOLDED %s", p);
808 #endif
809               bp = addbody(bp, &lp, p,
810                            (content==CONTENT_HTML?
811                             BODY_HTMLIZED:0)|
812                            (multilinenoend?BODY_CONTINUE:0)|
813                            bodyflags);
814               multilinenoend = FALSE; /* full line pushed */                                 n[1]=store;
815               p = n+1;
816             }
817             if (strlen(p)) {
818                                 /*
819                                 ** This line doesn't really end here,
820                                 ** we will get another line soon that
821                                 ** should get appended!
822                                 */
823 #if 0
824               printf("CONTINUE %s\n", p);
825 #endif
826               bp = addbody(bp, &lp, p,
827                            (content==CONTENT_HTML?
828                             BODY_HTMLIZED:0)|
829                            (multilinenoend?BODY_CONTINUE:0)|
830                            bodyflags);
831 
832                                 /*
833                                 ** We want the next line to get appended to this!
834                                 */
835               multilinenoend = TRUE;
836             }
837           }
838           else {
839             bp = addbody(bp, &lp, data,
840                          (content==CONTENT_HTML?
841                           BODY_HTMLIZED:0) | bodyflags );
842           }
843 #if 0
844           printf("ALIVE?\n");
845 #endif
846         }
847         else if (content == CONTENT_BINARY) {
848           if (-1 != binfile) {
849             if (datalen < 0)
850               datalen = strlen(data);
851 
852             /*fwrite(data, datalen, 1, binfile); */
853             write(binfile, data, datalen);
854             /*bp = addbody(bp, "file contents");*/
855           }
856         }
857 
858         if (ENCODE_QP == decode)
859           free(data); /* this was allocatd by mdecodeQP() */
860       }
861     }
862   }
863 
864   if (!isinheader || readone) {
865     while (rmlastlines(bp));
866     num++;
867   }
868 
869   fclose(fp);
870 
871   /* can we clean up a bit please... */
872 
873   if (boundp != NULL) {
874     if (boundp->line)
875       free(boundp->line);
876     free(boundp);
877   }
878 
879 
880   return bp;
881 }
882 
883