www.pudn.com > NETINFO.rar > EmailAnalyse.cpp
#include "EmailAnalyse.h"
#include "tools.h"
#include "EmailDecode.h"
//BNF grammer
#define LWSP " \t"
#define TSPECIALS "()<>@,;:\\\"/[]?="
//parameter names
#define CHARSET "charset"
#define BOUNDARY "boundary"
#define FILENAME "filename"
#define CONTENT_NAME "name"
//encoding types
#define ENCODE_7BIT "7bit"
#define ENCODE_8BIT "8bit"
#define ENCODE_BINARY "binary"
#define ENCODE_QUOTED_PRINTABLE "quoted-printable"
#define ENCODE_BASE64 "base64"
//media type
#define TYPE_TEXT "text"
#define TYPE_IMAGE "image"
#define TYPE_AUDIO "audio"
#define TYPE_VIDEO "video"
#define TYPE_APPLICATION "application"
#define TYPE_MESSAGE "message"
#define TYPE_MODEL "model"
#define TYPE_MULTIPART "multipart"
//media subtype
//text category
#define SUBTYPE_PLAIN "plain"
#define SUBTYPE_RTF "rtf"
#define SUBTYPE_HTML "html"
#define SUBTYPE_XML "xml"
//image category
#define SUBTYPE_GIF "gif"
#define SUBTYPE_JPEG "jpg"
#define SUBTYPE_PNG "png"
#define SUBTYPE_TIFF "tif"
//audio category
//video category
#define SUBTYPE_MEPG "mpg"
//application
#define SUBTYPE_OCTET_STREAM "octet-stream"
#define SUBTYPE_PDF "pdf"
//message subtypes
#define SUBTYPE_RFC822 "rfc822"
//header filed names
#define FROM "From"
#define TO "To"
#define SUBJECT "Subject"
#define MIME_VERSION "Mime-Version"
#define CONTENT_TYPE "Content-Type"
#define CONTENT_TRANSFER_ENCODING "Content-Transfer-Encoding"
#define CONTENT_DISPOSITION "Content-Disposition"
#define CONTENT_ID "Content-ID"
#define CONTENT_LANGUAGE "Content-Language"
void AnalyseParameter(const char parameters[], int n, const char* names[], char* values[])
{
int start = 0, i = 0, len = strlen(parameters);
if( i>=len ) return; //no parameter, so return
while( i < len ) //get attribute=value pair
{
char attribute[MAX_EMAIL_LINE], value[MAX_EMAIL_LINE];
//exract attribute token ( ascii except LWSP, CTRLS, TSPECIALS )
for(; i=pStart && isspace(*pEnd); pEnd--)NULL; //skip end space
if(pEnd>=pStart && pEnd - pStart + 2 < MAX_EMAIL_LINE )
{
memcpy(name, pStart, pEnd-pStart+1);
name[pEnd-pStart+1] = 0;
}
else
strcpy(name, "");
//extract value
const char* pZero = pColon + strlen(pColon);
for(pStart = pColon+1; pStart=pStart && isspace(*pEnd); pEnd--)NULL; //skip end space
if(pEnd>=pStart && pEnd - pStart + 2 < MAX_EMAIL_LINE )
{
memcpy(value, pStart, pEnd-pStart+1);
value[pEnd-pStart+1] = 0;
}
else
strcpy(value, "");
return TRUE;
}
void AnalyseHeader(const char header[MAX_EMAIL_LINE], struct MESSAGE_HEADERS *pHeaders)
{
char name[MAX_EMAIL_LINE], value[MAX_EMAIL_LINE];
if( !SplitMessageHeader(header, name, value) )return;
//extract from
if( stricmp(name, FROM)==0 )
strcpy(pHeaders->from, value);
//extract to
else if( stricmp(name, TO)==0 )
strcpy(pHeaders->to, value);
//extract subject
else if( stricmp(name, SUBJECT)==0 )
strcpy(pHeaders->subject, value);
//extract mime-version
else if( stricmp(name, MIME_VERSION)==0 )
strcpy(pHeaders->mime_Version, value);
//extract content-type
else if( stricmp(name, CONTENT_TYPE)==0 )
{
int i, len = strlen(header);
for(i=0; icontent_Type, header+start, i - start);
pHeaders->content_Type[ i - start ] = 0;
}
for(; icontent_Subtype, header+start, i - start);
pHeaders->content_Subtype[ i - start ] = 0;
}
for(; i=len ) return; //no parameter, so return
const static char* names[3] ={CHARSET, BOUNDARY, CONTENT_NAME};
char* values[3] = {pHeaders->charset, pHeaders->boundary, pHeaders->name};
AnalyseParameter(header+i, 3, names, values);
}
else if( stricmp(name, CONTENT_TRANSFER_ENCODING) == 0 )
strcpy(pHeaders->content_Transfer_Encoding, value);
else if( stricmp(name, CONTENT_DISPOSITION) == 0 )
{
int i, len = strlen(header);
for(i=0; icontent_Disposition, header+start, i - start);
pHeaders->content_Disposition[ i - start ] = 0;
}
for(; i=len ) return; //no parameter, so return
const static char* names[1] ={FILENAME};
char* values[1] = {pHeaders->filename};
AnalyseParameter(header+i, 1, names, values);
}
}
CMessageDecoder::CMessageDecoder(void*pUserData, const MESSAGE_DECODE_CALLBACKS *pFuncs)
:m_funcs(*pFuncs)
{
strcpy(m_szHeader, "");
memset(&m_headers, 0, sizeof(m_headers));
m_status = MESSAGE_BEGIN;
memset(&m_dataAccepter, 0, sizeof(m_dataAccepter));
m_messageType = MESSAGE_TYPE_DATA;
m_pUserData = pUserData;
};
CMessageDecoder::~CMessageDecoder()
{
if( m_status != MESSAGE_END )
FindEnd();
}
void CMessageDecoder::GetLine(const char inputLine[], int inputStrlen)
{
if( m_status == MESSAGE_BEGIN || m_status == MESSAGE_HEADER )
{
m_status = MESSAGE_HEADER;
//get rid of ending "\r\n" of line
char line[MAX_EMAIL_LINE];
memcpy(line, inputLine, inputStrlen-2);
line[inputStrlen-2] = 0;
//end of header erea
if( strcmp(line, "") == 0 )
{
AnalyseHeader(m_szHeader, &m_headers);
PostGetHeader();
m_status = MESSAGE_BODY;
}
//folded header line
else if( strchr(LWSP, line[0]) )
{
strcat(m_szHeader, line);
}
//another header
else
{
AnalyseHeader(m_szHeader, &m_headers);
strcpy(m_szHeader, line);
}
}
else if( m_status == MESSAGE_BODY )
{
if( m_messageType == MESSAGE_TYPE_MULTIPART )
{
char delimiter[MAX_EMAIL_LINE];
//delimiter of multipart
sprintf(delimiter, "--%s", m_headers.boundary);
if( memcmp(inputLine, delimiter, strlen(delimiter)) == 0 )
{
//delete current message decoder
if( m_dataAccepter.m_pMessageDecoder )
{
m_dataAccepter.m_pMessageDecoder->FindEnd();
delete m_dataAccepter.m_pMessageDecoder;
m_dataAccepter.m_pMessageDecoder = NULL;
}
//end delimiter of multipart
sprintf(delimiter, "--%s--", m_headers.boundary);
if( memcmp(inputLine, delimiter, strlen(delimiter)) == 0 )
{
m_status = MESSAGE_END;
return;
}
//not end of message, new CMessageDecoder to decode new Entity
else
m_dataAccepter.m_pMessageDecoder = new CMessageDecoder(m_pUserData, &m_funcs);
}
//normal data line need to be send to current MessageDecoder
else
{
if( m_dataAccepter.m_pMessageDecoder )
m_dataAccepter.m_pMessageDecoder->GetLine(inputLine, inputStrlen);
}
}
else if( m_messageType == MESSAGE_TYPE_MESSAGE )
{
if( m_dataAccepter.m_pMessageDecoder )
m_dataAccepter.m_pMessageDecoder->GetLine(inputLine, inputStrlen);
}
else if( m_messageType == MESSAGE_TYPE_DATA )
{
char data[MAX_EMAIL_LINE];
int length;
//decode data and send it to callback functions
if( !m_dataAccepter.m_pTransferDecoder
|| !m_dataAccepter.m_pTransferDecoder->Decode(inputLine, inputStrlen, data, MAX_EMAIL_LINE, &length) )
{
memcpy(data, inputLine, inputStrlen);
length = inputStrlen;
}
if( m_funcs.fnOnMessageEntityBody )
m_funcs.fnOnMessageEntityBody(m_pUserData, (const BYTE*)data, length);
}
}
}
void CMessageDecoder::PostGetHeader()
{
//decode header field encoding(include from, to, subject, name, filename)
char temp[MAX_EMAIL_LINE];
int outputLength;
if( HeaderDecode(m_headers.from, strlen(m_headers.from), temp, sizeof(temp), &outputLength) )
strcpy(m_headers.from, temp);
if( HeaderDecode(m_headers.to, strlen(m_headers.to), temp, sizeof(temp), &outputLength) )
strcpy(m_headers.to, temp);
if( HeaderDecode(m_headers.subject, strlen(m_headers.subject), temp, sizeof(temp), &outputLength) )
strcpy(m_headers.subject, temp);
if( HeaderDecode(m_headers.filename, strlen(m_headers.filename), temp, sizeof(temp), &outputLength) )
strcpy(m_headers.filename, temp);
if( HeaderDecode(m_headers.name, strlen(m_headers.name), temp, sizeof(temp), &outputLength) )
strcpy(m_headers.name, temp);
//send CALLBACKS with message header info such as: from, to, subject
if( strcmp(m_headers.from, "")!=0 )
{
if( m_funcs.fnOnMessageHeader )
m_funcs.fnOnMessageHeader(m_pUserData, m_headers.from, m_headers.to, m_headers.subject);
}
//set default values
if( stricmp(m_headers.content_Type, "") == 0 )
{
strcpy(m_headers.content_Type, TYPE_TEXT);
strcpy(m_headers.content_Subtype, SUBTYPE_PLAIN);
}
if( stricmp(m_headers.charset, "") == 0 )
strcpy(m_headers.charset, "us-ascii");
if( stricmp(m_headers.content_Transfer_Encoding, "") == 0 )
strcpy(m_headers.content_Transfer_Encoding, ENCODE_7BIT);
//get filename from name
if( strcmp(m_headers.filename, "") == 0 && strcmp(m_headers.name, "") !=0 )
{
strcpy(m_headers.filename, m_headers.name);
}
//get filename from MIME type & subtype, mainly to get proper extension of file name
if( strcmp(m_headers.filename, "") == 0 )
{
char extension[MAX_EMAIL_LINE] = "";
GetExtFromMimeType(m_headers.content_Type, m_headers.content_Subtype, extension);
sprintf(m_headers.filename, "%d.%s", rand(), extension);
}
else //get pure file name of filename possiblly include path( indicater is '\\' or '/' )
{
char filePath[MAX_EMAIL_LINE];
strcpy(filePath, m_headers.filename);
GetPureFileName(filePath, m_headers.filename);
}
//decode multipart
if( stricmp(m_headers.content_Type, TYPE_MULTIPART) == 0 )
{
m_messageType = MESSAGE_TYPE_MULTIPART;
m_dataAccepter.m_pMessageDecoder = NULL;
}
//decode message
else if( stricmp(m_headers.content_Type, TYPE_MESSAGE) == 0
&& stricmp(m_headers.content_Subtype, SUBTYPE_RFC822) == 0 )
{
m_messageType = MESSAGE_TYPE_MESSAGE;
m_dataAccepter.m_pMessageDecoder = new CMessageDecoder(m_pUserData, &m_funcs);
}
//pure data
else
{
m_messageType = MESSAGE_TYPE_DATA;
if( stricmp(m_headers.content_Transfer_Encoding, ENCODE_7BIT) == 0
|| stricmp(m_headers.content_Transfer_Encoding, ENCODE_8BIT) == 0 )
m_dataAccepter.m_pTransferDecoder = new C7bit8bitTranferDecoder();
else if( stricmp(m_headers.content_Transfer_Encoding, ENCODE_QUOTED_PRINTABLE) == 0 )
m_dataAccepter.m_pTransferDecoder = new CQPTranferDecoder();
else if( stricmp(m_headers.content_Transfer_Encoding, ENCODE_BASE64) == 0 )
m_dataAccepter.m_pTransferDecoder = new CBase64TranferDecoder();
else
m_dataAccepter.m_pTransferDecoder = NULL;
if( m_funcs.fnOnMessageEntityHeader )
m_funcs.fnOnMessageEntityHeader(m_pUserData, m_headers.content_Type, m_headers.content_Subtype, m_headers.filename);
}
}
void CMessageDecoder::FindEnd()
{
if( m_status == MESSAGE_HEADER )
{
PostGetHeader();
}
else if( m_status == MESSAGE_BODY )
{
if( m_messageType == MESSAGE_TYPE_MULTIPART
|| m_messageType == MESSAGE_TYPE_MESSAGE )
{
if( m_dataAccepter.m_pMessageDecoder )
{
m_dataAccepter.m_pMessageDecoder->FindEnd();
delete m_dataAccepter.m_pMessageDecoder;
m_dataAccepter.m_pMessageDecoder = NULL;
}
}
else if( m_messageType == MESSAGE_TYPE_DATA )
{
if( m_dataAccepter.m_pTransferDecoder )
{
delete m_dataAccepter.m_pTransferDecoder;
m_dataAccepter.m_pTransferDecoder = NULL;
}
if( m_funcs.fnOnMessageEntityBodyEnd ) m_funcs.fnOnMessageEntityBodyEnd(m_pUserData);
}
}
m_status = MESSAGE_END;
}