altermime-0.3.10/0000755000000000000000000000000011107756671012263 5ustar rootrootaltermime-0.3.10/altermime.c0000644000000000000000000003004011107756671014403 0ustar rootroot/*------------------------------------------------------------------------ Module: /extra/development/altermime-0.1.1/altermime.c Author: Paul L Daniels Project: AlterMime State: Development Creation Date: 02/05/2001 Description: Altermime is a program/object which will allow arbitary alterations to a given MIME encoded mailpack, such as disclaimer additions and attachment-nullification. ------------------------------------------------------------------------*/ #include #include #include #include #include "mime_alter.h" #include "logger.h" /* Global DEFINES */ char ALTERMIMEAPP_VERSION[]="alterMIME v0.3.10 (November-2008) by Paul L Daniels - http://www.pldaniels.com/altermime\n"; char ALTERMIMEAPP_USAGE[]="altermime --input= ( --input=- for stdin )\n" " [--disclaimer=]\n" " [--disclaimer-html=]\n" " [--disclaimer-b64=]\n" " [--htmltoo]\n" #ifdef DISPOS " [--textpos=]\n" " [--htmlpos=]\n" #endif #ifdef ALTERMIME_PRETEXT " [--pretext=]\n" " [--pretext-html=]\n" #endif " [--force-into-b64]\n" " [--force-for-bad-html]\n" " [--multipart-insert]\n" " [--remove=] (if filename contains a /, matches on mime-type )\n" " [--removeall]\n" " [--replace= --with=]\n" " [--xheader=\"...\"\n" " [--alter-header=\"...\" --alter-with=\"...\" --alter-mode=]\n" " [--altersigned]\n" " [--no-qmail-bounce]\n" " [--verbose]\n" " [--log-stdout]\n" " [--log-stderr]\n" " [--log-syslog]\n" " [--debug]\n" " [--version]\n\n" "Option Descriptions:\n" "\t--input=, Sets the mailpack file to be the filename supplied,\n" "\t\tif the filename is a single '-' (hyphen) then the mailpack\n" "\t\tis sourced via stdin and outputted via stdout.\n" "\n" "\t--disclaimer=, Set the plaintext disclaimer source file.\n" "\t--disclaimer-html=, Set the HTML disclaimer source file.\n" "\t--disclaimer-b64=, Set the BASE64 encoded disclaimer source file (implies --force-into-b64).\n" "\n" "\t--htmltoo, Sets alterMIME to insert the plaintext disclaimer into\n" "\t--force-into-b64, Sets alterMIME to insert disclaimers into BASE64 encoded text segments\n" "\t--force-for-bad-html, Force adding of the HTML disclaimer even when HTML is not correctly formatted\n" "\t\tthe HTML portion of the email body ( if there is no explicitly\n" "\t\tdefined HTML dislcaimer, see --disclaimer-html )\n" "\n" "\t--remove=, Remove any attachments which match the filename supplied,\n" "\t\tif the filename text contains a forward-slash '/', then the\n" "\t\tmatching will occur based on content-type headers rather than\n" "\t\tby filename.\n" "\t--removeall, Remove all attachments\n" "\t--replace=, Replace attachments matching the given filename. Requires to\n" "\t\tbe used with --with.\n" "\t--with=, Replace the attachments specified by --replace with the file\n" "\t\tspecified.\n" "\t--xheader=, Insert a header line as specified into the first set of headers.\n" "\t--alter-header=\"...\" --alter-with=\"...\" --alter-mode=(prefix|suffix|replace)\n" "\t\tAlter an existing header in the mailpack. This function modifies the\n" "\t\tvalue of the header, as apposed to the header name.\n" "\t--altersigned, Force alterMIME to modify 'signed' emails\n" "\t--no-qmail-bounce, Don't search into email bodies for attachment headers\n" "\t--verbose, Describe details of the process occuring\n" "\t--log-stdout, Send all output messages to stdout\n" "\t--log-stderr, Send all output messages to stderr\n" "\t--log-syslog, Send all output messages to syslog\n" "\t--debug, Provide greater verbosity and debugging information\n" "\t--version, display the alterMIME version string\n" "\n"; /* Global variables / settings */ char ALTERMIMEAPP_default_remove_prefix[]="removed"; char ALTERMIMEAPP_removeall_filename[]=".*"; struct ALTERMIMEAPP_globals { char *input_file; char *disclaimer_file; char *disclaimer_html_file; char *disclaimer_b64_file; char *pretext_file; char *pretext_html_file; int pretext_insert; char *remove_filename; char *replace; char *with; char *xheader; char *alter_header; char *alter_with; int alter_mode; int verbose; }; /*------------------------------------------------------------------------ Procedure: parse_args ID:1 Purpose: Parses the command line arguments and sets up internal parameters Input: argc: The number of arguments argv: Array of strings Output: none Errors: ------------------------------------------------------------------------*/ int ALTERMIMEAPP_parse_args( struct ALTERMIMEAPP_globals *glb, int argc, char **argv ) { int i; char *p=NULL; char *q=NULL; for (i = 1; i < argc; i++){ if ((argv[i][0] == '-')&&(argv[i][1] == '-')) { p = argv[i]; p += 2; if (strncmp(p,"input=",6)==0) { glb->input_file = p +strlen("input="); } else if (strncmp(p,"htmltoo",7)==0) { AM_set_HTMLtoo(1); } else if (strncmp(p,"force-into-b64",sizeof("force-into-b64"))==0) { AM_set_force_into_b64(1); } else if (strncmp(p,"force-for-bad-html",sizeof("force-for-bad-html"))==0) { AM_set_force_for_bad_html(1); } else if (strncmp(p,"multipart-insert", strlen("multipart-insert"))==0) { AM_set_multipart_insert(1); #ifdef ALTERMIME_PRETEXT } else if (strncmp(p,"pretext=",strlen("pretext="))==0) { glb->pretext_file = p +strlen("pretext="); AM_set_pretext_plain( glb->pretext_file, AM_PRETEXT_TYPE_FILENAME); AM_set_pretext_insert(1); } else if (strncmp(p,"pretext-html=",sizeof("pretext-html="))==0) { glb->pretext_html_file = p +strlen("pretext-html="); AM_set_pretext_HTML( glb->pretext_html_file, AM_PRETEXT_TYPE_FILENAME); AM_set_pretext_insert(1); #endif } else if (strncmp(p,"disclaimer=",11)==0) { glb->disclaimer_file = p +strlen("disclaimer="); AM_set_disclaimer_plain( glb->disclaimer_file, AM_DISCLAIMER_TYPE_FILENAME ); } else if (strncmp(p,"disclaimer-html=",16)==0) { glb->disclaimer_html_file = p +strlen("disclaimer-html="); AM_set_disclaimer_HTML( glb->disclaimer_html_file, AM_DISCLAIMER_TYPE_FILENAME ); AM_set_HTMLtoo(1); } else if (strncmp(p,"disclaimer-b64=",strlen("disclaimer-b64="))==0) { glb->disclaimer_b64_file = p +strlen("disclaimer-b64="); AM_set_disclaimer_b64( glb->disclaimer_b64_file, AM_DISCLAIMER_TYPE_FILENAME ); AM_set_force_into_b64(1); } else if (strncmp(p,"remove=",7)==0) { glb->remove_filename = p +strlen("remove="); } else if (strncmp(p, "remove_prefix=",13)==0) { LOGGER_log("--remove_prefix is depricated, ignoring"); //glb->remove_prefix = p +strlen("remove_prefix="); } else if (strncmp(p, "removeall",9)==0) { glb->remove_filename = ALTERMIMEAPP_removeall_filename; } else if (strncmp(p, "altersigned",11)==0) { AM_set_altersigned(1); } else if (strncmp(p, "replace",7)==0) { glb->replace = p +strlen("replace="); } else if (strncmp(p, "with",4)==0) { glb->with = p +strlen("with="); } else if (strncmp(p, "xheader",7)==0) { glb->xheader = p +strlen("xheader="); } else if (strncmp(p, "version",7)==0) { fprintf(stdout,"%s",ALTERMIMEAPP_VERSION); exit(0); } else if (strncmp(p, "debug", 5)==0) { AM_set_debug(1); } else if (strncmp(p, "no-qmail-bounce",15)==0) { AM_set_header_long_search(0); } else if (strncmp(p, "verbose",7)==0) { AM_set_verbose(1); } else if (strncmp(p, "alter-header", strlen("alter-header"))==0) { glb->alter_header = p +strlen("alter-header="); if (*glb->alter_header == '\"') glb->alter_header++; } else if (strncmp(p, "alter-with", strlen("alter-with"))==0) { glb->alter_with = p +strlen("alter-with="); if (*glb->alter_with == '\"') glb->alter_with++; } else if (strncmp(p, "alter-mode", strlen("alter-mode"))==0) { q = p +strlen("alter-mode="); if (*q == '\"') q++; glb->alter_mode = AM_HEADER_ADJUST_MODE_NONE; if (strncmp( q, "prefix", strlen("prefix"))==0) { glb->alter_mode = AM_HEADER_ADJUST_MODE_PREFIX; } else if (strncmp( q, "suffix", strlen("suffix"))==0) { glb->alter_mode = AM_HEADER_ADJUST_MODE_SUFFIX; } else if (strncmp( q, "replace", strlen("replace"))==0) { glb->alter_mode = AM_HEADER_ADJUST_MODE_REPLACE; } else { LOGGER_log("ERROR: Unknown header alter mode '%s'. Please use either of prefix, suffix or replace.", q); } } else if (strncmp(p, "log-stdout",strlen("log-stdout"))==0) { LOGGER_set_output_mode(_LOGGER_STDOUT); } else if (strncmp(p, "log-stderr",strlen("log-stderr"))==0) { LOGGER_set_output_mode(_LOGGER_STDERR); } else if (strncmp(p, "log-syslog",strlen("log-syslog"))==0) { LOGGER_set_output_mode(_LOGGER_SYSLOG); LOGGER_set_syslog_mode( LOG_MAIL|LOG_INFO ); } else { LOGGER_log("Error, unknown parameter \"%s\"\n",p); LOGGER_log("%s",ALTERMIMEAPP_USAGE); exit(1); } /* if-ELSE */ } /* if argv == '--' */ } /* for */ return 0; } /*-----------------------------------------------------------------\ Function Name : ALTERMIMEAPP_init Returns Type : int ----Parameter List 1. struct ALTERMIMEAPP_globals *glb , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ALTERMIMEAPP_init( struct ALTERMIMEAPP_globals *glb ) { glb->input_file = NULL; glb->disclaimer_file = NULL; glb->disclaimer_html_file = NULL; glb->disclaimer_b64_file = NULL; #ifdef ALTERMIME_PRETEXT glb->pretext_file = NULL; glb->pretext_html_file = NULL; glb->pretext_insert = 0; #endif glb->remove_filename = NULL; glb->replace = NULL; glb->with = NULL; glb->xheader = NULL; glb->verbose = 0; glb->alter_header = NULL; glb->alter_with = NULL; glb->alter_mode = AM_HEADER_ADJUST_MODE_NONE; return 0; } /*-----------------------------------------------------------------\ Function Name : main Returns Type : int ----Parameter List 1. int argc, 2. char **argv , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int main( int argc, char **argv ) { struct ALTERMIMEAPP_globals glb; LOGGER_set_output_mode(_LOGGER_STDOUT); ALTERMIMEAPP_init( &glb ); ALTERMIMEAPP_parse_args(&glb,argc, argv); if (!glb.input_file) { LOGGER_log("Error: No input file specified\n"); LOGGER_log(ALTERMIMEAPP_USAGE); exit(1); } if (((!glb.replace)&&(glb.with))||((glb.replace)&&(!glb.with))) { LOGGER_log("Error: Both --replace= and --with= must be set\n"); exit(1); } if( glb.input_file && !(glb.alter_mode||glb.replace||glb.disclaimer_file||glb.remove_filename||glb.xheader)) { LOGGER_log("Error: Must specify an action for the input file.\n"); LOGGER_log( ALTERMIMEAPP_USAGE); exit(1); } if ((strcmp(glb.input_file,"-") == 0) && (/**glb.disclaimer_file||**/glb.replace||glb.xheader)) { LOGGER_log("Error: reading/writing from stdin/stdout not implemented for --xheader,--disclaimer, or --replace.\n"); LOGGER_log(ALTERMIMEAPP_USAGE); exit(1); } if ((glb.alter_mode != AM_HEADER_ADJUST_MODE_NONE)&&(glb.alter_with != NULL)&&(glb.alter_header != NULL)) { AM_alter_header( glb.input_file, glb.alter_header, glb.alter_with, glb.alter_mode ); } if ((glb.replace)&&(glb.with)) AM_attachment_replace( glb.input_file, glb.replace, glb.with); if (glb.disclaimer_file) AM_add_disclaimer( glb.input_file ); #ifdef ALTERMIME_PRETEXT if (glb.pretext_insert) AM_add_pretext( glb.input_file ); #endif if (glb.remove_filename) AM_nullify_attachment(glb.input_file, glb.remove_filename); if (glb.xheader) AM_insert_Xheader( glb.input_file, glb.xheader); AM_done(); return 0; } /*-------------------------------------------------END. */ altermime-0.3.10/mime_alter.c0000644000000000000000000036707211107756671014564 0ustar rootroot#include #include #include #include #include #include #include #include #include #include #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 */ unsigned int altermime_status_flags; // Status flags 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 \ }; /* our base 64 decoder table */ static unsigned char b64[256]={ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \ }; 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.force_into_b64=0; /* By default don't insert into B64 encoded bodies */ glb.force_for_bad_html=0; /* By default don't try append into broken HTML */ snprintf(glb.ldelimeter,sizeof(glb.ldelimeter),"\r\n"); glb.disclaimer_plain=NULL; glb.disclaimer_plain_type=AM_DISCLAIMER_TYPE_NONE; glb.disclaimer_HTML=NULL; glb.disclaimer_HTML_type=AM_DISCLAIMER_TYPE_NONE; glb.disclaimer_b64=NULL; glb.disclaimer_b64_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); if (glb.disclaimer_b64 != NULL) free(glb.disclaimer_b64); 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; } /*-----------------------------------------------------------------\ Date Code: : 20070127-140050 Function Name : AM_set_force_into_b64 Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Set to non-zero (1) if you want alterMIME to attempt to insert disclaimers into BASE64 encoded texts. This isn't on by default because there's the possibility of malforming legitimate attachments. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_set_force_into_b64( int level ) { glb.force_into_b64 = level; return glb.force_into_b64; } /*-----------------------------------------------------------------\ 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; } /*-----------------------------------------------------------------\ Date Code: : 20081106-065138 Function Name : *AM_set_disclaimer_b64 Returns Type : char ----Parameter List 1. char *filename, 2. int disclaimer_type , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *AM_set_disclaimer_b64( char *filename, int disclaimer_type ) { glb.disclaimer_b64 = strdup( filename ); glb.disclaimer_b64_type = disclaimer_type; return glb.disclaimer_b64; } /*-----------------------------------------------------------------\ 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: Converts any singular occurances of \n into \r\n -------------------------------------------------------------------- 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; } /*-----------------------------------------------------------------\ Date Code: : 20081106-145300 Function Name : AM_adapt_linebreak Returns Type : int ----Parameter List 1. char *in, 2. FILE *out, 3. char *lb , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: ASSUMES that the input buffer (*in) is PROPERLY formatted as either \n or \r\n terminated text. This will NOT work properly if you feed it data with malformed linebreaks. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *AM_adapt_linebreak( char *in, char *lb ) { char safe[]="\r\n"; char *newblock; if (in == NULL) return NULL; if ((lb==NULL)||(lb[0] == 0)) lb = safe; DAM LOGGER_log("%s:%d:AM_adapt_linebreak:DEBUG: Linebreak=%x:%x",FL,lb[0],lb[1]); if (lb[0] == '\r') { if (strchr(in,'\r')) { DAM LOGGER_log("%s:%d:AM_adapt_linebreak:DEBUG: email and disclaimer are CRLF, no change",FL); return in; } else { DAM LOGGER_log("%s:%d:AM_adapt_linebreak:DEBUG: chaging LF only to CRLF",FL); newblock = strdup(in); newblock = PLD_strreplace(&newblock, "\n", "\r\n", 0); } } else if (lb[0] == '\n') { if (!strchr(in,'\r')) { DAM LOGGER_log("%s:%d:AM_adapt_linebreak:DEBUG: email and disclaimer are LF only, no change",FL); return in; } else { DAM LOGGER_log("%s:%d:AM_adapt_linebreak:DEBUG: changing CRLF to LF",FL); newblock = strdup(in); newblock = PLD_strreplace(&newblock, "\r\n", "\n", 0); } } return newblock; } /*------------------------------------------------------------------------ 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 */ { size_t bc; /* 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] = '='; bc = fwrite(outbuf, 1, 4, fout); /* write out the buffer */ if (bc != 4) LOGGER_log("%s:%d:AM_base64_encode:ERROR: Wrote %d bytes rather than %d to output", FL, bc); bc= fwrite( "\n", 1, 1, fout ); /* as this is the last line, put a trailing \n */ if (bc != 1) LOGGER_log("%s:%d:AM_base64_encode:ERROR: Wrote %d bytes rather than %d to output", FL, bc); break; /* exit out of the while loop */ } bc = fwrite(outbuf, 1, 4, fout); /* normal operation buffer-write */ if (bc != 4) LOGGER_log("%s:%d:AM_base64_encode:ERROR: Wrote %d bytes rather than %d to output", FL, bc); cc += 4; /* increment the output char count by 4 */ if (cc > 76) /* if we have more than 76 chars, then put in a \n */ { bc = fwrite( "\n", 1, 1, fout ); /* write the \n out */ if (bc != 1) LOGGER_log("%s:%d:AM_base64_encode:ERROR: Wrote %d bytes rather than %d to output", FL, bc); 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; } /*-----------------------------------------------------------------\ Date Code: : 20070124-133319 Function Name : AM_base64_encode_from_buffer Returns Type : int ----Parameter List 1. char *buffer, 2. size_t buffer_size, 3. char *out_fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Encodes a given buffer contents into BASE64 and writes to file out_fname -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_base64_encode_buffer_to_FILE( char *buffer, size_t buffer_size, FILE *fout) { unsigned char outbuf[4]; int cc; int chars = strlen(buffer); size_t bc; if (fout == NULL) { LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Output file stream was NULL", FL); return -1; } cc = 0; /* Character Count = 0 */ DAM LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:DEBUG: Encoding %d chars", FL, chars); while (chars > 0) /* while we get more than 0 bytes */ { /* Split 3 bytes into 4 bytes by taking 6bit blocks out of the 24 bit (3x8bit) * array */ if (chars >= 3) { outbuf[0] = AM_encode64[((buffer[0] & 0xFC) >> 2)]; outbuf[1] = AM_encode64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; outbuf[2] = AM_encode64[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; outbuf[3] = AM_encode64[(buffer[2] & 0x3F)]; buffer += 3; chars -= 3; } else { /* 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) */ DAM LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:DEBUG: Encoding remaining %d chars '%s'", FL, chars, buffer); if ( chars == 2 ) { DAM LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:DEBUG: '%c' '%c'", FL, buffer[0], buffer[1]); outbuf[0] = AM_encode64[((buffer[0] & 0xFC) >> 2)]; outbuf[1] = AM_encode64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; outbuf[2] = AM_encode64[((buffer[1] & 0x0F) << 2)]; outbuf[3] = '='; } if ( chars == 1 ) { DAM LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:DEBUG: '%c'", FL, buffer[0]); outbuf[0] = AM_encode64[((buffer[0] & 0xFC) >> 2)]; outbuf[1] = AM_encode64[((buffer[0] & 0x03) << 4)]; outbuf[3] = outbuf[2] = '='; } bc = fwrite(outbuf, 1, 4, fout); /* write out the buffer */ if (bc != 4) LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Wrote %d bytes rather than %d to output", FL, bc); bc = fwrite( "\n", 1, 1, fout ); /* as this is the last line, put a trailing \n */ if (bc != 1) LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Wrote %d bytes rather than %d to output", FL, bc); break; /* exit out of the while loop */ } bc = fwrite(outbuf, 1, 4, fout); /* normal operation buffer-write */ if (bc != 4) LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Wrote %d bytes rather than %d to output", FL, bc); cc += 4; /* increment the output char count by 4 */ if (cc > 76) /* if we have more than 76 chars, then put in a \n */ { bc = fwrite( "\n", 1, 1, fout ); /* write the \n out */ if (bc != 1) LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Wrote %d bytes rather than %d to output", FL, bc); cc = 0; /* reset the output char count */ } /* if bytecount == 3 */ } /* while more data to encode */ bc = fwrite("\n",1,1,fout); if (bc != 1) LOGGER_log("%s:%d:AM_base64_encode_buffer_to_FILE:ERROR: Wrote %d bytes rather than %d to output", FL, bc); return 0; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_short64 ID:1 Purpose: Decodes a BASE64 encoded realm Input: char *realm : base64 encoded NUL terminated string Output: decoded data is written to the buffer Errors: ------------------------------------------------------------------------*/ int AM_base64_decode_buffer( char *buffer, size_t length ) { int i; int realm_size = length; int stopcount = 0; /* How many stop (=) characters we've read in */ int c; /* a single char as retrieved using MDECODE_get_char() */ int char_count = 0; /* How many chars have been received */ char output[3]; /* The 4->3 byte output array */ char input[4]; /* The 4->3 byte input array */ char *outstring = buffer; char_count = 0; while (char_count < realm_size) { /* Initialise the decode buffer */ input[0] = input[1] = input[2] = input[3] = 0; /* snatch 4 characters from the input */ for (i = 0; i < 4; i++) { length--; /* get a char from the filestream */ c = *buffer; if (c == '\0') break; buffer++; /* assuming we've gotten this far, then we increment the char_count */ char_count++; /* if we detect the "stopchar" then we better increment the STOP counter */ if (c == '=') { stopcount++; } /* test for and discard invalid chars */ if (b64[c] == 0x80) { i--; continue; } /* do the conversion from encoded -> decoded */ input[i] = (char)b64[c]; } /* for */ /* now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data */ output[0] = (input[0] << 2) | (input[1] >> 4); output[1] = (input[1] << 4) | (input[2] >> 2); output[2] = (input[2] << 6) | input[3]; /* determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it */ if (i == 4) { for (i = 0; i < (3 -stopcount); i++){ *outstring = output[i]; outstring++; } /* copy our data across */ } /* if 4 chars were inputted */ } /* while more chars to proccess */ *outstring = '\0'; // Set the last char to NULL return 0; } /*-----------------------------------------------------------------\ Date Code: : 20070124-113935 Function Name : AM_base64_decodef Returns Type : int ----Parameter List 1. FILE *fin, existing open intput file 2. FILE *fout, existing open output file 3. size_t input_size , number of bytes to read from input file ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Decodes data contained in 'fin' and writes the decoded output to 'fout'. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_base64_decodef( FILE *fin, FILE *fout, size_t input_size ) { char *buffer; size_t bytes_read; /** Allocate memory for text file read **/ buffer = malloc( input_size *sizeof(char)); if (buffer == NULL) { LOGGER_log("%s:%d:AM_base64_decodef:ERROR: Cannot allocate %d bytes for base64 decoding", FL, input_size); } DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: Reading in %d bytes of RAW base64 data", FL, input_size); /** Read in entire block **/ bytes_read = fread( buffer, sizeof(char), input_size, fin ); if (bytes_read != input_size) { LOGGER_log("%s:%d:AM_base64_decodef:WARNING: Requested %d bytes from input file, only received %d", FL, input_size, bytes_read); } DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: %d bytes read", FL, bytes_read ); /** Don't forget to terminate the data **/ buffer[bytes_read] = '\0'; DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: Raw input -------\n%s\n----------", FL, buffer); /** Now, decode the data into human-readable **/ AM_base64_decode_buffer( buffer, bytes_read ); /** Write data to the output file **/ DAM LOGGER_log("%s:%d:AM_base64_decode_buffer:DEBUG: decoded output: ----------\n%s\n----------", FL, buffer); fprintf(fout, "%s", buffer ); fflush(fout); /** Free the memory used **/ free(buffer); return 0; } /*-----------------------------------------------------------------\ Date Code: : 20070124-131502 Function Name : AM_base64_decode_to_bufferf Returns Type : int ----Parameter List 1. FILE *fin, 2. FILE *fout, 3. size_t input_size , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Reads in data from filestream FIN and decodes BASE64 into buffer -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_base64_decode_to_bufferf( FILE *fin, size_t input_size, char **decoded_buffer ) { char *buffer; size_t bytes_read; /** Allocate memory for text file read **/ buffer = malloc( input_size *sizeof(char) +1); if (buffer == NULL) { LOGGER_log("%s:%d:AM_base64_decodef:ERROR: Cannot allocate %d bytes for base64 decoding", FL, input_size); } DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: Reading in %d bytes of RAW base64 data", FL, input_size); /** Read in entire block **/ bytes_read = fread( buffer, sizeof(char), input_size, fin ); if (bytes_read != input_size) { LOGGER_log("%s:%d:AM_base64_decodef:WARNING: Requested %d bytes from input file, only received %d", FL, input_size, bytes_read); } DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: %d bytes read", FL, bytes_read ); /** Don't forget to terminate the data **/ buffer[bytes_read] = '\0'; DAM LOGGER_log("%s:%d:AM_base64_decodef:DEBUG: Raw input -------\n%s\n----------", FL, buffer); /** Now, decode the data into human-readable **/ AM_base64_decode_buffer( buffer, bytes_read ); /** Write data to the output file **/ DAM LOGGER_log("%s:%d:AM_base64_decode_buffer:DEBUG: decoded output: ----------\n%s\n----------", FL, buffer); *decoded_buffer = buffer; return 0; } /*-----------------------------------------------------------------\ Date Code: : 20070124-114024 Function Name : AM_base64_decode Returns Type : int ----Parameter List 1. char *in_fname, NUL terminated input filename 2. char *out_fname , NUL terminated output filename ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Wrapper for _decodef(). Accepts an input and output filename which are then opened and then passed to _decodef() along with the intput file size. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_base64_decode( char *in_fname, char *out_fname ) { FILE *fin, *fout; struct stat st; int stat_result; stat_result = stat(in_fname, &st); if (stat_result) { LOGGER_log("%s:%d:AM_base64_decode:ERROR: Error stat'ing '%s' (%s)",FL, in_fname, strerror(errno)); return -1; } DAM LOGGER_log("%s:%d:AM_base64_decode:DEBUG: %s size = %d bytes", FL, in_fname, st.st_size); fin = fopen( in_fname, "rb" ); if (!fin) { LOGGER_log("AM_base64_decode: Cannot open \"%s\" for reading.(%s)",in_fname,strerror(errno)); return -1; } fout = fopen ( out_fname, "wb" ); if (!fout) { LOGGER_log("AM_base64_decode: Cannot open \"%s\" for writing.(%s)",out_fname,strerror(errno)); fclose(fin); return -1; } AM_base64_decodef( fin, fout, st.st_size ); /* encode and output */ fclose(fin); /* close the input file */ fclose(fout); /* close the output file */ return 0; } /*-----------------------------------------------------------------\ Date Code: : 20070124-131401 Function Name : AM_base64_decode_to_buffer Returns Type : int ----Parameter List 1. char *in_fname, input file containing base64 data 2. char **buffer , pointer to a char buffer ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Decodes the file in_fname which contains base64 data into the buffer (which is allocated on demand) -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_base64_decode_to_buffer( char *in_fname, char **buffer ) { FILE *fin; struct stat st; int stat_result; stat_result = stat(in_fname, &st); if (stat_result) { LOGGER_log("%s:%d:AM_base64_decode:ERROR: Error stat'ing '%s' (%s)",FL, in_fname, strerror(errno)); return -1; } DAM LOGGER_log("%s:%d:AM_base64_decode:DEBUG: %s size = %d bytes", FL, in_fname, st.st_size); fin = fopen( in_fname, "rb" ); if (!fin) { LOGGER_log("AM_base64_decode: Cannot open \"%s\" for reading.(%s)",in_fname,strerror(errno)); return -1; } AM_base64_decode_to_bufferf( fin, st.st_size, buffer ); /* encode and output */ fclose(fin); /* close the input 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(); DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG: lfcount=%d, crlfcount=%d", FL, hinfo.lf_count, hinfo.crlf_count); if (hinfo.lf_count > hinfo.crlf_count) { DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG:Delimeter set to LF only",FL); snprintf(glb.ldelimeter,sizeof(glb.ldelimeter),"\n"); } else { DAM LOGGER_log("%s:%d:AM_read_headers:DEBUG:Delimeter set to CRLF ",FL); snprintf(glb.ldelimeter,sizeof(glb.ldelimeter),"\r\n"); } /* 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) { //NAB SHORTCIRCUIT 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; size_t bc; *textptr = NULL; if (0 == stat(fname, &st)) { char *p; p = malloc((st.st_size +1)*sizeof(char)); // We have to add 1 so that we delimit the data with a \0 if (p == NULL) { LOGGER_log("%s:%d:AM_disclaimer_load_text:ERROR: Unable to allocate memory to load file '%s'", FL, fname); return 1; } if (p) { memset(p, '\0', (st.st_size +1)); f = fopen(fname,"r"); if (!f) { LOGGER_log("%s:%d:AM_disclaimer_load_text:ERROR: Cannot open %s for reading (%s)",FL,fname,strerror(errno)); return 0; } bc = fread(p, 1, st.st_size, f); if (bc != st.st_size) LOGGER_log("%s:%d:AM_disclaimer_load_text:ERROR: Read %d bytes instead of %d from %s", FL, bc, st.st_size, fname); // *textptr[st.st_size] = '\0'; DAM LOGGER_log("%s:%d:AM_disclaimer_load_text:DEBUG: Disclaimer Loaded (%s:%d):\n%s", FL, fname, st.st_size, p); fclose(f); *textptr = p; } } else { LOGGER_log("%s:%d:AM_disclaimer_load_text:ERROR: Cannot stat '%s' (%s)",FL,fname,strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Date Code: : 20070124-123353 Function Name : AM_disclaimer_html_perform_insertion Returns Type : int ----Parameter List 1. struct AM_disclaimer_details *dd, 2. FFGET_FILE *f, 3. FILE *newf , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int AM_disclaimer_html_perform_insertion( struct AM_disclaimer_details *dd, FFGET_FILE *f, FILE *newf ) { char *p; 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)); p = AM_adapt_linebreak(qp_data, glb.ldelimeter); DAM LOGGER_log("%s:%d:AM_disclaimer_html_perform_insertion:DEBUG: Inserting QP encoded disclaimer",FL); if (dd->disclaimer_text_HTML == NULL) { fprintf(newf,"
=%s",glb.ldelimeter);
			fprintf(newf,"%s",p);
			fprintf(newf,"

=%s",glb.ldelimeter); } else { fprintf(newf,"
=%s", glb.ldelimeter); fprintf(newf,"%s",p); fprintf(newf,"
=%s",glb.ldelimeter); } /** 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,"
%s", glb.ldelimeter);
			p = AM_adapt_linebreak(dd->disclaimer_text_plain, glb.ldelimeter);
			fprintf(newf,"%s

%s", p, glb.ldelimeter); } else { fprintf(newf,"
%s", glb.ldelimeter); p = AM_adapt_linebreak(dd->disclaimer_text_HTML, glb.ldelimeter); fprintf(newf,"%s
%s", p, glb.ldelimeter); } } /** quotedprintable / non encoding selector **/ return 0; } /*------------------------------------------------------------------------ Procedure: AM_add_disclaimer_insert_html ID:1 Purpose: Inserts a disclaimer with
 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,"html_inserted = 1;

			// prepare to print out the original up to the  or  part

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

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

			fprintf(newf,"%s%s",line, glb.ldelimeter);
			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, dud_b64=0;

	dd->disclaimer_text_HTML = NULL;
	dd->disclaimer_text_plain = NULL;
	dd->disclaimer_text_b64 = 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 ((glb.disclaimer_b64 != NULL)&&(glb.disclaimer_b64_type != AM_DISCLAIMER_TYPE_NONE))
	{
		if (glb.disclaimer_b64_type == AM_DISCLAIMER_TYPE_FILENAME)
		{
			AM_base64_decode_to_buffer( glb.disclaimer_b64, &(dd->disclaimer_text_b64) );
		} else {
			dd->disclaimer_text_b64 = strdup( glb.disclaimer_b64 );
			AM_base64_decode_buffer( dd->disclaimer_text_b64, strlen(dd->disclaimer_text_b64));
		}
	} else {
		dud_b64=1;
	}

	// If our disclaiemrs are all 'dud's, then we should just return with an error.
	if ((dud_html == 1)&&(dud_text == 1)&&(dud_b64 == 1))
	{
		LOGGER_log("%s:%d:AM_load_disclaimers: Neither the Plain-text , HTML or BASE64 disclaimer were valid to insert, skipping disclaimer-insertion routine\n");
		return -1;
	}

	return 0;

}





/*-----------------------------------------------------------------\
  Date Code:	: 20070124-135031
  Function Name	: *AM_insert_disclaimer_into_buffer
  Returns Type	: char
  ----Parameter List
  1. char *buffer, 
  2.  struct AM_disclaimer_details *dd , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

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

\------------------------------------------------------------------*/
char *AM_insert_disclaimer_into_buffer( char *buffer, struct AM_disclaimer_details *dd )
{
	char *p = buffer;
	size_t total_size;

	total_size = strlen(dd->disclaimer_text_plain) +strlen(buffer) +1;

	p = malloc( total_size *sizeof(char) );
	if (p) {
		snprintf(p, total_size-1, "%s\n%s", buffer, dd->disclaimer_text_plain);
	}

	return p;
}




/*-----------------------------------------------------------------\
  Date Code:	: 20070124-135051
  Function Name	: *AM_insert_HTML_disclaimer_into_buffer
  Returns Type	: char
  ----Parameter List
  1. char *buffer, 
  2.  struct AM_disclaimer_details *dd , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

Inserts a disclaimer into a HTML body, this is typically
selected based on the content-type of 'text/html' in the 
headers.

If no suitable HTML framework can be found in the text
the disclaimer can still be inserted by forcing it with
--force-for-bad-html

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

\------------------------------------------------------------------*/
char *AM_insert_HTML_disclaimer_into_buffer( char *buffer, struct AM_disclaimer_details *dd )
{
	char *p;
	char *new_buffer = NULL;

	if (buffer == NULL ) {
		LOGGER_log("%s:%d:AM_insert_HTML_disclaimer_into_buffer:ERROR: Input buffer is NULL",FL);
	}

	DAM LOGGER_log("%s:%d:AM_insert_HTML_disclaimer_into_buffer:DEBUG: Searching for BODY closing tag\n'''%s'''",FL, buffer);

	p = PLD_strstr( buffer, "disclaimer_text_HTML) {
			new_buffer = PLD_dprintf("%s\n%s\n<%s"
					,buffer
					,dd->disclaimer_text_HTML
					,(p+1)
					);
		} else {
			new_buffer = PLD_dprintf("%s\n
%s
\n<%s" ,buffer ,dd->disclaimer_text_plain ,(p+1) ); } dd->html_inserted = 1; } else { /** Getting desperate here, try for some other HTML tags that are * fairly common */ if ( PLD_strstr(buffer, "disclaimer_text_HTML) { new_buffer = PLD_dprintf("%s\n%s", buffer, dd->disclaimer_text_HTML); } else { new_buffer = PLD_dprintf("%s\n
%s
\n", buffer, dd->disclaimer_text_plain); } } else { /** If all else fails, just dupe the block **/ new_buffer = strdup(buffer); } } DAM LOGGER_log("%s:%d:AM_insert_HTML_disclaimer_into_buffer:DEBUG: Final segment '''%s'''", FL, new_buffer); return new_buffer; } /*-----------------------------------------------------------------\ Function Name : AM_insert_disclaimer_into_segment64 Returns Type : int ----Parameter List 1. FFGET_FILE *f, source file (original mailpack) 2. FILE *newf, destination file (new mailpack) 3. struct AM_disclaimer_details *dd , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Inserts a disclaimer into a BASE64 encoded segment -------------------------------------------------------------------- Changes: 20061220-1643-PLD: Added \------------------------------------------------------------------*/ int AM_insert_disclaimer_into_segment64( FFGET_FILE *f, FILE *newf, struct AM_disclaimer_details *dd ) { char line[AM_1K_BUFFER_SIZE+1]=""; int last_boundary_written = -1; int insert_success = 0; char *b64_buffer, *new_b64_buffer; char b64_raw_fname[128]; FILE *b64_raw_file; DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: Inserting disclaimer into segment",FL); /** This is the file that is used to store the B64 encoded segment, which we'll * DECODE, apply disclaimer, then ENCODE again and insert back into the * original mailpack * */ snprintf(b64_raw_fname, sizeof(b64_raw_fname), "altermime-raw-%d.b64", getpid()); b64_raw_file = fopen(b64_raw_fname, "w"); if (b64_raw_file == NULL) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:ERROR: Cannot open '%s' (%s)", FL, b64_raw_fname, strerror(errno)); return 1; } /** From here the output file is ready **/ if ( (0 == dd->text_inserted) && (dd->content_type == _CTYPE_TEXT_PLAIN ) && (dd->content_encoding == _CTRANS_ENCODING_B64) && (dd->isfile == 0) ) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: Conditions right to insert disclaimer\n",FL); DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64: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_segment64:DEBUG: Boundary hit",FL); break; } fprintf(b64_raw_file, "%s", line); /* write raw base64 data to the tmp file **/ //fprintf( newf, "%s", line ); //last_boundary_written = 1; } fclose(b64_raw_file); /** Okay, at this point, we've got our file containing the base64 encoded data * so now we just _decode_ it and add in the disclaimer as before (oh yes, very * easy indeed *cough* * * b64_name contains the raw file, b64_decoded_fname is the decoded file */ AM_base64_decode_to_buffer( b64_raw_fname, &b64_buffer ); /** Remove the raw file **/ unlink(b64_raw_fname); /** b64_buffer now contains the decoded text **/ new_b64_buffer = AM_insert_disclaimer_into_buffer( b64_buffer, dd ); if (new_b64_buffer) { /** Encode the text file back into B64 **/ DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: Inserting TEXT disclaimer (%s)\n", FL, dd->disclaimer_text_plain); AM_base64_encode_buffer_to_FILE( new_b64_buffer, strlen(new_b64_buffer), newf ); dd->text_inserted = 1; free(new_b64_buffer); } insert_success = 1; DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: TEXT disclaimer is inserted, now flushing file output",FL); fflush(newf); if ( last_boundary_written == 0 ) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: writing the boundary line '%s'", FL, line); fprintf( newf, "%s", line ); last_boundary_written = 1; } } else { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64: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_segment64: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->content_encoding == _CTRANS_ENCODING_B64) && ( dd->isfile == 0 ) ) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: Conditions right for HTML disclaimer to be added",FL); 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_segment64:DEBUG: Boundary hit",FL); break; } fprintf(b64_raw_file, "%s", line); /* write raw base64 data to the tmp file **/ //fprintf( newf, "%s", line ); } fclose(b64_raw_file); /** Convert the BASE64 code into human text **/ AM_base64_decode_to_buffer( b64_raw_fname, &b64_buffer ); /** Remove the raw file **/ unlink(b64_raw_fname); new_b64_buffer = AM_insert_HTML_disclaimer_into_buffer( b64_buffer, dd ); if (!new_b64_buffer) { new_b64_buffer = b64_buffer; } AM_base64_encode_buffer_to_FILE( new_b64_buffer, strlen(new_b64_buffer), newf ); free(new_b64_buffer); if ((glb.verbose)&&(0 == dd->html_inserted)) { LOGGER_log("WARNING: Could not insert HTML disclaimer into email"); } else { insert_success = 1; } if ( last_boundary_written == 0 ) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64:DEBUG: writing the boundary line '%s'", FL, line); fprintf( newf, "%s", line ); last_boundary_written = 1; } } else { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment64: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_segment64:DEBUG: Boundary hit, breaking out",FL); break; } } } 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]=""; char *p; DAM LOGGER_log("%s:%d:AM_add_disclaimer_no_boudary:DEBUG: Inserting disclaimer into a non-boundary email",FL); /** Don't insert into Calendar files **/ if ( dd->content_type == _CTYPE_TEXT_CALENDAR) { return -1; } if ( dd->content_encoding == _CTRANS_ENCODING_B64 && ( (dd->content_type == _CTYPE_TEXT_HTML) || (dd->content_type == _CTYPE_TEXT_PLAIN) ) ) { /** Normally we don't recommend inserting disclaimers into BASE64 encoded regions * however, if required, we can do it by setting --force-into-b64 * * Even still, we'll only (at this point) insert into text/html or text/plain * content-type segments. Inserting into other segments is just asking for * potential file mangling. * * Originally done for NAB. */ if (glb.force_into_b64) return AM_insert_disclaimer_into_segment64( f, newf, dd ); else return -1; /** _segment64 duplicates the rest of the existing _segment function, so there's * no need to go beyond here */ LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:ERROR: Executed beyond base64 segment insert, please report this.",FL); } // If we have a HTML email body, then insert the HTML based // disclaimer, or, the plain-text one with
...
// 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); // if (dd->content_encoding != _CTRANS_ENCODING_B64) { 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); p = AM_adapt_linebreak(dd->disclaimer_text_plain, glb.ldelimeter); // 200811061459:PLD: converts text linebreaks to appropriate format fprintf(newf,"%s",p); 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 - encoding=%d type=%d",FL, dd->content_encoding, dd->content_type); /** check for types that we're not supposed to be inserting into * * Currently the types not to insert to include; * text/calendar (calendar files) * * */ if (dd->content_type == _CTYPE_TEXT_CALENDAR) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Not inserting into a calendar file"); return -1; } if ( dd->content_encoding == _CTRANS_ENCODING_B64 && ( (dd->content_type == _CTYPE_TEXT_HTML) || (dd->content_type == _CTYPE_TEXT_PLAIN) ) ) { /** Normally we don't recommend inserting disclaimers into BASE64 encoded regions * however, if required, we can do it by setting --force-into-b64 * * Even still, we'll only (at this point) insert into text/html or text/plain * content-type segments. Inserting into other segments is just asking for * potential file mangling. * * Originally done for NAB. */ if (glb.force_into_b64) return AM_insert_disclaimer_into_segment64( f, newf, dd ); else return -1; /** _segment64 duplicates the rest of the existing _segment function, so there's * no need to go beyond here */ LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:ERROR: Executed beyond base64 segment insert, please report this.",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 { char *p; /** Normal plain-text insertion **/ p = AM_adapt_linebreak(dd->disclaimer_text_plain, glb.ldelimeter); // 200811061501:PLD: Adapts the linebreaks to match the email if (p) { fprintf(newf, "%s",p); } else { LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:WARNING: Disclaimer was NULL after linebreak adaption", FL); } } 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 ) { DAM LOGGER_log("%s:%d:AM_insert_disclaimer_into_segment:DEBUG: Writing boundary down",FL); 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 potentially 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); altermime_status_flags = 0; // clear the global status flags 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; dd.b64_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) { if ( (dd.content_type == _CTYPE_TEXT_HTML) ||(dd.content_type == _CTYPE_TEXT_PLAIN) ) { /* NAB-SPECIAL * If we've got a base64 encoded email right up front, then we will try * to decode it, insert disclaimer then reinsert. * * 1. Detect * 2. Save B64 segment to disk * 3. Decode B64 segment * 4. Insert disclaimer into the end of the decoded segment * 5. Encode segment in B64 * 6. Insert new B64 code into current output file position */ DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: B64 encoded body text(s)",FL); /** INSERT NAB CODE **/ } else { 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); remove(mpacktmp); // Remove the temporary file - patch supplied by Carlos Velasco 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 { /* DO loop is used here so that we can 'break' out without having to resort to ugly GOTO hacks or similar */ 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); } { int depth=0; // how many more headers to read before giving up entirely... do { // 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); } if (dd.content_type == _CTYPE_RFC822) { DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Located RFC822 type of header, so no more attempts to insert disclaimers",FL); break; // out of the do. } DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: inserting into SEGMENT %d",FL, 3+depth); AM_insert_disclaimer_into_segment( &f, newf, &dd ); DAM LOGGER_log("%s:%d:AM_add_disclaimer:DEBUG: Finished inserting into SEGMENT %d",FL, 3+depth); } else { DAM LOGGER_log("%s:%d:AM_add_disclaimer: Cannot read headers for SEGMENT %d",FL, 3+depth); } } while (( dd.html_inserted == 0 )&&( depth++ < 3 )); } } 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 b64-inserted=%d" ,FL ,dd.text_inserted ,dd.html_inserted ,dd.b64_inserted ); altermime_status_flags |= (dd.text_inserted<< AMSTATUSFLAGS_TEXT_INSERTED)|(dd.html_inserted<< AMSTATUSFLAGS_HTML_INSERTED)|(dd.b64_inserted<< AMSTATUSFLAGS_B64_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; size_t bc; 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)) { int sl; // 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 ); } sl = strlen(original_ptr); bc = fwrite( original_ptr, sizeof(char), sl, outputfile ); if (bc != sl) LOGGER_log("%s:%d:AM_nullify_attachment_clean_headers:ERROR: Wrote %d bytes instead of %d", FL, bc, sl); } // 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) { bc = fwrite( buffer, sizeof(char), buffer_len, outputfile ); if (bc != buffer_len) LOGGER_log("%s:%d:AM_nullify_attachment_recurse:ERROR: Wrote %d bytes instead of %d", FL, bc, buffer_len); } 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); regfree(&preg); FFGET_closestream(&f); // PLD-20070718-1841: We're done with the FFGET bits now, close this, possible truncation cause fclose(inputfile); fclose(outputfile); 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; } else { // LOGGER_log("%s:%d:AM_insert_Xheader:NOTICE: opened '%s' for writing", FL, tpn); } 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; } else { // LOGGER_log("%s:%d:AM_insert_Xheader:NOTICE: opened '%s' for reading", FL, fname); } //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 FFGET_closestream(&f); // PLD-20070718-1842 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)); } if (header_written) altermime_status_flags |= (1<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 , 4. char *delimeter, line termination to use ------------------ 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; size_t bc; 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) { int bl = strlen(original_ptr); bc = fwrite( original_ptr, sizeof(char), bl, outputfile ); if (bc != bl) LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Wrote %d bytes instead of %d", FL, bc, bl); 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, glb.ldelimeter, glb.ldelimeter\ , new_attachment_filename, glb.ldelimeter, glb.ldelimeter\ ); } 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, glb.ldelimeter ); attachment_data_written=1; } bc = fwrite( buffer, sizeof(char), buflen, outputfile ); if (bc != buflen) LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Wrote %d bytes instead of %d", FL, bc, buflen); 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) { bc = fwrite( buffer, sizeof(char), buflen, outputfile ); if (bc != buflen) LOGGER_log("%s:%d:AM_attachment_replace_recurse:ERROR: Wrote %d bytes instead of %d", FL, bc, buflen); } } 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, glb.ldelimeter ); } 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 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. 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---- altermime-0.3.10/mime_alter.h0000644000000000000000000000726011107756671014557 0ustar rootroot#ifndef __alterMIME__ #define __alterMIME__ #define LIBAM_VERSION "200811161138" #define AM_RETURN_SIGNED_EMAIL 10 #define AM_RETURN_B64_ENCODED_EMAIL 12 #define AM_DISCLAIMER_TYPE_NONE 0 #define AM_DISCLAIMER_TYPE_FILENAME 1 #define AM_DISCLAIMER_TYPE_TEXT 2 #define AM_PRETEXT_TYPE_NONE 0 #define AM_PRETEXT_TYPE_FILENAME 1 #define AM_PRETEXT_TYPE_TEXT 2 #define AM_HEADER_ADJUST_MODE_NONE 0 #define AM_HEADER_ADJUST_MODE_PREFIX 1 #define AM_HEADER_ADJUST_MODE_SUFFIX 2 #define AM_HEADER_ADJUST_MODE_REPLACE 4 #define AM_NULLIFY_MATCH_MODE_NONE 0 #define AM_NULLIFY_MATCH_MODE_FILENAME 1 #define AM_NULLIFY_MATCH_MODE_CONTENT_TYPE 2 struct AM_disclaimer_details { // Header details int content_type; int content_encoding; int boundary_found; char boundary[1024]; // int isb64; int ishtml; int isfile; int text_inserted; int html_inserted; int b64_inserted; // char *disclaimer_text_plain; char *disclaimer_text_HTML; char *disclaimer_text_b64; /** Positional definitions for the HTML and text disclaimers **/ char textpos[1024]; char htmlpos[1024]; }; #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 force_into_b64; /** Force headers into Base64 encoded bodies **/ 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 ldelimeter[3]; char *disclaimer_plain; int disclaimer_plain_type; char *disclaimer_HTML; int disclaimer_HTML_type; char *disclaimer_b64; int disclaimer_b64_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; }; #define AMSTATUSFLAGS_TEXT_INSERTED 1 #define AMSTATUSFLAGS_HTML_INSERTED 2 #define AMSTATUSFLAGS_B64_INSERTED 3 #define AMSTATUSFLAGS_XHEADER_INSERTED 4 extern unsigned int altermime_status_flags; // Status flags int AM_version( void ); int AM_init( void ); int AM_done( void ); int AM_set_debug( int level ); char *AM_set_disclaimer_b64( char *filename, int disclaimer_type ); char *AM_set_disclaimer_plain( char *filename, int disclaimer_type ); char *AM_set_disclaimer_HTML( char *filename, int disclaimer_type ); #ifdef ALTERMIME_PRETEXT int AM_set_pretext_plain( char *filename, int disclaimer_type ); int AM_set_pretext_HTML( char *filename, int disclaimer_type ); int AM_set_pretext_insert( int level ); #endif int AM_add_disclaimer( char *mpackname ); int AM_nullify_attachment( char *mpackname, char *attachmentname ); int AM_set_verbose( int level ); int AM_set_HTMLtoo( int level ); int AM_set_force_for_bad_html( int level ); int AM_set_force_into_b64( int level ); int AM_set_multipart_insert( int level ); int AM_set_nullifyall( int level ); int AM_set_altersigned( int level ); int AM_set_header_long_search( int level ); int AM_base64_encode( char *enc_fname, char *out_fname ); int AM_attachment_replace( char *mpackname, char *attachmentname, char *new_attachment_name ); int AM_insert_Xheader( char *fname, char *xheader); int AM_alter_header( char *filename, char *header, char *change, int change_mode ); //int AM_force_into_b64( int level ); #endif altermime-0.3.10/logger.c0000644000000000000000000001724211107756671013714 0ustar rootroot // Abstract logging system used to facilitate multiple modes // of logging #include #include #include #ifndef WIN32 #include #endif #include #include #include #include "logger.h" #ifndef WIN32 static int _LOGGER_mode = _LOGGER_SYSLOG; static int _LOGGER_syslog_mode = LOG_MAIL|LOG_INFO; #else static int _LOGGER_mode = _LOGGER_STDERR; static int _LOGGER_syslog_mode = 0; #endif static FILE *_LOGGER_outf; struct LOGGER_globals { int wrap; int wraplength; }; // Create and Initialise the global structure for LOGGER, // we init it to have NO wrapping. static struct LOGGER_globals LOGGER_glb={ 0, 0 }; /*------------------------------------------------------------------------ Procedure: LOGGER_get_file ID:1 Purpose: Returns the pointer to the file being used to output logs to Input: Output: Errors: ------------------------------------------------------------------------*/ FILE *LOGGER_get_file( void ) { return _LOGGER_outf; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_mode ID:1 Purpose: Sets the message/log output method, ie, stderr, stdout or syslog Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_mode( int modechoice ) { _LOGGER_mode = modechoice; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_output_file ID:1 Purpose: Sets the output file for when _LOGGER_mode is set to _LOGGER_file Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_output_file( FILE *f ) { _LOGGER_outf = f; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_syslog_mode ID:1 Purpose: Sets the mode that messaging to the syslog daemon will be sent as (ie, LOG_MAIL|LOG_INFO) Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_syslog_mode( int syslogmode ) { _LOGGER_syslog_mode = syslogmode; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_logfile ID:1 Purpose: Opens and setups the internal Log file file pointer with the log file as given by lfname Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_logfile( char *lfname ) { int result = 0; _LOGGER_outf = fopen(lfname,"a"); if (!_LOGGER_outf) { #ifndef WIN32 syslog(1,"LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)",lfname,strerror(errno)); #else fprintf(stderr, "LOGGER_set_logfile: ERROR - Cannot open logfile '%s' (%s)\n", lfname, strerror(errno)); #endif result = -1; } return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wraplength ID:1 Purpose: Sets the character count at which LOGGER will break a line Input: int length: Positive integer indicating number of chracters at which to wrap at Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wraplength( int length ) { if ( length >= 0 ) { LOGGER_glb.wraplength = length; } return LOGGER_glb.wraplength; } /*------------------------------------------------------------------------ Procedure: LOGGER_set_wrap ID:1 Purpose: Set log output wrapping to on or off Input: int level: 0 = no wrap, > 0 = wrap. Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_set_wrap( int level ) { if ( level >= 0 ) { LOGGER_glb.wrap = level; } return LOGGER_glb.wrap; } /*------------------------------------------------------------------------ Procedure: LOGGER_close_logfile ID:1 Purpose: Closes the modules log file pointer. Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_close_logfile( void ) { int result = 0; if (_LOGGER_outf) fclose(_LOGGER_outf); return result; } /*------------------------------------------------------------------------ Procedure: LOGGER_clean_output ID:1 Purpose: Checks through the output string for any characters which could cause potential 'isssues' with the data writing calls, items such as stray non-escaped % characters can cause havoc. Input: char *string: Raw string int maxsize: Maximum available buffer size for this string to expand to Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_clean_output( char *string, char **buffer ) { char *newstr; char *p, *q; char *next_space; int pc; int slen = strlen( string ); int line_size; int maxsize = slen *2; // First up, allocate maxsize bytes for a temporary new string. newstr = malloc(slen *2 +1); if ( newstr == NULL ) { // FIXME - Report an error here ... to -somewhere- return -1; } p = newstr; q = string; pc = 0; line_size = 0; while (slen--) { // Do we need to apply any wrapping to the output? If so then we // shall embark on a journey of strange space and distance // evaluations to determine if we should wrap now or later if ( LOGGER_glb.wrap > 0 ) { if (isspace((int)*q)) { next_space = strpbrk( (q+1), "\t\r\n\v " ); if (next_space != NULL) { if ((line_size +(next_space -q)) >= LOGGER_glb.wraplength) { *p = '\n'; p++; pc++; line_size = 0; } } } if ( line_size >= LOGGER_glb.wraplength ) { *p = '\n'; p++; pc++; line_size = 0; } } // If the string has a % in it, then we need to encode it as // a DOUBLE % symbol. if (*q == '%') { // if (strchr("fdlsxXn",*(q+1))) // { *p = '%'; p++; pc++; // } } // Copy the character of the string in *p = *q; // Move everything along. q++; p++; pc++; line_size++; if ( pc > (maxsize -1) ) { break; } } *p = '\0'; // This will have to be deallocated later! if (newstr) *buffer = newstr; return 0; } /*------------------------------------------------------------------------ Procedure: LOGGER_log ID:1 Purpose: Logs the params as supplied to the required output as defined by LOGGER_set_output Input: Output: Errors: ------------------------------------------------------------------------*/ int LOGGER_log( char *format, ...) { va_list ptr; char tmpoutput[10240]; char linebreak[]="\n"; char nolinebreak[]=""; char *lineend; char *output; // get our variable arguments va_start(ptr,format); // produce output, and spit to the log file #ifdef NO_SNPRINTF vsprintf(tmpoutput, format, ptr); #else vsnprintf(tmpoutput,sizeof(tmpoutput),format,ptr); #endif LOGGER_clean_output( tmpoutput, &output ); if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } if ( output[strlen(output)-1] == '\n' ) { lineend = nolinebreak; } else { lineend = linebreak; } // Send the output to the appropriate output destination switch (_LOGGER_mode) { case _LOGGER_STDERR: fprintf(stderr,"%s%s",output, lineend ); break; case _LOGGER_SYSLOG: syslog(_LOGGER_syslog_mode,"%s",output); break; case _LOGGER_STDOUT: fprintf(stdout,"%s%s",output, lineend); fflush(stdout); break; case _LOGGER_FILE: fprintf(_LOGGER_outf,"%s%s",output,lineend); fflush(_LOGGER_outf); break; case _LOGGER_NULL: // 20080303-2214:PLD: Added to allow us to direct all logging to NULL. break; default: fprintf(stdout,"LOGGER-Default: %s%s",output,lineend); } if (output) free(output); return 0; } altermime-0.3.10/logger.h0000644000000000000000000000113411107756671013712 0ustar rootroot #ifndef __LOGGER__ #define __LOGGER__ // LOGGER.h // #define _LOGGER_STDERR 1 #define _LOGGER_STDOUT 2 #define _LOGGER_FILE 3 #ifndef WIN32 #define _LOGGER_SYSLOG 4 #endif #define _LOGGER_NULL 5 #ifndef FL #define FL __FILE__,__LINE__ #endif int LOGGER_log( char *format, ...); int LOGGER_set_output_mode( int modechoice ); int LOGGER_set_output_file( FILE *f ); int LOGGER_set_syslog_mode( int syslogmode ); int LOGGER_set_logfile( char *lfname ); int LOGGER_set_wraplength( int length ); int LOGGER_set_wrap( int level ); int LOGGER_close_logfile( void ); FILE *LOGGER_get_file( void ); #endif altermime-0.3.10/ffget.c0000644000000000000000000005003011107756671013520 0ustar rootroot/*------------------------------------------------------------------------ Module: ffget.c Author: pldaniels Project: ripmime State: development Creation Date: 14/05/2001 Description: ffget is a small module which will be used to (we hope) speed up the fgetc() routine by line-buffering up first. 12/11/2002: Corrected input buffer termination with fgets where the last line of the input file does not terminate with a \n or \r. 27/09/2001: Added SGI specific compile time changes from char -> short contributed by Don Lafontaine ------------------------------------------------------------------------*/ #include #include #include #include #include #include "logger.h" #include "ffget.h" /* GLOBALS */ int ffget_linesize=0; int FFGET_doubleCR = 0; int FFGET_SDL_MODE = 0; // Single Char Delimeter int FFGET_SDL_WATCH = 0; // Set if we want to watch for double-CR exploits int FFGET_ALLOW_NUL = 0; // Dont Convert \0's to spaces. int FFGET_debug = 0; char SDL_MODE_DELIMITS[]="\n\r"; char NORM_MODE_DELIMITS[]="\n"; char *DELIMITERS=SDL_MODE_DELIMITS; /*------------------------------------------------------------------------ Procedure: FFGET_set_watch_SDL ID:1 Purpose: Set/Unset the flag to indicate that we should be watching out for a double-CR potential exploit mode when decoding files. Input: int level: 0 = don't watch, !0 = watch Output: Errors: ------------------------------------------------------------------------*/ int FFGET_set_watch_SDL( int level ) { FFGET_SDL_WATCH = level; return FFGET_SDL_WATCH; } /*-----------------------------------------------------------------\ Function Name : FFGET_set_allow_nul Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This tells the FFGET_raw() if it needs to remove \0's or not in the data it reads. This was added so as to ensure that \0 sequences were preserved in form-data from WWW pages. -------------------------------------------------------------------- Changes: Added 2004-Aug-09 by PLD. \------------------------------------------------------------------*/ int FFGET_set_allow_nul(int level ) { FFGET_ALLOW_NUL = level; return FFGET_ALLOW_NUL; } /*------------------------------------------------------------------------ Procedure: FFGET_set_debug ID:1 Purpose: Set debugging report/verbosity level Input: level Output: Returns the level set Errors: ------------------------------------------------------------------------*/ int FFGET_set_debug( int level ) { FFGET_debug = level; return FFGET_debug; } /*------------------------------------------------------------------------ Procedure: FFGET_getnewblock ID:1 Purpose: Reads a new block of data from the input file Input: FFGET_FILE record Output: Returns number of bytes read Errors: ------------------------------------------------------------------------*/ int FFGET_getnewblock( FFGET_FILE *f ) { int i; int bs = 0; char *p; // We read the maximum of FFGET_BUFFER_MAX -2, because later, when we // use fgets(), we may need to read in an /additional/ single byte // and if we dont allocate spare room, we may have a buffer overflow if (f->FILEEND > 0) { f->endpoint = f->buffer; f->startpoint = f->buffer +1; f->FFEOF = 1; return 0; } else { long block_pos; block_pos = ftell(f->f); /** Get our current read position so we can use it in FFGET_ftell if required **/ bs = fread( f->buffer, 1, FFGET_BUFFER_MAX -FFGET_BUFFER_PADDING, f->f ); if (bs < (FFGET_BUFFER_MAX -FFGET_BUFFER_PADDING)) { if (feof(f->f)) { f->FILEEND = 1; } else { LOGGER_log("%s:%d:FFGET_getnewblock:ERROR: File read failed with error:%s", FL, strerror(errno)); return 0; } } if (bs > 0) { // If we read in some data, then adjust the buffer to deal with it // // First we set the start point back to the start of the buffer, // then we set the end point to be the start +datasize we read, -1 // then we adjust the total bytes read (for the sake of record keeping // though it has no /real/ purpose) // f->buffer[bs] = '\0'; //20040208-1703:PLD:JS f->last_block_read_from = block_pos; // 200607150941:PLD f->startpoint = f->buffer; f->endpoint = f->startpoint +bs -1; f->bytes += bs; // Check the buffer for poisioning \0's // As these routines are being used for 7-bit valid text data, // we have to filter out any nasty \0's. if (FFGET_ALLOW_NUL == 0) { p = f->startpoint; for (i = 0; i < bs; i++) { if (*p == '\0') *p = ' '; p++; } *p = '\0'; } if (FFGET_DPEDANTIC) LOGGER_log("%s:%d:FFGET_getnewblock:DEBUG-PEDANTIC: Size: %ld bytes\n", FL, f->bytes); } } return bs; } /*------------------------------------------------------------------------ Procedure: FFGET_presetbuffer ID:1 Purpose: Presets the FFGET buffer with defined data Input: FFGET_FILE record Buffer to get data from Quantity of data Output: None Errors: ------------------------------------------------------------------------*/ int FFGET_presetbuffer( FFGET_FILE *f, char *buffer, int size ) { if (size > FFGET_BUFFER_MAX) size = FFGET_BUFFER_MAX; memcpy(f->buffer,buffer,size); f->startpoint = buffer; f->endpoint = buffer +size; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_setstream ID:1 Purpose: Sets the FILE * stream to the FFGET_FILE record Input: FFGET_FILE record Stream to use. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_setstream( FFGET_FILE *f, FILE *fi ) { // memset(f,0,sizeof(FFGET_FILE)); // be pedantic - clear the struct f->f = fi; f->bytes = 0; f->linecount = 0; f->endpoint = f->buffer; f->startpoint = f->endpoint +1; f->buffer_end = f->buffer +FFGET_BUFFER_MAX +FFGET_BUFFER_PADDING; f->trueblank = 0; f->ungetcset = 0; f->lastchar = '\0'; memset(f->buffer,'\0',FFGET_BUFFER_MAX +FFGET_BUFFER_PADDING); f->c = '\0'; f->FFEOF = 0; f->FILEEND = 0; f->last_block_read_from = -1; f->linebreak = FFGET_LINEBREAK_NONE; f->lastbreak[0] = '\0'; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_closestream ID:1 Purpose: Closes the stream contained in a FFGET record Input: FFGET record containing the stream to close. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_closestream( FFGET_FILE *f ) { f->startpoint = f->endpoint = NULL; f->f = NULL; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_feof ID:1 Purpose: Returns the status of FFGET's EOF Input: FFGET record Output: EOF status (0 == NOT eof, 1 == EOF has been reached) Errors: ------------------------------------------------------------------------*/ int FFGET_feof( FFGET_FILE *f ) { return f->FFEOF; } /*-----------------------------------------------------------------\ Function Name : FFGET_seek Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. size_t offset , ------------------ Exit Codes : -1 = error, check logs for reason of failure. Side Effects : -------------------------------------------------------------------- Comments: Seeks to 'offset' bytes from the first byte of the file. Reloads the buffer block. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int FFGET_seek( FFGET_FILE *f, long offset, int whence ) { int result = 0; /** Move to the new block location **/ result = fseek(f->f, offset, whence); if (result == -1) { LOGGER_log("%s:%d:FFGET_seek:ERROR: While attempting to seek to offset %ld from %d - [%s]", FL, offset, whence, strerror(errno)); return -1; } /** Read a whole new block **/ result = FFGET_getnewblock(f); return result; } /*-----------------------------------------------------------------\ Function Name : FFGET_tell Returns Type : size_t ----Parameter List 1. FFGET_FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Returns the position in the file that the current "file cursor" is pointing to. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ long FFGET_ftell( FFGET_FILE *f ) { long pos; pos = f->last_block_read_from +(f->startpoint -f->buffer); return pos; } /*------------------------------------------------------------------------ Procedure: FFGET_ungetc ID:1 Purpose: Pushes back into the buffer (effectively) a single character Input: FFGET record Character to retain for the next read. Output: Errors: ------------------------------------------------------------------------*/ int FFGET_ungetc( FFGET_FILE *f, char c ) { f->c = c; f->ungetcset = 1; return 0; } /*------------------------------------------------------------------------ Procedure: FFGET_getc ID:1 Purpose: Gets a single character from the FFGET buffer Input: FFGET record Output: Single character from the buffer, or EOF if end of file. Errors: ------------------------------------------------------------------------*/ #ifdef sgi short FFGET_fgetc( FFGET_FILE *f ) #else char FFGET_fgetc( FFGET_FILE *f ) #endif { int c; if (f->ungetcset) { f->ungetcset = 0; return f->c; } if ((!f->startpoint)||(f->startpoint > f->endpoint)) { FFGET_getnewblock(f); } if (f->FFEOF == 0) { c = *f->startpoint; f->startpoint++; } else { c = EOF; } return c; } /*------------------------------------------------------------------------ Procedure: FFGET_fgets ID:1 Purpose: Gets a single line from the input buffer. The line can be either \r \n \r\n terminated based on the status flags set/unset by previous reads. This function is the key to making tools like ripMIME be able to see double-vision, that is, to see emails like Outlook does, and also like RFC. Input: line: Buffer to write to max_size: Maximum number of bytes to write to line. f: FFGET record to use to read. Output: Pointer to line. Errors: ------------------------------------------------------------------------*/ char *FFGET_fgets( char *linein, int maxsize, FFGET_FILE *f ) { char *line = linein; char *crlfpos = NULL; int charstoCRLF = 0; int chardiff = 0; int result = 0; int max_size = maxsize; int endpoint_tainted = 0; int extra_char_kept=0; int c, nextchar; f->trueblank = 0; f->linebreak = FFGET_LINEBREAK_NONE; f->lastbreak[0] = '\0'; if (f->FFEOF != 0) { return NULL; } if ((FFGET_SDL_WATCH > 0)||(FFGET_SDL_MODE != 0)) { DELIMITERS = SDL_MODE_DELIMITS; } else DELIMITERS = NORM_MODE_DELIMITS; // fprintf(stderr,"FFGET_called, SDLMODE = %d, Offset = %d, maxsize = %d, DATA left = %d, first char is '%02X'\n", FFGET_SDL_MODE, (f->startpoint -f->buffer), max_size, (f->endpoint -f->startpoint)+1, (int)(*f->startpoint)); max_size = maxsize = maxsize -2; // memset(line, 0, max_size+1); // If we dont have enough data in the buffer to fill up the fgets request // we'll have to do a two step fill //fprintf(stderr,"DATA Reminianing : %d\n", (int)(f->endpoint -f->startpoint)+1); if ((f->startpoint > f->endpoint)) { result = FFGET_getnewblock(f); if (result == 0) { *linein = '\0'; return NULL; } } // This loop does not go around too many times, once, maybe twice max. while ((max_size > 0)&&(f->FFEOF == 0)) { crlfpos = strpbrk( f->startpoint, DELIMITERS); if (crlfpos) { extra_char_kept = 0; endpoint_tainted = 0; nextchar = -1; // if our next char is a CR or LF, then pick it up and // return it with the line. NOTE - this is to deal with // CRLF pairings which are common on DOS files. In fact, // this is a case of where UNIX is actually -wrong-. It // should have also used CRLF pairing to mark line ends, but // someone obviously (and understandably, to save space) // thought they'd leave make LF imply a CR as well. // Well done... another bugger up in life. // The logic of this nested IF statement is as follows... // If we do have another char available... // and if the pairing is not \n\n (which should be treated as two lines // and if the next char is a \n or a \r, // THEN we should increment the end of line pointer so that we // include the additional \n or \r into the line we're going to // return // If we are NOT in the Single-delimeter mode (SDL_MODE), and the next // char is available, then commence the delimeter testing routines if ((0==f->FILEEND)&&(0==f->FFEOF)&&( ((crlfpos +1) > f->endpoint))) { // We have an EOL character, get 1 more from the stream to test the next character nextchar = c = fgetc(f->f); if (c==EOF) { // fprintf(stderr,"EOF hit due to fgetc()\n"); f->FILEEND = 1; } else { if (c == '\0') c = ' '; // Check for character value vadality if ((c > 0) && (c <= 255)) { if (FFGET_DNORMAL) LOGGER_log("%s:%d:FFGET_fgets:DEBUG: Tainting endpoint +1 (%p -> %p, hard buffer end = %p, file read bytes = %ld)", FL, f->endpoint, f->endpoint+1, f->buffer_end, f->bytes); f->endpoint++; *(f->endpoint) = c; *(f->endpoint+1) = '\0'; endpoint_tainted = 1; } } } // If (crlfpos +1) is /not/ within our buffer bounds // If the next char from our CRLF pickup is within the bounds of // our endpoint, then proceed to test the CRLF combo if ( ((crlfpos +1) <= f->endpoint)) { // fprintf(stderr,"Found '%02X' [next is '%02X']\n",*crlfpos, *(crlfpos+1)); if ( *crlfpos == '\n' ) { f->linebreak = FFGET_LINEBREAK_LF; snprintf(f->lastbreak,sizeof(f->lastbreak),"\n"); if ( *(crlfpos +1) == '\r' ) { f->linebreak |= FFGET_LINEBREAK_CR; snprintf(f->lastbreak,sizeof(f->lastbreak),"\n\r"); crlfpos++; extra_char_kept = 1; } } // If our combo starts with a \r, then test it to see // if we have another \r after it, in which case, we // turn on SINGLE_DELIMETER_MODE. if ( (*crlfpos == '\r') ) { f->linebreak = FFGET_LINEBREAK_CR; snprintf(f->lastbreak,sizeof(f->lastbreak),"\r"); if ( *(crlfpos +1) == '\r' ) { // A \r\r sequence has just been detected, set our doubleCR flag // so that MIME_headers can read it and react accordingly. // Look out for single \r's from here on, as they are now seen as // EOL markers in Outlook. f->linebreak = FFGET_LINEBREAK_CR; FFGET_doubleCR=1; FFGET_SDL_MODE=1; crlfpos++; extra_char_kept = 1; } else if ( *(crlfpos +1) == '\n' ) { // If we see a \n after our \r, then treat this as a single // line delimeter if we are NOT in Single Delimeter mode // snprintf(f->lastbreak,sizeof(f->lastbreak),"\r\n"); f->linebreak |= FFGET_LINEBREAK_LF; if (!FFGET_SDL_MODE) { crlfpos++; extra_char_kept = 1; }// 20040208-1706:PLD //crlfpos++;// 20040208-1706:PLD // 20040306-0003:PLD - this line causes a CRCR test to fail; mailpack.virus.badtrans } else { // If we saw a \r, but then there was no other EOL type char (\r or \n) // then switch to SDL mode (Single delimeter). FFGET_SDL_MODE=1; } } // If combo starts with a \r } // If crlfpos +1 is within the bounds of our buffer. // Determine how many characters/bytes there are from the startpoint, // to the CRLF position. charstoCRLF = crlfpos -f->startpoint; // If the number of chars is -less- than that of the maximum line read // size which our calling program has specified, then we set the max_size // to be the number of chars. //DEBUG fprintf(stderr, "MAX_size = %d, charstoCRLF = %d\n", max_size, charstoCRLF); if ((charstoCRLF >= 0)&&(charstoCRLF < max_size)) max_size = charstoCRLF; if ((extra_char_kept == 0) && (nextchar != -1)) ungetc(nextchar,f->f); } // If CRLF pos found. // else crlfpos = (f->endpoint +1); // If the buffer amount remaining in our FFGET buffer is greater than // the maximum size available in our line buffer, then we // only copy the max_size amount across if (( f->endpoint -f->startpoint) >= max_size) { if (max_size < 0) LOGGER_log("%s:%d:FFGET_fgets:ERROR: Max size < 0\n", FL); memcpy(line, f->startpoint, max_size +1);//+1 f->startpoint += (max_size +1); //+1 *(line +max_size +1) = '\0'; //+1 max_size = 0; } else { // else, if the amount of data available is /LESS/ than what we can // accept in the line buffer then copy what remains out to the line // buffer and then tack on the results of a new read. chardiff = f->endpoint -f->startpoint; // fprintf(stderr,"CHARDiff = %d, FFEOF = %d, FILEEND = %d\n",chardiff, f->FFEOF, f->FILEEND); if (chardiff >= 0) { memcpy(line, f->startpoint, chardiff +1); *(line +chardiff +1) = '\0'; // 12-11-2002: Added this line to terminate the input buffer incase it wasn't already flushed with \0's line += (chardiff +1); max_size -= (chardiff +1); f->startpoint = f->endpoint +1; if (max_size < 0) max_size = 0; } FFGET_getnewblock(f); endpoint_tainted=0; } // If there wasn't enough data to satisfy ends. if (endpoint_tainted) { FFGET_getnewblock(f); endpoint_tainted = 0; } } // While we've got space to fill, and we've got data to read line = linein; f->trueblank = 0; if ((f->lastchar == '\n')||(f->lastchar == '\r')) { if ((line[0] == '\n')||(line[0] == '\r')) { f->trueblank = 1; } } f->lastchar = line[strlen(line) -1]; f->linecount++; // LOGGER_log("%s:%d:LINE='%s'",FL,linein); return linein; } /*------------------------------------------------------------------------ Procedure: FFGET_raw ID:1 Purpose: This is a hybrid binary-read and fgets type read. This function reads data from the input buffer until it encounters a \r \n \r\n at which point it will return to the calling parent with its buffer containing that line. This is required so that we dont miss any boundary specifiers which are located on new-lines. Input: f: FFGET record buffer: memory location to write data to max: maximum holding capacity of the raw buffer Output: Returns the number of bytes placed into the buffer. Errors: ------------------------------------------------------------------------*/ int FFGET_raw( FFGET_FILE *f, unsigned char *buffer, int max ) { unsigned char c; // read buffer int bytestogo = 0; int count = 0; // How many bytes read // Special situation here, if we have a return from MIME_headers which indicates // that we have data in a MIMEH_pushback, then we need to process that first, before we // go back into the data file. if ((!f->startpoint)||(f->startpoint > f->endpoint)) { bytestogo = FFGET_getnewblock(f); } else { bytestogo = f->endpoint -f->startpoint +1; } // Whilst we've got less bytes than the maximum availabe // for the buffer, we keep on reading // while (count < max) { if (!bytestogo) { bytestogo = FFGET_getnewblock(f); } if (!f->FFEOF) { c = *f->startpoint; f->startpoint++; *buffer = c; buffer++; count++; bytestogo--; // If we get a line delimeter, check to see that the next char (which is now // pointed to at f->startpoint isn't a delimeter as well which perhaps we should // be including in our line were' going to return // // 25/05/02 - Silly mistake, I had (!\n || !\r) when it should be && (ie, if the next // char is NEITHER of the \n or \r chars, then break. // if ((c == '\n')||(c == '\r')) { if ( (*(f->startpoint) != '\n') && (*(f->startpoint) != '\r') ) break; } } else break; } *buffer = '\0'; return count; } //--------------END. altermime-0.3.10/ffget.h0000644000000000000000000000342511107756671013533 0ustar rootroot #ifndef __FFGET__ #define __FFGET__ /* DEFINES */ #ifndef FL #define FL __FILE__,__LINE__ #endif #define FFGET_VERSION "1.0.0.8" #define FFGET_LASTUPDATED "200811061423" #define FFGET_DNORMAL ((FFGET_debug >= FFGET_DEBUG_NORMAL )) #define FFGET_DPEDANTIC ((FFGET_debug >= FFGET_DEBUG_PEDANTIC)) #define FFGET_MAX_LINE_LEN 1024 #define FFGET_BUFFER_MAX 8192 #define FFGET_BUFFER_PADDING 1 #define FFGET_DEBUG_NORMAL 1 #define FFGET_DEBUG_PEDANTIC 10 #define FFGET_LINEBREAK_NONE 0 #define FFGET_LINEBREAK_LF 1 #define FFGET_LINEBREAK_CR 2 struct _FFGET_FILE { FILE *f; char buffer[FFGET_BUFFER_MAX+4]; char *startpoint; char *endpoint; char *buffer_end; size_t last_block_read_from; int FILEEND; int FFEOF; char c; unsigned long int bytes; unsigned long int linecount; int ungetcset; int trueblank; char lastchar; int linebreak; char lastbreak[10]; }; typedef struct _FFGET_FILE FFGET_FILE; // Special Flag to indicate a Double CR Line. extern int FFGET_doubleCR; extern int FFGET_SDL_MODE; // Single Char Delimeter extern char SDL_MODE_DELIMITS[]; extern char NORM_MODE_DELIMITS[]; extern char *DELIMITERS; int FFGET_setstream( FFGET_FILE *f, FILE *fi ); #ifdef sgi short FFGET_fgetc( FFGET_FILE *f ); #else char FFGET_fgetc( FFGET_FILE *f ); #endif int FFGET_closestream( FFGET_FILE *f ); int FFGET_ungetc( FFGET_FILE *f, char c ); int FFGET_presetbuffer( FFGET_FILE *f, char *buffer, int size ); char *FFGET_fgets( char *linein, int max_size, FFGET_FILE *f ); int FFGET_raw( FFGET_FILE *f, unsigned char *buffer, int max ); int FFGET_feof( FFGET_FILE *f ); int FFGET_getnewblock( FFGET_FILE *f ); int FFGET_set_watch_SDL( int level ); int FFGET_set_allow_nul(int level ); long FFGET_ftell( FFGET_FILE *f ); int FFGET_fseek( FFGET_FILE *f, long offset, int whence ); #endif altermime-0.3.10/pldstr.c0000644000000000000000000005164311107756671013750 0ustar rootroot #include #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" /*-----------------------------------------------------------------\ Function Name : *PLD_strstr Returns Type : char ----Parameter List 1. char *haystack, 2. char *needle, 3. int insensitive, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strstr(char *haystack, char *needle, int insensitive) { char *hs, *ne; char *result; // LOGGER_log("%s:%d:\nHS=%s\nNE=%s\nIS=%d\n",FL, haystack, needle, insensitive ); if (insensitive > 0) { hs = strdup(haystack); PLD_strlower(hs); ne = strdup(needle); PLD_strlower(ne); } else { hs = haystack; ne = needle; } result = strstr(hs, ne); // if (result) LOGGER_log("%s:%d:HIT: %s",FL, result); // else LOGGER_log("%s:%d:MISS (looking for %s|%s)",FL, needle,ne); if ((result != NULL)&&(insensitive > 0)) { result = result -hs +haystack; // free(hs); // free(ne); // LOGGER_log("%s:%d:HIT - %s",FL, result ); } if (insensitive) { free(hs); free(ne); } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strncpy ID:1 Purpose: Copy characters from 'src' to 'dst', writing not more than 'len' characters to the destination, including the terminating \0. Thus, for any effective copying, len must be > 1. Input: char *dst: Destination string char *src: Source string size_t len: length of string Output: Returns a pointer to the destination string. Errors: ------------------------------------------------------------------------*/ char *PLD_strncpy (char *dst, const char *src, size_t len) { // Thanks go to 'defrost' of #c for providing the replacement // code which you now see here. It covers the errors better // than my own previous code. // If we have no buffer space, then it's futile attempting // to copy anything, just return NULL if (len==0) return NULL; // Providing our destination pointer isn't NULL, we can // commence copying data across if (dst) { char *dp = dst; // If our source string exists, start moving it to the // destination string character at a time. if (src) { char *sp = (char *)src; while ((--len)&&(*sp)) { *dp=*sp; dp++; sp++; } } *dp='\0'; } return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncat ID:1 Purpose: Buffer size limited string concat function for two strings. Input: char *dst: Destination string char *src: Source string size_t len: Destination string buffer size - total string size cannot exceed this Output: Errors: If the length of both strings in total is greater than the available buffer space in *dst, we copy the maximum possible amount of chars from *src such that buffer does not overflow. A suffixed '\0' will always be appended. ------------------------------------------------------------------------*/ char *PLD_strncat( char *dst, const char *src, size_t len ) { char *dp = dst; const char *sp = src; size_t cc; if (len == 0) return dst; len--; // Locate the end of the current string. cc = 0; while ((*dp)&&(cc < len)) { dp++; cc++; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: PLD_strncate ID:1 Purpose: Catencates a source string to the destination string starting from a given endpoint. This allows for faster catencation of strings by avoiding the computation required to locate the endpoint of the destination string. Input: char *dst: Destination string char *src: Source string size_t len: Destination buffer size char *endpoint: Endpoint of destination string, location from where new string will be appended Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ) { char *dp = dst; const char *sp = src; size_t cc = 0; if (len == 0) return dst; len--; // If endpoint does not relate correctly, then force manual detection // of the endpoint. if ((!endpoint)||(endpoint == dst)||((endpoint -dst +1)>(int)len)) { // Locate the end of the current string. cc = 0; while ((*dp != '\0')&&(cc < len)) { dp++; cc++; } } else { cc = endpoint -dst +1; dp = endpoint; } // If we have no more buffer space, then return the destination if (cc >= len) return dst; // While we have more source, and there's more char space left in the buffer while ((*sp)&&(cc < len)) { cc++; *dp = *sp; dp++; sp++; } // Terminate dst, as a gaurantee of string ending. *dp = '\0'; return dst; } /*------------------------------------------------------------------------ Procedure: XAM_strncasecmp ID:1 Purpose: Portable version of strncasecmp(), this may be removed in later versions as the strncase* type functions are more widely implemented Input: Output: Errors: ------------------------------------------------------------------------*/ int PLD_strncasecmp( char *s1, char *s2, int n ) { char *ds1 = s1, *ds2 = s2; char c1, c2; int result = 0; while(n > 0) { c1 = tolower(*ds1); c2 = tolower(*ds2); if (c1 == c2) { n--; ds1++; ds2++; } else { result = c2 - c1; n = 0; } } return result; } /*------------------------------------------------------------------------ Procedure: XAM_strtok ID:1 Purpose: A thread safe version of strtok() Input: Output: Errors: ------------------------------------------------------------------------*/ char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ) { char *stop; char *dc; char *result = NULL; if ( line ) { st->start = line; } //Strip off any leading delimeters dc = delimeters; while ((st->start)&&(*dc != '\0')) { if (*dc == *(st->start)) { st->start++; dc = delimeters; } else dc++; } // Where we are left, is the start of our token. result = st->start; if ((st->start)&&(st->start != '\0')) { stop = strpbrk( st->start, delimeters ); /* locate our next delimeter */ // If we found a delimeter, then that is good. We must now break the string here // and don't forget to store the character which we stopped on. Very useful bit // of information for programs which process expressions. if (stop) { // Store our delimeter. st->delimeter = *stop; // Terminate our token. *stop = '\0'; // Because we're emulating strtok() behaviour here, we have to // absorb all the concurrent delimeters, that is, unless we // reach the end of the string, we cannot return a string with // no chars. stop++; dc = delimeters; while (*dc != '\0') { if (*dc == *stop) { stop++; dc = delimeters; } else dc++; } // While if (*stop == '\0') st->start = NULL; else st->start = stop; } else { st->start = NULL; st->delimeter = '\0'; } } else { st->start = NULL; result = NULL; } return result; } /*------------------------------------------------------------------------ Procedure: PLD_strlower ID:1 Purpose: Converts a string to lowercase Input: char *convertme : string to convert Output: Errors: Comments: Really need to validate against high-ASCII chars. Tested against strings like; Logo de la République française Македонски ------------------------------------------------------------------------*/ int PLD_strlower( char *convertme ) { char *c = convertme; while ( *c != '\0') {*c = (unsigned char)tolower((int)*c); c++;} return 0; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char *source, Original buffer, \0 terminated 2. char *searchfor, String sequence to search for 3. char *replacewith, String sequence to replace 'searchfor' with 4. int replacenumber , How many times to replace 'searchfor', 0 == unlimited ------------------ Exit Codes : Returns a pointer to the new buffer space. The original buffer will still remain intact - ensure that the calling program free()'s the original buffer if it's no longer needed Side Effects : -------------------------------------------------------------------- Comments: Start out with static text matching - upgrade to regex later. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace_general( struct PLD_strreplace *replace_details ) { char *new_buffer=NULL; char *source_end; char *segment_start, *segment_end, *segment_p; char *new_p; char *preexist_location=NULL; char *postexist_location=NULL; int replace_count=0; int size_required; int size_difference; int source_length; int searchfor_length; int replacewith_length; int segment_ok; if (replace_details->source == NULL) return NULL; source_length = strlen( replace_details->source ); source_end = replace_details->source +source_length; searchfor_length = strlen(replace_details->searchfor); replacewith_length = strlen(replace_details->replacewith); size_difference = replacewith_length -searchfor_length; size_required = source_length; replace_count = replace_details->replacenumber; if ((replace_details->preexist != NULL)&&(strlen(replace_details->preexist) < 1)) replace_details->preexist = NULL; if ((replace_details->postexist != NULL)&&(strlen(replace_details->postexist) < 1)) replace_details->postexist = NULL; // If we have a 'pre-exist' request, then we need to check this out first // because if the pre-exist string cannot be found, then there's very // little point us continuing on in our search ( because without the // preexist string existing, we are thus not qualified to replace anything ) if (replace_details->preexist != NULL) { preexist_location = PLD_strstr(replace_details->source, replace_details->preexist, replace_details->insensitive); if (preexist_location == NULL) { return replace_details->source; } } // Determine if initial POSTexist tests will pass, if we don't pick up // anything here, then there's no point in continuing either if (replace_details->postexist != NULL) { char *p = replace_details->source; postexist_location = NULL; do { p = PLD_strstr(p, replace_details->postexist, replace_details->insensitive); if (p != NULL) { postexist_location = p; p = p +strlen(replace_details->postexist); } } while (p != NULL); if (postexist_location == NULL) { return replace_details->source; } } // Step 1 - determine the MAXIMUM number of times we might have to replace this string ( or the limit // set by replacenumber // // Note - we only need this number if the string we're going to be inserting into the // source is larger than the one we're replacing - this is so that we can ensure that // we have sufficient memory available in the buffer. if (size_difference > 0) { if (replace_count == 0) { char *p, *q; p = replace_details->source; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); while (q != NULL) { replace_count++; //size_required += size_difference; p = q +searchfor_length; q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive); } } size_required = source_length +(size_difference *replace_count) +1; } else size_required = source_length +1; // Allocate the memory required to hold the new string [at least], check to see that // all went well, if not, then return an error new_buffer = malloc( sizeof(char) *size_required); if (new_buffer == NULL) { LOGGER_log("%s:%d:PLD_strreplace:ERROR: Cannot allocate %d bytes of memory to perform replacement operation", FL, size_required); return replace_details->source; } // Our segment must always start at the beginning of the source, // on the other hand, the segment_end can be anything from the // next byte to NULL ( which is specially treated to mean to // the end of the source ) segment_start = replace_details->source; // Locate the first segment segment_ok = 0; segment_end = PLD_strstr(replace_details->source, replace_details->searchfor, replace_details->insensitive); // Determine if the first segment is valid in the presence of the // pre-exist and post-exist requirements while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } segment_p = segment_start; new_p = new_buffer; while (segment_start != NULL) { int replacewith_count; char *replacewith_p; if (segment_end == NULL) segment_end = source_end; replace_count--; // Perform the segment copy segment_p = segment_start; while ((segment_p < segment_end)&&(size_required > 0)) { *new_p = *segment_p; new_p++; segment_p++; size_required--; } // Perform the string replacement if (segment_end < source_end) { replacewith_count = replacewith_length; replacewith_p = replace_details->replacewith; while ((replacewith_count--)&&(size_required > 0)) { *new_p = *replacewith_p; new_p++; replacewith_p++; size_required--; } } if (size_required < 1 ) { LOGGER_log("%s:%d:PLD_strreplace_general: Allocated memory ran out while replacing '%s' with '%s'",FL, replace_details->searchfor, replace_details->replacewith); *new_p='\0'; break; } // Find the next segment segment_start = segment_end +searchfor_length; // If we've reached the end of the number of replacements we're supposed // to do, then we prepare the termination of the while loop by setting // our segment end to the end of the source. // // NOTE: Remember that the replace_count is pre-decremented at the start // of the while loop, so, if the caller requested '0' replacements // this will now be -1, thus, it won't get terminated from this == 0 // match. Just thought you'd like to be reminded of that incase you // were wondering "Huh? this would terminate an unlimited replacement" if (replace_count == 0) { segment_end = NULL; } else { // If our new segment to copy starts after the // end of the source, then we actually have // nothing else to copy, thus, we prepare the // segment_start varible to cause the while loop // to terminate. // // Otherwise, we try and locate the next segment // ending point, and set the starting point to // be on the 'other side' of the 'searchfor' string // which we found in the last search. // if (segment_start > source_end) { segment_start = NULL; } else { // Try find the next segment segment_ok = 0; segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); // If we have a pre/post-exist requirement, then enter into this // series of tests. NOTE - at least one of the pre or post tests // must fire to give an meaningful result - else we'll end up with // a loop which simply goes to the end of the searchspace buffer while ((segment_end != NULL)&&(segment_ok == 0)\ &&((replace_details->preexist != NULL)||(replace_details->postexist != NULL))) { int pre_ok = 0; int post_ok = 0; // The PREexist test assumes a couple of factors - please ensure these are // relevant if you change any code prior to this point. // // 1. preexist_location has already been computed and is not NULL // // 2. By relative position, the first preexist_location will be a valid location // on which to validate for ALL replacements beyond that point, thus, we // never actually have to recompute preexist_location again. // // 3. Conversely, the last computed postexist_location is valid for all // matches before it // if (preexist_location == NULL) pre_ok = 1; else if (preexist_location < segment_end){ pre_ok = 1;} if (postexist_location == NULL) post_ok = 1; else if (postexist_location > segment_end){ post_ok = 1;} if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); } else segment_ok = 1; } } // If-else segment_start > source_end } } *new_p = '\0'; // if (replace_details->source != NULL) free (replace_details->source); // replace_details->source = new_buffer; return new_buffer; } /*-----------------------------------------------------------------\ Function Name : *PLD_strreplace Returns Type : char ----Parameter List 1. char **source, 2. char *searchfor, 3. char *replacewith, 4. int replacenumber , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ) { struct PLD_strreplace replace_details; char *tmp_source; replace_details.source = *source; replace_details.searchfor = searchfor; replace_details.replacewith = replacewith; replace_details.replacenumber = replacenumber; replace_details.preexist = NULL; replace_details.postexist = NULL; replace_details.insensitive = 0; tmp_source = PLD_strreplace_general( &replace_details ); if (tmp_source != *source) *source = tmp_source; return *source; } /*-----------------------------------------------------------------\ Function Name : *PLD_dprintf Returns Type : char ----Parameter List 1. const char *format, 2. ..., ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This is a dynamic string allocation function, not as fast as some other methods, but it works across the board with both glibc 2.0 and 2.1 series. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *PLD_dprintf(const char *format, ...) { int n, size = 1024; // Assume we don't need more than 1K to start with char *p; va_list ap; // Attempt to allocate and then check p = malloc(size *sizeof(char)); if (p == NULL) return NULL; while (1) { // Attempt to print out string out into the allocated space va_start(ap, format); n = vsnprintf (p, size, format, ap); va_end(ap); // If things went well, then return the new string if ((n > -1) && (n < size)) return p; // If things didn't go well, then we have to allocate more space // based on which glibc we're using ( fortunately, the return codes // tell us which glibc is being used! *phew* // // If n > -1, then we're being told precisely how much space we need // else (older glibc) we have to just guess again ... if (n > -1) size = n+1; // Allocate precisely what is needed else size *= 2; // Double the amount allocated, note, we could just increase by 1K, but if we have a long string, we'd end up using a lot of realloc's // We could just realloc 'blind', but that'd be wrong and potentially cause a DoS, so // instead, we'll be good and first attempt to realloc to a temp variable then, if all // is well, we go ahead and update if (1) { char *tmp_p; tmp_p = realloc(p, size); if (tmp_p == NULL){ if (p != NULL) free(p); return NULL; } else p = tmp_p; } } } //-----------------END. altermime-0.3.10/pldstr.h0000644000000000000000000000165411107756671013752 0ustar rootroot#ifndef __PLDSTR__ #define __PLDSTR__ #ifndef FL #define FL __FILE__,__LINE__ #endif struct PLD_strtok { char *start; char delimeter; }; struct PLD_strreplace { char *source; char *searchfor; char *replacewith; char *preexist; char *postexist; int replacenumber; int insensitive; }; char *PLD_strstr(char *haystack, char *needle, int insensitive); char *PLD_strncpy( char *dst, const char *src, size_t len ); char *PLD_strncat( char *dst, const char *src, size_t len ); char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint ); char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters ); int PLD_strncasecmp( char *s1, char *s2, int n ); int PLD_strlower( char *convertme ); char *PLD_strreplace_general( struct PLD_strreplace *replace_details ); char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber ); char *PLD_dprintf(const char *fmt, ...); #endif altermime-0.3.10/filename-filters.c0000644000000000000000000001427411107756671015665 0ustar rootroot/*------------------------------------------------------------------------ Module: /extra/development/xamime/xamime_working/ripmime/filename-filters.c Author: Paul L Daniels Project: ripMIME State: Release Creation Date: 01 Jan 03 Description: Filename Filters is a module which is designed to check and 'safety-enhance' filenames which are passed to it. This may include things like removing directory risers ( ../.. ), root directory attempts ( / ), and parameter passing. ------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "pldstr.h" #include "logger.h" #include "filename-filters.h" #ifndef FL #define FL __FILE__, __LINE__ #endif #define FNFILTER_DEBUG_PEDANTIC 10 #define FNFILTER_DEBUG_NORMAL 1 // Debug precodes #define FNFILTER_DPEDANTIC ((glb.debug >= FNFILTER_DEBUG_PEDANTIC)) #define FNFILTER_DNORMAL ((glb.debug >= FNFILTER_DEBUG_NORMAL )) #define DFN if ((glb.debug >= FNFILTER_DEBUG_NORMAL)) struct FNFILTER_globals { int debug; int verbose; int paranoid; int x_mac; }; static struct FNFILTER_globals glb; int FNFILTER_init( void ) { glb.debug = 0; glb.verbose = 0; glb.paranoid = 0; glb.x_mac = 0; return 0; } int FNFILTER_set_debug( int level ) { glb.debug = level; return glb.debug; } int FNFILTER_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } int FNFILTER_set_mac( int level ) { glb.x_mac = level; return glb.x_mac; } int FNFILTER_set_paranoid( int level ) { glb.paranoid = level; return glb.paranoid; } /*------------------------------------------------------------------------ Procedure: quick_clean_filename ID:1 Purpose: Removes non-7bit characers from the filename Input: char *fname: Null terminated string Output: Errors: ------------------------------------------------------------------------*/ int FNFILTER_paranoid_filter( char *fname, int size ) { char tmp[1024]; char *p; /* Scan for . and .. filenames ** 20040727-12H54 ** Patch supplied by Marco Ariano ** Patch modified by Paul L Daniels ** */ if ((1 == size)&&('.' == *fname)) { *fname = '_'; return 0; } else if ((2 == size)&&(0 == strncmp(fname,"..",2))) { snprintf(fname,3,"__"); return 0; } /* scan out any directory separators */ p = strrchr(fname,'/'); if (p) { // Check to see that this seperator isn't the -last- char in the string if (*(p+1) == '\0') *p = '\0'; else { p++; PLD_strncpy(tmp,p,sizeof(tmp)); PLD_strncpy(fname,tmp,size); } } else if ( (p = strrchr(fname,'\\'))) { // Check to see that this seperator isn't the -last- char in the string if (*(p+1) == '\0') *p = '\0'; else { p++; PLD_strncpy(tmp,p,sizeof(tmp)); PLD_strncpy(fname,tmp,size); } } if ( glb.paranoid > 0 ) { // If we're really paranoid, then we go along and convert anything we don't like // the look of into 7-bit // // These days we shouldn't be using this any more as there are many filenames // which require > 7-bit charsets. while (*fname) { if( !isalnum((int)*fname) && (*fname != '.') ) *fname='_'; if( (*fname < ' ')||(*fname > '~') ) *fname='_'; fname++; } } return 0; } /*------------------------------------------------------------------------ Procedure: MIME_decode_filename ID:1 Purpose: Removed spurilous characters from filename strings. Input: char *fname: null terminated character string Output: Errors: ------------------------------------------------------------------------*/ int FNFILTER_filter( char *fname, int size ) { int fnl; char tmp[1024]; char *p; if (fname == NULL) return 0; fnl = strlen(fname); DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: fname[%d chars] = '%s'\n", FL, fnl, fname ); /** If we're handling a Mac file, prefilter **/ if (glb.x_mac == 1) { char *q = fname; DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Filtering x-mac filename '%s'",FL,fname); while (*q) { if (*q == '/') *q = '-'; /** Convert the Mac / separator to a hyphen **/ q++; } DFN LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: x-mac filename is now '%s'",FL,fname); } /* We only look at trimming the quotes off a filename if it has more than 2 chars * because obviously we'll need to strip off 2 chars (leading and finishing quote) */ if ( fnl > 2 ) { /* if the MIME_filename starts and ends with "'s */ if ((fname[0] == '\"') && (fname[fnl-1] == '\"')) { if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Trimming quotes off filename\n", FL ); /* reduce the file namelength by two*/ fnl = fnl -2; // 17-11-2002: was =-2, thanks to Vasily Chernikov for spotting the glaring error! /* shuffle the MIME_filename chars down */ memmove(fname,fname+1,fnl); /* terminate the string */ fname[fnl] = '\0'; if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Trimming filename done, fname = '%s'\n", FL, fname ); } /* if */ } /* if */ p = strrchr(fname,'/'); if (p) { p++; PLD_strncpy( tmp, p, sizeof(tmp) ); PLD_strncpy( fname, tmp, size); } else { // Check for Windows/DOS backslash seperator p = strrchr( fname, '\\' ); if ( p ) { if ( *(p+1) != '"' ) { p++; PLD_strncpy( tmp, p, sizeof(tmp) ); PLD_strncpy( fname, tmp, size ); } } } // Scan for ? symbols - these are often used to make the email client pass paremeters to the filename // Check though to see that the previous character is not a '=' symbol, because if it is, then we // actually have an ISO encoded filename p = strchr( fname, '?' ); if (p != NULL) { if (p > fname) { if (*(p-1) != '=') { *p = '\0'; } else { // leave the ? alone, as it's part of an ISO encoded filename } } else { // First char of the filename is a '?', change this to a hypen. *p = '-'; } } if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: Starting paranoia filter\n", FL ); FNFILTER_paranoid_filter( fname, strlen( fname ) ); if (FNFILTER_DNORMAL) LOGGER_log("%s:%d:FNFILTER_filter:DEBUG: paranoia filter done. Filename='%s'\n", FL, fname ); return 0; } altermime-0.3.10/filename-filters.h0000644000000000000000000000034111107756671015660 0ustar rootrootint FNFILTER_init( void ); int FNFILTER_set_debug( int level ); int FNFILTER_set_verbose( int level ); int FNFILTER_set_paranoid( int level ); int FNFILTER_set_mac( int level ); int FNFILTER_filter( char *fname, int size ); altermime-0.3.10/MIME_headers.c0000644000000000000000000026510511107756671014662 0ustar rootroot/*----------------------------------------------------------------------- ** ** ** MIME_headers ** ** Written by Paul L Daniels, originally for the Xamime project ** (http://www.xamime.com) but since spawned off to the ripMIME/alterMIME ** family of email parsing tools. ** ** Copyright PLD, 1999,2000,2001,2002,2003 ** Licence: BSD ** For more information on the licence and copyrights of this code, please ** email copyright@pldaniels.com ** CHANGES ** 2003-Jun-24: PLD: Added subject parsing ** */ #include #include #include #include #include #include #include #include #include #include #include "ffget.h" #include "pldstr.h" #include "libmime-decoders.h" #include "logger.h" #include "strstack.h" #include "boundary-stack.h" #include "filename-filters.h" #include "MIME_headers.h" #ifndef FL #define FL __FILE__, __LINE__ #endif // Debug precodes #define MIMEH_DPEDANTIC ((glb.debug >= _MIMEH_DEBUG_PEDANTIC)) #define MIMEH_DNORMAL ((glb.debug >= _MIMEH_DEBUG_NORMAL )) #define DMIMEH if ((glb.debug >= _MIMEH_DEBUG_NORMAL)) char *MIMEH_defect_description_array[_MIMEH_DEFECT_ARRAY_SIZE]; struct MIMEH_globals { int doubleCR; int doubleCR_save; char doubleCRname[_MIMEH_STRLEN_MAX +1]; char appledouble_filename[_MIMEH_STRLEN_MAX +1]; char subject[_MIMEH_STRLEN_MAX +1]; char *headerline; char *headerline_original; // Holds the original header-form without decoding. int save_headers; int save_headers_original; int test_mailbox; int debug; int webform; int doubleCR_count; int header_fix; int verbose; int verbose_contenttype; int header_longsearch; // keep searching until valid headers are found - this is used to filter out qmail bounced emails - breaks RFC's but people are wanting it :-( int longsearch_limit; // how many segments do we attempt to look ahead... char output_dir[_MIMEH_STRLEN_MAX +1]; FILE *header_file; FILE *original_header_file; int original_header_save_to_file; }; static struct MIMEH_globals glb; /*-----------------------------------------------------------------\ Function Name : MIMEH_version Returns Type : int ----Parameter List 1. void, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_version(void) { fprintf(stdout,"mimeheaders: %s\n", MIMEH_VERSION); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_init( void ) { glb.doubleCR = 0; glb.headerline = NULL; glb.headerline_original = NULL; glb.header_file = NULL; glb.original_header_file = NULL; glb.original_header_save_to_file = 0; glb.save_headers = 0; glb.save_headers_original = 0; glb.test_mailbox = 0; glb.debug = 0; glb.webform = 0; glb.doubleCR_count = 0; glb.doubleCR_save = 1; glb.header_fix = 1; glb.verbose = 0; glb.verbose_contenttype = 0; glb.output_dir[0]='\0'; glb.doubleCRname[0]='\0'; glb.appledouble_filename[0]='\0'; glb.header_longsearch=0; glb.longsearch_limit=1; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_doubleCR Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_doubleCR( void ) { return glb.doubleCR; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_doubleCR Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_doubleCR( int level ) { glb.doubleCR = level; return glb.doubleCR; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headerfix Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headerfix( int level ) { glb.header_fix = level; return glb.header_fix; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_doubleCR_save Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_doubleCR_save( int level ) { glb.doubleCR_save = level; return glb.doubleCR_save; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_doubleCR_save Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_doubleCR_save( void ) { return glb.doubleCR_save; } /*-----------------------------------------------------------------\ Function Name : *MIMEH_get_doubleCR_name Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_doubleCR_name( void ) { return glb.doubleCRname; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_outputdir Returns Type : int ----Parameter List 1. char *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_outputdir( char *dir ) { if (dir) snprintf(glb.output_dir,_MIMEH_STRLEN_MAX,"%s",dir); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_webform Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_webform( int level ) { glb.webform = level; return glb.webform; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_mailbox Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_mailbox( int level ) { glb.test_mailbox = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_verbosity Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_verbosity( int level ) { glb.verbose = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_verbosity_contenttype( int level ) { glb.verbose_contenttype = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_verbosity_contenttype Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_verbosity_contenttype( void ) { return glb.verbose_contenttype; } /*------------------------------------------------------------------------ Procedure: MIMEH_set_headers_save ID:1 Purpose: Sets MIMEH's headers save file (where MIMEH will save the headers it reads in from the mailpack) Input: Output: Errors: ------------------------------------------------------------------------*/ int MIMEH_set_headers_save( FILE *f ) { glb.header_file = f; glb.save_headers = 1; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_original_save_to_file Returns Type : int ----Parameter List 1. FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_original_save_to_file( FILE *f ) { if (f == NULL) glb.original_header_save_to_file = 0; else glb.original_header_save_to_file = 1; glb.original_header_file = f; return glb.original_header_save_to_file; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_nosave Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_nosave( void ) { glb.header_file = NULL; glb.save_headers = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers_save Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_headers_save( void ) { return glb.save_headers; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_headers_save_original Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_headers_save_original( int level ) { glb.save_headers_original = level; return glb.save_headers_original; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers_ptr Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_headers_ptr( void ) { return glb.headerline; } /*-----------------------------------------------------------------\ Function Name : *MIMEH_get_headers_original_ptr Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIMEH_get_headers_original_ptr( void ) { return glb.headerline_original; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_header_longsearch Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: The header long-search is a facility switch that will make the header searching to continue on until it either reaches the end of the file or it finds valid (??) headers to work on. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_header_longsearch( int level ) { glb.header_longsearch = level; return glb.header_longsearch; } /*-----------------------------------------------------------------\ Function Name : MIMEH_set_defect Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. int defect , The defect code to set ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_set_defect( struct MIMEH_header_info *hinfo, int defect ) { if ((defect >= 0)&&(defect < _MIMEH_DEFECT_ARRAY_SIZE)) { hinfo->defects[defect]++; hinfo->header_defect_count++; DMIMEH LOGGER_log("%s:%d:MIMEH_set_defect:DEBUG: Setting defect index '%d' to '%d'",FL, defect, hinfo->defects[defect]); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_contenttype Returns Type : int ----Parameter List 1. int range_type, 2. int content_type , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_contenttype( int range_type, int content_type ) { int diff; diff = content_type -range_type; if ((diff < _CTYPE_RANGE)&&(diff > 0)) return 1; else return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_binary Returns Type : int ----Parameter List 1. struct FFGET_FILE *f , ------------------ Exit Codes : 1 = yes, it's binary, 0 = no. Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_binary( char *fname ) { char buffer[1024]; int read_count; FILE *f; f = fopen(fname,"r"); if (!f) return 1; read_count = fread(buffer, 1, 1024, f); fclose(f); while (read_count) { read_count--; if (buffer[read_count] == 0) return 1; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_are_headers_RFC822 Returns Type : int ----Parameter List 1. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_are_headers_RFC822( char *headers ) { char conditions[7][16] = { "received", "from", "subject", "date", "content", "boundary" }; int hitcount = 0; int condition_item; char *lc_headers = NULL; if (headers == NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG: Headers are NULL"); return 0; } DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG:----\n%s\n----",FL,headers); lc_headers = strdup(headers); if (lc_headers == NULL) return 0; //PLD_strlower((unsigned char *)lc_headers); PLD_strlower(lc_headers); DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG:----(lowercase)----\n%s\n----",FL,lc_headers); for (condition_item=0; condition_item < 6; condition_item++) { char *p; DMIMEH LOGGER_log("%s:%d:MIMEH_are_headers_RFC822:DEBUG: Condition test item[%d] = '%s'",FL,condition_item,conditions[condition_item]); p = strstr(lc_headers, conditions[condition_item]); if (p != NULL) { if (p > lc_headers) { if ((*(p-1) == '\n')||(*(p-1) == '\r')) hitcount++; } else if (p == lc_headers) hitcount++; } } if (lc_headers != NULL) free(lc_headers); return hitcount; } /*-----------------------------------------------------------------\ Function Name : MIMEH_save_doubleCR Returns Type : int ----Parameter List 1. FFGET_FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_save_doubleCR( FFGET_FILE *f ) { //char c; int c; FILE *fo; struct stat st; // Determine a file name we can use. do { glb.doubleCR_count++; snprintf(glb.doubleCRname,_MIMEH_STRLEN_MAX,"%s/doubleCR.%d",glb.output_dir,glb.doubleCR_count); } while (stat(glb.doubleCRname, &st) == 0); fo = fopen(glb.doubleCRname,"w"); if (!fo) { LOGGER_log("%s:%d:MIMEH_save_doubleCR:ERROR: unable to open '%s' to write (%s)", FL,glb.doubleCRname,strerror(errno)); return -1; } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_save_doubleCR:DEBUG: Saving DoubleCR header: %s\n", FL,glb.doubleCRname); while (1) { c = FFGET_fgetc(f); fprintf(fo,"%c",c); if ((c == EOF)||(c == '\n')) { break; } } fclose(fo); return 0; } /*-----------------------------------------------------------------\ Function Name : * Returns Type : char ----Parameter List 1. MIMEH_absorb_whitespace( char *p , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char * MIMEH_absorb_whitespace( char *p ) { if (p) { while ((*p != '\0')&&((*p == ' ')||(*p == '\t'))) p++; } return p; } /*-----------------------------------------------------------------\ Function Name : MIMEH_strip_comments Returns Type : int ----Parameter List 1. char *input , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Removes comments from RFC[2]822 headers -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_strip_comments( char *input ) { char *p,*p_org; int in_quote=0; if (input == NULL) return 0; p = p_org = input; do { char *q = NULL; // Locate (if any) the first occurance of the ( while ((p_org != NULL)&&((*p_org != '(')||(in_quote==1))) { switch (*p_org) { case '"': in_quote ^= 1; break; case '\n': case '\r': in_quote = 0; break; case '\0': p_org = NULL; break; } if (p_org) p_org++; } p = p_org; if ((p != NULL)&&(in_quote == 0)) { int stop_searching = 0; DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Located open ( at %s",FL,p); // If we did locate an opening parenthesis, look for the closing one // NOTE - we cannot have a breaking \n or \r inbetween // q = strpbrk(p, ")\n\r"); q = p; while ( (q != NULL) && (stop_searching == 0) ) { switch (*q) { case '\0': stop_searching = 1; q = NULL; break; case '\n': case '\r': stop_searching = 1; in_quote = 0; break; case '"': in_quote ^= 1; break; case ')': DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Located closing ) at %s",FL,q); if (in_quote == 0) stop_searching = 1; break; } if ((q != NULL)&&(stop_searching == 0)) q++; } // If we've got both opening and closing, then we need to remove // the contents of the comment, including the parenthesis if (q != NULL) { if (*q != ')') { // if we found a \n or \r between the two (), then jump out // and move p to the next position. p_org++; continue; } else { // Move q to the first char after the closing parenthesis q++; DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: located closing ) at %s ",FL, q); // While there's more chars in string, copy them to where // the opening parenthesis is while (*q != '\0') { *p = *q; p++; q++; } // While q != '\0' DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: char copy done",FL); // Terminate the string *p = '\0'; } // if q !=/= ')' } else break; // if q == NULL } // if p == NULL } while ((p != NULL)&&(p_org != NULL)); // do-while more comments to remove DMIMEH LOGGER_log("%s:%d:MIMEH_strip_comments:DEBUG: Final string = '%s'",FL,input); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_fix_header_mistakes Returns Type : int ----Parameter List 1. char *data , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Some headers are broken in their wrapping, ie, they fail to put a leading space at the start of the next wrapped data line; ie Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="yxnjjhyk.xml" Which causes normal header processing to not locate the filename. This function will see if there are any lines with a trailing ; that do not have a leading space on the next line and subsequently replace the \r\n chars after the ; with blanks, effectively pulling the line up -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_fix_header_mistakes( char *data ) { int result = 0; char *p; DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Checking and fixing headers in '%s'",FL,data); if (glb.header_fix == 0) return result; p = data; while (p) { int nonblank_detected = 0; char *q; p = strchr(p, ';'); if (p == NULL) break; q = p+1; DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Located ';' at offset %d '%20s",FL, p -data, p); if ((*q == '\n')||(*q == '\r')) { nonblank_detected = 0; } else { /** If the ; isn't immediately followed by a \n or \r, then search till ** the end of the line to see if all the chars are blank **/ while ((*q != '\0')||(*q != '\r')||(*q != '\n')) { switch (*q) { case '\t': case '\n': case '\r': case ' ': nonblank_detected = 0; break; default: nonblank_detected = 1; } /*switch*/ if (nonblank_detected == 1) break; q++; } /** while looking for the end of the line **/ } /** ELSE - if *q wasn't a line break char **/ if (nonblank_detected == 1) { DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Line was normal/safe, continue...",FL); p++; continue; } /** if nonblank_detected == 1 **/ /** if we had nothing but blanks till the end of the ** line, then we need to pull up the next line **/ if (*q != '\0') { DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Line needs fixing",FL); *q = ' '; q++; if ((*q == '\n')||(*q == '\r')) *q = ' '; DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Line fixed",FL); p = q; } /** If q wasn't the end of data **/ } /** while looking for more ';' chars **/ DMIMEH LOGGER_log("%s:%d:MIMEH_fix_header_mistakes:DEBUG: Done",FL); return result; } /*------------------------------------------------------------------------ Procedure: MIMEH_read_headers ID:1 Purpose: Reads from the stream F until it detects a From line, or a blank line (end of headers) Input: Output: Errors: ------------------------------------------------------------------------*/ int MIMEH_read_headers( struct MIMEH_header_info *hinfo, FFGET_FILE *f ) { char buffer[_MIMEH_STRLEN_MAX+1]; int totalsize=0; int linesize=0; int totalsize_original=0; int result = 0; int firstline = 1; int search_count=0; char *tmp; char *tmp_original; char *fget_result = NULL; char *headerline_end; char *p; char *linestart; char *lineend; int is_RFC822_headers=0; // 20040208-1335:PLD: Added to give an indication if the headers are RFC822 like; used in conjunction with the header_longsearch for pulling apart qmail bouncebacks. /** Lets start the ugly big fat DO loop here so that we can, if needed search until we find headers which are actually valid. Personally I hate this - but people want it in order to detect malformed (deliberate or otherwise) emails. It'd be nice if for once in the software world someone actually enforced standards rather than trying to be overly intelligent about interpreting data. **/ if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: File position = %ld [0x%0X]" ,FL ,FFGET_ftell(f) ,FFGET_ftell(f) ); do { search_count++; glb.headerline = NULL; glb.headerline_original = NULL; tmp_original = NULL; while ((fget_result=FFGET_fgets(buffer,_MIMEH_STRLEN_MAX, f))) { linestart = buffer; linesize = strlen(linestart); lineend = linestart +linesize; if (strstr(linestart,"\r\n")) hinfo->crlf_count++; else if (strstr(linestart,"\r\r")) hinfo->crcr_count++; else if (strchr(linestart,'\n')) hinfo->lf_count++; if (MIMEH_DNORMAL)LOGGER_log("%s:%d:MIMEH_read_headers: Data In=[sz=%d:tb=%d:mem=%p]'%s'",FL, linesize, f->trueblank, glb.headerline, buffer); // If we are being told to copy the input data to an output file // then do so here (this is for the originals) if ((glb.original_header_save_to_file > 0)&&(glb.original_header_file != NULL)) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: saving to file...",FL); fprintf(glb.original_header_file,"%s",linestart); } // if we are being told to keep a copy of the original data // as it comes in from ffget, then do the storage here if (glb.save_headers_original > 0) { if (MIMEH_DNORMAL) LOGGER_log("MIMEH_read_headers:DEBUG:Data-In:[%d:%d] '%s'", strlen(linestart), linesize, linestart); tmp_original = realloc(glb.headerline_original, totalsize_original+linesize+1); if (tmp_original == NULL) { LOGGER_log("%s:%d:MIMEH_read_headers:ERROR: Cannot allocate %d bytes to contain new headers_original ", FL,totalsize_original +linesize +1); if (glb.headerline_original != NULL) free(glb.headerline_original); glb.headerline_original = NULL; return -1; } if (glb.headerline_original == NULL) { glb.headerline_original = tmp_original; totalsize_original = linesize +1; PLD_strncpy( glb.headerline_original, linestart, (linesize+1)); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: '%s'", FL, glb.headerline_original); } else { glb.headerline_original = tmp_original; PLD_strncpy( (glb.headerline_original +totalsize_original -1), linestart, (linesize +1)); totalsize_original += linesize; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: HO = '%s'", FL, glb.headerline_original); } //LOGGER_log("DEBUG:linesize=%d data='%s'",linesize, linestart); } /** Normal processing of the headers now starts. **/ if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: realloc'ing dataspace",FL); tmp = realloc(glb.headerline, totalsize+linesize+1); if (tmp == NULL) { LOGGER_log("%s:%d:MIMEH_read_headers:ERROR: Cannot allocate %d bytes to contain new headers ", FL,totalsize +linesize +1); if (glb.headerline != NULL) free(glb.headerline); glb.headerline = NULL; return -1; } if (glb.headerline == NULL) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Initial appending of head to dataspace headerline = NULL realloc block = %p linestart = %p linesize = %d",FL, tmp, linestart, linesize); glb.headerline = tmp; totalsize = linesize; PLD_strncpy(glb.headerline, linestart, (linesize +1)); headerline_end = glb.headerline +totalsize; } // If the global headerline is currently NULL else { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Appending of new data to existing header existing-headerline = %p new realloc block = %p linestart = %p linesize = %d",FL, glb.headerline, tmp, linestart, linesize); // Perform header unfolding by removing any CRLF's // of the last line if the first characters of the // newline are blank/space glb.headerline = tmp; if ((linestart < lineend)&&((*linestart == '\t')||(*linestart == ' '))) { // Here we start at the last character of the previous line // and check to see if it's a 'space' type charcter, if it is // we will then reduce the total size of the headers thus far and // move the pointer where we're going to append this new line back // one more character - Ultimately what we wish to achieve is that // the new line will tacked on [sans leading spaces] to the end of // the previous line. // // 'p' holds the location at the -end- of the current headers where // we are going to append the newly read line p = glb.headerline +totalsize -1; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: unwrapping headers headers=%p, p = %p",FL,glb.headerline, p); while ((p >= glb.headerline)&&(( *p == '\n' )||( *p == '\r' ))) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Removing trailing space p=[%p]%c",FL, p, *p); *p = '\0'; p--; totalsize--; } p = glb.headerline +totalsize -1; } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Memcopying line, source = %p, dest = %p, size = %d", FL, linestart, glb.headerline +totalsize, linesize); memcpy((glb.headerline +totalsize), linestart, (linesize)); totalsize += linesize; *(glb.headerline +totalsize) = '\0'; } // If the glb.headerline already is allocated and we're appending to it. if (f->trueblank) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Trueblank line detected in header reading",FL); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Headers /before/ decoding\n-------\n%s\n-------------------",FL, glb.headerline); MIMEH_fix_header_mistakes( glb.headerline ); MDECODE_decode_ISO( glb.headerline, totalsize ); if ((glb.save_headers)&&(glb.headerline)) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Saving header line.",FL); fprintf(glb.header_file,"%s",glb.headerline); } if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Final Headers\n------------------\n%s---------------", FL,glb.headerline); //result = 1; //result = 0; break; } // If the last line was in fact a true blank line // If there was a doubleCR at the end of the line, // then we need to save the next set of data until there // is a \n if (FFGET_doubleCR) { if (glb.doubleCR_save != 0) { MIMEH_save_doubleCR(f); glb.doubleCR = 1; } FFGET_doubleCR = 0; FFGET_SDL_MODE = 0; } // FFGET_doubleCR test firstline = 0; } // While reading more headers from the source file. // If FFGET ran out of data whilst processing the headers, then acknowledge this // by returning a -1. // // NOTE - This does not mean we do not have any data! // it just means that our input ran out. if (!fget_result) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:ERROR: FFGET module ran out of input while reading headers",FL); /** If we're meant to be saving the headers, we better do that now, even though we couldn't ** read everything we wanted to **/ if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: save_headers=%d totalsize=%d headerline=%s", FL, glb.save_headers, totalsize, glb.headerline); if ((glb.save_headers)&&(glb.headerline)) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_read_headers:DEBUG: Saving header line.",FL); MIMEH_fix_header_mistakes( glb.headerline ); MDECODE_decode_ISO( glb.headerline, totalsize ); fprintf(glb.header_file,"%s",glb.headerline); } result = -1; } else { if (glb.header_longsearch > 0) { /** Test the headers for RFC compliance... **/ is_RFC822_headers = MIMEH_are_headers_RFC822(glb.headerline); if (is_RFC822_headers == 0) { /** If not RFC822 headers, then clean up everything we allocated in here **/ DMIMEH LOGGER_log("%s:%d:MIME_read_headers:DEBUG: No RFC822 headers detected, cleanup."); MIMEH_headers_cleanup(); } } } } while ((is_RFC822_headers==0)&&(glb.header_longsearch>0)&&(result==0)&&(search_countcontent_type\ ,hinfo->boundary\ ,hinfo->filename\ ,hinfo->name\ ,hinfo->content_transfer_encoding\ ,hinfo->content_disposition); fflush(stdout); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_decode_multivalue_language_string Returns Type : int ----Parameter List 1. char *input , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_decode_multivalue_language_string( char *input ) { int sq_count = 0; int language_set = 0; char *q = input; DMIMEH LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:DEBUG: Decoding '%s'",FL,input); // Count the single-quotes while ((*q != '\0')&&(sq_count != 2)) if (*q++ == '\'') sq_count++; if (sq_count < 2) { // LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:WARNING: Insufficient single quotes for valid language-charset string",FL); q = input; } else { language_set = 1; } // q will be pointing at the 2nd single-quote, which is the end of // the language encoding set, so we just jump over that and start // reading off the data and decoding it. MDECODE_decode_multipart( q ); // If the language was set, we need to move down our decoded data to the // start of the input buffer if (language_set == 1) { while (*q != '\0') { *input = *q; input++; q++; } *input = '\0'; } DMIMEH LOGGER_log("%s:%d:MIMEH_decode_multivalue_language_string:DEBUG: Output = '%s'",FL,q); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_recompose_multivalue Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, Global header information, can be NULL 2. char *header_name_prefix, Prefix we're looking for (ie, filename) 3. char *header_value, String which the prefix should exist in 4. char *buffer, Output buffer 5. size_t buffer_size , Output buffer size ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Multivalue strings are ones which appear like: filename*0*=us-ascii'en-us'attachment%2E%65 filename*1*="xe" which should duly be recoded as: filename=attachment.exe Another example: (extracted from the RFC2231 document) Content-Type: application/x-stuff title*0*=us-ascii'en'This%20is%20even%20more%20 title*1*=%2A%2A%2Afun%2A%2A%2A%20 title*2="isn't it!" -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_recompose_multivalue( struct MIMEH_header_info *hinfo, char *header_name_prefix, char *header_value, char *buffer, size_t buffer_size, char **data_end_point ) { int result = 0; char *start_position = header_value; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: seeking for %s in %s and appending to '%s'. Buffer size=%d", FL, header_name_prefix, header_value,buffer, buffer_size ); // Locate the first part of the multipart string start_position = strstr(header_value, header_name_prefix); if (start_position != NULL) { char *q; char *buffer_start; // Setup our buffer insertion point for what ever new data we extract buffer_start = buffer +strlen(buffer); buffer_size -= strlen(buffer); q = start_position; // If the string we're looking for exists, then continue... do { char *p; char *end_point; char end_point_char='\0'; int decode_data=0; int q_len; p = strstr(q, header_name_prefix); if (p == NULL) break; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: prefix = %s", FL, p); q = strchr(p,'='); if (q == NULL) break; // Test to see if we have to look for a language encoding specification *sigh* if (*(q-1) == '*') { decode_data=1; } // Move the pointer past the '=' separator q++; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: data = %s", FL, q); // Find where this multipart string ends end_point = strpbrk(q, ";\t\n\r "); if (end_point != NULL) { *end_point = '\0'; end_point_char = *end_point; *data_end_point = end_point; // Set this so we know where to start decoding the next time we call this fn } else { char *ep; // If strpbrk comes up with nothing, then we set the data_end_point to the end of the string ep = q; while (*ep != '\0') ep++; *data_end_point = ep; } // Trim off quotes. if (*q == '"') { int bl; // LOGGER_log("%s:%d:DEBUG: Trimming '%s'", FL, q); q++; bl = strlen(q); if (*(q +bl -1) == '"') *(q +bl -1) = '\0'; //LOGGER_log("%s:%d:DEBUG: Trim done, '%s'", FL, q); } if (decode_data == 1) { MIMEH_decode_multivalue_language_string(q); } DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: segment value = '%s', appending to '%s'", FL, q, buffer); snprintf(buffer_start,buffer_size,"%s",q); q_len = strlen(q); buffer_size -= q_len; buffer_start += q_len; DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: Buffer[remaining=%d]= '%s'", FL, buffer_size,buffer); if (end_point != NULL) { *end_point = end_point_char; q = end_point +1; } else q = NULL; } while ((q != NULL)&&(buffer_size > 0)); } DMIMEH LOGGER_log("%s:%d:MIMEH_recompose_multivalue:DEBUG: End point set to: [%d] '%s'",FL, (*data_end_point -header_value), *data_end_point); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_header_parameter Returns Type : int ----Parameter List 1. char *data, 2. char *searchstr, 3. char *output_value, 4. int output_value_size , 5. char *data_end_point, used to keep track of the last point of successful data decoding is. ------------------ Exit Codes : 0 = Success, found the required parameter 1 = No luck, didn't find the required parameter Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 11-Aug-2004: Added new variable, data_end_point. This variable was required because without it, there was no way of telling where to continue on the search for more valid data, this is due to having to no longer rely on fixed atom separators like : and ; in the MIME text (thankyou MUA's which incorrectly interpreted the RFC's *sigh*) \------------------------------------------------------------------*/ int MIMEH_parse_header_parameter( struct MIMEH_header_info *hinfo, char *data, char *searchstr, char *output_value, int output_value_size, char **data_end_point ) { int return_value = 0; char *p; char *hl; // Set the data end point to be the beginning of the data, as we // have not yet searched through any of the header data *data_end_point = data; // Duplicate and convert to lowercase the header data // that we have been provided with. hl = strdup(data); //PLD_strlower((unsigned char *)hl); PLD_strlower(hl); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Seeking '%s' in '%s'", FL, searchstr, hl); // Look for the search string we're after (ie, filename, name, location etc) if (strncmp(hl,searchstr,strlen(searchstr))==0) p = hl; else p = NULL; // p = strstr (hl, searchstr); //TESTING if (p != NULL) { char *string = NULL; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: found %s in %s", FL, searchstr, p); // Work out where in the -original- string the located parameter is. // We need to work from the original string because we need to // preserve case and our searching string is in _lower-case_. // // After we've located it, we offset the pointer past the string we // searched for. At this position, we should see a separator of // some type in the set [*;:=\t ]. string = p -hl +data +strlen(searchstr); /** ** After searching for our parameter, if we've got a ** basic match via strstr, we should then proceed to ** check that the characters either side of it are ** relevant to a typical parameter specification ** ** the characters *, =, and tab can succeed a ** parameter name. **/ switch (*string) { case '*': case '=': case ' ': case '\t': /** ** Permitted characters were found after the parameter name ** so continue on... **/ break; default: /** ** Something other than the permitted characters was found, ** this implies (assumed) that the string match was actually ** just a bit of good luck, return to caller **/ if (hl) free(hl); return 1; } /** Switch **/ /** ** Don't forget to also test the character _BEFORE_ the search string **/ if (1) { char *before_string; before_string = string -1 -strlen(searchstr); if (before_string >= data) { /** ** The characters, , , ;, : may preceed a parameter name **/ switch (*(before_string)) { case ';': case ':': case ' ': case '\t': /** ** Permitted characters were found after the parameter name ** so continue on... **/ break; default: /** ** Something other than the permitted characters was found, ** this implies (assumed) that the string match was actually ** just a bit of good luck, return to caller **/ if (hl) free(hl); return 1; } /** Switch before_string **/ } /** if before_string > data **/ } /** 1 **/ // If the char is a '*', this means we've got a multivalue parameter // which needs to be decoded (ie, name*1*=foo name*2*=bar ) if (*string == '*') { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Found a '*' after the name, so attempting multipart value decode",FL); // PLD:DEV:11/08/2004-18H30 // Issue: RFC2231 handling return_value = MIMEH_recompose_multivalue( hinfo, searchstr, data, output_value, output_value_size, data_end_point); } else { // skip any spaces while (isspace((int) *string )) string++; //if ( *string != '=' ) if ( *string == '\0' ) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: In '%s' parsing, was expecting a '=' in the start of '%s'\n", FL, searchstr, string ); } else { char *endchar; // Eliminate multiple = separators. // Reference: c030804-006a // PLD:DEV: 11/08/2004-15H15 while ((*(string +1) == '=')&&(*(string+1) != '\0')) { string++; MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS); } // Get the end of our string endchar = string +strlen(string) -1; *data_end_point = endchar; // Strip off trailing whitespace while ((endchar > string)&&(isspace((int)*endchar))) { *endchar = '\0'; endchar--; } // we are at the '=' in the header, so skip it if (*string == '=') string++; else { MIMEH_set_defect(hinfo,MIMEH_DEFECT_MISSING_SEPARATORS); } // skip any spaces... again while ( isspace((int) *string ) ) string++; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Extracting value out of '%s'",FL,string); // Because of all the potential exploits and bad behaviour // we have to be really careful about how we determine // what the enclosed string is for our parameter. // // Previously we could _assume_ that we just get the last // quote (") on the line and copy out what was between, // unfortunately that doesn't work anymore. Instead now // we have to step along the data stream one char at a // time and make decisions along the way. switch (*string) { case '\"': { // If our first char is a quote, then we'll then try and find // the second quote which closes the string, alas, this is // not always present in the header data, either due to a // broken MUA or due to an exploit attempt. char *string_end; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Using quoted-string tests",FL); // Remove multiple-sequential quotes string++; while ((*string != '\0')&&(*string == '\"')){ string++; MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_QUOTES); } if (*string == '\0') break; // 20071030-0958: Added by Claudio Jeker - prevents overflow. // Find the next quote which isn't sequential to the above // quotes that we just skipped over string_end = strchr(string+1, '\"'); if (string_end != NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: End of value found",FL); *string_end = '\0'; *data_end_point = string_end +1; } else { // If string_end == NULL // // If we didn't find any more quotes, that // means we've probably got an unbalanced string (oh joy) // so then we convert to looking for other items such as // ;\n\r\t and space. // if (hinfo) MIMEH_set_defect(hinfo,MIMEH_DEFECT_UNBALANCED_QUOTES); string_end = strpbrk(string,"; \n\r\t"); if (string_end != NULL) { *string_end = '\0'; *data_end_point = string_end +1; } else { // There is no termination to the string, instead the // end of the string is \0. } } } break; default: { char *string_end; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Using NON-quoted-string tests",FL); string_end = strpbrk(string,"; \n\r\t"); if (string_end != NULL) { *string_end = '\0'; *data_end_point = string_end +1; } else { // There is no termination to the string, instead the // end of the string is \0. } } break; } /** end of switch **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Extracting value out of '%s'",FL,string); // Trim up and leading/trailing quotes if (((*string == '\"')&&(*(string +strlen(string)-1) == '\"')) || ((*string == '\'')&&(*(string +strlen(string)-1) == '\'')) ) { int slen = strlen(string) -2; char *s = string; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse-header_parameter:DEBUG: Stripping quotes from '%s'",FL,string); while (slen > 0) { *s = *(s+1); s++; slen--; } *s = '\0'; } // Now that our string is all cleaned up, save it to our output value snprintf( output_value, output_value_size, "%s", string ); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: Final value = '%s'",FL, output_value); } // If the first non-whitespace char wasn't a '=' } // If the first char after the search-string wasn't a '*' } else { return_value = 1; } if (hl != NULL) free(hl); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_header_parameter:DEBUG: [return=%d] Done seeking for '%s' data_end_point=%p (from %p)",FL, return_value, searchstr, *data_end_point, data); return return_value; } /*-----------------------------------------------------------------\ Function Name : MIMEH_is_valid_header_prefix Returns Type : int ----Parameter List 1. char *data, 2. char *prefix_name , ------------------ Exit Codes : 0 = no, not valid 1 = yes, valid. Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_is_valid_header_prefix( char *data, char *prefix_name ) { int plen = strlen(prefix_name); /** If our string doesn't start with content-type, then exit **/ if (strncasecmp(data, prefix_name, plen)!=0) { return 0; } else { char end_char; /** Test to see that the terminating char after the content-type ** string is suitable to indicating that the content-type is ** infact a header name **/ end_char = *(data +plen); switch (end_char){ case ':': case ' ': case '\t': case '\0': /** Valid terminating characters found **/ break; default: /** Otherwise, return 0 **/ return 0; } /** switch end_char **/ } /** if-else **/ return 1; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttype_linear Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttype_linear_EXPERIMENT( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *chv = header_value; char *chn = header_name; int boundary_found = 0; // int name_found = 0; // int filename_found = 0; /** Absorb whitespace **/ while (isspace(*chn)) chn++; /** Test if the content-type string is valid **/ if (MIMEH_is_valid_header_prefix(chn, "content-type")==0) return 0; /** Now, let's try parse our content-type parameter/value string **/ while (*chv) { while (isspace(*chv)) chv++; if ((boundary_found==0)&&(MIMEH_is_valid_header_prefix(chv,"boundary")==1)) { } // if (strncasecmp(chv, "boundary" } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttype Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttype( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { int return_value; char *p, *q; char *hv = strdup( header_value ); // CONTENT TYPE ------------------------------- // CONTENT TYPE ------------------------------- // CONTENT TYPE ------------------------------- DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Start",FL); p = strstr(header_name,"content-type"); if (p != NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype: Content-type string found in header-name",FL); /** 20041216-1106:PLD: Increase our sanity **/ hinfo->sanity++; PLD_strlower( header_value ); PLD_strlower( header_value ); q = header_value; if (strstr(q,"multipart/appledouble")) hinfo->content_type = _CTYPE_MULTIPART_APPLEDOUBLE; else if (strstr(q,"multipart/signed")) hinfo->content_type = _CTYPE_MULTIPART_SIGNED; else if (strstr(q,"multipart/related")) hinfo->content_type = _CTYPE_MULTIPART_RELATED; else if (strstr(q,"multipart/mixed")) hinfo->content_type = _CTYPE_MULTIPART_MIXED; else if (strstr(q,"multipart/alternative")) hinfo->content_type = _CTYPE_MULTIPART_ALTERNATIVE; else if (strstr(q,"multipart/report")) hinfo->content_type = _CTYPE_MULTIPART_REPORT; else if (strstr(q,"multipart/")) hinfo->content_type = _CTYPE_MULTIPART; else if (strstr(q,"text/calendar")) hinfo->content_type = _CTYPE_TEXT_CALENDAR; else if (strstr(q,"text/plain")) hinfo->content_type = _CTYPE_TEXT_PLAIN; else if (strstr(q,"text/html")) hinfo->content_type = _CTYPE_TEXT_HTML; else if (strstr(q,"text/")) hinfo->content_type = _CTYPE_TEXT; else if (strstr(q,"image/gif")) hinfo->content_type = _CTYPE_IMAGE_GIF; else if (strstr(q,"image/jpeg")) hinfo->content_type = _CTYPE_IMAGE_JPEG; else if (strstr(q,"image/")) hinfo->content_type = _CTYPE_IMAGE; else if (strstr(q,"audio/")) hinfo->content_type = _CTYPE_AUDIO; else if (strstr(q,"message/rfc822")) hinfo->content_type = _CTYPE_RFC822; else if (strstr(q,"/octet-stream")) hinfo->content_type = _CTYPE_OCTECT; else if (strstr(q,"/ms-tnef")) hinfo->content_type = _CTYPE_TNEF; else if (strstr(q,"application/applefile")) { hinfo->content_type = _CTYPE_APPLICATION_APPLEFILE; if ( hinfo->filename[0] == '\0' ) { if (strlen(glb.appledouble_filename)>0) { snprintf(hinfo->filename, sizeof(hinfo->filename), "%s.applemeta", glb.appledouble_filename ); } else { snprintf(hinfo->filename, sizeof(hinfo->filename), "applefile"); } } } else hinfo->content_type = _CTYPE_UNKNOWN; /** Is there an x-mac-type|creator parameter? **/ if ((strstr(header_value,"x-mac-type="))&&(strstr(header_value,"x-mac-creator="))) { /** By setting this flag to 1, we are saying that if the ** filename contains a forward slash '/' char, then it's ** to be treated as a normal char, not a directory ** separator. However, as we cannot generate a filename ** with that char normally, we'll convert it to something ** else **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Located x-mac attachment",FL); hinfo->x_mac = 1; FNFILTER_set_mac(hinfo->x_mac); } // Copy the string to our content-type string storage field p = header_value; if (p != NULL) { char *c = p; // Step 1 - jump over any whitespace while ( *c == ' ' || *c == '\t') c++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_type_string, c, _MIMEH_CONTENT_TYPE_MAX); // Step 3 - clean up the string c = hinfo->content_type_string; while (*c && *c != ' ' && *c != '\t' && *c != '\n' && *c != '\r' && *c != ';') c++; // Step 4 - Terminate the string *c = '\0'; } // If we have an additional parameter at the end of our content-type, then we // should search for a name="foobar" sequence. //p = strchr( hv, ';' ); p = strpbrk( hv, ";\t\n\r " ); if (p != NULL) { char *param = NULL; char *data_end_point = param; p++; param = strpbrk( p, ";\n\r\t " ); while ( param != NULL ) { /** ** ** The Process of decoding our line.... ** . While not end of the line... ** . Remove whitespace ** . test for 'name' ** . test for 'boundary' ** . Move to next char after parameter values ** ** Go to the next character after the 'token separator' character ** and then proceed to absorb any excess white space. ** Once we've stopped at a new, non-white character, we can begin ** to see if we've got a sensible parameter like name=, filename= ** or boundary= **/ param++; param = MIMEH_absorb_whitespace(param); /** ** If we get to the end of the line, just break out of the token ** parsing while loop **/ if (*param == '\0') break; /** ** Look for name or filename specifications in the headers ** Look for name or filename specifications in the headers ** Look for name or filename specifications in the headers **/ return_value = MIMEH_parse_header_parameter( hinfo, param, "name", hinfo->name, sizeof(hinfo->name), &data_end_point); /** Update param to point where data_end_point is ** this is so when we come around here again due ** to the while loop, we'll know where to pick up ** the search for more parameters **/ if (data_end_point > param) param = data_end_point; // If we finally had success, then copy the name into filename for hinfo if ( return_value == 0 ) { // Move the parameter search point up to where we stopped // processing the data in the MIMEH_parse_header_parameter() call DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushing new filename to stack '%s'",FL, hinfo->name); /** Step 1: Check to see if this filename already ** exists in the stack. We do this so that we don't ** duplicate entries and also to prevent false ** bad-header reports. **/ if (SS_cmp(&(hinfo->ss_names), hinfo->name, strlen(hinfo->name))==NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Filtering '%s'",FL, hinfo->name); FNFILTER_filter(hinfo->name, _MIMEH_FILENAMELEN_MAX); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushing '%s'",FL, hinfo->name); SS_push(&(hinfo->ss_names),hinfo->name,strlen(hinfo->name)); if (SS_count(&(hinfo->ss_names)) > 1) { MIMEH_set_defect(hinfo, MIMEH_DEFECT_MULTIPLE_NAMES); } if ( hinfo->filename[0] == '\0' ) { snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", hinfo->name ); } } /* If the file name doesn't already exist in the stack */ } /* If a filename was located in the headers */ /** ** Look for the MIME Boundary specification in the headers ** Look for the MIME Boundary specification in the headers ** Look for the MIME Boundary specification in the headers **/ return_value = MIMEH_parse_header_parameter(hinfo, param, "boundary", hinfo->boundary, sizeof(hinfo->boundary), &data_end_point); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Param<=>data_end gap = %d", FL,data_end_point -param); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); if (data_end_point > param) param = data_end_point; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); if ( return_value == 0 ) { // Move the parameter search point up to where we stopped // processing the data in the MIMEH_parse_header_parameter() call //hinfo->boundary_located = 1; hinfo->boundary_located++; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Pushed boundary to stack (%s)",FL, hinfo->boundary); BS_push(hinfo->boundary); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: Setting hinfo->boundary_located to %d",FL, hinfo->boundary_located ); if (hinfo->boundary_located > 1) { // Register the defect MIMEH_set_defect(hinfo, MIMEH_DEFECT_MULTIPLE_BOUNDARIES); //Reset the counter back to 1. hinfo->boundary_located=1; } } //param = PLD_strtok( &tx, NULL, ";\n\r" ); // * PLD:20040831-22H15: Added 'if (param != NULL)' prefix to debugging lines // * In response to bug #32, submitted by ICL ZA if (param != NULL) DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); param = strpbrk( param, ";\n\r " ); if (param != NULL) DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: param start pos = '%s'",FL, param); } // While } } if (hv != NULL) free(hv); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttype:DEBUG: end.",FL); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contentlocation Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contentlocation( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p, *q; // CONTENT LOCATION ------------------------------- // CONTENT LOCATION ------------------------------- // CONTENT LOCATION ------------------------------- PLD_strlower( header_name ); p = strstr(header_name,"content-location"); if (p) { /** 20041216-1108:PLD: Increase our sanity **/ hinfo->sanity++; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_contentlocation:DEBUG: Content Location line found - '%s'\n", FL, header_value); p = q = header_value; while (q) { q = strpbrk(p, "\\/"); if (q != NULL) p = q+1; } if (p) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_contentlocation:DEBUG: filename = %s\n", FL, p); snprintf(hinfo->name, sizeof(hinfo->name),"%s",p); snprintf(hinfo->filename, sizeof(hinfo->name),"%s",p); FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX); SS_push(&(hinfo->ss_filenames), hinfo->filename, strlen(hinfo->filename)); } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contenttransferencoding Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contenttransferencoding( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p, *q; char c = '\n'; // CONTENT TRANSFER ENCODING --------------------- // CONTENT TRANSFER ENCODING --------------------- // CONTENT TRANSFER ENCODING --------------------- p = strstr(header_name,"content-transfer-encoding"); if (p) { /** 20041216-1107:PLD: Increase our sanity **/ hinfo->sanity++; q = strpbrk(header_value,"\n\r;"); if (q != NULL) { c = *q; *q = '\0'; } p = header_value; PLD_strlower( p ); if (strstr(p,"base64")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_B64; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to BASE64", FL); } else if (strstr(p,"7bit")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_7BIT; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to 7-BIT ", FL); } else if (strstr(p,"8bit")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_8BIT; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to 8-BIT", FL); } else if (strstr(p,"quoted-printable")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_QP; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to Quoted-Printable", FL); } else if (strstr(p,"binary")) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_BINARY; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to Binary", FL); } else if ( (strstr(p,"uu")) ||(strstr(p,"x-u")) ||(strcmp(p,"u") == 0) ) { hinfo->content_transfer_encoding = _CTRANS_ENCODING_UUENCODE; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contenttransferencoding: Encoding set to UUENCODE", FL); } else hinfo->content_transfer_encoding = _CTRANS_ENCODING_RAW; // Copy the string to our content-transfer string storage field p = header_value; if (p != NULL) { char *cp = p; // Step 1 - jump over any whitespace while ( *cp == ' ' || *cp == '\t') cp++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_transfer_encoding_string, cp, _MIMEH_CONTENT_TRANSFER_ENCODING_MAX); // Step 3 - clean up the string cp = hinfo->content_transfer_encoding_string; while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\r' && *cp != ';') cp++; // Step 4 - Terminate the string *cp = '\0'; } // Set the character which we changed to a \0 back to its original form so that // we don't cause problems from tainted data for any further parsing calls // which use the data. if (q != NULL) *q = c; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_contentdisposition Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_contentdisposition( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { char *p; char *hv = strdup(header_value); // CONTENT DISPOSITION ------------------------------ // CONTENT DISPOSITION ------------------------------ // CONTENT DISPOSITION ------------------------------ //LOGGER_log("%s:%d:DEBUG: Headers='%s'",FL,header_value); p = strstr(header_name,"content-disposition"); if (p != NULL) { /** 20041216-1107:PLD: Increase our sanity **/ hinfo->sanity++; // Change p to now point to the header VALUE, p no longer // points to the content-disposition start! p = header_value; PLD_strlower( header_value ); // Here we just check to find out what type of disposition we have. if (strstr(p,"inline")) { hinfo->content_disposition = _CDISPOSITION_INLINE; } else if (strstr(p,"form-data")) { hinfo->content_disposition = _CDISPOSITION_FORMDATA; } else if (strstr(p,"attachment")) { hinfo->content_disposition = _CDISPOSITION_ATTACHMENT; } else { hinfo->content_disposition = _CDISPOSITION_UNKNOWN; } // Copy the string to our content-transfer string storage field if (p != NULL) { char *q = p; // Step 1 - jump over any whitespace while ( *q == ' ' || *q == '\t') q++; // Step 2 - Copy the string PLD_strncpy( hinfo->content_disposition_string, q, _MIMEH_CONTENT_DISPOSITION_MAX); // Step 3 - clean up the string q = hinfo->content_disposition_string; while (*q && *q != ' ' && *q != '\t' && *q != '\n' && *q != '\r' && *q != ';') q++; // Step 4 - Terminate the string *q = '\0'; } DMIMEH LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Disposition string = '%s'",FL, hv); // Commence to decode the disposition string into its components. p = strpbrk( hv, ";\t\n\r " ); if (p != NULL) { // struct PLD_strtok tx; char *param; hinfo->name[0]='\0'; p++; param = p; while ( param != NULL ) { int parse_result; char *data_end_point; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Parsing '%s'",FL,param); // Seek out possible 'filename' parameters parse_result = MIMEH_parse_header_parameter(hinfo, param, "filename", hinfo->name, sizeof(hinfo->name), &data_end_point); if (data_end_point > param) param = data_end_point; if (parse_result == 0) { FNFILTER_filter(hinfo->name, _MIMEH_FILENAMELEN_MAX); SS_push(&(hinfo->ss_filenames), hinfo->name, strlen(hinfo->name)); if (SS_count(&(hinfo->ss_filenames)) > 1) { MIMEH_set_defect(hinfo,MIMEH_DEFECT_MULTIPLE_FILENAMES); } } param = strpbrk( param , ";\n\r\t " ); if (param) param++; //param = PLD_strtok( &tx, NULL, ";\n\r\t " ); } // While if ( hinfo->filename[0] == '\0' ) { snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", hinfo->name ); } // Handle situations where we'll need the filename for the future. if ( hinfo->content_type == _CTYPE_MULTIPART_APPLEDOUBLE ) { snprintf( glb.appledouble_filename, sizeof(glb.appledouble_filename), "%s", hinfo->filename ); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_contentdisposition:DEBUG: Setting appledouble filename to: '%s'",FL,glb.appledouble_filename); } } // If the header-value contained ;'s ( indicating parameters ) } // If the header-name actually contained 'content-disposition' if (hv != NULL) free(hv); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_subject Returns Type : int ----Parameter List 1. char *header_name, contains the full headers 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_generic( char *header_name, char *header_value, struct MIMEH_header_info *hinfo, char *tokenstr, char *buffer, size_t bsize ) { int compare_result = 0; int tlen; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_generic:DEBUG: Searching for %s in %s",FL,tokenstr,header_name); /** Sanity check the parameters **/ if (hinfo == NULL) return -1; if (tokenstr == NULL) return -1; if (header_name == NULL) return -1; if (header_value == NULL) return -1; if (buffer == NULL) return -1; if (bsize < 1) return -1; tlen = strlen(tokenstr); compare_result = strncmp( header_name, tokenstr, tlen ); if (compare_result == 0) { switch (*(header_name +tlen)) { case ':': case ' ': case '\t': case '\0': DMIMEH LOGGER_log("%s:%d:MIMEH_parse_generic:DEBUG: Located! Sanity up +1",FL); snprintf( buffer, bsize, "%s", header_value ); hinfo->sanity++; break; } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_subject Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_subject( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { int result = 0; result = MIMEH_parse_generic( header_name, header_value, hinfo, "subject", hinfo->subject, sizeof(hinfo->subject) ); snprintf(glb.subject, sizeof(glb.subject),"%s", hinfo->subject); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_date Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_date( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "date", hinfo->date, sizeof(hinfo->date) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_from Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_from( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "from", hinfo->from, sizeof(hinfo->from) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_to Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_to( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "to", hinfo->to, sizeof(hinfo->to) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_messageid Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_messageid( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "message-id", hinfo->messageid, sizeof(hinfo->messageid) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_received Returns Type : int ----Parameter List 1. char *header_name, 2. char *header_value, 3. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_received( char *header_name, char *header_value, struct MIMEH_header_info *hinfo ) { return MIMEH_parse_generic( header_name, header_value, hinfo, "received", hinfo->received, sizeof(hinfo->received) ); } /*-----------------------------------------------------------------\ Function Name : MIMEH_process_headers Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. char *headers , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_process( struct MIMEH_header_info *hinfo, char *headers ) { /** scan through our headers string looking for information that is ** valid **/ char *safeh, *h, *safehl; char *current_header_position; int headerlength; if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Start [hinfo=%p]\n",FL, hinfo); safeh = h = headers; /** Duplicate the headers for processing - this way we don't 'taint' the ** original headers during our searching / altering. **/ headerlength = strlen(h); safehl = malloc(sizeof(char) *(headerlength+1)); PLD_strncpy(safehl, h, headerlength+1); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_headers:DEBUG: Header length = %d\n", FL,headerlength); MIMEH_strip_comments(h); current_header_position = h; // Searching through the headers, we seek out header 'name:value;value;value' sets, // Each set is then cleaned up, seperated and parsed. while ((current_header_position != NULL)&&( current_header_position <= (h +headerlength) )) { char *header_name, *header_value; char *header_name_end_position; char *header_value_end_position; DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Processing '%s'",FL,current_header_position); /** Tokenise for the header 'name', ie, content-type, subject etc **/ header_name = current_header_position; header_name_end_position = strpbrk( header_name, ":\t " ); if (header_name_end_position == NULL) { // We couldn't find a terminating :, so, instead we try to find // the first whitespace // // PLD:DEV:11/08/2004-15H27 // Issue: c030804-006a // // NOTE: this may activate on the true-blank lines, hence why we // dump the source string, just for confirmation DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Could not locate ':' separator, using whitespace (source='%s')",FL,header_name); header_name_end_position = strpbrk( header_name, "\t " ); if (header_name_end_position == NULL) { DMIMEH LOGGER_log("%s:%d:MIMEH_headers_process:DEBUG: Cannot find a header name:value pair in '%s'",FL, header_name); } } // Seek forward from the start of our header, looking for the first occurance // of the line end (implying the end of the current header name:value, // we can do this because we know that when the headers were read in, we // have already unfolded them, such that there should only be one header:value // pairing per 'line'. current_header_position = strpbrk( current_header_position, "\n\r"); if ( current_header_position == NULL ) { // Theoretically, this should not happen, as headers are always // terminated with a \n\r\n\r finishing byte sequence, thus // if this _does_ happen, then we will simply jump out of the // current iteration and let the loop try find another pairing // // There probably should be a logging entry here to indicate that // "something strange happened" continue; } else { // Shuffle our CHP (current-header-position) pointer along the header // data until it is no longer pointing to a \r or \n, this is so // that when the next iteration of this loop comes around, it'll // immediately be in the right place for starting the next parse while (( *current_header_position == '\n') ||( *current_header_position == '\r' )) current_header_position++; } if (( header_name_end_position == NULL )||( header_name_end_position > current_header_position)) { // Some headers can contain various levels of non name/value pairings, // while their presence could be debatable in terms of RFC validity // we will 'ignore' them rather than throwing up our arms. This // ensures that we are not made to break over spurilous data. if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: This line contains no header:value pair (%s)", FL, current_header_position); continue; } else { // Get the header-value string and prepare to // parse the data through our various parsing // functions. header_value = header_name_end_position +1; header_value_end_position = strpbrk( header_value, "\n\r" ); if ( header_value_end_position != NULL ) { *header_name_end_position = '\0'; *header_value_end_position = '\0'; if (MIMEH_DNORMAL) { LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Header Name ='%s'", FL, header_name ); LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Header Value='%s'", FL, header_value ); } // To make parsing simpler, convert our // header name to lowercase, that way // we also reduce the CPU requirements for // searching because pre-lowering the header-name // occurs once, but string testing against it // occurs multiple times ( at least once per parsing PLD_strlower( header_name ); MIMEH_parse_subject( header_name, header_value, hinfo ); MIMEH_parse_contenttype( header_name, header_value, hinfo ); MIMEH_parse_contenttransferencoding( header_name, header_value, hinfo ); MIMEH_parse_contentdisposition( header_name, header_value, hinfo ); /** These items aren't really -imperative- to have, but they do ** help with the sanity checking **/ MIMEH_parse_date( header_name, header_value, hinfo ); MIMEH_parse_from( header_name, header_value, hinfo ); MIMEH_parse_to( header_name, header_value, hinfo ); MIMEH_parse_messageid( header_name, header_value, hinfo ); MIMEH_parse_received( header_name, header_value, hinfo ); if (hinfo->filename[0] == '\0') { MIMEH_parse_contentlocation( header_name, header_value, hinfo ); } } else { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headerss:DEBUG: Header value end position is NULL",FL); } } } // while // Final analysis on our headers: if ( hinfo->content_type == _CTYPE_MULTIPART_APPLEDOUBLE ) { char tmp[128]; snprintf( tmp, sizeof(tmp), "mac-%s", hinfo->filename ); snprintf( hinfo->filename, sizeof(hinfo->filename), "%s", tmp ); snprintf( hinfo->name, sizeof(hinfo->name), "%s", tmp ); } // PLD:20031205 // Act like Outlook *God forbid!* and if there's a octect-stream // content-type, but the encoding is still null/empty, then // change the content-encoding to be RAW if ( hinfo->content_type == _CTYPE_OCTECT ) { if ((hinfo->content_transfer_encoding == _CTRANS_ENCODING_UNSPECIFIED) || (hinfo->content_transfer_encoding == _CTRANS_ENCODING_UNKNOWN) || (strlen(hinfo->content_transfer_encoding_string) < 1) ) { //LOGGER_log("%s:%d:DEBUG: Encoding pair was octet but no encoding, filename=%s\n",FL,hinfo->filename); hinfo->content_transfer_encoding = _CTRANS_ENCODING_RAW; } } if (safehl) { free(safehl); } else LOGGER_log("%s:%d:MIME_parse_headers:WARNING: Unable to free HEADERS allocated memory\n", FL); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: END [hinfo=%p]\n", FL, hinfo); return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_headers Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, 2. FFGET_FILE *f , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_get( struct MIMEH_header_info *hinfo, FFGET_FILE *f ) { int result = 0; // Setup some basic defaults hinfo->filename[0] = '\0'; hinfo->name[0] = '\0'; hinfo->content_type = _CTYPE_UNKNOWN; hinfo->subject[0] = '\0'; // 20040116-1234:PLD - added to appease valgrind hinfo->content_disposition = 0; hinfo->content_transfer_encoding = 0; hinfo->boundary_located = 0; hinfo->crlf_count=0; hinfo->crcr_count=0; hinfo->lf_count=0; snprintf(hinfo->delimeter,sizeof(hinfo->delimeter),"\r\n"); // Initialise header defects array. hinfo->header_defect_count = 0; memset(hinfo->defects, 0, _MIMEH_DEFECT_ARRAY_SIZE); snprintf( hinfo->content_type_string, _MIMEH_CONTENT_TYPE_MAX , "text/plain" ); // Read from the file, the headers we need FFGET_set_watch_SDL(1); result = MIMEH_read_headers(hinfo, f); FFGET_set_watch_SDL(0); if (hinfo->lf_count > hinfo->crlf_count) { snprintf(hinfo->delimeter,sizeof(hinfo->delimeter),"\n"); } // If we ran out of input whilst looking at headers, then, we basically // flag this, free up the headers, and return. if (result == -1) { if (glb.headerline) free(glb.headerline); return result; } // If we came back with an OKAY result, but there's nothing in the // headers, then flag off an error if (glb.headerline == NULL) { if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIME_parse_headers:DEBUG: null headerline\n", FL); return 1; } return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_headers_cleanup Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_headers_cleanup( void ) { if (glb.headerline != NULL) { free(glb.headerline); glb.headerline = NULL; } if (glb.headerline_original != NULL) { free(glb.headerline_original); glb.headerline_original = NULL; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_parse_headers Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_parse_headers( FFGET_FILE *f, struct MIMEH_header_info *hinfo ) { int result = 0; DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Start [F=%p, hinfo=%p]\n", FL, f, hinfo); /** 20041216-1100:PLD: Set the header sanity to zero **/ if ( result == 0 ) hinfo->sanity = 0; /** Proceed to read, process and finish headers **/ DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Getting headers",FL); if ( result == 0 ) result = MIMEH_headers_get( hinfo, f ); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Processing headers",FL); if ( result == 0 ) result = MIMEH_headers_process( hinfo, glb.headerline ); DMIMEH LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: Cleanup of headers",FL); if ( result == 0 ) result = MIMEH_headers_cleanup(); if (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_parse_headers:DEBUG: END [F=%p, hinfo=%p, sanity=%d]\n", FL, f, hinfo, hinfo->sanity); return result; } /*-----------------------------------------------------------------\ Function Name : MIMEH_dump_defects Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Displays a list of the located defects -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_dump_defects( struct MIMEH_header_info *hinfo ) { int i; MIMEH_defect_description_array[MIMEH_DEFECT_MISSING_SEPARATORS] = strdup("Missing separators"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_FIELD_OCCURANCE] = strdup("Multiple field occurance"); MIMEH_defect_description_array[MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE] = strdup("Unbalanced boundary quote"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_BOUNDARIES] = strdup("Multiple boundries"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_COLON_SEPARATORS] = strdup("Multiple colon separators"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS] = strdup("Multiple equals separators"); MIMEH_defect_description_array[MIMEH_DEFECT_UNBALANCED_QUOTES] = strdup("Unbalanced quotes"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_QUOTES] = strdup("Multiple quotes"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_NAMES] = strdup("Multiple names"); MIMEH_defect_description_array[MIMEH_DEFECT_MULTIPLE_FILENAMES] = strdup("Multiple filenames"); for (i = 0; i < _MIMEH_DEFECT_ARRAY_SIZE; i++) { if (hinfo->defects[i] > 0) { LOGGER_log("Header Defect: %s: %d",MIMEH_defect_description_array[i],hinfo->defects[i]); } } return 0; } /*-----------------------------------------------------------------\ Function Name : MIMEH_get_defect_count Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIMEH_get_defect_count( struct MIMEH_header_info *hinfo ) { return hinfo->header_defect_count; } //----------------------END altermime-0.3.10/MIME_headers.h0000644000000000000000000001420211107756671014655 0ustar rootroot#ifndef MIMEHEADERS #define MIMEHEADERS #define MIMEH_VERSION "200811151148" #define _CTYPE_RANGE 99 #define _CTYPE_UNSPECIFIED -1 #define _CTYPE_MESSAGE_START 1 #define _CTYPE_MESSAGE 1 #define _CTYPE_MESSAGE_END 100 #define _CTYPE_MULTIPART_START 100 #define _CTYPE_MULTIPART 100 #define _CTYPE_MULTIPART_MIXED 101 #define _CTYPE_MULTIPART_APPLEDOUBLE 102 #define _CTYPE_MULTIPART_RELATED 103 #define _CTYPE_MULTIPART_ALTERNATIVE 104 #define _CTYPE_MULTIPART_REPORT 105 #define _CTYPE_MULTIPART_SIGNED 106 #define _CTYPE_MULTIPART_END 199 #define _CTYPE_TEXT_START 200 #define _CTYPE_TEXT 200 #define _CTYPE_TEXT_PLAIN 201 #define _CTYPE_TEXT_UNKNOWN 202 #define _CTYPE_TEXT_HTML 203 #define _CTYPE_TEXT_CALENDAR 204 #define _CTYPE_TEXT_END 299 #define _CTYPE_IMAGE_START 300 #define _CTYPE_IMAGE 300 #define _CTYPE_IMAGE_GIF 301 #define _CTYPE_IMAGE_JPEG 302 #define _CTYPE_IMAGE_PNG 303 #define _CTYPE_IMAGE_END 399 #define _CTYPE_AUDIO_START 400 #define _CTYPE_AUDIO 400 #define _CTYPE_AUDIO_END 499 #define _CTYPE_OCTECT 800 #define _CTYPE_RFC822 500 #define _CTYPE_TNEF 600 #define _CTYPE_APPLICATION 700 #define _CTYPE_APPLICATION_APPLEFILE 701 #define _CTYPE_UNKNOWN 0 #define _CTRANS_ENCODING_UNSPECIFIED -1 #define _CTRANS_ENCODING_B64 100 #define _CTRANS_ENCODING_7BIT 101 #define _CTRANS_ENCODING_8BIT 102 #define _CTRANS_ENCODING_QP 103 #define _CTRANS_ENCODING_RAW 104 #define _CTRANS_ENCODING_BINARY 105 #define _CTRANS_ENCODING_UUENCODE 106 #define _CTRANS_ENCODING_UNKNOWN 0 #define _CDISPOSITION_UNSPECIFIED -1 #define _CDISPOSITION_INLINE 100 #define _CDISPOSITION_ATTACHMENT 200 #define _CDISPOSITION_FORMDATA 300 #define _CDISPOSITION_UNKNOWN 0 #define _MIMEH_FOUND_FROM 100 #define _MIMEH_STRLEN_MAX 1023 #define _MIMEH_FILENAMELEN_MAX 128 #define _MIMEH_CONTENT_TYPE_MAX 128 #define _MIMEH_SUBJECTLEN_MAX 128 #define _MIMEH_CONTENT_DESCRIPTION_MAX 128 #define _MIMEH_CONTENT_TRANSFER_ENCODING_MAX 256 #define _MIMEH_CONTENT_DISPOSITION_MAX 256 #define _MIMEH_DEBUG_NORMAL 1 #define _MIMEH_DEBUG_PEDANTIC 10 #define _MIMEH_DEFECT_ARRAY_SIZE 100 // Errors to throw back #define MIMEH_ERROR_DISK_FULL 128 // Defects #define MIMEH_DEFECT_MULTIPLE_QUOTES 1 #define MIMEH_DEFECT_UNBALANCED_QUOTES 2 #define MIMEH_DEFECT_MULTIPLE_EQUALS_SEPARATORS 3 #define MIMEH_DEFECT_MULTIPLE_COLON_SEPARATORS 4 #define MIMEH_DEFECT_MULTIPLE_BOUNDARIES 5 #define MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE 6 #define MIMEH_DEFECT_MULTIPLE_FIELD_OCCURANCE 7 #define MIMEH_DEFECT_MISSING_SEPARATORS 8 #define MIMEH_DEFECT_MULTIPLE_NAMES 9 #define MIMEH_DEFECT_MULTIPLE_FILENAMES 10 struct MIMEH_header_info { char scratch[_MIMEH_STRLEN_MAX +1]; int content_type; char content_type_string[ _MIMEH_CONTENT_TYPE_MAX +1 ]; char content_description_string[ _MIMEH_CONTENT_DESCRIPTION_MAX +1 ]; char boundary[_MIMEH_STRLEN_MAX +1]; int boundary_located; char subject[_MIMEH_SUBJECTLEN_MAX +1]; char filename[_MIMEH_FILENAMELEN_MAX +1]; char name[_MIMEH_STRLEN_MAX +1]; /** 20041217-1601:PLD: New header fields to keep **/ char from[_MIMEH_STRLEN_MAX +1]; char date[_MIMEH_STRLEN_MAX +1]; char to[_MIMEH_STRLEN_MAX +1]; char messageid[_MIMEH_STRLEN_MAX +1]; char received[_MIMEH_STRLEN_MAX +1]; /** end of new fields **/ // Store multiple filenames struct SS_object ss_filenames; // Store multiple names struct SS_object ss_names; int content_transfer_encoding; char content_transfer_encoding_string[ _MIMEH_CONTENT_TRANSFER_ENCODING_MAX +1 ]; int content_disposition; char content_disposition_string[ _MIMEH_CONTENT_DISPOSITION_MAX +1 ]; int charset; int format; int file_has_uuencode; char uudec_name[_MIMEH_FILENAMELEN_MAX +1]; // UUDecode name. This is a post-decode information field. int current_recursion_level; // Malformed email reporting int defects[_MIMEH_DEFECT_ARRAY_SIZE]; int header_defect_count; // Special Exception flags int x_mac; // Set if the content type contains x-mac-* entries, which means a filename may contain /'s /** Header sanity level - indicates if any of the headers we apparently read are good **/ int sanity; /** 20051117-0932:PLD: Will be non-zero if email is MIME **/ int is_mime; char delimeter[3]; int crlf_count; // 200811151149:PLD: Tally's the number of CRLF lines int crcr_count; // 200811151149:PLD: Tally's the number of CRLF lines int lf_count; // 200811151149:PLD: Tally's the number of LF only lines }; #ifdef RIPMIME_V2XX struct MIMEH_header_node { struct MIMEH_header_info *header_list; struct MIMEH_header_node *next; }; struct MIMEH_email_info { char mailpack_name[1024]; struct MIMEH_header_node *headers; }; #endif int MIMEH_version(void); int MIMEH_init( void ); int MIMEH_set_debug( int level ); int MIMEH_set_verbosity( int level ); int MIMEH_set_verbosity_contenttype( int level ); int MIMEH_get_verbosity_contenttype( void ); int MIMEH_get_headers_sanity(void); int MIMEH_is_contenttype( int range_type, int content_type ); int MIMEH_set_mailbox( int level ); int MIMEH_set_doubleCR( int level ); int MIMEH_set_doubleCR_save( int level ); int MIMEH_get_doubleCR_save( void ); int MIMEH_set_headerfix( int level ); int MIMEH_set_headers_save( FILE *f ); int MIMEH_set_headers_nosave( void ); int MIMEH_get_headers_save( void ); char *MIMEH_get_headers_ptr( void ); int MIMEH_set_headers_save_original( int level ); char *MIMEH_get_headers_original_ptr( void ); int MIMEH_set_headers_original_save_to_file( FILE *f ); int MIMEH_get_doubleCR( void ); char *MIMEH_get_doubleCR_name( void ); int MIMEH_set_header_longsearch( int level ); int MIMEH_read_headers( struct MIMEH_header_info *hinfo, FFGET_FILE *f ); int MIMEH_headers_get( struct MIMEH_header_info *hinfo, FFGET_FILE *f ); int MIMEH_headers_process( struct MIMEH_header_info *hinfo, char *headers ); int MIMEH_headers_cleanup(); int MIMEH_parse_headers( FFGET_FILE *f, struct MIMEH_header_info *hinfo ); int MIMEH_display_info( struct MIMEH_header_info *hinfo ); int MIMEH_set_webform( int level ); int MIMEH_set_outputdir( char *dir ); int MIMEH_set_defect( struct MIMEH_header_info *hinfo, int defect ); int MIMEH_dump_defects( struct MIMEH_header_info *hinfo ); int MIMEH_get_defect_count( struct MIMEH_header_info *hinfo ); int MIMEH_set_report_MIME( int level ); #endif altermime-0.3.10/libmime-decoders.c0000644000000000000000000004535611107756671015650 0ustar rootroot#include #include #include #include #include #include #include #include #include #include #include "ffget.h" #include "pldstr.h" #include "logger.h" #include "libmime-decoders.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define MDECODE_ISO_CHARSET_SIZE_MAX 16 // Debug precodes #define MDECODE_DPEDANTIC ((glb.debug >= MDECODE_DEBUG_PEDANTIC)) #define MDECODE_DNORMAL ((glb.debug >= MDECODE_DEBUG_NORMAL )) #define DMD if ((glb.debug >= MDECODE_DEBUG_NORMAL)) /* our base 64 decoder table */ static unsigned char b64[256]={ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \ }; /** * Jan 17th 2007 jjohnston * Fixed a bug that decoded invalid QP sequences */ static unsigned char hexconv[256]={ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 20, 20, 20, 20, 20,\ 20, 10, 11, 12, 13, 14, 15, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 10, 11, 12, 13, 14, 15, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,\ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 \ }; struct MDECODE_globals { int debug; int verbose; int decode_qp; int decode_b64; }; static struct MDECODE_globals glb; int MDECODE_init( void ) { glb.debug = 0; glb.verbose = 0; glb.decode_qp = 1; glb.decode_b64 = 1; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_debug ID:1 Purpose: Sets the debug level for reporting in MIME Input: int level : What level of debugging to use, currently there are only two levels, 0 = none, > 0 = debug info Output: Errors: ------------------------------------------------------------------------*/ int MDECODE_set_debug( int level ) { glb.debug = level; return glb.debug; } int MDECODE_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } int MDECODE_set_decode_qp( int level ) { glb.decode_qp = level; return glb.decode_qp; } int MDECODE_set_decode_b64( int level ) { glb.decode_b64 = level; return glb.decode_b64; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_short64 ID:1 Purpose: Decodes a BASE64 encoded realm Input: char *realm : base64 encoded NUL terminated string Output: decoded data is written to the short64 char Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_short64( char *short64 ) { int i; int realm_size = strlen( short64 ); int stopcount = 0; /* How many stop (=) characters we've read in */ int c; /* a single char as retrieved using MDECODE_get_char() */ int char_count = 0; /* How many chars have been received */ char output[3]; /* The 4->3 byte output array */ char input[4]; /* The 4->3 byte input array */ char *outstring = short64; char_count = 0; while (char_count < realm_size) { /* Initialise the decode buffer */ input[0] = input[1] = input[2] = input[3] = 0; /* snatch 4 characters from the input */ for (i = 0; i < 4; i++) { /* get a char from the filestream */ c = *short64; short64++; /* assuming we've gotten this far, then we increment the char_count */ char_count++; /* if we detect the "stopchar" then we better increment the STOP counter */ if (c == '=') { stopcount++; } /* test for and discard invalid chars */ if (b64[c] == 0x80) { i--; continue; } /* do the conversion from encoded -> decoded */ input[i] = (char)b64[c]; } /* for */ /* now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data */ output[0] = (input[0] << 2) | (input[1] >> 4); output[1] = (input[1] << 4) | (input[2] >> 2); output[2] = (input[2] << 6) | input[3]; /* determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it */ if (i == 4) { for (i = 0; i < (3 -stopcount); i++){ *outstring = output[i]; outstring++; } /* copy our data across */ } /* if 4 chars were inputted */ } /* while more chars to proccess */ *outstring = '\0'; // Set the last char to NULL return 0; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_quoted_printable ID:1 Purpose: Decodes quoted printable encoded data. Input: char *line : \0 terminated string possibly containing quoted printable data int qpmode : Selects which decoding ruleset to use ( refer to RFC2047 ) Output: Decoded string is superimposed over the provided line parameter Returns: Returns the number of bytes decoded. ------------------------------------------------------------------------*/ int MDECODE_decode_quoted_printable( char *line, int qpmode, char esc_char ) { char c; /* The Character to output */ int op, ip; /* OutputPointer and InputPointer */ int slen = strlen(line); /* Length of our line */ DMD LOGGER_log("%s:%d:MDECODE_decode_quoted_printable:DEBUG: input string = '%s' Input length = %d\n",FL, line, slen); /* Initialise our "pointers" to the start of the encoded string */ ip=op=0; /* for every character in the string... */ for (ip = 0; ip < slen; ip++) { c = line[ip]; /* if we have the quoted-printable esc char, then lets get cracking */ if (c == esc_char) { /* if we have another two chars... */ if ((ip +1) < slen ) { int original_ip = ip; /* Is our next char a \n\r ? if it is, then we have to eliminate any further \r\n's etc so as to turn the =\n\r into a 'soft return', which basically means that we ignore it. Soft-breaks are used so we can fit our long lines into the requirement of a maximum of 76 characters per line. So we move the input-pointer along skipping each character without incrementing the output pointer. */ /** Absorb any trailing whitespaces **/ if (1) { char *w = &(line[ip +1]); while ((*w == '\t') || (*w == ' ')) {w++;ip++;} } /** Do we now have a line break ? **/ if (( line[ip +1] == '\n') || (line[ip +1] == '\r' )) { ip++; if ((ip+1 < slen)&&(( line[ip +1] == '\n') || (line[ip +1] == '\r' ))) { ip++; } continue; } else { /* if the characters following the '=' symbol are not of the \n or \r pair, then we will [currently] assume that the next two characters are in fact the hexadecimal encodings of the character we do want */ /** Revert to original position **/ ip = original_ip; /* convert our encoded character from HEX -> decimal */ if ( ip < slen-1 ) // was 2, proving - if there are 3 chars in string, =AB, slen = 3, ip = 1 { /** * Jan 17th 2007 jjohnston * Fixed a bug that decoded invalid QP sequences */ if(hexconv[(int)line[ip+1]] == 20 || hexconv[(int)line[ip+2]] == 20) { //LOGGER_log("%s:%d:MIME_decode_quoted_printable:NOTICE: Invalid characters for quoted-printable at '=%c%c'\n", FL, (int)&line[ip+1], (int)&line[ip+2]); } else { c = (char)hexconv[(int)line[ip+1]]*16 +hexconv[(int)line[ip+2]]; /* shuffle the pointer up two spaces */ ip+=2; } } else { LOGGER_log("%s:%d:MIME_decode_quoted_printable:WARNING: Ran out of characters when decoding end of '%s'\n", FL, &line[ip] ); } } } /* if there were two extra chars after the ='s */ /* if we didn't have enough characters, then we'll make the char the * string terminator (such as what happens when we get a =\n */ else { /* 2002-12-16:18H31: changed from 'line[ip]' to 'line[op]' */ line[op] = '\0'; /* 2002-12-16:18H32: added break statement - if we're out of chars, then we quit the for loop */ break; } /* else */ } /* if c was a encoding char */ else if (( c == '_' ) && ( qpmode == MDECODE_QPMODE_ISO )) { // RFC2047 (Section 4.2.(2)(3)) says that if we encounter a '_' character in our ISO encodings then // we must convert that to a space ( as we are not allowed to have spaces in any c = ' '; } /* put in the new character, be it converted or not */ line[op] = c; /* shuffle up the output line pointer */ op++; } /* for loop */ /* terminate the line */ line[op]='\0'; DMD LOGGER_log("%s:%d:MDECODE_decode_quoted_printable:DEBUG: Output = '%s' Output length = %d\n", FL, line, strlen(line)); // 2003-01-26:PLD: Changed from (op -1) -=> op return op; } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_text_line ID:1 Purpose: Decodes a line of text, checking for Quoted-Printable characters and converting them. Note - if the character converted is a \0 (after decoding) it shouldn't affect the calling parent because the calling parent should read back the returned string byte size and use fwrite() or other non-\0 affected writing/processing functions Input: char *line: pointer to the buffer/line we wish to convert/scan Output: int: size of final buffer in bytes. Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_qp_text( char *line ) { if (glb.decode_qp == 0) return strlen(line); return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '=' ); } int MDECODE_decode_qp_ISO( char *line ) { // return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_ISO, '=' ); return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '=' ); } int MDECODE_decode_multipart( char *line ) { return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_STD, '%' ); } /*------------------------------------------------------------------------ Procedure: MDECODE_decode_ISO ID:1 Purpose: Decodes an ISO ( RFC2047 ) encoded string into native codepage dependent output Input: char *isostring : String containing =?code-page?encoding-type?string?= format int length : length of the string we're decoding Output: isostring is overwritten with the decoded string. Errors: ------------------------------------------------------------------------*/ int MDECODE_decode_ISO( char *isostring, int size ) { char *start_pair, *end_pair; char *iso, *iso_copy; char encoding_type='-'; char encoding_charset[ MDECODE_ISO_CHARSET_SIZE_MAX ]; char *iso_start, *iso_end; int iso_decoded; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-string='%s'",FL,isostring); // Process of decoding the ISO encoded string sequence. // ( this process is repeated until we run out of ISO sequences ) // // 1. Check that the string has a =? sequence within it ( indicates the start of the ISO encoding // // 2. tokenise the sequence succeeding the =? token into its three (3) parts, namely the code-page, encoding-type and string respectively // // 3. decode the string based on the encoding type, Q = Quoted-Printable, B = BASE64 // iso_end = iso_start = NULL; start_pair = end_pair = NULL; iso_copy = malloc( sizeof(char) *( size +1 ) ); do { iso_decoded = 0; start_pair = strstr( isostring, "=?" ); // if ( start_pair ) end_pair = strstr( start_pair +2, "?=" ); if (( start_pair != NULL )) { iso_start = start_pair; // There's probably a better way of doing this, but, for us to find the end of this // particular 'ISO' sequence, we need to hop past 3 more ?'s ( assuming we've already // found the first one. DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO start = %s",FL,iso_start); iso_end = strchr( iso_start +strlen("=?"), '?' ); // Jump past the encoding if (iso_end) iso_end = strchr( iso_end +1, '?' ); // Jump past the Q or B if (iso_end) iso_end = strpbrk( iso_end +1, "?\n\r\t;" ); // dropped the SPACE here. if ((iso_end != NULL)&&(*iso_end == '?')) iso_end+=2; if ( (iso_start) && (iso_end) ) { char *token_end; char restore_char='\0'; // Copy the Encoding page/code. iso = iso_start +strlen("=?"); token_end = strchr(iso,'?'); if (token_end) *token_end = '\0'; snprintf( encoding_charset, sizeof( encoding_charset ), "%s", iso); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO char set = '%s'",FL,encoding_charset); iso = token_end +1; // Get the encoding _type_ (BASE64/QuotedPrintable etc) token_end = strchr(iso,'?'); encoding_type = *iso; iso = token_end +1; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO encoding char = '%c'",FL,encoding_type); // Get the encoded string token_end = strpbrk(iso,"?;\n\r\t"); //DROPPED THE SPACE here if (token_end != NULL) { if ((*token_end != '?')&&(*token_end != ';')) { restore_char = *token_end; } *token_end = '\0'; } if (iso) { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Encoded String = '%s'\n", FL, iso ); switch ( encoding_type ) { case MDECODE_ISO_ENCODING_Q: case MDECODE_ISO_ENCODING_q: DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoding filename using Quoted-Printable (%s)\n", FL, iso); MDECODE_decode_qp_ISO(iso); iso_decoded = 1; break; case MDECODE_ISO_ENCODING_B: case MDECODE_ISO_ENCODING_b: DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoding filename using BASE64 (%s)\n", FL, iso); MDECODE_decode_short64( iso ); iso_decoded = 1; break; default: if (glb.verbose) LOGGER_log("%s:%d:MDECODE_decode_ISO:ERROR: The encoding character '%c' is not a valid type of encoding\n", FL, encoding_type ); } // If we decoded the string okay, then we need to recompose the string if ( iso_decoded == 1 ) { char *new_end_pos; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Decoded String = '%s'\n", FL, iso ); *iso_start = '\0'; // Terminate the original string before the start of the ISO data // Because sometimes ISO strings are broken over multiple lines // due to wrapping requirements of RFC(2)822, we need to // sniff out these tab or spaces and crop them out of our // final ISO string. We cannot simply search for the next // =? sequence using strstr() because it might traverse // beyond the end of the current 'line' (ie, \r\n termination) if (token_end) { iso_end = token_end +1; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: iso_end = '%20s'",FL, iso_end); while ((*iso_end == '?')||(*iso_end == '=')) iso_end++; DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: iso_end = '%20s'",FL, iso_end); new_end_pos = iso_end; while ((*new_end_pos == ' ')||(*new_end_pos == '\t')) new_end_pos++; if (strncmp(new_end_pos,"=?",2)==0) iso_end = new_end_pos; } else { iso_end = NULL; } DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-END = '%20s'",FL,iso_end); /** We now have the string split into 3 peices, ** isostring = pointing to the start ** iso = newly decoded string ** iso_end = start of string after the non-decoded ISO portion **/ /** Generate new string using the decoded ISO to a temporary string **/ if (restore_char != '\0') { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Recomposing string with restore-char of '%c'",FL,restore_char); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO-end (start of end of string) is \n%s",FL,iso_end); snprintf( iso_copy, size, "%s%s%c%s", isostring, iso, restore_char, (iso_end?iso_end:"") ); } else { DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: Recomposing string with NO restore-char",FL,restore_char); snprintf( iso_copy, size, "%s%s%s", isostring, iso, (iso_end?iso_end:"") ); } /** Switch the new headers over to the original headers again **/ snprintf( isostring, size, "%s", iso_copy ); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: New ISO string = \n%s",FL,isostring); } } } } // if (iso_start) } while (iso_decoded == 1 ); if (iso_copy) free(iso_copy); return 0; } //------------END libmime-decoders.c altermime-0.3.10/libmime-decoders.h0000644000000000000000000000151611107756671015643 0ustar rootroot /* Debug levels */ #define MDECODE_DEBUG_PEDANTIC 10 #define MDECODE_DEBUG_NORMAL 1 /* Filename Encoding characters */ #define MDECODE_ISO_ENCODING_Q 'Q' #define MDECODE_ISO_ENCODING_q 'q' #define MDECODE_ISO_ENCODING_B 'B' #define MDECODE_ISO_ENCODING_b 'b' /* Quoted-Printable decoding modes */ #define MDECODE_QPMODE_STD 0 #define MDECODE_QPMODE_ISO 1 int MDECODE_set_debug( int level ); int MDECODE_set_verbose( int level ); int MDECODE_set_decode_qp( int level ); int MDECODE_set_decode_b64( int level ); int MDECODE_init( void ); int MDECODE_decode_quoted_printable( char *line, int qpmode, char esc_char ); int MDECODE_decode_short64( char *short64 ); int MDECODE_decode_multipart( char *line ); int MDECODE_decode_qp_text( char *line ); int MDECODE_decode_qp_ISO( char *line ); int MDECODE_decode_ISO( char *isostring, int size ); altermime-0.3.10/boundary-stack.c0000644000000000000000000003320311107756671015356 0ustar rootroot#include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "boundary-stack.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define BS_STRLEN_MAX 1024 #define BS_BOUNDARY_DETECT_LIMIT_DEFAULT 4 #define DBS if (glb.debug) struct BS_globals { int debug; int verbose; int syslogging; int errlogging; int count; int detect_limit; int hold_limit; int smallest_length; int have_empty_boundary; struct BS_node *boundarystack; char boundarystacksafe[BS_STRLEN_MAX]; }; struct BS_node { char *boundary; int boundary_length; int boundary_nhl; // length of boundary without hyphens struct BS_node *next; }; static struct BS_globals glb; /*-----------------------------------------------------------------\ Function Name : BS_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_init( void ) { glb.debug = 0; glb.verbose = 0; glb.syslogging = 1; glb.errlogging = 0; glb.count = 0; glb.detect_limit = BS_BOUNDARY_DETECT_LIMIT_DEFAULT; glb.hold_limit = 0; glb.boundarystack = NULL; glb.smallest_length = -1; glb.have_empty_boundary = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_set_hold_limit Returns Type : int ----Parameter List 1. int limit , how many boundary strings to hold ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_hold_limit( int limit ) { glb.hold_limit = limit; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_set_verbose Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_verbose( int level ) { glb.verbose = level; return glb.verbose; } /*-----------------------------------------------------------------\ Function Name : BS_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : BS_set_boundary_detect_limit Returns Type : int ----Parameter List 1. int limit , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_set_boundary_detect_limit( int limit ) { if ((limit > 0)&&(limit < BS_STRLEN_MAX)) { glb.detect_limit = limit; } return glb.detect_limit; } /*-----------------------------------------------------------------\ Function Name : BS_clear Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_clear( void ) { struct BS_node *next; while (glb.boundarystack) { DBS LOGGER_log("%s:%d:BS_clear:DEBUG: Popping off %p",FL,glb.boundarystack); next = glb.boundarystack->next; free(glb.boundarystack->boundary); free(glb.boundarystack); glb.boundarystack = next; } glb.boundarystack = NULL; glb.count = 0; glb.smallest_length = -1; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_non_hyphen_length Returns Type : int ----Parameter List 1. char *boundary , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_non_hyphen_length( char *boundary ) { int count = 0; char *p = boundary; while (*p) { if (isalnum((int)*p)) count++; p++; }; return count; } /*-----------------------------------------------------------------\ Function Name : BS_push Returns Type : int ----Parameter List 1. char *boundary , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_push( char *boundary ) { struct BS_node *node; if ((glb.hold_limit > 0)&&(glb.count >= glb.hold_limit)) { DBS LOGGER_log("%s:%d:BS_push:DEBUG: Number of boundaries to hold is at limit (limit=%d)",FL,glb.hold_limit); return 0; } node = malloc(sizeof(struct BS_node)); DBS LOGGER_log("%s:%d:BS_push:DEBUG: head = %p, nn = %p boundary = '%s'",FL, glb.boundarystack, node, boundary); if (node) { node->next = glb.boundarystack; glb.boundarystack = node; glb.boundarystack->boundary = strdup(boundary); glb.boundarystack->boundary_length = strlen(glb.boundarystack->boundary); if (glb.boundarystack->boundary_length == 0) glb.have_empty_boundary = 1; glb.boundarystack->boundary_nhl = BS_non_hyphen_length(boundary); glb.count++; // Set the smallest length if (glb.smallest_length == -1) glb.smallest_length = glb.boundarystack->boundary_length; else if (glb.boundarystack->boundary_length < glb.smallest_length) glb.smallest_length = glb.boundarystack->boundary_length; DBS LOGGER_log("%s:%d:DEBUGX: smallest = %d, NHL = %d, boundary = '%s'",FL,glb.smallest_length, node->boundary_nhl, boundary); } else { LOGGER_log("%s:%d:BS_push:ERROR: Cannot allocate memory for boundary stack PUSH, %s", FL, strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : *BS_pop Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *BS_pop( void ) { struct BS_node *node = glb.boundarystack; if (glb.boundarystack) { glb.boundarystack = glb.boundarystack->next; PLD_strncpy(glb.boundarystacksafe,node->boundary, BS_STRLEN_MAX); free(node->boundary); free(node); glb.count--; } return glb.boundarystacksafe; } /*-----------------------------------------------------------------\ Function Name : *BS_top Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *BS_top( void ) { if (glb.boundarystack) { return glb.boundarystack->boundary; } else return NULL; } /*-----------------------------------------------------------------\ Function Name : BS_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_count( void ) { return glb.count; } /*-----------------------------------------------------------------\ Function Name : BS_is_long_enough Returns Type : int ----Parameter List 1. int blen , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_is_long_enough( int blen ) { if (glb.smallest_length == -1) return 0; if (blen >= glb.smallest_length) return 1; return 0; } /*-----------------------------------------------------------------\ Function Name : BS_boundary_detect Returns Type : int ----Parameter List 1. char *needle, 2. char *haystack , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_boundary_detect( char *haystack, char *needle, int needle_length ) { int result=1; int current_start = glb.detect_limit; char *haystack_start; if ((glb.have_empty_boundary == 1)&&(needle_length < 1)) { result = strncmp(haystack,"--",2); DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: empty-boundary test, result = %d",FL, result); return result; } if ((needle_length < 1)&&(glb.have_empty_boundary == 0)) return 1; DBS LOGGER_log("%s:%d:BS_boundary_detect: needle='%s', length=%d, haystack='%s', shift-window=%d" ,FL ,needle ,needle_length ,haystack ,current_start ); haystack_start = haystack; while ((current_start-- > 0)&&(*haystack_start != '\0')) { DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: CMP '%s' to '%s'",FL, needle, haystack_start); if (strncmp( needle, haystack_start, needle_length )==0) { DBS LOGGER_log("%s:%d:BS_boundary_detect:DEBUG: Hit on compare",FL); result = 0; break; } haystack_start++; } return result; } /*-----------------------------------------------------------------\ Function Name : BS_cmp Returns Type : int ----Parameter List 1. char *boundary, the boundary we want to check to see if is in the stack 2. int len , the length of the boundary ------------------ Exit Codes : 1 == boundary found, 0 == no boundary found Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int BS_cmp( char *boundary, int len ) { char testspace[128]; // was 1024 int testspacelen=127; // was 1023 int spin=1; int nhl=0; struct BS_node *node=glb.boundarystack; struct BS_node *nodetmp=NULL, *nodedel=NULL; if ((!boundary)||(glb.count == 0)) return 0; //if ((glb.smallest_length > 0)&&(len < glb.smallest_length)) return 0; if (BS_is_long_enough(len) == 0) return 0; nhl = BS_non_hyphen_length(boundary); DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: possible-boundary='%s', len=%d, smallest=%d, count=%d, NHL=%d" , FL , boundary , len , glb.smallest_length , glb.count , nhl ); // Crop the incoming string to fit in our testspace length if (len > testspacelen) len = testspacelen; // Copy the potential boundary into our testspace snprintf(testspace, testspacelen, "%s", boundary); // First, search through the stack looking for a boundary that matches // our search criterion // // When we do find one, we will jump out of this WHILE loop by setting // 'spin' to 0. while((node)&&(spin)) { // if (node->boundary_length <= len) if (node->boundary_nhl == nhl) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Comparing '%s' to '%s'", FL, boundary, node->boundary); // * 20040903-08H57:PLD: Set boundary length comparison from > 0 to >= 0 if ((node->boundary != NULL)&&(node->boundary_length >= 0)) { if ((BS_boundary_detect(testspace, node->boundary, node->boundary_length))==0) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Boundary HIT",FL); spin = 0; } } } if (spin != 0) node = node->next; } // If we have a hit on the matching, then, according // to nested MIME rules, we must "remove" any previous // boundaries // // We know that we had a HIT in matching if spin == 0, because // in our previous code block that's what we set spin to if if(spin==0) { DBS LOGGER_log("%s:%d:BS_cmp:DEBUG: Boundary hit on '%s' == '%s'",FL, boundary,node->boundary); // If our "HIT" node is /NOT/ the one on the top of the // stack, then we need to pop off and deallocate the nodes // PRIOR/Above the hit node. // // ie, if "NODE" is not the top, then pop off until we // do get to the node if (node != glb.boundarystack) { nodetmp = glb.boundarystack; while ((nodetmp)&&(nodetmp != node)) { // - Set the node to delete (nodedel) to the current temp // node (notetmp) // - Increment the nodetmp to the next node in the stack // - Free the node to delete (nodedel) nodedel = nodetmp; nodetmp = nodetmp->next; free(nodedel->boundary); free(nodedel); } glb.boundarystack = node; } return 1; } else { return 0; } return 0; } altermime-0.3.10/boundary-stack.h0000644000000000000000000000051111107756671015357 0ustar rootroot int BS_init( void ); int BS_set_verbose( int level ); int BS_set_debug( int level ); int BS_set_boundary_detect_limit( int limit ); int BS_set_hold_limit( int limit ); int BS_clear( void ); int BS_push( char *boundary ); char *BS_pop( void ); char *BS_top( void ); int BS_cmp( char *boundary, int len ); int BS_count( void ); altermime-0.3.10/strstack.c0000644000000000000000000001536211107756671014274 0ustar rootroot#include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "strstack.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define DSS if (ss->debug) /*-----------------------------------------------------------------\ Function Name : SS_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_init( struct SS_object *ss ) { ss->debug = 0; ss->verbose = 0; ss->count = 0; ss->stringstack = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : SS_set_verbose Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_set_verbose( struct SS_object *ss, int level ) { ss->verbose = level; return ss->verbose; } /*-----------------------------------------------------------------\ Function Name : SS_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_set_debug( struct SS_object *ss, int level ) { ss->debug = level; return ss->debug; } /*-----------------------------------------------------------------\ Function Name : SS_done Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_done( struct SS_object *ss ) { struct SS_node *next; while ((ss->stringstack != NULL)&&(ss->count > 0)) { DSS LOGGER_log("%s:%d:SS_done: Popping off %s",FL,ss->stringstack->data); next = ss->stringstack->next; free(ss->stringstack->data); free(ss->stringstack); ss->stringstack = next; ss->count--; } ss->stringstack = NULL; ss->count = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : SS_dump Returns Type : int ----Parameter List 1. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_dump( struct SS_object *ss ) { struct SS_node *n = ss->stringstack; while (n != NULL) { LOGGER_log("%s",n->data); n = n->next; } return 0; } /*-----------------------------------------------------------------\ Function Name : SS_push Returns Type : int ----Parameter List 1. char *string , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_push( struct SS_object *ss, char *data, size_t data_length ) { struct SS_node *node = malloc(sizeof(struct SS_node)); if (node) { DSS LOGGER_log("%s:%d:SS_push: Pushing %s to %p, stack count = %d",FL,data, ss->stringstack, ss->count); node->next = ss->stringstack; ss->stringstack = node; ss->stringstack->data = strdup(data); ss->stringstack->data_length = data_length; ss->count++; } else { LOGGER_log("%s:%d:SS_push:ERROR: Cannot allocate memory for string stack PUSH, %s", FL, strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : *SS_pop Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_pop( struct SS_object *ss ) { struct SS_node *node = ss->stringstack; if ((ss->stringstack)&&(ss->count > 0)) { ss->stringstack = ss->stringstack->next; PLD_strncpy(ss->datastacksafe,node->data, SS_STRLEN_MAX); free(node->data); free(node); ss->count--; } else return NULL; return ss->datastacksafe; } /*-----------------------------------------------------------------\ Function Name : *SS_top Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_top( struct SS_object *ss ) { if (ss->stringstack) { return ss->stringstack->data; } else return NULL; } /*-----------------------------------------------------------------\ Function Name : SS_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int SS_count( struct SS_object *ss ) { return ss->count; } /*-----------------------------------------------------------------\ Function Name : *SS_cmp Returns Type : char ----Parameter List 1. struct SS_object *ss, 2. char *find_me , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *SS_cmp( struct SS_object *ss, char *find_me, size_t find_me_len ) { struct SS_node *n = ss->stringstack; int hit=0; while ((n != NULL)&&(hit == 0)) { if (strncmp(find_me, n->data, find_me_len) == 0) hit++; if (hit == 0) n = n->next; } if (hit == 0) return NULL; else return n->data; } altermime-0.3.10/strstack.h0000644000000000000000000000141711107756671014275 0ustar rootroot #ifndef __STRSTACK__ #define __STRSTACK__ #define SS_STRLEN_MAX 1024 struct SS_node { char *data; size_t data_length; struct SS_node *next; }; struct SS_object { int debug; int verbose; int count; int detect_limit; struct SS_node *stringstack; char datastacksafe[SS_STRLEN_MAX]; }; int SS_init( struct SS_object *ss ); int SS_set_verbose( struct SS_object *ss, int level ); int SS_set_debug( struct SS_object *ss, int level ); int SS_push( struct SS_object *ss, char *data, size_t data_length ); char *SS_pop( struct SS_object *ss ); char *SS_top( struct SS_object *ss ); char *SS_cmp( struct SS_object *ss, char *find_me, size_t find_me_len ); int SS_dump( struct SS_object *ss ); int SS_count( struct SS_object *ss ); int SS_done( struct SS_object *ss ); #endif altermime-0.3.10/qpe.c0000644000000000000000000001064611107756671013223 0ustar rootroot#include #include #include #include #include #include #include #include #include "logger.h" #include "qpe.h" #define CRLF "\r\n" #define QPD if (qpe_debug > 0) int qpe_debug = 0; int qp_encode_set_debug( int level ) { qpe_debug = level; return 0; } int qp_encode( char *out, size_t out_size, char *in, size_t in_size ) { int result = 0; size_t out_remaining; char *linestart, *lineend, *p, *op; char paragraph[100], *pp; size_t pp_remaining = 100; char *input_data_limit = in +in_size; size_t current_line_length = 0; linestart = NULL; lineend = in; /** Set the output buffer variables **/ op = out; out_remaining = out_size; do { char charout[4]; int charout_size=0; if (lineend != '\0') { if (linestart == NULL) { linestart = in; } else { linestart = lineend; } lineend = strstr(linestart, CRLF); if (lineend == NULL) { QPD fprintf(stdout,"No CRLF found, setting line-end to end of the input\n"); lineend = in +in_size; } else { QPD { *lineend = '\0'; fprintf(stdout,"INPUT STRING: '%s'\n", linestart); *lineend = '\r'; } lineend += strlen(CRLF); } } /** Initialize the paragraph **/ paragraph[0] = '\0'; pp = paragraph; pp_remaining = sizeof(paragraph); current_line_length = 0; QPD fprintf(stdout,"Starting new line of encoding...\n"); /** Set p to point to the start of the new line that we have to encode **/ p = linestart; while ((p < lineend)) { if (*p < 32 || *p == 61 || *p > 126) { /** encode as hex **/ snprintf( charout, sizeof(charout), "=%02X", (unsigned char)*p); // 20070212-2238 Fix supplied by Julian Schneider charout_size = 3; } else { /** encode verbatim **/ snprintf( charout, sizeof(charout), "%c", *p); charout_size = 1; } if (current_line_length +charout_size >= 79) { // Was 76, updated to fix Outlook problems //snprintf(op, out_remaining, "%s=\r\n", paragraph); snprintf(op, out_remaining, "%s=\r\n", paragraph); op+= strlen(paragraph);// +3; /** jump the output + =\r\n **/ out_remaining-= (strlen(paragraph)); // Was +3, updated to fix Outlook problems QPD fprintf(stdout, "Soft break (%d + %d > 76 char) for '%s'\n", current_line_length, charout_size, paragraph); /** reinitialize the paragraph **/ paragraph[0] = '\0'; pp_remaining = sizeof(paragraph); pp = paragraph; current_line_length=-1; } snprintf(pp, pp_remaining, "%s", charout); QPD fprintf(stdout,"charout='%s', size=%d, pp_remain=%d result='%s'\n", charout, charout_size, pp_remaining, paragraph); pp += charout_size; pp_remaining -= charout_size; p++; current_line_length += charout_size; // 2007030901 - Patch provided by Yossi Gottlieb } /** for each char in the line to be converted **/ QPD fprintf(stdout,"Adding paragraph '%s' to output\n", paragraph ); snprintf(op, out_remaining, "%s\r\n", paragraph); op += (strlen(paragraph) +2); out_remaining -= (strlen(paragraph) +2); } while ((lineend < input_data_limit)&&(*lineend != '\0')); /** for each line **/ return result; } int qp_encode_from_file( char *fname ) { size_t bc; struct stat st; int stat_result; char *in_buffer; char *out_buffer; size_t in_size, out_size; FILE *f; stat_result = stat( fname, &st ); if (stat_result != 0){ QPD fprintf(stderr, "Cannot locate file '%s' for loading and QP encoding (%s)\n", fname, strerror(errno)); return -1; } in_size = st.st_size; out_size = in_size *3; in_buffer = malloc( sizeof(char) *in_size +1); if (in_buffer == NULL) { QPD fprintf(stdout,"Error allocating %d bytes for input buffer\n", in_size); return -1; } out_buffer = malloc( sizeof(char) *out_size *3 +1); if (in_buffer == NULL) { QPD fprintf(stdout,"Error allocating %d bytes for output buffer\n", out_size); return -1; } f = fopen( fname, "r" ); bc = fread( in_buffer, 1, in_size, f ); if (bc != in_size) LOGGER_log("%s:%d:qp_encode_from_file:ERROR: Read %d bytes but requested %d", FL, bc, in_size); fclose(f); /** zero terminate the buffer -- uhuh, if you forget that you'll wonder why ** we segfault ;) **/ *(in_buffer +in_size) = '\0'; QPD fprintf(stdout,"file %s is loaded, size = %d\n", fname, in_size); qp_encode( out_buffer, out_size, in_buffer, in_size ); fprintf( stdout, "%s", out_buffer ); free(in_buffer); free(out_buffer); return 0; } altermime-0.3.10/qpe.h0000644000000000000000000000030211107756671013214 0ustar rootroot#ifndef _QPENCODE_ #define _QPENCODE_ int qp_encode_set_debug(int level); int qp_encode( char *out, size_t out_size, char *in, size_t in_size ); int qp_encode_from_file( char *fname ); #endif altermime-0.3.10/Makefile0000644000000000000000000000144311107756671013725 0ustar rootroot# Wedit Makefile for project altermime #CC=cc #CC=ccmalloc gcc #CFLAGS=-Wall -g #CFLAGS=-Wall -ggdb # Optional builds # ALTERMIME_PRETEXT - Allows prefixing of the email body with a file, sort of the # opposite of a disclaimer. #ALTERMIME_OPTIONS=-DALTERMIME_PRETEXT ALTERMIME_OPTIONS= CFLAGS=-Wall -Werror -g -I. -O2 $(ALTERMIME_OPTIONS) OBJS= strstack.o mime_alter.o ffget.o pldstr.o filename-filters.o logger.o MIME_headers.o libmime-decoders.o boundary-stack.o qpe.o .c.o: ${CC} ${CFLAGS} -c $*.c all: altermime altermime: altermime.c ${OBJS} ${CC} ${CFLAGS} altermime.c ${OBJS} -o altermime # Build Install install: altermime strip altermime cp altermime /usr/local/bin chmod a+rx /usr/local/bin/altermime uninstall: rm -f /usr/local/bin/altermime clean: rm -f *.o altermime altermime-0.3.10/LICENCE0000644000000000000000000000753611107756671013263 0ustar rootroot alterMIME LICENSE The following license terms and conditions apply, unless a different license is obtained from P.L.Daniels, P.O.Box 6, Ravenswood, 4816 Australia, or by electronic mail at pldaniels@pldaniels.com. License Terms: Use, Modification and Redistribution (including distribution of any modified or derived work) in source and binary forms is permitted only if each of the following conditions is met: 1. Redistributions qualify as "freeware" or "Open Source Software" under one of the following terms: (a) Redistributions are made at no charge beyond the reasonable cost of materials and delivery. (b) Redistributions are accompanied by a copy of the Source Code or by an irrevocable offer to provide a copy of the Source Code for up to three years at the cost of materials and delivery. Such redistributions must allow further use, modification, and redistribution of the Source Code under substantially the same terms as this license. For the purposes of redistribution "Source Code" means the complete compilable and linkable source code of alterMIME including all modifications. 2. Redistributions of source code must retain the copyright notices as they appear in each source code file, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below. 3. Redistributions in binary form must reproduce the Copyright Notice, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below, in the documentation and/or other materials provided with the distribution. For the purposes of binary distribution the "Copyright Notice" refers to the following language: "Copyright (c) 2000 P.L.Daniels, All rights reserved." 4. Neither the name of alterMIME, nor Paul L Daniels, nor the the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. 5. All redistributions must comply with the conditions imposed by the University of California on certain embedded code, whose copyright notice and conditions for redistribution are as follows: (a) Copyright (c) 2000 P.L.Daniels, All rights reserved. (b) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: (i) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. (ii) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. (iii) Neither the name of alterMIME, nor P.L.Daniels, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY P.L.Daniels. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. altermime-0.3.10/INSTALL0000644000000000000000000000022011107756671013306 0ustar rootrootalterMIME - Install Somewhat easy... make make install That will place the program "altermime" into your /usr/local/bin directory. --END. altermime-0.3.10/README0000644000000000000000000000111111107756671013135 0ustar rootrootalterMIME - MIME encoded email pack alteration tool Author : Paul L Daniels Date of Creation : 02/05/2001 Package : Inflex/ripMIME/XaMime suite URL: http://pldaniels.com/altermime The alterMIME program (and objects) has been written to facilitate the modification of MIME encoded email packs when such things as disclaimer additions and attachment nullification is required. alterMIME is part of the suite of software which includes Inflex email scanner, ripMIME attachment extractor and the XaMime commercial email scanner. Regards. Paul L Daniels pldaniels@pldaniels.com --END. altermime-0.3.10/CHANGELOG0000644000000000000000000004356411107756671013511 0ustar rootroot alterMIME CHANGELOG: -------------------------------------------------------------------------- -Abbreviations Used: DEV = This update was released as a DEVELOPMENT release of alterMIME the change will be included in the next STABLE release as well. STA = This update was released as a STABLE release of alterMIME -------------------------------------------------------------------------- 16 Nov 2008: PLD-REL: 0.3.10 15 Nov 2008: PLD-DEV: Storing line delimeter in the alterMIME global struct. Saves having to redefine/locate/check it all the time. Added CR, CRLF and CRCR occurance counters in MIME_headers Fixed up problem with AM_adapt_linebreak which caused total stripping of delimeters 06 Nov 2008: PLD-DEV: Moved AM_globals structure to the mime_alter.h file so that we can prepare for 0.4.x series which will provide an API change forcing us to pass an AM_global struct into every AM_* function. This will allow for multiple instances of the alterMIME engine in a given process without clashing issues of various global vars. Should have done this years ago. PLD-DEV: Added an output filter AM_adapt_linebreak() to feed disclaimers through so that their linebreaks match the last break type detected with the FFGET() reading. 30 Oct 2008: PLD-REL: Cleaned up compile warnings (against Ubuntu's 8.10's gcc, including fread/fwrite return values and the PLD_strlower() signed parameter issues. 14 Oct 2008: PLD-DEV: qpe.c updated to handle broken Outlook issue. 07 Oct 2008: -PLD:DEV: Added altermime_status_flags global to allow for easy inspection of the result of a disclaimer or xheader insertion run. 15 Aug 2008: -PLD:DEV: Extended the search depth of the segment-3+ insertion of disclaimers allowing it to find potential HTML/Text disclaimer segments that may have been burried deeper in a non-forwarded email. Insertion search will terminate immediately if a RFC822 forwarded email header is located. 20 Jul 2007: -PLD:DEV: Added closing of open file handles in AM nullify. (this would cause files to be zeroed if one performed an operation after nullifying attachments). Fixed small memory leak from regex compilation in AM nullify. 13 Jul 2007: -PLD:REL: Release as 0.3.8 -PLD:DEV: Updated ffget module. Corrected some code where hard-coded buffer sizes were present rather than properly referenced sizes. 09 Jun 2007: -PLD:DEV: Temporary file removal if insertion is aborted due to signed email - Carlos Velasco -PLD:DEV: Added text/calendar detection to prevent mutating outlook calendars (and others) -PLD:DEV: Preparing for STABLE release 09 Mar 2007: -PLD:DEV: Fixed bug that can cause crashing on QP decoding where the charout size wasn't 1. Thanks to Yossi Gottlieb for suppling the patch. 07 Mar 2007: -PLD:DEV: Added text/calendar detection, this allows us to implicitly avoid adding disclaimers into these bodies by virtue of the fact that alterMIME won't detect it as a "plain or HTML" text body. 12 Feb 2007: -PLD:DEV: Patch applied for QP encoding problem of chars as hex. (Patch provided by Julian Schneider) 30 Jan 2007: -PLD:DEV: Fixed missing boundary situation after BASE64 encoded disclaimer insetion. Fixed non-MIME BASE64 disclaimer insertion routines (ie, for emails with only a single segment and no boundary). 28 Jan 2007: -PLD:DEV: Added BASE64 _encoded_ disclaimer option so that it's possible to insert a BASE64 pre encoded dislciamer file into BASE64 bodies ONLY (ie, it won't be applied to plain or QP encoded bodies). --disclaimer-b64= 23 Jan 2007: -PLD:DEV: Added BASE64 disclaimer insertion routines (NAB) Requires the use of --force-into-b64 to activate Currently only works for text/plain and text/html content-type segments. Using for other segment types may cause unpredictable results (as it is the results are not proven). 14 Jul 2006: -PLD:DEV: Rolled in new FFGET module with cleaner code. Implemented FFGET_ftell / FFGET_fseek in the FFGET routines to allow for better debugging and potential features in alterMIME 22 Jun 2006: -PLD:DEV: Fixed --help output to indicate that --remove= is the correct notion, as apposed to the non-existant "--remove-regex". Thanks to Alex Samorukov for pointing this out. 29 Mar 2006: -PLD:REL: Released as 0.3.7 27 Mar 2006: -PLD:DEV: Added SEGMENT 3 disclaimer insertion attempt to handle emails that have both text and HTML representations of the email message. 02 Mar 2006: -PLD:DEV: Applied patch from Tim Jackson which solves a BASE64 induced mail truncation if disclaimer insertion fails. 01 Mar 2006: -PLD:DEV: Added Quoted-printable encoding to disclaimers if required by the transfer-encoding header of the MIME segment. 14 Feb 2006: -PLD:DEV: Fixed disclaimer insertion issue in emails with both HTML and attachment. 27 Mar 2005: -PLD:REL: Release as 0.3.6 -PLD:DEV: -Added -O2 option to Makefile for optimised binary -Added --no-qmail-bounce parameter to alterMIME to allow people to disable the email body search for files/attachments during a remove/removeall operation. -Added filename searching for the name= header parameters for remove facility. -Added headers-longsearch (qmail bounce) type detection for remove facility. 24 Mar 2005: -PLD:DEV: -Fixed multipart[-insert] discrepency between help output and actual functionality. --multipart-insert is the now correct parameter. -PLD:DEV: -Fixed incorrect parsing of body data when a header could not be inserted on the first segment, causing alterMIME to start reading the body text as if it was header data. Thanks to Pieter Vanmeerbeek for pointing this out and providing a set of data that reliably produced the problem. -PLD:DEV: -Fixed help output to display 'suffix' rather than 'postfix' for header alteration feature. 04 Feb 2005: -PLD:DEV: -Changed header insertion line from using a hard coded \r\n to using the contents of the 'true blank' separator line. (see AM_insert_Xheader) 20 Nov 2004: -PLD:DEV: -Fixed mistake in AM_add_disclaimer_cleanup(), where it incorrectly tests the input file for stdin using strcmp() causing alterMIME to not rename the new file. 04 Nov 2004: -PLD:DEV: Changed \n to \r\n delimeter for the X-header insertion routine 19 Oct 2004 -PLD:DEV: Fixed up \r\n issues with UNIX created disclaimers showing up fine in UNIX but as a single line in Windows. This one was for Neil Wilson of ZA. -PLD:DEV: Added patch from Tim Bruylants to allow for forcing of disclaimers in HTML MIME segments even if the segment doesn't terminate with /body /html. The patch also permits the use of disclaimers from STDIN (to STDOUT). Many thanks Tim. Fixed misspelling of 'filname' in alterMIME help/usage 18 Sep 2004 -PLD:REL: Release 0.3.5 -PLD:DEV: Rewrite the AM_add_disclaimer() function, breaking it down into smaller functions in an attempt to make it readable. AM_add_disclaimer now calls; AM_load_disclaimers() - Load the disclaimers required to insert. AM_read_to_boundary() - read input file till a boundary is hit. AM_add_disclaimer_no_boudary() - Insert into a non-boundary email. AM_insert_disclaimer_into_segment() - Insert into a MIME segment 17 Sep 2004 -PLD:DEV: Added content type and content transfer encoding types to NOT insert disclaimers into. See the function AM_read_headers() where the switch hinfo.content_type and .content_transfer_encoding is listed. This prevents some potential infinite loops when alterMIME tried to read the data as headers, rather than as a MIME body. Added BS_set_hold_limit() to limit the number of boundaries held in the stack to a certain limit (in the case of alterMIME, 1). This is done so that alterMIME doesn't try to traverse into nested segments. 16 Sep 2004 -PLD:DEV: Changed boundary tests from strstr() to BS_cmp() Updated MIME_headers code to match the ripMIME engine Fixed bug where some infinite loops occured while trying to insert a HTML disclaimer 06 May 2004 -PLD:DEV: Stopped alterMIME from inserting a disclaimer in a B64 encoded main body. 10 Mar 2004 -PLD:REL: Release 0.3.4 09 Mar 2004 -PLD:DEV: Fixed up header clobbering where the fields were ISO encoded and duly un-encoded when filtered via alterMIME. Thanks to Viktoras Rynkevicius for persuing this one. 16 Jan 2003 -PLD:DEV: Added initializers for some hinfo fields to pacify valgrind -PLD:DEV: Added AM_done() function to clear any remaining disclaimer malloc'd data produced from the strdup() call inside of AM_set_disclaimer_*() 30 Nov 2003 -PLD:DEV: Implementing regex matching for attachment removal 14 Nov 2003 -PLD:REL: Released alterMIME 0.3.3 30 Oct 2003 26554 -PLD:DEV: Added x-header string filtering to prevent dubious strings getting through, such as ones containing \n\r sequences or 0 length strings. 21 Oct 2003 -PLD:DEV: Reimplementing S/MIME detection (development) 26 Jun 2003 -PLD:DEV: Applied patch from Ludwig Pummer to fix up stdin/out streaming for the --remove facility 11 Jun 2003 -PLD:DEV: Applied fix from Jorge Jimenez to resolve issues with HTML disclaimer insertion in certain situations. 14 May 2003 -PLD: Fixed attachment-replace path inclusion into the MIME-headers -PLD: Removed spurilous parameters off AM_add_disclaimer() call which caused some segfault behavior in other external applications which were using the alterMIME engine 04 May 2003 -PLD: Release as 0.3.1 28 April 2003 -PLD: replaced the attachment replacement code. Uses a very similar structure to the code as used by the nullification/remove system [ with some minor changes ]. There are still some outstanding issues, like how to cleanly deal with the 'main' headers of each MIME, mostly in regards to the fact that unlike the per-attachment headers, the main headers typically contain a lot of other email-related information. Overall the code works a lot better than the old one, despite the current limitations. Now, if an attachment is replaced, its old headers are also replaced. The new headers are just Content-Type, Content-Disposition and Content-Transfer-Encoding. There may be more headers added later depending on the needs encountered. Additionally, we can currently only add attachments in BASE64 encoding format, I will extend this in the future to support QuotedPrintable and plain-text. 27 April 2003 -PLD: Fixed header-alteration in Suffix mode problem where it would split a doubleCR or singleCR line incorrectly, because it was looking for a LF but didn't find one. -PLD: Changed the nullification logic slightly so that the first MIME segment can be 'nullified'. Rathern than being able to remove the actual headers which specify the file details, it's been currently chosen to clobber the filename in the headers by prefixing with either a _ or X character and then proceeding to nullify the contents -PLD: Nullification specification via --nullify= can be a regex expression. This means that you can now remove multiple files, ie --remove='\.exe$' --remove-all has been retained, despite the fact that the feature can be achived using --remove='.*' 26 April 2003 - Changed/replaced the AM_section1() call into AM_read_headers() in which I then call MIMEH_parse_headers(), which does a vastly superior job of detecting the /right/ headers. - Replaced the AM_nullify_attachment() with a far more compact version which does a far cleaner job of nullifying, removing the entire attachment and headers [ except for attachments which are the entire email body, in which case I have yet to still decide/workout how best to handle the situation ] 25 April 2003 - Releasing as 0.2.2, this will be the last [ feature ] release for the 0.2.2 series. After this I will rip out a lot of the code dealing with the header parsing and replace it with that which is now being used in ripMIME - The new series will be 0.3.x 21 March 2003 - Added generic header replacement tool: alterMIME now allows you to change headers [if they already exist] so that you can do things like change subject headers. A future extention will be to allow the modification of headers on all the MIME segments rather than just the main headers. To use: --alter-header="header-you-want-to-change" --alter-mode=prefix|suffix|replace --alter-with="text to use" --alter-mode= modes explanation: prefix: prefixes the text defined with --alter-with before the current existing text for the header suffix: suffixes the text defined with --alter-with after the current existing text for the header replace: replaces all the currently existing header text with the text provided by --alter-with 16 March 2003 - Released v0.2.1 23/02/2003 - Unified logging output routines ( all logging now goes via LOGGER ) - Added logging output mode option ( stdout / stderr / syslog ) - Added description of available options for alterMIME in verbose/help mode - Cleaned up altermime.c source code, placing all loose global vars into single passed struct 22/02/2003 - Replaced strlower, XAM_strtok and zstr with a single module 'pldstr'. - Added new modules filename-filter and libmimedecoders - Added --disclaimer-html option which allows you to have a seperate plaintext and HTML disclaimer - Added --multipart option to force alterMIME to insert disclaimers into the first level multipart block in an email, this means that you can effective insert disclaimers into forwarded emails. 12/11/2002 - Released as 0.1.14 - Corrected a FFGET module bug which caused terminating lines without a \n or \r to contain data from the previous line ( ie, there was no terminating \0 at the string ending, thus the previous data in the buffer simply filled up the rest of the buffer ) - Corrected the insertion of disclaimers in situations where a file attachment ends without a boundary line. - Corrected the insertion of disclaimers in situations where a single file is attached with no email bodies. 12/05/2002 - Released as 0.1.13 - Cleaned up disclaimer insertion routines so that disclaimers are not inserted into the incorrect MIME segment - Used ccmalloc to test the allocation of memory when running --xheader or --disclaimer , I'm pleased to say that both operations returned with no under or over use of memory :-) ccmalloc is a great program tool, I highly recommend it for when you're tracking down wierd bugs, often, just by cleaning up the memory spillages you fix you bugs. - ccmalloc is by Armin Biere. (biere at inf.ethz.ch) 26/04/2002 - Released as 0.1.12 - Corrected segfault issue with x-header insertion, would occur once every 1000 or so emails was caused by using snprintf() in alterMIME with one byte too high a buffer size value this in turn cased the FFGET_FILE variable to be overwritten with a \0 byte causing the program to crash on exit (which was rather interesting to watch). - Improved the disclaimer insertion routines. Hopefully now it'll correctly put in the right style of disclaimer (HTML or Plain) based on header information. 19/04/2002 - Corrected x-header insertion code, problem actually resided in the FFGET module which was returning NULL on FFGET_fgets() one call too early. 06/03/2002 - Replaced AM_build_table() with a static decoding table at compile time - Added FFGET library for file input and correct \n\r detections in headers - Updated AM_insert_Xheader() routine. 22/10/2001 - Release 0.1.10 - Moved strlower() call into mime_alter codebase and renamed it to AM_strlower. Also improved the speed of the strlower call by removing the testing for uppercase on each char. - Makefile: Added strip command to make install - mime_alter:AM_add_disclaimer: Added logic to deal with broken AOL emails [missing BODY tag] - Thanks to Sean Kelly for supplying the mailpack. 21/10/2001 - Released 0.1.9 - Corrected boundary quotation closure issue. 20/10/2001 - Release 0.1.8 - Changed location detection algorithm for disclaimer insertion, specifically to deal with emails which are [in effect] nested one level down despite being new emails. - No longer alters signed messages [for disclaimer addtion] without being forced via --altersigned option - Thanks to Raymond Bramwell for providing the assortment of mailpacks showing up these intersting issues 17/10/2001 - Improved HTML disclaimer addition, prevented spurilous line addtion in emails which had no additional attachments 15/10/2001 - Added ability to nullify all named attachments (feature Plead by Estaga) - Improved disclaimer insertion technique so as not to break files which didn't have tag on a seperate line - Made disclaimer addition remove the temp file after completion 09/03/2001 - Reworked replacement file section, making it buffer the headers before writing out, this allows us to then change the headers to suit the new file name without requiring two passes of the file. - Got rid of the spurilous alterMIME decoding messages (why did I leave them there in the first place!!#@$!!! 03/08/2001 - Fixed up potential sig11 issues with files being read which didn't exist. 21/07/2001 - Fixed up disclaimer intertion routine if file did not contain a boundary specifier. 01/07/2001 - Added xheader insertion facility (from ripMIME) 17/06/2001 - Added attachment replace ability - Fixed up disclaimer ability to enable both HTML and plaintext portions to be updated 05/06/2001- - Fixed up Makefile - Released as alterMIME 0.1.3 07/05/2001 - - Rewrote "unable to add disclaimer" message to make more sence in mime_alter.c 05/05/2001 - - Added Makefile to package. 02/05/2001 - - Initial development and release. - Features available are: disclaimer-addition attachment-nullification (rendering attachment to 0 bytes) ---------------------------------------------