Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1086 lines
36 KiB

#include "pch.hxx"
#include <cryptdlg.h>
#include "strconst.h"
#include "msoert.h"
#include "resource.h"
#include "mailutil.h"
#include "ipab.h"
#include "receipts.h"
#include "oestore.h"
#include "shlwapip.h"
#include "goptions.h"
#include "conman.h"
#include "multlang.h"
#include "demand.h"
#include "secutil.h"
#ifdef SMIME_V3
HRESULT ProcessSecureReceipt(IMimeMessage * pMsg, IStoreCallback *pStoreCB);
INT_PTR CALLBACK SecRecResDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
#endif // SMIME_V3
const WCHAR c_szReadableTextFirst[] = L"This is a receipt for the mail you sent to\r\n";
const WCHAR c_szReadableTextSecond[] = L"\r\n\r\nThis receipt verifies that the message has been displayed on the recipient's computer at ";
const WCHAR c_szReceiptsAt[] = L" at ";
const WCHAR c_szReadReceipt[] = L"Read: %s";
const WCHAR c_szSecReadReceipt[] = L"Secure Receipt: %s";
//Does not include NULL
const int cbReadableTextFirst = sizeof(c_szReadableTextFirst) - sizeof(*c_szReadableTextFirst);
const int cbReadableTextSecond = sizeof(c_szReadableTextSecond) - sizeof(*c_szReadableTextSecond);
const int cbReceiptsAt = sizeof(c_szReceiptsAt) - sizeof(*c_szReceiptsAt);
//Include NULL
const int cbReadReceipt = sizeof(c_szReadReceipt);
//Not wide, does not include NULL
const TCHAR c_szDisposition[] = TEXT("\r\nDisposition: manual-action/");
const TCHAR c_szMDNSendAutomatically[] = TEXT("MDN-sent-automatically; ");
const TCHAR c_szMDNSendManually[] = TEXT("MDN-sent-manually; ");
const TCHAR c_szOriginalRecipient[] = TEXT("\r\nOriginal-Recipient: rfc822;");
const TCHAR c_szFinalRecipient[] = TEXT("Final-Recipient: rfc822;");
const TCHAR c_szOriginalMessageId[] = TEXT("\r\nOriginal-Message-ID: ");
const TCHAR c_szDisplayed[] = TEXT("displayed\r\n");
//Does not include NULL
const int cbDisposition = sizeof(c_szDisposition) - sizeof(*c_szDisposition);
const int cbMDNSendAutomatically = sizeof(c_szMDNSendAutomatically) - sizeof(*c_szMDNSendAutomatically);
const int cbMDNSendManually = sizeof(c_szMDNSendManually) - sizeof(*c_szMDNSendManually);
const int cbOriginalRecipient = sizeof(c_szOriginalRecipient) - sizeof(*c_szOriginalRecipient);
const int cbFinalRecipient = sizeof(c_szFinalRecipient) - sizeof(*c_szFinalRecipient);
const int cbOriginalMessageId = sizeof(c_szOriginalMessageId) - sizeof(*c_szOriginalMessageId);
const int cbDisplayed = sizeof(c_szDisplayed) - sizeof(*c_szDisplayed);
HRESULT ProcessReturnReceipts(IMessageTable *pTable,
IStoreCallback *pStoreCB,
ROWINDEX iRow,
RECEIPTTYPE ReceiptType,
FOLDERID IdFolder,
IMimeMessage *pMsg)
{
IMimeMessage *pMessage = NULL;
IMimeMessage *pMessageRcpt = NULL;
BOOL fSendImmediate = FALSE;
BOOL fMail = TRUE;
LPWABAL lpWabal = NULL;
LPWSTR lpsz = NULL;
MESSAGEINFO *pMsgInfo = NULL;
IImnAccount *pTempAccount = NULL;
HRESULT hr = S_OK;
DWORD dwOption;
PROPVARIANT var = {0};
FOLDERINFO FolderInfo = {0};
TraceCall("ProcessReturnReceipts");
if (!pTable || !g_pAcctMan)
return TraceResult(E_INVALIDARG);
IF_FAILEXIT(hr = g_pStore->GetFolderInfo(IdFolder, &FolderInfo));
if (FolderInfo.tySpecial == FOLDER_OUTBOX || FolderInfo.tyFolder == FOLDER_NEWS)
{
goto exit;
}
//First of all check if this has already been processed
IF_FAILEXIT(hr = pTable->GetRow(iRow, &pMsgInfo));
if ((!pMsgInfo) || (pMsgInfo->dwFlags & (ARF_RCPT_PROCESSED | ARF_NEWSMSG)))
goto exit;
//Mark this flag as having been processed
IF_FAILEXIT(hr = pTable->Mark(&iRow, 1, APPLY_SPECIFIED, MARK_MESSAGE_RCPT_PROCESSED, pStoreCB));
if(!pMsg)
IF_FAILEXIT(hr = pTable->OpenMessage(iRow, 0, &pMessage, pStoreCB));
else
pMessage = pMsg;
#ifdef SMIME_V3
// Secure receipt check
hr = ProcessSecureReceipt(pMessage, pStoreCB);
if(hr != S_OK)
goto exit;
#endif // SMIME_V3
dwOption = DwGetOption(OPT_MDN_SEND_RECEIPT);
if (dwOption == MDN_DONT_SENDRECEIPT)
goto exit;
hr = MimeOleGetBodyPropW(pMessage, HBODY_ROOT, STR_HDR_DISP_NOTIFICATION_TO,
NOFLAGS, &lpsz);
if (FAILED(hr) || !lpsz || !*lpsz)
{
//Check if there is a Return-Receipt-To header
hr = MimeOleGetBodyPropW(pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_RETRCPTTO),
NOFLAGS, &lpsz);
if (FAILED(hr) || !lpsz || !*lpsz)
{
if (hr == MIME_E_NOT_FOUND)
{
//this is a legitimate error, so we don't want to show an error to the user
hr = S_OK;
}
goto exit;
}
}
if (dwOption == MDN_PROMPTFOR_SENDRECEIPT)
{
if (!PromptReturnReceipts(pStoreCB))
goto exit;
}
var.vt = VT_LPSTR;
IF_FAILEXIT(hr = pMessage->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var));
IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pTempAccount));
if ((dwOption == MDN_SENDRECEIPT_AUTO) && (!!DwGetOption(OPT_TO_CC_LINE_RCPT)))
{
IF_FAILEXIT(hr = CheckForLists(pMessage, pStoreCB, pTempAccount));
if (hr == S_FALSE)
{
// My name is not found in the list. So we don't send a receipt.
// However this is not failure.
goto exit;
}
}
IF_FAILEXIT(hr = HrCreateMessage (&pMessageRcpt));
IF_FAILEXIT(hr = SetRootHeaderFields(pMessageRcpt, pMessage, lpsz, READRECEIPT));
IF_FAILEXIT(hr = HrSetAccountByAccount(pMessageRcpt, pTempAccount));
IF_FAILEXIT(hr = HrGetWabalFromMsg(pMessageRcpt, &lpWabal));
IF_FAILEXIT(hr = HrSetSenderInfoUtil(pMessageRcpt, pTempAccount, lpWabal, TRUE, 0, FALSE));
IF_FAILEXIT(hr = HrSetWabalOnMsg(pMessageRcpt, lpWabal));
fSendImmediate = (DwGetOption(OPT_SENDIMMEDIATE) && !g_pConMan->IsGlobalOffline());
IF_FAILEXIT(hr = SendMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, fMail));
exit:
//Show an error if it failed
if (FAILED(hr))
ShowErrorMessage(pStoreCB);
g_pStore->FreeRecord(&FolderInfo);
SafeMemFree(var.pszVal);
pTable->ReleaseRow(pMsgInfo);
SafeRelease(pMessageRcpt);
SafeRelease(pTempAccount);
SafeRelease(lpWabal);
SafeMemFree(lpsz);
if(!pMsg)
SafeRelease(pMessage);
return hr;
}
HRESULT SetRootHeaderFields(IMimeMessage *pMessageRcpt,
IMimeMessage *pOriginalMsg,
LPWSTR lpszNotificationTo,
RECEIPTTYPE ReceiptType)
{
HRESULT hr = S_OK;
LPWSTR lpsz = NULL;
LPWSTR szParam = NULL;
UINT ResId;
LPWSTR pszRefs = NULL;
LPWSTR pszOrigRefs = NULL;
LPWSTR pszNewRef = NULL;
WCHAR lpBuffer[CCHMAX_STRINGRES];
int cch = 0;
// Trace
TraceCall("_SetRootHeaderFields");
// Set the subject and the To field.
IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, lpszNotificationTo));
IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, STR_ATT_NORMSUBJ, NOFLAGS, &lpsz));
switch (ReceiptType)
{
case DELETERECEIPT:
ResId = idsDeleteReceipt;
break;
case READRECEIPT:
default:
ResId = idsReadReceipt;
break;
}
if (fMessageEncodingMatch(pOriginalMsg))
{
AthLoadStringW(ResId, lpBuffer, ARRAYSIZE(lpBuffer));
}
else
{
//The encoding didn't match, so we just load english headers
StrCpyNW(lpBuffer, c_szReadReceipt, ARRAYSIZE(lpBuffer));
}
cch = lstrlenW(lpsz) + lstrlenW(lpBuffer) + 1;
IF_FAILEXIT(hr = HrAlloc((LPVOID*)&szParam, cch * sizeof(WCHAR)));
wnsprintfW(szParam, cch, lpBuffer, lpsz);
IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, szParam));
//Set Time
IF_FAILEXIT(hr = HrSetSentTimeProp(pMessageRcpt, NULL));
//Set References property
IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pszNewRef));
//No need to check for return value. It gets handled correctly in HrCreateReferences
MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, &pszOrigRefs);
IF_FAILEXIT(hr = HrCreateReferences(pszOrigRefs, pszNewRef, &pszRefs));
IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, pszRefs));
//Readable text
//TODO: Change the file name depending on the ReceiptType
IF_FAILEXIT(hr = InsertReadableText(pMessageRcpt, pOriginalMsg));
//Create the second part
IF_FAILEXIT(hr = InsertSecondComponent(pMessageRcpt, pOriginalMsg));
//Set Content-type field. Content-type: multipart/report; report-type=disposition-notification;
//Content-type is multipart, sub-content-type is report and report-type is a parameter whose
//value should be set to disposition-notification
IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_CNTTYPE), NOFLAGS, c_szMultiPartReport));
IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, HBODY_ROOT, STR_PAR_REPORTTYPE, PDF_ENCODED, c_szDispositionNotification));
exit:
SafeMemFree(lpsz);
SafeMimeOleFree(pszNewRef);
SafeMemFree(szParam);
SafeMimeOleFree(pszOrigRefs);
SafeMemFree(pszRefs);
return(hr);
}
HRESULT InsertReadableText(IMimeMessage *pMessageRcpt, IMimeMessage *pOriginalMsg)
{
HBODY hBody;
IStream *pStream = NULL;
HRESULT hr = S_OK;
LPWSTR lpsz = NULL;
ULONG cbWritten;
WCHAR lpBuffer[CCHMAX_STRINGRES];
HCHARSET hCharset = NULL;
WCHAR wszReceiptSentDate[CCHMAX_STRINGRES];
WCHAR wszOriginalSentDate[CCHMAX_STRINGRES];
PROPVARIANT varOriginal;
PROPVARIANT var;
INETCSETINFO CsetInfo = {0};
IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, &lpsz));
IF_FAILEXIT(hr = MimeOleCreateVirtualStream((IStream**)&pStream));
varOriginal.vt = VT_FILETIME;
IF_FAILEXIT(hr = pOriginalMsg->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &varOriginal));
var.vt = VT_FILETIME;
IF_FAILEXIT(hr = pMessageRcpt->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &var));
if (fMessageEncodingMatch(pOriginalMsg))
{
AthLoadStringW(idsReadableTextFirst, lpBuffer, CCHMAX_STRINGRES);
IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlenW(lpsz) * sizeof(WCHAR), &cbWritten));
*lpBuffer = 0;
AthLoadStringW(idsReceiptAt, lpBuffer, CCHMAX_STRINGRES);
IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
*wszOriginalSentDate = 0;
AthFileTimeToDateTimeW(&varOriginal.filetime, wszOriginalSentDate, ARRAYSIZE(wszOriginalSentDate),
DTM_NOSECONDS);
IF_FAILEXIT(hr = pStream->Write(wszOriginalSentDate, lstrlenW(wszOriginalSentDate) * sizeof(*wszOriginalSentDate),
&cbWritten));
AthLoadStringW(idsReadableTextSecond, lpBuffer, CCHMAX_STRINGRES);
IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
*wszReceiptSentDate = 0;
AthFileTimeToDateTimeW(&var.filetime, wszReceiptSentDate, ARRAYSIZE(wszReceiptSentDate),
DTM_NOSECONDS);
}
else
{
//Insert English headers and content
IF_FAILEXIT(hr = pStream->Write(c_szReadableTextFirst, cbReadableTextFirst, &cbWritten));
IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlenW(lpsz) * sizeof(WCHAR), &cbWritten));
IF_FAILEXIT(hr = pStream->Write(c_szReceiptsAt, cbReceiptsAt, &cbWritten));
//Original Sent Date
*wszOriginalSentDate = 0;
AthFileTimeToDateTimeW(&varOriginal.filetime, wszOriginalSentDate, ARRAYSIZE(wszOriginalSentDate),
DTM_FORCEWESTERN | DTM_NOSECONDS);
IF_FAILEXIT(hr = pStream->Write(wszOriginalSentDate, lstrlenW(wszOriginalSentDate) * sizeof(*wszOriginalSentDate),
&cbWritten));
//Second line of readable text
IF_FAILEXIT(hr = pStream->Write(c_szReadableTextSecond, cbReadableTextSecond, &cbWritten));
//Receipt Sent Date
*wszReceiptSentDate = 0;
AthFileTimeToDateTimeW(&var.filetime, wszReceiptSentDate, ARRAYSIZE(wszReceiptSentDate),
DTM_FORCEWESTERN | DTM_NOSECONDS);
}
IF_FAILEXIT(hr = pStream->Write(wszReceiptSentDate, lstrlenW(wszReceiptSentDate) * sizeof(*wszReceiptSentDate),
&cbWritten));
IF_FAILEXIT(hr = pOriginalMsg->GetCharset(&hCharset));
// re-map CP_JAUTODETECT and CP_KAUTODETECT if necessary
// re-map iso-2022-jp to default charset if they are in the same category
IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CsetInfo));
IF_NULLEXIT(hCharset = GetMimeCharsetFromCodePage(GetMapCP(CsetInfo.cpiInternet, FALSE)));
IF_FAILEXIT(hr = pMessageRcpt->SetCharset(hCharset, CSET_APPLY_ALL));
IF_FAILEXIT(hr = pMessageRcpt->SetTextBody(TXT_PLAIN, IET_UNICODE, NULL, pStream, &hBody));
exit:
SafeRelease(pStream);
SafeMemFree(lpsz);
return hr;
}
HRESULT InsertSecondComponent(IMimeMessage *pMessageRcpt, IMimeMessage *pOriginalMsg)
{
HBODY hBody;
ULONG cbWritten;
IStream *pStream = NULL;
LPTSTR lpsz = NULL;
HRESULT hr = S_OK;
IF_FAILEXIT(hr = MimeOleCreateVirtualStream((IStream**)&pStream));
//Final Recipient
IF_FAILEXIT(hr = AddOriginalAndFinalRecipient(pOriginalMsg, pMessageRcpt, pStream));
//Original Message Id
IF_FAILEXIT(hr = MimeOleGetBodyPropA(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &lpsz));
IF_FAILEXIT(hr = pStream->Write(c_szOriginalMessageId, cbOriginalMessageId, &cbWritten));
IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlen(lpsz) * sizeof(TCHAR), &cbWritten));
//TODO:If the receipt type is deleted, change the last parameter to reflect the correct string
IF_FAILEXIT(hr = pStream->Write(c_szDisposition, cbDisposition, &cbWritten));
if (DwGetOption(OPT_MDN_SEND_RECEIPT) == MDN_PROMPTFOR_SENDRECEIPT)
IF_FAILEXIT(hr = pStream->Write(c_szMDNSendManually, cbMDNSendManually, &cbWritten));
else
IF_FAILEXIT(hr = pStream->Write(c_szMDNSendAutomatically, cbMDNSendAutomatically, &cbWritten));
IF_FAILEXIT(hr = pStream->Write(c_szDisplayed, cbDisplayed, &cbWritten));
IF_FAILEXIT(hr = pStream->Commit(STGC_DEFAULT));
IF_FAILEXIT(hr = pMessageRcpt->AttachObject(IID_IStream, pStream, &hBody));
IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, hBody, PIDTOSTR(PID_HDR_CNTTYPE), NOFLAGS, c_szMessageDispNotification));
//Change the content-disposition header field to inline, so that this body gets copied and not get attached.
IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, hBody, PIDTOSTR(PID_HDR_CNTDISP), NOFLAGS, STR_DIS_INLINE));
exit:
MemFree(lpsz);
ReleaseObj(pStream);
return hr;
}
HRESULT AddOriginalAndFinalRecipient(IMimeMessage *pOriginalMsg,
IMimeMessage *pMessageRcpt,
IStream *pStream)
{
IMimeEnumAddressTypes *pEnumAddrTypes = NULL;
HRESULT hr = S_OK;
ULONG cbWritten;
ULONG Count;
ADDRESSPROPS AddressProps;
ULONG cFetched;
IMimeAddressTable *pAddrTable = NULL;
LPTSTR lpszOriginalRecip = NULL;
ADDRESSLIST addrList = {0};
IF_FAILEXIT(hr = pOriginalMsg->BindToObject(HBODY_ROOT, IID_IMimeAddressTable, (LPVOID*)&pAddrTable));
IF_FAILEXIT(hr = pAddrTable->EnumTypes(IAT_FROM, IAP_EMAIL, &pEnumAddrTypes));
#ifdef DEBUG
IF_FAILEXIT(hr = pEnumAddrTypes->Count(&Count));
Assert (Count == 1);
#endif DEBUG
IF_FAILEXIT(hr = pEnumAddrTypes->Next(1, &AddressProps, &cFetched));
Assert(cFetched == 1);
if (!(AddressProps.pszEmail) || !(*AddressProps.pszEmail))
{
hr = E_FAIL;
goto exit;
}
IF_FAILEXIT(hr = pStream->Write(c_szFinalRecipient, cbFinalRecipient, &cbWritten));
IF_FAILEXIT(hr = pStream->Write(AddressProps.pszEmail, lstrlen(AddressProps.pszEmail) * sizeof(TCHAR), &cbWritten));
hr = MimeOleGetBodyPropA(pOriginalMsg, HBODY_ROOT, STR_HDR_ORIG_RECIPIENT, NOFLAGS, &lpszOriginalRecip);
if (SUCCEEDED(hr))
{
IF_FAILEXIT(hr = MimeOleParseRfc822Address(IAT_TO, IET_ENCODED, lpszOriginalRecip, &addrList));
if (addrList.cAdrs > 0)
{
IF_FAILEXIT(hr = pStream->Write(c_szOriginalRecipient, cbOriginalRecipient, &cbWritten));
IF_FAILEXIT(hr = pStream->Write(addrList.prgAdr[0].pszEmail, lstrlen(addrList.prgAdr[0].pszEmail), &cbWritten));
}
}
else
{
if (hr == MIME_E_NOT_FOUND)
hr = S_OK;
}
exit:
if (g_pMoleAlloc)
{
g_pMoleAlloc->FreeAddressProps(&AddressProps);
if (addrList.cAdrs)
g_pMoleAlloc->FreeAddressList(&addrList);
}
ReleaseObj(pEnumAddrTypes);
ReleaseObj(pAddrTable);
MemFree(lpszOriginalRecip);
return hr;
}
HRESULT CheckForLists(IMimeMessage *pOriginalMsg, IStoreCallback *pStoreCB, IImnAccount *pDefAccount)
{
HRESULT hr = S_OK;
IMimeEnumAddressTypes *pEnumAddrTypes = NULL;
ULONG cbAddrTypes;
ULONG cFetched;
ADDRESSPROPS *prgAddress = NULL;
TCHAR szEmail[CCHMAX_EMAIL_ADDRESS];
DWORD index;
IF_FAILEXIT(hr = pOriginalMsg->EnumAddressTypes(IAT_TO | IAT_CC, IAP_EMAIL, &pEnumAddrTypes));
IF_FAILEXIT(hr = pEnumAddrTypes->Count(&cbAddrTypes));
IF_FAILEXIT(hr = HrAlloc((LPVOID*)&prgAddress, cbAddrTypes * sizeof(ADDRESSPROPS)));
IF_FAILEXIT(hr = pEnumAddrTypes->Next(cbAddrTypes, prgAddress, &cFetched));
IF_FAILEXIT(hr = pDefAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szEmail, ARRAYSIZE(szEmail)));
if (!(*szEmail))
{
hr = E_FAIL;
goto exit;
}
for (index = 0; index < cFetched; index++)
{
if (lstrcmpi(szEmail, (prgAddress + index)->pszEmail) == 0)
break;
}
if (index == cFetched)
{
//We did not find it.
hr = S_FALSE;
}
exit:
if (g_pMoleAlloc && prgAddress)
{
for (index = 0; index < cFetched; index++)
{
g_pMoleAlloc->FreeAddressProps(&prgAddress[index]);
}
}
MemFree(prgAddress);
ReleaseObj(pEnumAddrTypes);
return hr;
}
void ShowErrorMessage(IStoreCallback *pStoreCB)
{
HWND hwnd;
Assert(pStoreCB);
if (SUCCEEDED(pStoreCB->GetParentWindow(0, &hwnd)))
{
AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsReceiptsError),
0, MB_OK);
}
}
BOOL PromptReturnReceipts(IStoreCallback *pStoreCB)
{
HWND hwnd;
int idAnswer;
BOOL fRet = FALSE;
// HWND hwndFocus;
if (SUCCEEDED(pStoreCB->GetParentWindow(0, &hwnd)))
{
/*
hwndFocus = GetFocus();
if (hwndFocus != hwnd)
hwnd = hwndFocus;
*/
idAnswer = AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsPromptReturnReceipts),
0, MB_YESNO | MB_ICONEXCLAMATION );
fRet = (idAnswer == IDYES);
}
return fRet;
}
BOOL IsMDN(IMimeMessage *pMsg)
{
LPTSTR lpsz = NULL;
BOOL fRetVal = FALSE;
if (SUCCEEDED(MimeOleGetBodyPropA(pMsg, HBODY_ROOT, STR_PAR_REPORTTYPE, NOFLAGS, &lpsz)) && (lpsz) && (*lpsz))
{
if (lstrcmp(c_szDispositionNotification, lpsz))
{
fRetVal = TRUE;
}
MemFree(lpsz);
}
return fRetVal;
}
DWORD GetLockKeyValue(LPCTSTR pszValue)
{
DWORD cbData;
DWORD dwLocked = FALSE;
DWORD dwType;
HKEY hKeyLM;
cbData = sizeof(DWORD);
if ((ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, STR_REG_PATH_POLICY, c_szRequestMDNLocked, &dwType, (LPBYTE)&dwLocked, &cbData)) &&
(ERROR_SUCCESS != AthUserGetValue(NULL, pszValue, &dwType, (LPBYTE)&dwLocked, &cbData)))
dwLocked = FALSE;
return dwLocked;
}
BOOL fMessageEncodingMatch(IMimeMessage *pMsg)
{
INETCSETINFO CharsetInfo;
BOOL fret = FALSE;
HRESULT hr = S_OK;
HCHARSET hCharset = NULL;
CODEPAGEINFO CodePage = {0};
Assert(pMsg);
IF_FAILEXIT(hr = pMsg->GetCharset(&hCharset));
IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CharsetInfo));
/*
if (CharsetInfo.cpiWindows == GetACP() || CharsetInfo.cpiWindows == CP_UNICODE)
fret = TRUE;
*/
IF_FAILEXIT(hr = MimeOleGetCodePageInfo(CharsetInfo.cpiInternet, &CodePage));
if (CodePage.cpiFamily == GetACP() || CodePage.cpiFamily == CP_UNICODE)
fret = TRUE;
exit:
return fret;
}
#ifdef SMIME_V3
static const BYTE RgbASNForSHASign[11] =
{
0x30, 0x09, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e,
0x03, 0x02, 0x1a
};
// Auto association of signing certificate for secure receipt
BOOL AutoAssociateCert(BOOL *fAllowTryAgain, HWND hwnd, IImnAccount *pTempAccount)
{
if(*fAllowTryAgain)
{
*fAllowTryAgain = FALSE;
if (SUCCEEDED(_HrFindMyCertForAccount(hwnd, NULL, pTempAccount, FALSE)))
return(TRUE);
}
return(FALSE);
}
void ErrorSendSecReceipt(HWND hwnd, HRESULT hr, IImnAccount *pAccount)
{
if(hr == HR_E_ATHSEC_NOCERTTOSIGN)
{
if(DialogBoxParam(g_hLocRes,
MAKEINTRESOURCE(iddErrSecurityNoSigningCert), hwnd,
ErrSecurityNoSigningCertDlgProc, NULL) == idGetDigitalIDs)
GetDigitalIDs(pAccount);
}
else
AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsCannotSendSecReceipt),
0, MB_OK | MB_ICONSTOP);
return;
}
// Process secure receipts
HRESULT ProcessSecureReceipt(IMimeMessage * pMsg, IStoreCallback *pStoreCB)
{
IMimeMessage *pMessageRcpt = NULL;
IMimeBody * pBody = NULL;
IMimeBody * pBodyRec = NULL;
IMimeSecurity2 * pSMIME3 = NULL;
CERT_NAME_BLOB *rgReceiptFromList = NULL;
DWORD cReceiptFromList = 0;
PCX509CERT pCert = NULL;
THUMBBLOB tbCert = {0, 0};
HCERTSTORE hMyCertStore = NULL;
X509CERTRESULT certResult;
CERTSTATE cs;
LPVOID pv = NULL;
DWORD dwBits = 40;
DWORD cb = 0;
LPBYTE pBlobData = NULL;
ULONG ulSecurityType = MST_CLASS_SMIME_V1;
LPWABAL lpWabal = NULL;
PROPVARIANT var = {0};
DWORD dwOption = 0;
UINT uiRes = 0;
HRESULT hr = S_OK;
IImnAccount *pTempAccount = NULL;
HWND hwnd = NULL;
TCHAR szEmail[CCHMAX_EMAIL_ADDRESS];
BOOL fSendImmediate = FALSE;
LPWSTR lpsz = NULL;
WCHAR lpBuffer[CCHMAX_STRINGRES];
LPWSTR szParam = NULL;
SECSTATE secStateRec = {0};
BOOL fAllowTryAgain = TRUE;
PCCERT_CONTEXT *rgCertChain = NULL;
DWORD cCertChain = 0;
const DWORD dwIgnore = CERT_VALIDITY_NO_CRL_FOUND;
DWORD dwTrust = 0;
LPWSTR pszRefs = NULL;
LPWSTR pszOrigRefs = NULL;
LPWSTR pszNewRef = NULL;
HCHARSET hCharset = NULL;
INETCSETINFO CsetInfo = {0};
HBODY hBody = NULL;
int cch;
Assert(pMsg != NULL);
// get windof for error messages
IF_FAILEXIT(hr = pStoreCB->GetParentWindow(0, &hwnd));
// if option set DO NOT send secure receipt then exit
dwOption = DwGetOption(OPT_MDN_SEC_RECEIPT);
if (dwOption == MDN_DONT_SENDRECEIPT)
goto exit;
// check do we have secure receipt request in message?
IF_FAILEXIT(hr = HrGetInnerLayer(pMsg, &hBody));
IF_FAILEXIT(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody));
IF_FAILEXIT(hr = pBody->GetOption(OID_SECURITY_TYPE, &var));
if(!(var.ulVal & MST_RECEIPT_REQUEST))
{
var.ulVal = 0; // Set to 0, because var.pszVal and var.ulVal point to the same address.
hr = S_OK;
goto exit;
}
// Check do we trust this message: Bug 78118
IF_FAILEXIT(hr = HrGetSecurityState(pMsg, &secStateRec, NULL));
if(!IsSignTrusted(&secStateRec))
{
// do not show any error message in this case, just exit
hr = S_OK;
goto exit;
}
// we have request, check that we would like to send receipt
if (dwOption == MDN_PROMPTFOR_SENDRECEIPT)
{
uiRes = (UINT) DialogBoxParamWrapW(g_hLocRes, MAKEINTRESOURCEW(iddSecResponse),
hwnd, SecRecResDlgProc, (LPARAM) 0);
if(uiRes == 0)
{
hr = S_FALSE;
goto exit;
}
else if((uiRes != IDOK) && (uiRes != IDYES)) // IDYES means we will encrypt receipt
{
hr = S_OK;
goto exit;
}
}
// Get SMIME3 Security interface on message
IF_FAILEXIT(hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pSMIME3));
// Get List of people who should send receipts
IF_FAILEXIT(hr = pSMIME3->GetReceiptSendersList(0, &cReceiptFromList,&rgReceiptFromList));
// Check if asking for receipt from noone
if (hr == S_FALSE)
{
hr = S_OK;
goto exit;
}
// Check account information
var.vt = VT_LPSTR;
IF_FAILEXIT(hr = pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var));
if(FAILED(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pTempAccount)))
{
MemFree(var.pszVal);
goto exit;
}
SafeMemFree(var.pszVal);
// Check that we are in secure list
IF_FAILEXIT(hr = pTempAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szEmail, ARRAYSIZE(szEmail)));
if(!FNameInList(szEmail, cReceiptFromList,rgReceiptFromList))
{
// we are not in list
hr = S_FALSE;
goto exit;
}
// Get a certificate for receipt
Try_agian:
if((hr = pTempAccount->GetProp(AP_SMTP_CERTIFICATE, NULL, &tbCert.cbSize)) != S_OK)
{
if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
goto Try_agian;
else
{
hr = HR_E_ATHSEC_NOCERTTOSIGN;
goto exit;
}
}
IF_FAILEXIT(hr = HrAlloc((void**)&tbCert.pBlobData, tbCert.cbSize));
IF_FAILEXIT(hr = pTempAccount->GetProp(AP_SMTP_CERTIFICATE, tbCert.pBlobData, &tbCert.cbSize));
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING,
NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
if (hMyCertStore == NULL)
goto exit;
certResult.cEntries = 1;
certResult.rgcs = &cs;
certResult.rgpCert = &pCert;
if((hr = MimeOleGetCertsFromThumbprints(&tbCert, &certResult, &hMyCertStore, 1) != S_OK))
{
if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
goto Try_agian;
else
{
hr = HR_E_ATHSEC_NOCERTTOSIGN;
goto exit;
}
}
// Check certificate
// As to CRLs, if we'll ever have one!
dwTrust = DwGenerateTrustedChain(hwnd, NULL, pCert,
dwIgnore, TRUE, &cCertChain, &rgCertChain);
if (rgCertChain)
{
for (cCertChain--; int(cCertChain)>=0; cCertChain--)
CertFreeCertificateContext(rgCertChain[cCertChain]);
MemFree(rgCertChain);
rgCertChain = NULL;
}
if (dwTrust)
{
if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
goto Try_agian;
else
{
hr = HR_E_ATHSEC_NOCERTTOSIGN;
goto exit;
}
}
// Create receipt message
IF_FAILEXIT(hr = pSMIME3->CreateReceipt(0, lstrlen(szEmail), (const BYTE *) szEmail, 1, &pCert, &pMessageRcpt));
// IF_FAILEXIT(hr = HrCreateMessage (&pMessageRcpt));
// IF_FAILEXIT(hr = SetRootHeaderFields(pMessageRcpt, pMsg, szEmail, READRECEIPT)); // szEmail is bug!
// Subject
IF_FAILEXIT(hr = MimeOleGetBodyPropW(pMsg, HBODY_ROOT, STR_ATT_NORMSUBJ, NOFLAGS, &lpsz));
if (fMessageEncodingMatch(pMsg))
{
AthLoadStringW(idsSecureReceiptText, lpBuffer, ARRAYSIZE(lpBuffer));
}
else
{
//The encoding didn't match, so we just load english headers
StrCpyNW(lpBuffer, c_szSecReadReceipt, ARRAYSIZE(lpBuffer));
}
// Init security options
#if 0
if(DwGetOption(OPT_MAIL_INCLUDECERT))
{
ulSecurityType |= ((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
HrInitSecurityOptions(pMessageRcpt, ulSecurityType);
}
#endif // 0
cch = lstrlenW(lpsz) + lstrlenW(lpBuffer) + 1;
IF_FAILEXIT(hr = HrAlloc((LPVOID*)&szParam, cch * sizeof(WCHAR)));
wnsprintfW(szParam, cch, lpBuffer, lpsz);
IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, szParam));
//Set Time
IF_FAILEXIT(hr = HrSetSentTimeProp(pMessageRcpt, NULL));
//Set References property
IF_FAILEXIT(hr = MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pszNewRef));
//No need to check for return value. It gets handled correctly in HrCreateReferences
MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, &pszOrigRefs);
IF_FAILEXIT(hr = HrCreateReferences(pszOrigRefs, pszNewRef, &pszRefs));
IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, pszRefs));
IF_FAILEXIT(hr = HrSetAccountByAccount(pMessageRcpt, pTempAccount));
//
IF_FAILEXIT(hr = HrGetWabalFromMsg(pMessageRcpt, &lpWabal));
IF_FAILEXIT(hr = HrSetSenderInfoUtil(pMessageRcpt, pTempAccount, lpWabal, TRUE, 0, FALSE));
IF_FAILEXIT(hr = HrSetWabalOnMsg(pMessageRcpt, lpWabal));
//#if 0
IF_FAILEXIT(hr = pMessageRcpt->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pBodyRec));
// Set hash algorithm
// Init with no symcap gives max allowed by providers
MimeOleSMimeCapInit(NULL, NULL, &pv);
MimeOleSMimeCapGetHashAlg(pv, NULL, &cb, &dwBits);
var.vt = VT_BLOB;
if(cb > 0)
{
if(!MemAlloc((LPVOID *)&pBlobData, cb))
goto exit;
// ZeroMemory(&pBlobData, cb);
MimeOleSMimeCapGetHashAlg(pv, pBlobData, &cb, &dwBits);
var.blob.cbSize = cb;
var.blob.pBlobData = pBlobData;
}
else
{
var.blob.cbSize = sizeof(RgbASNForSHASign);
var.blob.pBlobData = (LPBYTE) RgbASNForSHASign;
}
IF_FAILEXIT(hr = pBodyRec->SetOption(OID_SECURITY_ALG_HASH, &var));
#ifdef _WIN64
var.vt = VT_UI8;
var.pulVal = (ULONG *) hwnd;
IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER_64, &var));
#else
var.vt = VT_UI4;
var.ulVal = (DWORD) hwnd;
IF_FAILEXIT(hr = pBodyRec->SetOption(OID_SECURITY_HWND_OWNER, &var));
var.ulVal = 0; // Set to 0, because var.pszVal and var.ulVal point to the same address.
#endif // _WIN64
// IF_FAILEXIT(hr = pMessageRcpt->Commit(0));
// #endif //0
IF_FAILEXIT(hr = pMsg->GetCharset(&hCharset));
// re-map CP_JAUTODETECT and CP_KAUTODETECT if necessary
// re-map iso-2022-jp to default charset if they are in the same category
IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CsetInfo));
IF_NULLEXIT(hCharset = GetMimeCharsetFromCodePage(GetMapCP(CsetInfo.cpiInternet, FALSE)));
IF_FAILEXIT(hr = pMessageRcpt->SetCharset(hCharset, CSET_APPLY_ALL));
// should be new header
fSendImmediate = (DwGetOption(OPT_SENDIMMEDIATE) && !g_pConMan->IsGlobalOffline());
if (DwGetOption(OPT_MAIL_INCLUDECERT))
IF_FAILEXIT(hr = SendSecureMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, TRUE, NULL));
else
IF_FAILEXIT(hr = SendMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, TRUE));
exit:
if(FAILED(hr))
ErrorSendSecReceipt(hwnd, hr, pTempAccount);
CleanupSECSTATE(&secStateRec);
SafeMemFree(lpsz);
SafeMemFree(szParam);
SafeMemFree(pBlobData);
SafeMemFree(pv);
SafeRelease(pBodyRec);
SafeRelease(pMessageRcpt);
if(hMyCertStore)
CertCloseStore(hMyCertStore, 0);
if(pCert)
CertFreeCertificateContext(pCert);
// if(tbCert.pBlobData)
SafeMemFree(tbCert.pBlobData);
SafeRelease(lpWabal);
SafeRelease(pTempAccount);
SafeRelease(pSMIME3);
SafeRelease(pBody);
SafeMimeOleFree(pszNewRef);
SafeMimeOleFree(pszOrigRefs);
MemFree(pszRefs);
return(hr);
}
INT_PTR CALLBACK SecRecResDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
CenterDialog(hwndDlg);
CheckDlgButton(hwndDlg, IDC_ENCRECEIPT, (!!DwGetOption(OPT_SECREC_ENCRYPT)) ? BST_CHECKED : BST_UNCHECKED);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
EndDialog(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_ENCRECEIPT) ? IDYES : IDOK);
break;
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
#endif // SMIME_V3