/***************************************************************************
libdbx.c - DBX handling Library
-------------------
begin : April 2001
copyright : (C) 2001 by David Smith
email : Dave.S@Earthcorp.Com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/*********************************************************/
/* Heavily patched to make it run on big-endian machines */
/*********************************************************/
/* #include <stdio.h> */
#include <stdlib.h>
#include <string.h>
#include "libdbx.h"
#include "define.h"
# if defined DBX_BIG_ENDIAN
/* '((x & 0xff000000) >> 24) & 0x000000ff' is a fix for Solaris' ucbcc */
# define LE32_CPU(x) \
x = (((((x) & 0xff000000) >> 24) & 0x000000ff) | \
(((x) & 0x00ff0000) >> 8 ) | \
(((x) & 0x0000ff00) << 8 ) | \
(((x) & 0x000000ff) << 24));
# define LE16_CPU(x) \
x = ((((x) & 0xff00) >> 8) | \
(((x) & 0x00ff) << 8));
# elif defined DBX_LITTLE_ENDIAN
# define LE32_CPU(x) {}
# define LE16_CPU(x) {}
# else
# error "Byte order not supported by this library"
# endif
int dbx_errno = 0;
/* could be 0xE4 or 0x30 */
#define INDEX_POINTER 0xE4
#define ITEM_COUNT 0xC4
/* Internal Prototypes */
int _dbx_get (FILE *fp, void *buf, unsigned int size);
int _dbx_getAtPos (FILE *fp, int pos, void* buf, unsigned int size);
int _dbx_getitem (FILE *fp, int pos, void** item, int type, int flags);
int _dbx_getindex (FILE* fd, int pos, DBX *dbx);
int _dbx_getIndexes (FILE* fd, DBX *dbx);
int _dbx_getstruct (FILE *fp, int pos, DBXFOLDER* folder);
int _dbx_get_from_buf (char* buffer, int pos, void** dest, int type, int max);
int _dbx_getBody (FILE *fp, char** x, int ptr);
char * dbx_errmsgs[] = {
/* DBX_NOERROR */
"",
/* DBX_BADFILE */
"DBX File operation failed. Open or close",
/* DBX_ITEMCOUNT */
"Read of \"Item Count\" from DBX file failed",
/* DBX_INDEX_READ */
"Read of \"Index Pointer\" from DBX file failed",
/* DBX_INDEX_UNDERREAD */
"Number of indexes read from dbx file is less than expected",
/* DBX_INDEX_OVERREAD */
"Number of indexes read from dbx file is greater than expected",
/* DBX_INDEXCOUNT */
"Request was made for index reference greater than exists (subscript out of range)",
/* DBX_DATA_READ */
"Reading of data from dbx file failed",
/* DBX_NEWS_ITEM */
"Item is a news item not an email"
};
/* dbx_open - Opens a dbx file and returns a DBX struct to the caller
@fname - Filename of dbx file to open*/
DBX *dbx_open(const char * fname)
{
FILE *fp;
if ( !(fp = fopen(fname, "rb")) ) {
dbx_errno = DBX_BADFILE;
return NULL;
}
return dbx_open_stream(fp);
}
DBX *dbx_open_stream(FILE *fp) {
DBX *dbx = (DBX*) malloc (sizeof(DBX));
int signature[4];
dbx->fd = fp;
/* SIGNATURE */
_dbx_getAtPos(dbx->fd, 0x0, &signature, 16);
LE32_CPU(signature[0]);
LE32_CPU(signature[1]);
LE32_CPU(signature[2]);
LE32_CPU(signature[3]);
if ( signature[0] == 0xFE12ADCF && signature[1] == 0x6F74FDC5 &&
signature[2] == 0x11D1E366 && signature[3] == 0xC0004E9A ) {
/* OE 5 & OE 5 BETA SIGNATURE */
dbx->type = DBX_TYPE_EMAIL;
} else if ( signature[0] == 0x36464D4A && signature[1] == 0x00010003 ) {
/*It is an OE4 dbx file*/
dbx_errno = DBX_BADFILE;
return NULL;
} else if ( signature[0] == 0xFE12ADCF && signature[1] == 0x6F74FDC6 &&
/*Difference is C6 instead of C5*/
signature[2] == 0x11D1E366 && signature[3] == 0xC0004E9A ) {
/*It is a Folders.dbx type file*/
dbx->type = DBX_TYPE_FOLDER;
} else {
dbx_errno = DBX_BADFILE;
return NULL;
}
if (_dbx_getIndexes(dbx->fd, dbx)) {
/* dbx_errno is already set by getIndexes */
return NULL;
}
dbx_errno = DBX_NOERROR;
return dbx;
}
/* dbx_close - Closes a dbx file and deletes the internal struct
@dbx - DBX struct associated with dbx */
int dbx_close(DBX *dbx)
{
if (dbx == NULL || dbx->fd == NULL) {
dbx_errno = DBX_BADFILE;
return -1;
}
fclose(dbx->fd);
if (dbx->indexes) {
free(dbx->indexes);
}
free(dbx);
dbx_errno = DBX_NOERROR;
return 0;
}
int dbx_free(DBX *dbx, void *item)
{
return dbx_free_item(item);
}
int dbx_free_item(void *item)
{
DBXNON *it = item;
DBXEMAIL *email;
DBXFOLDER *fol;
if (!item)
return 1;
if (it->type == DBX_TYPE_EMAIL) {
email = (DBXEMAIL*)item;
if (email->email)
free(email->email);
if (email->subject)
free(email->subject);
if (email->psubject)
free(email->psubject);
if (email->messageid)
free(email->messageid);
if (email->parent_message_ids)
free(email->parent_message_ids);
if (email->sender_name)
free(email->sender_name);
if (email->sender_address)
free(email->sender_address);
if (email->recip_name)
free(email->recip_name);
if (email->recip_address)
free(email->recip_address);
if (email->oe_account_name)
free(email->oe_account_name);
if (email->oe_account_num)
free(email->oe_account_num);
if (email->fetched_server)
free(email->fetched_server);
free(email);
} else if (it->type == DBX_TYPE_FOLDER) {
fol = (DBXFOLDER*)item;
if (fol->name)
free(fol->name);
if (fol->fname)
free(fol->fname);
free(fol);
} else {
printf("Aaarghhh. Cannot free an unknown type!\n");
}
return 0;
}
/* dbx_get - Gets an item from a dbx file
@dbx - dbx file to get item from
@index - item 0..itemcount to fetch
@flags - which parts to get */
void * dbx_get(DBX *dbx, int index, int flags)
{
int size;
void * ret = NULL;
if (!dbx || !dbx->fd) {
dbx_errno = DBX_BADFILE;
return NULL;
}
if (index >= dbx->indexCount || index < 0) {
dbx_errno = DBX_INDEXCOUNT;
return NULL;
}
if (dbx->type == DBX_TYPE_EMAIL || dbx->type == DBX_TYPE_FOLDER) {
size = _dbx_getitem(dbx->fd, dbx->indexes[index], &ret,
dbx->type, flags);
((DBXEMAIL*)ret)->num = index;
} else {
dbx_errno = DBX_BADFILE;
return NULL;
}
dbx_errno = DBX_NOERROR;
if (dbx->type == DBX_TYPE_EMAIL) {
/* we explicitely need to swap the filetime */
LE32_CPU(((DBXEMAIL*)ret)->date.dwLowDateTime);
LE32_CPU(((DBXEMAIL*)ret)->date.dwHighDateTime);
/* we weren't allowed to swap 'flag' because it
* is treated rather like a char */
LE32_CPU(((DBXEMAIL*)ret)->flag);
}
return ret;
}
/* dbx_perror - Print the error message to stderr
@str - prepend this message */
int dbx_perror(const char *str)
{
fprintf(stderr, "%s: %s\n", str, dbx_errmsgs[dbx_errno]);
return 0;
}
/* dbx_get_body - Load the body for the current email
@dbx - handle for the dbx file
@start - file offset from email pointer
@ptr - location to store data */
int dbx_get_body(DBX* dbx, int start, char** ptr) {
if (!dbx || !dbx->fd) {
dbx_errno = DBX_BADFILE;
return -1;
}
return _dbx_getBody(dbx->fd, ptr, start);
}
/* dbx_get_email_body - Load the body of an email and store it in the email
@dbx - handle for the dbx file
@email - email to fillin */
int dbx_get_email_body(DBX *dbx, DBXEMAIL* email)
{
if (!dbx || !dbx->fd) {
dbx_errno = DBX_BADFILE;
return -1;
}
return _dbx_getBody(dbx->fd, &(email->email), email->data_offset);
}
/* dbx_free_email_body - Clear the body of an email. To be called after
* dbx_get_email_body
@email - email to remove body from */
int dbx_free_email_body(DBXEMAIL* email)
{
free (email->email);
email->email = NULL;
return 0;
}
const char* dbx_strerror(int err)
{
return dbx_errmsgs[err];
}
/* Private Functions */
struct _dbx_tableindexstruct {
int self;
int unknown1;
int anotherTablePtr;
int parent;
char unknown2;
char ptrCount;
char reserve3;
char reserve4;
int indexCount;
};
struct _dbx_indexstruct {
int indexptr;
int anotherTablePtr;
int indexCount;
};
struct _dbx_folder_hdrstruct {
int self;
int blocksize;
short int unknown2;
char intcount;
char unknown3;
};
struct _dbx_block_hdrstruct {
int self;
int nextaddressoffset;
short int blocksize;
char intcount;
char unknown1;
int nextaddress;
};
struct _dbx_folderstruct {
int id;
int parent;
int unknown6;
char unknown61;
char length1;
char unknown7;
char unknown8;
};
struct _dbx_email_headerstruct {
int self;
int size;
short int u1;
unsigned char count;
unsigned char u2;
};
/* Email types - values thereof
0x01 - buffer pointer to Flag (char?)
0x02 -
0x04 - buffer pointer to file offset of email data
0x05 - buffer pointer to asciiz string containing the subject of email
0x06 -
0x07 - buffer pointer to asciiz message id of email
0x08 - buffer pointer to asciiz another string containing the subject
of email
0x09 -
0x0B -
0x0A - buffer pointer to asciiz message ids of parent emails
0x0C - buffer pointer to asciiz name of server where email was fetched from
0x0D - buffer pointer to asciiz Name of sender
0x0E - buffer pointer to asciiz Email address of sender
0x11 -
0x12 -
0x13 - buffer pointer to asciiz Name of recipient
0x14 - buffer pointer to asciiz Email address of recipient
0x1A - buffer pointer to asciiz name of email account used to fetch email
0x1B - buffer pointer to asciiz number of email account (e.g. "00000001")
0x1C -
0x80 -
0x81 - email's flag (char?)
0x84 - file offset to email data
0x90 -
0x91 -
*/
/* Folder types - values thereof
0x02 - Descriptive Name
0x03 - Filename
0x80 - Folder ID
0x81 - ID of parent
*/
struct _dbx_email_pointerstruct {
unsigned char type;
int val; /* this is supposed to be a 3 byte int */
};
int _dbx_getIndexes (FILE* fp, DBX *dbx)
{
int indexptr;
int itemcount;
/* first table of indexes */
if (_dbx_getAtPos(fp, INDEX_POINTER, &indexptr, sizeof(indexptr))) {
dbx_errno = DBX_INDEX_READ;
return 2;
}
LE32_CPU(indexptr);
/* count of items */
if (_dbx_getAtPos(fp, ITEM_COUNT, &itemcount, sizeof(itemcount))) {
dbx_errno = DBX_ITEMCOUNT;
return 1;
}
LE32_CPU(itemcount);
dbx->indexes = (int*) malloc(itemcount * sizeof(int));
dbx->indexCount = itemcount;
if (_dbx_getindex(fp, indexptr, dbx)) {
return 4;
}
if (dbx->indexCount != 0) {
dbx_errno = DBX_INDEX_UNDERREAD;
return 3;
}
/* reassign itemcount after call cause it should equal zero now */
dbx->indexCount = itemcount;
return 0;
}
int _dbx_getindex(FILE* fp, int pos, DBX *dbx)
{
int x;
struct _dbx_tableindexstruct tindex;
struct _dbx_indexstruct index;
RET_ERROR(_dbx_getAtPos(fp, pos, &tindex, sizeof(tindex)), DBX_INDEX_READ);
LE32_CPU(tindex.self);
LE32_CPU(tindex.unknown1);
LE32_CPU(tindex.anotherTablePtr);
LE32_CPU(tindex.parent);
LE32_CPU(tindex.indexCount);
if (tindex.indexCount > 0) {
_dbx_getindex (fp, tindex.anotherTablePtr, dbx);
}
pos += sizeof(struct _dbx_tableindexstruct);
for (x = 1; x <= tindex.ptrCount; x++) {
RET_ERROR(_dbx_getAtPos(fp, pos, &index,
sizeof(struct _dbx_indexstruct)), DBX_INDEX_READ);
LE32_CPU(index.indexptr);
LE32_CPU(index.anotherTablePtr);
LE32_CPU(index.indexCount);
RET_ERROR(dbx->indexCount < 0, DBX_INDEX_OVERREAD);
dbx->indexes[--dbx->indexCount] = index.indexptr;
pos += sizeof(struct _dbx_indexstruct);
if (index.indexCount > 0)
_dbx_getindex(fp, index.anotherTablePtr, dbx);
}
return 0;
}
#define STRING_TYPE 0
#define INT_TYPE 1
#define W32FT_TYPE 2
#define CHAR_TYPE 3
int _dbx_getitem (FILE *fp, int pos, void **item, int type, int flags) {
int x;
char *bufptr, *buffer, **bufx;
int readtype=STRING_TYPE;
DBXEMAIL *email = NULL;
DBXFOLDER *folder = NULL;
struct _dbx_email_headerstruct blockhdr;
struct _dbx_email_pointerstruct blockp;
int body = (flags&DBX_FLAG_BODY?1:0);
if (type == DBX_TYPE_EMAIL) {
email = (DBXEMAIL*) malloc(sizeof(DBXEMAIL));
memset (email, 0, sizeof(DBXEMAIL));
email->type = DBX_TYPE_EMAIL;
*item = email;
email->email = NULL;
} else {
folder = (DBXFOLDER*) malloc(sizeof(DBXFOLDER));
memset (folder, 0, sizeof(DBXFOLDER));
folder->type = DBX_TYPE_FOLDER;
*item = folder;
}
RET_ERROR(_dbx_getAtPos(fp, pos, &blockhdr, sizeof(blockhdr)),
DBX_INDEX_READ);
LE32_CPU(blockhdr.self);
LE32_CPU(blockhdr.size);
LE32_CPU(blockhdr.u1);
/* we will load all the block into memory
* as we will be accessing it byte by byte */
buffer = (char*) malloc(blockhdr.size);
RET_ERROR(_dbx_get(fp, buffer, blockhdr.size), DBX_DATA_READ);
bufptr = buffer;
if (email)
email->data_offset = -1;
for (x = 0; x < blockhdr.count; x++) {
blockp.val = 0;
memcpy(&(blockp.type), bufptr, 1); /* this will copy the type */
memcpy(&(blockp.val), bufptr+1, 3); /* and the 3 byte int */
/* we pretend it's a four byte integer */
LE32_CPU(blockp.val);
if (type == DBX_TYPE_EMAIL) {
switch (blockp.type) {
case 0x01:
/* pointer to flag */
email->flag = 0;
((int*)bufx) = &(email->flag);
readtype = CHAR_TYPE;
break;
case 0x04:
/*pointer to dataptr */
((int*)bufx) = &(email->data_offset);
readtype = INT_TYPE;
break;
case 0x05:
/* asciiz string of subject (without RE: or FWD: etc...) */
bufx = &(email->psubject);
readtype = STRING_TYPE;
break;
case 0x07:
/* message id of email */
bufx = &(email->messageid);
readtype = STRING_TYPE;
break;
case 0x08:
/* second copy of subject. Original text (with RE: etc...) */
bufx = &(email->subject);
readtype = STRING_TYPE;
break;
case 0x0A:
/* msg-id of parent(s) */
bufx = &(email->parent_message_ids);
readtype = STRING_TYPE;
break;
case 0x0C:
/* name of server used to fetch email */
bufx = &(email->fetched_server);
readtype = STRING_TYPE;
break;
case 0x0D:
/* Sender's name */
bufx = &(email->sender_name);
readtype = STRING_TYPE;
break;
case 0x0E:
/* Sender's email address */
bufx = &(email->sender_address);
readtype = STRING_TYPE;
break;
case 0x12:
/* date - of what i'm not sure.
* It is in a win32 FILETIME structure.
* needs converting to something */
((struct FILETIME*)bufx) = &(email->date);
readtype = W32FT_TYPE;
break;
case 0x13:
/* recipient's name */
bufx = &(email->recip_name);
readtype = STRING_TYPE;
break;
case 0x14:
/* recipient's email address */
bufx = &(email->recip_address);
readtype = STRING_TYPE;
break;
case 0x1A:
/* Name of Account used to fetch email */
bufx = &(email->oe_account_name);
readtype = STRING_TYPE;
break;
case 0x1B:
/* String version of account number
* used to fetch email (eg "00000001") */
bufx = &(email->oe_account_num);
readtype = STRING_TYPE;
break;
case 0x80:
/* email's ID */
bufx = NULL;
email->id = blockp.val;
break;
case 0x81:
/* email's flag */
bufx=NULL;
email->flag = blockp.val;
break;
case 0x84:
/* direct offset of first email data block */
email->data_offset = blockp.val;
bufx = NULL;
break;
/*
case 0x02: //currently unknown
case 0x06: //currently unknown
case 0x09:
case 0x0B:
case 0x0C:
case 0x11: //currently unknown
case 0x1A:
case 0x1B:
case 0x1C: //currently unknown
case 0x81: //currently unknown
case 0x90: //currently unknown
case 0x91: //currently unknown
bufx = NULL;
break; */
default:
bufx = NULL;
}
} else {
switch(blockp.type) {
case 0x02:
/* descriptive name */
bufx = &(folder->name);
readtype = STRING_TYPE;
break;
case 0x03:
/* filename */
bufx = &(folder->fname);
readtype = STRING_TYPE;
break;
case 0x80:
/* current id */
bufx = NULL;
folder->id = blockp.val;
break;
case 0x81:
/* parent id */
bufx = NULL;
folder->parentid = blockp.val;
break;
/*
case 0x86: //unknown
case 0x87: //unknown
case 0x88: //unknown
case 0x8A: //unknown
case 0x8B: //unknown
bufx = NULL;
break;*/
default:
bufx = NULL;
}
}
if (bufx)
if (_dbx_get_from_buf(buffer, blockp.val + (blockhdr.count*4),
(void**)bufx, readtype, blockhdr.size))
return 1; /* an error occured */
bufptr += 4; /* size of data */
}
free (buffer);
/* if we are doing folder types, we have now finished */
if (type == DBX_TYPE_FOLDER || body == 0)
return 0;
RET_ERROR(email->data_offset == -1, DBX_DATA_READ);
return _dbx_getBody(fp, &(email->email), email->data_offset);
}
int _dbx_getBody(FILE *fp, char** x, int ptr)
{
int bufsize = 0;
struct _dbx_block_hdrstruct hdr;
*x = NULL;
while (ptr != 0) {
RET_ERROR(_dbx_getAtPos(fp, ptr, &hdr, sizeof(hdr)), DBX_DATA_READ);
LE32_CPU(hdr.self);
LE32_CPU(hdr.nextaddressoffset);
LE16_CPU(hdr.blocksize);
LE32_CPU(hdr.nextaddress);
/* this plus one will not be accumulative cause we don't add it to
* bufsize but we need it so we can terminate the buffer */
*x = realloc(*x, bufsize + hdr.blocksize + 1);
RET_ERROR(_dbx_get(fp, (*x)+bufsize, hdr.blocksize), DBX_DATA_READ);
bufsize += hdr.blocksize;
ptr = hdr.nextaddress;
}
if (*x)
(*x)[bufsize] = '\0'; /* terminate the buffer */
/* size of data read */
return bufsize;
}
int _dbx_getstruct(FILE *fp, int pos, DBXFOLDER* folder)
{
struct _dbx_folder_hdrstruct hdr;
struct _dbx_folderstruct fol;
char *buf, *fname;
int msgoffset, blockpos=0;
folder->name = NULL;
RET_ERROR(_dbx_getAtPos(fp, pos, &hdr, sizeof(hdr)), DBX_DATA_READ);
LE32_CPU(hdr.self);
LE32_CPU(hdr.blocksize);
LE16_CPU(hdr.unknown2);
RET_ERROR(_dbx_get(fp, &fol, sizeof(fol)), DBX_DATA_READ);
LE32_CPU(fol.id);
LE32_CPU(fol.parent);
LE32_CPU(fol.unknown6)
blockpos += sizeof(hdr);
buf = (char*) malloc(fol.length1);
msgoffset = hdr.intcount * sizeof(int);
RET_ERROR(_dbx_getAtPos(fp, pos+blockpos+msgoffset, buf, fol.length1),
DBX_DATA_READ);
if (strlen(buf) != fol.length1 - 1) { }
/* Allocate space big enough to hold remainder of block */
fname = (char*) malloc(hdr.blocksize - blockpos);
if (!fname) {
return -1;
}
RET_ERROR(_dbx_get(fp, fname, hdr.blocksize-blockpos), DBX_DATA_READ);
folder->name = buf;
folder->fname = fname;
folder->id = fol.id;
folder->parentid = fol.parent;
dbx_errno = DBX_NOERROR;
return strlen(buf);
}
int _dbx_getAtPos(FILE *fp, int pos, void* buf, unsigned int size) {
if (fseek(fp, pos, SEEK_SET) == -1) {
return 1;
}
if (fread(buf, 1, size, fp) < size) {
return 2;
}
return 0;
}
int _dbx_get (FILE *fp, void *buf, unsigned int size) {
if (fread(buf, 1, size, fp) < size) {
return 1;
}
return 0;
}
int _dbx_get_from_buf(char* buffer, int pos, void** dest, int type, int max)
{
int y;
/* copy data from buffer to string pointed to by bufx */
if (type == STRING_TYPE) {
y = strlen(&buffer[pos]) + 1; /* plus one for string terminator */
RET_ERROR(y > max, DBX_DATA_READ);
if (!*dest)
*dest = (char*) malloc(y);
strncpy(*dest, &buffer[pos], y);
} else if (type == INT_TYPE) {
memcpy((int*)dest, &buffer[pos], 4);
} else if (type == W32FT_TYPE) {
memcpy((struct FILETIME*)dest, &buffer[pos], 8);
} else if (type == CHAR_TYPE) {
memcpy((unsigned char*)dest, &buffer[pos], 1);
}
return 0;
}