1 /*-----------------------------------------------------------------------
2 **
3 **
4 ** mime.c
5 **
6 ** Written by Paul L Daniels, originally for the Xamime project
7 ** (http://www.xamime.com) but since spawned off to the ripMIME/alterMIME
8 ** family of email parsing tools.
9 **
10 ** Copyright PLD, 1999,2000,2001,2002,2003
11 ** Licence: BSD
12 ** For more information on the licence and copyrights of this code, please
13 ** email copyright@pldaniels.com
14
15 ** CHANGES
16 ** 2003-Jun-24: PLD: Added subject retaining in the global struct
17 ** this is useful for when you want to retrieve such information
18 ** from an external application - without having to dive into
19 ** the hinfo struct directly. Also, the subject is retained only
20 ** for the /primary/ headers, all subsequent headers which [may]
21 ** contain 'subject:' are ignored.
22 **
23
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <ctype.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <time.h>
36 #include <errno.h>
37 #include <dirent.h>
38
39 #ifdef MEMORY_DEBUG
40 #define DEBUG_MEMORY 1
41 #include "xmalloc.h"
42 #endif
43
44 #include "pldstr.h"
45 #include "boundary-stack.h"
46 #include "ffget.h"
47 #include "mime.h"
48 #include "tnef/tnef_api.h"
49 #include "ripOLE/ole.h"
50 #include "libmime-decoders.h"
51 #include "uuencode.h"
52 #include "filename-filters.h"
53 #include "logger.h"
54 #include "strstack.h"
55
56 #include "MIME_headers.h"
57
58
59 int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss );
60 int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss );
61 int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss );
62 int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss );
63 int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss );
64 int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss );
65
66 // Predefined filenames
67 #define MIME_BLANKZONE_FILENAME_DEFAULT "_blankzone_"
68 #define MIME_HEADERS_FILENAME "_headers_"
69
70 #ifndef FL
71 #define FL __FILE__, __LINE__
72 #endif
73
74 #define _ENC_UNKNOWN 0
75 #define _ENC_BASE64 1
76 #define _ENC_PLAINTEXT 2
77 #define _ENC_QUOTED 3
78 #define _ENC_EMBEDDED 4
79 #define _ENC_NOFILE -1
80 #define _MIME_CHARS_PER_LINE 32
81 #define _MIME_MAX_CHARS_PER_LINE 76
82 #define _RECURSION_LEVEL_DEFAULT 20
83
84 #define _BOUNDARY_CRASH 2
85
86 // BASE64 / UUDEC and other binary writing routines use the write buffer (now in v1.2.16.3+)
87 // The "limit" define is a check point marker which indicates that on our next run through
88 // either the BASE64 or UUDEC routines, we need to flush the buffer to disk
89
90 #define MIME_MIME_READ_BUFFER_SIZE (8 *1024)
91 #define _MIME_WRITE_BUFFER_SIZE (8 *1024)
92 #define _MIME_WRITE_BUFFER_LIMIT (_MIME_WRITE_BUFFER_SIZE -4)
93
94
95
96 // Debug precodes
97 #define MIME_DPEDANTIC ((glb.debug >= _MIME_DEBUG_PEDANTIC))
98 #define MIME_DNORMAL ((glb.debug >= _MIME_DEBUG_NORMAL ))
99 #define MIME_VERBOSE ((glb.verbosity > 0 ))
100 #define MIME_VERBOSE_12 ((glb.verbosity_12x_style > 0 ))
101 #define DMIME if (glb.debug >= _MIME_DEBUG_NORMAL)
102
103 #define FL __FILE__,__LINE__
104
105 /* our base 64 decoder table */
106 static unsigned char b64[256]={
107 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
108 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
109 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\
110 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\
111 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\
112 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\
113 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\
114 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\
115 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
116 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
117 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
118 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
119 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
120 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
121 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\
122 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \
123 };
124
125
126
127
128 struct MIME_globals {
129
130 int header_defect_count;
131 int filecount;
132 char blankfileprefix[_MIME_STRLEN_MAX];
133 int blankfileprefix_expliticly_set;
134 int verbosity;
135 int verbosity_12x_style;
136 int verbosity_contenttype;
137 int verbose_defects;
138 int report_mime;
139 int debug;
140 int quiet;
141 int syslogging;
142 int stderrlogging;
143 int unique_names;
144 int rename_method;
145 char headersname[_MIME_STRLEN_MAX];
146 char tempdirectory[_MIME_STRLEN_MAX];
147 int save_headers;
148 int attachment_count;
149 int current_line;
150 int no_nameless;
151 int name_by_type;
152 int mailbox_format;
153
154 int decode_uu;
155 int decode_tnef;
156 int decode_b64;
157 int decode_qp;
158 int decode_mht;
159 int decode_ole;
160
161 int multiple_filenames;
162
163 int header_longsearch;
164 int max_recursion_level;
165
166 int blankzone_saved;
167 int blankzone_save_option;
168 char blankzone_filename[_MIMEH_STRLEN_MAX +1];
169
170 int (*filename_decoded_reporter)(char *, char *); // Pointer to the reporting function for filenames as they are decoded
171
172
173 // First subject is an important item, because this
174 // represents the "main" subject of the email, as
175 // seen by the MUA. We have to store this locally
176 // rather than rely purely on hinfo, because other
177 // wise, any consequent parsing of sub-message bodies
178 // will result in the clobbering of the hinfo struct
179 char subject[_MIME_STRLEN_MAX];
180
181 };
182
183 static struct MIME_globals glb;
184
185
186
187 //char OK[]="OKAY";
188
189 static char scratch[1024];
190
191
192
193 /* File pointer for the headers output */
194 FILE *headers;
195
196
197
198
199 /*-----------------------------------------------------------------\
200 Function Name : MIME_version
201 Returns Type : int
202 ----Parameter List
203 1. void ,
204 ------------------
205 Exit Codes :
206 Side Effects :
207 --------------------------------------------------------------------
208 Comments:
209
210 --------------------------------------------------------------------
211 Changes:
212
213 \------------------------------------------------------------------*/
MIME_version(void)214 int MIME_version( void )
215 {
216 fprintf(stderr,"ripMIME: %s\n", LIBMIME_VERSION);
217 MIMEH_version();
218 #ifdef RIPOLE
219 OLE_version();
220 #endif
221
222 return 0;
223 }
224
225
226 /*-----------------------------------------------------------------\
227 Function Name : MIME_set_name_by_type
228 Returns Type : int
229 ----Parameter List
230 1. int level ,
231 ------------------
232 Exit Codes :
233 Side Effects :
234 --------------------------------------------------------------------
235 Comments:
236 Used to set (on/off) the name-by-type feature, which will
237 use the content-type paramter to name the attachment rather
238 than just the default textfile* naming
239
240 0 = don't name by type
241 1 = name by type.
242
243 We may consider adding more levels to this to account for
244 the different types of content-types.
245
246 --------------------------------------------------------------------
247 Changes:
248
249 \------------------------------------------------------------------*/
MIME_set_name_by_type(int level)250 int MIME_set_name_by_type( int level )
251 {
252 glb.name_by_type = level;
253
254 return glb.name_by_type;
255 }
256
257 /*------------------------------------------------------------------------
258 Procedure: MIME_set_debug ID:1
259 Purpose: Sets the debug level for reporting in MIME
260 Input: int level : What level of debugging to use, currently there
261 are only two levels, 0 = none, > 0 = debug info
262 Output:
263 Errors:
264 ------------------------------------------------------------------------*/
MIME_set_debug(int level)265 int MIME_set_debug( int level )
266 {
267 glb.debug = level;
268
269 BS_set_debug(level);
270 TNEF_set_debug(level);
271 MIMEH_set_debug(level);
272 MDECODE_set_debug(level);
273 UUENCODE_set_debug(level);
274 FNFILTER_set_debug(level);
275
276 return glb.debug;
277 }
278
279 /*-----------------------------------------------------------------\
280 Function Name : MIME_set_quiet
281 Returns Type : int
282 ----Parameter List
283 1. int level ,
284 ------------------
285 Exit Codes :
286 Side Effects :
287 --------------------------------------------------------------------
288 Comments:
289
290 --------------------------------------------------------------------
291 Changes:
292
293 \------------------------------------------------------------------*/
MIME_set_quiet(int level)294 int MIME_set_quiet( int level )
295 {
296 glb.quiet = level;
297 MIME_set_debug(0);
298 MIME_set_verbosity(0);
299
300 return glb.quiet;
301 }
302
303
304 /*-----------------------------------------------------------------\
305 Function Name : MIME_set_recursion_level
306 Returns Type : int
307 ----Parameter List
308 1. int level ,
309 ------------------
310 Exit Codes :
311 Side Effects :
312 --------------------------------------------------------------------
313 Comments:
314
315 --------------------------------------------------------------------
316 Changes:
317
318 \------------------------------------------------------------------*/
MIME_set_recursion_level(int level)319 int MIME_set_recursion_level( int level )
320 {
321 glb.max_recursion_level = level;
322 return glb.max_recursion_level;
323 }
324
325 /*-----------------------------------------------------------------\
326 Function Name : MIME_set_decode_tnef
327 Returns Type : int
328 ----Parameter List
329 1. int level ,
330 ------------------
331 Exit Codes :
332 Side Effects :
333 --------------------------------------------------------------------
334 Comments:
335
336 --------------------------------------------------------------------
337 Changes:
338
339 \------------------------------------------------------------------*/
MIME_set_decode_tnef(int level)340 int MIME_set_decode_tnef( int level )
341 {
342 glb.decode_tnef = level;
343
344 TNEF_set_decode( level );
345
346
347 return level;
348 }
349
350
351 /*-----------------------------------------------------------------\
352 Function Name : MIME_set_decode_ole
353 Returns Type : int
354 ----Parameter List
355 1. int level ,
356 ------------------
357 Exit Codes :
358 Side Effects :
359 --------------------------------------------------------------------
360 Comments:
361
362 --------------------------------------------------------------------
363 Changes:
364
365 \------------------------------------------------------------------*/
MIME_set_decode_ole(int level)366 int MIME_set_decode_ole( int level )
367 {
368 glb.decode_ole = level;
369
370 return level;
371 }
372
373 /*-----------------------------------------------------------------\
374 Function Name : MIME_set_decode_uudecode
375 Returns Type : int
376 ----Parameter List
377 1. int level ,
378 ------------------
379 Exit Codes :
380 Side Effects :
381 --------------------------------------------------------------------
382 Comments:
383
384 --------------------------------------------------------------------
385 Changes:
386
387 \------------------------------------------------------------------*/
MIME_set_decode_uudecode(int level)388 int MIME_set_decode_uudecode( int level )
389 {
390 glb.decode_uu = level;
391 UUENCODE_set_decode( level );
392
393 return glb.decode_uu;
394 }
395
396 /*-----------------------------------------------------------------\
397 Function Name : MIME_set_decode_base64
398 Returns Type : int
399 ----Parameter List
400 1. int level ,
401 ------------------
402 Exit Codes :
403 Side Effects :
404 --------------------------------------------------------------------
405 Comments:
406
407 --------------------------------------------------------------------
408 Changes:
409
410 \------------------------------------------------------------------*/
MIME_set_decode_base64(int level)411 int MIME_set_decode_base64( int level )
412 {
413 glb.decode_b64 = level;
414
415 return glb.decode_b64;
416 }
417
418 /*-----------------------------------------------------------------\
419 Function Name : MIME_set_decode_qp
420 Returns Type : int
421 ----Parameter List
422 1. int level ,
423 ------------------
424 Exit Codes :
425 Side Effects :
426 --------------------------------------------------------------------
427 Comments:
428
429 --------------------------------------------------------------------
430 Changes:
431
432 \------------------------------------------------------------------*/
MIME_set_decode_qp(int level)433 int MIME_set_decode_qp( int level )
434 {
435 glb.decode_qp = level;
436 MDECODE_set_decode_qp(level);
437
438 return glb.decode_qp;
439 }
440
441 /*-----------------------------------------------------------------\
442 Function Name : MIME_set_decode_doubleCR
443 Returns Type : int
444 ----Parameter List
445 1. int level ,
446 ------------------
447 Exit Codes :
448 Side Effects :
449 --------------------------------------------------------------------
450 Comments:
451
452 --------------------------------------------------------------------
453 Changes:
454
455 \------------------------------------------------------------------*/
MIME_set_decode_doubleCR(int level)456 int MIME_set_decode_doubleCR( int level )
457 {
458 MIMEH_set_doubleCR_save(level); /** 20041106-0859:PLD: Was '0' **/
459
460 return MIMEH_get_doubleCR_save();
461 }
462
463 /*-----------------------------------------------------------------\
464 Function Name : MIME_set_decode_mht
465 Returns Type : int
466 ----Parameter List
467 1. int level ,
468 ------------------
469 Exit Codes :
470 Side Effects :
471 --------------------------------------------------------------------
472 Comments:
473
474 --------------------------------------------------------------------
475 Changes:
476
477 \------------------------------------------------------------------*/
MIME_set_decode_mht(int level)478 int MIME_set_decode_mht( int level )
479 {
480 glb.decode_mht = level;
481 return glb.decode_mht;
482 }
483
484
485 /*-----------------------------------------------------------------\
486 Function Name : MIME_set_header_longsearch
487 Returns Type : int
488 ----Parameter List
489 1. int level ,
490 ------------------
491 Exit Codes :
492 Side Effects :
493 --------------------------------------------------------------------
494 Comments:
495
496 --------------------------------------------------------------------
497 Changes:
498
499 \------------------------------------------------------------------*/
MIME_set_header_longsearch(int level)500 int MIME_set_header_longsearch( int level )
501 {
502 glb.header_longsearch = level;
503 return glb.header_longsearch;
504 }
505
506 /*------------------------------------------------------------------------
507 Procedure: MIME_set_tmpdir ID:1
508 Purpose: Sets the internal Temporary directory name.
509 Input:
510 Output:
511 Errors:
512 ------------------------------------------------------------------------*/
MIME_set_tmpdir(char * setto)513 int MIME_set_tmpdir( char *setto )
514 {
515
516 PLD_strncpy(glb.tempdirectory,setto, _MIME_STRLEN_MAX);
517
518 return 0;
519 }
520
521
522
523
524
525 /*------------------------------------------------------------------------
526 Procedure: MIME_set_glb.blankfileprefix ID:1
527 Purpose: Sets the filename prefix which is to be used for any files which are saved and do not have a defined filename.
528 Input: char *prefix : \0 terminated character array defining the filename prefix
529 Output:
530 Errors:
531 ------------------------------------------------------------------------*/
MIME_set_blankfileprefix(char * prefix)532 int MIME_set_blankfileprefix( char *prefix )
533 {
534 PLD_strncpy( glb.blankfileprefix, prefix, _MIME_STRLEN_MAX );
535 glb.blankfileprefix_expliticly_set = 1;
536 return 0;
537 }
538
539
540 /*------------------------------------------------------------------------
541 Procedure: MIME_get_glb.blankfileprefix ID:1
542 Purpose:
543 Input:
544 Output:
545 Errors:
546 ------------------------------------------------------------------------*/
MIME_get_blankfileprefix(void)547 char *MIME_get_blankfileprefix( void )
548 {
549 return glb.blankfileprefix;
550 }
551
552
553
554
555
556
557 /*-------------------------------------------------------
558 * MIME_setglb.verbosity
559 *
560 * By default, MIME reports nothing as its working
561 * Setting the verbosity level > 0 means that it'll
562 * report things like the name of the files it's
563 * writing/extracting.
564 *
565 */
MIME_set_verbosity(int level)566 int MIME_set_verbosity( int level )
567 {
568
569 glb.verbosity = level;
570
571 MIMEH_set_verbosity( level );
572 TNEF_set_verbosity( level );
573 FNFILTER_set_verbose( level );
574 UUENCODE_set_verbosity( level );
575 MDECODE_set_verbose( level );
576 BS_set_verbose( level );
577
578
579 return 0;
580 }
581
582
583
584
585 /*-----------------------------------------------------------------\
586 Function Name : MIME_set_verbosity_12x_style
587 Returns Type : int
588 ----Parameter List
589 1. int level ,
590 ------------------
591 Exit Codes :
592 Side Effects :
593 --------------------------------------------------------------------
594 Comments:
595
596 --------------------------------------------------------------------
597 Changes:
598
599 \------------------------------------------------------------------*/
MIME_set_verbosity_12x_style(int level)600 int MIME_set_verbosity_12x_style( int level )
601 {
602 glb.verbosity_12x_style = level;
603 MIME_set_verbosity( level );
604 return level;
605 }
606
607
608 /*-----------------------------------------------------------------\
609 Function Name : MIME_set_verbosity_contenttype
610 Returns Type : int
611 ----Parameter List
612 1. int level ,
613 ------------------
614 Exit Codes :
615 Side Effects :
616 --------------------------------------------------------------------
617 Comments:
618
619 --------------------------------------------------------------------
620 Changes:
621
622 \------------------------------------------------------------------*/
MIME_set_verbosity_contenttype(int level)623 int MIME_set_verbosity_contenttype( int level )
624 {
625 glb.verbosity_contenttype = level;
626 MIMEH_set_verbosity_contenttype( level );
627 UUENCODE_set_verbosity_contenttype( level );
628 TNEF_set_verbosity_contenttype( level );
629
630 return glb.verbosity_contenttype;
631 }
632
633
634 /*-----------------------------------------------------------------\
635 Function Name : MIME_set_verbose_defects
636 Returns Type : int
637 ----Parameter List
638 1. int level ,
639 ------------------
640 Exit Codes :
641 Side Effects :
642 --------------------------------------------------------------------
643 Comments:
644
645 --------------------------------------------------------------------
646 Changes:
647
648 \------------------------------------------------------------------*/
MIME_set_verbose_defects(int level)649 int MIME_set_verbose_defects( int level )
650 {
651 glb.verbose_defects = level;
652
653 return glb.verbose_defects;
654 }
655
656
657 /*-----------------------------------------------------------------\
658 Function Name : MIME_set_report_MIME
659 Returns Type : int
660 ----Parameter List
661 1. int level ,
662 ------------------
663 Exit Codes :
664 Side Effects :
665 --------------------------------------------------------------------
666 Comments:
667
668 --------------------------------------------------------------------
669 Changes:
670
671 \------------------------------------------------------------------*/
MIME_set_report_MIME(int level)672 int MIME_set_report_MIME( int level )
673 {
674 glb.report_mime = level;
675 /**MIMEH_set_report_MIME(glb.report_mime); - not yet implemented **/
676
677 return 0;
678 }
679
680 /*-----------------------------------------------------------------\
681 Function Name : MIME_set_filename_report_fn
682 Returns Type : int
683 ----Parameter List
684 1. int (*ptr_to_fn)(char *,
685 2. char *) ,
686 ------------------
687 Exit Codes :
688 Side Effects :
689 --------------------------------------------------------------------
690 Comments:
691
692 --------------------------------------------------------------------
693 Changes:
694
695 \------------------------------------------------------------------*/
MIME_set_filename_report_fn(int (* ptr_to_fn)(char *,char *))696 int MIME_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) )
697 {
698 glb.filename_decoded_reporter = ptr_to_fn;
699 UUENCODE_set_filename_report_fn( ptr_to_fn );
700 TNEF_set_filename_report_fn( ptr_to_fn );
701
702 return 0;
703 }
704
705
706
707 /*-------------------------------------------------------
708 * MIME_set_dumpheaders
709 *
710 * By default MIME wont dump the headers to a text file
711 * but at times this is useful esp for checking
712 * for new styles of viruses like the KAK.worm
713 *
714 * Anything > 0 will make the headers be saved
715 *
716 */
MIME_set_dumpheaders(int level)717 int MIME_set_dumpheaders( int level )
718 {
719
720 glb.save_headers = level;
721
722 return 0;
723 }
724
725
726 /*-----------------------------------------------------------------\
727 Function Name : MIME_set_multiple_filenames
728 Returns Type : int
729 ----Parameter List
730 1. int level ,
731 ------------------
732 Exit Codes :
733 Side Effects :
734 --------------------------------------------------------------------
735 Comments:
736
737 --------------------------------------------------------------------
738 Changes:
739
740 \------------------------------------------------------------------*/
MIME_set_multiple_filenames(int level)741 int MIME_set_multiple_filenames( int level )
742 {
743 glb.multiple_filenames = level;
744
745 return 0;
746 }
747
748 /*------------------------------------------------------
749 * MIME_set_headersname
750 *
751 * by default, the headers would be dropped to a file
752 * called '_headers_'. Using this call we can make
753 * it pretty much anything we like
754 */
MIME_set_headersname(char * fname)755 int MIME_set_headersname( char *fname )
756 {
757
758 PLD_strncpy(glb.headersname, fname, _MIME_STRLEN_MAX);
759
760 return 0;
761 }
762
763
764
765 /*------------------------------------------------------------------------
766 Procedure: MIME_get_headersname ID:1
767 Purpose: Returns a pointer to the current glb.headersname string.
768 Input:
769 Output:
770 Errors:
771 ------------------------------------------------------------------------*/
MIME_get_headersname(void)772 char *MIME_get_headersname( void )
773 {
774 return glb.headersname;
775 }
776
777
778 /*-----------------------------------------------------------------\
779 Function Name : *MIME_get_subject
780 Returns Type : char
781 ----Parameter List
782 1. void ,
783 ------------------
784 Exit Codes :
785 Side Effects :
786 --------------------------------------------------------------------
787 Comments:
788
789 --------------------------------------------------------------------
790 Changes:
791
792 \------------------------------------------------------------------*/
MIME_get_subject(void)793 char *MIME_get_subject( void )
794 {
795 return glb.subject;
796 }
797
798 #ifdef RIPMIME_BLANKZONE
799 /* The blankzone functions are responsbile for telling ripMIME what to do
800 about the block of data which resides between the first/main headers
801 and the first attachment/block of a MIME encoded email.
802
803 Normally this would come out as textfile0, -except- for non-MIME
804 encoded emails which would actually have their real body saved as
805 textfile0.
806
807 There are potential kludge options for doing this, but I am going to try
808 and do it right.
809 */
MIME_set_blankzone_save_option(int option)810 int MIME_set_blankzone_save_option( int option )
811 {
812 switch ( option ) {
813 case MIME_BLANKZONE_SAVE_TEXTFILE:
814 glb.blankzone_save_option = option;
815 break;
816
817 case MIME_BLANKZONE_SAVE_FILENAME:
818 glb.blankzone_save_option = option;
819 snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT );
820 break;
821
822 default:
823 LOGGER_log("%s:%d:MIME_set_blankzone_save_option:WARNING: Unknown option for saving method (%d). Setting to '%s'",FL, option, MIME_BLANKZONE_FILENAME_DEFAULT );
824 glb.blankzone_save_option = MIME_BLANKZONE_SAVE_FILENAME;
825 snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT );
826 }
827
828 return glb.blankzone_save_option;
829
830 }
831
832 /*-----------------------------------------------------------------\
833 Function Name : MIME_set_blankzone_filename
834 Returns Type : int
835 ----Parameter List
836 1. char *filename ,
837 ------------------
838 Exit Codes :
839 Side Effects :
840 --------------------------------------------------------------------
841 Comments:
842
843 --------------------------------------------------------------------
844 Changes:
845
846 \------------------------------------------------------------------*/
MIME_set_blankzone_filename(char * filename)847 int MIME_set_blankzone_filename( char *filename )
848 {
849 PLD_strncpy( glb.blankzone_filename, filename, _MIME_STRLEN_MAX);
850
851 return 0;
852 }
853
854 /*-----------------------------------------------------------------\
855 Function Name : *MIME_get_blankzone_filename
856 Returns Type : char
857 ----Parameter List
858 1. void ,
859 ------------------
860 Exit Codes :
861 Side Effects :
862 --------------------------------------------------------------------
863 Comments:
864
865 --------------------------------------------------------------------
866 Changes:
867
868 \------------------------------------------------------------------*/
MIME_get_blankzone_filename(void)869 char *MIME_get_blankzone_filename( void )
870 {
871 return glb.blankzone_filename;
872 }
873 #endif
874
875
876
877
878
879
880 /*------------------------------------------------------------------------
881 Procedure: MIME_setglb.no_nameless ID:1
882 Purpose:
883 Input:
884 Output:
885 Errors:
886 ------------------------------------------------------------------------*/
MIME_set_no_nameless(int level)887 int MIME_set_no_nameless( int level )
888 {
889
890 glb.no_nameless = level;
891
892 return 0;
893 }
894
895
896
897
898
899
900 /*------------------------------------------------------------------------
901 Procedure: MIME_set_uniquenames ID:1
902 Purpose:
903 Input:
904 Output:
905 Errors:
906 ------------------------------------------------------------------------*/
MIME_set_uniquenames(int level)907 int MIME_set_uniquenames( int level )
908 {
909 glb.unique_names = level;
910
911 return 0;
912 }
913
914
915
916
917 /*------------------------------------------------------------------------
918 Procedure: MIME_set_noparanoid ID:1
919 Purpose: If set, will prevent MIME from clobbering what it considers
920 to be non-safe characters in the file name.
921 Input:
922 Output:
923 Errors:
924 ------------------------------------------------------------------------*/
MIME_set_paranoid(int level)925 int MIME_set_paranoid( int level )
926 {
927
928 FNFILTER_set_paranoid( level );
929
930 return level;
931 }
932
933
934
935
936 /*------------------------------------------------------------------------
937 Procedure: MIME_set_mailboxformat ID:1
938 Purpose: If sets the value for the _mailboxformat variable
939 in MIME, this indicates to functions later on
940 that they should be aware of possible mailbox
941 format specifiers.
942 Input:
943 Output:
944 Errors:
945 ------------------------------------------------------------------------*/
MIME_set_mailboxformat(int level)946 int MIME_set_mailboxformat( int level )
947 {
948 glb.mailbox_format = level;
949 MIMEH_set_mailbox( level );
950 return 0;
951 }
952
953
954
955
956
957
958 /*------------------------------------------------------------------------
959 Procedure: MIME_set_renamemethod ID:1
960 Purpose:
961 Input:
962 Output:
963 Errors:
964 ------------------------------------------------------------------------*/
MIME_set_renamemethod(int method)965 int MIME_set_renamemethod( int method )
966 {
967 if (( method >= _MIME_RENAME_METHOD_INFIX ) && ( method <= _MIME_RENAME_METHOD_POSTFIX ))
968 {
969 glb.rename_method = method;
970 }
971 else
972 {
973 LOGGER_log("%s:%d:MIME_set_renamemethod:ERROR: selected method not within %d > x > %d range", FL, _MIME_RENAME_METHOD_INFIX, _MIME_RENAME_METHOD_POSTFIX );
974 return -1;
975 }
976
977 return 0;
978 }
979
980
981
982
983 /*-----------------------------------------------------------------\
984 Function Name : MIME_get_header_defect_count
985 Returns Type : int
986 ----Parameter List
987 1. void ,
988 ------------------
989 Exit Codes :
990 Side Effects :
991 --------------------------------------------------------------------
992 Comments:
993
994 --------------------------------------------------------------------
995 Changes:
996
997 \------------------------------------------------------------------*/
MIME_get_header_defect_count(void)998 int MIME_get_header_defect_count( void )
999 {
1000 return glb.header_defect_count;
1001 }
1002
1003
1004 /*------------------------------------------------------------------------
1005 Procedure: MIME_getglb.attachment_count ID:1
1006 Purpose:
1007 Input:
1008 Output:
1009 Errors:
1010 ------------------------------------------------------------------------*/
MIME_get_attachment_count(void)1011 int MIME_get_attachment_count( void )
1012 {
1013 return glb.attachment_count;
1014 }
1015
1016
1017
1018
1019 /*------------------------------------------------------------------------
1020 Procedure: MIME_test_uniquename ID:1
1021 Purpose: Checks to see that the filename specified is unique. If it's not
1022 unique, it will modify the filename
1023 Input: char *path: Path in which to look for similar filenames
1024 char *fname: Current filename
1025 int method: Method of altering the filename (infix, postfix, prefix)
1026 Output:
1027 Errors:
1028 ------------------------------------------------------------------------*/
MIME_test_uniquename(char * path,char * fname,int method)1029 int MIME_test_uniquename( char *path, char *fname, int method )
1030 {
1031
1032 struct stat buf;
1033
1034 char newname[_MIME_STRLEN_MAX +1];
1035 char scr[_MIME_STRLEN_MAX +1]; /** Scratch var **/
1036 char *frontname, *extention;
1037
1038 int cleared = 0;
1039 int count = 1;
1040
1041 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Start (%s)",FL,fname);
1042
1043 frontname = extention = NULL; // shuts the compiler up
1044
1045 if (method == _MIME_RENAME_METHOD_INFIX)
1046 {
1047 PLD_strncpy(scr,fname, _MIMEH_STRLEN_MAX);
1048 frontname = scr;
1049 extention = strrchr(scr,'.');
1050
1051 if (extention)
1052 {
1053 *extention = '\0';
1054 extention++;
1055 }
1056 else
1057 {
1058 method = _MIME_RENAME_METHOD_POSTFIX;
1059 }
1060 }
1061
1062 snprintf(newname,_MIME_STRLEN_MAX,"%s/%s",path,fname);
1063
1064 while (!cleared)
1065 {
1066 if ((stat(newname, &buf) == -1))
1067 {
1068 cleared++;
1069 }
1070 else
1071 {
1072 if (method == _MIME_RENAME_METHOD_PREFIX)
1073 {
1074 snprintf(newname,_MIME_STRLEN_MAX,"%s/%d_%s",path,count,fname);
1075 }
1076 else
1077 if (method == _MIME_RENAME_METHOD_INFIX)
1078 {
1079 snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d.%s",path,frontname,count,extention);
1080 }
1081 else
1082 if (method == _MIME_RENAME_METHOD_POSTFIX)
1083 {
1084 snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d",path,fname,count);
1085 }
1086 count++;
1087 }
1088 }
1089
1090 if (count > 1)
1091 {
1092 frontname = strrchr(newname,'/');
1093 if (frontname) frontname++;
1094 else frontname = newname;
1095
1096 PLD_strncpy(fname, frontname, _MIMEH_FILENAMELEN_MAX); //FIXME - this assumes that the buffer space is at least MIME_STRLEN_MAX sized.
1097
1098 }
1099
1100 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Done (%s)",FL,fname);
1101 return 0;
1102 }
1103
1104
1105
1106 /*------------------------------------------------------------------------
1107 Procedure: MIME_is_file_mime ID:1
1108 Purpose: Determines if the file handed to it is a MIME type email file.
1109
1110 Input: file name to analyze
1111 Output: Returns 0 for NO, 1 for YES, -1 for "Things di
1112 Errors:
1113 ------------------------------------------------------------------------*/
MIME_is_file_RFC822(char * fname)1114 int MIME_is_file_RFC822( char *fname )
1115 {
1116 char conditions[16][16] = {
1117 "Received: ", "From: ", "Subject: ", "Date: ", "Content-", "content-", "from: ", "subject: ", "date: ", "boundary=", "Boundary=" };
1118 int result = 0;
1119 int hitcount = 0;
1120 int linecount = 100; // We should only need to read the first 10 lines of any file.
1121 char *line;
1122 FILE *f;
1123
1124
1125 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Testing %s for RFC822 headers",FL,fname);
1126
1127 f = fopen(fname,"r");
1128 if (!f)
1129 {
1130 if (glb.quiet == 0)
1131 {
1132 LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot open file '%s' for reading (%s)", FL, fname,strerror(errno));
1133 }
1134 return 0;
1135 }
1136
1137 line = malloc(sizeof(char) *1025);
1138 if (!line)
1139 {
1140 LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot allocate memory for read buffer", FL);
1141 return 0;
1142 }
1143
1144 while ((hitcount < 2)&&(fgets(line,1024,f))&&(linecount--))
1145 {
1146 /** test every line for possible headers, until we get a blank line **/
1147
1148 if ((glb.header_longsearch == 0)&&(*line == '\n' || *line == '\r')) break;
1149
1150 for (result = 0; result < 11; result++)
1151 {
1152 /** Test for every possible MIME header prefix, ie, From: Subject: etc **/
1153 if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_is_file_mime:DEBUG: Testing for '%s' in '%s'", FL, line, conditions[result]);
1154 if (strncasecmp(line,conditions[result],strlen(conditions[result]))==0)
1155 {
1156 /** If we get a match, then increment the hit counter **/
1157 hitcount++;
1158 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit on %s",FL,conditions[result]);
1159 }
1160 }
1161 }
1162
1163 fclose(f);
1164
1165 if (hitcount >= 2) result = 1;
1166 else result = 0;
1167
1168 if (line) free(line);
1169
1170 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit count = %d, result = %d",FL,hitcount,result);
1171 return result;
1172 }
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186 /*------------------------------------------------------------------------
1187 Procedure: MIME_getchar_start ID:1
1188 Purpose: This function is used on a once-off basis. It's purpose is to locate a
1189 non-whitespace character which (in the context of its use) indicates
1190 that the commencement of BASE64 encoding data has commenced.
1191 Input: FFGET f: file stream
1192 Output: First character of the BASE64 encoding.
1193 Errors:
1194 ------------------------------------------------------------------------*/
MIME_getchar_start(FFGET_FILE * f)1195 int MIME_getchar_start( FFGET_FILE *f )
1196 {
1197
1198 int c;
1199
1200 /* loop for eternity, as we're "returning out" */
1201 while (1)
1202 {
1203
1204 /* get a single char from file */
1205 c = FFGET_fgetc(f);
1206
1207 /* if that char is an EOF, or the char is something beyond
1208 * ASCII 32 (space) then return */
1209 if ((c == EOF) || (c > ' '))
1210 {
1211 return c;
1212 }
1213
1214 } /* while */
1215
1216 /* Although we shouldn't ever actually get here, we best put it in anyhow */
1217 return EOF;
1218 }
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229 /*------------------------------------------------------------------------
1230 Procedure: MIME_decode_TNEF ID:1
1231 Purpose: Decodes TNEF encoded attachments
1232 Input:
1233 Output:
1234 Errors:
1235 ------------------------------------------------------------------------*/
MIME_decode_TNEF(char * unpackdir,struct MIMEH_header_info * hinfo,int keep)1236 int MIME_decode_TNEF( char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
1237 {
1238 int result=0;
1239 char fullpath[1024];
1240
1241 snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename);
1242
1243 TNEF_set_path(unpackdir);
1244
1245 result = TNEF_main( fullpath );
1246
1247 if (result >= 0)
1248 {
1249 // result = remove( fullpath );
1250 if (result == -1)
1251 {
1252 if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_decode_TNEF: Removing %s failed (%s)",FL,fullpath,strerror(errno));
1253 }
1254 }
1255
1256 return result;
1257 }
1258
1259
1260 #ifdef RIPOLE
MIME_report_filename_decoded_RIPOLE(char * filename)1261 int MIME_report_filename_decoded_RIPOLE(char *filename)
1262 {
1263 LOGGER_log("Decoding filename=%s", filename);
1264
1265 return 0;
1266 }
1267
1268
1269 /*-----------------------------------------------------------------\
1270 Function Name : MIME_decode_OLE
1271 Returns Type : int
1272 ----Parameter List
1273 1. char *unpackdir,
1274 2. struct MIMEH_header_info *hinfo,
1275 3. int keep ,
1276 ------------------
1277 Exit Codes :
1278 Side Effects :
1279 --------------------------------------------------------------------
1280 Comments:
1281
1282 --------------------------------------------------------------------
1283 Changes:
1284
1285 \------------------------------------------------------------------*/
MIME_decode_OLE(char * unpackdir,struct MIMEH_header_info * hinfo,int keep)1286 int MIME_decode_OLE( char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
1287 {
1288 struct OLE_object ole;
1289 char fullpath[1024];
1290 int result;
1291
1292 snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename);
1293
1294
1295 OLE_init(&ole);
1296 OLE_set_quiet(&ole,glb.quiet);
1297 OLE_set_verbose(&ole,glb.verbosity);
1298 OLE_set_debug(&ole,glb.debug);
1299 OLE_set_save_unknown_streams(&ole,0);
1300 OLE_set_filename_report_fn(&ole, MIME_report_filename_decoded_RIPOLE );
1301
1302 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Starting OLE Decode",FL);
1303 result = OLE_decode_file(&ole, fullpath, unpackdir );
1304 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode done, cleaning up.",FL);
1305 OLE_decode_file_done(&ole);
1306
1307 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode returned with code = %d",FL,result);
1308
1309 return result;
1310 }
1311
1312 #endif
1313
1314 /*------------------------------------------------------------------------
1315 Procedure: MIME_decode_raw ID:1
1316 Purpose: Decodes a binary type attachment, ie, no encoding, just raw data.
1317 Input:
1318 Output:
1319 Errors:
1320 ------------------------------------------------------------------------*/
MIME_decode_raw(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo,int keep)1321 int MIME_decode_raw( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
1322 {
1323 int result = 0;
1324 char fullpath[1024];
1325 int bufsize=1024;
1326 char *buffer = malloc((bufsize +1)*sizeof(char));
1327 size_t readcount;
1328 int file_has_uuencode = 0;
1329 int decode_entire_file = 0;
1330 int fo;
1331
1332 /* Decoding / reading a binary attachment is a real interesting situation, as we
1333 * still use the fgets() call, but we do so repeatedly until it returns a line with a
1334 * \n\r and the boundary specifier in it.... all in all, I wouldn't rate this code
1335 * as being perfect, it's activated only if the user intentionally specifies it with
1336 * --binary flag
1337 */
1338
1339 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Start\n",FL);
1340
1341 snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename);
1342 fo = open(fullpath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
1343
1344 if (fo == -1)
1345 {
1346 LOGGER_log("%s:%d:MIME_decode_raw:ERROR: cannot open file %s for writing. (%s)\n\n",FL,fullpath,strerror(errno));
1347 return -1;
1348 }
1349
1350 while ((readcount=FFGET_raw(f, (unsigned char *) buffer,bufsize)) > 0)
1351 {
1352 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: BUFFER[%p]= '%s'\n",FL,buffer, buffer);
1353
1354 if ((!file_has_uuencode)&&(UUENCODE_is_uuencode_header( buffer )))
1355 {
1356 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: UUENCODED is YES (buffer=[%p]\n",FL,buffer);
1357
1358 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: File contains UUENCODED data(%s)\n",FL,buffer);
1359
1360 file_has_uuencode = 1;
1361 }
1362
1363 if (BS_cmp(buffer, readcount))
1364 {
1365 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Boundary located - breaking out.\n",FL);
1366
1367 break;
1368
1369 } else {
1370
1371 size_t bc;
1372 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: writing: %s\n",FL, buffer);
1373
1374 bc = write( fo, buffer, readcount);
1375 }
1376 }
1377
1378 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Completed reading RAW data\n",FL);
1379
1380 free(buffer);
1381 close(fo);
1382
1383 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Closed file and free'd buffer\n",FL);
1384
1385 // If there was UUEncoded portions [potentially] in the email, the
1386 // try to extract them using the MIME_decode_uu()
1387
1388 if (file_has_uuencode)
1389 {
1390 char full_decode_path[512];
1391
1392 snprintf(full_decode_path,sizeof(full_decode_path),"%s/%s",unpackdir,hinfo->filename);
1393 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding UUencoded data\n",FL);
1394 if ( hinfo->content_transfer_encoding == _CTRANS_ENCODING_UUENCODE ) decode_entire_file = 0;
1395
1396
1397 //result = UUENCODE_decode_uu(NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep );
1398 result = UUENCODE_decode_uu(NULL, unpackdir, full_decode_path, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep );
1399
1400 if (result == -1)
1401 {
1402 switch (uuencode_error) {
1403 case UUENCODE_STATUS_SHORT_FILE:
1404 case UUENCODE_STATUS_CANNOT_OPEN_FILE:
1405 case UUENCODE_STATUS_CANNOT_FIND_FILENAME:
1406 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error);
1407 result = 0;
1408 break;
1409
1410 case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY:
1411 LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL);
1412 return -1;
1413 break;
1414
1415 default:
1416 LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error);
1417 return -1;
1418 }
1419 }
1420
1421 if (result == UUENCODE_STATUS_SHORT_FILE) result = 0;
1422
1423 glb.attachment_count += result;
1424
1425 if (strlen(hinfo->uudec_name))
1426 {
1427 if (strcasecmp(hinfo->uudec_name,"winmail.dat")==0)
1428 {
1429 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding TNEF format\n",FL);
1430 snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name);
1431 MIME_decode_TNEF( unpackdir, hinfo, keep);
1432 }
1433 else LOGGER_log("%s:%d:MIME_decode_raw:WARNING: hinfo has been clobbered.\n",FL);
1434 }
1435 }
1436
1437 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: End[result = %d]\n",FL,result);
1438
1439 return result;
1440 }
1441
1442
1443
1444 /*------------------------------------------------------------------------
1445 Procedure: MIME_decode_text ID:1
1446 Purpose: Decodes an input stream into a text file.
1447 Input: unpackdir : directory where to place new text file
1448 hinfo : struct containing information from the last parsed headers
1449 keep : if set, retain the file
1450 Output:
1451 Errors:
1452 ------------------------------------------------------------------------*/
MIME_decode_text(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo,int keep)1453 int MIME_decode_text( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
1454 {
1455
1456 FILE *of; // output file
1457 int linecount = 0; // The number of lines
1458 int file_has_uuencode = 0; // Flag to indicate this text has UUENCODE in it
1459 char fullfilename[1024]=""; // Filename of the output file
1460 char line[1024]; // The input lines from the file we're decoding
1461 char *get_result = &line[0];
1462 int lastlinewasboundary = 0;
1463 int result = 0;
1464 int decodesize=0;
1465
1466 snprintf(fullfilename,sizeof(fullfilename),"%s/%s",unpackdir,hinfo->filename);
1467
1468 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TEXT [encoding=%d] to %s\n",FL, hinfo->content_transfer_encoding, fullfilename);
1469
1470 if (!f)
1471 {
1472 /** If we cannot open the file for reading, leave an error and return -1 **/
1473 LOGGER_log("%s:%d:MIME_decode_text:ERROR: print-quotable input stream broken.",FL);
1474 return -1;
1475 }
1476
1477 if (f)
1478 {
1479 /** If we were able to open the input file, try opening the output file and process the data **/
1480 of = fopen(fullfilename,"w");
1481 if (!of)
1482 {
1483 /** If we were unable to open the output file, report the error and return -1 **/
1484 LOGGER_log("%s:%d:MIME_decode_text:ERROR: cannot open %s for writing",FL,fullfilename);
1485 return -1;
1486 }
1487
1488 while ((get_result = FFGET_fgets(line,1023,f))&&(of))
1489 {
1490 int line_len = strlen(line);
1491 linecount++;
1492 // if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line=%s",FL,line);
1493 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line[len=%d]=%s",FL,line_len,line);
1494 //20041217-1529:PLD:
1495 if (line[0] == '-')
1496 {
1497 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_DNORMAL:DEBUG: Testing boundary",FL);
1498 if ((BS_count() > 0)&&(BS_cmp(line,line_len)))
1499 {
1500 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_DNORMAL:DEBUG: Hit a boundary on the line",FL);
1501 lastlinewasboundary = 1;
1502 result = 0;
1503 break;
1504 }
1505 }
1506
1507
1508 if (lastlinewasboundary == 0)
1509 {
1510 if (hinfo->content_transfer_encoding == _CTRANS_ENCODING_QP)
1511 {
1512 size_t bc;
1513
1514 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_DNORMAL:DEBUG: Hit a boundary on the line",FL);
1515 decodesize = MDECODE_decode_qp_text(line);
1516 bc = fwrite(line, 1, decodesize, of);
1517
1518 } else {
1519 fprintf(of,"%s",line);
1520 }
1521
1522
1523 if ((!file_has_uuencode)&&( UUENCODE_is_uuencode_header( line )))
1524 {
1525 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: UUENCODED data located in file.\n",FL);
1526 file_has_uuencode = 1;
1527 }
1528 }
1529 // linecount++;
1530
1531 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_DNORMAL:DEBUG: End processing line.",FL);
1532
1533 } // while
1534
1535 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done writing output file '%s'...now attempting to close.",FL, fullfilename);
1536
1537
1538
1539 // if the file is still safely open
1540 if (of)
1541 {
1542 fclose(of);
1543 } // if file still safely open
1544
1545 if (linecount == 0)
1546 {
1547 result = MIME_STATUS_ZERO_FILE;
1548 return result;
1549 }
1550
1551 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Closed.",FL);
1552
1553 } // if main input file stream was open
1554
1555
1556 // If our input from the file was invalid due to EOF or other
1557 // then we return a -1 code to indicate that the end has
1558 // occured.
1559 //
1560 if (!get_result) {
1561 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: FFGET module ran out of file data while attempting to decode",FL);
1562 // result = -1;
1563 result = MIME_ERROR_FFGET_EMPTY; // 20040305-1323:PLD
1564 }
1565
1566 // If there was UUEncoded portions [potentially] in the email, the
1567 // try to extract them using the MIME_decode_uu()
1568 //
1569 if (file_has_uuencode)
1570 {
1571 char ffname[256];
1572
1573 snprintf(ffname,256,"%s/%s", unpackdir, hinfo->filename);
1574
1575 // PLD-20040627-1212
1576 // Make sure uudec_name is blank too
1577 //
1578 hinfo->uudec_name[0] = '\0';
1579
1580
1581 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding UUencoded data in file '%s'\n",FL,hinfo->filename);
1582
1583 //result = UUENCODE_decode_uu( NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep );
1584 // Attempt to decode the UUENCODED data in the file,
1585 // NOTE - hinfo->uudec_name is a blank buffer which will be filled by the UUENCODE_decode_uu
1586 // function once it has located a filename in the UUENCODED data. A bit of a problem here
1587 // is that it can only hold ONE filename!
1588 //
1589 // NOTE - this function returns the NUMBER of attachments it decoded in the return value! Don't
1590 // propergate this value unintentionally to parent functions (ie, if you were thinking it was
1591 // an error-status return value
1592
1593 result = UUENCODE_decode_uu( NULL, unpackdir, ffname, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep );
1594 if (result == -1)
1595 {
1596 switch (uuencode_error) {
1597 case UUENCODE_STATUS_SHORT_FILE:
1598 case UUENCODE_STATUS_CANNOT_OPEN_FILE:
1599 case UUENCODE_STATUS_CANNOT_FIND_FILENAME:
1600 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error);
1601 result = 0;
1602 break;
1603
1604 case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY:
1605 LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL);
1606 return -1;
1607 break;
1608
1609 default:
1610 LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error);
1611 return -1;
1612 }
1613 }
1614
1615 if ( result > 0 ) { glb.attachment_count += result; result = 0; }
1616
1617 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: hinfo = %p\n",FL,hinfo);
1618 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done. [ UUName = '%s' ]\n",FL,hinfo->uudec_name);
1619
1620 if (strncasecmp(hinfo->uudec_name,"winmail.dat",11)==0)
1621 {
1622 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TNEF format\n",FL);
1623 snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name);
1624 MIME_decode_TNEF( unpackdir, hinfo, keep );
1625 }
1626
1627 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Completed decoding UUencoded data.\n",FL);
1628 }
1629
1630 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: result=%d ----------------Done\n",FL,result);
1631
1632 return result;
1633 }
1634
1635
1636
1637
1638 /*------------------------------------------------------------------------
1639 Procedure: MIME_decode_64 ID:1
1640 Purpose: This routine is very very very important, it's the key to ensuring
1641 we get our attachments out of the email file without trauma!
1642 NOTE - this has been -slightly altered- in order to make provision
1643 of the fact that the attachment may end BEFORE the EOF is received
1644 as is the case with multiple attachments in email. Hence, we
1645 now have to detect the start character of the "boundary" marker
1646 I may consider testing the 1st n' chars of the boundary marker
1647 just incase it's not always a hypen '-'.
1648 Input: FGET_FILE *f: stream we're reading from
1649 char *unpackdir: directory we have to write the file to
1650 struct MIMEH_header_info *hinfo: Auxillairy information such as the destination filename
1651 Output:
1652 Errors:
1653 ------------------------------------------------------------------------*/
MIME_decode_64(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo)1654 int MIME_decode_64( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo )
1655 {
1656
1657 int i;
1658 int cr_total = 0;
1659 int cr_count = 0; /* the number of consecutive \n's we've read in, used to detect End of B64 enc */
1660 int ignore_crcount = 0; /* If we have a boundary in our emails, then ignore the CR counts */
1661 int stopcount = 0; /* How many stop (=) characters we've read in */
1662 int eom_reached = 0; /* flag to say that we've reached the End-Of-MIME encoding. */
1663 int status = 0; /* Overall status of decoding operation */
1664 int c; /* a single char as retrieved using MIME_get_char() */
1665 int char_count = 0; /* How many chars have been received */
1666 int boundary_crash = 0; /* if we crash into a boundary, set this */
1667 long int bytecount=0; /* The total file decoded size */
1668 char output[3]; /* The 4->3 byte output array */
1669 char input[4]; /* The 4->3 byte input array */
1670 char fullMIME_filename[_MIME_STRLEN_MAX]=""; /* Full Filename of output file */
1671
1672 // Write Buffer routine
1673
1674 unsigned char *writebuffer;
1675 unsigned char *wbpos;
1676 int wbcount = 0;
1677 int loop;
1678
1679 int of; /* output file pointer */
1680
1681 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: attempting to decode '%s'", FL, hinfo->filename);
1682
1683 /* generate the MIME_filename, and open it up... */
1684 if (glb.unique_names) MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method );
1685 snprintf(fullMIME_filename,_MIME_STRLEN_MAX,"%s/%s",unpackdir,hinfo->filename);
1686
1687
1688 //of = fopen(fullMIME_filename,"wb");
1689 of = open(fullMIME_filename, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
1690
1691
1692 /* if we were unable to open the output file, then we better log an error and drop out */
1693 if (of < 0)
1694 {
1695 LOGGER_log("%s:%d:MIME_decode_64:ERROR: Cannot open output file %s for BASE64 decoding. (%s)",FL,fullMIME_filename, strerror(errno));
1696 // exit(_EXITERR_BASE64_OUTPUT_NOT_OPEN);
1697 return -1;
1698 }
1699
1700
1701 // Allocate the write buffer. By using the write buffer we gain an additional 10% in performance
1702 // due to the lack of function call (fwrite) overheads
1703
1704 writebuffer = malloc( _MIME_WRITE_BUFFER_SIZE *sizeof(unsigned char));
1705 if (!writebuffer)
1706 {
1707 LOGGER_log("%s:%d:MIME_decode_64:ERROR: cannot allocate %dbytes of memory for the write buffer",FL, _MIME_WRITE_BUFFER_SIZE);
1708 return -1;
1709 }
1710 else {
1711 wbpos = writebuffer;
1712 wbcount = 0;
1713 }
1714
1715 /* Set our ignore_crcount flag */
1716 if (BS_count() > 0)
1717 {
1718 //LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Ignore CR set to 1",FL);
1719 ignore_crcount = 1;
1720 }
1721
1722
1723 /* collect prefixing trash (if any, such as spaces, CR's etc)*/
1724 // c = MIME_getchar_start(f);
1725
1726 /* and push the good char back */
1727 // FFGET_ungetc(f,c);
1728
1729 c = '\0';
1730
1731
1732 /* do an endless loop, as we're -breaking- out later */
1733 while (1)
1734 {
1735
1736 int lastchar_was_linebreak=0;
1737
1738 /* Initialise the decode buffer */
1739 input[0] = input[1] = input[2] = input[3] = 0; // was '0' - Stepan Kasal patch
1740
1741 /* snatch 4 characters from the input */
1742 for (i = 0; i < 4; i++)
1743 {
1744
1745 // Get Next char from the file input
1746 //
1747 // A lot of CPU is wasted here due to function call overheads, unfortunately
1748 // I cannot yet work out how to make this call (FFGET) operate effectively
1749 // without including a couple of dozen lines in this section.
1750 //
1751 // Times like this C++'s "inline" statement would be nice.
1752 //
1753
1754
1755 //----------INLINE Version of FFGET_getchar()
1756 //
1757
1758 do {
1759
1760 if ((c == '\n')||(c == '\r')) lastchar_was_linebreak = 1; else lastchar_was_linebreak = 0;
1761
1762 if (f->ungetcset)
1763 {
1764 f->ungetcset = 0;
1765 c = f->c;
1766 }
1767 else
1768 {
1769
1770 if ((!f->startpoint)||(f->startpoint > f->endpoint))
1771 {
1772 FFGET_getnewblock(f);
1773 }
1774
1775 if (f->startpoint <= f->endpoint)
1776 {
1777 c = *f->startpoint;
1778 f->startpoint++;
1779 }
1780 else
1781 {
1782 c = EOF;
1783 }
1784 }
1785
1786 }
1787 while ( (c != EOF) && ( c < ' ' ) && ( c != '\n' ) && (c != '-') && (c != '\r') );
1788
1789 //
1790 //-------END OF INLINE---------------------------------------------
1791
1792 if ((ignore_crcount == 1)&&(c == '-'))
1793 //&&(lastchar_was_linebreak == 1))
1794 {
1795 int hit = 0;
1796 char *p;
1797
1798 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: leader '-' found at %50s",FL,(f->startpoint -1));
1799 p = strchr((f->startpoint -1), '\n');
1800 if (p == NULL) {
1801 /* The boundary test was failing because sometimes the full line
1802 * wasn't available to test against in BM_cmp(), so if we can't
1803 * see a \n (or \r even), then we should load up some more data
1804 */
1805
1806 char scratch[1024];
1807 FFGET_fgets(scratch,sizeof(scratch), f);
1808
1809 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Scratch = '%s'", FL, scratch);
1810 hit = BS_cmp(scratch,strlen(scratch) +1);
1811 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Boundary hit = %d", FL, hit);
1812
1813 } else {
1814
1815 *p = '\0';
1816 hit = BS_cmp((f->startpoint -1),strlen(f->startpoint) +1);
1817 *p = '\n';
1818 }
1819
1820 if (hit > 0) {
1821 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Boundary detected and breaking out ",FL);
1822 // FFGET_fgets(scratch,sizeof(scratch),f);
1823 // eom_reached = 1;
1824 boundary_crash = 1;
1825 break;
1826 } else {
1827 /** 20041105-22H56:PLD: Applied Stepan Kasal patch **/
1828 i--;
1829 continue;
1830 }
1831 }
1832
1833 // If we get a CR then we need to check a few things, namely to see
1834 // if the BASE64 stream has terminated ( indicated by two consecutive
1835 // \n's
1836 //
1837 // This should not be affected by \r's because the \r's will just pass
1838 // through and be absorbed by the detection of possible 'invalid'
1839 // characters
1840
1841 if ((ignore_crcount == 0)&&(c == '\n'))
1842 {
1843 cr_count++;
1844 cr_total++;
1845
1846 // Because there are some mail-agents out there which are REALLY naughty
1847 // and do things like sticking random blank lines into B64 data streams
1848 // (why o why!?) we need to have a settable option here to decide if
1849 // we want to test for double-CR breaking or just ignore it
1850 //
1851 // To avoid this, we relax the double-CR extention, and say "Okay, we'll let
1852 // you have a single blank line - but any more than that and we'll slap you
1853 // hand". Honestly, I do not like this solution, but it seems to be the only
1854 // way to preserve decoding accurancy without resorting to having to use
1855 // a command line switch to dictate which method to use ( NO-CR's or ignore
1856 // CR's ).
1857
1858
1859 if (cr_count > 2)
1860 {
1861 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: EOF Reached due to two consecutive CR's on line %d\n",FL,cr_total);
1862 eom_reached++;
1863 break;
1864 }
1865 else
1866 {
1867 char_count = 0;
1868 i--;
1869 continue;
1870 } // else if it wasn't our 3rd CR
1871
1872 } else {
1873 cr_count=0;
1874 }
1875
1876
1877
1878 /* if we get an EOF char, then we know something went wrong */
1879 if ( c == EOF )
1880 {
1881 size_t bc;
1882 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: input stream broken for base64 decoding for file %s. %ld bytes of data in buffer to be written out\n",FL,hinfo->filename,wbcount);
1883
1884 status = MIME_ERROR_B64_INPUT_STREAM_EOF;
1885 //fwrite(writebuffer, 1, wbcount, of);
1886 bc = write( of, writebuffer, wbcount);
1887 close(of);
1888 if (writebuffer) free(writebuffer);
1889 return status;
1890 break;
1891 } /* if c was the EOF */
1892 else if (c == '=')
1893 {
1894 // Once we've found a stop char, we can actually just "pad" in the rest
1895 // of the stop chars because we know we're at the end. Some MTA's dont
1896 // put in enough stopchars... at least it seems X-MIMEOLE: Produced By Microsoft MimeOLE V5.50.4133.2400
1897 // doesnt.
1898
1899 if (i == 2)
1900 {
1901 input[2] = input[3] = (char)b64[c];
1902 }
1903 else if (i == 3)
1904 {
1905 input[3] = (char)b64[c];
1906 }
1907
1908 // NOTE------
1909 // Previously we relied on the fact that if we detected a stop char, that FFGET()
1910 // would automatically absorb the data till EOL. This is no longer the case as we
1911 // are now only retrieving data byte at a time.
1912 // So, now we -absorb- till the end of the line using FFGET_fgets()
1913
1914 stopcount = 4 -i;
1915 FFGET_fgets(scratch,sizeof(scratch),f);
1916 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Stop char detected pos=%d...StopCount = %d\n",FL,i,stopcount);
1917 i = 4;
1918
1919
1920
1921 break; // out of FOR.
1922
1923 }
1924 /*
1925 else if ((char_count == 0)&&(c == '-' )) {
1926 if (FFGET_fgetc(f) == '-')
1927 {
1928 boundary_crash++;
1929 eom_reached++;
1930 break;
1931 }
1932 }
1933 */
1934
1935
1936 /* test for and discard invalid chars */
1937 if (b64[c] == 0x80)
1938 {
1939 i--;
1940 continue;
1941 }
1942
1943
1944 // Finally, if we get this far without the character having been picked
1945 // out as some special meaning character, we decode it and place the
1946 // resultant byte into the input[] array.
1947
1948 input[i] = (char)b64[c];
1949
1950 /* assuming we've gotten this far, then we increment the char_count */
1951 char_count++;
1952
1953 } // FOR
1954
1955
1956 // now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data
1957
1958 output[0] = (input[0] << 2) | (input[1] >> 4);
1959 output[1] = (input[1] << 4) | (input[2] >> 2);
1960 output[2] = (input[2] << 6) | input[3];
1961
1962 // determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it
1963
1964 if (i == 4)
1965 {
1966 // If our buffer is full beyond the 'write out limit', then we write the buffered
1967 // data to the file - We use this method in order to save calling fwrite() too
1968 // many times, thus avoiding function call overheads and [ possibly ] disk write
1969 // interrupt costs.
1970
1971 if ( wbcount > _MIME_WRITE_BUFFER_LIMIT )
1972 {
1973 size_t bc;
1974 bc = write ( of, writebuffer, wbcount );
1975 // fwrite(writebuffer, 1, wbcount, of);
1976 wbpos = writebuffer;
1977 wbcount = 0;
1978 }
1979
1980 // Copy our converted bytes to the write buffer
1981
1982 for (loop = 0; loop < (3 -stopcount); loop++)
1983 {
1984 *wbpos = output[loop];
1985 wbpos++;
1986 wbcount++;
1987 }
1988
1989 // tally up our total byte conversion count
1990
1991 bytecount+=(3 -stopcount);
1992
1993 }
1994 else if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: could not attain 4 bytes input\n",FL);
1995
1996
1997 // if we wrote less than 3 chars, it means we were at the end of the encoded file thus we exit
1998 if ((eom_reached)||(stopcount > 0)||(boundary_crash)||(i!=4))
1999 {
2000
2001 // Write out the remaining contents of our write buffer - If we don't do this
2002 // we'll end up with truncated files.
2003
2004 if (wbcount > 0)
2005 {
2006 size_t bc;
2007 //fwrite(writebuffer, 1, wbcount, of);
2008 bc = write( of, writebuffer, wbcount);
2009 }
2010
2011 /* close the output file, we're done writing to it */
2012 close(of);
2013
2014 /* if we didn't really write anything, then trash the file */
2015 if (bytecount == 0)
2016 {
2017 // unlink(fullMIME_filename);
2018 status = MIME_BASE64_STATUS_ZERO_FILE;
2019 }
2020
2021 if (boundary_crash)
2022 {
2023 // Absorb to end of line
2024 //
2025 status = MIME_BASE64_STATUS_HIT_BOUNDARY; // was _BOUNDARY_CRASH
2026 }
2027
2028 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: File size = %ld bytes, Exit Status = %d, Boundary Crash = %d\n",FL, bytecount, status, boundary_crash);
2029
2030 if (writebuffer) free(writebuffer);
2031
2032 return status;
2033
2034 } // if End-of-MIME or Stopchars appeared
2035
2036 } // while
2037
2038 if (writebuffer) free(writebuffer);
2039
2040 return status;
2041
2042 }
2043
2044
2045 /*-----------------------------------------------------------------\
2046 Function Name : MIME_decode_64_cleanup
2047 Returns Type : int
2048 ----Parameter List
2049 1. FFGET_FILE *f,
2050 2. char *unpackdir,
2051 3. struct MIMEH_header_info *hinfo,
2052 ------------------
2053 Exit Codes :
2054 Side Effects :
2055 --------------------------------------------------------------------
2056 Comments:
2057
2058 --------------------------------------------------------------------
2059 Changes:
2060
2061 \------------------------------------------------------------------*/
MIME_decode_64_cleanup(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo)2062 int MIME_decode_64_cleanup( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo)
2063 {
2064 int result = 0;
2065 char buffer[128];
2066
2067 while (FFGET_fgets(buffer, sizeof(buffer), f))
2068 {
2069 if (FFGET_feof(f) != 0) break;
2070 if (BS_cmp(buffer,strlen(buffer)) > 0) break;
2071 }
2072
2073 return result;
2074
2075 }
2076
2077
2078
2079 /*------------------------------------------------------------------------
2080 Procedure: MIME_doubleCR_decode ID:1
2081 Purpose: Decodes a text sequence as detected in the processing of the MIME headers.
2082 This is a specialised call, not really a normal part of MIME decoding, but is
2083 required in order to deal with decyphering MS Outlook visable defects.
2084 Input: char *filename: Name of the encoded file we need to decode
2085 char *unpackdir: Directory we need to unpack the file to
2086 struct MIMEH_header_info *hinfo: Header information already gleaned from the headers
2087 int current_recursion_level: How many nest levels we are deep
2088 Output:
2089 Errors:
2090 ------------------------------------------------------------------------*/
MIME_doubleCR_decode(char * filename,char * unpackdir,struct MIMEH_header_info * hinfo,int current_recursion_level)2091 int MIME_doubleCR_decode( char *filename, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level )
2092 {
2093 int result = 0;
2094 struct MIMEH_header_info h;
2095 char *p;
2096 // PLD:260303-1317
2097 // if ((p=strrchr(filename,'/'))) p++;
2098 // else p = filename;
2099
2100 p = filename;
2101
2102 // * Initialise the header fields
2103 h.uudec_name[0] = '\0';
2104
2105 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: filename=%s, path=%s, recursion=%d", FL, filename, unpackdir, current_recursion_level );
2106
2107 memcpy(&h, hinfo, sizeof(h));
2108
2109 // Works for ripMIME snprintf(h.filename, sizeof(h.filename), "%s/%s", unpackdir, p);
2110
2111 snprintf(h.filename, sizeof(h.filename), "%s", p); /// Works for Xamime
2112
2113 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: header.filename = %s", FL, h.filename );
2114
2115 if (MIME_is_file_RFC822(filename))
2116 {
2117 if (MIME_VERBOSE) LOGGER_log("Attempting to decode Double-CR delimeted MIME attachment '%s'\n",filename);
2118 result = MIME_unpack( unpackdir, filename, current_recursion_level ); // 20040305-1303:PLD - Capture the result of the unpack and propagate up
2119 }
2120 else if (UUENCODE_is_file_uuencoded(h.filename))
2121 {
2122 if (MIME_VERBOSE) LOGGER_log("Attempting to decode UUENCODED attachment from Double-CR delimeted attachment '%s'\n",filename);
2123 UUENCODE_set_doubleCR_mode(1);
2124 result = UUENCODE_decode_uu(NULL, unpackdir, filename, h.uudec_name, _MIMEH_FILENAMELEN_MAX , 1, 1 );
2125 UUENCODE_set_doubleCR_mode(0);
2126 glb.attachment_count += result;
2127 result = 0;
2128 }
2129
2130 return result;
2131 }
2132
2133
2134 /*------------------------------------------------------------------------
2135 Procedure: MIME_read ID:1
2136 Purpose: Reads data from STDIN and saves the mailpack to the filename
2137 specified
2138 Input: char *mpname: full pathname of the file to save the data from STDIN
2139 to
2140 Output:
2141 Errors:
2142 28-Feb-2003
2143 This function has been modified to use feof() and fread/fwrite
2144 calls in order to ensure that binary data in the input does not
2145 cause the reading to prematurely terminate [ as it used to
2146 prior to the modification ]
2147 ------------------------------------------------------------------------*/
MIME_read_raw(char * src_mpname,char * dest_mpname,size_t rw_buffer_size)2148 size_t MIME_read_raw( char *src_mpname, char *dest_mpname, size_t rw_buffer_size )
2149 {
2150 size_t readcount, writecount;
2151 size_t fsize=-1;
2152 char *rw_buffer;
2153 int fin;
2154 int fout;
2155
2156 rw_buffer = NULL;
2157
2158 if (*src_mpname == '\0') {
2159 fin = STDIN_FILENO;
2160 } else {
2161 fin = open(src_mpname, O_RDONLY);
2162 if (fin == -1) {
2163 LOGGER_log("%s:%d:MIME_read_raw:ERROR: Cannot open '%s' for reading (%s)", FL, src_mpname, strerror(errno));
2164 return -1;
2165 }
2166 }
2167
2168 rw_buffer = malloc( (rw_buffer_size +1) *sizeof(char) );
2169 if ( !rw_buffer )
2170 {
2171 LOGGER_log("%s:%d:MIME_read:ERROR: could not allocate %d of memory for file read buffer\n",FL, rw_buffer_size );
2172 return -1;
2173 }
2174
2175 /* open up our input file */
2176 fout = open(dest_mpname,O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
2177 if (fout == -1) {
2178 LOGGER_log("%s:%d:MIME_read_raw:ERROR: Cannot open '%s' for writing. (%s)",FL, dest_mpname, strerror(errno));
2179 return -1;
2180 }
2181
2182 fsize=0;
2183
2184 /* while there is more data, consume it */
2185 do {
2186 readcount = read( fin, rw_buffer, rw_buffer_size );
2187 if (readcount > 0) {
2188 writecount = write( fout, rw_buffer, readcount );
2189 if (writecount == -1) {
2190 LOGGER_log("%s:%d:MIME_read_raw:ERROR: While attempting to write data to '%s' (%s)", FL, dest_mpname, strerror(errno));
2191 return -1;
2192 }
2193
2194 if ( readcount != writecount )
2195 {
2196 LOGGER_log("%s:%d:MIME_read_raw:ERROR: Attempted to write %d bytes, but only managed %d to file '%s'",FL, readcount, writecount, dest_mpname );
2197 }
2198
2199 fsize += writecount;
2200 }
2201 } while (readcount > 0);
2202
2203 if ((*src_mpname != '\0')&&(readcount >= 0)) {
2204 close(fin);
2205 }
2206
2207 if (readcount == -1) {
2208 LOGGER_log("%s:%d:MIME_read_raw:ERROR: read() '%s'",FL, strerror(errno));
2209 return -1;
2210 }
2211
2212 close(fout);
2213
2214 if ( rw_buffer != NULL ) free( rw_buffer );
2215
2216 return (size_t)(fsize);
2217 }
2218
2219
2220
2221
2222
2223
2224 /*------------------------------------------------------------------------
2225 Procedure: MIME_read ID:1
2226 Purpose: Reads data from STDIN and saves the mailpack to the filename
2227 specified
2228 Input: char *mpname: full pathname of the file to save the data from STDIN
2229 to
2230 Output:
2231 Errors:
2232 28-Feb-2003
2233 This function has been modified to use feof() and fread/fwrite
2234 calls in order to ensure that binary data in the input does not
2235 cause the reading to prematurely terminate [ as it used to
2236 prior to the modification ]
2237 ------------------------------------------------------------------------*/
MIME_read(char * mpname)2238 int MIME_read( char *mpname )
2239 {
2240 long int fsize=-1;
2241 char *buffer;
2242 size_t readcount, writecount;
2243 FILE *fout;
2244
2245
2246 buffer = malloc( MIME_MIME_READ_BUFFER_SIZE *sizeof(char) );
2247 if ( !buffer )
2248 {
2249 LOGGER_log("%s:%d:MIME_read:ERROR: could not allocate 4K of memory for file read buffer\n",FL );
2250 return -1;
2251 }
2252
2253 /* open up our input file */
2254 fout = fopen(mpname,"w");
2255
2256
2257
2258 /* check that out file opened up okay */
2259 if (!fout)
2260 {
2261 LOGGER_log("%s:%d:MIME_read:ERROR: Cannot open file %s for writing... check permissions perhaps?",FL,mpname);
2262 //exit(_EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT);
2263 return -1;
2264 }
2265
2266 /* assuming our file actually opened up */
2267 if (fout)
2268 {
2269
2270 fsize=0;
2271
2272 /* while there is more data, consume it */
2273 while ( !feof(stdin) )
2274 {
2275 readcount = fread( buffer, 1, ((MIME_MIME_READ_BUFFER_SIZE -1) *sizeof(char)), stdin );
2276 if ( readcount > 0 )
2277 {
2278 writecount = fwrite( buffer, 1, readcount, fout );
2279 fsize += writecount;
2280
2281 if ( readcount != writecount )
2282 {
2283 LOGGER_log("%s:%d:MIME_read:ERROR: Attempted to write %d bytes, but only managed %d to file '%s'",FL, readcount, writecount, mpname );
2284 }
2285
2286 } else {
2287 break;
2288 }
2289 }
2290
2291 /* clean up our buffers and close */
2292 fflush(fout);
2293 fclose(fout);
2294
2295 if ( feof(stdin) ) clearerr(stdin);
2296
2297 } /* end if fout was received okay */
2298
2299 if ( buffer ) free( buffer );
2300 /* return our byte count in KB */
2301 return (int)(fsize /1024);
2302 }
2303
2304
2305
2306
2307 /*------------------------------------------------------------------------
2308 Procedure: MIME_init ID:1
2309 Purpose: Initialise various required parameters to ensure a clean starting of
2310 MIME decoding.
2311 Input:
2312 Output:
2313 Errors:
2314 ------------------------------------------------------------------------*/
MIME_init(void)2315 int MIME_init( void )
2316 {
2317
2318 BS_init(); // Boundary-stack initialisations
2319 MIMEH_init(); // Initialise MIME header routines.
2320 UUENCODE_init(); // uuen:coding decoding initialisations
2321 FNFILTER_init(); // Filename filtering
2322 MDECODE_init(); // ISO filename decoding initialisation
2323 TNEF_init(); // TNEF decoder
2324
2325
2326 glb.header_defect_count = 0;
2327 glb.filecount = 0;
2328 glb.attachment_count = 0;
2329 glb.current_line = 0;
2330 glb.verbosity = 0;
2331 glb.verbose_defects = 0;
2332 glb.debug = 0;
2333 glb.quiet = 0;
2334 glb.syslogging = 0;
2335 glb.stderrlogging = 1;
2336 glb.unique_names = 0;
2337 glb.save_headers = 0;
2338 glb.no_nameless = 0;
2339 glb.mailbox_format = 0;
2340 glb.name_by_type = 0;
2341 glb.rename_method = _MIME_RENAME_METHOD_INFIX;
2342
2343 glb.header_longsearch = 0;
2344 glb.max_recursion_level = _RECURSION_LEVEL_DEFAULT;
2345
2346 glb.decode_qp = 1;
2347 glb.decode_b64 = 1;
2348 glb.decode_tnef = 1;
2349 glb.decode_ole = 1;
2350 glb.decode_uu = 1;
2351 glb.decode_mht = 1;
2352
2353 glb.multiple_filenames = 1;
2354
2355 glb.blankzone_save_option = MIME_BLANKZONE_SAVE_TEXTFILE;
2356 glb.blankzone_saved = 0;
2357
2358 snprintf( glb.headersname, sizeof(glb.headersname), "_headers_" );
2359 snprintf( glb.blankfileprefix, sizeof( glb.blankfileprefix ), "textfile" );
2360 glb.blankfileprefix_expliticly_set = 0;
2361
2362 glb.subject[0]='\0';
2363
2364 return 0;
2365 }
2366
2367
2368
2369 /*-----------------------------------------------------------------\
2370 Function Name : MIME_generate_multiple_hardlink_filenames
2371 Returns Type : int
2372 ----Parameter List
2373 1. struct MIMEH_header_info *hinfo,
2374 ------------------
2375 Exit Codes :
2376 Side Effects :
2377 --------------------------------------------------------------------
2378 Comments:
2379 If there is more than one filename/name for a given attachment
2380 due to an exploit attempt,then we need to generate the required
2381 hardlinks to replicate this in our output.
2382
2383 --------------------------------------------------------------------
2384 Changes:
2385
2386 \------------------------------------------------------------------*/
MIME_generate_multiple_hardlink_filenames(struct MIMEH_header_info * hinfo,char * unpackdir)2387 int MIME_generate_multiple_hardlink_filenames(struct MIMEH_header_info *hinfo, char *unpackdir)
2388 {
2389 char *name;
2390 char oldname[1024];
2391
2392 if (glb.multiple_filenames == 0) return 0;
2393
2394 //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Generating hardlinks for %s",FL, hinfo->filename);
2395 snprintf(oldname,sizeof(oldname),"%s/%s",unpackdir, hinfo->filename);
2396
2397 if (SS_count(&(hinfo->ss_names)) > 1){
2398 do {
2399
2400 name = SS_pop(&(hinfo->ss_names));
2401 if (name != NULL)
2402 {
2403 char *np;
2404 char newname[1024];
2405 int rv;
2406
2407 /** Strip off any leading path **/
2408 np = strrchr(name, '/');
2409 if (np) np++; else np = name;
2410
2411 snprintf(newname,sizeof(newname),"%s/%s",unpackdir, np);
2412 //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname);
2413 rv = link(oldname, newname);
2414 if (rv == -1)
2415 {
2416 if (errno != EEXIST)
2417 {
2418 LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno));
2419 }
2420
2421 } else {
2422 if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE))
2423 {
2424 glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL));
2425 }
2426 }
2427 }
2428
2429 } while(name != NULL);
2430 }
2431
2432 if (SS_count(&(hinfo->ss_filenames)) > 1) {
2433 do {
2434
2435 name = SS_pop(&(hinfo->ss_filenames));
2436 if (name != NULL)
2437 {
2438 char newname[1024];
2439 int rv;
2440
2441 snprintf(newname,sizeof(newname),"%s/%s",unpackdir, name);
2442 //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname);
2443 rv = link(oldname, newname);
2444 if (rv == -1)
2445 {
2446 if (errno != EEXIST)
2447 {
2448 LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno));
2449 }
2450
2451 } else {
2452 if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE))
2453 {
2454 glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL));
2455 }
2456 }
2457 }
2458
2459 } while(name != NULL);
2460 }
2461
2462 return 0;
2463
2464 }
2465
2466 /*------------------------------------------------------------------------
2467 Procedure: MIME_decode_encoding ID:1
2468 Purpose: Based on the contents of hinfo, this function will call the
2469 required function needed to decode the contents of the file
2470 which is contained within the MIME structure
2471 Input:
2472 Output:
2473 Errors:
2474 ------------------------------------------------------------------------*/
MIME_decode_encoding(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo,struct SS_object * ss)2475 int MIME_decode_encoding( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, struct SS_object *ss )
2476 {
2477 int keep = 1;
2478 int result = -1;
2479
2480 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Start:DEBUG: (%s)\n",FL, hinfo->filename);
2481
2482 // If we have a valid filename, then put it through the process of
2483 // cleaning and filtering
2484
2485 if (isprint((int)hinfo->filename[0]))
2486 {
2487 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is valid, cleaning\n",FL);
2488 FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX); /* check out thefilename for ISO filenames */
2489 }
2490
2491 // If the filename is NOT valid [doesn't have a printable first char]
2492 // then we must create a new file name for it.
2493 //
2494 if (hinfo->filename[0] == '\0')
2495 {
2496 if (glb.name_by_type == 1)
2497 {
2498 // If we're to name our nameless files based on the content-type
2499 // then we need to get the content-type string from the hinfo
2500 // and then strip it of any nasty characters (such as / and \)
2501
2502 char *filename_prefix;
2503
2504 filename_prefix = strdup( hinfo->content_type_string );
2505 if (filename_prefix != NULL)
2506 {
2507 char *pp;
2508
2509 pp = filename_prefix;
2510 while (*pp) { if ((*pp == '/') || (*pp == '\\')) *pp = '-'; pp++; }
2511
2512 snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%s%d", glb.blankfileprefix_expliticly_set?glb.blankfileprefix:"", filename_prefix, glb.filecount );
2513
2514 free(filename_prefix);
2515
2516 } else {
2517 snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount );
2518 }
2519 } else {
2520
2521 // If we don't care to rename our files based on the content-type
2522 // then we'll simply use the blankfileprefix.
2523
2524 snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount );
2525 }
2526
2527 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is empty, setting to default...(%s)\n",FL, hinfo->filename);
2528
2529 if (glb.no_nameless) keep = 0;
2530 glb.filecount++;
2531 }
2532 else if (strncmp(hinfo->filename, glb.blankfileprefix, strlen(glb.blankfileprefix)) != 0)
2533 {
2534 // If the filename does not contain the blankfile prefix at the beginning, then we
2535 // will consider it a normal attachment, thus, we
2536 // need to increment the attachment count
2537
2538 glb.attachment_count++;
2539 }
2540
2541 // If we are required to have "unique" filenames for everything, rather than
2542 // allowing ripMIME to overwrite stuff, then we put the filename through
2543 // its tests here
2544
2545 if ((glb.unique_names)&&(keep))
2546 {
2547 MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method );
2548 }
2549
2550 // If the calling program requested verbosity, then indicate that we're decoding
2551 // the file here
2552
2553 if ((keep)&&(MIME_VERBOSE))
2554 {
2555 if (MIME_VERBOSE_12)
2556 {
2557 LOGGER_log("Decoding: %s\n", hinfo->filename);
2558 } else {
2559
2560 // Now, please bare with me on this horrid little piece of tortured code,
2561 // ... basically, now that we're using LOGGER_log to generate our output, we
2562 // need to compose everything onto a single LOGGER_log() call, otherwise our
2563 // output will get split into several lines, not entirely the most plesant
2564 // thing we want to see when we've got another program most likely reading
2565 // the output.
2566 //
2567 // The %s%s pair is DELIBERATELY pressed up against the 'Decoding' word because
2568 // otherwise, if we are not outputting any data, we'll end up with a double
2569 // space between Decoding and filename, certainly not very good thing to do
2570 // if we're trying to present a consistant output.
2571 //
2572 // The parameters for the content-type output are decided on by the result
2573 // of the MIMEH_get_verbosity_contenttype() call. If the call returns > 0
2574 // then the TRUE token is selected to be displayed, else the FALSE token
2575 // ( in this case, an empty string "" ) is selected.
2576 // The format of the evaluation is:
2577 //
2578 // (logic-test?true-expression:false-expression)
2579
2580 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: About to execute callback [0x%p]",FL,glb.filename_decoded_reporter);
2581 if (glb.filename_decoded_reporter != NULL)
2582 {
2583 glb.filename_decoded_reporter( hinfo->filename, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL));
2584 }
2585
2586 } // If we were using the new filename telling format
2587 } // If we were telling the filename (verbosity)
2588
2589 if (1)
2590 {
2591 char *fp;
2592
2593 /** Find the start of the filename. **/
2594 fp = strrchr(hinfo->filename, '/');
2595 if (fp) fp++; else fp = hinfo->filename;
2596
2597 //LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Pushing filename %s to the stack",FL,fp);
2598
2599 // 20040305-1419:PLD
2600 // Store the filename we're going to use to save the file to in the filename stack
2601 SS_push(ss, fp, strlen(fp));
2602 }
2603
2604 // Select the decoding method based on the content transfer encoding
2605 // method which we read from the headers
2606
2607 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: ENCODING = %d\n",FL, hinfo->content_transfer_encoding);
2608
2609 switch (hinfo->content_transfer_encoding)
2610 {
2611 case _CTRANS_ENCODING_B64:
2612 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding BASE64 format\n",FL);
2613 result = MIME_decode_64(f, unpackdir, hinfo);
2614
2615 switch (result) {
2616 case MIME_ERROR_B64_INPUT_STREAM_EOF:
2617 break;
2618
2619 case MIME_BASE64_STATUS_HIT_BOUNDARY:
2620 result = 0;
2621 break;
2622 case 0:
2623 result = MIME_decode_64_cleanup(f, unpackdir, hinfo);
2624 break;
2625 default:
2626 break;
2627 }
2628 break;
2629
2630 case _CTRANS_ENCODING_7BIT:
2631 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 7BIT format\n",FL);
2632 result = MIME_decode_text(f, unpackdir, hinfo, keep);
2633 break;
2634
2635 case _CTRANS_ENCODING_8BIT:
2636 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 8BIT format\n",FL);
2637 result = MIME_decode_text(f, unpackdir, hinfo, keep);
2638 break;
2639
2640 case _CTRANS_ENCODING_BINARY:
2641 case _CTRANS_ENCODING_RAW:
2642 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding RAW format\n",FL);
2643 result = MIME_decode_raw(f, unpackdir, hinfo, keep);
2644 break;
2645
2646 case _CTRANS_ENCODING_QP:
2647 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding Quoted-Printable format\n",FL);
2648 result = MIME_decode_text(f, unpackdir, hinfo, keep);
2649 break;
2650
2651 case _CTRANS_ENCODING_UUENCODE:
2652 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UUENCODED format\n",FL);
2653 // Added as a test - remove if we can get this to work in a better way
2654 snprintf(hinfo->uudec_name,sizeof(hinfo->uudec_name),"%s",hinfo->filename);
2655 result = UUENCODE_decode_uu(f, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 0, keep );
2656 glb.attachment_count += result;
2657 // Because this is a file-count, it's not really an 'error result' as such, so, set the
2658 // return code back to 0!
2659 result = 0;
2660 break;
2661
2662 case _CTRANS_ENCODING_UNKNOWN:
2663 switch (hinfo->content_disposition) {
2664 case _CDISPOSITION_FORMDATA:
2665 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format of FORMDATA disposition\n",FL);
2666 result = MIME_decode_raw(f, unpackdir, hinfo, keep);
2667 break;
2668
2669 default:
2670 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format\n",FL);
2671 result = MIME_decode_text(f, unpackdir, hinfo, keep);
2672 }
2673
2674 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: UNKNOWN Decode completed, result = %d\n",FL,result);
2675 break;
2676
2677 case _CTRANS_ENCODING_UNSPECIFIED:
2678 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNSPECIFIED format\n",FL);
2679 result = MIME_decode_text(f, unpackdir, hinfo, keep);
2680 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding result for UNSPECIFIED format = %d\n",FL, result);
2681
2682 // 20040114-1236:PLD: Added nested mail checking
2683 //
2684 // Sometimes mailpacks have false headers at the start, resulting
2685 // in ripMIME terminating the recursion process too early. This
2686 // little test here checks to see if the output of the file is
2687 // a nested MIME email (or even an ordinary email
2688 //
2689 // It should be noted, that the reason why the test occurs /here/ is
2690 // because dud headers will always result in a UNSPECIFIED encoding
2691 //
2692 // Original sample mailpack was sent by Farit - thanks.
2693
2694 if (1)
2695 {
2696 snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename);
2697 LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG:REMOVEME: Testing for RFC822 headers in file %s",FL,hinfo->scratch);
2698 if (MIME_is_file_RFC822(hinfo->scratch) > 0 )
2699 {
2700 // 20040305-1304:PLD: unpack the file, propagate result upwards
2701 result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss );
2702 }
2703 }
2704 break;
2705
2706 default:
2707 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding format is not defined (%d)\n",FL, hinfo->content_transfer_encoding);
2708 result = MIME_decode_raw(f, unpackdir, hinfo, keep);
2709 break;
2710 }
2711
2712 // Analyze our results
2713 switch (result) {
2714 case 0:
2715 break;
2716
2717 case MIME_STATUS_ZERO_FILE:
2718 return MIME_STATUS_ZERO_FILE;
2719 break;
2720
2721
2722 case MIME_ERROR_FFGET_EMPTY:
2723 return result;
2724 break;
2725
2726 case MIME_ERROR_RECURSION_LIMIT_REACHED:
2727 return result;
2728 break;
2729 }
2730
2731 if ((result != -1)&&(result != MIME_STATUS_ZERO_FILE))
2732 {
2733
2734
2735
2736
2737 #ifdef RIPOLE
2738 // If we have OLE decoding active and compiled in, then
2739 // do a quick attempt at decoding the file, fortunately
2740 // because the ripOLE engine detects if the file is not an
2741 // OLE file, it does not have a major impact on the
2742 // performance of the ripMIME decoding engine
2743 if (glb.decode_ole > 0)
2744 {
2745 MIME_decode_OLE( unpackdir, hinfo, 0 );
2746 }
2747 #endif
2748
2749
2750
2751 // If our content-type was TNEF, then we need to decode it
2752 // using an external decoding engine (see tnef/tnef.c)
2753 // Admittingly, this is not the most ideal way of picking out
2754 // TNEF files from our decoded attachments, but, for now
2755 // it'll have to do, besides, it does work fine, without any
2756 // side-effects
2757
2758 if (hinfo->content_type == _CTYPE_TNEF)
2759 {
2760 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding TNEF format\n",FL);
2761 glb.attachment_count++;
2762 MIME_decode_TNEF( unpackdir, hinfo, 0 );
2763 } // Decode TNEF
2764
2765
2766
2767 // Look for Microsoft MHT files... and try decode them.
2768 // MHT files are just embedded email files, except they're usually
2769 // encoded as BASE64... so, you have to -unencode- them, to which
2770 // you find out that lo, you have another email.
2771
2772 if ((strstr(hinfo->filename,".mht"))||(strstr(hinfo->name,".mht")) )
2773 {
2774 if (glb.decode_mht != 0)
2775 {
2776 // Patched 26-01-03: supplied by Chris Hine
2777 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Microsoft MHT format email filename='%s'\n",FL, hinfo->filename);
2778 snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename);
2779
2780 // 20040305-1304:PLD: unpack the file, propagate result upwards
2781 result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss );
2782 }
2783 } // Decode MHT files
2784
2785
2786
2787
2788 } // If result != -1
2789
2790 // End.
2791
2792 MIME_generate_multiple_hardlink_filenames(hinfo,unpackdir);
2793
2794 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Done for filename = '%s'",FL,hinfo->filename);
2795
2796 return result;
2797 }
2798
2799
2800
2801
2802
2803 /*------------------------------------------------------------------------
2804 Procedure: MIME_postdecode_cleanup ID:1
2805 Purpose: Performs any cleanup operations required after the immediate completion of the mailpack decoding.
2806 Input: char *unpackdir - directory where the mailpack was unpacked to
2807 Output: none
2808 Errors:
2809 ------------------------------------------------------------------------*/
MIME_postdecode_cleanup(char * unpackdir,struct SS_object * ss)2810 int MIME_postdecode_cleanup( char *unpackdir, struct SS_object *ss )
2811 {
2812 char fullpath[256];
2813 int result;
2814
2815 result = 0;
2816
2817 do {
2818 char *filename;
2819
2820 if (MIME_DNORMAL) LOGGER_log("%s:%d: Popping file...",FL);
2821 filename = SS_pop(ss);
2822 if (filename == NULL) break;
2823
2824 if (MIME_DNORMAL) LOGGER_log("%s:%d: Popped file '%s'",FL, filename);
2825
2826 if ( strncmp( glb.blankfileprefix, filename, strlen( glb.blankfileprefix ) ) == 0 )
2827 {
2828 snprintf( fullpath, sizeof(fullpath), "%s/%s", unpackdir, filename );
2829 result = unlink( fullpath );
2830 if (MIME_VERBOSE)
2831 {
2832 if (result == -1) LOGGER_log("Error removing '%s'; %s", fullpath, strerror(errno));
2833 else LOGGER_log("Removed %s [status = %d]\n", fullpath, result );
2834 }
2835 }
2836 } while (1);
2837
2838
2839 return 0;
2840 }
2841
2842
2843
2844 /*-----------------------------------------------------------------\
2845 Function Name : MIME_handle_multipart
2846 Returns Type : int
2847 ----Parameter List
2848 1. FFGET_FILE *f,
2849 2. char *unpackdir,
2850 3. struct MIMEH_header_info *hinfo,
2851 4. int current_recursion_level,
2852 5. struct SS_object *ss ,
2853 ------------------
2854 Exit Codes :
2855 Side Effects :
2856 --------------------------------------------------------------------
2857 Comments:
2858
2859 --------------------------------------------------------------------
2860 Changes:
2861
2862 \------------------------------------------------------------------*/
MIME_handle_multipart(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * h,int current_recursion_level,struct SS_object * ss)2863 int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss )
2864 {
2865 int result = 0;
2866
2867 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Decoding multipart/embedded \n",FL);
2868
2869 // If there is no filename, then we have a "standard"
2870 // embedded message, which can be just read off as a
2871 // continuous stream (simply with new boundaries
2872 //
2873
2874 if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' ))
2875 {
2876 char *p;
2877
2878
2879 // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it
2880 // (ie, it's just the existing email with a new set of headers around it
2881 // then rather than saving it to a file, we'll just peel off these outter
2882 // layer of headers and get into the core of the message. This is why we
2883 // call unpack_stage2() because this function just reads the next set of
2884 // headers and decodes.
2885
2886 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL);
2887
2888 h->boundary_located = 0;
2889
2890 result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss);
2891
2892 p = BS_top();
2893 if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary));
2894
2895 } else {
2896
2897 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename);
2898 result = MIME_decode_encoding( f, unpackdir, h, ss );
2899 if (result == 0)
2900 {
2901 // Because we're calling MIME_unpack_single again [ie, recursively calling it
2902 // we need to now adjust the input-filename so that it correctly is prefixed
2903 // with the directory we unpacked to.
2904
2905 snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
2906 snprintf(h->filename,sizeof(h->filename),"%s",scratch);
2907
2908 //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss);
2909 result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level, ss );
2910 }
2911
2912 } // else-if transfer-encoding != B64 && filename was empty
2913
2914 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: done handling '%s' result = %d",FL,h->filename, result);
2915
2916 return result;
2917 }
2918
2919
2920
2921
2922 /*-----------------------------------------------------------------\
2923 Function Name : MIME_handle_rfc822
2924 Returns Type : int
2925 ----Parameter List
2926 1. FFGET_FILE *f, Input stream
2927 2. char *unpackdir, Directory to write files to
2928 3. struct MIMEH_header_info *hinfo, Header information structure
2929 4. int current_recursion_level, Current recursion level
2930 5. struct SS_object *ss , String stack containing already decoded file names
2931 ------------------
2932 Exit Codes :
2933 Side Effects :
2934 --------------------------------------------------------------------
2935 Comments:
2936
2937 --------------------------------------------------------------------
2938 Changes:
2939
2940 \------------------------------------------------------------------*/
MIME_handle_rfc822(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * h,int current_recursion_level,struct SS_object * ss)2941 int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss )
2942 {
2943 /** Decode a RFC822 encoded stream of data from *f **/
2944 int result = 0;
2945
2946 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Decoding RFC822 message\n",FL);
2947
2948 /** If there is no filename, then we have a "standard"
2949 ** embedded message, which can be just read off as a
2950 ** continuous stream (simply with new boundaries
2951 **/
2952 DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Filename='%s', encoding = %d",FL, h->filename, h->content_transfer_encoding);
2953
2954 // if ((0)&&( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '0' )) 20041217-1635:PLD:
2955 if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' ))
2956 {
2957 /** Handle a simple plain text wrapped RFC822 email with no encoding applied to it **/
2958 char *p;
2959
2960
2961 // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it
2962 // (ie, it's just the existing email with a new set of headers around it
2963 // then rather than saving it to a file, we'll just peel off these outter
2964 // layer of headers and get into the core of the message. This is why we
2965 // call unpack_stage2() because this function just reads the next set of
2966 // headers and decodes.
2967
2968 DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL);
2969
2970 h->boundary_located = 0;
2971
2972 result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss);
2973
2974 p = BS_top();
2975 if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary));
2976
2977 } else {
2978 /** ...else... if the section has a filename or B64 type encoding, we need to put it through extra decoding **/
2979
2980 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename);
2981 result = MIME_decode_encoding( f, unpackdir, h, ss );
2982 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Result of extracting %s is %d",FL,h->filename, result);
2983
2984 if (result == 0) {
2985 /** Because we're calling MIME_unpack_single again [ie, recursively calling it
2986 we need to now adjust the input-filename so that it correctly is prefixed
2987 with the directory we unpacked to. **/
2988
2989 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,h->filename);
2990 snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
2991 snprintf(h->filename,sizeof(h->filename),"%s",scratch);
2992
2993 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,scratch);
2994 result = MIME_unpack_single( unpackdir, scratch, current_recursion_level, ss );
2995 result = 0;
2996 }
2997
2998 } /** else-if transfer-encoding != B64 && filename was empty **/
2999
3000 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: done handling '%s' result = %d",FL,h->filename, result);
3001
3002 return result;
3003 }
3004
3005
3006
3007 /*-----------------------------------------------------------------\
3008 Function Name : MIME_handle_plain
3009 Returns Type : int
3010 ----Parameter List
3011 1. FFGET_FILE *f,
3012 2. char *unpackdir,
3013 3. struct MIMEH_header_info *h,
3014 4. int current_recursion_level,
3015 5. struct SS_object *ss ,
3016 ------------------
3017 Exit Codes :
3018 Side Effects :
3019 --------------------------------------------------------------------
3020 Comments:
3021
3022 --------------------------------------------------------------------
3023 Changes:
3024
3025 \------------------------------------------------------------------*/
MIME_handle_plain(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * h,int current_recursion_level,struct SS_object * ss)3026 int MIME_handle_plain( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss )
3027 {
3028 /** Handle a plain text encoded data stream from *f **/
3029 int result = 0;
3030
3031 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_plain:DEBUG: Handling plain email",FL);
3032
3033 result = MIME_decode_encoding( f, unpackdir, h, ss );
3034 if ((result == MIME_ERROR_FFGET_EMPTY)||(result == 0))
3035 {
3036 /** Test for RFC822 content... if so, go decode it **/
3037 snprintf(h->scratch,sizeof(h->scratch),"%s/%s",unpackdir,h->filename);
3038 if (MIME_is_file_RFC822(h->scratch)==1)
3039 {
3040 /** If the file is RFC822, then decode it using MIME_unpack_single() **/
3041 if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(glb.header_longsearch);
3042 result = MIME_unpack_single( unpackdir, h->scratch, current_recursion_level, ss );
3043 if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(0);
3044 }
3045 }
3046
3047 return result;
3048 }
3049
3050
3051 /*------------------------------------------------------------------------
3052 Procedure: MIME_unpack_stage2 ID:1
3053 Purpose: This function commenced with the file decoding of the attachments
3054 as required by the MIME structure of the file.
3055 Input:
3056 Output:
3057 Errors:
3058 ------------------------------------------------------------------------*/
MIME_unpack_stage2(FFGET_FILE * f,char * unpackdir,struct MIMEH_header_info * hinfo,int current_recursion_level,struct SS_object * ss)3059 int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss )
3060 {
3061 int result = 0;
3062 struct MIMEH_header_info *h;
3063 char *p;
3064
3065 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Start, recursion %d\n",FL, current_recursion_level);
3066
3067 if (current_recursion_level > glb.max_recursion_level)
3068 {
3069 /** Test for recursion limits **/
3070 if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_unpack_stage2:WARNING: Current recursion level of %d is greater than permitted %d"\
3071 ,FL, current_recursion_level, glb.max_recursion_level);
3072 return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040306-1301:PLD
3073 }
3074
3075 h = hinfo;
3076
3077 // Get our headers and determin what we have...
3078 //
3079 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing headers (initial)\n",FL);
3080
3081 // Parse the headers, extracting what information we need
3082
3083 /** 20041216-1102:PLD: Keep attempting to read headers until we get a sane set **/
3084 do {
3085 /** Read next set of headers, repeat until a sane set of headers are found **/
3086 result = MIMEH_parse_headers(f,h);
3087 DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing of headers done, sanity = %d, result = %d",FL,h->sanity, result);
3088 } while ((h->sanity == 0)&&(result != -1));
3089
3090 DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Repeat loop of header-reading done, sanity = %d, result = %d",FL,h->sanity, result);
3091 glb.header_defect_count += MIMEH_get_defect_count(h);
3092
3093 // Test the result output
3094 switch (result) {
3095 case -1:
3096 return MIME_ERROR_FFGET_EMPTY;
3097 break;
3098
3099 }
3100
3101 // Copy the subject over to our global structure if the subject is not already set.
3102 // this will give us the 'main' subject of the entire email and prevent
3103 // and subsequent subjects from clobbering it.
3104 //if (glb.subject[0] == '\0') snprintf(glb.subject, _MIME_STRLEN_MAX, "%s", h->subject );
3105 if ((strlen(glb.subject) < 1)&&(h->subject != NULL)&&(strlen(h->subject) > 0))
3106 {
3107 snprintf(glb.subject, sizeof(glb.subject), "%s", h->subject );
3108 }
3109
3110
3111 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Headers parsed, Result = %d, Boundary located = %d\n"\
3112 ,FL,result, hinfo->boundary_located);
3113
3114 // Test to see if we encountered any DoubleCR situations while we
3115 // were processing the headers. If we did encounter a doubleCR
3116 // then we need to decode that data and then reset the doubleCR
3117 // flag.
3118 if (MIMEH_get_doubleCR() != 0)
3119 {
3120 MIME_doubleCR_decode( MIMEH_get_doubleCR_name(), unpackdir, h, current_recursion_level);
3121 MIMEH_set_doubleCR( 0 );
3122 FFGET_SDL_MODE = 0;
3123 }
3124
3125 // If we located a boundary being specified (as apposed to existing)
3126 // then we push it to the BoundaryStack
3127
3128 if (h->boundary_located)
3129 {
3130 char *lbc;
3131 char *fbc;
3132
3133 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Boundary located, pushing to stack (%s)\n",FL,h->boundary);
3134 BS_push(h->boundary);
3135
3136 // Get the first and last character positions of the boundary
3137 // so we can test these for quotes.
3138
3139 fbc = h->boundary;
3140 lbc = h->boundary +strlen(h->boundary) -1;
3141
3142 // Circumvent attempts to trick the boundary
3143 // Even though we may end up pushing 3 boundaries to the stack
3144 // they will be popped off if they're not needed.
3145
3146 if (*fbc == '"') { BS_push((fbc +1)); MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); }
3147 if (*lbc == '"') { *lbc = '\0'; BS_push(h->boundary); *lbc = '"'; MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); }
3148
3149 h->boundary_located = 0;
3150
3151 } else {
3152
3153 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding in BOUNDARY-LESS mode\n",FL);
3154
3155 if (h->content_type == _CTYPE_RFC822)
3156 {
3157
3158 // Pass off to the RFC822 handler
3159 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with RFC822 decoder\n",FL);
3160 result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss);
3161
3162
3163 } else if (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type)) {
3164
3165 // Pass off to the multipart handler
3166 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with Multipart decoder\n",FL);
3167 result = MIME_handle_multipart(f,unpackdir,h,current_recursion_level,ss);
3168
3169 } else {
3170
3171 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding boundaryless file (%s)...\n",FL,h->filename);
3172
3173 result = MIME_handle_plain( f, unpackdir,h,current_recursion_level,ss);
3174
3175 } // else-if content was RFC822 or multi-part
3176
3177 return result;
3178
3179 } // End of the boundary-LESS mode ( processing the mail which has no boundaries in the primary headers )
3180
3181
3182
3183 if ((BS_top()!=NULL)&&(result == 0))
3184 {
3185 // Commence decoding WITH boundary awareness.
3186
3187 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with boundaries (filename = %s)\n",FL,h->filename);
3188
3189 // Decode the data in the current MIME segment
3190 // based on the header information retrieved from
3191 // the start of this function.
3192
3193 result = MIME_decode_encoding(f, unpackdir, h, ss);
3194 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Done decoding, result = %d",FL,result);
3195
3196 if (result == 0)
3197 {
3198
3199 if (BS_top()!=NULL)
3200 {
3201
3202 // As this is a multipart email, then, each section will have its
3203 // own headers, so, we just simply call the MIMEH_parse call again
3204 // and get the attachment details
3205
3206 while ((result == 0)||(result == MIME_STATUS_ZERO_FILE))
3207 {
3208 h->content_type = -1;
3209 h->filename[0] = '\0';
3210 h->name[0] = '\0';
3211 h->content_transfer_encoding = -1;
3212 h->content_disposition = -1;
3213
3214 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding headers...\n",FL);
3215 do {
3216 result = MIMEH_parse_headers(f,h);
3217 } while ((h->sanity == 0)&&(result != -1));
3218
3219 glb.header_defect_count += MIMEH_get_defect_count(h);
3220 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Mime header parsing result = %d\n",FL, result);
3221 // 20040305-1331:PLD
3222 if (result == -1)
3223 {
3224 result = MIME_ERROR_FFGET_EMPTY;
3225 return result;
3226 }
3227
3228 if (h->boundary_located)
3229 {
3230 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Pushing boundary %s\n",FL, h->boundary);
3231 BS_push(h->boundary);
3232 h->boundary_located = 0;
3233 }
3234
3235 if (result == _MIMEH_FOUND_FROM)
3236 {
3237 return _MIMEH_FOUND_FROM;
3238 }
3239
3240 if (result == 0)
3241 {
3242
3243 // If we locate a new boundary specified, it means we have a
3244 // embedded message, also if we have a ctype of RFC822
3245
3246 if ( (h->boundary_located) \
3247 || (h->content_type == _CTYPE_RFC822)\
3248 || (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type))\
3249 )
3250 {
3251 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Multipart/RFC822 mail headers found\n",FL);
3252
3253 /* If there is no filename, then we have a "standard"
3254 * embedded message, which can be just read off as a
3255 * continuous stream (simply with new boundaries */
3256
3257 if (( h->content_type == _CTYPE_RFC822 ))
3258 {
3259 // If the content_type is set to message/RFC822
3260 // then we simply read off the data to the next
3261 // boundary into a seperate file, then 'reload'
3262 // the file into ripMIME. Certainly, this is not
3263 // the most efficent way of dealing with nested emails
3264 // however, it is a rather robust/reliable way.
3265
3266 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Chose Content-type == RFC822 clause",FL);
3267 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Calling MIME_decode_encoding()",FL);
3268
3269 result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss);
3270 // First up - extract the RFC822 body out of the parent mailpack
3271 // XX result = MIME_decode_encoding( f, unpackdir, h, ss );
3272
3273 // Now decode the RFC822 body.
3274 /**
3275 XX
3276 if (result == 0)
3277 {
3278 snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
3279 snprintf(h->filename,sizeof(h->filename),"%s",scratch);
3280 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now calling MIME_unpack_single() on the file '%s' for our RFC822 decode operation.",FL, scratch);
3281 //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss);
3282 result = MIME_unpack( unpackdir, h->filename, current_recursion_level +1 );
3283 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Unpack result = %d", FL, result);
3284 result = 0;
3285 } else return result; // 20040305-1312:PLD
3286 */
3287
3288
3289
3290
3291
3292 } else if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&(h->filename[0] == '\0' )) {
3293
3294 // Decode nameless MIME segments which are not BASE64 encoded
3295 //
3296 // To be honest, i've forgotten what this section of test is supposed to pick out
3297 // in terms of files - certainly it does pick out some, but I'm not sure what format
3298 // they are. Shame on me for not remembering, in future I must comment more.
3299
3300 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: NON-BASE64 DECODE\n",FL);
3301 h->boundary_located = 0;
3302 result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss);
3303
3304 // When we've exited from decoding the sub-mailpack, we need to restore the original
3305 // boundary
3306 p = BS_top();
3307 if (p) snprintf(h->boundary,_MIME_STRLEN_MAX,"%s",p);
3308
3309
3310
3311
3312
3313
3314 /** 20041207-0106:PLD:
3315 ** changed to test for _INLINE **/
3316 //} else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) {
3317 } else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) {
3318
3319 // AppleDouble needs to be handled explicity - as even though it's
3320 // and embedded format, it does not have the normal headers->[headers->data]
3321 // layout of other nested emails
3322
3323 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Handle Appledouble explicitly",FL);
3324 result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss);
3325
3326
3327
3328
3329
3330 } else {
3331
3332 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: RFC822 Message to be decoded...\n",FL);
3333 result = MIME_decode_encoding( f, unpackdir, h, ss );
3334 if (result != 0) return result; // 20040305-1313:PLD
3335 else
3336 {
3337 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now running ripMIME over decoded RFC822 message...\n",FL);
3338
3339 // Because we're calling MIME_unpack_single again [ie, recursively calling it
3340 // we need to now adjust the input-filename so that it correctly is prefixed
3341 // with the directory we unpacked to.
3342
3343 snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
3344 snprintf(h->filename,sizeof(h->filename),"%s",scratch);
3345 //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss);
3346 result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level,ss );
3347 }
3348
3349 } // else-if transfer-encoding wasn't B64 and filename was blank
3350
3351
3352
3353
3354 } else {
3355
3356 // If the attachment included in this MIME segment is NOT a
3357 // multipart or RFC822 embedded email, we can then simply use
3358 // the normal decoding function to interpret its data.
3359
3360 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment \n",FL);
3361 result = MIME_decode_encoding( f, unpackdir, h, ss );
3362
3363 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment '%s' done. \n",FL, h->filename);
3364 // See if we have an attachment output which is actually another
3365 // email.
3366 //
3367 // Added 24 Aug 2003 by PLD
3368 // Ricardo Kleemann supplied offending mailpack to display
3369 // this behavior
3370
3371 if (result != 0) return result; // 20040305-1314:PLD
3372 else
3373 {
3374 char *mime_fname;
3375
3376 mime_fname = PLD_dprintf("%s/%s", unpackdir, h->filename);
3377 if(mime_fname != NULL)
3378 {
3379 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Testing '%s' for email type",FL,mime_fname);
3380 if (MIME_is_file_RFC822(mime_fname))
3381 {
3382 //MIME_unpack_single( unpackdir, mime_fname, (hinfo->current_recursion_level+1), ss);
3383 MIME_unpack_single( unpackdir, mime_fname, current_recursion_level+1,ss);
3384 }
3385 free(mime_fname);
3386 }
3387 }
3388
3389 } // if there was a boundary, RFC822 content or it was multi-part
3390
3391 } else {
3392 // if the result is not 0
3393 break;
3394 } // result == 0 test
3395
3396 } // While (result)
3397
3398 // ???? Why do we bother to pop the stack ???
3399 // The way BS is designed it will auto-pop the inner nested boundaries
3400 // when a higher-up one is located.
3401 //if (result == 0) BS_pop(); // 20040305-2219:PLD
3402
3403 } // if MIME_BS_top()
3404
3405 } // if result == 0
3406
3407 } // if (result)
3408
3409
3410 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Exiting with result=%d recursion=%d\n",FL,result, current_recursion_level);
3411
3412 return result;
3413 }
3414
3415
3416
3417
3418
3419
3420 /*------------------------------------------------------------------------
3421 Procedure: MIME_decode_mailbox ID:1
3422 Purpose: Decodes mailbox formatted email files
3423 Input:
3424 Output:
3425 Errors:
3426 ------------------------------------------------------------------------*/
MIME_unpack_mailbox(char * unpackdir,char * mpname,int current_recursion_level,struct SS_object * ss)3427 int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss )
3428 {
3429 FFGET_FILE f;
3430 FILE *fi;
3431 FILE *fo;
3432 char fname[1024];
3433 char line[1024];
3434 int mcount=0;
3435 int lastlinewasblank=1;
3436 int result;
3437 int input_is_stdin=0;
3438
3439 snprintf(fname,sizeof(fname),"%s/tmp.email000.mailpack",unpackdir);
3440
3441 if ((mpname[0] == '-')&&(mpname[1] == '\0'))
3442 {
3443 fi = stdin;
3444 input_is_stdin=1;
3445
3446 } else {
3447 // fi = fopen(mpname,"r");
3448 if (strcmp(mpname,"-")==0) fi = stdin; else fi = fopen(mpname,"r"); // 20040208-1715:PLD
3449 if (!fi)
3450 {
3451 LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for reading (%s)",FL, mpname,strerror(errno));
3452 return -1;
3453 }
3454 }
3455
3456 fo = fopen(fname,"w");
3457 if (!fo)
3458 {
3459 LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for writing (%s)",FL, fname,strerror(errno));
3460 return -1;
3461 }
3462
3463 FFGET_setstream(&f, fi);
3464
3465 while (FFGET_fgets(line,sizeof(line),&f))
3466 {
3467 // If we have the construct of "\n\rFrom ", then we
3468 // can be -pretty- sure that a new email is about
3469 // to start
3470
3471 if ((lastlinewasblank==1)&&(strncasecmp(line,"From ",5)==0))
3472 {
3473 // Close the mailpack
3474
3475 fclose(fo);
3476
3477 // Now, decode the mailpack
3478
3479 //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss);
3480 // 20040317-2358:PLD
3481 MIME_unpack_single(unpackdir, fname, current_recursion_level ,ss );
3482
3483 // Remove the now unpacked mailpack
3484
3485 result = remove(fname);
3486 if (result == -1)
3487 {
3488 LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno));
3489 }
3490
3491 // Create a new mailpack filename, and keep on going...
3492
3493 snprintf(fname,sizeof(fname),"%s/tmp.email%03d.mailpack",unpackdir,++mcount);
3494 fo = fopen(fname,"w");
3495 }
3496 else
3497 {
3498 fprintf(fo,"%s",line);
3499 }
3500
3501 // If the line is blank, then note this down because
3502 // if our NEXT line is a From, then we know that
3503 // we have reached the end of the email
3504 //
3505 if ((line[0] == '\n') || (line[0] == '\r'))
3506 {
3507 lastlinewasblank=1;
3508 }
3509 else lastlinewasblank=0;
3510
3511 } // While fgets()
3512
3513 // Don't attempt to close STDIN if that's where the mailpack/mailbox
3514 // has come from. Although this should really cause problems,
3515 // it's better to be safe than sorry.
3516
3517 if (input_is_stdin == 0)
3518 {
3519 fclose(fi);
3520 }
3521
3522 // Now, even though we have run out of lines from our main input file
3523 // it DOESNT mean we dont have some more decoding to do, in fact
3524 // quite the opposite, we still have one more file to decode
3525
3526 // Close the mailpack
3527
3528 fclose(fo);
3529
3530 // Now, decode the mailpack
3531
3532 //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss);
3533 // 20040317-2358:PLD
3534 MIME_unpack_single(unpackdir, fname, current_recursion_level , ss );
3535
3536 // Remove the now unpacked mailpack
3537
3538 result = remove(fname);
3539 if (result == -1)
3540 {
3541 LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno));
3542 }
3543
3544 return 0;
3545 }
3546
3547 /*-----------------------------------------------------------------\
3548 Function Name : MIME_unpack_single
3549 Returns Type : int
3550 ----Parameter List
3551 1. char *unpackdir,
3552 2. char *mpname,
3553 3. int current_recursion_level ,
3554 ------------------
3555 Exit Codes :
3556 Side Effects :
3557 --------------------------------------------------------------------
3558 Comments:
3559
3560 --------------------------------------------------------------------
3561 Changes:
3562
3563 \------------------------------------------------------------------*/
MIME_unpack_single(char * unpackdir,char * mpname,int current_recursion_level,struct SS_object * ss)3564 int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss )
3565 {
3566 FILE *fi; /* Pointer for the MIME file we're going to be going through */
3567 int result = 0;
3568
3569 if (current_recursion_level > glb.max_recursion_level)
3570 {
3571 LOGGER_log("%s:%d:MIME_unpack_single:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level);
3572 return MIME_ERROR_RECURSION_LIMIT_REACHED;
3573 }
3574
3575 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: dir=%s packname=%s level=%d (max = %d)\n",FL, unpackdir, mpname, current_recursion_level, glb.max_recursion_level);
3576
3577 /* if we're reading in from STDIN */
3578 if( mpname[0] == '-' && mpname[1] == '\0' )
3579 {
3580 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: STDIN opened...\n",FL);
3581 fi = stdin;
3582 }
3583 else
3584 {
3585 fi = fopen(mpname,"r");
3586 if (!fi)
3587 {
3588 LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Cannot open file '%s' for reading.\n",FL, mpname);
3589 return -1;
3590 }
3591 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Input file (%s) opened...\n",FL, mpname);
3592
3593 }
3594
3595 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Checking input streams...\n",FL);
3596
3597 /* check to see if we had problems opening the file */
3598 if (fi == NULL)
3599 {
3600 LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Could not open mailpack file '%s' (%s)",FL, mpname, strerror(errno));
3601 return -1;
3602 }
3603
3604 // 20040317-2359:PLD
3605 result = MIME_unpack_single_fp(unpackdir,fi,current_recursion_level , ss);
3606 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: result = %d, recursion = %d, filename = '%s'", FL, result, current_recursion_level, mpname );
3607 if ((current_recursion_level > 1)&&(result == 241)) result = 0;
3608
3609 fclose(fi);
3610
3611 return result;
3612 }
3613
3614
3615
3616
3617
3618 /*------------------------------------------------------------------------
3619 Procedure: MIME_unpack_single ID:1
3620 Purpose: Decodes a single mailpack file (as apposed to mailbox format) into its
3621 possible attachments and text bodies
3622 Input: char *unpackdir: Directory to unpack the attachments to
3623 char *mpname: Name of the mailpack we have to decode
3624 int current_recusion_level: Level of recursion we're currently at.
3625 Output:
3626 Errors:
3627 ------------------------------------------------------------------------*/
MIME_unpack_single_fp(char * unpackdir,FILE * fi,int current_recursion_level,struct SS_object * ss)3628 int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss )
3629 {
3630 struct MIMEH_header_info h;
3631 int result = 0;
3632 int headers_save_set_here = 0;
3633
3634 FFGET_FILE f;
3635 FILE *hf = NULL;
3636
3637
3638 // Because this MIME module gets used in both CLI and daemon modes
3639 // we should check to see that we can report to stderr
3640 //
3641
3642 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: dir=%s level=%d (max = %d)\n",FL, unpackdir, current_recursion_level, glb.max_recursion_level);
3643
3644 if (current_recursion_level > glb.max_recursion_level)
3645 {
3646 LOGGER_log("%s:%d:MIME_unpack_single_fp:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level);
3647 // return -1;
3648 return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040305-1302:PLD
3649 //return 0; // 20040208-1723:PLD
3650 }
3651 else h.current_recursion_level = current_recursion_level;
3652
3653 glb.current_line = 0;
3654
3655 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: recursion level checked...%d\n",FL, current_recursion_level);
3656
3657 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: DumpHeaders = %d\n",FL, glb.save_headers);
3658
3659 if ((!hf)&&(glb.save_headers)&&(MIMEH_get_headers_save()==0))
3660 {
3661 // Prepend the unpackdir path to the headers file name
3662
3663 snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, glb.headersname);
3664 hf = fopen(scratch,"w");
3665 if (!hf)
3666 {
3667 glb.save_headers = 0;
3668 LOGGER_log("%s:%d:MIME_unpack_single_fp:ERROR: Cannot open '%s' for writing (%s)",FL, glb.headersname,strerror(errno));
3669 }
3670 else
3671 {
3672 headers_save_set_here = 1;
3673 MIMEH_set_headers_save(hf);
3674 }
3675 }
3676
3677
3678 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Setting up streams to decode\n",FL);
3679 FFGET_setstream(&f, fi);
3680
3681 /** Initialize the header record **/
3682 h.boundary[0] = '\0';
3683 h.boundary_located = 0;
3684 h.filename[0] = '\0';
3685 h.name[0] = '\0';
3686 h.content_transfer_encoding = -1;
3687 h.content_disposition = -1;
3688 h.content_type = -1;
3689 h.x_mac = 0;
3690 SS_init(&(h.ss_filenames));
3691 SS_init(&(h.ss_names));
3692 if (MIME_DNORMAL) { SS_set_debug(&(h.ss_filenames), 1); SS_set_debug(&(h.ss_names), 1); }
3693 if (glb.verbose_defects) {
3694 int i;
3695
3696 for (i = 0; i < _MIMEH_DEFECT_ARRAY_SIZE; i++)
3697 {
3698 h.defects[i] = 0;
3699 }
3700 }
3701
3702
3703 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: preparing to decode, calling stage2...\n",FL);
3704 // 20040318-0001:PLD
3705 result = MIME_unpack_stage2(&f, unpackdir, &h, current_recursion_level +1, ss);
3706 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: done decoding ( in stage2 ) result=%d, to %s\n",FL, result, unpackdir);
3707
3708 // fclose(fi); 20040208-1726:PLD
3709
3710 if ( headers_save_set_here > 0 )
3711 {
3712 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Closing header file.\n",FL);
3713 fflush(stdout);
3714 MIMEH_set_headers_nosave();
3715 fclose(hf);
3716 }
3717
3718 if (glb.verbose_defects) MIMEH_dump_defects(&h);
3719
3720 /** Flush out the string stacks **/
3721 SS_done(&(h.ss_filenames));
3722 SS_done(&(h.ss_names));
3723
3724 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Done. Result=%d Recursion=%d\n",FL, result, current_recursion_level);
3725
3726 return result;
3727 // return status; // 20040305-1318:PLD
3728 }
3729
3730
3731
3732
3733
3734
3735 /*------------------------------------------------------------------------
3736 Procedure: MIME_unpack ID:1
3737 Purpose: Front end to unpack_mailbox and unpack_single. Decides
3738 which one to execute based on the mailbox setting
3739 Input:
3740 Output:
3741 Errors:
3742 ------------------------------------------------------------------------*/
MIME_unpack(char * unpackdir,char * mpname,int current_recursion_level)3743 int MIME_unpack( char *unpackdir, char *mpname, int current_recursion_level )
3744 {
3745 int result = 0;
3746 struct SS_object ss; // Stores the filenames that are created in the unpack operation
3747
3748 if (current_recursion_level > glb.max_recursion_level) return MIME_ERROR_RECURSION_LIMIT_REACHED;
3749
3750 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking %s to %s, recursion level is %d",FL,mpname,unpackdir,current_recursion_level);
3751
3752 MIMEH_set_outputdir(unpackdir);
3753 if (MIME_DNORMAL) SS_set_debug(&ss,1);
3754 SS_init(&ss);
3755
3756 if (glb.mailbox_format > 0)
3757 {
3758 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking using mailbox format",FL);
3759 result = MIME_unpack_mailbox( unpackdir, mpname, (current_recursion_level), &ss );
3760 }
3761 else
3762 {
3763 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking standard mailpack",FL,mpname,unpackdir,current_recursion_level);
3764 result = MIME_unpack_single( unpackdir, mpname, (current_recursion_level +1), &ss );
3765 }
3766
3767 if (glb.no_nameless)
3768 {
3769 MIME_postdecode_cleanup( unpackdir, &ss );
3770 }
3771
3772 if (MIME_DNORMAL)
3773 {
3774 LOGGER_log("%s:%d:MIME_unpack: Files unpacked from '%s' (recursion=%d);",FL,mpname,current_recursion_level);
3775 SS_dump(&ss);
3776 }
3777 SS_done(&ss);
3778 if (MIME_DNORMAL) SS_set_debug(&ss,1);
3779
3780 // If the result is a non-critical one (ie, just running out of data from the
3781 // file we attempted to decode - then don't propergate it, on the other
3782 switch (result) {
3783 case 1:
3784 result = 0;
3785 break;
3786 case MIME_STATUS_ZERO_FILE:
3787 result = 0;
3788 break;
3789 case MIME_ERROR_FFGET_EMPTY:
3790 case MIME_ERROR_RECURSION_LIMIT_REACHED:
3791 result = 0;
3792 break;
3793 default:
3794 break;
3795 }
3796
3797 if (current_recursion_level == 0)
3798 {
3799 //LOGGER_log("%s:%d:MIME_unpack:DEBUG: Clearing boundary stack",FL);
3800 BS_clear();
3801 }
3802
3803 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking of %s is done.",FL,mpname);
3804 if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: -----------------------------------",FL);
3805
3806 return result;
3807
3808 }
3809
3810
3811
3812
3813
3814
3815 /*--------------------------------------------------------------------
3816 * MIME_close
3817 *
3818 * Closes the files used in MIME_unpack, such as headers etc */
MIME_close(void)3819 int MIME_close( void )
3820 {
3821 if (headers)
3822 {
3823 fclose(headers);
3824 }
3825
3826 return 0;
3827 }
3828
3829
3830
3831
3832 /*----------END OF MIME.c------------*/
3833
3834