1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <stdlib.h>
5
6 #include "compat.h"
7 #include "config.h"
8 #include "core.h"
9 #include "fileapi.h"
10 #include "variables.h"
11 #include "regexp.h"
12 #include "unmime.h"
13 #include "mystring.h"
14 #include "codes.h"
15 #include "rfc2047.h"
16 #include "moderate.h"
17
18 struct mime_handler *mime_handlers;
19
20 int mimecounter;
21
22 /* Utility function to read a header line correctly */
read_mime_line(char * buf,int size,FILE * stream,int inbody)23 int read_mime_line(char *buf, int size, FILE *stream, int inbody)
24 {
25 if (inbody) {
26 char *temp = read_file(buf, size, stream);
27 if (temp) return 1;
28 else return 0;
29 } else {
30 int i = 0;
31 while(1) {
32 int a_char = getc_file(stream);
33 if (a_char == EOF) {
34 if (i < size) {
35 buf[i] = '\0';
36 } else {
37 buf[size-1] = '\0';
38 }
39 if (buf[0] != '\0') return 1;
40 return 0;
41 } else if (a_char == '\n') {
42 /* If we have a new line, check the next char */
43 a_char = getc_file(stream);
44 ungetc_file(a_char, stream);
45 /* If it's not a space or tab, then, terminate the line */
46 if (a_char != ' ' && a_char != '\t') {
47 if (i < size) {
48 buf[i++] = '\n';
49 }
50 if (i < size) {
51 buf[i] = '\0';
52 } else {
53 buf[size-1] = '\0';
54 }
55 return 1;
56 }
57 /* it was a space or tab, just keep going through the loop */
58 } else {
59 if (i < size) buf[i++] = a_char;
60 }
61 }
62 if (buf[0] != '\0') return 1;
63 return 0;
64 }
65 }
66
67
68 /* Eat allocated memory. *Burp* */
mime_eat_header(struct mime_header * header)69 void mime_eat_header(struct mime_header *header)
70 {
71 struct mime_field *field;
72 int loop;
73
74 if (!header) return;
75
76 for (loop = 0; loop < header->numfields; loop++) {
77 field = header->fields[loop];
78
79 if (field->params) free((char *)field->params);
80 if (field->value) free((char *)field->value);
81 if (field->name) free((char *)field->name);
82 free(field);
83 }
84 free(header->fields);
85 header->fields = NULL;
86 }
87
mime_getfield(struct mime_header * header,const char * fieldname)88 struct mime_field *mime_getfield(struct mime_header *header, const char
89 *fieldname)
90 {
91 struct mime_field *temp = NULL;
92 int loop, done;
93 if (header == NULL)
94 return NULL;
95
96 loop = done = 0;
97
98 while ((loop < header->numfields) && !done) {
99 temp = header->fields[loop];
100
101 if (strcasecmp(temp->name,fieldname) == 0) done = 1;
102 loop++;
103 }
104
105 if (!done) return NULL; else return temp;
106 }
107
108 /* YOU WILL **NOT** WANT TO FREE THIS RETURN RESULT */
mime_fieldval(struct mime_header * header,const char * fieldname)109 const char *mime_fieldval(struct mime_header *header, const char *fieldname)
110 {
111 struct mime_field *field;
112
113 if (!header) return NULL;
114
115 field = mime_getfield(header,fieldname);
116
117 if (!field) return NULL; else return field->value;
118 }
119
120 /* YOU WILL NEED TO FREE THIS RETURN RESULT */
mime_parameter(struct mime_header * header,const char * fieldname,const char * paramname)121 const char *mime_parameter(struct mime_header *header, const char *fieldname,
122 const char *paramname)
123 {
124 char temp[BIG_BUF], temp2[SMALL_BUF];
125 char *tptr, *tptr2;
126 int done, eattrail, length;
127 struct mime_field *field;
128 int escape;
129
130 field = mime_getfield(header,fieldname);
131 if (!field) return NULL;
132 if (!field->params) return NULL;
133
134 /* Get some sane temporary workspace */
135 buffer_printf(temp, sizeof(temp) - 1, "%s", field->params);
136
137 tptr = strtok(temp,";");
138
139 done = 0;
140
141 while (tptr && !done) {
142 /* Clear our temporary buffer */
143 memset(temp2, 0, sizeof(temp2));
144
145 tptr2 = &temp2[0];
146
147 while (*tptr && (*tptr != '=') && tptr2 < temp2 + sizeof(temp2) - 1) {
148 if (!isspace((int)*tptr)) *tptr2++ = *tptr;
149 tptr++;
150 }
151
152 /* Is this our parameter? */
153 if (strcasecmp(temp2,paramname) == 0) {
154 char *endspace = NULL ;
155 done = 1; eattrail = 1;
156 tptr++;
157
158 escape = 0;
159
160 /* Clear our temporary buffer */
161 memset(temp2, 0, sizeof(temp2));
162
163 tptr2 = &temp2[0];
164
165 while (*tptr && (*tptr != ';') && tptr2 < temp2 + sizeof(temp2) - 1) {
166 if ( (!escape) && isspace((int)*tptr) ) {
167 if (!eattrail) {
168 /* We store the position to remove end spaces */
169 if (!endspace) endspace = tptr2 ;
170 *tptr2++ = *tptr;
171 }
172 } else {
173 eattrail = 0;
174 endspace = NULL ;
175
176 if ( !(*tptr == '\"' || *tptr == '\\') || escape) {
177 *tptr2++ = *tptr;
178 escape = 0;
179 }
180 else { /* this is to avoid a '\' to escape itself */
181 if (*tptr == '\\') escape = 1;
182 }
183 }
184
185 tptr++;
186 }
187
188 if (endspace) *endspace = 0 ;
189
190 } else tptr = strtok(NULL,";");
191 }
192
193 if (!done) return NULL;
194
195 length = strlen(temp2);
196
197 tptr = (char *)malloc(length + 1);
198 memset(tptr,0,length + 1);
199 buffer_printf(tptr,length + 1,"%s",temp2);
200
201 return tptr;
202 }
203
mime_newheader(struct mime_field * field)204 struct mime_header *mime_newheader(struct mime_field *field)
205 {
206 struct mime_header *header;
207
208 if (!field) return NULL;
209
210 header = (struct mime_header *)malloc(sizeof(struct mime_header));
211 header->numfields = 1;
212 header->fields = (struct mime_field **)malloc(sizeof(struct mime_field *));
213 header->fields[0] = field;
214
215 return header;
216 }
217
mime_addheader(struct mime_header * header,struct mime_field * field)218 struct mime_header * mime_addheader(struct mime_header *header,
219 struct mime_field *field)
220 {
221 if (!header) return mime_newheader(field);
222 if (!field) return header;
223
224 header->numfields++;
225
226 header->fields = (struct mime_field **)realloc(header->fields,
227 header->numfields * sizeof(struct mime_field *));
228
229 header->fields[header->numfields - 1] = field;
230
231 return header;
232 }
233
234
mime_makefield(FILE * instream,const char * line,int * readlast)235 struct mime_field *mime_makefield(FILE *instream, const char *line,
236 int *readlast)
237 {
238 char tempbuf[BIG_BUF];
239 char finalbuf[HUGE_BUF];
240 char *name, *value, *params;
241 char *tptr, *tptr2;
242 struct mime_field *field;
243
244 *readlast = 0;
245
246 if (!line) return NULL;
247
248 memset(tempbuf, 0, sizeof(tempbuf));
249 memset(finalbuf, 0, sizeof(finalbuf));
250 strncpy(finalbuf, line, HUGE_BUF-1);
251
252 /* The use of BIG_BUF here is deliberate */
253 if (finalbuf[strlen(finalbuf) - 1] == '\n')
254 finalbuf[strlen(finalbuf) - 1] = 0;
255
256 if (!finalbuf[0]) return NULL;
257
258 tptr = strchr(finalbuf,':');
259 if (!tptr) return NULL;
260
261 *tptr++ = 0;
262
263 name = (char *)malloc(strlen(finalbuf) + 1);
264 buffer_printf(name,strlen(finalbuf) + 1,"%s",finalbuf);
265
266 /* Eat whitespace */
267 while (isspace((int)*tptr)) tptr++;
268
269 tptr2 = strchr(tptr,';');
270 if (tptr2) *tptr2++ = 0;
271
272 value = (char *)malloc(strlen(tptr) + 1);
273 buffer_printf(value,strlen(tptr) + 1,"%s",tptr);
274
275 params = NULL;
276
277 if (tptr2) {
278 tptr = tptr2;
279 while (isspace((int)*tptr)) tptr++;
280
281 params = (char *)malloc(strlen(tptr) + 1);
282 buffer_printf(params,strlen(tptr) + 1,"%s",tptr);
283 }
284
285 field = (struct mime_field *)malloc(sizeof(struct mime_field));
286 field->name = name;
287 field->value = value;
288 field->params = params;
289
290 log_printf(50, "field name: .%s.\n", field->name);
291 log_printf(50, "field value: .%s.\n", field->value);
292 log_printf(50, "field params: .%s.\n", field->params);
293
294 return field;
295 }
296
new_mime_handlers()297 void new_mime_handlers()
298 {
299 mime_handlers = NULL;
300 mimecounter = 0;
301 }
302
nuke_mime_handlers()303 void nuke_mime_handlers()
304 {
305 struct mime_handler *handler, *temp;
306
307 handler = mime_handlers;
308
309 while (handler) {
310 temp = handler->next;
311 if(handler->mimetype) free(handler->mimetype);
312 free(handler);
313 handler = temp;
314 }
315 mime_handlers = NULL;
316 }
317
add_mime_handler(const char * mimetype,int priority,MimeFn function)318 void add_mime_handler(const char *mimetype, int priority, MimeFn function)
319 {
320 struct mime_handler *handler, **temp;
321
322 handler = (struct mime_handler *)malloc(sizeof(struct mime_handler));
323 handler->mimetype = lowerstr(mimetype);
324 handler->handler = function;
325 handler->priority = priority;
326 handler->next = mime_handlers;
327
328 temp = &mime_handlers;
329
330 while(*temp && ((*temp)->priority <= priority))
331 temp = &((*temp)->next);
332
333 handler->next = *temp;
334 *temp = handler;
335 }
336
get_mime_handler(const char * mimetype)337 struct mime_handler *get_mime_handler(const char *mimetype)
338 {
339 struct mime_handler *handler;
340 char *tempstring;
341
342 if (!mimetype) return NULL;
343
344 tempstring = lowerstr(mimetype);
345
346 handler = mime_handlers;
347
348 while (handler) {
349 regexp *treg;
350
351 treg = regcomp((char *)handler->mimetype);
352 if (treg) {
353 if (regexec(treg, tempstring)) {
354 free(treg); free(tempstring); return handler;
355 }
356 free(treg);
357 }
358 handler = handler->next;
359 }
360
361 free(tempstring);
362
363 return NULL;
364 }
365
MIME_HANDLER(mimehandle_multipart_default)366 MIME_HANDLER(mimehandle_multipart_default)
367 {
368 FILE *infile, *outfile, *outfile2;
369 char templine[BIG_BUF];
370 const char *bound;
371 struct mime_field *field;
372 int done=0, readlast, firstcont, loop2;
373 int firstfile;
374 char overridetype[BIG_BUF];
375 int override;
376
377 if ((infile = open_file(mimefile,"r")) == NULL) return MIME_HANDLE_FAIL;
378
379 bound = NULL;
380
381 loop2 = 0;
382
383 firstfile = 1;
384
385 bound = mime_parameter(header,"content-type","boundary");
386
387 firstcont = 1;
388
389 while (loop2 ? !done : read_file(templine, sizeof(templine), infile) && !done) {
390 struct mime_header *subheader;
391 const char *temp;
392
393 subheader = NULL;
394
395 readlast = 0; override = 0;
396
397 loop2 = 1;
398
399 if (strlen(templine) > 3 ? strncmp(&templine[2],bound,strlen(bound)) == 0 : 0)
400 loop2 = 0;
401
402 while(!readlast) {
403 read_mime_line(templine, sizeof(templine), infile, loop2);
404 field = mime_makefield(infile,templine,&readlast);
405 loop2 = 0;
406 if (field) {
407 subheader = mime_addheader(subheader,field);
408 } else readlast = 1;
409
410 /* Handle GNUs' odd MIME implementation where it's valid
411 * for the first attachment to have no content-type and thus
412 * count as text/plain... fooie.
413 *
414 * This is pretty hackish, but, eh... */
415 if (readlast && !subheader && firstfile) {
416
417 field = (struct mime_field *)malloc(sizeof(struct mime_field));
418
419 field->name = strdup("Content-type");
420 field->value = strdup("text/plain");
421 field->params = NULL;
422
423 subheader = mime_addheader(subheader,field);
424 }
425 }
426
427 loop2 = 1;
428
429 /* This links in with PantoMIME, if installed */
430 if (get_bool("unmime-forceweb") && !firstfile && !get_bool("unmime-moderate-mode")) {
431 override = 1;
432 buffer_printf(overridetype, sizeof(overridetype) - 1, "ecartis-internal/pantomime");
433 }
434
435 /* Ok, now, yes this IS a cheap hack, forcing ALL attachments,
436 no matter the type, to be stripped. */
437 if (strcmp(get_string("mode"), "bounce") &&
438 !get_bool("unmime-moderate-mode") &&
439 get_bool("rabid-mime") && !firstfile) {
440 override = 1;
441 buffer_printf(overridetype, sizeof(overridetype) - 1, "ecartis-internal/rabid");
442 }
443
444 /* Here's another cheap hack. This prevents someone from having
445 something that strips message/rfc822 down to just a few headers,
446 and thus having it break something. */
447 if (get_bool("unmime-moderate-mode") &&
448 subheader ? strcasecmp(mime_fieldval(subheader,"content-type"),"message/rfc822") == 0 : 0) {
449 override = 1;
450 buffer_printf(overridetype, sizeof(overridetype) - 1,
451 "ecartis-internal/moderate");
452 }
453
454 firstfile = 0;
455
456 temp = mime_fieldval(subheader,"content-type");
457 if (temp) {
458 char subfilename[BIG_BUF];
459 int coding;
460 int donechunk;
461 int haveread, havewritten;
462 struct mime_handler *handler;
463
464 coding = 0;
465
466 donechunk = 0;
467
468 havewritten = 0;
469
470 if (!override) {
471 handler = get_mime_handler(temp);
472 } else {
473 handler = get_mime_handler(overridetype);
474 }
475
476 /* Hey, look! The EASY way to do multipart/alternative <grin> */
477 if (strcasecmp(mime_fieldval(header,"content-type"),"multipart/alternative") == 0) {
478 if (strcasecmp(mime_fieldval(subheader,"content-type"),"text/plain") != 0)
479 handler = NULL;
480 }
481
482 if (!handler) {
483 /* What do we do here? */
484
485 while(read_file(templine, sizeof(templine), infile) && !donechunk) {
486 if (strlen(templine) > 3 ? strncasecmp(&templine[2],
487 bound, strlen(bound)) == 0 : 0) {
488 donechunk = 1; haveread = 1; havewritten = 0;
489 }
490 }
491
492 if (!donechunk) done = 1;
493
494 } else {
495 buffer_printf(subfilename, sizeof(subfilename) - 1, "%s.%d", mimefile, mimecounter++);
496
497 outfile = open_file(subfilename,"w");
498
499 temp = mime_fieldval(subheader,"content-transfer-encoding");
500
501 if (temp) {
502 if (strcasecmp(temp,"base64") == 0) coding = 1; else
503 if (strcasecmp(temp,"quoted-printable") == 0) coding = 2;
504 else coding = 0;
505 }
506
507 haveread = 0;
508
509 switch(coding) {
510 case 0:
511 while(read_file(templine, sizeof(templine), infile) && !donechunk) {
512 if (strlen(templine) > 3 ?
513 (strncmp(&templine[2],bound,strlen(bound)) == 0)
514 : 0) {
515 donechunk = 1;
516 } else write_file(outfile,"%s",templine);
517 haveread = 1;
518 }
519 close_file(outfile);
520 handler->handler(subheader,subfilename);
521 havewritten = 1;
522 break;
523 case 1:
524 {
525 char **boundaries;
526 int boundcount;
527 char tbuf[BIG_BUF];
528
529 boundaries = (char **)malloc(2 * sizeof(char *));
530 buffer_printf(tbuf, sizeof(tbuf) - 1, "--%s", bound);
531 boundaries[0] = &tbuf[0];
532 boundcount = 1;
533
534 from64(infile,outfile,boundaries,&boundcount);
535 close_file(outfile);
536 handler->handler(subheader,subfilename);
537 loop2 = 0;
538 havewritten = 1;
539 }
540 break;
541 case 2:
542 {
543 char **boundaries;
544 int boundcount;
545 char tbuf[BIG_BUF];
546
547 boundaries = (char **)malloc(2 * sizeof(char *));
548 buffer_printf(tbuf, sizeof(tbuf) - 1, "--%s", bound);
549 boundaries[0] = &tbuf[0];
550 boundcount = 1;
551
552 fromqp(infile,outfile,boundaries,&boundcount);
553 close_file(outfile);
554 handler->handler(subheader,subfilename);
555 havewritten = 1;
556 loop2 = 0;
557 }
558 break;
559 case 3:
560 close_file(outfile);
561 break;
562 }
563
564 if (havewritten) {
565 FILE *infile2;
566 char tempbuffer[BIG_BUF];
567 char tempfilename[BIG_BUF];
568
569 buffer_printf(tempfilename, sizeof(tempfilename) - 1, "%s.output", mimefile);
570
571 if ((outfile2 = open_file(tempfilename,"a")) == NULL) {
572 /* bad stuff happens here */
573
574 close_file(infile);
575 unlink_file(subfilename);
576 return MIME_HANDLE_FAIL;
577 }
578
579 if ((infile2 = open_file(subfilename,"r")) == NULL) {
580 write_file(outfile2,"-- Unable to access decoded file.\n");
581 close_file(outfile2);
582 } else {
583 while (read_file(tempbuffer, sizeof(tempbuffer), infile2)) {
584 write_file(outfile2,"%s",tempbuffer);
585 }
586 write_file(outfile2,"\n");
587 close_file(infile2);
588 close_file(outfile2);
589 unlink_file(subfilename);
590 }
591 } else {
592 unlink_file(subfilename);
593 }
594 }
595 } else {
596 done = 1;
597 }
598 }
599
600 close_file(infile);
601 if(bound) free((char *)bound);
602
603 buffer_printf(templine, sizeof(templine) - 1, "%s.output", mimefile);
604 replace_file(templine,mimefile);
605
606 return MIME_HANDLE_OK;
607 }
608
MIME_HANDLER(mimehandle_text)609 MIME_HANDLER(mimehandle_text)
610 {
611 FILE *tempfile, *infile;
612 char tempfilename[BIG_BUF], tempbuf[BIG_BUF];
613 const char *temp;
614 int coding = 0;
615
616 buffer_printf(tempfilename, sizeof(tempfilename) - 1, "%s.altertext", mimefile);
617 if ((infile = open_file(mimefile,"r")) == NULL) {
618 return MIME_HANDLE_FAIL;
619 }
620 if ((tempfile = open_file(tempfilename,"w")) == NULL) {
621 close_file(infile);
622 return MIME_HANDLE_FAIL;
623 }
624
625 if (!get_bool("unmime-first-level")) {
626 write_file(tempfile,"-- Attached file included as plaintext by %s --\n",
627 SERVICE_NAME_MC);
628
629 temp = mime_parameter(header,"content-type","name");
630 if (temp) {
631 write_file(tempfile,"-- File: %s\n", temp);
632 free((char *)temp);
633 }
634 temp = mime_fieldval(header,"content-description");
635 if (temp) {
636 if (*temp) write_file(tempfile,"-- Desc: %s\n", temp);
637 }
638 write_file(tempfile,"\n");
639 } else clean_var("unmime-first-level", VAR_TEMP);
640
641
642 // Someone might have done some encoding *grrr*
643 temp = mime_fieldval(header, "content-transfer-encoding");
644 if(temp) {
645 if (strcasecmp(temp, "base64") == 0) coding = 1;
646 else if (strcasecmp(temp, "quoted-printable") == 0) coding = 2;
647 }
648 switch (coding) {
649 case 2: {
650 fromqp(infile, tempfile, NULL, NULL);
651 break;
652 }
653 case 1: {
654 from64(infile, tempfile, NULL, NULL);
655 break;
656 }
657 default: {
658 while(read_file(tempbuf, sizeof(tempbuf), infile)) {
659 write_file(tempfile,"%s",tempbuf);
660 }
661 break;
662 }
663 }
664 close_file(infile);
665 close_file(tempfile);
666
667 replace_file(tempfilename,mimefile);
668
669 return MIME_HANDLE_OK;
670 }
671
MIME_HANDLER(mimehandle_moderate)672 MIME_HANDLER(mimehandle_moderate)
673 {
674 clean_var("unmime-moderate-mode",VAR_GLOBAL);
675
676 do_moderate(mimefile);
677 unlink_file(mimefile);
678
679 set_var("unmime-moderate-mode","yes",VAR_GLOBAL);
680
681 return MIME_HANDLE_OK;
682 }
683
684
MIME_HANDLER(mimehandle_unknown)685 MIME_HANDLER(mimehandle_unknown)
686 {
687 FILE *tempfile, *infile;
688 char tempfilename[BIG_BUF];
689 const char *temp;
690
691 clean_var("unmime-first-level", VAR_TEMP);
692
693 buffer_printf(tempfilename, sizeof(tempfilename) - 1, "%s.alterimg", mimefile);
694 if ((infile = open_file(mimefile,"r")) == NULL) {
695 return MIME_HANDLE_FAIL;
696 }
697 if ((tempfile = open_file(tempfilename,"w")) == NULL) {
698 close_file(infile);
699 return MIME_HANDLE_FAIL;
700 }
701
702 if (!get_bool("unmime-quiet")) {
703 write_file(tempfile,"-- Binary/unsupported file stripped by %s --\n",
704 SERVICE_NAME_MC);
705
706 temp = mime_fieldval(header,"content-type");
707 if (temp) {
708 write_file(tempfile,"-- Type: %s\n", temp);
709 }
710
711 temp = mime_parameter(header,"content-type","name");
712 if (temp) {
713 write_file(tempfile,"-- File: %s\n", temp);
714 free((char *)temp);
715 }
716
717 temp = mime_fieldval(header,"content-description");
718 if (temp) {
719 if (*temp) write_file(tempfile,"-- Desc: %s\n", temp);
720 }
721
722 write_file(tempfile,"\n");
723 }
724
725 close_file(infile);
726 close_file(tempfile);
727
728 replace_file(tempfilename,mimefile);
729
730 return MIME_HANDLE_OK;
731 }
732
MIME_HANDLER(mimehandle_rabid)733 MIME_HANDLER(mimehandle_rabid)
734 {
735 FILE *tempfile, *infile;
736 char tempfilename[BIG_BUF];
737 const char *temp;
738
739 clean_var("unmime-first-level", VAR_TEMP);
740
741 buffer_printf(tempfilename, sizeof(tempfilename) - 1, "%s.alterimg", mimefile);
742 if ((infile = open_file(mimefile,"r")) == NULL) {
743 return MIME_HANDLE_FAIL;
744 }
745 if ((tempfile = open_file(tempfilename,"w")) == NULL) {
746 close_file(infile);
747 return MIME_HANDLE_FAIL;
748 }
749
750 if (!get_bool("unmime-quiet")) {
751 write_file(tempfile,"-- No attachments (even text) are allowed --\n");
752
753 temp = mime_fieldval(header,"content-type");
754 if (temp) {
755 write_file(tempfile,"-- Type: %s\n", temp);
756 }
757
758 temp = mime_parameter(header,"content-type","name");
759 if (temp) {
760 write_file(tempfile,"-- File: %s\n", temp);
761 free((char *)temp);
762 }
763
764 temp = mime_fieldval(header,"content-description");
765 if (temp) {
766 if (*temp) write_file(tempfile,"-- Desc: %s\n", temp);
767 }
768
769 write_file(tempfile,"\n");
770 }
771
772 close_file(infile);
773 close_file(tempfile);
774
775 replace_file(tempfilename,mimefile);
776
777 return MIME_HANDLE_OK;
778 }
779
780
unmime_file(const char * file1,const char * file2)781 void unmime_file(const char *file1, const char *file2)
782 {
783 FILE *infile, *infile2, *outfile;
784 char templine[BIG_BUF], tempfile[BIG_BUF];
785 const char *bound, *charsetp;
786 char charset[SMALL_BUF];
787 struct mime_header *header;
788 struct mime_field *field;
789 struct mime_handler *handler;
790 int done=0, readlast, firstcont;
791 int inbody;
792 int eatline, encoding;
793
794 clean_var("just-unmimed", VAR_GLOBAL);
795
796 if (get_bool("unmimed-file")) return;
797
798 set_var("unmime-first-level","yes",VAR_TEMP);
799
800 if ((infile = open_file(file1,"r")) == NULL) return;
801
802 buffer_printf(tempfile, sizeof(tempfile) - 1, "%s.demime", file2);
803
804 header = NULL;
805 bound = NULL;
806 firstcont = 1;
807
808 inbody = 0;
809 outfile = NULL;
810
811 readlast = 0;
812
813 while (read_mime_line(templine, sizeof(templine), infile, inbody) && !done) {
814
815 if ((strncasecmp("Content-type:",templine,13) == 0) && firstcont && !inbody) {
816 field = mime_makefield(infile, templine,&readlast);
817 if (field) header = mime_addheader(header,field);
818 if (readlast) inbody = 1;
819
820 bound = mime_parameter(header,"content-type","boundary");
821 firstcont = 0;
822 }
823
824 if ((strncasecmp(templine,"Content-transfer-encoding:",26) == 0) &&
825 !inbody) {
826 field = mime_makefield(infile, templine,&readlast);
827 if (field) header = mime_addheader(header,field);
828 if (readlast) inbody = 1;
829 }
830
831 if ((templine[0] == '\n') && !inbody) {
832
833 /* If header is NULL, there was no Content-type line. If there
834 * was no Content-type line, this isn't a MIME message. */
835 if (!header) {
836 close_file(infile);
837 return;
838 }
839
840 /* For moderation - the logic behind this is that you might
841 * conceivably have a text/plain handler and we do NOT want
842 * it called during moderation. -- likewise on message/rfc822 */
843 if (get_bool("unmime-moderate-mode")) {
844 const char *content = mime_fieldval(header,"content-type");
845
846 if (strcasecmp(content,"text/plain") == 0 ||
847 strcasecmp(content,"message/rfc822") == 0) {
848 /* let's clean up our header and bail */
849 mime_eat_header(header);
850 close_file(infile);
851 return;
852 }
853 }
854
855 inbody = 1;
856 firstcont = 1;
857 }
858 if (inbody) {
859 const char *content = mime_fieldval(header,"content-type");
860
861 if (firstcont) {
862 if ((strlen(templine) > 3 && bound) ? strncmp(&templine[2],bound,strlen(bound)) == 0 : 0) {
863 firstcont = 0;
864 outfile = open_file(tempfile,"w");
865 } else if (content) {
866 if (!strncasecmp(content,"multipart",9) == 0) {
867 firstcont = 0;
868 outfile = open_file(tempfile,"w");
869 }
870 }
871 }
872 if (outfile && inbody && !firstcont)
873 write_file(outfile,"%s",templine);
874 }
875 }
876
877 close_file(infile);
878 if(outfile) close_file(outfile);
879
880 memset(charset, 0, sizeof(charset));
881
882 charsetp = mime_parameter(header,"content-type","charset");
883 if (charsetp) {
884 buffer_printf(charset, sizeof(charset) - 1, "%s", charsetp);
885 free((char *)charsetp);
886 }
887
888 handler = get_mime_handler(mime_fieldval(header,"content-type"));
889 if (handler) {
890 handler->handler(header,tempfile);
891 }
892
893 mime_eat_header(header);
894 free(header);
895 header = NULL;
896 if(bound) free((char *)bound);
897
898 if (get_bool("unmime-moderate-mode")) {
899 set_var("unmimed-file", "yes", VAR_GLOBAL);
900 set_var("just-unmimed", "yes", VAR_GLOBAL);
901 }
902
903 if ((infile = open_file(file1,"r")) == NULL) {
904 unlink_file(tempfile);
905 return;
906 }
907
908 if ((infile2 = open_file(tempfile,"r")) == NULL) {
909 close_file(infile);
910 unlink_file(tempfile);
911 return;
912 }
913
914 if ((outfile = open_file(file2,"w")) == NULL) {
915 close_file(infile);
916 unlink_file(tempfile);
917 return;
918 }
919
920 done = 0; eatline = 0;
921 encoding = 0;
922
923 while(read_file(templine, sizeof(templine), infile) && !done) {
924 if (templine[0] == '\n') done = 1;
925
926 /* Eat the transfer-encoding line */
927 else if (strncasecmp(templine,"Content-transfer-encoding:",26) == 0) {
928 write_file(outfile,"Content-Transfer-Encoding: 8bit\n");
929 encoding = 1;
930 }
931
932 /* Handle content-type line */
933 else if (strncasecmp(templine,"Content-type:",13) == 0) {
934 write_file(outfile,"Content-type: text/plain");
935
936 /* Preserve character set if explicit */
937 if (charset[0]) write_file(outfile, "; charset=%s", charset);
938
939 write_file(outfile,"\n");
940 eatline = 1;
941 } else if (!isspace((int)templine[0]) || !eatline) {
942 eatline = 0;
943 write_file(outfile,"%s",templine);
944 }
945 }
946 close_file(infile);
947
948 if (!encoding) {
949 write_file(outfile,"Content-Transfer-Encoding: 8bit\n");
950 }
951
952 write_file(outfile,"\n");
953
954 done = 0;
955
956 while (read_file(templine, sizeof(templine), infile2)) {
957 /* well, this is an UGLY hack */
958 if (templine[0] == '\n' && !done) { done++; continue; }
959 write_file(outfile,"%s",templine);
960 }
961
962 close_file(infile2);
963 close_file(outfile);
964 unlink_file(tempfile);
965
966 set_var("unmimed-file", "yes", VAR_GLOBAL);
967 set_var("just-unmimed", "yes", VAR_GLOBAL);
968
969 return;
970 }
971
unquote_string(const char * orig,char * dest,int len)972 void unquote_string(const char *orig, char *dest, int len)
973 {
974 if(!orig || !dest || ((unsigned int)len < strlen(orig))) {
975 if(dest && orig) {
976 /* we need to have something in the dest, so */
977 strncpy(dest, orig, len - 1);
978 } else if(dest) {
979 strncpy(dest, "", len - 1);
980 }
981 }
982 rfc2047_decode(orig, dest, len);
983 }
984
requote_string(const char * orig,char * dest,int len)985 void requote_string(const char *orig, char *dest, int len)
986 {
987 toqps((unsigned const char *)orig, dest, len);
988 }
989
unquote_file(const char * file1,const char * file2)990 void unquote_file(const char *file1, const char *file2)
991 {
992 char tbuf[BIG_BUF];
993 char tempfilename[SMALL_BUF];
994 int doneheader;
995 int done = 0;
996 FILE *infile, *outfile;
997
998 if (get_bool("humanize-mime")) return;
999
1000 clean_var("just-unquoted", VAR_GLOBAL);
1001
1002 if(get_bool("unquoted-file")) return;
1003
1004 if (!exists_file(file1)) return;
1005
1006 if ((infile = open_file(file1,"r")) == NULL)
1007 return;
1008
1009 buffer_printf(tempfilename, sizeof(tempfilename) - 1, "%s.tmpquote", file2);
1010
1011 if ((outfile = open_file(file2,"w")) == NULL) {
1012 close_file(infile);
1013 return;
1014 }
1015
1016 doneheader = 0;
1017
1018 while(!done && read_mime_line(tbuf, sizeof(tbuf), infile, doneheader)) {
1019 log_printf(85, "tbuf: .%s.\n", tbuf);
1020 if(!doneheader &&
1021 (strncasecmp(tbuf,"Content-Transfer-Encoding:",26) == 0)) {
1022 char *tptr = &tbuf[27];
1023 if(strncasecmp(tptr, "quoted-printable", 16) == 0) {
1024 write_file(outfile,"Content-Transfer-Encoding: 8bit\n");
1025 write_file(outfile,"X-MIME-Autoconverted: from quoted-printable to 8bit by %s\n", SERVICE_NAME_MC);
1026 fromqp(infile, outfile, NULL, NULL);
1027 done = 1;
1028 } else {
1029 write_file(outfile, "%s", tbuf);
1030 }
1031 } else if (strncasecmp(tbuf,"Content-type:",13) == 0) {
1032 int readlast = 0;
1033 struct mime_field *field;
1034 struct mime_header *header;
1035
1036 field = mime_makefield(infile, tbuf,&readlast);
1037 if (field) {
1038 const char *encoding, *charset;
1039
1040 header = NULL;
1041
1042 header = mime_addheader(header,field);
1043
1044 charset = mime_parameter(header, "content-type", "charset") ;
1045 /* We have a charset, let's register it... */
1046 if (charset) {
1047 if (!get_var("headers-charset")) {
1048 if (!get_var("headers-charset-frombody")) {
1049 log_printf(5, "Setting frombody charset : %s\n", charset);
1050 set_var("headers-charset-frombody", charset, VAR_GLOBAL);
1051 }
1052 }
1053 }
1054
1055 encoding = mime_parameter(header, "content-type", "content-encoding");
1056 if (encoding) {
1057 /* We got an encoding, let's deal about it */
1058 if (strncasecmp(encoding, "quoted-printable", 16) == 0) {
1059 /* What about un-quoted-printabling ? */
1060
1061 /* this is a bad hack as we should try to rebuild the
1062 * Content-Type field by modifying the content-encoding instead
1063 * of making a new one from scratch
1064 * This is mostly a quick fix and I am in lazy mode */
1065 if (charset) {
1066 write_file(outfile,"Content-type: %s; charset=\"%s\"\n", mime_fieldval(header, "content-type"), charset) ;
1067 } else {
1068 write_file(outfile,"Content-type: %s\n", mime_fieldval(header, "content-type"));
1069 }
1070
1071 write_file(outfile,"Content-Transfer-Encoding: 8bit\n");
1072 write_file(outfile,"X-MIME-Autoconverted: from quoted-printable to 8bit by %s\n", SERVICE_NAME_MC);
1073 fromqp(infile, outfile, NULL, NULL);
1074 done = 1;
1075 } else {
1076 /* I am lazy, I already have the field Content-Type */
1077 if (field->params) {
1078 write_file(outfile, "Content-Type: %s; %s\n", field->value, field->params) ;
1079 } else {
1080 write_file(outfile, "Content-Type: %s\n", field->value) ;
1081 }
1082 }
1083 } else {
1084 /* Still lazy... :-) */
1085 if (field->params) {
1086 write_file(outfile, "Content-Type: %s; %s\n", field->value, field->params) ;
1087 } else {
1088 write_file(outfile, "Content-Type: %s\n", field->value) ;
1089 }
1090 }
1091 }
1092
1093 } else {
1094 write_file(outfile, "%s", tbuf);
1095 if (tbuf[0] == '\n') {
1096 doneheader = 1;
1097 }
1098 }
1099 }
1100 close_file(infile);
1101 close_file(outfile);
1102
1103 set_var("just-unquoted","yes",VAR_GLOBAL);
1104 set_var("unquoted-file","yes",VAR_GLOBAL);
1105 }
1106