Documentation for randint. Better randomness defaults. Fixes: #722
[exim.git] / src / src / demime.c
CommitLineData
2632889e 1/* $Cambridge: exim/src/src/demime.c,v 1.9 2006/02/22 14:46:44 ph10 Exp $ */
8523533c
TK
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
17uschar demime_reason_buffer[1024];
18struct file_extension *file_extensions = NULL;
19
20int demime(uschar **listptr) {
21 int sep = 0;
22 uschar *list = *listptr;
23 uschar *option;
24 uschar option_buffer[64];
c6e692d7 25 unsigned long mbox_size;
8523533c
TK
26 FILE *mbox_file;
27 uschar defer_error_buffer[1024];
28 int demime_rc = 0;
8e669ac1 29
8523533c
TK
30 /* reset found_extension variable */
31 found_extension = NULL;
8e669ac1 32
8523533c
TK
33 /* try to find 1st option */
34 if ((option = string_nextinlist(&list, &sep,
35 option_buffer,
36 sizeof(option_buffer))) != NULL) {
8e669ac1 37
8523533c
TK
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 };
8e669ac1 48
8523533c
TK
49 /* make sure the eml mbox file is spooled up */
50 mbox_file = spool_mbox(&mbox_size);
8e669ac1 51
8523533c
TK
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");
8e669ac1 56 return DEFER;
8523533c 57 };
8e669ac1 58
8523533c
TK
59 /* call demimer if not already done earlier */
60 if (!demime_ok)
61 demime_rc = mime_demux(mbox_file, defer_error_buffer);
8e669ac1 62
f1e894f3 63 (void)fclose(mbox_file);
8e669ac1 64
8523533c
TK
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 };
8e669ac1 71
8523533c
TK
72 /* set demime_ok to avoid unpacking again */
73 demime_ok = 1;
8e669ac1 74
8523533c
TK
75 /* check for file extensions, if there */
76 while (option != NULL) {
77 struct file_extension *this_extension = file_extensions;
8e669ac1 78
8523533c
TK
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 */
8e669ac1 87 while (this_extension != NULL) {
8523533c
TK
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 };
8e669ac1 95
8523533c
TK
96 /* grab next extension from option list */
97 option = string_nextinlist(&list, &sep,
98 option_buffer,
99 sizeof(option_buffer));
100 };
8e669ac1 101
8523533c
TK
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
114unsigned int mime_hstr_i(uschar *cptr) {
115 unsigned int i, j = 0;
8e669ac1 116
8523533c
TK
117 while (cptr && *cptr && isxdigit(*cptr)) {
118 i = *cptr++ - '0';
119 if (9 < i) i -= 7;
120 j <<= 4;
121 j |= (i & 0x0f);
122 }
8e669ac1 123
8523533c
TK
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
140uschar *mime_decode_qp(uschar *qp_p,int *c) {
141 uschar hex[] = {0,0,0};
142 int nan = 0;
143 uschar *initial_pos = qp_p;
8e669ac1 144
8523533c
TK
145 /* advance one char */
146 qp_p++;
8e669ac1 147
8523533c
TK
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 }
8e669ac1 170 else if (*qp_p == '\n') {
8523533c
TK
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 };
8e669ac1 180
8523533c
TK
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;
8e669ac1 192 return initial_pos;
8523533c
TK
193 };
194 }
195 else {
196 /* illegal char */
197 *c = -2;
8e669ac1 198 return initial_pos;
8523533c 199 };
8e669ac1 200
8523533c
TK
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
213int 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;
8e669ac1 219
8523533c
TK
220 if (extension == NULL)
221 extension = default_extension;
8e669ac1 222
8523533c
TK
223 /* scan the proposed extension.
224 if it is longer than 4 chars, or
225 contains exotic chars, use the default extension */
8e669ac1 226
8523533c
TK
227/* if (Ustrlen(extension) > 4) {
228 extension = default_extension;
229 };
8e669ac1
PH
230*/
231
8523533c 232 p = extension+1;
8e669ac1 233
8523533c
TK
234 while (*p != 0) {
235 *p = (uschar)tolower((uschar)*p);
236 if ( (*p < 97) || (*p > 122) ) {
237 extension = default_extension;
8e669ac1 238 break;
8523533c
TK
239 };
240 p++;
241 };
8e669ac1 242
8523533c
TK
243 /* find a new file to write to */
244 file_nr = 0;
245 do {
246 struct stat mystat;
8e669ac1 247
b07e6aa3 248 (void)string_format(file_name,1024,"%s/scan/%s/%s-%05u%s",spool_directory,message_id,message_id,file_nr,extension);
8523533c
TK
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);
8e669ac1 258
2632889e 259 *f = modefopen(file_name,"wb+",SPOOL_MODE);
8523533c
TK
260 if (*f == NULL) {
261 /* cannot open new dump file, disk full ? -> soft error */
b07e6aa3 262 (void)string_format(info, 1024,"unable to open dump file");
8523533c
TK
263 return -2;
264 };
8e669ac1 265
8523533c
TK
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
8e669ac1 276
8523533c
TK
277 returns: 0 - nothing found
278 1 - found param
279 2 - found param + value
280*/
281
282int mime_header_find(uschar *header, uschar *param, uschar **value) {
283 uschar *needle;
8e669ac1 284
8523533c
TK
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;
8e669ac1 292
8523533c
TK
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;
8e669ac1 300
8523533c
TK
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
324int 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;
8e669ac1 329
8523533c 330 *num_copied = 0;
8e669ac1 331
8523533c 332 while(!done) {
8e669ac1 333
8523533c
TK
334 c = fgetc(f);
335 if (c == EOF) break;
8e669ac1 336
8523533c
TK
337 /* --------- header mode -------------- */
338 if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
8e669ac1 339
8523533c
TK
340 /* always skip CRs */
341 if (c == '\r') continue;
8e669ac1 342
8523533c
TK
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;
f1e894f3 349 (void)ungetc(c,f);
8523533c
TK
350 };
351 /* end of the header, terminate with ';' */
352 c = ';';
353 done = 1;
354 };
8e669ac1 355
8523533c
TK
356 /* skip control characters */
357 if (c < 32) continue;
8e669ac1 358
8523533c
TK
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;
8e669ac1 367
8523533c
TK
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 */
8e669ac1 392 header_value_mode = 1;
8523533c 393 };
8e669ac1 394
8523533c
TK
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 };
8e669ac1 409
8523533c
TK
410 /* copy the char to the buffer */
411 buffer[*num_copied] = (uschar)c;
412 /* raise counter */
413 (*num_copied)++;
8e669ac1 414
8523533c
TK
415 /* break if buffer is full */
416 if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1) {
417 done = 1;
418 };
419 }
8e669ac1 420
8523533c
TK
421 /* 0-terminate */
422 buffer[*num_copied] = '\0';
8e669ac1 423
8523533c
TK
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
443int 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;
8e669ac1 447
8523533c
TK
448 /* check for '--' first */
449 if (Ustrncmp(line,"--",2) == 0) {
8e669ac1 450
8523533c
TK
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];
384152a6 455 j++;
8523533c
TK
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;
8e669ac1 465 };
8523533c
TK
466 return 1;
467 };
468 thisboundary = thisboundary->next;
469 };
470 };
8e669ac1
PH
471
472 return 0;
8523533c
TK
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
484int mime_check_uu_start(uschar *line, uschar *uu_file_extension, int *has_tnef) {
8e669ac1 485
8523533c
TK
486 if ( (strncmpic(line,US"begin ",6) == 0)) {
487 uschar *uu_filename = &line[6];
8e669ac1 488
8523533c
TK
489 /* skip perms, if present */
490 Ustrtoul(&line[6],&uu_filename,10);
8e669ac1 491
8523533c
TK
492 /* advance one char */
493 uu_filename++;
8e669ac1 494
8523533c
TK
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) {
8e669ac1 499 *has_tnef = 1;
8523533c 500 };
8e669ac1 501
8523533c
TK
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,'.');
8e669ac1 506
8523533c
TK
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
524int warned_about_uudec_line_sanity_1 = 0;
525int warned_about_uudec_line_sanity_2 = 0;
526long 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;
8e669ac1 532
8523533c
TK
533 /* allocate memory for data and work buffer */
534 *data = (uschar *)malloc(line_len);
535 if (*data == NULL) {
b07e6aa3 536 (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
8523533c
TK
537 return -2;
538 };
539
540 work = (uschar *)malloc(line_len);
541 if (work == NULL) {
b07e6aa3 542 (void)string_format(info, 1024,"unable to allocate %lu bytes",line_len);
8523533c
TK
543 return -2;
544 };
8e669ac1 545
8523533c 546 memcpy(work,line,line_len);
8e669ac1 547
8523533c
TK
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 };
8e669ac1 557
8523533c
TK
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];
8e669ac1 567
8523533c
TK
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)) {
8e669ac1 587
8523533c
TK
588 /* byte 0 ---------------------- */
589 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
590 return 0;
591 }
8e669ac1 592
8523533c
TK
593 (*data)[num_decoded] = *p;
594 (*data)[num_decoded] <<= 2;
8e669ac1 595
8523533c
TK
596 tmp_c = *(p+1);
597 tmp_c >>= 4;
598 (*data)[num_decoded] |= tmp_c;
8e669ac1 599
8523533c
TK
600 num_decoded++;
601 p++;
8e669ac1 602
8523533c
TK
603 /* byte 1 ---------------------- */
604 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
605 return 0;
606 }
8e669ac1 607
8523533c
TK
608 (*data)[num_decoded] = *p;
609 (*data)[num_decoded] <<= 4;
8e669ac1 610
8523533c
TK
611 tmp_c = *(p+1);
612 tmp_c >>= 2;
613 (*data)[num_decoded] |= tmp_c;
8e669ac1 614
8523533c
TK
615 num_decoded++;
616 p++;
8e669ac1 617
8523533c
TK
618 /* byte 2 ---------------------- */
619 if ((p - &work[1] + 1) >= uu_encoded_line_len) {
620 return 0;
621 }
8e669ac1 622
8523533c
TK
623 (*data)[num_decoded] = *p;
624 (*data)[num_decoded] <<= 6;
8e669ac1 625
8523533c 626 (*data)[num_decoded] |= *(p+1);
8e669ac1 627
8523533c
TK
628 num_decoded++;
629 p+=2;
8e669ac1 630
8523533c 631 };
8e669ac1 632
8523533c
TK
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
646int warned_about_b64_line_length = 0;
647int warned_about_b64_line_sanity = 0;
648int warned_about_b64_illegal_char = 0;
649int warned_about_qp_line_sanity = 0;
650long 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;
8e669ac1 655
8523533c
TK
656 /* allocate memory for data */
657 *data = (uschar *)malloc(max_data_len);
658 if (*data == NULL) {
b07e6aa3 659 (void)string_format(info, 1024,"unable to allocate %lu bytes",max_data_len);
8523533c
TK
660 return -2;
661 };
8e669ac1 662
8523533c
TK
663 if (mime_demux_mode == MIME_DEMUX_MODE_BASE64) {
664 /* ---------------------------------------------- */
8e669ac1 665
8523533c
TK
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 };
8e669ac1 673
8523533c
TK
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;
8e669ac1 699
8523533c
TK
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 };
8e669ac1 707
8523533c
TK
708 /* line is translated, start bit shifting */
709 p = line;
710 num_decoded = 0;
8e669ac1 711
8523533c 712 while(*p != 255) {
8e669ac1 713
8523533c
TK
714 /* byte 0 ---------------------- */
715 if (*(p+1) == 255) {
716 break;
717 }
8e669ac1 718
8523533c
TK
719 (*data)[num_decoded] = *p;
720 (*data)[num_decoded] <<= 2;
8e669ac1 721
8523533c
TK
722 tmp_c = *(p+1);
723 tmp_c >>= 4;
724 (*data)[num_decoded] |= tmp_c;
8e669ac1 725
8523533c
TK
726 num_decoded++;
727 p++;
8e669ac1 728
8523533c
TK
729 /* byte 1 ---------------------- */
730 if (*(p+1) == 255) {
731 break;
732 }
8e669ac1 733
8523533c
TK
734 (*data)[num_decoded] = *p;
735 (*data)[num_decoded] <<= 4;
8e669ac1 736
8523533c
TK
737 tmp_c = *(p+1);
738 tmp_c >>= 2;
739 (*data)[num_decoded] |= tmp_c;
8e669ac1 740
8523533c
TK
741 num_decoded++;
742 p++;
8e669ac1 743
8523533c
TK
744 /* byte 2 ---------------------- */
745 if (*(p+1) == 255) {
746 break;
747 }
8e669ac1 748
8523533c
TK
749 (*data)[num_decoded] = *p;
750 (*data)[num_decoded] <<= 6;
8e669ac1 751
8523533c 752 (*data)[num_decoded] |= *(p+1);
8e669ac1 753
8523533c
TK
754 num_decoded++;
755 p+=2;
8e669ac1 756
8523533c 757 };
8e669ac1 758 return num_decoded;
8523533c
TK
759 /* ---------------------------------------------- */
760 }
761 else if (mime_demux_mode == MIME_DEMUX_MODE_QP) {
762 /* ---------------------------------------------- */
763 p = line;
8e669ac1 764
8523533c
TK
765 while (*p != 0) {
766 if (*p == '=') {
767 int decode_qp_result;
8e669ac1 768
8523533c 769 p = mime_decode_qp(p,&decode_qp_result);
8e669ac1 770
8523533c
TK
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) {
8e669ac1 782 /* End of the line with soft line break.
8523533c
TK
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 };
8e669ac1 801
8523533c
TK
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
812variable and the demime_errorlevel gauge. */
813
814void 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);
b07e6aa3 823 (void)string_vformat(US f, 16383,(char *)format, ap);
8523533c
TK
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
853int 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;
8e669ac1 865
8523533c
TK
866 /* allocate room for our linebuffer */
867 line = (uschar *)malloc(MIME_SANITY_MAX_LINE_LENGTH);
868 if (line == NULL) {
b07e6aa3 869 (void)string_format(info, 1024,"unable to allocate %u bytes",MIME_SANITY_MAX_LINE_LENGTH);
8523533c
TK
870 return DEFER;
871 };
8e669ac1 872
8523533c
TK
873 /* clear MIME header structure */
874 memset(&mime_part_p,0,sizeof(mime_part));
8e669ac1 875
8523533c
TK
876 /* ----------------------- start demux loop --------------------- */
877 while (mime_read_line_status == MIME_READ_LINE_OK) {
8e669ac1 878
8523533c
TK
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);
8e669ac1 882
8523533c
TK
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 --------------------- */
8e669ac1 895
8523533c
TK
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;
8e669ac1 901
8523533c
TK
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 };
8e669ac1 911
8523533c
TK
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;
8e669ac1 927
8523533c
TK
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;
8e669ac1 935
8523533c
TK
936 /* check for message/rfcxxx attachments */
937 if (mime_header_find(line,US"message/rfc822",NULL) > 0)
938 has_rfc822 = 1;
8e669ac1 939
8523533c
TK
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);
8e669ac1 947 mime_part_p.extension = value;
8523533c
TK
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));
8e669ac1
PH
957
958 this_extension->file_extension_string =
8523533c
TK
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 };
8e669ac1 967
8523533c
TK
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) {
8e669ac1 974 mime_trigger_error(MIME_ERRORLEVEL_BOUNDARY_LENGTH);
8523533c
TK
975 }
976 else {
977 thisboundary = (struct boundary*)malloc(sizeof(boundary));
978 thisboundary->next = boundaries;
979 thisboundary->boundary_string = value;
980 boundaries = thisboundary;
981 };
982 };
8e669ac1 983
8523533c
TK
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 -------------- */
8e669ac1 994
8523533c
TK
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;
8e669ac1 1014
8523533c
TK
1015 if (mime_part_p.seen_content_disposition == 0) {
1016 mime_part_p.seen_content_disposition = 1;
8e669ac1 1017
8523533c
TK
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));
8e669ac1
PH
1031
1032 this_extension->file_extension_string =
8523533c
TK
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. */
8e669ac1 1075
8523533c 1076 data_len = uu_decode_line(line,&data,line_len,info);
8e669ac1 1077
8523533c
TK
1078 if (data_len == -2) {
1079 /* temp error, turn off uudecode mode */
1080 if (uu_dump_file != NULL) {
f1e894f3 1081 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
1082 };
1083 uu_mode = MIME_UU_MODE_OFF;
1084 return DEFER;
1085 }
1086 else if (data_len == -1) {
1087 if (uu_dump_file != NULL) {
f1e894f3 1088 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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) {
f1e894f3 1104 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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) {
f1e894f3 1113 (void)fclose(uu_dump_file); uu_dump_file = NULL;
8523533c
TK
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 };
8e669ac1 1124
8523533c
TK
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 */
b07e6aa3 1129 (void)string_format(info, 1024,"short write on uudecode dump file");
8523533c 1130 free(line);
8e669ac1 1131 return DEFER;
8523533c
TK
1132 };
1133 };
1134 };
8e669ac1 1135
8523533c
TK
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 };
8e669ac1 1154
f1e894f3 1155 (void)fclose(mime_dump_file); mime_dump_file = NULL;
8523533c
TK
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 };
8e669ac1 1169
f1e894f3 1170 (void)fclose(mime_dump_file); mime_dump_file = NULL;
8523533c
TK
1171 };
1172 }
1173 else {
1174 uschar *data;
1175 long data_len = 0;
8e669ac1 1176
8523533c
TK
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 };
8e669ac1 1190
8523533c
TK
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 */
b07e6aa3 1195 (void)string_format(info, 1024,"short write on dump file");
8523533c 1196 free(line);
8e669ac1 1197 return DEFER;
8523533c
TK
1198 };
1199 };
8e669ac1 1200
8523533c
TK
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 };
8e669ac1 1212
8523533c
TK
1213 };
1214 /* ------------------------------------------------ */
1215 };
1216 };
1217 /* ----------------------- end demux loop ----------------------- */
8e669ac1 1218
8523533c
TK
1219 /* close files, they could still be open */
1220 if (mime_dump_file != NULL)
f1e894f3 1221 (void)fclose(mime_dump_file);
8523533c 1222 if (uu_dump_file != NULL)
f1e894f3 1223 (void)fclose(uu_dump_file);
8e669ac1 1224
8523533c
TK
1225 /* release line buffer */
1226 free(line);
8e669ac1 1227
8523533c
TK
1228 /* FIXME: release boundary buffers.
1229 Not too much of a problem since
1230 this instance of exim is not resident. */
8e669ac1 1231
8523533c
TK
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 */
8e669ac1 1236
b07e6aa3 1237 (void)string_format(file_name,1024,"%s/scan/%s",spool_directory,message_id);
8523533c
TK
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