#include "pch.hxx" #include #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