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.

832 lines
27 KiB

  1. /*
  2. * b o d y u t i l . c p p
  3. *
  4. * Purpose:
  5. * utility functions for body
  6. *
  7. * History
  8. * August '96: brettm - created
  9. *
  10. * Copyright (C) Microsoft Corp. 1995, 1996.
  11. */
  12. #include <pch.hxx>
  13. #include "dllmain.h"
  14. #include "olealloc.h"
  15. #include "resource.h"
  16. #include "htmlstr.h"
  17. #include "strconst.h"
  18. #include "bodyutil.h"
  19. #include "plainstm.h"
  20. #include "mimeutil.h"
  21. #include "util.h"
  22. #include "demand.h"
  23. ASSERTDATA
  24. /*
  25. * t y p e d e f s
  26. */
  27. enum
  28. {
  29. HEADER_FROM=0,
  30. HEADER_NEWSGROUP,
  31. HEADER_TO,
  32. HEADER_CC,
  33. HEADER_SENT,
  34. HEADER_ATTACH,
  35. HEADER_SUBJECT,
  36. HEADER_MAX
  37. };
  38. /*
  39. * m a c r o s
  40. */
  41. #define WSZ_CB(str) (lstrlenW(str)*sizeof(WCHAR))
  42. #define WSZ_CBNULL(str) ((lstrlenW(str)+1)*sizeof(WCHAR))
  43. #define WSZ_CCH(str) lstrlenW(str)
  44. #define WSZ_CCHNULL(str) (lstrlenW(str)+1)
  45. /*
  46. * c o n s t a n t s
  47. */
  48. static const WCHAR c_wszHtmlDIV_Close[] = L"</DIV>\r\n",
  49. c_wszTableTag_Close[] = L"</TABLE>\r\n",
  50. c_wszHtml[] = L"<HTML>\r\n",
  51. c_wszHtmlReplyAnchor[] = L"<A href=\"mailto:%s\" TITLE=\"%s\">%s</A>\r\n",
  52. c_wszHtml_HeaderDIV[] = L"<DIV>\r\n<B>%s</B> ",
  53. c_wszHtml_DIV_BR_DIV[] = L"<DIV><BR></DIV>\r\n",
  54. c_wszHtml_FromDIV[] = L"<DIV STYLE=\"background:#E4E4E4; font-color:black\">\r\n<B>%s</B> ",
  55. c_wszHtml_HeaderDIVFmt_Start[] = L"<DIV>\r\n<B>",
  56. c_wszHtml_HeaderDIVFmt_Middle[] = L"</B> ",
  57. c_wszHtml_HeaderDIVFmt_End[] = L"</DIV>",
  58. c_wszHtml_HeaderDIVFmt_Plain_Start[]= L"<DIV>\r\n",
  59. c_wszHtml_HeaderDIVFmt_Plain_Middle[]= L" ",
  60. c_wszHtml_HeaderDIVFmt_Plain_End[] = L"</DIV>";
  61. static const int c_cchHtml_HeaderDIVFmt_Start = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_Start) - 1,
  62. c_cchHtml_HeaderDIVFmt_Middle = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_Middle) - 1,
  63. c_cchHtml_HeaderDIVFmt_End = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_End) - 1,
  64. c_cchHtml_HeaderDIVFmt_Plain_Start = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_Plain_Start) - 1,
  65. c_cchHtml_HeaderDIVFmt_Plain_Middle = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_Plain_Middle) - 1,
  66. c_cchHtml_HeaderDIVFmt_Plain_End = ARRAYSIZE(c_wszHtml_HeaderDIVFmt_Plain_End) - 1;
  67. // HARDCODED Western headers
  68. static const LPWSTR c_rgwszHeaders[HEADER_MAX] = { L"From:",
  69. L"Newsgroups:",
  70. L"To:",
  71. L"Cc:",
  72. L"Sent:",
  73. L"Attach:",
  74. L"Subject:"};
  75. static const int c_rgidsHeaders[HEADER_MAX] ={
  76. idsFromField,
  77. idsNewsgroupsField,
  78. idsToField,
  79. idsCcField,
  80. idsDateField,
  81. idsAttachField,
  82. idsSubjectField};
  83. /*
  84. * g l o b a l s
  85. */
  86. /*
  87. * p r o t o t y p e s
  88. */
  89. HRESULT CreatePrintHeader(IMimeMessageW *pMsg, LPWSTR pwszUser, DWORD dwFlags, LPSTREAM pstm);
  90. HRESULT CreateMailHeader(IMimeMessageW *pMsg, DWORD dwFlags, LPSTREAM pstm);
  91. HRESULT CreateNewsHeader(IMimeMessageW *pMsg, DWORD dwFlags, LPSTREAM pstm);
  92. HRESULT GetHeaderLabel(ULONG uHeader, DWORD dwFlags, LPWSTR *ppwszOut);
  93. HRESULT GetHeaderText(IMimeMessageW *pMsg, ULONG uHeader, DWORD dwFlags, LPWSTR *ppwszOut);
  94. HRESULT CreateHTMLAddressLine(IMimeMessageW *pMsg, DWORD dwAdrTypes, LPWSTR *ppwszOut);
  95. void DropAngles(LPWSTR pwszIn, LPWSTR *ppwszOut);
  96. /*
  97. * f u n c t i o n s
  98. */
  99. /*
  100. * Trident doesn't do a good job of converting tables->plaintext, so we can't use
  101. * a table to construct the header of a re: fw: etc to get nice alignment on the
  102. * field boundaries. We can use a table, however for printing. We need to be able to
  103. * construct a header (in html source) in 3 flavours:
  104. *
  105. * HDR_PLAIN (regular text, eg: reply in plain-mode)
  106. * HDR_HTML (regular text, with bolded fields, eg: reply in html-mode)
  107. * HDR_TABLE (use table to get improved output, eg: printing)
  108. *
  109. */
  110. HRESULT GetHeaderTable(IMimeMessageW *pMsg, LPWSTR pwszUserName, DWORD dwHdrStyle, LPSTREAM *ppstm)
  111. {
  112. HRESULT hr = S_OK;
  113. LPSTREAM pstm = NULL;
  114. BYTE bUniMark = 0xFF;
  115. if (pMsg==NULL || ppstm==NULL)
  116. IF_FAILEXIT(hr = E_INVALIDARG);
  117. IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&pstm));
  118. // Write out the BOM
  119. IF_FAILEXIT(hr = pstm->Write(&bUniMark, sizeof(bUniMark), NULL));
  120. bUniMark = 0xFE;
  121. IF_FAILEXIT(hr = pstm->Write(&bUniMark, sizeof(bUniMark), NULL));
  122. if (dwHdrStyle & HDR_TABLE)
  123. IF_FAILEXIT(hr = CreatePrintHeader(pMsg, pwszUserName, dwHdrStyle, pstm));
  124. else if (dwHdrStyle & HDR_NEWSSTYLE)
  125. IF_FAILEXIT(hr = CreateNewsHeader(pMsg, dwHdrStyle, pstm));
  126. else
  127. IF_FAILEXIT(hr = CreateMailHeader(pMsg, dwHdrStyle, pstm));
  128. *ppstm = pstm;
  129. pstm = NULL;
  130. exit:
  131. ReleaseObj(pstm);
  132. return hr;
  133. }
  134. HRESULT CreateNewsHeader(IMimeMessageW *pMsg, DWORD dwFlags, LPSTREAM pstm)
  135. {
  136. HRESULT hr = S_OK;
  137. WCHAR wsz[CCHMAX_STRINGRES];
  138. ULONG cch,
  139. cb;
  140. LPWSTR pwsz = NULL,
  141. pwszFrom = NULL,
  142. pwszMsgId = NULL,
  143. pwszFmtMsgId = NULL,
  144. pwszHtml;
  145. Assert(pMsg);
  146. Assert(pstm);
  147. pMsg->GetAddressFormatW(IAT_FROM, AFT_DISPLAY_BOTH, &pwszFrom);
  148. MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pwszMsgId);
  149. *wsz = 0;
  150. cch = LoadStringWrapW(g_hLocRes, idsReplyTextPrefix, wsz, ARRAYSIZE(wsz));
  151. cb = cch*sizeof(WCHAR);
  152. // Opie assumes me these Sz function never return NULL. Only an empty buffer
  153. if (pwszFrom)
  154. cb += WSZ_CB(pwszFrom);
  155. else
  156. pwszFrom=(LPWSTR)c_szEmptyW;
  157. if (pwszMsgId)
  158. {
  159. // We purposely remove < and > from the msgId so Trident will correctly
  160. // format the msgId as a news: link and not a mailto:
  161. DropAngles(pwszMsgId, &pwszFmtMsgId);
  162. cb += WSZ_CB(pwszFmtMsgId);
  163. }
  164. else
  165. pwszFmtMsgId=(LPWSTR)c_szEmptyW;
  166. // Add null
  167. cb += sizeof(WCHAR);
  168. IF_NULLEXIT(MemAlloc((LPVOID *)&pwsz, cb));
  169. IF_FAILEXIT(hr = pstm->Write(c_wszHtml_DivOpen, WSZ_CB(c_wszHtml_DivOpen), NULL));
  170. // We just write out the e-mail name and news:message-id,
  171. // trident does URL autodetection and makes the e-mail into a mailto link and the
  172. // news: into a news link.
  173. wnsprintfW(pwsz, cb, wsz, pwszFrom, pwszFmtMsgId);
  174. IF_FAILEXIT(hr = EscapeStringToHTML(pwsz, &pwszHtml));
  175. pstm->Write(pwszHtml, WSZ_CB(pwszHtml), NULL);
  176. MemFree(pwszHtml);
  177. IF_FAILEXIT(hr = pstm->Write(c_wszHtml_DivClose, WSZ_CB(c_wszHtml_DivClose), NULL));
  178. exit:
  179. if (pwszFrom != c_szEmptyW)
  180. MemFree(pwszFrom);
  181. MemFree(pwszMsgId);
  182. // pwszFmtMsgId was freed when we freed lpsMsgId
  183. MemFree(pwsz);
  184. return hr;
  185. }
  186. /*
  187. <DIV style="font: 10pt arial">
  188. -----Original Message-----
  189. <DIV STYLE="background:gainsboro; font-color:black">
  190. <B>From:</B> <a href="mailto:[email protected]">Justin Ferrari</a>
  191. </DIV>
  192. <DIV>
  193. <B>To:</B>
  194. <A href="mailto:[email protected]" TITLE="[email protected]">John Tafoya</A>;
  195. <A href="mailto:[email protected]" TITLE="[email protected]">Lauren Antonoff</A>;
  196. <A href="mailto:[email protected]" TITLE="[email protected]">Dave Haws</A>;
  197. <A href="mailto:[email protected]" TITLE="[email protected]">John Doe</A>
  198. </DIV>
  199. <DIV>
  200. <B>Date:</B> Tuesday, September 01, 1998 10:40 AM
  201. </DIV>
  202. <DIV>
  203. <B>Subject: </B>Re: A plea to save the life of the News Server combo box
  204. </DIV>
  205. <DIV><BR></DIV>
  206. </DIV>
  207. */
  208. HRESULT CreateMailHeader(IMimeMessageW *pMsg, DWORD dwFlags, LPSTREAM pstm)
  209. {
  210. ULONG uHeader,
  211. cchFmt;
  212. WCHAR wsz[CCHMAX_STRINGRES],
  213. wszText[CCHMAX_STRINGRES];
  214. LPWSTR pwsz = NULL,
  215. pwszText = NULL,
  216. pwszLabel = NULL,
  217. pwszHtml = NULL;
  218. LPCWSTR pwszFmtStart,
  219. pwszFmtMiddle,
  220. pwszFmtEnd;
  221. int cchFmtStart,
  222. cchFmtMiddle,
  223. cchFmtEnd;
  224. HRESULT hr = S_OK;
  225. if (pMsg==NULL || pstm==NULL)
  226. IF_FAILEXIT(hr = E_INVALIDARG);
  227. // emit a table to display the username
  228. if (LoadStringWrapW(g_hLocRes,
  229. dwFlags & HDR_HTML ? idsReplyHeader_Html_SepBlock : idsReplyHeader_SepBlock,
  230. wsz,
  231. ARRAYSIZE(wsz)))
  232. {
  233. *wszText=0;
  234. if (dwFlags & HDR_HARDCODED)
  235. StrCpyNW(wszText, L"----- Original Message -----", ARRAYSIZE(wszText));
  236. else
  237. LoadStringWrapW(g_hLocRes, idsReplySep, wszText, ARRAYSIZE(wszText));
  238. IF_NULLEXIT(pwsz = PszAllocW(WSZ_CCH(wszText) + WSZ_CCH(wsz) + 1));
  239. wnsprintfW(pwsz, (WSZ_CCH(wszText) + WSZ_CCH(wsz) + 1), wsz, wszText);
  240. pstm->Write(pwsz, WSZ_CB(pwsz), NULL);
  241. SafeMemFree(pwsz);
  242. }
  243. if (dwFlags & HDR_HTML)
  244. {
  245. pwszFmtStart = c_wszHtml_HeaderDIVFmt_Start;
  246. pwszFmtMiddle = c_wszHtml_HeaderDIVFmt_Middle;
  247. pwszFmtEnd = c_wszHtml_HeaderDIVFmt_End;
  248. cchFmtStart = c_cchHtml_HeaderDIVFmt_Start;
  249. cchFmtMiddle = c_cchHtml_HeaderDIVFmt_Middle;
  250. cchFmtEnd = c_cchHtml_HeaderDIVFmt_End;
  251. }
  252. else
  253. {
  254. pwszFmtStart = c_wszHtml_HeaderDIVFmt_Plain_Start;
  255. pwszFmtMiddle = c_wszHtml_HeaderDIVFmt_Plain_Middle;
  256. pwszFmtEnd = c_wszHtml_HeaderDIVFmt_Plain_End;
  257. cchFmtStart = c_cchHtml_HeaderDIVFmt_Plain_Start;
  258. cchFmtMiddle = c_cchHtml_HeaderDIVFmt_Plain_Middle;
  259. cchFmtEnd = c_cchHtml_HeaderDIVFmt_Plain_End;
  260. }
  261. // write out each header row
  262. for (uHeader=0; uHeader<HEADER_MAX; uHeader++)
  263. {
  264. // skip attachment header for reply and forward headers
  265. if (uHeader == HEADER_ATTACH)
  266. continue;
  267. // special case address-headers in HTML-Mode
  268. if (dwFlags & HDR_HTML)
  269. {
  270. switch (uHeader)
  271. {
  272. case HEADER_FROM:
  273. {
  274. if (CreateHTMLAddressLine(pMsg, IAT_FROM, &pwsz)==S_OK)
  275. {
  276. if (GetHeaderLabel(uHeader, dwFlags, &pwszLabel)==S_OK)
  277. {
  278. // pszLabel is fixed size
  279. wnsprintfW(wsz, ARRAYSIZE(wsz), c_wszHtml_FromDIV, pwszLabel);
  280. pstm->Write(wsz, WSZ_CB(wsz), NULL);
  281. pstm->Write(pwsz, WSZ_CB(pwsz), NULL);
  282. pstm->Write(c_wszHtmlDIV_Close, WSZ_CB(c_wszHtmlDIV_Close), NULL);
  283. SafeMemFree(pwszLabel);
  284. }
  285. SafeMemFree(pwsz);
  286. }
  287. continue;
  288. }
  289. case HEADER_TO:
  290. case HEADER_CC:
  291. {
  292. if (CreateHTMLAddressLine(pMsg, uHeader == HEADER_TO ? IAT_TO : IAT_CC, &pwsz)==S_OK)
  293. {
  294. if (GetHeaderLabel(uHeader, dwFlags, &pwszLabel)==S_OK)
  295. {
  296. wnsprintfW(wsz, ARRAYSIZE(wsz), c_wszHtml_HeaderDIV, pwszLabel); // pszLabel is fixed size
  297. pstm->Write(wsz, WSZ_CB(wsz), NULL);
  298. pstm->Write(pwsz, WSZ_CB(pwsz), NULL);
  299. pstm->Write(c_wszHtmlDIV_Close, WSZ_CB(c_wszHtmlDIV_Close), NULL);
  300. SafeMemFree(pwszLabel);
  301. }
  302. SafeMemFree(pwsz);
  303. }
  304. continue;
  305. }
  306. }
  307. }
  308. // normal headers
  309. if (GetHeaderLabel(uHeader, dwFlags, &pwszLabel)==S_OK)
  310. {
  311. if (GetHeaderText(pMsg, uHeader, dwFlags, &pwszText)==S_OK)
  312. {
  313. if (*pwszText) // ignore empty strings
  314. {
  315. int cch = 0;
  316. int cchLabel;
  317. int cchHtml;
  318. IF_FAILEXIT(hr = EscapeStringToHTML(pwszText, &pwszHtml));
  319. cchLabel = WSZ_CCH(pwszLabel);
  320. cchHtml = WSZ_CCH(pwszHtml);
  321. // 1 for the NULL
  322. DWORD cchSize = (cchLabel + cchHtml + cchFmtStart + cchFmtMiddle + cchFmtEnd + 1);
  323. IF_NULLEXIT(pwsz = PszAllocW(cchSize));
  324. // Avoid potentially dumping > 1024 into wnsprintfW
  325. StrCpyNW(pwsz, pwszFmtStart, cchSize);
  326. cch = cchFmtStart;
  327. StrCpyNW(&pwsz[cch], pwszLabel, cchSize - cch);
  328. cch += cchLabel;
  329. StrCpyNW(&pwsz[cch], pwszFmtMiddle, cchSize - cch);
  330. cch += cchFmtMiddle;
  331. StrCpyNW(&pwsz[cch], pwszHtml, cchSize - cch);
  332. cch += cchHtml;
  333. StrCpyNW(&pwsz[cch], pwszFmtEnd, cchSize - cch);
  334. cch += cchFmtEnd;
  335. pstm->Write(pwsz, cch * sizeof(WCHAR), NULL);
  336. SafeMemFree(pwsz);
  337. SafeMemFree(pwszHtml);
  338. }
  339. SafeMemFree(pwszText);
  340. }
  341. SafeMemFree(pwszLabel);
  342. }
  343. }
  344. // Close the <DIV> that wraps the whole thing in a font
  345. pstm->Write(c_wszHtmlDIV_Close, WSZ_CB(c_wszHtmlDIV_Close), NULL);
  346. // add a <DIV><BR></DIV> to give one line-spacing
  347. pstm->Write(c_wszHtml_DIV_BR_DIV, WSZ_CB(c_wszHtml_DIV_BR_DIV), NULL);
  348. exit:
  349. MemFree(pwszHtml);
  350. MemFree(pwszText);
  351. MemFree(pwszLabel);
  352. MemFree(pwsz);
  353. return hr;
  354. }
  355. HRESULT CreatePrintHeader(IMimeMessageW *pMsg, LPWSTR pwszUser, DWORD dwFlags, LPSTREAM pstm)
  356. {
  357. ULONG uHeader;
  358. WCHAR wsz[CCHMAX_STRINGRES];
  359. LPWSTR pwsz = NULL,
  360. pwszText = NULL,
  361. pwszLabel = NULL,
  362. pwszHtml = NULL;
  363. HRESULT hr = S_OK;
  364. int cch = 0;
  365. if (pMsg==NULL || pstm==NULL)
  366. IF_FAILEXIT(hr = E_INVALIDARG);
  367. // if no username, use "" so wsprintf is happy
  368. if (pwszUser == NULL)
  369. pwszUser = (LPWSTR)c_szEmptyW;
  370. // Emit an <HTML> tag for trident autodetector (used on the print-header)
  371. pstm->Write(c_wszHtml, WSZ_CB(c_wszHtml), NULL);
  372. // emit a table to display the username
  373. if (LoadStringWrapW(g_hLocRes, idsPrintTable_UserName, wsz, ARRAYSIZE(wsz)))
  374. {
  375. IF_FAILEXIT(hr = EscapeStringToHTML(pwszUser, &pwszHtml));
  376. IF_NULLEXIT(pwsz = PszAllocW(WSZ_CCH(wsz) + WSZ_CCH(pwszHtml) + 1));
  377. wnsprintfW(pwsz, (WSZ_CCH(wsz) + WSZ_CCH(pwszHtml) + 1), wsz, pwszHtml);
  378. pstm->Write(pwsz, WSZ_CB(pwsz), NULL);
  379. SafeMemFree(pwsz);
  380. SafeMemFree(pwszHtml);
  381. }
  382. // start the main-table
  383. if (LoadStringWrapW(g_hLocRes, idsPrintTable_Header, wsz, ARRAYSIZE(wsz)))
  384. {
  385. pstm->Write(wsz, WSZ_CB(wsz), NULL);
  386. if (LoadStringWrapW(g_hLocRes, idsPrintTable_HeaderRow, wsz, ARRAYSIZE(wsz)))
  387. {
  388. // write out each header row
  389. for (uHeader=0; uHeader<HEADER_MAX; uHeader++)
  390. {
  391. if (GetHeaderLabel(uHeader, dwFlags, &pwszLabel)==S_OK)
  392. {
  393. if (GetHeaderText(pMsg, uHeader, dwFlags, &pwszText)==S_OK)
  394. {
  395. if (*pwszText) // ignore empty strings
  396. {
  397. IF_FAILEXIT(hr = EscapeStringToHTML(pwszText, &pwszHtml));
  398. cch = lstrlenW(pwszLabel) + lstrlenW(pwszHtml) + lstrlenW(wsz) +1;
  399. IF_NULLEXIT(pwsz = PszAllocW(cch));
  400. wnsprintfW(pwsz, cch, wsz, pwszLabel, pwszHtml);
  401. pstm->Write(pwsz, WSZ_CB(pwsz), NULL);
  402. SafeMemFree(pwsz);
  403. SafeMemFree(pwszHtml);
  404. }
  405. SafeMemFree(pwszText);
  406. }
  407. SafeMemFree(pwszLabel);
  408. }
  409. }
  410. }
  411. pstm->Write(c_wszTableTag_Close, WSZ_CB(c_wszTableTag_Close), NULL);
  412. }
  413. exit:
  414. MemFree(pwsz);
  415. MemFree(pwszHtml);
  416. MemFree(pwszText);
  417. MemFree(pwszLabel);
  418. return hr;
  419. }
  420. HRESULT GetHeaderText(IMimeMessageW *pMsg, ULONG uHeader, DWORD dwFlags, LPWSTR *ppwszOut)
  421. {
  422. PROPVARIANT pv;
  423. HBODY *rghAttach = NULL;
  424. ULONG cAttach,
  425. uAttach,
  426. cchSize=0;
  427. LPWSTR *rgpwsz = NULL,
  428. pwszWrite = NULL,
  429. pwszEnd = NULL;
  430. HRESULT hr = S_OK;
  431. Assert(pMsg);
  432. Assert(ppwszOut);
  433. *ppwszOut = 0;
  434. switch (uHeader)
  435. {
  436. case HEADER_FROM:
  437. pMsg->GetAddressFormatW(IAT_FROM, AFT_DISPLAY_BOTH, ppwszOut);
  438. break;
  439. case HEADER_NEWSGROUP:
  440. MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, ppwszOut);
  441. break;
  442. case HEADER_TO:
  443. pMsg->GetAddressFormatW(IAT_TO, AFT_DISPLAY_BOTH, ppwszOut);
  444. break;
  445. case HEADER_CC:
  446. pMsg->GetAddressFormatW(IAT_CC, AFT_DISPLAY_BOTH, ppwszOut);
  447. break;
  448. case HEADER_ATTACH:
  449. if (pMsg->GetAttachments(&cAttach, &rghAttach)==S_OK && cAttach)
  450. {
  451. // create cache-string list
  452. IF_NULLEXIT(rgpwsz = (LPWSTR*)ZeroAllocate(sizeof(*rgpwsz) * cAttach));
  453. for (uAttach=0; uAttach<cAttach; uAttach++)
  454. {
  455. if (MimeOleGetBodyPropW(pMsg, rghAttach[uAttach], PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &rgpwsz[uAttach])==S_OK)
  456. cchSize += (WSZ_CCH(rgpwsz[uAttach]) + 2); // +2 for "; "
  457. }
  458. IF_NULLEXIT(MemAlloc((LPVOID *)ppwszOut, cchSize * sizeof((*ppwszOut)[0])));
  459. pwszWrite = *ppwszOut;
  460. pwszEnd = pwszWrite + cchSize;
  461. for (uAttach=0; uAttach<cAttach; uAttach++)
  462. {
  463. StrCpyNW(pwszWrite, rgpwsz[uAttach], (DWORD)(pwszEnd-pwszWrite));
  464. pwszWrite += WSZ_CCH(rgpwsz[uAttach]);
  465. if (uAttach != cAttach -1)
  466. {
  467. StrCpyNW(pwszWrite, L"; ", (DWORD)(pwszEnd-pwszWrite));
  468. pwszWrite += 2;
  469. }
  470. }
  471. }
  472. break;
  473. case HEADER_SENT:
  474. pv.vt = VT_FILETIME;
  475. if (SUCCEEDED(pMsg->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &pv)))
  476. {
  477. LPWSTR lpwsz = NULL;
  478. INT cch = 0;
  479. IF_NULLEXIT(MemAlloc((LPVOID*)&lpwsz, CCHMAX_STRINGRES * sizeof(*lpwsz)));
  480. *lpwsz = 0;
  481. // always force a western date if using hardcoded headers
  482. // For the formatted date, DTM_FORCEWESTERN flag returns English date and time.
  483. // Without DTM_FORCEWESTERN the formatted time may not be representable in ASCII.
  484. cch = AthFileTimeToDateTimeW(&pv.filetime, lpwsz, CCHMAX_STRINGRES,
  485. DTM_NOSECONDS|DTM_LONGDATE|(dwFlags & HDR_HARDCODED ?DTM_FORCEWESTERN:0));
  486. *ppwszOut = lpwsz;
  487. }
  488. break;
  489. case HEADER_SUBJECT:
  490. MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, ppwszOut);
  491. break;
  492. default:
  493. AssertSz(0, "Not Supported");
  494. }
  495. exit:
  496. // free cached string-list
  497. if (rgpwsz)
  498. {
  499. for (uAttach=0; uAttach<cAttach; uAttach++)
  500. MemFree(rgpwsz[uAttach]);
  501. MemFree(rgpwsz);
  502. }
  503. MemFree(rghAttach);
  504. // In this case, we failed but that is ok so don't trace result.
  505. // Just means that the field asked for is empty.
  506. if (SUCCEEDED(hr) && (0 == *ppwszOut))
  507. hr = E_FAIL;
  508. return hr;
  509. }
  510. HRESULT GetHeaderLabel(ULONG uHeader, DWORD dwFlags, LPWSTR *ppwszOut)
  511. {
  512. WCHAR wsz[CCHMAX_STRINGRES];
  513. Assert(ppwszOut);
  514. *ppwszOut = 0;
  515. if (dwFlags & HDR_HARDCODED)
  516. *ppwszOut = PszDupW(c_rgwszHeaders[uHeader]);
  517. else
  518. {
  519. if(LoadStringWrapW(g_hLocRes, c_rgidsHeaders[uHeader], wsz, ARRAYSIZE(wsz)))
  520. *ppwszOut = PszDupW(wsz);
  521. }
  522. return *ppwszOut ? S_OK : E_FAIL;
  523. }
  524. HRESULT CreateHTMLAddressLine(IMimeMessageW *pMsg, DWORD dwAdrTypes, LPWSTR *ppwszOut)
  525. {
  526. ADDRESSLIST rAdrList = {0};
  527. BOOL fFreeAdrList;
  528. ULONG i;
  529. ULONG cb=0;
  530. LPWSTR pwsz = NULL,
  531. pwszWrite = NULL,
  532. pwszEnd = NULL,
  533. pwszEmail = NULL,
  534. pwszEmailLoop = NULL,
  535. pwszFriendly = NULL,
  536. *rgpwszEmail = NULL,
  537. *rgpwszFriendly = NULL;
  538. ULONG cAddr = 0;
  539. HRESULT hr = S_OK;
  540. Assert (pMsg);
  541. // Get address table
  542. IF_FAILEXIT(hr = pMsg->GetAddressTypes(dwAdrTypes, IAP_FRIENDLYW|IAP_EMAIL, &rAdrList));
  543. cAddr = rAdrList.cAdrs;
  544. // If no addresses, bail
  545. if (cAddr == 0)
  546. IF_FAILEXIT(hr = E_FAIL);
  547. // Allocate array for escaped email addresses
  548. IF_NULLEXIT(rgpwszEmail = (LPWSTR*)ZeroAllocate(sizeof(*rgpwszEmail) * cAddr));
  549. // Allocate array for escaped displaynames
  550. IF_NULLEXIT(rgpwszFriendly = (LPWSTR*)ZeroAllocate(sizeof(*rgpwszFriendly) * cAddr));
  551. for (i=0; i < cAddr; i++)
  552. {
  553. // escape all of the names into our name array
  554. if (rAdrList.prgAdr[i].pszEmail)
  555. {
  556. IF_NULLEXIT(pwszEmail = PszToUnicode(CP_ACP, rAdrList.prgAdr[i].pszEmail));
  557. // Escape Into Array
  558. IF_FAILEXIT(hr = EscapeStringToHTML(pwszEmail, &rgpwszEmail[i]));
  559. // Use Email if no Friendly Name
  560. if (NULL == (pwszFriendly = rAdrList.prgAdr[i].pszFriendlyW))
  561. pwszFriendly = pwszEmail;
  562. IF_FAILEXIT(hr = EscapeStringToHTML(pwszFriendly, &rgpwszFriendly[i]));
  563. // for each address we use 2*email + display + htmlgoo + "; "
  564. // if display == null, we use email. We skip if email==NULL (failure case)
  565. cb += (
  566. 2*WSZ_CCH(rgpwszEmail[i]) +
  567. WSZ_CCH(rgpwszFriendly[i]) +
  568. WSZ_CCH(c_wszHtmlReplyAnchor) +
  569. 2
  570. ) * sizeof(WCHAR);
  571. SafeMemFree(pwszEmail);
  572. }
  573. }
  574. IF_NULLEXIT(MemAlloc((LPVOID *)&pwsz, cb));
  575. pwszWrite = pwsz;
  576. pwszEnd = pwszWrite + (cb/sizeof(WCHAR));
  577. for (i=0; i < cAddr; i++)
  578. {
  579. if (pwszEmailLoop = rgpwszEmail[i])
  580. {
  581. pwszFriendly = rgpwszFriendly[i];
  582. Assert (pwszFriendly);
  583. wnsprintfW(pwszWrite, (DWORD)(pwszEnd-pwszWrite), c_wszHtmlReplyAnchor, pwszEmailLoop, pwszEmailLoop, pwszFriendly);
  584. pwszWrite += WSZ_CCH(pwszWrite);
  585. if (i != cAddr-1)
  586. {
  587. StrCpyNW(pwszWrite, L"; ", (DWORD)(pwszEnd-pwszWrite));
  588. pwszWrite += 2;
  589. }
  590. }
  591. }
  592. *ppwszOut = pwsz;
  593. pwsz = NULL; // freed by caller
  594. exit:
  595. if (rgpwszEmail)
  596. {
  597. for (i=0; i < cAddr; i++)
  598. MemFree(rgpwszEmail[i]);
  599. MemFree(rgpwszEmail);
  600. }
  601. if (rgpwszFriendly)
  602. {
  603. for (i=0; i < cAddr; i++)
  604. MemFree(rgpwszFriendly[i]);
  605. MemFree(rgpwszFriendly);
  606. }
  607. MemFree(pwsz);
  608. MemFree(pwszEmail);
  609. g_pMoleAlloc->FreeAddressList(&rAdrList);
  610. return hr;
  611. }
  612. void GetRGBFromString(DWORD* pRGB, LPSTR pszColor)
  613. {
  614. DWORD dwShiftAmount = 0,
  615. rgb = 0,
  616. n;
  617. CHAR ch;
  618. Assert(pRGB);
  619. Assert(lstrlen(pszColor) == lstrlen("RRGGBB"));
  620. *pRGB = 0;
  621. while (0 != (ch = *pszColor++))
  622. {
  623. if (ch >= '0' && ch <= '9')
  624. n = ch - '0';
  625. else if(ch >= 'A' && ch <= 'F')
  626. n = ch - 'A' + 10;
  627. else
  628. {
  629. Assert(ch >= 'a' && ch <= 'f')
  630. n = ch - 'a' + 10;
  631. }
  632. rgb = (rgb << 4) + n;
  633. }
  634. rgb = ((rgb & 0x00ff0000) >> 16 ) | (rgb & 0x0000ff00) | ((rgb & 0x000000ff) << 16);
  635. *pRGB = rgb;
  636. }
  637. void GetStringRGB(DWORD rgb, LPSTR pszColor)
  638. {
  639. INT i;
  640. DWORD crTemp;
  641. Assert(pszColor);
  642. rgb = ((rgb & 0x00ff0000) >> 16 ) | (rgb & 0x0000ff00) | ((rgb & 0x000000ff) << 16);
  643. for(i = 0; i < 6; i++)
  644. {
  645. crTemp = (rgb & (0x00f00000 >> (4*i))) >> (4*(5-i));
  646. pszColor[i] = (CHAR)((crTemp < 10)? (crTemp+'0') : (crTemp+ 'a' - 10));
  647. }
  648. pszColor[6] = '\0';
  649. }
  650. /*
  651. This function drops the enclosing < and > around a msg-id
  652. RFC822: msg-id = "<" addr-spec ">"
  653. NOTE:
  654. The input buffer is MODIFIED and the output pointer
  655. points to memory in pszIn!
  656. */
  657. void DropAngles(LPWSTR pwszIn, LPWSTR *ppwszOut)
  658. {
  659. if (pwszIn)
  660. {
  661. WCHAR ch;
  662. LPWSTR pwszCurrent = pwszIn;
  663. // First character should be <, but be robust and scan for it
  664. while ((ch = *pwszCurrent++) && (ch != L'<'));
  665. if (ch)
  666. // We are interested in stuff after the angle bracket
  667. *ppwszOut = pwszCurrent;
  668. else
  669. // Perhaps the message-id was malformed and doesn't contain a <
  670. *ppwszOut = pwszIn;
  671. pwszCurrent = *ppwszOut;
  672. // Find the close bracket and null it out
  673. while ((ch = *pwszCurrent) && (ch != L'>'))
  674. pwszCurrent++;
  675. // If we found a >, overwrite it with NULL
  676. if (ch)
  677. *pwszCurrent = NULL;
  678. }
  679. }