/*------------------------------------------------------------------------
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 "strstack.h"
#include "boundary-stack.h"
#include "MIME_headers.h"
#include "filename-filters.h"
#include "mime_alter.h"
#include "qpe.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)

#define DAM if (glb.debug > 0)
#define VAM if (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 force_for_bad_html;	/** Force insertion of HTML disclaimer even when we can't find the end **/
	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 ? */
	int header_long_search;	/* do we search through email bodies for more headers, like qmail bounces */

	char *disclaimer_plain;
	int disclaimer_plain_type;

	char *disclaimer_HTML;
	int disclaimer_HTML_type;

#ifdef ALTERMIME_PRETEXT
	char *pretext_plain;
	int pretext_plain_type;
	char *pretext_HTML;
	int pretext_HTML_type;

	int pretext_insert;
#endif

	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.header_long_search=1;	/* Search into email bodies for more attachments */

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

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

#ifdef ALTERMIME_PRETEXT
	glb.pretext_plain=NULL;
	glb.pretext_plain_type=AM_PRETEXT_TYPE_NONE;
	glb.pretext_HTML=NULL;
	glb.pretext_HTML_type=AM_PRETEXT_TYPE_NONE;
	glb.pretext_insert = 0;
#endif

	glb.headerbuffermax=0;

	return 0;
}



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

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

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

	return 0;
}


/*-----------------------------------------------------------------\
 Function Name	: AM_set_force_for_bad_html
 Returns Type	: int
 	----Parameter List
	1. int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_set_force_for_bad_html( int level )
{
	glb.force_for_bad_html = level;
	return glb.force_for_bad_html;
}


/*-----------------------------------------------------------------\
 Function Name	: AM_set_pretext_insert
 Returns Type	: int
 	----Parameter List
	1. int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
#ifdef ALTERMIME_PRETEXT
int AM_set_pretext_insert( int level )
{
	glb.pretext_insert = level;

	return glb.pretext_insert;
}
#endif


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

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

\------------------------------------------------------------------*/
int AM_set_debug( int level )
{
	glb.debug = level;
	AM_set_verbose( level );
	MIMEH_set_debug(level);

	return level;

}


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

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

\------------------------------------------------------------------*/
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;
}


/*-----------------------------------------------------------------\
  Function Name	: *AM_hbuffer_add
  Returns Type	: char
  ----Parameter List
  1. char *headerline, 
  2.  FILE *f , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

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

\------------------------------------------------------------------*/
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;
}




/*-----------------------------------------------------------------\
 Function Name	: AM_set_header_long_search
 Returns Type	: int
 	----Parameter List
	1. int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_set_header_long_search( int level )
{
	glb.header_long_search = level;
	return glb.header_long_search;
}

/*------------------------------------------------------------------------
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;
}


/*-----------------------------------------------------------------\
 Function Name	: *AM_set_pretext_plain
 Returns Type	: char
 	----Parameter List
	1. char *filename, 
	2.  int pretext_type , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
#ifdef ALTERMIME_PRETEXT
int AM_set_pretext_plain( char *filename, int pretext_type )
{

	glb.pretext_plain = strdup( filename );
	glb.pretext_plain_type = pretext_type;

	return 0;
}
#endif




/*-----------------------------------------------------------------\
 Function Name	: *AM_set_pretext_HTML
 Returns Type	: char
 	----Parameter List
	1. char *filename, 
	2.  int pretext_type , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
#ifdef ALTERMIME_PRETEXT
int AM_set_pretext_HTML( char *filename, int pretext_type )
{
	glb.pretext_HTML = strdup( filename );
	glb.pretext_HTML_type = pretext_type;

	return 0;
}
#endif

/*------------------------------------------------------------------------
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;
}




/*-----------------------------------------------------------------\
 Function Name	: AM_ntorn
 Returns Type	: int
 	----Parameter List
	1. char *in, 
	2.  FILE *out , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int AM_ntorn( char *in, FILE *out )
{
	char *p = in;
	char lastchar=' ';

	while (*p) 
	{
		if ((*p == '\n')&&(lastchar != '\r')) fprintf(out, "\r");
		fprintf(out, "%c", *p);
		lastchar = *p;
		p++;
	}

	return 0;
}




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

	DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG: Starting to read headers", FL );

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

	memset( &hinfo, '\0', sizeof(struct MIMEH_header_info));

	hinfo.uudec_name[0] = '\0';

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

	//	MIMEH_set_headers_save( newf ); // 20040309-2243:PLD
	MIMEH_set_doubleCR_save(0);
	MIMEH_set_header_longsearch(0); /** Turn off qmail searching, is not applicable here **/
	MIMEH_set_headers_original_save_to_file( newf );
	MIMEH_parse_headers( f, &hinfo );

	MIMEH_set_headers_original_save_to_file( NULL );
	MIMEH_set_doubleCR_save(1);
	MIMEH_set_headers_nosave();

	/* Copy over our information */
	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 (strlen(hinfo.filename) > 0) dd->isfile = 1;
	if (strlen(hinfo.name) > 0) dd->isfile = 1;

	/**
	 ** list here any content transfer encodings you
	 ** don't want to have disclaimers inserted into
	 **/
	switch (hinfo.content_transfer_encoding) {
		case _CTRANS_ENCODING_B64:
		case _CTRANS_ENCODING_UUENCODE:
		case _CTRANS_ENCODING_BINARY:
			dd->isfile = 1;
			break;
	}

	switch (hinfo.content_type) {
		case _CTYPE_MULTIPART_REPORT:
		case _CTYPE_RFC822:
			dd->isfile = 1;
			break;
	}

	/** 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)
	{
		DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG: Email is signed, return SIGNED_EMAIL",FL);
		return AM_RETURN_SIGNED_EMAIL;
	}

	DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG: 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);
			DAM 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;
}





int AM_disclaimer_html_perform_insertion( struct AM_disclaimer_details *dd, FFGET_FILE *f, FILE *newf )
{
	  if (dd->content_encoding == _CTRANS_ENCODING_QP) {
		  char *qp_data;
		  size_t qp_data_size;
		  char *data_to_use;

		  if (dd->disclaimer_text_HTML == NULL) data_to_use = dd->disclaimer_text_plain;
		  else data_to_use = dd->disclaimer_text_HTML;

			qp_data_size = strlen(data_to_use) *3 +1;
			qp_data = malloc( qp_data_size *sizeof(char));
			if (qp_data == NULL) {
				LOGGER_log("%s:%d:AM_disclaimer_html_perform_insertion:DEBUG: Error trying to allocate %d bytes of memory for QP encoded disclaimer",FL, qp_data_size);
				return -1;
			}
			qp_encode( qp_data, qp_data_size, data_to_use, strlen(data_to_use));
			DAM LOGGER_log("%s:%d:AM_disclaimer_html_perform_insertion:DEBUG: Inserting QP encoded disclaimer",FL);
			if (dd->disclaimer_text_HTML == NULL) {
				fprintf(newf,"<br><pre>=\r\n");
				AM_ntorn(qp_data, newf);
				fprintf(newf,"</pre><br>=\r\n");
			} else {
			  fprintf(newf,"<br>=\r\n");
				AM_ntorn(qp_data, newf);
			  fprintf(newf,"<br>=\r\n");
			}

			/** cleanup **/
			if (qp_data) free(qp_data);

		  /** Quoted printable email segment, so we have to encode everything **/

	  } else {

		  if (dd->disclaimer_text_HTML == NULL)
		  {
			  fprintf(newf,"<br><pre>\r\n");
			  AM_ntorn(dd->disclaimer_text_plain, newf);
			  fprintf(newf,"</pre><br>\r\n");
		  }
		  else
		  {
			  fprintf(newf,"<br>\r\n");
			  AM_ntorn(dd->disclaimer_text_HTML, newf);
			  fprintf(newf,"<br>\r\n");
		  }
	  } /** quotedprintable / non encoding selector **/

	  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 boundary[ AM_1K_BUFFER_SIZE +1];
	int boundary_length = 0;
	char line[ AM_1K_BUFFER_SIZE +1];
	char lline[ AM_1K_BUFFER_SIZE +1];
	char *prebody, *tmpbody;

	DAM LOGGER_log("%s:%d:AM_add_disclaimer_insert_html:DEBUG: Starting to attempt to insert HTML disclaimer",FL);

	if (dd->boundary_found == 1)
	{
		snprintf(boundary, sizeof(boundary), "--%s", dd->boundary);
		boundary_length = strlen(boundary);
	}

	while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, f))
	{
		/** 20041019-1135:PLD: Applied diff as contributed by Tim (datalore)
		  **
      ** If we reached the end of the boundary, check if force html insertion is
     ** enabled. If so, force the html disclaimer into the message.
	  **/
     if(dd->boundary_found == 1 && strncmp(boundary, line, boundary_length) == 0)
     {
        DAM LOGGER_log("%s:%d:AM_add_disclaimer_insert_html: End of boundary reached before html disclamer was added...",FL);
        if (glb.force_for_bad_html == 1)
        {
           DAM LOGGER_log("%s:%d:Forcing insertion of html disclaimer into non valid html body...",FL);

           dd->html_inserted = 1;

				AM_disclaimer_html_perform_insertion( dd, f, newf );

        }

        // write the boundary line
        fprintf(newf, "%s", line);

        // stop searching/adding/whatever
        break;
     }

     /** not at end of boundary, so search for body/html tag **/

	  /** End of patch as supplied by Tim (datalore) **/



		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)
		{
			DAM LOGGER_log("%s:%d:AM_add_disclaimer_insert_html:DEBUG: prebody = %s",FL,prebody);
			DAM LOGGER_log("%s:%d:AM_add_disclaimer_insert_html:DEBUG: Inserting html-body disclaimer",FL);

			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

			fprintf(newf,"%s\r\n",line);
			AM_disclaimer_html_perform_insertion( dd, f, newf );
			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 )
{

	/** If the input file wasn't stdin, then we will
	  ** need to close the input and output files and
	  ** rename the old to the new **/
	if (strcmp(mpackname,"-"))
	{
		// Close our files

		fclose(mp);
		fclose(newf);

		// rename the files

		rename(mpacktmp, mpackname);
	}

	return 0;
}

/*-----------------------------------------------------------------\
  Function Name	: AM_add_disclaimer_flush
  Returns Type	: int
  ----Parameter List
  1. FFGET_FILE *f, 
  2. FILE *newf, 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

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

\------------------------------------------------------------------*/
int AM_add_disclaimer_flush( FFGET_FILE *f, FILE *newf )
{
	char line[AM_1K_BUFFER_SIZE+1]="";
	
	if ( ! FFGET_feof( f ) )
	{
		DAM LOGGER_log("%s:%d:AM_add_disclaimer_flush:DEBUG: Appending remaining of file",FL);
		while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, f))
		{
			fprintf(newf,"%s",line);
		}
		DAM LOGGER_log("%s:%d:AM_add_disclaimer_flush:DEBUG: Done appending.",FL);
	}

	return 0;
}


/*-----------------------------------------------------------------\
  Function Name	: AM_read_to_boundary
  Returns Type	: int
  ----Parameter List
  1. FFGET_FILE *infile, Source data file
  2.  FILE *outf , File to send data to
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:
Copies data over from the current MIME segment from infile to outf.
Copying stops when a boundary line is found.
The boundary line is written to the output file.


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

\------------------------------------------------------------------*/
int AM_read_to_boundary( FFGET_FILE *infile, FILE *outf, char *buffer, size_t buffer_size )
{
	while (FFGET_fgets(buffer, buffer_size, infile))
	{
		fprintf(outf,"%s",buffer);
		if ( (BS_cmp(buffer,strlen(buffer))==1) ) 
		{
			DAM LOGGER_log("%s:%d:AM_read_to_boundary:DEBUG: Boundary hit while reading MIME segment, breaking out of while loop",FL);
			break;
		}
	}
	return 0;
}



/*-----------------------------------------------------------------\
  Function Name	: AM_load_disclaimers
  Returns Type	: int
  ----Parameter List
  1. struct AM_disclaimer_details *dd , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:
Load the disclaimers into the disclaimer structure.
Returns -1 if neither of the disclaimers could be 
loaded.

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

\------------------------------------------------------------------*/
int AM_load_disclaimers( struct AM_disclaimer_details *dd )
{
	int dud_html=0, dud_text=0;

	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("%s:%d:AM_load_disclaimers: Neither the Plain-text or HTML disclaimer were valid to insert, skipping disclaimer-insertion routine\n");
		return -1;
	}

	return 0;

}


/*-----------------------------------------------------------------\
  Function Name	: AM_add_disclaimer_plain_text
  Returns Type	: int
  ----Parameter List
  1. FFGET_FILE *f, 
  2.  FILE *newf, 
  3.  struct AM_disclaimer_details *dd , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

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

\------------------------------------------------------------------*/
int AM_add_disclaimer_no_boudary( FFGET_FILE *f, FILE *newf, struct AM_disclaimer_details *dd )
{
	char line[AM_1K_BUFFER_SIZE+1]="";

	DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: Inserting disclaimer into a non-boundary email",FL);


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

	switch (dd->content_type) {
		case _CTYPE_TEXT_HTML:
			DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: Conditions right for HTML disclaimer to be added",FL);
			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");
			}
			break;

		default:

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

			DAM LOGGER_log("%s:%d:AM_add_disclaimer_insert_html:DEBUG: Seeking to the end of the file for plain-text insertion...",FL);
			/** Read to the end of the file **/
			AM_read_to_boundary( f, newf, line, AM_1K_BUFFER_SIZE );

			DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: About to write disclaimer '%s'",FL,dd->disclaimer_text_plain);
			fprintf(newf,"%s",dd->disclaimer_text_plain);
			dd->text_inserted = 1;
			DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: Disclaimer now written to file",FL);
			break;
	}

	DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: Done, text-inserted=%d, html-inserted=%d"
			,FL
			,dd->text_inserted
			,dd->html_inserted
			);

	return 0;

}














/*-----------------------------------------------------------------\
  Function Name	: AM_insert_disclaimer_into_segment
  Returns Type	: int
  ----Parameter List
  1. FFGET_FILE *f, 
  2.  FILE *newf, 
  3.  struct AM_disclaimer_details *dd , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

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

\------------------------------------------------------------------*/
int AM_insert_disclaimer_into_segment( FFGET_FILE *f, FILE *newf, struct AM_disclaimer_details *dd )
{
	char line[AM_1K_BUFFER_SIZE+1]="";
	int last_boundary_written = -1;
	int result = 0;
	int insert_success = 0;

	DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Inserting disclaimer into segment",FL);

	if ( (0 == dd->text_inserted) \
			&& (dd->content_type == _CTYPE_TEXT_PLAIN ) \
			&& (dd->isfile == 0) \
		)
	{
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Conditions right to insert disclaimer\n",FL);
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: 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 ( BS_cmp(line,strlen(line))==1 )
			{
				DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Boundary hit",FL);
				break;
			}
			fprintf( newf, "%s", line );
			last_boundary_written = 1;
		}

		if ( FFGET_feof( f ) ) return -1;

		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Inserting TEXT disclaimer (%s)\n", FL, dd->disclaimer_text_plain);

		if (dd->content_encoding == _CTRANS_ENCODING_QP)
		{
			/** convert the disclaimer into QP **/
			char *qp_data;
			size_t qp_data_size;

			qp_data_size = strlen(dd->disclaimer_text_plain) *3 +1;
			qp_data = malloc( qp_data_size *sizeof(char));
			if (qp_data == NULL) {
				LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Error trying to allocate %d bytes of memory for QP encoded disclaimer",FL, qp_data_size);
				return -1;
			}
			qp_encode( qp_data, qp_data_size, dd->disclaimer_text_plain, strlen(dd->disclaimer_text_plain));
			DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Inserting QP encoded disclaimer",FL);
			fprintf( newf, "%s\n", qp_data );

			/** cleanup **/
			if (qp_data) free(qp_data);

		}  else {
			/** Normal plain-text insertion **/
			fprintf( newf, "%s\n", dd->disclaimer_text_plain );
		}

		dd->text_inserted = 1; /** Our disclaimer has been inserted, so set the flag **/
		insert_success = 1;
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: TEXT disclaimer is inserted, now flushing file output",FL);
		fflush(newf);

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

	} else {
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Conditions not right to insert a TEXT disclaimer",FL);

	}


	// Add in the HTML disclaimer (if wanted)

	DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: glb.HTML_too=%d, dd->html_inserted=%d, dd->content_type=%d, dd->isfile=%d"
			,FL
			,glb.HTML_too
			,dd->html_inserted
			,dd->content_type
			,dd->isfile
			);

	if ( ( glb.HTML_too ) \
			&& ( 0 == dd->html_inserted ) \
			&& ( dd->content_type == _CTYPE_TEXT_HTML )\
			&& ( dd->isfile == 0 ) \
		)
	{
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: 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");
		} else {
			dd->html_inserted = 1;
			insert_success = 1;
		}
	} else {
		DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Conditions not right to insert HTML disclaimer",FL);
	}

	/** If we weren't able to insert a disclaimer, then read through to the end of the segment **/
	if (insert_success == 0) {
		while (FFGET_fgets(line, AM_1K_BUFFER_SIZE, f)) {
			last_boundary_written = 0;
			fprintf( newf, "%s", line );
			if ( BS_cmp(line,strlen(line))==1 ) {
				DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Boundary hit, breaking out",FL);
				break;
			}
		}
	}

	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 segment_read = 0;

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

	if (strcmp(mpackname,"-")==0)
	{
		mp = stdin;
		newf = stdout;
	} else {
		/* Initialise our files */
		newf = fopen(mpacktmp,"w");
		mp = fopen(mpackname,"r");
	}

	/* Initialise the Boundary-stack globals */
	BS_init();
	/* Allow 10 boundaries to be stored */
	BS_set_hold_limit(10);

	// 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
	//
	result = AM_load_disclaimers( &dd );
	if (result == -1) return 0; // Just exit if we couldn't load the disclaimers.


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


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

	DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Reading main headers",FL);

	result = AM_read_headers( &dd, &f, newf );
	DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Main Headers have been read",FL);

	// Check to see if we can actually insert into this
	//		email's encoding.
	//
	if (dd.content_encoding == _CTRANS_ENCODING_B64)
	{
		AM_add_disclaimer_flush( &f, newf );
		/** 20050626-0111: Patch to remove tmp file on failure supplied by Carlos Velasco **/
		fclose(newf);
		remove(mpacktmp);
		/** endpatch **/
		VAM LOGGER_log("Email is BASE64 encoded, we cannot insert into these emails\n");
		return AM_RETURN_B64_ENCODED_EMAIL;
	}

	// Check to see if the email is signed
	//
	//
	if ((AM_RETURN_SIGNED_EMAIL == result)||(-1 == result))
	{
		AM_add_disclaimer_flush( &f, newf );
		fclose(mp);
		fclose(newf);
		VAM LOGGER_log("Email is signed with a crypto, we do not alter these emails\n");
		return AM_RETURN_SIGNED_EMAIL;
	}


	// 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
	//&&((dd.content_type >= _CTYPE_TEXT_START && dd.content_type <= _CTYPE_TEXT_END ))

	if (
			(0 == dd.boundary_found)
			&&(0 == dd.isfile)
		)
	{
		DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Inserting disclaimer into an email with no boundary",FL);
		result = AM_add_disclaimer_no_boudary( &f, newf, &dd );
		DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Inserting done, txt-inserted=%d html-inserted=%d"
				,FL
				,dd.text_inserted
				,dd.html_inserted
				);
	}


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


	if ( dd.boundary_found == 1 )
	{
		do {
			segment_read = 0;
			if (FFGET_feof(&f)) break;

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

			DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: 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).

			DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Reading spurilous data before first MIME segment\n",FL);

			AM_read_to_boundary( &f, newf, line, AM_1K_BUFFER_SIZE );
			if (FFGET_feof(&f))
			{
				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: End of file is reached, pulling out early",FL);
				break;
			}


			// Read SEGMENT 1 headers - and attempt to insert disclaimer
			if (AM_read_headers( &dd, &f, newf ) == 0)
			{
				/** Did we stumble on a boundary, and was it a non-RFC822 form 
				  ** (ie, not likely a forwarded email ) **/
				if ((dd.boundary_found == 1)&&(dd.content_type != _CTYPE_RFC822))
				{
					DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Located a new non-RFC822 boundary, adding to list\n",FL);
				}


				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: inserting into SEGMENT 1",FL);
				AM_insert_disclaimer_into_segment( &f, newf, &dd );
				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Finished inserting into SEGMENT 1",FL);
			} else {
				DAM LOGGER_log("%s:%d:AM_add_disclaimer: Cannot read headers for SEGMENT 1",FL);
			}


			// Read SEGMENT 2 headers - and attempt to insert disclaimer
			if (AM_read_headers( &dd, &f, newf ) == 0)
			{
				/** Did we stumble on a boundary, and was it a non-RFC822 form 
				  ** (ie, not likely a forwarded email ) **/
				if ((dd.boundary_found == 1)&&(dd.content_type != _CTYPE_RFC822))
				{
					DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Located a new non-RFC822 boundary, adding to list\n",FL);
				}

				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: inserting into SEGMENT 2",FL);
				AM_insert_disclaimer_into_segment( &f, newf, &dd );
				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Finished inserting into SEGMENT 2",FL);
			} else {
				DAM LOGGER_log("%s:%d:AM_add_disclaimer: Cannot read headers for SEGMENT 2",FL);
			}



			// Read SEGMENT 3 headers - and attempt to insert disclaimer, typically we do this
			//		to handle TXT + HTML combo emails
			if (AM_read_headers( &dd, &f, newf ) == 0)
			{
				/** Did we stumble on a boundary, and was it a non-RFC822 form 
				  ** (ie, not likely a forwarded email ) **/
				if ((dd.boundary_found == 1)&&(dd.content_type != _CTYPE_RFC822))
				{
					DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Located a new non-RFC822 boundary, adding to list\n",FL);
				}

				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: inserting into SEGMENT 3",FL);
				AM_insert_disclaimer_into_segment( &f, newf, &dd );
				DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Finished inserting into SEGMENT 3",FL);
			} else {
				DAM LOGGER_log("%s:%d:AM_add_disclaimer: Cannot read headers for SEGMENT 3",FL);
			}

		} while (0);
	}


	// Clean up any remaining lines in the email
	AM_add_disclaimer_flush( &f, newf );


	VAM 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);
	}

	DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Inserting done, txt-inserted=%d html-inserted=%d"
			,FL
			,dd.text_inserted
			,dd.html_inserted
			);

	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);
		MIMEH_set_header_longsearch(glb.header_long_search);
		MIMEH_set_doubleCR_save(1);

		DAM LOGGER_log("%s:%d:AM_nullify_attachment_recurse:DEBUG: Reading headers... Iteration %d",FL);
		result = MIMEH_headers_get( hinfo, f );
		if (result != 0)
		{ 
			DAM LOGGER_log("%s:%d:AM_nullify_attachment_recurse:DEBUG: Failure during header read (EOF?), exiting loop",FL);
			break;
		}

		/** If we detect a signed message and we're not explicitly wanting to alter them, then exit **/
		if ((hinfo->content_type == _CTYPE_MULTIPART_SIGNED)&&(glb.alter_signed==0)) {
			DAM LOGGER_log("%s:%d:AM_nullify_attachment_recurse:DEBUG: Message is signed, exiting",FL);
			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);
				} 
				if ((regresult != 0)&&(strlen(hinfo->name) > 0))
				{
					regresult = regexec( preg, hinfo->name, 0, NULL, 0 );
					if (AM_DNORMAL)LOGGER_log("%s:%d:AM_nullify_attachment: Match result=%d for '%s'", FL, regresult, hinfo->name);
				}
				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 about 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;
	}

	SS_init(&(hinfo.ss_filenames));
	SS_init(&(hinfo.ss_names));
	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 (!fname) {
		LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Filename is NULL",FL);
		return 1;
	}

	if (!xheader) {
		LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Xheader to insert is NULL",FL);
		return 1;
	}

	if (strlen(fname) < 1) {
		LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Filename is too short",FL);
		return 1;
	}

	if (strlen(xheader) < 1) {
		LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Header to insert is too short",FL);
	  	return 1;
	}

	if (1)
	{
		/** Strip off any trailing line breaks **/
		char *p;
		p = strpbrk(xheader,"\r\n");
		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))
		{
			/** Changed the temp filename suffix chat to a hypen because under
			  ** windows appending multiple .'s results in a filename that isn't
			  ** acceptable - Thanks to Nico for bringing this to my attention **/
			LOGGER_log("%s:%d:AM_insert_Xheader:NOTICE: Adjusting temp file name for header insert",FL);
			strcat(tpn,"X");

		} else {
			LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Temporary file name string buffer out of space!",FL);
			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("%s:%d:AM_insert_Xheader:ERROR: Cannot open temporary file, '%s' for writing (%s)",FL, tpn, strerror(errno));
		return 1;
	}


	fi = fopen(fname,"r");
	if (!fi)
	{
		LOGGER_log("%s:%d:AM_insert_Xheader:ERROR: Cannot open Mailpack file '%s' for reading, (%s)",FL, fname, strerror(errno));
		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))
		{
			/** 20041104-12H52:PLD: Changed from \n to \r\n **/
			/** 20050204-11H04:PLD: Changed from \r\n to instead use the 'blank line', this ensures
			  ** that the right \r\n or \n combination is used **/
			fprintf(fo,"%s%s",xheader,line);
			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 sense 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("%s:%d:AM_insert_Xheader:ERROR: while attempting to rename '%s' to '%s' (%s) ", FL, 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----




syntax highlighted by Code2HTML, v. 0.9.1