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