/*------------------------------------------------------
*
 * MIME.C
*
 * This file is the core component of the ripMIME
* and other packages
*
* Copyright PLD / Paul L Daniels 2002.
*
*
* NOTES:
*	All dates used in this code are of the format Day[-/]Month[-/]Year
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <dirent.h>

#ifdef MEMORY_DEBUG
#define DEBUG_MEMORY 1
#include "xmalloc.h"
#endif

#include "pldstr.h"
#include "boundary-stack.h"
#include "ffget.h"
#include "mime.h"
#include "tnef/tnef_api.h"
#include "libmime-decoders.h"
#include "uuencode.h"
#include "filename-filters.h"

#include "MIME_headers.h"


#define _SL 1

#define _ENC_UNKNOWN 0
#define _ENC_BASE64 1
#define _ENC_PLAINTEXT 2
#define _ENC_QUOTED 3
#define _ENC_EMBEDDED 4
#define _ENC_NOFILE -1
#define _MIME_CHARS_PER_LINE 32
#define _MIME_MAX_CHARS_PER_LINE 76
#define _RECURSION_LEVEL_MAX 20

#define _BOUNDARY_CRASH 2

// BASE64 / UUDEC and other binary writing routines use the write buffer (now in v1.2.16.3+)
// 	The "limit" define is a check point marker which indicates that on our next run through
//	either the BASE64 or UUDEC routines, we need to flush the buffer to disk

#define _MIME_WRITE_BUFFER_SIZE 102400
#define _MIME_WRITE_BUFFER_LIMIT (_MIME_WRITE_BUFFER_SIZE -4)



// Debug precodes
#define MIME_DPEDANTIC ((glb.debug >= _MIME_DEBUG_PEDANTIC))
#define MIME_DNORMAL   ((glb.debug >= _MIME_DEBUG_NORMAL  ))

#define FL __FILE__,__LINE__

/*----Structures-----*/
struct _MIME_info {
	int lastlinewasboundary;	/* Last line we read in had the boundary in it */
	int lastlinewasfrom;		 /* last line we read in had the From: line proceeded by a blank */
	int lastlinewasblank;		/* (part of lastlinewasfrom) */
	int lastencoding;			 /* the encoding specifier last read in */
	int boundarylen;			  /* Length of the boundary */
	char *inputok;				 /* Indicator of file reading sanity */
	char boundary[_MIME_STRLEN_MAX]; 		 /* The boundary */
	char filename[_MIME_STRLEN_MAX];		  /* Filename of current attachment */
	char uudec_name[_MIME_STRLEN_MAX];	// UUDecode name. This is a post-decode information field.

};


/* our base 64 decoder table */
static unsigned char b64[256]={
	128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,   62,  128,  128,  128,   63,\
  52,   53,   54,   55,   56,   57,   58,   59,   60,   61,  128,  128,  128,    0,  128,  128,\
 128,    0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,\
  15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25,  128,  128,  128,  128,  128,\
 128,   26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,\
  41,   42,   43,   44,   45,   46,   47,   48,   49,   50,   51,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,\
 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128 \
	};




struct MIME_globals {

	int filecount;
	char blankfileprefix[_MIME_STRLEN_MAX];
	int verbosity;
	int debug;
	int syslogging;
	int stderrlogging;
	int no_uudecode;
	int unique_names;
	int rename_method;
	char headersname[_MIME_STRLEN_MAX];
	char tempdirectory[_MIME_STRLEN_MAX];
	int save_headers;
	int attachment_count;
	int current_line;
	int no_nameless;
	int mailbox_format;

};

static struct MIME_globals glb;



//char OK[]="OKAY";

static char scratch[1024];



/* File pointer for the headers output */
FILE *headers;






/*------------------------------------------------------------------------
Procedure:     MIME_set_debug ID:1
Purpose:       Sets the debug level for reporting in MIME
Input:         int level : What level of debugging to use, currently there
are only two levels, 0 = none, > 0 = debug info
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_debug( int level )
{
	glb.debug = level;

	TNEF_set_debug(level);
	MIMEH_set_debug(level);
	MDECODE_set_debug(level);
	UUENCODE_set_debug(level);
	FNFILTER_set_debug(level);

	return glb.debug;
}


/*------------------------------------------------------------------------
Procedure:     MIME_set_no_uudecode ID:1
Purpose:       Sets the uudecode option
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_no_uudecode( int level )
{
	UUENCODE_set_nodecode( level );

	return level;
}


/*------------------------------------------------------------------------
Procedure:     MIME_set_tmpdir ID:1
Purpose:       Sets the internal Temporary directory name.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_tmpdir( char *setto )
{

	PLD_strncpy(glb.tempdirectory,setto, _MIME_STRLEN_MAX);

	return 0;
}





/*------------------------------------------------------------------------
Procedure:     MIME_set_glb.blankfileprefix ID:1
Purpose:       Sets the filename prefix which is to be used for any files which are saved and do not have a defined filename.
Input:         char *prefix : \0 terminated character array defining the filename prefix
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_blankfileprefix( char *prefix )
{
	PLD_strncpy( glb.blankfileprefix, prefix, _MIME_STRLEN_MAX );
	return 0;
}


/*------------------------------------------------------------------------
Procedure:     MIME_get_glb.blankfileprefix ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
char *MIME_get_blankfileprefix( void )
{
	return glb.blankfileprefix;
}






/*-------------------------------------------------------
* MIME_setglb.verbosity
*
* By default, MIME reports nothing as its working
* Setting the verbosity level > 0 means that it'll
* report things like the name of the files it's
* writing/extracting.
*
 */
int MIME_set_verbosity( int level )
{

	glb.verbosity = level;

	TNEF_set_verbosity( level );
	FNFILTER_set_verbose( level );
	UUENCODE_set_verbose( level );
	MDECODE_set_verbose( level );
	BS_set_verbose( level );


	return 0;
}




/*-------------------------------------------------------
* MIME_set_dumpheaders
*
 * By default MIME wont dump the headers to a text file
* but at times this is useful esp for checking
* for new styles of viruses like the KAK.worm
*
 * Anything > 0 will make the headers be saved
*
 */
int MIME_set_dumpheaders( int level )
{

	glb.save_headers = level;

	return 0;
}


/*------------------------------------------------------
* MIME_set_headersname
*
* by default, the headers would be dropped to a file
* called '_headers_'.  Using this call we can make
* it pretty much anything we like
*/
int MIME_set_headersname( char *fname )
{

	PLD_strncpy(glb.headersname, fname, _MIME_STRLEN_MAX);

	return 0;
}




/*------------------------------------------------------------------------
Procedure:     MIME_get_headersname ID:1
Purpose:       Returns a pointer to the current glb.headersname string.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
char *MIME_get_headersname( void )
{
	return glb.headersname;
}



/*-------------------------------------------------------
* MIME_set_glb.syslogging
*
 * By default, glb.syslogging is off, setting the "level" to a value above zero(0)
* will make MIME log to the system
*/
int MIME_set_syslogging( int level )
{

	glb.syslogging = level;
	TNEF_set_syslogging(level);

	return 0;
}


/*-------------------------------------------------------
* MIME_set_glb.stderrlogging
*
 * By default logging is done via stderr, set this to zero (0) to turn off
*/
int MIME_set_stderrlogging(int level)
{

	glb.stderrlogging = level;
	TNEF_set_stderrlogging(level);

	return 0;
}





/*------------------------------------------------------------------------
Procedure:     MIME_setglb.no_nameless ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_no_nameless( int level )
{

	glb.no_nameless = level;

	return 0;
}






/*------------------------------------------------------------------------
Procedure:     MIME_set_uniquenames ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_uniquenames( int level )
{
	glb.unique_names = level;

	return 0;
}




/*------------------------------------------------------------------------
Procedure:     MIME_set_noparanoid ID:1
Purpose:       If set, will prevent MIME from clobbering what it considers
to be non-safe characters in the file name.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_paranoid( int level )
{

	FNFILTER_set_paranoid( level );

	return level;
}




/*------------------------------------------------------------------------
Procedure:     MIME_set_mailboxformat ID:1
Purpose:       If sets the value for the _mailboxformat variable
in MIME, this indicates to functions later on
that they should be aware of possible mailbox
format specifiers.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_mailboxformat( int level )
{
	glb.mailbox_format = level;
	MIMEH_set_mailbox( level );
	return 0;
}






/*------------------------------------------------------------------------
Procedure:     MIME_set_renamemethod ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_set_renamemethod( int method )
{
	if (( method >= _MIME_RENAME_METHOD_PREFIX ) && ( method <= _MIME_RENAME_METHOD_POSTFIX ))
	{
		glb.rename_method = method;
	}
	else
	    {
		//		#FIXME - make me report correct values to stderr or syslog
		return -1;
	}

	return 0;
}






/*------------------------------------------------------------------------
Procedure:     MIME_getglb.attachment_count ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_get_attachment_count( void )
{
	return glb.attachment_count;
}




/*------------------------------------------------------------------------
Procedure:     MIME_test_uniquename ID:1
Purpose:       Checks to see that the filename specified is unique. If it's not
unique, it will modify the filename
Input:         char *path: Path in which to look for similar filenames
char *fname: Current filename
int method: Method of altering the filename (infix, postfix, prefix)
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_test_uniquename( char *path, char *fname, int method )
{

	struct stat buf;

	char newname[_MIME_STRLEN_MAX +1];
	char scratch[_MIME_STRLEN_MAX +1];
	char *frontname, *extention;

	int cleared = 0;
	int count = 1;

	if (!scratch)
	{
		return -1;
	}

	//DEBUG fprintf(stdout,"----------------------------\n");
//DEBUG fprintf(stdout,"mime.c: %d: input = %s\n",__LINE__,fname);

	frontname = extention = NULL;  // shuts the compiler up

	if (method == _MIME_RENAME_METHOD_INFIX)
	{
		PLD_strncpy(scratch,fname, _MIMEH_STRLEN_MAX);
		//		snprintf(scratch,_MIME_STRLEN_MAX,"%s",fname);
		frontname = scratch;
		extention = strrchr(scratch,'.');
		//DEBUG fprintf(stdout,"mime.c: %d: Scratch: %s - %s-%s\n",__LINE__,scratch,frontname,extention);

		if (extention)
		{
			*extention = '\0';
			extention++;
		}
		else
		    {
			method = _MIME_RENAME_METHOD_POSTFIX;
		}
	}

	snprintf(newname,_MIME_STRLEN_MAX,"%s/%s",path,fname);

	while (!cleared)
	{
		if ((stat(newname, &buf) == -1))
		{
			cleared++;
		}
		else
		    {
			if (method == _MIME_RENAME_METHOD_PREFIX)
			{
				snprintf(newname,_MIME_STRLEN_MAX,"%s/%d_%s",path,count,fname);
			}
			else
			    if (method == _MIME_RENAME_METHOD_INFIX)
			{
				snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d.%s",path,frontname,count,extention);
			}
			else
			    if (method == _MIME_RENAME_METHOD_POSTFIX)
			{
				snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d",path,fname,count);
			}
			count++;
		}
	}

	if (count > 1)
	{
		frontname = strrchr(newname,'/');
		if (frontname) frontname++;
		else frontname = newname;

		PLD_strncpy(fname, frontname, _MIMEH_FILENAMELEN_MAX); //FIXME - this assumes that the buffer space is at least MIME_STRLEN_MAX sized.

	}

	return 0;
}



/*------------------------------------------------------------------------
Procedure:     MIME_is_file_mime ID:1
Purpose:       Determines if the file handed to it is a MIME type email file.

Input:         file name to analyze
Output:        Returns 0 for NO, 1 for YES, -1 for "Things di
Errors:
------------------------------------------------------------------------*/
int MIME_is_file_mime( char *fname )
{
	char conditions[15][15] = {
		"From: ", "Subject: ", "Date: ", "Content-", "content-", "from: ", "subject: ", "date: ", "boundary=", "Boundary=" 		};
	int result = 0;
	int hitcount = 0;
	char *line;
	FILE *f;


	f = fopen(fname,"r");
	if (!f)
	{
		syslog(1,"MIME_is_file_mime: Error, cannot open file '%s' for reading (%s)",fname,strerror(errno));
		return 0;
	}

	line = malloc(sizeof(char) *1025);
	if (!line)
	{
		syslog(1,"MIME_is_file_mime: Error, cannot allocate memory for read buffer");
		return 0;
	}

	while ((hitcount < 2)&&(fgets(line,1024,f)))
	{
		for (result = 0; result < 8; result++)
		{
			//DEBUG			fprintf(stdout,"Testing for : %s\n",conditions[result]);
			if (strstr(line,conditions[result])) hitcount++;
		}
	}

	fclose(f);

	if (hitcount >= 2) result = 1;
	else result = 0;

	if (line) free(line);

	return result;
}













/*------------------------------------------------------------------------
Procedure:     MIME_getchar_start ID:1
Purpose:       This function is used on a once-off basis. It's purpose is to locate a
non-whitespace character which (in the context of its use) indicates
that the commencement of BASE64 encoding data has commenced.
Input:         FFGET f: file stream
Output:        First character of the BASE64 encoding.
Errors:
------------------------------------------------------------------------*/
int MIME_getchar_start( FFGET_FILE *f )
{

	int c;

	/* loop for eternity, as we're "returning out" */
	while (1)
	{

		/* get a single char from file */
		c = FFGET_fgetc(f);

		/* if that char is an EOF, or the char is something beyond
		* ASCII 32 (space) then return */
		if ((c == EOF) || (c > ' '))
		{
			return c;
		}

	} /* while */

	/* Although we shouldn't ever actually get here, we best put it in anyhow */
	return EOF;
}










/*------------------------------------------------------------------------
Procedure:     MIME_decode_TNEF ID:1
Purpose:       Decodes TNEF encoded attachments
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_decode_TNEF( char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
{
	int result=0;
	char fullpath[1024];

	snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename);

	TNEF_set_path(unpackdir);

	result = TNEF_main( fullpath );

	if (result >= 0)
	{
		//		result = remove( fullpath );
		if (result == -1)
		{
			if (glb.verbosity) fprintf(stderr,"MIME_decode_TNEF: Removing %s failed (%s)",fullpath,strerror(errno));
		}
	}

	return result;
}




/*------------------------------------------------------------------------
Procedure:     MIME_decode_raw ID:1
Purpose:       Decodes a binary type attachment, ie, no encoding, just raw data.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_decode_raw( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
{
	int result = 0;
	char fullpath[1024];
	int bufsize=1024;
	char *buffer = malloc((bufsize +1)*sizeof(char));
	int readcount;
	int file_has_uuencode = 0;
	int decode_entire_file = 0;
	FILE *fo;

	/* Decoding / reading a binary attachment is a real interesting situation, as we
	* still use the fgets() call, but we do so repeatedly until it returns a line with a
	* \n\r and the boundary specifier in it.... all in all, I wouldn't rate this code
	* as being perfect, it's activated only if the user intentionally specifies it with
	* --binary flag
	*/

	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: Start\n");

	snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename);
	fo = fopen(fullpath,"wb");

	if (!fo)
	{
		if (glb.stderrlogging) fprintf(stderr,"MIME_decode_raw: Error, cannot open file %s for writing. (%s)\n\n",fullpath,strerror(errno));
		if (glb.syslogging) syslog(_SL,"MIME_decode_raw: Error, cannot open file %s for writing. (%s)\n\n",fullpath,strerror(errno));
		return -1;
	}

	while ((readcount=FFGET_raw(f, buffer,bufsize)) > 0)
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: BUFFER[%p]= '%s'\n",buffer, buffer);

		if ((!file_has_uuencode)&&(UUENCODE_is_uuencode_header( buffer )))
		{
			if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: UUENCODED is YES (buffer=[%p]\n",buffer);

			if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: File contains UUENCODED data(%s)\n",buffer);

			file_has_uuencode = 1;
		}

		if (BS_cmp(buffer, readcount))
		{
			if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: Boundary located - breaking out.\n");

			break;
		}
		else
		    {
			if (MIME_DNORMAL) fprintf(stdout,".,\n");

			fwrite(buffer,sizeof(char),readcount,fo);
		}
	}

	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: Completed reading RAW data\n");

	free(buffer);
	fclose(fo);

	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: Closed file and free'd buffer\n");

	// If there was UUEncoded portions [potentially] in the email, the
	// try to extract them using the MIME_decode_uu()

	if (file_has_uuencode)
	{
		if (MIME_DNORMAL) fprintf(stdout,"Decoding UUencoded data\n");
		if ( hinfo->content_transfer_encoding == _CTRANS_ENCODING_UUENCODE ) decode_entire_file = 0;

		result = UUENCODE_decode_uu(NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep );
		glb.attachment_count += result;

		if (strlen(hinfo->uudec_name))
		{
			if (strcasecmp(hinfo->uudec_name,"winmail.dat")==0)
			{
				if (MIME_DNORMAL) fprintf(stdout,"Decoding TNEF format\n");
				snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name);
				MIME_decode_TNEF( unpackdir, hinfo, keep);
			}
			else fprintf(stdout,"MIME_decode_raw: hinfo has been clobbered.\n");
		}
	}

	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_raw: End[%d]\n",result);

	return result;
}



/*------------------------------------------------------------------------
Procedure:     MIME_decode_text ID:1
Purpose:       Decodes an input stream into a text file.
Input:         unpackdir : directory where to place new text file
hinfo : struct containing information from the last parsed headers
keep : if set, retain the file
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_decode_text( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep )
{

	FILE *of; 							// output file

	int linecount = 0;  				// The number of lines

	int file_has_uuencode = 0;			// Flag to indicate this text has UUENCODE in it

	char fullfilename[1024]=""; 	// Filename of the output file

	char line[1024]; 					// The input lines from the file we're decoding

	char *get_result = &line[0];
	int lastlinewasboundary = 0;
	int result = 0;
	int decodesize=0;

	snprintf(fullfilename,sizeof(fullfilename),"%s/%s",unpackdir,hinfo->filename);

	if (MIME_DNORMAL) fprintf(stdout,"Decoding Print Quotable to %s\n",fullfilename);

	if (!f)
	{
		if (glb.syslogging > 0) syslog(_SL,"Error: ripMIME print-quotable input stream broken.");
		if (glb.stderrlogging > 0) fprintf(stderr,"Error: ripMIME print-quotable input stream broken.");
		//exit(_EXITERR_PRINT_QUOTABLE_INPUT_NOT_OPEN);
		return -1;
	}

	// if our FILE stream is open
	if (f)
	{

		of = fopen(fullfilename,"w");
		if (!of)
		{
			if (glb.syslogging > 0) syslog(_SL,"Error: cannot open %s for writing",fullfilename);
			if (glb.stderrlogging > 0) fprintf(stderr,"Error: cannot open %s for writing",fullfilename);
			//			exit(_EXITERR_PRINT_QUOTABLE_OUTPUT_NOT_OPEN);
			return -1;
		}

		while ((get_result = FFGET_fgets(line,1023,f))&&(of))
		{
			//			if (MIME_DPEDANTIC) fprintf(stdout,"DEBUG:%d: %s",__LINE__,line); // DEBUG

			if ((hinfo->boundary[0] != '\0')&&(BS_cmp(line,sizeof(line)-1)))
			{
				lastlinewasboundary = 1;
				result = 0;
				break;
			}


			if (lastlinewasboundary == 0)
			{
				if (hinfo->content_transfer_encoding == _CTRANS_ENCODING_QP)
				{
					decodesize = MDECODE_decode_qp_text(line);
					fwrite(line, 1, decodesize, of);
				}
				else
				    {
					fprintf(of,"%s",line);
				}


				if ((!file_has_uuencode)&&( UUENCODE_is_uuencode_header( line )))
				{
					if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: UUENCODED data located in file.\n");
					file_has_uuencode = 1;
				}
			}
			linecount++;

		} // while

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: Done writing output file...now attempting to close.");



		// if the file is still safely open
		if (of)
		{

			// close it
			fclose(of);
			// if we wrote nothing, then trash the file
			if ((keep == 0)||(linecount == 0))
			{
				if (MIME_DNORMAL) fprintf(stdout,"Removing saved attachment (keep=%d, linecount=%d)\n",keep,linecount);
				unlink(fullfilename);
			}
		} // if file still safely open

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: Closed.");

	} // if main input file stream was open


	// If our input from the file was invalid due to EOF or other
	// then we return a -1 code to indicate that the end has
	// occured.
	//
	if (!get_result) result = -1;

	// If there was UUEncoded portions [potentially] in the email, the
	// try to extract them using the MIME_decode_uu()
	//
	if (file_has_uuencode)
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: Decoding UUencoded data\n");

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: hinfo = %p\n", hinfo);

		result = UUENCODE_decode_uu( NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep );
		glb.attachment_count += result;

//		result = UUENCODE_decode_uu( NULL, unpackdir, hinfo, keep );

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: hinfo = %p\n", hinfo);

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: Done. [ UUName = '%s' ]\n", hinfo->uudec_name);

		if (strncasecmp(hinfo->uudec_name,"winmail.dat",11)==0)
		{
			if (MIME_DNORMAL) fprintf(stdout,"Decoding TNEF format\n");
			snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name);
			MIME_decode_TNEF( unpackdir, hinfo, keep );
		}

		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_text: Completed decoding UUencoded data.\n");
	}

	if (MIME_DNORMAL) fprintf(stdout,"-----------------Done\n");

	return result;
}




/*------------------------------------------------------------------------
Procedure:     MIME_decode_64 ID:1
Purpose:       This routine is very very very important, it's the key to ensuring
we get our attachments out of the email file without trauma!
NOTE - this has been -slightly altered- in order to make provision
of the fact that the attachment may end BEFORE the EOF is received
as is the case with multiple attachments in email.  Hence, we
now have to detect the start character of the "boundary" marker
I may consider testing the 1st n' chars of the boundary marker
just incase it's not always a hypen '-'.
Input:         FGET_FILE *f: stream we're reading from
char *unpackdir: directory we have to write the file to
struct MIMEH_header_info *hinfo: Auxillairy information such as the destination filename
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_decode_64( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo )
{

	int i;
	int cr_total = 0;
	int cr_count = 0; /* the number of consecutive \n's we've read in, used to detect End of B64 enc */
	int stopcount = 0; /* How many stop (=) characters we've read in */
	int eom_reached = 0; /* flag to say that we've reached the End-Of-MIME encoding. */
	int status = 0; /* Overall status of decoding operation */
	int c; /* a single char as retrieved using MIME_get_char() */
	int char_count = 0; /* How many chars have been received */
	int boundary_crash = 0; /* if we crash into a boundary, set this */
	long int bytecount=0; /* The total file decoded size */
	char output[3]; /* The 4->3 byte output array */
	char input[4]; /* The 4->3 byte input array */
	char fullMIME_filename[_MIME_STRLEN_MAX]=""; /* Full Filename of output file */

	// Write Buffer routine

	unsigned char *writebuffer;
	unsigned char *wbpos;
	int wbcount = 0;
	int loop;

	FILE *of; /* output file pointer */

	/* generate the MIME_filename, and open it up... */
	if (glb.unique_names) MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method );
	snprintf(fullMIME_filename,_MIME_STRLEN_MAX,"%s/%s",unpackdir,hinfo->filename);
	of = fopen(fullMIME_filename,"wb");


	/* if we were unable to open the output file, then we better log an error and drop out */
	if (!of)
	{
		if (glb.syslogging > 0) syslog(_SL,"Error: Cannot open output file %s for BASE64 decoding.",fullMIME_filename);
		if (glb.stderrlogging > 0) fprintf(stderr,"Error: Cannot open output file %s for BASE64 decoding.",fullMIME_filename);
		//		exit(_EXITERR_BASE64_OUTPUT_NOT_OPEN);
		return -1;
	}


	// Allocate the write buffer.  By using the write buffer we gain an additional 10% in performance
	// due to the lack of function call (fwrite) overheads

	writebuffer = malloc( _MIME_WRITE_BUFFER_SIZE *sizeof(unsigned char));
	if (!writebuffer)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_decode_64: Error: cannot allocate 100K of memory for the write buffer");
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_decode_64: Error: cannot allocate 100K of memory for the write buffer");
		return -1;
	}
	else {
		wbpos = writebuffer;
		wbcount = 0;
	}



	/* collect prefixing trash (if any, such as spaces, CR's etc)*/
	c = MIME_getchar_start(f);

	/* and push the good char back */
	FFGET_ungetc(f,c);


	/* do an endless loop, as we're -breaking- out later */
	while (1)
	{

		/* Initialise the decode buffer */
		input[0] = input[1] = input[2] = input[3] = '0';

		/* snatch 4 characters from the input */
		for (i = 0; i < 4; i++)
		{

			// Get Next char from the file input
			//
			// A lot of CPU is wasted here due to function call overheads, unfortunately
			// I cannot yet work out how to make this call (FFGET) operate effectively
			// without including a couple of dozen lines in this section.
			//
			// Times like this C++'s "inline" statement would be nice.
			//


			//----------INLINE Version of FFGET_getchar()
			//

			do {
				if (f->ungetcset)
				{
					f->ungetcset = 0;
					c = f->c;
				}
				else
				    {
					if ((!f->startpoint)||(f->startpoint > f->endpoint))
					{
						FFGET_getnewblock(f);
					}

					if (f->startpoint <= f->endpoint)
					{
						c = *f->startpoint;
						f->startpoint++;
					}
					else
					    {
						c = EOF;
					}
				}


			}
			while ( (c != EOF) && ( c < ' ' ) && ( c != '\n' ) && (c != '-') );

			//
			//-------END OF INLINE---------------------------------------------


			// If we get a CR then we need to check a few things, namely to see
			//	if the BASE64 stream has terminated ( indicated by two consecutive
			//	\n's
			//
			// This should not be affected by \r's because the \r's will just pass
			//	through and be absorbed by the detection of possible 'invalid'
			//	characters

			if (c == '\n')
			{
				cr_count++;
				cr_total++;

				if (cr_count > 1)
				{
					if (MIME_DNORMAL) fprintf(stdout,"EOF Reached due to two consecutive CR's on line %d\n", cr_total);
					eom_reached++;
					break;
				}
				else
				    {
					//					fprintf(stdout,"%s:%d:DEBUG-FINE:%d characters read from input\n",__FILE__,__LINE__,char_count);
					char_count = 0;
					i--;
					continue;
				} // else if it wasn't our 2nd CR

			}
			else
			    {
				cr_count=0;
			}



			/* if we get an EOF char, then we know something went wrong */
			if ( c == EOF )
			{
				//				if (glb.syslogging > 0) syslog(_SL, "Error: input stream broken for base64 decoding for file %s\n",hinfo->filename);
//				if (MIME_DNORMAL) fprintf(stderr,"Error: input stream broken for base64 decoding for file %s\n",hinfo->filename);
				status = -1;
				fwrite(writebuffer, 1, wbcount, of);
				fclose(of);
				if (writebuffer) free(writebuffer);
				return status;
				break;
			} /* if c was the EOF */
			else
			    if (c == '=')
			{
				// Once we've found a stop char, we can actually just "pad" in the rest
				// of the stop chars because we know we're at the end. Some MTA's dont
				// put in enough stopchars... at least it seems X-MIMEOLE: Produced By Microsoft MimeOLE V5.50.4133.2400
				// doesnt.

				if (i == 2)
				{
					input[2] = input[3] = (char)b64[c];
				}
				else if (i == 3)
				{
					input[3] = (char)b64[c];
				}

				// Some Microsoft mail generators dont put in sufficient number of = symbols
				// to pad the file out to a multiple of 4 chars. Hence, in order to prevent
				// an over-read, we stop once we detect the first one.

				// NOTE------
				// Previously we relied on the fact that if we detected a stop char, that FFGET()
				// would automatically absorb the data till EOL. This is no longer the case as we
				// are now only retrieving data byte at a time.
				// So, now we -absorb- till the end of the line using FFGET_fgets()

				stopcount = 4 -i;
				FFGET_fgets(scratch,sizeof(scratch),f);
				if (MIME_DNORMAL) fprintf(stdout,"Stop char detected pos=%d...StopCount = %d\n",i,stopcount);
				i = 4;



				break; // out of FOR.

			}
			else
			    if (c == '-' )
			{
				if (FFGET_fgetc(f) == '-')
				{
					boundary_crash++;
					eom_reached++;
					break;
				}
			}


			/* test for and discard invalid chars */
			if (b64[c] == 0x80)
			{
				i--;
				continue;
			}


			// Finally, if we get this far without the character having been picked
			//	out as some special meaning character, we decode it and place the
			//	resultant byte into the input[] array.

			input[i] = (char)b64[c];

			/* assuming we've gotten this far, then we increment the char_count */
			char_count++;

		} // FOR


		// 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

		output[0] = (input[0] << 2) | (input[1] >> 4);
		output[1] = (input[1] << 4) | (input[2] >> 2);
		output[2] = (input[2] << 6) | input[3];

		// 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

		if (i == 4)
		{
			// If our buffer is full beyond the 'write out limit', then we write the buffered
			//	data to the file -  We use this method in order to save calling fwrite() too
			//	many times, thus avoiding function call overheads and [ possibly ] disk write
			//	interrupt costs.

			if ( wbcount > _MIME_WRITE_BUFFER_LIMIT )
			{
				fwrite(writebuffer, 1, wbcount, of);
				wbpos = writebuffer;
				wbcount = 0;
			}

			// Copy our converted bytes to the write buffer

			for (loop = 0; loop < (3 -stopcount); loop++)
			{
				*wbpos = output[loop];
				wbpos++;
				wbcount++;
			}

			// tally up our total byte conversion count

			bytecount+=(3 -stopcount);

		}
		else if (MIME_DNORMAL) fprintf(stdout,"ERROR - could not attain 4 bytes input\n");


		/* if we wrote less than 3 chars, it means we were at the end of the encoded file thus we exit */
		if ((eom_reached)||(stopcount > 0)||(boundary_crash)||(i!=4))
		{

			// Write out the remaining contents of our write buffer - If we don't do this
			//	we'll end up with truncated files.

			if (wbcount > 0)
			{
				fwrite(writebuffer, 1, wbcount, of);
			}

			/* close the output file, we're done writing to it */
			fclose(of);

			/* if we didn't really write anything, then trash the  file */
			if (bytecount == 0)
			{
				unlink(fullMIME_filename);
			}

			if (boundary_crash) status = 1; // was _BOUNDARY_CRASH

			if (MIME_DNORMAL) fprintf(stdout,"File size = %ld bytes, Exit Status = %d, Boundary Crash = %d\n",bytecount, status, boundary_crash);

			if (writebuffer) free(writebuffer);

			return status;

		} /* if End-of-MIME or Stopchars appeared */

	} /* while */

	if (writebuffer) free(writebuffer);

	return status;

}





/*------------------------------------------------------------------------
Procedure:     MIME_doubleCR_decode ID:1
Purpose:       Decodes a text sequence as detected in the processing of the MIME headers.
This is a specialised call, not really a normal part of MIME decoding, but is
required in order to deal with decyphering MS Outlook visable defects.
Input:         char *filename: Name of the encoded file we need to decode
char *unpackdir: Directory we need to unpack the file to
struct MIMEH_header_info *hinfo: Header information already gleaned from the headers
int current_recursion_level: How many nest levels we are deep
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_doubleCR_decode( char *filename, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level )
{
	int result = 0;
	struct MIMEH_header_info h;
	char *p;

	if ((p=strrchr(filename,'/'))) p++;
	else p = filename;

	memcpy(&h, hinfo, sizeof(h));

	// Works for ripMIME	snprintf(h.filename, sizeof(h.filename), "%s/%s", unpackdir, p);

	snprintf(h.filename, sizeof(h.filename), "%s", p); /// Works for Xamime

	if (MIME_is_file_mime(filename))
	{
		if (glb.verbosity) fprintf(stdout,"MIMEH_doubleCRdecode: Attempting to decode MIME attachment '%s'\n",filename);
		MIME_unpack( unpackdir, filename, current_recursion_level +1);
	}
	else if (UUENCODE_is_file_uuencoded(h.filename))
	{
		if (glb.verbosity) fprintf(stdout,"MIMEH_doubleCRdecode: Attempting to decode UUENCODED attachment '%s'\n",filename);
		result = UUENCODE_decode_uu(NULL, unpackdir, filename, h.uudec_name, sizeof(h.uudec_name), 1, 1 );
		glb.attachment_count += result;
		result = 0;
	}

	return result;
}






/*------------------------------------------------------------------------
Procedure:     MIME_read ID:1
Purpose:       Reads data from STDIN and saves the mailpack to the filename
specified
Input:         char *mpname: full pathname of the file to save the data from STDIN
to
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_read( char *mpname )
{
	long int fsize=-1;
	char *buffer;
	int readcount, writecount;
	FILE *fout;


	buffer = malloc( 4096 *sizeof(char) );
	if ( !buffer )
	{
		syslog(_SL,"%s:%d: ERROR: could not allocate 4K of memory for file read buffer\n", __FILE__, __LINE__ );
		return -1;
	}

	/* open up our input file */
	fout = fopen(mpname,"w");



	/* check that out file opened up okay */
	if (!fout)
	{
		if (glb.syslogging > 0) syslog(_SL,"Error: Cannot open file %s for writing... check permissions perhaps?",mpname);
		if (glb.stderrlogging > 0) fprintf(stderr,"Error: Cannot open file %s for writing... check permissions perhaps?",mpname);
		//exit(_EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT);
		return -1;
	}

	/* assuming our file actually opened up */
	if (fout)
	{

		fsize=0;

		/* while there is more data, consume it */
		while ( !feof(stdin) )
		{
			readcount = fread( buffer, 1, (4096 *sizeof(char)), stdin );
			if ( readcount > 0 )
			{
				writecount = fwrite( buffer, 1, readcount, fout );
				fsize += writecount;

				if ( readcount != writecount )
				{
					syslog(_SL,"%s:%d: ERROR: Attempted to write %d bytes, but only managed %d to file '%s'", __FILE__, __LINE__, readcount, writecount, mpname );
				}

			} else {
				break;
			}
		}

		/* clean up our buffers and close */
		fflush(fout);
		fclose(fout);

		if ( feof(stdin) ) clearerr(stdin);

	} /* end if fout was received okay */

	if ( buffer ) free( buffer );
	/* return our byte count in KB */
	return (int)(fsize /1024);
}




/*------------------------------------------------------------------------
Procedure:     MIME_init ID:1
Purpose:       Initialise various required parameters to ensure a clean starting of
MIME decoding.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_init( void )
{

	BS_init();  		// Boundary-stack initialisations
	UUENCODE_init();	// uuencoding decoding initialisations
	FNFILTER_init();	// Filename filtering
	MDECODE_init();		// ISO filename decoding initialisation


	glb.filecount = 0;
	glb.attachment_count = 0;
	glb.current_line = 0;
	glb.verbosity = 0;
	glb.debug = 0;
	glb.syslogging = 0;
	glb.stderrlogging = 1;
	glb.no_uudecode = 0;
	glb.unique_names = 0;
	glb.save_headers = 0;
	glb.no_nameless = 0;
	glb.mailbox_format = 0;
	glb.rename_method = _MIME_RENAME_METHOD_INFIX;

	snprintf( glb.headersname, sizeof(glb.headersname), "_headers_" );
	snprintf( glb.blankfileprefix, sizeof( glb.blankfileprefix ), "textfile" );

	return 0;
}




/*------------------------------------------------------------------------
Procedure:     MIME_decode_encoding ID:1
Purpose:       Based on the contents of hinfo, this function will call the
required function needed to decode the contents of the file
which is contained within the MIME structure
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_decode_encoding( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo )
{
	int keep = 1;
	int result = -1;

	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Start: (%s)\n",hinfo->filename);

	// If we have a valid filename, then put it through the process of
	// 	cleaning and filtering
	//
	if (isprint(hinfo->filename[0]))
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Filename is valid, cleaning\n");

		FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX);	/* check out thefilename for ISO filenames */
		if (MIME_DNORMAL) {
			fprintf(stdout,"MIME_decode_encoding: Cleaned filename Stage 1.\n");
			fflush(stdout);
		}
		// TMPDEBUG 26-01-03		quick_clean_filename(hinfo->filename, _MIMEH_FILENAMELEN_MAX); 	/* cleanup garbage characters */
		if (MIME_DNORMAL) {
			fprintf(stdout,"MIME_decode_encoding: Cleaned filename Stage 2.\n");
			fflush(stdout);
		}


	}

	// If the filename is NOT valid [doesn't have a printable first char]
	// 	then we must create a new file name for it.
	//
// TMPDEBUG 26-01-03	if (!isprint(hinfo->filename[0]))
//	This has been changed from isprint() because characters returning from the decode_filename() call
//	may not fall under the "US ASCII" printable criterion.

	if ( hinfo->filename[0] == '\0' )
	{
		sprintf( hinfo->filename, "%s%d", glb.blankfileprefix, glb.filecount );
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Filename is not valid, setting to default...(%s)\n",hinfo->filename);
		glb.filecount++;
		if (glb.no_nameless) keep = 0;
	}
	else
	    if ( strncmp(hinfo->filename, glb.blankfileprefix, strlen( glb.blankfileprefix )) != 0)
	{
		glb.attachment_count++;
	}

	// If we are required to have "unique" filenames for everything, rather than
	// 	allowing ripMIME to overwrite stuff, then we put the filename through
	//		its tests here

	if ((glb.unique_names)&&(keep)) MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method );

	// If the calling program requested verbosity, then indicate that we're decoding
	//		the file here

	if ((keep)&&(glb.verbosity)) fprintf(stdout,"Decoding %s\n", hinfo->filename);


	// Select the decoding method based on the content transfer encoding
	// 	method which we read from the headers


	if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: ENCODING = %d\n",hinfo->content_transfer_encoding);

	switch (hinfo->content_transfer_encoding)
	{
	case _CTRANS_ENCODING_B64:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding BASE64 format\n");
		result = MIME_decode_64(f, unpackdir, hinfo);
		if (result == 1) result = 0;  // If we get a boundary crash, ignore it.

		break;
	case _CTRANS_ENCODING_7BIT:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding 7BIT format\n");
		result = MIME_decode_text(f, unpackdir, hinfo, keep);
		break;
	case _CTRANS_ENCODING_8BIT:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding 8BIT format\n");
		result = MIME_decode_text(f, unpackdir, hinfo, keep);
		break;
	case _CTRANS_ENCODING_RAW:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding RAW format\n");
		result = MIME_decode_raw(f, unpackdir, hinfo, keep);
		break;
	case _CTRANS_ENCODING_QP:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding QuotePrintable format\n");
		result = MIME_decode_text(f, unpackdir, hinfo, keep);
		break;
	case _CTRANS_ENCODING_UUENCODE:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding UUENCODED format\n");
		result = UUENCODE_decode_uu(f, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 0, keep );
		glb.attachment_count += result;
//		result = UUDECODE_decode_uu(f, unpackdir, hinfo, keep);
		break;
	case _CTRANS_ENCODING_UNKNOWN:
		result = MIME_decode_text(f, unpackdir, hinfo, keep);
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: UNKNOWN Decode completed\n");
		break;
	case _CTRANS_ENCODING_UNSPECIFIED:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding UNSPECIFIED format\n");
		result = MIME_decode_text(f, unpackdir, hinfo, keep);
		break;
	default:
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Decoding format is not defined (%d)\n",hinfo->content_transfer_encoding);
		if (glb.syslogging > 0) syslog(1,"MIME_decode_encoding: Unknown encoding %d",hinfo->content_transfer_encoding);
		result = MIME_decode_raw(f, unpackdir, hinfo, keep);
		break;
	}

	if ((result != -1)&&(hinfo->content_type == _CTYPE_TNEF))
	{
		if ((MIME_DNORMAL)||(glb.verbosity)) fprintf(stdout,"MIME_decode_encoding: Decoding TNEF format\n");
		glb.attachment_count++;
		MIME_decode_TNEF( unpackdir, hinfo, 0 );
	}

	// Look for Microsoft MHT files... and try decode them.
	//	MHT files are just embedded email files, except they're usually
	//	encoded as BASE64... so, you have to -unencode- them, to which
	//	you find out that lo, you have another email.

	if ( (result != -1) && ( (strstr(hinfo->filename,".mht"))||(strstr(hinfo->name,".mht"))) )
	{
		//	Patched 26-01-03: supplied by Chris Hine
//		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Last filename was : %s\n",hinfo->name);
//		snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->name);
		if (MIME_DNORMAL) fprintf(stdout,"MIME_decode_encoding: Last filename was : %s\n",hinfo->filename);
		snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename);

		MIME_unpack( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1) );
	}




	return result;
}





/*------------------------------------------------------------------------
Procedure:     MIME_postdecode_cleanup ID:1
Purpose:       Performs any cleanup operations required after the immediate completion of the mailpack decoding.
Input:         char *unpackdir - directory where the mailpack was unpacked to
Output:        none
Errors:
------------------------------------------------------------------------*/
int MIME_postdecode_cleanup( char *unpackdir )
{
	struct dirent *dir_entry;
	char fullpath[256];
	int result;
	DIR *adir;

	result = 0;

	adir = opendir( unpackdir );
	if ( !adir )
	{
		fprintf(stdout,"Error attempting to read directory contents from '%s'(%s)", unpackdir, strerror(errno) );
		return 0;
	}

	do {
		dir_entry = readdir( adir );
		if ( dir_entry )
		{
			if ( strncmp( glb.blankfileprefix, dir_entry->d_name, strlen( glb.blankfileprefix ) ) == 0 )
			{
				snprintf( fullpath, sizeof(fullpath), "%s/%s", unpackdir, dir_entry->d_name );
				result = unlink( fullpath );
				if ( glb.verbosity ) fprintf( stdout, "Removed %s [status = %d]\n", fullpath, result );
			}
		}
	} while ( dir_entry );

	return 0;
}




/*------------------------------------------------------------------------
Procedure:     MIME_unpack_stage2 ID:1
Purpose:       This function commenced with the file decoding of the attachments
as required by the MIME structure of the file.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level )
{
	int result = 0;
	struct MIMEH_header_info *h;
	char *p;

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: Start, recursion %d\n",current_recursion_level);

	if (current_recursion_level > _RECURSION_LEVEL_MAX)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_stage2(): Current Recursion level of %d is greater than permitted %d",current_recursion_level, _RECURSION_LEVEL_MAX);
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_stage2(): Current Recursion level of %d is greater than permitted %d\n",current_recursion_level, _RECURSION_LEVEL_MAX);
		return -1;
	}

	h = hinfo;

	// Get our headers and determin what we have...
	//
	if (MIME_DNORMAL) fprintf(stdout,"%s:%d:Parsing headers (initial)\n",FL);

	// Parse the headers, extracting what information we need

	result = MIMEH_parse_headers(f,h);

	if (MIME_DNORMAL) fprintf(stdout,"%s:%d:Headers parsed, Result = %d\n",FL,result);

	if (MIMEH_get_doubleCR())
	{
		MIME_doubleCR_decode( MIMEH_get_doubleCR_name(), unpackdir, h, current_recursion_level);
		MIMEH_set_doubleCR( 0 );
		FFGET_SDL_MODE = 0;
	}

	// If we dont get what we expected, then jump out before
	// 	we push things too far and possibly cause a segfault

	if (result == -1) return result;

	// If we located a boundary being specified (as apposed to existing)
	// then we push it to the BoundaryStack


	if (h->boundary_located)
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:Boundary located, pushing to stack (%s)\n",FL,h->boundary);
		BS_push(h->boundary);
		h->boundary_located = 0;
	}
	else
	    {
		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:Decoding in BOUNDARY-LESS mode\n",FL);

		if ((h->content_type == _CTYPE_RFC822)||(h->content_type == _CTYPE_MULTIPART))
		{
			if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:Decoding multipart/embedded message\n",FL);

			// If there is no filename, then we have a "standard"
			// embedded message, which can be just read off as a
			// continuous stream (simply with new boundaries
			//

			if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' ))
			{
				if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:Non base64 encoding AND no filename, embedded message\n",FL);

				h->boundary_located = 0;

				result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level +1);

				p = BS_top();
				if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary));
			}
			else
			    {
				if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:embedded message has a filename, decoding to file %s",FL,h->filename);
				result = MIME_decode_encoding( f, unpackdir, h );

				// Because we're calling MIME_unpack_single again [ie, recursively calling it
								// we need to now adjust the input-filename so that it correctly is prefixed
								// with the directory we unpacked to.

				snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
				snprintf(h->filename,sizeof(h->filename),"%s",scratch);

				result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1);

			}
		}
		else
		    {
			if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:ecoding boundaryless file (%s)...\n",FL,h->filename);
			result = MIME_decode_encoding( f, unpackdir, h );
		}

		return result;
	}



	if ((BS_top()!=NULL)&&(result == 0))
	{

		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2:%s:%d:Decoding with boundaries (filename = %s)\n",FL,h->filename);

		// Explain this..... if the first headers of the message says RFC822, then we
		// have in effect a wrapped up message, a little crazy to have, but, happens
		// 	So, we dont want to try and read the headers in that case


		result = MIME_decode_encoding(f, unpackdir, h);

		if (result == 0)
		{

			if (BS_top()!=NULL)
			{

				// As this is a multipart email, then, each section will have its
				// own headers, so, we just simply call the MIMEH_parse call again
				// and get the attachment details

				while (result == 0)
				{
					h->content_type = -1;
					h->filename[0] = '\0';
					h->name[0]     = '\0';
					h->content_transfer_encoding = -1;
					h->content_disposition = -1;

					if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: Decoding headers...\n");
					result = MIMEH_parse_headers(f,h);
					if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: result = %d\n",result);

					if (h->boundary_located)
					{
						if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: Pushing boundary %s\n",h->boundary);
						BS_push(h->boundary);
						h->boundary_located = 0;
					}

					if (result == _MIMEH_FOUND_FROM)
					{
						return _MIMEH_FOUND_FROM;
					}

					if (result == 0)
					{

						// If we locate a new boundary specified, it means we have a
						// embedded message, also if we have a ctype of RFC822

						if ( (h->boundary_located) \
						|| (h->content_type == _CTYPE_RFC822)\
					  	|| (h->content_type == _CTYPE_MULTIPART)\
					   )
						{
							if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: Multipart mail headers found\n");

							/* If there is no filename, then we have a "standard"
							* embedded message, which can be just read off as a
							* continuous stream (simply with new boundaries */

							if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&(h->filename[0] == '\0' ))
							{
								if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: NON-BASE64 DECODE\n");
								h->boundary_located = 0;
								result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level +1);

								p = BS_top();
								if (p) snprintf(h->boundary,_MIME_STRLEN_MAX,"%s",p);
							}
							else
							    {
								if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: RFC822 Message to be decoded...\n");
								result = MIME_decode_encoding( f, unpackdir, h );
								if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_stage2: ... NOW running ripMIME over decoded RFC822 message...\n");

								// Because we're calling MIME_unpack_single again [ie, recursively calling it
								// we need to now adjust the input-filename so that it correctly is prefixed
								// with the directory we unpacked to.

								snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename);
								snprintf(h->filename,sizeof(h->filename),"%s",scratch);
								result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1);
							}
						}
						else
						    {
							result = MIME_decode_encoding( f, unpackdir, h );
						}
					}
					else break;

				} // While (result)

				if (result == 0) BS_pop();

			} // if MIME_BS_top()

		} // if result == 0

	} // if (result)


	return result;
}






/*------------------------------------------------------------------------
Procedure:     MIME_decode_mailbox ID:1
Purpose:       Decodes mailbox formatted email files
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level )
{
	FFGET_FILE f;
	FILE *fi;
	FILE *fo;
	char fname[1024];
	char line[1024];
	int mcount=0;
	int lastlinewasblank=1;
	int result;

	snprintf(fname,sizeof(fname),"%s/tmp.email000.mailpack",unpackdir);

	fi = fopen(mpname,"r");
	if (!fi)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_mailbox: Cannot open '%s' for reading (%s)",mpname,strerror(errno));
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_mailbox: Cannot open '%s' for reading (%s)\n",mpname,strerror(errno));
		return -1;
	}

	fo = fopen(fname,"w");
	if (!fo)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_mailbox: Cannot open '%s' for writing  (%s)",fname,strerror(errno));
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_mailbox: Cannot open '%s' for writing (%s)\n",fname,strerror(errno));
		return -1;
	}

	FFGET_setstream(&f, fi);

	while (FFGET_fgets(line,1024,&f))
	{
		// If we have the construct of "\n\rFrom ", then we
		//		can be -pretty- sure that a new email is about
		//		to start

		if ((lastlinewasblank==1)&&(strncasecmp(line,"From ",5)==0))
		{
			// Close the mailpack

			fclose(fo);

			// Now, decode the mailpack

			MIME_unpack_single(unpackdir, fname, current_recursion_level);

			// Remove the now unpacked mailpack

			result = remove(fname);
			if (result == -1)
			{
				if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_mailbox: Error removing temporary mailpack '%s' (%s)",fname,strerror(errno));
			}

			// Create a new mailpack filename, and keep on going...

			snprintf(fname,sizeof(fname),"%s/tmp.email%03d.mailpack",unpackdir,++mcount);
			fo = fopen(fname,"w");
		}
		else
		{
			fprintf(fo,"%s",line);
		}

		// If the line is blank, then note this down because
		// 	if our NEXT line is a From, then we know that
		//		we have reached the end of the email
		//
		if ((line[0] == '\n') || (line[0] == '\r'))
		{
			lastlinewasblank=1;
		}
		else lastlinewasblank=0;

	} // While fgets()

	fclose(fi);

	// Now, even though we have run out of lines from our main input file
	// 	it DOESNT mean we dont have some more decoding to do, in fact
	//		quite the opposite, we still have one more file to decode

	// Close the mailpack

	fclose(fo);

	// Now, decode the mailpack

	MIME_unpack_single(unpackdir, fname, current_recursion_level);

	// Remove the now unpacked mailpack

	result = remove(fname);
	if (result == -1)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_mailbox: Error removing temporary mailpack '%s' (%s)",fname,strerror(errno));
	}

	return 0;
}



/*------------------------------------------------------------------------
Procedure:     MIME_unpack_single ID:1
Purpose:       Decodes a single mailpack file (as apposed to mailbox format) into its
possible attachments and text bodies
Input:         char *unpackdir: Directory to unpack the attachments to
char *mpname: Name of the mailpack we have to decode
int current_recusion_level: Level of recursion we're currently at.
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level )
{
	struct MIMEH_header_info h;
	int result = 0;
	int status = 0;		/* Global status */
	int headers_save_set_here = 0;

	FFGET_FILE f;
	FILE *fi;			/* Pointer for the MIME file we're going to be going through */
	FILE *hf = NULL;


	// Because this MIME module gets used in both CLI and daemon modes
	// 	we should check to see that we can report to stderr
	//

	//	glb.debug = (glb.debug & glb.stderrlogging);

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: dir=%s packname=%s level=%d (max = %d)\n",unpackdir, mpname, current_recursion_level, _RECURSION_LEVEL_MAX);

	if (current_recursion_level > _RECURSION_LEVEL_MAX)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_single: Current Recursion level of %d is greater than permitted %d",current_recursion_level, _RECURSION_LEVEL_MAX);
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_single: Current Recursion level of %d is greater than permitted %d\n",current_recursion_level, _RECURSION_LEVEL_MAX);
		return -1;
	}
	else h.current_recursion_level = current_recursion_level;

	glb.current_line = 0;

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Recursion level checked...\n");

	/* if we're reading in from STDIN */
	if( mpname[0] == '-' && mpname[1] == '\0' )
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: STDIN opened...\n");
		fi = stdin;
	}
	else
	{
		fi = fopen(mpname,"r");
		if (!fi)
		{
			if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: WARNING failed to open new mailpack\n");
			if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_single: Error opening '%s' for reading (%s)",mpname,strerror(errno));
			return -1;
		}
		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Input file (%s) opened...\n",mpname);

	}

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: DumpHeaders = %d\n", glb.save_headers);

	if ((!hf)&&(glb.save_headers)&&(MIMEH_get_headers_save()==0))
	{
		// Prepend the unpackdir path to the headers file name

		snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, glb.headersname);
		hf = fopen(scratch,"w");
		if (!hf)
		{
			glb.save_headers = 0;
			if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_single: Cannot open '%s' for writing  (%s)",glb.headersname,strerror(errno));
			if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_single: Cannot open '%s' for writing (%s)\n",glb.headersname,strerror(errno));
		}
		else
		    {
			headers_save_set_here = 1;
			MIMEH_set_headers_save(hf);
		}
	}




	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Checking input streams...\n");

	/* check to see if we had problems opening the file */
	if (!fi)
	{
		if (glb.syslogging > 0) syslog(_SL,"MIME_unpack_single: Could not open mailpack file '%s' (%s)",mpname, strerror(errno));
		if (glb.stderrlogging > 0) fprintf(stderr,"MIME_unpack_single: Could not open mailpack file '%s' (%s)\n\n",mpname, strerror(errno));
		return -1;
	}

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Setting up streams to decode...\n");
	FFGET_setstream(&f, fi);

	h.content_type = -1;
	h.boundary[0] = '\0';
	h.boundary_located = 0;
	h.filename[0] = '\0';
	h.name[0]     = '\0';
	h.content_transfer_encoding = -1;
	h.content_disposition = -1;

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: preparing to decode (calling stage2...\n");
	result = MIME_unpack_stage2(&f, unpackdir, &h, 0);
	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: done decoding %s to %s\n",mpname, unpackdir);

	fclose(fi);

	if ( headers_save_set_here > 0 )
	{
		if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Closing header file.\n");
		fflush(stdout);
		MIMEH_set_headers_nosave();
		fclose(hf);
	}

	if (MIME_DNORMAL) fprintf(stdout,"MIME_unpack_single: Done.\n");

	return status;
}






/*------------------------------------------------------------------------
Procedure:     MIME_unpack ID:1
Purpose:       Front end to unpack_mailbox and unpack_single.  Decides
which one to execute based on the mailbox setting
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int MIME_unpack( char *unpackdir, char *mpname, int current_recursion_level )
{
	int result = 0;

	if (glb.mailbox_format > 0)
		result = MIME_unpack_mailbox( unpackdir, mpname, current_recursion_level );
	else
	    result = MIME_unpack_single( unpackdir, mpname, current_recursion_level );

	BS_clear();

	if (glb.no_nameless)
	{
		MIME_postdecode_cleanup( unpackdir );
	}

	return result;

}






/*--------------------------------------------------------------------
* MIME_close
*
* Closes the files used in MIME_unpack, such as headers etc */
int MIME_close( void )
{
	if (headers)
	{
		fclose(headers);
	}

	return 0;
}




/*----------END OF MIME.c------------*/

