Commit | Line | Data |
---|---|---|
8e669ac1 | 1 | /* $Cambridge: exim/src/src/demime.c,v 1.4 2005/02/17 11:58:26 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 | ||
17 | uschar demime_reason_buffer[1024]; | |
18 | struct file_extension *file_extensions = NULL; | |
19 | ||
20 | int demime(uschar **listptr) { | |
21 | int sep = 0; | |
22 | uschar *list = *listptr; | |
23 | uschar *option; | |
24 | uschar option_buffer[64]; | |
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 | |
8523533c | 63 | 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 | ||
114 | unsigned 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 | ||
140 | uschar *mime_decode_qp(uschar *qp_p,int *c) { | |
141 | uschar hex[] = {0,0,0}; | |
142 | int nan = 0; | |
143 | uschar *initial_pos = qp_p; | |
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 | ||
213 | int mime_get_dump_file(uschar *extension, FILE **f, uschar *info) { | |
214 | uschar file_name[1024]; | |
215 | int result; | |
216 | unsigned int file_nr; | |
217 | uschar default_extension[] = ".com"; | |
218 | uschar *p; | |
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 | |
8523533c TK |
248 | snprintf(CS file_name,1024,"%s/scan/%s/%s-%05u%s",spool_directory,message_id,message_id,file_nr,extension); |
249 | file_nr++; | |
250 | if (file_nr >= MIME_SANITY_MAX_DUMP_FILES) { | |
251 | /* max parts reached */ | |
252 | mime_trigger_error(MIME_ERRORLEVEL_TOO_MANY_PARTS); | |
253 | break; | |
254 | }; | |
255 | result = stat(CS file_name,&mystat); | |
256 | } | |
257 | while(result != -1); | |
8e669ac1 | 258 | |
8523533c TK |
259 | *f = fopen(CS file_name,"w+"); |
260 | if (*f == NULL) { | |
261 | /* cannot open new dump file, disk full ? -> soft error */ | |
262 | snprintf(CS info, 1024,"unable to open dump file"); | |
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 | ||
282 | int 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 | ||
324 | int mime_read_line(FILE *f, int mime_demux_mode, uschar *buffer, long *num_copied) { | |
325 | int c = EOF; | |
326 | int done = 0; | |
327 | int header_value_mode = 0; | |
328 | int header_open_brackets = 0; | |
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; | |
349 | ungetc(c,f); | |
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 | ||
443 | int mime_check_boundary(uschar *line, struct boundary *boundaries) { | |
444 | struct boundary *thisboundary = boundaries; | |
445 | uschar workbuf[MIME_SANITY_MAX_LINE_LENGTH+1]; | |
446 | unsigned int i,j=0; | |
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]; | |
455 | j++; | |
456 | }; | |
457 | }; | |
458 | workbuf[j+1]='\0'; | |
459 | ||
460 | while(thisboundary != NULL) { | |
461 | if (Ustrncmp(workbuf,thisboundary->boundary_string,Ustrlen(thisboundary->boundary_string)) == 0) { | |
462 | if (Ustrncmp(&workbuf[Ustrlen(thisboundary->boundary_string)],"--",2) == 0) { | |
463 | /* final boundary found */ | |
464 | return 2; | |
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 | ||
484 | int 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 | ||
524 | int warned_about_uudec_line_sanity_1 = 0; | |
525 | int warned_about_uudec_line_sanity_2 = 0; | |
526 | long uu_decode_line(uschar *line, uschar **data, long line_len, uschar *info) { | |
527 | uschar *p; | |
528 | long num_decoded = 0; | |
529 | uschar tmp_c; | |
530 | uschar *work; | |
531 | int uu_decoded_line_len, uu_encoded_line_len; | |
8e669ac1 | 532 | |
8523533c TK |
533 | /* allocate memory for data and work buffer */ |
534 | *data = (uschar *)malloc(line_len); | |
535 | if (*data == NULL) { | |
536 | snprintf(CS info, 1024,"unable to allocate %lu bytes",line_len); | |
537 | return -2; | |
538 | }; | |
539 | ||
540 | work = (uschar *)malloc(line_len); | |
541 | if (work == NULL) { | |
542 | snprintf(CS info, 1024,"unable to allocate %lu bytes",line_len); | |
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 | ||
646 | int warned_about_b64_line_length = 0; | |
647 | int warned_about_b64_line_sanity = 0; | |
648 | int warned_about_b64_illegal_char = 0; | |
649 | int warned_about_qp_line_sanity = 0; | |
650 | long mime_decode_line(int mime_demux_mode,uschar *line, uschar **data, long max_data_len, uschar *info) { | |
651 | uschar *p; | |
652 | long num_decoded = 0; | |
653 | int offset = 0; | |
654 | uschar tmp_c; | |
8e669ac1 | 655 | |
8523533c TK |
656 | /* allocate memory for data */ |
657 | *data = (uschar *)malloc(max_data_len); | |
658 | if (*data == NULL) { | |
659 | snprintf(CS info, 1024,"unable to allocate %lu bytes",max_data_len); | |
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 | |
812 | variable and the demime_errorlevel gauge. */ | |
813 | ||
814 | void mime_trigger_error(int level, uschar *format, ...) { | |
815 | char *f; | |
816 | va_list ap; | |
817 | ||
818 | if( (f = malloc(16384+23)) != NULL ) { | |
819 | /* first log the incident */ | |
820 | sprintf(f,"demime acl condition: "); | |
821 | f+=22; | |
822 | va_start(ap, format); | |
823 | vsnprintf(f, 16383,(char *)format, ap); | |
824 | va_end(ap); | |
825 | f-=22; | |
826 | log_write(0, LOG_MAIN, f); | |
827 | /* then copy to demime_reason_buffer if new | |
828 | level is greater than old level */ | |
829 | if (level > demime_errorlevel) { | |
830 | demime_errorlevel = level; | |
831 | Ustrcpy(demime_reason_buffer, US f); | |
832 | demime_reason = demime_reason_buffer; | |
833 | }; | |
834 | free(f); | |
835 | }; | |
836 | } | |
837 | ||
838 | /************************************************* | |
839 | * Demultiplex MIME stream. * | |
840 | *************************************************/ | |
841 | ||
842 | /* We can handle BASE64, QUOTED-PRINTABLE, and UUENCODE. | |
843 | UUENCODE does not need to have a proper | |
844 | transfer-encoding header, we detect it with "begin" | |
845 | ||
846 | This function will report human parsable errors in | |
847 | *info. | |
848 | ||
849 | returns DEFER -> soft error (see *info) | |
850 | OK -> EOF hit, all ok | |
851 | */ | |
852 | ||
853 | int mime_demux(FILE *f, uschar *info) { | |
854 | int mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS; | |
855 | int uu_mode = MIME_UU_MODE_OFF; | |
856 | FILE *mime_dump_file = NULL; | |
857 | FILE *uu_dump_file = NULL; | |
858 | uschar *line; | |
859 | int mime_read_line_status = MIME_READ_LINE_OK; | |
860 | long line_len; | |
861 | struct boundary *boundaries = NULL; | |
862 | struct mime_part mime_part_p; | |
863 | int has_tnef = 0; | |
864 | int has_rfc822 = 0; | |
8e669ac1 | 865 | |
8523533c TK |
866 | /* allocate room for our linebuffer */ |
867 | line = (uschar *)malloc(MIME_SANITY_MAX_LINE_LENGTH); | |
868 | if (line == NULL) { | |
869 | snprintf(CS info, 1024,"unable to allocate %u bytes",MIME_SANITY_MAX_LINE_LENGTH); | |
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) { | |
1081 | fclose(uu_dump_file); uu_dump_file = NULL; | |
1082 | }; | |
1083 | uu_mode = MIME_UU_MODE_OFF; | |
1084 | return DEFER; | |
1085 | } | |
1086 | else if (data_len == -1) { | |
1087 | if (uu_dump_file != NULL) { | |
1088 | fclose(uu_dump_file); uu_dump_file = NULL; | |
1089 | }; | |
1090 | uu_mode = MIME_UU_MODE_OFF; | |
1091 | data_len = 0; | |
1092 | } | |
1093 | else if (data_len > 0) { | |
1094 | /* we have at least decoded a valid byte | |
1095 | turn on confirmed mode */ | |
1096 | uu_mode = MIME_UU_MODE_CONFIRMED; | |
1097 | }; | |
1098 | } | |
1099 | else if (uu_mode == MIME_UU_MODE_CONFIRMED) { | |
1100 | /* If we are in confirmed UU mode, | |
1101 | check for single "end" tag on line */ | |
1102 | if ((strncmpic(line,US"end",3) == 0) && (line[3] < 32)) { | |
1103 | if (uu_dump_file != NULL) { | |
1104 | fclose(uu_dump_file); uu_dump_file = NULL; | |
1105 | }; | |
1106 | uu_mode = MIME_UU_MODE_OFF; | |
1107 | } | |
1108 | else { | |
1109 | data_len = uu_decode_line(line,&data,line_len,info); | |
1110 | if (data_len == -2) { | |
1111 | /* temp error, turn off uudecode mode */ | |
1112 | if (uu_dump_file != NULL) { | |
1113 | fclose(uu_dump_file); uu_dump_file = NULL; | |
1114 | }; | |
1115 | uu_mode = MIME_UU_MODE_OFF; | |
1116 | return DEFER; | |
1117 | } | |
1118 | else if (data_len == -1) { | |
1119 | /* skip this line */ | |
1120 | data_len = 0; | |
1121 | }; | |
1122 | }; | |
1123 | }; | |
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 */ | |
1129 | snprintf(CS info, 1024,"short write on uudecode dump file"); | |
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 | |
8523533c TK |
1155 | fclose(mime_dump_file); mime_dump_file = NULL; |
1156 | }; | |
1157 | } | |
1158 | else if (tmp == 2) { | |
1159 | /* We have hit a known end boundary. | |
1160 | That puts us into scanning mode, which will end when we hit another known start boundary */ | |
1161 | mime_demux_mode = MIME_DEMUX_MODE_SCANNING; | |
1162 | if (mime_dump_file != NULL) { | |
1163 | /* if the attachment was a RFC822 message, recurse into it */ | |
1164 | if (has_rfc822) { | |
1165 | has_rfc822 = 0; | |
1166 | rewind(mime_dump_file); | |
1167 | mime_demux(mime_dump_file,info); | |
1168 | }; | |
8e669ac1 | 1169 | |
8523533c TK |
1170 | fclose(mime_dump_file); mime_dump_file = NULL; |
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 */ | |
1195 | snprintf(CS info, 1024,"short write on dump file"); | |
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) | |
1221 | fclose(mime_dump_file); | |
1222 | if (uu_dump_file != NULL) | |
1223 | 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 | |
8523533c TK |
1237 | snprintf(CS file_name,1024,"%s/scan/%s",spool_directory,message_id); |
1238 | /* Removed FTTB. We need to decide on TNEF inclusion */ | |
1239 | /* mime_unpack_tnef(file_name); */ | |
1240 | }; | |
1241 | ||
1242 | return 0; | |
1243 | } | |
1244 | ||
1245 | #endif |