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.

744 lines
25 KiB

  1. #include "pch.hxx"
  2. #include "note.h"
  3. #include "header.h"
  4. #include "envcid.h"
  5. #include "envguid.h"
  6. #include "bodyutil.h"
  7. #include "oleutil.h"
  8. #include "acctutil.h"
  9. #include "menures.h"
  10. #include "instance.h"
  11. #include "inetcfg.h"
  12. #include "ipab.h"
  13. #include "msgprop.h"
  14. #include "mlang.h"
  15. #include "shlwapip.h"
  16. #include "demand.h"
  17. #include <ruleutil.h>
  18. #include "instance.h"
  19. #include "mapiutil.h"
  20. #include <mapi.h>
  21. LHANDLE g_lhSession = 0;
  22. //
  23. // FUNCTION: NewsUtil_ReFwdByMapi
  24. //
  25. // PURPOSE: Allows the caller to reply to the specified message via Simple
  26. // MAPI instead of Athena Mail.
  27. //
  28. // PARAMETERS:
  29. // hwnd - Handle of the window to display UI over.
  30. // pNewsMsg - Pointer to the news message to reply/forward to
  31. // fReply - TRUE if we should reply, FALSE to forward.
  32. //
  33. // RETURN VALUE:
  34. // HRESULT.
  35. //
  36. HRESULT NewsUtil_ReFwdByMapi(HWND hwnd, LPMIMEMESSAGE pMsg, DWORD msgtype)
  37. {
  38. // Locals
  39. HRESULT hr=S_OK;
  40. LPMAPIFREEBUFFER pfnMAPIFreeBuffer;
  41. LPMAPIRESOLVENAME pfnMAPIResolveName;
  42. LPMAPISENDMAIL pfnMAPISendMail;
  43. MapiMessage mm;
  44. MapiFileDesc *pFileDesc=NULL;
  45. MapiRecipDesc *pRecips=NULL;
  46. ULONG uAttach;
  47. ULONG cAttach=0;
  48. HBODY *rghAttach=NULL;
  49. LPSTR pszReply=NULL;
  50. LPSTR pszSubject=NULL;
  51. LPSTR pszFrom=NULL;
  52. LPSTR pszTo=NULL;
  53. LPSTR pszFile=NULL;
  54. LPSTR pszFull=NULL;
  55. LPSTR pszDisplay=NULL;
  56. LPSTR pszAddr=NULL;
  57. ADDRESSLIST addrList={0};
  58. HBODY hBody;
  59. BOOL fQP;
  60. TCHAR szNewSubject[256];
  61. LPWSTR pwsz=NULL;
  62. ULONG cchRead;
  63. LPSTREAM pBodyStream=NULL;
  64. LPSTREAM pQuotedStream=NULL;
  65. INT cch;
  66. DWORD cbUnicode;
  67. CHAR szTempPath[MAX_PATH];
  68. LPMIMEBODY pBody=NULL;
  69. MapiFileDesc *pCur;
  70. // Trace
  71. TraceCall("NewsUtil_ReFwdByMapi");
  72. // Initialize
  73. ZeroMemory(&mm, sizeof(mm));
  74. // Load the MAPI DLL. If we don't succeed, then we can't continue
  75. IF_FAILEXIT(hr = NewsUtil_LoadMAPI(hwnd));
  76. // pfnMAPIFreeBuffer
  77. pfnMAPIFreeBuffer = (LPMAPIFREEBUFFER)GetProcAddress(g_hlibMAPI, c_szMAPIFreeBuffer);
  78. if (NULL == pfnMAPIFreeBuffer)
  79. {
  80. hr = TraceResult(E_FAIL);
  81. goto exit;
  82. }
  83. // pfnMAPIResolveName
  84. pfnMAPIResolveName = (LPMAPIRESOLVENAME) GetProcAddress(g_hlibMAPI, c_szMAPIResolveName);
  85. if (NULL == pfnMAPIResolveName)
  86. {
  87. hr = TraceResult(E_FAIL);
  88. goto exit;
  89. }
  90. // pfnMAPISendMail
  91. pfnMAPISendMail = (LPMAPISENDMAIL) GetProcAddress(g_hlibMAPI, c_szMAPISendMail);
  92. if (NULL == pfnMAPISendMail)
  93. {
  94. hr = TraceResult(E_FAIL);
  95. goto exit;
  96. }
  97. // From
  98. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_FROM), NOFLAGS, &pwsz)))
  99. {
  100. IF_NULLEXIT(pszFrom = PszToANSI(CP_ACP, pwsz));
  101. SafeMemFree(pwsz);
  102. }
  103. // Reply-To
  104. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_REPLYTO), NOFLAGS, &pwsz)))
  105. {
  106. IF_NULLEXIT(pszReply = PszToANSI(CP_ACP, pwsz));
  107. SafeMemFree(pwsz);
  108. }
  109. // To
  110. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, &pwsz)))
  111. {
  112. IF_NULLEXIT(pszTo = PszToANSI(CP_ACP, pwsz));
  113. SafeMemFree(pwsz);
  114. }
  115. // If this is a reply or forward, we need to get the normalized subject. Otherwise, we just get the regular subject.
  116. if (MSGTYPE_REPLY == msgtype || MSGTYPE_FWD == msgtype)
  117. {
  118. // Normalized Subject
  119. if (FAILED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_ATT_NORMSUBJ), NOFLAGS, &pwsz)))
  120. pwsz = NULL;
  121. }
  122. // Subject
  123. else if (FAILED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &pwsz)))
  124. pwsz = NULL;
  125. // Convert to ansi
  126. if (pwsz)
  127. {
  128. IF_NULLEXIT(pszSubject = PszToANSI(CP_ACP, pwsz));
  129. SafeMemFree(pwsz);
  130. }
  131. // Attempt to generate a reciepent list for MAPI if we're replying or CC'ing.
  132. if (msgtype == MSGTYPE_REPLY || msgtype == MSGTYPE_CC)
  133. {
  134. // Figure out which address to use
  135. if (msgtype == MSGTYPE_REPLY)
  136. {
  137. // If there's a reply-to field on the message, then use that
  138. if (pszReply)
  139. pszFull = pszReply;
  140. else
  141. // Otherwise, we'll use the address in the from header
  142. pszFull = pszFrom;
  143. }
  144. // Who to address to
  145. else
  146. pszFull = pszTo;
  147. // Bug #24587 - Use IAT_TO instead of IAT_UNKNOWN.
  148. if (MimeOleParseRfc822Address(IAT_TO, IET_DECODED, pszFull, &addrList)==S_OK)
  149. {
  150. UINT i;
  151. lpMapiRecipDesc paRecips,pCurrent;
  152. DWORD cchSizeName = 128;
  153. DWORD cchSizeAddress = 128;
  154. // we arbitrarily chose 128 as typical for EIDSize and address string lengths
  155. int cAlloc = (sizeof(MapiRecipDesc) + (sizeof(TCHAR) * cchSizeName) + 128) * addrList.cAdrs;
  156. int cUsed = sizeof(MapiRecipDesc) * addrList.cAdrs;
  157. LPBYTE pVal = NULL;
  158. IF_FAILEXIT(hr = HrAlloc((LPVOID *)&paRecips, cAlloc));
  159. pCurrent = paRecips;
  160. pVal = (LPBYTE)pCurrent + sizeof(MapiRecipDesc) * addrList.cAdrs;
  161. // More than one address
  162. for (i=0; i < addrList.cAdrs ;i++)
  163. {
  164. int cBytes;
  165. // free Safe Friendly Name (not used here, but was allocated)
  166. SafeMemFree(addrList.prgAdr[i].pszFriendly);
  167. addrList.prgAdr[i].pszFriendly = NULL;
  168. // Save E-mail address
  169. pszAddr = addrList.prgAdr[i].pszEmail;
  170. addrList.prgAdr[i].pszEmail = NULL;
  171. // Resolve Name
  172. if ((cUsed < cAlloc) && SUCCESS_SUCCESS == pfnMAPIResolveName(g_lhSession, (ULONG_PTR) hwnd, pszAddr, MAPI_DIALOG, 0, &pRecips))
  173. {
  174. pRecips->ulRecipClass = MAPI_TO;
  175. // copy pRecip
  176. pCurrent->ulReserved = pRecips->ulReserved;
  177. pCurrent->ulRecipClass = pRecips->ulRecipClass;
  178. pCurrent->ulEIDSize = pRecips->ulEIDSize;
  179. do {
  180. if (pRecips->lpszName)
  181. {
  182. cBytes = (lstrlen(pRecips->lpszName)+1)*sizeof(TCHAR);
  183. cUsed += cBytes;
  184. if (cUsed > cAlloc)
  185. break;
  186. pCurrent->lpszName = (LPTSTR)pVal;
  187. StrCpyN(pCurrent->lpszName, pRecips->lpszName, cchSizeName);
  188. pVal += cBytes;
  189. }
  190. else
  191. {
  192. pCurrent->lpszName = NULL;
  193. }
  194. if (pRecips->lpszAddress)
  195. {
  196. cBytes = (lstrlen(pRecips->lpszAddress)+1)*sizeof(TCHAR);
  197. cUsed += cBytes;
  198. if (cUsed > cAlloc)
  199. break;
  200. pCurrent->lpszAddress = (LPTSTR)pVal;
  201. StrCpyN(pCurrent->lpszAddress, pRecips->lpszAddress, cchSizeAddress);
  202. pVal += cBytes;
  203. }
  204. else
  205. {
  206. pCurrent->lpszAddress = NULL;
  207. }
  208. if (pRecips->ulEIDSize)
  209. {
  210. cUsed += pRecips->ulEIDSize;
  211. if (cUsed > cAlloc)
  212. break;
  213. pCurrent->lpEntryID = pVal;
  214. CopyMemory(pCurrent->lpEntryID, pRecips->lpEntryID, (size_t)pRecips->ulEIDSize);
  215. pVal += pRecips->ulEIDSize;
  216. }
  217. else
  218. {
  219. pCurrent->lpEntryID = NULL;
  220. }
  221. pCurrent++;
  222. mm.nRecipCount++;
  223. } while (FALSE);
  224. // Free recips
  225. (*pfnMAPIFreeBuffer)((LPVOID)pRecips);
  226. pRecips = NULL;
  227. }
  228. SafeMemFree(pszAddr);
  229. pszAddr = NULL;
  230. }
  231. mm.lpRecips = paRecips;
  232. // Free the Address List
  233. g_pMoleAlloc->FreeAddressList(&addrList);
  234. }
  235. }
  236. // If this is a reply or forward, then create a normalized subject
  237. if (msgtype == MSGTYPE_REPLY || msgtype == MSGTYPE_FWD)
  238. {
  239. // Pull in the new prefix from resource...
  240. if (msgtype == MSGTYPE_REPLY)
  241. {
  242. StrCpyN(szNewSubject, c_szPrefixRE, ARRAYSIZE(szNewSubject));
  243. }
  244. else
  245. {
  246. StrCpyN(szNewSubject, c_szPrefixFW, ARRAYSIZE(szNewSubject));
  247. }
  248. // If we have a pszSubject
  249. if (pszSubject)
  250. {
  251. // Get Length
  252. cch = lstrlen(szNewSubject);
  253. // Append the Subject
  254. StrCpyN(szNewSubject + cch, pszSubject, ARRAYSIZE(szNewSubject) - cch - 1);
  255. }
  256. // Set the Subject
  257. mm.lpszSubject = szNewSubject;
  258. }
  259. // Don't append anything
  260. else
  261. {
  262. // If this is a CC, then just use the regular subject field
  263. mm.lpszSubject = pszSubject;
  264. }
  265. // Set the note text.
  266. // If this is a fwd as attachment, there won't be a body, don't use IF_FAILEXIT
  267. if(SUCCEEDED(pMsg->GetTextBody(TXT_PLAIN, IET_UNICODE, &pBodyStream, &hBody)))
  268. {
  269. // Convert from unicode to CP_ACP - WARNING: HrStreamToByte allocates 10 extra bytes so I can slam in a L'\0'
  270. IF_FAILEXIT(hr = HrStreamToByte(pBodyStream, (LPBYTE *)&pwsz, &cbUnicode));
  271. // Store null
  272. pwsz[cbUnicode / sizeof(WCHAR)] = L'\0';
  273. // Convert to ANSI
  274. IF_NULLEXIT(mm.lpszNoteText = PszToANSI(CP_ACP, pwsz));
  275. // Release pBodyStream
  276. SafeRelease(pBodyStream);
  277. // Bug #24159 - We need to quote forwards as well as replies
  278. if (DwGetOption(OPT_INCLUDEMSG) && (msgtype == MSGTYPE_REPLY || msgtype == MSGTYPE_FWD))
  279. {
  280. // Create a new stream
  281. IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&pBodyStream));
  282. // Dump mm.lpszNoteText into pBodyStream
  283. IF_FAILEXIT(hr = pBodyStream->Write(mm.lpszNoteText, lstrlen(mm.lpszNoteText), NULL));
  284. // Commit
  285. IF_FAILEXIT(hr = pBodyStream->Commit(STGC_DEFAULT));
  286. // Rewind
  287. IF_FAILEXIT(hr = HrRewindStream(pBodyStream));
  288. // QP
  289. fQP = HrHasEncodedBodyParts(pMsg, 1, &hBody)==S_OK;
  290. // Quote the body text
  291. NewsUtil_QuoteBodyText(pMsg, pBodyStream, &pQuotedStream, TRUE, fQP, pszFrom ? pszFrom : c_szEmpty);
  292. // Free
  293. SafeMemFree(mm.lpszNoteText);
  294. // Dup
  295. IF_FAILEXIT(hr = HrStreamToByte(pQuotedStream, (LPBYTE *)&mm.lpszNoteText, &cchRead));
  296. // Null Term
  297. *(mm.lpszNoteText + cchRead) = '\0';
  298. }
  299. }
  300. // If this is a reply, then we don't include any attachments, otherwise we do.
  301. if (msgtype != MSGTYPE_REPLY)
  302. {
  303. // Get Attachment Count
  304. IF_FAILEXIT(hr = pMsg->GetAttachments(&cAttach, &rghAttach));
  305. // Ar there attachments
  306. if (cAttach)
  307. {
  308. // Get the temp file path so we have a place to store temp files.
  309. GetTempPath(ARRAYSIZE(szTempPath), szTempPath);
  310. // Create the MapiFileDesc array.
  311. IF_FAILEXIT(hr = HrAlloc((LPVOID*) &pFileDesc, sizeof(MapiFileDesc) * cAttach));
  312. // Zero It
  313. ZeroMemory(pFileDesc, sizeof(MapiFileDesc) * cAttach);
  314. // Set Current
  315. pCur = pFileDesc;
  316. // Loop
  317. for (uAttach = 0; uAttach < cAttach; uAttach++)
  318. {
  319. // Get a temp file name
  320. IF_FAILEXIT(hr = HrAlloc((LPVOID *)&(pCur->lpszPathName), sizeof(TCHAR) * MAX_PATH));
  321. // Create temp filename
  322. GetTempFileName(szTempPath, "NAB", 0, pCur->lpszPathName);
  323. // Bind to the body
  324. IF_FAILEXIT(hr = pMsg->BindToObject(rghAttach[uAttach], IID_IMimeBody, (LPVOID *)&pBody));
  325. // Safe It
  326. IF_FAILEXIT(hr = pBody->SaveToFile(IET_INETCSET, pCur->lpszPathName));
  327. // Release
  328. SafeRelease(pBody);
  329. // Get the filename
  330. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, rghAttach[uAttach], STR_ATT_GENFNAME, NOFLAGS, &pwsz)))
  331. {
  332. IF_NULLEXIT(pszFile = PszToANSI(CP_ACP, pwsz));
  333. SafeMemFree(pwsz);
  334. }
  335. // Set up the MAPI attachment list
  336. pCur->ulReserved = 0;
  337. pCur->flFlags = 0;
  338. pCur->nPosition = (ULONG) -1;
  339. pCur->lpszFileName = pszFile;
  340. pCur->lpFileType = NULL;
  341. // Increment
  342. pCur++;
  343. // Don't Free It
  344. pszFile = NULL;
  345. }
  346. mm.nFileCount = cAttach;
  347. mm.lpFiles = pFileDesc;
  348. }
  349. }
  350. // Finally send this off to MAPI for sending. If we're doing a CC, we try not to use UI
  351. IF_FAILEXIT(hr = (HRESULT) pfnMAPISendMail(g_lhSession, (ULONG_PTR)hwnd, &mm, (msgtype == MSGTYPE_CC) ? 0 : MAPI_DIALOG, 0));
  352. exit:
  353. // If we have a file description
  354. if (pFileDesc)
  355. {
  356. // Walk through the attachments
  357. for (uAttach=0; uAttach<cAttach; uAttach++)
  358. {
  359. // Free It
  360. MemFree(pFileDesc[uAttach].lpszFileName);
  361. // If we have a file path
  362. if (pFileDesc[uAttach].lpszPathName)
  363. {
  364. // Delete the file
  365. DeleteFile(pFileDesc[uAttach].lpszPathName);
  366. // Free It
  367. MemFree(pFileDesc[uAttach].lpszPathName);
  368. }
  369. }
  370. // Free It
  371. MemFree(pFileDesc);
  372. }
  373. // Free recips
  374. if (pRecips)
  375. (*pfnMAPIFreeBuffer)((LPVOID)pRecips);
  376. // Cleanup
  377. SafeMemFree(mm.lpRecips);
  378. SafeMemFree(pszAddr);
  379. SafeMemFree(pszDisplay);
  380. SafeMemFree(mm.lpszNoteText);
  381. SafeMemFree(pwsz);
  382. SafeMemFree(pszReply);
  383. SafeMemFree(pszSubject);
  384. SafeMemFree(pszFrom);
  385. SafeMemFree(rghAttach);
  386. SafeMemFree(pszTo);
  387. SafeRelease(pQuotedStream);
  388. SafeRelease(pBodyStream);
  389. SafeRelease(pBody);
  390. // If we logged on to MAPI, we must log off
  391. NewsUtil_FreeMAPI();
  392. return(hr);
  393. }
  394. //
  395. // FUNCTION: NewsUtil_LoadMAPI()
  396. //
  397. // PURPOSE: Takes care of checking to see if Simple MAPI is available, and
  398. // if so loads the library and logs the user on. If successful,
  399. // then the global variable g_hlibMAPI is set to the library for
  400. // MAPI.
  401. //
  402. HRESULT NewsUtil_LoadMAPI(HWND hwnd)
  403. {
  404. LPMAPILOGON pfnMAPILogon;
  405. HRESULT hr = E_FAIL;
  406. // Load mapi32 dll if we haven't already
  407. if (!g_hlibMAPI)
  408. {
  409. // Check to see if Simple MAPI is available
  410. if (1 != GetProfileInt(c_szMailIni, c_szMAPI, 0))
  411. {
  412. // Bug #17561 - Need to tell the user they can't send mail without
  413. // a mail client.
  414. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews),
  415. MAKEINTRESOURCEW(idsErrNoMailInstalled), 0, MB_OK | MB_ICONSTOP);
  416. return (E_FAIL);
  417. }
  418. g_hlibMAPI = (HMODULE) LoadLibrary(c_szMAPIDLL);
  419. if (!g_hlibMAPI)
  420. {
  421. // Bug #17561 - Need to tell the user they can't send mail without
  422. // a mail client.
  423. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews),
  424. MAKEINTRESOURCEW(idsErrNoMailInstalled), 0, MB_OK | MB_ICONSTOP);
  425. return (E_FAIL);
  426. }
  427. }
  428. // Get the entry point for MAPILogon and the other APIs we'll use.
  429. pfnMAPILogon = (LPMAPILOGON) GetProcAddress(g_hlibMAPI, c_szMAPILogon);
  430. if (!pfnMAPILogon)
  431. {
  432. AssertSz(pfnMAPILogon, TEXT("Couldn't find the MAPILogon() API"));
  433. goto error;
  434. }
  435. // Attempt to log on.
  436. // Bug #17558 - Can't used the FAILED() macro to check the success of this
  437. // one, MAPI is not returning HRESULTs, just numbers.
  438. if (SUCCESS_SUCCESS != (hr = pfnMAPILogon(NULL, NULL, NULL, MAPI_LOGON_UI, 0, &g_lhSession)))
  439. {
  440. // AssertSz(FALSE, TEXT("Failed call to MAPILogon()"));
  441. goto error;
  442. }
  443. return (S_OK);
  444. error:
  445. if (g_hlibMAPI)
  446. {
  447. FreeLibrary(g_hlibMAPI);
  448. g_hlibMAPI = 0;
  449. }
  450. g_lhSession = 0;
  451. return (hr);
  452. }
  453. //
  454. // FUNCTION: NewsUtil_FreeMAPI()
  455. //
  456. // PURPOSE: Frees the Simple MAPI library if it was previous library and
  457. // also logs the user off from the current MAPI session.
  458. //
  459. void NewsUtil_FreeMAPI(void)
  460. {
  461. LPMAPILOGOFF pfnMAPILogoff;
  462. if (!g_hlibMAPI)
  463. return;
  464. pfnMAPILogoff = (LPMAPILOGOFF) GetProcAddress(g_hlibMAPI, c_szMAPILogoff);
  465. if (g_lhSession)
  466. pfnMAPILogoff(g_lhSession, NULL, 0, 0);
  467. g_lhSession = 0;
  468. }
  469. //
  470. // FUNCTION: NewsUtil_QuoteBodyText()
  471. //
  472. // PURPOSE: Takes a body text stream (ASCII plain text) and copies the
  473. // text to a separate outbound stream while prepending the
  474. // current quote character (">") to the beginning of each line.
  475. //
  476. // PARAMETERS:
  477. // pMsg - Pointer to the message being replied to. We use this
  478. // to add the "On 1/1/96, B.L. Opie Bailey wrote..."
  479. // pStreamIn - Pointer to the inbound body stream to quote.
  480. // ppStreamOut - Pointer to where the new quoted stream will return.
  481. // fInsertDesc - TRUE if we should insert the "On 1/1/96 ..." line.
  482. // fQP - we now pass a flag to say if it's QP or not as there's no
  483. // function on the message object
  484. //
  485. // RETURN VALUE:
  486. // Returns an HRESULT signifying success or failure.
  487. //
  488. const DWORD c_cBufferSize = 1024;
  489. HRESULT NewsUtil_QuoteBodyText(LPMIMEMESSAGE pMsg, LPSTREAM pStreamIn,
  490. LPSTREAM* ppStreamOut, BOOL fInsertDesc, BOOL fQP, LPCSTR pszFrom)
  491. {
  492. HRESULT hr = S_OK;
  493. ULONG cbRead;
  494. LPTSTR pch;
  495. TCHAR szQuoteChar;
  496. LPSTR lpszMsgId=0;
  497. szQuoteChar = (TCHAR)DwGetOption(OPT_NEWSINDENT);
  498. // Validate the inbound stream.
  499. if (!pStreamIn)
  500. {
  501. AssertSz(pStreamIn, TEXT("NewsUtil_QuoteBodyText - Need an inbound stream to process."));
  502. return (E_INVALIDARG);
  503. }
  504. // Create our outbound stream.
  505. if (FAILED(MimeOleCreateVirtualStream(ppStreamOut)))
  506. {
  507. AssertSz(FALSE, TEXT("NewsUtil_QuoteBodyText - Failed to allocate memory."));
  508. return (E_OUTOFMEMORY);
  509. }
  510. // Create a buffer to read into and parse etc.
  511. LPTSTR pszBuffer;
  512. if (!MemAlloc((LPVOID*) &pszBuffer, c_cBufferSize * sizeof(TCHAR)))
  513. {
  514. (*ppStreamOut)->Release();
  515. AssertSz(FALSE, TEXT("NewsUtil_QuoteBodyText - Failed to allocate memory."));
  516. return (E_OUTOFMEMORY);
  517. }
  518. ZeroMemory(pszBuffer, c_cBufferSize * sizeof(TCHAR));
  519. MimeOleGetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &lpszMsgId);
  520. if (lpszMsgId == NULL)
  521. lpszMsgId = (LPSTR)c_szEmpty;
  522. if (fQP)
  523. {
  524. // If the text has some quoted printable stuff in it, then we don't want
  525. // to introduce hard line breaks. Instead we just prefix the stream with
  526. // the normal desc stuff and end it with a suitable line.
  527. // Add the quote line.
  528. LPTSTR pszStringRes;
  529. int ids = 0;
  530. if (fInsertDesc)
  531. {
  532. pszStringRes = AthLoadString(idsReplyTextPrefix, 0, 0);
  533. wnsprintf(pszBuffer, c_cBufferSize, pszStringRes, pszFrom, lpszMsgId);
  534. AthFreeString(pszStringRes);
  535. (*ppStreamOut)->Write((LPVOID) g_szCRLF2, lstrlen(g_szCRLF2), NULL);
  536. (*ppStreamOut)->Write((LPVOID) pszBuffer, lstrlen(pszBuffer), NULL);
  537. (*ppStreamOut)->Write((LPVOID) g_szCRLF, lstrlen(g_szCRLF), NULL);
  538. }
  539. while (TRUE)
  540. {
  541. // Read a buffer from the input and write it to the output.
  542. hr = pStreamIn->Read((LPVOID) pszBuffer, c_cBufferSize - 2, &cbRead);
  543. if (FAILED(hr))
  544. goto exit;
  545. if (cbRead == 0)
  546. break;
  547. (*ppStreamOut)->Write((LPVOID) pszBuffer, cbRead, NULL);
  548. }
  549. // Write the trailing comment.
  550. pszStringRes = AthLoadString(idsReplyTextAppend, 0, 0);
  551. (*ppStreamOut)->Write((LPVOID) pszStringRes, lstrlen(pszStringRes), NULL);
  552. AthFreeString(pszStringRes);
  553. }
  554. else
  555. {
  556. if (fInsertDesc)
  557. {
  558. // Add the quote line.
  559. LPTSTR pszStringRes;
  560. int ids = 0;
  561. pszStringRes = AthLoadString(idsReplyTextPrefix, 0, 0);
  562. wnsprintf(pszBuffer, c_cBufferSize, pszStringRes, pszFrom, lpszMsgId);
  563. AthFreeString(pszStringRes);
  564. (*ppStreamOut)->Write((LPVOID) g_szCRLF2, lstrlen(g_szCRLF2), NULL);
  565. (*ppStreamOut)->Write((LPVOID) pszBuffer, lstrlen(pszBuffer), NULL);
  566. (*ppStreamOut)->Write((LPVOID) g_szCRLF, lstrlen(g_szCRLF), NULL);
  567. }
  568. // Write the first quote char to the new stream.
  569. // Bug #26297 - Still go through this bs even if no quote char is necessary
  570. // to make sure we get the attribution line right.
  571. if (szQuoteChar != INDENTCHAR_NONE)
  572. {
  573. (*ppStreamOut)->Write((const LPVOID) &szQuoteChar,
  574. sizeof(TCHAR), NULL);
  575. (*ppStreamOut)->Write((const LPVOID) g_szSpace, sizeof(TCHAR), NULL);
  576. }
  577. // Now start the reading and parsing.
  578. // NOTE - Right now all we're doing is adding a quote char to the beginning
  579. // of each line. We're not trying to wrap lines or re-wrap previously
  580. // quoted areas. - SteveSer
  581. while (TRUE)
  582. {
  583. hr = pStreamIn->Read((LPVOID) pszBuffer, c_cBufferSize - 2, &cbRead);
  584. if (FAILED(hr))
  585. goto exit;
  586. if (cbRead == 0)
  587. break;
  588. pch = pszBuffer;
  589. // Make sure the buffer is NULL terminated
  590. *(pch + cbRead) = 0;
  591. // Now run through the stream. Whenever we find a line break, we
  592. // insert a quote char after the line break.
  593. while (*pch)
  594. {
  595. (*ppStreamOut)->Write((const LPVOID) pch,
  596. (ULONG)((IsDBCSLeadByte(*pch) ? 2 * sizeof(TCHAR) : sizeof(TCHAR))),
  597. NULL);
  598. if (*pch == *g_szNewline)
  599. {
  600. // Bug #26297 - Still go through this bs even if no quote char is necessary
  601. // to make sure we get the attribution line right.
  602. if (szQuoteChar != INDENTCHAR_NONE)
  603. {
  604. (*ppStreamOut)->Write((const LPVOID) &szQuoteChar,
  605. sizeof(TCHAR), NULL);
  606. (*ppStreamOut)->Write((const LPVOID) g_szSpace, sizeof(TCHAR),
  607. NULL);
  608. }
  609. }
  610. pch = CharNext(pch);
  611. // Do some checking to see if we're at the end of a buffer.
  612. if (IsDBCSLeadByte(*(pch)) && (0 == *(pch + 1)))
  613. {
  614. // Here's a little special case. If we have one byte left in
  615. // the buffer, and that byte happens to be the first byte in
  616. // a DBCS character, we need to write that byte now, then move
  617. // the pointer to the end of the buffer so the next character
  618. // get's read off the next stream OK.
  619. (*ppStreamOut)->Write((const LPVOID) pch, sizeof(TCHAR), NULL);
  620. pch++;
  621. }
  622. }
  623. }
  624. }
  625. exit:
  626. if (pszBuffer)
  627. MemFree(pszBuffer);
  628. if (lpszMsgId != c_szEmpty)
  629. SafeMimeOleFree(lpszMsgId);
  630. return (hr);
  631. }