TLS fixes for OpenSSL.
[exim.git] / src / src / demime.c
CommitLineData
8523533c
TK
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
15uschar demime_reason_buffer[1024];
16struct file_extension *file_extensions = NULL;
17
18int demime(uschar **listptr) {
19 int sep = 0;
20 uschar *list = *listptr;
21 uschar *option;
22 uschar option_buffer[64];
c6e692d7 23 unsigned long mbox_size;
8523533c
TK
24 FILE *mbox_file;
25 uschar defer_error_buffer[1024];
26 int demime_rc = 0;
8e669ac1 27
8523533c
TK
28 /* reset found_extension variable */
29 found_extension = NULL;
8e669ac1 30
8523533c
TK
31 /* try to find 1st option */
32 if ((option = string_nextinlist(&list, &sep,
33 option_buffer,
34 sizeof(option_buffer))) != NULL) {
8e669ac1 35
8523533c
TK
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 };
8e669ac1 46
8523533c 47 /* make sure the eml mbox file is spooled up */
8544e77a 48 mbox_file = spool_mbox(&mbox_size, NULL);
8e669ac1 49
8523533c
TK
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");
8e669ac1 54 return DEFER;
8523533c 55 };
8e669ac1 56
8523533c
TK
57 /* call demimer if not already done earlier */
58 if (!demime_ok)
59 demime_rc = mime_demux(mbox_file, defer_error_buffer);
8e669ac1 60
f1e894f3 61 (void)fclose(mbox_file);
8e669ac1 62
8523533c
TK
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 };
8e669ac1 69
8523533c
TK
70 /* set demime_ok to avoid unpacking again */
71 demime_ok = 1;
8e669ac1 72
8523533c
TK
73 /* check for file extensions, if there */
74 while (option != NULL) {
75 struct file_extension *this_extension = file_extensions;
8e669ac1 76
8523533c
TK
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 */
8e669ac1 85 while (this_extension != NULL) {
8523533c
TK
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 };
8e669ac1 93
8523533c
TK
94 /* grab next extension from option list */
95 option = string_nextinlist(&list, &sep,
96 option_buffer,
97 sizeof(option_buffer));
98 };
8e669ac1 99
8523533c
TK
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
112unsigned int mime_hstr_i(uschar *cptr) {
113 unsigned int i, j = 0;
8e669ac1 114
8523533c
TK
115 while (cptr && *cptr && isxdigit(*cptr)) {
116 i = *cptr++ - '0';
117 if (9 < i) i -= 7;
118 j <<= 4;
119 j |= (i & 0x0f);
120 }
8e669ac1 121
8523533c
TK
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
138uschar *mime_decode_qp(uschar *qp_p,int *c) {
139 uschar hex[] = {0,0,0};
140 int nan = 0;
141 uschar *initial_pos = qp_p;
8e669ac1 142
8523533c
TK
143 /* advance one char */
144 qp_p++;
8e669ac1 145
8523533c
TK
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 }
8e669ac1 168 else if (*qp_p == '\n') {
8523533c
TK
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 };
8e669ac1 178
8523533c
TK
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;
8e669ac1 190 return initial_pos;
8523533c
TK
191 };
192 }
193 else {
194 /* illegal char */
195 *c = -2;
8e669ac1 196 return initial_pos;
8523533c 197 };
8e669ac1 198
8523533c
TK
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
211int 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;
8e669ac1 217
8523533c
TK
218 if (extension == NULL)
219 extension = default_extension;
8e669ac1 220
8523533c
TK
221 /* scan the proposed extension.
222 if it is longer than 4 chars, or
223 contains exotic chars, use the default extension */
8e669ac1 224
8523533c
TK
225/* if (Ustrlen(extension) > 4) {
226 extension = default_extension;
227 };
8e669ac1
PH
228*/
229
8523533c 230 p = extension+1;
8e669ac1 231
8523533c
TK
232 while (*p != 0) {
233 *p = (uschar)tolower((uschar)*p);
234 if ( (*p < 97) || (*p > 122) ) {
235 extension = default_extension;
8e669ac1 236 break;
8523533c
TK
237 };
238 p++;
239 };
8e669ac1 240
8523533c
TK
241 /* find a new file to write to */
242 file_nr = 0;
243 do {
244 struct stat mystat;
8e669ac1 245
b07e6aa3 246 (void)string_format(file_name,1024,"%s/scan/%s/%s-%05u%s",spool_directory,message_id,message_id,file_nr,extension);
8523533c
TK
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);
8e669ac1 256
2632889e 257 *f = modefopen(file_name,"wb+",SPOOL_MODE);
8523533c
TK
258 if (*f == NULL) {
259 /* cannot open new dump file, disk full ? -> soft error */
b07e6aa3 260 (void)string_format(info, 1024,"unable to open dump file");
8523533c
TK
261 return -2;
262 };
8e669ac1 263
8523533c
TK
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
8e669ac1 274
8523533c
TK
275 returns: 0 - nothing found
276 1 - found param
277 2 - found param + value
278*/
279
280int mime_header_find(uschar *header, uschar *param, uschar **value) {
281 uschar *needle;
8e669ac1 282
8523533c
TK
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;
8e669ac1 290
8523533c
TK
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;
8e669ac1 298
8523533c
TK
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
322int 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;
8e669ac1 327
8523533c 328 *num_copied = 0;
8e669ac1 329
8523533c 330 while(!done) {
8e669ac1 331
8523533c
TK
332 c = fgetc(f);
333 if (c == EOF) break;
8e669ac1 334
8523533c
TK
335 /* --------- header mode -------------- */
336 if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
8e669ac1 337
8523533c
TK
338 /* always skip CRs */
339 if (c == '\r') continue;
8e669ac1 340
8523533c
TK
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;
f1e894f3 347 (void)ungetc(c,f);
8523533c
TK
348 };
349 /* end of the header, terminate with ';' */
350 c = ';';
351 done = 1;
352 };
8e669ac1 353
8523533c
TK
354 /* skip control characters */
355 if (c < 32) continue;
8e669ac1 356
8523533c
TK
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;
8e669ac1 365
8523533c
TK
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 */
8e669ac1 390 header_value_mode = 1;
8523533c 391 };
8e669ac1 392
8523533c
TK
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 };
8e669ac1 407
8523533c
TK
408 /* copy the char to the buffer */
409 buffer[*num_copied] = (uschar)c;
410 /* raise counter */
411 (*num_copied)++;
8e669ac1 412
8523533c
TK
413 /* break if buffer is full */
414 if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1) {
415 done = 1;
416 };
417 }
8e669ac1 418
8523533c
TK
419 /* 0-terminate */
420 buffer[*num_copied] = '\0';
8e669ac1 421
8523533c
TK
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
441int 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;
8e669ac1 445
8523533c
TK
446 /* check for '--' first */
447 if (Ustrncmp(line,"--",2) == 0) {
8e669ac1 448
8523533c
TK
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];
384152a6 453 j++;
8523533c
TK
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;
8e669ac1 463 };
8523533c
TK
464 return 1;
465 };
466 thisboundary = thisboundary->next;
467 };
468 };
8e669ac1
PH
469
470 return 0;
8523533c
TK
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
482int mime_check_uu_start(uschar *line, uschar *uu_file_extension, int *has_tnef) {
8e669ac1 483
8523533c
TK
484 if ( (strncmpic(line,US"begin ",6) == 0)) {
485 uschar *uu_filename = &line[6];
8e669ac1 486
8523533c
TK
487 /* skip perms, if present */
488 Ustrtoul(&line[6],&uu_filename,10);
8e669ac1 489
8523533c
TK
490 /* advance one char */
491 uu_filename++;
8e669ac1 492
8523533c
TK
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) {
8e669ac1 497 *has_tnef = 1;
8523533c 498 };
8e669ac1 499
8523533c
TK
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,'.');
8e669ac1 504
8523533c
TK
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
522int warned_about_uudec_line_sanity_1 = 0;
523int warned_about_uudec_line_sanity_2 = 0;
524long 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;
8e669ac1 530
8523533c
TK
531 /* allocate memory for data and work buffer */
532 *data = (uschar *)malloc(line_len);
533 if (*data == NULL) {
b07e6aa3 534 (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
8523533c
TK
535 return -2;
536 };
537
538 work = (uschar *)malloc(line_len);
539 if (work == NULL) {
b07e6aa3 540 (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
8523533c
TK
541 return -2;
542 };
8e669ac1 543
8523533c 544 memcpy(work,line,line_len);
8e669ac1 545
8523533c
TK
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 };
8e669ac1 555
8523533c
TK
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];
8e669ac1 565
8523533c
TK
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)) {
8e669ac1 585
8523533c
TK
586 /* byte 0 ---------------------- */
587 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
588 return 0;
589 }
8e669ac1 590
8523533c
TK
591 (*data)[num_decoded] = *p;
592 (*data)[num_decoded] <<= 2;
8e669ac1 593
8523533c
TK
594 tmp_c = *(p+1);
595 tmp_c >>= 4;
596 (*data)[num_decoded] |= tmp_c;
8e669ac1 597
8523533c
TK
598 num_decoded++;
599 p++;
8e669ac1 600
8523533c
TK
601 /* byte 1 ---------------------- */
602 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
603 return 0;
604 }
8e669ac1 605
8523533c
TK
606 (*data)[num_decoded] = *p;
607 (*data)[num_decoded] <<= 4;
8e669ac1 608
8523533c
TK
609 tmp_c = *(p+1);
610 tmp_c >>= 2;
611 (*data)[num_decoded] |= tmp_c;
8e669ac1 612
8523533c
TK
613 num_decoded++;
614 p++;
8e669ac1 615
8523533c
TK
616 /* byte 2 ---------------------- */
617 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
618 return 0;
619 }
8e669ac1 620
8523533c
TK
621 (*data)[num_decoded] = *p;
622 (*data)[num_decoded] <<= 6;
8e669ac1 623
8523533c 624 (*data)[num_decoded] |= *(p+1);
8e669ac1 625
8523533c
TK
626 num_decoded++;
627 p+=2;
8e669ac1 628
8523533c 629 };
8e669ac1 630
8523533c
TK
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
644int warned_about_b64_line_length = 0;
645int warned_about_b64_line_sanity = 0;
646int warned_about_b64_illegal_char = 0;
647int warned_about_qp_line_sanity = 0;
648long 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;
8e669ac1 653
8523533c
TK
654 /* allocate memory for data */
655 *data = (uschar *)malloc(max_data_len);
656 if (*data == NULL) {
b07e6aa3 657 (void)string_format(info, 1024,"unable to allocate %lu bytes",max_data_len);
8523533c
TK
658 return -2;
659 };
8e669ac1 660
8523533c
TK
661 if (mime_demux_mode == MIME_DEMUX_MODE_BASE64) {
662 /* ---------------------------------------------- */
8e669ac1 663
8523533c
TK
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 };
8e669ac1 671
8523533c
TK
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;
8e669ac1 697
8523533c
TK
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 };
8e669ac1 705
8523533c
TK
706 /* line is translated, start bit shifting */
707 p = line;
708 num_decoded = 0;
8e669ac1 709
8523533c 710 while(*p != 255) {
8e669ac1 711
8523533c
TK
712 /* byte 0 ---------------------- */
713 if (*(p+1) == 255) {
714 break;
715 }
8e669ac1 716
8523533c
TK
717 (*data)[num_decoded] = *p;
718 (*data)[num_decoded] <<= 2;
8e669ac1 719
8523533c
TK
720 tmp_c = *(p+1);
721 tmp_c >>= 4;
722 (*data)[num_decoded] |= tmp_c;
8e669ac1 723
8523533c
TK
724 num_decoded++;
725 p++;
8e669ac1 726
8523533c
TK
727 /* byte 1 ---------------------- */
728 if (*(p+1) == 255) {
729 break;
730 }
8e669ac1 731
8523533c
TK
732 (*data)[num_decoded] = *p;
733 (*data)[num_decoded] <<= 4;
8e669ac1 734
8523533c
TK
735 tmp_c = *(p+1);
736 tmp_c >>= 2;
737 (*data)[num_decoded] |= tmp_c;
8e669ac1 738
8523533c
TK
739 num_decoded++;
740 p++;
8e669ac1 741
8523533c
TK
742 /* byte 2 ---------------------- */
743 if (*(p+1) == 255) {
744 break;
745 }
8e669ac1 746
8523533c
TK
747 (*data)[num_decoded] = *p;
748 (*data)[num_decoded] <<= 6;
8e669ac1 749
8523533c 750 (*data)[num_decoded] |= *(p+1);
8e669ac1 751
8523533c
TK
752 num_decoded++;
753 p+=2;
8e669ac1 754
8523533c 755 };
8e669ac1 756 return num_decoded;
8523533c
TK
757 /* ---------------------------------------------- */
758 }
759 else if (mime_demux_mode == MIME_DEMUX_MODE_QP) {
760 /* ---------------------------------------------- */
761 p = line;
8e669ac1 762
8523533c
TK
763 while (*p != 0) {
764 if (*p == '=') {
765 int decode_qp_result;
8e669ac1 766
8523533c 767 p = mime_decode_qp(p,&decode_qp_result);
8e669ac1 768
8523533c
TK
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) {
8e669ac1 780 /* End of the line with soft line break.
8523533c
TK
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 };
8e669ac1 799
8523533c
TK
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
810variable and the demime_errorlevel gauge. */
811
812void 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);
b07e6aa3 821 (void)string_vformat(US f, 16383,(char *)format, ap);
8523533c
TK
822 va_end(ap);
823 f-=22;
e0df1c83 824 log_write(0, LOG_MAIN, "%s", f);
8523533c
TK
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
851int 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;
8e669ac1 863
8523533c
TK
864 /* allocate room for our linebuffer */
865 line = (uschar *)malloc(MIME_SANITY_MAX_LINE_LENGTH);
866 if (line == NULL) {
b07e6aa3 867 (void)string_format(info, 1024,"unable to allocate %u bytes",MIME_SANITY_MAX_LINE_LENGTH);
8523533c
TK
868 return DEFER;
869 };
8e669ac1 870
8523533c
TK
871 /* clear MIME header structure */
872 memset(&mime_part_p,0,sizeof(mime_part));
8e669ac1 873
8523533c
TK
874 /* ----------------------- start demux loop --------------------- */
875 while (mime_read_line_status == MIME_READ_LINE_OK) {
8e669ac1 876
8523533c
TK
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);
8e669ac1 880
8523533c
TK
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 --------------------- */
8e669ac1 893
8523533c
TK
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;
8e669ac1 899
8523533c
TK
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 };
8e669ac1 909
8523533c
TK
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;
8e669ac1 925
8523533c
TK
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;
8e669ac1 933
8523533c
TK
934 /* check for message/rfcxxx attachments */
935 if (mime_header_find(line,US"message/rfc822",NULL) > 0)
936 has_rfc822 = 1;
8e669ac1 937
8523533c
TK
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);
8e669ac1 945 mime_part_p.extension = value;
8523533c
TK
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));
8e669ac1
PH
955
956 this_extension->file_extension_string =
8523533c
TK
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 };
8e669ac1 965
8523533c
TK
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) {
8e669ac1 972 mime_trigger_error(MIME_ERRORLEVEL_BOUNDARY_LENGTH);
8523533c
TK
973 }
974 else {
975 thisboundary = (struct boundary*)malloc(sizeof(boundary));
976 thisboundary->next = boundaries;
977 thisboundary->boundary_string = value;
978 boundaries = thisboundary;
979 };
980 };
8e669ac1 981
8523533c
TK
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 -------------- */
8e669ac1 992
8523533c
TK
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;
8e669ac1 1012
8523533c
TK
1013 if (mime_part_p.seen_content_disposition == 0) {
1014 mime_part_p.seen_content_disposition = 1;
8e669ac1 1015
8523533c
TK
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));
8e669ac1
PH
1029
1030 this_extension->file_extension_string =
8523533c
TK
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. */
8e669ac1 1073
8523533c 1074 data_len = uu_decode_line(line,&data,line_len,info);
8e669ac1 1075
8523533c
TK
1076 if (data_len == -2) {
1077 /* temp error, turn off uudecode mode */
1078 if (uu_dump_file != NULL) {
f1e894f3 1079 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
1080 };
1081 uu_mode = MIME_UU_MODE_OFF;
1082 return DEFER;
1083 }
1084 else if (data_len == -1) {
1085 if (uu_dump_file != NULL) {
f1e894f3 1086 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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) {
f1e894f3 1102 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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) {
f1e894f3 1111 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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 };
8e669ac1 1122
8523533c
TK
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 */
b07e6aa3 1127 (void)string_format(info, 1024,"short write on uudecode dump file");
8523533c 1128 free(line);
8e669ac1 1129 return DEFER;
8523533c
TK
1130 };
1131 };
1132 };
8e669ac1 1133
8523533c
TK
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 };
8e669ac1 1152
f1e894f3 1153 (void)fclose(mime_dump_file); mime_dump_file = NULL;
8523533c
TK
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 };
8e669ac1 1167
f1e894f3 1168 (void)fclose(mime_dump_file); mime_dump_file = NULL;
8523533c
TK
1169 };
1170 }
1171 else {
1172 uschar *data;
1173 long data_len = 0;
8e669ac1 1174
8523533c
TK
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 };
8e669ac1 1188
8523533c
TK
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 */
b07e6aa3 1193 (void)string_format(info, 1024,"short write on dump file");
8523533c 1194 free(line);
8e669ac1 1195 return DEFER;
8523533c
TK
1196 };
1197 };
8e669ac1 1198
8523533c
TK
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 };
8e669ac1 1210
8523533c
TK
1211 };
1212 /* ------------------------------------------------ */
1213 };
1214 };
1215 /* ----------------------- end demux loop ----------------------- */
8e669ac1 1216
8523533c
TK
1217 /* close files, they could still be open */
1218 if (mime_dump_file != NULL)
f1e894f3 1219 (void)fclose(mime_dump_file);
8523533c 1220 if (uu_dump_file != NULL)
f1e894f3 1221 (void)fclose(uu_dump_file);
8e669ac1 1222
8523533c
TK
1223 /* release line buffer */
1224 free(line);
8e669ac1 1225
8523533c
TK
1226 /* FIXME: release boundary buffers.
1227 Not too much of a problem since
1228 this instance of exim is not resident. */
8e669ac1 1229
8523533c
TK
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 */
8e669ac1 1234
b07e6aa3 1235 (void)string_format(file_name,1024,"%s/scan/%s",spool_directory,message_id);
8523533c
TK
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