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