/*
* This code implements retrieving info from MIME header
*
* (c) majvan 2002-2004
*/
#include "../stdafx.h"
//--------------------------------------------------------------------------------------------------
//Copies one string to another
// srcstart- source string
// srcend- address to the end of source string
// dest- pointer that stores new allocated string that contains copy of source string
// mode- MIME_PLAIN or MIME_MAIL (MIME_MAIL deletes '"' characters (or '<' and '>') if they are at start and end of source string
void CopyToHeader(char *srcstart, char *srcend, char **dest, int mode);
//Extracts email address (finds nick name and mail and then stores them to strings)
// finder- source string
// storeto- pointer that receives address of mail string
// storetonick- pointer that receives address of nickname
void ExtractAddressFromLine(char *finder, char **storeto, char **storetonick);
//Extracts simple text from string
// finder- source string
// storeto- pointer that receives address of string
void ExtractStringFromLine(char *finder, char **storeto);
//Extracts some item from content-type string
//Example: ContentType string: "TEXT/PLAIN; charset=US-ASCII", item:"charset=", returns: "US-ASCII"
// ContetType- content-type string
// value- string item
// returns extracted string (or NULL when not found)
char *ExtractFromContentType(char *ContentType, char *value);
//Extracts info from header text into header members
//Note that this function as well as struct CShortHeadwer can be always changed, because there are many items to extract
//(e.g. the X-Priority and Importance and so on)
// items- translated header (see TranslateHeaderFcn)
// head- header to be filled with values extracted from items
void ExtractShortHeader(struct CMimeItem *items, struct CShortHeader *head);
//Extracts header to mail using ExtractShortHeader fcn.
// items- translated header (see TranslateHeaderFcn)
// CP- codepage used when no default found
// head- header to be filled with values extracted from items, in unicode (wide char)
void ExtractHeader(struct CMimeItem *items, int &CP, struct CHeader *head);
//Deletes items in CShortHeader structure
// head- structure whose items are deleted
void DeleteShortHeaderContent(struct CShortHeader *head);
//Deletes list of YAMN_MIMENAMES structures
// Names- pointer to first item of list
void DeleteNames(CMimeNames *Names);
//Deletes list of YAMN_MIMESHORTNAMES structures
// Names- pointer to first item of list
void DeleteShortNames(CShortNames *Names);
//Makes a string lowercase
// string- string to be lowercased
void inline ToLower(char *string);
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
void CopyToHeader(char *srcstart, char *srcend, char **dest, int mode)
{
char *dst;
if (dest == nullptr)
return;
if (srcstart >= srcend)
return;
if ((mode == MIME_MAIL) && (((*srcstart == '"') && (*(srcend - 1) == '"')) || ((*srcstart == '<') && (*(srcend - 1) == '>')))) {
srcstart++;
srcend--;
}
if (srcstart >= srcend)
return;
if (nullptr != *dest)
delete[] * dest;
if (nullptr == (*dest = new char[srcend - srcstart + 1]))
return;
dst = *dest;
for (; srcstart < srcend; dst++, srcstart++) {
if (ENDLINE(srcstart)) {
while (ENDLINE(srcstart) || WS(srcstart)) srcstart++;
*dst = ' ';
srcstart--; //because at the end of "for loop" we increment srcstart
}
else
*dst = *srcstart;
}
*dst = 0;
}
void ExtractAddressFromLine(char *finder, char **storeto, char **storetonick)
{
if (finder == nullptr) {
*storeto = *storetonick = nullptr;
return;
}
while (WS(finder)) finder++;
if ((*finder) != '<') {
char *finderend = finder + 1;
do {
if (ENDLINEWS(finderend)) //after endline information continues
finderend += 2;
while (!ENDLINE(finderend) && !EOS(finderend)) finderend++; //seek to the end of line or to the end of string
}
while (ENDLINEWS(finderend));
finderend--;
while (WS(finderend) || ENDLINE(finderend)) finderend--; //find the end of text, no whitespace
if (*finderend != '>') //not '>' at the end of line
CopyToHeader(finder, finderend + 1, storeto, MIME_MAIL);
else //at the end of line, there's '>'
{
char *finder2 = finderend;
while ((*finder2 != '<') && (finder2 > finder)) finder2--; //go to matching '<' or to the start
CopyToHeader(finder2, finderend + 1, storeto, MIME_MAIL);
if (*finder2 == '<') //if we found '<', the rest copy as from nick
{
finder2--;
while (WS(finder2) || ENDLINE(finder2)) finder2--; //parse whitespace
CopyToHeader(finder, finder2 + 1, storetonick, MIME_MAIL); //and store nickname
}
}
}
else {
char *finderend = finder + 1;
do {
if (ENDLINEWS(finderend)) //after endline information continues
finderend += 2;
while (!ENDLINE(finderend) && (*finderend != '>') && !EOS(finderend)) finderend++; //seek to the matching < or to the end of line or to the end of string
}
while (ENDLINEWS(finderend));
CopyToHeader(finder, finderend + 1, storeto, MIME_MAIL); //go to first '>' or to the end and copy
finder = finderend + 1;
while (WS(finder)) finder++; //parse whitespace
if (!ENDLINE(finder) && !EOS(finder)) //if there are chars yet, it's nick
{
finderend = finder + 1;
while (!ENDLINE(finderend) && !EOS(finderend)) finderend++; //seek to the end of line or to the end of string
finderend--;
while (WS(finderend)) finderend--; //find the end of line, no whitespace
CopyToHeader(finder, finderend + 1, storetonick, MIME_MAIL);
}
}
}
void ExtractStringFromLine(char *finder, char **storeto)
{
if (finder == nullptr) {
*storeto = nullptr;
return;
}
while (WS(finder)) finder++;
char *finderend = finder;
do {
if (ENDLINEWS(finderend)) finderend++; //after endline information continues
while (!ENDLINE(finderend) && !EOS(finderend)) finderend++;
}
while (ENDLINEWS(finderend));
finderend--;
while (WS(finderend)) finderend--; //find the end of line, no whitespace
CopyToHeader(finder, finderend + 1, storeto, MIME_PLAIN);
}
char *ExtractFromContentType(char *ContentType, char *value)
{
char *lowered = _strdup(ContentType);
ToLower(lowered);
char *finder = strstr(lowered, value);
if (finder == nullptr) {
free(lowered);
return nullptr;
}
finder = finder - lowered + ContentType;
free(lowered);
char *temp, *copier;
char *CopiedString;
temp = finder - 1;
while ((temp > ContentType) && WS(temp)) temp--; //now we have to find, if the word "Charset=" is located after ';' like "; Charset="
if (*temp != ';' && !ENDLINE(temp) && temp != ContentType)
return nullptr;
finder = finder + mir_strlen(value); //jump over value string
while (WS(finder)) finder++; //jump over whitespaces
temp = finder;
while (*temp != 0 && *temp != ';') temp++; //jump to the end of setting (to the next ;)
temp--;
while (WS(temp)) temp--; //remove whitespaces from the end
if (*finder == '\"') { //remove heading and tailing quotes
finder++;
if (*temp == '\"') temp--;
}
if (nullptr == (CopiedString = new char[++temp - finder + 1]))
return nullptr;
for (copier = CopiedString; finder != temp; *copier++ = *finder++); //copy string
*copier = 0; //and end it with zero character
return CopiedString;
}
void ExtractShortHeader(struct CMimeItem *items, struct CShortHeader *head)
{
for (; items != nullptr; items = items->Next) {
//at the start of line
//MessageBox(NULL,items->value,items->name,0);
if (0 == _strnicmp(items->name, "From", 4)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractAddressFromLine(items->value, &head->From, &head->FromNick);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "Return-Path", 11)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractAddressFromLine(items->value, &head->ReturnPath, &head->ReturnPathNick);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "Subject", 7)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractStringFromLine(items->value, &head->Subject);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "Body", 4)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractStringFromLine(items->value, &head->Body);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "Date", 4)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractStringFromLine(items->value, &head->Date);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "Content-Type", 12)) {
if (items->value == nullptr)
continue;
char *ContentType = nullptr, *CharSetStr;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
ExtractStringFromLine(items->value, &ContentType);
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
ToLower(ContentType);
if (nullptr != (CharSetStr = ExtractFromContentType(ContentType, "charset="))) {
head->CP = GetCharsetFromString(CharSetStr, mir_strlen(CharSetStr));
delete[] CharSetStr;
}
delete[] ContentType;
}
else if (0 == _strnicmp(items->name, "Importance", 10)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
if (head->Priority != -1) {
if (0 == strncmp(items->value, "low", 3))
head->Priority = 5;
else if (0 == strncmp(items->value, "normal", 6))
head->Priority = 3;
else if (0 == strncmp(items->value, "high", 4))
head->Priority = 1;
}
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
else if (0 == _strnicmp(items->name, "X-Priority", 10)) {
if (items->value == nullptr)
continue;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "");
#endif
if ((*items->value >= '1') && (*items->value <= '5'))
head->Priority = *items->value - '0';
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
}
}
}
void ExtractHeader(struct CMimeItem *items, int &CP, struct CHeader *head)
{
struct CShortHeader ShortHeader;
memset(&ShortHeader, 0, sizeof(struct CShortHeader));
ShortHeader.Priority = ShortHeader.CP = -1;
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
ExtractShortHeader(items, &ShortHeader);
head->Priority = ShortHeader.Priority == -1 ? 3 : ShortHeader.Priority;
CP = ShortHeader.CP == -1 ? CP : ShortHeader.CP;
#ifdef DEBUG_DECODE
if (NULL != ShortHeader.From)
DebugLog(DecodeFile, "%s%s%s%s%s%s\n");
DebugLog(DecodeFile, "\n");
#endif
ConvertCodedStringToUnicode(ShortHeader.From, &head->From, CP, MIME_PLAIN);
#ifdef DEBUG_DECODE
if (NULL != head->From)
DebugLogW(DecodeFile, L"%s\n", head->From);
#endif
ConvertCodedStringToUnicode(ShortHeader.FromNick, &head->FromNick, CP, MIME_MAIL);
#ifdef DEBUG_DECODE
if (NULL != head->FromNick)
DebugLogW(DecodeFile, L"%s\n", head->FromNick);
#endif
ConvertCodedStringToUnicode(ShortHeader.ReturnPath, &head->ReturnPath, CP, MIME_PLAIN);
#ifdef DEBUG_DECODE
if (NULL != head->ReturnPath)
DebugLogW(DecodeFile, L"%s\n", head->ReturnPath);
#endif
ConvertCodedStringToUnicode(ShortHeader.ReturnPathNick, &head->ReturnPathNick, CP, MIME_MAIL);
#ifdef DEBUG_DECODE
if (NULL != head->ReturnPathNick)
DebugLogW(DecodeFile, L"%s\n", head->ReturnPathNick);
#endif
ConvertCodedStringToUnicode(ShortHeader.Subject, &head->Subject, CP, MIME_PLAIN);
#ifdef DEBUG_DECODE
if (NULL != head->Subject)
DebugLogW(DecodeFile, L"%s\n", head->Subject);
#endif
ConvertCodedStringToUnicode(ShortHeader.Date, &head->Date, CP, MIME_PLAIN);
#ifdef DEBUG_DECODE
if (NULL != head->Date)
DebugLogW(DecodeFile, L"%s\n", head->Date);
#endif
ConvertCodedStringToUnicode(ShortHeader.Body, &head->Body, CP, MIME_PLAIN);
#ifdef DEBUG_DECODE
if (NULL != head->Body)
DebugLogW(DecodeFile, L"%s\n", head->Body);
#endif
#ifdef DEBUG_DECODE
DebugLog(DecodeFile, "\n");
#endif
DeleteShortHeaderContent(&ShortHeader);
// head->From=L"Frommmm";
// head->Subject=L"Subject";
return;
}
void DeleteShortHeaderContent(struct CShortHeader *head)
{
if (head->From != nullptr) delete[] head->From;
if (head->FromNick != nullptr) delete[] head->FromNick;
if (head->ReturnPath != nullptr) delete[] head->ReturnPath;
if (head->ReturnPathNick != nullptr) delete[] head->ReturnPathNick;
if (head->Subject != nullptr) delete[] head->Subject;
if (head->Date != nullptr) delete[] head->Date;
if (head->To != nullptr) DeleteShortNames(head->To);
if (head->Cc != nullptr) DeleteShortNames(head->Cc);
if (head->Bcc != nullptr) DeleteShortNames(head->Bcc);
if (head->Body != nullptr) delete[] head->Body;
}
void DeleteHeaderContent(struct CHeader *head)
{
if (head->From != nullptr) delete[] head->From;
if (head->FromNick != nullptr) delete[] head->FromNick;
if (head->ReturnPath != nullptr) delete[] head->ReturnPath;
if (head->ReturnPathNick != nullptr) delete[] head->ReturnPathNick;
if (head->Subject != nullptr) delete[] head->Subject;
if (head->Date != nullptr) delete[] head->Date;
if (head->Body != nullptr) delete[] head->Body;
if (head->To != nullptr) DeleteNames(head->To);
if (head->Cc != nullptr) DeleteNames(head->Cc);
if (head->Bcc != nullptr) DeleteNames(head->Bcc);
}
void DeleteNames(CMimeNames *Names)
{
CMimeNames *Parser = Names;
for (; Parser != nullptr; Parser = Parser->Next) {
if (Parser->Value != nullptr)
delete[] Parser->Value;
if (Parser->ValueNick != nullptr)
delete[] Parser->ValueNick;
CMimeNames *Old = Parser;
Parser = Parser->Next;
delete Old;
}
}
void DeleteShortNames(CShortNames *Names)
{
CShortNames *Parser = Names;
for (; Parser != nullptr; Parser = Parser->Next) {
if (Parser->Value != nullptr)
delete[] Parser->Value;
if (Parser->ValueNick != nullptr)
delete[] Parser->ValueNick;
CShortNames *Old = Parser;
Parser = Parser->Next;
delete Old;
}
}
void inline ToLower(char *string)
{
for (; *string != 0; string++)
if (*string >= 'A' && *string <= 'Z') *string = *string - 'A' + 'a';
}
#define TE_UNKNOWN
#define TE_QUOTEDPRINTABLE 1
#define TE_BASE64 2
struct APartDataType
{
char *Src;//Input
char *ContType;
int CodePage;
char *TransEnc;
BYTE TransEncType; //TE_something
char *body;
int bodyLen;
WCHAR *wBody;
};
void ParseAPart(APartDataType *data)
{
size_t len = mir_strlen(data->Src);
try {
char *finder = data->Src;
char *prev1, *prev2, *prev3;
while (finder <= (data->Src + len)) {
while (ENDLINEWS(finder)) finder++;
//at the start of line
if (finder > data->Src) {
if (*(finder - 2) == '\r' || *(finder - 2) == '\n')
*(finder - 2) = 0;
if (*(finder - 1) == '\r' || *(finder - 1) == '\n')
*(finder - 1) = 0;
}
prev1 = finder;
while (*finder != ':' && !EOS(finder) && !ENDLINE(finder)) finder++;
if (ENDLINE(finder) || EOS(finder)) {
// no ":" in the line? here the body begins;
data->body = prev1;
break;
}
prev2 = finder++;
while (WS(finder) && !EOS(finder)) finder++;
if (!EOS(finder))
prev3 = finder;
else
break;
do {
if (ENDLINEWS(finder)) finder += 2; //after endline information continues
while (!ENDLINE(finder) && !EOS(finder)) finder++;
}
while (ENDLINEWS(finder));
if (!_strnicmp(prev1, "Content-type", prev2 - prev1)) {
data->ContType = prev3;
}
else if (!_strnicmp(prev1, "Content-Transfer-Encoding", prev2 - prev1)) {
data->TransEnc = prev3;
}
if (EOS(finder))
break;
finder++;
if (ENDLINE(finder)) {
finder++;
if (ENDLINE(finder)) {
// end of headers. message body begins
if (finder > data->Src) {
if (*(finder - 2) == '\r' || *(finder - 2) == '\n')
*(finder - 2) = 0;
if (*(finder - 1) == '\r' || *(finder - 1) == '\n')
*(finder - 1) = 0;
}
finder++;
if (ENDLINE(finder))finder++;
prev1 = finder;
while (!EOS(finder + 1))finder++;
if (ENDLINE(finder))finder--;
prev2 = finder;
if (prev2 > prev1) { // yes, we have body
data->body = prev1;
}
break; // there is nothing else
}
}
}
}
catch (...) {
MessageBox(nullptr, TranslateT("Translate header error"), L"", 0);
}
if (data->body) data->bodyLen = (int)mir_strlen(data->body);
}
//from decode.cpp
int DecodeQuotedPrintable(char *Src, char *Dst, int DstLen, BOOL isQ);
int DecodeBase64(char *Src, char *Dst, int DstLen);
int ConvertStringToUnicode(char *stream, unsigned int cp, WCHAR **out);
WCHAR *ParseMultipartBody(char *src, char *bond)
{
char *srcback = _strdup(src);
size_t sizebond = mir_strlen(bond);
int numparts = 1;
int i;
char *courbond = srcback;
WCHAR *dest;
for (; (courbond = strstr(courbond, bond)); numparts++, courbond += sizebond);
APartDataType *partData = new APartDataType[numparts];
memset(partData, 0, sizeof(APartDataType)*numparts);
partData[0].Src = courbond = srcback;
for (i = 1; (courbond = strstr(courbond, bond)); i++, courbond += sizebond) {
*(courbond - 2) = 0;
partData[i].Src = courbond + sizebond;
while (ENDLINE(partData[i].Src)) partData[i].Src++;
}
size_t resultSize = 0;
for (i = 0; i < numparts; i++) {
ParseAPart(&partData[i]);
if (partData[i].body) {
if (partData[i].TransEnc) {
if (!_stricmp(partData[i].TransEnc, "base64")) partData[i].TransEncType = TE_BASE64;
else if (!_stricmp(partData[i].TransEnc, "quoted-printable"))partData[i].TransEncType = TE_QUOTEDPRINTABLE;
}
if (partData[i].ContType) {
char *CharSetStr;
if (nullptr != (CharSetStr = ExtractFromContentType(partData[i].ContType, "charset="))) {
partData[i].CodePage = GetCharsetFromString(CharSetStr, mir_strlen(CharSetStr));
delete[] CharSetStr;
}
}
if (partData[i].ContType && !_strnicmp(partData[i].ContType, "text", 4)) {
char *localBody = nullptr;
switch (partData[i].TransEncType) {
case TE_BASE64:
{
int size = partData[i].bodyLen * 3 / 4 + 5;
localBody = new char[size + 1];
DecodeBase64(partData[i].body, localBody, size);
}break;
case TE_QUOTEDPRINTABLE:
{
int size = partData[i].bodyLen + 2;
localBody = new char[size + 1];
DecodeQuotedPrintable(partData[i].body, localBody, size, FALSE);
}break;
}
ConvertStringToUnicode(localBody ? localBody : partData[i].body, partData[i].CodePage, &partData[i].wBody);
if (localBody) delete[] localBody;
}
else if (partData[i].ContType && !_strnicmp(partData[i].ContType, "multipart/", 10)) {
//Multipart in mulitipart recursive? should be SPAM. Ah well
char *bondary = nullptr;
if (nullptr != (bondary = ExtractFromContentType(partData[i].ContType, "boundary="))) {
partData[i].wBody = ParseMultipartBody(partData[i].body, bondary);
delete[] bondary;
}
else goto FailBackRaw; //multipart with no boundary? badly formatted messages.
}
else {
FailBackRaw:
ConvertStringToUnicode(partData[i].body, partData[i].CodePage, &partData[i].wBody);
}
resultSize += mir_wstrlen(partData[i].wBody);
}// if (partData[i].body)
resultSize += 100 + 4 + 3; //cr+nl+100+ 3*bullet
}
dest = new WCHAR[resultSize + 1];
size_t destpos = 0;
for (i = 0; i < numparts; i++) {
if (i) { // part before first boudary should not have headers
char infoline[1024]; size_t linesize = 0;
mir_snprintf(infoline, "%s %d", Translate("Part"), i);
linesize = mir_strlen(infoline);
if (partData[i].TransEnc) {
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].TransEnc);
linesize = mir_strlen(infoline);
}
if (partData[i].ContType) {
char *CharSetStr = strchr(partData[i].ContType, ';');
if (CharSetStr) {
CharSetStr[0] = 0;
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].ContType);
linesize = mir_strlen(infoline);
partData[i].ContType = CharSetStr + 1;
if (nullptr != (CharSetStr = ExtractFromContentType(partData[i].ContType, "charset="))) {
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", CharSetStr);
linesize = mir_strlen(infoline);
delete[] CharSetStr;
}
if (nullptr != (CharSetStr = ExtractFromContentType(partData[i].ContType, "name="))) {
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; \"%s\"", CharSetStr);
linesize = mir_strlen(infoline);
delete[] CharSetStr;
}
}
else {
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, "; %s", partData[i].ContType);
linesize = mir_strlen(infoline);
}
}
mir_snprintf(infoline + linesize, _countof(infoline) - linesize, ".\r\n");
{
WCHAR *temp = nullptr;
dest[destpos] = dest[destpos + 1] = dest[destpos + 2] = 0x2022; // bullet;
destpos += 3;
ConvertStringToUnicode(infoline, CP_ACP, &temp);
size_t wsize = mir_wstrlen(temp);
mir_wstrcpy(&dest[destpos], temp);
destpos += wsize;
delete[] temp;
}
} // if (i)
if (partData[i].wBody) {
size_t wsize = mir_wstrlen(partData[i].wBody);
mir_wstrcpy(&dest[destpos], partData[i].wBody);
destpos += wsize;
delete[] partData[i].wBody;
}
}
free(srcback);
delete[] partData;
dest[resultSize] = 0;//just in case
return dest;
}