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