1
2 extern "C" {
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <ctype.h>
8 #include <unistd.h>
9 #include <strings.h>
10 #include <errno.h>
11 #include <limits.h>
12 #include <iconv.h>
13 }
14
15 #include "mime.h"
16 #include "mimetype.h"
17 #include "my_regex.h"
18 #include "tempfile.h"
19 #include "base64.h"
20
21 #define SILLY_SUN_STUFF /* Handle Sun's attachment formats */
22
23 /* MIME default values */
24 #define DEFAULT_VERSION "1.0"
25 #define DEFAULT_CONTENT "text/plain"
26 #define DEFAULT_ENCODING "7bit"
27 #define DEFAULT_CHARSET "UTF-8"
28
29 /* Strip HTML from a body of text */
30 static int StripHTML(const char *filename, char **outfile);
31
32 /* Check if a line is a MIME boundary */
33 static int IsBoundary(char *line, char *boundary, int boundlen);
34
35 /* List of MIME encodings and stream handlers */
36 struct encoding_handler {
37 char *type; /* Case insensitive */
38 int (*encode)(FILE *input, IObottle *output);
39 int (*decode)(IObottle *, iconv_t, char **, int *, char **);
40 };
41
42 int No_Encode(FILE *input, IObottle *output);
43 int QP_Encode(FILE *input, IObottle *output);
44 int Base64_Encode(FILE *input, IObottle *output);
45
46 int No_Decode(IObottle *body,iconv_t iconv_ctx,char *endstrings[],int endlens[], char **outfile);
47 int QP_Decode(IObottle *body,iconv_t iconv_ctx,char *endstrings[],int endlens[], char **outfile);
48 int Base64_Decode(IObottle *body, iconv_t iconv_ctx,char *endstrings[], int endlens[], char **outfile);
49
50 /* List of MIME encodings and handlers for them */
51 static struct encoding_handler encodings[] =
52 {
53 { "7bit", No_Encode, No_Decode },
54 { "8bit", No_Encode, No_Decode },
55 { "binary", No_Encode, No_Decode },
56 { "quoted-printable", QP_Encode, QP_Decode },
57 { "base64", Base64_Encode, Base64_Decode },
58 { NULL, No_Encode, No_Decode }
59 };
60
GetContentAttribute(const char * content,const char * attribute,char * output,unsigned int length)61 static bool GetContentAttribute(const char *content, const char *attribute, char *output, unsigned int length)
62 {
63 const char *ptr;
64 unsigned int i;
65 int matchlen = strlen(attribute);
66
67 ptr = content;
68 while ( (ptr=strchr(ptr, ';')) != NULL ) {
69 ++ptr;
70 while ( isspace(*ptr) ) ++ptr;
71 if ( strncasecmp(ptr, attribute, matchlen) == 0 )
72 break;
73 }
74 if ( ptr == NULL ) {
75 return false;
76 }
77
78 ptr += matchlen;
79 for ( i = 0; i < length-1; ++i ) {
80 if ( *ptr == ';' || *ptr == ' ' || *ptr == '\0' ) {
81 break;
82 }
83 output[i] = *ptr++;
84 }
85 output[i] = '\0';
86
87 return true;
88 }
89
90 /* Converts text between character sets */
ConvertCharset(iconv_t iconv_ctx,char * ibuf,size_t ibytesleft,char * obuf,size_t obytesleft)91 static size_t ConvertCharset(iconv_t iconv_ctx,
92 char *ibuf, size_t ibytesleft,
93 char *obuf, size_t obytesleft)
94 {
95 static char temp[BUFSIZ*2];
96 static size_t templeft;
97
98 /* Should we reset our conversion state? */
99 if ( ibuf == NULL ) {
100 templeft = 0;
101 return 0;
102 }
103
104 /* Use any data left over from the pervious pass */
105 if ( templeft ) {
106 memcpy(&temp[templeft], ibuf, ibytesleft);
107 ibuf = temp;
108 ibytesleft += templeft;
109 templeft = 0;
110 }
111 size_t available = obytesleft;
112 while ( ibytesleft > 0 ) {
113 iconv(iconv_ctx, &ibuf, &ibytesleft, &obuf, &obytesleft);
114
115 if ( ibytesleft > 0 ) {
116 if ( errno == EILSEQ ) {
117 /* Stuff this one and keep trying */
118 *obuf = '?';
119 ++ibuf;
120 --ibytesleft;
121 ++obuf;
122 --obytesleft;
123 iconv(iconv_ctx, NULL, NULL, NULL, NULL);
124 continue;
125 }
126 memcpy(temp, ibuf, ibytesleft);
127 templeft = ibytesleft;
128 break;
129 }
130 }
131 return(available-obytesleft);
132 }
133
134 /* Decodes lines based on RFC 2047 */
DecodeAtom(const char * & atom,char * & output,const char * native_charset)135 static void DecodeAtom(const char *&atom, char *&output, const char *native_charset)
136 {
137 char charset[80];
138 char encoding;
139 char *spot;
140 iconv_t iconv_ctx = (iconv_t)-1;
141
142 /* Skip =? */
143 atom += 2;
144
145 /* Copy character set */
146 spot = charset;
147 while ( *atom && *atom != '?' ) {
148 *spot++ = *atom++;
149 }
150 if ( *atom ) {
151 ++atom;
152 }
153 *spot = '\0';
154
155 /* Encoding: 'Q' = quoted-printable, 'B' = base64 */
156 encoding = *atom;
157 while ( *atom && *atom != '?' ) {
158 ++atom;
159 }
160 if ( *atom ) {
161 ++atom;
162 }
163
164 /* Go, and translate */
165 spot = output;
166 if ( encoding == 'Q' || encoding == 'q' ) {
167 while ( *atom && (*atom != '?') ) {
168 if ( *atom == '_' ) {
169 *spot = ' ';
170 ++spot;
171 ++atom;
172 } else if ( *atom == '=' ) {
173 /* Make sure no buffer overflow */
174 ++atom;
175 if ( ! *atom || ! *(atom+1) )
176 continue;
177
178 /* Convert from hexidecimal */
179 *spot = '\0';
180 if ( isdigit(*atom) )
181 *spot |= (*atom-'0');
182 else
183 *spot |= (toupper(*atom)-'A'+10);
184 ++atom;
185 *spot <<= 4;
186 if ( isdigit(*atom) )
187 *spot |= (*atom-'0');
188 else
189 *spot |= (toupper(*atom)-'A'+10);
190 ++spot;
191 ++atom;
192 } else
193 *(spot++) = *(atom++);
194 }
195 } else if ( encoding == 'B' || encoding == 'b' ) {
196 unsigned int olen;
197 const char *from = atom;
198 while ( *atom && (*atom != '?') ) {
199 ++atom;
200 }
201 olen = base64_decode(from, (atom - from), spot, ~0);
202 spot += olen;
203 } else {
204 while ( *atom && (*atom != '?') ) {
205 *(spot++) = *(atom++);
206 }
207 }
208 /* Skip ?= */
209 if ( *atom )
210 ++atom;
211 if ( *atom == '=' )
212 ++atom;
213
214 if ( strcasecmp(charset, native_charset) != 0 ) {
215 iconv_ctx = iconv_open(native_charset, charset);
216 }
217 if ( iconv_ctx != (iconv_t)-1 ) {
218 char temp[BUFSIZ];
219 size_t length = ConvertCharset(iconv_ctx,
220 output, (spot - output),
221 temp, sizeof(temp));
222 memcpy(output, temp, length);
223 spot = (output+length);
224 iconv_close(iconv_ctx);
225 }
226 output = spot;
227 }
228 char *
DecodeLine(const char * line)229 MIME_body::DecodeLine(const char *line)
230 {
231 char *data, *ptr;
232 data = new char[strlen(line)+1];
233
234 ptr = data;
235 while ( *line ) {
236 if ( line[0] == '=' && line[1] == '?' ) {
237 do {
238 DecodeAtom(line, ptr, mime_charset);
239
240 /* Look ahead to see if there is another atom */
241 const char *spot;
242 for ( spot = line; isspace(*spot); ++spot )
243 ;
244 if ( spot[0] == '=' && spot[1] == '?' )
245 line = spot;
246
247 } while ( line[0] == '=' && line[1] == '?' );
248
249 continue;
250 }
251 *ptr++ = *line++;
252 }
253 *ptr = '\0';
254
255 return(data);
256 }
257
258 /* Allow exclusion of particular MIME types (this parser sucks..) */
259 char *MIME_body::mime_charset = DEFAULT_CHARSET;
260 void
MIME_Charset(char * charset)261 MIME_body::MIME_Charset(char *charset)
262 {
263 if ( charset ) {
264 mime_charset = charset;
265 } else {
266 mime_charset = DEFAULT_CHARSET;
267 }
268 }
269
270 /* Allow exclusion of particular MIME types (this parser sucks..) */
271 char **MIME_body::mime_ignore = NULL;
272 void
MIME_Ignore(char * ignorelist)273 MIME_body::MIME_Ignore(char *ignorelist)
274 {
275 char *ptr, *word;
276 int i, nwords;
277
278 /* Clean out existing list */
279 if ( mime_ignore ) {
280 for ( i=0; mime_ignore[i]; ++i )
281 delete[] mime_ignore[i];
282 delete[] mime_ignore;
283 }
284 mime_ignore = NULL;
285
286 /* Find out how long the new list is */
287 nwords = 0;
288 for ( ptr=ignorelist; ptr; ) {
289 while ( *ptr && isspace(*ptr) ) ++ptr;
290 if ( ! *ptr )
291 break;
292 while ( *ptr && !isspace(*ptr) ) ++ptr;
293 ++nwords;
294 }
295 /* We're done if we have an empty list */
296 if ( nwords == 0 )
297 return;
298
299 mime_ignore = new char *[nwords+1];
300 for ( i=0, ptr=ignorelist; i<nwords; ++i ) {
301 int wordlen;
302 while ( *ptr && isspace(*ptr) ) ++ptr;
303 word = ptr;
304 while ( *ptr && !isspace(*ptr) ) ++ptr;
305 wordlen = (ptr-word);
306 mime_ignore[i] = new char[wordlen+1];
307 memcpy(mime_ignore[i], word, wordlen);
308 if ( mime_ignore[i][wordlen-1] == ';' ) {
309 mime_ignore[i][wordlen-1] = 0;
310 } else {
311 mime_ignore[i][wordlen] = 0;
312 }
313 }
314 mime_ignore[i] = NULL;
315 }
316
317
318 /* The MIME_body class constructor */
MIME_body(IObottle * RawFile,char * endings[],MIME_body * lineage)319 MIME_body:: MIME_body(IObottle *RawFile, char *endings[],
320 MIME_body *lineage)
321 {
322 MD5_CTX md5_ctx;
323 int i;
324 char *ptr;
325 char line[BUFSIZ];
326
327 /* Get the file. :) */
328 rawfile = RawFile;
329 parent = lineage;
330 decoded = NULL;
331 bodyref = 0;
332 hstart = rawfile->tellg();
333
334 /* Copy the boundary string pointers */
335 for ( numends = 0; endings[numends]; ++numends );
336 endlens = new int[numends];
337 endstrings = new char *[numends+1];
338 for ( i = 0; endings[i]; ++i ) {
339 endstrings[i] = endings[i];
340 endlens[i] = strlen(endings[i]);
341 }
342 endstrings[i] = NULL;
343
344 /* Read in the header until the first newline */
345 char lastfield[1024], *lastdata=NULL;
346 do {
347 /* Note, we don't check for boundary lines here.
348 It's mostly because mail messages will have a boundary
349 line as the first line, while MIME messages will not.
350 If I don't check for boundaries in the header, I avoid
351 the problem, but make it possible to mis-read a header
352 that isn't followed by a newline. (a problem?)
353 */
354 rawfile->readline(line, sizeof(line));
355
356 /* This allows us to put comments in a "header" :-) */
357 if ( line[0] == '#' )
358 continue;
359
360 /* If the header line starts with whitespace, it continues
361 the last field.
362 */
363 if ( isspace(line[0]) && lastdata ) {
364 /* Remove the last field */
365 fieldtab.Remove(lastfield);
366
367 /* Create new appended field data */
368 for ( ptr=line; isspace(*ptr); ++ptr );
369 char *data = new char
370 [strlen(lastdata)+1+strlen(ptr)+1];
371 sprintf(data, "%s %s", lastdata, ptr);
372 delete[] lastdata;
373
374 /* Insert it into the hash list */
375 fieldtab.Add(lastfield, data);
376 lastdata = data;
377 } else {
378 /* If the header line contains ": " it is hashable */
379 lastdata = NULL;
380 if ( ((ptr=strstr(line, ":")) != NULL)
381 && isspace(*(ptr+1)) ) {
382 char *data;
383 *ptr = '\0';
384 if ( GetField(line) == NULL ) {
385 for ( ptr += 2; isspace(*ptr); ++ptr );
386 /* Translate inline QP characters */
387 data = DecodeLine(ptr);
388 fieldtab.Add(line, data);
389 strcpy(lastfield, line);
390 lastdata = data;
391 }
392 }
393 }
394 } while ( ! rawfile->eof() && (line[0] != '\0') );
395
396 /* We should check MIME version, but... :-)
397 We're an incomplete implementation anyway, and future versions
398 of MIME will probably be backwards compatible.
399 */
400 if ( (version=GetField("Mime-Version")) == NULL )
401 version = DEFAULT_VERSION;
402
403 /* Set the content */
404 if ( (content=GetField("Content-Type")) != NULL ) {
405 #ifdef SILLY_SUN_STUFF
406 /* Handle special attachment types */
407 if ( strncasecmp(content, "X-sun-attachment",
408 strlen("X-sun-attachment")) == 0 ) {
409 content = "multipart/mixed; boundary=\"--------\"";
410 }
411 #endif /* SILLY_SUN_STUFF */
412 } else
413 content = DEFAULT_CONTENT;
414
415 /* Set the encoding (support MIME and Sun protocol) */
416 if ( (encoding=GetField("Content-Transfer-Encoding")) == NULL ) {
417 #ifdef SILLY_SUN_STUFF
418 /* Handle special attachment types */
419 if ( (encoding=GetField("X-Sun-Encoding-Info")) != NULL ) {
420 if ( strcasecmp(encoding, "base64") == 0 ) {
421 /* Set an application content type */
422 content = "application/X-sun-unknown";
423 }
424 } else
425 #endif /* SILLY_SUN_STUFF */
426 encoding = DEFAULT_ENCODING;
427 }
428
429 /* Try to get a name for this body */
430 if ((ptr=(char *)match_nocase((char *)content, "Name=", &i)) != NULL) {
431 /* Pull out an unquoted name */
432 ptr += i;
433 GetWord(ptr, &name);
434 } else
435 if ( (ptr=(char *)match_nocase((char *)GetField("Content-Disposition"),
436 "FileName=", &i)) != NULL ) {
437 /* Pull out an unquoted name */
438 ptr += i;
439 GetWord(ptr, &name);
440 } else
441 #ifdef SILLY_SUN_STUFF
442 if ( (ptr=(char *)GetField("X-Sun-Data-Type")) != NULL ) {
443 name = new char[strlen(ptr)+1];
444 strcpy(name, ptr);
445 } else
446 #endif /* SILLY_SUN_STUFF */
447 name = NULL;
448
449 /* Suck up the body, handling multipart messages */
450 bstart = rawfile->tellg();
451 MD5Init(&md5_ctx);
452 multipart = NULL;
453 if ( strncasecmp(content, "multipart", strlen("multipart")) == 0 ) {
454 (void) ParseMulti(&md5_ctx);
455
456 /* For now, delete complex parts we can't view */
457 if ( multipart && (strncasecmp(content, "multipart/alternative",
458 strlen("multipart/alternative")) == 0)){
459 MIME_body **dataptr;
460 multipart->InitIterator();
461 multipart->Iterate(); /* Keep simple part */
462 while ( (dataptr=multipart->Iterate()) ) {
463 MIME_body *oldpart = *dataptr;
464 multipart->Remove(dataptr);
465 delete oldpart; /* Delete other part */
466 }
467 }
468
469 /* Delete any MIME parts that we have been told to ignore */
470 if ( multipart && mime_ignore ) {
471 MIME_body **dataptr;
472 multipart->InitIterator();
473 while ( (dataptr=multipart->Iterate()) ) {
474 for ( i=0; mime_ignore[i]; ++i ) {
475 if ( strncasecmp((*dataptr)->Content(), mime_ignore[i], strlen(mime_ignore[i])) == 0 ) {
476 MIME_body *oldpart = *dataptr;
477 multipart->Remove(dataptr);
478 delete oldpart;
479 break;
480 }
481 }
482 }
483 }
484
485 /* Multipart bodies that have only one subpart are, in effect,
486 proxies for us, and have our parents.
487 */
488 if ( multipart && (multipart->Size() == 1) ) {
489 MIME_body **dataptr;
490
491 multipart->InitIterator();
492 dataptr = multipart->Iterate();
493 (*dataptr)->parent = parent;
494 }
495 } else {
496 /* Move the file pointer to the end of this body */
497 for ( ; ; ) {
498 int n, len;
499 long here;
500
501 /* Read in the next line, checking for boundary */
502 here = rawfile->tellg();
503 len = rawfile->readline(line, sizeof(line));
504 if ( rawfile->eof() )
505 break;
506 for ( n=0; endstrings[n]; ++n ) {
507 /* If we have a match, break out */
508 if (IsBoundary(line, endstrings[n], endlens[n]))
509 break;
510 }
511 if ( endstrings[n] ) {
512 rawfile->seekg(here);
513 break;
514 }
515 MD5Update(&md5_ctx, (unsigned char *)line, len);
516 }
517 }
518 MD5Final(md5sum, &md5_ctx);
519
520 /* Tidy up */
521 addedparts = NULL;
522 if ( multipart )
523 currentpart = 1;
524 else
525 currentpart = 0;
526 return;
527 }
528
~MIME_body()529 MIME_body:: ~MIME_body()
530 {
531 /* Free up memory used by header data strucures */
532 char **dataptr;
533 fieldtab.InitIterator();
534 while ( (dataptr=fieldtab.Iterate()) )
535 delete[] *dataptr;
536
537 /* Remove any temp file */
538 while ( FreeFile() == 0 ) {
539 /* Force a close */;
540 }
541
542 /* Free up any sub-part MIME bodies */
543 if ( multipart ) {
544 MIME_body **dataptr;
545 multipart->InitIterator();
546 while ( (dataptr=multipart->Iterate()) )
547 delete *dataptr;
548 delete multipart;
549 }
550 if ( addedparts ) {
551 IObottle **dataptr;
552 addedparts->InitIterator();
553 while ( (dataptr=addedparts->Iterate()) )
554 delete *dataptr;
555 delete addedparts;
556 }
557 if ( name )
558 delete[] name;
559
560 /* Finally, clean up random memory chunks */
561 delete[] endstrings;
562 delete[] endlens;
563 }
564
565 /* Quick utility routine for getting a (possibly quoted) word from a string */
566 void
GetWord(const char * src,char ** dst)567 MIME_body:: GetWord(const char *src, char **dst)
568 {
569 const char *src_head;
570 int wordlen;
571
572 src_head = src;
573 if ( *src == '"' ) {
574 ++src_head;
575 ++src;
576 while ( *src && (*src != '"') )
577 ++src;
578 } else {
579 while ( *src && ! isspace(*src) && (*src != ';') )
580 ++src;
581 }
582 wordlen = (src-src_head);
583 *dst = new char[wordlen+1];
584 strncpy(*dst, src_head, wordlen);
585 (*dst)[wordlen] = '\0';
586 }
587
588 /* Create a sub-menu of body parts ;-) */
589 int
ParseMulti(MD5_CTX * md5ctx)590 MIME_body:: ParseMulti(MD5_CTX *md5ctx)
591 {
592 int n, seplen;
593 char *newcontent;
594 char **newends;
595 int *newendlens;
596 char line[BUFSIZ];
597 long here;
598
599 /* Copy the content string so we can muck it up :) */
600 newcontent = new char[strlen(content)+1];
601 strcpy(newcontent, content);
602
603 /* Try to find the boundary */
604 char *mstr, *sepstr;
605 int mlen;
606 if ( (sepstr=(char *)match_nocase(newcontent, "Boundary=", &mlen)) == NULL ) {
607 delete[] newcontent;
608 return(-1);
609 }
610 sepstr += mlen;
611 if ( *sepstr == '"' ) {
612 for ( mstr=(++sepstr); *mstr && (*mstr != '"'); ++mstr );
613 } else {
614 for ( mstr=sepstr;
615 *mstr && (*mstr != ';') && !isspace(*mstr); ++mstr );
616 }
617 *mstr = '\0';
618 /* MIME boundaries are "--boundary" */
619 *(--sepstr) = '-';
620 *(--sepstr) = '-';
621
622 /* Add in the boundary as a new ending */
623 newends = new char *[numends+1+1];
624 newendlens = new int [numends+1];
625 for ( n=0; endstrings[n]; ++n ) {
626 newends[n] = endstrings[n];
627 newendlens[n] = endlens[n];
628 }
629 newends[n] = sepstr;
630 seplen = strlen(sepstr);
631 newendlens[n] = seplen;
632 newends[n+1] = NULL;
633
634 /* Suck up to the first boundary -- no pun intended. :) */
635 here = rawfile->tellg();
636 for ( ; ; ) {
637 int len;
638
639 here = rawfile->tellg();
640
641 /* Read in the next line, checking for termination boundary */
642 len = rawfile->readline(line, sizeof(line));
643 if ( rawfile->eof() )
644 break;
645
646 for ( n=0; newends[n]; ++n ) {
647 if ( IsBoundary(line, newends[n], newendlens[n]) )
648 break;
649 }
650 if ( newends[n] )
651 break;
652
653 MD5Update(md5ctx, (unsigned char *)line, len);
654 }
655 if ( newends[n] == NULL ) { /* EOF? */
656 delete[] newcontent;
657 delete[] newends;
658 delete[] newendlens;
659 return(-1);
660 }
661
662 /* Make sure we didn't hit an upper boundary */
663 if ( strncmp(newends[n], sepstr, seplen) != 0 ) {
664 rawfile->seekg(here);
665 delete[] newcontent;
666 delete[] newends;
667 delete[] newendlens;
668 return(-1);
669 }
670
671 /* Warning: Memory Leak!
672 At this point we never free 'newcontent', since it stays in use
673 by any child MIME bodies we create here.
674 */
675
676 multipart = new List<MIME_body *>;
677 do {
678 /* Elegant, isn't it? */
679 MIME_body *newbody = new MIME_body(rawfile, newends, this);
680 MD5Update(md5ctx, newbody->md5sum, 16);
681 multipart->Add(newbody);
682
683 /* Check the next boundary line */
684 here = rawfile->tellg();
685 rawfile->readline(line, sizeof(line));
686
687 /* Anything other than our separator, we quit here */
688 if ( strncmp(line, sepstr, seplen) != 0 )
689 break;
690
691 /* If we find "boundary--", then we suck up input and quit */
692 if ( (line[seplen] == '-') && (line[seplen+1] == '-') ) {
693 for ( ; ; ) {
694 here = rawfile->tellg();
695
696 rawfile->readline(line, sizeof(line));
697 if ( rawfile->eof() )
698 break;
699
700 for ( n=0; endstrings[n]; ++n ) {
701 if ( IsBoundary(line, endstrings[n],
702 endlens[n]) )
703 break;
704 }
705 if ( endstrings[n] )
706 break;
707 }
708 break;
709 }
710 } while ( ! rawfile->eof() );
711
712 /* That's it! We're done! :) */
713 if ( ! rawfile->eof() )
714 rawfile->seekg(here);
715 delete[] newends;
716 delete[] newendlens;
717 return(0);
718 }
719
720 int
Open(void)721 MIME_body:: Open(void)
722 {
723 int i, status;
724 iconv_t iconv_ctx = (iconv_t)-1;
725
726 /* Save the body to a file, with appropriate translation */
727 rawfile->seekg(bstart);
728
729 if ( strncmp(Content(), "text", strlen("text")) == 0 ) {
730 char charset[1024];
731 if ( GetContentAttribute(Content(), "charset=", charset, sizeof(charset)) &&
732 (strcasecmp(charset, mime_charset) != 0) ) {
733 iconv_ctx = iconv_open(mime_charset, charset);
734 }
735 }
736 for ( i=0; encodings[i].type; ++i ) {
737 if ( strcasecmp(encodings[i].type, encoding) == 0 )
738 break;
739 }
740 status = encodings[i].decode(rawfile, iconv_ctx, endstrings, endlens, &decoded);
741 if ( iconv_ctx != (iconv_t)-1 ) {
742 iconv_close(iconv_ctx);
743 }
744 if ( status < 0 ) {
745 return status;
746 }
747 if ( IsHTML() ) {
748 char *filename = decoded;
749 if ( StripHTML(filename, &decoded) == 0 ) {
750 unlink(filename);
751 delete[] filename;
752 }
753 }
754 return status;
755 }
756
757 char *
GetHeader(char ** keyholder)758 MIME_body:: GetHeader(char **keyholder)
759 {
760 char **value;
761 if ( keyholder == NULL ) {
762 fieldtab.InitIterator();
763 return(NULL);
764 }
765 if ( (value=fieldtab.Iterate(keyholder)) == NULL )
766 return(NULL);
767 return(*value);
768 }
769
770 const char *
GetField(const char * key)771 MIME_body:: GetField(const char *key)
772 {
773 char **dataptr;
774 if ( (dataptr=fieldtab.Search(key)) )
775 return(*dataptr);
776 return(NULL);
777 }
778
779 void
NewField(const char * name,const char * value)780 MIME_body:: NewField(const char *name, const char *value)
781 {
782 char *sptr;
783
784 /* Remove any existing value */
785 if ((sptr=(char *)GetField(name))) {
786 delete[] sptr;
787 fieldtab.Remove(name);
788 }
789 sptr = new char[strlen(value)+1];
790 strcpy(sptr, value);
791 fieldtab.Add(name, sptr);
792 }
793
794 /* Allow searching of MIME bodies, matching within lines */
795 MIME_body *
Search(const char * pattern)796 MIME_body:: Search(const char *pattern)
797 {
798 FILE *rawbody;
799 int found = 0;
800
801 /* First check ourselves */
802 if ( (strncmp(Content(), "text", strlen("text")) == 0) &&
803 ((rawbody=fopen(File(), "r")) != NULL) ) {
804 char buffer[BUFSIZ];
805 int matchlen;
806
807 while ( fgets(buffer, BUFSIZ-1, rawbody) ) {
808 if ( match(buffer, pattern, &matchlen) ) {
809 found = 1;
810 break;
811 }
812 }
813 fclose(rawbody);
814 FreeFile();
815 }
816 if ( found )
817 return(this);
818
819 /* Check our children */
820 if ( multipart ) {
821 MIME_body **bodyptr;
822 MIME_body *okay;
823
824 multipart->InitIterator();
825 okay = NULL;
826 while ( ! okay && (bodyptr=multipart->Iterate()) ) {
827 okay = (*bodyptr)->Search(pattern);
828 }
829 return(okay ? okay : (MIME_body *)0);
830 }
831 return(NULL);
832 }
833
834 /* Add a part to the current body.
835 Note, because we call MultipartMe(), we must be rewritten (with a greater
836 length) to a new IO stream with SaveRaw(), or these changes will be lost.
837 */
838 int
AddPart(const char * file,int is_mime)839 MIME_body:: AddPart(const char *file, int is_mime)
840 {
841 MD5_CTX md5_ctx;
842 int (*EncodeFile)(FILE *, IObottle *);
843 FILE *input;
844 char line[BUFSIZ];
845 int len, blen;
846 long here;
847 const char *ptr;
848 char *boundary;
849 char *newcontent, *newencoding;
850
851 /* Open the input file */
852 if ( ((newencoding=MIME_Encoding(file)) == NULL) ||
853 ((input=fopen(file, "r")) == NULL) )
854 return(-1);
855
856 /* Find the encoder we use */
857 for ( len = 0; encodings[len].type; ++len ) {
858 if ( strcasecmp(newencoding, encodings[len].type) == 0 )
859 break;
860 }
861 EncodeFile = encodings[len].encode;
862
863 /* Make sure we are a multipart message */
864 if ( MultipartMe(&boundary) < 0 ) {
865 fclose(input);
866 return(-1);
867 }
868 blen = strlen(boundary);
869
870 /* Add the new file, first seek to end of message */
871 rawfile->seekg(bstart);
872 do {
873 here = rawfile->tellg();
874 len = rawfile->readline(line, BUFSIZ);
875 if ( (line[0] == '-') && (line[1] == '-') ) {
876 if ( strncmp(&line[2], boundary, blen) == 0 ) {
877 if ( (line[2+blen] == '-') &&
878 (line[2+blen+1] == '-') ) {
879 break;
880 }
881 }
882 }
883 } while ( ! rawfile->fail() && ! rawfile->eof() );
884 rawfile->seekp(here);
885
886 /* Add boundary */
887 rawfile->printf("--%s\n", boundary);
888 if ( ! is_mime ) {
889 if ( (newcontent=MIME_Type(file)) == NULL ) {
890 if ( strcmp(newencoding, "base64") == 0 )
891 newcontent = DEFAULT_BIN;
892 else
893 newcontent = DEFAULT_TXT;
894 }
895 /* Skip the full path of the file when naming the content */
896 if ( (ptr=strrchr(file, '/')) != NULL )
897 file = ptr+1;
898 rawfile->printf("Content-Type: %s; name=\"%s\"\n",
899 newcontent, file);
900 rawfile->printf("Content-Transfer-Encoding: %s\n", newencoding);
901 rawfile->printf("\n");
902 }
903 if ( (*EncodeFile)(input, rawfile) < 0 ) {
904 fclose(input);
905 rawfile->seekg(here);
906 rawfile->printf("--%s--\n", boundary);
907 rawfile->truncate(rawfile->tellp());
908 delete[] boundary;
909 return(-1);
910 }
911 fclose(input);
912 rawfile->printf("--%s--\n", boundary);
913 rawfile->truncate(rawfile->tellp());
914 delete[] boundary;
915
916 /* Free up any sub-part MIME bodies */
917 if ( multipart ) {
918 MIME_body **dataptr;
919 multipart->InitIterator();
920 while ( (dataptr=multipart->Iterate()) )
921 delete *dataptr;
922 delete multipart;
923 multipart = NULL;
924 }
925
926 /* Create the new MIME body sublist */
927 rawfile->seekg(bstart);
928 MD5Init(&md5_ctx);
929 (void) ParseMulti(&md5_ctx);
930 MD5Final(md5sum, &md5_ctx);
931 return(0);
932 }
933
934 /* NOTE: After this function runs, we are no longer tied to the main MIME
935 message base. We are fully contained in a separate temporary file,
936 and must be rewritten to a stream via the SaveRaw() function.
937 */
938 int
MultipartMe(char ** boundaryptr)939 MIME_body:: MultipartMe(char **boundaryptr)
940 {
941 /* Output variables */
942 int i;
943 char temp_name[PATH_MAX];
944 IObottle *output;
945
946 /* MIME variables */
947 const char *oldcontent = Content();
948 const char *oldencoding = Encoding();
949 char *newcontent = "multipart/mixed; boundary=";
950 char *boundary, *ptr;
951 int set_version, set_content, set_encoding;
952
953 /* Buffer copy variables */
954 char line[BUFSIZ];
955 int len, n;
956 long oldpos;
957
958 /* Don't do anything if we are already multipart */
959 if ( strncmp(oldcontent, "multipart/mixed", strlen("multipart/mixed"))
960 == 0 ) {
961 /* Try to find the boundary */
962 char *mstr, *sepstr;
963 int mlen;
964
965 newcontent = new char[strlen(oldcontent)+1];
966 strcpy(newcontent, oldcontent);
967 if ( (sepstr=(char *)match_nocase(newcontent, "Boundary=",
968 &mlen)) == NULL ) {
969 delete[] newcontent;
970 return(-1);
971 }
972 sepstr += mlen;
973 if ( *sepstr == '"' )
974 for (mstr=(++sepstr); *mstr && (*mstr != '"'); ++mstr);
975 else
976 for (mstr=sepstr; *mstr && !isspace(*mstr); ++mstr);
977 *mstr = '\0';
978
979 /* Copy it in.. */
980 *boundaryptr = new char[strlen(sepstr)+1];
981 strcpy(*boundaryptr, sepstr);
982 delete[] newcontent;
983 return(0);
984 }
985
986 /* Open a new file */
987 FILE *teaser = create_tempfile("w", temp_name);
988 if ( teaser == NULL ) {
989 return(-1);
990 }
991 fclose(teaser);
992 output = new IObottle(temp_name);
993 if ( output->fail() ) {
994 delete output;
995 return(-1);
996 }
997 /* We are primed and ready. :) */
998
999 /* Create the new content type and boundary */
1000 /* WARNING: Memory leak!
1001 content[] is never freed, since normally it is read-only.
1002 */
1003 content = new char[strlen(newcontent)+1+32+1+1+1];
1004 strcpy((char *)content, newcontent);
1005 strcat((char *)content, "\"");
1006 boundary = (char *)content+strlen(newcontent)+1;
1007 for ( i=0, ptr=boundary; i<16; ++i, ptr += 2 )
1008 sprintf(ptr, "%.2X", md5sum[i]);
1009 *ptr = '\0';
1010 *boundaryptr = new char[2*16+1];
1011 strcpy(*boundaryptr, boundary);
1012 strcat((char *)content, "\";");
1013 encoding = "7bit";
1014
1015 /* Write out our new header */
1016 oldpos = rawfile->tellg();
1017 rawfile->seekg(hstart);
1018 hstart = 0;
1019 set_version = set_content = set_encoding = 0;
1020 do {
1021 /* Read a header line */
1022 len = rawfile->readline(line, BUFSIZ);
1023
1024 /* Check for header clean up at EOH (here) */
1025 if ( len == 1 ) {
1026 if ( ! set_version ) {
1027 output->printf("MIME-Version: %s\n", version);
1028 }
1029 if ( ! set_content ) {
1030 output->printf("Content-Type: %s\n", content);
1031 }
1032 if ( ! set_encoding ) {
1033 output->printf(
1034 "Content-Transfer-Encoding: %s\n", encoding);
1035 }
1036 }
1037
1038 /* Translate a couple of fields, spit out the rest */
1039 if ( strncasecmp(line, "MIME-Version:",
1040 strlen("MIME-Version:")) == 0 ) {
1041 output->printf("MIME-Version: %s\n", version);
1042 set_version = 1;
1043 } else
1044 if ( strncasecmp(line, "Content-Type:",
1045 strlen("Content-Type:")) == 0 ) {
1046 output->printf("Content-Type: %s\n", content);
1047 set_content = 1;
1048 } else
1049 if ( strncasecmp(line, "Content-Transfer-Encoding:",
1050 strlen("Content-Transfer-Encoding:")) == 0 ) {
1051 output->printf("Content-Transfer-Encoding: %s\n",
1052 encoding);
1053 set_encoding = 1;
1054 } else
1055 output->writeline(line, len);
1056 } while ( strlen(line) > 0 );
1057
1058 /* Print the intro and the initial body separator */
1059 bstart = output->tellg();
1060 output->writeline("\nThis is a multi-part message in MIME format.\n\n");
1061 output->printf("--%32.32s\n", boundary);
1062
1063 /* Print out our existing body */
1064 output->printf("Content-Type: %s; name=\"Message Text\"\n", oldcontent);
1065 output->printf("Content-Transfer-Encoding: %s\n", oldencoding);
1066 output->printf("\n");
1067 for ( ; ; ) {
1068 /* Read in the next line, checking for boundary */
1069 len = rawfile->readline(line, BUFSIZ);
1070 if ( rawfile->eof() )
1071 break;
1072 for ( n=0; endstrings[n]; ++n ) {
1073 /* If we have a match, break out */
1074 if (IsBoundary(line, endstrings[n], endlens[n]))
1075 break;
1076 }
1077 if ( endstrings[n] )
1078 break;
1079 output->writeline(line, len);
1080 }
1081 output->printf("--%32.32s--\n", boundary);
1082 output->flush();
1083
1084 /* That's it! */
1085 if ( addedparts == NULL )
1086 addedparts = new List<IObottle *>;
1087 addedparts->Add(output);
1088 rawfile = output;
1089 return(1);
1090 }
1091
1092 /* Save the body to a file */
1093 int
Save(char * filename,int overwrite)1094 MIME_body:: Save(char *filename, int overwrite)
1095 {
1096 char *ourfile;
1097 FILE *input, *output;
1098 char *mode;
1099 char buffer[BUFSIZ];
1100 unsigned int blen;
1101 int retval;
1102
1103 if ( ! filename || ! *filename )
1104 return(0);
1105
1106 switch (overwrite) {
1107 case 0: {
1108 struct stat sb;
1109 if ( stat(filename, &sb) == 0 ) {
1110 errno = EEXIST;
1111 return(-1);
1112 }
1113 mode = "w";
1114 }
1115 break;
1116 case 1:
1117 mode = "w";
1118 break;
1119 case 2:
1120 mode = "a";
1121 break;
1122 default:
1123 errno = EINVAL;
1124 return(-1);
1125 }
1126
1127 /* Open the files */
1128 ourfile = decoded;
1129 if ( ourfile == NULL ) { /* Do a temporary open */
1130 if ( Open() < 0 ) {
1131 return(-1);
1132 }
1133 ourfile = decoded;
1134 decoded = NULL;
1135 }
1136 if ( (output=fopen(filename, mode)) == NULL ) {
1137 if ( decoded == NULL )
1138 (void) unlink(ourfile);
1139 return(-1);
1140 }
1141 if ( (input=fopen(ourfile, "r")) == NULL ) {
1142 fclose(output);
1143 if ( decoded == NULL )
1144 (void) unlink(ourfile);
1145 return(-1);
1146 }
1147
1148 /* Just DO it */
1149 retval = 0;
1150 while ( (blen=fread(buffer, 1, BUFSIZ, input)) > 0 ) {
1151 /* Write okay? */
1152 if ( fwrite(buffer, 1, blen, output) != blen ) {
1153 retval = -1;
1154 break;
1155 }
1156 }
1157 /* Read error? */
1158 if ( ! feof(input) )
1159 retval = -1;
1160
1161 /* Clean up and exit */
1162 fclose(input);
1163 fclose(output);
1164 if ( decoded == NULL )
1165 (void) unlink(ourfile);
1166 return(retval);
1167 }
1168
StripHTML(const char * filename,char ** outfile)1169 static int StripHTML(const char *filename, char **outfile)
1170 {
1171 char temp_name[PATH_MAX];
1172 FILE *rfp, *wfp;
1173 char ibuffer[BUFSIZ];
1174 char obuffer[BUFSIZ];
1175 size_t ilength;
1176 size_t olength;
1177 bool in_element = false;
1178
1179 rfp = fopen(filename, "r");
1180 if ( rfp == NULL ) {
1181 return -1;
1182 }
1183 wfp = create_tempfile("w", temp_name);
1184 if ( wfp == NULL ) {
1185 fclose(rfp);
1186 return -1;
1187 }
1188 while ( (ilength=fread(ibuffer, 1, sizeof(ibuffer), rfp)) > 0 ) {
1189 char *output = obuffer;
1190 for ( size_t i = 0; i < ilength; ++i ) {
1191 if ( in_element ) {
1192 if ( ibuffer[i] == '>' ) {
1193 in_element = false;
1194 }
1195 } else if ( ibuffer[i] == '<' ) {
1196 in_element = true;
1197 } else if ( ibuffer[i] == '&' ) {
1198 if ( strncmp(&ibuffer[i], " ", 6) == 0 ) {
1199 *output++ = ' ';
1200 i += 5;
1201 } else if ( strncmp(&ibuffer[i], "&", 5) == 0 ) {
1202 *output++ = '&';
1203 i += 4;
1204 } else if ( strncmp(&ibuffer[i], "<", 4) == 0 ) {
1205 *output++ = '<';
1206 i += 3;
1207 } else if ( strncmp(&ibuffer[i], ">", 4) == 0 ) {
1208 *output++ = '>';
1209 i += 3;
1210 } else {
1211 *output++ = ibuffer[i];
1212 }
1213 } else {
1214 *output++ = ibuffer[i];
1215 }
1216 }
1217 olength = (output - obuffer);
1218 if ( fwrite(obuffer, 1, olength, wfp) != olength ) {
1219 fclose(rfp);
1220 fclose(wfp);
1221 unlink(temp_name);
1222 return(-1);
1223 }
1224 }
1225 fclose(rfp);
1226 fclose(wfp);
1227
1228 *outfile = new char[strlen(temp_name)+1];
1229 strcpy(*outfile, temp_name);
1230 return(0);
1231 }
1232
1233 /* Check if a line is a MIME boundary */
IsBoundary(char * line,char * boundary,int boundlen)1234 static int IsBoundary(char *line, char *boundary, int boundlen)
1235 {
1236 int bodies_ended = 0;
1237
1238 /* Check for boundary matching */
1239 if ( strncmp(line, boundary, boundlen) != 0 )
1240 return(0);
1241 #ifdef DEBUG
1242 printf("Boundary Matched: %s\n", line);
1243 #endif
1244 /* First part of boundary matched, now either it's a normal
1245 MIME boundary (terminated by whitespace) or it's a special
1246 "no more bodies" boundary (terminated by "--")
1247 */
1248 if ( (line[boundlen] == '-') && (line[boundlen+1] == '-') ) {
1249 bodies_ended = 1;
1250 boundlen += 2;
1251 }
1252 if ( ! line[boundlen] || isspace(line[boundlen]) )
1253 return(1+bodies_ended);
1254 return(0);
1255 }
1256
1257 /* Okay, the basic structure of a decoder is:
1258
1259 Open a temporary file.
1260 Copy from IObottle to temporary file, performing appropriate decoding.
1261 Set outfile to NULL on write error and return -1.
1262 Quit writing at "endstrings" and rewind to the line before.
1263 Set outfile to name of temporary file and return 0.
1264 */
1265
No_Decode(IObottle * body,iconv_t iconv_ctx,char * endstrings[],int endlens[],char ** outfile)1266 int No_Decode(IObottle *body, iconv_t iconv_ctx, char *endstrings[], int endlens[], char **outfile)
1267 {
1268 long here = body->tellg();
1269 char temp_name[PATH_MAX];
1270 FILE *output;
1271 char line[BUFSIZ], converted_line[BUFSIZ*2], *data;
1272 int n, finished;
1273
1274 /* Grab a temporary file */
1275 if ( (output=create_tempfile("w", temp_name)) == NULL )
1276 return(-1);
1277
1278 if ( iconv_ctx != (iconv_t)-1 ) {
1279 ConvertCharset(iconv_ctx, NULL, 0, NULL, 0);
1280 }
1281
1282 /* Just DO it! :) */
1283 for ( finished = 0; ! finished; ) {
1284 here = body->tellg();
1285
1286 /* Read in the next line, checking for termination boundary */
1287 body->readline(line, sizeof(line));
1288 if ( body->eof() ) {
1289 finished = 1;
1290 continue;
1291 }
1292 for ( n=0; endstrings[n]; ++n ) {
1293 if ( IsBoundary(line,endstrings[n],endlens[n]) )
1294 break;
1295 }
1296 if ( endstrings[n] ) {
1297 finished = 1;
1298 continue;
1299 }
1300
1301 /* Do any processing here */
1302 if ( iconv_ctx != (iconv_t)-1 ) {
1303 size_t len = ConvertCharset(iconv_ctx, line, strlen(line), converted_line, sizeof(converted_line)-1);
1304 converted_line[len] = '\0';
1305 data = converted_line;
1306 } else {
1307 data = line;
1308 }
1309
1310 /* Write to the temporary file */
1311 if ( (fputs(data, output) == EOF) ||
1312 (fputc('\n', output) == EOF) ) {
1313 /* WRITE ERROR! */
1314 fclose(output);
1315 (void) unlink(temp_name);
1316 return(-1);
1317 }
1318 }
1319 fclose(output);
1320
1321 /* Rewind to before the terminating line */
1322 body->seekg(here);
1323 *outfile = new char[strlen(temp_name)+1];
1324 strcpy(*outfile, temp_name);
1325 return(0);
1326 }
No_Encode(FILE * input,IObottle * output)1327 int No_Encode(FILE *input, IObottle *output)
1328 {
1329 char buffer[BUFSIZ];
1330 unsigned int len;
1331
1332 while ( (len=fread(buffer, 1, sizeof(buffer), input)) > 0 ) {
1333 if ( output->write(buffer, len) != len )
1334 return(-1);
1335 }
1336 return(0);
1337 }
1338
QP_Decode(IObottle * body,iconv_t iconv_ctx,char * endstrings[],int endlens[],char ** outfile)1339 int QP_Decode(IObottle *body, iconv_t iconv_ctx, char *endstrings[], int endlens[], char **outfile)
1340 {
1341 long here = body->tellg();
1342 char temp_name[PATH_MAX];
1343 FILE *output;
1344 char line[BUFSIZ], newline[BUFSIZ], converted_line[BUFSIZ*2], *data;
1345 int i, j, n;
1346 int finished;
1347
1348 /* Grab a temporary file */
1349 if ( (output=create_tempfile("w", temp_name)) == NULL )
1350 return(-1);
1351
1352 if ( iconv_ctx != (iconv_t)-1 ) {
1353 ConvertCharset(iconv_ctx, NULL, 0, NULL, 0);
1354 }
1355
1356 /* Just DO it! :) */
1357 for ( finished = 0; ! finished; ) {
1358 here = body->tellg();
1359
1360 /* Read in the next line, checking for termination boundary */
1361 body->readline(line, sizeof(line));
1362 if ( body->eof() ) {
1363 finished = 1;
1364 continue;
1365 }
1366 for ( n=0; endstrings[n]; ++n ) {
1367 if ( IsBoundary(line,endstrings[n],endlens[n]) )
1368 break;
1369 }
1370 if ( endstrings[n] ) {
1371 finished = 1;
1372 continue;
1373 }
1374
1375 /* Strip trailing whitespace */
1376 for ( i = strlen(line); (i > 0) && isspace(line[i-1]); --i );
1377 line[i] = '\0';
1378
1379 /* Convert lines from Quoted-Printable to raw data */
1380 for ( i = 0, j = 0; line[i]; ++i, ++j ) {
1381 if ( line[i] == '=' ) {
1382 /* Check for soft return */
1383 if ( line[i+1] == '\0' )
1384 break;
1385
1386 /* Make sure no buffer overflow */
1387 if ( ! line[i+1] || ! line[i+2] )
1388 continue;
1389
1390 /* Convert from hexidecimal */
1391 newline[j] = '\0';
1392 if ( isdigit(line[++i]) )
1393 newline[j] |= (line[i]-'0');
1394 else
1395 newline[j] |= (toupper(line[i])-'A'+10);
1396 newline[j] <<= 4;
1397 if ( isdigit(line[++i]) )
1398 newline[j] |= (line[i]-'0');
1399 else
1400 newline[j] |= (toupper(line[i])-'A'+10);
1401 } else
1402 newline[j] = line[i];
1403 }
1404 if ( line[i] == '=' ) {
1405 /* Soft Return, don't add a return to the stream */;
1406 } else {
1407 newline[j++] = '\n';
1408 }
1409 newline[j] = '\0';
1410
1411 /* Do any processing here */
1412 if ( iconv_ctx != (iconv_t)-1 ) {
1413 size_t len = ConvertCharset(iconv_ctx, newline, j, converted_line, sizeof(converted_line)-1);
1414 converted_line[len] = '\0';
1415 data = converted_line;
1416 } else {
1417 data = newline;
1418 }
1419
1420 /* Write to the temporary file */
1421 if ( fputs(data, output) == EOF ) {
1422 /* WRITE ERROR! */
1423 fclose(output);
1424 (void) unlink(temp_name);
1425 return(-1);
1426 }
1427 }
1428 fclose(output);
1429
1430 /* Rewind to before the terminating line */
1431 body->seekg(here);
1432 *outfile = new char[strlen(temp_name)+1];
1433 strcpy(*outfile, temp_name);
1434 return(0);
1435 }
1436 /* A loose interpretation of the original... :-) */
1437 /* This implementation allows whitespace at the end of the line.
1438 I do this mainly because a bunch of whitespace representations at the
1439 ends of many lines looks really bad. Personal taste. :-)
1440 */
QP_Encode(FILE * input,IObottle * output)1441 int QP_Encode(FILE *input, IObottle *output)
1442 {
1443 char buffer[BUFSIZ];
1444 char outline[80];
1445 char *ptr;
1446 unsigned int inlen, outlen;
1447
1448 outlen = 0;
1449 while ( fgets(buffer, sizeof(buffer)-1, input) ) {
1450 inlen = strlen(buffer);
1451
1452 /* The standard specifies no more than 76 chars per line */
1453 for ( ptr=buffer; *ptr && (*ptr != '\n'); ++ptr ) {
1454 /* Allowed non-encoded characters:
1455 '\t', ' ', '!'-'<', '>'-'~' inc. A-Za-z
1456 */
1457 if ( (*ptr == '\t') || (*ptr == ' ') ||
1458 ((*ptr >= 33) && (*ptr <= 60) ) ||
1459 ((*ptr >= 62) && (*ptr <= 126) ) ) {
1460 outline[outlen++] = *ptr;
1461
1462 /* If we've hit soft limit, put soft return */
1463 if ( outlen == 76 ) {
1464 outline[outlen++] = '=';
1465 outline[outlen++] = '\n';
1466 if ( output->write(outline, outlen) !=
1467 outlen ) {
1468 return(-1);
1469 }
1470 outlen = 0;
1471 }
1472 } else {
1473 /* If we've hit soft limit, put soft return */
1474 if ( outlen >= (76-3) ) {
1475 outline[outlen++] = '=';
1476 outline[outlen++] = '\n';
1477 if ( output->write(outline, outlen) !=
1478 outlen ) {
1479 return(-1);
1480 }
1481 outlen = 0;
1482 }
1483
1484 /* Must encode */
1485 sprintf(&outline[outlen], "=%2.2X", *ptr);
1486 outlen += 3;
1487 }
1488 }
1489
1490 /* Take care of the (possible) newline */
1491 if ( buffer[strlen(buffer)-1] == '\n' ) {
1492 outline[outlen++] = '\n';
1493 if ( output->write(outline, outlen) != outlen )
1494 return(-1);
1495 outlen = 0;
1496 }
1497 }
1498 /* Flush any remaining output (file not terminated by newline) */
1499 if ( outlen > 0 ) {
1500 outline[outlen++] = '\n';
1501 if ( output->write(outline, outlen) != outlen )
1502 return(-1);
1503 outlen = 0;
1504 }
1505 return(0);
1506 }
1507
1508 /* Note, this hasn't been fully tested. */
Base64_Decode(IObottle * body,iconv_t iconv_ctx,char * endstrings[],int endlens[],char ** outfile)1509 int Base64_Decode(IObottle *body, iconv_t iconv_ctx, char *endstrings[], int endlens[], char **outfile)
1510 {
1511 long here = body->tellg();
1512 char temp_name[PATH_MAX];
1513 FILE *output;
1514 char line[BUFSIZ], newline[BUFSIZ], converted_line[BUFSIZ*2], *data;
1515 unsigned int n, newlen, finished;
1516
1517 /* Grab a temporary file */
1518 if ( (output=create_tempfile("w", temp_name)) == NULL )
1519 return(-1);
1520
1521 if ( iconv_ctx != (iconv_t)-1 ) {
1522 ConvertCharset(iconv_ctx, NULL, 0, NULL, 0);
1523 }
1524
1525 /* Just DO it! :) */
1526 for ( finished = 0; ! finished; ) {
1527 here = body->tellg();
1528
1529 /* Read in the next line, checking for termination boundary */
1530 body->readline(line, sizeof(line));
1531 if ( body->eof() ) {
1532 finished = 1;
1533 continue;
1534 }
1535 for ( n=0; endstrings[n]; ++n ) {
1536 if ( IsBoundary(line,endstrings[n],endlens[n]) )
1537 break;
1538 }
1539 if ( endstrings[n] ) {
1540 finished = 1;
1541 continue;
1542 }
1543
1544 /* Do any processing here */
1545 newlen = 0;
1546 if ( (newlen=base64_decode(line, strlen(line), newline, sizeof(newline))) == 0 ) {
1547 /* End of Base64 text -- eat up remaining text */
1548 for ( ; ; ) {
1549 here = body->tellg();
1550
1551 body->readline(line, sizeof(line));
1552 if ( body->eof() )
1553 break;
1554 for ( n=0; endstrings[n]; ++n ) {
1555 if ( IsBoundary(line, endstrings[n],
1556 endlens[n]) )
1557 break;
1558 }
1559 if ( endstrings[n] )
1560 break;
1561 }
1562 break;
1563 }
1564 if ( iconv_ctx != (iconv_t)-1 ) {
1565 newlen = ConvertCharset(iconv_ctx, newline, newlen, converted_line, sizeof(converted_line));
1566 data = converted_line;
1567 } else {
1568 data = newline;
1569 }
1570
1571
1572 /* Write to the temporary file */
1573 if ( fwrite(data, 1, newlen, output) != newlen ) {
1574 /* WRITE ERROR! */
1575 fclose(output);
1576 (void) unlink(temp_name);
1577 return(-1);
1578 }
1579 }
1580 fclose(output);
1581
1582 /* Rewind to before the terminating line */
1583 body->seekg(here);
1584 *outfile = new char[strlen(temp_name)+1];
1585 strcpy(*outfile, temp_name);
1586 return(0);
1587 }
Base64_Encode(FILE * input,IObottle * output)1588 int Base64_Encode(FILE *input, IObottle *output)
1589 {
1590 char buffer[19*3];
1591 char outline[19*4+5];
1592 unsigned int len;
1593
1594 /* Loop, reading in 19*3 bytes, transforming them into 19*4 bytes */
1595 /* 57 76 */
1596 while ( (len=fread(buffer, 1, 19*3, input)) > 0 ) {
1597 len = base64_encode(buffer, len, outline, sizeof(outline));
1598 outline[len++] = '\n';
1599 if ( output->write(outline, len) != len ) {
1600 return(-1);
1601 }
1602 }
1603 return(0);
1604 }
1605