/***************************************************************************
* 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 <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#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 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.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)
{
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];
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;
}
fwrite(tsp, sizeof(uint8), size, out);
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];
static int file_num = 0;
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", file_num);
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);
read_16(&checksum, tsp+bytes);
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 **/
read_16(&tnef_attachkey, tsp);
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.
syntax highlighted by Code2HTML, v. 0.9.1