0ab7877412864722b58537debd7605b60c9dac0b
[exim.git] / src / src / demime.c
1 /* $Cambridge: exim/src/src/demime.c,v 1.8 2005/08/01 14:41:25 ph10 Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
8 /* License: GPL */
9
10 /* Code for unpacking MIME containers. Called from acl.c. */
11
12 #include "exim.h"
13 #ifdef WITH_OLD_DEMIME
14
15 #include "demime.h"
16
17 uschar demime_reason_buffer[1024];
18 struct file_extension *file_extensions = NULL;
19
20 int demime(uschar **listptr) {
21   int sep = 0;
22   uschar *list = *listptr;
23   uschar *option;
24   uschar option_buffer[64];
25   unsigned long mbox_size;
26   FILE *mbox_file;
27   uschar defer_error_buffer[1024];
28   int demime_rc = 0;
29
30   /* reset found_extension variable */
31   found_extension = NULL;
32
33   /* try to find 1st option */
34   if ((option = string_nextinlist(&list, &sep,
35                                   option_buffer,
36                                   sizeof(option_buffer))) != NULL) {
37
38     /* parse 1st option */
39     if ( (Ustrcmp(option,"false") == 0) || (Ustrcmp(option,"0") == 0) ) {
40       /* explicitly no demimeing */
41       return FAIL;
42     };
43   }
44   else {
45     /* no options -> no demimeing */
46     return FAIL;
47   };
48
49   /* make sure the eml mbox file is spooled up */
50   mbox_file = spool_mbox(&mbox_size);
51
52   if (mbox_file == NULL) {
53     /* error while spooling */
54     log_write(0, LOG_MAIN|LOG_PANIC,
55            "demime acl condition: error while creating mbox spool file");
56     return DEFER;
57   };
58
59   /* call demimer if not already done earlier */
60   if (!demime_ok)
61     demime_rc = mime_demux(mbox_file, defer_error_buffer);
62
63   (void)fclose(mbox_file);
64
65   if (demime_rc == DEFER) {
66     /* temporary failure (DEFER => DEFER) */
67     log_write(0, LOG_MAIN,
68         "demime acl condition: %s", defer_error_buffer);
69     return DEFER;
70   };
71
72   /* set demime_ok to avoid unpacking again */
73   demime_ok = 1;
74
75   /* check for file extensions, if there */
76   while (option != NULL) {
77     struct file_extension *this_extension = file_extensions;
78
79     /* Look for the wildcard. If it is found, we always return true.
80     The user must then use a custom condition to evaluate demime_errorlevel */
81     if (Ustrcmp(option,"*") == 0) {
82       found_extension = NULL;
83       return OK;
84     };
85
86     /* loop thru extension list */
87     while (this_extension != NULL) {
88       if (strcmpic(option, this_extension->file_extension_string) == 0) {
89         /* found one */
90         found_extension = this_extension->file_extension_string;
91         return OK;
92       };
93       this_extension = this_extension->next;
94     };
95
96     /* grab next extension from option list */
97     option = string_nextinlist(&list, &sep,
98                                option_buffer,
99                                sizeof(option_buffer));
100   };
101
102   /* nothing found */
103   return FAIL;
104 }
105
106
107 /*************************************************
108 * small hex_str -> integer conversion function   *
109 *************************************************/
110
111 /* needed for quoted-printable
112 */
113
114 unsigned int mime_hstr_i(uschar *cptr) {
115   unsigned int i, j = 0;
116
117   while (cptr && *cptr && isxdigit(*cptr)) {
118     i = *cptr++ - '0';
119     if (9 < i) i -= 7;
120     j <<= 4;
121     j |= (i & 0x0f);
122   }
123
124   return(j);
125 }
126
127
128 /*************************************************
129 * decode quoted-printable chars                  *
130 *************************************************/
131
132 /* gets called when we hit a =
133    returns: new pointer position
134    result code in c:
135           -2 - decode error
136           -1 - soft line break, no char
137            0-255 - char to write
138 */
139
140 uschar *mime_decode_qp(uschar *qp_p,int *c) {
141   uschar hex[] = {0,0,0};
142   int nan = 0;
143   uschar *initial_pos = qp_p;
144
145   /* advance one char */
146   qp_p++;
147
148   REPEAT_FIRST:
149   if ( (*qp_p == '\t') || (*qp_p == ' ') || (*qp_p == '\r') )  {
150     /* tab or whitespace may follow
151        just ignore it, but remember
152        that this is not a valid hex
153        encoding any more */
154     nan = 1;
155     qp_p++;
156     goto REPEAT_FIRST;
157   }
158   else if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F'))  || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
159     /* this is a valid hex char, if nan is unset */
160     if (nan) {
161       /* this is illegal */
162       *c = -2;
163       return initial_pos;
164     }
165     else {
166       hex[0] = *qp_p;
167       qp_p++;
168     };
169   }
170   else if (*qp_p == '\n') {
171     /* hit soft line break already, continue */
172     *c = -1;
173     return qp_p;
174   }
175   else {
176     /* illegal char here */
177     *c = -2;
178     return initial_pos;
179   };
180
181   if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F')) || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
182     if (hex[0] > 0) {
183       hex[1] = *qp_p;
184       /* do hex conversion */
185       *c = mime_hstr_i(hex);
186       qp_p++;
187       return qp_p;
188     }
189     else {
190       /* huh ? */
191       *c = -2;
192       return initial_pos;
193     };
194   }
195   else {
196     /* illegal char */
197     *c = -2;
198     return initial_pos;
199   };
200
201 }
202
203
204 /*************************************************
205 * open new dump file                             *
206 *************************************************/
207
208 /* open new dump file
209    returns: -2 soft error
210             or file #, FILE * in f
211 */
212
213 int mime_get_dump_file(uschar *extension, FILE **f, uschar *info) {
214   uschar file_name[1024];
215   int result;
216   unsigned int file_nr;
217   uschar default_extension[] = ".com";
218   uschar *p;
219
220   if (extension == NULL)
221     extension = default_extension;
222
223   /* scan the proposed extension.
224      if it is longer than 4 chars, or
225      contains exotic chars, use the default extension */
226
227 /*  if (Ustrlen(extension) > 4) {
228     extension = default_extension;
229   };
230 */
231
232   p = extension+1;
233
234   while (*p != 0) {
235     *p = (uschar)tolower((uschar)*p);
236     if ( (*p < 97) || (*p > 122) ) {
237       extension = default_extension;
238       break;
239     };
240     p++;
241   };
242
243   /* find a new file to write to */
244   file_nr = 0;
245   do {
246     struct stat mystat;
247
248     (void)string_format(file_name,1024,"%s/scan/%s/%s-%05u%s",spool_directory,message_id,message_id,file_nr,extension);
249     file_nr++;
250     if (file_nr >= MIME_SANITY_MAX_DUMP_FILES) {
251       /* max parts reached */
252       mime_trigger_error(MIME_ERRORLEVEL_TOO_MANY_PARTS);
253       break;
254     };
255     result = stat(CS file_name,&mystat);
256   }
257   while(result != -1);
258
259   *f = fopen(CS file_name,"wb+");
260   if (*f == NULL) {
261     /* cannot open new dump file, disk full ? -> soft error */
262     (void)string_format(info, 1024,"unable to open dump file");
263     return -2;
264   };
265
266   return file_nr;
267 }
268
269
270 /*************************************************
271 * Find a string in a mime header                 *
272 *************************************************/
273
274 /* Find a string in a mime header, and optionally fill in
275    the value associated with it into *value
276
277    returns: 0 - nothing found
278             1 - found param
279             2 - found param + value
280 */
281
282 int mime_header_find(uschar *header, uschar *param, uschar **value) {
283   uschar *needle;
284
285   needle = strstric(header,param,FALSE);
286   if (needle != NULL) {
287     if (value != NULL) {
288       needle += Ustrlen(param);
289       if (*needle == '=') {
290         uschar *value_start;
291         uschar *value_end;
292
293         value_start = needle + 1;
294         value_end = strstric(value_start,US";",FALSE);
295         if (value_end != NULL) {
296           /* allocate mem for value */
297           *value = (uschar *)malloc((value_end - value_start)+1);
298           if (*value == NULL)
299             return 0;
300
301           Ustrncpy(*value,value_start,(value_end - value_start));
302           (*value)[(value_end - value_start)] = '\0';
303           return 2;
304         };
305       };
306     };
307     return 1;
308   };
309   return 0;
310 }
311
312
313 /*************************************************
314 * Read a line of MIME input                      *
315 *************************************************/
316 /* returns status code, one of
317    MIME_READ_LINE_EOF 0
318    MIME_READ_LINE_OK 1
319    MIME_READ_LINE_OVERFLOW 2
320
321    In header mode, the line will be "cooked".
322 */
323
324 int mime_read_line(FILE *f, int mime_demux_mode, uschar *buffer, long *num_copied) {
325   int c = EOF;
326   int done = 0;
327   int header_value_mode = 0;
328   int header_open_brackets = 0;
329
330   *num_copied = 0;
331
332   while(!done) {
333
334     c = fgetc(f);
335     if (c == EOF) break;
336
337     /* --------- header mode -------------- */
338     if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
339
340       /* always skip CRs */
341       if (c == '\r') continue;
342
343       if (c == '\n') {
344         if ((*num_copied) > 0) {
345           /* look if next char is '\t' or ' ' */
346           c = fgetc(f);
347           if (c == EOF) break;
348           if ( (c == '\t') || (c == ' ') ) continue;
349           (void)ungetc(c,f);
350         };
351         /* end of the header, terminate with ';' */
352         c = ';';
353         done = 1;
354       };
355
356       /* skip control characters */
357       if (c < 32) continue;
358
359       /* skip whitespace + tabs */
360       if ( (c == ' ') || (c == '\t') )
361         continue;
362
363       if (header_value_mode) {
364         /* --------- value mode ----------- */
365         /* skip quotes */
366         if (c == '"') continue;
367
368         /* leave value mode on ';' */
369         if (c == ';') {
370           header_value_mode = 0;
371         };
372         /* -------------------------------- */
373       }
374       else {
375         /* -------- non-value mode -------- */
376         if (c == '\\') {
377           /* quote next char. can be used
378           to escape brackets. */
379           c = fgetc(f);
380           if (c == EOF) break;
381         }
382         else if (c == '(') {
383           header_open_brackets++;
384           continue;
385         }
386         else if ((c == ')') && header_open_brackets) {
387           header_open_brackets--;
388           continue;
389         }
390         else if ( (c == '=') && !header_open_brackets ) {
391           /* enter value mode */
392           header_value_mode = 1;
393         };
394
395         /* skip chars while we are in a comment */
396         if (header_open_brackets > 0)
397           continue;
398         /* -------------------------------- */
399       };
400     }
401     /* ------------------------------------ */
402     else {
403     /* ----------- non-header mode -------- */
404       /* break on '\n' */
405       if (c == '\n')
406         done = 1;
407     /* ------------------------------------ */
408     };
409
410     /* copy the char to the buffer */
411     buffer[*num_copied] = (uschar)c;
412     /* raise counter */
413     (*num_copied)++;
414
415     /* break if buffer is full */
416     if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1) {
417       done = 1;
418     };
419   }
420
421   /* 0-terminate */
422   buffer[*num_copied] = '\0';
423
424   if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1)
425     return MIME_READ_LINE_OVERFLOW;
426   else
427     if (c == EOF)
428       return MIME_READ_LINE_EOF;
429     else
430       return MIME_READ_LINE_OK;
431 }
432
433
434 /*************************************************
435 * Check for a MIME boundary                      *
436 *************************************************/
437
438 /* returns: 0 - no boundary found
439             1 - start boundary found
440             2 - end boundary found
441 */
442
443 int mime_check_boundary(uschar *line, struct boundary *boundaries) {
444   struct boundary *thisboundary = boundaries;
445   uschar workbuf[MIME_SANITY_MAX_LINE_LENGTH+1];
446   unsigned int i,j=0;
447
448   /* check for '--' first */
449   if (Ustrncmp(line,"--",2) == 0) {
450
451     /* strip tab and space */
452     for (i = 2; i < Ustrlen(line); i++) {
453       if ((line[i] != ' ') && (line[i] != '\t')) {
454         workbuf[j] = line[i];
455         j++;
456       };
457     };
458     workbuf[j+1]='\0';
459
460     while(thisboundary != NULL) {
461       if (Ustrncmp(workbuf,thisboundary->boundary_string,Ustrlen(thisboundary->boundary_string)) == 0) {
462         if (Ustrncmp(&workbuf[Ustrlen(thisboundary->boundary_string)],"--",2) == 0) {
463           /* final boundary found */
464           return 2;
465         };
466         return 1;
467       };
468       thisboundary = thisboundary->next;
469     };
470   };
471
472   return 0;
473 }
474
475
476 /*************************************************
477 * Check for start of a UUENCODE block            *
478 *************************************************/
479
480 /* returns 0 for no hit,
481            >0 for hit
482 */
483
484 int mime_check_uu_start(uschar *line, uschar *uu_file_extension, int *has_tnef) {
485
486   if ( (strncmpic(line,US"begin ",6) == 0)) {
487     uschar *uu_filename = &line[6];
488
489     /* skip perms, if present */
490     Ustrtoul(&line[6],&uu_filename,10);
491
492     /* advance one char */
493     uu_filename++;
494
495     /* This should be the filename.
496     Check if winmail.dat is present,
497     which indicates TNEF. */
498     if (strncmpic(uu_filename,US"winmail.dat",11) == 0) {
499       *has_tnef = 1;
500     };
501
502     /* reverse to dot if present,
503     copy up to 4 chars for the extension */
504     if (Ustrrchr(uu_filename,'.') != NULL)
505       uu_filename = Ustrrchr(uu_filename,'.');
506
507     return sscanf(CS uu_filename, "%4[.0-9A-Za-z]",CS uu_file_extension);
508   }
509   else {
510     /* nothing found */
511     return 0;
512   };
513 }
514
515
516 /*************************************************
517 * Decode a uu line                               *
518 *************************************************/
519
520 /* returns number of decoded bytes
521          -2 for soft errors
522 */
523
524 int warned_about_uudec_line_sanity_1 = 0;
525 int warned_about_uudec_line_sanity_2 = 0;
526 long uu_decode_line(uschar *line, uschar **data, long line_len, uschar *info) {
527   uschar *p;
528   long num_decoded = 0;
529   uschar tmp_c;
530   uschar *work;
531   int uu_decoded_line_len, uu_encoded_line_len;
532
533   /* allocate memory for data and work buffer */
534   *data = (uschar *)malloc(line_len);
535   if (*data == NULL) {
536     (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
537     return -2;
538   };
539
540   work = (uschar *)malloc(line_len);
541   if (work == NULL) {
542     (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
543     return -2;
544   };
545
546   memcpy(work,line,line_len);
547
548   /* First char is line length
549   This is microsofts way of getting it. Scary. */
550   if (work[0] < 32) {
551     /* ignore this line */
552     return 0;
553   }
554   else {
555     uu_decoded_line_len = uudec[work[0]];
556   };
557
558   p = &work[1];
559
560   while (*p > 32) {
561     *p = uudec[*p];
562     p++;
563   };
564
565   uu_encoded_line_len = (p - &work[1]);
566   p = &work[1];
567
568   /* check that resulting line length is a multiple of 4 */
569   if ( ( uu_encoded_line_len % 4 ) != 0) {
570     if (!warned_about_uudec_line_sanity_1) {
571       mime_trigger_error(MIME_ERRORLEVEL_UU_MISALIGNED);
572       warned_about_uudec_line_sanity_1 = 1;
573     };
574     return -1;
575   };
576
577   /* check that the line length matches */
578   if ( ( (((uu_encoded_line_len/4)*3)-2) > uu_decoded_line_len ) || (((uu_encoded_line_len/4)*3) < uu_decoded_line_len) ) {
579     if (!warned_about_uudec_line_sanity_2) {
580       mime_trigger_error(MIME_ERRORLEVEL_UU_LINE_LENGTH);
581       warned_about_uudec_line_sanity_2 = 1;
582     };
583     return -1;
584   };
585
586   while ( ((p - &work[1]) < uu_encoded_line_len) && (num_decoded < uu_decoded_line_len)) {
587
588     /* byte 0 ---------------------- */
589     if ((p - &work[1] + 1) >= uu_encoded_line_len) {
590       return 0;
591     }
592
593     (*data)[num_decoded] = *p;
594     (*data)[num_decoded] <<= 2;
595
596     tmp_c = *(p+1);
597     tmp_c >>= 4;
598     (*data)[num_decoded] |= tmp_c;
599
600     num_decoded++;
601     p++;
602
603     /* byte 1 ---------------------- */
604     if ((p - &work[1] + 1) >= uu_encoded_line_len) {
605       return 0;
606     }
607
608     (*data)[num_decoded] = *p;
609     (*data)[num_decoded] <<= 4;
610
611     tmp_c = *(p+1);
612     tmp_c >>= 2;
613     (*data)[num_decoded] |= tmp_c;
614
615     num_decoded++;
616     p++;
617
618     /* byte 2 ---------------------- */
619     if ((p - &work[1] + 1) >= uu_encoded_line_len) {
620       return 0;
621     }
622
623     (*data)[num_decoded] = *p;
624     (*data)[num_decoded] <<= 6;
625
626     (*data)[num_decoded] |= *(p+1);
627
628     num_decoded++;
629     p+=2;
630
631   };
632
633   return uu_decoded_line_len;
634 }
635
636
637 /*************************************************
638 * Decode a b64 or qp line                        *
639 *************************************************/
640
641 /* returns number of decoded bytes
642          -1 for hard errors
643          -2 for soft errors
644 */
645
646 int warned_about_b64_line_length = 0;
647 int warned_about_b64_line_sanity = 0;
648 int warned_about_b64_illegal_char = 0;
649 int warned_about_qp_line_sanity = 0;
650 long mime_decode_line(int mime_demux_mode,uschar *line, uschar **data, long max_data_len, uschar *info) {
651   uschar *p;
652   long num_decoded = 0;
653   int offset = 0;
654   uschar tmp_c;
655
656   /* allocate memory for data */
657   *data = (uschar *)malloc(max_data_len);
658   if (*data == NULL) {
659     (void)string_format(info, 1024,"unable to allocate %lu bytes",max_data_len);
660     return -2;
661   };
662
663   if (mime_demux_mode == MIME_DEMUX_MODE_BASE64) {
664     /* ---------------------------------------------- */
665
666     /* NULL out trailing '\r' and '\n' chars */
667     while (Ustrrchr(line,'\r') != NULL) {
668       *(Ustrrchr(line,'\r')) = '\0';
669     };
670     while (Ustrrchr(line,'\n') != NULL) {
671       *(Ustrrchr(line,'\n')) = '\0';
672     };
673
674     /* check maximum base 64 line length */
675     if (Ustrlen(line) > MIME_SANITY_MAX_B64_LINE_LENGTH ) {
676       if (!warned_about_b64_line_length) {
677         mime_trigger_error(MIME_ERRORLEVEL_B64_LINE_LENGTH);
678         warned_about_b64_line_length = 1;
679       };
680     };
681
682     p = line;
683     offset = 0;
684     while (*(p+offset) != '\0') {
685       /* hit illegal char ? */
686       if (b64[*(p+offset)] == 128) {
687         if (!warned_about_b64_illegal_char) {
688           mime_trigger_error(MIME_ERRORLEVEL_B64_ILLEGAL_CHAR);
689           warned_about_b64_illegal_char = 1;
690         };
691         offset++;
692       }
693       else {
694         *p = b64[*(p+offset)];
695         p++;
696       };
697     };
698     *p = 255;
699
700     /* check that resulting line length is a multiple of 4 */
701     if ( ( (p - &line[0]) % 4 ) != 0) {
702       if (!warned_about_b64_line_sanity) {
703         mime_trigger_error(MIME_ERRORLEVEL_B64_MISALIGNED);
704         warned_about_b64_line_sanity = 1;
705       };
706     };
707
708     /* line is translated, start bit shifting */
709     p = line;
710     num_decoded = 0;
711
712     while(*p != 255) {
713
714       /* byte 0 ---------------------- */
715       if (*(p+1) == 255) {
716         break;
717       }
718
719       (*data)[num_decoded] = *p;
720       (*data)[num_decoded] <<= 2;
721
722       tmp_c = *(p+1);
723       tmp_c >>= 4;
724       (*data)[num_decoded] |= tmp_c;
725
726       num_decoded++;
727       p++;
728
729       /* byte 1 ---------------------- */
730       if (*(p+1) == 255) {
731         break;
732       }
733
734       (*data)[num_decoded] = *p;
735       (*data)[num_decoded] <<= 4;
736
737       tmp_c = *(p+1);
738       tmp_c >>= 2;
739       (*data)[num_decoded] |= tmp_c;
740
741       num_decoded++;
742       p++;
743
744       /* byte 2 ---------------------- */
745       if (*(p+1) == 255) {
746         break;
747       }
748
749       (*data)[num_decoded] = *p;
750       (*data)[num_decoded] <<= 6;
751
752       (*data)[num_decoded] |= *(p+1);
753
754       num_decoded++;
755       p+=2;
756
757     };
758     return num_decoded;
759     /* ---------------------------------------------- */
760   }
761   else if (mime_demux_mode == MIME_DEMUX_MODE_QP) {
762     /* ---------------------------------------------- */
763     p = line;
764
765     while (*p != 0) {
766       if (*p == '=') {
767         int decode_qp_result;
768
769         p = mime_decode_qp(p,&decode_qp_result);
770
771         if (decode_qp_result == -2) {
772           /* Error from decoder. p is unchanged. */
773           if (!warned_about_qp_line_sanity) {
774             mime_trigger_error(MIME_ERRORLEVEL_QP_ILLEGAL_CHAR);
775             warned_about_qp_line_sanity = 1;
776           };
777           (*data)[num_decoded] = '=';
778           num_decoded++;
779           p++;
780         }
781         else if (decode_qp_result == -1) {
782           /* End of the line with soft line break.
783           Bail out. */
784           goto QP_RETURN;
785         }
786         else if (decode_qp_result >= 0) {
787           (*data)[num_decoded] = decode_qp_result;
788           num_decoded++;
789         };
790       }
791       else {
792         (*data)[num_decoded] = *p;
793         num_decoded++;
794         p++;
795       };
796     };
797     QP_RETURN:
798     return num_decoded;
799     /* ---------------------------------------------- */
800   };
801
802   return 0;
803 }
804
805
806
807 /*************************************************
808 * Log demime errors and set mime error level     *
809 *************************************************/
810
811 /* This sets the global demime_reason expansion
812 variable and the demime_errorlevel gauge. */
813
814 void mime_trigger_error(int level, uschar *format, ...) {
815   char *f;
816   va_list ap;
817
818   if( (f = malloc(16384+23)) != NULL ) {
819     /* first log the incident */
820     sprintf(f,"demime acl condition: ");
821     f+=22;
822     va_start(ap, format);
823     (void)string_vformat(US f, 16383,(char *)format, ap);
824     va_end(ap);
825     f-=22;
826     log_write(0, LOG_MAIN, f);
827     /* then copy to demime_reason_buffer if new
828     level is greater than old level */
829     if (level > demime_errorlevel) {
830       demime_errorlevel = level;
831       Ustrcpy(demime_reason_buffer, US f);
832       demime_reason = demime_reason_buffer;
833     };
834     free(f);
835   };
836 }
837
838 /*************************************************
839 * Demultiplex MIME stream.                       *
840 *************************************************/
841
842 /* We can handle BASE64, QUOTED-PRINTABLE, and UUENCODE.
843  UUENCODE does not need to have a proper
844  transfer-encoding header, we detect it with "begin"
845
846  This function will report human parsable errors in
847  *info.
848
849  returns DEFER -> soft error (see *info)
850          OK    -> EOF hit, all ok
851 */
852
853 int mime_demux(FILE *f, uschar *info) {
854   int mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
855   int uu_mode = MIME_UU_MODE_OFF;
856   FILE *mime_dump_file = NULL;
857   FILE *uu_dump_file = NULL;
858   uschar *line;
859   int mime_read_line_status = MIME_READ_LINE_OK;
860   long line_len;
861   struct boundary *boundaries = NULL;
862   struct mime_part mime_part_p;
863   int has_tnef = 0;
864   int has_rfc822 = 0;
865
866   /* allocate room for our linebuffer */
867   line = (uschar *)malloc(MIME_SANITY_MAX_LINE_LENGTH);
868   if (line == NULL) {
869     (void)string_format(info, 1024,"unable to allocate %u bytes",MIME_SANITY_MAX_LINE_LENGTH);
870     return DEFER;
871   };
872
873   /* clear MIME header structure */
874   memset(&mime_part_p,0,sizeof(mime_part));
875
876   /* ----------------------- start demux loop --------------------- */
877   while (mime_read_line_status == MIME_READ_LINE_OK) {
878
879     /* read a line of input. Depending on the mode we are in,
880     the returned format will differ. */
881     mime_read_line_status = mime_read_line(f,mime_demux_mode,line,&line_len);
882
883     if (mime_read_line_status == MIME_READ_LINE_OVERFLOW) {
884       mime_trigger_error(MIME_ERRORLEVEL_LONG_LINE);
885       /* despite the error, continue  .. */
886       mime_read_line_status = MIME_READ_LINE_OK;
887       continue;
888     }
889     else if (mime_read_line_status == MIME_READ_LINE_EOF) {
890       break;
891     };
892
893     if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
894       /* -------------- header mode --------------------- */
895
896       /* Check for an empty line, which is the end of the headers.
897        In HEADER mode, the line is returned "cooked", with the
898        final '\n' replaced by a ';' */
899       if (line_len == 1) {
900         int tmp;
901
902         /* We have reached the end of the headers. Start decoding
903         with the collected settings. */
904         if (mime_part_p.seen_content_transfer_encoding > 1) {
905           mime_demux_mode = mime_part_p.seen_content_transfer_encoding;
906         }
907         else {
908           /* default to plain mode if no specific encoding type found */
909           mime_demux_mode = MIME_DEMUX_MODE_PLAIN;
910         };
911
912         /* open new dump file */
913         tmp = mime_get_dump_file(mime_part_p.extension, &mime_dump_file, info);
914         if (tmp < 0) {
915           return DEFER;
916         };
917
918         /* clear out mime_part */
919         memset(&mime_part_p,0,sizeof(mime_part));
920       }
921       else {
922         /* Another header to check for file extensions,
923         encoding type and boundaries */
924         if (strncmpic(US"content-type:",line,Ustrlen("content-type:")) == 0) {
925           /* ---------------------------- Content-Type header ------------------------------- */
926           uschar *value = line;
927
928           /* check for message/partial MIME type and reject it */
929           if (mime_header_find(line,US"message/partial",NULL) > 0)
930             mime_trigger_error(MIME_ERRORLEVEL_MESSAGE_PARTIAL);
931
932           /* check for TNEF content type, remember to unpack TNEF later. */
933           if (mime_header_find(line,US"application/ms-tnef",NULL) > 0)
934             has_tnef = 1;
935
936           /* check for message/rfcxxx attachments */
937           if (mime_header_find(line,US"message/rfc822",NULL) > 0)
938             has_rfc822 = 1;
939
940           /* find the file extension, but do not fill it in
941           it is already set, since content-disposition has
942           precedence. */
943           if (mime_part_p.extension == NULL) {
944             if (mime_header_find(line,US"name",&value) == 2) {
945               if (Ustrlen(value) > MIME_SANITY_MAX_FILENAME)
946                 mime_trigger_error(MIME_ERRORLEVEL_FILENAME_LENGTH);
947               mime_part_p.extension = value;
948               mime_part_p.extension = Ustrrchr(value,'.');
949               if (mime_part_p.extension == NULL) {
950                 /* file without extension, setting
951                 NULL will use the default extension later */
952                 mime_part_p.extension = NULL;
953               }
954               else {
955                 struct file_extension *this_extension =
956                   (struct file_extension *)malloc(sizeof(file_extension));
957
958                 this_extension->file_extension_string =
959                   (uschar *)malloc(Ustrlen(mime_part_p.extension)+1);
960                 Ustrcpy(this_extension->file_extension_string,
961                         mime_part_p.extension+1);
962                 this_extension->next = file_extensions;
963                 file_extensions = this_extension;
964               };
965             };
966           };
967
968           /* find a boundary and add it to the list, if present */
969           value = line;
970           if (mime_header_find(line,US"boundary",&value) == 2) {
971             struct boundary *thisboundary;
972
973             if (Ustrlen(value) > MIME_SANITY_MAX_BOUNDARY_LENGTH) {
974               mime_trigger_error(MIME_ERRORLEVEL_BOUNDARY_LENGTH);
975             }
976             else {
977               thisboundary = (struct boundary*)malloc(sizeof(boundary));
978               thisboundary->next = boundaries;
979               thisboundary->boundary_string = value;
980               boundaries = thisboundary;
981             };
982           };
983
984           if (mime_part_p.seen_content_type == 0) {
985             mime_part_p.seen_content_type = 1;
986           }
987           else {
988             mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
989           };
990           /* ---------------------------------------------------------------------------- */
991         }
992         else if (strncmpic(US"content-transfer-encoding:",line,Ustrlen("content-transfer-encoding:")) == 0) {
993           /* ---------------------------- Content-Transfer-Encoding header -------------- */
994
995          if (mime_part_p.seen_content_transfer_encoding == 0) {
996             if (mime_header_find(line,US"base64",NULL) > 0) {
997               mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_BASE64;
998             }
999             else if (mime_header_find(line,US"quoted-printable",NULL) > 0) {
1000               mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_QP;
1001             }
1002             else {
1003               mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_PLAIN;
1004             };
1005           }
1006           else {
1007             mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
1008           };
1009           /* ---------------------------------------------------------------------------- */
1010         }
1011         else if (strncmpic(US"content-disposition:",line,Ustrlen("content-disposition:")) == 0) {
1012           /* ---------------------------- Content-Disposition header -------------------- */
1013           uschar *value = line;
1014
1015           if (mime_part_p.seen_content_disposition == 0) {
1016             mime_part_p.seen_content_disposition = 1;
1017
1018             if (mime_header_find(line,US"filename",&value) == 2) {
1019               if (Ustrlen(value) > MIME_SANITY_MAX_FILENAME)
1020                 mime_trigger_error(MIME_ERRORLEVEL_FILENAME_LENGTH);
1021               mime_part_p.extension = value;
1022               mime_part_p.extension = Ustrrchr(value,'.');
1023               if (mime_part_p.extension == NULL) {
1024                 /* file without extension, setting
1025                 NULL will use the default extension later */
1026                 mime_part_p.extension = NULL;
1027               }
1028               else {
1029                 struct file_extension *this_extension =
1030                   (struct file_extension *)malloc(sizeof(file_extension));
1031
1032                 this_extension->file_extension_string =
1033                   (uschar *)malloc(Ustrlen(mime_part_p.extension)+1);
1034                 Ustrcpy(this_extension->file_extension_string,
1035                         mime_part_p.extension+1);
1036                 this_extension->next = file_extensions;
1037                 file_extensions = this_extension;
1038               };
1039             };
1040           }
1041           else {
1042             mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
1043           };
1044           /* ---------------------------------------------------------------------------- */
1045         };
1046       };    /* End of header checks */
1047       /* ------------------------------------------------ */
1048     }
1049     else {
1050       /* -------------- non-header mode ----------------- */
1051       int tmp;
1052
1053       if (uu_mode == MIME_UU_MODE_OFF) {
1054         uschar uu_file_extension[5];
1055         /* We are not currently decoding UUENCODE
1056         Check for possible UUENCODE start tag. */
1057         if (mime_check_uu_start(line,uu_file_extension,&has_tnef)) {
1058           /* possible UUENCODING start detected.
1059           Set unconfirmed mode first. */
1060           uu_mode = MIME_UU_MODE_UNCONFIRMED;
1061           /* open new uu dump file */
1062           tmp = mime_get_dump_file(uu_file_extension, &uu_dump_file, info);
1063           if (tmp < 0) {
1064             free(line);
1065             return DEFER;
1066           };
1067         };
1068       }
1069       else {
1070         uschar *data;
1071         long data_len = 0;
1072
1073         if (uu_mode == MIME_UU_MODE_UNCONFIRMED) {
1074          /* We are in unconfirmed UUENCODE mode. */
1075
1076          data_len = uu_decode_line(line,&data,line_len,info);
1077
1078          if (data_len == -2) {
1079            /* temp error, turn off uudecode mode */
1080            if (uu_dump_file != NULL) {
1081             (void)fclose(uu_dump_file); uu_dump_file = NULL;
1082            };
1083            uu_mode = MIME_UU_MODE_OFF;
1084            return DEFER;
1085          }
1086          else if (data_len == -1) {
1087            if (uu_dump_file != NULL) {
1088             (void)fclose(uu_dump_file); uu_dump_file = NULL;
1089            };
1090            uu_mode = MIME_UU_MODE_OFF;
1091            data_len = 0;
1092          }
1093          else if (data_len > 0) {
1094            /* we have at least decoded a valid byte
1095            turn on confirmed mode */
1096            uu_mode = MIME_UU_MODE_CONFIRMED;
1097          };
1098         }
1099         else if (uu_mode == MIME_UU_MODE_CONFIRMED) {
1100           /* If we are in confirmed UU mode,
1101           check for single "end" tag on line */
1102           if ((strncmpic(line,US"end",3) == 0) && (line[3] < 32)) {
1103             if (uu_dump_file != NULL) {
1104               (void)fclose(uu_dump_file); uu_dump_file = NULL;
1105             };
1106             uu_mode = MIME_UU_MODE_OFF;
1107           }
1108           else {
1109             data_len = uu_decode_line(line,&data,line_len,info);
1110             if (data_len == -2) {
1111                /* temp error, turn off uudecode mode */
1112                if (uu_dump_file != NULL) {
1113                  (void)fclose(uu_dump_file); uu_dump_file = NULL;
1114                };
1115                uu_mode = MIME_UU_MODE_OFF;
1116                return DEFER;
1117              }
1118              else if (data_len == -1) {
1119                /* skip this line */
1120                data_len = 0;
1121              };
1122           };
1123         };
1124
1125         /* write data to dump file, if available */
1126         if (data_len > 0) {
1127           if (fwrite(data,1,data_len,uu_dump_file) < data_len) {
1128             /* short write */
1129             (void)string_format(info, 1024,"short write on uudecode dump file");
1130             free(line);
1131             return DEFER;
1132           };
1133         };
1134       };
1135
1136       if (mime_demux_mode != MIME_DEMUX_MODE_SCANNING) {
1137         /* Non-scanning and Non-header mode. That means
1138         we are currently decoding data to the dump
1139         file. */
1140
1141         /* Check for a known boundary. */
1142         tmp = mime_check_boundary(line,boundaries);
1143         if (tmp == 1) {
1144           /* We have hit a known start boundary.
1145           That will put us back in header mode. */
1146           mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
1147           if (mime_dump_file != NULL) {
1148             /* if the attachment was a RFC822 message, recurse into it */
1149             if (has_rfc822) {
1150               has_rfc822 = 0;
1151               rewind(mime_dump_file);
1152               mime_demux(mime_dump_file,info);
1153             };
1154
1155             (void)fclose(mime_dump_file); mime_dump_file = NULL;
1156           };
1157         }
1158         else if (tmp == 2) {
1159           /* We have hit a known end boundary.
1160           That puts us into scanning mode, which will end when we hit another known start boundary */
1161           mime_demux_mode = MIME_DEMUX_MODE_SCANNING;
1162           if (mime_dump_file != NULL) {
1163             /* if the attachment was a RFC822 message, recurse into it */
1164             if (has_rfc822) {
1165               has_rfc822 = 0;
1166               rewind(mime_dump_file);
1167               mime_demux(mime_dump_file,info);
1168             };
1169
1170             (void)fclose(mime_dump_file); mime_dump_file = NULL;
1171           };
1172         }
1173         else {
1174           uschar *data;
1175           long data_len = 0;
1176
1177           /* decode the line with the appropriate method */
1178           if (mime_demux_mode == MIME_DEMUX_MODE_PLAIN) {
1179             /* in plain mode, just dump the line */
1180             data = line;
1181             data_len = line_len;
1182           }
1183           else if ( (mime_demux_mode == MIME_DEMUX_MODE_QP) || (mime_demux_mode == MIME_DEMUX_MODE_BASE64) ) {
1184             data_len = mime_decode_line(mime_demux_mode,line,&data,line_len,info);
1185             if (data_len < 0) {
1186               /* Error reported from the line decoder. */
1187               data_len = 0;
1188             };
1189           };
1190
1191           /* write data to dump file */
1192           if (data_len > 0) {
1193             if (fwrite(data,1,data_len,mime_dump_file) < data_len) {
1194               /* short write */
1195               (void)string_format(info, 1024,"short write on dump file");
1196               free(line);
1197               return DEFER;
1198             };
1199           };
1200
1201         };
1202       }
1203       else {
1204         /* Scanning mode. We end up here after a end boundary.
1205         This will usually be at the end of a message or at
1206         the end of a MIME container.
1207         We need to look for another start boundary to get
1208         back into header mode. */
1209         if (mime_check_boundary(line,boundaries) == 1) {
1210           mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
1211         };
1212
1213       };
1214       /* ------------------------------------------------ */
1215     };
1216   };
1217   /* ----------------------- end demux loop ----------------------- */
1218
1219   /* close files, they could still be open */
1220   if (mime_dump_file != NULL)
1221     (void)fclose(mime_dump_file);
1222   if (uu_dump_file != NULL)
1223     (void)fclose(uu_dump_file);
1224
1225   /* release line buffer */
1226   free(line);
1227
1228   /* FIXME: release boundary buffers.
1229   Not too much of a problem since
1230   this instance of exim is not resident. */
1231
1232   if (has_tnef) {
1233     uschar file_name[1024];
1234     /* at least one file could be TNEF encoded.
1235     attempt to send all decoded files thru the TNEF decoder */
1236
1237     (void)string_format(file_name,1024,"%s/scan/%s",spool_directory,message_id);
1238     /* Removed FTTB. We need to decide on TNEF inclusion */
1239     /* mime_unpack_tnef(file_name); */
1240   };
1241
1242   return 0;
1243 }
1244
1245 #endif