ripmime-1.4.0.10.debian.1/0000770000175000017500000000000011667102337013070 5ustar pjbpjbripmime-1.4.0.10.debian.1/libmime-decoders.c0000640000175000017500000005032011667102337016437 0ustar pjbpjb#include #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 // 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; char out_charset[MDECODE_ISO_CHARSET_SIZE_MAX]; char current_charset[MDECODE_ISO_CHARSET_SIZE_MAX]; }; static struct MDECODE_globals glb; int MDECODE_init( void ) { glb.debug = 0; glb.verbose = 0; glb.decode_qp = 1; glb.decode_b64 = 1; glb.out_charset[0] = '\0'; glb.current_charset[0] = '\0'; 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; } int MDECODE_set_out_charset( char *charset ) { PLD_strncpy(glb.out_charset, charset, sizeof(glb.out_charset)); return 0; } static void iconvert(char *text) { if ((glb.out_charset[0] == 0) || (glb.current_charset[0] == 0) || (strcmp(glb.out_charset, glb.current_charset) == 0)) return; iconv_t _iconv = iconv_open(glb.out_charset, glb.current_charset); if ((iconv_t) -1 != _iconv) { size_t inbytesleft = strlen(text); size_t outbytesleft = inbytesleft; char *out_str = malloc(outbytesleft +1); char *out_str_pp = out_str; char *in_str_pp = text; size_t iconv_res = iconv(_iconv, &in_str_pp, &inbytesleft, &out_str_pp, &outbytesleft); if (iconv_res == -1) { switch(errno) { case EILSEQ: case EINVAL: LOGGER_log("%s: iconv: invalid multibyte sequence\n", FL); break; case E2BIG: // This is possible if the input string is encoded in a single-byte encoding, // and output need to Multibyte. // This situation we can not process, // because input string is part of the buffer header mime... LOGGER_log("%s: iconv: there is not sufficient room at outbuf\n", FL); break; default: LOGGER_log("%s: iconv: unknown error\n", FL); break; } abort(); } *out_str_pp = '\0'; snprintf(text, strlen(text), "%s", out_str); iconv_close(_iconv); free(out_str); } } /*------------------------------------------------------------------------ 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_STD, '=' ); return MDECODE_decode_quoted_printable( line, MDECODE_QPMODE_ISO, '=' ); } 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 *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( glb.current_charset, sizeof(glb.current_charset), "%s", iso); DMD LOGGER_log("%s:%d:MDECODE_decode_ISO:DEBUG: ISO char set = '%s'",FL,glb.current_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); iconvert(isostring); return 0; } //------------END libmime-decoders.c ripmime-1.4.0.10.debian.1/generate-buildcodes.sh0000750000175000017500000000067411667102337017341 0ustar pjbpjb#!/bin/sh build_code=`date +%s` build_date=`date` build_box=`uname -a` #build_cmd=`history !-1` BCF='buildcodes.h' printf "\n// Autogenerated buildcode file - this will change on every make\n" > $BCF printf "#define BUILD_CODE \"$build_code\"\n" >> $BCF printf "#define BUILD_DATE \"$build_date\"\n" >> $BCF printf "#define BUILD_BOX \"$build_box\"\n" >> $BCF #printf "#define BUILD_CMD \"$build_cmd\"\n" >> $BCF printf "\n" >> $BCF exit 0 ripmime-1.4.0.10.debian.1/boundary-stack.h0000640000175000017500000000051111667102337016163 0ustar pjbpjb 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 ); ripmime-1.4.0.10.debian.1/ripmime-api.c0000750000175000017500000000632511667102337015454 0ustar pjbpjb/*---------------------------------------- * ripmime-api * * Written by Paul L Daniels * pldaniels@pldaniels.com * * (C)2001 P.L.Daniels * http://www.pldaniels.com/ripmime * */ #include #include #include #include #include #include #include #include #include #include #include "logger.h" #include "ffget.h" #include "strstack.h" #include "mime.h" #include "MIME_headers.h" #include "ripmime-api.h" char defaultdir[] = "."; char version[] = "v1.4.0.1 - 30/08/2004 (C) PLDaniels http://www.pldaniels.com/ripmime"; /*-----------------------------------------------------------------\ Function Name : RIPMIME_init Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_init (struct RIPMIME_object *rm) { rm->outputdir = defaultdir; rm->mailpack = NULL; LOGGER_set_output_mode(_LOGGER_STDOUT); MIME_init(); MIME_set_uniquenames(1); MIME_set_paranoid(0); MIME_set_renamemethod(_MIME_RENAME_METHOD_INFIX); MIME_set_verbosity(0); return 0; } /*-----------------------------------------------------------------\ Function Name : main Returns Type : int ----Parameter List 1. int argc, 2. char **argv, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_decode( struct RIPMIME_object *rm, char *mailpack, char *outputdir ) { int result = 0; if (!mailpack) { LOGGER_log("%s:%d:RIPMIME_decode: mailpack filename is NULL\n",FL); return 1; } else { rm->mailpack = strdup(mailpack); } if (!outputdir) { LOGGER_log("%s:%d:RIPMIME_decode: output directory is NULL\n",FL); return 1; } else { rm->outputdir = strdup(outputdir); } // Fire up the randomizer srand (time (NULL)); // clean up the output directory name if required (remove any trailing /'s, as suggested by James Cownie 03/02/2001 if (rm->outputdir[strlen (rm->outputdir) - 1] == '/') { rm->outputdir[strlen (rm->outputdir) - 1] = '\0'; } // Create the output directory required as specified by the -d parameter if (rm->outputdir != defaultdir) { result = mkdir (rm->outputdir, S_IRWXU); // if we had a problem creating a directory, and it wasn't just // due to the directory already existing, then we have a bit of // a problem on our hands, hence, report it. // if ((result == -1) && (errno != EEXIST)) { fprintf (stderr, "ripMIME: Cannot create directory '%s' (%s)\n", rm->outputdir, strerror (errno)); return -1; } } // Unpack the contents MIMEH_set_outputdir(rm->outputdir); MIME_unpack (rm->outputdir, rm->mailpack, 0); // do any last minute things MIME_close (); return 0; } /*-END-----------------------------------------------------------*/ ripmime-1.4.0.10.debian.1/build_tnef0000750000175000017500000000003011667102337015120 0ustar pjbpjb#!/bin/sh cd tnef make ripmime-1.4.0.10.debian.1/rawget.h0000640000175000017500000000007411667102337014532 0ustar pjbpjbint RAWGET_get( unsigned char *buffer, int max, FILE *f ); ripmime-1.4.0.10.debian.1/boundary-stack.c0000640000175000017500000003320311667102337016162 0ustar pjbpjb#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; } ripmime-1.4.0.10.debian.1/LICENSE0000640000175000017500000000270411667102337014077 0ustar pjbpjbCopyright (c) 2003, PLD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of the PLD nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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 DAMAGE. ripmime-1.4.0.10.debian.1/filename-filters.c0000640000175000017500000001427411667102337016471 0ustar pjbpjb/*------------------------------------------------------------------------ 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; } ripmime-1.4.0.10.debian.1/pldstr.h0000640000175000017500000000165411667102337014556 0ustar pjbpjb#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 ripmime-1.4.0.10.debian.1/tnef/0000770000175000017500000000000012364735745014036 5ustar pjbpjbripmime-1.4.0.10.debian.1/tnef/config.h0000640000175000017500000000635211667102337015447 0ustar pjbpjb/*************************************************************************** * * config.h for tnef decoder by Brandon Long * Based on config.h from S3MOD by Dan Marks and David Jeske * * (C) 1994,1995 By Daniel Marks and David Jeske * * While we retain the copyright to this code, this source code is FREE. * You may use it in any way you wish, in any product you wish. You may * NOT steal the copyright for this code from us. * * We respectfully ask that you email one of us, if possible, if you * produce something significant with this code, or if you have any bug * fixes to contribute. We also request that you give credit where * credit is due if you include part of this code in a program of your own. * *************************************************************************** * * config.h - compile time configuration options and system specific defines * */ #ifndef _CONFIG_H #define _CONFIG_H 1 /***************************************************************************/ /* The following are system specific settings */ /***************************************************************************/ #if defined(SUN) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined (HPUX) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined(DEC) #undef NEAR_FAR_PTR #elif defined(__sgi) #define BIT_32 #define ___TNEF_BYTE_ORDER 4321 #undef NEAR_FAR_PTR #elif defined(AIX) #undef NEAR_FAR_PTR #define ___TNEF_BYTE_ORDER 4321 #define BIT_32 #elif defined(LINUX) #define BIT_32 #undef NEAR_FAR_PTR #elif defined(MSDOS) #define NEAR_FAR_PTR #undef BIT_32 #else #undef NEAR_FAR_PTR #define BIT_32 #endif /* OS/MACH TYPE */ /***************************************************************************/ /* 16/32 Bit and Byte Order hacks */ /***************************************************************************/ #ifdef BIT_32 typedef short int int16; typedef unsigned short int uint16; typedef int int32; typedef unsigned int uint32; typedef char int8; typedef unsigned char uint8; #else typedef int int16; typedef unsigned int uint16; typedef long int int32; typedef unsigned long int uint32; typedef char int8; typedef unsigned char uint8; #endif /* BIT_32 */ #ifndef WIN32_TYPES #define ULONG uint32 #define SCODE uint32 #define FAR #define LPVOID void * #define WORD uint16 #define DWORD uint32 #define LONG int32 #define BYTE uint8 #endif /* !WIN32_TYPES */ #define endian_switch(x) (((((uint16)(x)) & 0xFF00) >> 8) | \ ((((uint16)(x)) & 0xFF) << 8)) #define long_endian_switch(x) ( ((((uint32)(x)) & 0xFF00UL) << 8) | \ ((((uint32)(x)) & 0xFFUL) << 24) | \ ((((uint32)(x)) & 0xFF0000UL) >> 8) | \ ((((uint32)(x)) & 0xFF000000UL) >> 24)) #if ___TNEF_BYTE_ORDER == 4321 #define big_endian(x) (x) #define long_big_endian(x) (x) #define little_endian(x) (endian_switch(x)) #define long_little_endian(x) (long_endian_switch(x)) #else #define big_endian(x) (endian_switch(x)) #define long_big_endian(x) (long_endian_switch(x)) #define little_endian(x) (x) #define long_little_endian(x) (x) #endif /* ___TNEF_BYTE_ORDER */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #endif /* _CONFIG_H */ ripmime-1.4.0.10.debian.1/tnef/ms-tnef.out0000640000175000017500000003427211667102337016135 0ustar pjbpjb ++++ ms-tnef ++++ TNEF_SIGNATURE (0x223E9F78), offset: 0x00000000 Attach Key -- 0x100D ATTRIBUTE: attTnefVersion (0x00089006) Component -- Message (0x01) Offset -- 0x00000006 Size -- 4 Encoding -- Tnef Version Information Version: 1.0 Computed Checksum 0x0001 Expected Checksum 0x0001 ATTRIBUTE: attOemCodepage (0x00069007) Component -- Message (0x01) Offset -- 0x00000015 Size -- 8 Encoding -- Tnef Codepage Information Codepage: Primary: 1252 Secondary: 0 Computed Checksum 0x00E8 Expected Checksum 0x00E8 ATTRIBUTE: attPriority (0x0004800D) Component -- Message (0x01) Offset -- 0x00000028 Size -- 2 Encoding -- Down-level Attribute Data: 02 00 *..* Computed Checksum 0x0002 Expected Checksum 0x0002 ATTRIBUTE: attRecipTable (0x00069004) Component -- Message (0x01) Offset -- 0x00000035 Size -- 256 Encoding -- MAPI Table No. Rows -- 1 ROW 0: No. Props -- 12 PROPERTY: PR_ROWID (0x30000003) Offset -- 0x00000046 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 03 00 00 00 *....* Local Checksum -- 0x0036 PROPERTY: PR_RESPONSIBILITY (0x0E0F000B) Offset -- 0x0000004E PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0028 PROPERTY: PR_ENTRYID (0x0FFF0102) Offset -- 0x00000056 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 59 Data (1): 00 00 00 00 81 2B 1F A4 -- BE A3 10 19 9D 6E 00 DD *.....+.......n..* 01 0F 54 02 00 00 00 00 -- 62 6C 6F 6E 67 40 75 69 *..T.....blong@ui* 75 63 2E 65 64 75 00 53 -- 4D 54 50 00 62 6C 6F 6E *uc.edu.SMTP.blon* 67 40 75 69 75 63 2E 65 -- 64 75 00 *g@uiuc.edu.* Pad -- 1 byte Local Checksum -- 0x12C0 PROPERTY: PR_ADDRTYPE (0x3002001E) Offset -- 0x0000009E PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 5 Data (1): 53 4D 54 50 00 *SMTP.* Pad -- 3 bytes Local Checksum -- 0x019A PROPERTY: PR_EMAIL_ADDRESS (0x3003001E) Offset -- 0x000000B2 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 15 Data (1): 62 6C 6F 6E 67 40 75 69 -- 75 63 2E 65 64 75 00 *blong@uiuc.edu.* Pad -- 1 byte Local Checksum -- 0x05D5 PROPERTY: PR_RECIPIENT_TYPE (0x0C150003) Offset -- 0x000000CE PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 01 00 00 00 *....* Local Checksum -- 0x0025 PROPERTY: PR_OBJECT_TYPE (0x0FFE0003) Offset -- 0x000000D6 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 06 00 00 00 *....* Local Checksum -- 0x0116 PROPERTY: PR_DISPLAY_NAME (0x3001001E) Offset -- 0x000000DE PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 17 Data (1): 27 62 6C 6F 6E 67 40 75 -- 69 75 63 2E 65 64 75 27 *'blong@uiuc.edu'* 00 *.* Pad -- 3 bytes Local Checksum -- 0x0623 PROPERTY: PR_SEARCH_KEY (0x300B0102) Offset -- 0x000000FE PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 20 Data (1): 53 4D 54 50 3A 42 4C 4F -- 4E 47 40 55 49 55 43 2E *SMTP:BLONG@UIUC.* 45 44 55 00 *EDU.* Local Checksum -- 0x05C5 PROPERTY: PR_DISPLAY_TYPE (0x39000003) Offset -- 0x0000011E PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x003C PROPERTY: PR_SEND_RICH_INFO (0x3A40000B) Offset -- 0x00000126 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 01 00 00 00 *....* Local Checksum -- 0x0086 PROPERTY: PR_INSTANCE_KEY (0x0FF60102) Offset -- 0x0000012E PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 4 Data (1): 00 00 00 03 *....* Local Checksum -- 0x0110 PROPERTIES: end ROWS: end Computed Size 256 Expected Size 256 Computed Checksum 0x298F Expected Checksum 0x298F ATTRIBUTE: attMessageClass (0x00078008) Component -- Message (0x01) Offset -- 0x00000140 Size -- 24 Encoding -- Down-level Attribute Data: 49 50 4D 2E 4D 69 63 72 -- 6F 73 6F 66 74 20 4D 61 *IPM.Microsoft Ma* 69 6C 2E 4E 6F 74 65 00 *il.Note.* Computed Checksum 0x0831 Expected Checksum 0x0831 ATTRIBUTE: attSubject (0x00018004) Component -- Message (0x01) Offset -- 0x00000163 Size -- 60 Encoding -- Down-level Attribute Data: 41 41 41 41 41 41 41 41 -- 41 41 41 41 41 52 52 52 *AAAAAAAAAAAAARRR* 52 52 52 52 52 52 47 47 -- 47 47 47 47 47 47 47 47 *RRRRRRGGGGGGGGGG* 47 47 48 48 48 48 48 48 -- 48 48 48 48 21 21 21 21 *GGHHHHHHHHHH!!!!* 21 21 21 21 21 21 21 21 -- 21 21 21 00 *!!!!!!!!!!!.* Computed Checksum 0x0E42 Expected Checksum 0x0E42 ATTRIBUTE: attDateSent (0x00038005) Component -- Message (0x01) Offset -- 0x000001AA Size -- 14 Encoding -- Down-level DTR Date: Friday, 05/23/1997, 11:29:10 CD 07 05 00 17 00 0B 00 -- 1D 00 0A 00 05 00 *..............* Computed Checksum 0x0127 Expected Checksum 0x0127 ATTRIBUTE: attDateModified (0x00038020) Component -- Message (0x01) Offset -- 0x000001C3 Size -- 14 Encoding -- Down-level DTR Date: Friday, 05/23/1997, 11:27:59 CD 07 05 00 17 00 0B 00 -- 1B 00 3B 00 05 00 *..........;...* Computed Checksum 0x0156 Expected Checksum 0x0156 ATTRIBUTE: attMessageID (0x00018009) Component -- Message (0x01) Offset -- 0x000001DC Size -- 33 Encoding -- Down-level Attribute Data: 34 32 38 35 34 32 30 43 -- 34 33 44 33 44 30 31 31 *4285420C43D3D011* 42 36 35 37 30 30 32 30 -- 41 46 41 31 41 30 30 33 *B6570020AFA1A003* 00 *.* Computed Checksum 0x06D3 Expected Checksum 0x06D3 ATTRIBUTE: attMAPIProps (0x00069003) Component -- Message (0x01) Offset -- 0x00000208 Size -- 508 Encoding -- MAPI Properties No. Props -- 18 PROPERTY: PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED (0x0023000B) Offset -- 0x00000215 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x002E PROPERTY: PR_PRIORITY (0x00260003) Offset -- 0x0000021D PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0029 PROPERTY: PR_READ_RECEIPT_REQUESTED (0x0029000B) Offset -- 0x00000225 PROP_TYPE -- PT_BOOLEAN (0x0000000B) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0034 PROPERTY: PR_SENSITIVITY (0x00360003) Offset -- 0x0000022D PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0039 PROPERTY: PR_CLIENT_SUBMIT_TIME (0x00390040) Offset -- 0x00000235 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 20 B7 AA 71 96 67 BC 01 * ..q.g..* Local Checksum -- 0x0425 PROPERTY: PR_CONVERSATION_TOPIC (0x0070001E) Offset -- 0x00000241 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 60 Data (1): 41 41 41 41 41 41 41 41 -- 41 41 41 41 41 52 52 52 *AAAAAAAAAAAAARRR* 52 52 52 52 52 52 47 47 -- 47 47 47 47 47 47 47 47 *RRRRRRGGGGGGGGGG* 47 47 48 48 48 48 48 48 -- 48 48 48 48 21 21 21 21 *GGHHHHHHHHHH!!!!* 21 21 21 21 21 21 21 21 -- 21 21 21 00 *!!!!!!!!!!!.* Local Checksum -- 0x0F0D PROPERTY: PR_CONVERSATION_INDEX (0x00710102) Offset -- 0x00000289 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 22 Data (1): 01 BC 67 96 71 A1 0C 42 -- 85 43 D3 43 11 D0 B6 57 *..g.q..B.C.C...W* 00 20 AF A1 A0 03 *. ....* Pad -- 2 bytes Local Checksum -- 0x0984 PROPERTY: PR_SENDER_ADDRTYPE (0x0C1E001E) Offset -- 0x000002AD PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 5 Data (1): 53 4D 54 50 00 *SMTP.* Pad -- 3 bytes Local Checksum -- 0x0192 PROPERTY: PR_SENDER_EMAIL_ADDRESS (0x0C1F001E) Offset -- 0x000002C1 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 19 Data (1): 6E 6F 61 68 62 61 6E 40 -- 64 76 73 77 65 62 2E 63 *noahban@dvsweb.c* 6F 6D 00 *om.* Pad -- 1 byte Local Checksum -- 0x076C PROPERTY: PR_RTF_SYNC_BODY_CRC (0x10060003) Offset -- 0x000002E1 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 3B EE 7D 8D *;.}.* Local Checksum -- 0x024C PROPERTY: PR_RTF_SYNC_BODY_COUNT (0x10070003) Offset -- 0x000002E9 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 14 00 00 00 *....* Local Checksum -- 0x002E PROPERTY: PR_RTF_SYNC_BODY_TAG (0x1008001E) Offset -- 0x000002F1 PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 21 Data (1): 57 48 41 54 48 41 53 48 -- 41 50 50 45 4E 45 44 54 *WHATHASHAPPENEDT* 4F 55 53 3F 00 *OUS?.* Pad -- 3 bytes Local Checksum -- 0x062B PROPERTY: PR_RTF_COMPRESSED (0x10090102) Offset -- 0x00000315 PROP_TYPE -- PT_BINARY (0x00000102) Encoding -- multi-valued No. values -- 1 Size (1) -- 180 Data (1): B0 00 00 00 9B 01 00 00 -- 4C 5A 46 75 50 49 B9 90 *........LZFuPI..* FF 00 0A 01 0F 02 15 02 -- A8 05 EB 02 83 00 50 02 *..............P.* F2 09 02 00 63 68 0A C0 -- 73 65 74 32 37 06 00 06 *....ch..set27...* C3 02 83 32 03 C5 02 00 -- 70 72 42 71 11 E2 73 74 *...2....prBq..st* 65 6D 02 83 33 F7 02 E4 -- 07 13 02 83 34 03 45 13 *em..3.......4.E.* 35 07 6D 02 80 FE 7D 0A -- 80 08 CF 09 D9 02 80 0A *5.m...}.........* 81 0D B1 0B 60 E0 6E 67 -- 31 30 33 14 50 0B 0A 15 *....`.ng103.P...* 61 F9 0B F0 34 20 0A 85 -- 1B 9F 1C AF 1D BF 1E CF *a...4 ..........* F7 1F DF 20 EF 21 F8 77 -- 11 80 05 40 11 80 04 20 *... .!.w...@... * 19 11 80 70 70 09 F0 09 -- 80 20 74 6F F0 20 75 73 *...pp.... to. us* 3F 1A AC 0A A0 03 60 13 -- D0 2E 63 05 40 0A 85 17 *?.....`...c.@...* 61 00 26 A0 *a.&.* Local Checksum -- 0x39D6 PROPERTY: PR_RTF_SYNC_PREFIX_COUNT (0x10100003) Offset -- 0x000003D5 PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0023 PROPERTY: PR_RTF_SYNC_TRAILING_COUNT (0x10110003) Offset -- 0x000003DD PROP_TYPE -- PT_LONG (0x00000003) Size -- 4 Data: 00 00 00 00 *....* Local Checksum -- 0x0024 PROPERTY: PR_CREATION_TIME (0x30070040) Offset -- 0x000003E5 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 00 00 02 47 96 67 BC 01 *...G.g..* Local Checksum -- 0x027A PROPERTY: PR_LAST_MODIFICATION_TIME (0x30080040) Offset -- 0x000003F1 PROP_TYPE -- PT_SYSTIME (0x00000040) Size -- 8 Data: 00 00 02 47 96 67 BC 01 *...G.g..* Local Checksum -- 0x027B PROPERTY: PR_SUBJECT_PREFIX (0x003D001E) Offset -- 0x000003FD PROP_TYPE -- PT_STRING8 (0x0000001E) Encoding -- multi-valued No. values -- 1 Size (1) -- 1 Data (1): 00 *.* Pad -- 3 bytes Local Checksum -- 0x005D PROPERTIES: end Computed Size 508 Expected Size 508 Computed Checksum 0x6E9E Expected Checksum 0x6E9E TNEF: end ripmime-1.4.0.10.debian.1/tnef/logger.h0000640000175000017500000000110511667102337015450 0ustar pjbpjb #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 #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 ripmime-1.4.0.10.debian.1/tnef/logger.c0000640000175000017500000001705311667102337015454 0ustar pjbpjb // 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,10240,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,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; default: fprintf(stdout,"LOGGER-Default: %s%s",output,lineend); } if (output) free(output); return 0; } ripmime-1.4.0.10.debian.1/tnef/Makefile0000640000175000017500000000063611667102337015470 0ustar pjbpjb # Possible platforms are SUN, HPUX, DEC, SGI, AIX, Linux and MSDOS # The main definitions are for 32/16 bits and for byte order, the # default is big endian. You can do -D__TNEF_BYTE_ORDER 1234 for little # endian PLATFORM=-D___TNEF_BYTE_ORDER=4321 CFLAGS=-Wall -g -O2 -I. default: config.h tnef.h tnef.c $(CC) $(CFLAGS) $(PLATFORM) -c tnef.c lib: tnef.o ar ruvs tnef.a tnef.o clean: rm -f *.o *.~[ch] ripmime-1.4.0.10.debian.1/tnef/tnef_api.h0000640000175000017500000000064511667102337015766 0ustar pjbpjb// API for external programs wanting to use TNEF decoding // #ifndef __TNEF_API__ #define __TNEF_API__ int TNEF_init( void ); int TNEF_main( char *filename ); int TNEF_set_filename_report_fn( int (*ptr_to_fn)(char *, char *)); int TNEF_set_verbosity( int level ); int TNEF_set_verbosity_contenttype( int level ); int TNEF_set_debug( int level ); int TNEF_set_path( char *path ); int TNEF_set_decode( int level ); #endif ripmime-1.4.0.10.debian.1/tnef/tnef.c0000640000175000017500000005054211667102337015131 0ustar pjbpjb/*************************************************************************** * tnef2txt * A program to decode application/ms-tnef MIME attachments into text * for those fortunate enough not to be running either a Microsoft * operating system or mailer. * * 18/10/2001 * Brutally cropped by Paul L Daniels (pldaniels@pldaniels.com) in order * to accommodate the needs of ripMIME/Xamime/Inflex without carrying too * much excess baggage. * * Brandon Long (blong@uiuc.edu), April 1997 * 1.0 Version * Supports most types, but doesn't decode properties. Maybe some other * time. * * 1.1 Version (7/1/97) * Supports saving of attAttachData to a file given by attAttachTitle * start of property decoding support * * 1.2 Version (7/19/97) * Some architectures don't like reading 16/32 bit data on unaligned * boundaries. Fixed, losing efficiency, but this doesn't really * need efficiency anyways. (Still...) * Also, the #pragma pack from the MSVC include file wasn't liked * by most Unix compilers, replaced with a GCCism. This should work * with GCC, but other compilers I don't know. * * 1.3 Version (7/22/97) * Ok, take out the DTR over the stream, now uses read_16. * * NOTE: THIS SOFTWARE IS FOR YOUR PERSONAL GRATIFICATION ONLY. I DON'T * IMPLY IN ANY LEGAL SENSE THAT THIS SOFTWARE DOES ANYTHING OR THAT IT WILL * BE USEFULL IN ANY WAY. But, you can send me fixes to it, I don't mind. ***************************************************************************/ #include #include #include #include #include #include #include "logger.h" #include "config.h" #include "tnef.h" #include "mapidefs.h" #include "mapitags.h" #include "tnef_api.h" #define VERSION "pldtnef/0.0.2" #ifndef FL #define FL __FILE__,__LINE__ #endif #define TNEF_VERBOSE ((TNEF_glb.verbose > 0)) #define TNEF_DEBUG ((TNEF_glb.debug > 0)) /** 20041207-1246:PLD: Added RT32 macro to allow for large numbers of read-tests **/ #define RT32( num_addr, offset ) if (read_32(num_addr, offset)==-1) return -1 #define TNEF_PATH_SIZE 1024 struct TNEF_globals { int file_num; int verbose; int verbosity_contenttype; int debug; char path[ TNEF_PATH_SIZE +1]; int TNEF_Verbose; int savedata; uint8 *tnef_home; uint8 *tnef_limit; int (*filename_decoded_report)(char *, char *); // Pointer to our filename reporting function }; static struct TNEF_globals TNEF_glb; // The variables below have been pushed into the TNEF globals //int Verbose = FALSE; //int SaveData = FALSE; //uint8 *tnef_home; //uint8 *tnef_limit; /*-----------------------------------------------------------------\ Function Name : TNEF_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_init( void ) { TNEF_glb.file_num = 0; TNEF_glb.verbose = 0; TNEF_glb.verbosity_contenttype = 0; TNEF_glb.debug = 0; TNEF_glb.savedata = 1; TNEF_glb.TNEF_Verbose = 0; TNEF_glb.filename_decoded_report = NULL; TNEF_glb.path[0] = '\0'; return 0; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_decode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_decode( int level ) { TNEF_glb.savedata = level; return TNEF_glb.savedata; } /*------------------------------------------------------------------------ Procedure: TNEF_set_path ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_path( char *path ) { snprintf( TNEF_glb.path, TNEF_PATH_SIZE , "%s", path); return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_set_verbosity ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_verbosity( int level ) { TNEF_glb.verbose = level; return TNEF_glb.verbose; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_verbosity_contenttype( int level ) { TNEF_glb.verbosity_contenttype = level; return TNEF_glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : TNEF_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int TNEF_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { TNEF_glb.filename_decoded_report = ptr_to_fn; return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_set_debug ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_set_debug( int level ) { TNEF_glb.debug = level; TNEF_set_verbosity( level ); return TNEF_glb.debug; } /* Some systems don't like to read unaligned data */ /*------------------------------------------------------------------------ Procedure: read_32 ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_32( uint32 *value, uint8 *tsp) { uint8 a,b,c,d; if ((tsp +4) > TNEF_glb.tnef_limit) { if ((TNEF_VERBOSE)||(TNEF_DEBUG)) LOGGER_log("%s:%d:TNEF_read_32:ERROR: Attempting to read beyond end of memory block",FL); return -1; } a = *tsp; b = *(tsp+1); c = *(tsp+2); d = *(tsp+3); *value = long_little_endian(a<<24 | b<<16 | c<<8 | d); return 0; } /*------------------------------------------------------------------------ Procedure: read_16 ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_16( uint16 *value, uint8 *tsp) { uint8 a,b; if ( ((tsp +2) > TNEF_glb.tnef_limit) //PLD-20070707-17H20, how do we even compare tsp to a negative!? ||(tsp == -1) ) { if ((TNEF_VERBOSE)||(TNEF_DEBUG)) LOGGER_log("%s:%d:TNEF_read_16:ERROR: Attempting to read past end\n",FL); return -1; } // if (TNEF_DEBUG) fprintf(stderr,"Read_16: Offset read %d\n", tsp -tnef_home); a = *tsp; b = *(tsp + 1); *value = little_endian(a<<8 | b); return 0; } /*------------------------------------------------------------------------ Procedure: make_string ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ char *make_string(uint8 *tsp, int size) { static char s[256] = ""; snprintf(s,sizeof(s),"%s",tsp); /** 20041106-0929:PLD: Remove this ugly old code and use snprintf() instead int len = (size>sizeof(s)-1) ? sizeof(s)-1 : size; strncpy(s,tsp, len); s[len] = '\0'; **/ return s; } /*------------------------------------------------------------------------ Procedure: save_attach_data ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int save_attach_data(char *title, uint8 *tsp, uint32 size) { FILE *out; char filename[1024]; size_t bc; snprintf(filename, sizeof(filename),"%s/%s", TNEF_glb.path, title ); out = fopen(filename, "w"); if (!out) { LOGGER_log("%s:%d:TNEF_save_attach_data:ERROR: Failed opening file %s for writing (%s)\n", FL, filename, strerror(errno)); return -1; } bc = fwrite(tsp, sizeof(uint8), size, out); if (bc != size) LOGGER_log("%s:%d:TNEF_save_attach_data:WARNING: Only wrote %d of %d bytes", FL, bc, size); fclose(out); return 0; } /*------------------------------------------------------------------------ Procedure: handle_props ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int handle_props(uint8 *tsp) { int bytes = 0; uint32 num_props = 0; uint32 x = 0; RT32(&num_props, tsp); bytes += sizeof(num_props); while (x < num_props) { uint32 prop_tag; uint32 num; char filename[256]; RT32(&prop_tag, tsp+bytes); bytes += sizeof(prop_tag); switch (prop_tag & PROP_TYPE_MASK) { case PT_BINARY: RT32(&num, tsp+bytes); bytes += sizeof(num); RT32(&num, tsp+bytes); bytes += sizeof(num); if (prop_tag == PR_RTF_COMPRESSED) { sprintf (filename, "XAM_%d.rtf", TNEF_glb.file_num); TNEF_glb.file_num++; save_attach_data(filename, tsp+bytes, num); } /* num + PAD */ bytes += num + ((num % 4) ? (4 - num%4) : 0); break; case PT_STRING8: RT32(&num, tsp+bytes); bytes += sizeof(num); RT32(&num, tsp+bytes); bytes += sizeof(num); make_string(tsp+bytes,num); bytes += num + ((num % 4) ? (4 - num%4) : 0); break; case PT_UNICODE: case PT_OBJECT: break; case PT_I2: bytes += 2; break; case PT_LONG: bytes += 4; break; case PT_R4: bytes += 4; break; case PT_DOUBLE: bytes += 8; break; case PT_CURRENCY: case PT_APPTIME: case PT_ERROR: bytes += 4; break; case PT_BOOLEAN: bytes += 4; break; case PT_I8: bytes += 8; case PT_SYSTIME: bytes += 8; break; } x++; } return 0; } /*------------------------------------------------------------------------ Procedure: default_handler ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int default_handler(uint32 attribute, uint8 *tsp, uint32 size) { uint16 type = ATT_TYPE(attribute); switch (type) { case atpTriples: break; case atpString: case atpText: break; case atpDate: break; case atpShort: break; case atpLong: break; case atpByte: break; case atpWord: break; case atpDword: break; default: break; } return 0; } /*------------------------------------------------------------------------ Procedure: read_attribute ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int read_attribute(uint8 *tsp) { int bytes = 0, header = 0; int rv = 0; uint32 attribute; // uint8 component = 0; uint32 size = 0; uint16 checksum = 0; static char attach_title[256] = { 0 }; static uint32 attach_size = 0; //static uint32 attach_loc = 0; // 2003-02-22-1231-PLD static uint8 *attach_loc = 0; //component = *tsp; bytes += sizeof(uint8); // Read the attributes of this component if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Reading Attribute...\n",FL); rv = read_32(&attribute, tsp+bytes); if (rv == -1) return -1; bytes += sizeof(attribute); // Read the size of the information we have to read if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute: Reading Size...\n",FL); rv = read_32(&size, tsp+bytes); if (rv == -1) return -1; bytes += sizeof(size); // The header size equals the sum of all the things we've read // so far. header = bytes; // The is a bit of a tricky one [if you're being slow // it moves the number of bytes ahead by the amount of data of // the attribute we're about to read, so that for next // "read_attribute()" // call starts in the right place. bytes += size; // Read in the checksum for this component // // AMMENDMENT - 19/07/02 - 17H01 // Small code change to deal with strange sitations that occur with non // english characters. - Submitted by wtcheuk@netvigator.com @ 19/07/02 if ( bytes < 0 ) return -1; // --END of ammendment. if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Reading Checksum...(offset %d, bytes=%d)\n", FL, tsp -TNEF_glb.tnef_home, bytes); if (read_16(&checksum, tsp+bytes) == -1) return -1; bytes += sizeof(checksum); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_read_attribute:DEBUG: Decoding attribute %d\n", FL, attribute); switch (attribute) { case attNull: default_handler(attribute, tsp+header, size); break; case attFrom: default_handler(attribute, tsp+header, size); break; case attSubject: break; case attDateSent: break; case attDateRecd: break; case attMessageStatus: break; case attMessageClass: break; case attMessageID: break; case attParentID: break; case attConversationID: break; case attBody: default_handler(attribute, tsp+header, size); break; case attPriority: break; case attAttachData: attach_size=size; // attach_loc =(int)tsp+header; // 2003-02-22-1232-PLD attach_loc =(uint8 *)tsp+header; if (TNEF_glb.savedata && strlen(attach_title)>0 && attach_size > 0) { if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size)) { if (TNEF_VERBOSE) { if (TNEF_glb.filename_decoded_report == NULL) { LOGGER_log("Decoding: %s\n", attach_title); } else { TNEF_glb.filename_decoded_report( attach_title, (TNEF_glb.verbosity_contenttype>0?"tnef":NULL)); } } } else { LOGGER_log("%s:%d:TNEF_read_attribute:ERROR: While saving attachment '%s'\n", FL, attach_title); } } break; case attAttachTitle: strncpy(attach_title, make_string(tsp+header,size),255); if (TNEF_glb.savedata && strlen(attach_title)>0 && attach_size > 0) { if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size)) { if (TNEF_VERBOSE) { if (TNEF_glb.filename_decoded_report == NULL) { LOGGER_log("Decoding: %s\n", attach_title); } else { TNEF_glb.filename_decoded_report( attach_title, (TNEF_glb.verbosity_contenttype>0?"tnef":NULL)); } } } else { LOGGER_log("%s:%d:TNEF_read_attribute:ERROR: While saving attachment '%s'\n", FL, attach_title); } } break; case attAttachMetaFile: default_handler(attribute, tsp+header, size); break; case attAttachCreateDate: break; case attAttachModifyDate: break; case attDateModified: break; case attAttachTransportFilename: default_handler(attribute, tsp+header, size); break; case attAttachRenddata: attach_title[0]=0; attach_size=0; attach_loc=0; default_handler(attribute, tsp+header, size); break; case attMAPIProps: if (handle_props(tsp+header)==-1) return -1; break; case attRecipTable: default_handler(attribute, tsp+header, size); break; case attAttachment: default_handler(attribute, tsp+header, size); break; case attTnefVersion: { uint32 version; rv = read_32(&version, tsp+header); if (rv == -1) return -1; } break; case attOemCodepage: default_handler(attribute, tsp+header, size); break; case attOriginalMessageClass: break; case attOwner: default_handler(attribute, tsp+header, size); break; case attSentFor: default_handler(attribute, tsp+header, size); break; case attDelegate: default_handler(attribute, tsp+header, size); break; case attDateStart: break; case attDateEnd: break; case attAidOwner: default_handler(attribute, tsp+header, size); break; case attRequestRes: default_handler(attribute, tsp+header, size); break; default: default_handler(attribute, tsp+header, size); break; } return bytes; } /*------------------------------------------------------------------------ Procedure: decode_tnef ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_decode_tnef(uint8 *tnef_stream, int size) { int ra_response; uint32 tnefs; uint16 tnef_attachkey; uint8 *tsp; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Start. Size = %d\n", FL,size); // TSP == TNEF Stream Pointer (well memory block actually!) // tsp = tnef_stream; // Read in the signature of this TNEF // ra_response = read_32(&tnefs, tsp); if ((ra_response != -1)&&(TNEF_SIGNATURE == tnefs)) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF signature is good\n",FL); } else { if (TNEF_VERBOSE) LOGGER_log("%s:%d:TNEF_decode_tnef:WARNING: Bad TNEF signature, expecting %lx got %lx\n",FL,TNEF_SIGNATURE,tnefs); } // Move tsp pointer along // tsp += sizeof(TNEF_SIGNATURE); /** Read the TNEF Attach key **/ if (read_16(&tnef_attachkey, tsp) == -1) return -1; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF Attach Key: %x\n",FL,tnef_attachkey); // Move tsp pointer along // tsp += sizeof(uint16); // While we still have more bytes to process, // go through entire memory block and extract // all the required attributes and files // if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: TNEF - Commence reading attributes\n",FL); while ((tsp - tnef_stream) < size) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Offset = %d\n", FL,tsp -TNEF_glb.tnef_home); ra_response = read_attribute(tsp); if ( ra_response > 0 ) { tsp += ra_response; } else { // Must find out /WHY/ this happens, and, how to rectify the issue. tsp++; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:WARNING: TNEF - Attempting to read attribute at %d resulted in a sub-zero response, ending decoding to be safe\n",FL,tsp); break; } } if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_decode_tnef:DEBUG: Done.\n",FL); return 0; } /*------------------------------------------------------------------------ Procedure: TNEF_main ID:1 Purpose: Decodes a given TNEF encoded file Input: Output: Errors: ------------------------------------------------------------------------*/ int TNEF_main( char *filename ) { FILE *fp; struct stat sb; uint8 *tnef_stream; int size, nread; if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: Start, decoding %s\n",FL, filename); if (TNEF_glb.savedata == 0 ) { if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_name:DEBUG: decode_tnef is set to 0, not decoding file.",FL); return 0; } // Test to see if the file actually exists // if (stat(filename,&sb) == -1) { LOGGER_log("%s:%d:TNEF_main:ERROR: while attempting to get details on file %s (%s)\n", FL, filename,strerror(errno)); return -1; } // Get the filesize // size = sb.st_size; // Allocate enough memory to read in the ENTIRE file // FIXME - This could be a real consumer if multiple // instances of TNEF decoding is going on // TNEF_glb.tnef_home = tnef_stream = (uint8 *)malloc(size); TNEF_glb.tnef_limit = TNEF_glb.tnef_home +size; // If we were unable to allocate enough memory, then we // should report this // if (tnef_stream == NULL) { LOGGER_log("%s:%d:TNEF_main:ERROR: When allocating %d bytes for loading file (%s)\n", FL, size,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Attempt to open up the TNEF encoded file... if it fails // then report the failed condition to syslog // if ((fp = fopen(filename,"r")) == NULL) { LOGGER_log("%s:%d:TNEF_main:ERROR: opening file %s for reading (%s)\n", FL, filename,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Attempt to read in the entire file // nread = fread(tnef_stream, sizeof(uint8), size, fp); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: Read %d bytes\n", FL, nread); // If we did not read in all the bytes, then let syslogs know! // if (nread < size) { LOGGER_log("%s:%d:TNEF_main:ERROR: while reading stream from file %s (%s)\n", FL, filename,strerror(errno)); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); return -1; } // Close the file // fclose(fp); // Proceed to decode the file // TNEF_decode_tnef(tnef_stream,size); if (TNEF_glb.tnef_home) free(TNEF_glb.tnef_home); if (TNEF_DEBUG) LOGGER_log("%s:%d:TNEF_main:DEBUG: finished decoding.\n",FL); return 0; } //--------------------------END. ripmime-1.4.0.10.debian.1/tnef/ms-tnef0000640000175000017500000000201711667102337015317 0ustar pjbpjbxŸ>" äè € 0 ÿ;+¤¾£nÝTblong@uiuc.eduSMTPblong@uiuc.edu0SMTP0blong@uiuc.edu þ0'blong@uiuc.edu' 0SMTP:BLONG@UIUC.EDU9 @:ö)€IPM.Microsoft Mail.Note1€<AAAAAAAAAAAAARRRRRRRRRGGGGGGGGGGGGHHHHHHHHHH!!!!!!!!!!!!!!!B€Í  ' €Í ;V €!4285420C43D3D011B6570020AFA1A003Óü #& )6@9 ·ªq–g¼p<AAAAAAAAAAAAARRRRRRRRRGGGGGGGGGGGGHHHHHHHHHH!!!!!!!!!!!!!!!q¼g–q¡ B…CÓCжW ¯¡  SMTP noahban@dvsweb.com;î}WHATHASHAPPENEDTOUS? ´°›LZFuPI¹ÿ ¨ëƒPò ch Àset27Ã2ÅprBqâstemƒ3÷äƒ4E5m€þ} €Ï Ù€ ± `àng103P aù ð4 …Ÿ¯¿Ï÷ß ï!øw€@€ €pp ð € toð us?¬  `Ð.c@ …a& @0G–g¼@0G–g¼=žnripmime-1.4.0.10.debian.1/strstack.h0000640000175000017500000000141711667102337015101 0ustar pjbpjb #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 ripmime-1.4.0.10.debian.1/libmime-decoders.h0000640000175000017500000000164411667102337016451 0ustar pjbpjb /* 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 #define MDECODE_ISO_CHARSET_SIZE_MAX 16 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_set_out_charset( char *charset ); 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 ); ripmime-1.4.0.10.debian.1/rawget.c0000640000175000017500000000162211667102337014525 0ustar pjbpjb#include #include int RAWGET_get( unsigned char *buffer, int max, FILE *f ) { unsigned char c; // read buffer 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. // // Whilst we've got less bytes than the maximum availabe // for the buffer, we keep on reading // while (count < max) { // If we do infact read in 1 bytes... if (fread(&c,1,1,f)==1) { *buffer = c; // Set the byte in the buffer buffer++; // move the buffer pointer count++; // Increment the byte cound if (c == '\n') // If we encounter a \n (or \r) { break; // Hop out of while loop } } else break; // if we didn't read right, then jump out as well } return count; } ripmime-1.4.0.10.debian.1/logger.h0000640000175000017500000000113411667102337014516 0ustar pjbpjb #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 ripmime-1.4.0.10.debian.1/INSTALL0000640000175000017500000000152111667102337014117 0ustar pjbpjbINSTALL--------------------------------------------------------------- Very simple (as root)... make make install [ Make install will install the ripmime binary to /usr/local/bin, there are no other support files required. ] To use ripMIME... If you have an MIME encoded archive (say from sendmail's output) called recvmail001.mime and you wish to unpack it to a directory (which already exists) called "/var/tmp/unpack", then use ripMIME as follows... ripmime -i recvmail001.mime -d /var/tmp/unpack It's truly that simple. --- If you want more PERFORMANCE out of ripMIME (most people will), you can select some compile flags in the Makefile which suit your CPU better, ie, for recent Pentiums/Celerons, you can use -Wall -O3 -march=i686 ---------------------------------------------------------------------- Enjoy. ripmime-1.4.0.10.debian.1/CONTRIBUTORS0000640000175000017500000000546111667102337014755 0ustar pjbpjbOriginal Author : Paul L Daniels --------------------------- Author : Farit Date : 14/01/2004 Contrib: Supplied various mailpacks with exploits/viruses/worms and alternative 'header' prefixing. --------------------------- Change : Correction of various spurilous messages appearing even when verbosity is not active Date : 26/01/2003 Author : Chris Hine --------------------------- Change : Change char * to unsigned char * in strlower() function to deal with non-ASCII characters ( ie, French, Chinese ) Date : 09/11/2002 Author : Emmanuel Collignon --------------------------- Change : Patch to deal with spaces inbetween name=, filename= type MIME headers. Submitted by Phil Brutsche Date : 06/11/2002 --------------------------- Change : Updates to FFGET routines. Improve handling of one-off issues and buffer boundary conditions Author : Joseph Gooch Email : Date : 27/02/2002 Notification : Version number update (I hate it when I forget that) Notifier : wes schreiner Date : 21/02/2002 --------------------------- Change : TNEF decoding improvements - Quoted Printable decoding fixes Author : Chea Chee Keong Email : ckchea@mailandnews.com Date : 20/02/2002 --------------------------- Change : CRCR logic clarification Author : Joseph Gooch Date : 18/02/2002 --------------------------- Change : TNEF decoding improvements Author : Chea Chee Keong Email : ckchea@mailandnews.com Date : 01/01/2002 --------------------------- Change : add ability to read mimepack from STDIN Author : Matthew McNaughton Date : 24/11/2000 --------------------------- Fixes: Change : Correction for handling varying line-length BASE64 encoded files Author : Trent, Michael (Xerox USA) and John Armstrong (EFI.com) Date : 05/11/2001 --------------------------- Change : Support for SGI systems (fixes lockup) Author : Don Lafontaine Date : 27/09/2001 --------------------------- Change : Correct problem where a filename will contain multiple consecutive /'s Author : James Cownie Date : 03/02/2001 --------------------------- Change : Replaced the tmpnam() call for temporary filename with MIME_tmpnam() Author : Dave DeMaagd & Paul L Daniels Date : 29/01/2001 --------------------------- Change : added patch to detected boundaries without surrounding quotations Author : Rolf Date : 12/01/2001 --------------------------- Support: Date : 07/01/2000 Person: Hans Harder What: Supplied me with a mailpack which couldn't be correctly decoded by ripMIME due to it not having a trailing CR/LF after the base64 encoded portion of the attachment. --------------------------- Date : 29/01/2001 Person : Sven Rassmaan What: Supplied me with a mailpack/spool which I could test the multiple-email-spool-extract facility (also happened to be the requestor or such) ripmime-1.4.0.10.debian.1/logger.c0000640000175000017500000001724211667102337014520 0ustar pjbpjb // 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; } ripmime-1.4.0.10.debian.1/strstack.c0000640000175000017500000001536211667102337015100 0ustar pjbpjb#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; } ripmime-1.4.0.10.debian.1/ffget.h0000640000175000017500000000260111667102337014332 0ustar pjbpjb /* DEFINES */ #define FFGET_MAX_LINE_LEN 1024 #define FFGET_BUFFER_MAX 4096 #define FFGET_BUFFER_PADDING 1 #define FFGET_DEBUG_NORMAL 1 #define FFGET_DEBUG_PEDANTIC 10 struct _FFGET_FILE { char buffer[FFGET_BUFFER_MAX +FFGET_BUFFER_PADDING]; 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; FILE *f; }; 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 ); ripmime-1.4.0.10.debian.1/ripOLE/0000770000175000017500000000000011667102200014207 5ustar pjbpjbripmime-1.4.0.10.debian.1/ripOLE/bt-int.c0000640000175000017500000000254011667102235015560 0ustar pjbpjb#include #include #include "bt-int.h" int BTI_init( struct bti_node **n ) { *n = NULL; return 0; } int BTI_add( struct bti_node **n, int value ) { int collision = 0; int dir = 0; struct bti_node *p = NULL, *node = *n; // fprintf(stdout,"Adding %d to %p\n", value, *n); while (node != NULL) { if (value > node->data) { p = node; dir=1; node = node->r; } else if (value < node->data) { p = node; dir=-1; node = node->l; } else if (value == node->data) { collision = 1; break; } } if (collision == 0) { struct bti_node *leaf; leaf = malloc(sizeof(struct bti_node)); if (leaf == NULL) { return -1; } leaf->data = value; leaf->l = leaf->r = NULL; if (p != NULL) { switch (dir) { case 1: p->r = leaf; break; case -1: p->l = leaf; break; } } else { *n = leaf; } } return collision; } int BTI_dump( struct bti_node **n ) { struct bti_node *node; node = *n; if (node->l) BTI_dump(&(node->l)); if (*n) { fprintf(stdout,"%d, ", node->data); } if (node->r) BTI_dump(&(node->r)); return 0; } int BTI_done( struct bti_node **n ) { struct bti_node *node; if (n == NULL) return 0; if (*n == NULL) return 0; node = *n; if (node->l) BTI_done(&(node->l)); if (node->r) BTI_done(&(node->r)); if (*n) { free(*n); *n = NULL; } return 0; } ripmime-1.4.0.10.debian.1/ripOLE/bt-int.h0000640000175000017500000000040611667102235015564 0ustar pjbpjb#ifndef __BT_INT__ #define __BT_INT__ struct bti_node { int data; struct bti_node *l,*r; }; int BTI_init( struct bti_node **n ); int BTI_add( struct bti_node **n, int value ); int BTI_dump( struct bti_node **n ); int BTI_done( struct bti_node **n ); #endif ripmime-1.4.0.10.debian.1/ripOLE/ole.c0000640000175000017500000016007711667102235015154 0ustar pjbpjb#include #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "bt-int.h" #include "bytedecoders.h" #include "olestream-unwrap.h" #include "ole.h" /** Sector ID values (predefined) **/ #define OLE_SECTORID_FREE -1 /** Unallocated sector **/ #define OLE_SECTORID_ENDOFCHAIN -2 /** Sector marks the end of the a sector-ID chain **/ #define OLE_SECTORID_SAT -3 /** Sector used by sector allocation Table **/ #define OLE_SECTORID_MSAT -4 /** Sector used by master sector allocation Table **/ // Main header accessors #define header_id(x) ((x) +0) #define header_clid(x) ((x) +0x08) #define header_minor_version(x) ((x) +0x18) #define header_dll_version(x) ((x) +0x1a) #define header_byte_order(x) ((x) +0x1c) #define header_sector_shift(x) ((x) +0x1e) #define header_mini_sector_shift(x) ((x) +0x20) #define header_fat_sector_count(x) ((x) +0x2c) #define header_directory_stream_start_sector(x) ((x) +0x30) #define header_mini_cutoff_size(x) ((x) +0x38) #define header_mini_fat_start(x) ((x) +0x3c) #define header_mini_fat_sector_count(x) ((x) +0x40) #define header_dif_start_sector(x) ((x) +0x44) #define header_dif_sector_count(x) ((x) +0x48) #define header_fat_sectors(x) ((x) +0x4c) //Property Storage accessor macros #define pps_rawname(x) ((x) +0) #define pps_sizeofname(x) ((x) +0x40) #define pps_type(x) ((x) +0x42) #define pps_previouspps(x) ((x) +0x44) #define pps_nextpps(x) ((x) +0x48) #define pps_directorypps(x) ((x) +0x4c) #define pps_time1seconds(x) ((x) +0x64) #define pps_time1days(x) ((x) +0x68) #define pps_time2seconds(x) ((x) +0x6c) #define pps_time2days(x) ((x) +0x70) #define pps_propertystart(x) ((x) +0x74) #define pps_sizeofproperty(x) ((x) +0x78) // Type lenghts #define LEN_BYTE 1 #define LEN_USHORT 2 #define LEN_ULONG 4 // Directory types #define STGTY_INVALID 0 #define STGTY_STORAGE 1 #define STGTY_STREAM 2 #define STGTY_LOCKBYTES 3 #define STGTY_PROPERTY 4 #define STGTY_ROOT 5 // Directory tag colours #define DE_RED 0 #define DE_BLACK 1 #define DOLE if (OLE_DNORMAL(ole->debug)) #define VOLE if (ole->verbose) unsigned char OLE_id_v2[]={ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; unsigned char OLE_id_v1[]={ 0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0 }; /*-----------------------------------------------------------------\ Function Name : OLE_version Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_version( void ) { fprintf(stderr,"ripOLE: %s\n", LIBOLE_VERSION); return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_init Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 20041127-2029:PLD: Added file_size initialization \------------------------------------------------------------------*/ int OLE_init( struct OLE_object *ole ) { ole->debug = 0; ole->verbose = 0; ole->quiet = 0; ole->filename_report_fn = NULL; ole->f = NULL; ole->file_size = 0; ole->FAT = NULL; ole->FAT_limit = NULL; ole->miniFAT = NULL; ole->miniFAT_limit = NULL; ole->header_block[0] = '\0'; ole->ministream = NULL; ole->properties = NULL; ole->header.sector_shift = 0; ole->header.mini_sector_shift = 0; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_dir_init Returns Type : int ----Parameter List 1. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_dir_init(struct OLE_directory_entry *dir ) { memset(dir->element_name,'\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE); dir->element_name_byte_count = 0; dir->element_type = 0; dir->element_colour = 0; dir->left_child = 0; dir->right_child = 0; dir->root = 0; dir->class[0] = '\0'; dir->userflags = 0; dir->timestamps[0] = '\0'; dir->start_sector = 0; dir->stream_size = 0; return 0; }; /*-----------------------------------------------------------------\ Function Name : OLE_set_verbose Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_verbose( struct OLE_object *ole, int level ) { ole->verbose = level; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_quiet Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_quiet( struct OLE_object *ole, int level ) { ole->quiet = level; ole->verbose = 0; ole->debug = 0; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_debug Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_debug( struct OLE_object *ole, int level ) { ole->debug = level; if (ole->debug > 0) LOGGER_log("%s:%d:OLE_set_debug: Debug level set to %d",FL, ole->debug); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_set_save_unknown_streams Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_save_unknown_streams( struct OLE_object *ole, int level ) { ole->save_unknown_streams = level; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_sectorpos Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int SID , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Given a sector ID, this function will return the file position offset. Assumes that the offset for the file starts at 512 bytes (which is the size of the OLE header) -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_sectorpos( struct OLE_object *ole, int SID ) { int pos = 0; pos = 512 +(SID *ole->header.sector_size); return pos; } /*-----------------------------------------------------------------\ Function Name : OLE_get_block Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int block_index, Block indexes / Sector ID's are signed ints. 3. unsigned char *block_buffer , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_block( struct OLE_object *ole, int block_index, unsigned char *block_buffer ) { if (block_buffer == NULL) { LOGGER_log("%s:%d:OLE_get_block:ERROR: Block buffer is NULL",FL); return -1; } if (ole->f != NULL) { int read_count = 0; int fseek_result = 0; size_t offset = 0; unsigned char *bb = NULL; bb = malloc(sizeof(unsigned char) *ole->header.sector_size); if (bb == NULL) { LOGGER_log("%s:%d:OLE_get_block:ERROR: Cannot allocate %d bytes for OLE block",FL, ole->header.sector_size); return -1; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: BlockIndex=%d, Buffer=0x%x",FL, block_index, block_buffer); //20051211-2343:PLD: offset = (block_index +1) << ole->header.sector_shift; offset = OLE_sectorpos(ole, block_index); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Read offset in file = 0x%x size to read= 0x%x",FL,offset,ole->header.sector_size); fseek_result = fseek(ole->f, offset, SEEK_SET); if (fseek_result != 0) { if (bb != NULL) { free(bb); bb = NULL; } LOGGER_log("%s:%d:OLE_get_block:ERROR: Seek failure (block=%d:%d)",FL, block_index,offset, strerror(errno)); return OLEER_GET_BLOCK_SEEK; } //read_count = fread(block_buffer, sizeof(unsigned char), ole->header.sector_size, ole->f); read_count = fread(bb, sizeof(unsigned char), ole->header.sector_size, ole->f); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Read %d byte of data",FL,read_count); if (read_count != (int)ole->header.sector_size) { if (bb != NULL){ free(bb); bb = NULL; } VOLE LOGGER_log("%s:%d:Mismatch in bytes read. Requested %d, got %d\n", FL, ole->header.sector_size, read_count); return OLEER_GET_BLOCK_READ; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Copying over memory read from file",FL); memcpy(block_buffer, bb, ole->header.sector_size); DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: memory block copied to block_buffer",FL); /* We're now done with BB, dispose of it */ if (bb) { free(bb); bb = NULL; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Disposed of temporary bb block",FL); } else { LOGGER_log("%s:%d:OLE_get_block:ERROR: OLE file is closed\n",FL); return -1; } DOLE LOGGER_log("%s:%d:OLE_get_block:DEBUG: Done",FL); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_get_miniblock Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. unsigned int block_index, 3. unsigned char *block_buffer , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_miniblock( struct OLE_object *ole, int block_index, unsigned char *block_buffer ) { if (ole->ministream) { int offset = block_index << ole->header.mini_sector_shift; memcpy( block_buffer, ole->ministream +offset, ole->header.mini_sector_size); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_dbstosbs Returns Type : int ----Parameter List 1. char *raw_string, 2. size_t char_count, 3. char *clean_string , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_dbstosbs( char *raw_string, size_t byte_count, char *clean_string, int clean_string_len ) { char *limit = raw_string +byte_count -1; clean_string_len--; while ((raw_string < limit)&&(byte_count >0)&&(byte_count--)&&(clean_string_len--)) { int v = (char)*raw_string; if (isprint(v)) { *clean_string = v; clean_string++; } raw_string += 2; } *clean_string = '\0'; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_string Returns Type : int ----Parameter List 1. char *string, 2. size_t length , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_string( char *string, size_t char_count) { while (char_count--) { printf("%c",*string); string += 2; } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_sector Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. unsigned char *sector , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_sector( struct OLE_object *ole, char *sector, unsigned int bytes) { int current_byte; int ubytes = bytes; printf("\n"); for (current_byte = 0; current_byte < ubytes; current_byte++ ) { printf("%02X ", *(sector +current_byte)); if (((current_byte+1) %32)==0) { int j; for (j = current_byte -31; j <=current_byte; j++) { if (isalnum(*(sector +j))) printf("%c",*(sector+j)); else printf("."); } printf("\n"); } } printf("\n"); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_is_OLE_file Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_is_file_OLE( struct OLE_object *ole ) { if (memcmp(OLE_id_v1, ole->header_block, sizeof(OLE_id_v1))==0) return 1; if (memcmp(OLE_id_v2, ole->header_block, sizeof(OLE_id_v2))==0) return 1; return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_get_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_get_header( struct OLE_object *ole ) { int result; ole->header.sector_size = OLE_HEADER_BLOCK_SIZE; result = OLE_get_block( ole, -1, ole->header_block ); if (OLE_is_file_OLE( ole ) == 0) { return OLEER_NOT_OLE_FILE; } return result; // should be OLE_OK } /*-----------------------------------------------------------------\ Function Name : OLE_convert_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_convert_header( struct OLE_object *ole ) { struct OLE_header *h; unsigned char *hb; /** pointer to the header block **/ unsigned char *fat_start; unsigned int i; h = &(ole->header); hb = ole->header_block; /** Note that the header_*(hb) calls are actually macros which are ** defined in the ole.h file. These macros basically take the ** hb value and add the required offset, they don't affect the ** value of hb (or they certainly SHOULD NOT! ) **/ h->minor_version = get_uint16((char *)header_minor_version(hb)); h->dll_version = get_uint16((char *)header_dll_version(hb)); h->byte_order = get_uint16((char *)header_byte_order(hb)); /** 0xFEFF = Little endian, 0xFFFE = big endian **/ h->sector_shift = get_uint16((char *)header_sector_shift(hb)); h->sector_size = 1 << h->sector_shift; h->mini_sector_shift = get_uint16((char *)header_mini_sector_shift(hb)); h->mini_sector_size = 1 << h->mini_sector_shift; h->fat_sector_count = get_uint32((char *)header_fat_sector_count(hb)); /** Total number of sectors use for the SAT **/ h->directory_stream_start_sector = get_uint32((char *)header_directory_stream_start_sector(hb)); /** Start sector-ID for the DIRECTORY STREAM **/ h->mini_cutoff_size = get_uint32((char *)header_mini_cutoff_size(hb)); h->mini_fat_start = get_uint32((char *)header_mini_fat_start(hb)); h->mini_fat_sector_count = get_uint32((char *)header_mini_fat_sector_count(hb)); h->dif_start_sector = get_uint32((char *)header_dif_start_sector(hb)); h->dif_sector_count = get_uint32((char *)header_dif_sector_count(hb)); /** Compute the maximum possible sector number by taking our OLE filesize ** and dividing it by the size of our sector size. While this isn't ** absolutely accurate it is at least useful in providing us with an ** upper-bound of what is an acceptable sector ID **/ ole->last_sector = ole->file_size >> h->sector_shift; /** Decode our first 109 sector-ID's into the master sector allocation table (MSAT/FAT) **/ fat_start = header_fat_sectors(hb); for (i = 0; i < h->fat_sector_count; i++) { if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break; h->FAT[i] = get_uint32( (char *)( fat_start +(LEN_ULONG *i))); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_header_sanity_check Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Determines the degree of insanity in the header, returning it as an integer, 1 per degree of insanity. -------------------------------------------------------------------- Changes: 20041127-2027:PLD: Initial version \------------------------------------------------------------------*/ int OLE_header_sanity_check( struct OLE_object *ole ) { int insanity=0; int max_sectors; struct OLE_header *h; h = &(ole->header); max_sectors = ole->file_size / h->sector_size; if (h->sector_shift > 20) insanity++; if (h->mini_sector_shift > 10) insanity++; if (h->fat_sector_count < 0) insanity++; if (h->fat_sector_count > max_sectors) insanity++; if (h->directory_stream_start_sector > max_sectors) insanity++; return insanity; } /*-----------------------------------------------------------------\ Function Name : OLE_print_header Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_header( struct OLE_object *ole ) { unsigned int i; struct OLE_header *h; h = &(ole->header); printf( "Minor version = %d\n" "DLL version = %d\n" "Byte order = %d\n\n" "Sector shift = %d\n" "Sector size = %d\n" "Mini Sector shift = %d\n" "Mini sector size = %d\n\n" "FAT sector count = %d\n" "First FAT sector = %d\n\n" "Maximum ministream size = %d\n\n" "First MiniFAT sector = %d\n" "MiniFAT sector count = %d\n\n" "First DIF sector = %d\n" "DIF sector count = %d\n" "--------------------------------\n" ,h->minor_version ,h->dll_version ,h->byte_order ,h->sector_shift ,h->sector_size ,h->mini_sector_shift ,h->mini_sector_size ,h->fat_sector_count ,h->directory_stream_start_sector ,h->mini_cutoff_size ,h->mini_fat_start ,h->mini_fat_sector_count ,h->dif_start_sector ,h->dif_sector_count ); // Print out the FAT chain for (i = 0; i < h->fat_sector_count; i++) { if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break; // We can't read beyond the 109th sector location printf("FAT[%d] = %d\n", i, h->FAT[i]); } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_convert_directory Returns Type : int ----Parameter List 1. unsigned char *buf, 2. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_convert_directory( struct OLE_object *ole, unsigned char *buf, struct OLE_directory_entry *dir ) { /** Converts a raw block of 128 bytes from the file to a ** struct OLE_directory_entry data structure **/ /** Flush the element name **/ memset( dir->element_name, '\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE); /** The first 64 bytes of the structure are the element's name ** in 16-bite UNICODE, meaning a maximum of 31 characters when ** we account for the trailing zero byte **/ /** Copy the first 64 bytes of our *buf parameter into the element name **/ memcpy( dir->element_name, buf, OLE_DIRECTORY_ELEMENT_NAME_SIZE ); /** how many bytes of the above 64 bytes are used for the name (NOT CHARACTERS!), ** ** example, for a 8 character string with a trailing zero we use ** ** (8+1)*2 = 18 bytes **/ dir->element_name_byte_count = get_int16( (char *) buf + 0x40 ); /** Element type is of the following: ** 0x00 - empty ** 0x01 - user storage ** 0x02 - user stream ** 0x03 - lock bytes (we don't know what this is for) ** 0x04 - property (again, we don't know) ** 0x05 - root storage **/ dir->element_type = get_int8((char *) buf +0x42 ); /** Element colour for the red-black tree: ** 0x00 - Red ** 0x01 - Black **/ dir->element_colour = get_int8((char*) buf +0x43 ); /** Directory ID (DID) of the left child, -1 if no sibling **/ dir->left_child = get_uint32((char*) buf +0x44 ); /** Directory ID (DID) of the right child, -1 if no sibling **/ dir->right_child = get_uint32((char *) buf +0x48 ); /** Directory ID (DID) of the root node entry of the RB tree of all ** storage members (if this entry is a storage), else -1. **/ dir->root = get_uint32((char*) buf +0x4c ); memcpy( dir->class, buf +0x50, 16 ); dir->userflags = get_uint32((char*) buf +0x60 ); memcpy( dir->timestamps, buf +0x64, 16 ); /** Actually consists of 2 8 byte stamps **/ /** Sector ID of the first sector or short-sector **/ dir->start_sector = get_uint32((char*) buf +0x74 ); /** Size of this stream **/ DOLE LOGGER_log("%s:%d:OLE_directory_entry:DEBUG: stream size = 0x%x %x %x %x" ,FL ,*(buf +0x78) ,*(buf +0x79) ,*(buf +0x7A) ,*(buf +0x7B) ); dir->stream_size = get_uint32((char *) buf +0x78 ); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_print_directory Returns Type : int ----Parameter List 1. struct OLE *ole, 2. struct OLE_directory_entry *dir , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_print_directory( struct OLE_object *ole, struct OLE_directory_entry *dir ) { char element[64]; OLE_dbstosbs( dir->element_name, dir->element_name_byte_count, element, sizeof(element) ); printf( "Element Name = %s\n" "Element type = %d\n" "Element colour = %d\n" "Left Child = %d\n" "Right Child = %d\n" "Root = %d\n" "User flags = %d\n" "Start sector = %ld\n" "Stream Size = %d\n" ,element ,dir->element_type ,dir->element_colour ,dir->left_child ,dir->right_child ,dir->root ,dir->userflags ,dir->start_sector ,dir->stream_size ); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_load_FAT Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_load_FAT( struct OLE_object *ole ) { unsigned int FAT_size; FAT_size = ole->header.fat_sector_count << ole->header.sector_shift; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG:Allocating for %d sectors (%d bytes) \n" ,FL,ole->header.fat_sector_count, FAT_size); ole->FAT = malloc( FAT_size *sizeof(unsigned char)); ole->FAT_limit = ole->FAT +FAT_size; if (ole->FAT != NULL) { unsigned int i; unsigned char *fat_position = ole->FAT; unsigned int sector_count = ole->header.fat_sector_count; if (sector_count > OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) { sector_count = OLE_HEADER_FAT_SECTOR_COUNT_LIMIT; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: sector count greater than limit; set to %d",FL, sector_count); } // Load in all our primary-FAT sectors from the OLE file for (i = 0; i < sector_count; i++) { int getblock_result = 0; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Loading sector %d",FL, i); getblock_result = OLE_get_block(ole, ole->header.FAT[i], fat_position); if (getblock_result != 0) { // if the get block fails, return the error - but keep the FAT // pointer alive - so that we can facilitate debugging // otherwise the caller is always going to get a NULL pointer // and have no idea to what extent the data was read. // // This behavior may be changed later - but for now (beta development) // it'll be okay to leave it here - just make sure we know to // free the FAT block later. return getblock_result; } fat_position += ole->header.sector_size; if (fat_position > ole->FAT_limit) { LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: FAT boundary limit exceeded %p > %p", FL, fat_position, ole->FAT_limit); return -1; } } // If our DIF count is > 0, this means we have a pretty big // file on hand (> 7Mb) and thus we now have to do some // fancy double-dereferenced sector request - enough to // twist your brain if you're not alert, you have been // warned. if (ole->header.dif_sector_count > 0) { unsigned char *fat_block; unsigned char *fat_block_end; unsigned int current_sector = ole->header.dif_start_sector; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Allocating %d bytes to fat_block\n",FL, ole->header.sector_size); fat_block = malloc( ole->header.sector_size ); if (fat_block == NULL) { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: Unable to allocate %d bytes\n", FL, ole->header.sector_size); return -1; // exit(1); } // We need to know where the end of this block is - because it's // used to show us where the NEXT FAT block is going to come from // NOTE - this only occurs if we do have another block, else // we'll simply have to just realise that we don't need any more // blocks and stop with this one. fat_block_end = fat_block +ole->header.sector_size -LEN_ULONG; // We know we've got 'dif_sector_count' blocks to read, each of // these blocks hold no more than 127 sector addresses which // contain the actual FAT data we're after (this is the double // dereference bit that twists your brain ) DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Loading DIF sectors (count = %d)",FL,ole->header.dif_sector_count); for (i = 0; i < ole->header.dif_sector_count; i++) { int import_sector; unsigned char *DIF = fat_block; int tick = 0; int getblock_result; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT index-data[%d] from sector 0x%x",FL,i,current_sector); getblock_result = OLE_get_block(ole, current_sector, fat_block); if (getblock_result != OLE_OK) { if (fat_block) free(fat_block); return getblock_result; } if (OLE_DPEDANTIC(ole->debug)) OLE_print_sector(ole, (char *)fat_block, ole->header.sector_size); // Now, traverse this block until we hit a < 0 // If we get what is a non-valid sector value // we know we've reached the end of the valid // sectors from which to read more extended FAT // data. do { import_sector = get_int32( (char *) DIF ); DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: import sector = 0x%x",FL,import_sector); if (import_sector >= 0) { if (fat_position +ole->header.sector_size <= ole->FAT_limit) { DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT-data[%d] from sector 0x%x",FL,tick,import_sector); getblock_result = OLE_get_block(ole, import_sector, fat_position); if (getblock_result != OLE_OK) { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: Not able to load block, import sector = 0x%x, fat position = 0x%x",FL, import_sector, fat_position); if (fat_block) free(fat_block); return getblock_result; } fat_position += ole->header.sector_size; DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: FAT position = 0x%x (start = 0x%x, end = 0x%x)" ,FL ,fat_position ,fat_block ,ole->FAT_limit ); //if (fat_position +ole->header.sector_size > ole->FAT_limit) if (fat_position > ole->FAT_limit) { DOLE LOGGER_log("%s:%d:OLE_load_FAT:ERROR: FAT memory boundary limit exceeded %p >= %p",FL,fat_position,ole->FAT_limit); if (fat_block) free(fat_block); return OLEER_MEMORY_OVERFLOW; } tick++; DIF += LEN_ULONG; } else { LOGGER_log("%s:%d:OLE_load_FAT:ERROR: FAT memory boundary limit exceeded %p >= %p",FL,fat_position,ole->FAT_limit); if (fat_block) free(fat_block); return OLEER_MEMORY_OVERFLOW; } } else { VOLE LOGGER_log("%s:%d:OLE_load_FAT:ERROR: sector request was negative (%d)",FL, import_sector); } DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: DIF = 0x%x",FL,DIF); } while ((import_sector >= 0)&&(DIF < fat_block_end)); // Get the next sector of DIF/XBAT data ... // // If we still have more sectors full of extended FAT // sectors that we have to read, then we neet to // obtain the address of the next FAT-sector filled // sector if ( i < ole->header.dif_sector_count -1 ) { current_sector = get_uint32((char *) fat_block_end ); DOLE LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Next DIF/XBAT index sector located at 0x%x",FL,current_sector); if (current_sector < 0) break; } } // For every DIF/XBAT sector we're supposed to read if (fat_block) free(fat_block); } // If we have DIF/XBAT sectors to read into the FAT } // If we managed to allocate memory for our FAT table return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_follow_chain Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_follow_chain( struct OLE_object *ole, int FAT_sector_start ) { int current_sector = FAT_sector_start; int chain_length=0; int last_sector_of_file = ole->last_sector; int break_out = 0; struct bti_node *n; BTI_init(&n); if (FAT_sector_start < 0) return 0; DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: Starting chain follow at sector %d",FL, FAT_sector_start ); do { unsigned int next_sector; unsigned char *next_sector_location; next_sector_location = ole->FAT +(LEN_ULONG *current_sector); if (next_sector_location > (ole->FAT_limit -4)) { DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: ERROR: Next sector was outside of the limits of this file (%ld > %ld)",FL, next_sector_location, ole->FAT_limit); break; } //next_sector = get_4byte_value( ole->FAT +(LEN_ULONG *current_sector)); next_sector = get_uint32((char*) next_sector_location ); if (BTI_add(&n, next_sector) != 0) { DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: Sector collision, terminating chain traversal",FL); chain_length=-1; break; } DOLE LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: 0x%0X:%d)->(0x%0X:%d)\n",FL, current_sector, current_sector, next_sector, next_sector); // 20040729-10H37 Added this to prevent endless loop which sometimes occurs at sector 0 if (next_sector == current_sector) break; // fflush(stdout); current_sector = next_sector; chain_length++; /** Test to see if we should terminate this chain traversal **/ switch (current_sector) { case OLE_SECTORID_MSAT: case OLE_SECTORID_SAT: case OLE_SECTORID_ENDOFCHAIN: case OLE_SECTORID_FREE: break_out=1; break; default: break_out=0; }; if (current_sector < 0) break_out = 1; } while ((break_out==0)&&(current_sector < last_sector_of_file)); BTI_done(&n); return chain_length; } /*-----------------------------------------------------------------\ Function Name : OLE_follow_minichain Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_follow_minichain( struct OLE_object *ole, int miniFAT_sector_start ) { //unsigned int current_sector = miniFAT_sector_start; int current_sector = miniFAT_sector_start; int chain_length=0; int break_out = 0; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Starting at sector %d",FL, miniFAT_sector_start); if (miniFAT_sector_start < 0) return 0; do { unsigned int next_sector; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Requesting 4-byte value at '%d'",FL, ole->miniFAT +(LEN_ULONG *current_sector)); if (ole->miniFAT +(LEN_ULONG *current_sector) > ole->miniFAT_limit) { DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Requested location is out of bounds\n",FL); return 0; } next_sector = get_uint32((char*) ole->miniFAT +(LEN_ULONG *current_sector)); DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Current Msector(0x%0X:%d)->next(0x%0X:%d)\n", FL, current_sector, current_sector, next_sector, next_sector); /** Check for conditions that indicate we should stop traversing this chain **/ /** 1. We cannot point to ourselves **/ if (current_sector == next_sector) break; chain_length++; current_sector = next_sector; /** Test for non-positive type sector ID's **/ switch (current_sector) { case OLE_SECTORID_MSAT: case OLE_SECTORID_SAT: case OLE_SECTORID_ENDOFCHAIN: case OLE_SECTORID_FREE: break_out=1; break; default: break_out=0; }; DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: current sector = %d",FL,current_sector); } while ((break_out==0)&&(current_sector <= ole->last_sector )); DOLE LOGGER_log("%s:%d:OLE_follow_minichain:DEBUG: Done. Chainlength=%d",FL, chain_length); return chain_length; } /*-----------------------------------------------------------------\ Function Name : char Returns Type : unsigned ----Parameter List 1. *OLE_load_minichain( struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: PLD:2003-Aug-28: Added sanity checking on the miniFAT_sector_start value so that we didn't try to load up a miniFAT starting on a negative value \------------------------------------------------------------------*/ unsigned char *OLE_load_minichain( struct OLE_object *ole, int miniFAT_sector_start ) { int chain_length = 0; int current_sector = miniFAT_sector_start; unsigned char *buffer; unsigned char *bp; DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Loading minichain starting at %d",FL, miniFAT_sector_start); // Added this sanity checking 2003 Aug 28 if (miniFAT_sector_start < 0) return NULL; chain_length = OLE_follow_minichain( ole, miniFAT_sector_start ); DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Found %d mini-sectors to load (%d bytes)\n",FL, chain_length, chain_length *ole->header.mini_sector_size); // 20040911-21H59 // If our chain is 0 length, then there's nothing to return if (chain_length == 0) return NULL; bp = buffer = malloc( chain_length *ole->header.mini_sector_size *sizeof(unsigned char)); if (buffer != NULL) { do { unsigned int next_sector; DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Loading sector %d",FL, current_sector); OLE_get_miniblock( ole, current_sector, bp ); bp += ole->header.mini_sector_size; next_sector = get_uint32((char *)( ole->miniFAT +(LEN_ULONG *current_sector))); current_sector = next_sector; } while ((current_sector != OLE_SECTORID_ENDOFCHAIN)&&(current_sector >= 0)&&(current_sector <= ole->last_sector)); } else { LOGGER_log("%s:%d:OLE_get_miniblock:ERROR: Failed to allocate enough memory for miniChain",FL); } DOLE LOGGER_log("%s:%d:OLE_load_minichain:DEBUG: Done. buffer=%p",FL, buffer); return buffer; } /*-----------------------------------------------------------------\ Function Name : char Returns Type : unsigned ----Parameter List 1. *OLE_load_chain( struct OLE_object *ole, 2. int FAT_sector_start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: Make the loading aware of negative-value sectors so that it can make more intelligent exit strategies. \------------------------------------------------------------------*/ unsigned char *OLE_load_chain( struct OLE_object *ole, int FAT_sector_start ) { int chain_length = 0; int current_sector = FAT_sector_start; unsigned char *buffer = NULL; unsigned char *bp = NULL; ole->last_chain_size = 0; if (FAT_sector_start < 0) return NULL; DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading chain, starting at sector %d",FL,FAT_sector_start); chain_length = OLE_follow_chain( ole, FAT_sector_start ); DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: %d sectors need to be loaded",FL,chain_length); if (chain_length > 0) { size_t offset; offset = ole->last_chain_size = chain_length << ole->header.sector_shift; bp = buffer = malloc( offset *sizeof(unsigned char)); if (buffer == NULL) { LOGGER_log("%s:%d:OLE_load_chain:ERROR: Cannot allocate %d bytes for OLE chain",FL,offset); return NULL; } if (buffer != NULL) { int tick = 0; unsigned char *bp_limit; bp_limit = bp +offset; do { int next_sector; DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading sector[%d] %d",FL, tick, current_sector ); ole->error = OLE_get_block( ole, current_sector, bp ); if (ole->error != OLE_OK) { //FREE5 if (bp != NULL) free(bp); return NULL; } bp += ole->header.sector_size; if (bp > bp_limit) { if (buffer != NULL) { free(buffer); bp = buffer = NULL; } VOLE LOGGER_log("%s:%d:OLE_load_chain:ERROR: Load-chain went over memory boundary",FL); return NULL; }; next_sector = get_uint32((char *)( ole->FAT +(LEN_ULONG *current_sector))); current_sector = next_sector; tick++; } while ((current_sector >= 0)&&(current_sector <= ole->last_sector)); } } DOLE LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Done loading chain",FL); return buffer; } /*-----------------------------------------------------------------\ Function Name : OLE_open_file Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *fullpath , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: 20041127-2033:PLD: Added ole->file_size setting so that we can use this in the sanity checking to see if the requested sectors are outside of the possible valid filesize range. \------------------------------------------------------------------*/ int OLE_open_file( struct OLE_object *ole, char *fullpath ) { struct stat st; int stat_result; FILE *f; stat_result = stat(fullpath, &st); if (stat_result != 0) { DOLE LOGGER_log("%s:%d:OLE_open_file:ERROR: Cannot locate file '%s' for opening (%s)",FL, fullpath, strerror(errno)); return OLEER_BAD_INPUT_FILE; } DOLE LOGGER_log("%s:%d:OLE_open_file:DEBUG: File size of %s = %ld",FL, fullpath, st.st_size); if ((stat_result == 0) && (st.st_size < 512)) return OLEER_NOT_OLE_FILE; ole->file_size = st.st_size; f = fopen(fullpath,"r"); if (f == NULL) { ole->f = NULL; if (ole->quiet == 0) { LOGGER_log("%s:%d:OLE_open_file:ERROR:Cannot open %s for reading (%s)\n",FL,fullpath, strerror(errno)); } return -1; } else { ole->f = f; ole->file_size = st.st_size; ole->last_sector = -1; } return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_open_directory Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *directory , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_open_directory( struct OLE_object *ole, char *directory ) { int result=0; result = mkdir( directory, S_IRWXU ); if ((result != 0)&&(errno != EEXIST)) { LOGGER_log("%s:%d:OLE_open_directory:ERROR: %s",FL,strerror(errno)); } else result = OLE_OK; return result; } /*-----------------------------------------------------------------\ Function Name : OLE_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: This is merely a passthrough function to the OLEUW one, we do this in order to avoid having to force the calling parent from having to #include the OLEUW headers as well -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_set_filename_report_fn( struct OLE_object *ole, int (*ptr_to_fn)(char *) ) { ole->filename_report_fn = ptr_to_fn; return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_store_stream Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. char *stream_name, 3. char *directory, 4. unsigned char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_store_stream( struct OLE_object *ole, char *stream_name, char *directory, char *stream, size_t stream_size ) { char *full_path = NULL; full_path = PLD_dprintf("%s/%s", directory, stream_name); if (full_path == NULL) { LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot compose full filename string from '%s' and '%s'", FL, directory, stream_name); return -1; } else { FILE *f; f = fopen(full_path,"w"); if (f == NULL) { LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot open %s for writing (%s)",FL, full_path, strerror(errno)); if (full_path) free(full_path); return -1; } else { size_t written_bytes; written_bytes = fwrite( stream, 1, stream_size, f ); if (written_bytes != stream_size) { LOGGER_log("%s:%d:OLE_store_stream:WARNING: Only wrote %d of %d bytes to file %s",FL,written_bytes,stream_size,full_path); } fclose(f); if ((OLE_VNORMAL(ole->verbose))&&(ole->filename_report_fn != NULL)) { ole->filename_report_fn( stream_name ); } } // if file is valid } // if full_path is valid if (full_path) free(full_path); return OLE_OK; } /*-----------------------------------------------------------------\ Function Name : OLE_decode_file_done Returns Type : int ----Parameter List 1. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_decode_file_done( struct OLE_object *ole ) { if (ole->f) fclose(ole->f); /** Why weren't these active? (they were commented out ) **/ if (ole->FAT) free(ole->FAT); if (ole->miniFAT) free(ole->miniFAT); if (ole->ministream) free(ole->ministream); if (ole->properties) free(ole->properties); return 0; } /*-----------------------------------------------------------------\ Function Name : OLE_terminate_and_return Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. int result , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_terminate_and_return( struct OLE_object *ole, int result ) { OLE_decode_file_done(ole); return result; } #ifdef RIPOLE_WALK_TREE int OLE_walk_tree( struct OLE_object *ole, char *fname, char *decode_path, int depth ) { /** Sanity check **/ if (depth > 100) return 0; if (ole->total_file_count > 10000) return 0; if (element_type < 0) return 0; switch (element_type) { case STGTY_ROOT: /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ DOLE LOGGER_log("%s:%d:OLE_walk_tree:DEBUG: Loading ministream/SmallBlockArray",FL); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: ministream done",FL); } } else if (adir->element_type == STGTY_STORAGE) { /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Item is directory, start child is at index %d\n",FL,i); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: DIRECTORY ministream done",FL); } #endif /*-----------------------------------------------------------------\ Date Code: : 20081101-020137 Function Name : OLE_decode_stream Returns Type : int ----Parameter List 1. struct OLE_object *ole, 2. struct OLE_directory_entry *adir, 3. char *decode_path , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_decode_stream( struct OLE_object *ole, struct OLE_directory_entry *adir, char *decode_path ) { unsigned char *stream_data; struct OLEUNWRAP_object oleuw; int decode_result = OLEUW_STREAM_NOT_DECODED; char element_name[64]; int result = 0; memset(element_name, '\0', 64); OLE_dbstosbs( adir->element_name, adir->element_name_byte_count, element_name, 64 ); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Decoding stream '%s'",FL, element_name); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Initializing stream unwrapper",FL); OLEUNWRAP_init(&oleuw); OLEUNWRAP_set_debug(&oleuw,ole->debug); OLEUNWRAP_set_verbose(&oleuw,ole->verbose); OLEUNWRAP_set_filename_report_fn(&oleuw, ole->filename_report_fn); OLEUNWRAP_set_save_unknown_streams(&oleuw, ole->save_unknown_streams); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Unwrap engine set.",FL); if (adir->stream_size >= ole->header.mini_cutoff_size) { /** Standard size sector stored stream **/ /** Standard size sector stored stream **/ /** Standard size sector stored stream **/ DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Loading normal sized chain starting at sector %d",FL, adir->start_sector); stream_data = OLE_load_chain( ole, (int)adir->start_sector ); if (stream_data == NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Terminating from stream data being NULL ",FL); //OLE_decode_file_done(ole); return OLEER_MINISTREAM_STREAM_READ_FAIL; } DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Normal decode START. element name ='%s' stream size = '%ld'",FL, element_name, adir->stream_size); decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, (char *)stream_data, adir->stream_size, decode_path ); DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Normal decode done.",FL); } else { /** Minichain/Minisector stored stream **/ /** Minichain/Minisector stored stream **/ /** Minichain/Minisector stored stream **/ DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Minichain loader, starting at sector %d" ,FL ,adir->start_sector ); stream_data = OLE_load_minichain( ole, adir->start_sector ); if (stream_data == NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Ministream was non-existant, terminating",FL); //OLE_decode_file_done(ole); return OLEER_NORMALSTREAM_STREAM_READ_FAIL; } DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode START.",FL); decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, (char *)stream_data, adir->stream_size, decode_path ); DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode done.",FL); } if ((stream_data != NULL)&&(decode_result == OLEUW_STREAM_NOT_DECODED)&&(ole->save_unknown_streams)) { char *lfname; lfname = PLD_dprintf("ole-stream.%d",adir->start_sector); if (lfname != NULL) { DOLE LOGGER_log("%s:%d:OLE_decode_stream:DEBUG: Saving stream to %s",FL,lfname); OLE_store_stream( ole, lfname, decode_path, (char *) stream_data, adir->stream_size ); free(lfname); } } // If we needed to save an unknown stream // Clean up an stream_data which we may have // read in from the chain-loader. if (stream_data) free(stream_data); return result; } /*-----------------------------------------------------------------\ Function Name : OLE_decode_file Returns Type : int ----Parameter List 1. char *fname, 2. char *decode_path , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLE_decode_file( struct OLE_object *ole, char *fname, char *decode_path ) { unsigned char *current_property, *property_limit; int result = 0; int i; // Reject any bad paramters. if (ole == NULL) return OLEER_DECODE_NULL_OBJECT; if (fname == NULL) return OLEER_DECODE_NULL_FILENAME; if (decode_path == NULL) return OLEER_DECODE_NULL_PATH; // We need to gain access to the OLE2 data file, without // this pretty much everything is pointless. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening %s", FL, fname ); result = OLE_open_file( ole, fname ); if (result != 0) return result; // Try create the output directory which we're using // to write the decoded files out to. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening output directory %s", FL, decode_path); result = OLE_open_directory( ole, decode_path ); if (result != 0) return result; // In order to successfully decode an OLE2 stream, we have to read // and understand the first 512 bytes of the file, this is the // OLE2 header. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Getting main header", FL); result = OLE_get_header( ole ); if (result != 0) return result; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Converting main header", FL); result = OLE_convert_header( ole ); if (result != 0) return result; result = OLE_header_sanity_check( ole ); if (result > 0) return OLEER_INSANE_OLE_FILE; DOLE OLE_print_header( ole ); DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading FAT", FL); result = OLE_load_FAT( ole ); if (result != 0) return result; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading miniFAT chain", FL); ole->miniFAT = OLE_load_chain( ole, ole->header.mini_fat_start ); if (ole->miniFAT == NULL) return OLEER_MINIFAT_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading Directory stream chain", FL); ole->properties = OLE_load_chain( ole, ole->header.directory_stream_start_sector ); if (ole->properties == NULL) return OLEER_PROPERTIES_READ_FAIL; i=0; current_property = ole->properties; property_limit = current_property +ole->last_chain_size ; // while(1) while (current_property < property_limit) { struct OLE_directory_entry a_dir_object, *adir; int property_value=0; adir = &a_dir_object; OLE_dir_init(adir); property_value = get_uint8((char *)current_property); if (property_value < 1) break; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG:--------- DIRECTORY INDEX: %d",FL,i); OLE_convert_directory( ole, current_property, adir ); DOLE { LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Printing directory details...",FL); OLE_print_directory( ole, adir); LOGGER_log("%s:%d:OLE_decode_file:DEBUG: End of directory details",FL); } if (adir->element_colour > 1) break; if ((adir->element_type == STGTY_INVALID)||(adir->element_type > STGTY_ROOT)) { DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: breaking out due to element type %d",FL, adir->element_type); break; } else if (adir->element_type == STGTY_ROOT){ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ /** ROOT DIRECTORY ENTRY **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading ministream/SmallBlockArray",FL); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: ministream done",FL); } else if (adir->element_type == STGTY_STORAGE) { /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ /** STORAGE ELEMENT **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Item is directory, start child is at index %d\n",FL,i); ole->ministream = OLE_load_chain( ole, adir->start_sector ); if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL; DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: DIRECTORY ministream done",FL); } else if (adir->element_type == STGTY_STREAM) { /** STREAM ELEMENT **/ /** STREAM ELEMENT **/ /** STREAM ELEMENT **/ OLE_decode_stream( ole, adir, decode_path ); } else { /** If the element isn't of the above types then it's possibly ** an empty element or just one used for the MSAT/SAT ** either way we just step over it and carry on **/ DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Element type %d does not need to be handled",FL,adir->element_type); } // Jump to the next property record, which // is always 128 bytes ahead. current_property += 128; i++; } // While there are still more directory entries to read in. DOLE LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Finished",FL); /* //if (ole->f) fclose(ole->f); fclose(ole->f); if (ole->FAT) free(ole->FAT); if (ole->miniFAT) free(ole->miniFAT); if (ole->ministream) free(ole->ministream); if (ole->properties) free(ole->properties); */ return OLE_OK; } ripmime-1.4.0.10.debian.1/ripOLE/LICENSE0000640000175000017500000000270411667102235015226 0ustar pjbpjbCopyright (c) 2003, PLD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of the PLD nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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 DAMAGE. ripmime-1.4.0.10.debian.1/ripOLE/bytedecoders.h0000640000175000017500000000041511667102235017043 0ustar pjbpjb#ifndef __BYTEDECODERS__ #define __BYTEDECODERS__ int get_int8( char *start ); int get_int16( char *start ); int get_int32( char *start ); unsigned int get_uint8( char *start ); unsigned int get_uint16( char *start ); unsigned int get_uint32( char *start ); #endif ripmime-1.4.0.10.debian.1/ripOLE/pldstr.h0000640000175000017500000000165411667102235015705 0ustar pjbpjb#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 ripmime-1.4.0.10.debian.1/ripOLE/ripole.c0000640000175000017500000002045011667102235015655 0ustar pjbpjb // Microsoft OLE2 stream parser. #include #include #include #include #include "logger.h" #include "pldstr.h" #include "ole.h" struct ripOLE_object { int debug; int verbose; int save_unknown_streams; char *inputfile; char *outputdir; }; #define ROLE_VERSION "0.2.1" static char defaultdir[]="."; static char version[]="0.2.1 - November 1, 2008 (C) PLDaniels http://www.pldaniels.com/ripole"; static char help[]="ripOLE -i [ -d ] [--save-unknown-streams] [--version|-V] [--verbose|-v] [--debug] [--help|-h]"; /*-----------------------------------------------------------------\ Function Name : set_defaults Returns Type : int ----Parameter List 1. struct ripOLE_object *role , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_set_defaults( struct ripOLE_object *role ) { role->outputdir = defaultdir; role->debug = 0; role->verbose = 0; role->save_unknown_streams = 0; role->inputfile = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : parse_parameters Returns Type : int ----Parameter List 1. struct ripOLE_object *role, 2. int argc, 3. char **argv , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_parse_parameters( struct ripOLE_object *role, int argc, char **argv ) { int i; int result = 0; role->outputdir = defaultdir; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'i': i++; role->inputfile = strdup(argv[i]); break; case 'd': i++; role->outputdir = strdup(argv[i]); break; case 'v': role->verbose = 1; break; case 'V': fprintf (stdout, "%s\n", version); exit (1); break; case 'h': fprintf (stdout, "%s\n", help); exit (1); break; // if we get ANOTHER - symbol, then we have an extended flag case '-': if (strncmp (&(argv[i][2]), "verbose", 7) == 0) { role->verbose=1; } else if (strncmp (&(argv[i][2]), "save-unknown-streams", 20) == 0) { role->save_unknown_streams = 1; } else if (strncmp (&(argv[i][2]), "debug", 5) == 0) { role->debug=1; } else if (strncmp (&(argv[i][2]), "version", 7) == 0) { fprintf (stdout, "%s\n", version); exit (1); } else if (strncmp(&(argv[i][2]),"help",4)==0) { fprintf(stdout,"%s\n",help); exit(1); } else { fprintf(stdout,"Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } break; // else, just dump out the help message default: fprintf(stdout,"Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } // Switch argv[i][1] } // if argv[i][0] == - } // for return result; } /*-----------------------------------------------------------------\ Function Name : set_parameters Returns Type : int ----Parameter List 1. struct ripOLE_object *role, 2. struct OLE_object *ole , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_set_parameters( struct ripOLE_object *role, struct OLE_object *ole ) { if(role->debug == 1) { OLE_set_debug(ole, OLE_DEBUG_NORMAL); } if (role->verbose == 1) { OLE_set_verbose(ole, OLE_VERBOSE_NORMAL); } if (role->save_unknown_streams == 1) { OLE_set_save_unknown_streams(ole, 1); } return 0; } /*-----------------------------------------------------------------\ Function Name : ripOLE_report_filename_decoded Returns Type : int ----Parameter List 1. char *filename, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_report_filename_decoded(char *filename) { LOGGER_log("Decoding filename=%s", filename); return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_init Returns Type : int ----Parameter List 1. struct ripOLE_object *role, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_init(struct ripOLE_object *role) { role->debug = 0; role->verbose = 0; role->save_unknown_streams = 0; role->inputfile = NULL; role->outputdir = NULL; return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_done Returns Type : int ----Parameter List 1. struct ripOLE_object *role, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_done(struct ripOLE_object *role) { if (role->inputfile != NULL) free(role->inputfile); if (role->outputdir != NULL) free(role->outputdir); return 0; } /*-----------------------------------------------------------------\ Function Name : ROLE_validate Returns Type : int ----Parameter List 1. struct ripOLE_object *role , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int ROLE_validate(struct ripOLE_object *role ) { int result = 0; if (role->inputfile == NULL) { fprintf(stderr,"ripOLE requires an input file to decode\n"); return -1; } return result; } /*-----------------------------------------------------------------\ 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 ripOLE_object role; struct OLE_object *ole = NULL; int result = 0; if (argc == 1) { fprintf (stdout, "%s\n", help); exit(1); } ole = malloc(sizeof(struct OLE_object)); if (ole == NULL) { LOGGER_log("ripOLE: Cannot allocate memory for OLE object"); return 1; } LOGGER_set_output_mode(_LOGGER_STDOUT); OLE_init(ole); ROLE_init(&role); ROLE_set_defaults( &role ); ROLE_parse_parameters(&role, argc, argv); result = ROLE_validate(&role); if (result != 0) return result; ROLE_set_parameters(&role, ole); OLE_set_filename_report_fn(ole, ROLE_report_filename_decoded ); result = OLE_decode_file( ole, role.inputfile, role.outputdir ); OLE_decode_file_done(ole); if ((result != 0)) { if (role.verbose) { switch (result) { case OLEER_NO_INPUT_FILE: case OLEER_BAD_INPUT_FILE: LOGGER_log("Cannot locate input file '%s'",role.inputfile); break; case OLEER_NOT_OLE_FILE: LOGGER_log("File '%s' is not OLE2 format",role.inputfile); break; case OLEER_INSANE_OLE_FILE: LOGGER_log("OLE input file '%s' is insane", role.inputfile); break; default: LOGGER_log("ripOLE: decoding of %s resulted in error %d\n", role.inputfile, result ); } } return result; } if (ole != NULL) free(ole); ROLE_done(&role); return result; } /** end of ripOLE **/ ripmime-1.4.0.10.debian.1/ripOLE/olestream-unwrap.h0000640000175000017500000000204311667102235017673 0ustar pjbpjb #define OLEUW_ELEMENT_10NATIVE 10 #define OLEUW_ELEMENT_10NATIVE_STRING "Ole10Native" #define OLEUW_ELEMENT_DATA "Data" #define OLEUW_OK 0 #define OLEUW_STREAM_NOT_DECODED 100 struct OLEUNWRAP_object { int (*filename_report_fn)(char *); int debug; int verbose; int save_unknown_streams; }; int OLEUNWRAP_init( struct OLEUNWRAP_object *oleuw ); int OLEUNWRAP_set_debug( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_set_verbose( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_set_save_unknown_streams( struct OLEUNWRAP_object *oleuw, int level ); int OLEUNWRAP_save_stream( struct OLEUNWRAP_object *oleuw, char *fname, char *decode_path, char *stream, size_t bytes ); int OLEUNWRAP_decode_attachment( struct OLEUNWRAP_object *oleuw, char *stream, size_t stream_size, char *decode_path ); int OLEUNWRAP_decodestream( struct OLEUNWRAP_object *oleuw, char *element_string, char *stream, size_t stream_size, char *decode_path ); int OLEUNWRAP_set_filename_report_fn( struct OLEUNWRAP_object *oleuw, int (*ptr_to_fn)(char *) ); ripmime-1.4.0.10.debian.1/ripOLE/logger.h0000640000175000017500000000113411667102235015645 0ustar pjbpjb #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 ripmime-1.4.0.10.debian.1/ripOLE/ole.h0000640000175000017500000001117511667102235015153 0ustar pjbpjb #ifndef LIBOLE #define LIBOLE #define LIBOLE_VERSION "200811010143" #define OLE_OK 0 #define OLEER_NO_INPUT_FILE 100 #define OLEER_BAD_INPUT_FILE 101 #define OLEER_NOT_OLE_FILE 102 #define OLEER_INSANE_OLE_FILE 103 #define OLEER_DECODE_NULL_OBJECT 10 #define OLEER_DECODE_NULL_FILENAME 11 #define OLEER_DECODE_NULL_PATH 12 #define OLEER_MINIFAT_READ_FAIL 30 #define OLEER_PROPERTIES_READ_FAIL 31 #define OLEER_MINISTREAM_READ_FAIL 32 #define OLEER_MINISTREAM_STREAM_READ_FAIL 33 #define OLEER_NORMALSTREAM_STREAM_READ_FAIL 34 #define OLEER_GET_BLOCK_SEEK 41 #define OLEER_GET_BLOCK_READ 42 #define OLEER_MEMORY_OVERFLOW 50 #define OLE_VERBOSE_NORMAL 1 #define OLE_VERBOSE_FATREAD 2 #define OLE_VERBOSE_DIRREAD 4 #define OLE_VERBOSE_STREAMREAD 8 #define OLE_VERBOSE_STREAMDECODE 16 #define OLE_VNORMAL(x) ((x) && OLE_VERBOSE_NORMAL == OLE_VERBOSE_NORMAL ) #define OLE_DEBUG_NORMAL 1 #define OLE_DEBUG_PEDANTIC 2 #define OLE_DNORMAL(x) ((x) && OLE_DEBUG_NORMAL == OLE_DEBUG_NORMAL) #define OLE_DPEDANTIC(x) ((x) && OLE_DEBUG_PEDANTIC == OLE_DEBUG_PEDANTIC) #define OLE_HEADER_FAT_SECTOR_COUNT_LIMIT 109 struct OLE_header { unsigned int minor_version; unsigned int dll_version; unsigned int byte_order; unsigned int sector_shift; unsigned int sector_size; unsigned int mini_sector_shift; unsigned int mini_sector_size; unsigned int fat_sector_count; unsigned int directory_stream_start_sector; unsigned int mini_cutoff_size; unsigned int mini_fat_start; unsigned int mini_fat_sector_count; unsigned int dif_start_sector; unsigned int dif_sector_count; unsigned int FAT[OLE_HEADER_FAT_SECTOR_COUNT_LIMIT]; }; #define OLE_DIRECTORY_ELEMENT_NAME_SIZE 64 #define OLE_DIRECTORY_CLASS_SIZE 16 #define OLE_DIRECTORY_TIMESTAMPS_SIZE 16 struct OLE_directory_entry { char element_name[OLE_DIRECTORY_ELEMENT_NAME_SIZE]; int element_name_byte_count; char element_type; char element_colour; unsigned int left_child; unsigned int right_child; unsigned int root; unsigned char class[OLE_DIRECTORY_CLASS_SIZE]; unsigned int userflags; unsigned char timestamps[OLE_DIRECTORY_TIMESTAMPS_SIZE]; long int start_sector; unsigned int stream_size; }; #define OLE_HEADER_BLOCK_SIZE 512 struct OLE_object { int error; size_t file_size; int last_sector; size_t last_chain_size; FILE *f; unsigned char *FAT; unsigned char *FAT_limit; /** Added to prevent segment violations **/ unsigned char *miniFAT; unsigned char *miniFAT_limit; /** Added to prevent segment violations **/ unsigned char header_block[OLE_HEADER_BLOCK_SIZE]; unsigned char *ministream; unsigned char *properties; struct OLE_header header; // End user configurable parameters: int debug; int verbose; int quiet; int save_unknown_streams; int save_streams; int save_mini_streams; int save_normal_streams; int decode_streams; int decode_mini_streams; int decode_normal_streams; int (*filename_report_fn)(char *); }; // Prototypes int OLE_version( void ); int OLE_init( struct OLE_object *ole ); int OLE_set_verbose( struct OLE_object *ole, int level ); int OLE_set_debug( struct OLE_object *ole, int level ); int OLE_set_quiet( struct OLE_object *ole, int level ); int OLE_set_save_unknown_streams( struct OLE_object *ole, int level ); int OLE_get_block( struct OLE_object *ole, int block_index, unsigned char *block_buffer ); int OLE_get_miniblock( struct OLE_object *ole, int block_index, unsigned char *block_buffer ); int OLE_dbstosbs( char *raw_string, size_t char_count, char *clean_string, int clean_string_len ); int OLE_print_string( char *string, size_t char_count); int OLE_print_sector( struct OLE_object *ole, char *sector, unsigned int bytes); int OLE_get_header( struct OLE_object *ole ); int OLE_convert_header( struct OLE_object *ole ); int OLE_print_header( struct OLE_object *ole ); int OLE_convert_directory(struct OLE_object *ole, unsigned char *buf, struct OLE_directory_entry *dir ); int OLE_print_directory( struct OLE_object *ole, struct OLE_directory_entry *dir ); int OLE_load_FAT( struct OLE_object *ole ); int OLE_follow_chain( struct OLE_object *ole, int FAT_sector_start ); int OLE_follow_minichain( struct OLE_object *ole, int miniFAT_sector_start ); unsigned char *OLE_load_minichain( struct OLE_object *ole, int miniFAT_sector_start ); unsigned char *OLE_load_chain( struct OLE_object *ole, int FAT_sector_start ); int OLE_open_file( struct OLE_object *ole, char *fullpath ); int OLE_decode_file( struct OLE_object *ole, char *fname, char *decode_path ); int OLE_decode_file_done( struct OLE_object *ole ); // Our callbacks. int OLE_set_filename_report_fn( struct OLE_object *ole, int (*ptr_to_fn)(char *) ); #endif ripmime-1.4.0.10.debian.1/ripOLE/INSTALL0000640000175000017500000000005511667102235015247 0ustar pjbpjb -----INSTALL make ./ripole your-ole.doc ripmime-1.4.0.10.debian.1/ripOLE/CONTRIBUTORS0000640000175000017500000000025411667102235016077 0ustar pjbpjb Original code by Paul L Daniels Source documentation supplied by: defrost, Chris Hine and google Patches: Chris Hine:0.0.3:2003-07-04: Buffer,bp initialisation patch. ripmime-1.4.0.10.debian.1/ripOLE/logger.c0000640000175000017500000001724211667102235015647 0ustar pjbpjb // 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; } ripmime-1.4.0.10.debian.1/ripOLE/CHANGELOG0000640000175000017500000001475011667102235015437 0ustar pjbpjb -----------------------------------ripOLE--------------------- 20111205-1844:PLD:REL -- Version 0.2.2 Fixed some compiler warnings from new gcc editions 20100327-1800:PLD:REL -- Version 0.2.1 Fixed 512/4096 sector size issues after being prompted by Michel Boudinot. 20081104-1245:PLD:DEV Added unknown_stream_count to global structure OLEUNWRAP_object which we use to keep a track of how many unknown data blobs we've written so that we can dump them out as OLE_data.xxx files (where xxx is a 0 padded count suffix ) This has been done to prevent dumping of spurilous trash filenames derived from invalid data interpretation. 20081101-0215:PLD:DEV Cleaned up source code to make sure it builds without errors with the new stricter build defaults on many systems (such as Ubuntu 8.10). Changed the old bytedecoders file to now use the more sane get_uintX() and get_intX() type calls that make a bit more sense, also solves the annoying signed return value casting issues. 20051231-1055:PLD: ---Development update Improved the image signature detection routines to allow for variable length signatures and possible non-zero offsets. Signatures are now stored as an array of struct typesig. It could be possible to use an external filetype signature library to do this process but because we're only wanting to seek out images and of very few formats it simply isn't worth the overheads or speed penalty (we don't want to have to search through 200 bytes looking for up to 1000 different filetypes). 20051224-1030:PLD: ---Development update Added primative image detection routines to pull out PNG or JPG encoded data from an Escher formatted stream. The Escher format is rather complex so for now instead of trying to decode it properly we just scan ahead in the data a short distance to look for specific PNG or JPG signatures. 20051212-1100:PLD: ---Version 0.2.0 RELEASE Fixed up OLE Stream decoding - attachments now come out with the stream headers correctly stripped off. FINALLY! Reverted the sector-ID handling back to a SIGNED 4-byte integer, as negative values are required to signal special sector ID's. 20050315-1544:PLD: Added last-sector check sanitisation while stepping along FAT/miniFAT streams 20050119-2108:PLD: Added filename sanitization, so that obscure 8-bit values didn't get used. See OLEUNWRAP_sanitize_filename() 20041127-2051:PLD: Added sanity checking for the OLE header, see the fn OLE_header_sanity_check(). If the OLE file fails the sanity check, a return code of 103 will be issued. 2004-Oct-18: Changed unsigned int sectors to signed in OLE_follow_minichain() so that negative sector requests could be detected and prevent segfaults. Thanks to Gabriele Cariole for supplying the offending mailpack. 2004-Sep-30: Added a loop detection system into OLE_follow_chain to detect situations where the loop isn't tight (ie, if it goes A->B->C->D->A). This requires two new files to be added to the archive (bt-int.[ch]) Thanks to Brian for offering the mailpack which exposed this condition. 2004-Sep-25: Fixed old style fprintf(stdout) logging calls. Removed exit(1) calls. Fixed up memory leak in OLE_get_block(), where bb would not be explicitly free'd on either errors or normal exit. 2004-Aug-16: Added ole->FAT_limit into the general OLE struct so that when traversing the FAT chain later, it can be possible to check for requests beyond the memory block limit (which would other- wise cause segfaults) 2004-Jul-17: Fixed up unwanted verbosity about read mismatch in ole.c:321 Added macro VOLE which is 'if (ole->verbose)'. Fixed up unwanted verbosity from ole when a negative sector read request is made and not in verbosity mode. (ole.c:891) 2004-May-22: Fixed up minifat walk routines, made current_sector comparisons use hex notation rather than assumed signed int comparison. Added start section sanity checking with minifat walker. ie, ensuring that the starting sector is infact greater than zero 2004-May-21: Fixed up various memory leaks which were due to premature function exits, but without cleaning up any preallocated memory blocks. 2004-Apr-5: 19H50:PLD:REL Released as v0.1.3 19H48:PLD:DEV Added 'help' output for when no paramters are given. 14H22:PLD:DEV Added stream size sanity checking when decoding the attachment size in olestream-unwrap which would occasionally generate some rather unlrealistc stream sizes. OLEUNWRAP_decode_attachment(); 01H21:PLD:DEV Finally fixed occasional segfault bug with some OLE2 files. Fix was to define the memory boundary limit of the ole properties field and check on each pass of the loop that it wasn't being breached. 2003-Nov-26: 20H13:PLD:DEV Release 0.0.4 Fixed up some potential logging segfaults if used in conjunction with syslog output. 2003-Nov-17: 16H20:PLD:DEV Added bounds checking to entire processing system so that it cannot request a sector outside of the real limits of the loaded OLE file. I consider this only to be a partial fix to a problem of various documents which do not seem to be able to be decoded reliably, fortunately at least the bounds checking stops the segfaults. 2003-Oct-31: 07H38:PLD:DEV Added -q (quiet) option, which silences various non-fatal error messages. 2003-Aug-28: 22H04:PLD:DEV Added bounds checking on Mini-FAT loader, to ensure it would not attempt to load up a FAT chain from a negative starting sector. 2003-Jul-16: 21H23:PLD:DEV Added bounds check on chain follower/loader so that it would not attempt to seek from a starting sector < 0 2003-Jul-11: 19H53:PLD:DEV Added bounds checking to OLE_dbstosbs() 2003-Jul-05: 01H18:PLD:DEV Added --save-unknown-streams facility, this allows saving of streams which ripOLE doesn't yet know how to explicitly handle. 2003-Jul-04: 20H52:PLD:DEV Posted to Freshmeat Applied small patch to stop compile warnings about bp and buffer pointers being used (possibly) uninitialised. 08H13:PLD:DEV Fixed segfault in code when extended-FAT/BAT/DIF was required. This was caused by the header-conversion fn because it did not stop attempting to copy sectors beyond the first 109. 00H10:PLD:DEV: Fixed all 3 FAT modes, mini, standard and extended. All files now decode assuming they have the 'Ole10Native' element-name 2003-Jul-03: Initial code release, not currently usable to extract files but the code does dump the OLE streams to file, in which you can see the attachment information. ripmime-1.4.0.10.debian.1/ripOLE/pldstr.c0000640000175000017500000005164311667102235015703 0ustar pjbpjb #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. ripmime-1.4.0.10.debian.1/ripOLE/Makefile0000640000175000017500000000047611667102235015665 0ustar pjbpjb OBJS= ole.o olestream-unwrap.o bytedecoders.o logger.o pldstr.o bt-int.o CFLAGS=-Wall -g -O2 -I. -Werror .c.o: $(CC) $(CFLAGS) $(DEFINES) -c $*.c default: ripole clean: rm -f *.o ripole ripole: $(OBJS) ripole.[ch] $(CC) $(CFLAGS) $(OBJS) $(DEFINES) ripole.c -o ripole validate: ripole cp ripole validate ripmime-1.4.0.10.debian.1/ripOLE/bytedecoders.c0000640000175000017500000000745111667102235017045 0ustar pjbpjb#include /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_byte_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_int8( char *start ) { return (int) *start; } /*-----------------------------------------------------------------\ Date Code: : 20081101-014815 Function Name : char Returns Type : unsigned ----Parameter List 1. get_uint8( char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ unsigned int get_uint8( char *start ) { return (unsigned char) *start; } /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_ushort_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_int16( char *start ) { int value = 0; value = (unsigned char)*start | (((unsigned char)*(start +1)) << 8); return value; } /*-----------------------------------------------------------------\ Date Code: : 20081101-014643 Function Name : int Returns Type : unsigned ----Parameter List 1. get_uint16( char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ unsigned int get_uint16( char *start ) { unsigned int value = 0; value = (unsigned char)*start | (((unsigned char)*(start +1)) << 8); return value; } /*-----------------------------------------------------------------\ Function Name : int Returns Type : unsigned ----Parameter List 1. get_ulong_value( unsigned char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int get_int32( char *start ) { int value = 0; value = (int)((unsigned char)*start) |(((unsigned char)*(start +1)) << 8) |(((unsigned char)*(start +2)) << 16) |(((unsigned char)*(start +3)) << 24); // printf("String=0x%x %x %x %x:%u = %d\n", *start, *(start +1), *(start +2), *(start +3), *(start +3), value); return value; } /*-----------------------------------------------------------------\ Date Code: : 20081101-014748 Function Name : int Returns Type : unsigned ----Parameter List 1. get_uint32( char *start , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ unsigned int get_uint32( char *start ) { unsigned int value = 0; value = (unsigned int)((unsigned char)*start) |(((unsigned char)*(start +1)) << 8) |(((unsigned char)*(start +2)) << 16) |(((unsigned char)*(start +3)) << 24); return value; } ripmime-1.4.0.10.debian.1/ripOLE/olestream-unwrap.c0000640000175000017500000003500111667102235017666 0ustar pjbpjb#include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "bytedecoders.h" #include "olestream-unwrap.h" #define DUW if (oleuw->debug) struct OLE10_header{ unsigned char data[6]; char *attach_name; unsigned char data2[8]; char *fname_1; char *fname_2; size_t attach_size; size_t attach_size_1; size_t attach_start_offset; }; struct ESCHER_header_fixed { int spid_max; size_t cidcl; size_t cspsaved; size_t cdgsaved; }; struct typesig { char *sequence; int length; int offset; }; /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_init Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_init( struct OLEUNWRAP_object *oleuw ) { oleuw->debug = 0; oleuw->verbose = 0; oleuw->filename_report_fn = NULL; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_debug Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_debug( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->debug = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_verbose Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_verbose( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->verbose = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_save_unknown_streams Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_save_unknown_streams( struct OLEUNWRAP_object *oleuw, int level ) { oleuw->save_unknown_streams = level; return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_save_stream Returns Type : int ----Parameter List 1. char *fname, 2. char *stream, 3. size_t bytes , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_save_stream( struct OLEUNWRAP_object *oleuw, char *fname, char *decode_path, char *stream, size_t bytes ) { char *full_name; FILE *f; int result = 0; DUW LOGGER_log("%s:%d:OLEUNWRAP_save_stream:DEBUG: fname=%s, decodepath=%s, size=%ld" ,FL ,fname ,decode_path ,bytes ); full_name = PLD_dprintf("%s/%s", decode_path, fname ); if (full_name == NULL) { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:ERROR: Unable to create filename string from '%s' and '%s'",FL,fname,decode_path); return -1; } f = fopen(full_name,"w"); if (f != NULL) { size_t write_count; write_count = fwrite( stream, 1, bytes, f ); if (write_count != bytes) { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:WARNING: Only wrote %d of %d bytes to file %s\n",FL, write_count, bytes, full_name ); } fclose(f); } else { LOGGER_log("%s:%d:OLEUNWRAP_save_stream:ERROR: Unable to open %s for writing (%s)\n",FL,full_name, strerror(errno)); result = -1; } if (full_name) free(full_name); DUW LOGGER_log("%s:%d:OLEUNWRAP_save_stream:DEBUG: Done saving '%s'",FL, fname); return result; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_sanitize_filename Returns Type : int ----Parameter List 1. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_sanitize_filename( char *fname ) { if (fname == NULL) return 0; while (*fname) { if( !isalnum((int)*fname) && (*fname != '.') ) *fname='_'; if( (*fname < ' ')||(*fname > '~') ) *fname='_'; fname++; } return 0; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_seach_for_file_sig Returns Type : int ----Parameter List 1. char *block , ------------------ Exit Codes : Returns the offset from the block to the start of the signature. Returns -1 if not found. Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_seach_for_file_sig( struct OLEUNWRAP_object *oleuw, char *block, size_t block_len ) { int result = -1; int hit = 0; char *p; /** signature pointer **/ char *bp; /** pointer in the block **/ struct typesig sigs[]= { { "\x89\x50\x4e\x47", 4, 0 }, /** PNG **/ { "\xff\xd8\xff", 3, 0 }, /** JPEG **/ { NULL, -1, -1 } /** End of array **/ }; bp = block; block_len -= 4; /** While there's more data in the block and we're not found a match **/ while ((block_len > 0)&&(hit==0)) { struct typesig *tsp; /** Type signature pointer **/ block_len--; tsp = sigs; /** tsp points to the first signature in the array **/ /** While there's more valid signatures in the array **/ while (tsp->length > 0) { int cmpresult = 0; p = tsp->sequence; /** set p to point to the start of the image signature sequence **/ cmpresult = memcmp(bp, p, 3); if (cmpresult == 0) { DUW LOGGER_log("%s:%d:OLEUNWRAP_seach_for_file_sig:DEBUG: Hit at offset %d for signature %d",FL,(bp-block),(tsp -sigs)); hit = 1; break; } /** If we had a match in the signatures **/ tsp++; /** go to the next signature **/ } /** While more signatures **/ if (hit == 0) bp++; /** If we didn't get a hit, move to the next byte in the file **/ } /** while more data in the block **/ if (hit == 1) { result = bp -block; } else { result = -1; } return result; } /** Look for PNG signature **/ /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_decode_attachment Returns Type : int ----Parameter List 1. char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_decode_attachment( struct OLEUNWRAP_object *oleuw, char *stream, size_t stream_size, char *decode_path ) { struct OLE10_header oh; char *sp = stream; char *data_start_point = stream; int result = OLEUW_OK; // Get the data size oh.attach_size_1 = (size_t)get_int32( sp ); sp += 4; DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: attachsize = %d [ 0x%x ], stream length = %d [ 0x%x] \n", FL, oh.attach_size_1, oh.attach_size_1, stream_size, stream_size ); oh.attach_start_offset = (stream_size -oh.attach_size_1); data_start_point = stream +oh.attach_start_offset; //if (oh.attach_start_offset == 4) if (oh.attach_start_offset < 4) { int cbheader; // number of bytes in PIC int mfpmm; int mfpxext; int mfpyext; int mfphmf; // check next 4 bytes. cbheader = get_uint16( sp ); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: cbHeader = %d [ 0x%x ]", FL, cbheader, cbheader); mfpmm = get_uint16( sp +2 ); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: mfp.mm = %d [ 0x%x ]", FL, mfpmm, mfpmm); mfpxext = get_uint16( sp +4 ); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: mfp.xext = %d [ 0x%x ]", FL, mfpxext, mfpxext); mfpyext = get_uint16( sp +8 ); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: mfp.yext = %d [ 0x%x ]", FL, mfpyext, mfpyext); mfphmf = get_uint16( sp +10 ); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: mfp.hmf = %d [ 0x%x ]", FL, mfphmf, mfphmf); // If we only had the stream byte-lenght in our header // then we know we don't have a complex header. DUW { switch (mfpmm) { case 100: LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Image is Escher format",FL); break; case 99: LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Image is Bitmapped",FL); break; case 98: LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Image is TIFF",FL); break; default: LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Unknown image type for code '%d'",FL, mfpmm); } } data_start_point = sp +cbheader -4; if (mfpmm == 100) { int imageoffset = 0; int search_size = 500; DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: searcing for image signatures",FL); if (stream_size < (search_size +68)) search_size = (stream_size -69); /** just make sure we don't over-search the stream **/ imageoffset = OLEUNWRAP_seach_for_file_sig(oleuw, data_start_point, search_size); if (imageoffset >= 0) { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Image data found at offset %d",FL,imageoffset); data_start_point += imageoffset; } else { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Could not detect image signature, dumping whole stream",FL); } } oh.attach_name = PLD_dprintf("image-%ld",oh.attach_size_1); oh.attach_size = oh.attach_size_1; oh.fname_1 = oh.fname_2 = NULL; DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Setting attachment name to '%s', size = %d",FL,oh.attach_name, oh.attach_size); } else { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Decoding file information header",FL); // Unknown memory segment memcpy( oh.data, sp, 2 ); sp += 2; // Full attachment string oh.attach_name = strdup( sp ); sp = sp + strlen(oh.attach_name) +1; // Attachment full path oh.fname_1 = strdup( sp ); sp += strlen(oh.fname_1) +1; // Unknown memory segment memcpy( oh.data2, sp, 8 ); sp = sp +8; // Attachment full path oh.fname_2 = strdup( sp ); sp += strlen(oh.fname_2) +1; oh.attach_size = (size_t)get_uint32( sp ); sp += 4; if (oh.attach_size > stream_size) oh.attach_size = stream_size; data_start_point = sp; } DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Attachment %s:%s:%s size = %d\n",FL, oh.attach_name, oh.fname_1, oh.fname_2, oh.attach_size ); /** 20050119:2053:PLD - Added to sanitize 8-bit filenames **/ /** Sanitize the output filename **/ OLEUNWRAP_sanitize_filename(oh.attach_name); OLEUNWRAP_sanitize_filename(oh.fname_1); OLEUNWRAP_sanitize_filename(oh.fname_2); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Sanitized attachment filenames",FL); result = OLEUNWRAP_save_stream( oleuw, oh.attach_name, decode_path, data_start_point, oh.attach_size ); if (result == OLEUW_OK) { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Calling reporter for the filename",FL); if ((oleuw->verbose > 0)&&(oleuw->filename_report_fn != NULL)) { oleuw->filename_report_fn(oh.attach_name); } // Do call back to reporting function } DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Cleaning up",FL); // Clean up our previously allocated data if (oh.fname_1 != NULL) free(oh.fname_1); if (oh.attach_name != NULL) free(oh.attach_name); if (oh.fname_2 != NULL) free(oh.fname_2); DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: done.",FL); return OLEUW_OK; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_decodestream Returns Type : int ----Parameter List 1. char *element_string, 2. char *stream , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_decodestream( struct OLEUNWRAP_object *oleuw, char *element_string, char *stream, size_t stream_size, char *decode_path ) { int result = OLEUW_OK; if (strstr(element_string, OLEUW_ELEMENT_10NATIVE_STRING) != NULL) { DUW LOGGER_log("%s:%d:OLEUNWRAP_decodestream:DEBUG: Debugging element '%s'",FL, element_string); OLEUNWRAP_decode_attachment( oleuw, stream, stream_size, decode_path ); } else if (strstr(element_string, OLEUW_ELEMENT_DATA) != NULL) { DUW LOGGER_log("%s:%d:OLEUNWRAP_decodestream:DEBUG: Debugging element '%s'",FL, element_string); OLEUNWRAP_decode_attachment( oleuw, stream, stream_size, decode_path ); } else { DUW LOGGER_log("%s:%d:OLEUNWRAP_decode_attachment:DEBUG: Unable to decode stream with element string '%s'\n", FL, element_string); result = OLEUW_STREAM_NOT_DECODED; } return result; } /*-----------------------------------------------------------------\ Function Name : OLEUNWRAP_set_filename_report_fn Returns Type : int ----Parameter List 1. struct OLEUNWRAP_object *oleuw, 2. int (*ptr_to_fn)(char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int OLEUNWRAP_set_filename_report_fn( struct OLEUNWRAP_object *oleuw, int (*ptr_to_fn)(char *) ) { oleuw->filename_report_fn = ptr_to_fn; return 0; } ripmime-1.4.0.10.debian.1/ripOLE/README0000640000175000017500000000066011667102235015100 0ustar pjbpjb--------- 2005-December-12 ripOLE has now started to become a useful tool in its own right, though it is more directly being developed for incorporation into ripMIME and subsequently Xamime. pldaniels@pldaniels.com --------- To check for [and save] attachments in a MS Office file, simply do the following: ./ripole -i officefile.doc -v -d tmp ..and ripOLE will decode the attachments to the 'tmp' directory -END. ripmime-1.4.0.10.debian.1/ripOLE/TODO0000640000175000017500000000005711667102235014710 0ustar pjbpjb ----------------------- Make things work :-) ripmime-1.4.0.10.debian.1/mime.h0000640000175000017500000000760411667102337014176 0ustar pjbpjb #ifndef LIBMIME #define LIBMIME /* MIME.h */ #define LIBMIME_VERSION "200811050000" /* Exit Error codes */ #define _EXITERR_UNDEFINED_BOUNDARY 100 #define _EXITERR_PRINT_QUOTABLE_INPUT_NOT_OPEN 200 #define _EXITERR_PRINT_QUOTABLE_OUTPUT_NOT_OPEN 201 #define _EXITERR_BASE64_OUTPUT_NOT_OPEN 210 #define _EXITERR_BASE64_UNABLE_TO_OUTPUT 211 #define _EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT 220 #define _EXITERR_MIMEREAD_CANNOT_WRITE_OUTPUT 221 #define _EXITERR_MIMEUNPACK_CANNOT_OPEN_INPUT_FILE 230 #define _EXITERR_MIMEUNPACK_CANNOT_OPEN_HEADERS_FILE 231 #define MIME_ERROR_RECURSION_LIMIT_REACHED 240 #define MIME_ERROR_FFGET_EMPTY 241 #define MIME_ERROR_B64_INPUT_STREAM_EOF 242 #define _MIME_RENAME_METHOD_INFIX 1 #define _MIME_RENAME_METHOD_PREFIX 2 #define _MIME_RENAME_METHOD_POSTFIX 3 #define _MIME_STRLEN_MAX 1023 /* Debug levels */ #define _MIME_DEBUG_PEDANTIC 10 #define _MIME_DEBUG_NORMAL 1 /* Filename Encoding characters */ #define MIME_ISO_ENCODING_Q 'Q' #define MIME_ISO_ENCODING_q 'q' #define MIME_ISO_ENCODING_B 'B' #define MIME_ISO_ENCODING_b 'b' #define MIME_BLANKZONE_SAVE_TEXTFILE 4 #define MIME_BLANKZONE_SAVE_FILENAME 8 /* Quoted-Printable decoding modes */ #define MIME_QPMODE_STD 0 #define MIME_QPMODE_ISO 1 /* BASE64 decode status return codes */ #define MIME_BASE64_STATUS_HIT_BOUNDARY 1 #define MIME_BASE64_STATUS_ZERO_FILE 2 #define MIME_BASE64_STATUS_OK 0 /* status return codes */ #define MIME_STATUS_ZERO_FILE 100 int MIME_version( void ); size_t MIME_read_raw( char *src_mpname, char *dest_mpname, size_t rw_buffer_size ); int MIME_read( char *mpname ); /* returns filesize in KB */ int MIME_unpack( char *unpackdir, char *mpname, int current_recusion_level ); //int MIME_unpack_single( char *unpackdir, char *mpname, int current_recusion_level ); //int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recusion_level ); //int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level ); int MIME_insert_Xheader( char *fname, char *xheader ); int MIME_set_blankfileprefix( char *prefix ); int MIME_set_recursion_level(int level); int MIME_set_verbosity( int level ); int MIME_set_verbosity_contenttype( int level ); int MIME_set_verbosity_12x_style( int level ); int MIME_set_verbose_defects( int level ); int MIME_set_report_MIME( int level ); int MIME_set_quiet( int level ); int MIME_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ); int MIME_set_debug( int level ); int MIME_set_dumpheaders( int level ); int MIME_set_headersname( char *fname ); int MIME_set_decode_uudecode( int level ); int MIME_set_decode_tnef( int level ); int MIME_set_decode_ole( int level ); int MIME_set_decode_qp( int level ); int MIME_set_decode_base64( int level ); int MIME_set_decode_doubleCR( int level ); int MIME_set_decode_mht( int level ); int MIME_set_header_longsearch( int level ); int MIME_set_blankzone_save_option( int option ); int MIME_set_blankzone_filename( char *filename ); char *MIME_get_blankzone_filename( void ); int MIME_set_syslogging( int level ); int MIME_set_stderrlogging( int level ); int MIME_set_no_nameless( int level ); int MIME_set_uniquenames( int level ); int MIME_set_renamemethod( int method ); int MIME_set_paranoid( int level ); int MIME_set_mailboxformat( int level ); int MIME_set_webform( int level ); int MIME_get_attachment_count( void ); int MIME_set_name_by_type( int level ); int MIME_set_multiple_filenames( int level ); int MIME_get_header_defect_count( void ); int MIME_set_out_charset( char *charset ); int MIME_is_file_mime( char *fname ); int MIME_is_file_uuenc( char *fname ); char *MIME_get_blankfileprefix( void ); char *MIME_get_headersname( void ); char *MIME_get_subject( void ); int MIME_init( void ); int MIME_close( void ); int MIME_set_tmpdir( char *tmpdir ); //int MIME_postdecode_cleanup( char *unpackdir, struct SS_object *ss ); //int MIME_decode_TNEF( FILE *f, char *unpackdir, struct _header_info *hinfo, int keep ); #endif ripmime-1.4.0.10.debian.1/CHANGELOG0000640000175000017500000017616111667102337014315 0ustar pjbpjbripMIME CHANGELOG Format of entry line: - : CHANGES--------------------------------------------------------------- Monday Dec 12, 2011 -PLD:REL:1.4.0.10 Added iconv output support via -x thanks to КонÑтантин Молчанов Fixed many compile errors that have come up with newer gcc versions Fixed an incorrectly matched size/var pairing in MIME_headers.c thanks to David Binderman, Gary Petersen, Dag Andersen and Tullio Andreatta Fri Nov 08 2008 -PLD:REL:1.4.0.9 Fixed up the BASE64 decoding detection of boundaries which are sometimes missed because of the FFGET buffer limits are reached part way through the boundary line. This is now solved by calling a FFGET_fgets() on the lines which are presumed to include a boundary specifier (eg, line starts with a leading hyphen ). This situation was primarily due to the use of the FFGET_fgetc() call which only looks ahead 1 char, before calling the BS_cmp(). Mon Nov 03 2008 -PLD:REL:1.4.0.8 Cleaned up ripOLE, OpenTNEF and ripMIME compile warnings based on the new more pedantic default settings as with Ubuntu 8.10. Updated ripOLE to handle nameless / unknown stream blobs, now produces OLE_data.xxx files in place of the random names being generated previously. Sun Sep 21 2008 -PLD:DEV Adjusted tnef/tnef.c to ensure consistent filename generation between multiple passes of a decode within the same process (see TNEF changelog) Fri Feb 08 2008 -PLD:DEV Added QP decoding patch from jjohnston Added invalid char detection Tue Oct 30 2007 - PLD:DEV Added small patch from Claudio Jeker to fix a potential overflow in MIME_headers.c Wed Oct 24 2007 - PLD:DEV Added MIME_read_raw which uses open/read/write calls rather than fopen/fread/fwrite. This isn't yet used directly by ripMIME but rather is needed for its parent development tree for Xamime. Sun Jul 08 2007 - PLD:DEV Fixed FFGET_fgets call boundary condition fault that can cause excess bytes to be consumed resulting in either smaller output files or potential segfaults. This was an incredibly difficult bug to track down as it simply refused to show up in regular testing and when it did finally show up it was still very confusing in its behavior due to it being a combination of two bugs in the same function. Fri Mar 03 2006 - PLD:DEV:2211 Added a header fixing routine in MIME_headers, this fix attempts to unwrap headers which are missing a leading space on the next line (original mailpack provided by Ruust - thanks) This feature can be disabled using --disable-headerfix Fri Feb 24 2006 - PLD:DEV:1748 Fixed situation where a null-body email would result in the _header_ file not being produced even when explicitly asked to via the '-e' option Mon Dec 12 2005 - PLD:REL:1235 RELEASE 1.4.0.6 Updated ripOLE to version 0.2.0 Wed Mar 30 2005 - PLD:REL:0644 RELEASE 1.4.0.5 Fixed -d -i - combination error causing ripMIME to fail to decode Thanks to Daniel Fraga for noticing this. Tue Mar 29 2005 - PLD:REL:1703 RELEASE 1.4.0.4 Tue Mar 15 2005 - PLD:DEV:1545 OLE2 decoding fix (see ripOLE) Thu Feb 03 2005 - PLD:DEV:1950 Added recursion facility if the input mailpack/MIME file is a directory. Fri Dec 17 2004 - Release 1.4.0.3 - Improved attachment search abilities due to previous cleanups - Cleaned up excessively long header-search times - Cleaned up excess unwanted textfile outputs generated from the qmail-bounce searching. A reduction of ~25% has been attained. - Handled Exim bounced email attachments - TNEF segfault issues cleaned up (see TNEF's CHANGELOG) Tue Dec 07 2004 - PLD:DEV:0102 Fixed up Apple double content disposition INLINE issue which could cause ripMIME to try decoding a mailpack for a VERY long time. Thanks to Nicolas MacPherson for the offending mailpack. Wed Dec 01 2004 - PLD:DEV:2330 Fixed QP decoding error where trailing TABs and spaces were not culled. Mon Nov 29 2004 - PLD:DEV:1106 Fixed possible filename buffer overrun in MIME_decode_encoding() function where sprintf() was being used to generate the filenames rather than the correct snprintf() call. Sat Nov 27 2004 - PLD:DEV:2049 Added OLE2 decoding sanity fix (see ripOLE CHANGELOG) Fri Nov 26 2004 - PLD:REL:2238 Release as 1.4.0.2 - PLD:DEV:2028 Fixed up ISO decode issues in the headers causing premature truncation. Sat Nov 06 2004 - PLD:DEV:10H27 Fixed up numerous unsigned/signed comparison warnings. Fixed up TNEF read_32/_16 functions to allow for error condition returns. Fri Nov 05 2004 - PLD:DEV:22H57 Applied Stepan Kasal patches for MIME_decode_b64() 4-char array initization and for the boundary_crash test continue if statement. Wed Nov 03 2004 - PLD:DEV:09H48 Fixed compiler warning about MIMEH_save_doubleCR "comparison is always false". Tue Nov 02 2004 - PLD:DEV:10H41 Added handling for x-mac filename forward-slash separators. Sat Oct 23 2004 - PLD:REL:17H14 Release as 1.4.0.1 - PLD:DEV:16H52 Applied patch from Andreas Jobs to fix up situations where the multiple-filenames may not all be correctly hardlinked due to modifications being made via the FNFILTER_filter() calls. Many thanks to Andreas for picking up on this. Mon Oct 18 2004 - PLD:DEV:23H08 Updated ripOLE code to prevent segfault caused by negative sector values in OLE_follow_minichain() Sun Sep 26 2004 - PLD:DEV:17H27 Fixed UUENCODING detection in MIME_headers which would sometimes incorrectly choose UU rather than Quoted Printable. Thanks to Stepan Kasal for pointing this out. Sat Sep 25 2004 - PLD:DEV:21H24 Fixed memory leak where the header-info structure's string-stacks file filenames and names were not being flushed after use. Fixed accidental flagging of bad headers when a header set contained the same filename in both the content-type and content-disposition headers. Sat Sep 11 2004 - PLD:DEV:23H22 Fixed an endless loop in ole.c when dealing with zero length MINIfat reads. Tue Sep 07 2004 - PLD:DEV:21H10 Fixed UUENCODED decoding when the UUE filename is specified in the headers (see uuencode.c) Sun Sep 05 2004 - PLD:DEV:09H30 Fixed OLE_version() inclusion in MIME_version() when RIPOLE wasn't defined. Thanks to Tomasz Klim for noting this. Sat Sep 04 2004 - PLD:DEV:10H47 Fixed incorrect return-code propagation from lower recursion levels, causing ripMIME to prematurely terminate some decoding, thus not seeing some attachments. Thanks to Fred Van Andel for supplying the offending mailpack. Fri Sep 03 2004 - PLD:DEV:09H57 Fixed empty-boundary handling (Mailpack sent by Ian Samuel) Tue Aug 31 2004 - PLD:DEV:22H18 Fixed up a segfault which could occur while running with --debug on some emails (See bug #32) Mon Aug 30 2004 - PLD:DEV:20H21 Fixed up 'make solib' option (Thanks to Oden Eriksson for pointing out that it was broken) Tue Aug 17 2004 - PLD:REL:16H36 Released as 1.4.0.0 Mon Aug 16 2004 - PLD:DEV:13H43 Added strstack objects into the MIME headers struct (hinfo) to track multiple filenames exploits. When the filename has been saved to the disk, the additional filenames are then hardlinked (this prevents excessive disk consumption as hardlinks don't really take up space). Sun Aug 15 2004 - PLD:DEV:22H21 Added '--verbose-defects' which will dump all the header defects detected during the scanning of the MIME segment Decoding filename=attachment.exe Header Defect: Missing separators: 1 The number trailing the defect description is the number of occurances within the headers that the defect occurred. Detected defects won't stop the ripMIME decoding by default, instead ripMIME will step around the defects to the best of its abilities and flag the defect register in the hinfo defects[] array. Wed Aug 11 2004 - PLD:DEV:23H42 Added a new array 'defects' into the hinfo structure in MIME_headers.h. The purpose of this array is to track all the defects/mistakes in the MIME headers, allowing the calling program (of the MIME lib) to make a decision on the mailpack (ie, should defective mailpacks be allowed to pass through). More details will be added over the next few days. This new feature isn't quite ready yet. - PLD:DEV:23H22 Fixed handling of multiple sequential quotes on filenames and other parameters, ie; name=""attachment.exe"" - PLD:DEV:23H10 Added boundary pushing to the stack within MIME_headers in order to catch out situations where multiple boundaries are defined in the headers. - PLD:DEV:22H21 Changed ISO decoding routine to _not_ convert underscores to spaces. This is so that ISO encoded items such as boundary definitions aren't turned into non-valid items, ie __BOUNDARY__ becomes ' BOUNDARY '. Wed Aug 11 2004 - PLD:DEV:18H33 Added correct handling of RFC2231 encoded strings; such as: title*0*=us-ascii'en'This%20is%20even%20more%20 title*1*=%2A%2A%2Afun%2A%2A%2A%20 title*2="isn't it!" - PLD:DEV:17H10 Fixed up handling of multiple atom separators (=,: and ;) - PLD:DEV:12H57 Added RFC822 comment stripping from headers (while being processed in the MIME_headers module). The original headers will be preserved verbatim in _headers_ file. Mon Aug 09 2004 - PLD:DEV:14H48 Added --timeout feature which allows you to set a CPU seconds timeout. If the alarm goes off, ripMIME will terminate with an error code of RIPMIME_ERROR_TIMEOUT (5) and leave a log entry (if required). This feature is useful to prevent server-load runaway if ripMIME has a problem decoding a particular email - PLD:DEV:14H11 Added --formdata option to allow the decoding of HTML form data which often contains embedded \0 sequences which cannot be accurately handled by normal email-MIME decoding routines (yet) Fri Jul 30 2004 - PLD:REL:21H06 !!!!URGENT RELEASE!!!! Released 1.3.2.3 There's viruses going around exploiting the ability to hide the majority of their data in an attachment by using blank lines and other tricks to make scanning systems prematurely terminate their base64 decoding. This release fixes that by ignoring consecutive CR checks in the BASE64 data and rather attempting to treat it as a stream of data with arbitary invalid bytes in it. Thu Jul 29 2004 - PLD:DEV:23H11 Fixed infinite loop with OLE decoding in ripOLE, mailpack supplied by Dallas L. Engelken. The decoding is not correctly handled at this point (no sensible data can be extracted from the .doc file) but at least ripMIME/ripOLE will not infinately loop. Tue Jul 27 2004 - PLD:DEV:12H55 Applied filename paranoia patch provided by Marco Ariano Mon Jul 19 2004 - PLD:REL:12H04 Released 1.3.2.2 Sat Jul 17 2004 - PLD:DEV:14H22 Silenced unwanted verbosity from recursion bounds breach report in mime.c:2674 Silenced unwanted verbosity from tnef decoder about invalid signature when in non-verbose mode. Silenced unwanted verbosity from ripOLE (see ripOLE CHANGELOG) Sat Jul 03 2004 - PLD:DEV:14H18 libmime-decoders.c:MDECODE_decode_ISO() Fixed situation where ISO encoded string without a language page specification would cause the rest of the headers to be truncated from that point; typical example is: Subject: =??Q?Radiant=20Rubies=20Just=20for=20You.=20SAVE=20$$$'S.?= note the =?? rather than say =?iso-9352 etc Made the ISO decoder complain about a dud encoding type character if 'verbosity' is on, else, it doesn't report the error (this ensures that things like qmail etc don't get upset) Mon Jun 28 2004 - Release as 1.3.2.0 Sun Jun 27 2004 - PLD:DEV:15H31 Fixed duplication of 'Decoding Filename=' reporting when decoding a uuencoded MIME segment with the filename defined in the headers. - PLD:DEV:14H09 Added '--extended-errors' option to allow the turning on of non-fatal errors. Corrected various return-code issues #define RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR 1 #define RIPMIME_ERROR_CANT_OPEN_INPUT_FILE 2 #define RIPMIME_ERROR_NO_INPUT_FILE 3 #define RIPMIME_ERROR_INSUFFICIENT_PARAMETERS 4 - PLD:DEV:12H15 Fixed UUdecode situation where the MIME segment defines the filename in the headers, rather than the more common case of having a "BEGIN 666 filename" content. Thanks to Dallas L. Engelken for detecting this and supplying a suitable mailpack. Fri May 21 2004 - PLD:DEV:13H19 Fixed up boundary stack BS_cmp() size compairson check by creating a new function BS_is_long_enough() which takes the length of the line being tested and compares it to the current 'smallest permissable' boundary. Fixed special case boundary situation where the boundary is defined only as '-'. Thu May 13 2004 - PLD:REL:14H07 Release 1.3.1.2 - PLD:DEV:00H15 Due to the MIME headers alteration, the ISO decoding for filenames started having issues because of gaps in the decoded ISO string. This has now been fixed. Wed May 12 2004 - PLD:DEV:21H57 MIME_headers.c:1295; added \t as another possible header line delimiter (along with \n \r and ;) - PLD:DEV:21H55 Corrected the header unwrapping according to RFC2822 specifications. See comments in MIME_headers.c ~ line 886 Mon Apr 12 2004 - PLD:DEV:21H12 Fixed up some compile errors on Solaris 9 (char indexes) Mon Apr 5 2004 - PLD:REL:19H38 Released as 1.3.1.1 - PLD:DEV:11H28 Fixed TNEF LOGGER call with missing paramter Fixed possible underrun in the MIMEH_read_headers() call when trimming off trailing \r and \n chars from the read header line. Fixed some LOGGING lines which were not correctly setup with the MIMEH_DEBUG test (resulting in spurilous debugging info when it wasn't desired) Added 'smallest stored boundary' (glb.smallest_length) to the boundary-stack module so that it could quickly eliminate a lot of boundary tests based purely on the size of the input test line. This also eliminated the problem that would sometimes appear in valgrind where an error in BS_cmp() with the strcmp() call would arise. The smallest_length variable can only decriment. Theoretically it could be reduced to a point where it's value is '1' but it won't affect the selectivity of the algorithm. The worst case scenario is that smallest_length is set to 1 in which case the algorithm will behave as before the change in the code was made. - PLD:DEV:01H59 Added header subject initializer into MIME_headers_get() to stop valgrind complaning on certain emails where the subject would not be located in the headers. Added more stringent testing of the subject copy-over process in MIME_unpack_stage2 after the headers get call. Sat Apr 4 2004 - PLD:DEV:23H54 Corrected a segfault when running debug mode and mime.c:2623 logger line was called (didn't have a ,FL at the end) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_plain:DEBUG: Handling plain email",FL); Explicitly called OLE_decode_file_done() after the OLE decode process because sometimes files weren't being closed due to a non-OLE file being opened (hence closing premature of the internal call of _file_done(). It's okay to call this function multiple times as it checks all the variables to ensure that they're not NULL before trying to clean them up. Thu Mar 11 2004 - PLD:DEV:12H44 Updated man page for 1.3.1.x Wed Mar 10 2004 - PLD:REL:23H59 Re-released 1.3.1.0 - PLD:DEV:23H59 Switched all calls (interal) to MIME_unpack() to MIME_unpack_single(), especially for the mailbox handling routine - which would cause the whole system to loop and fail. - PLD:REL:08H31 Corrected generate-buildcodes.sh where the date stamp might not return a valid integer. Tue Mar 9 2004 - PLD:REL:01H15 Release 1.3.1.0 Set --qmail-bounce option to be on by default (appears to be quite stable) and added --disable-qmail-bounce to turn it off instead. Sun Mar 07 2004 - PLD:DEV:15H30 Dev release. - PLD:DEV:13H20 Altered UUEncode's error reporting system. Now when a function returns -1, the calling program should check the global uuencode_error to see what the error is. This system is similar to those used with a lot of standard functions like fopen(). The error codes are listed in uuencode.h We needed to do this so that we could return positive-integer error codes from UUENCODE_decode_uu(), because normally this function will return a positive integer representing the number of files it decoded from the supplied file. - PLD:DEV:12H50 Corrected mistake with the recursion level passing in MIME_unpack_single_fp() which was passing '0' value to MIME_unpack_stage2(), causing ripMIME to fail with a segfault when very deeply recursed emails were encountered. NOTE this segfault occurs because of running out of stackspace, not because ripMIME specifically is broken. ripMIME seems to be able to recurse to about 160 levels deep on the test machine before stack failure. - PLD:DEV: Set MIME_set_header_longsearch() to only take effect if we're calling a plain-text file, ie, being handled by MIME_handle_plain() Sat Mar 06 2004 - PLD:DEV: Unbelievable amount of restructuring required so I could get 2 mailpacks with seperate problems to work. Improved the discrimination and speed of the BS_cmp() operation by first taking a count of all the numeric+alpha chars in the boundary and then comparing against the existing boundaries. This helps speed things up because there's no longer a string comparison per test if there's not even going to be a chance of a match. Also, the discrimination is improved because I had an incident where two boundaries existed, with one being a substring of the other, this was passing through ripMIME with the previous 1.3-dev release for some reason. A lot of code was removed from the MIME_unpack_stage2() call and placed rather into seperate function calls. The new function calls are; MIME_handle_multipart() MIME_handle_rfc822() MIME_handle_plain() No doubt more of these will be added over time as I work my way through the old code. Fri Mar 05 2004 - PLD:DEV: Added a stack system for storing the filenames used during any given MIME_unpack() call, this means that when the MIME_unpack() call finishes, it doesn't clobber the textfile* files which might have actually been owned by a parent process (as MIME_unpack() is recursive in some situations). This addition once more beefs up the size of ripMIME, but is required in order to deal with the ever increasing complexity of emails which are passing through our systems. The new files are strstack.c strstack.h These two new files are actually adapted/stripped-down versions of the boundarystack files. - PLD:DEV: Added a new script to the build process to generate a file called 'buildcodes.h'; this file contains the date in UNIX timestamp and human-readable date, along with the output from 'uname -a' This has been added to assist mostly in local build checking, but also may serve as a good indicator on other machines to see if a ripMIME version is absolutely current. Mon Mar 01 2004 - PLD:DEV: Corrected misspelling of 'QuotePrintable' to 'Quoted-Printable' - PLD:DEV: Updated ripOLE licence to match the ripMIME licence. - PLD:DEV: Altered the UUDECODING routine to not complain about lack of permissions on a line which starts with begin but is perhaps from a plain-text file (ie, not UUENCODED). Suggestion given by Daniel Fraga. - PLD:DEV: Removed --syslog_on and --stderr_off paramters from the help dump (thanks to Mat Williams for noticing this). These paramters have been deprecated for quite a long time. Mon Feb 16 2004 - PLD:DEV: Added ripmime.1 man file Sun Feb 08 2004 - PLD:DEV: Adding facility to pull out/decode qmail bounced emails - this is against RFC compliance, but people are wanting this badly so I suppose, once again, Microsoft wins (because Outlook seems to gleefully find the attachments when they should in fact be displayed as verbatim - not decoded). use the --qmail-bounce parameter to activate this 'feature'. - PLD:DEV: Rolled in a patch by James Stevens to make ripMIME usable for his AV milter tool. Also included a patch to (for now) split the MIME_unpack_single() into _single_fp() and _single(), paving the way for future file-handle based processing rather than FILE * - PLD:DEV: Added recursion-maximum level paramter (--recursion-max) as suggested and patch-supplied by James Stevens (some alterations made to the patch) Wed Jan 14 2004 - PLD:DEV: Added RCF822 content checking for files generated by the text dump from an UNSPECIFIED encoding/type format. This allows us to pick out mailpack contents even when there's a set of false headers prior to the real body content (as depicted by the samples provided by Farit - many thanks ). Thu Dec 18 2003 - PLD:DEV: Updated licence file to correctly indicate the true nature of this project Sun Dec 07 2003 - PLD:DEV: Completed the 'quiet' functionality - PLD:DEV: Fixed up decoding of multiple segment filenames in headers, ie; filename*1="foo"; filename*2="bar."; filename*3="exe" - PLD:DEV: Corrected handling of */octet content type storage, opting to use RAW decoding rather than plain text. Sat Nov 14 2003 - PLD:REL: Released ripMIME v1.3.0.5 Fri Oct 30 2003 - PLD:DEV: Fixed up decoding issue in stage2 where ripMIME would try to decode a zero-byte file produced from the BASE64 decoder routine. Fri Oct 23 2003 - PLD:DEV: Added integration of the -p flag with the --name-by-type flag. The prefix will only appear on the type-based named attachments if it's explicitly requested (ie, it will not appear if you don't use the -p flag, even though there is a default prefix ). Thu Oct 23 2003 - PLD:DEV: Added --name-by-type feature (as requested by Marcel Manz) to create the filename of an attachment if it has no filename= or name= header based on its content-type Fri Oct 10 2003 - PLD:DEV: Moved house - completely changed everything in my life. Haven't changed much in ripMIME. Fixed some Makefile issues and incorrectly included .o files, as reported by Paul Theodoropoulos (thanks!) Wed Sep 10 2003 - PLD:DEV: Rolled in changes from valgrind process. Fixes several potenial segfault situations, especially with ripOLE. Fri Aug 29 2003 - PLD:DEV: Fixed mailbox decoding from STDIN Thu Aug 28 2003 - PLD:DEV: Changed the mime-headers output path setting to be set from within the MIME_unpack() call, rather than allowing it to be set externally Prior to this change, doubleCR files were being saved in places like the root directory and other unsavory locations. ripOLE library updated - segfault fixed when reading miniFAT with a negative start sector value Sun Aug 24 2003 - PLD:DEV: Fixed up situations where an embedded email with an attachment filename (As apposed to just raw embedded with RFC822 content-type) could not be decoded (would be passed over and ignored). Thanks to Ricardo Kleemann for supplying a sample mailpack. On fixing up for this one mailpack, my test suite jumped from decoding 1092 attachments to 1193 attachments. Wed Jul 16 2003 - PLD:DEV: LOGGER % filtering solution fixed - PLD:DEV: Bundled in the ripOLE engine into ripMIME, this means that ripMIME can now extract files which have been included in an MS Office file. Currently ripOLE cannot extract attachments which have been embedded directly into the WordDocument stream data however (this is usually Images and some sound files). However it does successfully get most/any files which were included using Insert->File Added --no-ole option to make ripMIME not decode OLE files. Mon Jun 30 2003 - PLD:DEV: Added libripmime build ability, allowing developers to use #include "ripmime-api.h" and then link with libripmime. Sun Jun 22 2003 - PLD:DEV: Fixed QuotedPrintable end-of-string decoding 1-off char error which would cause ripMIME to complain that it ran out of data because it was comparing "if (ip < slen -2)" when it should have been comparing to slen -1 (because the '<' implies a -1 by default ) Thanks to Paul Theodoropoulos for his submissions of mailpacks which have greatly assisted in locating these obscure errors. Sat Jun 21 2003 - PLD:DEV: Fixed UUdecode false-positive hit in UUDECODE_is_uuencode_header() (thanks to Bernard Fischer once more for supplying a mailpack which demonstrated the fault and for providing a patch ) - PLD:DEV: Added an explicit RFC822 decoding routine into MIME_unpack_stage2() because a supplied mailpack which had multiple documents, all in individually wrapped RFC822 attachments caused ripMIME to incorrectly interpret the segment, till the end of the mailpack as the entire RFC822 message ( consequently, the recusion limit would eventually kick in and exit ripMIME ). By placing an explicit detection into _unpack_stage2(), the problem has been eliminated without affecting the decoding accuracy of other mailpacks. Wed Jun 18 2003 - PLD:DEV: Relaxed the base64 decoding standard to allow for a possible single blank line within the B64 stream. This has been needed because there are some rare mailpacks produced by [obviously] broken agents which have spurilous blank lines amongs the data-stream ( but are terminated by boundaries ) I feel this is a really ugly way to deal with this problem - people should really make their B64 encoders conform to realistic standards rather than hoping that all the decoders will pick this up accurately I would have left out the CR sensitivity entirely - except that there is another example mailpack I have where [incorrectly] there is plain text after the B64 stream has ended ( incorrectly inserted disclaimer no doubt ). I may revoke this relaxation if it can be found that the 'other' example mailpack is in fact interpreted [wrongly] by all other MUA's. Update - both mailpacks are correctly decoded by Sylpheed :-\ Sun Jun 15 2003 - PLD:REL: Released 1.3.0.4 - PLD:DEV: Corrected UUdecoder path issues, causing UUencoded attachments to sometimes be missed from decoding Sat Jun 07 2003 - PLD: Corrected misinterpretation of the return result from uuencoded_decode where mime.c was propergating the return result into the _decode_stage2() calls, causing processing of the mailpack to prematurely halt. Thanks to Jiri Kucik for submitting the offending mailpack. Fri May 09 2003 - PLD: Attempting to clean up the handling of % symbols in filenames and their output via the LOGGER_log() call. Normally they used to be converted into %% pairs so that the final printf type call would not try and interpret the % as a positional identifier. ---still in work. Tue May 05 2003 - PLD: Added NULL test for filename reporting callback in mime.c Without this NULL test, other applications using the mime module could fail if they did not have the callback function set to their own. Sat May 03 2003 - PLD: Added Content-Transfer-Encoding 'binary' to ripMIME, which causes ripMIME to interpret the data the same as 'raw'. Fri May 02 2003 - PLD: Added --no-mht switch, disables the decoding of MHT files (A microsoft 'combined' WWW-page-in-email sort of format) Wed Apr 30 2003 - PLD: X-men 2 at theaters - PLD: Added option-usage for --no-tnef - PLD: Enhanced pldstr module to provide string search/replace functionality and case-insensitive strstr Sun Apr 27 2003 - PLD: Pushed global variables into singular global struct for boundary-stack module - PLD: Optimised the boundary-stack BS_cmp() call so that it will only do strcmp calls (rather than strstr()) for the first N characters, where N is defined by BS_BOUNDARY_DETECT_LIMIT_DEFAULT and consequently, BS_set_boundary_detect_limit(). Currently the default is set to 4 characters, I do not know of any mailpacks out there which are using more than the 'defacto' '--' prefix to the boundary to seperate MIME sections - PLD: Added extra parameter to the BS node, to store the length of the boundary, saves processing time later when comparing boundaries in BS_cmp() - PLD: Corrected DoubleCR decoding issue in UUENCODE_decode_uu() - PLD: Fixed ranging error in filename renaming method selector (MIME_set_renamemethod) - PLD: Activated the --no-quotedprintable option. This will stop ripMIME from attempting to decode/decypher attachments which have been QuotedPrintable encoded (some forwarded emails are encoded like this ) - PLD: Added a small cleanup function for after BASE64 decoding. Normally when BASE64 decoding finishes there's often a single line left before the next boundary, this typically has been written out as a textfileXX file. The additional function absorbs this data [and discards]. This also should improve [ a little ] the performance as ripMIME will not have to go through the entire header-reading/parsing/ decoding process each time. Sat Apr 26 2003 - PLD: Dropped a lot of old code which was residing in MIME_headers for the sake of 'rollback' in the event that the new header parsing code didn't work. Sat Apr 19 2002 - PLD: Added fixes for VCARD/UUENCODE detections thanks to Bernard Fischer. Sat Mar 29 2003 - PLD: Added better handling of the apple-double encoding, now when and attachment is decoded, it'll be decoded into 3 parts (overall) 1. mac-FILENAME : This usually contains nothing and can be ignored. The file is saved in the event [for what ever reason] that data or text was placed at the start of the apple-double attachment pair [ Similar in a way to the potential presence of data at the beginning of a MIME encoded email, which usually says "This email is encoded with MIME". 2. FILENAME.applemeta : This is the metadata associated with the original attachment 3. FILENAME : This is the actual file/attachment Fri Mar 28 2003 - PLD: Release - PLD: Replaced header parsing code with one which follows the 'standards' compliant structures ( while still looking out for various exploits ). This new parsing code will open the way for better information passing [back] to programs using the ripMIME API. Also, the new header code improves the false-positive rate with filenames. - PLD: More intelligent support for the Macintosh Applefile format, which uses a two 'file' approach for sending files ( meta data file and data file ). Now, any mac files will have their applefile-metadata stored as 'applefile_*'. Although this is not yet perfect, it at least ensures that the real data file is correctly saved with the right name. Thu Mar 20 2003 - PLD: More typos in the LOGGER_log() calls fixed up - PLD: Removed the functions MIME_set_nouudecode() and UUENCODE_set_nodecode() because they were 'confusing' due to inverted logic, additionally it brings the functions inline with all the other modules which use _set_decode() to [en/dis]able the decoding process. Wed Mar 19 2003 - PLD: Corrected several search-replace messups from original conversion to running everything under LOGGER. - PLD: Added facility to report filename decodings from within the various decoding modules (TNEF and UUENCODE at this point) so that the reporting function can be defined from the calling application. If no callback is provided, the modules will default back to a very basic 1.2.x format. Mon Mar 17 2003 - PLD: Emergency release of 1.3.0.2 - PLD: Fixed major bug in the MIMEH_headers_read() call which would cause the first line of the headers to be overwritten by following lines ( in memory ) - Ultimate effect of this is that several files would not be decoded because the vital file information would be lost. - PLD: Unified logging output across all modules - PLD: Fixed space retention on header-wrapped filenames ( again thanks to Kees Damen for providing a suitable mailpack ) Fri Mar 14 2003 - PLD: Fixed linewrap filename issues in headers ( thanks to Kees Damen for pointing this issue out ). - PLD: Fixed 0 length string issue with PLD_strncpy and replaced existing algorithm with a more robust one as provide by 'Defrost' - PLD: Rolled in unified pldstr module which replaces strlower, XAM_strtok and zstr. Sat Mar 01 2003 - PLD: Patched parameter parsing code to allow it to accept non-space seperated parameters, ie, -d/tmp ( Patch supplied by Bernard Fischer ) Sat Feb 22 2003 - PLD: TNEF: Corrected pointer clobbering on 64-bit systems in TNEF decoding, and added memory allocation free'ing. Both patches submitted by Bernard Fischer and adapted by PLD Thu Feb 19 2003 - PLD: Fixed content-type 'trailing' issue causing ripMIME to report the previous content-type if the new attachment headers did not contain a content-type. ( Mailpack contributed by Bernard Fischer ) Wed Feb 19 2003 - PLD: Converted command-line options to use hyphens '-' rather than underscores '_' for word seperators. NOTE, the old underscore seperators will still work but they are considered depricated. - PLD: Added multiple additional verbosity options: --verbose-oldstyle : Output information in the format used from the 1.2.x series of ripMIME --verbose-contenttype : Output content-type information of each file decoded ( Contributed by Thomas E Dell ) - PLD: Introduced a new verbose information format for file details. The new format uses name=value pairings for the output, this allows parsers to easially decode the output into useful data if need be. An example of the output is now: Decoding content-type=text/html filename=foobar.html Currently only 'content-type' and 'filename' are reported but more options will most likely be added in the future. - PLD: Commenced v1.3-dev development. Fri Feb 07 2003 - PLD: Released as 1.2.17.0 ( This is the last 1.2.x release, we will now move to 1.3-dev and 1.4-stable releases Thu Feb 06 2003 - PLD: Corrected testing conditions of the name= substring to test for both a space OR a ';' (semicolon) in order to accept the value after the 'name=' as being a filename. (Thanks to Marc Bleuwart for the supplied mailpack exhibiting this issue ) Wed Feb 05 2003 - PLD: Added total BASE64 decode line count output for failed decoding operations when in --debug mode Sat Feb 01 2003 - PLD: Corrected some *_init() calls and *_set_verbose() as well as *_set_debug(). - PLD: Broke down the mime.c file into some smaller modules: boundary-stack.[ch] - Handles the boundary string ordering for the mailpack decoding. filename-filter.[ch] - Performs basic filename sanity / paranoia checking. uuencode.[ch] - A bit of a misnomer, this module currently deals with the detection and decoding of uuencoded data. - PLD: Merged 3 string handling modules into one. While this seems to go against the flow against the move to modularise the mime.c file it is better to have the files XAM_strtok, zstr and strlower all merged as they are all part of the same family of strings handlers. - PLD: Moved ISO decoding to directly after all the headers have been read and a "true blank" has been received. This way we do not have to worry about decoding the individual components of the headers, rather they are presented in an already decoded form when the calling program gets back the header pointer. Fri Jan 31 2003 - PLD: Reordered the MIME_decode_filename() function so that it attempts to decode ISO filenames -before- any paranoia filters are applied. This is because certain BASE64 characters ( ie / ) are interepreted as paranoid invoking characters, thus are stripped from the filename before ISO decode gets to use them. Thanks to Michal Perchel for supplying the mailpack exhibiting this property. Tue Jan 28 2003 - PLD: Replaced MIME_read() fgetc/fputc calls with a fread/fwrite binary functions to prevent ripMIME from terminating its file/stdin reading too soon if it received an EOF character. Mon Jan 27 2003 - PLD: Corrected segfault in parameter parsing if the last parameter was a -d, -i or -p. Whole parameter parsing block moved to its own function. - PLD: Replaced scandir() calls in mime.c ( for post-decode cleanup ) with opendir()/readdir() - Solaris ( and possibly other OS's ) have different implementations of scandir() compared to Linux/BSD - PLD: Silenced several messages in MIME_headers and MIME tnef decoding which were appearing despite having -v selected Sun Jan 26 2003 - PLD: in MIME_decode_filename(), removed limitation of filenames only having a space character and lengths greater than two (2). - PLD: Corrected header unfolding in MIMEH_read_headers() This rectifies the situation where some filenames are cropped because they were spread over two lines in the headers. - PLD: Moved ISO decoding functions and QuotedPrintable functions into a new module called 'libmime-decoders'. - PLD: Initialised encoding_type in the ISO decoding routines in order to silence some compilers about a 'usage unitilised' warning. - PLD: Corrected situation where an attempt to extract a file name would be misled due to a substring 'name' appearing anywhere within headers. Narrowed down the conditions required in order for a 'name' word to appear. This corrects the issue as reported by Chris Hine - PLD: I've sorted out the Quoted-Printable decoding issues. Who would have thought such a relatively simple decoding process could cause so much pain. As a consequence some of the prior 'patches' have now been removed as they are no longer needed. ( removed patches: Mirko Buffoni ) Sat Jan 25 2003 - Planning on releasing as 1.2.17.0 Fri Jan 24 2003 - Created a new function to handled decoding of ISO encoded strings, now called MIME_decode_ISO(). This function is used by MIME_decode_filename() and will also be used by any programs wanting to decode other ISO encoded strings such as email addresses and such. - Removed MIME_init_hexconv() call ( which previously setup the BASE64 conversion matrix - but we now have a compile-time static array setup for that ) - Renamed 'quick_clean_filename()' to be MIME_filename_paranoid_filter() which is called from within MIME_decode_filename() - Renamed MIME_clean_MIME_filename() to MIME_decode_filename() as its primary function is mostly to handle ISO decoding as well as path prefix stripping. - Deprecated --no_paranoid and rather added in --paranoid. Setting --paranoid on will result in any high-ascii characters to be set to _'s - Changed void return value of quick_clean_filename to 'int' - Added testing of '=' char prior to a ? for testing presence of an ISO encoded filename in MIME_clean_MIME_filename() - Added functional handling of ISO encoded filenames in headers ( see MIME_clean_MIME_filename() ). Still need to merge the functions of MIME_clean_MIME_filename() and quick_clean_filename() Wed Jan 22 2003 - Corrected functioning of --no_nameless operations. Tue Jan 21 2003 - Added trailing \n break after decoding a QuotedPrintable line (submitted by Mirko Buffoni ) Mon Dec 16 2002 - Corrected soft-break decoding of Quoted-Printable encoded data Tue Nov 12 2002 - Released as 1.2.16.21 - Updated FFGET module to 1.0.0.3 release - fixes garbage-end problem with some BASE64 decodes which don't terminate their data with a \n[\r] Sat Nov 09 2002 - NOT-RELEASED (internal use only, see 1.2.16.21) 1.2.16.20 - Changed the strlower() function to use unsigned char rather than signed char, this allows for non-ASCII conversions on the tolower() call without breaking (ie on non-english character sets) Wed Nov 06 2002 - Released as 1.2.16.19 - Added a patch from Phil Brutsche to deal with space-embedded filename, name MIME fields. Fri Oct 11 2002 - Corrected segfault when a filename with a \" ending was encountered - Cropped filenames with ? in them which are NOT ISO encoded to only include the part before the ? Thu Oct 10 2002 - Updated the logger file which contained a segfault issue when sending a string containing a % symbol to syslog. - Wed Jun 26 2002 - Added MIME-headers save-file closing function. This prevents a segfault when MIME_unpack() is called multiple times within the same program. Fri May 31 2002 - Release as 1.2.16.16 - Fixed same issue for Double-CR saved files - Failure to prefix the unpackdir onto the recursive decoding of mailpacks extracted from within mailpacks (ie, EML files) caused extractions to directories other than the present-working-dir to fail [partially] due to not being able to locate the newly created / extrated mailpack. That teaches me to 'ignore' the warnings that were popping up in my syslogs about 'Cannot locate file'. Sun May 26 2002 - Release as 1.2.16.15 - bug causing ripMIME to enter into an infinite loop if the boundary contained the phrase "boundary=" within it. Thanks to Steffen Clausjuergens for supplying the mailpack exhibiting this defect! Sat May 25 2002 - Release as 1.2.16.14 - Minor bug in FFGET_raw() causing missing of decoding due in mailpacks which have mixed EOL definitions, ie, the email uses \n only in the main headers, but then switches to \r\n in the bodies [ Can someone /PLEASE/ tell me why any peice of email software would do this !?!? ] Wed May 22 2002 - Release as 1.2.16.13 - Corrected block-boundary read line error with FFGET_fgets() - Corrected segfault error with UUDECODE due to XAM_strtok() Sun May 12 2002 (Mothers Day - Be nice to your mother and take the day off from IRC'ing or sufing the net) - Release 1.2.16.12 .... one day I'll release 1.2.17.0 - Improved nested decoding. There was a very strange size-specific decoding mishap which was causing some packs to unpack flawlessly, others to fail, with the only difference being that one had more than 1089 bytes of headers. Cleaning up the memory leak issues resolved it. Though I'm always a bit fearful of such dissapearing bugs. - Cleaned up memory free'ing, used 'ccmalloc', nice little tool I recommend it to people trying to at least get a coarse view of what is being left out. I found that simply by fixing up the memory leaks I had, one of my "wierd" bugs went away very nice of it. Tue May 07 2002 pldaniels@pldaniels.com - Added new function to MIME_headers object, to return the current status of the headers save status. This allows us to prevent opening up multiple FILE *'s on the same named _header_ file, potentially causing corruption - Removed the "WARNING: Possible pointer size being sent" debugging message. Tue Apr 30 2002 pldaniels@pldaniels.com - Updated TNEF with patch supplied by Joe Gooch (Thanks Joe) corrects segfault when decoding certain TNEF's. - Added new function in FFGET. FFGET_set_watch_SDL() tells FFGET to either be watching out for ^M^M sequence, or not to (1, 0 respectively). This is required so that we can prevent ripMIME from interpreting the ^M^M sequence if it's found in the email BODY as a SDL-Outlook exploit. Thanks to Elio de Santi for the mailpack which revealed this Fri Apr 26 2002 pldaniels@pldaniels.com - Lots of fixes in the FFGET routines. Including the introduction of a two-stage FEOF status. Initially we mark that we've reached the end of the /real/ file by flagging FILEEND, then, when we actually try to read a new data block using FFGET_getnewblock() we flag FFEOF. Previously we'd flag FEOF too early. This would cause corruption of various attachments. Tue Apr 23 2002 pldaniels@pldaniels.com - corrected memory leak causing "Killed" messages on large mailpacks with lots of text/print-quotable attachments Mon Apr 22 2002 pldaniels@pldaniels.com - Re-tuned the UUencode line detection sequence. If anyone is aware of a /precise/ method to detect a uuencoded attachment in mailpacks, both as RFC and Outlook[stupid], please email me on pldaniels@pldaniels.com Fri Apr 19 2002 pldaniels@pldaniels.com - Corrected FFGET_fgets() function which was returning NULL one call too early. Rather than sending back the last "partial" line read (end of buffer), it would assign the data to the buffer, but also return NULL, which caused some programs to exit early from their reading loops. I'm actually quite surprised things even worked with this error... amazing what can slip by. Sun Apr 14 2002 pldaniels@pldaniels.com - Corrected _headers_ file output location, which was defaulting to the execution dir, rather than the unpackdir Fri Mar 08 2002 pldaniels@pldaniels.com --Released as 1.2.16.6 - Added ability to decode "x-uuencode" files even though this type technically is not specified by the RFC's (one should be using BASE64 or QuotedPrintable. - Corrected UUencode filename extraction from the header triple Thu Mar 07 2002 pldaniels@pldaniels.com --Released as 1.2.16.5 - Fixed XAM_strtok() call which was occasionally failing if there was a delimeter at the end of the string. Wed Mar 06 2002 pldaniels@pldaniels.com - Renamed zstrncpy.[ch] as zstr.[ch] as it's becoming a generic library with more than just the zstrncpy function in it. It now contains the zstrncat and zstrncate calls Tue Mar 05 2002 pldaniels@pldaniels.com --Released as 1.2.16.4 - Replaced all strncpy and strcpy routines with zstrncpy() calls. Sun Mar 03 2002 pldaniels@pldaniels.com --Released as 1.2.16.3 - Replaced file write routines in BASE64 and UUDEC decoders so that they use buffered write (reduces the number of fwrite() calls. This also gained another 10% of performance over direct write. Sat Mar 02 2002 pldaniels@pldaniels.com - Replaced in MIME_base64_decode() the FFGET_fgetc() call with a direct source code from the FFGET_fgetc() routine. Whilst this does mean that I will have to keep track of the changes I make in FFGET_fgetc and ensure that I port them across to the BASE64 decode routine, it does make for a good 10% gain in performance as shown on a 80Mb data test suite. - Converted Base64, Hexconv and UUDEC routines into static (compile time) fixed tables so that there isn't any need to build them each time the program is run. This saves us on startup time, although minimal, it does help. Thu Feb 28 2002 pldaniels@pldaniels.com - Released as 1.2.16.2 - Updated FFGET routines with patch as supplied by J.Gooch Tue Feb 26 2002 pldaniels@pldaniels.com - Moved to 4 digit version numbers - Added f->FFEOF = 0 to the ffget_set_stream function. Was causing an intermittent FEOF() status based on random data without it being there. Sat Feb 23 2002 pldaniels2pldaniels.com - Committed ripMIME to local CVS Wed Feb 20 2002 pldaniels@pldaniels.com - Releasing as v1.2.16 - Cleaned up FFGET routines and added more comments. - Added new DEBUG mode selection routines. Now debugging can be activated on a per-level basis, with (default) now there being *_DNORMAL == Debug normal *_DPEDANTIC == Pedantic debugging (lots of output) ie, in mime.c, there is now lines like if (MIME_DPEDANTIC) fprintf(stdout,"%s",line); This replaces the existing non-level sensitive routines of if (_MIME_debug) .... - HUGE stuff up. Non-quotedprintable encoded text files were not being written out because I accidently left out the fprintf statement in MIME_decode_text() routine... REALLY bad MISTAKE. This was causing text files to be written out as 0-byte files. Fortuntely this error was picked up by the Xamime testing suite. - Releasing as v1.2.15 (I wonder if these releases will stop coming out so quickly) - _FINALLY_ fixed TNEF decoding, many thanks to Chea Chee Keong for the help provided, very valuable. - Releasing as v1.2.14 - FEOF detection mishandling in MIME_decode_uu() cleaned up (uses the FFGET_feof() call rather than feof(), which (the feof) could prematurely return TRUE rather than when FFGET returns true, this is due to the fact that the FFGET routines will (on the last valid read) read up to the end of the file causing feof() to return TRUE, however, until all the data is read out of the FFGET buffer, it should not return true. Hence, all routines using feof() were replaced with FFGET_feof() Mon Feb 18 2002 pldaniels@pldaniels.com - Releasing as v1.2.13 - CRCR logic cleaned up in FFGET_fgets() (thanks to Joseph Gooch for his help in resolving the logic routines) - Initialisation of ungetc flag in FFGET - Removed MIME detection debugging output - Added anti \0 poisoning filtering Sun Feb 17 2002 pldaniels@pldaniels.com - Releasing as v1.2.12 Although I've not had a chance to verify that ripMIME extracts all the possible \r explioits which can be used against Outlook/Express I am certainly confident it can extract most of them. I've ran this ripMIME against about 600Mb of normal emails, all of which have extracted correctly in regression tests. I'm _STILL_ seeking more assistance to deal with TNEF decoding. I have one mail pack which refuses to correctly extract, rather it results in the TNEF routines attempting to access data beyond the limits of the file (yes, it's protected against that, but it's still annoying!) - Added FFGET_fgets() true blank detection for reads, this is to deal with potential trickery as mentioned in bugtraq regarding fgets() falsely returning a single \n as though it was a blank line when really, it was just the previous fgets didn't have enough buffer space to include the last \n. - Corrected file Unique Nameing name cropping. Fri Feb 15 2002 pldaniels@pldaniels.com - Made ripMIME report its version via STDOUT instead of STDERR - Changed "Decoding TNEF format" report to report to STDOUT instead of STDERR - Replaced entire file I/O routines to use FFGET, which now supports fgets(), fgetc() and raw-get routines. This was done so that we could (originally) detect the presence of an offending double byte in the headers of some emails which may be used to exploit Outlook's interpretation. - Fixed zstrncpy which was copying one byte too few when a strncpy was done. This was causing filename crop problems. -NOTE- this was primarily due to the fact that the size of buffer specified was inclusive of the \0 terminator.... as apposed to an actual BUG - Added into MIME_headers the ability to decode "Content-Location" to extract filenames from the new Microsoft MHF formatted emails (basically it's a bundled together WWW page HTML + images) Thu Feb 14 2002 pldaniels@pldaniels.com - Added header detection of double CR exploit usable against MS Outlook (This feature is more of use to programs which actively filter against exploits such as Inflex/Xamime > Need more help with TNEF decoding!!!!! Wed Jan 30 2002 pldaniels@pldaniels.com - v1.2.11 - Added ability to decode MULTIPLE UUEncoded attachments within the same body of text. Tue Jan 29 2002 pldaniels@pldaniels.com - v1.2.10 - Corrected mistaken use of 'n' count in UUdecode byte count resulting in excessive sized files (with useless data) - v1.2.9 released - Removed non-used functions MIME_getchar() and MIME_insert_X_header() - Added UUDecode routines - Added --no_uudecode option Mon Jan 28 2002 pldaniels@pldaniels.com - v1.2.8 released - Patch suppled by Tim J. Robbins for buffer-smash as specified by BugTraq applied Thu Nov 15 2001 pldaniels@pldaniels.com - Corrected potential buffer overrun situation in raw-decode mode - Corrected buffer overrun with MIME_headers which allowed massive sized filenames to cause segfaults (Thanks to Tristan Aston for bringing this to my attention) - Various code cleanups and extentions to the mime.[ch] files to deal with new advances in Xamime (http://xamime.com) Fri Nov 09 2001 pldaniels@pldaniels.com - Replaced several sprintf()'s with snprintf - Set a global _MIME_STRLEN_MAX length to 1023 and used with snprintf() to avoid buffer-overflows as reported by Tristan Aston (Solaris 8). - Mon Nov 05 2001 pldaniels@pldaniels.com - Resolved TNEF segfault issue with some winmail.dat packs which do not seem to conform to the standard which we have for the current tnef decoding module. Some winmail.dat packs still dont unpack but that's an issue with the MAPI data I believe (anyone want to help?) - Added "RAW" format for encoding, RAW basically is a slower form of read/decode, but, it will at least correctly (??) save non-format specified files. RAW decoding routines are kept in rawget.[ch] which is nothing more than a specific version of fgets(). This may change later [back to fgets()]. The difference is that rawget returns how many bytes it read, because the data returned by a binary file may contain \0's which would otherwise fool length testing function calls (ie, strlen()) into indicating there are less bytes than what there really is. This could be resolved by performing a test for the \n or \r, but it's more overhead. - Added patch from Xerox to correct occasional issue with non constant BASE64 line lengths (previously used to determine when the encoding had finished) Fri Oct 26 2001 pldaniels@pldaniels.com - 1.2.5 released - Removed mailbox "\n\rFrom " line detection routines out of text decoding section, created new function MIME_unpackmailbox() which will break apart the mailbox email at at time and feed it to MIME_unpack() - [mime.c, tnef.h, tnef.c ] Fixed issue with TNEF decoding segfaulting if -d param was used in ripMIME - [config.h] Cleaned up the __BYTE_ORDER mangling issue by renaming as __TNEF_BYTE_ORDER - Added numerous debugging messages available by using --debug on ripMIME - [tnef.c] Removed extensive deadcode from tnef modules - [tnef.c] Removed compressed RTF decoding - Improved error reporting and messages Thu Oct 18 2001 pldaniels@pldaniels.com - 1.2.4 released - Added TNEF decoding ability through use of modified tnef2txt library as created by Brandon Long (blong@uiuc.edu), April 1997. As yet, I have not been able to successfully contact this person. Tue Oct 09 2001 pldaniels@pldaniels.com - Updated MIME Boundary stack "cmp" operation so that it pops off any boundaries -above- the detected one. This is because technically, all boundaries should be nested in their occurances. This is a hoped fix for most of the .eml attachment extraction problems that were occuring. Tue Sep 11 2001 pldaniels@pldaniels.com - Cleaned up source and added in more comments - Released v1.2.3 Mon Sep 10 2001 pldaniels@pldaniels.com - changed the detection string for quoted-printable from "quote-printable" to "quoted-printable". A small, but oh so painful mistake! - changed boundary line detection in header files to improve the detection and extraction of multiply-nested mailpacks Sun Sep 9 2001 pldaniels@pldaniels.com - Improved name= parameter discrimination when headers have mixed in components. Now correctly decodes name= when the headers have a type identifier. - Changed XAM_strncasecmp and strlower to use inline/macro version of isupper / tolower Wed Aug 29 2001 pldaniels@pldaniels.com - Added ability for ripMIME to uncompress BASE64 encoded inline mailpacks (these are like forwarded emails, but they've been coded up in BASE64 format! I kid you not!) - Added Handling for Quoted-Printable filenames Fri Aug 24 2001 pldaniels@pldaniels.com - Added recursive calling if mailpack contained encoded mailpack ie, when we get a named rfc822/message type attachment. - Inserting new code from MIME_headers.[ch] which will do explicit analysis of the headers in a single-line stream format. Makes things far far easier to deal with later. Tue Aug 21 2001 pldaniels@pldaniels.com - mime.c: added explicit handling of mailbox format (/var/spool/mail) Fri Aug 03 2001 pldaniels@pldaniels.com - mime.c: added --no_paranoid option for filename filtering Wed Aug 01 2001 pldaniels@pldaniels.com - mime.c: MIME_test_uniquename(): used strrchr rather than XAM_strtok() to locate the last '.' seperator for filenames. Fixes problem with misnaming of multiple .'d filenames. Tue Jul 31 2001 pldaniels@pldaniels.com - XAM_strtok.c: Corrected end-of-tokens bug in XAM_strtok(), would return a null string when it should return the remainder of the string. - mime.c: added additional method for renaming of file --prefix --infix --postfix - mime.c: added "--unique_name" function to preserve existing filenames when decoding (ie, no clobbering) Tue Jun 12 2001 pldaniels@pldaniels.com - mime.c: replaced strncasecmp() with XAM_strncasecmp() to permit portability to sysV and others - mime.c: replaced random() call with rand() - Added early exit routine for base64 if it detects two consecutive hyphens ('-'), this is needed in order to deal with 1-in-1000 situation where a small file is encoded, and there is no separating double CR\LF between the end of the base64 encoding, and the new boundary line. Sun Jun 03 2001 pldaniels@pldaniels.com -Release as 1.0.2 - Fixed occasional issue of a second attachment decoding to 57 bytes in a BASE64 decode, added extra test routine for the length comparisons so that a length of 0 could not be accepted. MIME_decode_64: else if (p_length == -1) { ++ if (t_length > 0) p_length = t_length; } - Replaced strtok() calls with own XAM_strtok() from the XaMime project NOTE- This strtok call DOES NOT compress consecutive delimeters like strtok. The behaviour of XAM_strtok() is more like that of strsep(). The replacement was required to ensure thread-safety, as strtok() is far from being thread-safe (it's bad enough when you 'accidently' nest strtok() calls!. XAM_strtok() can be safely nested. Mon May 21 2001 pldaniels@pldaniels.com - Fixed issue of ripMIME not successfully extracting a RFC822 "embedded" pack if it started with "From" or ">From" Mon May 14 2001 pldaniels@pldaniels.com - Replaced fgetc() calls in BASE64 decoding routine with a set of routines as defined in the new module ffget.[ch]. These new routines read in the MIME encoded file line at a time using fgets() and then feed out the file char at a time using the call FFGET_fgetc(). Speed improvement is nearly DOUBLE thanks to this routine (the more BASE64 decodes you do the better. Mon Apr 02 20:54:45 EST 2001 pldaniels - Added new var, _no_nameless[= 0]. If set to something other than zero, then any attachments without an internally specified filename will be removed (After processing) - Added CLI param to turn on no_nameless. Wed Mar 28 14:26:06 EST 2001 pldaniels - Added patch for filename cleaning. Fixed patch to not overwrite '.'s - Put MIME_clean_MIME_filename() into action, hopefully ISO filenames should be supported without kludgy characters now. Tue Mar 27 21:39:24 EST 2001 pldaniels - Added MIME_get_attachment_count() which returns how many filename'd attachments were found. - Added in _current_line for the purpose of debugging, good for being able to tell where ripMIME is through the reading of a mailpack - Added "default" encoding method as being PLAINTEXT in the event that there is no encoding format handled specifically (Thanks to Hans Arder for supplying mailpack to display this) Tue Mar 21 -PR5 -a debugging line in the collect_headers fn was causing potential encoding-format loss due to absorbing the vital Content-Type line. Fri Mar 16 -Prerelease 4 -Implemented a stack based boundary layer detection system to allow for nested in-line attachments, as typically generated by drag'n'dropping files to email -ISO filename decoding commenced, needing more examples of mailpacks with ISO encoded filenames! -Created the "daily update" version of ripMIME, to follow suit with the new Inflex style of releases. Tue Feb 13 -Prerelease 3 -Fixed up some quoted-printable glitches (pack supplied by James Cownie, thanks!) Tue Jan 30 00:49:51 EST 2001 worker -Prerelease 2 -Removed tmpnam() call out of MIME_XHeader() function, not that this is actually ever used by ripMIME (its part of the cInflex suite) thanks to both Dave DeMaagd and Justin (SuidAfrika) for pointing this out to me. - Corrected directory naming spec so that there aren't double //'s (Thanks to James Cownie for preliminary notification) -Changed the 'From' line detection routine to work with both un-read (ones with just 'From joe@xxx.bbbb') as well as READ emails (ones with '>From joe@xxx.bbbb'). Thanks to Sven for pointing this out (Im too used to using pop3-pickup ;) Also allowed the FROM line to be detected in the MIME_find_next_header() function. Sun Jan 28 15:10:49 EST 2001 worker -v1.0.0 PreRelease 1 -Added in multiple-email (sequential) and nested (forwarded) email attachment extraction support (painful!) -Passed ripMIME through several thousand "real life" emails to verify extraction ability -Modified flag passing structures to contain most in struct _MIME_info (required to get nested emails to extract correctly) -Allowed user to turn on/off each logging option (syslog or stderr) -Tried cleaning up comments a bit -Added in more _debug() calls, making watching the process of decoding via syslog() a bit easier. Fri Jan 12 11:00:12 EST 2001 worker - Added patch supplied by Rolf to also counter for boundary specifiers which dont' have enclosing "'s Tue Jan 09 13:38:16 EST 2001 worker (0.1.10) -Added ability to handle mailpacks which had no CR/LF separator after the end of BASE64 decoding (Thanks to Hans Harder for suppling the mailpack!) Sun Jan 07 00:02:19 EST 2001 worker (0.1.9) - Removed error reporting code from mime.c which said that end-of-file had been reached, this is no longer required as we correctly handle plain-text (into the _headers_ file) -Considering to release as v1.0.0 Sat Dec 30 23:47:18 SAST 2000 worker (0.1.8) - Added print-quotable decoder - release 0.1.9 Thu Dec 28 22:13:56 SAST 2000 worker - Handler for mailpacks WITHOUT a boundary specified (single file emails) - Forgot to change the version indicator in 0.1.7 Sun Dec 24 23:32:09 SAST 2000 worker - Modify base64 decoder to allow a _MAXIMUM_ of 76 chars on the line. and capable of any length of line less than 76 chars. - Release 0.1.7 Fri Dec 22 23:02:57 SAST 2000 worker Commenced fixing ripMIME for new style of MIME headers which have multiple statements on a single line. Sat Nov 25 17:45:45 SAST 2000 worker version 0.1.6 -Applied patch from Matthew to allow input from STDIN -Added feature to allow headers to be dumped to a file (for picking up kak virus etc) -Added more comments to source code Thu Nov 23 18:30:20 SAST 2000 worker version 0.1.5 -Corrected EOL detection for 76 char MIME encodings Thu Nov 23 18:30:20 SAST 2000 worker version 0.1.4 -Corrected End of encoding MIME breakage -Added better -DDEBUG reporting via syslog() Mon Oct 30 09:45:04 SAST 2000 worker version 0.1.3 -Added input parameters to allow for more control -Added ability to specify own text-only filename prefix Mon Oct 30 00:46:05 SAST 2000 worker version 0.1.2 -Added code to remove zero byte files. Sun Oct 29 18:16:46 SAST 2000 worker version 0.1.1 released. ripmime-1.4.0.10.debian.1/uuencode.h0000640000175000017500000000152311667102337015050 0ustar pjbpjb#define UUENCODE_STATUS_SHORT_FILE 100 #define UUENCODE_STATUS_CANNOT_OPEN_FILE 101 #define UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY 102 #define UUENCODE_STATUS_CANNOT_FIND_FILENAME 103 #define UUENCODE_STATUS_OK 0 extern int uuencode_error; int UUENCODE_init( void ); int UUENCODE_set_debug( int level ); int UUENCODE_set_verbosity( int level ); int UUENCODE_set_verbosity_contenttype( int level ); int UUENCODE_set_nodecode( int level ); int UUENCODE_set_decode( int level ); int UUENCODE_set_doubleCR_mode( int level ); int UUENCODE_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ); int UUENCODE_is_uuencode_header( char *line ); int UUENCODE_is_file_uuencoded( char *fname ); int UUENCODE_decode_uu( FFGET_FILE *f, char *unpackdir, char *input_filename, char *out_filename, int out_filename_size, int decode_whole_file, int keep ); ripmime-1.4.0.10.debian.1/ffget.c0000640000175000017500000004733011667102337014335 0ustar pjbpjb/*------------------------------------------------------------------------ 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" #ifndef FL #define FL __FILE__,__LINE__ #endif #define FFGET_VERSION "1.0.0.7" #define FFGET_LASTUPDATED "200707091500" #define FFGET_DNORMAL ((FFGET_debug >= FFGET_DEBUG_NORMAL )) #define FFGET_DPEDANTIC ((FFGET_debug >= FFGET_DEBUG_PEDANTIC)) /* 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; 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; 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' ) { if ( *(crlfpos +1) == '\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') ) { 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. 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 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. ripmime-1.4.0.10.debian.1/pldstr.c0000640000175000017500000005164311667102337014554 0ustar pjbpjb #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. ripmime-1.4.0.10.debian.1/ripmime-api.h0000640000175000017500000000030211667102337015444 0ustar pjbpjb struct RIPMIME_object { char *mailpack; char *outputdir; }; int RIPMIME_init( struct RIPMIME_object *rm ); int RIPMIME_decode( struct RIPMIME_object *rm, char *mailpack, char *outputdir ); ripmime-1.4.0.10.debian.1/MIME_headers.c0000640000175000017500000026554011667102337015471 0ustar pjbpjb/*----------------------------------------------------------------------- ** ** ** 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( 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 (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 (MIMEH_DNORMAL) LOGGER_log("%s:%d:MIMEH_read_headers:DEBUG: Header line end = %p", FL, headerline_end); } // 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); } // Get charset name const char ch[] = "charset="; char *ch_begin = strstr(header_value, ch); if (ch_begin) { ch_begin += sizeof(ch) -1; char *ch_end = strpbrk(ch_begin, ";\n\r "); if (!ch_end) ch_end = ch_begin + strlen(ch_begin); ch_end -= 1; if (*ch_begin == '"' && *ch_end == '"') { ++ch_begin; --ch_end; } const size_t ch_len = ch_end - ch_begin +2; PLD_strncpy(hinfo->charset, ch_begin, ch_len > _MIMEH_STRLEN_MAX ? _MIMEH_STRLEN_MAX : ch_len); } // 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->filename),"%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 *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; 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'; hinfo->charset[0] = '\0'; // 20040116-1234:PLD - added to appease valgrind hinfo->content_disposition = 0; hinfo->content_transfer_encoding = 0; hinfo->boundary_located = 0; // 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(f); FFGET_set_watch_SDL(0); // 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 ripmime-1.4.0.10.debian.1/Makefile0000640000175000017500000000432511667102337014533 0ustar pjbpjb# # VERSION CHANGES # # 0.1.13: Added -I. LOCATION=/usr/local VERSION=0.1.14 # VERSION changes #--------------------- # 0.1.14: Added 'install' usage # #CC=gcc # if there isn't already a default CFLAGS set, # use our recommended settings. #CFLAGS ?= -Wall -g -O2 -Werror $(CPU_OPTS) #CFLAGS=-Wall -g -O2 -Wundef -Wshadow -Wsign-compare -I. CFLAGS=-Wall -g -I. -O2 -Werror # OLE decoding is still considered to be 'beta' mode - so it # disabled in the stable release of ripMIME for now # You can turn it on, but watch out for various office files # which may/probably-will break ripMIME with a segfault # or other strange errors. If you do wish to test this # and find a dud mailpack, please send through to # mailpacks@pldaniels.com # COMPONENTS= -DRIPOLE LIBS= #COMPONENTS= # DEBUGGING Related Flags OBJ=ripmime RIPOLE_OBJS= ripOLE/ole.o ripOLE/olestream-unwrap.o ripOLE/bytedecoders.o ripOLE/bt-int.o #RIPOLE_OBJS= OFILES= strstack.o mime.o ffget.o MIME_headers.o tnef/tnef.o rawget.o pldstr.o logger.o libmime-decoders.o boundary-stack.o uuencode.o filename-filters.o $(RIPOLE_OBJS) default: tnef/tnef.o ripmime ripOLE/ole.o buildcodes.h: ./generate-buildcodes.sh ripOLE/ole.o: ./build_ripOLE tnef/tnef.o: ./build_tnef .c.o: ${CC} ${CFLAGS} $(COMPONENTS) -c $*.c all: ${OBJ} solib: ${OFILES} ripmime-api.o gcc --shared -Wl,-soname,libripmime.so.1 ${OFILES} ripmime-api.o -o libripmime.so.1.4.0 -lc libripmime: ${OFILES} ripmime-api.o ar ruvs libripmime.a ${OFILES} ripmime-api.o ripl: ripmime.a ${CC} ${CFLAGS} ripmime.c ripmime.a -o ripmime sco: ${OFILES} ${CC} ${CFLAGS} ripmime.c ${OFILES} -o ripmime -lsocket ripmime: ${OFILES} ripmime.c buildcodes.h ${CC} ${CFLAGS} $(COMPONENTS) ripmime.c ${OFILES} -o ripmime ${LIBS} riptest: ${OFILES} ${CC} ${CFLAGS} riptest.c ${OFILES} -o riptest install: ${OBJ} strip ripmime cp ripmime ${LOCATION}/bin/ cp ripmime.1 ${LOCATION}/man/man1 ffget_test: ffget_mmap_test.c ffget_mmap.[ch] logger.o ffget_mmap.o ${CC} ${CFLAGS} ffget_mmap_test.c logger.o ffget_mmap.o -o ffgt clean: rm -f *.o *core ${OBJ} buildcodes.h rm -f tnef/*.o rm -f ripOLE/*.o ripOLE/ripole MIMEH: MIME_headers.o strlower.o ${CC} ${CFLAGS} MIMEH_test.c MIME_headers.o strlower.o -o MIMEH_test ripmime-1.4.0.10.debian.1/filename-filters.h0000640000175000017500000000034111667102337016464 0ustar pjbpjbint 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 ); ripmime-1.4.0.10.debian.1/mime.c0000640000175000017500000035134211667102337014172 0ustar pjbpjb/*----------------------------------------------------------------------- ** ** ** mime.c ** ** 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 retaining in the global struct ** this is useful for when you want to retrieve such information ** from an external application - without having to dive into ** the hinfo struct directly. Also, the subject is retained only ** for the /primary/ headers, all subsequent headers which [may] ** contain 'subject:' are ignored. ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MEMORY_DEBUG #define DEBUG_MEMORY 1 #include "xmalloc.h" #endif #include "pldstr.h" #include "boundary-stack.h" #include "ffget.h" #include "mime.h" #include "tnef/tnef_api.h" #include "ripOLE/ole.h" #include "libmime-decoders.h" #include "uuencode.h" #include "filename-filters.h" #include "logger.h" #include "strstack.h" #include "MIME_headers.h" int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss ); int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ); int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ); int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ); // Predefined filenames #define MIME_BLANKZONE_FILENAME_DEFAULT "_blankzone_" #define MIME_HEADERS_FILENAME "_headers_" #ifndef FL #define FL __FILE__, __LINE__ #endif #define _ENC_UNKNOWN 0 #define _ENC_BASE64 1 #define _ENC_PLAINTEXT 2 #define _ENC_QUOTED 3 #define _ENC_EMBEDDED 4 #define _ENC_NOFILE -1 #define _MIME_CHARS_PER_LINE 32 #define _MIME_MAX_CHARS_PER_LINE 76 #define _RECURSION_LEVEL_DEFAULT 20 #define _BOUNDARY_CRASH 2 // BASE64 / UUDEC and other binary writing routines use the write buffer (now in v1.2.16.3+) // The "limit" define is a check point marker which indicates that on our next run through // either the BASE64 or UUDEC routines, we need to flush the buffer to disk #define MIME_MIME_READ_BUFFER_SIZE (8 *1024) #define _MIME_WRITE_BUFFER_SIZE (8 *1024) #define _MIME_WRITE_BUFFER_LIMIT (_MIME_WRITE_BUFFER_SIZE -4) // Debug precodes #define MIME_DPEDANTIC ((glb.debug >= _MIME_DEBUG_PEDANTIC)) #define MIME_DNORMAL ((glb.debug >= _MIME_DEBUG_NORMAL )) #define MIME_VERBOSE ((glb.verbosity > 0 )) #define MIME_VERBOSE_12 ((glb.verbosity_12x_style > 0 )) #define DMIME if (glb.debug >= _MIME_DEBUG_NORMAL) #define FL __FILE__,__LINE__ /* our base 64 decoder table */ static unsigned char b64[256]={ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63,\ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 0, 128, 128,\ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,\ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128,\ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,\ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 \ }; struct MIME_globals { int header_defect_count; int filecount; char blankfileprefix[_MIME_STRLEN_MAX]; int blankfileprefix_expliticly_set; int verbosity; int verbosity_12x_style; int verbosity_contenttype; int verbose_defects; int report_mime; int debug; int quiet; int syslogging; int stderrlogging; int unique_names; int rename_method; char headersname[_MIME_STRLEN_MAX]; char tempdirectory[_MIME_STRLEN_MAX]; int save_headers; int attachment_count; int current_line; int no_nameless; int name_by_type; int mailbox_format; char out_charset[MDECODE_ISO_CHARSET_SIZE_MAX]; int decode_uu; int decode_tnef; int decode_b64; int decode_qp; int decode_mht; int decode_ole; int multiple_filenames; int header_longsearch; int max_recursion_level; int blankzone_saved; int blankzone_save_option; char blankzone_filename[_MIMEH_STRLEN_MAX +1]; int (*filename_decoded_reporter)(char *, char *); // Pointer to the reporting function for filenames as they are decoded // First subject is an important item, because this // represents the "main" subject of the email, as // seen by the MUA. We have to store this locally // rather than rely purely on hinfo, because other // wise, any consequent parsing of sub-message bodies // will result in the clobbering of the hinfo struct char subject[_MIME_STRLEN_MAX]; }; static struct MIME_globals glb; //char OK[]="OKAY"; static char scratch[1024]; /* File pointer for the headers output */ FILE *headers; /*-----------------------------------------------------------------\ Function Name : MIME_version Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_version( void ) { fprintf(stderr,"ripMIME: %s\n", LIBMIME_VERSION); MIMEH_version(); #ifdef RIPOLE OLE_version(); #endif return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_name_by_type Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: Used to set (on/off) the name-by-type feature, which will use the content-type paramter to name the attachment rather than just the default textfile* naming 0 = don't name by type 1 = name by type. We may consider adding more levels to this to account for the different types of content-types. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_name_by_type( int level ) { glb.name_by_type = level; return glb.name_by_type; } int MIME_set_out_charset( char *charset ) { MDECODE_set_out_charset(charset); PLD_strncpy(glb.out_charset, charset, sizeof(glb.out_charset)); 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 MIME_set_debug( int level ) { glb.debug = level; BS_set_debug(level); TNEF_set_debug(level); MIMEH_set_debug(level); MDECODE_set_debug(level); UUENCODE_set_debug(level); FNFILTER_set_debug(level); return glb.debug; } /*-----------------------------------------------------------------\ Function Name : MIME_set_quiet Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_quiet( int level ) { glb.quiet = level; MIME_set_debug(0); MIME_set_verbosity(0); return glb.quiet; } /*-----------------------------------------------------------------\ Function Name : MIME_set_recursion_level Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_recursion_level( int level ) { glb.max_recursion_level = level; return glb.max_recursion_level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_tnef Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_tnef( int level ) { glb.decode_tnef = level; TNEF_set_decode( level ); return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_ole Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_ole( int level ) { glb.decode_ole = level; return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_uudecode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_uudecode( int level ) { glb.decode_uu = level; UUENCODE_set_decode( level ); return glb.decode_uu; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_base64 Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_base64( int level ) { glb.decode_b64 = level; return glb.decode_b64; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_qp Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_qp( int level ) { glb.decode_qp = level; MDECODE_set_decode_qp(level); return glb.decode_qp; } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_doubleCR Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_doubleCR( int level ) { MIMEH_set_doubleCR_save(level); /** 20041106-0859:PLD: Was '0' **/ return MIMEH_get_doubleCR_save(); } /*-----------------------------------------------------------------\ Function Name : MIME_set_decode_mht Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_decode_mht( int level ) { glb.decode_mht = level; return glb.decode_mht; } /*-----------------------------------------------------------------\ Function Name : MIME_set_header_longsearch Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_header_longsearch( int level ) { glb.header_longsearch = level; return glb.header_longsearch; } /*------------------------------------------------------------------------ Procedure: MIME_set_tmpdir ID:1 Purpose: Sets the internal Temporary directory name. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_tmpdir( char *setto ) { PLD_strncpy(glb.tempdirectory,setto, _MIME_STRLEN_MAX); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_glb.blankfileprefix ID:1 Purpose: Sets the filename prefix which is to be used for any files which are saved and do not have a defined filename. Input: char *prefix : \0 terminated character array defining the filename prefix Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_blankfileprefix( char *prefix ) { PLD_strncpy( glb.blankfileprefix, prefix, _MIME_STRLEN_MAX ); glb.blankfileprefix_expliticly_set = 1; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_get_glb.blankfileprefix ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ char *MIME_get_blankfileprefix( void ) { return glb.blankfileprefix; } /*------------------------------------------------------- * MIME_setglb.verbosity * * By default, MIME reports nothing as its working * Setting the verbosity level > 0 means that it'll * report things like the name of the files it's * writing/extracting. * */ int MIME_set_verbosity( int level ) { glb.verbosity = level; MIMEH_set_verbosity( level ); TNEF_set_verbosity( level ); FNFILTER_set_verbose( level ); UUENCODE_set_verbosity( level ); MDECODE_set_verbose( level ); BS_set_verbose( level ); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbosity_12x_style Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbosity_12x_style( int level ) { glb.verbosity_12x_style = level; MIME_set_verbosity( level ); return level; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbosity_contenttype( int level ) { glb.verbosity_contenttype = level; MIMEH_set_verbosity_contenttype( level ); UUENCODE_set_verbosity_contenttype( level ); TNEF_set_verbosity_contenttype( level ); return glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : MIME_set_verbose_defects Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_verbose_defects( int level ) { glb.verbose_defects = level; return glb.verbose_defects; } /*-----------------------------------------------------------------\ Function Name : MIME_set_report_MIME Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_report_MIME( int level ) { glb.report_mime = level; /**MIMEH_set_report_MIME(glb.report_mime); - not yet implemented **/ return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { glb.filename_decoded_reporter = ptr_to_fn; UUENCODE_set_filename_report_fn( ptr_to_fn ); TNEF_set_filename_report_fn( ptr_to_fn ); return 0; } /*------------------------------------------------------- * MIME_set_dumpheaders * * By default MIME wont dump the headers to a text file * but at times this is useful esp for checking * for new styles of viruses like the KAK.worm * * Anything > 0 will make the headers be saved * */ int MIME_set_dumpheaders( int level ) { glb.save_headers = level; return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_set_multiple_filenames Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_multiple_filenames( int level ) { glb.multiple_filenames = level; return 0; } /*------------------------------------------------------ * MIME_set_headersname * * by default, the headers would be dropped to a file * called '_headers_'. Using this call we can make * it pretty much anything we like */ int MIME_set_headersname( char *fname ) { PLD_strncpy(glb.headersname, fname, _MIME_STRLEN_MAX); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_get_headersname ID:1 Purpose: Returns a pointer to the current glb.headersname string. Input: Output: Errors: ------------------------------------------------------------------------*/ char *MIME_get_headersname( void ) { return glb.headersname; } /*-----------------------------------------------------------------\ Function Name : *MIME_get_subject Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIME_get_subject( void ) { return glb.subject; } #ifdef RIPMIME_BLANKZONE /* The blankzone functions are responsbile for telling ripMIME what to do about the block of data which resides between the first/main headers and the first attachment/block of a MIME encoded email. Normally this would come out as textfile0, -except- for non-MIME encoded emails which would actually have their real body saved as textfile0. There are potential kludge options for doing this, but I am going to try and do it right. */ int MIME_set_blankzone_save_option( int option ) { switch ( option ) { case MIME_BLANKZONE_SAVE_TEXTFILE: glb.blankzone_save_option = option; break; case MIME_BLANKZONE_SAVE_FILENAME: glb.blankzone_save_option = option; snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT ); break; default: LOGGER_log("%s:%d:MIME_set_blankzone_save_option:WARNING: Unknown option for saving method (%d). Setting to '%s'",FL, option, MIME_BLANKZONE_FILENAME_DEFAULT ); glb.blankzone_save_option = MIME_BLANKZONE_SAVE_FILENAME; snprintf( glb.blankzone_filename, _MIME_STRLEN_MAX, "%s", MIME_BLANKZONE_FILENAME_DEFAULT ); } return glb.blankzone_save_option; } /*-----------------------------------------------------------------\ Function Name : MIME_set_blankzone_filename Returns Type : int ----Parameter List 1. char *filename , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_set_blankzone_filename( char *filename ) { PLD_strncpy( glb.blankzone_filename, filename, _MIME_STRLEN_MAX); return 0; } /*-----------------------------------------------------------------\ Function Name : *MIME_get_blankzone_filename Returns Type : char ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ char *MIME_get_blankzone_filename( void ) { return glb.blankzone_filename; } #endif /*------------------------------------------------------------------------ Procedure: MIME_setglb.no_nameless ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_no_nameless( int level ) { glb.no_nameless = level; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_uniquenames ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_uniquenames( int level ) { glb.unique_names = level; return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_noparanoid ID:1 Purpose: If set, will prevent MIME from clobbering what it considers to be non-safe characters in the file name. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_paranoid( int level ) { FNFILTER_set_paranoid( level ); return level; } /*------------------------------------------------------------------------ Procedure: MIME_set_mailboxformat ID:1 Purpose: If sets the value for the _mailboxformat variable in MIME, this indicates to functions later on that they should be aware of possible mailbox format specifiers. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_mailboxformat( int level ) { glb.mailbox_format = level; MIMEH_set_mailbox( level ); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_set_renamemethod ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_set_renamemethod( int method ) { if (( method >= _MIME_RENAME_METHOD_INFIX ) && ( method <= _MIME_RENAME_METHOD_POSTFIX )) { glb.rename_method = method; } else { LOGGER_log("%s:%d:MIME_set_renamemethod:ERROR: selected method not within %d > x > %d range", FL, _MIME_RENAME_METHOD_INFIX, _MIME_RENAME_METHOD_POSTFIX ); return -1; } return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_get_header_defect_count Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_get_header_defect_count( void ) { return glb.header_defect_count; } /*------------------------------------------------------------------------ Procedure: MIME_getglb.attachment_count ID:1 Purpose: Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_get_attachment_count( void ) { return glb.attachment_count; } /*------------------------------------------------------------------------ Procedure: MIME_test_uniquename ID:1 Purpose: Checks to see that the filename specified is unique. If it's not unique, it will modify the filename Input: char *path: Path in which to look for similar filenames char *fname: Current filename int method: Method of altering the filename (infix, postfix, prefix) Output: Errors: ------------------------------------------------------------------------*/ int MIME_test_uniquename( char *path, char *fname, int method ) { struct stat buf; char newname[_MIME_STRLEN_MAX +1]; char scr[_MIME_STRLEN_MAX +1]; /** Scratch var **/ char *frontname, *extention; int cleared = 0; int count = 1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Start (%s)",FL,fname); frontname = extention = NULL; // shuts the compiler up if (method == _MIME_RENAME_METHOD_INFIX) { PLD_strncpy(scr,fname, _MIMEH_STRLEN_MAX); frontname = scr; extention = strrchr(scr,'.'); if (extention) { *extention = '\0'; extention++; } else { method = _MIME_RENAME_METHOD_POSTFIX; } } snprintf(newname,_MIME_STRLEN_MAX,"%s/%s",path,fname); while (!cleared) { if ((stat(newname, &buf) == -1)) { cleared++; } else { if (method == _MIME_RENAME_METHOD_PREFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%d_%s",path,count,fname); } else if (method == _MIME_RENAME_METHOD_INFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d.%s",path,frontname,count,extention); } else if (method == _MIME_RENAME_METHOD_POSTFIX) { snprintf(newname,_MIME_STRLEN_MAX,"%s/%s_%d",path,fname,count); } count++; } } if (count > 1) { frontname = strrchr(newname,'/'); if (frontname) frontname++; else frontname = newname; PLD_strncpy(fname, frontname, _MIMEH_FILENAMELEN_MAX); //FIXME - this assumes that the buffer space is at least MIME_STRLEN_MAX sized. } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_test_uniquename:DEBUG: Done (%s)",FL,fname); return 0; } /*------------------------------------------------------------------------ Procedure: MIME_is_file_mime ID:1 Purpose: Determines if the file handed to it is a MIME type email file. Input: file name to analyze Output: Returns 0 for NO, 1 for YES, -1 for "Things di Errors: ------------------------------------------------------------------------*/ int MIME_is_file_RFC822( char *fname ) { char conditions[16][16] = { "Received: ", "From: ", "Subject: ", "Date: ", "Content-", "content-", "from: ", "subject: ", "date: ", "boundary=", "Boundary=" }; int result = 0; int hitcount = 0; int linecount = 100; // We should only need to read the first 10 lines of any file. char *line; FILE *f; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Testing %s for RFC822 headers",FL,fname); f = fopen(fname,"r"); if (!f) { if (glb.quiet == 0) { LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot open file '%s' for reading (%s)", FL, fname,strerror(errno)); } return 0; } line = malloc(sizeof(char) *1025); if (!line) { LOGGER_log("%s:%d:MIME_is_file_mime:ERROR: cannot allocate memory for read buffer", FL); return 0; } while ((hitcount < 2)&&(fgets(line,1024,f))&&(linecount--)) { /** test every line for possible headers, until we get a blank line **/ if ((glb.header_longsearch == 0)&&(*line == '\n' || *line == '\r')) break; for (result = 0; result < 11; result++) { /** Test for every possible MIME header prefix, ie, From: Subject: etc **/ if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_is_file_mime:DEBUG: Testing for '%s' in '%s'", FL, line, conditions[result]); if (strncasecmp(line,conditions[result],strlen(conditions[result]))==0) { /** If we get a match, then increment the hit counter **/ hitcount++; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit on %s",FL,conditions[result]); } } } fclose(f); if (hitcount >= 2) result = 1; else result = 0; if (line) free(line); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_is_file_RFC822:DEBUG: Hit count = %d, result = %d",FL,hitcount,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_getchar_start ID:1 Purpose: This function is used on a once-off basis. It's purpose is to locate a non-whitespace character which (in the context of its use) indicates that the commencement of BASE64 encoding data has commenced. Input: FFGET f: file stream Output: First character of the BASE64 encoding. Errors: ------------------------------------------------------------------------*/ int MIME_getchar_start( FFGET_FILE *f ) { int c; /* loop for eternity, as we're "returning out" */ while (1) { /* get a single char from file */ c = FFGET_fgetc(f); /* if that char is an EOF, or the char is something beyond * ASCII 32 (space) then return */ if ((c == EOF) || (c > ' ')) { return c; } } /* while */ /* Although we shouldn't ever actually get here, we best put it in anyhow */ return EOF; } /*------------------------------------------------------------------------ Procedure: MIME_decode_TNEF ID:1 Purpose: Decodes TNEF encoded attachments Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_TNEF( char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { int result=0; char fullpath[1024]; snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); TNEF_set_path(unpackdir); result = TNEF_main( fullpath ); if (result >= 0) { // result = remove( fullpath ); if (result == -1) { if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_decode_TNEF: Removing %s failed (%s)",FL,fullpath,strerror(errno)); } } return result; } #ifdef RIPOLE int MIME_report_filename_decoded_RIPOLE(char *filename) { LOGGER_log("Decoding filename=%s", filename); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_decode_OLE Returns Type : int ----Parameter List 1. char *unpackdir, 2. struct MIMEH_header_info *hinfo, 3. int keep , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_decode_OLE( char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { struct OLE_object ole; char fullpath[1024]; int result; snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); OLE_init(&ole); OLE_set_quiet(&ole,glb.quiet); OLE_set_verbose(&ole,glb.verbosity); OLE_set_debug(&ole,glb.debug); OLE_set_save_unknown_streams(&ole,0); OLE_set_filename_report_fn(&ole, MIME_report_filename_decoded_RIPOLE ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Starting OLE Decode",FL); result = OLE_decode_file(&ole, fullpath, unpackdir ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode done, cleaning up.",FL); OLE_decode_file_done(&ole); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_OLE:DEBUG: Decode returned with code = %d",FL,result); return result; } #endif /*------------------------------------------------------------------------ Procedure: MIME_decode_raw ID:1 Purpose: Decodes a binary type attachment, ie, no encoding, just raw data. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_raw( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { int result = 0; char fullpath[1024]; int bufsize=1024; char *buffer = malloc((bufsize +1)*sizeof(char)); size_t readcount; int file_has_uuencode = 0; int decode_entire_file = 0; int fo; /* Decoding / reading a binary attachment is a real interesting situation, as we * still use the fgets() call, but we do so repeatedly until it returns a line with a * \n\r and the boundary specifier in it.... all in all, I wouldn't rate this code * as being perfect, it's activated only if the user intentionally specifies it with * --binary flag */ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Start\n",FL); snprintf(fullpath,sizeof(fullpath),"%s/%s",unpackdir,hinfo->filename); fo = open(fullpath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if (fo == -1) { LOGGER_log("%s:%d:MIME_decode_raw:ERROR: cannot open file %s for writing. (%s)\n\n",FL,fullpath,strerror(errno)); return -1; } while ((readcount=FFGET_raw(f, (unsigned char *) buffer,bufsize)) > 0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: BUFFER[%p]= '%s'\n",FL,buffer, buffer); if ((!file_has_uuencode)&&(UUENCODE_is_uuencode_header( buffer ))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: UUENCODED is YES (buffer=[%p]\n",FL,buffer); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: File contains UUENCODED data(%s)\n",FL,buffer); file_has_uuencode = 1; } if (BS_cmp(buffer, readcount)) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Boundary located - breaking out.\n",FL); break; } else { size_t bc; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: writing: %s\n",FL, buffer); bc = write( fo, buffer, readcount); if (bc != readcount) LOGGER_log("%s:%d:MIME_decode_raw:WARNING: Only wrote %d of %d bytes", FL, bc, readcount); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Completed reading RAW data\n",FL); free(buffer); close(fo); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Closed file and free'd buffer\n",FL); // If there was UUEncoded portions [potentially] in the email, the // try to extract them using the MIME_decode_uu() if (file_has_uuencode) { char full_decode_path[512]; snprintf(full_decode_path,sizeof(full_decode_path),"%s/%s",unpackdir,hinfo->filename); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding UUencoded data\n",FL); if ( hinfo->content_transfer_encoding == _CTRANS_ENCODING_UUENCODE ) decode_entire_file = 0; //result = UUENCODE_decode_uu(NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep ); result = UUENCODE_decode_uu(NULL, unpackdir, full_decode_path, hinfo->uudec_name, sizeof(hinfo->uudec_name), decode_entire_file, keep ); if (result == -1) { switch (uuencode_error) { case UUENCODE_STATUS_SHORT_FILE: case UUENCODE_STATUS_CANNOT_OPEN_FILE: case UUENCODE_STATUS_CANNOT_FIND_FILENAME: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error); result = 0; break; case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL); return -1; break; default: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error); return -1; } } if (result == UUENCODE_STATUS_SHORT_FILE) result = 0; glb.attachment_count += result; if (strlen(hinfo->uudec_name)) { if (strcasecmp(hinfo->uudec_name,"winmail.dat")==0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Decoding TNEF format\n",FL); snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name); MIME_decode_TNEF( unpackdir, hinfo, keep); } else LOGGER_log("%s:%d:MIME_decode_raw:WARNING: hinfo has been clobbered.\n",FL); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: End[result = %d]\n",FL,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_text ID:1 Purpose: Decodes an input stream into a text file. Input: unpackdir : directory where to place new text file hinfo : struct containing information from the last parsed headers keep : if set, retain the file Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_text( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int keep ) { FILE *of; // output file int linecount = 0; // The number of lines int file_has_uuencode = 0; // Flag to indicate this text has UUENCODE in it char fullfilename[1024]=""; // Filename of the output file char line[1024]; // The input lines from the file we're decoding char *get_result = &line[0]; int lastlinewasboundary = 0; int result = 0; int decodesize=0; snprintf(fullfilename,sizeof(fullfilename),"%s/%s",unpackdir,hinfo->filename); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TEXT [encoding=%d] to %s\n",FL, hinfo->content_transfer_encoding, fullfilename); if (!f) { /** If we cannot open the file for reading, leave an error and return -1 **/ LOGGER_log("%s:%d:MIME_decode_text:ERROR: print-quotable input stream broken.",FL); return -1; } if (f) { /** If we were able to open the input file, try opening the output file and process the data **/ of = fopen(fullfilename,"w"); if (!of) { /** If we were unable to open the output file, report the error and return -1 **/ LOGGER_log("%s:%d:MIME_decode_text:ERROR: cannot open %s for writing",FL,fullfilename); return -1; } while ((get_result = FFGET_fgets(line,1023,f))&&(of)) { int line_len = strlen(line); linecount++; // if (MIME_DPEDANTIC) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line=%s",FL,line); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: line[len=%d]=%s",FL,line_len,line); //20041217-1529:PLD: if (line[0] == '-') { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Testing boundary",FL); if ((BS_count() > 0)&&(BS_cmp(line,line_len))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Hit a boundary on the line",FL); lastlinewasboundary = 1; result = 0; break; } } if (lastlinewasboundary == 0) { if (hinfo->content_transfer_encoding == _CTRANS_ENCODING_QP) { size_t bc; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Hit a boundary on the line",FL); decodesize = MDECODE_decode_qp_text(line); bc = fwrite(line, 1, decodesize, of); if (bc != decodesize) LOGGER_log("%s:%d:MIME_decode_text:WARNING: Only wrote %d of %d bytes", FL, bc, decodesize); } else { fprintf(of,"%s",line); } if ((!file_has_uuencode)&&( UUENCODE_is_uuencode_header( line ))) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: UUENCODED data located in file.\n",FL); file_has_uuencode = 1; } } // linecount++; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: End processing line.",FL); } // while if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done writing output file '%s'...now attempting to close.",FL, fullfilename); // if the file is still safely open if (of) { fclose(of); } // if file still safely open if (linecount == 0) { result = MIME_STATUS_ZERO_FILE; return result; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Closed.",FL); } // if main input file stream was open // If our input from the file was invalid due to EOF or other // then we return a -1 code to indicate that the end has // occured. // if (!get_result) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: FFGET module ran out of file data while attempting to decode",FL); // result = -1; result = MIME_ERROR_FFGET_EMPTY; // 20040305-1323:PLD } // If there was UUEncoded portions [potentially] in the email, the // try to extract them using the MIME_decode_uu() // if (file_has_uuencode) { char ffname[256]; snprintf(ffname,256,"%s/%s", unpackdir, hinfo->filename); // PLD-20040627-1212 // Make sure uudec_name is blank too // hinfo->uudec_name[0] = '\0'; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding UUencoded data in file '%s'\n",FL,hinfo->filename); //result = UUENCODE_decode_uu( NULL, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep ); // Attempt to decode the UUENCODED data in the file, // NOTE - hinfo->uudec_name is a blank buffer which will be filled by the UUENCODE_decode_uu // function once it has located a filename in the UUENCODED data. A bit of a problem here // is that it can only hold ONE filename! // // NOTE - this function returns the NUMBER of attachments it decoded in the return value! Don't // propergate this value unintentionally to parent functions (ie, if you were thinking it was // an error-status return value result = UUENCODE_decode_uu( NULL, unpackdir, ffname, hinfo->uudec_name, sizeof(hinfo->uudec_name), 1, keep ); if (result == -1) { switch (uuencode_error) { case UUENCODE_STATUS_SHORT_FILE: case UUENCODE_STATUS_CANNOT_OPEN_FILE: case UUENCODE_STATUS_CANNOT_FIND_FILENAME: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_raw:DEBUG: Nullifying uuencode_error result %d",FL, uuencode_error); result = 0; break; case UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Failure to allocate memory for UUdecode process",FL); return -1; break; default: LOGGER_log("%s:%d:MIME_decode_raw:ERROR: Unknown return code from UUDecode process [%d]",FL,uuencode_error); return -1; } } if ( result > 0 ) { glb.attachment_count += result; result = 0; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: hinfo = %p\n",FL,hinfo); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Done. [ UUName = '%s' ]\n",FL,hinfo->uudec_name); if (strncasecmp(hinfo->uudec_name,"winmail.dat",11)==0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Decoding TNEF format\n",FL); snprintf(hinfo->filename, 128, "%s", hinfo->uudec_name); MIME_decode_TNEF( unpackdir, hinfo, keep ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: Completed decoding UUencoded data.\n",FL); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_text:DEBUG: result=%d ----------------Done\n",FL,result); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_64 ID:1 Purpose: This routine is very very very important, it's the key to ensuring we get our attachments out of the email file without trauma! NOTE - this has been -slightly altered- in order to make provision of the fact that the attachment may end BEFORE the EOF is received as is the case with multiple attachments in email. Hence, we now have to detect the start character of the "boundary" marker I may consider testing the 1st n' chars of the boundary marker just incase it's not always a hypen '-'. Input: FGET_FILE *f: stream we're reading from char *unpackdir: directory we have to write the file to struct MIMEH_header_info *hinfo: Auxillairy information such as the destination filename Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_64( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo ) { int i; int cr_total = 0; int cr_count = 0; /* the number of consecutive \n's we've read in, used to detect End of B64 enc */ int ignore_crcount = 0; /* If we have a boundary in our emails, then ignore the CR counts */ int stopcount = 0; /* How many stop (=) characters we've read in */ int eom_reached = 0; /* flag to say that we've reached the End-Of-MIME encoding. */ int status = 0; /* Overall status of decoding operation */ int c; /* a single char as retrieved using MIME_get_char() */ int char_count = 0; /* How many chars have been received */ int boundary_crash = 0; /* if we crash into a boundary, set this */ long int bytecount=0; /* The total file decoded size */ char output[3]; /* The 4->3 byte output array */ char input[4]; /* The 4->3 byte input array */ char fullMIME_filename[_MIME_STRLEN_MAX]=""; /* Full Filename of output file */ // Write Buffer routine unsigned char *writebuffer; unsigned char *wbpos; int wbcount = 0; int loop; int of; /* output file pointer */ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: attempting to decode '%s'", FL, hinfo->filename); /* generate the MIME_filename, and open it up... */ if (glb.unique_names) MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method ); snprintf(fullMIME_filename,_MIME_STRLEN_MAX,"%s/%s",unpackdir,hinfo->filename); //of = fopen(fullMIME_filename,"wb"); of = open(fullMIME_filename, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); /* if we were unable to open the output file, then we better log an error and drop out */ if (of < 0) { LOGGER_log("%s:%d:MIME_decode_64:ERROR: Cannot open output file %s for BASE64 decoding. (%s)",FL,fullMIME_filename, strerror(errno)); // exit(_EXITERR_BASE64_OUTPUT_NOT_OPEN); return -1; } // Allocate the write buffer. By using the write buffer we gain an additional 10% in performance // due to the lack of function call (fwrite) overheads writebuffer = malloc( _MIME_WRITE_BUFFER_SIZE *sizeof(unsigned char)); if (!writebuffer) { LOGGER_log("%s:%d:MIME_decode_64:ERROR: cannot allocate %dbytes of memory for the write buffer",FL, _MIME_WRITE_BUFFER_SIZE); return -1; } else { wbpos = writebuffer; wbcount = 0; } /* Set our ignore_crcount flag */ if (BS_count() > 0) { //LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Ignore CR set to 1",FL); ignore_crcount = 1; } /* collect prefixing trash (if any, such as spaces, CR's etc)*/ // c = MIME_getchar_start(f); /* and push the good char back */ // FFGET_ungetc(f,c); c = '\0'; /* do an endless loop, as we're -breaking- out later */ while (1) { int lastchar_was_linebreak=0; /* Initialise the decode buffer */ input[0] = input[1] = input[2] = input[3] = 0; // was '0' - Stepan Kasal patch /* snatch 4 characters from the input */ for (i = 0; i < 4; i++) { // Get Next char from the file input // // A lot of CPU is wasted here due to function call overheads, unfortunately // I cannot yet work out how to make this call (FFGET) operate effectively // without including a couple of dozen lines in this section. // // Times like this C++'s "inline" statement would be nice. // //----------INLINE Version of FFGET_getchar() // do { if ((c == '\n')||(c == '\r')) lastchar_was_linebreak = 1; else lastchar_was_linebreak = 0; if (f->ungetcset) { f->ungetcset = 0; c = f->c; } else { if ((!f->startpoint)||(f->startpoint > f->endpoint)) { FFGET_getnewblock(f); } if (f->startpoint <= f->endpoint) { c = *f->startpoint; f->startpoint++; } else { c = EOF; } } } while ( (c != EOF) && ( c < ' ' ) && ( c != '\n' ) && (c != '-') && (c != '\r') ); // //-------END OF INLINE--------------------------------------------- if ((lastchar_was_linebreak == 1)&&(MIME_DNORMAL)) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Last char was linebreak",FL); if ((ignore_crcount == 1)&&(c == '-')) //&&(lastchar_was_linebreak == 1)) { int hit = 0; char *p; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: leader '-' found at %50s",FL,(f->startpoint -1)); p = strchr((f->startpoint -1), '\n'); if (p == NULL) { /* The boundary test was failing because sometimes the full line * wasn't available to test against in BM_cmp(), so if we can't * see a \n (or \r even), then we should load up some more data */ char scratch[1024]; FFGET_fgets(scratch,sizeof(scratch), f); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Scratch = '%s'", FL, scratch); hit = BS_cmp(scratch,strlen(scratch) +1); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Boundary hit = %d", FL, hit); } else { *p = '\0'; hit = BS_cmp((f->startpoint -1),strlen(f->startpoint) +1); *p = '\n'; } if (hit > 0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Boundary detected and breaking out ",FL); // FFGET_fgets(scratch,sizeof(scratch),f); // eom_reached = 1; boundary_crash = 1; break; } else { /** 20041105-22H56:PLD: Applied Stepan Kasal patch **/ i--; continue; } } // If we get a CR then we need to check a few things, namely to see // if the BASE64 stream has terminated ( indicated by two consecutive // \n's // // This should not be affected by \r's because the \r's will just pass // through and be absorbed by the detection of possible 'invalid' // characters if ((ignore_crcount == 0)&&(c == '\n')) { cr_count++; cr_total++; // Because there are some mail-agents out there which are REALLY naughty // and do things like sticking random blank lines into B64 data streams // (why o why!?) we need to have a settable option here to decide if // we want to test for double-CR breaking or just ignore it // // To avoid this, we relax the double-CR extention, and say "Okay, we'll let // you have a single blank line - but any more than that and we'll slap you // hand". Honestly, I do not like this solution, but it seems to be the only // way to preserve decoding accurancy without resorting to having to use // a command line switch to dictate which method to use ( NO-CR's or ignore // CR's ). if (cr_count > 2) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: EOF Reached due to two consecutive CR's on line %d\n",FL,cr_total); eom_reached++; break; } else { char_count = 0; i--; continue; } // else if it wasn't our 3rd CR } else { cr_count=0; } /* if we get an EOF char, then we know something went wrong */ if ( c == EOF ) { size_t bc; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: input stream broken for base64 decoding for file %s. %ld bytes of data in buffer to be written out\n",FL,hinfo->filename,wbcount); status = MIME_ERROR_B64_INPUT_STREAM_EOF; //fwrite(writebuffer, 1, wbcount, of); bc = write( of, writebuffer, wbcount); if (bc != wbcount) LOGGER_log("%s:%d:MIME_decode_64:WARNING: Could only write %d of %d bytes", FL, bc, wbcount); close(of); if (writebuffer) free(writebuffer); return status; break; } /* if c was the EOF */ else if (c == '=') { // Once we've found a stop char, we can actually just "pad" in the rest // of the stop chars because we know we're at the end. Some MTA's dont // put in enough stopchars... at least it seems X-MIMEOLE: Produced By Microsoft MimeOLE V5.50.4133.2400 // doesnt. if (i == 2) { input[2] = input[3] = (char)b64[c]; } else if (i == 3) { input[3] = (char)b64[c]; } // NOTE------ // Previously we relied on the fact that if we detected a stop char, that FFGET() // would automatically absorb the data till EOL. This is no longer the case as we // are now only retrieving data byte at a time. // So, now we -absorb- till the end of the line using FFGET_fgets() stopcount = 4 -i; FFGET_fgets(scratch,sizeof(scratch),f); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: Stop char detected pos=%d...StopCount = %d\n",FL,i,stopcount); i = 4; break; // out of FOR. } /* else if ((char_count == 0)&&(c == '-' )) { if (FFGET_fgetc(f) == '-') { boundary_crash++; eom_reached++; break; } } */ /* test for and discard invalid chars */ if (b64[c] == 0x80) { i--; continue; } // Finally, if we get this far without the character having been picked // out as some special meaning character, we decode it and place the // resultant byte into the input[] array. input[i] = (char)b64[c]; /* assuming we've gotten this far, then we increment the char_count */ char_count++; } // FOR // now that our 4-char buffer is full, we can do some fancy bit-shifting and get the required 3-chars of 8-bit data output[0] = (input[0] << 2) | (input[1] >> 4); output[1] = (input[1] << 4) | (input[2] >> 2); output[2] = (input[2] << 6) | input[3]; // determine how many chars to write write and check for errors if our input char count was 4 then we did receive a propper 4:3 Base64 block, hence write it if (i == 4) { // If our buffer is full beyond the 'write out limit', then we write the buffered // data to the file - We use this method in order to save calling fwrite() too // many times, thus avoiding function call overheads and [ possibly ] disk write // interrupt costs. if ( wbcount > _MIME_WRITE_BUFFER_LIMIT ) { size_t bc; bc = write ( of, writebuffer, wbcount ); if (bc != wbcount) LOGGER_log("%s:%d:MIME_decode_64:WARNING: Could only write %d of %d bytes", FL, bc, wbcount); // fwrite(writebuffer, 1, wbcount, of); wbpos = writebuffer; wbcount = 0; } // Copy our converted bytes to the write buffer for (loop = 0; loop < (3 -stopcount); loop++) { *wbpos = output[loop]; wbpos++; wbcount++; } // tally up our total byte conversion count bytecount+=(3 -stopcount); } else if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: could not attain 4 bytes input\n",FL); // if we wrote less than 3 chars, it means we were at the end of the encoded file thus we exit if ((eom_reached)||(stopcount > 0)||(boundary_crash)||(i!=4)) { // Write out the remaining contents of our write buffer - If we don't do this // we'll end up with truncated files. if (wbcount > 0) { size_t bc; //fwrite(writebuffer, 1, wbcount, of); bc = write( of, writebuffer, wbcount); if (bc != wbcount) LOGGER_log("%s:%d:MIME_decode_64:WARNING: Could only write %d of %d bytes", FL, bc, wbcount); } /* close the output file, we're done writing to it */ close(of); /* if we didn't really write anything, then trash the file */ if (bytecount == 0) { // unlink(fullMIME_filename); status = MIME_BASE64_STATUS_ZERO_FILE; } if (boundary_crash) { // Absorb to end of line // status = MIME_BASE64_STATUS_HIT_BOUNDARY; // was _BOUNDARY_CRASH } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_64:DEBUG: File size = %ld bytes, Exit Status = %d, Boundary Crash = %d\n",FL, bytecount, status, boundary_crash); if (writebuffer) free(writebuffer); return status; } // if End-of-MIME or Stopchars appeared } // while if (writebuffer) free(writebuffer); return status; } /*-----------------------------------------------------------------\ Function Name : MIME_decode_64_cleanup Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *hinfo, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_decode_64_cleanup( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo) { int result = 0; char buffer[128]; while (FFGET_fgets(buffer, sizeof(buffer), f)) { if (FFGET_feof(f) != 0) break; if (BS_cmp(buffer,strlen(buffer)) > 0) break; } return result; } /*------------------------------------------------------------------------ Procedure: MIME_doubleCR_decode ID:1 Purpose: Decodes a text sequence as detected in the processing of the MIME headers. This is a specialised call, not really a normal part of MIME decoding, but is required in order to deal with decyphering MS Outlook visable defects. Input: char *filename: Name of the encoded file we need to decode char *unpackdir: Directory we need to unpack the file to struct MIMEH_header_info *hinfo: Header information already gleaned from the headers int current_recursion_level: How many nest levels we are deep Output: Errors: ------------------------------------------------------------------------*/ int MIME_doubleCR_decode( char *filename, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level ) { int result = 0; struct MIMEH_header_info h; char *p; // PLD:260303-1317 // if ((p=strrchr(filename,'/'))) p++; // else p = filename; p = filename; // * Initialise the header fields h.uudec_name[0] = '\0'; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: filename=%s, path=%s, recursion=%d", FL, filename, unpackdir, current_recursion_level ); memcpy(&h, hinfo, sizeof(h)); // Works for ripMIME snprintf(h.filename, sizeof(h.filename), "%s/%s", unpackdir, p); snprintf(h.filename, sizeof(h.filename), "%s", p); /// Works for Xamime if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_doubleCR_decode:DEBUG: header.filename = %s", FL, h.filename ); if (MIME_is_file_RFC822(filename)) { if (MIME_VERBOSE) LOGGER_log("Attempting to decode Double-CR delimeted MIME attachment '%s'\n",filename); result = MIME_unpack( unpackdir, filename, current_recursion_level ); // 20040305-1303:PLD - Capture the result of the unpack and propagate up } else if (UUENCODE_is_file_uuencoded(h.filename)) { if (MIME_VERBOSE) LOGGER_log("Attempting to decode UUENCODED attachment from Double-CR delimeted attachment '%s'\n",filename); UUENCODE_set_doubleCR_mode(1); result = UUENCODE_decode_uu(NULL, unpackdir, filename, h.uudec_name, _MIMEH_FILENAMELEN_MAX , 1, 1 ); UUENCODE_set_doubleCR_mode(0); glb.attachment_count += result; result = 0; } return result; } /*------------------------------------------------------------------------ Procedure: MIME_read ID:1 Purpose: Reads data from STDIN and saves the mailpack to the filename specified Input: char *mpname: full pathname of the file to save the data from STDIN to Output: Errors: 28-Feb-2003 This function has been modified to use feof() and fread/fwrite calls in order to ensure that binary data in the input does not cause the reading to prematurely terminate [ as it used to prior to the modification ] ------------------------------------------------------------------------*/ size_t MIME_read_raw( char *src_mpname, char *dest_mpname, size_t rw_buffer_size ) { size_t readcount, writecount; size_t fsize=-1; char *rw_buffer; int fin; int fout; if (*src_mpname == '\0') { fin = STDIN_FILENO; } else { fin = open(src_mpname, O_RDONLY); if (fin == -1) { LOGGER_log("%s:%d:MIME_read_raw:ERROR: Cannot open '%s' for reading (%s)", FL, src_mpname, strerror(errno)); return -1; } } rw_buffer = malloc( rw_buffer_size *sizeof(char) ); if ( !rw_buffer ) { LOGGER_log("%s:%d:MIME_read:ERROR: could not allocate 4K of memory for file read buffer\n",FL ); return -1; } /* open up our input file */ fout = open(dest_mpname,O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if (fout == -1) { LOGGER_log("%s:%d:MIME_read_raw:ERROR: Cannot open '%s' for writing. (%s)",FL, dest_mpname, strerror(errno)); return -1; } fsize=0; /* while there is more data, consume it */ do { readcount = read( fin, rw_buffer, rw_buffer_size ); if (readcount > 0) { writecount = write( fout, rw_buffer, readcount ); if ( readcount != writecount ) { LOGGER_log("%s:%d:MIME_read:ERROR: Attempted to write %d bytes, but only managed %d to file '%s'",FL, readcount, writecount, dest_mpname ); } fsize += writecount; } } while (readcount > 0); /* clean up our buffers and close */ close(fout); if (*src_mpname != '\0') { close(fin); } if ( rw_buffer ) free( rw_buffer ); return (size_t)(fsize); } /*------------------------------------------------------------------------ Procedure: MIME_read ID:1 Purpose: Reads data from STDIN and saves the mailpack to the filename specified Input: char *mpname: full pathname of the file to save the data from STDIN to Output: Errors: 28-Feb-2003 This function has been modified to use feof() and fread/fwrite calls in order to ensure that binary data in the input does not cause the reading to prematurely terminate [ as it used to prior to the modification ] ------------------------------------------------------------------------*/ int MIME_read( char *mpname ) { long int fsize=-1; char *buffer; size_t readcount, writecount; FILE *fout; buffer = malloc( MIME_MIME_READ_BUFFER_SIZE *sizeof(char) ); if ( !buffer ) { LOGGER_log("%s:%d:MIME_read:ERROR: could not allocate 4K of memory for file read buffer\n",FL ); return -1; } /* open up our input file */ fout = fopen(mpname,"w"); /* check that out file opened up okay */ if (!fout) { LOGGER_log("%s:%d:MIME_read:ERROR: Cannot open file %s for writing... check permissions perhaps?",FL,mpname); //exit(_EXITERR_MIMEREAD_CANNOT_OPEN_OUTPUT); return -1; } /* assuming our file actually opened up */ if (fout) { fsize=0; /* while there is more data, consume it */ while ( !feof(stdin) ) { readcount = fread( buffer, 1, ((MIME_MIME_READ_BUFFER_SIZE -1) *sizeof(char)), stdin ); if ( readcount > 0 ) { writecount = fwrite( buffer, 1, readcount, fout ); fsize += writecount; if ( readcount != writecount ) { LOGGER_log("%s:%d:MIME_read:ERROR: Attempted to write %d bytes, but only managed %d to file '%s'",FL, readcount, writecount, mpname ); } } else { break; } } /* clean up our buffers and close */ fflush(fout); fclose(fout); if ( feof(stdin) ) clearerr(stdin); } /* end if fout was received okay */ if ( buffer ) free( buffer ); /* return our byte count in KB */ return (int)(fsize /1024); } /*------------------------------------------------------------------------ Procedure: MIME_init ID:1 Purpose: Initialise various required parameters to ensure a clean starting of MIME decoding. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_init( void ) { BS_init(); // Boundary-stack initialisations MIMEH_init(); // Initialise MIME header routines. UUENCODE_init(); // uuen:coding decoding initialisations FNFILTER_init(); // Filename filtering MDECODE_init(); // ISO filename decoding initialisation TNEF_init(); // TNEF decoder glb.header_defect_count = 0; glb.filecount = 0; glb.attachment_count = 0; glb.current_line = 0; glb.verbosity = 0; glb.verbose_defects = 0; glb.debug = 0; glb.quiet = 0; glb.syslogging = 0; glb.stderrlogging = 1; glb.unique_names = 0; glb.save_headers = 0; glb.no_nameless = 0; glb.mailbox_format = 0; glb.name_by_type = 0; glb.rename_method = _MIME_RENAME_METHOD_INFIX; glb.header_longsearch = 0; glb.max_recursion_level = _RECURSION_LEVEL_DEFAULT; glb.decode_qp = 1; glb.decode_b64 = 1; glb.decode_tnef = 1; glb.decode_ole = 1; glb.decode_uu = 1; glb.decode_mht = 1; glb.multiple_filenames = 1; glb.blankzone_save_option = MIME_BLANKZONE_SAVE_TEXTFILE; glb.blankzone_saved = 0; snprintf( glb.headersname, sizeof(glb.headersname), "_headers_" ); snprintf( glb.blankfileprefix, sizeof( glb.blankfileprefix ), "textfile" ); glb.blankfileprefix_expliticly_set = 0; glb.subject[0]='\0'; glb.out_charset[0] = '\0'; return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_generate_multiple_hardlink_filenames Returns Type : int ----Parameter List 1. struct MIMEH_header_info *hinfo, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: If there is more than one filename/name for a given attachment due to an exploit attempt,then we need to generate the required hardlinks to replicate this in our output. -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_generate_multiple_hardlink_filenames(struct MIMEH_header_info *hinfo, char *unpackdir) { char *name; char oldname[1024]; if (glb.multiple_filenames == 0) return 0; //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Generating hardlinks for %s",FL, hinfo->filename); snprintf(oldname,sizeof(oldname),"%s/%s",unpackdir, hinfo->filename); if (SS_count(&(hinfo->ss_names)) > 1){ do { name = SS_pop(&(hinfo->ss_names)); if (name != NULL) { char *np; char newname[1024]; int rv; /** Strip off any leading path **/ np = strrchr(name, '/'); if (np) np++; else np = name; snprintf(newname,sizeof(newname),"%s/%s",unpackdir, np); //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname); rv = link(oldname, newname); if (rv == -1) { if (errno != EEXIST) { LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno)); } } else { if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE)) { glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } } } while(name != NULL); } if (SS_count(&(hinfo->ss_filenames)) > 1) { do { name = SS_pop(&(hinfo->ss_filenames)); if (name != NULL) { char newname[1024]; int rv; snprintf(newname,sizeof(newname),"%s/%s",unpackdir, name); //LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:DEBUG: Linking %s->%s",FL,newname, oldname); rv = link(oldname, newname); if (rv == -1) { if (errno != EEXIST) { LOGGER_log("%s:%d:MIME_generate_multiple_hardlink_filenames:WARNING: While trying to create '%s' link to '%s' (%s)",FL, newname, oldname,strerror(errno)); } } else { if ((glb.filename_decoded_reporter != NULL)&&(MIME_VERBOSE)) { glb.filename_decoded_reporter( name, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } } } while(name != NULL); } return 0; } /*------------------------------------------------------------------------ Procedure: MIME_decode_encoding ID:1 Purpose: Based on the contents of hinfo, this function will call the required function needed to decode the contents of the file which is contained within the MIME structure Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_decode_encoding( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, struct SS_object *ss ) { int keep = 1; int result = -1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Start:DEBUG: (%s)\n",FL, hinfo->filename); // If we have a valid filename, then put it through the process of // cleaning and filtering if (isprint((int)hinfo->filename[0])) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is valid, cleaning\n",FL); FNFILTER_filter(hinfo->filename, _MIMEH_FILENAMELEN_MAX); /* check out thefilename for ISO filenames */ } // If the filename is NOT valid [doesn't have a printable first char] // then we must create a new file name for it. // if (hinfo->filename[0] == '\0') { if (glb.name_by_type == 1) { // If we're to name our nameless files based on the content-type // then we need to get the content-type string from the hinfo // and then strip it of any nasty characters (such as / and \) char *filename_prefix; filename_prefix = strdup( hinfo->content_type_string ); if (filename_prefix != NULL) { char *pp; pp = filename_prefix; while (*pp) { if ((*pp == '/') || (*pp == '\\')) *pp = '-'; pp++; } snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%s%d", glb.blankfileprefix_expliticly_set?glb.blankfileprefix:"", filename_prefix, glb.filecount ); free(filename_prefix); } else { snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount ); } } else { // If we don't care to rename our files based on the content-type // then we'll simply use the blankfileprefix. snprintf( hinfo->filename, _MIMEH_FILENAMELEN_MAX, "%s%d", glb.blankfileprefix, glb.filecount ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Filename is empty, setting to default...(%s)\n",FL, hinfo->filename); if (glb.no_nameless) keep = 0; glb.filecount++; } else if (strncmp(hinfo->filename, glb.blankfileprefix, strlen(glb.blankfileprefix)) != 0) { // If the filename does not contain the blankfile prefix at the beginning, then we // will consider it a normal attachment, thus, we // need to increment the attachment count glb.attachment_count++; } // If we are required to have "unique" filenames for everything, rather than // allowing ripMIME to overwrite stuff, then we put the filename through // its tests here if ((glb.unique_names)&&(keep)) { MIME_test_uniquename( unpackdir, hinfo->filename, glb.rename_method ); } // If the calling program requested verbosity, then indicate that we're decoding // the file here if ((keep)&&(MIME_VERBOSE)) { if (MIME_VERBOSE_12) { LOGGER_log("Decoding: %s\n", hinfo->filename); } else { // Now, please bare with me on this horrid little piece of tortured code, // ... basically, now that we're using LOGGER_log to generate our output, we // need to compose everything onto a single LOGGER_log() call, otherwise our // output will get split into several lines, not entirely the most plesant // thing we want to see when we've got another program most likely reading // the output. // // The %s%s pair is DELIBERATELY pressed up against the 'Decoding' word because // otherwise, if we are not outputting any data, we'll end up with a double // space between Decoding and filename, certainly not very good thing to do // if we're trying to present a consistant output. // // The parameters for the content-type output are decided on by the result // of the MIMEH_get_verbosity_contenttype() call. If the call returns > 0 // then the TRUE token is selected to be displayed, else the FALSE token // ( in this case, an empty string "" ) is selected. // The format of the evaluation is: // // (logic-test?true-expression:false-expression) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: About to execute callback [0x%p]",FL,glb.filename_decoded_reporter); if (glb.filename_decoded_reporter != NULL) { glb.filename_decoded_reporter( hinfo->filename, (MIMEH_get_verbosity_contenttype()?hinfo->content_type_string:NULL)); } } // If we were using the new filename telling format } // If we were telling the filename (verbosity) if (1) { char *fp; /** Find the start of the filename. **/ fp = strrchr(hinfo->filename, '/'); if (fp) fp++; else fp = hinfo->filename; //LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Pushing filename %s to the stack",FL,fp); // 20040305-1419:PLD // Store the filename we're going to use to save the file to in the filename stack SS_push(ss, fp, strlen(fp)); } // Select the decoding method based on the content transfer encoding // method which we read from the headers if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: ENCODING = %d\n",FL, hinfo->content_transfer_encoding); switch (hinfo->content_transfer_encoding) { case _CTRANS_ENCODING_B64: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding BASE64 format\n",FL); result = MIME_decode_64(f, unpackdir, hinfo); switch (result) { case MIME_ERROR_B64_INPUT_STREAM_EOF: break; case MIME_BASE64_STATUS_HIT_BOUNDARY: result = 0; break; case 0: result = MIME_decode_64_cleanup(f, unpackdir, hinfo); break; default: break; } break; case _CTRANS_ENCODING_7BIT: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 7BIT format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_8BIT: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding 8BIT format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_BINARY: case _CTRANS_ENCODING_RAW: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding RAW format\n",FL); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_QP: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding Quoted-Printable format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); break; case _CTRANS_ENCODING_UUENCODE: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UUENCODED format\n",FL); // Added as a test - remove if we can get this to work in a better way snprintf(hinfo->uudec_name,sizeof(hinfo->uudec_name),"%s",hinfo->filename); result = UUENCODE_decode_uu(f, unpackdir, hinfo->filename, hinfo->uudec_name, sizeof(hinfo->uudec_name), 0, keep ); glb.attachment_count += result; // Because this is a file-count, it's not really an 'error result' as such, so, set the // return code back to 0! result = 0; break; case _CTRANS_ENCODING_UNKNOWN: switch (hinfo->content_disposition) { case _CDISPOSITION_FORMDATA: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format of FORMDATA disposition\n",FL); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; default: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNKNOWN format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: UNKNOWN Decode completed, result = %d\n",FL,result); break; case _CTRANS_ENCODING_UNSPECIFIED: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding UNSPECIFIED format\n",FL); result = MIME_decode_text(f, unpackdir, hinfo, keep); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding result for UNSPECIFIED format = %d\n",FL, result); // 20040114-1236:PLD: Added nested mail checking // // Sometimes mailpacks have false headers at the start, resulting // in ripMIME terminating the recursion process too early. This // little test here checks to see if the output of the file is // a nested MIME email (or even an ordinary email // // It should be noted, that the reason why the test occurs /here/ is // because dud headers will always result in a UNSPECIFIED encoding // // Original sample mailpack was sent by Farit - thanks. if (1) { snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename); LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG:REMOVEME: Testing for RFC822 headers in file %s",FL,hinfo->scratch); if (MIME_is_file_RFC822(hinfo->scratch) > 0 ) { // 20040305-1304:PLD: unpack the file, propagate result upwards result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss ); } } break; default: if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding format is not defined (%d)\n",FL, hinfo->content_transfer_encoding); result = MIME_decode_raw(f, unpackdir, hinfo, keep); break; } // Analyze our results switch (result) { case 0: break; case MIME_STATUS_ZERO_FILE: return MIME_STATUS_ZERO_FILE; break; case MIME_ERROR_FFGET_EMPTY: return result; break; case MIME_ERROR_RECURSION_LIMIT_REACHED: return result; break; } if ((result != -1)&&(result != MIME_STATUS_ZERO_FILE)) { #ifdef RIPOLE // If we have OLE decoding active and compiled in, then // do a quick attempt at decoding the file, fortunately // because the ripOLE engine detects if the file is not an // OLE file, it does not have a major impact on the // performance of the ripMIME decoding engine if (glb.decode_ole > 0) { MIME_decode_OLE( unpackdir, hinfo, 0 ); } #endif // If our content-type was TNEF, then we need to decode it // using an external decoding engine (see tnef/tnef.c) // Admittingly, this is not the most ideal way of picking out // TNEF files from our decoded attachments, but, for now // it'll have to do, besides, it does work fine, without any // side-effects if (hinfo->content_type == _CTYPE_TNEF) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Decoding TNEF format\n",FL); glb.attachment_count++; MIME_decode_TNEF( unpackdir, hinfo, 0 ); } // Decode TNEF // Look for Microsoft MHT files... and try decode them. // MHT files are just embedded email files, except they're usually // encoded as BASE64... so, you have to -unencode- them, to which // you find out that lo, you have another email. if ((strstr(hinfo->filename,".mht"))||(strstr(hinfo->name,".mht")) ) { if (glb.decode_mht != 0) { // Patched 26-01-03: supplied by Chris Hine if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Microsoft MHT format email filename='%s'\n",FL, hinfo->filename); snprintf(hinfo->scratch,sizeof(hinfo->scratch),"%s/%s",unpackdir,hinfo->filename); // 20040305-1304:PLD: unpack the file, propagate result upwards result = MIME_unpack_single( unpackdir, hinfo->scratch, (hinfo->current_recursion_level+1),ss ); } } // Decode MHT files } // If result != -1 // End. MIME_generate_multiple_hardlink_filenames(hinfo,unpackdir); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_decode_encoding:DEBUG: Done for filename = '%s'",FL,hinfo->filename); return result; } /*------------------------------------------------------------------------ Procedure: MIME_postdecode_cleanup ID:1 Purpose: Performs any cleanup operations required after the immediate completion of the mailpack decoding. Input: char *unpackdir - directory where the mailpack was unpacked to Output: none Errors: ------------------------------------------------------------------------*/ int MIME_postdecode_cleanup( char *unpackdir, struct SS_object *ss ) { char fullpath[256]; int result; result = 0; do { char *filename; if (MIME_DNORMAL) LOGGER_log("%s:%d: Popping file...",FL); filename = SS_pop(ss); if (filename == NULL) break; if (MIME_DNORMAL) LOGGER_log("%s:%d: Popped file '%s'",FL, filename); if ( strncmp( glb.blankfileprefix, filename, strlen( glb.blankfileprefix ) ) == 0 ) { snprintf( fullpath, sizeof(fullpath), "%s/%s", unpackdir, filename ); result = unlink( fullpath ); if (MIME_VERBOSE) { if (result == -1) LOGGER_log("Error removing '%s'; %s", fullpath, strerror(errno)); else LOGGER_log("Removed %s [status = %d]\n", fullpath, result ); } } } while (1); return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_multipart Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *hinfo, 4. int current_recursion_level, 5. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_multipart( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { int result = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Decoding multipart/embedded \n",FL); // If there is no filename, then we have a "standard" // embedded message, which can be just read off as a // continuous stream (simply with new boundaries // if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' )) { char *p; // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it // (ie, it's just the existing email with a new set of headers around it // then rather than saving it to a file, we'll just peel off these outter // layer of headers and get into the core of the message. This is why we // call unpack_stage2() because this function just reads the next set of // headers and decodes. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); p = BS_top(); if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary)); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (result == 0) { // Because we're calling MIME_unpack_single again [ie, recursively calling it // we need to now adjust the input-filename so that it correctly is prefixed // with the directory we unpacked to. snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level, ss ); } } // else-if transfer-encoding != B64 && filename was empty if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_multipart:DEBUG: done handling '%s' result = %d",FL,h->filename, result); return result; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_rfc822 Returns Type : int ----Parameter List 1. FFGET_FILE *f, Input stream 2. char *unpackdir, Directory to write files to 3. struct MIMEH_header_info *hinfo, Header information structure 4. int current_recursion_level, Current recursion level 5. struct SS_object *ss , String stack containing already decoded file names ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_rfc822( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { /** Decode a RFC822 encoded stream of data from *f **/ int result = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Decoding RFC822 message\n",FL); /** If there is no filename, then we have a "standard" ** embedded message, which can be just read off as a ** continuous stream (simply with new boundaries **/ DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Filename='%s', encoding = %d",FL, h->filename, h->content_transfer_encoding); // if ((0)&&( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '0' )) 20041217-1635:PLD: if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&( h->filename[0] == '\0' )) { /** Handle a simple plain text wrapped RFC822 email with no encoding applied to it **/ char *p; // If this is a simple 'wrapped' RFC822 email which has no encoding applied to it // (ie, it's just the existing email with a new set of headers around it // then rather than saving it to a file, we'll just peel off these outter // layer of headers and get into the core of the message. This is why we // call unpack_stage2() because this function just reads the next set of // headers and decodes. DMIME LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Non base64 encoding AND no filename, embedded message\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); p = BS_top(); if (p) PLD_strncpy(h->boundary, p,sizeof(h->boundary)); } else { /** ...else... if the section has a filename or B64 type encoding, we need to put it through extra decoding **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Embedded message has a filename, decoding to file %s",FL,h->filename); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Result of extracting %s is %d",FL,h->filename, result); if (result == 0) { /** Because we're calling MIME_unpack_single again [ie, recursively calling it we need to now adjust the input-filename so that it correctly is prefixed with the directory we unpacked to. **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,h->filename); snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: Now attempting to extract contents of '%s'",FL,scratch); result = MIME_unpack_single( unpackdir, scratch, current_recursion_level, ss ); result = 0; } } /** else-if transfer-encoding != B64 && filename was empty **/ if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_handle_rfc822:DEBUG: done handling '%s' result = %d",FL,h->filename, result); return result; } static size_t iconvert(iconv_t _iconv, char *from, size_t from_size, char **to, size_t *to_size) { assert(from); size_t inbytesleft = from_size; size_t outbytesleft = 0; if (*to_size == 0) outbytesleft = from_size; else outbytesleft = *to_size; char *to_str = malloc(outbytesleft); *to = to_str; *to_size = outbytesleft; char *from_str_pp = from; char *to_str_pp = to_str; size_t iconv_res = iconv(_iconv, &from_str_pp, &inbytesleft, &to_str_pp, &outbytesleft); *to_size -= outbytesleft; if (iconv_res == -1) { switch(errno) { case EILSEQ: LOGGER_log("%s: iconv: invalid multibyte sequence\n", FL); abort(); break; case EINVAL: break; case E2BIG: free(to_str); *to = 0; *to_size += inbytesleft; return iconvert(_iconv, from, from_size, to, to_size); break; default: LOGGER_log("%s: iconv: unknown error\n", FL); abort(); break; } } return inbytesleft; } int MIME_convert_charset(char *unpackdir, struct MIMEH_header_info *hinfo) { if (MIME_DNORMAL) LOGGER_log("charset: %s filename: %s", hinfo->charset,hinfo->filename); if ((glb.out_charset[0] == 0) || (strcmp(glb.out_charset, hinfo->charset) == 0)) return 0; iconv_t _iconv = iconv_open(glb.out_charset, hinfo->charset); if (_iconv == (iconv_t)-1) { LOGGER_log("%s:%d:MIME_convert_charset:ERROR: cannot open iconv for convert from '%s' to '%s'", FL, hinfo->charset, glb.out_charset); return -1; } FILE *ifile; // Output file FILE *ofile; // Output file char ifullfilename[1024]=""; // Filename of the input file char ofullfilename[1024]=""; // Filename of the output file char line[1024]; // The input lines from the file we're decoding int result = 0; char *get_result = &line[0]; snprintf(ifullfilename, sizeof(ifullfilename), "%s/%s", unpackdir, hinfo->filename); snprintf(ofullfilename, sizeof(ofullfilename), "%s/tmp_%s", unpackdir, hinfo->filename); ifile = fopen(ifullfilename, "r"); if (!ifile) { LOGGER_log("%s:%d:MIME_convert_charset:ERROR: cannot open %s for reading", FL, ifullfilename); return -1; } ofile = fopen(ofullfilename, "w"); if (!ofile) { LOGGER_log("%s:%d:MIME_convert_charset:ERROR: cannot open %s for writing", FL, ofullfilename); return -1; } int idx = 0; const size_t line_size = sizeof(line); size_t bytes_to_read = line_size; while ((get_result = fgets(&line[idx], bytes_to_read, ifile)) && (ofile)) { char *out_bytes = 0; size_t out_size = 0; size_t bytes_to_convert = strlen(line); size_t bytes_left = iconvert(_iconv, line, bytes_to_convert, &out_bytes, &out_size); if (bytes_left == 0) { const size_t writed = fwrite(out_bytes, 1, out_size, ofile); if (writed < out_size) { LOGGER_log("%s:%d:MIME_convert_charset:ERROR: error while write to '%s'", FL, ofullfilename); abort(); } idx = 0; bytes_to_read = line_size; } else { memmove(line, &line[bytes_to_convert - bytes_left], bytes_left); idx = bytes_left; bytes_to_read = line_size - bytes_left; } if (out_bytes) free(out_bytes); } fclose(ifile); fclose(ofile); iconv_close(_iconv); if (rename(ofullfilename, ifullfilename) == -1) { LOGGER_log("%s:%d:MIME_convert_charset:ERROR: rename from '%s' to '%s' failed: %s", FL, ofullfilename, ifullfilename, strerror(errno)); abort(); } return result; } /*-----------------------------------------------------------------\ Function Name : MIME_handle_plain Returns Type : int ----Parameter List 1. FFGET_FILE *f, 2. char *unpackdir, 3. struct MIMEH_header_info *h, 4. int current_recursion_level, 5. struct SS_object *ss , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_handle_plain( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *h, int current_recursion_level, struct SS_object *ss ) { /** Handle a plain text encoded data stream from *f **/ int result = 0; if (MIME_DNORMAL) LOGGER_log("charset: %s filename: %s", h->charset, h->filename); result = MIME_decode_encoding( f, unpackdir, h, ss ); if ((result == MIME_ERROR_FFGET_EMPTY)||(result == 0)) { /** Test for RFC822 content... if so, go decode it **/ snprintf(h->scratch,sizeof(h->scratch),"%s/%s",unpackdir,h->filename); if (MIME_is_file_RFC822(h->scratch)==1) { /** If the file is RFC822, then decode it using MIME_unpack_single() **/ if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(glb.header_longsearch); result = MIME_unpack_single( unpackdir, h->scratch, current_recursion_level, ss ); if (glb.header_longsearch != 0) MIMEH_set_header_longsearch(0); } MIME_convert_charset(unpackdir, h); } return result; } /*------------------------------------------------------------------------ Procedure: MIME_unpack_stage2 ID:1 Purpose: This function commenced with the file decoding of the attachments as required by the MIME structure of the file. Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_stage2( FFGET_FILE *f, char *unpackdir, struct MIMEH_header_info *hinfo, int current_recursion_level, struct SS_object *ss ) { int result = 0; struct MIMEH_header_info *h; char *p; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Start, recursion %d\n",FL, current_recursion_level); if (current_recursion_level > glb.max_recursion_level) { /** Test for recursion limits **/ if (MIME_VERBOSE) LOGGER_log("%s:%d:MIME_unpack_stage2:WARNING: Current recursion level of %d is greater than permitted %d"\ ,FL, current_recursion_level, glb.max_recursion_level); return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040306-1301:PLD } h = hinfo; // Get our headers and determin what we have... // if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing headers (initial)\n",FL); // Parse the headers, extracting what information we need /** 20041216-1102:PLD: Keep attempting to read headers until we get a sane set **/ do { /** Read next set of headers, repeat until a sane set of headers are found **/ result = MIMEH_parse_headers(f,h); DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Parsing of headers done, sanity = %d, result = %d",FL,h->sanity, result); } while ((h->sanity == 0)&&(result != -1)); DMIME LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Repeat loop of header-reading done, sanity = %d, result = %d",FL,h->sanity, result); glb.header_defect_count += MIMEH_get_defect_count(h); // Test the result output switch (result) { case -1: return MIME_ERROR_FFGET_EMPTY; break; } // Copy the subject over to our global structure if the subject is not already set. // this will give us the 'main' subject of the entire email and prevent // and subsequent subjects from clobbering it. //if (glb.subject[0] == '\0') snprintf(glb.subject, _MIME_STRLEN_MAX, "%s", h->subject ); if ((strlen(glb.subject) < 1)&&(h->subject != NULL)&&(strlen(h->subject) > 0)) { snprintf(glb.subject, sizeof(glb.subject), "%s", h->subject ); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Headers parsed, Result = %d, Boundary located = %d\n"\ ,FL,result, hinfo->boundary_located); // Test to see if we encountered any DoubleCR situations while we // were processing the headers. If we did encounter a doubleCR // then we need to decode that data and then reset the doubleCR // flag. if (MIMEH_get_doubleCR() != 0) { MIME_doubleCR_decode( MIMEH_get_doubleCR_name(), unpackdir, h, current_recursion_level); MIMEH_set_doubleCR( 0 ); FFGET_SDL_MODE = 0; } // If we located a boundary being specified (as apposed to existing) // then we push it to the BoundaryStack if (h->boundary_located) { char *lbc; char *fbc; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Boundary located, pushing to stack (%s)\n",FL,h->boundary); BS_push(h->boundary); // Get the first and last character positions of the boundary // so we can test these for quotes. fbc = h->boundary; lbc = h->boundary +strlen(h->boundary) -1; // Circumvent attempts to trick the boundary // Even though we may end up pushing 3 boundaries to the stack // they will be popped off if they're not needed. if (*fbc == '"') { BS_push((fbc +1)); MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); } if (*lbc == '"') { *lbc = '\0'; BS_push(h->boundary); *lbc = '"'; MIMEH_set_defect( hinfo, MIMEH_DEFECT_UNBALANCED_BOUNDARY_QUOTE); } h->boundary_located = 0; } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding in BOUNDARY-LESS mode\n",FL); if (h->content_type == _CTYPE_RFC822) { // Pass off to the RFC822 handler if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with RFC822 decoder\n",FL); result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss); } else if (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type)) { // Pass off to the multipart handler if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with Multipart decoder\n",FL); result = MIME_handle_multipart(f,unpackdir,h,current_recursion_level,ss); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding boundaryless file (%s)...\n",FL,h->filename); result = MIME_handle_plain( f, unpackdir,h,current_recursion_level,ss); } // else-if content was RFC822 or multi-part return result; } // End of the boundary-LESS mode ( processing the mail which has no boundaries in the primary headers ) if ((BS_top()!=NULL)&&(result == 0)) { // Commence decoding WITH boundary awareness. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding with boundaries (filename = %s)\n",FL,h->filename); // Decode the data in the current MIME segment // based on the header information retrieved from // the start of this function. result = MIME_decode_encoding(f, unpackdir, h, ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Done decoding, result = %d",FL,result); if (result == 0) { if (BS_top()!=NULL) { // As this is a multipart email, then, each section will have its // own headers, so, we just simply call the MIMEH_parse call again // and get the attachment details while ((result == 0)||(result == MIME_STATUS_ZERO_FILE)) { h->content_type = -1; h->filename[0] = '\0'; h->name[0] = '\0'; h->content_transfer_encoding = -1; h->content_disposition = -1; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding headers...\n",FL); do { result = MIMEH_parse_headers(f,h); } while ((h->sanity == 0)&&(result != -1)); glb.header_defect_count += MIMEH_get_defect_count(h); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Mime header parsing result = %d\n",FL, result); // 20040305-1331:PLD if (result == -1) { result = MIME_ERROR_FFGET_EMPTY; return result; } if (h->boundary_located) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Pushing boundary %s\n",FL, h->boundary); BS_push(h->boundary); h->boundary_located = 0; } if (result == _MIMEH_FOUND_FROM) { return _MIMEH_FOUND_FROM; } if (result == 0) { // If we locate a new boundary specified, it means we have a // embedded message, also if we have a ctype of RFC822 if ( (h->boundary_located) \ || (h->content_type == _CTYPE_RFC822)\ || (MIMEH_is_contenttype(_CTYPE_MULTIPART, h->content_type))\ ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Multipart/RFC822 mail headers found\n",FL); /* If there is no filename, then we have a "standard" * embedded message, which can be just read off as a * continuous stream (simply with new boundaries */ if (( h->content_type == _CTYPE_RFC822 )) { // If the content_type is set to message/RFC822 // then we simply read off the data to the next // boundary into a seperate file, then 'reload' // the file into ripMIME. Certainly, this is not // the most efficent way of dealing with nested emails // however, it is a rather robust/reliable way. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Chose Content-type == RFC822 clause",FL); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Calling MIME_decode_encoding()",FL); result = MIME_handle_rfc822(f,unpackdir,h,current_recursion_level,ss); // First up - extract the RFC822 body out of the parent mailpack // XX result = MIME_decode_encoding( f, unpackdir, h, ss ); // Now decode the RFC822 body. /** XX if (result == 0) { snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now calling MIME_unpack_single() on the file '%s' for our RFC822 decode operation.",FL, scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack( unpackdir, h->filename, current_recursion_level +1 ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Unpack result = %d", FL, result); result = 0; } else return result; // 20040305-1312:PLD */ } else if (( h->content_transfer_encoding != _CTRANS_ENCODING_B64)&&(h->filename[0] == '\0' )) { // Decode nameless MIME segments which are not BASE64 encoded // // To be honest, i've forgotten what this section of test is supposed to pick out // in terms of files - certainly it does pick out some, but I'm not sure what format // they are. Shame on me for not remembering, in future I must comment more. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: NON-BASE64 DECODE\n",FL); h->boundary_located = 0; result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); // When we've exited from decoding the sub-mailpack, we need to restore the original // boundary p = BS_top(); if (p) snprintf(h->boundary,_MIME_STRLEN_MAX,"%s",p); /** 20041207-0106:PLD: ** changed to test for _INLINE **/ //} else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) { } else if (( h->content_type = _CTYPE_MULTIPART_APPLEDOUBLE )&&(h->content_disposition != _CDISPOSITION_INLINE)) { // AppleDouble needs to be handled explicity - as even though it's // and embedded format, it does not have the normal headers->[headers->data] // layout of other nested emails if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Handle Appledouble explicitly",FL); result = MIME_unpack_stage2(f, unpackdir, h, current_recursion_level , ss); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: RFC822 Message to be decoded...\n",FL); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (result != 0) return result; // 20040305-1313:PLD else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Now running ripMIME over decoded RFC822 message...\n",FL); // Because we're calling MIME_unpack_single again [ie, recursively calling it // we need to now adjust the input-filename so that it correctly is prefixed // with the directory we unpacked to. snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, h->filename); snprintf(h->filename,sizeof(h->filename),"%s",scratch); //result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level +1, ss); result = MIME_unpack_single( unpackdir, h->filename, current_recursion_level,ss ); } } // else-if transfer-encoding wasn't B64 and filename was blank } else { // If the attachment included in this MIME segment is NOT a // multipart or RFC822 embedded email, we can then simply use // the normal decoding function to interpret its data. if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment \n",FL); result = MIME_decode_encoding( f, unpackdir, h, ss ); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Decoding a normal attachment '%s' done. \n",FL, h->filename); // See if we have an attachment output which is actually another // email. // // Added 24 Aug 2003 by PLD // Ricardo Kleemann supplied offending mailpack to display // this behavior if (result != 0) return result; // 20040305-1314:PLD else { char *mime_fname; mime_fname = PLD_dprintf("%s/%s", unpackdir, h->filename); if(mime_fname != NULL) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Testing '%s' for email type",FL,mime_fname); if (MIME_is_file_RFC822(mime_fname)) { //MIME_unpack_single( unpackdir, mime_fname, (hinfo->current_recursion_level+1), ss); MIME_unpack_single( unpackdir, mime_fname, current_recursion_level+1,ss); } free(mime_fname); } } } // if there was a boundary, RFC822 content or it was multi-part } else { // if the result is not 0 break; } // result == 0 test } // While (result) // ???? Why do we bother to pop the stack ??? // The way BS is designed it will auto-pop the inner nested boundaries // when a higher-up one is located. //if (result == 0) BS_pop(); // 20040305-2219:PLD } // if MIME_BS_top() } // if result == 0 } // if (result) if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_stage2:DEBUG: Exiting with result=%d recursion=%d\n",FL,result, current_recursion_level); return result; } /*------------------------------------------------------------------------ Procedure: MIME_decode_mailbox ID:1 Purpose: Decodes mailbox formatted email files Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_mailbox( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ) { FFGET_FILE f; FILE *fi; FILE *fo; char fname[1024]; char line[1024]; int mcount=0; int lastlinewasblank=1; int result; int input_is_stdin=0; snprintf(fname,sizeof(fname),"%s/tmp.email000.mailpack",unpackdir); if ((mpname[0] == '-')&&(mpname[1] == '\0')) { fi = stdin; input_is_stdin=1; } else { // fi = fopen(mpname,"r"); if (strcmp(mpname,"-")==0) fi = stdin; else fi = fopen(mpname,"r"); // 20040208-1715:PLD if (!fi) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for reading (%s)",FL, mpname,strerror(errno)); return -1; } } fo = fopen(fname,"w"); if (!fo) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: Cannot open '%s' for writing (%s)",FL, fname,strerror(errno)); return -1; } FFGET_setstream(&f, fi); while (FFGET_fgets(line,sizeof(line),&f)) { // If we have the construct of "\n\rFrom ", then we // can be -pretty- sure that a new email is about // to start if ((lastlinewasblank==1)&&(strncasecmp(line,"From ",5)==0)) { // Close the mailpack fclose(fo); // Now, decode the mailpack //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss); // 20040317-2358:PLD MIME_unpack_single(unpackdir, fname, current_recursion_level ,ss ); // Remove the now unpacked mailpack result = remove(fname); if (result == -1) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno)); } // Create a new mailpack filename, and keep on going... snprintf(fname,sizeof(fname),"%s/tmp.email%03d.mailpack",unpackdir,++mcount); fo = fopen(fname,"w"); } else { fprintf(fo,"%s",line); } // If the line is blank, then note this down because // if our NEXT line is a From, then we know that // we have reached the end of the email // if ((line[0] == '\n') || (line[0] == '\r')) { lastlinewasblank=1; } else lastlinewasblank=0; } // While fgets() // Don't attempt to close STDIN if that's where the mailpack/mailbox // has come from. Although this should really cause problems, // it's better to be safe than sorry. if (input_is_stdin == 0) { fclose(fi); } // Now, even though we have run out of lines from our main input file // it DOESNT mean we dont have some more decoding to do, in fact // quite the opposite, we still have one more file to decode // Close the mailpack fclose(fo); // Now, decode the mailpack //MIME_unpack_single(unpackdir, fname, current_recursion_level, ss); // 20040317-2358:PLD MIME_unpack_single(unpackdir, fname, current_recursion_level , ss ); // Remove the now unpacked mailpack result = remove(fname); if (result == -1) { LOGGER_log("%s:%d:MIME_unpack_mailbox:ERROR: removing temporary mailpack '%s' (%s)",FL, fname,strerror(errno)); } return 0; } /*-----------------------------------------------------------------\ Function Name : MIME_unpack_single Returns Type : int ----Parameter List 1. char *unpackdir, 2. char *mpname, 3. int current_recursion_level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int MIME_unpack_single( char *unpackdir, char *mpname, int current_recursion_level, struct SS_object *ss ) { FILE *fi; /* Pointer for the MIME file we're going to be going through */ int result = 0; if (current_recursion_level > glb.max_recursion_level) { LOGGER_log("%s:%d:MIME_unpack_single:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level); return MIME_ERROR_RECURSION_LIMIT_REACHED; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: dir=%s packname=%s level=%d (max = %d)\n",FL, unpackdir, mpname, current_recursion_level, glb.max_recursion_level); /* if we're reading in from STDIN */ if( mpname[0] == '-' && mpname[1] == '\0' ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: STDIN opened...\n",FL); fi = stdin; } else { fi = fopen(mpname,"r"); if (!fi) { LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Cannot open file '%s' for reading.\n",FL, mpname); return -1; } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Input file (%s) opened...\n",FL, mpname); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: Checking input streams...\n",FL); /* check to see if we had problems opening the file */ if (fi == NULL) { LOGGER_log("%s:%d:MIME_unpack_single:ERROR: Could not open mailpack file '%s' (%s)",FL, mpname, strerror(errno)); return -1; } // 20040317-2359:PLD result = MIME_unpack_single_fp(unpackdir,fi,current_recursion_level , ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single:DEBUG: result = %d, recursion = %d, filename = '%s'", FL, result, current_recursion_level, mpname ); if ((current_recursion_level > 1)&&(result == 241)) result = 0; fclose(fi); return result; } /*------------------------------------------------------------------------ Procedure: MIME_unpack_single ID:1 Purpose: Decodes a single mailpack file (as apposed to mailbox format) into its possible attachments and text bodies Input: char *unpackdir: Directory to unpack the attachments to char *mpname: Name of the mailpack we have to decode int current_recusion_level: Level of recursion we're currently at. Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack_single_fp( char *unpackdir, FILE *fi, int current_recursion_level, struct SS_object *ss ) { struct MIMEH_header_info h; int result = 0; int headers_save_set_here = 0; FFGET_FILE f; FILE *hf = NULL; // Because this MIME module gets used in both CLI and daemon modes // we should check to see that we can report to stderr // if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: dir=%s level=%d (max = %d)\n",FL, unpackdir, current_recursion_level, glb.max_recursion_level); if (current_recursion_level > glb.max_recursion_level) { LOGGER_log("%s:%d:MIME_unpack_single_fp:WARNING: Current recursion level of %d is greater than permitted %d",FL, current_recursion_level, glb.max_recursion_level); // return -1; return MIME_ERROR_RECURSION_LIMIT_REACHED; // 20040305-1302:PLD //return 0; // 20040208-1723:PLD } else h.current_recursion_level = current_recursion_level; glb.current_line = 0; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: recursion level checked...%d\n",FL, current_recursion_level); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: DumpHeaders = %d\n",FL, glb.save_headers); if ((!hf)&&(glb.save_headers)&&(MIMEH_get_headers_save()==0)) { // Prepend the unpackdir path to the headers file name snprintf(scratch,sizeof(scratch),"%s/%s",unpackdir, glb.headersname); hf = fopen(scratch,"w"); if (!hf) { glb.save_headers = 0; LOGGER_log("%s:%d:MIME_unpack_single_fp:ERROR: Cannot open '%s' for writing (%s)",FL, glb.headersname,strerror(errno)); } else { headers_save_set_here = 1; MIMEH_set_headers_save(hf); } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Setting up streams to decode\n",FL); FFGET_setstream(&f, fi); /** Initialize the header record **/ h.boundary[0] = '\0'; h.boundary_located = 0; h.filename[0] = '\0'; h.name[0] = '\0'; h.content_transfer_encoding = -1; h.content_disposition = -1; h.content_type = -1; h.x_mac = 0; SS_init(&(h.ss_filenames)); SS_init(&(h.ss_names)); if (MIME_DNORMAL) { SS_set_debug(&(h.ss_filenames), 1); SS_set_debug(&(h.ss_names), 1); } if (glb.verbose_defects) { int i; for (i = 0; i < _MIMEH_DEFECT_ARRAY_SIZE; i++) { h.defects[i] = 0; } } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: preparing to decode, calling stage2...\n",FL); // 20040318-0001:PLD result = MIME_unpack_stage2(&f, unpackdir, &h, current_recursion_level +1, ss); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: done decoding ( in stage2 ) result=%d, to %s\n",FL, result, unpackdir); // fclose(fi); 20040208-1726:PLD if ( headers_save_set_here > 0 ) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Closing header file.\n",FL); fflush(stdout); MIMEH_set_headers_nosave(); fclose(hf); } if (glb.verbose_defects) MIMEH_dump_defects(&h); /** Flush out the string stacks **/ SS_done(&(h.ss_filenames)); SS_done(&(h.ss_names)); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack_single_fp:DEBUG: Done. Result=%d Recursion=%d\n",FL, result, current_recursion_level); return result; // return status; // 20040305-1318:PLD } /*------------------------------------------------------------------------ Procedure: MIME_unpack ID:1 Purpose: Front end to unpack_mailbox and unpack_single. Decides which one to execute based on the mailbox setting Input: Output: Errors: ------------------------------------------------------------------------*/ int MIME_unpack( char *unpackdir, char *mpname, int current_recursion_level ) { int result = 0; struct SS_object ss; // Stores the filenames that are created in the unpack operation if (current_recursion_level > glb.max_recursion_level) return MIME_ERROR_RECURSION_LIMIT_REACHED; if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking %s to %s, recursion level is %d",FL,mpname,unpackdir,current_recursion_level); MIMEH_set_outputdir(unpackdir); if (MIME_DNORMAL) SS_set_debug(&ss,1); SS_init(&ss); if (glb.mailbox_format > 0) { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking using mailbox format",FL); result = MIME_unpack_mailbox( unpackdir, mpname, (current_recursion_level), &ss ); } else { if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking standard mailpack",FL,mpname,unpackdir,current_recursion_level); result = MIME_unpack_single( unpackdir, mpname, (current_recursion_level +1), &ss ); } if (glb.no_nameless) { MIME_postdecode_cleanup( unpackdir, &ss ); } if (MIME_DNORMAL) { LOGGER_log("%s:%d:MIME_unpack: Files unpacked from '%s' (recursion=%d);",FL,mpname,current_recursion_level); SS_dump(&ss); } SS_done(&ss); if (MIME_DNORMAL) SS_set_debug(&ss,1); // If the result is a non-critical one (ie, just running out of data from the // file we attempted to decode - then don't propergate it, on the other switch (result) { case 1: result = 0; break; case MIME_STATUS_ZERO_FILE: result = 0; break; case MIME_ERROR_FFGET_EMPTY: case MIME_ERROR_RECURSION_LIMIT_REACHED: result = 0; break; default: break; } if (current_recursion_level == 0) { //LOGGER_log("%s:%d:MIME_unpack:DEBUG: Clearing boundary stack",FL); BS_clear(); } if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: Unpacking of %s is done.",FL,mpname); if (MIME_DNORMAL) LOGGER_log("%s:%d:MIME_unpack: -----------------------------------",FL); return result; } /*-------------------------------------------------------------------- * MIME_close * * Closes the files used in MIME_unpack, such as headers etc */ int MIME_close( void ) { if (headers) { fclose(headers); } return 0; } /*----------END OF MIME.c------------*/ ripmime-1.4.0.10.debian.1/uuencode.c0000640000175000017500000005125111667102337015046 0ustar pjbpjb/*------------------------------------------------------------------------ Module: /extra/development/xamime/xamime_working/ripmime/uuencode.c Author: Paul L Daniels Project: Xamime:ripMIME State: Stable Creation Date: Description: uuencode is a collection of functions to aid the decoding / encoding of UUENCODED data files. This module is primarily intended to be used with ripMIME rather than for 'stand alone' use. The biggest issue is that the interfaces to the decoding functions are too specific at this point for a more generic use. ------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "logger.h" #include "pldstr.h" #include "ffget.h" #include "filename-filters.h" #include "uuencode.h" #ifndef FL #define FL __FILE__,__LINE__ #endif #define UUENCODE_DEBUG_PEDANTIC 10 #define UUENCODE_DEBUG_NORMAL 1 #define UUENCODE_STRLEN_MAX 1024 // Debug precodes #define UUENCODE_DPEDANTIC ((glb.debug >= UUENCODE_DEBUG_PEDANTIC)) #define UUENCODE_DNORMAL ((glb.debug >= UUENCODE_DEBUG_NORMAL )) #define UUENCODE_VERBOSE ((glb.verbosity > 0 )) #define UUENCODE_WRITE_BUFFER_SIZE 4096 #define UUENCODE_WRITE_BUFFER_LIMIT 4000 static unsigned char uudec[256]={ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ 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, 26, 27, 28, 29, 30, 31 \ }; struct UUENCODE_globals { int debug; int verbosity; int verbosity_contenttype; int decode; int doubleCR_mode; int (*filename_decoded_report)(char *, char *); // Pointer to our filename reporting function }; static struct UUENCODE_globals glb; int uuencode_error; // this contains the error code for parents to check if they receive a -1 /*-----------------------------------------------------------------\ Function Name : UUENCODE_init Returns Type : int ----Parameter List 1. void , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_init( void ) { glb.debug = 0; glb.verbosity = 0; glb.verbosity_contenttype = 0; glb.decode = 1; glb.doubleCR_mode = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_debug Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_debug( int level ) { glb.debug = level; return glb.debug; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_verbosity Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_verbosity( int level ) { glb.verbosity = level; return glb.verbosity; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_verbosity_contenttype Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_verbosity_contenttype( int level ) { glb.verbosity_contenttype = level; return glb.verbosity_contenttype; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_decode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_decode( int level ) { glb.decode = level; return glb.decode; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_doubleCR_mode Returns Type : int ----Parameter List 1. int level , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_doubleCR_mode( int level ) { glb.doubleCR_mode = level; return glb.doubleCR_mode; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_set_filename_report_fn Returns Type : int ----Parameter List 1. int (*ptr_to_fn)(char *, 2. char *) , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_set_filename_report_fn( int (*ptr_to_fn)(char *, char *) ) { glb.filename_decoded_report = ptr_to_fn; return 0; } /*------------------------------------------------------------------------ Procedure: UUENCODE_is_uuencode_header ID:1 Purpose: Tries to determine if the line handed to it is a UUencode header. Input: Output: 0 = No 1 = Yes Errors: ------------------------------------------------------------------------*/ int UUENCODE_is_uuencode_header( char *line ) { struct PLD_strtok tx; char buf[UUENCODE_STRLEN_MAX]; char *bp,*fp; int result = 0; // If we're not supposed to be decoding UUencoded files, then return 0 if (glb.decode == 0) return 0; snprintf( buf, sizeof(buf), "%s", line ); bp = buf; // If you're wondering why we dont check for "begin ",it's because we dont know // if begin is followed by a \t or ' ' or some other white space // Also, check to see that we don't have a VCARD ( look to see if there's a trailing // colon after the BEGIN // // 2003-08-12:PLD:Added *(bp+6) test as recommended by Bernard Fischer to ensure there's more // data after the begin if ((bp)&&(strncasecmp(bp,"begin",5)==0)&&(*(bp+5)!=':')&&(isspace((int)*(bp+5)))&&(*(bp+6))) { fp = NULL; bp = PLD_strtok(&tx, buf, " \n\r\t"); // Get the begin if (bp) fp = PLD_strtok(&tx, NULL, " \n\r\t"); // Get the file-permissions if (fp) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_uuencode_header:DEBUG: PERMISSIONS = %s\n", FL, fp); if ((atoi(fp) == 0)||(atoi(fp) > 777)) // Maximum is 777, because R+W+X = 7 { result = 0; } else result = 1; } else { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_uuencode_header:WARNING: Cannot read permissions for UUENCODED data file (%s)\n", FL, line); } } return result; } /*------------------------------------------------------------------------ Procedure: UUENCODE_is_file_uuenc ID:1 Purpose: Tries to determine if a given file is UUEncoded, or at least contains a UUENCODED file to it. This should only be run -after- we've checked with is_file_mime() because if the file is MIME, then it'll be able to detect UUencoding within the normal decoding routines. Input: filename to test Output: 0 = not uuencoded 1 = _probably_ uuencoded. Errors: ------------------------------------------------------------------------*/ int UUENCODE_is_file_uuencoded( char *fname ) { int result = 0; int linecount = 0; int limit=20; char line[ UUENCODE_STRLEN_MAX ]; FILE *f; f = fopen(fname,"r"); if (!f) { LOGGER_log("%s:%d:UUENCODE_is_file_uuencoded:ERROR: cannot open file '%s' for reading (%s)", FL, fname,strerror(errno)); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } while ((linecount < limit)&&(fgets(line, sizeof(line), f))) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_is_file_uuencoded:DEBUG: Testing line '%s'\n", FL, line); if (UUENCODE_is_uuencode_header( line )) { result = 1; break; } linecount++; } fclose(f); return result; } /*-----------------------------------------------------------------\ Function Name : UUENCODE_decode_uu Returns Type : int ----Parameter List 1. FFGET_FILE *f, Source Data Stream 2. char *unpackdir, Directory to prefix to our write output 3. char *input_filename, The fully pathed input filename, containing UU data 4. char *out_filename, Pointer to a buffer where we will write the filename of the UU data 5. int out_filename_size, out_filename buffers size 6. int decode_whole_file, 0 == only first segment, >0 == all 7. int keep , Keep the files we create, don't delete ------------------ Exit Codes : Returns the number of attachments decoded in the data Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int UUENCODE_decode_uu( FFGET_FILE *f, char *unpackdir, char *input_filename, char *out_filename, int out_filename_size, int decode_whole_file, int keep ) { int filename_found = 0; char buf[ UUENCODE_STRLEN_MAX ]; char *bp = buf, *fn, *fp; int n, i, expected; char fullpath[ UUENCODE_STRLEN_MAX ]=""; struct PLD_strtok tx; unsigned char *writebuffer = NULL; unsigned char *wbpos; int wbcount = 0; int loop = 0; int buflen = 0; int filecount = 0; FFGET_FILE ffinf; // Local static FFGET struct used if *f is NULL FFGET_FILE *finf; // Points to either *f or &ffinf FILE *outf; int outfo =0; // set if outfile was opened. FILE *inf = NULL; int output_filename_supplied = 0; int start_found = 0; if ((out_filename != NULL)) { if ((out_filename[0] != '\0')) { output_filename_supplied = 1; } } bp = buf; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Starting.(input=%s,output=%s)\n", FL, input_filename,out_filename ); // If no FFGET_FILE param is passed to us directly, then we must create out own. if (!f) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: NULL FFGET source stream given to us, create our own.\n",FL); // Setup the full source-data file path. snprintf(fullpath, sizeof(fullpath),"%s", input_filename ); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Full INPUT file path set as '%s'\n", FL, fullpath); inf = fopen(fullpath,"r"); if (!inf) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: Cannot open file '%s' for reading (%s)", FL, fullpath, strerror(errno)); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } FFGET_setstream(&ffinf, inf); FFGET_set_watch_SDL( glb.doubleCR_mode ); finf = &ffinf; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Creation done. [FFGET-FILE=%p, FILE=%p]\n", FL, finf, inf); } else { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: File handle already exists to read from, using",FL); finf = f; } writebuffer = malloc( UUENCODE_WRITE_BUFFER_SIZE *sizeof(unsigned char)); if (!writebuffer) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: cannot allocate 100K of memory for the write buffer",FL); uuencode_error = UUENCODE_STATUS_CANNOT_ALLOCATE_MEMORY; return -1; } else { wbpos = writebuffer; wbcount = 0; } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Beginning.(%s)\n",FL,fullpath); while (!FFGET_feof(finf)) { filename_found = 0; // First lets locate the BEGIN line of this UUDECODE file // if (output_filename_supplied == 0) if (1) /** 20041105-23H00:PLD: Stepan Kasal - UUbegin patch **/ { while (FFGET_fgets(buf, sizeof(buf), finf)) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: BUFFER: \n%s\n", FL, buf); // Check for the presence of 'BEGIN', but make sure it's not followed by a // colon ( which indicates a VCARD instead of UUENCODE if ((strncasecmp(buf,"begin",5)==0)&&(buf[5] !=':')&&(isspace((int)buf[5]))) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Located BEGIN\n",FL); // Okay, so the line contains begin at the start, now, lets get the decode details fp = fn = NULL; bp = PLD_strtok(&tx, buf, " \n\r\t"); // Get the begin if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: BEGIN = '%s'\n", FL, bp); if (bp) fp = PLD_strtok(&tx, NULL, " \n\r\t"); // Get the file-permissions if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Permissions/Name = '%s'\n", FL, fp); if (fp) fn = PLD_strtok(&tx, NULL, "\n\r"); // Get the file-name if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Name = '%s'\n", FL, fn); if (!fn) { bp = fp; } else bp = fn; if ((!bp)&&(!f)) { LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: unable to obtain filename from UUencoded text file header", FL); if (writebuffer) free(writebuffer); fclose(inf); uuencode_error = UUENCODE_STATUS_CANNOT_FIND_FILENAME; return -1; } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Full path = (%s)\n",FL,bp); filename_found = 1; break; } // If line starts with BEGIN } // While more lines in the INPUT file. } /** 20041105-23H02:PLD: Stepan Kasal Patch **/ // Filename from header has precedence: if (output_filename_supplied != 0) { filename_found = 1; bp = out_filename; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Output filename set to '%s'",FL, bp); } // If we have a filename, and we have our bp as NON-null, then we shall commence // to decode the UUencoded data from the stream. if ((filename_found != 0)&&(bp)) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Located filename (%s), now decoding.\n", FL, bp); // Clean up the file name FNFILTER_filter( bp, out_filename_size ); // If our filename wasn't supplied via the params, then copy it over here if (output_filename_supplied == 0) snprintf( out_filename, out_filename_size, "%s", bp ); // Create the new output full path snprintf(fullpath, sizeof(fullpath), "%s/%s", unpackdir, bp ); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Filename = (%s)\n", FL, fullpath); outf = fopen(fullpath, "wb"); if (!outf) { LOGGER_log("%s:%d:UUENCODE_decode_uu:ERROR: Cannot open file '%s' (%s)", FL, fullpath,strerror(errno)); if (writebuffer) free(writebuffer); uuencode_error = UUENCODE_STATUS_CANNOT_OPEN_FILE; return -1; } else outfo = 1; // Allocate the write buffer. By using the write buffer we gain an additional 10% in performance // due to the lack of function call (fwrite) overheads // Okay, now we have the UUDECODE data to decode... wbcount = 0; wbpos = writebuffer; while (outf) { // for each input line FFGET_fgets(buf, sizeof(buf), finf); if (UUENCODE_DPEDANTIC) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Read line:\n%s",FL,buf); if (FFGET_feof(finf) != 0) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Short file (%s)\n",FL, fullpath); if (writebuffer != NULL) free(writebuffer); uuencode_error = UUENCODE_STATUS_SHORT_FILE; return -1; } // If we've reached the end of the UUencoding if (strncasecmp(buf,"end",3)==0) { if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: End of UUencoding detected\n",FL); break; } if ( !strpbrk(buf,"\r\n") ) { LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Excessive length line\n",FL); } // The first char of the line indicates how many bytes are to be expected n = uudec[(int)*buf]; // If the line is a -blank- then break out. if ((start_found == 0)&&((*buf == '\n')||(*buf == '\r'))) continue; else start_found =1 ; if ((start_found != 0)&&((n <= 0) || (*buf == '\n'))) break; // Calculate expected # of chars and pad if necessary expected = ((n+2)/3)<<2; buflen = strlen(buf) -1; for (i = buflen; i <= expected; i++) buf[i] = ' '; bp = &buf[1]; // Decode input buffer to output file. while (n > 0) { // In order to reduce function call overheads, we've bought the UUDecoding // bit shifting routines into the UUDecode main decoding routines. This should // save us about 250,000 function calls per Mb. // UUENCODE_outdec(bp, outf, n); char c[3]; int m = n; c[0] = uudec[(int)*bp] << 2 | uudec[(int)*(bp+1)] >> 4; c[1] = uudec[(int)*(bp+1)] << 4 | uudec[(int)*(bp+2)] >> 2; c[2] = uudec[(int)*(bp+2)] << 6 | uudec[(int)*(bp+3)]; if (m > 3) m = 3; if ( wbcount >= UUENCODE_WRITE_BUFFER_LIMIT ) { size_t bc; bc = fwrite(writebuffer, 1, wbcount, outf); if (bc != wbcount) LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Only wrote %d of %d bytes", FL, bc, wbcount); wbpos = writebuffer; wbcount = 0; } // Transfer the decoded data to the write buffer. // The reason why we use a loop, rather than just a set of if // statements is just for code-viewing simplicity. It's a lot // easier to read than some nested chain of if's for (loop = 0; loop < m; loop++) { *wbpos = c[loop]; wbpos++; wbcount++; } bp += 4; n -= 3; } // while (n > 0) } // While (1) if ((outfo)&&(wbcount > 0)) { size_t bc; bc = fwrite(writebuffer, 1, wbcount, outf); if (bc != wbcount) LOGGER_log("%s:%d:UUENCODE_decode_uu:WARNING: Only wrote %d of %d bytes", FL, bc, wbcount); } if (outfo) fclose(outf); // Call our reporting function, else, if no function is defined, use the default // standard call if ((UUENCODE_VERBOSE) && (output_filename_supplied == 0)) { if (glb.filename_decoded_report == NULL) { LOGGER_log("Decoded: %s\n", fullpath); } else { glb.filename_decoded_report( fullpath, (glb.verbosity_contenttype>0?"uuencoded":NULL) ); } } filecount++; } // If valid filename was found for UUdecode else { out_filename[0] = '\0'; if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: No FILENAME was found in data...\n",FL); } if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Segment completed\n",FL); // If this file was a result of the x-uuencode content encoding, then we need to exit out // as we're reading in the -stream-, and we dont want to carry on reading because we'll // end up just absorbing email data which we weren't supposed to. if ((f)&&( !decode_whole_file )) break; } // While !feof(inf) if (writebuffer) free(writebuffer); if (UUENCODE_DNORMAL) LOGGER_log("%s:%d:UUENCODE_decode_uu:DEBUG: Completed\n",FL); if (inf) fclose(inf); return filecount; } ripmime-1.4.0.10.debian.1/ripmime.c0000640000175000017500000005170611667102337014706 0ustar pjbpjb/*---------------------------------------- * ripMIME - * * Written by Paul L Daniels * pldaniels@pldaniels.com * * (C)2001 P.L.Daniels * http://www.pldaniels.com/ripmime * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "buildcodes.h" #include "logger.h" #include "ffget.h" #include "mime.h" #include "strstack.h" #include "MIME_headers.h" #include "libmime-decoders.h" #define RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR 1 #define RIPMIME_ERROR_CANT_OPEN_INPUT_FILE 2 #define RIPMIME_ERROR_NO_INPUT_FILE 3 #define RIPMIME_ERROR_INSUFFICIENT_PARAMETERS 4 #define RIPMIME_ERROR_TIMEOUT 5 struct RIPMIME_globals { char *inputfile; char *dir; int use_return_codes; int timeout; int quiet; int verbose_defects; int verbose; }; //-b [blankzone file name] : Dump the contents of the MIME blankzone to 'filename' char defaultdir[] = "."; struct RIPMIME_globals *ripmime_globals = NULL; char version[] = "v1.4.0.9 - November 07, 2008 (C) PLDaniels http://www.pldaniels.com/ripmime"; char help[] = "ripMIME -i -d " "[-p prefix] [-e [header file]] [-vVh] [--version]" "[--no_nameless] [--unique_names [--prefix|--postfix|--infix]]" "[--paranoid] [--mailbox] [--formdata] [--debug]" "[--no-tnef] [--no-quotedprintable] [--no-uudecode]\n" "Options available :\n" "-i : Input MIME encoded file (use '-' to input from STDIN)\n" "\tIf is a directory, it will be recursed\n" "-d : Output directory\n" "-p : Specify prefix filename to be used on files without a filename (default 'text')\n" "-e [headers file name] : Dump headers from mailpack (default '_headers_')\n" "-v : Turn on verbosity\n" "-q : Run quietly, do no report non-fatal errors\n" "-x: Set character set for all output\n" "\n" "--verbose-contenttype : Turn on verbosity of file content type\n" "--verbose-oldstyle : Uses the v1.2.x style or filename reporting\n" "--verbose-defects: Display a summary of defects in the email\n" "--verbose-mime: Displays 'Email is MIME' if the email is MIME\n" "--stdout : All reporting goes to stdout (Default)\n" "--stderr : All reporting goes to stderr\n" "--syslog : All reporting goes to syslog\n" "\n" "--no-paranoid : [ Deprecated ] Turns off strict ascii-alnum filenaming\n" "--paranoid: Converts all filenames to strict 7-bit compliance\n" "--name-by-type: Saves a given attachment by its content-type if it has no other name\n" "--no-nameless : Do not save nameless attachments\n" "--overwrite : Overwrite files if they have the same name on extraction\n" "--unique-names : Dont overwrite existing files (This is the default behaviour)\n" "--prefix : rename by putting unique code at the front of the filename\n" "--postfix : rename by putting unique code at the end of the filename\n" "--infix : rename by putting unique code in the middle of the filename\n" "\n" "--mailbox : Process mailbox file\n" "--formdata : Process as form data (from HTML form etc)\n" "\n" "--no-tnef : Turn off TNEF/winmail.dat decoding\n" "--no-ole : Turn off OLE decoding\n" "--no-uudecode : Turns off the facility of detecting UUencoded attachments in emails\n" "--no-quotedprintable : Turns off the facility of decoding QuotedPrintable data\n" "--no-doublecr : Turns off saving of double-CR embedded data\n" "--no-mht : Turns off MHT (a Microsoft mailpack attachment format ) decoding\n" "--no-multiple-filenames : Turns off the multiple filename exploit handling\n" "\n" "--disable-header-fix : Turns off attempts to fix broken headers\n" "--disable-qmail-bounce : Turns off qmail bounced email testing\n" "--recursion-max : Set the maximum recursion level to 'level'\n" "--timeout : Set the maximum number of CPU seconds ripMIME can run for\n" "\n" "--debug : Produces detailed information about the whole decoding process\n" "--extended-errors: Produces more return codes, even for non-fatals\n" "-V --version : Give version information\n" "--buildcodes : Give the build information (tstamp, date and system information)\n" "-h : This message (help)\n\n\n"; /*-----------------------------------------------------------------\ Function Name : RIPMIME_report_filename_decoded Returns Type : int ----Parameter List 1. char *filename, 2. char *contenttype, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_report_filename_decoded (char *filename, char *contenttype) { char *p; p = strrchr(filename,'/'); if (!p) p = filename; else p++; if (contenttype != NULL) { LOGGER_log ("Decoding content-type=%s filename=%s", contenttype, p); } else { LOGGER_log ("Decoding filename=%s", p); } return 0; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_parse_parameters Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, 2. int argc, 3. char **argv, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_parse_parameters (struct RIPMIME_globals *glb, int argc, char **argv) { int i; int result = 0; MIME_set_filename_report_fn (RIPMIME_report_filename_decoded); for (i = 1; i < argc; i++) { // if the first char of the argument is a '-', then we possibly have a flag if (argv[i][0] == '-') { // test the 2nd char of the parameter switch (argv[i][1]) { case 'i': if (argv[i][2] != '\0') { glb->inputfile = &argv[i][2]; } else { i++; if (i < argc) { glb->inputfile = argv[i]; } else { LOGGER_log("ERROR: insufficient parameters after '-i'\n"); } } break; case 'd': if (argv[i][2] != '\0') { glb->dir = &(argv[i][2]); } else { i++; if (i < argc) { glb->dir = argv[i]; } else { LOGGER_log("ERROR: insufficient parameters after '-d'\n"); } } break; case 'p': if (argv[i][2] != '\0') { MIME_set_blankfileprefix (&argv[i][2]); } else { i++; if (i < argc) { MIME_set_blankfileprefix (argv[i]); } else { LOGGER_log("ERROR: insufficient parameters after '-p'\n"); } } break; // this is in mime.h case 'e': MIME_set_dumpheaders (1); if (argv[i][2] != '\0') { MIME_set_headersname (&argv[i][2]); } else { if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) MIME_set_headersname (argv[++i]); } break; // makes MIME dump out the headers to a file case 'x': if (argv[i][2] != '\0') { MIME_set_out_charset(&argv[i][2]); } else { LOGGER_log("ERROR: insufficient parameters after '-x'\n"); abort(); } break; #ifdef RIPMIME_BLANKZONE case 'b': if (argv[i][2] != '\0') { MIME_set_blankzone_filename (&argv[i][2]); } else { if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) MIME_set_blankzone_filename (argv[++i]); } break; // blankzone storage option #endif case 'v': MIME_set_verbosity (1); glb->verbose = 1; break; case 'q': glb->quiet = 1; MIME_set_quiet(glb->quiet); break; case 'V': fprintf (stdout, "%s\n", version); exit (1); break; case 'h': fprintf (stdout, "%s\n", help); exit (1); break; // if we get ANOTHER - symbol, then we have an extended flag case '-': if (strncmp (&(argv[i][2]), "verbose-contenttype", strlen ("verbose-contenttype")) == 0) { MIME_set_verbosity_contenttype (1); /** 20051117-0927:PLD: ** If client uses --verbose-mime, then make ripMIME report when it ** detects a MIME encoded email **/ } else if (strncmp(&(argv[i][2]), "verbose-mime", strlen("verbose-mime"))==0) { MIME_set_report_MIME(1); } else if (strncmp (&(argv[i][2]), "verbose-oldstyle", strlen ("verbose-oldstyle")) == 0) { MIME_set_verbosity_12x_style (1); MIME_set_filename_report_fn (NULL); } else if (strncmp (&(argv[i][2]), "verbose-defects",15) == 0) { glb->verbose_defects = 1; MIME_set_verbose_defects(1); } else if (strncmp (&(argv[i][2]), "paranoid", 8) == 0) { MIME_set_paranoid (1); } else if (strncmp (&(argv[i][2]), "no_paranoid", 11) == 0) { MIME_set_paranoid (0); } else if (strncmp (&(argv[i][2]), "no-paranoid", 11) == 0) { MIME_set_paranoid (0); } else if (strncmp (&(argv[i][2]), "prefix", 6) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_PREFIX); } else if (strncmp (&(argv[i][2]), "postfix", 7) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_POSTFIX); } else if (strncmp (&(argv[i][2]), "infix", 5) == 0) { MIME_set_renamemethod (_MIME_RENAME_METHOD_INFIX); } else if (strncmp (&(argv[i][2]), "overwrite", 9) == 0) { MIME_set_uniquenames (0); } else if (strncmp (&(argv[i][2]), "unique_names", 12) == 0) { MIME_set_uniquenames (1); } else if (strncmp (&(argv[i][2]), "unique-names", 12) == 0) { MIME_set_uniquenames (1); } else if (strncmp(&(argv[i][2]), "name-by-type", 12) == 0) { MIME_set_name_by_type(1); } else if (strncmp (&(argv[i][2]), "syslog", 9) == 0) { LOGGER_set_output_mode (_LOGGER_SYSLOG); LOGGER_set_syslog_mode (LOG_MAIL | LOG_INFO); } else if (strncmp (&(argv[i][2]), "stderr", 10) == 0) { LOGGER_set_output_mode (_LOGGER_STDERR); } else if (strncmp (&(argv[i][2]), "stdout", 9) == 0) { LOGGER_set_output_mode (_LOGGER_STDOUT); } else if (strncmp (&(argv[i][2]), "no_nameless", 11) == 0) { MIME_set_no_nameless (1); } else if (strncmp (&(argv[i][2]), "no-nameless", 11) == 0) { MIME_set_no_nameless (1); } else if (strncmp (&(argv[i][2]), "debug", 5) == 0) { MIME_set_debug (1); } else if (strncmp (&(argv[i][2]), "mailbox", 7) == 0) { MIME_set_mailboxformat (1); } else if (strncmp(&(argv[i][2]), "formdata", 8) == 0) { // Form data usually contains embedded \0 sequences // so we need to explicitly turn this off. FFGET_set_allow_nul(1); } else if (strncmp (&(argv[i][2]), "no_uudecode", 11) == 0) { // We are transitioning away from negative-logic function // names because they can cause confusion when reading, so // from here on, we will use things like _set_foo() rather // than _set_no_foo() MIME_set_decode_uudecode(0); } else if (strncmp (&(argv[i][2]), "no-uudecode", 11) == 0) { MIME_set_decode_uudecode(0); } else if (strncmp (&(argv[i][2]), "no-tnef", 7) == 0) { MIME_set_decode_tnef (0); } else if (strncmp (&(argv[i][2]), "no-ole", 6) == 0) { MIME_set_decode_ole(0); } else if (strncmp (&(argv[i][2]), "no-base64", 9) == 0) { MIME_set_decode_base64(0); } else if (strncmp (&(argv[i][2]), "no-quotedprintable", strlen("no-quotedprintable")) == 0) { MIME_set_decode_qp(0); } else if (strncmp(&(argv[i][2]), "no-doublecr", strlen("no-doublecr")) == 0) { MIME_set_decode_doubleCR(0); } else if (strncmp(&(argv[i][2]), "no-mht", strlen("no-mht")) == 0) { MIME_set_decode_mht(0); } else if (strncmp(&(argv[i][2]), "disable-header-fix", strlen("disable-headerfix")) == 0) { MIMEH_set_headerfix(0); } else if (strncmp(&(argv[i][2]), "qmail-bounce", strlen("qmail-bounce")) == 0) { MIME_set_header_longsearch(1); } else if (strncmp(&(argv[i][2]), "disable-qmail-bounce", strlen("disable-qmail-bounce")) == 0) { MIME_set_header_longsearch(0); } else if (strncmp(&(argv[i][2]), "no-multiple-filenames", strlen("no-multiple-filenames")) == 0) { MIME_set_multiple_filenames(0); } else if (strncmp(&(argv[i][2]), "recursion-max", strlen("recursion-max")) == 0) { if (argv[i+1] != NULL) { int level; level = atoi(argv[i+1]); if (level > 0) { MIME_set_recursion_level(level); } } } else if (strncmp(&(argv[i][2]), "timeout", strlen("timeout")) == 0) { if (argv[i+1] != NULL) { int seconds; seconds = atoi(argv[i+1]); if (seconds > 0) { glb->timeout = seconds; } } } else if (strncmp (&(argv[i][2]), "buildcodes", 10) == 0) { fprintf(stdout,"%s\n%s\n%s\n", BUILD_CODE, BUILD_DATE, BUILD_BOX); exit(0); } else if (strncmp (&(argv[i][2]), "version", 7) == 0) { fprintf (stdout, "%s\n", version); exit (0); } else if (strncmp(&(argv[i][2]), "extended-errors", strlen("extended-errors")) == 0) { glb->use_return_codes = 1; } else { LOGGER_log ("Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } break; // else, just dump out the help message default: LOGGER_log ("Cannot interpret option \"%s\"\n%s\n", argv[i], help); exit (1); break; } // Switch argv[i][1] } // if argv[i][0] == - } // for return result; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_init Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_init (struct RIPMIME_globals *glb) { glb->dir = defaultdir; glb->inputfile = NULL; glb->use_return_codes = 0; glb->timeout = 0; glb->quiet = 0; glb->verbose_defects = 0; glb->verbose = 0; return 0; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_signal_alarm Returns Type : void ----Parameter List 1. int sig , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ void RIPMIME_signal_alarm( int sig ) { if (ripmime_globals->quiet == 0) LOGGER_log("%s:%d:RIPMIME_signal_alarm: ripMIME took too long to complete. Mailpack is \"%s\", output dir is \"%s\"",FL, ripmime_globals->inputfile, ripmime_globals->dir ); exit(RIPMIME_ERROR_TIMEOUT); } /*-----------------------------------------------------------------\ Function Name : RIPMIME_unpack_single Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb, 2. char *fname , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_unpack_single( struct RIPMIME_globals *glb, char *fname ) { int result = 0; /** Set the alarm timeout feature. **/ if (glb->timeout > 0) { signal(SIGALRM, RIPMIME_signal_alarm); alarm(glb->timeout); } MIMEH_set_outputdir (glb->dir); result = MIME_unpack (glb->dir, fname, 0); // do any last minute things MIME_close (); return result; } /*-----------------------------------------------------------------\ Function Name : RIPMIME_unpack Returns Type : int ----Parameter List 1. struct RIPMIME_globals *glb , ------------------ Exit Codes : Side Effects : -------------------------------------------------------------------- Comments: -------------------------------------------------------------------- Changes: \------------------------------------------------------------------*/ int RIPMIME_unpack( struct RIPMIME_globals *glb ) { struct stat st; int stat_result = 0; int result = 0; int input_is_directory = 0; /** If we're not inputting from STDIN, check to see if the ** input is a directory **/ if (strcmp(glb->inputfile,"-")!=0) { stat_result = stat(glb->inputfile, &st); if (stat_result != 0) return -1; if (S_ISDIR(st.st_mode)) input_is_directory = 1; } if (input_is_directory == 1) { /** Unpack all files in directory **/ DIR *dir; struct dirent *dir_entry; fprintf(stderr,"input file is a directory, recursing\n"); dir = opendir(glb->inputfile); if (dir == NULL) return -1; do { /** Check every entry in the directory provided and if it's a file ** try to unpack it **/ char fullfilename[1024]; dir_entry = readdir( dir ); if (dir_entry == NULL) break; if (strcmp(dir_entry->d_name, ".")==0) continue; if (strcmp(dir_entry->d_name, "..")==0) continue; snprintf(fullfilename,sizeof(fullfilename),"%s/%s", glb->inputfile, dir_entry->d_name); stat_result = stat( fullfilename, &st ); if (stat_result != 0) continue; if (S_ISREG(st.st_mode)) { /** If the directory entry is a file, then unpack it **/ fprintf(stdout,"Unpacking mailpack %s\n",fullfilename); result = RIPMIME_unpack_single(glb, fullfilename); } } while (dir_entry != NULL); /** While more files in the directory **/ closedir( dir ); } else { /** If the supplied file was actually a normal file, then decode normally **/ result = RIPMIME_unpack_single( glb, glb->inputfile ); } return result; } /*-----------------------------------------------------------------\ 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 RIPMIME_globals glb; int result = 0; /* if the user has just typed in "ripmime" and nothing else, then we had better give them * the rundown on how to use this program */ if (argc < 2) { fprintf (stderr, "%s\n%s", version, help); return RIPMIME_ERROR_INSUFFICIENT_PARAMETERS; } // Set the global pointer ripmime_globals to point to // the glb struct, so that if we have a timeout condition // we can use the data ripmime_globals = &glb; // Set up our initial logging mode - so that we can always get // report messages if need be. LOGGER_set_output_mode (_LOGGER_STDOUT); // Perform system initialisations MIME_init (); RIPMIME_init (&glb); // Setup our default behaviours */ MIME_set_uniquenames (1); MIME_set_paranoid (0); MIME_set_header_longsearch(1); // 20040310-0117:PLD - Added by default as it seems stable, use --disable-qmail-bounce to turn off MIME_set_renamemethod (_MIME_RENAME_METHOD_INFIX); RIPMIME_parse_parameters (&glb, argc, argv); // if our input filename wasn't specified, then we better let the user know! if (!glb.inputfile) { LOGGER_log("Error: No input file was specified\n"); return RIPMIME_ERROR_NO_INPUT_FILE; } // Fire up the randomizer srand (time (NULL)); // clean up the output directory name if required (remove any trailing /'s, as suggested by James Cownie 03/02/2001 if (glb.dir[strlen (glb.dir) - 1] == '/') { glb.dir[strlen (glb.dir) - 1] = '\0'; } // Create the output directory required as specified by the -d parameter if (glb.dir != defaultdir) { result = mkdir (glb.dir, S_IRWXU); // if we had a problem creating a directory, and it wasn't just // due to the directory already existing, then we have a bit of // a problem on our hands, hence, report it. // if ((result == -1) && (errno != EEXIST)) { LOGGER_log("ripMIME: Cannot create directory '%s' (%s)\n", glb.dir, strerror (errno)); return RIPMIME_ERROR_CANT_CREATE_OUTPUT_DIR; } } // Unpack the contents RIPMIME_unpack(&glb); // Possible exit codes include; // 0 - all okay // 240 - processing stopped due to recursion limit if (glb.use_return_codes == 0) result = 0; return result; } /*-END-----------------------------------------------------------*/ ripmime-1.4.0.10.debian.1/build_ripOLE0000750000175000017500000000004610041075743015320 0ustar pjbpjb#!/bin/sh cd ripOLE make clean make ripmime-1.4.0.10.debian.1/MIME_headers.h0000640000175000017500000001361411667102337015467 0ustar pjbpjb#ifndef MIMEHEADERS #define MIMEHEADERS #define MIMEH_VERSION "200603032208" #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 ]; char charset[_MIMEH_STRLEN_MAX +1]; 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; }; #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( 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 ripmime-1.4.0.10.debian.1/README0000640000175000017500000000132311667102337013746 0ustar pjbpjbREADME---------------------------------------------------------------- ripMIME is a small program which has been developed as part of the commercial Xamime development (http://www.xamime.com). ripMIME is being presented here to the public under a BSD style licence (see the LICENCE file included in with this package). ripMIME has been written with one sole purpose in mind, to extract the attached files out of a MIME encoded email package. ripMIME was written by Paul L Daniels of PLDaniels. ripMIME is Copyright to Paul L Daniels. Any comments/suggestions/correspondence can be directed to Paul Daniels via email at pldaniels@pldaniels.com. ---------------------------------------------------------------------- ripmime-1.4.0.10.debian.1/ripmime.10000640000175000017500000001223311667102337014614 0ustar pjbpjb.\" ripMIME is a simple program which provides extensive email attachment extraction abilities. .TH "ripMIME" "1" "1.4.0.1" "Paul L Daniels" "MIME decoders" .SH "NAME" ripMIME \- email attachment / decomposition tool. .SH "SYNTAX" ripMIME \-i \-d .br [\-p prefix][\-e [headerfile]] .br [\-v][\-q][\-\-verbose\-contenttype][\-\-verbose\-oldstyle][\-\-verbose\-defects][\-\-stdout][\-\-stderr][\-\-syslog] .br [\-\-paranoid] .br [\-\-name\-by\-type][\-\-no\-nameless][\-\-overwrite][\-\-no_nameless] .br [\-\-unique_names[\-\-prefix|\-\-postfix|\-\-infix]][\-\-mailbox] .br [\-\-no\-tnef][\-\-no\-quotedprintable][\-\-no\-uudecode][\-\-no\-ole][\-\-no\-doublecr][\-\-no\-mht] .br [\-\-disable\-qmail\-bounce][\-\-recursion\-max ] .br [\-\-no\-multiple\-filenames] .br [\-\-exteded\-errors][\-\-debug][\-\-version|\-V][\-\-buildcodes][\-h] .br .SH "DESCRIPTION" ripMIME is a command line tool used to aid in the extraction of email borne attachments to files which can be processed using other UNIX tools. ripMIME supports both the RFC MIME standards as well as being able to behave according to various MUA 'features' which are often used as exploitation holes. .SH "OPTIONS" .LP .TP \-i Input MIME encoded file (use '\-' to input from STDIN) .TP \-d Output directory .TP \-p Specify prefix filename to be used on files without a filename (default 'text') .TP \-e [headers file name] Dump headers from mailpack (default '_headers_') .TP \-v Turn on verbosity .TP \-q Run quietly, do not report non\-fatal errors .TP \-\-verbose\-contenttype Turn on verbosity of file content type .TP \-\-verbose\-oldstyle Uses the v1.2.x style or filename reporting .TP \-\-verbose\-defects Report MIME header/body defects located in the mailpack .TP \-\-stdout All reporting goes to stdout (Default) .TP \-\-stderr All reporting goes to stderr .TP \-\-syslog All reporting goes to syslog .TP \-\-no\-paranoid [ Deprecated ] Turns off strict ascii\-alnum filenaming .TP \-\-paranoid Converts all filenames to strict 7\-bit compliance .TP \-\-name\-by\-type Saves a given attachment by its content\-type if it has no other name .TP \-\-no\-nameless Do not save nameless attachments .TP \-\-overwrite Overwrite files if they have the same name on extraction .TP \-\-unique\-names Dont overwrite existing files (This is the default behaviour) .TP \-\-prefix rename by putting unique code at the front of the filename .TP \-\-postfix rename by putting unique code at the end of the filename .TP \-\-infix rename by putting unique code in the middle of the filename .TP \-\-recursion\-max Set the maximum recursion level into a mailpack. Often emails are forwarded copies of an existing email, each time this is done a new recursion level is required. Malicious emails can be constructed with many hundereds of recursion levels to induce stack faults in decoding programs. ripMIME is hard coded with a default of 20 levels, this may be overidden using this parameter. .TP \-\-mailbox Process mailbox file .TP \-\-extended\-errors Returns error codes for non\-fatal decoding situations .TP \-\-debug Produces detailed information about the whole decoding process .SS "Attachment type decoding switches" .TP \-\-no\-tnef Turn off TNEF/winmail.dat decoding .TP \-\-no\-ole Turn off OLE decoding .TP \-\-no\-uudecode Turns off the facility of detecting UUencoded attachments in emails .TP \-\-no\-quotedprintable Turns off the facility of decoding QuotedPrintable data .TP \-\-no\-doublecr Turns off saving of double\-CR embedded data .TP \-\-no\-mht Turns off MHT (a Microsoft mailpack attachment format ) decoding .TP \-\-disable\-qmailbounce Turns off ripMIME's look\-ahead searching for RFC822 headers within a body of text. Normally the look\-ahead is useful for decoding embedded emails which have been bounced back by systems like qmail, but have been included in such a way that they are not meant to be decoded, unfortunately some MUA (Outlook for one) still decode it, hence we need to by default check for attachments in such forwarded bodies. .TP \-\-no\-multiple\-filenames Turns off the behaviour of handling multiple filename occurances in a single MIME header. Normally ripMIME will generate a hard link for each additional filename beyond the original attachment filename detected. .SS Auxillary parameters .TP \-\-buildcodes Displays the information obtained by the Makefile script when ripMIME was built. This includes the Unix timestamp, human readable version of the timestamp and the output from 'uname \-a'. .TP \-V \-\-version Give version information .TP \-h Terse information on how to use ripMIME. .SH "FILES" .I None .br .SH "ENVIRONMENT VARIABLES" .TP .I None .SH "EXAMPLES" .LP To unpack an email in a file 'mailpack' to the directory /tmp with verbose output of the files unpacked; .LP ripmime \-i mailpack \-v \-d /tmp .LP .SH "AUTHORS" Paul L Daniels .br ripMIME WWW site http://www.pldaniels.com/ripmime .br ripMIME mailing list .br For mailpacks which do not appear to decode correctly \- please email to .SH "SEE ALSO" .BR altermime (1), .BR inflex (1), .BR ripole (1), .BR opentnef (1) ripmime-1.4.0.10.debian.1/TODO0000640000175000017500000000013011667102337013551 0ustar pjbpjb------TODO list--------- * fix up unsigned/signed mismatches ------------------------