/*------------------------------------------------------------------------
Module:        mime_alter.c
Author:        Paul L Daniels / Steven Coutts
Project:       ripMIME
State:         development
Creation Date: 01/05/2001
Description:   MIME-alter is an additional module in the mime.[ch] object, which will contain various functions which will -alter- mime-packs.
------------------------------------------------------------------------*/

// 0908 10H57 - Added ability for disclaimer to be actual text, as well as
// 	a filename.  I just detect for a \n or \r in the string, if detected
//		disclaimer is considered as "text"
//

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

#include "ffget.h"
#include "pldstr.h"
#include "boundary-stack.h"
#include "MIME_headers.h"
#include "filename-filters.h"
#include "mime_alter.h"
#include "logger.h"


#define AM_1K_BUFFER_SIZE 1024
#define AM_DISCLAIMER_FILENAME_LENGTH_MAX 256;

#define AM_DNORMAL (glb.debug > 0)
#define AM_VERBOSE (glb.verbose > 0)

/* Globals */
char *AM_scratch_buffer[AM_1K_BUFFER_SIZE]; /* allocate a static array of 1024 pointers to char */


//06/03/2002 - replaced AM_setup_table() with this compile-time array. Saves a few startup cycles.

unsigned char AM_encode64[64]={
	65,   66,   67,   68,   69,   70,   71,   72,   73,   74,   75,   76,   77,   78,   79,   80,\
		81,   82,   83,   84,   85,   86,   87,   88,   89,   90,   97,   98,   99,  100,  101,  102,\
		103,  104,  105,  106,  107,  108,  109,  110,  111,  112,  113,  114,  115,  116,  117,  118,\
		119,  120,  121,  122,   48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   43,   47 \
};


#define AM_HEADERBUFFER_MAX 100
#define AM_HEADERBUFFER_ITEM_SIZE 1024

struct AM_globals {

	int debug;				// Low level debugging
	int verbose;				/* do we talk as we walk */
	int paranoid;				/* set paranoid to yes! */
	int HTML_too;				/* Add footer to the HTML email too */
	int multipart_insert;	/* Should we insert into emails which are embedded into another */
	int nullify_all;			/* Remove ALL filename'd attachments */
	int alter_signed;		/* Do we alter signed emails ? */

	char *disclaimer_plain;
	int disclaimer_plain_type;

	char *disclaimer_HTML;
	int disclaimer_HTML_type;

	char *headerbuffer[ AM_HEADERBUFFER_MAX ];	// 100 lines for the header buffers

	int headerbuffermax;
};

static struct AM_globals glb;




/*-----------------------------------------------------------------\
 Function Name	: AM_version
 Returns Type	: int
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_version( void )
{
	fprintf(stderr,"alterMIME: %s", LIBAM_VERSION);

	return 0;
}


/*-----------------------------------------------------------------\
  Function Name	: AM_init
  Returns Type	: int
  ----Parameter List
  1. void , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_init( void )
{
	glb.debug=0;				// Low level debugging
	glb.verbose=0;				/* do we talk as we walk */
	glb.paranoid=1;				/* set paranoid to yes! */
	glb.HTML_too=1;				/* Add footer to the HTML email too */
	glb.multipart_insert=0;	/* do not insert into multipart attachments */
	glb.nullify_all=0;			/* Remove ALL filename'd attachments */
	glb.alter_signed=0;		/* Do we alter signed emails ? - default is NO */

	glb.disclaimer_plain=NULL;
	glb.disclaimer_plain_type=AM_DISCLAIMER_TYPE_NONE;

	glb.disclaimer_HTML=NULL;
	glb.disclaimer_HTML_type=AM_DISCLAIMER_TYPE_NONE;

	glb.headerbuffermax=0;

	return 0;
}



int AM_done( void )
{
	if (glb.disclaimer_plain != NULL) free(glb.disclaimer_plain);
	if (glb.disclaimer_HTML != NULL) free(glb.disclaimer_HTML);

	return 0;
}

/*------------------------------------------------------------------------
Procedure:     AM_set_debug ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_debug( int level )
{
	glb.debug = level;
	AM_set_verbose( level );
	MIMEH_set_debug(level);

	return level;

}


/*------------------------------------------------------------------------
Procedure:     AM_hbuffer_reset ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_hbuffer_reset( void )
{
	int i;

	for (i = 0; i < glb.headerbuffermax; i++)
	{
		if (glb.headerbuffer[i]) free(glb.headerbuffer[i]);
	}
	glb.headerbuffermax=0;
	return 0;
}


/*------------------------------------------------------------------------
Procedure:     AM_hbuffer_add ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
char *AM_hbuffer_add( char *headerline, FILE *f )
{
	int i;

	if (glb.headerbuffermax == AM_HEADERBUFFER_MAX)
	{
		if (glb.headerbuffer[0])
		{
			fprintf(f,"%s",glb.headerbuffer[0]);
			free(glb.headerbuffer[0]);
		}
		for (i = 0; i < (AM_HEADERBUFFER_MAX-1); i++)
		{
			glb.headerbuffer[i] = glb.headerbuffer[i+1];
		}
		glb.headerbuffermax = AM_HEADERBUFFER_MAX -1;
	}

	glb.headerbuffer[glb.headerbuffermax] = malloc(sizeof(char) *(AM_HEADERBUFFER_ITEM_SIZE +1));

	if (!glb.headerbuffer[glb.headerbuffermax])
	{
		LOGGER_log("alterMIME: AM_hbuffer_add: Error: cannot allocate %d bytes for new header", AM_HEADERBUFFER_ITEM_SIZE +1);
		return NULL;
	}

	strncpy(glb.headerbuffer[glb.headerbuffermax], headerline, AM_HEADERBUFFER_ITEM_SIZE);
	glb.headerbuffermax++;

	return glb.headerbuffer[glb.headerbuffermax-1];
}


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




/*------------------------------------------------------------------------
Procedure:     AM_hbuffer_getline ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
char *AM_hbuffer_getline( int index )
{
	if ((index >=0)&&(index < glb.headerbuffermax))
		return glb.headerbuffer[index];
	else return NULL;
}



/*------------------------------------------------------------------------
Procedure:     AM_set_glb.paranoid ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_paranoid( int level )
{
	glb.paranoid = level;
	return glb.paranoid;
}





/*------------------------------------------------------------------------
Procedure:     AM_set_glb.verbose ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_verbose( int level )
{
	glb.verbose = level;
	return glb.verbose;
}




/*------------------------------------------------------------------------
Procedure:     AM_set_HTMLtoo ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_HTMLtoo( int level )
{
	glb.HTML_too = level;
	return glb.HTML_too;
}


/*------------------------------------------------------------------------
Procedure:     AM_set_multipart_insert ID:1
Purpose:       Set the multipart-insertion of dislcaimers to level.  Multipart disclaimers are not on by default
because they would imply inserting disclaimers into previously forwarded messages.
Input:         level
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_multipart_insert( int level )
{
	glb.multipart_insert = level;
	return glb.multipart_insert;
}



/*-----------------------------------------------------------------\
  Function Name	: *AM_set_disclaimer_plain
  Returns Type	: char
  ----Parameter List
  1. char *filename, 
  2.  int disclaimer_type , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:
	Sets the disclaimer text and text-type for Plain-text disclaimers.
	What we mean here is, if the disclaimer_type is 'TEXT' then we
	don't interpret the contents of .disclaimer_plain as a filename
	rather, we take it literally as the text to use for our disclaimer

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
char *AM_set_disclaimer_plain( char *filename, int disclaimer_type )
{
	glb.disclaimer_plain = strdup( filename );
	glb.disclaimer_plain_type = disclaimer_type;

	return glb.disclaimer_plain;
}



/*-----------------------------------------------------------------\
  Function Name	: *AM_set_disclaimer_HTML
  Returns Type	: char
  ----Parameter List
  1. char *filename, 
  2.  int disclaimer_type , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
char *AM_set_disclaimer_HTML( char *filename, int disclaimer_type )
{
	glb.disclaimer_HTML = strdup( filename );
	glb.disclaimer_HTML_type = disclaimer_type;

	return glb.disclaimer_HTML;
}


/*------------------------------------------------------------------------
Procedure:     AM_set_nullifyall ID:1
Purpose:
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_nullifyall( int level )
{
	glb.nullify_all = level;
	return glb.nullify_all;
}



/*------------------------------------------------------------------------
Procedure:     AM_set_altersigned ID:1
Purpose:       Turns on or off the option to alter signed email messages.
Default is to be -off-.
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int AM_set_altersigned( int level )
{
	glb.alter_signed = level;
	return glb.alter_signed;
}





/*------------------------------------------------------------------------
Procedure:     AM_base64_encodef ID:1
Purpose:       encode to BASE64 an input stream to an output stream
Input:         FILE *fin: Input stream
FILE *fout: Output stream
Output:
Errors:
------------------------------------------------------------------------*/
int AM_base64_encodef( FILE *fin, FILE *fout )
{
	unsigned char inbuf[3];
	unsigned char outbuf[4];
	int cc;
	int byte_count;

	if (!fin)
	{
		LOGGER_log("AM_base64_encodef: Error: input file stream not open, please use AM_base64_encode(infile,outfile)");
		return -1;
	}

	if (!fout)
	{
		LOGGER_log("AM_base64_encodef: Error: output file stream not open, please use AM_base64_encode(infile,outfile)");
		return -1;
	}

	cc = 0;	/* Character Count = 0 */

	while ((byte_count = fread(inbuf, 1, 3, fin)))	/* while we get more than 0 bytes */
	{
		/* Split 3 bytes into 4 bytes by taking 6bit blocks out of the 24 bit (3x8bit)
		 * array */
		outbuf[0] = AM_encode64[((inbuf[0] & 0xFC) >> 2)];
		outbuf[1] = AM_encode64[((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4)];
		outbuf[2] = AM_encode64[((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6)];
		outbuf[3] = AM_encode64[(inbuf[2] & 0x3F)];

		/* if we didn't get a full 3 bytes from the file read, then we need to then
		 * pad the output with '=' (base64 padding char) */
		if ( byte_count < 3 )
		{

			if ( byte_count == 2 ) outbuf[3] = '=';
			if ( byte_count == 1 ) outbuf[3] = outbuf[2] = '=';

			fwrite(outbuf, 1, 4, fout); 	/* write out the buffer */
			fwrite( "\n", 1, 1, fout ); 	/* as this is the last line, put a trailing \n */
			break; 								/* exit out of the while loop */
		}

		fwrite(outbuf, 1, 4, fout);		/* normal operation buffer-write */

		cc += 4;									/* increment the output char count by 4 */

		if (cc > 76)							/* if we have more than 76 chars, then put in a \n */
		{
			fwrite( "\n", 1, 1, fout );	/* write the \n out */
			cc = 0;								/* reset the output char count */
		} /* if bytecount == 3 */
	} /* while more data to read in */

	return 0;
}






/*------------------------------------------------------------------------
Procedure:     AM_base64_encode ID:1
Purpose:       Encodes a given input stream into BASE64 and outputs to a given
output stream
Input:         char *enc_fname: Input filename of the stream to encode
char *out_fname: Output filename of the stream to send to
Output:
Errors:
------------------------------------------------------------------------*/
int AM_base64_encode( char *enc_fname, char *out_fname )
{
	FILE *fin, *fout;

	fin = fopen( enc_fname, "rb" );
	if (!fin)
	{
		LOGGER_log("AM_base64_encode: Cannot open \"%s\" for reading.(%s)",enc_fname,strerror(errno));
		return -1;
	}

	fout = fopen ( out_fname, "wb" );
	if (!fout)
	{
		LOGGER_log("AM_base64_encode: Cannot open \"%s\" for writing.(%s)",out_fname,strerror(errno));
		fclose(fin);
		return -1;
	}

	AM_base64_encodef( fin, fout );	/* encode and output */

	fclose(fin);	/* close the input file */
	fclose(fout);	/* close the output file */

	return 0;
}



/*------------------------------------------------------------------------
Procedure:     AM_add_section1 ID:1
Purpose:       Reads the main headers off an email and places the values it finds for
content-type, encoding and other such into the dd record
Input:         struct AM_disclaimer_details *dd : pointer to the record where all the details will go
FFGET_FILE *f : Open stream which we're reading the headers from
FILE *newf : Open stream which we're dumping the headers we read back into.
Output:
Errors:
------------------------------------------------------------------------*/
int AM_read_headers(struct AM_disclaimer_details *dd, FFGET_FILE *f, FILE *newf )
{
	struct MIMEH_header_info hinfo;

	dd->isfile = 0;
	dd->ishtml = 0;

	if (FFGET_feof(f) != 0) return -1;

	MIMEH_set_headers_save( newf );
	MIMEH_set_doubleCR_save(0);
	MIMEH_parse_headers( f, &hinfo );
	MIMEH_set_doubleCR_save(1);
	MIMEH_set_headers_nosave();

	// Keep reading in lines until we have read a true-blank ( indicating end of headers )

	dd->content_type = hinfo.content_type;
	dd->content_encoding = hinfo.content_transfer_encoding;
	if (hinfo.boundary_located > 0) {
		dd->boundary_found = 1;
		snprintf( dd->boundary, sizeof(dd->boundary), "%s", hinfo.boundary );
	}

	// If the headers revealed a filename, or were of disposition 'ATTACHMENT' then
	//		we'll day that the current MIME block is a file one.
	if ((strlen(hinfo.filename) > 0)||(hinfo.content_disposition == _CDISPOSITION_ATTACHMENT)) dd->isfile = 1;

	// If our content-type is of HTML, then we'll mark that we're in a HTML
	//		section.  NOTE - this doesn't mean that the section isn't a file!
	//		so, we have to check later on to see that dd.isfile == 0 still.
	if (dd->content_type == _CTYPE_TEXT_HTML) dd->ishtml = 1;

	if (hinfo.content_type == _CTYPE_MULTIPART_SIGNED) return AM_RETURN_SIGNED_EMAIL;

	if (glb.debug) LOGGER_log("%s:%d:AM_read_headers:Exit ( Header read section ).\n\t-- isfile=%d ishtml=%d boundaryfound=%d\n\n", FL, dd->isfile, dd->ishtml, dd->boundary_found );
	return 0;
}



/*------------------------------------------------------------------------
Procedure:     AM_disclaimer_load_text ID:1
Purpose:       Loads the text from the file given in fname, and mallocs the required amount
of memory to place the entire contents of the file into textptr.
Input:         char *fname : The file name from which to read the dislcaimer text
char **textptr : a derefernced pointer to the buffer variable which will hold the text.
Output:
Errors:
------------------------------------------------------------------------*/
int AM_disclaimer_load_text( char *fname, char **textptr )
{
	FILE *f;
	struct stat st;

	if (0 == stat(fname, &st))
	{
		*textptr = malloc(st.st_size +1);  // We have to add 1 so that we delimit the data with a \0

		if (textptr)
		{

			memset(*textptr, '\0', (st.st_size +1));

			f = fopen(fname,"r");
			if (!f)
			{
				LOGGER_log("%s:%d:ERROR - Cannot open %s for reading (%s)",__FILE__,__LINE__,fname,strerror(errno));
				textptr = NULL;
				return 0;
			}

			fread(*textptr, 1, st.st_size, f);
			if (glb.debug) LOGGER_log("Disclaimer Loaded:\n%s",*textptr);
			fclose(f);
		}
	}
	else
	{
		LOGGER_log("%s:%d:ERROR - Cannot stat '%s' (%s)",__FILE__,__LINE__,fname,strerror(errno));
	}

	return 0;
}




/*------------------------------------------------------------------------
Procedure:     AM_add_disclaimer_insert_html ID:1
Purpose:       Inserts a disclaimer with <PRE> tagging into a HTML block of text just prior to the
end of the HTML. Assumes that it has been placed at the start of the HTML block
Input:         AM_disclaimer_details *dd : Disclaimer tracking information
FFGET_FILE *f : Input file
FILE *newf : Output file
Output:
Errors:
------------------------------------------------------------------------*/
int AM_add_disclaimer_insert_html( 	struct AM_disclaimer_details *dd, FFGET_FILE *f, FILE *newf )
{
	char line[ AM_1K_BUFFER_SIZE +1];
	char lline[ AM_1K_BUFFER_SIZE +1];
	char *prebody, *tmpbody;

	while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, f))
	{
		strcpy(lline,line);
		PLD_strlower(lline);

		// Look for either a BODY or HTML closing tag.

		prebody = strstr(lline,"</body>");
		if (!prebody) prebody = strstr(lline,"</html>");

		// If we found one of the tags, then insert our disclaimer aruond about
		// here.

		if (prebody != NULL)
		{
			if (glb.debug) LOGGER_log("Inserting html-body disclaimer...");

			dd->html_inserted = 1;

			// prepare to print out the original up to the </body> or </html> part

			tmpbody = line +(prebody -lline);
			*tmpbody = '\0';

			// save to file the first part of the line segment

			if ( dd->disclaimer_text_HTML == NULL )
			{
				fprintf(newf,"%s<BR><PRE>\n%s</pre><BR>",line, dd->disclaimer_text_plain);
			} else {
// replaced 20030611:0837:PLD				fprintf(newf,"%s<BR>", dd->disclaimer_text_HTML );
				fprintf(newf,"%s<BR>\n%s<BR>",line, dd->disclaimer_text_HTML );
			}

			fprintf(newf,"<%s",(tmpbody+1));

			break;
		}
		else
		{
			fprintf(newf,"%s",line);
		}

	} // While FFGET_fgets()

	return dd->html_inserted;
}




/*-----------------------------------------------------------------\
  Function Name	: AM_add_disclaimer_cleanup
  Returns Type	: int
  ----Parameter List
  1. FILE *mp, 
  2.  FILE *newf, 
  3.  char *mpacktmp, 
  4.  char *mpackname , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_add_disclaimer_cleanup( FILE *mp, FILE *newf, char *mpacktmp, char *mpackname )
{

	// Close our files

	fclose(mp);
	fclose(newf);

	// rename the files

	rename(mpacktmp, mpackname);

	return 0;
}


/*------------------------------------------------------------------------
Procedure:     AM_add_disclaimer ID:1
Purpose:       Adds a disclaimer to [potentially] plain-text, mixed/multipart and HTML emails.
Input:         char *mpackname: Mailpack/email name we're modifying
char *disclaimer: either the text, or the filename of the text we want to insert
int istext: 0 = dislciamer is text, 1 = disclaimer is filename
Output:
Errors:
------------------------------------------------------------------------*/
int AM_add_disclaimer( char *mpackname )
{
	FILE *mp, *newf;
	FFGET_FILE f;
	char line[AM_1K_BUFFER_SIZE+1]="";
	char mpacktmp[AM_1K_BUFFER_SIZE+1]="";
	char mpackold[AM_1K_BUFFER_SIZE+1]="";
	struct AM_disclaimer_details dd;
	int result = 0;
	int last_boundary_written = -1;
	int dud_html=0, dud_text=0;

	/* create our temp filename */
	snprintf(mpacktmp,AM_1K_BUFFER_SIZE, "%s.tmp",mpackname);
	snprintf(mpackold,AM_1K_BUFFER_SIZE, "%s.old",mpackname);

	/* Initialise our files */
	newf = fopen(mpacktmp,"w");
	mp = fopen(mpackname,"r");


	// Test file statuses... and hop out if we had troubles with either
	//
	if (!newf)
	{
		LOGGER_log("AM_add_disclaimer: Cannot open %s, %s",mpacktmp,strerror(errno));
		return -1;
	}

	if (!mp)
	{
		LOGGER_log("AM_add_disclaimer: Cannot open %s, %s",mpacktmp,strerror(errno));
		return -1;
	}


	// Set up the disclaimer text as required to be inserted into the file
	//

	dd.disclaimer_text_HTML = NULL;
	dd.disclaimer_text_plain = NULL;

	if ((glb.disclaimer_plain != NULL )&&(glb.disclaimer_plain_type != AM_DISCLAIMER_TYPE_NONE))
	{
		if (glb.disclaimer_plain_type == AM_DISCLAIMER_TYPE_FILENAME)
		{
			AM_disclaimer_load_text( glb.disclaimer_plain, &(dd.disclaimer_text_plain) );
		}
		else
		{
			dd.disclaimer_text_plain = glb.disclaimer_plain;
		}
	} else {
//		LOGGER_log("AM_add_disclaimer: Plain-text disclaimer has not been setup correctly\n");
		dud_text=1;
	}


	if ((glb.disclaimer_HTML != NULL )&&(glb.disclaimer_HTML_type != AM_DISCLAIMER_TYPE_NONE))
	{
		if (glb.disclaimer_HTML_type == AM_DISCLAIMER_TYPE_FILENAME)
		{
			AM_disclaimer_load_text( glb.disclaimer_HTML, &(dd.disclaimer_text_HTML) );
		}
		else
		{
			dd.disclaimer_text_HTML = glb.disclaimer_HTML;
		}
	} else {
//		LOGGER_log("AM_add_disclaimer: HTML-text disclaimer has not been setup correctly\n");
		dud_html=1;
	}

	// If both our disclaiemrs are 'dud's, then we should just return quietly
	if ((dud_html == 1)&&(dud_text == 1))
	{
		LOGGER_log("AM_add_disclaimer: Neither the Plain-text or HTML disclaimer were valid to insert, skipping disclaimer-insertion routine\n");
		return 0;
	}


	// Initialise our variables

	dd.content_type = _CTYPE_UNKNOWN;
	dd.content_encoding = _CTRANS_ENCODING_UNKNOWN;
	dd.boundary_found = 0;
	memset(dd.boundary, 0, sizeof(dd.boundary));
	dd.ishtml = 0;
	dd.isfile = 0;
	dd.text_inserted = 0;
	dd.html_inserted = 0;


	if (glb.verbose) LOGGER_log("Attempting to add disclaimer");

	FFGET_setstream(&f, mp);

	// The process....
	//
	//	First, we read through the email until we at least come across the traditional
	//  true-blank seperator line between the headers and the body of the email.
	//  Once we have found that, we can then choose how we're going to detect our first
	//  disclaimer position based on what we discovered in the headers (ie, did we
	//  find a boundary specifier? are we using plain-text here, or does the email have
	//  HTML in it?
	//
	//  If we have just a plain text email, then we wait till we reach the end of the email
	//  and we just append the disclaimer
	//
	//  If we [most likely] dont have a plain email, then we need to wait until we find
	//  the boundary line, then, insert the disclaimer there.
	//
	//  Next, we need to check to see if the NEXT section is the HTML version of the first
	//  section (well, we dont /really/ compare them, but we check the headers to see if
	//  it has things like text/html and no filename
	//  If the section is HTML and non-filenamed, we once more wait until the boundary then
	//  we add in a HTML version of the disclaimer.
	//
	// Sounds easy ?
	//
	//	Note - It should be pointed out also, that alterMIME will not insert disclaimers into what
	//		are just forwarded emails [ Content-Type: message/rfc822 ].
	//
	// Note - the boundary is ONLY taken from the main headers. This is to prevent us
	//		attempting to dive in deeper into the email adding disclaimers to parts which
	//		were not generated by the last mail user agent.


	// Section 1 - read in the MAIN headers until a true-blank.
	//		This will give us some indication of what to expect in the email, be it a multipart
	//		a file, or a plain text email.
	//

	if (glb.debug) LOGGER_log("Reading main headers\n");

	result = AM_read_headers( &dd, &f, newf );

	if ((AM_RETURN_SIGNED_EMAIL == result)||(-1 == result))
	{
		fclose(mp);
		fclose(newf);
		if (glb.verbose) LOGGER_log("Email is signed with a crypto, we do not alter these emails\n");
		return AM_RETURN_SIGNED_EMAIL;
	}


	// Section 2 - Look for the end of the first text-body
	//
	// Now, if we have no boundary on this email, and it's of
	// text/* content-type
	//

	// If we have no boundary, and we have either text or no content-type
	// then we can just append the disclaimer to the /end/ of the email

	if ((0 == dd.boundary_found)&&((dd.content_type >= _CTYPE_TEXT_START && dd.content_type <= _CTYPE_TEXT_END )))
	{
		if (glb.debug) LOGGER_log("Inserting disclaimer into a non-boundary email\n");

		// If we have a HTML email body, then insert the HTML based
		//		disclaimer, or, the plain-text one with <PRE>...</pre>
		//		tagging

		if (dd.content_type == _CTYPE_TEXT_HTML)
		{
			if (glb.debug) LOGGER_log("Conditions right for HTML disclaimer to be added\n");

			AM_add_disclaimer_insert_html(  &dd, &f, newf );
			if ((glb.verbose)&&( dd.html_inserted == 0 ))
			{
				LOGGER_log("WARNING: Could not insert HTML disclaimer into email\n");
			}

		} else {

			// If we have a plain-text email body, then just get to the end of the file
			//		and append the disclaimer to the end.

			while (FFGET_fgets(line, sizeof(line), &f))
			{
				fprintf(newf,"%s",line);
			}
			fprintf(newf,"%s",dd.disclaimer_text_plain);

			dd.text_inserted = 1;
		}


	}


	// BOUNDARY LIMITED EMAILS -----------------
	//
	// These emails are somewhat /harder/ to insert attachments into
	// namely because of the profound number of convoulted ways in which
	// email clients can forward/reply/mangle emails.
	//
	// We use a 'WHILE' loop here just so that we can cheat a bit when
	// trying to /exit/ out of the processing prematurely when we run
	// out of input (as happens at the end of the input file.
	//
	// By using a 'WHILE', we can just use 'break', rather than resorting
	// to other ugly methods :)


	while (1 == dd.boundary_found)
	{
		if (FFGET_feof(&f)) break;

		// If we've found a boundary and a text content section...
		//

		if (glb.debug) LOGGER_log("%s:%d:AM_add_disclaimer:Inserting into a MIME email\n",FL);


		// Step 1 - first read off any spurilous data prior to the first
		// 	MIME block, this typically includes things like
		//   "If you can read this, then your email client is not MIME
		//	  compliant"  (etc).

		if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Reading spurilous data before first MIME segment\n",FL);

		while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, &f))
		{
			fprintf(newf,"%s",line);
			if ( strstr(line,dd.boundary) ) break;
		}


		// IF for some reason we run into the end of the file, then we just quit out
		//	using our break.

		if (FFGET_feof(&f))
		{
			break;
		}


		// Okay, while we haven't yet managed to insert the disclaimer text
		//	or while we haven't inserted the HTML disclaimer text, we will
		//	keep going around in this loop

		while ((0 == dd.text_inserted)||(0 == dd.html_inserted))
		{

			// Step 2 - Now that we have the first block out of the way, we
			// can read to the bottom of the 2nd block, which is / should be
			// our text body


			// Read the headers and get the content-type information

			if (AM_read_headers( &dd, &f, newf ) < 0)
			{
				break;
				if (glb.verbose) LOGGER_log("\tRan out of input whilst processing headers\n");
			}

			// If this segment is a file, then rather than sifting through anything else
			//	just read until we get to the next boundary line and 'continue' on from
			//	the start of the WHILE loop again.

			if ( dd.isfile == 1 )
			{
				if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Jumping this MIME section as it is a FILE\n",FL);

				while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, &f))
				{

					//					if (FFGET_feof( &f ))
					//					{
					//						fprintf(stdout,"debug: Lastline = --'%s'--", line );
					//					}

					fprintf( newf, "%s", line );
					if ( strstr( line,dd.boundary ) ) break;
				}

				if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:File writing DONE.\n",FL);

				// IF for some reason we run into the end of the file, then we just quit out
				//	using our break.

				if (FFGET_feof(&f))
				{
					break;
				}

				continue;
			} // If segment was a FILE


			// based on our findings from the header reading,
			//	If our MIME block is plain text...
			//
			// Also, take note that if the MIME block is for a file, we dont
			//	insert into it ( else our disclaimer will potentially become
			//	part of the file, really clever stuff!

			if ( (0 == dd.text_inserted) \
					&& (dd.content_type == _CTYPE_TEXT_PLAIN ) \
					&& (dd.isfile == 0) \
				)
			{
				if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Conditions right to insert disclaimer\n",FL);

				// Read in the data (line by line) until we locate the
				// boundary line, then, we write out the disclaimer text
				// -before- we write the disclaimer line down.

				if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Reading first segment looking for boundary line\n",FL);

				// Read and write data until we locate the boundary line.
				//	NOTE - we -deliberately- break before writing the boundary
				//	line because we want to insert the disclaimer /before/
				//	we write the boundary line.

				while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, &f))
				{
					last_boundary_written = 0;
					if ( strstr( line,dd.boundary ) ) break;
					fprintf( newf, "%s", line );
					last_boundary_written = 1;
				}

				// If we got to the end of the file, then, well, ooops, I guess
				//	we'll have to break out.

				if ( FFGET_feof( &f ) )
				{
					break;
				}


				// Assuming that if the file is NOT encoded in base64, then we can attempt
				//	to insert the data. - This is a bit of a assumptious test, because we're
				//	going on the fact that our outer while-loop did a test to make sure that
				//	the current MIME section is NOT a file, therefore, we're saying
				//	"This segment is not a file and is not B64 encoded, so, we can now
				//	insert some disclaimer text into it"

				if (dd.content_encoding != _CTRANS_ENCODING_B64 )
				{
					if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Inserting TEXT disclaimer (%s)\n", FL, dd.disclaimer_text_plain);

					fprintf( newf, "%s\n", dd.disclaimer_text_plain );
					dd.text_inserted = 1; // Our disclaimer has been inserted, so set the flag

				} // if the content encoding is not B64

				if ( last_boundary_written == 0 )
				{
					fprintf( newf, "%s", line );
					last_boundary_written = 1;
				}

			} // If text not inserted and the segment is not a file


			// Add in the HTML disclaimer (if wanted)

			if ( ( glb.HTML_too ) \
					&& ( 0 == dd.html_inserted ) \
					&& ( dd.content_type == _CTYPE_TEXT_HTML )\
					&& ( dd.isfile == 0 ) \
				)
			{
				if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Conditions right for HTML disclaimer to be added",FL);

				result = AM_add_disclaimer_insert_html(  &dd, &f, newf );
				if ((glb.verbose)&&(0 == result))
				{
					LOGGER_log("WARNING: Could not insert HTML disclaimer into email");
				}
			} // If we also want to include a disclaimer in HTML body

		} // If conditions are right to insert our disclaimer into this email with boundaries




		// IF for some reason we run into the end of the file, then we just quit out
		//	using our break.
		if (FFGET_feof(&f))
		{
			if (glb.debug) LOGGER_log("\t%s:%d:AM_add_disclaimer:Main boundary outter-loop FEOF detector hit, breaking out",FL);
			break;
		}

		// Clean up any remaining lines in the email
		while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, &f))
		{
			fprintf(newf,"%s",line);
		}

	} // If a boundary was found in the main headers


	// Clean up any remaining lines in the email
	if ( ! FFGET_feof( &f ) )
	{
		while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, &f)) fprintf(newf,"%s",line);
	}


	// if we didn't get the disclaimer inserted, and this email has no boundary
	//	and there's no file-headers, then we can insert the plain text disclaimer
	//	at the end.
	if (\
			(dd.boundary_found == 0) \
			&& (dd.text_inserted == 0) \
			&& (dd.html_inserted == 0) \
			&& (dd.isfile == 0) \
			&& (\
				((dd.content_type >= _CTYPE_TEXT_START) && (dd.content_type <= _CTYPE_TEXT_END))\
				||(dd.content_type == _CTYPE_UNKNOWN)\
				)\
		)
	{
		if (glb.verbose) LOGGER_log("\tNo disclaimer added, despite going through entire file.");
		fprintf(newf,"\n%s\n\n",dd.disclaimer_text_plain);
	}

	if (glb.verbose) LOGGER_log("Done.\n");


	AM_add_disclaimer_cleanup( mp, newf, mpacktmp, mpackname);


	// Free the malloc'd text if required
	if ((glb.disclaimer_plain_type == AM_DISCLAIMER_TYPE_FILENAME)&&(dd.disclaimer_text_plain))
	{
		free(dd.disclaimer_text_plain);
	}

	if ((glb.disclaimer_HTML_type == AM_DISCLAIMER_TYPE_FILENAME)&&(dd.disclaimer_text_HTML))
	{
		free(dd.disclaimer_text_HTML);
	}

	return result;
}






/*------------------------------------------------------------------------
Procedure:     AM_filename_fix ID:1
Purpose:       Converts a MIME header
Input:         line: string with the filename
removed_prefix: filename prefix to use
removed_count: the number of attachments we've processed / ID
Output:
Errors:
------------------------------------------------------------------------*/
int AM_filename_fix( char *line, char *removed_prefix, int removed_count )
{
	char *pos;
	char lline[AM_1K_BUFFER_SIZE];

	/* process the current line, as it contains the filename, and replace the filename
	 * with a -removed- one */
	pos = strrchr(line,'='); /* locate where the filename starts */
	pos++; /* move one char along */
	*pos = '\0'; /* terminate the string */

	sprintf(lline,"%s\"%s%d\"\n",line,removed_prefix,removed_count); /* create a new string, in lline (scratch pad)*/
	strcpy(line,lline);

	return 0;
}

/*-----------------------------------------------------------------\
 Function Name	: AM_headers_remove_header
 Returns Type	: int
 	----Parameter List
	1. char *headers, 
	2.  char *header_name , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_headers_remove_header( char *headers, char *header_name )
{
	char delimeter_size;
	char *segment_start, *segment_end;
	char *p;

	if (AM_DNORMAL)LOGGER_log("%s:%d:AM_headers_remove_header:DEBUG: Removing %s", FL, header_name );

	delimeter_size=1;
	if (strstr(headers,"\n\r") != NULL) delimeter_size=2;
	if (strstr(headers,"\r\n") != NULL) delimeter_size=2;

	segment_start = PLD_strstr( headers, header_name, 1 );
	if (segment_start == NULL) return 1;

	segment_end = segment_start +strlen( header_name );
	do {
		segment_end = strpbrk( segment_end, "\n\r" );
		if (delimeter_size==2) segment_end+=2; else segment_end++;
	} while (*segment_end==' '||*segment_end=='\t');

	p = segment_end;
	while (*p)
	{
		*segment_start = *p;
		segment_start++;
		p++;
	}
	
	*segment_start = '\0';
	
	return 0;
}

/*-----------------------------------------------------------------\
  Function Name	: AM_nullify_attachment_clean_headers
  Returns Type	: int
  ----Parameter List
  1. struct MIMEH_header_info *hinfo, 
  2.  char *headers , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_nullify_attachment_clean_headers( struct MIMEH_header_info *hinfo, char *headers )
{

	AM_headers_remove_header( headers, "content-type:" );
	AM_headers_remove_header( headers, "content-disposition:");
	AM_headers_remove_header( headers, "content-transfer-encoding");
	/*
	char *p;
	char *q;

	q = headers;
	p = strstr( q, hinfo->filename );
	while (( p != NULL )&&( p > q ))
	{
		if (*p == '_') *p = 'X'; else *p = '_';
		q = p+1;
		p = strstr( q, hinfo->filename );
	}
*/
	return 0;
}


/*-----------------------------------------------------------------\
  Function Name	: AM_nullify_attachment_recurse
  Returns Type	: int
  ----Parameter List
  1. struct MIMEH_header_info *hinf, 
  2.  FFGET_FILE *f, 
  3.  FILE *outputfile, 
  4.  pregex_t *preg , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_nullify_attachment_recurse( struct MIMEH_header_info *hinfo, FFGET_FILE *f, FILE *outputfile, regex_t *preg, int match_mode, int iteration )
{
	int result = 0;

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_nullify_attachment_recurse: Checking segment-iteration '%d'",FL,iteration);
	while (1)
	{
		int regresult=0;
		char *header_ptr=NULL;
		char *original_ptr=NULL;
		char buffer[1024];

		MIMEH_set_doubleCR_save(0);
		result = MIMEH_headers_get( hinfo, f );
		MIMEH_set_doubleCR_save(1);
		if (result != 0)
		{ 
			break;
		}

		if ((hinfo->content_type == _CTYPE_MULTIPART_SIGNED)&&(glb.alter_signed==0))
			return 0;

		original_ptr = MIMEH_get_headers_original_ptr();
		header_ptr = MIMEH_get_headers_ptr();

		if (AM_DNORMAL)LOGGER_log("%s:%d:AM_nullify_attachment_recurse:DEBUG: Headers =\n%s\n-----------END OF HEADERS\n",FL, original_ptr );

		if (original_ptr == NULL)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Original headers came back NULL",FL);
			return 1;
		}

		if (header_ptr == NULL)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Header ptr (for processing) came back NULL",FL);
			return 1;
		}

		result = MIMEH_headers_process( hinfo, header_ptr );
		if (result != 0)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment:ERROR: While processing headers for mailpack", FL );
			break;
		}

		// Check to see if we have a new boundary
		if ((hinfo->content_type >= _CTYPE_MULTIPART_START && hinfo->content_type <= _CTYPE_MULTIPART_END)\
				&& (hinfo->boundary_located > 0))
		{
			BS_push( hinfo->boundary );
		}

		// Now, determine if this block/segment is the one which contains our file which we must 'nullify'
		regresult = 1;
		switch (match_mode) {
			case AM_NULLIFY_MATCH_MODE_FILENAME:
				if (strlen(hinfo->filename) > 0)
				{
					regresult = regexec( preg, hinfo->filename, 0, NULL, 0 );
					if (AM_DNORMAL)LOGGER_log("%s:%d:AM_nullify_attachment: Match result=%d for '%s'", FL, regresult, hinfo->filename);
				} 
				break;
			case AM_NULLIFY_MATCH_MODE_CONTENT_TYPE:
				if (strlen(hinfo->content_type_string) > 0)
				{
					regresult = regexec( preg, hinfo->content_type_string, 0, NULL, 0 );
					if (AM_DNORMAL)LOGGER_log("%s:%d:AM_nullify_attachment: Match result=%d for '%s'", FL, regresult, hinfo->content_type_string);
				}
				break;
			default:
				LOGGER_log("%s:%d:AM_nullify_attachment: unknown Nullify match mode '%d'", FL, match_mode);
		}


		// If we're on our first pass, or we've not found the section/block with the attachment
		//	then write the headers out.
		//
		//	 A bit of an issue which comes up is, what to do if the entire email is the attachment
		//	ie, it's just a something which was 'right-click->email'd.
		//
		//	Until I come up with a better solution, I'll still write the headers, but I'll then 
		//		eliminate all the content [until the next boundary, assuming a boundary exists]

		if ((regresult != 0)||(iteration == 1))
		{
			// If we've got a match which is in the first section of the email, then
			//		we have to 'modify' the headers rather than not writing them at all.
			//		this way, we still keep the email structure intact, but we make it so
			//		that the attachment-related portions of the email changed.
			if ((iteration == 1)&&(regresult == 0))
			{
				AM_nullify_attachment_clean_headers( hinfo, original_ptr );
			}

			fwrite( original_ptr, sizeof(char), strlen(original_ptr), outputfile );
		}

		// Clean up the memory allocation
		result = MIMEH_headers_cleanup();
		if (result != 0)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment:ERROR: while attempting to clean up headers memory allocation", FL );
			break;
		}

		// Now, if we have a Multipart/Mixed attachment, then, alas, we need to recurse into
		//		it and see if it contains anything interesting for us to seek out.
		if (hinfo->content_type == _CTYPE_RFC822)
		{
			result=AM_nullify_attachment_recurse( hinfo, f, outputfile, preg, match_mode, 1 );
		}

		// Now, we shall go through and read the email until we happen across another boundary.
		while (FFGET_fgets(buffer, sizeof(buffer), f))
		{
			int buffer_len = strlen(buffer);

			if (regresult != 0)
			{
				fwrite( buffer, sizeof(char), buffer_len, outputfile );
			}

			if (BS_cmp( buffer, buffer_len ) == 1)
			{
				break;
			}
		} // While ffgets

		if (FFGET_feof(f)) break;


		iteration++;

	} // Infinite while(1)

	return 0;
}


/*------------------------------------------------------------------------
Procedure:     AM_nullify_attachment ID:1
Purpose:       Removes any attachment within the mailpack which matches
(minimally) with the attachmentname parameter
Input:         mpackname - The mailpack name
attachmentname - The name of attachments which is to be removed
Output:        none
Errors:        If attachment wasn't found, or could not be removed, a non-zero return value.
------------------------------------------------------------------------*/
int AM_nullify_attachment( char *mpackname, char *attachmentname )
{
	struct MIMEH_header_info hinfo;
	regex_t preg;
	int result = 0;
	int match_mode = AM_NULLIFY_MATCH_MODE_NONE;
	char tmpfname[256];
	char oldfname[256];
	FILE *inputfile;
	FILE *outputfile;
	FFGET_FILE f;

	// Nullifying an attachment can sometimes be a little bit tricky, we have to
	//		dig down into the nesting of the MIME email and keep tabs on our
	//		boundaries (via a Boundary-stack).  This all requires abot as much work
	//		as ripMIME just to get rid of one attachment.
	//
	// Additionally - we require some special functionality from MIME_headers, so
	//		so that we can hold-off saving the headers to the file until we've 
	//		checked them to see if we want them or not.

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_nullify_attachment: Starting nullification of file '%s' from mailpack '%s'",FL, attachmentname, mpackname);
	BS_init();

	if (strcmp( mpackname, "-") == 0)
		inputfile = stdin;
	else
		inputfile = fopen( mpackname, "r" );

	if (inputfile == NULL)
	{
		LOGGER_log("%s:%d:AM_nullify_attachment: Unable to open mailpack '%s' for reading (%s)", FL, mpackname, strerror(errno));
		return 1;
	}

	snprintf( tmpfname, sizeof(tmpfname), "%s.tmp", mpackname );
	
	if (strcmp( mpackname, "-") == 0)
		outputfile = stdout;
	else
		outputfile = fopen( tmpfname, "w" );

	if (outputfile == NULL)
	{
		if (inputfile != NULL) fclose(inputfile);
		LOGGER_log("%s:%d:AM_nullify_attachment: Unable to open temporary file '%s' for writing (%s)", FL, tmpfname, strerror(errno));
		return 1;
	}

	FFGET_setstream( &f, inputfile );

	MIMEH_set_headers_nosave();
	MIMEH_set_headers_save_original(1);


	// Determine which mode of matching we'll be using
	if (strchr(attachmentname, '/') == NULL) match_mode = AM_NULLIFY_MATCH_MODE_FILENAME;
	else match_mode = AM_NULLIFY_MATCH_MODE_CONTENT_TYPE;

	// Compile our Regular-expression for the filename.
	result = regcomp( &preg, attachmentname, REG_EXTENDED|REG_ICASE|REG_NOSUB );
	if (result != 0)
	{
		LOGGER_log("%s:%d:AM_nullify_attachment: Unable to compile regular expression '%s'", FL, attachmentname );
		return 0;
	}

	result=AM_nullify_attachment_recurse( &hinfo, &f, outputfile, &preg, match_mode, 1 );

	MIMEH_set_headers_save_original(0);	

	snprintf(oldfname,sizeof(oldfname),"%s.old", mpackname);
	
	if (strcmp( mpackname, "-") != 0)
	{
		result = rename( mpackname, oldfname );

		if ( result != 0 ) 
		{
			LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Unable to rename original mailpack '%s' to '%s' (%s)", FL, mpackname, oldfname, strerror(errno));
			return 1;
		}

		result = rename( tmpfname, mpackname );
		if (result != 0)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Unable to rename temporary mailpack '%s' to '%s' (%s)", FL, tmpfname, mpackname, strerror(errno));
			return 1;
		}	

		result = unlink( oldfname );
		if ( result != 0)
		{
			LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Unable to unlink/remove '%s' (%s)", FL, oldfname, strerror(errno));
			return 1;
		}
	}
	
	// Clean up our boundary stack 
	BS_clear();

	return result;
}


/*------------------------------------------------------------------------
Procedure:     AM_insert_Xheader ID:1
Purpose:       Adds an X-header to the first set of headers in an email mailpack.
Input:         char *fname: mailpack name
char *xheader: header string
Output:
Errors:
------------------------------------------------------------------------*/
int AM_insert_Xheader( char *fname, char *xheader)
{

	/* Tempfile tmpfile() fix contributed by David DeMaagd - 29/01/2001 */

	char line[ AM_1K_BUFFER_SIZE +1];
	char tpn[ AM_1K_BUFFER_SIZE +1];
	int header_written = 0;
	int result = 0;
	struct stat st;
	FFGET_FILE f;
	FILE *fi;
	FILE *fo;

	// Sanity checks
	//	
	//	. check that the x-header string is valid
	//	. check the x-header to ensure there's no \r or \n's
	//

	if (strlen(xheader) < 1) return 0;

	if (1)
	{
		char *p;

		p = strchr(xheader,'\n');
		if (p) *p = '\0';

		p = strchr(xheader,'\r');
		if (p) *p = '\0';
	}

	// Create a temporary file name, but, so that we dont
	// overwrite an existing file, we must check the name using
	// stat() to see if we get a non-zero response.

	snprintf(tpn, AM_1K_BUFFER_SIZE, "%s",fname);

	do {
		if (strlen(tpn) < (sizeof(tpn) -2))
		{
			strcat(tpn,".");
		}
		else
		{
			LOGGER_log("ERROR:%s:%d: Temporary file name string buffer out of space!\n",__FILE__,__LINE__);
			return 1;
		}
		//		LOGGER_log("DEBUG:%s:%d: Testing filename %s\n",__FILE__,__LINE__,tpn);
	}
	while (0 == stat(tpn,&st));


	// Attempt to open up the temporary file in write mode.
	// If the open fails, then this whole operation fails.
	// Ensure there's lots of good logging here so that when something
	// does go wrong, at least people wont be left in the dark as to
	// what went on.
	//
	// Same applies for opening the source file
	//

	fo = fopen(tpn,"w");
	if (!fo)
	{
		LOGGER_log("Error: Cannot open temporary file for writing");
		return 1;
	}


	fi = fopen(fname,"r");
	if (!fi)
	{
		LOGGER_log("Error: Cannot open Mailpack file for reading, %s",fname);
		return 2;
	}

	//setup our FFGET stream

	FFGET_setstream(&f, fi);
	f.trueblank = 0;


	// Load and go through every line in the email file, testing it for "trueblank"
	// status.  Once a trueblank is found, we write our header, as this marks the
	// end of the headers (the trueblank).  Beyond that, we keep on reading and
	// writing because we obviously want to keep the entire email content.

	while (FFGET_fgets(line, AM_1K_BUFFER_SIZE ,&f))
	{

		// If we've found that the line we just read is a /true/ blank (as apposed to a carefully
		// crafted line length to catch out our fgets(), /and/ if we've not written in a header
		// then we will insert the header.

		if ((0 != f.trueblank)&&(0 == header_written))
		{
			fprintf(fo,"%s\n",xheader);
			header_written = 1;
		}

		// In all cases, we must write out the line we read in, even if it was the true-blank line
		// Otherwise we'll end up losing a the header seperation, and all of a sudden your emails
		// do not make any more sence to email programs.

		fprintf(fo,"%s",line);
	}

	// Close our input files


	fclose(fo);
	fclose(fi);

	// We now can rename the temporary file to the new file name.
	// NOTE - previously we removed then renamed, however, on checking
	// with the manpages, 'man 2 rename' it seems it's not required, so
	// rather than waste CPU cycles, we'll just go by the book, and use
	// only rename.  There are a couple of situations where rename can/will
	// fail, obviously such as if the original file is marked read-only, or
	// we do not have write permissions to it.


	if (rename(tpn,fname) == -1)
	{
		result = 1;
		LOGGER_log("ERROR while attempting to rename '%s' to '%s' (%s) ", tpn, fname, strerror(errno));
	}

	return result;
}




/*------------------------------------------------------------------------
Procedure:     AM_header_adjust ID:1
Purpose:       Adjusts an -existing- header 
Input:         
char *xheader: header string
Output:
Errors:
------------------------------------------------------------------------*/
int AM_alter_header( char *filename, char *header, char *change, int change_mode )
{

	char line[ AM_1K_BUFFER_SIZE +1];
	char tpn[ AM_1K_BUFFER_SIZE +1];
	int main_headers_complete = 0;
	int header_written = 0;
	int result = 0;
	struct stat st;
	FFGET_FILE f;
	FILE *fi;
	FILE *fo;

	// Create a temporary file name, but, so that we dont
	// overwrite an existing file, we must check the name using
	// stat() to see if we get a non-zero response.

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_alter_header:DEBUG: Start [ %s, %s, %s, %d ]", FL, filename, header, change, change_mode );

	snprintf(tpn, AM_1K_BUFFER_SIZE, "%s",filename);

	do {
		if (strlen(tpn) < (sizeof(tpn) -2))
		{
			strcat(tpn,".");
		}
		else
		{
			LOGGER_log("%s:%d:AM_header_adjust:ERROR: Temporary file name string buffer out of space!\n",FL);
			return 1;
		}
	}
	while (0 == stat(tpn,&st));

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_alter_header:DEBUG: Temporary filename = %s", FL, tpn );

	// Attempt to open up the temporary file in write mode.
	// If the open fails, then this whole operation fails.
	// Ensure there's lots of good logging here so that when something
	// does go wrong, at least people wont be left in the dark as to
	// what went on.
	//
	// Same applies for opening the source file
	//

	fo = fopen(tpn,"w");
	if (!fo)
	{
		LOGGER_log("%s:%d:AM_header_adjust:ERROR: Cannot open temporary file '%s' for writing",FL,tpn, strerror(errno));
		return 1;
	}


	fi = fopen(filename,"r");
	if (!fi)
	{
		LOGGER_log("%s:%d:AM_header_adjust:ERROR: Cannot open Mailpack file for reading, %s",FL,filename,strerror(errno));
		return 2;
	}

	//setup our FFGET stream

	FFGET_setstream(&f, fi);

	// Convert the header we're looking for into lower case so that 
	// we make it esaier to search for

	PLD_strlower( header );

	// Load and go through every line in the email file, testing it for "trueblank"
	// status.  Once a trueblank is found, we write our header, as this marks the
	// end of the headers (the trueblank).  Beyond that, we keep on reading and
	// writing because we obviously want to keep the entire email content.

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust:DEBUG: Starting seek through file. header_written = %d", FL, header_written );
	while (FFGET_fgets(line, AM_1K_BUFFER_SIZE ,&f))
	{
		int line_written = 0;

		// If we find a true-blank line, then we note that the main-headers section is now
		//	complete - We do not process any headers after this, as the rest of the data
		//	is the email body/content [ which we do not wish to alter ].

		if ((main_headers_complete == 0)&&(f.trueblank == 1)) main_headers_complete = 1;


		// If we're still processing the main headers, and we've not written/altered our
		//	particular header which we wish to alter, then proceed to check this newly
		//	read line to see if it is the one we wish to alter,
		//
		//	Irrespective of if we alter this line or not, it will be written at the bottom
		//	of this while-loop.  NOTE - we don't write the line within the header-alteration
		//	"if" block, as this would only make things more complicated on exit ( ie, did we
		//	write the line, or are we okay... more logic tests, more chance of a bungle ).

		if ((main_headers_complete == 0)&&(header_written == 0))
		{
			char *p;
			char low_line[AM_1K_BUFFER_SIZE +1];

			if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust: line=%s",FL, line);

			// Make a copy of the header line, and convert it to lowercase so that we
			//	can make a string comparison

			snprintf( low_line, AM_1K_BUFFER_SIZE, "%s", line );
			PLD_strlower( low_line );

			p = strstr(low_line, header);
			if (p != NULL)
			{
				char *first_colon_position;

				// Because it's quite possible to put a heaer line in which replicates itself, such as...
				//	Subject:  the Subject: is foo
				//	we need to first check that the header position we got from strstr is /BEFORE/ the first
				//	colon in the line.

				first_colon_position = strchr(low_line, ':');
				if ( p < first_colon_position )
				{
					char *header_start, *header_end;
					char startc, finishc;

					// Convert the position in the low_line string over to that
					//	of the normal 'line' string.

					p = line +(p - low_line);

					if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust:DEBUG: Located header line",FL);
					header_start = strchr(p,':');
					if ( header_start == NULL )
					{
						LOGGER_log("%s:%d:AM_header_adjust:WARNING: Could not locate terminating ':' character on header name (%s)",FL,p);
						continue;
					}
					header_start++;
					startc = *header_start;

					header_end = strpbrk( header_start, "\r\n;" );
					if ( header_end == NULL )
					{
						LOGGER_log("%s:%d:AM_header_adjust:WARNING: Could not locate end of header value (%s)",FL,header_start);
						continue;
					}
					finishc = *header_end;

					// We now have the start and end points of the header string, we now place in the additional 
					// data ( or entirely new data ) based on what the mode is

					switch ( change_mode ) {
						case AM_HEADER_ADJUST_MODE_PREFIX:
							*header_start = '\0';
							fprintf(fo,"%s %s%c%s", line, change, startc, header_start+1);
							header_written = 1;
							line_written = 1;
							if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust:DEBUG: Prefix mode output written",FL);
							break;
						case AM_HEADER_ADJUST_MODE_SUFFIX:
							*header_end = '\0';
							fprintf(fo,"%s %s%c%s", line, change, finishc, header_end+1 );
							header_written = 1;
							line_written = 1;
							if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust:DEBUG: Suffix mode output written",FL);
							break;
						case AM_HEADER_ADJUST_MODE_REPLACE:
							*header_start = '\0';
							fprintf(fo,"%s %s%s", line, change, header_end );
							header_written = 1;
							line_written = 1;
							if (AM_DNORMAL) LOGGER_log("%s:%d:AM_header_adjust:DEBUG: Replace mode output written",FL);
							break;

						default:
							LOGGER_log("%s:%d:AM_header_adjust:ERROR: Unknown header adjustment mode '%d'",FL, change_mode );
					} // Switch (change_mode)
				} // If p < first_colon_position

			} // If p != NULL, ie, we found a matching string for the header			
		} // If we're still in the headers and we've still not found a matching header 

		// In all cases, we must write out the line we read in, even if it was the true-blank line
		// Otherwise we'll end up losing a the header seperation, and all of a sudden your emails
		// do not make any more sence to email programs.

		if (line_written == 0) fprintf(fo,"%s",line);
	} // While we still have more lines in the file.

	// Close our input files


	fclose(fo);
	fclose(fi);

	// We now can rename the temporary file to the new file name.
	// NOTE - previously we removed then renamed, however, on checking
	// with the manpages, 'man 2 rename' it seems it's not required, so
	// rather than waste CPU cycles, we'll just go by the book, and use
	// only rename.  There are a couple of situations where rename can/will
	// fail, obviously such as if the original file is marked read-only, or
	// we do not have write permissions to it.


	if (rename(tpn,filename) == -1)
	{
		result = 1;
		LOGGER_log("%s:%d:AM_header_adjust:ERROR: while attempting to rename '%s' to '%s' (%s) ", FL, tpn, filename, strerror(errno));
	}

	return result;
}






/*-----------------------------------------------------------------\
  Function Name	: AM_attachment_replace_header_filter
  Returns Type	: int
  ----Parameter List
  1. struct MIMEH_header_info *hinfo, 
  2.  FILE *outputfile, 
  3.  char *new_attachment_name, 
  4.  char *headers , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_attachment_replace_header_filter( struct MIMEH_header_info *hinfo, char *new_attachment_name, char **headers )
{
	struct PLD_strreplace replace;

	// Because we're dealing with the primary headers, we have to be careful
	//		about how we search-replace the data within these headers, lest we
	//		break something we're not supposed to :-(

	if (AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_header_filter:DEBUG: Start.",FL);
	replace.source = *headers;
	replace.searchfor = hinfo->filename;
	replace.replacewith = new_attachment_name;
	replace.replacenumber = 1; // only 1 replacement thanks!
	replace.insensitive = 1; // Emails come in all shapes and sizes - do not assume a case.
	replace.postexist = NULL;

	replace.preexist = "content-type:";
	*headers = PLD_strreplace_general( &replace );

	replace.source = *headers;
	replace.preexist = "content-disposition:";
	*headers = PLD_strreplace_general( &replace );

	// Because we can currently only encode our new attachment using BASE64
	//		we need to make sure that the content-transfer-encoding field is
	//		appropriately set.
	if (hinfo->content_transfer_encoding != _CTRANS_ENCODING_B64)
	{
		if (strlen(hinfo->content_transfer_encoding_string) < 1)
		{
			char CTE_string[256];

			snprintf(CTE_string, sizeof(CTE_string),"Content-Transfer-Encoding: base64\nContent-Type:");
			replace.preexist=NULL;
			replace.source=*headers;
			replace.searchfor="content-type:";
			replace.replacewith=CTE_string;
			*headers = PLD_strreplace_general( &replace );
		} else {
			replace.preexist = "content-transfer-encoding:";
			replace.source = *headers;
			replace.searchfor = hinfo->content_transfer_encoding_string;
			replace.replacewith = "base64";
			*headers = PLD_strreplace_general( &replace );
		}
	}

	if (AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_header_filter:DEBUG: End.",FL);
	
	return 0;
}


/*-----------------------------------------------------------------\
 Function Name	: AM_attachment_replace_write_data
 Returns Type	: int
 	----Parameter List
	1. struct MIMEH_header_info *hinfo, 
	2.  char *new_attachment_name, 
	3.  FILE *outputfile , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_attachment_replace_write_data( char *new_attachment_name, FILE *outputfile, char *delimeter )
{
	int result = 0;
	FILE *newatt;

	newatt = fopen( new_attachment_name, "r" );
	if (newatt == NULL)
	{
		LOGGER_log("%s:%d:AM_attachment_replace_write_data:ERROR: Could not open '%s' for reading to insert into mailpack (%s)"\
				,FL, new_attachment_name, strerror(errno));
		return 1;
	}
	if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_write_data:DEBUG: Writing out new attachment data",FL);
	AM_base64_encodef( newatt, outputfile );
	fprintf( outputfile, "%s%s", delimeter, delimeter );
	if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_write_data:DEBUG: done.",FL);

	return result;
}

/*-----------------------------------------------------------------\
  Function Name	: AM_nullify_attachment_recurse
  Returns Type	: int
  ----Parameter List
  1. struct MIMEH_header_info *hinf, 
  2.  FFGET_FILE *f, 
  3.  FILE *outputfile, 
  4.  pregex_t *preg , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
int AM_attachment_replace_recurse( struct MIMEH_header_info *hinfo, FFGET_FILE *f, FILE *outputfile, regex_t *preg, char *new_attachment_name, int iteration )
{
	int result = 0;
	int boundary_exists=0;

	if (AM_DNORMAL) LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Starting: iteration=%d",FL, iteration );
	while (1)
	{
		int regresult=0;
		int attachment_data_written=0;
		char *header_ptr=NULL;
		char *original_ptr=NULL;
		char buffer[1024];
		char CR[]="\n";
		char CRLF[]="\n\r";
		char *delimeter;


		MIMEH_set_doubleCR_save(0);
		result = MIMEH_headers_get( hinfo, f );
		MIMEH_set_doubleCR_save(1);
		if (result != 0)
		{ 
			break;
		}

		/* If we're not supposed to be altering Signed EMAILS, then don't
			start altering them now.  Exit with a 0 return
			*/
		if ((hinfo->content_type == _CTYPE_MULTIPART_SIGNED)&&(glb.alter_signed==0))
		{
			return 0;
		}


		if (AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Headers read, now processing", FL );

		original_ptr = MIMEH_get_headers_original_ptr();
		header_ptr = MIMEH_get_headers_ptr();

		if (AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Headers=\n%s\n", FL, original_ptr );

		if (original_ptr == NULL)
		{
			LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Original headers came back NULL",FL);
			return 1;
		}

		if (header_ptr == NULL)
		{
			LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Header ptr (for processing) came back NULL",FL);
			return 1;
		}

		// Because we'll be later on adding new lines into the mailpack, we need to know what
		//		the currently used line-delimeter is, this way we don't end up with a mailpack which
		//		has multiple personalities and thus potentially confusing the MUA.
		if (strstr(original_ptr,CRLF)) delimeter=CRLF; else delimeter=CR;

		result = MIMEH_headers_process( hinfo, header_ptr );
		if (result != 0)
		{
			LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: While processing headers for mailpack", FL );
			break;
		}

		// Check to see if we have a new boundary
		//		Sometimes a new segment in the email reveals a nested MIME encoded
		//		data within.  This is typically indicated by Content-Type: rfc822
		//
		//	Should we have a new boundary, we add it to the top of the current boundary stack
		//		later, when this MIME segment finishes (and the previous boundary comes back
		//		into appearance, the old boundary will be popped off the stack automatically
		//
		if (((hinfo->content_type == _CTYPE_RFC822)\
					||(hinfo->content_type >= _CTYPE_MULTIPART_START && hinfo->content_type <= _CTYPE_MULTIPART_END))\
				&& (hinfo->boundary_located > 0))
		{
			if (AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: pushing BS='%s'",FL, hinfo->boundary );
			BS_push( hinfo->boundary );
			boundary_exists = 1;
		}

		// Now, determine if this block/segment is the one which contains our file which we must 'nullify'
		regresult=1;
		if (strlen(hinfo->filename) > 0)
		{
			regresult = regexec( preg, hinfo->filename, 0, NULL, 0 );
			if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: FileName Regex match = %d [filename = '%s']"\
					,FL,regresult,hinfo->filename);
		} 


		// If we're on our first pass, or we've not found the section/block with the attachment
		//	then write the headers out.
		//
		//	 A bit of an issue which comes up is, what to do if the entire email is the attachment
		//	ie, it's just a something which was 'right-click->email'd.
		//
		//	Until I come up with a better solution, I'll still write the headers, but I'll then 
		//		eliminate all the content [until the next boundary, assuming a boundary exists]
		//	
		if (regresult > 0)
		{
			fwrite( original_ptr, sizeof(char), strlen(original_ptr), outputfile );
			if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Wrote original headers:\n%s",FL,original_ptr);
		} 
		else 
		{
			// If we did have a filename "HIT" in these headers
			//
			// We will need to suitably alter, or write new headers
			//	in order to reflect the new filenames that we'll be
			//	using for the replaced attachment

			char *new_attachment_filename;

			// Check for delimeters in the new attachment name, and make
			//	the new_attachment_filename just the last segment of that.

			new_attachment_filename=strrchr(new_attachment_name,'/');

			// If looking for the forward-slash failed, try looking for the backslash
			if (new_attachment_filename == NULL) new_attachment_filename=strrchr(new_attachment_name,'\\');

			// If both forward and backslash searches failed, then just let the new attachment filename
			//		be the same as the one passed to us via the parameters
			if (new_attachment_filename == NULL)
			{
				new_attachment_filename = new_attachment_name;
			} else {

				// If we did get a hit - then we increment by one character so that
				//	we don't have the directory seperator in our way.
				new_attachment_filename++;
			}

			// When it comes to creating the new headers, we have to check to see if we're
			//		in a suitable situation to either (a) entirely replace the headers with our own
			//		or (b) modify existing headers.
			//
			// Existing header modification is required when we're dealing with headers that
			//		make up the start of the whole MIME package, this is because there's a lot
			//		more information contained in them than just the file-attachment information
			//		Thus, for this situation, we'll use a header-modification function.
			//
			// If the headers are not the primary ones, we can just remove the existing ones
			//		and write in our own as generated by the content-type, disposition and encoding

			if (iteration > 1)
			{
				// If we're dealing with a non-primary header situation, just replace the old headers
				//		with our new fabricated ones

				if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Writing clean headers",FL);

				fprintf( outputfile, "Content-Type: %s;name=\"%s\"%sContent-Transfer-Encoding: base64%sContent-Disposition: attachment;filename=\"%s\"%s%s"\
						, hinfo->content_type_string\
						, new_attachment_filename, delimeter, delimeter\
						, new_attachment_filename, delimeter, delimeter\
						);
			} else {
				
				// If we're dealing with a primary-header situation, we have to carefully
				//		search-replace the old filenames with our own.  This has to be done
				//		within the strict confines between Content-Type:...;\n and/or Content-Disposition

				char *duplicate_headers;

				if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Primary header attachment replacement",FL,new_attachment_filename);
				duplicate_headers = strdup( original_ptr );
				if (duplicate_headers == NULL)
				{
					LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Could not allocate memory to hold temporary copy of headers",FL);
					return 1;
				}

				//if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Seeking and replacing content type, disposition headers in main headers",FL);
				AM_attachment_replace_header_filter( hinfo, new_attachment_filename, &duplicate_headers );

				if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Writing recycled headers\n%s",FL, duplicate_headers);
				fprintf( outputfile, "%s", duplicate_headers);

				if (duplicate_headers != NULL) free( duplicate_headers );
			}
		}

		// Clean up the memory allocation
		result = MIMEH_headers_cleanup();
		if (result != 0)
		{
			LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: while attempting to clean up headers memory allocation", FL );
			break;
		}

		// Now, if we have a Multipart/Mixed attachment, then, alas, we need to recurse into
		//		it and see if it contains anything interesting for us to seek out.
		if ((regresult != 0)&&((hinfo->content_type == _CTYPE_RFC822)))
		{
			result=AM_attachment_replace_recurse( hinfo, f, outputfile, preg, new_attachment_name, 1 );
		}


		// Now, we shall go through and read the email until we happen across another boundary.
		//		or we reach the end of the file.
		//
		//
		do {
			int buflen;

			FFGET_fgets(buffer, sizeof(buffer), f);
			if (FFGET_feof(f) == 0)
			{
				buflen = strlen(buffer);

				// Once we have a boundary match, it's a end-of-line situation for this DO
				//		loop, as we will 'break' out of it once the attachment has been written
				//		and the trailing 'boundary' written as well.
				if ((BS_cmp(buffer, buflen) == 1))
				{
					if ((AM_DNORMAL)&&(boundary_exists==1))\
						LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: Boundary hit on line %d\nBoundary Exists=%d\nBoundary line=%s"\
								,FL,f->linecount,boundary_exists, buffer);

					// If we have a match for the attachment replacement, then this is where
					//		all the work we do going to find this place comes to a head, as here
					//		we finally insert the encoded attachment into the mailpack we're
					//		creating anew from the existing one.
					if (regresult == 0)
					{
						AM_attachment_replace_write_data( new_attachment_name, outputfile, delimeter );
						attachment_data_written=1;
					}

					fwrite( buffer, sizeof(char), buflen, outputfile );
					break;
				} // end of boundary-detect


				// If we didn't have a match on the filename with this particular MIME segment
				//		we simply just write out all the lines we are reading in.  This means
				//		that the data we're saving should be identical to that being read from the
				//		original file.
				if (regresult != 0)
				{
					fwrite( buffer, sizeof(char), buflen, outputfile );
				}

			} else {

				// If we hit the EOF, then we need to check to see if
				//		we were supposed to write any attachment data, and, if so
				//		did we actually get to write it out?
				//
				// If we haven't written it, then we must write it now.  
				//
				// This situation can occur when there's no trailing boundary
				//		line at the end of the last MIME segment of the mailpack.

				if ((regresult == 0)&&(attachment_data_written == 0))
				{
					AM_attachment_replace_write_data( new_attachment_name, outputfile, delimeter );
				}

				break; // break if FEOF occurs
			}

		} while (1);


		if (FFGET_feof(f)) break;


		iteration++;

	} // Infinite while(1)

	if(AM_DNORMAL)LOGGER_log("%s:%d:AM_attachment_replace_recurse:DEBUG: End of function.",FL);
	return 0;
}


/*-----------------------------------------------------------------\
 Function Name	: AM_attachment_replace
 Returns Type	: int
 	----Parameter List
	1. char *mpackname,  mailpack which we're going to replace the file in
	2.  char *attachmentname, name of the attachment we're looking for in the mailpack [ to replace ]. This is a regular-expression syntax
	3.  char *new_attachment_name , full path of the file which we're going to use in place of *attachmentname
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
	Replaces an attachment located in a mailpack with a new file. The
	new file is encoded [currently only] in base64.  If the new attachment
	name / path contains a path component ie, /usr/local/some-file, then
	only the last segment of the full path will be used in the headers
	[ ie, 'some-file' ]
	
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_attachment_replace( char *mpackname, char *attachmentname, char *new_attachment_name )
{
	struct MIMEH_header_info hinfo;
	regex_t preg;
	int result = 0;
	char tmpfname[256];
	char oldfname[256];
	FILE *inputfile;
	FILE *outputfile;
	FFGET_FILE f;

	// Nullifying an attachment can sometimes be a little bit tricky, we have to
	//		dig down into the nesting of the MIME email and keep tabs on our
	//		boundaries (via a Boundary-stack).  This all requires abot as much work
	//		as ripMIME just to get rid of one attachment.
	//
	// Additionally - we require some special functionality from MIME_headers, so
	//		so that we can hold-off saving the headers to the file until we've 
	//		checked them to see if we want them or not.

	BS_init();

	inputfile = fopen( mpackname, "r" );
	if (inputfile == NULL)
	{
		LOGGER_log("%s:%d:AM_replace_attachment: Unable to open mailpack '%s' for reading (%s)", FL, mpackname, strerror(errno));
		return 1;
	}

	snprintf( tmpfname, sizeof(tmpfname), "%s.tmp", mpackname );
	outputfile = fopen( tmpfname, "w" );
	if (outputfile == NULL)
	{
		if (inputfile != NULL) fclose(inputfile);
		LOGGER_log("%s:%d:AM_replace_attachment: Unable to open temporary file '%s' for writing (%s)", FL, tmpfname, strerror(errno));
		return 1;
	}

	FFGET_setstream( &f, inputfile );
	MIMEH_set_headers_nosave();
	MIMEH_set_headers_save_original(1);


	// Compile our Regular-expression for the filename.
	result = regcomp( &preg, attachmentname, REG_EXTENDED|REG_ICASE|REG_NOSUB );
	if (result != 0)
	{
		LOGGER_log("%s:%d:AM_replace_attachment: Unable to compile regular expression '%s'", FL, attachmentname );
		return 0;
	}

	result=AM_attachment_replace_recurse( &hinfo, &f, outputfile, &preg, new_attachment_name, 1 );

	MIMEH_set_headers_save_original(0);	

	snprintf(oldfname,sizeof(oldfname),"%s.old", mpackname);
	result = rename( mpackname, oldfname );
	if ( result != 0 ) 
	{
		LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Unable to rename original mailpack '%s' to '%s' (%s)", FL, mpackname, oldfname, strerror(errno));
		return 1;
	}

	result = rename( tmpfname, mpackname );
	if (result != 0)
	{
		LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Unable to rename temporary mailpack '%s' to '%s' (%s)", FL, tmpfname, mpackname, strerror(errno));
		return 1;
	}	

	result = unlink( oldfname );
	if ( result != 0)
	{
		LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Unable to unlink/remove '%s' (%s)", FL, oldfname, strerror(errno));
		return 1;
	}

	// Clear the boundary stack
	BS_clear();

	return result;
}

//-----------------------------------END----


