Logo Search packages:      
Sourcecode: p3scan version File versions  Download package

tnef.c

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



Generated by  Doxygen 1.6.0   Back to index