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

  1. #include "pch.hxx"
  2. #include <cryptdlg.h>
  3. #include "strconst.h"
  4. #include "msoert.h"
  5. #include "resource.h"
  6. #include "mailutil.h"
  7. #include "ipab.h"
  8. #include "receipts.h"
  9. #include "oestore.h"
  10. #include "shlwapip.h"
  11. #include "goptions.h"
  12. #include "conman.h"
  13. #include "multlang.h"
  14. #include "demand.h"
  15. #include "secutil.h"
  16. #ifdef SMIME_V3
  17. HRESULT ProcessSecureReceipt(IMimeMessage * pMsg, IStoreCallback *pStoreCB);
  18. INT_PTR CALLBACK SecRecResDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  19. #endif // SMIME_V3
  20. const WCHAR c_szReadableTextFirst[] = L"This is a receipt for the mail you sent to\r\n";
  21. const WCHAR c_szReadableTextSecond[] = L"\r\n\r\nThis receipt verifies that the message has been displayed on the recipient's computer at ";
  22. const WCHAR c_szReceiptsAt[] = L" at ";
  23. const WCHAR c_szReadReceipt[] = L"Read: %s";
  24. const WCHAR c_szSecReadReceipt[] = L"Secure Receipt: %s";
  25. //Does not include NULL
  26. const int cbReadableTextFirst = sizeof(c_szReadableTextFirst) - sizeof(*c_szReadableTextFirst);
  27. const int cbReadableTextSecond = sizeof(c_szReadableTextSecond) - sizeof(*c_szReadableTextSecond);
  28. const int cbReceiptsAt = sizeof(c_szReceiptsAt) - sizeof(*c_szReceiptsAt);
  29. //Include NULL
  30. const int cbReadReceipt = sizeof(c_szReadReceipt);
  31. //Not wide, does not include NULL
  32. const TCHAR c_szDisposition[] = TEXT("\r\nDisposition: manual-action/");
  33. const TCHAR c_szMDNSendAutomatically[] = TEXT("MDN-sent-automatically; ");
  34. const TCHAR c_szMDNSendManually[] = TEXT("MDN-sent-manually; ");
  35. const TCHAR c_szOriginalRecipient[] = TEXT("\r\nOriginal-Recipient: rfc822;");
  36. const TCHAR c_szFinalRecipient[] = TEXT("Final-Recipient: rfc822;");
  37. const TCHAR c_szOriginalMessageId[] = TEXT("\r\nOriginal-Message-ID: ");
  38. const TCHAR c_szDisplayed[] = TEXT("displayed\r\n");
  39. //Does not include NULL
  40. const int cbDisposition = sizeof(c_szDisposition) - sizeof(*c_szDisposition);
  41. const int cbMDNSendAutomatically = sizeof(c_szMDNSendAutomatically) - sizeof(*c_szMDNSendAutomatically);
  42. const int cbMDNSendManually = sizeof(c_szMDNSendManually) - sizeof(*c_szMDNSendManually);
  43. const int cbOriginalRecipient = sizeof(c_szOriginalRecipient) - sizeof(*c_szOriginalRecipient);
  44. const int cbFinalRecipient = sizeof(c_szFinalRecipient) - sizeof(*c_szFinalRecipient);
  45. const int cbOriginalMessageId = sizeof(c_szOriginalMessageId) - sizeof(*c_szOriginalMessageId);
  46. const int cbDisplayed = sizeof(c_szDisplayed) - sizeof(*c_szDisplayed);
  47. HRESULT ProcessReturnReceipts(IMessageTable *pTable,
  48. IStoreCallback *pStoreCB,
  49. ROWINDEX iRow,
  50. RECEIPTTYPE ReceiptType,
  51. FOLDERID IdFolder,
  52. IMimeMessage *pMsg)
  53. {
  54. IMimeMessage *pMessage = NULL;
  55. IMimeMessage *pMessageRcpt = NULL;
  56. BOOL fSendImmediate = FALSE;
  57. BOOL fMail = TRUE;
  58. LPWABAL lpWabal = NULL;
  59. LPWSTR lpsz = NULL;
  60. MESSAGEINFO *pMsgInfo = NULL;
  61. IImnAccount *pTempAccount = NULL;
  62. HRESULT hr = S_OK;
  63. DWORD dwOption;
  64. PROPVARIANT var = {0};
  65. FOLDERINFO FolderInfo = {0};
  66. TraceCall("ProcessReturnReceipts");
  67. if (!pTable || !g_pAcctMan)
  68. return TraceResult(E_INVALIDARG);
  69. IF_FAILEXIT(hr = g_pStore->GetFolderInfo(IdFolder, &FolderInfo));
  70. if (FolderInfo.tySpecial == FOLDER_OUTBOX || FolderInfo.tyFolder == FOLDER_NEWS)
  71. {
  72. goto exit;
  73. }
  74. //First of all check if this has already been processed
  75. IF_FAILEXIT(hr = pTable->GetRow(iRow, &pMsgInfo));
  76. if ((!pMsgInfo) || (pMsgInfo->dwFlags & (ARF_RCPT_PROCESSED | ARF_NEWSMSG)))
  77. goto exit;
  78. //Mark this flag as having been processed
  79. IF_FAILEXIT(hr = pTable->Mark(&iRow, 1, APPLY_SPECIFIED, MARK_MESSAGE_RCPT_PROCESSED, pStoreCB));
  80. if(!pMsg)
  81. IF_FAILEXIT(hr = pTable->OpenMessage(iRow, 0, &pMessage, pStoreCB));
  82. else
  83. pMessage = pMsg;
  84. #ifdef SMIME_V3
  85. // Secure receipt check
  86. hr = ProcessSecureReceipt(pMessage, pStoreCB);
  87. if(hr != S_OK)
  88. goto exit;
  89. #endif // SMIME_V3
  90. dwOption = DwGetOption(OPT_MDN_SEND_RECEIPT);
  91. if (dwOption == MDN_DONT_SENDRECEIPT)
  92. goto exit;
  93. hr = MimeOleGetBodyPropW(pMessage, HBODY_ROOT, STR_HDR_DISP_NOTIFICATION_TO,
  94. NOFLAGS, &lpsz);
  95. if (FAILED(hr) || !lpsz || !*lpsz)
  96. {
  97. //Check if there is a Return-Receipt-To header
  98. hr = MimeOleGetBodyPropW(pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_RETRCPTTO),
  99. NOFLAGS, &lpsz);
  100. if (FAILED(hr) || !lpsz || !*lpsz)
  101. {
  102. if (hr == MIME_E_NOT_FOUND)
  103. {
  104. //this is a legitimate error, so we don't want to show an error to the user
  105. hr = S_OK;
  106. }
  107. goto exit;
  108. }
  109. }
  110. if (dwOption == MDN_PROMPTFOR_SENDRECEIPT)
  111. {
  112. if (!PromptReturnReceipts(pStoreCB))
  113. goto exit;
  114. }
  115. var.vt = VT_LPSTR;
  116. IF_FAILEXIT(hr = pMessage->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var));
  117. IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pTempAccount));
  118. if ((dwOption == MDN_SENDRECEIPT_AUTO) && (!!DwGetOption(OPT_TO_CC_LINE_RCPT)))
  119. {
  120. IF_FAILEXIT(hr = CheckForLists(pMessage, pStoreCB, pTempAccount));
  121. if (hr == S_FALSE)
  122. {
  123. // My name is not found in the list. So we don't send a receipt.
  124. // However this is not failure.
  125. goto exit;
  126. }
  127. }
  128. IF_FAILEXIT(hr = HrCreateMessage (&pMessageRcpt));
  129. IF_FAILEXIT(hr = SetRootHeaderFields(pMessageRcpt, pMessage, lpsz, READRECEIPT));
  130. IF_FAILEXIT(hr = HrSetAccountByAccount(pMessageRcpt, pTempAccount));
  131. IF_FAILEXIT(hr = HrGetWabalFromMsg(pMessageRcpt, &lpWabal));
  132. IF_FAILEXIT(hr = HrSetSenderInfoUtil(pMessageRcpt, pTempAccount, lpWabal, TRUE, 0, FALSE));
  133. IF_FAILEXIT(hr = HrSetWabalOnMsg(pMessageRcpt, lpWabal));
  134. fSendImmediate = (DwGetOption(OPT_SENDIMMEDIATE) && !g_pConMan->IsGlobalOffline());
  135. IF_FAILEXIT(hr = SendMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, fMail));
  136. exit:
  137. //Show an error if it failed
  138. if (FAILED(hr))
  139. ShowErrorMessage(pStoreCB);
  140. g_pStore->FreeRecord(&FolderInfo);
  141. SafeMemFree(var.pszVal);
  142. pTable->ReleaseRow(pMsgInfo);
  143. SafeRelease(pMessageRcpt);
  144. SafeRelease(pTempAccount);
  145. SafeRelease(lpWabal);
  146. SafeMemFree(lpsz);
  147. if(!pMsg)
  148. SafeRelease(pMessage);
  149. return hr;
  150. }
  151. HRESULT SetRootHeaderFields(IMimeMessage *pMessageRcpt,
  152. IMimeMessage *pOriginalMsg,
  153. LPWSTR lpszNotificationTo,
  154. RECEIPTTYPE ReceiptType)
  155. {
  156. HRESULT hr = S_OK;
  157. LPWSTR lpsz = NULL;
  158. LPWSTR szParam = NULL;
  159. UINT ResId;
  160. LPWSTR pszRefs = NULL;
  161. LPWSTR pszOrigRefs = NULL;
  162. LPWSTR pszNewRef = NULL;
  163. WCHAR lpBuffer[CCHMAX_STRINGRES];
  164. int cch = 0;
  165. // Trace
  166. TraceCall("_SetRootHeaderFields");
  167. // Set the subject and the To field.
  168. IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, lpszNotificationTo));
  169. IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, STR_ATT_NORMSUBJ, NOFLAGS, &lpsz));
  170. switch (ReceiptType)
  171. {
  172. case DELETERECEIPT:
  173. ResId = idsDeleteReceipt;
  174. break;
  175. case READRECEIPT:
  176. default:
  177. ResId = idsReadReceipt;
  178. break;
  179. }
  180. if (fMessageEncodingMatch(pOriginalMsg))
  181. {
  182. AthLoadStringW(ResId, lpBuffer, ARRAYSIZE(lpBuffer));
  183. }
  184. else
  185. {
  186. //The encoding didn't match, so we just load english headers
  187. StrCpyNW(lpBuffer, c_szReadReceipt, ARRAYSIZE(lpBuffer));
  188. }
  189. cch = lstrlenW(lpsz) + lstrlenW(lpBuffer) + 1;
  190. IF_FAILEXIT(hr = HrAlloc((LPVOID*)&szParam, cch * sizeof(WCHAR)));
  191. wnsprintfW(szParam, cch, lpBuffer, lpsz);
  192. IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, szParam));
  193. //Set Time
  194. IF_FAILEXIT(hr = HrSetSentTimeProp(pMessageRcpt, NULL));
  195. //Set References property
  196. IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pszNewRef));
  197. //No need to check for return value. It gets handled correctly in HrCreateReferences
  198. MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, &pszOrigRefs);
  199. IF_FAILEXIT(hr = HrCreateReferences(pszOrigRefs, pszNewRef, &pszRefs));
  200. IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, pszRefs));
  201. //Readable text
  202. //TODO: Change the file name depending on the ReceiptType
  203. IF_FAILEXIT(hr = InsertReadableText(pMessageRcpt, pOriginalMsg));
  204. //Create the second part
  205. IF_FAILEXIT(hr = InsertSecondComponent(pMessageRcpt, pOriginalMsg));
  206. //Set Content-type field. Content-type: multipart/report; report-type=disposition-notification;
  207. //Content-type is multipart, sub-content-type is report and report-type is a parameter whose
  208. //value should be set to disposition-notification
  209. IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_CNTTYPE), NOFLAGS, c_szMultiPartReport));
  210. IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, HBODY_ROOT, STR_PAR_REPORTTYPE, PDF_ENCODED, c_szDispositionNotification));
  211. exit:
  212. SafeMemFree(lpsz);
  213. SafeMimeOleFree(pszNewRef);
  214. SafeMemFree(szParam);
  215. SafeMimeOleFree(pszOrigRefs);
  216. SafeMemFree(pszRefs);
  217. return(hr);
  218. }
  219. HRESULT InsertReadableText(IMimeMessage *pMessageRcpt, IMimeMessage *pOriginalMsg)
  220. {
  221. HBODY hBody;
  222. IStream *pStream = NULL;
  223. HRESULT hr = S_OK;
  224. LPWSTR lpsz = NULL;
  225. ULONG cbWritten;
  226. WCHAR lpBuffer[CCHMAX_STRINGRES];
  227. HCHARSET hCharset = NULL;
  228. WCHAR wszReceiptSentDate[CCHMAX_STRINGRES];
  229. WCHAR wszOriginalSentDate[CCHMAX_STRINGRES];
  230. PROPVARIANT varOriginal;
  231. PROPVARIANT var;
  232. INETCSETINFO CsetInfo = {0};
  233. IF_FAILEXIT(hr = MimeOleGetBodyPropW(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, &lpsz));
  234. IF_FAILEXIT(hr = MimeOleCreateVirtualStream((IStream**)&pStream));
  235. varOriginal.vt = VT_FILETIME;
  236. IF_FAILEXIT(hr = pOriginalMsg->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &varOriginal));
  237. var.vt = VT_FILETIME;
  238. IF_FAILEXIT(hr = pMessageRcpt->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &var));
  239. if (fMessageEncodingMatch(pOriginalMsg))
  240. {
  241. AthLoadStringW(idsReadableTextFirst, lpBuffer, CCHMAX_STRINGRES);
  242. IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
  243. IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlenW(lpsz) * sizeof(WCHAR), &cbWritten));
  244. *lpBuffer = 0;
  245. AthLoadStringW(idsReceiptAt, lpBuffer, CCHMAX_STRINGRES);
  246. IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
  247. *wszOriginalSentDate = 0;
  248. AthFileTimeToDateTimeW(&varOriginal.filetime, wszOriginalSentDate, ARRAYSIZE(wszOriginalSentDate),
  249. DTM_NOSECONDS);
  250. IF_FAILEXIT(hr = pStream->Write(wszOriginalSentDate, lstrlenW(wszOriginalSentDate) * sizeof(*wszOriginalSentDate),
  251. &cbWritten));
  252. AthLoadStringW(idsReadableTextSecond, lpBuffer, CCHMAX_STRINGRES);
  253. IF_FAILEXIT(hr = pStream->Write(lpBuffer, lstrlenW(lpBuffer) * sizeof(WCHAR), &cbWritten));
  254. *wszReceiptSentDate = 0;
  255. AthFileTimeToDateTimeW(&var.filetime, wszReceiptSentDate, ARRAYSIZE(wszReceiptSentDate),
  256. DTM_NOSECONDS);
  257. }
  258. else
  259. {
  260. //Insert English headers and content
  261. IF_FAILEXIT(hr = pStream->Write(c_szReadableTextFirst, cbReadableTextFirst, &cbWritten));
  262. IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlenW(lpsz) * sizeof(WCHAR), &cbWritten));
  263. IF_FAILEXIT(hr = pStream->Write(c_szReceiptsAt, cbReceiptsAt, &cbWritten));
  264. //Original Sent Date
  265. *wszOriginalSentDate = 0;
  266. AthFileTimeToDateTimeW(&varOriginal.filetime, wszOriginalSentDate, ARRAYSIZE(wszOriginalSentDate),
  267. DTM_FORCEWESTERN | DTM_NOSECONDS);
  268. IF_FAILEXIT(hr = pStream->Write(wszOriginalSentDate, lstrlenW(wszOriginalSentDate) * sizeof(*wszOriginalSentDate),
  269. &cbWritten));
  270. //Second line of readable text
  271. IF_FAILEXIT(hr = pStream->Write(c_szReadableTextSecond, cbReadableTextSecond, &cbWritten));
  272. //Receipt Sent Date
  273. *wszReceiptSentDate = 0;
  274. AthFileTimeToDateTimeW(&var.filetime, wszReceiptSentDate, ARRAYSIZE(wszReceiptSentDate),
  275. DTM_FORCEWESTERN | DTM_NOSECONDS);
  276. }
  277. IF_FAILEXIT(hr = pStream->Write(wszReceiptSentDate, lstrlenW(wszReceiptSentDate) * sizeof(*wszReceiptSentDate),
  278. &cbWritten));
  279. IF_FAILEXIT(hr = pOriginalMsg->GetCharset(&hCharset));
  280. // re-map CP_JAUTODETECT and CP_KAUTODETECT if necessary
  281. // re-map iso-2022-jp to default charset if they are in the same category
  282. IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CsetInfo));
  283. IF_NULLEXIT(hCharset = GetMimeCharsetFromCodePage(GetMapCP(CsetInfo.cpiInternet, FALSE)));
  284. IF_FAILEXIT(hr = pMessageRcpt->SetCharset(hCharset, CSET_APPLY_ALL));
  285. IF_FAILEXIT(hr = pMessageRcpt->SetTextBody(TXT_PLAIN, IET_UNICODE, NULL, pStream, &hBody));
  286. exit:
  287. SafeRelease(pStream);
  288. SafeMemFree(lpsz);
  289. return hr;
  290. }
  291. HRESULT InsertSecondComponent(IMimeMessage *pMessageRcpt, IMimeMessage *pOriginalMsg)
  292. {
  293. HBODY hBody;
  294. ULONG cbWritten;
  295. IStream *pStream = NULL;
  296. LPTSTR lpsz = NULL;
  297. HRESULT hr = S_OK;
  298. IF_FAILEXIT(hr = MimeOleCreateVirtualStream((IStream**)&pStream));
  299. //Final Recipient
  300. IF_FAILEXIT(hr = AddOriginalAndFinalRecipient(pOriginalMsg, pMessageRcpt, pStream));
  301. //Original Message Id
  302. IF_FAILEXIT(hr = MimeOleGetBodyPropA(pOriginalMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &lpsz));
  303. IF_FAILEXIT(hr = pStream->Write(c_szOriginalMessageId, cbOriginalMessageId, &cbWritten));
  304. IF_FAILEXIT(hr = pStream->Write(lpsz, lstrlen(lpsz) * sizeof(TCHAR), &cbWritten));
  305. //TODO:If the receipt type is deleted, change the last parameter to reflect the correct string
  306. IF_FAILEXIT(hr = pStream->Write(c_szDisposition, cbDisposition, &cbWritten));
  307. if (DwGetOption(OPT_MDN_SEND_RECEIPT) == MDN_PROMPTFOR_SENDRECEIPT)
  308. IF_FAILEXIT(hr = pStream->Write(c_szMDNSendManually, cbMDNSendManually, &cbWritten));
  309. else
  310. IF_FAILEXIT(hr = pStream->Write(c_szMDNSendAutomatically, cbMDNSendAutomatically, &cbWritten));
  311. IF_FAILEXIT(hr = pStream->Write(c_szDisplayed, cbDisplayed, &cbWritten));
  312. IF_FAILEXIT(hr = pStream->Commit(STGC_DEFAULT));
  313. IF_FAILEXIT(hr = pMessageRcpt->AttachObject(IID_IStream, pStream, &hBody));
  314. IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, hBody, PIDTOSTR(PID_HDR_CNTTYPE), NOFLAGS, c_szMessageDispNotification));
  315. //Change the content-disposition header field to inline, so that this body gets copied and not get attached.
  316. IF_FAILEXIT(hr = MimeOleSetBodyPropA(pMessageRcpt, hBody, PIDTOSTR(PID_HDR_CNTDISP), NOFLAGS, STR_DIS_INLINE));
  317. exit:
  318. MemFree(lpsz);
  319. ReleaseObj(pStream);
  320. return hr;
  321. }
  322. HRESULT AddOriginalAndFinalRecipient(IMimeMessage *pOriginalMsg,
  323. IMimeMessage *pMessageRcpt,
  324. IStream *pStream)
  325. {
  326. IMimeEnumAddressTypes *pEnumAddrTypes = NULL;
  327. HRESULT hr = S_OK;
  328. ULONG cbWritten;
  329. ULONG Count;
  330. ADDRESSPROPS AddressProps;
  331. ULONG cFetched;
  332. IMimeAddressTable *pAddrTable = NULL;
  333. LPTSTR lpszOriginalRecip = NULL;
  334. ADDRESSLIST addrList = {0};
  335. IF_FAILEXIT(hr = pOriginalMsg->BindToObject(HBODY_ROOT, IID_IMimeAddressTable, (LPVOID*)&pAddrTable));
  336. IF_FAILEXIT(hr = pAddrTable->EnumTypes(IAT_FROM, IAP_EMAIL, &pEnumAddrTypes));
  337. #ifdef DEBUG
  338. IF_FAILEXIT(hr = pEnumAddrTypes->Count(&Count));
  339. Assert (Count == 1);
  340. #endif DEBUG
  341. IF_FAILEXIT(hr = pEnumAddrTypes->Next(1, &AddressProps, &cFetched));
  342. Assert(cFetched == 1);
  343. if (!(AddressProps.pszEmail) || !(*AddressProps.pszEmail))
  344. {
  345. hr = E_FAIL;
  346. goto exit;
  347. }
  348. IF_FAILEXIT(hr = pStream->Write(c_szFinalRecipient, cbFinalRecipient, &cbWritten));
  349. IF_FAILEXIT(hr = pStream->Write(AddressProps.pszEmail, lstrlen(AddressProps.pszEmail) * sizeof(TCHAR), &cbWritten));
  350. hr = MimeOleGetBodyPropA(pOriginalMsg, HBODY_ROOT, STR_HDR_ORIG_RECIPIENT, NOFLAGS, &lpszOriginalRecip);
  351. if (SUCCEEDED(hr))
  352. {
  353. IF_FAILEXIT(hr = MimeOleParseRfc822Address(IAT_TO, IET_ENCODED, lpszOriginalRecip, &addrList));
  354. if (addrList.cAdrs > 0)
  355. {
  356. IF_FAILEXIT(hr = pStream->Write(c_szOriginalRecipient, cbOriginalRecipient, &cbWritten));
  357. IF_FAILEXIT(hr = pStream->Write(addrList.prgAdr[0].pszEmail, lstrlen(addrList.prgAdr[0].pszEmail), &cbWritten));
  358. }
  359. }
  360. else
  361. {
  362. if (hr == MIME_E_NOT_FOUND)
  363. hr = S_OK;
  364. }
  365. exit:
  366. if (g_pMoleAlloc)
  367. {
  368. g_pMoleAlloc->FreeAddressProps(&AddressProps);
  369. if (addrList.cAdrs)
  370. g_pMoleAlloc->FreeAddressList(&addrList);
  371. }
  372. ReleaseObj(pEnumAddrTypes);
  373. ReleaseObj(pAddrTable);
  374. MemFree(lpszOriginalRecip);
  375. return hr;
  376. }
  377. HRESULT CheckForLists(IMimeMessage *pOriginalMsg, IStoreCallback *pStoreCB, IImnAccount *pDefAccount)
  378. {
  379. HRESULT hr = S_OK;
  380. IMimeEnumAddressTypes *pEnumAddrTypes = NULL;
  381. ULONG cbAddrTypes;
  382. ULONG cFetched;
  383. ADDRESSPROPS *prgAddress = NULL;
  384. TCHAR szEmail[CCHMAX_EMAIL_ADDRESS];
  385. DWORD index;
  386. IF_FAILEXIT(hr = pOriginalMsg->EnumAddressTypes(IAT_TO | IAT_CC, IAP_EMAIL, &pEnumAddrTypes));
  387. IF_FAILEXIT(hr = pEnumAddrTypes->Count(&cbAddrTypes));
  388. IF_FAILEXIT(hr = HrAlloc((LPVOID*)&prgAddress, cbAddrTypes * sizeof(ADDRESSPROPS)));
  389. IF_FAILEXIT(hr = pEnumAddrTypes->Next(cbAddrTypes, prgAddress, &cFetched));
  390. IF_FAILEXIT(hr = pDefAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szEmail, ARRAYSIZE(szEmail)));
  391. if (!(*szEmail))
  392. {
  393. hr = E_FAIL;
  394. goto exit;
  395. }
  396. for (index = 0; index < cFetched; index++)
  397. {
  398. if (lstrcmpi(szEmail, (prgAddress + index)->pszEmail) == 0)
  399. break;
  400. }
  401. if (index == cFetched)
  402. {
  403. //We did not find it.
  404. hr = S_FALSE;
  405. }
  406. exit:
  407. if (g_pMoleAlloc && prgAddress)
  408. {
  409. for (index = 0; index < cFetched; index++)
  410. {
  411. g_pMoleAlloc->FreeAddressProps(&prgAddress[index]);
  412. }
  413. }
  414. MemFree(prgAddress);
  415. ReleaseObj(pEnumAddrTypes);
  416. return hr;
  417. }
  418. void ShowErrorMessage(IStoreCallback *pStoreCB)
  419. {
  420. HWND hwnd;
  421. Assert(pStoreCB);
  422. if (SUCCEEDED(pStoreCB->GetParentWindow(0, &hwnd)))
  423. {
  424. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsReceiptsError),
  425. 0, MB_OK);
  426. }
  427. }
  428. BOOL PromptReturnReceipts(IStoreCallback *pStoreCB)
  429. {
  430. HWND hwnd;
  431. int idAnswer;
  432. BOOL fRet = FALSE;
  433. // HWND hwndFocus;
  434. if (SUCCEEDED(pStoreCB->GetParentWindow(0, &hwnd)))
  435. {
  436. /*
  437. hwndFocus = GetFocus();
  438. if (hwndFocus != hwnd)
  439. hwnd = hwndFocus;
  440. */
  441. idAnswer = AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsPromptReturnReceipts),
  442. 0, MB_YESNO | MB_ICONEXCLAMATION );
  443. fRet = (idAnswer == IDYES);
  444. }
  445. return fRet;
  446. }
  447. BOOL IsMDN(IMimeMessage *pMsg)
  448. {
  449. LPTSTR lpsz = NULL;
  450. BOOL fRetVal = FALSE;
  451. if (SUCCEEDED(MimeOleGetBodyPropA(pMsg, HBODY_ROOT, STR_PAR_REPORTTYPE, NOFLAGS, &lpsz)) && (lpsz) && (*lpsz))
  452. {
  453. if (lstrcmp(c_szDispositionNotification, lpsz))
  454. {
  455. fRetVal = TRUE;
  456. }
  457. MemFree(lpsz);
  458. }
  459. return fRetVal;
  460. }
  461. DWORD GetLockKeyValue(LPCTSTR pszValue)
  462. {
  463. DWORD cbData;
  464. DWORD dwLocked = FALSE;
  465. DWORD dwType;
  466. HKEY hKeyLM;
  467. cbData = sizeof(DWORD);
  468. if ((ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, STR_REG_PATH_POLICY, c_szRequestMDNLocked, &dwType, (LPBYTE)&dwLocked, &cbData)) &&
  469. (ERROR_SUCCESS != AthUserGetValue(NULL, pszValue, &dwType, (LPBYTE)&dwLocked, &cbData)))
  470. dwLocked = FALSE;
  471. return dwLocked;
  472. }
  473. BOOL fMessageEncodingMatch(IMimeMessage *pMsg)
  474. {
  475. INETCSETINFO CharsetInfo;
  476. BOOL fret = FALSE;
  477. HRESULT hr = S_OK;
  478. HCHARSET hCharset = NULL;
  479. CODEPAGEINFO CodePage = {0};
  480. Assert(pMsg);
  481. IF_FAILEXIT(hr = pMsg->GetCharset(&hCharset));
  482. IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CharsetInfo));
  483. /*
  484. if (CharsetInfo.cpiWindows == GetACP() || CharsetInfo.cpiWindows == CP_UNICODE)
  485. fret = TRUE;
  486. */
  487. IF_FAILEXIT(hr = MimeOleGetCodePageInfo(CharsetInfo.cpiInternet, &CodePage));
  488. if (CodePage.cpiFamily == GetACP() || CodePage.cpiFamily == CP_UNICODE)
  489. fret = TRUE;
  490. exit:
  491. return fret;
  492. }
  493. #ifdef SMIME_V3
  494. static const BYTE RgbASNForSHASign[11] =
  495. {
  496. 0x30, 0x09, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e,
  497. 0x03, 0x02, 0x1a
  498. };
  499. // Auto association of signing certificate for secure receipt
  500. BOOL AutoAssociateCert(BOOL *fAllowTryAgain, HWND hwnd, IImnAccount *pTempAccount)
  501. {
  502. if(*fAllowTryAgain)
  503. {
  504. *fAllowTryAgain = FALSE;
  505. if (SUCCEEDED(_HrFindMyCertForAccount(hwnd, NULL, pTempAccount, FALSE)))
  506. return(TRUE);
  507. }
  508. return(FALSE);
  509. }
  510. void ErrorSendSecReceipt(HWND hwnd, HRESULT hr, IImnAccount *pAccount)
  511. {
  512. if(hr == HR_E_ATHSEC_NOCERTTOSIGN)
  513. {
  514. if(DialogBoxParam(g_hLocRes,
  515. MAKEINTRESOURCE(iddErrSecurityNoSigningCert), hwnd,
  516. ErrSecurityNoSigningCertDlgProc, NULL) == idGetDigitalIDs)
  517. GetDigitalIDs(pAccount);
  518. }
  519. else
  520. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsCannotSendSecReceipt),
  521. 0, MB_OK | MB_ICONSTOP);
  522. return;
  523. }
  524. // Process secure receipts
  525. HRESULT ProcessSecureReceipt(IMimeMessage * pMsg, IStoreCallback *pStoreCB)
  526. {
  527. IMimeMessage *pMessageRcpt = NULL;
  528. IMimeBody * pBody = NULL;
  529. IMimeBody * pBodyRec = NULL;
  530. IMimeSecurity2 * pSMIME3 = NULL;
  531. CERT_NAME_BLOB *rgReceiptFromList = NULL;
  532. DWORD cReceiptFromList = 0;
  533. PCX509CERT pCert = NULL;
  534. THUMBBLOB tbCert = {0, 0};
  535. HCERTSTORE hMyCertStore = NULL;
  536. X509CERTRESULT certResult;
  537. CERTSTATE cs;
  538. LPVOID pv = NULL;
  539. DWORD dwBits = 40;
  540. DWORD cb = 0;
  541. LPBYTE pBlobData = NULL;
  542. ULONG ulSecurityType = MST_CLASS_SMIME_V1;
  543. LPWABAL lpWabal = NULL;
  544. PROPVARIANT var = {0};
  545. DWORD dwOption = 0;
  546. UINT uiRes = 0;
  547. HRESULT hr = S_OK;
  548. IImnAccount *pTempAccount = NULL;
  549. HWND hwnd = NULL;
  550. TCHAR szEmail[CCHMAX_EMAIL_ADDRESS];
  551. BOOL fSendImmediate = FALSE;
  552. LPWSTR lpsz = NULL;
  553. WCHAR lpBuffer[CCHMAX_STRINGRES];
  554. LPWSTR szParam = NULL;
  555. SECSTATE secStateRec = {0};
  556. BOOL fAllowTryAgain = TRUE;
  557. PCCERT_CONTEXT *rgCertChain = NULL;
  558. DWORD cCertChain = 0;
  559. const DWORD dwIgnore = CERT_VALIDITY_NO_CRL_FOUND;
  560. DWORD dwTrust = 0;
  561. LPWSTR pszRefs = NULL;
  562. LPWSTR pszOrigRefs = NULL;
  563. LPWSTR pszNewRef = NULL;
  564. HCHARSET hCharset = NULL;
  565. INETCSETINFO CsetInfo = {0};
  566. HBODY hBody = NULL;
  567. int cch;
  568. Assert(pMsg != NULL);
  569. // get windof for error messages
  570. IF_FAILEXIT(hr = pStoreCB->GetParentWindow(0, &hwnd));
  571. // if option set DO NOT send secure receipt then exit
  572. dwOption = DwGetOption(OPT_MDN_SEC_RECEIPT);
  573. if (dwOption == MDN_DONT_SENDRECEIPT)
  574. goto exit;
  575. // check do we have secure receipt request in message?
  576. IF_FAILEXIT(hr = HrGetInnerLayer(pMsg, &hBody));
  577. IF_FAILEXIT(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody));
  578. IF_FAILEXIT(hr = pBody->GetOption(OID_SECURITY_TYPE, &var));
  579. if(!(var.ulVal & MST_RECEIPT_REQUEST))
  580. {
  581. var.ulVal = 0; // Set to 0, because var.pszVal and var.ulVal point to the same address.
  582. hr = S_OK;
  583. goto exit;
  584. }
  585. // Check do we trust this message: Bug 78118
  586. IF_FAILEXIT(hr = HrGetSecurityState(pMsg, &secStateRec, NULL));
  587. if(!IsSignTrusted(&secStateRec))
  588. {
  589. // do not show any error message in this case, just exit
  590. hr = S_OK;
  591. goto exit;
  592. }
  593. // we have request, check that we would like to send receipt
  594. if (dwOption == MDN_PROMPTFOR_SENDRECEIPT)
  595. {
  596. uiRes = (UINT) DialogBoxParamWrapW(g_hLocRes, MAKEINTRESOURCEW(iddSecResponse),
  597. hwnd, SecRecResDlgProc, (LPARAM) 0);
  598. if(uiRes == 0)
  599. {
  600. hr = S_FALSE;
  601. goto exit;
  602. }
  603. else if((uiRes != IDOK) && (uiRes != IDYES)) // IDYES means we will encrypt receipt
  604. {
  605. hr = S_OK;
  606. goto exit;
  607. }
  608. }
  609. // Get SMIME3 Security interface on message
  610. IF_FAILEXIT(hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pSMIME3));
  611. // Get List of people who should send receipts
  612. IF_FAILEXIT(hr = pSMIME3->GetReceiptSendersList(0, &cReceiptFromList,&rgReceiptFromList));
  613. // Check if asking for receipt from noone
  614. if (hr == S_FALSE)
  615. {
  616. hr = S_OK;
  617. goto exit;
  618. }
  619. // Check account information
  620. var.vt = VT_LPSTR;
  621. IF_FAILEXIT(hr = pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var));
  622. if(FAILED(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pTempAccount)))
  623. {
  624. MemFree(var.pszVal);
  625. goto exit;
  626. }
  627. SafeMemFree(var.pszVal);
  628. // Check that we are in secure list
  629. IF_FAILEXIT(hr = pTempAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szEmail, ARRAYSIZE(szEmail)));
  630. if(!FNameInList(szEmail, cReceiptFromList,rgReceiptFromList))
  631. {
  632. // we are not in list
  633. hr = S_FALSE;
  634. goto exit;
  635. }
  636. // Get a certificate for receipt
  637. Try_agian:
  638. if((hr = pTempAccount->GetProp(AP_SMTP_CERTIFICATE, NULL, &tbCert.cbSize)) != S_OK)
  639. {
  640. if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
  641. goto Try_agian;
  642. else
  643. {
  644. hr = HR_E_ATHSEC_NOCERTTOSIGN;
  645. goto exit;
  646. }
  647. }
  648. IF_FAILEXIT(hr = HrAlloc((void**)&tbCert.pBlobData, tbCert.cbSize));
  649. IF_FAILEXIT(hr = pTempAccount->GetProp(AP_SMTP_CERTIFICATE, tbCert.pBlobData, &tbCert.cbSize));
  650. hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING,
  651. NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
  652. if (hMyCertStore == NULL)
  653. goto exit;
  654. certResult.cEntries = 1;
  655. certResult.rgcs = &cs;
  656. certResult.rgpCert = &pCert;
  657. if((hr = MimeOleGetCertsFromThumbprints(&tbCert, &certResult, &hMyCertStore, 1) != S_OK))
  658. {
  659. if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
  660. goto Try_agian;
  661. else
  662. {
  663. hr = HR_E_ATHSEC_NOCERTTOSIGN;
  664. goto exit;
  665. }
  666. }
  667. // Check certificate
  668. // As to CRLs, if we'll ever have one!
  669. dwTrust = DwGenerateTrustedChain(hwnd, NULL, pCert,
  670. dwIgnore, TRUE, &cCertChain, &rgCertChain);
  671. if (rgCertChain)
  672. {
  673. for (cCertChain--; int(cCertChain)>=0; cCertChain--)
  674. CertFreeCertificateContext(rgCertChain[cCertChain]);
  675. MemFree(rgCertChain);
  676. rgCertChain = NULL;
  677. }
  678. if (dwTrust)
  679. {
  680. if(AutoAssociateCert(&fAllowTryAgain, hwnd, pTempAccount))
  681. goto Try_agian;
  682. else
  683. {
  684. hr = HR_E_ATHSEC_NOCERTTOSIGN;
  685. goto exit;
  686. }
  687. }
  688. // Create receipt message
  689. IF_FAILEXIT(hr = pSMIME3->CreateReceipt(0, lstrlen(szEmail), (const BYTE *) szEmail, 1, &pCert, &pMessageRcpt));
  690. // IF_FAILEXIT(hr = HrCreateMessage (&pMessageRcpt));
  691. // IF_FAILEXIT(hr = SetRootHeaderFields(pMessageRcpt, pMsg, szEmail, READRECEIPT)); // szEmail is bug!
  692. // Subject
  693. IF_FAILEXIT(hr = MimeOleGetBodyPropW(pMsg, HBODY_ROOT, STR_ATT_NORMSUBJ, NOFLAGS, &lpsz));
  694. if (fMessageEncodingMatch(pMsg))
  695. {
  696. AthLoadStringW(idsSecureReceiptText, lpBuffer, ARRAYSIZE(lpBuffer));
  697. }
  698. else
  699. {
  700. //The encoding didn't match, so we just load english headers
  701. StrCpyNW(lpBuffer, c_szSecReadReceipt, ARRAYSIZE(lpBuffer));
  702. }
  703. // Init security options
  704. #if 0
  705. if(DwGetOption(OPT_MAIL_INCLUDECERT))
  706. {
  707. ulSecurityType |= ((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
  708. HrInitSecurityOptions(pMessageRcpt, ulSecurityType);
  709. }
  710. #endif // 0
  711. cch = lstrlenW(lpsz) + lstrlenW(lpBuffer) + 1;
  712. IF_FAILEXIT(hr = HrAlloc((LPVOID*)&szParam, cch * sizeof(WCHAR)));
  713. wnsprintfW(szParam, cch, lpBuffer, lpsz);
  714. IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, szParam));
  715. //Set Time
  716. IF_FAILEXIT(hr = HrSetSentTimeProp(pMessageRcpt, NULL));
  717. //Set References property
  718. IF_FAILEXIT(hr = MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pszNewRef));
  719. //No need to check for return value. It gets handled correctly in HrCreateReferences
  720. MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, &pszOrigRefs);
  721. IF_FAILEXIT(hr = HrCreateReferences(pszOrigRefs, pszNewRef, &pszRefs));
  722. IF_FAILEXIT(hr = MimeOleSetBodyPropW(pMessageRcpt, HBODY_ROOT, PIDTOSTR(PID_HDR_REFS), NOFLAGS, pszRefs));
  723. IF_FAILEXIT(hr = HrSetAccountByAccount(pMessageRcpt, pTempAccount));
  724. //
  725. IF_FAILEXIT(hr = HrGetWabalFromMsg(pMessageRcpt, &lpWabal));
  726. IF_FAILEXIT(hr = HrSetSenderInfoUtil(pMessageRcpt, pTempAccount, lpWabal, TRUE, 0, FALSE));
  727. IF_FAILEXIT(hr = HrSetWabalOnMsg(pMessageRcpt, lpWabal));
  728. //#if 0
  729. IF_FAILEXIT(hr = pMessageRcpt->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pBodyRec));
  730. // Set hash algorithm
  731. // Init with no symcap gives max allowed by providers
  732. MimeOleSMimeCapInit(NULL, NULL, &pv);
  733. MimeOleSMimeCapGetHashAlg(pv, NULL, &cb, &dwBits);
  734. var.vt = VT_BLOB;
  735. if(cb > 0)
  736. {
  737. if(!MemAlloc((LPVOID *)&pBlobData, cb))
  738. goto exit;
  739. // ZeroMemory(&pBlobData, cb);
  740. MimeOleSMimeCapGetHashAlg(pv, pBlobData, &cb, &dwBits);
  741. var.blob.cbSize = cb;
  742. var.blob.pBlobData = pBlobData;
  743. }
  744. else
  745. {
  746. var.blob.cbSize = sizeof(RgbASNForSHASign);
  747. var.blob.pBlobData = (LPBYTE) RgbASNForSHASign;
  748. }
  749. IF_FAILEXIT(hr = pBodyRec->SetOption(OID_SECURITY_ALG_HASH, &var));
  750. #ifdef _WIN64
  751. var.vt = VT_UI8;
  752. var.pulVal = (ULONG *) hwnd;
  753. IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER_64, &var));
  754. #else
  755. var.vt = VT_UI4;
  756. var.ulVal = (DWORD) hwnd;
  757. IF_FAILEXIT(hr = pBodyRec->SetOption(OID_SECURITY_HWND_OWNER, &var));
  758. var.ulVal = 0; // Set to 0, because var.pszVal and var.ulVal point to the same address.
  759. #endif // _WIN64
  760. // IF_FAILEXIT(hr = pMessageRcpt->Commit(0));
  761. // #endif //0
  762. IF_FAILEXIT(hr = pMsg->GetCharset(&hCharset));
  763. // re-map CP_JAUTODETECT and CP_KAUTODETECT if necessary
  764. // re-map iso-2022-jp to default charset if they are in the same category
  765. IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CsetInfo));
  766. IF_NULLEXIT(hCharset = GetMimeCharsetFromCodePage(GetMapCP(CsetInfo.cpiInternet, FALSE)));
  767. IF_FAILEXIT(hr = pMessageRcpt->SetCharset(hCharset, CSET_APPLY_ALL));
  768. // should be new header
  769. fSendImmediate = (DwGetOption(OPT_SENDIMMEDIATE) && !g_pConMan->IsGlobalOffline());
  770. if (DwGetOption(OPT_MAIL_INCLUDECERT))
  771. IF_FAILEXIT(hr = SendSecureMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, TRUE, NULL));
  772. else
  773. IF_FAILEXIT(hr = SendMailToOutBox((IStoreCallback*)pStoreCB, pMessageRcpt, fSendImmediate, FALSE, TRUE));
  774. exit:
  775. if(FAILED(hr))
  776. ErrorSendSecReceipt(hwnd, hr, pTempAccount);
  777. CleanupSECSTATE(&secStateRec);
  778. SafeMemFree(lpsz);
  779. SafeMemFree(szParam);
  780. SafeMemFree(pBlobData);
  781. SafeMemFree(pv);
  782. SafeRelease(pBodyRec);
  783. SafeRelease(pMessageRcpt);
  784. if(hMyCertStore)
  785. CertCloseStore(hMyCertStore, 0);
  786. if(pCert)
  787. CertFreeCertificateContext(pCert);
  788. // if(tbCert.pBlobData)
  789. SafeMemFree(tbCert.pBlobData);
  790. SafeRelease(lpWabal);
  791. SafeRelease(pTempAccount);
  792. SafeRelease(pSMIME3);
  793. SafeRelease(pBody);
  794. SafeMimeOleFree(pszNewRef);
  795. SafeMimeOleFree(pszOrigRefs);
  796. MemFree(pszRefs);
  797. return(hr);
  798. }
  799. INT_PTR CALLBACK SecRecResDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  800. {
  801. switch(msg)
  802. {
  803. case WM_INITDIALOG:
  804. CenterDialog(hwndDlg);
  805. CheckDlgButton(hwndDlg, IDC_ENCRECEIPT, (!!DwGetOption(OPT_SECREC_ENCRYPT)) ? BST_CHECKED : BST_UNCHECKED);
  806. break;
  807. case WM_COMMAND:
  808. switch (LOWORD(wParam))
  809. {
  810. case IDOK:
  811. EndDialog(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_ENCRECEIPT) ? IDYES : IDOK);
  812. break;
  813. case IDCANCEL:
  814. EndDialog(hwndDlg, IDCANCEL);
  815. break;
  816. default:
  817. return FALSE;
  818. }
  819. break;
  820. default:
  821. return FALSE;
  822. }
  823. return TRUE;
  824. }
  825. #endif // SMIME_V3