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.

10472 lines
310 KiB

  1. // --------------------------------------------------------------------------------
  2. // BookTree.cpp
  3. // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  4. // Steven J. Bailey
  5. // --------------------------------------------------------------------------------
  6. #include "pch.hxx"
  7. #include <stddef.h>
  8. #include "dllmain.h"
  9. #include "booktree.h"
  10. #include "stmlock.h"
  11. #include "ibdylock.h"
  12. #include "resource.h"
  13. #include "vstream.h"
  14. #include "ixputil.h"
  15. #include "olealloc.h"
  16. #include "smime.h"
  17. #include "objheap.h"
  18. #include "internat.h"
  19. #include "icoint.h"
  20. #include "ibdystm.h"
  21. #include "symcache.h"
  22. #include "urlmon.h"
  23. #include "mhtmlurl.h"
  24. #include "shlwapi.h"
  25. #include "shlwapip.h"
  26. #include "inetstm.h"
  27. #include "imnxport.h"
  28. #include "bookbody.h"
  29. #include "mimeapi.h"
  30. #include "strconst.h"
  31. #include "bindstm.h"
  32. #include "enriched.h"
  33. #include "webpage.h"
  34. #include "demand.h"
  35. //#define TRACEPARSE 1
  36. // --------------------------------------------------------------------------------
  37. // _IsMultiPart
  38. // --------------------------------------------------------------------------------
  39. inline BOOL _IsMultiPart(LPTREENODEINFO pNode)
  40. {
  41. return pNode->pContainer->IsContentType(STR_CNT_MULTIPART, NULL) == S_OK;
  42. }
  43. // --------------------------------------------------------------------------------
  44. // BINDASSERTARGS
  45. // --------------------------------------------------------------------------------
  46. #define BINDASSERTARGS(_bindstate, _fBoundary) \
  47. Assert(m_pBindNode && m_pBindNode->pBody && m_pBindNode->pContainer && _bindstate == m_pBindNode->bindstate && (FALSE == _fBoundary || ISVALIDSTRINGA(&m_pBindNode->rBoundary)))
  48. // --------------------------------------------------------------------------------
  49. // Array of Bind Parsing States to Functions
  50. // --------------------------------------------------------------------------------
  51. const PFNBINDPARSER CMessageTree::m_rgBindStates[BINDSTATE_LAST] = {
  52. NULL, // BINDSTATE_COMPLETE
  53. (PFNBINDPARSER)CMessageTree::_HrBindParsingHeader, // BINDSTATE_PARSING_HEADER
  54. (PFNBINDPARSER)CMessageTree::_HrBindFindingMimeFirst, // BINDSTATE_FINDING_MIMEFIRST
  55. (PFNBINDPARSER)CMessageTree::_HrBindFindingMimeNext, // BINDSTATE_FINDING_MIMENEXT
  56. (PFNBINDPARSER)CMessageTree::_HrBindFindingUuencodeBegin, // BINDSTATE_FINDING_UUBEGIN
  57. (PFNBINDPARSER)CMessageTree::_HrBindFindingUuencodeEnd, // BINDSTATE_FINDING_UUEND
  58. (PFNBINDPARSER)CMessageTree::_HrBindRfc1154, // BINDSTATE_PARSING_RFC1154
  59. };
  60. // --------------------------------------------------------------------------------
  61. // Used in IMimeMessageTree::ToMultipart
  62. // --------------------------------------------------------------------------------
  63. static LPCSTR g_rgszToMultipart[] = {
  64. PIDTOSTR(PID_HDR_CNTTYPE),
  65. PIDTOSTR(PID_HDR_CNTDESC),
  66. PIDTOSTR(PID_HDR_CNTDISP),
  67. PIDTOSTR(PID_HDR_CNTXFER),
  68. PIDTOSTR(PID_HDR_CNTID),
  69. PIDTOSTR(PID_HDR_CNTBASE),
  70. PIDTOSTR(PID_HDR_CNTLOC)
  71. };
  72. // --------------------------------------------------------------------------------
  73. // Used in IMimeMessage::AttachObject IID_IMimeBody
  74. // --------------------------------------------------------------------------------
  75. static LPCSTR g_rgszAttachBody[] = {
  76. PIDTOSTR(PID_HDR_CNTTYPE),
  77. PIDTOSTR(PID_HDR_CNTDESC),
  78. PIDTOSTR(PID_HDR_CNTDISP),
  79. PIDTOSTR(PID_HDR_CNTXFER),
  80. PIDTOSTR(PID_HDR_CNTID),
  81. PIDTOSTR(PID_HDR_CNTBASE),
  82. PIDTOSTR(PID_HDR_CNTLOC)
  83. };
  84. static const WEBPAGEOPTIONS g_rDefWebPageOpt = {
  85. sizeof(WEBPAGEOPTIONS), // cbsize
  86. WPF_NOMETACHARSET | WPF_HTML | WPF_AUTOINLINE, // dwFlags
  87. 3000, // dwDelay
  88. NULL // wchQuote
  89. };
  90. // --------------------------------------------------------------------------------
  91. // Default Tree Options
  92. // --------------------------------------------------------------------------------
  93. static const TREEOPTIONS g_rDefTreeOptions = {
  94. DEF_CLEANUP_TREE_ON_SAVE, // OID_CLEANUP_TREE_ON_SAVE
  95. DEF_HIDE_TNEF_ATTACHMENTS, // OID_HIDE_TNEF_ATTACHMENTS
  96. DEF_ALLOW_8BIT_HEADER, // OID_ALLOW_8BIT_HEADER
  97. DEF_GENERATE_MESSAGE_ID, // OID_GENERATE_MESSAGE_ID
  98. DEF_WRAP_BODY_TEXT, // OID_WRAP_BODY_TEXT
  99. DEF_CBMAX_HEADER_LINE, // OID_CBMAX_HEADER_LINE
  100. DEF_CBMAX_BODY_LINE, // OID_CBMAX_BODY_LINE
  101. SAVE_RFC1521, // OID_SAVE_FORMAT
  102. NULL, // hCharset
  103. CSET_APPLY_UNTAGGED, // csetapply
  104. DEF_TRANSMIT_TEXT_ENCODING, // OID_TRANSMIT_TEXT_ENCODING
  105. DEF_XMIT_PLAIN_TEXT_ENCODING, // OID_XMIT_PLAIN_TEXT_ENCODING
  106. DEF_XMIT_HTML_TEXT_ENCODING, // OID_XMIT_HTML_TEXT_ENCODING
  107. 0, // OID_SECURITY_ENCODE_FLAGS
  108. DEF_HEADER_RELOAD_TYPE_TREE, // OID_HEADER_REALOD_TYPE
  109. DEF_CAN_INLINE_TEXT_BODIES, // OID_CAN_INLINE_TEXT_BODIES
  110. DEF_SHOW_MACBINARY, // OID_SHOW_MACBINARY
  111. DEF_SAVEBODY_KEEPBOUNDARY, // OID_SAVEBODY_KEEPBOUNDARY
  112. FALSE, // OID_LOAD_USE_BIND_FILE
  113. DEF_HANDSOFF_ONSAVE, // OID_HANDSOFF_ONSAVE
  114. DEF_SUPPORT_EXTERNAL_BODY, // OID_SUPPORT_EXTERNAL_BODY
  115. DEF_DECODE_RFC1154 // OID_DECODE_RFC1154
  116. };
  117. extern BOOL FIsMsasn1Loaded();
  118. #ifdef DEBUG
  119. // --------------------------------------------------------------------------------
  120. // These booleans determine if the tree is dumped to the output window
  121. // --------------------------------------------------------------------------------
  122. static BOOL s_fWriteMessageDump = 0;
  123. static BOOL s_fKeepBoundary = 0;
  124. static BOOL s_fDumpMessage = 0;
  125. static BOOL s_fWriteXClient = 0;
  126. // --------------------------------------------------------------------------------
  127. // This writes the message X-Mailer or X-Newsreader
  128. // --------------------------------------------------------------------------------
  129. void CMessageTree::DebugWriteXClient()
  130. {
  131. if (s_fWriteXClient)
  132. {
  133. LPSTR pszX;
  134. if (SUCCEEDED(m_pRootNode->pContainer->GetProp(SYM_HDR_XMAILER, &pszX)) && pszX)
  135. {
  136. DebugTrace("X-Mailer: %s\n", pszX);
  137. MemFree(pszX);
  138. }
  139. else if (SUCCEEDED(m_pRootNode->pContainer->GetProp(SYM_HDR_XNEWSRDR, &pszX)) && pszX)
  140. {
  141. DebugTrace("X-Newsreader: %s\n", pszX);
  142. MemFree(pszX);
  143. }
  144. }
  145. }
  146. // --------------------------------------------------------------------------------
  147. // This dumps the current tree to debug output window
  148. // --------------------------------------------------------------------------------
  149. void CMessageTree::DebugDumpTree(LPSTR pszfunc, BOOL fWrite)
  150. {
  151. if (TRUE == fWrite)
  152. {
  153. DebugTrace("---------------------------------------------------------------------------\n");
  154. DebugTrace("CMessageTree::%s\n", pszfunc);
  155. }
  156. DebugDumpTree(m_pRootNode, 0, fWrite);
  157. }
  158. // --------------------------------------------------------------------------------
  159. // This macros writes _pstm to a file
  160. // --------------------------------------------------------------------------------
  161. #define DEBUGMESSAGEOUT "c:\\lastmsg.txt"
  162. void DebugWriteMsg(LPSTREAM pstm)
  163. {
  164. if (TRUE == s_fDumpMessage)
  165. {
  166. LPSTREAM pstmFile;
  167. if (SUCCEEDED(OpenFileStream(DEBUGMESSAGEOUT, CREATE_ALWAYS, GENERIC_WRITE, &pstmFile)))
  168. {
  169. HrRewindStream(pstm);
  170. HrCopyStream(pstm, pstmFile, NULL);
  171. pstmFile->Commit(STGC_DEFAULT);
  172. pstmFile->Release();
  173. }
  174. }
  175. }
  176. #else // DEBUG
  177. #define DebugDumpTree 1 ? (void)0 : (void)
  178. #define DebugWriteMsg 1 ? (void)0 : (void)
  179. #define DebugAssertNotLinked 1 ? (void)0 : (void)
  180. #define DebugIsRootContainer 1 ? (void)0 : (void)
  181. #endif // DEBUG
  182. // --------------------------------------------------------------------------------
  183. // WebBookContentTree_CreateInstance
  184. // --------------------------------------------------------------------------------
  185. HRESULT WebBookContentTree_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppUnknown)
  186. {
  187. // Invalid Arg
  188. Assert(ppUnknown);
  189. // Initialize
  190. *ppUnknown = NULL;
  191. // Create me
  192. CMessageTree *pNew = new CMessageTree(pUnkOuter);
  193. if (NULL == pNew)
  194. return TrapError(E_OUTOFMEMORY);
  195. // Return the Innter
  196. *ppUnknown = pNew->GetInner();
  197. // Done
  198. return S_OK;
  199. }
  200. // --------------------------------------------------------------------------------
  201. // Text Type Information Array
  202. // --------------------------------------------------------------------------------
  203. static const TEXTTYPEINFO g_rgTextInfo[] = {
  204. { TXT_PLAIN, STR_SUB_PLAIN, 0 },
  205. { TXT_HTML, STR_SUB_HTML, 5 }
  206. };
  207. // --------------------------------------------------------------------------------
  208. // CMessageTree::CMessageTree
  209. // --------------------------------------------------------------------------------
  210. CMessageTree::CMessageTree(IUnknown *pUnkOuter) : CPrivateUnknown(pUnkOuter)
  211. {
  212. DllAddRef();
  213. m_dwState = 0;
  214. m_pCallback = NULL;
  215. m_pBindNode = NULL;
  216. m_pRootStm = NULL;
  217. m_pInternet = NULL;
  218. m_pStmBind = NULL;
  219. m_pBinding = NULL;
  220. m_pMoniker = NULL;
  221. m_pBC = NULL;
  222. m_cbMessage = 0;
  223. m_pStmLock = NULL;
  224. m_pRootNode = NULL;
  225. m_pwszFilePath = NULL;
  226. m_hrBind = S_OK;
  227. m_pPending = NULL;
  228. m_pComplete = NULL;
  229. m_wTag = LOWORD(GetTickCount());
  230. m_pActiveUrl = NULL;
  231. m_pWebPage = NULL;
  232. m_fApplySaveSecurity = FALSE;
  233. m_pBT1154 = NULL;
  234. while(m_wTag == 0 || m_wTag == 0xffff) m_wTag++;
  235. ZeroMemory(&m_rRootUrl, sizeof(PROPSTRINGA));
  236. ZeroMemory(&m_rTree, sizeof(TREENODETABLE));
  237. CopyMemory(&m_rWebPageOpt, &g_rDefWebPageOpt, sizeof(WEBPAGEOPTIONS));
  238. CopyMemory(&m_rOptions, &g_rDefTreeOptions, sizeof(TREEOPTIONS));
  239. InitializeCriticalSection(&m_cs);
  240. }
  241. // --------------------------------------------------------------------------------
  242. // CMessageTree::~CMessageTree
  243. // --------------------------------------------------------------------------------
  244. CMessageTree::~CMessageTree(void)
  245. {
  246. if(m_pActiveUrl && g_pUrlCache)
  247. {
  248. //Bug #101348 - free CActiveUrl leaked to the CMimeActiveUrlCache
  249. g_pUrlCache->RemoveUrl(m_pActiveUrl);
  250. m_pActiveUrl = NULL;
  251. }
  252. // Reset the Object
  253. _ResetObject(BOOKTREE_RESET_DECONSTRUCT);
  254. SafeRelease(m_pStmLock);
  255. // Kill the Critical Section
  256. DeleteCriticalSection(&m_cs);
  257. // Releaes the Dll
  258. DllRelease();
  259. }
  260. // --------------------------------------------------------------------------------
  261. // CMessageTree::PrivateQueryInterface
  262. // --------------------------------------------------------------------------------
  263. HRESULT CMessageTree::PrivateQueryInterface(REFIID riid, LPVOID *ppv)
  264. {
  265. // check params
  266. if (ppv == NULL)
  267. return TrapError(E_INVALIDARG);
  268. // Interface Map
  269. if (IID_IPersist == riid)
  270. *ppv = (IPersist *)(IPersistStreamInit *)this;
  271. else if (IID_IPersistStreamInit == riid)
  272. *ppv = (IPersistStreamInit *)this;
  273. else if (IID_IMimeMessage == riid)
  274. *ppv = (IMimeMessage *)this;
  275. else if (IID_IMimeMessageW == riid)
  276. *ppv = (IMimeMessageW *)this;
  277. else if (IID_IMimeMessageTree == riid)
  278. *ppv = (IMimeMessageTree *)this;
  279. else if (IID_IDataObject == riid)
  280. *ppv = (IDataObject *)this;
  281. else if (IID_IPersistFile == riid)
  282. *ppv = (IPersistFile *)this;
  283. else if (IID_IBindStatusCallback == riid)
  284. *ppv = (IBindStatusCallback *)this;
  285. else if (IID_IServiceProvider == riid)
  286. *ppv = (IServiceProvider *)this;
  287. else if (IID_CMessageTree == riid)
  288. *ppv = (CMessageTree *)this;
  289. else if (IID_IPersistMoniker == riid)
  290. *ppv = (IPersistMoniker *)this;
  291. #ifdef SMIME_V3
  292. else if (IID_IMimeSecurity2 == riid)
  293. *ppv = (IMimeSecurity2 *) this;
  294. #endif // SMIME_V3
  295. // E_NOINTERFACE
  296. else
  297. {
  298. *ppv = NULL;
  299. return TrapError(E_NOINTERFACE);
  300. }
  301. // AddRef It
  302. ((IUnknown *)*ppv)->AddRef();
  303. // Done
  304. return S_OK;
  305. }
  306. #ifdef DEBUG
  307. // --------------------------------------------------------------------------------
  308. // CMessageTree::DebugDumpTree
  309. // --------------------------------------------------------------------------------
  310. void CMessageTree::DebugDumpTree(LPTREENODEINFO pParent, ULONG ulLevel, BOOL fVerbose)
  311. {
  312. // Locals
  313. LPSTR pszPriType=NULL,
  314. pszEncoding=NULL,
  315. pszFileName=NULL;
  316. LPTREENODEINFO pChild,
  317. pPrev,
  318. pNext;
  319. ULONG cChildren;
  320. LONG lRendered=-1;
  321. PROPVARIANT rVariant;
  322. // Get Content Type
  323. if (fVerbose)
  324. {
  325. Assert(pParent->pContainer->GetProp(SYM_HDR_CNTTYPE, &pszPriType) == S_OK);
  326. Assert(pParent->pContainer->GetProp(SYM_HDR_CNTXFER, &pszEncoding) == S_OK);
  327. Assert(pParent->pContainer->GetProp(SYM_ATT_GENFNAME, &pszFileName) == S_OK);
  328. rVariant.vt = VT_UI4;
  329. if (SUCCEEDED(pParent->pContainer->GetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)))
  330. lRendered = (LONG)rVariant.ulVal;
  331. for (ULONG i=0; i<ulLevel; i++)
  332. DebugTrace(" ");
  333. DebugTrace("%0x == > %s (%s - %s) Rendered: %ld\n", pParent->hBody, pszPriType, pszFileName, pszEncoding, lRendered);
  334. }
  335. // IsMultiPart
  336. if (_IsMultiPart(pParent))
  337. {
  338. // Count Children
  339. cChildren = 0;
  340. pPrev = NULL;
  341. // Increment the level
  342. ulLevel++;
  343. // Loop Chilren
  344. for (pChild=pParent->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  345. {
  346. // Verify Handle
  347. Assert(_FIsValidHandle(pChild->hBody));
  348. // Check Parent
  349. AssertSz(pChild->pParent == pParent, "Parent is wrong");
  350. // Check pParent Child Head
  351. if (NULL == pChild->pPrev)
  352. Assert(pParent->pChildHead == pChild);
  353. // Check pParent Child Tail
  354. if (NULL == pChild->pNext)
  355. Assert(pParent->pChildTail == pChild);
  356. // Valid Prev
  357. Assert(pChild->pPrev == pPrev);
  358. // Dump This Child
  359. DebugDumpTree(pChild, ulLevel, fVerbose);
  360. // Count Children
  361. cChildren++;
  362. // Set Previous
  363. pPrev = pChild;
  364. }
  365. // Verify Children
  366. Assert(pParent->cChildren == cChildren);
  367. }
  368. // Cleanup
  369. SafeMemFree(pszPriType);
  370. SafeMemFree(pszEncoding);
  371. SafeMemFree(pszFileName);
  372. }
  373. // --------------------------------------------------------------------------------
  374. // CMessageTree::DebugAssertNotLinked
  375. // This insures that pNode is not referenced by the tree
  376. // --------------------------------------------------------------------------------
  377. void CMessageTree::DebugAssertNotLinked(LPTREENODEINFO pNode)
  378. {
  379. // Better not be the root
  380. Assert(m_pRootNode != pNode);
  381. // Loop through bodies
  382. for (ULONG i=0; i<m_rTree.cNodes; i++)
  383. {
  384. // Readability
  385. if (NULL == m_rTree.prgpNode[i])
  386. continue;
  387. // Check if linked to pBody
  388. Assert(m_rTree.prgpNode[i]->pParent != pNode);
  389. Assert(m_rTree.prgpNode[i]->pChildHead != pNode);
  390. Assert(m_rTree.prgpNode[i]->pChildTail != pNode);
  391. Assert(m_rTree.prgpNode[i]->pNext != pNode);
  392. Assert(m_rTree.prgpNode[i]->pPrev != pNode);
  393. }
  394. }
  395. #endif // DEBUG
  396. // --------------------------------------------------------------------------------
  397. // CMessageTree::IsState
  398. // --------------------------------------------------------------------------------
  399. HRESULT CMessageTree::IsState(DWORD dwState)
  400. {
  401. EnterCriticalSection(&m_cs);
  402. HRESULT hr = (ISFLAGSET(m_dwState, dwState)) ? S_OK : S_FALSE;
  403. LeaveCriticalSection(&m_cs);
  404. return hr;
  405. }
  406. // --------------------------------------------------------------------------------
  407. // CMessageTree::GetRootMoniker (This Function will die soon)
  408. // --------------------------------------------------------------------------------
  409. STDMETHODIMP CMessageTree::GetRootMoniker(LPMONIKER *ppmk)
  410. {
  411. Assert(FALSE);
  412. return E_FAIL;
  413. }
  414. // --------------------------------------------------------------------------------
  415. // CMessageTree::CreateWebPage
  416. // --------------------------------------------------------------------------------
  417. STDMETHODIMP CMessageTree::CreateWebPage(IStream *pStmRoot, LPWEBPAGEOPTIONS pOptions,
  418. IMimeMessageCallback *pCallback, IMoniker **ppMoniker)
  419. {
  420. // Locals
  421. HRESULT hr=S_OK;
  422. LPWSTR pwszRootUrl=NULL;
  423. // Invalid Arg
  424. if (NULL == ppMoniker)
  425. return TrapError(E_INVALIDARG);
  426. // If an options structure was passed in, is it the right size ?
  427. if (pOptions && sizeof(WEBPAGEOPTIONS) != pOptions->cbSize)
  428. return TrapError(E_INVALIDARG);
  429. // Init
  430. *ppMoniker = NULL;
  431. // Thread Safety
  432. EnterCriticalSection(&m_cs);
  433. // Release Current BindRoot Stream
  434. SafeRelease(m_pRootStm);
  435. SafeRelease(m_pWebPage);
  436. // Null pStream is allowed
  437. if (pStmRoot)
  438. {
  439. // Save Root Stream
  440. m_pRootStm = pStmRoot;
  441. m_pRootStm->AddRef();
  442. }
  443. // Otherwise, we can inline text bodies...
  444. else
  445. {
  446. // Change Option
  447. m_rOptions.fCanInlineText = TRUE;
  448. }
  449. // Release current webpage callback
  450. SafeRelease(m_pCallback);
  451. // Setup the new webpage callback
  452. if (pCallback)
  453. {
  454. m_pCallback = pCallback;
  455. m_pCallback->AddRef();
  456. }
  457. // Save WebPageOptions
  458. if (pOptions)
  459. CopyMemory(&m_rWebPageOpt, pOptions, sizeof(WEBPAGEOPTIONS));
  460. else
  461. CopyMemory(&m_rWebPageOpt, &g_rDefWebPageOpt, sizeof(WEBPAGEOPTIONS));
  462. // Already have a Base Url from IMimeMessageTree::IPersitMoniker::Load
  463. if (NULL == m_rRootUrl.pszVal)
  464. {
  465. // Locals
  466. CHAR szRootUrl[CCHMAX_MID + 8];
  467. // Build MessageID
  468. m_rRootUrl.cchVal = wnsprintf(szRootUrl, ARRAYSIZE(szRootUrl), "mhtml:mid://%08d/", DwCounterNext());
  469. // Allocate
  470. CHECKALLOC(m_rRootUrl.pszVal = (LPSTR)g_pMalloc->Alloc(m_rRootUrl.cchVal + 1));
  471. // Copy memory
  472. CopyMemory((LPBYTE)m_rRootUrl.pszVal, (LPBYTE)szRootUrl, m_rRootUrl.cchVal + 1);
  473. // Register this object in the list of active objects
  474. Assert(g_pUrlCache);
  475. CHECKHR(hr = g_pUrlCache->RegisterActiveObject(m_rRootUrl.pszVal, this));
  476. // We shuould have a m_pActiveUrl now
  477. Assert(m_pActiveUrl != NULL);
  478. // Set some flags on the activeurl
  479. m_pActiveUrl->SetFlag(ACTIVEURL_ISFAKEURL);
  480. // Is valid
  481. Assert(ISVALIDSTRINGA(&m_rRootUrl));
  482. }
  483. // Convert Url to Wide
  484. CHECKALLOC(pwszRootUrl = PszToUnicode(CP_ACP, m_rRootUrl.pszVal));
  485. // Create a dummy moniker
  486. CHECKHR(hr = CreateURLMoniker(NULL, pwszRootUrl, ppMoniker));
  487. exit:
  488. // Cleanup
  489. SafeMemFree(pwszRootUrl);
  490. // Thread Safety
  491. LeaveCriticalSection(&m_cs);
  492. // Done
  493. return hr;
  494. }
  495. // ---------------------------------------------------------------------------
  496. // CMessageTree::SetActiveUrl
  497. // ---------------------------------------------------------------------------
  498. HRESULT CMessageTree::SetActiveUrl(CActiveUrl *pActiveUrl)
  499. {
  500. // Thread Safety
  501. EnterCriticalSection(&m_cs);
  502. // NULL ?
  503. if (NULL == pActiveUrl)
  504. {
  505. Assert(m_pActiveUrl);
  506. SafeRelease(m_pActiveUrl);
  507. }
  508. else
  509. {
  510. Assert(NULL == m_pActiveUrl);
  511. m_pActiveUrl = pActiveUrl;
  512. m_pActiveUrl->AddRef();
  513. }
  514. // Thread Safety
  515. LeaveCriticalSection(&m_cs);
  516. // Done
  517. return S_OK;
  518. }
  519. // --------------------------------------------------------------------------------
  520. // CMessageTree::CompareRootUrl
  521. // --------------------------------------------------------------------------------
  522. HRESULT CMessageTree::CompareRootUrl(LPCSTR pszUrl)
  523. {
  524. // Locals
  525. HRESULT hr=S_OK;
  526. // Invalid ARg
  527. Assert(pszUrl);
  528. // Thread Safety
  529. EnterCriticalSection(&m_cs);
  530. // No Root Url
  531. if (NULL == m_rRootUrl.pszVal)
  532. {
  533. Assert(FALSE);
  534. hr = S_FALSE;
  535. goto exit;
  536. }
  537. // This url must start with mhtml:
  538. Assert(StrCmpNI(m_rRootUrl.pszVal, "mhtml:", 6) == 0);
  539. // Compare
  540. hr = MimeOleCompareUrl(m_rRootUrl.pszVal + 6, FALSE, pszUrl, FALSE);
  541. exit:
  542. // Thread Safety
  543. LeaveCriticalSection(&m_cs);
  544. // Done
  545. return hr;
  546. }
  547. // ----------------------------------------------------------------------------
  548. // CMessageTree::Load
  549. // ----------------------------------------------------------------------------
  550. STDMETHODIMP CMessageTree::Load(BOOL fFullyAvailable, IMoniker *pMoniker, IBindCtx *pBindCtx, DWORD grfMode)
  551. {
  552. // Locals
  553. HRESULT hr=S_OK;
  554. IStream *pStream=NULL;
  555. ULONG cb;
  556. LPOLESTR pwszUrl=NULL;
  557. LPSTR pszUrl=NULL;
  558. ULONG cchUrl;
  559. BOOL fReSynchronize;
  560. // Invalid Arg
  561. if (NULL == pMoniker)
  562. return TrapError(E_INVALIDARG);
  563. // Thread Safety
  564. EnterCriticalSection(&m_cs);
  565. // Remember if TREESTATE_RESYNCHRONIZE is set...
  566. fReSynchronize = ISFLAGSET(m_dwState, TREESTATE_RESYNCHRONIZE);
  567. // InitNew
  568. CHECKHR(hr = _HrLoadInitNew());
  569. // Reset pragma no cache
  570. if (fReSynchronize)
  571. {
  572. // Reset
  573. FLAGSET(m_dwState, TREESTATE_RESYNCHRONIZE);
  574. }
  575. // We better have a tree
  576. Assert(NULL == m_pMoniker);
  577. // Assume the Moniker
  578. m_pMoniker = pMoniker;
  579. m_pMoniker->AddRef();
  580. // No Bind Context was given ?
  581. if (NULL == pBindCtx)
  582. {
  583. // Create me a BindContext
  584. CHECKHR(hr = CreateBindCtx(0, &pBindCtx));
  585. }
  586. // Otherwise, assume the Bind Context Passed Into me
  587. else
  588. pBindCtx->AddRef();
  589. Assert (m_pBC==NULL);
  590. m_pBC = pBindCtx; // released in OnStopBinding
  591. // Get the Url from this dude
  592. CHECKHR(hr = m_pMoniker->GetDisplayName(NULL, NULL, &pwszUrl));
  593. // Save as Root Url
  594. CHECKALLOC(pszUrl = PszToANSI(CP_ACP, pwszUrl));
  595. // Unescape inplace
  596. CHECKHR(hr = UrlUnescapeA(pszUrl, NULL, NULL, URL_UNESCAPE_INPLACE));
  597. // Raid-2508: Comment tag ( <! comment> ) doesn't work in mhtml
  598. ReplaceChars(pszUrl, '!', '_');
  599. // Better not have mhtml: on it
  600. Assert(StrCmpNI(pszUrl, "mhtml:", 6) != 0);
  601. // Get the length of pszUrl
  602. cchUrl = lstrlen(pszUrl);
  603. // Create "mhtml://" + pszUrl + '/' + '\0'
  604. DWORD cchSize = (10 + cchUrl);
  605. CHECKALLOC(m_rRootUrl.pszVal = (LPSTR)g_pMalloc->Alloc(cchSize));
  606. // Format the string
  607. SideAssert(wnsprintf(m_rRootUrl.pszVal, cchSize, "%s%s", c_szMHTMLColon, pszUrl) <= (LONG)(10 + cchUrl));
  608. // Register my bind status callback in the bind context
  609. CHECKHR(hr = RegisterBindStatusCallback(pBindCtx, (IBindStatusCallback *)this, NULL, 0));
  610. // Assume the Bind has Finished
  611. FLAGCLEAR(m_dwState, TREESTATE_BINDDONE);
  612. // I only support share deny none
  613. FLAGSET(m_dwState, TREESTATE_BINDUSEFILE);
  614. // I was loaded by a moniker
  615. FLAGSET(m_dwState, TREESTATE_LOADEDBYMONIKER);
  616. // This better be synchronous
  617. hr = m_pMoniker->BindToStorage(pBindCtx, NULL, IID_IStream, (LPVOID *)&pStream);
  618. if (FAILED(hr) || MK_S_ASYNCHRONOUS == hr)
  619. {
  620. TrapError(hr);
  621. goto exit;
  622. }
  623. exit:
  624. // Cleanup
  625. SafeRelease(pStream);
  626. SafeMemFree(pwszUrl);
  627. SafeMemFree(pszUrl);
  628. // Thread Safety
  629. LeaveCriticalSection(&m_cs);
  630. // Done
  631. return hr;
  632. }
  633. // ----------------------------------------------------------------------------
  634. // CMessageTree::GetCurMoniker
  635. // ----------------------------------------------------------------------------
  636. STDMETHODIMP CMessageTree::GetCurMoniker(IMoniker **ppMoniker)
  637. {
  638. // Locals
  639. HRESULT hr=S_OK;
  640. // Invalid Arg
  641. if (NULL == ppMoniker)
  642. return TrapError(E_INVALIDARG);
  643. // Thread Safety
  644. EnterCriticalSection(&m_cs);
  645. // No Data
  646. if (NULL == m_pMoniker)
  647. {
  648. hr = TrapError(E_FAIL);
  649. goto exit;
  650. }
  651. // Return It
  652. *ppMoniker = m_pMoniker;
  653. (*ppMoniker)->AddRef();
  654. exit:
  655. // Thread Safety
  656. LeaveCriticalSection(&m_cs);
  657. // Done
  658. return hr;
  659. }
  660. // ----------------------------------------------------------------------------
  661. // CMessageTree::GetCurFile
  662. // ----------------------------------------------------------------------------
  663. STDMETHODIMP CMessageTree::GetCurFile(LPOLESTR *ppszFileName)
  664. {
  665. // Locals
  666. HRESULT hr=S_OK;
  667. // Invalid Arg
  668. if (NULL == ppszFileName)
  669. return TrapError(E_INVALIDARG);
  670. // Thread Safety
  671. EnterCriticalSection(&m_cs);
  672. // Return File Name
  673. if (NULL == m_pwszFilePath)
  674. {
  675. hr = TrapError(E_FAIL);
  676. goto exit;
  677. }
  678. // Dup and return
  679. CHECKALLOC(*ppszFileName = PszDupW(m_pwszFilePath));
  680. exit:
  681. // Thread Safety
  682. LeaveCriticalSection(&m_cs);
  683. // Done
  684. return hr;
  685. }
  686. // ----------------------------------------------------------------------------
  687. // CMessageTree::Load
  688. // ----------------------------------------------------------------------------
  689. STDMETHODIMP CMessageTree::Load(LPCOLESTR pszFileName, DWORD dwMode)
  690. {
  691. // Locals
  692. HRESULT hr=S_OK;
  693. IStream *pstmFile=NULL;
  694. DWORD dwAccess=GENERIC_READ;
  695. DWORD dwShare=FILE_SHARE_READ|FILE_SHARE_WRITE;
  696. BOOL fBindUseFile;
  697. // Invalid Arg
  698. if (NULL == pszFileName)
  699. return TrapError(E_INVALIDARG);
  700. // Thread Safety
  701. EnterCriticalSection(&m_cs);
  702. // Determine Access
  703. if (ISFLAGSET(dwMode, STGM_WRITE))
  704. FLAGSET(dwAccess, GENERIC_WRITE);
  705. if (ISFLAGSET(dwMode, STGM_READWRITE))
  706. FLAGSET(dwAccess, GENERIC_READ | GENERIC_WRITE);
  707. // Determine Share Mode
  708. dwMode &= 0x00000070; // the STGM_SHARE_* flags are not individual bits
  709. if (STGM_SHARE_DENY_NONE == dwMode)
  710. dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
  711. else if (STGM_SHARE_DENY_READ == dwMode)
  712. dwShare = FILE_SHARE_WRITE;
  713. else if (STGM_SHARE_DENY_WRITE == dwMode)
  714. dwShare = FILE_SHARE_READ;
  715. else if (STGM_SHARE_EXCLUSIVE == dwMode)
  716. dwShare = 0;
  717. // Save Option
  718. fBindUseFile = m_rOptions.fBindUseFile;
  719. // If the user wants file sharing on this file, then I need to put this into my own file
  720. if (ISFLAGSET(dwShare, FILE_SHARE_WRITE))
  721. m_rOptions.fBindUseFile = TRUE;
  722. // Open File Stream
  723. CHECKHR(hr = OpenFileStreamShareW((LPWSTR)pszFileName, OPEN_EXISTING, dwAccess, dwShare, &pstmFile));
  724. // Bind the message
  725. CHECKHR(hr = Load(pstmFile));
  726. // Reset Option
  727. m_rOptions.fBindUseFile = fBindUseFile;
  728. // Free Current File
  729. SafeMemFree(m_pwszFilePath);
  730. // Assume new file
  731. CHECKALLOC(m_pwszFilePath = PszDupW(pszFileName));
  732. exit:
  733. // Cleanup
  734. SafeRelease(pstmFile);
  735. // Thread Safety
  736. LeaveCriticalSection(&m_cs);
  737. // Done
  738. return hr;
  739. }
  740. // ----------------------------------------------------------------------------
  741. // CMessageTree::Save
  742. // ----------------------------------------------------------------------------
  743. STDMETHODIMP CMessageTree::Save(LPCOLESTR pszFileName, BOOL fRemember)
  744. {
  745. // Locals
  746. HRESULT hr=S_OK;
  747. IStream *pstmFile=NULL,
  748. *pstmSource=NULL;
  749. // Invalid Arg
  750. if (NULL == pszFileName)
  751. return TrapError(E_INVALIDARG);
  752. // Thread Safety
  753. EnterCriticalSection(&m_cs);
  754. // Open File Stream
  755. CHECKHR(hr = OpenFileStreamW((LPWSTR)pszFileName, CREATE_ALWAYS, GENERIC_READ | GENERIC_WRITE, &pstmFile));
  756. // If Remember
  757. if (fRemember)
  758. {
  759. // Bind the message
  760. CHECKHR(hr = Save(pstmFile, TRUE));
  761. }
  762. // Otherwise, get message source, and copy...
  763. else
  764. {
  765. // Get Message Source
  766. CHECKHR(hr = GetMessageSource(&pstmSource, COMMIT_ONLYIFDIRTY));
  767. // Copy...
  768. CHECKHR(hr = HrCopyStream(pstmSource, pstmFile, NULL));
  769. }
  770. // Commit
  771. CHECKHR(hr = pstmFile->Commit(STGC_DEFAULT));
  772. // If Remember
  773. if (fRemember)
  774. {
  775. // Free Current File
  776. SafeMemFree(m_pwszFilePath);
  777. // Assume new file
  778. CHECKALLOC(m_pwszFilePath = PszDupW(pszFileName));
  779. }
  780. exit:
  781. // Cleanup
  782. SafeRelease(pstmFile);
  783. SafeRelease(pstmSource);
  784. // Thread Safety
  785. LeaveCriticalSection(&m_cs);
  786. // Done
  787. return hr;
  788. }
  789. // ----------------------------------------------------------------------------
  790. // CMessageTree::SaveCompleted
  791. // ----------------------------------------------------------------------------
  792. STDMETHODIMP CMessageTree::SaveCompleted(LPCOLESTR pszFileName)
  793. {
  794. return E_NOTIMPL;
  795. }
  796. // ----------------------------------------------------------------------------
  797. // CMessageTree::GetClassID
  798. // ----------------------------------------------------------------------------
  799. STDMETHODIMP CMessageTree::GetClassID(CLSID *pClassID)
  800. {
  801. // Invalid Arg
  802. if (NULL == pClassID)
  803. return TrapError(E_INVALIDARG);
  804. // Copy Class Id
  805. CopyMemory(pClassID, &IID_IMimeMessageTree, sizeof(CLSID));
  806. // Done
  807. return S_OK;
  808. }
  809. // ----------------------------------------------------------------------------
  810. // CMessageTree::GetSizeMax
  811. // ----------------------------------------------------------------------------
  812. STDMETHODIMP CMessageTree::GetSizeMax(ULARGE_INTEGER* pcbSize)
  813. {
  814. // Locals
  815. HRESULT hr=S_OK;
  816. ULONG cbSize;
  817. // Invalid Arg
  818. if (NULL == pcbSize)
  819. return TrapError(E_INVALIDARG);
  820. // INit
  821. pcbSize->QuadPart = 0;
  822. // Get Message Size
  823. CHECKHR(hr = GetMessageSize(&cbSize, COMMIT_ONLYIFDIRTY));
  824. // Set Size
  825. pcbSize->QuadPart = cbSize;
  826. exit:
  827. // Done
  828. return hr;
  829. }
  830. // ----------------------------------------------------------------------------
  831. // CMessageTree::_FIsValidHandle
  832. // ----------------------------------------------------------------------------
  833. BOOL CMessageTree::_FIsValidHandle(HBODY hBody)
  834. {
  835. // Its Valid
  836. if ((WORD)HBODYTAG(hBody) == m_wTag &&
  837. HBODYINDEX(hBody) < m_rTree.cNodes &&
  838. m_rTree.prgpNode[HBODYINDEX(hBody)] &&
  839. m_rTree.prgpNode[HBODYINDEX(hBody)]->hBody == hBody)
  840. return TRUE;
  841. // Not Valid
  842. return FALSE;
  843. }
  844. // ----------------------------------------------------------------------------
  845. // CMessageTree::_PNodeFromHBody
  846. // ----------------------------------------------------------------------------
  847. LPTREENODEINFO CMessageTree::_PNodeFromHBody(HBODY hBody)
  848. {
  849. Assert(_FIsValidHandle(hBody));
  850. return m_rTree.prgpNode[HBODYINDEX(hBody)];
  851. }
  852. // --------------------------------------------------------------------------------
  853. // CMessageTree::GetMessageSize
  854. // --------------------------------------------------------------------------------
  855. STDMETHODIMP CMessageTree::GetMessageSize(ULONG *pcbSize, DWORD dwFlags)
  856. {
  857. // Locals
  858. HRESULT hr=S_OK;
  859. LPSTREAM pstmSource=NULL;
  860. // Invalid Arg
  861. if (pcbSize == NULL)
  862. return TrapError(E_INVALIDARG);
  863. // Init
  864. *pcbSize = 0;
  865. // Thread Safety
  866. EnterCriticalSection(&m_cs);
  867. // Get the message source
  868. CHECKHR(hr = GetMessageSource(&pstmSource, dwFlags));
  869. // Get the stream Size
  870. CHECKHR(hr = HrGetStreamSize(pstmSource, pcbSize));
  871. // If you hit this assert, please let me know. t-erikne
  872. // I'm trying to see if we have to call HrGetStreamSize here.
  873. Assert(m_cbMessage == *pcbSize);
  874. exit:
  875. // Cleanup
  876. SafeRelease(pstmSource);
  877. // Thread Safety
  878. LeaveCriticalSection(&m_cs);
  879. // Done
  880. return hr;
  881. }
  882. // ---------------------------------------------------------------------------
  883. // CMessageTree::_ApplyOptionToAllBodies
  884. // ---------------------------------------------------------------------------
  885. void CMessageTree::_ApplyOptionToAllBodies(const TYPEDID oid, LPCPROPVARIANT pValue)
  886. {
  887. // Loop through bodies and set on each body
  888. for (ULONG i=0; i<m_rTree.cNodes; i++)
  889. {
  890. // Check if deleted
  891. if (NULL == m_rTree.prgpNode[i])
  892. continue;
  893. // Dirty Header...
  894. m_rTree.prgpNode[i]->pBody->SetOption(oid, pValue);
  895. }
  896. }
  897. // ---------------------------------------------------------------------------
  898. // CMessageTree::SetOption
  899. // ---------------------------------------------------------------------------
  900. STDMETHODIMP CMessageTree::SetOption(const TYPEDID oid, LPCPROPVARIANT pValue)
  901. {
  902. // Locals
  903. HRESULT hr=S_OK;
  904. // check params
  905. if (NULL == pValue)
  906. return TrapError(E_INVALIDARG);
  907. // Thread Safety
  908. EnterCriticalSection(&m_cs);
  909. // Handle Optid
  910. switch(oid)
  911. {
  912. case OID_HANDSOFF_ONSAVE:
  913. m_rOptions.fHandsOffOnSave = pValue->boolVal ? TRUE : FALSE;
  914. break;
  915. case OID_SUPPORT_EXTERNAL_BODY:
  916. _ApplyOptionToAllBodies(oid, pValue);
  917. break;
  918. case OID_SHOW_MACBINARY:
  919. if (m_rOptions.fShowMacBin != (pValue->boolVal ? TRUE : FALSE))
  920. {
  921. m_rOptions.fShowMacBin = pValue->boolVal ? TRUE : FALSE;
  922. _ApplyOptionToAllBodies(oid, pValue);
  923. }
  924. break;
  925. case OID_HEADER_RELOAD_TYPE:
  926. if (pValue->ulVal > RELOAD_HEADER_REPLACE)
  927. {
  928. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  929. goto exit;
  930. }
  931. if (m_rOptions.ReloadType != (RELOADTYPE)pValue->ulVal)
  932. {
  933. FLAGSET(m_dwState, TREESTATE_DIRTY);
  934. m_rOptions.ReloadType = (RELOADTYPE)pValue->ulVal;
  935. }
  936. break;
  937. case OID_LOAD_USE_BIND_FILE:
  938. m_rOptions.fBindUseFile = pValue->boolVal ? TRUE : FALSE;
  939. break;
  940. case OID_CLEANUP_TREE_ON_SAVE:
  941. m_rOptions.fCleanupTree = pValue->boolVal ? TRUE : FALSE;
  942. break;
  943. case OID_SAVEBODY_KEEPBOUNDARY:
  944. if (m_rOptions.fKeepBoundary != (pValue->boolVal ? TRUE : FALSE))
  945. {
  946. FLAGSET(m_dwState, TREESTATE_DIRTY);
  947. m_rOptions.fKeepBoundary = pValue->boolVal ? TRUE : FALSE;
  948. }
  949. break;
  950. case OID_CAN_INLINE_TEXT_BODIES:
  951. if (m_rOptions.fCanInlineText != (pValue->boolVal ? TRUE : FALSE))
  952. {
  953. FLAGSET(m_dwState, TREESTATE_DIRTY);
  954. m_rOptions.fCanInlineText = pValue->boolVal ? TRUE : FALSE;
  955. }
  956. break;
  957. case OID_HIDE_TNEF_ATTACHMENTS:
  958. if (m_rOptions.fHideTnef != (pValue->boolVal ? TRUE : FALSE))
  959. {
  960. m_rOptions.fHideTnef = pValue->boolVal ? TRUE : FALSE;
  961. _ApplyOptionToAllBodies(oid, pValue);
  962. }
  963. break;
  964. case OID_ALLOW_8BIT_HEADER:
  965. if (m_rOptions.fAllow8bitHeader != (pValue->boolVal ? TRUE : FALSE))
  966. {
  967. FLAGSET(m_dwState, TREESTATE_DIRTY);
  968. m_rOptions.fAllow8bitHeader = pValue->boolVal ? TRUE : FALSE;
  969. }
  970. break;
  971. case OID_CBMAX_HEADER_LINE:
  972. if (pValue->ulVal < MIN_CBMAX_HEADER_LINE || pValue->ulVal > MAX_CBMAX_HEADER_LINE)
  973. {
  974. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  975. goto exit;
  976. }
  977. if (m_rOptions.cchMaxHeaderLine != pValue->ulVal)
  978. {
  979. FLAGSET(m_dwState, TREESTATE_DIRTY);
  980. m_rOptions.cchMaxHeaderLine = pValue->ulVal;
  981. }
  982. break;
  983. case OID_SAVE_FORMAT:
  984. if (SAVE_RFC822 != pValue->ulVal && SAVE_RFC1521 != pValue->ulVal)
  985. {
  986. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  987. goto exit;
  988. }
  989. if (m_rOptions.savetype != (MIMESAVETYPE)pValue->ulVal)
  990. {
  991. FLAGSET(m_dwState, TREESTATE_DIRTY);
  992. m_rOptions.savetype = (MIMESAVETYPE)pValue->ulVal;
  993. }
  994. break;
  995. case OID_TRANSMIT_TEXT_ENCODING:
  996. if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pValue->ulVal))
  997. {
  998. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  999. goto exit;
  1000. }
  1001. if (m_rOptions.ietTextXmit != (ENCODINGTYPE)pValue->ulVal)
  1002. {
  1003. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1004. m_rOptions.ietTextXmit = (ENCODINGTYPE)pValue->ulVal;
  1005. }
  1006. break;
  1007. case OID_XMIT_PLAIN_TEXT_ENCODING:
  1008. if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pValue->ulVal))
  1009. {
  1010. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  1011. goto exit;
  1012. }
  1013. if (m_rOptions.ietPlainXmit != (ENCODINGTYPE)pValue->ulVal)
  1014. {
  1015. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1016. m_rOptions.ietPlainXmit = (ENCODINGTYPE)pValue->ulVal;
  1017. }
  1018. break;
  1019. case OID_XMIT_HTML_TEXT_ENCODING:
  1020. if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pValue->ulVal))
  1021. {
  1022. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  1023. goto exit;
  1024. }
  1025. if (m_rOptions.ietHtmlXmit != (ENCODINGTYPE)pValue->ulVal)
  1026. {
  1027. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1028. m_rOptions.ietHtmlXmit = (ENCODINGTYPE)pValue->ulVal;
  1029. }
  1030. break;
  1031. case OID_WRAP_BODY_TEXT:
  1032. if (m_rOptions.fWrapBodyText != (pValue->boolVal ? TRUE : FALSE))
  1033. {
  1034. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1035. m_rOptions.fWrapBodyText = pValue->boolVal ? TRUE : FALSE;
  1036. }
  1037. break;
  1038. case OID_CBMAX_BODY_LINE:
  1039. if (pValue->ulVal < MIN_CBMAX_BODY_LINE || pValue->ulVal > MAX_CBMAX_BODY_LINE)
  1040. {
  1041. hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
  1042. goto exit;
  1043. }
  1044. if (m_rOptions.cchMaxBodyLine != pValue->ulVal)
  1045. {
  1046. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1047. m_rOptions.cchMaxBodyLine = pValue->ulVal;
  1048. }
  1049. break;
  1050. case OID_GENERATE_MESSAGE_ID:
  1051. if (m_rOptions.fGenMessageId != (pValue->boolVal ? TRUE : FALSE))
  1052. {
  1053. FLAGSET(m_dwState, TREESTATE_DIRTY);
  1054. m_rOptions.fGenMessageId = pValue->boolVal ? TRUE : FALSE;
  1055. }
  1056. break;
  1057. case OID_SECURITY_ENCODE_FLAGS:
  1058. m_rOptions.ulSecIgnoreMask = pValue->ulVal;
  1059. break;
  1060. case OID_DECODE_RFC1154:
  1061. m_rOptions.fDecodeRfc1154 = pValue->boolVal ? TRUE : FALSE;
  1062. break;
  1063. default:
  1064. hr = TrapError(MIME_E_INVALID_OPTION_ID);
  1065. break;
  1066. }
  1067. exit:
  1068. // Thread Safety
  1069. LeaveCriticalSection(&m_cs);
  1070. // Done
  1071. return hr;
  1072. }
  1073. // ---------------------------------------------------------------------------
  1074. // CMessageTree::GetOption
  1075. // ---------------------------------------------------------------------------
  1076. STDMETHODIMP CMessageTree::GetOption(const TYPEDID oid, LPPROPVARIANT pValue)
  1077. {
  1078. // Locals
  1079. HRESULT hr=S_OK;
  1080. // check params
  1081. if (NULL == pValue)
  1082. return TrapError(E_INVALIDARG);
  1083. // Thread Safety
  1084. EnterCriticalSection(&m_cs);
  1085. pValue->vt = TYPEDID_TYPE(oid);
  1086. // Handle Optid
  1087. switch(oid)
  1088. {
  1089. case OID_HANDSOFF_ONSAVE:
  1090. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fHandsOffOnSave;
  1091. break;
  1092. case OID_LOAD_USE_BIND_FILE:
  1093. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fBindUseFile;
  1094. break;
  1095. case OID_SHOW_MACBINARY:
  1096. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fShowMacBin;
  1097. break;
  1098. case OID_HEADER_RELOAD_TYPE:
  1099. pValue->ulVal = m_rOptions.ReloadType;
  1100. break;
  1101. case OID_CAN_INLINE_TEXT_BODIES:
  1102. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fCanInlineText;
  1103. break;
  1104. case OID_CLEANUP_TREE_ON_SAVE:
  1105. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fCleanupTree;
  1106. break;
  1107. case OID_SAVEBODY_KEEPBOUNDARY:
  1108. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fKeepBoundary;
  1109. break;
  1110. case OID_HIDE_TNEF_ATTACHMENTS:
  1111. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fHideTnef;
  1112. break;
  1113. case OID_ALLOW_8BIT_HEADER:
  1114. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fAllow8bitHeader;
  1115. break;
  1116. case OID_WRAP_BODY_TEXT:
  1117. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fWrapBodyText;
  1118. break;
  1119. case OID_CBMAX_HEADER_LINE:
  1120. pValue->ulVal = m_rOptions.cchMaxHeaderLine;
  1121. break;
  1122. case OID_SAVE_FORMAT:
  1123. pValue->ulVal = (ULONG)m_rOptions.savetype;
  1124. break;
  1125. case OID_TRANSMIT_TEXT_ENCODING:
  1126. pValue->ulVal = (ULONG)m_rOptions.ietTextXmit;
  1127. break;
  1128. case OID_XMIT_PLAIN_TEXT_ENCODING:
  1129. pValue->ulVal = (ULONG)m_rOptions.ietPlainXmit;
  1130. break;
  1131. case OID_XMIT_HTML_TEXT_ENCODING:
  1132. pValue->ulVal = (ULONG)m_rOptions.ietHtmlXmit;
  1133. break;
  1134. case OID_CBMAX_BODY_LINE:
  1135. pValue->ulVal = m_rOptions.cchMaxBodyLine;
  1136. break;
  1137. case OID_GENERATE_MESSAGE_ID:
  1138. pValue->boolVal = m_rOptions.fGenMessageId;
  1139. break;
  1140. case OID_SECURITY_ENCODE_FLAGS:
  1141. pValue->ulVal = m_rOptions.ulSecIgnoreMask;
  1142. break;
  1143. case OID_DECODE_RFC1154:
  1144. pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fDecodeRfc1154;
  1145. break;
  1146. default:
  1147. hr = TrapError(MIME_E_INVALID_OPTION_ID);
  1148. break;
  1149. }
  1150. // Thread Safety
  1151. LeaveCriticalSection(&m_cs);
  1152. // Done
  1153. return hr;
  1154. }
  1155. // --------------------------------------------------------------------------------
  1156. // CMessageTree::_ResetObject
  1157. // --------------------------------------------------------------------------------
  1158. void CMessageTree::_ResetObject(BOOKTREERESET ResetType)
  1159. {
  1160. // Load InitNew
  1161. if (BOOKTREE_RESET_LOADINITNEW == ResetType)
  1162. {
  1163. // There has to be a root (look at impl of ::_HrLoadInitNew)
  1164. Assert(m_pRootNode);
  1165. // Don't Crash
  1166. if (m_pRootNode)
  1167. {
  1168. // Delete all bodies, except for the root, if there is one...
  1169. if (m_pRootNode->pBody->IsType(IBT_EMPTY) == S_FALSE || m_pRootNode->pContainer->CountProps() > 0)
  1170. {
  1171. // Delete the root body, this simply removed properties and empties the body, but leave the root body
  1172. DeleteBody(m_pRootNode->hBody, 0);
  1173. }
  1174. // Lighweight FreeTree Node Info
  1175. _FreeTreeNodeInfo(m_pRootNode, FALSE);
  1176. // Validate
  1177. Assert(m_pRootNode->cChildren == 0);
  1178. Assert(m_pRootNode->pParent == NULL);
  1179. Assert(m_pRootNode->pNext == NULL);
  1180. Assert(m_pRootNode->pPrev == NULL);
  1181. Assert(m_pRootNode->pChildHead == NULL);
  1182. Assert(m_pRootNode->pChildTail == NULL);
  1183. Assert(m_pRootNode->pBody);
  1184. Assert(m_pRootNode->pContainer);
  1185. // Quick Reset
  1186. TREENODEINFO rTemp;
  1187. CopyMemory(&rTemp, m_pRootNode, sizeof(TREENODEINFO));
  1188. ZeroMemory(m_pRootNode, sizeof(TREENODEINFO));
  1189. m_pRootNode->pBody = rTemp.pBody;
  1190. m_pRootNode->pContainer = rTemp.pContainer;
  1191. m_pRootNode->hBody = rTemp.hBody;
  1192. // Set OID_RELOAD_HEADER_TYPE
  1193. PROPVARIANT rOption;
  1194. rOption.vt = VT_UI4;
  1195. rOption.ulVal = (ULONG)m_rOptions.ReloadType;
  1196. m_pRootNode->pContainer->SetOption(OID_HEADER_RELOAD_TYPE, &rOption);
  1197. }
  1198. }
  1199. // Free All Elements
  1200. else
  1201. _FreeNodeTableElements();
  1202. // Free Bind Request Table
  1203. _ReleaseUrlRequestList(&m_pPending);
  1204. _ReleaseUrlRequestList(&m_pComplete);
  1205. // Free and Release Objects
  1206. SafeRelease(m_pCallback);
  1207. SafeRelease(m_pWebPage);
  1208. SafeMemFree(m_pwszFilePath);
  1209. SafeRelease(m_pBinding);
  1210. SafeRelease(m_pMoniker);
  1211. SafeRelease(m_pBC);
  1212. SafeRelease(m_pInternet);
  1213. SafeRelease(m_pStmBind);
  1214. SafeRelease(m_pRootStm);
  1215. SafeMemFree(m_rRootUrl.pszVal);
  1216. SafeMemFree(m_pBT1154);
  1217. // Clear Current BindNode
  1218. m_pBindNode = NULL;
  1219. // Orphan CStreamLockBytes
  1220. if (m_pStmLock)
  1221. {
  1222. m_pStmLock->HrHandsOffStorage();
  1223. m_pStmLock->Release();
  1224. m_pStmLock = NULL;
  1225. }
  1226. // If Deconstructing
  1227. if (BOOKTREE_RESET_DECONSTRUCT == ResetType)
  1228. {
  1229. // Release the body table array
  1230. SafeMemFree(m_rTree.prgpNode);
  1231. // If I'm registered as a Url
  1232. if (m_pActiveUrl)
  1233. m_pActiveUrl->RevokeWebBook(this);
  1234. // Better not have an active Url
  1235. Assert(NULL == m_pActiveUrl);
  1236. }
  1237. }
  1238. // --------------------------------------------------------------------------------
  1239. // CMessageTree::_HrLoadInitNew
  1240. // --------------------------------------------------------------------------------
  1241. HRESULT CMessageTree::_HrLoadInitNew(void)
  1242. {
  1243. // Locals
  1244. HRESULT hr=S_OK;
  1245. // If there is not root body, normal InitNew
  1246. if (NULL == m_pRootNode || RELOAD_HEADER_RESET == m_rOptions.ReloadType)
  1247. {
  1248. // InitNew
  1249. CHECKHR(hr = InitNew());
  1250. }
  1251. // Otherwise, smart init new, allowing root header merge
  1252. else
  1253. {
  1254. // Reset the Object
  1255. _ResetObject(BOOKTREE_RESET_LOADINITNEW);
  1256. // Reset Vars
  1257. m_cbMessage = 0;
  1258. m_dwState = 0;
  1259. // Assume the Bind has Finished
  1260. FLAGSET(m_dwState, TREESTATE_BINDDONE);
  1261. // Reset charset to system charset
  1262. m_rOptions.pCharset = CIntlGlobals::GetDefBodyCset();
  1263. }
  1264. exit:
  1265. // Done
  1266. return hr;
  1267. }
  1268. // --------------------------------------------------------------------------------
  1269. // CMessageTree::_InitNewWithoutRoot
  1270. // --------------------------------------------------------------------------------
  1271. void CMessageTree::_InitNewWithoutRoot(void)
  1272. {
  1273. // Reset the Object
  1274. _ResetObject(BOOKTREE_RESET_INITNEW);
  1275. // Reset Vars
  1276. m_cbMessage = 0;
  1277. m_dwState = 0;
  1278. m_wTag++;
  1279. // Invalid Tag Numbers
  1280. while(m_wTag == 0 || m_wTag == 0xffff)
  1281. m_wTag++;
  1282. // Assume the Bind has Finished
  1283. FLAGSET(m_dwState, TREESTATE_BINDDONE);
  1284. // Reset charset to system charset
  1285. m_rOptions.pCharset = CIntlGlobals::GetDefBodyCset();
  1286. }
  1287. // --------------------------------------------------------------------------------
  1288. // CMessageTree::InitNew
  1289. // --------------------------------------------------------------------------------
  1290. STDMETHODIMP CMessageTree::InitNew(void)
  1291. {
  1292. // Locals
  1293. HRESULT hr=S_OK;
  1294. // Thread Safety
  1295. EnterCriticalSection(&m_cs);
  1296. // _InitNewWithoutRoot
  1297. _InitNewWithoutRoot();
  1298. // Init the Root Body...
  1299. CHECKHR(hr = InsertBody(IBL_ROOT, NULL, NULL));
  1300. exit:
  1301. // Thread Safety
  1302. LeaveCriticalSection(&m_cs);
  1303. // Done
  1304. return hr;
  1305. }
  1306. // --------------------------------------------------------------------------------
  1307. // CMessageTree::IsDirty
  1308. // --------------------------------------------------------------------------------
  1309. STDMETHODIMP CMessageTree::IsDirty(void)
  1310. {
  1311. // Locals
  1312. HRESULT hr=S_FALSE;
  1313. ULONG i;
  1314. // Thread Safety
  1315. EnterCriticalSection(&m_cs);
  1316. // If Dirty...
  1317. if (ISFLAGSET(m_dwState, TREESTATE_DIRTY))
  1318. {
  1319. hr = S_OK;
  1320. goto exit;
  1321. }
  1322. // Loop through bodies and ask IMimeHeader's and IMimeBody's
  1323. for (i=0; i<m_rTree.cNodes; i++)
  1324. {
  1325. // Better have it
  1326. if (NULL == m_rTree.prgpNode[i])
  1327. continue;
  1328. // Dirty Header...
  1329. if (m_rTree.prgpNode[i]->pBody->IsDirty() == S_OK)
  1330. {
  1331. hr = S_OK;
  1332. goto exit;
  1333. }
  1334. }
  1335. exit:
  1336. // Thread Safety
  1337. LeaveCriticalSection(&m_cs);
  1338. // Done
  1339. return hr;
  1340. }
  1341. // --------------------------------------------------------------------------------
  1342. // CMessageTree::_RecursiveGetFlags
  1343. // --------------------------------------------------------------------------------
  1344. void CMessageTree::_RecursiveGetFlags(LPTREENODEINFO pNode, LPDWORD pdwFlags, BOOL fInRelated)
  1345. {
  1346. // Locals
  1347. DWORD dw;
  1348. LPTREENODEINFO pChild;
  1349. // Invalid Arg
  1350. Assert(pNode && pdwFlags);
  1351. // $$WARNING$$ Don't use pNode->pContainer here, that will circumvent CMimeBody's chance to set some flags
  1352. dw = pNode->pBody->DwGetFlags(m_rOptions.fHideTnef);
  1353. // If in related, clear IMF_ATTACHMENTS
  1354. if (fInRelated)
  1355. FLAGCLEAR(dw, IMF_ATTACHMENTS);
  1356. // Raid-44446: not getting paperclip icon in listview on pegasus messages w/ text attach
  1357. // If dw has text and no attachments and pdwFlags has text and no attachments, add attachments
  1358. //
  1359. // Raid-11617: OE: GetAttachmentCount should not include vcards
  1360. if (ISFLAGSET(dw, IMF_TEXT) && !ISFLAGSET(dw, IMF_HASVCARD) && ISFLAGSET(*pdwFlags, IMF_TEXT) && !ISFLAGSET(dw, IMF_ATTACHMENTS) && !ISFLAGSET(*pdwFlags, IMF_ATTACHMENTS))
  1361. {
  1362. // As long as pNode is not in an alternative section
  1363. if (NULL == pNode->pParent || pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_FALSE)
  1364. {
  1365. // This message must have text attachments
  1366. FLAGSET(*pdwFlags, IMF_ATTACHMENTS);
  1367. }
  1368. }
  1369. // Add in Flags
  1370. FLAGSET(*pdwFlags, dw);
  1371. // Partial...
  1372. if (ISFLAGSET(pNode->dwType, NODETYPE_INCOMPLETE))
  1373. FLAGSET(*pdwFlags, IMF_PARTIAL);
  1374. // If this is a multipart item, lets search it's children
  1375. if (_IsMultiPart(pNode))
  1376. {
  1377. // Sub-multipart
  1378. FLAGSET(*pdwFlags, IMF_SUBMULTIPART);
  1379. // If fInRelated == FALSE...
  1380. if (FALSE == fInRelated)
  1381. fInRelated = (S_OK == pNode->pContainer->IsContentType(NULL, STR_SUB_RELATED) ? TRUE : FALSE);
  1382. // Loop Children
  1383. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  1384. {
  1385. // Check body
  1386. Assert(pChild->pParent == pNode);
  1387. // Get the flags for this child node
  1388. _RecursiveGetFlags(pChild, pdwFlags, fInRelated);
  1389. }
  1390. }
  1391. }
  1392. // --------------------------------------------------------------------------------
  1393. // CMessageTree::DwGetFlags
  1394. // --------------------------------------------------------------------------------
  1395. DWORD CMessageTree::DwGetFlags(void)
  1396. {
  1397. // Locals
  1398. DWORD dwFlags=0;
  1399. // Thread Safety
  1400. EnterCriticalSection(&m_cs);
  1401. // Recurse the tree
  1402. if (m_pRootNode && m_pRootNode->pBody->IsType(IBT_EMPTY) == S_FALSE)
  1403. _RecursiveGetFlags(m_pRootNode, &dwFlags, (S_OK == m_pRootNode->pContainer->IsContentType(NULL, STR_SUB_RELATED) ? TRUE : FALSE));
  1404. if (m_pRootNode && ISFLAGSET(m_pRootNode->dwType, NODETYPE_RFC1154_ROOT))
  1405. FLAGSET(dwFlags, IMF_RFC1154);
  1406. // Thread Safety
  1407. LeaveCriticalSection(&m_cs);
  1408. // Done
  1409. return dwFlags;
  1410. }
  1411. // ----------------------------------------------------------------------------
  1412. // CMessageTree::GetFlags
  1413. // ----------------------------------------------------------------------------
  1414. STDMETHODIMP CMessageTree::GetFlags(DWORD *pdwFlags)
  1415. {
  1416. // Invalid Arg
  1417. if (NULL == pdwFlags)
  1418. return TrapError(E_INVALIDARG);
  1419. // dwgetflags has a critsec
  1420. *pdwFlags = DwGetFlags();
  1421. // Done
  1422. return S_OK;
  1423. }
  1424. // ----------------------------------------------------------------------------
  1425. // CMessageTree::_FreeTreeNodeInfo
  1426. // ----------------------------------------------------------------------------
  1427. void CMessageTree::_FreeTreeNodeInfo(LPTREENODEINFO pNode, BOOL fFull /* TRUE */)
  1428. {
  1429. // Invalid
  1430. Assert(pNode);
  1431. // Free Boundary info
  1432. if (!ISFLAGSET(pNode->dwState, NODESTATE_BOUNDNOFREE))
  1433. SafeMemFree(pNode->rBoundary.pszVal);
  1434. // Full Free
  1435. if (TRUE == fFull)
  1436. {
  1437. // Release the Container
  1438. SafeRelease(pNode->pContainer);
  1439. // Revoke the TreeNode from the body
  1440. if (pNode->pBody)
  1441. {
  1442. // Revoke pNode
  1443. pNode->pBody->RevokeTreeNode();
  1444. // Release the body object
  1445. SafeRelease(pNode->pBody);
  1446. // Null It
  1447. pNode->pBody = NULL;
  1448. }
  1449. }
  1450. // Orphan the lockbytes
  1451. if (pNode->pLockBytes)
  1452. {
  1453. // Orhpan It
  1454. pNode->pLockBytes->HrHandsOffStorage();
  1455. // Release Body Lock Bytes
  1456. SafeRelease(pNode->pLockBytes);
  1457. }
  1458. // Free Bind Request List
  1459. if (pNode->pResolved)
  1460. _ReleaseUrlRequestList(&pNode->pResolved);
  1461. // Free the node
  1462. if (fFull)
  1463. g_pMalloc->Free(pNode);
  1464. }
  1465. // ----------------------------------------------------------------------------
  1466. // CMessageTree::_FreeNodeTableElements
  1467. // ----------------------------------------------------------------------------
  1468. void CMessageTree::_FreeNodeTableElements(void)
  1469. {
  1470. // Release all of the headers
  1471. for (ULONG i=0; i<m_rTree.cNodes; i++)
  1472. {
  1473. // Better have a bindinfo
  1474. if (NULL == m_rTree.prgpNode[i])
  1475. continue;
  1476. // Free the node info
  1477. _FreeTreeNodeInfo(m_rTree.prgpNode[i]);
  1478. }
  1479. // Zero
  1480. m_rTree.cNodes = 0;
  1481. m_rTree.cEmpty = 0;
  1482. // No Root Body...
  1483. m_pRootNode = NULL;
  1484. }
  1485. // --------------------------------------------------------------------------------
  1486. // CMessageTree::_HrAllocateTreeNode
  1487. // --------------------------------------------------------------------------------
  1488. HRESULT CMessageTree::_HrAllocateTreeNode(ULONG ulIndex)
  1489. {
  1490. // Locals
  1491. HRESULT hr=S_OK;
  1492. LPTREENODEINFO pNode;
  1493. // Check Params
  1494. Assert(ulIndex < m_rTree.cAlloc);
  1495. // Allocate a TREENODEINFO Object
  1496. CHECKALLOC(pNode = (LPTREENODEINFO)g_pMalloc->Alloc(sizeof(TREENODEINFO)));
  1497. // ZeroInit
  1498. ZeroMemory(pNode, sizeof(TREENODEINFO));
  1499. // Allocate the body
  1500. CHECKALLOC(pNode->pBody = new CMessageBody(pNode));
  1501. // InitNew
  1502. CHECKHR(hr = pNode->pBody->InitNew());
  1503. // Pass Down Some Inherited Options
  1504. if (m_rOptions.fExternalBody != DEF_SUPPORT_EXTERNAL_BODY)
  1505. {
  1506. // Locals
  1507. PROPVARIANT Variant;
  1508. // Initialize the Variant
  1509. Variant.vt = VT_BOOL;
  1510. Variant.boolVal = (VARIANT_BOOL) !!m_rOptions.fExternalBody;
  1511. // Set the Option
  1512. SideAssert(SUCCEEDED(pNode->pBody->SetOption(OID_SUPPORT_EXTERNAL_BODY, &Variant)));
  1513. }
  1514. // Get the Container
  1515. SideAssert(SUCCEEDED(pNode->pBody->BindToObject(IID_CMimePropertyContainer, (LPVOID *)&pNode->pContainer)));
  1516. // Create hBody
  1517. pNode->hBody = HBODYMAKE(ulIndex);
  1518. // Readability
  1519. m_rTree.prgpNode[ulIndex] = pNode;
  1520. exit:
  1521. // Done
  1522. return hr;
  1523. }
  1524. // --------------------------------------------------------------------------------
  1525. // CMessageTree::LoadOffsetTable
  1526. // --------------------------------------------------------------------------------
  1527. STDMETHODIMP CMessageTree::LoadOffsetTable(LPSTREAM pStream)
  1528. {
  1529. // Locals
  1530. HRESULT hr=S_OK;
  1531. CACHEINFOV2 rInfo;
  1532. LPCACHENODEV2 prgNode=NULL;
  1533. ULONG cbNodes,
  1534. i;
  1535. LPTREENODEINFO pNode;
  1536. // check params
  1537. if (NULL == pStream)
  1538. return TrapError(E_INVALIDARG);
  1539. // Thread Safety
  1540. EnterCriticalSection(&m_cs);
  1541. // Init New
  1542. _InitNewWithoutRoot();
  1543. // Free the root
  1544. Assert(NULL == m_pRootNode && 0 == m_rTree.cNodes);
  1545. // Read Header...
  1546. CHECKHR(hr = pStream->Read(&rInfo, sizeof(CACHEINFOV2), NULL));
  1547. // Current Version...
  1548. if (VER_BODYTREEV2 == rInfo.wVersion)
  1549. {
  1550. // Save Message Size
  1551. m_cbMessage = rInfo.cbMessage;
  1552. // Are there bodies...
  1553. Assert(rInfo.cNodes >= 1);
  1554. // Better have a root
  1555. if (FVerifySignedNode(rInfo, rInfo.iRoot) == FALSE)
  1556. {
  1557. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1558. goto exit;
  1559. }
  1560. // Compute sizeof Nodes
  1561. cbNodes = sizeof(CACHENODEV2) * rInfo.cNodes;
  1562. Assert(cbNodes % 4 == 0);
  1563. // Allocate prgNode array
  1564. CHECKHR(hr = HrAlloc((LPVOID *)&prgNode, cbNodes));
  1565. // Read Nodes...
  1566. CHECKHR(hr = pStream->Read(prgNode, cbNodes, NULL));
  1567. // Set body count
  1568. m_rTree.cNodes = rInfo.cNodes;
  1569. m_rTree.cAlloc = m_rTree.cNodes + 5;
  1570. // Build Body Table
  1571. CHECKHR(hr = HrRealloc((LPVOID *)&m_rTree.prgpNode, sizeof(LPTREENODEINFO) * m_rTree.cAlloc));
  1572. // Zero Init
  1573. ZeroMemory(m_rTree.prgpNode, sizeof(LPTREENODEINFO) * m_rTree.cAlloc);
  1574. // Build bodies
  1575. for (i=0; i<m_rTree.cNodes; i++)
  1576. {
  1577. // Allocate LPBINDINFO
  1578. CHECKHR(hr = _HrAllocateTreeNode(i));
  1579. }
  1580. // Link Body Table
  1581. for (i=0; i<m_rTree.cNodes; i++)
  1582. {
  1583. // Readability
  1584. pNode = m_rTree.prgpNode[i];
  1585. Assert(pNode);
  1586. // Flags
  1587. pNode->dwType = prgNode[i].dwType;
  1588. // Number of Children
  1589. pNode->cChildren = prgNode[i].cChildren;
  1590. // Valid Boundary
  1591. if (prgNode[i].dwBoundary >= BOUNDARY_LAST || 2 == prgNode[i].dwBoundary)
  1592. pNode->boundary = BOUNDARY_NONE;
  1593. else
  1594. pNode->boundary = (BOUNDARYTYPE)prgNode[i].dwBoundary;
  1595. // Offset
  1596. pNode->cbBoundaryStart = prgNode[i].cbBoundaryStart;
  1597. pNode->cbHeaderStart = prgNode[i].cbHeaderStart;
  1598. pNode->cbBodyStart = prgNode[i].cbBodyStart;
  1599. pNode->cbBodyEnd = prgNode[i].cbBodyEnd;
  1600. // Parent
  1601. if (prgNode[i].iParent)
  1602. {
  1603. // Validate the handle with the signature
  1604. if (FVerifySignedNode(rInfo, prgNode[i].iParent) == FALSE)
  1605. {
  1606. AssertSz(FALSE, "MIME_E_CORRUPT_CACHE_TREE");
  1607. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1608. goto exit;
  1609. }
  1610. // Get the parent
  1611. pNode->pParent = PNodeFromSignedNode(prgNode[i].iParent);
  1612. }
  1613. // Next
  1614. if (prgNode[i].iNext)
  1615. {
  1616. // Validate the handle with the signature
  1617. if (FVerifySignedNode(rInfo, prgNode[i].iNext) == FALSE)
  1618. {
  1619. AssertSz(FALSE, "MIME_E_CORRUPT_CACHE_TREE");
  1620. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1621. goto exit;
  1622. }
  1623. // Get the Next
  1624. pNode->pNext = PNodeFromSignedNode(prgNode[i].iNext);
  1625. }
  1626. // Prev
  1627. if (prgNode[i].iPrev)
  1628. {
  1629. // Validate the handle with the signature
  1630. if (FVerifySignedNode(rInfo, prgNode[i].iPrev) == FALSE)
  1631. {
  1632. AssertSz(FALSE, "MIME_E_CORRUPT_CACHE_TREE");
  1633. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1634. goto exit;
  1635. }
  1636. // Get the Prev
  1637. pNode->pPrev = PNodeFromSignedNode(prgNode[i].iPrev);
  1638. }
  1639. // First Child
  1640. if (prgNode[i].iChildHead)
  1641. {
  1642. // Validate the handle with the signature
  1643. if (FVerifySignedNode(rInfo, prgNode[i].iChildHead) == FALSE)
  1644. {
  1645. AssertSz(FALSE, "MIME_E_CORRUPT_CACHE_TREE");
  1646. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1647. goto exit;
  1648. }
  1649. // Get the first child
  1650. pNode->pChildHead = PNodeFromSignedNode(prgNode[i].iChildHead);
  1651. }
  1652. // Tail
  1653. if (prgNode[i].iChildTail)
  1654. {
  1655. // Validate the handle with the signature
  1656. if (FVerifySignedNode(rInfo, prgNode[i].iChildTail) == FALSE)
  1657. {
  1658. AssertSz(FALSE, "MIME_E_CORRUPT_CACHE_TREE");
  1659. hr = TrapError(MIME_E_CORRUPT_CACHE_TREE);
  1660. goto exit;
  1661. }
  1662. // Get the last child
  1663. pNode->pChildTail = PNodeFromSignedNode(prgNode[i].iChildTail);
  1664. }
  1665. }
  1666. // Save Root Handle
  1667. Assert(NULL == m_pRootNode);
  1668. m_pRootNode = PNodeFromSignedNode(rInfo.iRoot);
  1669. }
  1670. // Otherwise, bad version...
  1671. else
  1672. {
  1673. hr = TrapError(MIME_E_UNKNOWN_BODYTREE_VERSION);
  1674. goto exit;
  1675. }
  1676. // Tree Loaded
  1677. FLAGSET(m_dwState, TREESTATE_LOADED);
  1678. exit:
  1679. // Cleanup
  1680. SafeMemFree(prgNode);
  1681. // Thread Safety
  1682. LeaveCriticalSection(&m_cs);
  1683. // Done
  1684. return hr;
  1685. }
  1686. // --------------------------------------------------------------------------------
  1687. // CMessageTree::SaveOffsetTable
  1688. // --------------------------------------------------------------------------------
  1689. STDMETHODIMP CMessageTree::SaveOffsetTable(LPSTREAM pStream, DWORD dwFlags)
  1690. {
  1691. // Locals
  1692. HRESULT hr=S_OK;
  1693. ULONG i,
  1694. cbNodes=0,
  1695. iNode;
  1696. LPTREENODEINFO pNode;
  1697. CACHEINFOV2 rInfo;
  1698. LPCACHENODEV2 prgNode=NULL;
  1699. // check params
  1700. if (NULL == pStream)
  1701. return TrapError(E_INVALIDARG);
  1702. // Thread Safety
  1703. EnterCriticalSection(&m_cs);
  1704. // We better have some bodies (we always have a root)
  1705. Assert(m_rTree.cNodes >= 1);
  1706. // If Dirty, SaveMessage needs to be called first...
  1707. if (ISFLAGSET(dwFlags, COMMIT_ONLYIFDIRTY) && IsDirty() == S_OK)
  1708. {
  1709. // Commit it
  1710. CHECKHR(hr = Commit(dwFlags));
  1711. }
  1712. // I removed this check because of the addition of OID_HANDSOFF_ONSAVE option
  1713. // I need to be able to save the offsettable even if i don't have m_pStmLock
  1714. Assert(NULL == m_pStmLock ? S_FALSE == IsDirty() : TRUE);
  1715. #if 0
  1716. if (NULL == m_pStmLock)
  1717. {
  1718. hr = TrapError(MIME_E_NOTHING_TO_SAVE);
  1719. goto exit;
  1720. }
  1721. #endif
  1722. // Init rHeader
  1723. ZeroMemory(&rInfo, sizeof(CACHEINFOV2));
  1724. // Loop bodies
  1725. for (i=0; i<m_rTree.cNodes; i++)
  1726. {
  1727. if (m_rTree.prgpNode[i])
  1728. m_rTree.prgpNode[i]->iCacheNode = rInfo.cNodes++;
  1729. }
  1730. // Version
  1731. rInfo.wVersion = VER_BODYTREEV2;
  1732. rInfo.wSignature = m_wTag;
  1733. rInfo.cbMessage = m_cbMessage;
  1734. // Better have a root
  1735. Assert(m_pRootNode);
  1736. // Compute sizeof Nodes
  1737. cbNodes = sizeof(CACHENODEV2) * rInfo.cNodes;
  1738. Assert(cbNodes % 4 == 0);
  1739. // Allocate prgNode array
  1740. CHECKHR(hr = HrAlloc((LPVOID *)&prgNode, cbNodes));
  1741. // Zero the array
  1742. ZeroMemory(prgNode, cbNodes);
  1743. // Loop bodies
  1744. for (i=0, iNode=0; i<m_rTree.cNodes; i++)
  1745. {
  1746. // Readability
  1747. pNode = m_rTree.prgpNode[i];
  1748. if (NULL == pNode)
  1749. continue;
  1750. // Validate this node
  1751. Assert(pNode->hBody == HBODYMAKE(i));
  1752. Assert(pNode->iCacheNode == iNode);
  1753. // Is this the root
  1754. if (pNode == m_pRootNode)
  1755. {
  1756. Assert(0 == rInfo.iRoot);
  1757. rInfo.iRoot = DwSignNode(rInfo, pNode->iCacheNode);
  1758. Assert(FVerifySignedNode(rInfo, rInfo.iRoot));
  1759. }
  1760. // Copy Offset Information
  1761. prgNode[iNode].dwBoundary = pNode->boundary;
  1762. prgNode[iNode].cbBoundaryStart = pNode->cbBoundaryStart;
  1763. prgNode[iNode].cbHeaderStart = pNode->cbHeaderStart;
  1764. prgNode[iNode].cbBodyStart = pNode->cbBodyStart;
  1765. prgNode[iNode].cbBodyEnd = pNode->cbBodyEnd;
  1766. // Bitmask of NODETYPE_xxx describing this body
  1767. prgNode[iNode].dwType = pNode->dwType;
  1768. // Number of children
  1769. prgNode[iNode].cChildren = pNode->cChildren;
  1770. // Parent
  1771. if (pNode->pParent)
  1772. {
  1773. prgNode[iNode].iParent = DwSignNode(rInfo, pNode->pParent->iCacheNode);
  1774. Assert(FVerifySignedNode(rInfo, prgNode[iNode].iParent));
  1775. }
  1776. // ChildHead
  1777. if (pNode->pChildHead)
  1778. {
  1779. prgNode[iNode].iChildHead = DwSignNode(rInfo, pNode->pChildHead->iCacheNode);
  1780. Assert(FVerifySignedNode(rInfo, prgNode[iNode].iChildHead));
  1781. }
  1782. // ChildTail
  1783. if (pNode->pChildTail)
  1784. {
  1785. prgNode[iNode].iChildTail = DwSignNode(rInfo, pNode->pChildTail->iCacheNode);
  1786. Assert(FVerifySignedNode(rInfo, prgNode[iNode].iChildTail));
  1787. }
  1788. // Next
  1789. if (pNode->pNext)
  1790. {
  1791. prgNode[iNode].iNext = DwSignNode(rInfo, pNode->pNext->iCacheNode);
  1792. Assert(FVerifySignedNode(rInfo, prgNode[iNode].iNext));
  1793. }
  1794. // Prev
  1795. if (pNode->pPrev)
  1796. {
  1797. prgNode[iNode].iPrev = DwSignNode(rInfo, pNode->pPrev->iCacheNode);
  1798. Assert(FVerifySignedNode(rInfo, prgNode[iNode].iPrev));
  1799. }
  1800. // Increment iNode
  1801. iNode++;
  1802. }
  1803. // Write the header...
  1804. Assert(sizeof(CACHEINFOV2) % 4 == 0 && rInfo.iRoot);
  1805. CHECKHR(hr = pStream->Write(&rInfo, sizeof(CACHEINFOV2), NULL));
  1806. // Write the nodes
  1807. CHECKHR(hr = pStream->Write(prgNode, cbNodes, NULL));
  1808. exit:
  1809. // Cleanup
  1810. SafeMemFree(prgNode);
  1811. // Thread Safety
  1812. LeaveCriticalSection(&m_cs);
  1813. // Done
  1814. return hr;
  1815. }
  1816. // --------------------------------------------------------------------------------
  1817. // CMessageTree::Commit
  1818. // --------------------------------------------------------------------------------
  1819. STDMETHODIMP CMessageTree::Commit(DWORD dwFlags)
  1820. {
  1821. // Locals
  1822. HRESULT hr=S_OK;
  1823. LPSTREAM pStream=NULL;
  1824. ULARGE_INTEGER uli;
  1825. // Thread Safety
  1826. EnterCriticalSection(&m_cs);
  1827. // Not Dirty and it has been saved into m_pStmLock
  1828. if (IsDirty() == S_FALSE && m_pStmLock)
  1829. goto exit;
  1830. // Reuse Storage
  1831. if (ISFLAGSET(dwFlags, COMMIT_REUSESTORAGE) && ISFLAGSET(m_dwState, TREESTATE_HANDSONSTORAGE) && m_pStmLock)
  1832. {
  1833. // Get the current stream from m_pStmLock
  1834. m_pStmLock->GetCurrentStream(&pStream);
  1835. // Hands off of current storage
  1836. CHECKHR(hr = HandsOffStorage());
  1837. // Rewind the stream
  1838. CHECKHR(hr = HrRewindStream(pStream));
  1839. // SetSize to Zero
  1840. INT64SET(&uli, 0);
  1841. pStream->SetSize(uli);
  1842. // Call Save Message
  1843. CHECKHR(hr = _HrWriteMessage(pStream, TRUE, FALSE, FALSE));
  1844. }
  1845. // Otherwise, I'll create my own storage
  1846. else
  1847. {
  1848. // Create a new stream
  1849. CHECKALLOC(pStream = new CVirtualStream);
  1850. // Call Save Message
  1851. CHECKHR(hr = _HrWriteMessage(pStream, TRUE, FALSE,
  1852. !!(dwFlags & COMMIT_SMIMETRANSFERENCODE)));
  1853. // Hands are off..
  1854. FLAGCLEAR(m_dwState, TREESTATE_HANDSONSTORAGE);
  1855. }
  1856. exit:
  1857. // Cleanup
  1858. SafeRelease(pStream);
  1859. // Thread Safety
  1860. LeaveCriticalSection(&m_cs);
  1861. // Done
  1862. return hr;
  1863. }
  1864. // --------------------------------------------------------------------------------
  1865. // CMessageTree::Save
  1866. // --------------------------------------------------------------------------------
  1867. STDMETHODIMP CMessageTree::Save(IStream *pStream, BOOL fClearDirty)
  1868. {
  1869. // Locals
  1870. HRESULT hr=S_OK;
  1871. HRESULT hrWarnings=S_OK;
  1872. // check params
  1873. if (pStream == NULL)
  1874. return TrapError(E_INVALIDARG);
  1875. // Thread Safety
  1876. EnterCriticalSection(&m_cs);
  1877. // Not dirty, and we have a stream $$$INFO$$ should be using m_pLockBytes here if we have one
  1878. if (IsDirty() == S_FALSE && m_pStmLock)
  1879. {
  1880. // Copy Lock Bytes to Stream
  1881. CHECKHR(hr = HrCopyLockBytesToStream(m_pStmLock, pStream, NULL));
  1882. // Commit
  1883. CHECKHR(hr = pStream->Commit(STGC_DEFAULT));
  1884. // Raid-33985: MIMEOLE: CMessageTree:Save does not respect fHandsOffOnSave == FALSE if the message is not dirty
  1885. if (FALSE == m_rOptions.fHandsOffOnSave)
  1886. {
  1887. // Replace internal stream
  1888. m_pStmLock->ReplaceInternalStream(pStream);
  1889. // Hands are on..
  1890. FLAGSET(m_dwState, TREESTATE_HANDSONSTORAGE);
  1891. }
  1892. // Were Done
  1893. goto exit;
  1894. }
  1895. // Write the message
  1896. CHECKHR(hr = _HrWriteMessage(pStream, fClearDirty, m_rOptions.fHandsOffOnSave, FALSE));
  1897. // Return Warnings
  1898. if (S_OK != hr)
  1899. hrWarnings = TrapError(hr);
  1900. exit:
  1901. // Thread Safety
  1902. LeaveCriticalSection(&m_cs);
  1903. // Done
  1904. return (hr == S_OK) ? hrWarnings : hr;
  1905. }
  1906. // --------------------------------------------------------------------------------
  1907. // CMessageTree::_HrWriteMessage
  1908. // --------------------------------------------------------------------------------
  1909. HRESULT CMessageTree::_HrWriteMessage(IStream *pStream, BOOL fClearDirty, BOOL fHandsOffOnSave, BOOL fSMimeCTE)
  1910. {
  1911. // Locals
  1912. HRESULT hr=S_OK;
  1913. HRESULT hrWarnings=S_OK;
  1914. MIMEPROPINFO rPropInfo;
  1915. DWORD dwSaveFlags;
  1916. INETCSETINFO rCharset;
  1917. LPINETCSETINFO pOriginal=NULL;
  1918. // This Function is re-entrant when saving a message that is signed and/or encrypted
  1919. if (FALSE == m_fApplySaveSecurity)
  1920. {
  1921. // Character Set Fixup
  1922. if (m_rOptions.pCharset)
  1923. {
  1924. // RAID-25300 - FE-J:Athena: Newsgroup article and mail sent with charset=_autodetect Internet Encoded and Windows Encoding are CPI_AUTODETECT
  1925. if (CP_JAUTODETECT == m_rOptions.pCharset->cpiInternet)
  1926. {
  1927. // Save Current Charset
  1928. pOriginal = m_rOptions.pCharset;
  1929. // Find ISO-2022-JP
  1930. SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(c_szISO2022JP, &m_rOptions.pCharset)));
  1931. }
  1932. // Raid-8436: OE: non standard MIME header is composed when send from Send as Unicode dialog, if UTF-7 or UTF-8, and not saving as mime...
  1933. else if (SAVE_RFC822 == m_rOptions.savetype && (CP_UTF7 == m_rOptions.pCharset->cpiInternet || CP_UTF8 == m_rOptions.pCharset->cpiInternet))
  1934. {
  1935. // Save Current Charset
  1936. pOriginal = m_rOptions.pCharset;
  1937. // Get the default body charset
  1938. if (FAILED(g_pInternat->HrOpenCharset(GetACP(), CHARSET_BODY, &m_rOptions.pCharset)))
  1939. m_rOptions.pCharset = NULL;
  1940. }
  1941. }
  1942. // State
  1943. m_fApplySaveSecurity = TRUE;
  1944. // Do Message Save Security
  1945. hr = _HrApplySaveSecurity();
  1946. // Not in Apply Save security
  1947. m_fApplySaveSecurity = FALSE;
  1948. // Failure
  1949. if (FAILED(hr))
  1950. goto exit;
  1951. }
  1952. // Cleanup the message (i.e. remove empty multiparts, multiparts that have a single child that is a multipart, TNEF)
  1953. if (TRUE == m_rOptions.fCleanupTree)
  1954. {
  1955. // Call Espiranza and have her do the cleaning
  1956. CHECKHR(hr = _HrCleanupMessageTree(m_pRootNode));
  1957. }
  1958. // Generate Message Id...
  1959. if (m_rOptions.fGenMessageId)
  1960. {
  1961. // Set the message Id
  1962. _HrSetMessageId(m_pRootNode);
  1963. }
  1964. // Determine if we are saving a News Message
  1965. rPropInfo.dwMask = 0;
  1966. if (SUCCEEDED(m_pRootNode->pContainer->GetPropInfo(PIDTOSTR(PID_HDR_XNEWSRDR), &rPropInfo)) ||
  1967. SUCCEEDED(m_pRootNode->pContainer->GetPropInfo(PIDTOSTR(PID_HDR_NEWSGROUPS), &rPropInfo)))
  1968. FLAGSET(m_dwState, TREESTATE_SAVENEWS);
  1969. // Set MIME Version
  1970. CHECKHR(hr = m_pRootNode->pContainer->SetProp(PIDTOSTR(PID_HDR_MIMEVER), c_szMimeVersion));
  1971. // X-MimeOLE Version
  1972. CHECKHR(hr = m_pRootNode->pContainer->SetProp(STR_HDR_XMIMEOLE, STR_MIMEOLE_VERSION));
  1973. // Remove Types...
  1974. m_pRootNode->pContainer->DeleteProp(STR_HDR_ENCODING);
  1975. // Root
  1976. m_pRootNode->boundary = BOUNDARY_ROOT;
  1977. m_pRootNode->cbBoundaryStart = 0;
  1978. // Set SaveBody Flags
  1979. dwSaveFlags = SAVEBODY_UPDATENODES;
  1980. if (m_rOptions.fKeepBoundary)
  1981. FLAGSET(dwSaveFlags, SAVEBODY_KEEPBOUNDARY);
  1982. if (fSMimeCTE)
  1983. FLAGSET(dwSaveFlags, SAVEBODY_SMIMECTE);
  1984. // Save Root body
  1985. CHECKHR(hr = _HrSaveBody(fClearDirty, dwSaveFlags, pStream, m_pRootNode, 0));
  1986. if ( S_OK != hr )
  1987. hrWarnings = TrapError(hr);
  1988. // Commit
  1989. CHECKHR(hr = pStream->Commit(STGC_DEFAULT));
  1990. // Hands Off On Save ?
  1991. if (FALSE == fHandsOffOnSave)
  1992. {
  1993. // Reset message size
  1994. CHECKHR(hr = HrSafeGetStreamSize(pStream, &m_cbMessage));
  1995. // Save this new stream
  1996. SafeRelease(m_pStmLock);
  1997. // Create a new Stream Lock Bytes Wrapper
  1998. CHECKALLOC(m_pStmLock = new CStreamLockBytes(pStream));
  1999. // Hands are on the storage
  2000. FLAGSET(m_dwState, TREESTATE_HANDSONSTORAGE);
  2001. }
  2002. // Debug to temp file...
  2003. DebugWriteMsg(pStream);
  2004. // Clear Dirty
  2005. if (fClearDirty)
  2006. ClearDirty();
  2007. exit:
  2008. // Reset Original Charset
  2009. if (pOriginal)
  2010. m_rOptions.pCharset = pOriginal;
  2011. // Remove state flag the tells us to reuse multipart/signed boundaries
  2012. FLAGCLEAR(m_dwState, TREESTATE_REUSESIGNBOUND);
  2013. // Reset
  2014. FLAGCLEAR(m_dwState, TREESTATE_SAVENEWS);
  2015. // Done
  2016. return (hr == S_OK) ? hrWarnings : hr;
  2017. }
  2018. // --------------------------------------------------------------------------------
  2019. // CMessageTree::_HrApplySaveSecurity
  2020. // --------------------------------------------------------------------------------
  2021. HRESULT CMessageTree::_HrApplySaveSecurity(void)
  2022. {
  2023. // Locals
  2024. HRESULT hr=S_OK;
  2025. PROPVARIANT var;
  2026. CSMime *pSMime=NULL;
  2027. // Invalid Arg
  2028. Assert(m_pRootNode);
  2029. m_pRootNode->pBody->GetOption(OID_NOSECURITY_ONSAVE, &var);
  2030. if (var.boolVal) goto exit;
  2031. // Query the root body for secure status
  2032. m_pRootNode->pBody->GetOption(OID_SECURITY_TYPE, &var);
  2033. if (MST_NONE != var.ulVal)
  2034. {
  2035. // Create the object
  2036. CHECKALLOC(pSMime = new CSMime);
  2037. // Initialize the object
  2038. CHECKHR(hr = pSMime->InitNew());
  2039. // Set state flag the tells us to reuse multipart/signed boundaries
  2040. FLAGSET(m_dwState, TREESTATE_REUSESIGNBOUND);
  2041. // Encode the message
  2042. CHECKHR(hr = pSMime->EncodeMessage(this, m_rOptions.ulSecIgnoreMask));
  2043. }
  2044. exit:
  2045. ReleaseObj(pSMime);
  2046. // Done
  2047. return hr;
  2048. }
  2049. // --------------------------------------------------------------------------------
  2050. // CMessageTree::_HrCleanupMessageTree
  2051. // --------------------------------------------------------------------------------
  2052. HRESULT CMessageTree::_HrCleanupMessageTree(LPTREENODEINFO pParent)
  2053. {
  2054. // Locals
  2055. HRESULT hr=S_OK;
  2056. LPTREENODEINFO pNode;
  2057. ULONG i;
  2058. BOOL fKeepOnTruckin=TRUE;
  2059. // check params
  2060. Assert(pParent);
  2061. // This could require multiple passes
  2062. while(fKeepOnTruckin)
  2063. {
  2064. // Assume we will not have to do another pass
  2065. fKeepOnTruckin = FALSE;
  2066. // Loop through bodies
  2067. for (i=0; i<m_rTree.cNodes; i++)
  2068. {
  2069. // Readability
  2070. pNode = m_rTree.prgpNode[i];
  2071. if (NULL == pNode)
  2072. continue;
  2073. // Hiding TNEF Attachments ?
  2074. if (TRUE == m_rOptions.fHideTnef && pNode->pContainer->IsContentType(STR_CNT_APPLICATION, STR_SUB_MSTNEF) == S_OK)
  2075. {
  2076. // Remove this TNEF attachment
  2077. CHECKHR(hr = DeleteBody(pNode->hBody, 0));
  2078. // Lets Stop right here, and start another pass
  2079. fKeepOnTruckin = TRUE;
  2080. // Done
  2081. break;
  2082. }
  2083. // Empty multipart... and not the root... ?
  2084. else if (_IsMultiPart(pNode))
  2085. {
  2086. // No Children ?
  2087. if (0 == pNode->cChildren)
  2088. {
  2089. // If this is the root...simply change the content type
  2090. if (m_pRootNode == pNode)
  2091. {
  2092. // Make the body empty
  2093. pNode->pBody->EmptyData();
  2094. // text/plain
  2095. pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN);
  2096. }
  2097. // Otherwise, delete the body
  2098. else
  2099. {
  2100. // Delete delete the body
  2101. CHECKHR(hr = DeleteBody(pNode->hBody, 0));
  2102. // Lets Stop right here, and start another pass
  2103. fKeepOnTruckin = TRUE;
  2104. // Done
  2105. break;
  2106. }
  2107. }
  2108. // Otherwise, Multipart with a single child...
  2109. else if (pNode->cChildren == 1)
  2110. {
  2111. // Do a ReplaceBody
  2112. CHECKHR(hr = DeleteBody(pNode->hBody, DELETE_PROMOTE_CHILDREN));
  2113. // Lets Stop right here, and start another pass
  2114. fKeepOnTruckin = TRUE;
  2115. // Done
  2116. break;
  2117. }
  2118. }
  2119. }
  2120. }
  2121. exit:
  2122. // Done
  2123. return hr;
  2124. }
  2125. // --------------------------------------------------------------------------------
  2126. // CMessageTree::SaveBody
  2127. // --------------------------------------------------------------------------------
  2128. STDMETHODIMP CMessageTree::SaveBody(HBODY hBody, DWORD dwFlags, IStream *pStream)
  2129. {
  2130. // Locals
  2131. HRESULT hr=S_OK;
  2132. LPTREENODEINFO pNode;
  2133. // Invalid ARg
  2134. if (NULL == pStream)
  2135. return TrapError(E_INVALIDARG);
  2136. // Thread Safety
  2137. EnterCriticalSection(&m_cs);
  2138. // Get body
  2139. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  2140. // Save From This Body On Down
  2141. CHECKHR(hr = _HrSaveBody(TRUE, dwFlags, pStream, pNode, 0));
  2142. exit:
  2143. // Thread Safety
  2144. LeaveCriticalSection(&m_cs);
  2145. // Done
  2146. return hr;
  2147. }
  2148. // --------------------------------------------------------------------------------
  2149. // CMessageTree::_HrSaveBody
  2150. // --------------------------------------------------------------------------------
  2151. HRESULT CMessageTree::_HrSaveBody(BOOL fClearDirty, DWORD dwFlags, IStream *pStream,
  2152. LPTREENODEINFO pNode, ULONG ulLevel)
  2153. {
  2154. // Locals
  2155. HRESULT hr=S_OK;
  2156. HRESULT hrWarnings=S_OK;
  2157. TREENODEINFO rOriginal;
  2158. BOOL fWeSetSaveBoundary=FALSE;
  2159. // Parameters
  2160. Assert(pStream && pNode);
  2161. if (ISFLAGSET(dwFlags, SAVEBODY_KEEPBOUNDARY))
  2162. {
  2163. if (!ISFLAGSET(m_dwState, TREESTATE_REUSESIGNBOUND))
  2164. {
  2165. fWeSetSaveBoundary = TRUE;
  2166. FLAGSET(m_dwState, TREESTATE_REUSESIGNBOUND);
  2167. }
  2168. }
  2169. // Save the Current Node
  2170. if (!ISFLAGSET(dwFlags, SAVEBODY_UPDATENODES))
  2171. CopyMemory(&rOriginal, pNode, sizeof(TREENODEINFO));
  2172. // Override Options
  2173. _HrBodyInheritOptions(pNode);
  2174. // Starting Boundary pNode->boundary and pNode->cbBoundaryStart are expected to be set on entry
  2175. pNode->cbHeaderStart = 0;
  2176. pNode->cbBodyStart = 0;
  2177. pNode->cbBodyEnd = 0;
  2178. // If this is a multipart content item, lets read its child
  2179. if (_IsMultiPart(pNode))
  2180. {
  2181. // Save Multipart Children
  2182. CHECKHR(hr = _HrSaveMultiPart(fClearDirty, dwFlags, pStream, pNode, ulLevel));
  2183. if ( S_OK != hr )
  2184. hrWarnings = TrapError(hr);
  2185. }
  2186. #ifdef SMIME_V3
  2187. // OID content types are saved by just copying the body into the save
  2188. // location.
  2189. else if (pNode->pContainer->IsContentType("OID", NULL) == S_OK)
  2190. {
  2191. CHECKHR(hr = pNode->pBody->GetDataHere(IET_BINARY, pStream));
  2192. if (hr != S_OK)
  2193. {
  2194. hrWarnings = TrapError(hr);
  2195. }
  2196. }
  2197. #endif // SMIME_V3
  2198. // Otherwise, parse single part
  2199. else
  2200. {
  2201. // Save SinglePart Children
  2202. CHECKHR(hr = _HrSaveSinglePart(fClearDirty, dwFlags, pStream, pNode, ulLevel));
  2203. if ( S_OK != hr )
  2204. hrWarnings = TrapError(hr);
  2205. }
  2206. // Reset the Node
  2207. if (!ISFLAGSET(dwFlags, SAVEBODY_UPDATENODES))
  2208. CopyMemory(pNode, &rOriginal, sizeof(TREENODEINFO));
  2209. exit:
  2210. if (fWeSetSaveBoundary)
  2211. FLAGCLEAR(m_dwState, TREESTATE_REUSESIGNBOUND);
  2212. // Done
  2213. return (hr == S_OK) ? hrWarnings : hr;
  2214. }
  2215. // --------------------------------------------------------------------------------
  2216. // CMessageTree::_HrSetMessageId
  2217. // --------------------------------------------------------------------------------
  2218. HRESULT CMessageTree::_HrSetMessageId(LPTREENODEINFO pNode)
  2219. {
  2220. // Locals
  2221. HRESULT hr= S_OK;
  2222. CHAR szMessageId[CCHMAX_MID];
  2223. FILETIME ft;
  2224. SYSTEMTIME st;
  2225. // Invalid Arg
  2226. Assert(pNode);
  2227. // Get Current Time
  2228. GetSystemTime(&st);
  2229. SystemTimeToFileTime(&st, &ft);
  2230. // Build MessageID
  2231. CHECKHR(hr = MimeOleGenerateMID(szMessageId, sizeof(szMessageId), FALSE));
  2232. // Write the message Id
  2233. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_MESSAGEID, szMessageId));
  2234. exit:
  2235. // Done
  2236. return hr;
  2237. }
  2238. // --------------------------------------------------------------------------------
  2239. // CMessageTree::_GenerateBoundary
  2240. // --------------------------------------------------------------------------------
  2241. void CMessageTree::_GenerateBoundary(LPSTR pszBoundary, DWORD cchSize, ULONG ulLevel)
  2242. {
  2243. // Locals
  2244. SYSTEMTIME stNow;
  2245. FILETIME ftNow;
  2246. WORD wCounter;
  2247. // Get Local Time
  2248. GetLocalTime(&stNow);
  2249. SystemTimeToFileTime(&stNow, &ftNow);
  2250. // Format the string
  2251. wnsprintfA(pszBoundary, cchSize, "----=_NextPart_%03d_%04X_%08.8lX.%08.8lX", ulLevel, DwCounterNext(), ftNow.dwHighDateTime, ftNow.dwLowDateTime);
  2252. }
  2253. // --------------------------------------------------------------------------------
  2254. // CMessageTree::_HrWriteBoundary
  2255. // --------------------------------------------------------------------------------
  2256. HRESULT CMessageTree::_HrWriteBoundary(LPSTREAM pStream, LPSTR pszBoundary, BOUNDARYTYPE boundary,
  2257. LPDWORD pcboffStart, LPDWORD pcboffEnd)
  2258. {
  2259. // Locals
  2260. HRESULT hr=S_OK;
  2261. DWORD cbBoundaryStart;
  2262. // Invalid Arg
  2263. Assert(pStream && pszBoundary);
  2264. // Header body CRLF
  2265. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2266. // Starting Boundary Start
  2267. if (pcboffStart)
  2268. CHECKHR(hr = HrGetStreamPos(pStream, pcboffStart));
  2269. // --
  2270. CHECKHR(hr = pStream->Write(c_szDoubleDash, lstrlen(c_szDoubleDash), NULL));
  2271. // Write the boundary
  2272. CHECKHR(hr = pStream->Write(pszBoundary, lstrlen(pszBoundary), NULL));
  2273. // If end
  2274. if (BOUNDARY_MIMEEND == boundary)
  2275. {
  2276. // Write ending double dash
  2277. CHECKHR(hr = pStream->Write(c_szDoubleDash, lstrlen(c_szDoubleDash), NULL));
  2278. }
  2279. // Otherwise, set pNode->cbBoundaryStart
  2280. else
  2281. Assert(BOUNDARY_MIMENEXT == boundary);
  2282. // Emit Line Break;
  2283. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2284. // BUG 38411: to be complient with RFC 1847 we have to include
  2285. // the last CRLF in the hash of a signed message. The S/MIME
  2286. // code relies on cbBodyEnd, so place this after the CRLF emit.
  2287. // Ending offset
  2288. if (pcboffEnd)
  2289. CHECKHR(hr = HrGetStreamPos(pStream, pcboffEnd));
  2290. exit:
  2291. // Done
  2292. return hr;
  2293. }
  2294. // --------------------------------------------------------------------------------
  2295. // CMessageTree::_HrComputeBoundary
  2296. // --------------------------------------------------------------------------------
  2297. HRESULT CMessageTree::_HrComputeBoundary(LPTREENODEINFO pNode, ULONG ulLevel, LPSTR pszBoundary, LONG cchMax)
  2298. {
  2299. // Locals
  2300. HRESULT hr=S_OK;
  2301. BOOL fGenerate=TRUE;
  2302. LPSTR pszCurrent=NULL;
  2303. // If reusing tree boundaries...
  2304. if (ISFLAGSET(m_dwState, TREESTATE_REUSESIGNBOUND))
  2305. {
  2306. // (this is required for multipart/signed -- t-erikne)
  2307. if (SUCCEEDED(pNode->pContainer->GetProp(SYM_PAR_BOUNDARY, &pszCurrent)))
  2308. {
  2309. // Better fit into cchMax
  2310. if (lstrlen(pszCurrent) <= cchMax - 1)
  2311. {
  2312. // Copy it to the out param
  2313. StrCpyN(pszBoundary, pszCurrent, cchMax);
  2314. // Don't generate
  2315. fGenerate = FALSE;
  2316. }
  2317. }
  2318. }
  2319. // Generate a boundary ?
  2320. if (TRUE == fGenerate)
  2321. {
  2322. // Generate boundary
  2323. _GenerateBoundary(pszBoundary, cchMax, ulLevel);
  2324. // Set the boundary property...
  2325. CHECKHR(hr = pNode->pContainer->SetProp(SYM_PAR_BOUNDARY, pszBoundary));
  2326. }
  2327. exit:
  2328. // Cleanup
  2329. SafeMemFree(pszCurrent);
  2330. // Done
  2331. return hr;
  2332. }
  2333. // --------------------------------------------------------------------------------
  2334. // CMessageTree::_HrSaveMultiPart
  2335. // --------------------------------------------------------------------------------
  2336. HRESULT CMessageTree::_HrSaveMultiPart(BOOL fClearDirty, DWORD dwFlags, LPSTREAM pStream,
  2337. LPTREENODEINFO pNode, ULONG ulLevel)
  2338. {
  2339. // Locals
  2340. HRESULT hr=S_OK;
  2341. HRESULT hrWarnings=S_OK;
  2342. CHAR szRes[100];
  2343. CHAR szBoundary[CCHMAX_BOUNDARY];
  2344. LPTREENODEINFO pChild;
  2345. LPSTR pszBoundary=NULL;
  2346. // Invalid Arg
  2347. Assert(pStream && pNode);
  2348. // MIME
  2349. if (SAVE_RFC1521 == m_rOptions.savetype)
  2350. {
  2351. // Remove Fake Multipart flag, its a real multipart now...
  2352. FLAGCLEAR(pNode->dwType, NODETYPE_FAKEMULTIPART);
  2353. FLAGCLEAR(pNode->dwType, NODETYPE_RFC1154_ROOT);
  2354. FLAGCLEAR(pNode->dwType, NODETYPE_RFC1154_BINHEX);
  2355. // HrComputeBoundary
  2356. CHECKHR(hr = _HrComputeBoundary(pNode, ulLevel, szBoundary, ARRAYSIZE(szBoundary)));
  2357. // Delete any charset information (doesn't make sense on a multipart)
  2358. pNode->pContainer->DeleteProp(SYM_PAR_CHARSET);
  2359. // Write the header
  2360. CHECKHR(hr = _HrWriteHeader(fClearDirty, pStream, pNode));
  2361. // Remove SMIME_CTE for Multipart/signed
  2362. if ((pNode->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_SIGNED) == S_OK) &&
  2363. (pNode->cChildren == 2))
  2364. {
  2365. FLAGCLEAR(dwFlags, SAVEBODY_SMIMECTE);
  2366. FLAGSET(dwFlags, SAVEBODY_REUSECTE);
  2367. }
  2368. // Multipart-Preamble
  2369. if (0 == ulLevel)
  2370. {
  2371. LoadString(g_hLocRes, IDS_MULTIPARTPROLOG, szRes, ARRAYSIZE(szRes));
  2372. CHECKHR(hr = pStream->Write(szRes, lstrlen(szRes), NULL));
  2373. }
  2374. // Increment Level
  2375. ulLevel++;
  2376. // Loop Chilren
  2377. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  2378. {
  2379. // Check body
  2380. Assert(pChild->pParent == pNode);
  2381. // Set Boundary
  2382. pChild->boundary = BOUNDARY_MIMENEXT;
  2383. // Write Boundary
  2384. CHECKHR(hr = _HrWriteBoundary(pStream, szBoundary, BOUNDARY_MIMENEXT, &pChild->cbBoundaryStart, NULL));
  2385. // Bind the body table for this dude
  2386. CHECKHR(hr = _HrSaveBody(fClearDirty, dwFlags, pStream, pChild, ulLevel));
  2387. if ( S_OK != hr )
  2388. hrWarnings = TrapError(hr);
  2389. }
  2390. // Write Ending Boundary
  2391. CHECKHR(hr = _HrWriteBoundary(pStream, szBoundary, BOUNDARY_MIMEEND, NULL, &pNode->cbBodyEnd));
  2392. }
  2393. // Otherwise, SAVE_RFC822
  2394. else
  2395. {
  2396. // Only write UUENCODED root header...
  2397. if (0 == ulLevel)
  2398. {
  2399. // Write the header
  2400. CHECKHR(hr = _HrWriteHeader(fClearDirty, pStream, pNode));
  2401. }
  2402. // Increment Level
  2403. ulLevel++;
  2404. // Now its a fakemultipart...
  2405. FLAGSET(pNode->dwType, NODETYPE_FAKEMULTIPART);
  2406. // Loop Chilren
  2407. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  2408. {
  2409. // Check body
  2410. Assert(pChild->pParent == pNode);
  2411. // Bind the body table for this dude
  2412. CHECKHR(hr = _HrSaveBody(fClearDirty, dwFlags, pStream, pChild, ulLevel));
  2413. if ( S_OK != hr )
  2414. hrWarnings = TrapError(hr);
  2415. }
  2416. // Body Start...
  2417. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyEnd));
  2418. }
  2419. exit:
  2420. // Done
  2421. return (hr == S_OK) ? hrWarnings : hr;
  2422. }
  2423. // --------------------------------------------------------------------------------
  2424. // CMessageTree::_HrWriteHeader
  2425. // --------------------------------------------------------------------------------
  2426. HRESULT CMessageTree::_HrWriteHeader(BOOL fClearDirty, IStream *pStream, LPTREENODEINFO pNode)
  2427. {
  2428. // Locals
  2429. HRESULT hr=S_OK;
  2430. // Invalid Arg
  2431. Assert(pStream && pNode);
  2432. // Better be the root
  2433. Assert(pNode->boundary == BOUNDARY_ROOT || pNode->boundary == BOUNDARY_MIMENEXT ||
  2434. pNode->boundary == BOUNDARY_NONE);
  2435. // Get current stream position
  2436. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbHeaderStart));
  2437. // Write the header...
  2438. CHECKHR(hr = pNode->pContainer->Save(pStream, fClearDirty));
  2439. // Header body CRLF
  2440. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2441. // Get Header End
  2442. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyStart));
  2443. exit:
  2444. // Done
  2445. return hr;
  2446. }
  2447. // --------------------------------------------------------------------------------
  2448. // CMessageTree::_GetContentTransferEncoding
  2449. // --------------------------------------------------------------------------------
  2450. HRESULT CMessageTree::_GetContentTransferEncoding(LPTREENODEINFO pNode, BOOL fText,
  2451. BOOL fPlain, BOOL fMessage, BOOL fAttachment, DWORD dwFlags,
  2452. ENCODINGTYPE *pietEncoding)
  2453. {
  2454. // Locals
  2455. HRESULT hr=S_OK;
  2456. HRESULT hrWarnings=S_OK;
  2457. TRANSMITINFO rXmitInfo;
  2458. PROPVARIANT rOption;
  2459. *pietEncoding=IET_UNKNOWN;
  2460. if (ISFLAGSET(dwFlags, SAVEBODY_REUSECTE))
  2461. {
  2462. pNode->pBody->GetPreviousEncoding(pietEncoding);
  2463. if (*pietEncoding != IET_UNKNOWN)
  2464. goto exit;
  2465. }
  2466. // If mesage/*, always use 7bit
  2467. if (fMessage)
  2468. {
  2469. // Don't Wrap It
  2470. rOption.vt = VT_BOOL;
  2471. rOption.boolVal = FALSE;
  2472. pNode->pBody->SetOption(OID_WRAP_BODY_TEXT, &rOption);
  2473. // Set Encoding
  2474. *pietEncoding = (SAVE_RFC1521 == m_rOptions.savetype) ? IET_7BIT : IET_UUENCODE;
  2475. // Done
  2476. goto smimeExit;
  2477. }
  2478. // Use Option for text transmit format
  2479. if (fText && !fAttachment)
  2480. {
  2481. // Default to plain text encoding first
  2482. if (IET_UNKNOWN != m_rOptions.ietTextXmit)
  2483. *pietEncoding = m_rOptions.ietTextXmit;
  2484. // Plain
  2485. if (IET_UNKNOWN != m_rOptions.ietPlainXmit && pNode->pContainer->IsContentType(NULL, STR_SUB_PLAIN) == S_OK)
  2486. *pietEncoding = m_rOptions.ietPlainXmit;
  2487. // Html
  2488. else if (IET_UNKNOWN != m_rOptions.ietHtmlXmit && pNode->pContainer->IsContentType(NULL, STR_SUB_HTML) == S_OK)
  2489. *pietEncoding = m_rOptions.ietHtmlXmit;
  2490. }
  2491. // Not known yet, using body option...
  2492. if (IET_UNKNOWN == *pietEncoding)
  2493. {
  2494. // Try to get the body option
  2495. if (SUCCEEDED(pNode->pBody->GetOption(OID_TRANSMIT_BODY_ENCODING, &rOption)) && IET_UNKNOWN != rOption.ulVal)
  2496. *pietEncoding = (ENCODINGTYPE)rOption.ulVal;
  2497. }
  2498. // Save as MIME
  2499. if (SAVE_RFC1521 == m_rOptions.savetype)
  2500. {
  2501. // Get the current encoding of the body..
  2502. if (IET_UNKNOWN == *pietEncoding)
  2503. pNode->pBody->GetCurrentEncoding(pietEncoding);
  2504. // If CTE is IET_QP or IET_BASE64 or IET_UUENCODE, were done
  2505. if (IET_QP == *pietEncoding || IET_BASE64 == *pietEncoding || IET_UUENCODE == *pietEncoding)
  2506. goto exit;
  2507. // Ask the pody to suggest an ietEncoding
  2508. hr = pNode->pBody->GetTransmitInfo(&rXmitInfo);
  2509. if (SUCCEEDED(hr) )
  2510. {
  2511. if ( S_OK != hr )
  2512. hrWarnings = TrapError(hr);
  2513. // Must not need wrapping
  2514. if (IET_7BIT == rXmitInfo.ietXmitMime)
  2515. {
  2516. rOption.vt = VT_BOOL;
  2517. rOption.boolVal = FALSE;
  2518. pNode->pBody->SetOption(OID_WRAP_BODY_TEXT, &rOption);
  2519. }
  2520. // If IET_7BIT and there are 8bit chars, bump upto 8bit
  2521. if (IET_7BIT == *pietEncoding || IET_8BIT == *pietEncoding)
  2522. {
  2523. // 8bit
  2524. *pietEncoding = (rXmitInfo.cExtended > 0) ? IET_8BIT : IET_7BIT;
  2525. }
  2526. // Just use the suggested mime cte from GetTransmitInfo
  2527. else
  2528. *pietEncoding = rXmitInfo.ietXmitMime;
  2529. }
  2530. // Transmit ietEncoding still unknown
  2531. else
  2532. *pietEncoding = (IET_UNKNOWN == *pietEncoding) ? (fText ? IET_QP : IET_BASE64) : *pietEncoding;
  2533. }
  2534. // Save a non-MIME
  2535. else
  2536. {
  2537. // If I already know this body is TREENODE_INCOMPLETE, it will be 7bit...
  2538. if (ISFLAGSET(pNode->dwType, NODETYPE_INCOMPLETE))
  2539. {
  2540. // No Encoding
  2541. *pietEncoding = IET_7BIT;
  2542. // Tell the body that its 7bit
  2543. pNode->pBody->SetCurrentEncoding(IET_7BIT);
  2544. }
  2545. // Raid 41599 - lost/munged attachments on forward/uuencode - Text attachments were not
  2546. // getting encoded when: *pietEncoding = (fText && fPlain) ? IET_7BIT : IET_UUENCODE;
  2547. else
  2548. *pietEncoding = (fText && fPlain && !fAttachment) ? IET_7BIT : IET_UUENCODE;
  2549. }
  2550. // If we are doing S/MIME at this point, we need to make sure that the
  2551. // content encoding rules for S/MIME are followed. Specifically we
  2552. // want to make sure that binary and 8bit are not allowed.
  2553. smimeExit:
  2554. if (ISFLAGSET(dwFlags, SAVEBODY_SMIMECTE))
  2555. {
  2556. if (*pietEncoding == IET_8BIT)
  2557. *pietEncoding = IET_QP;
  2558. if (*pietEncoding == IET_BINARY)
  2559. *pietEncoding = IET_BASE64;
  2560. }
  2561. exit:
  2562. // Done
  2563. return (hr == S_OK) ? hrWarnings : hr;
  2564. }
  2565. // --------------------------------------------------------------------------------
  2566. // CMessageTree::_HrWriteUUFileName
  2567. // --------------------------------------------------------------------------------
  2568. HRESULT CMessageTree::_HrWriteUUFileName(IStream *pStream, LPTREENODEINFO pNode)
  2569. {
  2570. // Locals
  2571. HRESULT hr=S_OK;
  2572. PROPVARIANT rFileName;
  2573. // Init rFileName
  2574. ZeroMemory(&rFileName, sizeof(PROPVARIANT));
  2575. rFileName.vt = VT_LPSTR;
  2576. // RAID-22479: FE-J:Athena:SJIS is used on file name on the message source with Uuencode/JIS.
  2577. if (FAILED(pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_GENFNAME), PDF_ENCODED, &rFileName)))
  2578. {
  2579. // Write the filename
  2580. CHECKHR(hr = pStream->Write(c_szUUENCODE_DAT, lstrlen(c_szUUENCODE_DAT), NULL));
  2581. // Done
  2582. goto exit;
  2583. }
  2584. // Write the filename
  2585. CHECKHR(hr = pStream->Write(rFileName.pszVal, lstrlen(rFileName.pszVal), NULL));
  2586. exit:
  2587. // Cleanup
  2588. MimeOleVariantFree(&rFileName);
  2589. // Done
  2590. return hr;
  2591. }
  2592. // --------------------------------------------------------------------------------
  2593. // CMessageTree::_HrSaveSinglePart
  2594. // --------------------------------------------------------------------------------
  2595. HRESULT CMessageTree::_HrSaveSinglePart(BOOL fClearDirty, DWORD dwFlags, LPSTREAM pStream,
  2596. LPTREENODEINFO pNode, ULONG ulLevel)
  2597. {
  2598. // Locals
  2599. HRESULT hr=S_OK;
  2600. HRESULT hrWarnings=S_OK;
  2601. LPSTR pszFileName=NULL;
  2602. BOOL fText=FALSE;
  2603. BOOL fMessage=FALSE;
  2604. BOOL fAttachment=FALSE;
  2605. ENCODINGTYPE ietEncoding;
  2606. BOOL fPlain=FALSE;
  2607. PROPVARIANT val;
  2608. LPINETCSETINFO pTaggedCset=NULL;
  2609. // Invalid Arg
  2610. Assert(pStream && pNode);
  2611. // Text/Plain
  2612. if (pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN) == S_OK)
  2613. fText = fPlain = TRUE;
  2614. // Text Body
  2615. else if (pNode->pContainer->IsContentType(STR_CNT_TEXT, NULL) == S_OK)
  2616. fText = TRUE;
  2617. // Message/*
  2618. else if (pNode->pContainer->IsContentType(STR_CNT_MESSAGE, NULL) == S_OK)
  2619. {
  2620. // We have a message
  2621. fMessage = TRUE;
  2622. fAttachment = TRUE;
  2623. }
  2624. // fAttachment has not been set yet
  2625. if (!fAttachment)
  2626. {
  2627. fAttachment = (pNode->pContainer->QueryProp(SYM_HDR_CNTDISP, STR_DIS_ATTACHMENT, FALSE, FALSE) == S_OK ||
  2628. pNode->pContainer->IsPropSet(PIDTOSTR(PID_PAR_FILENAME)) == S_OK ||
  2629. pNode->pContainer->IsPropSet(PIDTOSTR(PID_PAR_NAME)) == S_OK);
  2630. }
  2631. // Get Content Transfer Encoding
  2632. hr = _GetContentTransferEncoding(pNode, fText, fPlain, fMessage, fAttachment,
  2633. dwFlags, &ietEncoding);
  2634. if ( S_OK != hr )
  2635. hrWarnings = TrapError(hr);
  2636. // Sanity Check
  2637. Assert(ietEncoding != IET_UNKNOWN && (SAVE_RFC1521 == m_rOptions.savetype || SAVE_RFC822 == m_rOptions.savetype));
  2638. // Set Content-Transfer-Encoding...
  2639. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTXFER, g_rgEncodingMap[ietEncoding].pszName));
  2640. pNode->pBody->SetPreviousEncoding(ietEncoding);
  2641. // Compute Character Set for the message...
  2642. if (m_rOptions.pCharset && TRUE == fText && (FALSE == fAttachment || S_OK == pNode->pBody->IsType(IBT_CSETTAGGED)))
  2643. {
  2644. #if 0 // Raid-69667: OE5: Kor: Only the charset, euc-kr, is used for news message
  2645. // ISO-2022-KR -> EUC-KR for News text/plain
  2646. if (ISFLAGSET(m_dwState, TREESTATE_SAVENEWS) && 949 == m_rOptions.pCharset->cpiWindows && pNode->pContainer->IsContentType(NULL, STR_SUB_PLAIN) == S_OK)
  2647. {
  2648. // Locals
  2649. LPINETCSETINFO pEUCKR;
  2650. // Find EUC-KR
  2651. if (SUCCEEDED(g_pInternat->HrOpenCharset("EUC-KR", &pEUCKR)))
  2652. pNode->pBody->SetCharset(pEUCKR->hCharset, CSET_APPLY_UNTAGGED);
  2653. }
  2654. // Otherwise, use current charset
  2655. else
  2656. #endif // Raid-69667: OE5: Kor: Only the charset, euc-kr, is used for news message
  2657. // Store the Character set
  2658. pNode->pBody->SetCharset(m_rOptions.pCharset->hCharset, m_rOptions.csetapply);
  2659. // Get original charset
  2660. if (fAttachment && S_OK == pNode->pBody->IsType(IBT_CSETTAGGED))
  2661. {
  2662. // Get the tagged charset
  2663. pTaggedCset = pNode->pBody->PGetTaggedCset();
  2664. // Set the charset property
  2665. pNode->pContainer->SetProp(PIDTOSTR(PID_PAR_CHARSET), pTaggedCset->szName);
  2666. // Remove the CSETTAGGED state, and then reset it after we write the body
  2667. // This will keep the body from being character set converted
  2668. pNode->pBody->ClearState(BODYSTATE_CSETTAGGED);
  2669. }
  2670. }
  2671. // Otherwise, remove the charset parameter, we don't encode attachments in a character set
  2672. else
  2673. {
  2674. // Remove the CharacterSet parameter from the body
  2675. pNode->pContainer->DeleteProp(SYM_PAR_CHARSET);
  2676. }
  2677. // Write the header...
  2678. if (SAVE_RFC1521 == m_rOptions.savetype || 0 == ulLevel)
  2679. {
  2680. // Write the header
  2681. CHECKHR(hr = _HrWriteHeader(fClearDirty, pStream, pNode));
  2682. }
  2683. // Determine the send ietEncoding
  2684. if (SAVE_RFC1521 == m_rOptions.savetype)
  2685. {
  2686. // Write body data into the stream
  2687. CHECKHR(hr = pNode->pBody->GetDataHere(ietEncoding, pStream));
  2688. if ( S_OK != hr )
  2689. hrWarnings = TrapError(hr);
  2690. // Body End...
  2691. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyEnd));
  2692. }
  2693. // Otherwise, SAVE_RFC822
  2694. else if (SAVE_RFC822 == m_rOptions.savetype && IET_UUENCODE == ietEncoding)
  2695. {
  2696. // Starting boundary/header
  2697. if (ulLevel > 0)
  2698. pNode->boundary = BOUNDARY_UUBEGIN;
  2699. // Start new line
  2700. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2701. // Get Boundary Start
  2702. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBoundaryStart));
  2703. // Header Start and boundary start are the same
  2704. if (ulLevel > 0)
  2705. pNode->cbHeaderStart = pNode->cbBoundaryStart;
  2706. // Write begin
  2707. CHECKHR(hr = pStream->Write(c_szUUENCODE_BEGIN, lstrlen(c_szUUENCODE_BEGIN), NULL));
  2708. // Write the file permission
  2709. CHECKHR(hr = pStream->Write(c_szUUENCODE_666, lstrlen(c_szUUENCODE_666), NULL));
  2710. // Write UU File Name
  2711. CHECKHR(hr = _HrWriteUUFileName(pStream, pNode));
  2712. // Start new line
  2713. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2714. // Get Header End
  2715. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyStart));
  2716. // Write the data
  2717. CHECKHR(hr = pNode->pBody->GetDataHere(IET_UUENCODE, pStream));
  2718. if ( S_OK != hr )
  2719. hrWarnings = TrapError(hr);
  2720. // Body End...
  2721. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyEnd));
  2722. // Write end
  2723. CHECKHR(hr = pStream->Write(c_szUUENCODE_END, lstrlen(c_szUUENCODE_END), NULL));
  2724. }
  2725. // Otherwise, SAVE_RFC822 and IET_7BIT
  2726. else if (SAVE_RFC822 == m_rOptions.savetype && IET_7BIT == ietEncoding)
  2727. {
  2728. // Get Boundary Start....
  2729. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyStart));
  2730. // Starting boundary/header
  2731. if (ulLevel > 0)
  2732. {
  2733. // No Boundary
  2734. pNode->boundary = BOUNDARY_NONE;
  2735. // Boundoff
  2736. pNode->cbBoundaryStart = pNode->cbBodyStart;
  2737. // Same as header start
  2738. pNode->cbHeaderStart = pNode->cbBoundaryStart;
  2739. }
  2740. // Write the data
  2741. CHECKHR(hr = pNode->pBody->GetDataHere(IET_7BIT, pStream));
  2742. if ( S_OK != hr )
  2743. hrWarnings = TrapError(hr);
  2744. // Write final crlf
  2745. CHECKHR(hr = pStream->Write(c_szCRLF, lstrlen(c_szCRLF), NULL));
  2746. // Body End...
  2747. CHECKHR(hr = HrGetStreamPos(pStream, &pNode->cbBodyEnd));
  2748. }
  2749. // Otherwise...
  2750. else
  2751. AssertSz(FALSE, "A body is about to be lost. Contact sbailey at x32553 NOW!!!");
  2752. exit:
  2753. // Try to fixup the body
  2754. if (pTaggedCset)
  2755. {
  2756. pNode->pBody->SetCharset(pTaggedCset->hCharset, CSET_APPLY_UNTAGGED);
  2757. pNode->pBody->SetState(BODYSTATE_CSETTAGGED);
  2758. }
  2759. // Free BodyInfo
  2760. SafeMemFree(pszFileName);
  2761. // Done
  2762. return (hr == S_OK) ? hrWarnings : hr;
  2763. }
  2764. // --------------------------------------------------------------------------------
  2765. // CMessageTree::_HrBodyInheritOptions
  2766. // --------------------------------------------------------------------------------
  2767. HRESULT CMessageTree::_HrBodyInheritOptions(LPTREENODEINFO pNode)
  2768. {
  2769. // Locals
  2770. HRESULT hr=S_OK;
  2771. PROPVARIANT rValue;
  2772. // Invalid ARg
  2773. Assert(pNode);
  2774. // Allow 8bit in header
  2775. rValue.boolVal = m_rOptions.fAllow8bitHeader;
  2776. CHECKHR(hr = pNode->pBody->SetOption(OID_ALLOW_8BIT_HEADER, &rValue));
  2777. // Wrap Body Text
  2778. rValue.boolVal = m_rOptions.fWrapBodyText;
  2779. CHECKHR(hr = pNode->pBody->SetOption(OID_WRAP_BODY_TEXT, &rValue));
  2780. // Max Header Line
  2781. rValue.ulVal = m_rOptions.cchMaxHeaderLine;
  2782. CHECKHR(hr = pNode->pBody->SetOption(OID_CBMAX_HEADER_LINE, &rValue));
  2783. // Persist Type
  2784. rValue.ulVal = (ULONG)m_rOptions.savetype;
  2785. CHECKHR(hr = pNode->pBody->SetOption(OID_SAVE_FORMAT, &rValue));
  2786. // Max Body Line
  2787. rValue.ulVal = m_rOptions.cchMaxBodyLine;
  2788. CHECKHR(hr = pNode->pBody->SetOption(OID_CBMAX_BODY_LINE, &rValue));
  2789. exit:
  2790. // Done
  2791. return hr;
  2792. }
  2793. // --------------------------------------------------------------------------------
  2794. // CMessageTree::Load
  2795. // --------------------------------------------------------------------------------
  2796. STDMETHODIMP CMessageTree::Load(IStream *pStream)
  2797. {
  2798. // Locals
  2799. HRESULT hr=S_OK;
  2800. HRESULT hrWarnings=S_OK;
  2801. ULONG i;
  2802. HCHARSET hCharset;
  2803. PROPVARIANT rVersion;
  2804. STGMEDIUM rMedium;
  2805. // check params
  2806. if (NULL == pStream)
  2807. return TrapError(E_INVALIDARG);
  2808. // Thread Safety
  2809. EnterCriticalSection(&m_cs);
  2810. // Assume the Bind has Finished
  2811. FLAGCLEAR(m_dwState, TREESTATE_BINDDONE);
  2812. // Release m_pStmLock
  2813. SafeRelease(m_pStmLock);
  2814. // Do I have a tree already...
  2815. if (!ISFLAGSET(m_dwState, TREESTATE_LOADED) || FAILED(_HrBindOffsetTable(pStream, &m_pStmLock)))
  2816. {
  2817. // InitNew
  2818. CHECKHR(hr = _HrLoadInitNew());
  2819. // Use file
  2820. if (m_rOptions.fBindUseFile)
  2821. FLAGSET(m_dwState, TREESTATE_BINDUSEFILE);
  2822. // If this fails, I assume the clients stream is already rewound and they don't support this
  2823. HrRewindStream(pStream);
  2824. // Fake OnStartBinding
  2825. OnStartBinding(0, NULL);
  2826. // Setup the Storage Medium
  2827. ZeroMemory(&rMedium, sizeof(STGMEDIUM));
  2828. rMedium.tymed = TYMED_ISTREAM;
  2829. rMedium.pstm = pStream;
  2830. // Fake OnDataAvailable
  2831. OnDataAvailable(BSCF_LASTDATANOTIFICATION, 0, NULL, &rMedium);
  2832. // Fake OnStartBinding
  2833. OnStopBinding(S_OK, NULL);
  2834. // If bind failed, return warnings
  2835. if (FAILED(m_hrBind))
  2836. hrWarnings = MIME_S_INVALID_MESSAGE;
  2837. }
  2838. // Otherwise, we are finished binding
  2839. else
  2840. {
  2841. // HandleCanInlineTextOption
  2842. _HandleCanInlineTextOption();
  2843. // Bind Finished
  2844. FLAGSET(m_dwState, TREESTATE_BINDDONE);
  2845. // DispatchBindRequest
  2846. _HrProcessPendingUrlRequests();
  2847. }
  2848. // Assume the stream
  2849. Assert(m_pStmLock);
  2850. // Allow for zero-byte stream to be Loaded
  2851. if (m_cbMessage)
  2852. {
  2853. // Is MIME ?
  2854. rVersion.vt = VT_UI4;
  2855. if (SUCCEEDED(m_pRootNode->pContainer->GetProp(PIDTOSTR(PID_HDR_MIMEVER), 0, &rVersion)))
  2856. {
  2857. // Its a Mime Message
  2858. m_rOptions.savetype = SAVE_RFC1521;
  2859. // Invalid Version
  2860. if (rVersion.ulVal != TREE_MIMEVERSION)
  2861. hrWarnings = MIME_S_MIME_VERSION;
  2862. }
  2863. // Otherwise, savetype should default to rfc822
  2864. else
  2865. m_rOptions.savetype = SAVE_RFC822;
  2866. // Detect Partials and Set FileName/Encoding Correctly
  2867. _FuzzyPartialRecognition(m_rOptions.savetype == SAVE_RFC822 ? FALSE : TRUE);
  2868. // Bind All Bodies to the Tree
  2869. for (i=0; i<m_rTree.cNodes; i++)
  2870. {
  2871. // Readability - Should not have any deleted bodies yet
  2872. if (m_rTree.prgpNode[i] && !ISFLAGSET(m_rTree.prgpNode[i]->dwState, NODESTATE_BOUNDTOTREE))
  2873. {
  2874. // BindState is done
  2875. m_rTree.prgpNode[i]->bindstate = BINDSTATE_COMPLETE;
  2876. // Bind to the tree
  2877. CHECKHR(hr = m_rTree.prgpNode[i]->pBody->HrBindToTree(m_pStmLock, m_rTree.prgpNode[i]));
  2878. }
  2879. }
  2880. // Determine the dominent charcter set of the message
  2881. if (SUCCEEDED(_HrGetCharsetTree(m_pRootNode, &hCharset)) && hCharset)
  2882. {
  2883. // Apply Charset to Untagged bodies
  2884. SetCharset(hCharset, CSET_APPLY_UNTAGGED);
  2885. }
  2886. }
  2887. #ifdef DEBUG
  2888. // Write X-Mailer or X-NewsReader
  2889. DebugWriteXClient();
  2890. #endif
  2891. // My hands are on the storage
  2892. FLAGSET(m_dwState, TREESTATE_HANDSONSTORAGE);
  2893. // Dirty
  2894. ClearDirty();
  2895. exit:
  2896. // Thread Safety
  2897. LeaveCriticalSection(&m_cs);
  2898. // Done
  2899. return (hr == S_OK) ? hrWarnings : hr;
  2900. }
  2901. // --------------------------------------------------------------------------------
  2902. // CMessageTree::_HandleCanInlineTextOption
  2903. // --------------------------------------------------------------------------------
  2904. void CMessageTree::_HandleCanInlineTextOption(void)
  2905. {
  2906. // Locals
  2907. FINDBODY rFind;
  2908. HBODY hMixed;
  2909. LPTREENODEINFO pNode;
  2910. LPTREENODEINFO pChild;
  2911. LPTREENODEINFO pText=NULL;
  2912. // Only do this if the client doesn't support inlining multiple text bodies, such as Outlook Express
  2913. if (TRUE == m_rOptions.fCanInlineText)
  2914. return;
  2915. // Raid 53456: mail: We should be displaying the plain text portion and making enriched text an attachment for attached msg
  2916. // Raid 53470: mail: We are not forwarding the attachment in the attached message
  2917. // I am going to find the first multipart/mixed section, then find the first text/plain body, and then
  2918. // mark all of the text/*, non-attachment bodies after that as attachments
  2919. ZeroMemory(&rFind, sizeof(FINDBODY));
  2920. rFind.pszPriType = (LPSTR)STR_CNT_MULTIPART;
  2921. rFind.pszSubType = (LPSTR)STR_SUB_MIXED;
  2922. // Find First
  2923. if (FAILED(FindFirst(&rFind, &hMixed)))
  2924. goto exit;
  2925. // Get node for hMixed
  2926. pNode = _PNodeFromHBody(hMixed);
  2927. // Loop
  2928. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  2929. {
  2930. // Not an attachment
  2931. if (S_FALSE == pChild->pBody->IsType(IBT_ATTACHMENT))
  2932. {
  2933. // Is text/plain
  2934. if (S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN) ||
  2935. S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_HTML))
  2936. {
  2937. pText = pChild;
  2938. break;
  2939. }
  2940. }
  2941. }
  2942. // If we found a text body
  2943. if (NULL == pText)
  2944. goto exit;
  2945. // Loop through the children again
  2946. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  2947. {
  2948. // Is text/*
  2949. if (pChild != pText && S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, NULL) && S_FALSE == pChild->pBody->IsType(IBT_ATTACHMENT))
  2950. {
  2951. // Mark as attachment
  2952. pChild->pContainer->SetProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_ATTACHMENT);
  2953. // Set a special flag to denote it was converted to an attachment
  2954. FLAGSET(pChild->dwState, NODESTATE_AUTOATTACH);
  2955. }
  2956. }
  2957. exit:
  2958. // Done
  2959. return;
  2960. }
  2961. // --------------------------------------------------------------------------------
  2962. // CMessageTree::_HrBindOffsetTable
  2963. // --------------------------------------------------------------------------------
  2964. HRESULT CMessageTree::_HrBindOffsetTable(IStream *pStream, CStreamLockBytes **ppStmLock)
  2965. {
  2966. // Locals
  2967. HRESULT hr=S_OK;
  2968. ULONG cb;
  2969. CInternetStream cInternet;
  2970. // Init
  2971. *ppStmLock = NULL;
  2972. // Get Sizeof Stream
  2973. CHECKHR(hr = HrSafeGetStreamSize(pStream, &cb));
  2974. // Otherwise bind the body table
  2975. if (cb != m_cbMessage)
  2976. {
  2977. hr = TrapError(MIME_E_MSG_SIZE_DIFF);
  2978. goto exit;
  2979. }
  2980. // Init the Internet Stream
  2981. CHECKHR(hr = cInternet.HrInitNew(pStream));
  2982. // Fast Parse Body
  2983. CHECKHR(hr = _HrFastParseBody(&cInternet, m_pRootNode));
  2984. // Success, get the lockbytes from the internet stream
  2985. cInternet.GetLockBytes(ppStmLock);
  2986. exit:
  2987. // Done
  2988. return hr;
  2989. }
  2990. // --------------------------------------------------------------------------------
  2991. // CMessageTree::GetBodyOffsets
  2992. // --------------------------------------------------------------------------------
  2993. STDMETHODIMP CMessageTree::GetBodyOffsets(HBODY hBody, LPBODYOFFSETS pOffsets)
  2994. {
  2995. // Locals
  2996. HRESULT hr=S_OK;
  2997. LPTREENODEINFO pNode;
  2998. // Invalid Arg
  2999. if (NULL == hBody || NULL == pOffsets)
  3000. return TrapError(E_INVALIDARG);
  3001. // Thread Safety
  3002. EnterCriticalSection(&m_cs);
  3003. // Get body
  3004. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  3005. // No Data ?
  3006. CHECKHR(hr = pNode->pBody->GetOffsets(pOffsets));
  3007. exit:
  3008. // Thread Safety
  3009. LeaveCriticalSection(&m_cs);
  3010. // Done
  3011. return hr;
  3012. }
  3013. // --------------------------------------------------------------------------------
  3014. // CMessageTree::ClearDirty
  3015. // --------------------------------------------------------------------------------
  3016. void CMessageTree::ClearDirty(void)
  3017. {
  3018. // If Dirty...
  3019. FLAGCLEAR(m_dwState, TREESTATE_DIRTY);
  3020. // Loop through bodies and ask IMimeHeader's and IMimeBody's
  3021. for (ULONG i=0; i<m_rTree.cNodes; i++)
  3022. {
  3023. // If NULL...
  3024. if (NULL == m_rTree.prgpNode[i])
  3025. continue;
  3026. // Dirty Header...
  3027. m_rTree.prgpNode[i]->pBody->ClearDirty();
  3028. }
  3029. }
  3030. // --------------------------------------------------------------------------------
  3031. // CMessageTree::GetCharset
  3032. // --------------------------------------------------------------------------------
  3033. STDMETHODIMP CMessageTree::GetCharset(LPHCHARSET phCharset)
  3034. {
  3035. // Locals
  3036. HRESULT hr=S_OK;
  3037. HCHARSET hCharset;
  3038. // Check Params
  3039. if (NULL == phCharset)
  3040. return TrapError(E_INVALIDARG);
  3041. // Thread Safety
  3042. EnterCriticalSection(&m_cs);
  3043. // Init
  3044. *phCharset = NULL;
  3045. // Recurse the current tree
  3046. if (NULL == m_rOptions.pCharset && m_pRootNode)
  3047. {
  3048. // Get charset
  3049. if (SUCCEEDED(_HrGetCharsetTree(m_pRootNode, &hCharset)))
  3050. {
  3051. // Get Pointer to Charset
  3052. SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(hCharset, &m_rOptions.pCharset)));
  3053. }
  3054. }
  3055. // No Charset
  3056. if (NULL == m_rOptions.pCharset)
  3057. {
  3058. hr = TrapError(E_FAIL);
  3059. goto exit;
  3060. }
  3061. // Set Return
  3062. *phCharset = m_rOptions.pCharset->hCharset;
  3063. exit:
  3064. // Thread Safety
  3065. LeaveCriticalSection(&m_cs);
  3066. // Done
  3067. return hr;
  3068. }
  3069. // --------------------------------------------------------------------------------
  3070. // CMessageTree::_HrGetCharsetTree
  3071. // --------------------------------------------------------------------------------
  3072. HRESULT CMessageTree::_HrGetCharsetTree(LPTREENODEINFO pNode, LPHCHARSET phCharset)
  3073. {
  3074. // Locals
  3075. LPTREENODEINFO pChild;
  3076. // Invalid Arg
  3077. Assert(pNode && phCharset && m_rOptions.pCharset);
  3078. // Init
  3079. *phCharset = NULL;
  3080. // If this is a multipart item, lets search it's children
  3081. if (_IsMultiPart(pNode))
  3082. {
  3083. // Loop Children
  3084. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  3085. {
  3086. // Check body
  3087. Assert(pChild->pParent == pNode);
  3088. // Bind the body table for this dude
  3089. if (SUCCEEDED(_HrGetCharsetTree(pChild, phCharset)) && *phCharset)
  3090. break;
  3091. }
  3092. }
  3093. // If the Header was tagged with a charset, use that charset
  3094. else if (pNode->pContainer->IsState(COSTATE_CSETTAGGED) == S_OK)
  3095. {
  3096. // Get Internal Character Set
  3097. if (SUCCEEDED(pNode->pContainer->GetCharset(phCharset)) && *phCharset)
  3098. goto exit;
  3099. }
  3100. exit:
  3101. // Done
  3102. return (*phCharset) ? S_OK : E_FAIL;
  3103. }
  3104. // --------------------------------------------------------------------------------
  3105. // CMessageTree::SetCharset
  3106. // --------------------------------------------------------------------------------
  3107. STDMETHODIMP CMessageTree::SetCharset(HCHARSET hCharset, CSETAPPLYTYPE applytype)
  3108. {
  3109. // Locals
  3110. HRESULT hr=S_OK;
  3111. // Check Params
  3112. if (NULL == hCharset)
  3113. return TrapError(E_INVALIDARG);
  3114. // Thread Safety
  3115. EnterCriticalSection(&m_cs);
  3116. // Lookiup Charset Info
  3117. if (FALSE == g_pInternat->FIsValidHandle(hCharset))
  3118. {
  3119. hr = TrapError(MIME_E_INVALID_HANDLE);
  3120. goto exit;
  3121. }
  3122. // Save the charset
  3123. SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(hCharset, &m_rOptions.pCharset)));
  3124. // Save apply type
  3125. m_rOptions.csetapply = applytype;
  3126. // If we have a root body
  3127. if (m_pRootNode)
  3128. {
  3129. // Recurse all bodies and set the charset
  3130. CHECKHR(hr = _HrSetCharsetTree(m_pRootNode, m_rOptions.pCharset->hCharset, m_rOptions.csetapply));
  3131. }
  3132. exit:
  3133. // Thread Safety
  3134. LeaveCriticalSection(&m_cs);
  3135. // Done
  3136. return hr;
  3137. }
  3138. // --------------------------------------------------------------------------------
  3139. // CMessageTree::_HrSetCharsetTree
  3140. // --------------------------------------------------------------------------------
  3141. HRESULT CMessageTree::_HrSetCharsetTree(LPTREENODEINFO pNode, HCHARSET hCharset, CSETAPPLYTYPE applytype)
  3142. {
  3143. // Locals
  3144. HRESULT hr=S_OK;
  3145. LPTREENODEINFO pChild;
  3146. // Invalid Arg
  3147. Assert(pNode);
  3148. // Raid-22662: OExpress: if content-type on fileattach does not have charset should apply same as message body
  3149. pNode->pBody->SetCharset(hCharset, applytype);
  3150. // If this is a multipart item, lets search it's children
  3151. if (_IsMultiPart(pNode))
  3152. {
  3153. // Loop Children
  3154. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  3155. {
  3156. // Check body
  3157. Assert(pChild->pParent == pNode);
  3158. // Bind the body table for this dude
  3159. CHECKHR(hr = _HrSetCharsetTree(pChild, hCharset, applytype));
  3160. }
  3161. }
  3162. exit:
  3163. // Done
  3164. return hr;
  3165. }
  3166. // --------------------------------------------------------------------------------
  3167. // CMessageTree::_HrValidateOffsets
  3168. // --------------------------------------------------------------------------------
  3169. HRESULT CMessageTree::_HrValidateOffsets(LPTREENODEINFO pNode)
  3170. {
  3171. // Invalid Arg
  3172. Assert(pNode);
  3173. // Validate the offsets
  3174. if (pNode->cbBodyStart > m_cbMessage || pNode->cbBodyEnd > m_cbMessage ||
  3175. pNode->cbHeaderStart > m_cbMessage || pNode->cbBoundaryStart > m_cbMessage)
  3176. {
  3177. Assert(FALSE);
  3178. return TrapError(MIME_E_BODYTREE_OUT_OF_SYNC);
  3179. }
  3180. // Validate the offsets
  3181. if (pNode->cbBodyStart > pNode->cbBodyEnd || pNode->cbBoundaryStart > pNode->cbHeaderStart ||
  3182. pNode->cbHeaderStart > pNode->cbBodyStart || pNode->cbBoundaryStart > pNode->cbBodyEnd)
  3183. {
  3184. Assert(FALSE);
  3185. return TrapError(MIME_E_BODYTREE_OUT_OF_SYNC);
  3186. }
  3187. // Done
  3188. return S_OK;
  3189. }
  3190. // --------------------------------------------------------------------------------
  3191. // CMessageTree::_HrValidateStartBoundary
  3192. // --------------------------------------------------------------------------------
  3193. HRESULT CMessageTree::_HrValidateStartBoundary(CInternetStream *pInternet, LPTREENODEINFO pNode,
  3194. LPSTR *ppszFileName)
  3195. {
  3196. // Locals
  3197. HRESULT hr=S_OK;
  3198. PROPSTRINGA rLine;
  3199. // Is there a boundary to read...
  3200. if (BOUNDARY_MIMENEXT == pNode->boundary)
  3201. {
  3202. // Seek to the boundary start..
  3203. pInternet->Seek(pNode->cbBoundaryStart);
  3204. // Readline and verify the header
  3205. CHECKHR(hr = pInternet->HrReadLine(&rLine));
  3206. // Read and verify the boundary...
  3207. if (!ISVALIDSTRINGA(&pNode->rBoundary) || BOUNDARY_MIMENEXT != _GetMimeBoundaryType(&rLine, &pNode->rBoundary))
  3208. {
  3209. AssertSz(FALSE, "MIME_E_BODYTREE_OUT_OF_SYNC");
  3210. hr = TrapError(MIME_E_BODYTREE_OUT_OF_SYNC);
  3211. goto exit;
  3212. }
  3213. }
  3214. // Otherwise, verify UU boundary
  3215. else if (BOUNDARY_UUBEGIN == pNode->boundary)
  3216. {
  3217. // Seek to the boundary start..
  3218. pInternet->Seek(pNode->cbBoundaryStart);
  3219. // Readline and verify the header
  3220. CHECKHR(hr = pInternet->HrReadLine(&rLine));
  3221. // Read and verify the boundary...
  3222. if (FALSE == _FIsUuencodeBegin(&rLine, ppszFileName))
  3223. {
  3224. AssertSz(FALSE, "MIME_E_BODYTREE_OUT_OF_SYNC");
  3225. hr = TrapError(MIME_E_BODYTREE_OUT_OF_SYNC);
  3226. goto exit;
  3227. }
  3228. // FileName..
  3229. AssertSz(!ISFLAGSET(pNode->dwType, NODETYPE_FAKEMULTIPART), "pszFileName is not going to get set.");
  3230. }
  3231. // Otherwise, header start should be same as boundary start
  3232. else
  3233. Assert(BOUNDARY_ROOT == pNode->boundary ? TRUE : pNode->cbBoundaryStart == pNode->cbHeaderStart);
  3234. exit:
  3235. // Done
  3236. return hr;
  3237. }
  3238. // --------------------------------------------------------------------------------
  3239. // CMessageTree::_HrFastParseBody
  3240. // --------------------------------------------------------------------------------
  3241. HRESULT CMessageTree::_HrFastParseBody(CInternetStream *pInternet, LPTREENODEINFO pNode)
  3242. {
  3243. // Locals
  3244. HRESULT hr=S_OK;
  3245. MIMEVARIANT rVariant;
  3246. LPSTR pszFileName=NULL;
  3247. LPTREENODEINFO pChild,
  3248. pTemp;
  3249. // check params
  3250. Assert(pInternet && pNode);
  3251. // Validate Offset
  3252. CHECKHR(hr = _HrValidateOffsets(pNode));
  3253. // Validate Start Boundary
  3254. CHECKHR(hr = _HrValidateStartBoundary(pInternet, pNode, &pszFileName));
  3255. // Is there a header to read...
  3256. if (BOUNDARY_MIMENEXT == pNode->boundary || BOUNDARY_ROOT == pNode->boundary)
  3257. {
  3258. // Load the header
  3259. Assert(pNode->boundary == BOUNDARY_ROOT ? m_pRootNode == pNode : TRUE);
  3260. // Seek to the boundary start..
  3261. pInternet->Seek(pNode->cbHeaderStart);
  3262. // Load the Header
  3263. CHECKHR(hr = pNode->pContainer->Load(pInternet));
  3264. // Raid-38646: Mimeole: Multipart/Digest not being parsed correctly initially, but save fine
  3265. // Message In a Message
  3266. if (pNode->pContainer->IsContentType(STR_CNT_MESSAGE, NULL) == S_OK)
  3267. {
  3268. // We are parsing a message attachment
  3269. FLAGSET(pNode->dwState, NODESTATE_MESSAGE);
  3270. }
  3271. // Otherwise, if parent and parent is a multipart/digest
  3272. else if (pNode->pParent && pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_DIGEST) == S_OK &&
  3273. pNode->pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTTYPE)) == S_FALSE)
  3274. {
  3275. // Change the Content Type
  3276. pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MSG_RFC822);
  3277. // This is a message
  3278. FLAGSET(pNode->dwState, NODESTATE_MESSAGE);
  3279. }
  3280. }
  3281. // Encoding
  3282. else if (!ISFLAGSET(pNode->dwType, NODETYPE_FAKEMULTIPART))
  3283. {
  3284. // ComputeDefaultContent
  3285. CHECKHR(hr = _HrComputeDefaultContent(pNode, pszFileName));
  3286. }
  3287. // Fake Multipart...
  3288. if (ISFLAGSET(pNode->dwType, NODETYPE_FAKEMULTIPART))
  3289. {
  3290. // Application/octet-stream
  3291. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MPART_MIXED));
  3292. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_7BIT));
  3293. // Loop children
  3294. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  3295. {
  3296. // Check pChild
  3297. Assert(pChild->pParent == pNode);
  3298. // Bind the body table for this dude
  3299. CHECKHR(hr = _HrFastParseBody(pInternet, pChild));
  3300. }
  3301. }
  3302. // If Multipart...cruise through the children
  3303. else if (_IsMultiPart(pNode))
  3304. {
  3305. // Get the boundary from the header
  3306. rVariant.type = MVT_STRINGA;
  3307. if (FAILED(pNode->pContainer->GetProp(SYM_PAR_BOUNDARY, 0, &rVariant)))
  3308. {
  3309. hr = TrapError(MIME_E_NO_MULTIPART_BOUNDARY);
  3310. goto exit;
  3311. }
  3312. // But the Boundary into pNode->rBoundary
  3313. pNode->rBoundary.pszVal = rVariant.rStringA.pszVal;
  3314. pNode->rBoundary.cchVal = rVariant.rStringA.cchVal;
  3315. // Free this boundary later
  3316. FLAGCLEAR(pNode->dwState, NODESTATE_BOUNDNOFREE);
  3317. // Loop children
  3318. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  3319. {
  3320. // Check pChild
  3321. Assert(pChild->pParent == pNode);
  3322. // Put the Boundary into pChild
  3323. pChild->rBoundary.pszVal = rVariant.rStringA.pszVal;
  3324. pChild->rBoundary.cchVal = rVariant.rStringA.cchVal;
  3325. // Done free the boundary
  3326. FLAGSET(pChild->dwState, NODESTATE_BOUNDNOFREE);
  3327. // Bind the body table for this dude
  3328. CHECKHR(hr = _HrFastParseBody(pInternet, pChild));
  3329. }
  3330. }
  3331. exit:
  3332. // Cleanup
  3333. SafeMemFree(pszFileName);
  3334. // Done
  3335. return hr;
  3336. }
  3337. // --------------------------------------------------------------------------------
  3338. // CMessageTree::_FuzzyPartialRecognition
  3339. // --------------------------------------------------------------------------------
  3340. void CMessageTree::_FuzzyPartialRecognition(BOOL fIsMime)
  3341. {
  3342. // Locals
  3343. CHAR szFile[MAX_PATH];
  3344. ULONG ulTotal;
  3345. ULONG ulPart;
  3346. BOOL fCntTypeSet=FALSE;
  3347. LPSTR pszContentType=NULL;
  3348. CHAR szExt[_MAX_EXT];
  3349. // Better have a Root
  3350. Assert(m_pRootNode);
  3351. // Only if this is the
  3352. if (fIsMime || m_pRootNode->cChildren || m_pRootNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN) == S_FALSE)
  3353. goto exit;
  3354. // Extract FileName and part/total from the subject
  3355. if (FAILED(MimeOleGetSubjectFileName(m_pRootNode->pBody, &ulPart, &ulTotal, szFile, MAX_PATH)))
  3356. goto exit;
  3357. // Mark as Partial
  3358. FLAGSET(m_pRootNode->dwType, NODETYPE_INCOMPLETE);
  3359. // A Little Debugging
  3360. DebugTrace("FuzzyPartialRecognition - FileName = '%s', Part = %d, Total = %d\n", szFile, ulPart, ulTotal);
  3361. // Store the FileName
  3362. if (FAILED(m_pRootNode->pContainer->SetProp(SYM_ATT_FILENAME, szFile)))
  3363. goto exit;
  3364. // Get File Extesion
  3365. if (SUCCEEDED(MimeOleGetFileExtension(szFile, szExt, sizeof(szExt))))
  3366. {
  3367. // GetExtContentType
  3368. if (SUCCEEDED(MimeOleGetExtContentType(szExt, &pszContentType)))
  3369. {
  3370. // Set Content Type
  3371. m_pRootNode->pContainer->SetProp(SYM_HDR_CNTTYPE, pszContentType);
  3372. // We Set the content type
  3373. fCntTypeSet = TRUE;
  3374. }
  3375. }
  3376. // Default to Application/octet-stream
  3377. if (FALSE == fCntTypeSet)
  3378. m_pRootNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_APPL_STREAM);
  3379. // Set Encoding
  3380. m_pRootNode->pContainer->SetProp(SYM_HDR_CNTDISP, STR_DIS_ATTACHMENT);
  3381. // I Should Actualy do some detection...
  3382. if (FALSE == fIsMime)
  3383. m_pRootNode->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_UUENCODE);
  3384. exit:
  3385. // Cleanup
  3386. SafeMemFree(pszContentType);
  3387. // Done
  3388. return;
  3389. }
  3390. // --------------------------------------------------------------------------------
  3391. // CMessageTree::_HrComputeDefaultContent
  3392. // --------------------------------------------------------------------------------
  3393. HRESULT CMessageTree::_HrComputeDefaultContent(LPTREENODEINFO pNode, LPCSTR pszFileName)
  3394. {
  3395. // Locals
  3396. HRESULT hr=S_OK;
  3397. CHAR szExt[256];
  3398. LPSTR pszContentType=NULL;
  3399. LPSTR pszDecoded=NULL;
  3400. // Invalid Arg
  3401. Assert(pNode);
  3402. // Otherwise, lets get the content type
  3403. if (pszFileName)
  3404. {
  3405. // Set File Name
  3406. PROPVARIANT rVariant;
  3407. rVariant.vt = VT_LPSTR;
  3408. rVariant.pszVal = (LPSTR)pszFileName;
  3409. // Set the file name
  3410. CHECKHR(hr = pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_FILENAME), PDF_ENCODED, &rVariant));
  3411. // Get the filename back out so that its decoded...
  3412. CHECKHR(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_FILENAME), &pszDecoded));
  3413. // Test for winmail.dat
  3414. if (lstrcmpi(pszDecoded, c_szWinmailDotDat) == 0)
  3415. {
  3416. // Make sure the stream is really TNEF
  3417. FLAGSET(pNode->dwState, NODESTATE_VERIFYTNEF);
  3418. }
  3419. // Get File Extesion
  3420. if (SUCCEEDED(MimeOleGetFileExtension(pszDecoded, szExt, sizeof(szExt))))
  3421. {
  3422. // GetExtContentType
  3423. if (SUCCEEDED(MimeOleGetExtContentType(szExt, &pszContentType)))
  3424. {
  3425. // Set Content Type
  3426. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, pszContentType));
  3427. }
  3428. else
  3429. {
  3430. // Set Content Type
  3431. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_APPL_STREAM));
  3432. }
  3433. }
  3434. // Set Encoding
  3435. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTDISP, STR_DIS_ATTACHMENT));
  3436. }
  3437. // Otherwise
  3438. else
  3439. {
  3440. // Default to text/plain
  3441. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN));
  3442. }
  3443. // Boundary Was UUencode
  3444. if (BOUNDARY_UUBEGIN == pNode->boundary)
  3445. {
  3446. // Set Encoding
  3447. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_UUENCODE));
  3448. }
  3449. else if (ISFLAGSET(pNode->dwType,NODETYPE_RFC1154_BINHEX))
  3450. {
  3451. // This is BINHEX from RFC1154
  3452. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTDISP, STR_DIS_ATTACHMENT));
  3453. CHECKHR(hr = pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_PRITYPE), STR_CNT_APPLICATION));
  3454. CHECKHR(hr = pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_SUBTYPE), STR_SUB_BINHEX));
  3455. }
  3456. // Otherwise
  3457. else
  3458. {
  3459. // Set Encoding
  3460. CHECKHR(hr = pNode->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_7BIT));
  3461. }
  3462. exit:
  3463. // Cleanup
  3464. SafeMemFree(pszContentType);
  3465. SafeMemFree(pszDecoded);
  3466. // Done
  3467. return hr;
  3468. }
  3469. // --------------------------------------------------------------------------------
  3470. // CMessageTree::HandsOffStorage
  3471. // --------------------------------------------------------------------------------
  3472. STDMETHODIMP CMessageTree::HandsOffStorage(void)
  3473. {
  3474. // Locals
  3475. HRESULT hr=S_OK;
  3476. LPSTREAM pstmNew=NULL;
  3477. // Thread Safety
  3478. EnterCriticalSection(&m_cs);
  3479. // No Internal Stream...
  3480. if (NULL == m_pStmLock)
  3481. goto exit;
  3482. // I own the stream
  3483. if (!ISFLAGSET(m_dwState, TREESTATE_HANDSONSTORAGE))
  3484. goto exit;
  3485. // Copy m_pStmLock to a local place...
  3486. CHECKALLOC(pstmNew = new CVirtualStream);
  3487. // Go through m_pLockBytes to continue to provide thread safety to m_pStmLock
  3488. CHECKHR(hr = HrCopyLockBytesToStream(m_pStmLock, pstmNew, NULL));
  3489. // Rewind and commit
  3490. CHECKHR(hr = pstmNew->Commit(STGC_DEFAULT));
  3491. // Replace internal stream
  3492. m_pStmLock->ReplaceInternalStream(pstmNew);
  3493. // Hands are off..
  3494. FLAGCLEAR(m_dwState, TREESTATE_HANDSONSTORAGE);
  3495. exit:
  3496. // Cleanup
  3497. SafeRelease(pstmNew);
  3498. // Thread Safety
  3499. LeaveCriticalSection(&m_cs);
  3500. // Done
  3501. return hr;
  3502. }
  3503. // --------------------------------------------------------------------------------
  3504. // CMessageTree::GetMessageSource
  3505. // --------------------------------------------------------------------------------
  3506. STDMETHODIMP CMessageTree::GetMessageSource(IStream **ppStream, DWORD dwFlags)
  3507. {
  3508. // Locals
  3509. HRESULT hr=S_OK;
  3510. IStream *pStream=NULL;
  3511. // Invalid Arg
  3512. if (NULL == ppStream)
  3513. return TrapError(E_INVALIDARG);
  3514. // Init
  3515. *ppStream = NULL;
  3516. // Thread Safety
  3517. EnterCriticalSection(&m_cs);
  3518. // If Dirty
  3519. if (ISFLAGSET(dwFlags, COMMIT_ONLYIFDIRTY) && IsDirty() == S_OK && FALSE == m_rOptions.fHandsOffOnSave)
  3520. {
  3521. // Commit
  3522. CHECKHR(hr = Commit(dwFlags));
  3523. }
  3524. // Raid-19644: MIMEOLE: GetMessageSource fails with MIME_E_NO_DATA (because of OID_HANDSOFF_ONSAVE = TRUE)
  3525. if (NULL == m_pStmLock || TRUE == m_rOptions.fHandsOffOnSave)
  3526. {
  3527. // Create a new stream
  3528. CHECKALLOC(pStream = new CVirtualStream);
  3529. // Call Save Message
  3530. CHECKHR(hr = _HrWriteMessage(pStream, FALSE, m_rOptions.fHandsOffOnSave, FALSE));
  3531. // All good
  3532. *ppStream = pStream;
  3533. // Null pStream
  3534. pStream = NULL;
  3535. }
  3536. // Otherwise, just wrap m_pStmLock
  3537. else if (m_pStmLock)
  3538. {
  3539. // Locked Stream
  3540. CHECKALLOC(*ppStream = (IStream *)new CLockedStream(m_pStmLock, m_cbMessage));
  3541. }
  3542. // Otherwise, failure
  3543. else
  3544. {
  3545. hr = TrapError(MIME_E_NO_DATA);
  3546. goto exit;
  3547. }
  3548. exit:
  3549. // Thread Safety
  3550. LeaveCriticalSection(&m_cs);
  3551. // Cleanup
  3552. SafeRelease(pStream);
  3553. // Done
  3554. return hr;
  3555. }
  3556. // --------------------------------------------------------------------------------
  3557. // CMessageTree::QueryService
  3558. // --------------------------------------------------------------------------------
  3559. STDMETHODIMP CMessageTree::QueryService(REFGUID rsid, REFIID riid, void **ppvObject) /* IServiceProvider */
  3560. {
  3561. // Locals
  3562. HRESULT hr=S_OK;
  3563. // Invalid Arg
  3564. if (NULL == ppvObject)
  3565. return TrapError(E_INVALIDARG);
  3566. // Thread Safety
  3567. EnterCriticalSection(&m_cs);
  3568. // IID_IBindMessageStream
  3569. if (IID_IBindMessageStream == riid)
  3570. {
  3571. // We should not have a lock bytes yet
  3572. Assert(NULL == m_pStmLock);
  3573. // Create a Virtual Stream
  3574. CHECKHR(hr = MimeOleCreateVirtualStream((IStream **)ppvObject));
  3575. }
  3576. // IID_IBinding
  3577. else if (IID_IBinding == riid)
  3578. {
  3579. // No Bind Context Yet
  3580. if (NULL == m_pBinding)
  3581. {
  3582. hr = TrapError(E_UNEXPECTED);
  3583. goto exit;
  3584. }
  3585. // Return It
  3586. (*ppvObject) = m_pBinding;
  3587. ((IUnknown *)*ppvObject)->AddRef();
  3588. }
  3589. // IID_IMoniker
  3590. else if (IID_IMoniker == riid)
  3591. {
  3592. // No Bind Context Yet
  3593. if (NULL == m_pMoniker)
  3594. {
  3595. hr = TrapError(E_UNEXPECTED);
  3596. goto exit;
  3597. }
  3598. // Return It
  3599. (*ppvObject) = m_pMoniker;
  3600. ((IUnknown *)*ppvObject)->AddRef();
  3601. }
  3602. // Otherwise, no object
  3603. else
  3604. {
  3605. hr = TrapError(E_NOINTERFACE);
  3606. goto exit;
  3607. }
  3608. exit:
  3609. // Thread Safety
  3610. LeaveCriticalSection(&m_cs);
  3611. // Done
  3612. return hr;
  3613. }
  3614. // --------------------------------------------------------------------------------
  3615. // CMessageTree::BinToObject
  3616. // --------------------------------------------------------------------------------
  3617. STDMETHODIMP CMessageTree::BindToObject(const HBODY hBody, REFIID riid, void **ppvObject)
  3618. {
  3619. // Locals
  3620. HRESULT hr=S_OK;
  3621. LPTREENODEINFO pNode;
  3622. // check params
  3623. if (NULL == ppvObject)
  3624. return TrapError(E_INVALIDARG);
  3625. // Thread Safety
  3626. EnterCriticalSection(&m_cs);
  3627. // Get body
  3628. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  3629. // BindToObject on the body
  3630. CHECKHR(hr = pNode->pBody->BindToObject(riid, ppvObject));
  3631. exit:
  3632. // Thread Safety
  3633. LeaveCriticalSection(&m_cs);
  3634. // Done
  3635. return hr;
  3636. }
  3637. // --------------------------------------------------------------------------------
  3638. // CMessageTree::_PoseCreateTreeNode
  3639. // --------------------------------------------------------------------------------
  3640. void CMessageTree::_PostCreateTreeNode(HRESULT hrResult, LPTREENODEINFO pNode)
  3641. {
  3642. // Failure...
  3643. if (FAILED(hrResult) && pNode)
  3644. {
  3645. // Set Index
  3646. ULONG ulIndex = HBODYINDEX(pNode->hBody);
  3647. // This body better be here
  3648. Assert(m_rTree.prgpNode[ulIndex] == pNode);
  3649. // Lets make sure nobody else is referencing this node...
  3650. #ifdef DEBUG
  3651. for (ULONG i=0; i<m_rTree.cNodes; i++)
  3652. {
  3653. if (m_rTree.prgpNode[i])
  3654. {
  3655. AssertSz(m_rTree.prgpNode[i]->pPrev != pNode, "Killing a linked node is not good");
  3656. AssertSz(m_rTree.prgpNode[i]->pNext != pNode, "Killing a linked node is not good");
  3657. AssertSz(m_rTree.prgpNode[i]->pParent != pNode, "Killing a linked node is not good");
  3658. AssertSz(m_rTree.prgpNode[i]->pChildHead != pNode, "Killing a linked node is not good");
  3659. AssertSz(m_rTree.prgpNode[i]->pChildTail != pNode, "Killing a linked node is not good");
  3660. }
  3661. }
  3662. #endif
  3663. // This node should not have been linked yet...
  3664. AssertSz(pNode->pPrev == NULL, "Killing a linked node is not good");
  3665. AssertSz(pNode->pNext == NULL, "Killing a linked node is not good");
  3666. AssertSz(pNode->pParent == NULL, "Killing a linked node is not good");
  3667. AssertSz(pNode->pChildHead == NULL, "Killing a linked node is not good");
  3668. AssertSz(pNode->pChildTail == NULL, "Killing a linked node is not good");
  3669. AssertSz(pNode->cChildren == 0, "Deleting a node with children");
  3670. // Free It
  3671. _FreeTreeNodeInfo(pNode);
  3672. // Reset entry in table
  3673. m_rTree.prgpNode[ulIndex] = NULL;
  3674. // If Index is last item
  3675. if (ulIndex + 1 == m_rTree.cNodes)
  3676. m_rTree.cNodes--;
  3677. // Otherwise, Increment Empty Count...
  3678. else
  3679. m_rTree.cEmpty++;
  3680. }
  3681. }
  3682. // --------------------------------------------------------------------------------
  3683. // CMessageTree::_HrCreateTreeNode
  3684. // --------------------------------------------------------------------------------
  3685. HRESULT CMessageTree::_HrCreateTreeNode(LPTREENODEINFO *ppNode)
  3686. {
  3687. // Locals
  3688. HRESULT hr=S_OK;
  3689. ULONG i=0;
  3690. BOOL fUsingEmpty=FALSE;
  3691. // Invalid Arg
  3692. Assert(ppNode);
  3693. // Use Empty Cell
  3694. if (m_rTree.cEmpty)
  3695. {
  3696. // Find First Empty Cell..
  3697. for (i=0; i<m_rTree.cNodes; i++)
  3698. {
  3699. // Empty ?
  3700. if (NULL == m_rTree.prgpNode[i])
  3701. {
  3702. fUsingEmpty = TRUE;
  3703. break;
  3704. }
  3705. }
  3706. }
  3707. // If not using empty
  3708. if (FALSE == fUsingEmpty)
  3709. {
  3710. // Lets grow the table first...
  3711. if (m_rTree.cNodes + 1 > m_rTree.cAlloc)
  3712. {
  3713. // Grow my current property value array
  3714. CHECKHR(hr = HrRealloc((LPVOID *)&m_rTree.prgpNode, sizeof(LPTREENODEINFO) * (m_rTree.cAlloc + 10)));
  3715. // Increment alloc size
  3716. m_rTree.cAlloc += 10;
  3717. }
  3718. // Index to use
  3719. i = m_rTree.cNodes;
  3720. }
  3721. // Set to empty
  3722. m_rTree.prgpNode[i] = NULL;
  3723. // Allocate this node...
  3724. CHECKHR(hr = _HrAllocateTreeNode(i));
  3725. // Return It
  3726. *ppNode = m_rTree.prgpNode[i];
  3727. // If not using empty cell, increment body count
  3728. if (FALSE == fUsingEmpty)
  3729. m_rTree.cNodes++;
  3730. // Otherwise, decrement number of empty cells
  3731. else
  3732. m_rTree.cEmpty--;
  3733. exit:
  3734. // Done
  3735. return hr;
  3736. }
  3737. // --------------------------------------------------------------------------------
  3738. // CMessageTree::InsertBody
  3739. // --------------------------------------------------------------------------------
  3740. STDMETHODIMP CMessageTree::InsertBody(BODYLOCATION location, HBODY hPivot, LPHBODY phBody)
  3741. {
  3742. // Locals
  3743. HRESULT hr=S_OK;
  3744. LPTREENODEINFO pNode=NULL;
  3745. LPTREENODEINFO pPivot=NULL;
  3746. LPTREENODEINFO pPrev;
  3747. LPTREENODEINFO pNext;
  3748. LPTREENODEINFO pParent;
  3749. // Invalid Arg
  3750. if (IBL_PARENT == location)
  3751. return TrapError(E_INVALIDARG);
  3752. // Thread Safety
  3753. EnterCriticalSection(&m_cs);
  3754. // Init
  3755. if (phBody)
  3756. *phBody = NULL;
  3757. // Handle Body Type
  3758. if (IBL_ROOT == location)
  3759. {
  3760. // Currently No root
  3761. if (NULL == m_pRootNode)
  3762. {
  3763. // Create Object
  3764. CHECKHR(hr = _HrCreateTreeNode(&pNode));
  3765. // Better not be any bodies
  3766. Assert(m_rTree.cNodes == 1);
  3767. // Set as root
  3768. m_pRootNode = pNode;
  3769. }
  3770. // Otherwise, re-use the root
  3771. else
  3772. {
  3773. hr = TrapError(MIME_E_CANT_RESET_ROOT);
  3774. goto exit;
  3775. }
  3776. }
  3777. // All non-root inserts
  3778. else
  3779. {
  3780. // Get Pivot
  3781. if (_FIsValidHandle(hPivot) == FALSE)
  3782. {
  3783. hr = TrapError(MIME_E_INVALID_HANDLE);
  3784. goto exit;
  3785. }
  3786. // Cast it..
  3787. pPivot = _PNodeFromHBody(hPivot);
  3788. // Create Object
  3789. CHECKHR(hr = _HrCreateTreeNode(&pNode));
  3790. // First or Last Child
  3791. if (IBL_LAST == location || IBL_FIRST == location)
  3792. {
  3793. // Better be a multipart
  3794. if (!_IsMultiPart(pPivot))
  3795. {
  3796. hr = TrapError(MIME_E_NOT_MULTIPART);
  3797. goto exit;
  3798. }
  3799. // DON'T FAIL FROM HERE TO END OF FUNCTION
  3800. // No Children on pPivot
  3801. if (NULL == pPivot->pChildHead)
  3802. {
  3803. Assert(pPivot->pChildTail == NULL);
  3804. pPivot->pChildHead = pNode;
  3805. pPivot->pChildTail = pNode;
  3806. pNode->pParent = pPivot;
  3807. }
  3808. // IBL_LAST
  3809. else if (IBL_LAST == location)
  3810. {
  3811. pPrev = pPivot->pChildTail;
  3812. pNode->pPrev = pPrev;
  3813. pPrev->pNext = pNode;
  3814. pPivot->pChildTail = pNode;
  3815. pNode->pParent = pPivot;
  3816. }
  3817. // IBL_FIRST
  3818. else if (IBL_FIRST == location)
  3819. {
  3820. pNext = pPivot->pChildHead;
  3821. pNode->pNext = pNext;
  3822. pNext->pPrev = pNode;
  3823. pPivot->pChildHead = pNode;
  3824. pNode->pParent = pPivot;
  3825. }
  3826. // Increment Count
  3827. pPivot->cChildren++;
  3828. }
  3829. // Otherwise
  3830. else if (IBL_NEXT == location || IBL_PREVIOUS == location)
  3831. {
  3832. // Need a parent
  3833. pParent = pPivot->pParent;
  3834. // No Parent
  3835. if (NULL == pParent)
  3836. {
  3837. hr = TrapError(MIME_E_NOT_MULTIPART);
  3838. goto exit;
  3839. }
  3840. // DON'T FAIL FROM HERE TO END OF FUNCTION
  3841. // Parent Better be a multipart
  3842. Assert(_IsMultiPart(pParent));
  3843. // Set Parent
  3844. pNode->pParent = pParent;
  3845. // IBL_NEXT
  3846. if (IBL_NEXT == location)
  3847. {
  3848. // Set Previous
  3849. pPrev = pPivot;
  3850. // Append to the end
  3851. if (NULL == pPrev->pNext)
  3852. {
  3853. pPrev->pNext = pNode;
  3854. pNode->pPrev = pPrev;
  3855. pParent->pChildTail = pNode;
  3856. }
  3857. // Otherwise, inserting between two nodes
  3858. else
  3859. {
  3860. pNext = pPrev->pNext;
  3861. pNode->pPrev = pPrev;
  3862. pNode->pNext = pNext;
  3863. pPrev->pNext = pNode;
  3864. pNext->pPrev = pNode;
  3865. }
  3866. }
  3867. // IBL_PREVIOUS
  3868. else if (IBL_PREVIOUS == location)
  3869. {
  3870. // Set Previous
  3871. pNext = pPivot;
  3872. // Append to the end
  3873. if (NULL == pNext->pPrev)
  3874. {
  3875. pNext->pPrev = pNode;
  3876. pNode->pNext = pNext;
  3877. pParent->pChildHead = pNode;
  3878. }
  3879. // Otherwise, inserting between two nodes
  3880. else
  3881. {
  3882. pPrev = pNext->pPrev;
  3883. pNode->pNext = pNext;
  3884. pNode->pPrev = pPrev;
  3885. pPrev->pNext = pNode;
  3886. pNext->pPrev = pNode;
  3887. }
  3888. }
  3889. // Increment Count
  3890. pParent->cChildren++;
  3891. }
  3892. // Otherwise bad body location
  3893. else
  3894. {
  3895. hr = TrapError(MIME_E_BAD_BODY_LOCATION);
  3896. goto exit;
  3897. }
  3898. }
  3899. // Set Return
  3900. if (phBody)
  3901. *phBody = pNode->hBody;
  3902. // Dirty
  3903. FLAGSET(m_dwState, TREESTATE_DIRTY);
  3904. exit:
  3905. // Failure
  3906. _PostCreateTreeNode(hr, pNode);
  3907. // Thread Safety
  3908. LeaveCriticalSection(&m_cs);
  3909. // Done
  3910. return hr;
  3911. }
  3912. // --------------------------------------------------------------------------------
  3913. // CMessageTree::GetBody
  3914. // --------------------------------------------------------------------------------
  3915. STDMETHODIMP CMessageTree::GetBody(BODYLOCATION location, HBODY hPivot, LPHBODY phBody)
  3916. {
  3917. // Locals
  3918. HRESULT hr=S_OK;
  3919. LPTREENODEINFO pPivot, pCurr;
  3920. // check params
  3921. if (NULL == phBody)
  3922. return TrapError(E_INVALIDARG);
  3923. // Thread Safety
  3924. EnterCriticalSection(&m_cs);
  3925. // Init
  3926. *phBody = NULL;
  3927. // Handle Root Case
  3928. if (IBL_ROOT == location)
  3929. {
  3930. if (m_pRootNode)
  3931. *phBody = m_pRootNode->hBody;
  3932. else
  3933. hr = MIME_E_NOT_FOUND;
  3934. }
  3935. // Otherwise
  3936. else
  3937. {
  3938. // Validate handle
  3939. if (_FIsValidHandle(hPivot) == FALSE)
  3940. {
  3941. hr = TrapError(MIME_E_INVALID_HANDLE);
  3942. goto exit;
  3943. }
  3944. // Cast It
  3945. pPivot = _PNodeFromHBody(hPivot);
  3946. // Handle Get Type
  3947. switch(location)
  3948. {
  3949. // ----------------------------------------------
  3950. case IBL_PARENT:
  3951. if (pPivot->pParent)
  3952. *phBody = pPivot->pParent->hBody;
  3953. else
  3954. hr = MIME_E_NOT_FOUND;
  3955. break;
  3956. // ----------------------------------------------
  3957. case IBL_FIRST:
  3958. if (pPivot->pChildHead)
  3959. *phBody = pPivot->pChildHead->hBody;
  3960. else
  3961. hr = MIME_E_NOT_FOUND;
  3962. break;
  3963. // ----------------------------------------------
  3964. case IBL_LAST:
  3965. if (pPivot->pChildTail)
  3966. *phBody = pPivot->pChildTail->hBody;
  3967. else
  3968. hr = MIME_E_NOT_FOUND;
  3969. break;
  3970. // ----------------------------------------------
  3971. case IBL_NEXT:
  3972. if (pPivot->pNext)
  3973. *phBody = pPivot->pNext->hBody;
  3974. else
  3975. hr = MIME_E_NOT_FOUND;
  3976. break;
  3977. // ----------------------------------------------
  3978. case IBL_PREVIOUS:
  3979. if (pPivot->pPrev)
  3980. *phBody = pPivot->pPrev->hBody;
  3981. else
  3982. hr = MIME_E_NOT_FOUND;
  3983. break;
  3984. // ----------------------------------------------
  3985. default:
  3986. hr = TrapError(MIME_E_BAD_BODY_LOCATION);
  3987. goto exit;
  3988. }
  3989. }
  3990. exit:
  3991. // Thread Safety
  3992. LeaveCriticalSection(&m_cs);
  3993. // Done
  3994. return hr;
  3995. }
  3996. // --------------------------------------------------------------------------------
  3997. // CMessageTree::DeleteBody
  3998. // --------------------------------------------------------------------------------
  3999. STDMETHODIMP CMessageTree::DeleteBody(HBODY hBody, DWORD dwFlags)
  4000. {
  4001. // Locals
  4002. HRESULT hr=S_OK;
  4003. LPTREENODEINFO pNode;
  4004. BOOL fMultipart;
  4005. // check params
  4006. if (NULL == hBody)
  4007. return TrapError(E_INVALIDARG);
  4008. // Thread Safety
  4009. EnterCriticalSection(&m_cs);
  4010. // Validate handle
  4011. if (_FIsValidHandle(hBody) == FALSE)
  4012. {
  4013. hr = TrapError(MIME_E_INVALID_HANDLE);
  4014. goto exit;
  4015. }
  4016. // Cast
  4017. pNode = _PNodeFromHBody(hBody);
  4018. // Free Children...
  4019. fMultipart = (_IsMultiPart(pNode)) ? TRUE :FALSE;
  4020. // Promote Children ?
  4021. if (TRUE == fMultipart && ISFLAGSET(dwFlags, DELETE_PROMOTE_CHILDREN) && pNode->cChildren > 0)
  4022. {
  4023. // Call Helper
  4024. CHECKHR(hr = _HrDeletePromoteChildren(pNode));
  4025. }
  4026. // Otherwise
  4027. else
  4028. {
  4029. // If multipart, delete children
  4030. if (fMultipart && pNode->cChildren > 0)
  4031. {
  4032. // Remove the children
  4033. _DeleteChildren(pNode);
  4034. }
  4035. // If Not Children Only
  4036. if (!ISFLAGSET(dwFlags, DELETE_CHILDREN_ONLY))
  4037. {
  4038. // Was this the root
  4039. if (pNode == m_pRootNode)
  4040. {
  4041. // Delete the content type
  4042. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTBASE);
  4043. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTLOC);
  4044. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTID);
  4045. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTTYPE);
  4046. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTXFER);
  4047. m_pRootNode->pContainer->DeleteProp(SYM_HDR_CNTDISP);
  4048. // Empty the body
  4049. m_pRootNode->pBody->EmptyData();
  4050. }
  4051. // Otherwise, not deleting the root
  4052. else
  4053. {
  4054. // Unlink the node
  4055. _UnlinkTreeNode(pNode);
  4056. // Fix up the table
  4057. m_rTree.prgpNode[HBODYINDEX(hBody)] = NULL;
  4058. // Increment Empty Count
  4059. m_rTree.cEmpty++;
  4060. // Free this node
  4061. _FreeTreeNodeInfo(pNode);
  4062. }
  4063. }
  4064. }
  4065. // Dirty
  4066. FLAGSET(m_dwState, TREESTATE_DIRTY);
  4067. exit:
  4068. // Thread Safety
  4069. LeaveCriticalSection(&m_cs);
  4070. // Done
  4071. return hr;
  4072. }
  4073. // --------------------------------------------------------------------------------
  4074. // CMessageTree::_HrDeletePromoteChildren
  4075. // --------------------------------------------------------------------------------
  4076. HRESULT CMessageTree::_HrDeletePromoteChildren(LPTREENODEINFO pNode)
  4077. {
  4078. // Locals
  4079. HRESULT hr=S_OK;
  4080. LPTREENODEINFO pParent, pChild, pNext, pPrev;
  4081. // Get Parent
  4082. pParent = pNode->pParent;
  4083. // Single Child...
  4084. if (1 == pNode->cChildren)
  4085. {
  4086. // Promote the child up one level...
  4087. Assert(pNode->pChildHead && pNode->pChildHead && pNode->pChildHead == pNode->pChildTail);
  4088. // Get Child
  4089. pChild = pNode->pChildHead;
  4090. Assert(pChild->pNext == NULL && pChild->pPrev == NULL && pChild->pParent == pNode);
  4091. // Replace pBody with pChild
  4092. pChild->pParent = pNode->pParent;
  4093. pChild->pNext = pNode->pNext;
  4094. pChild->pPrev = pNode->pPrev;
  4095. // Is there a parent ?
  4096. if (pParent)
  4097. {
  4098. // Fixup pChildHead and pChildTail
  4099. if (pParent->pChildHead == pNode)
  4100. pParent->pChildHead = pChild;
  4101. if (pParent->pChildTail == pNode)
  4102. pParent->pChildTail = pChild;
  4103. }
  4104. // pNode's next and Previous
  4105. LPTREENODEINFO pNext = pNode->pNext;
  4106. LPTREENODEINFO pPrev = pNode->pPrev;
  4107. // Fixup Next and Previuos
  4108. if (pNext)
  4109. pNext->pPrev = pChild;
  4110. if (pPrev)
  4111. pPrev->pNext = pChild;
  4112. // pNode Basically does not have any children now
  4113. Assert(pNode->cChildren == 1);
  4114. pNode->cChildren = 0;
  4115. // Was this the root ?
  4116. if (m_pRootNode == pNode)
  4117. {
  4118. // OE5 Raid: 51543
  4119. if(S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN))
  4120. {
  4121. pChild->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN);
  4122. }
  4123. // Raid 41595 - Athena: Reply to a message includes the body of the message being replied to as an attachment
  4124. CHECKHR(hr = pChild->pContainer->MoveProps(0, NULL, m_pRootNode->pBody));
  4125. // Reset Header on pChild
  4126. pChild->pBody->SwitchContainers(m_pRootNode->pBody);
  4127. // Copy Options from p and tell m_pRootNode->pBody
  4128. m_pRootNode->pBody->CopyOptionsTo(pChild->pBody, TRUE);
  4129. // New root
  4130. m_pRootNode = pChild;
  4131. }
  4132. // We have now totally unlinked pNode
  4133. DebugAssertNotLinked(pNode);
  4134. // Fix up the table
  4135. m_rTree.prgpNode[HBODYINDEX(pNode->hBody)] = NULL;
  4136. // Increment Empty Count
  4137. m_rTree.cEmpty++;
  4138. // Free this node
  4139. _FreeTreeNodeInfo(pNode);
  4140. }
  4141. // Or parent is a multipart
  4142. else
  4143. {
  4144. // No parent or not multipart
  4145. if (NULL == pParent || FALSE == _IsMultiPart(pParent))
  4146. {
  4147. hr = TrapError(MIME_E_INVALID_DELETE_TYPE);
  4148. goto exit;
  4149. }
  4150. // Set Previous
  4151. pPrev = pParent->pChildTail;
  4152. // Walk children of pBody and append as children of pParent
  4153. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  4154. {
  4155. // pPrev
  4156. pChild->pPrev = pPrev;
  4157. // pNext
  4158. pChild->pNext = NULL;
  4159. // pPrev->pNext
  4160. if (pPrev)
  4161. pPrev->pNext = pChild;
  4162. // pChildTail
  4163. pParent->pChildTail = pChild;
  4164. // Set Parent
  4165. pChild->pParent = pParent;
  4166. // Increment pParent child count
  4167. pParent->cChildren++;
  4168. // Save pPrev
  4169. pPrev = pChild;
  4170. }
  4171. // Unlink the node
  4172. _UnlinkTreeNode(pNode);
  4173. // Fix up the table
  4174. m_rTree.prgpNode[HBODYINDEX(pNode->hBody)] = NULL;
  4175. // Increment Empty Count
  4176. m_rTree.cEmpty++;
  4177. // Free this node
  4178. _FreeTreeNodeInfo(pNode);
  4179. }
  4180. exit:
  4181. // Done
  4182. return hr;
  4183. }
  4184. // --------------------------------------------------------------------------------
  4185. // CMessageTree::_DeleteChildren
  4186. // --------------------------------------------------------------------------------
  4187. void CMessageTree::_DeleteChildren(LPTREENODEINFO pParent)
  4188. {
  4189. // Locals
  4190. ULONG i;
  4191. LPTREENODEINFO pNode;
  4192. // check params
  4193. Assert(pParent);
  4194. // Loop through bodies
  4195. for (i=0; i<m_rTree.cNodes; i++)
  4196. {
  4197. // Readability
  4198. pNode = m_rTree.prgpNode[i];
  4199. // Could be null if I already deleted it
  4200. if (NULL == pNode)
  4201. continue;
  4202. // pBody is Parent...
  4203. if (pParent == pNode->pParent)
  4204. {
  4205. // Free Children...
  4206. if (_IsMultiPart(pNode))
  4207. {
  4208. // Delete Children
  4209. _DeleteChildren(pNode);
  4210. }
  4211. // Unlink the node
  4212. _UnlinkTreeNode(pNode);
  4213. // Free this node
  4214. _FreeTreeNodeInfo(pNode);
  4215. // Fix up the table
  4216. m_rTree.prgpNode[i] = NULL;
  4217. // Increment Empty Count
  4218. m_rTree.cEmpty++;
  4219. }
  4220. }
  4221. }
  4222. // --------------------------------------------------------------------------------
  4223. // CMessageTree::MoveBody
  4224. // --------------------------------------------------------------------------------
  4225. STDMETHODIMP CMessageTree::MoveBody(HBODY hBody, BODYLOCATION location)
  4226. {
  4227. // Locals
  4228. HRESULT hr=S_OK;
  4229. LPTREENODEINFO pNode;
  4230. LPTREENODEINFO pPrev;
  4231. LPTREENODEINFO pNext;
  4232. LPTREENODEINFO pParent;
  4233. // check params
  4234. if (NULL == hBody)
  4235. return TrapError(E_INVALIDARG);
  4236. // Thread Safety
  4237. EnterCriticalSection(&m_cs);
  4238. // Validate handle
  4239. if (_FIsValidHandle(hBody) == FALSE)
  4240. {
  4241. hr = TrapError(MIME_E_INVALID_HANDLE);
  4242. goto exit;
  4243. }
  4244. // Cast
  4245. pNode = _PNodeFromHBody(hBody);
  4246. // Handle Location Type
  4247. switch(location)
  4248. {
  4249. // ------------------------------------------------------------------------------------
  4250. case IBL_PARENT:
  4251. // Root already
  4252. AssertSz(FALSE, "UNTESTED - PLEASE CALL SBAILEY AT X32553");
  4253. if (NULL == pNode->pParent || NULL == pNode->pParent->pParent)
  4254. {
  4255. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4256. goto exit;
  4257. }
  4258. // Set Parent
  4259. pParent = pNode->pParent;
  4260. // Parent better be a multipart
  4261. Assert(_IsMultiPart(pParent) && _IsMultiPart(pNode->pParent));
  4262. // Unlink from tree
  4263. _UnlinkTreeNode(pNode);
  4264. // Get the current first child
  4265. pPrev = pParent->pChildTail;
  4266. // Fixup pCurrent
  4267. pNode->pPrev = pPrev;
  4268. if (pPrev)
  4269. {
  4270. // Fixup pPrev
  4271. pPrev->pNext = pNode;
  4272. }
  4273. // Fixup Tail
  4274. pParent->pChildTail = pNode;
  4275. // Increment child count
  4276. pParent->cChildren++;
  4277. // Done
  4278. break;
  4279. // ------------------------------------------------------------------------------------
  4280. // This is a swap of two nodes in a doubly-linked list
  4281. case IBL_NEXT:
  4282. // No Next ?
  4283. AssertSz(FALSE, "UNTESTED - PLEASE CALL SBAILEY AT X32553");
  4284. if (NULL == pNode->pNext)
  4285. {
  4286. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4287. goto exit;
  4288. }
  4289. // Setup for move
  4290. pPrev = pNode->pPrev;
  4291. pNext = pNode->pNext;
  4292. // Set pNext up...
  4293. Assert(pNext->pPrev == pNode);
  4294. pNext->pPrev = pPrev;
  4295. // Setup pPrev
  4296. if (pPrev)
  4297. {
  4298. Assert(pPrev->pNext == pNode);
  4299. pPrev->pNext = pNext;
  4300. }
  4301. // Setup pNode->pNext
  4302. pNode->pNext = pNext->pNext;
  4303. if (pNode->pNext)
  4304. {
  4305. Assert(pNode->pNext->pPrev == pNext);
  4306. pNode->pNext->pPrev = pNode;
  4307. }
  4308. pNext->pNext = pNode;
  4309. // Setup pNode->pPrev
  4310. pNode->pPrev = pNext;
  4311. // Get Parent
  4312. pParent = pNode->pParent;
  4313. // Adjust Child and Tail...
  4314. if (pNode == pParent->pChildHead)
  4315. pParent->pChildHead = pNext;
  4316. if (pNext == pParent->pChildTail)
  4317. pParent->pChildTail = pNode;
  4318. // Done
  4319. break;
  4320. // ------------------------------------------------------------------------------------
  4321. // This is a swap of two nodes in a doubly-linked list (reverse of IBL_NEXT)
  4322. case IBL_PREVIOUS:
  4323. // No pPrev ?
  4324. AssertSz(FALSE, "UNTESTED - PLEASE CALL SBAILEY AT X32553");
  4325. if (NULL == pNode->pPrev)
  4326. {
  4327. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4328. goto exit;
  4329. }
  4330. // Setup for move
  4331. pPrev = pNode->pPrev;
  4332. pNext = pNode->pNext;
  4333. // Set pNext
  4334. Assert(pPrev->pNext == pNode);
  4335. pPrev->pNext = pNext;
  4336. // Setup pPrev
  4337. pPrev->pPrev = pNode;
  4338. // Fixup Net
  4339. if (pNext)
  4340. {
  4341. Assert(pNext->pPrev == pNode);
  4342. pNext->pPrev = pPrev;
  4343. }
  4344. // Setup pNode->pNext
  4345. pNode->pNext = pPrev;
  4346. // Setup pNode->pPrev
  4347. pNode->pPrev = pPrev->pPrev;
  4348. // Setup two(prev)->next
  4349. if (pNode->pPrev)
  4350. {
  4351. Assert(pNode->pPrev->pNext == pPrev);
  4352. pNode->pPrev->pNext = pNode;
  4353. }
  4354. // Get Parent
  4355. pParent = pNode->pParent;
  4356. // Adjust Child and Tail...
  4357. if (pNode == pParent->pChildTail)
  4358. pParent->pChildTail = pPrev;
  4359. if (pPrev == pParent->pChildHead)
  4360. pParent->pChildHead = pNode;
  4361. // Done
  4362. break;
  4363. // ------------------------------------------------------------------------------------
  4364. case IBL_FIRST:
  4365. // No Parent ?
  4366. if (NULL == pNode->pParent)
  4367. {
  4368. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4369. goto exit;
  4370. }
  4371. // Set Parent
  4372. pParent = pNode->pParent;
  4373. // Better be first child
  4374. if (NULL == pNode->pPrev)
  4375. {
  4376. Assert(pNode == pParent->pChildHead);
  4377. goto exit;
  4378. }
  4379. // Unlink this body
  4380. pPrev = pNode->pPrev;
  4381. pNext = pNode->pNext;
  4382. // If pPrev
  4383. pPrev->pNext = pNext;
  4384. // If pNext or pChildTail
  4385. if (pNext)
  4386. {
  4387. Assert(pNext->pPrev == pNode);
  4388. pNext->pPrev = pPrev;
  4389. }
  4390. else if (pParent)
  4391. {
  4392. Assert(pParent->pChildTail == pNode);
  4393. pParent->pChildTail = pPrev;
  4394. }
  4395. // Setup pNode
  4396. pNode->pNext = pParent->pChildHead;
  4397. pParent->pChildHead->pPrev = pNode;
  4398. pNode->pPrev = NULL;
  4399. pParent->pChildHead = pNode;
  4400. // Done
  4401. break;
  4402. // ------------------------------------------------------------------------------------
  4403. case IBL_LAST:
  4404. // No Parent ?
  4405. AssertSz(FALSE, "UNTESTED - PLEASE CALL SBAILEY AT X32553");
  4406. if (NULL == pNode->pParent)
  4407. {
  4408. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4409. goto exit;
  4410. }
  4411. // Set Parent
  4412. pParent = pNode->pParent;
  4413. // Better be first child
  4414. if (NULL == pNode->pNext)
  4415. {
  4416. Assert(pNode == pParent->pChildTail);
  4417. goto exit;
  4418. }
  4419. // Unlink this body
  4420. pPrev = pNode->pPrev;
  4421. pNext = pNode->pNext;
  4422. // If pPrev
  4423. pNext->pPrev = pPrev;
  4424. // If pNext or pChildTail
  4425. if (pPrev)
  4426. {
  4427. Assert(pPrev->pNext == pNode);
  4428. pPrev->pNext = pNext;
  4429. }
  4430. else if (pParent)
  4431. {
  4432. Assert(pParent->pChildHead == pNode);
  4433. pParent->pChildHead = pNext;
  4434. }
  4435. // Setup pNode
  4436. pNode->pPrev = pParent->pChildTail;
  4437. pNode->pNext = NULL;
  4438. pParent->pChildTail = pNode;
  4439. // Done
  4440. break;
  4441. // ------------------------------------------------------------------------------------
  4442. case IBL_ROOT:
  4443. hr = TrapError(MIME_E_CANT_MOVE_BODY);
  4444. goto exit;
  4445. // ------------------------------------------------------------------------------------
  4446. default:
  4447. hr = TrapError(MIME_E_BAD_BODY_LOCATION);
  4448. goto exit;
  4449. }
  4450. // Dirty
  4451. FLAGSET(m_dwState, TREESTATE_DIRTY);
  4452. exit:
  4453. // Thread Safety
  4454. LeaveCriticalSection(&m_cs);
  4455. // Done
  4456. return hr;
  4457. }
  4458. #ifndef WIN16
  4459. // --------------------------------------------------------------------------------
  4460. // CMessageTree::_UnlinkTreeNode
  4461. // --------------------------------------------------------------------------------
  4462. void CMessageTree::_UnlinkTreeNode(LPTREENODEINFO pNode)
  4463. {
  4464. // Locals
  4465. LPTREENODEINFO pPrev;
  4466. LPTREENODEINFO pNext;
  4467. LPTREENODEINFO pParent;
  4468. // Check Params
  4469. Assert(pNode);
  4470. // Set Next and Previous
  4471. pParent = pNode->pParent;
  4472. pPrev = pNode->pPrev;
  4473. pNext = pNode->pNext;
  4474. // If pPrev
  4475. if (pPrev)
  4476. pPrev->pNext = pNext;
  4477. else if (pParent)
  4478. pParent->pChildHead = pNext;
  4479. // If pNext
  4480. if (pNext)
  4481. pNext->pPrev = pPrev;
  4482. else if (pParent)
  4483. pParent->pChildTail = pPrev;
  4484. // Delete Children on Parent
  4485. if (pParent)
  4486. pParent->cChildren--;
  4487. // Cleanup pNode
  4488. pNode->pParent = NULL;
  4489. pNode->pNext = NULL;
  4490. pNode->pPrev = NULL;
  4491. pNode->pChildHead = NULL;
  4492. pNode->pChildTail = NULL;
  4493. }
  4494. // --------------------------------------------------------------------------------
  4495. // CMessageTree::CountBodies
  4496. // --------------------------------------------------------------------------------
  4497. STDMETHODIMP CMessageTree::CountBodies(HBODY hParent, boolean fRecurse, ULONG *pcBodies)
  4498. {
  4499. // Locals
  4500. HRESULT hr=S_OK;
  4501. LPTREENODEINFO pNode;
  4502. // check params
  4503. if (NULL == pcBodies)
  4504. return TrapError(E_INVALIDARG);
  4505. // Thread Safety
  4506. EnterCriticalSection(&m_cs);
  4507. // Init
  4508. *pcBodies = 0;
  4509. // No Parent ?
  4510. if (NULL == hParent || HBODY_ROOT == hParent)
  4511. {
  4512. // Is there a root..
  4513. if (NULL == m_pRootNode)
  4514. goto exit;
  4515. // Use Root
  4516. pNode = m_pRootNode;
  4517. }
  4518. // Otherwise, get parent...
  4519. else
  4520. {
  4521. // Validate handle
  4522. if (_FIsValidHandle(hParent) == FALSE)
  4523. {
  4524. hr = TrapError(MIME_E_INVALID_HANDLE);
  4525. goto exit;
  4526. }
  4527. // Cast
  4528. pNode = _PNodeFromHBody(hParent);
  4529. }
  4530. // Include the root
  4531. (*pcBodies)++;
  4532. // Count the children...
  4533. _CountChildrenInt(pNode, fRecurse, pcBodies);
  4534. exit:
  4535. // Thread Safety
  4536. LeaveCriticalSection(&m_cs);
  4537. // Done
  4538. return hr;
  4539. }
  4540. // --------------------------------------------------------------------------------
  4541. // CMessageTree::_CountChildrenInt
  4542. // --------------------------------------------------------------------------------
  4543. void CMessageTree::_CountChildrenInt(LPTREENODEINFO pParent, BOOL fRecurse, ULONG *pcChildren)
  4544. {
  4545. // Locals
  4546. LPTREENODEINFO pNode;
  4547. // check params
  4548. Assert(pParent && pcChildren);
  4549. // Loop through bodies
  4550. for (ULONG i=0; i<m_rTree.cNodes; i++)
  4551. {
  4552. // Readability
  4553. pNode = m_rTree.prgpNode[i];
  4554. // Empty..
  4555. if (NULL == pNode)
  4556. continue;
  4557. // pNode is Parent...
  4558. if (pParent == pNode->pParent)
  4559. {
  4560. // Increment Count
  4561. (*pcChildren)++;
  4562. // Free Children...
  4563. if (fRecurse && _IsMultiPart(pNode))
  4564. _CountChildrenInt(pNode, fRecurse, pcChildren);
  4565. }
  4566. }
  4567. }
  4568. // --------------------------------------------------------------------------------
  4569. // CMessageTree::FindFirst
  4570. // --------------------------------------------------------------------------------
  4571. STDMETHODIMP CMessageTree::FindFirst(LPFINDBODY pFindBody, LPHBODY phBody)
  4572. {
  4573. // Invalid Arg
  4574. if (NULL == pFindBody)
  4575. return TrapError(E_INVALIDARG);
  4576. // Init Find
  4577. pFindBody->dwReserved = 0;
  4578. // Find Next
  4579. return FindNext(pFindBody, phBody);
  4580. }
  4581. // --------------------------------------------------------------------------------
  4582. // CMessageTree::FindNext
  4583. // --------------------------------------------------------------------------------
  4584. STDMETHODIMP CMessageTree::FindNext(LPFINDBODY pFindBody, LPHBODY phBody)
  4585. {
  4586. // Locals
  4587. HRESULT hr=S_OK;
  4588. ULONG i;
  4589. LPTREENODEINFO pNode;
  4590. // check params
  4591. if (NULL == pFindBody || NULL == phBody)
  4592. return TrapError(E_INVALIDARG);
  4593. // Thread Safety
  4594. EnterCriticalSection(&m_cs);
  4595. // Init
  4596. *phBody = NULL;
  4597. // Loop
  4598. for (i=pFindBody->dwReserved; i<m_rTree.cNodes; i++)
  4599. {
  4600. // If delete
  4601. pNode = m_rTree.prgpNode[i];
  4602. // Empty
  4603. if (NULL == pNode)
  4604. continue;
  4605. // Compare content type
  4606. if (pNode->pContainer->IsContentType(pFindBody->pszPriType, pFindBody->pszSubType) == S_OK)
  4607. {
  4608. // Save Index of next item to search
  4609. pFindBody->dwReserved = i + 1;
  4610. *phBody = pNode->hBody;
  4611. goto exit;
  4612. }
  4613. }
  4614. // Error
  4615. pFindBody->dwReserved = m_rTree.cNodes;
  4616. hr = MIME_E_NOT_FOUND;
  4617. exit:
  4618. // Thread Safety
  4619. LeaveCriticalSection(&m_cs);
  4620. // Done
  4621. return hr;
  4622. }
  4623. // --------------------------------------------------------------------------------
  4624. // CMessageTree::ToMultipart
  4625. // --------------------------------------------------------------------------------
  4626. STDMETHODIMP CMessageTree::ToMultipart(HBODY hBody, LPCSTR pszSubType, LPHBODY phMultipart)
  4627. {
  4628. // Locals
  4629. HRESULT hr=S_OK;
  4630. LPTREENODEINFO pNode;
  4631. LPTREENODEINFO pNew=NULL;
  4632. LPTREENODEINFO pParent;
  4633. LPTREENODEINFO pNext;
  4634. LPTREENODEINFO pPrev;
  4635. // check params
  4636. if (NULL == hBody || NULL == pszSubType)
  4637. return TrapError(E_INVALIDARG);
  4638. // Thread Safety
  4639. EnterCriticalSection(&m_cs);
  4640. // Init
  4641. if (phMultipart)
  4642. *phMultipart = NULL;
  4643. // Get the body from hBody
  4644. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4645. // We better have a root
  4646. Assert(m_pRootNode);
  4647. // If pNode does not have a parent...
  4648. if (NULL == pNode->pParent)
  4649. {
  4650. // pNode must be the root ?
  4651. Assert(m_pRootNode == pNode);
  4652. // Create Object
  4653. //N duplicated
  4654. CHECKHR(hr = _HrCreateTreeNode(&pNew));
  4655. // Set pNode First and Last...
  4656. pNew->pChildHead = m_pRootNode;
  4657. pNew->pChildTail = m_pRootNode;
  4658. m_pRootNode->pParent = pNew;
  4659. // Set Children Count
  4660. pNew->cChildren = 1;
  4661. // Set new root
  4662. m_pRootNode = pNew;
  4663. // Return New Multipart Handle
  4664. if (phMultipart)
  4665. *phMultipart = pNew->hBody;
  4666. // Swap Property Sets...
  4667. Assert(m_pRootNode != pNode);
  4668. m_pRootNode->pBody->SwitchContainers(pNode->pBody);
  4669. // Copy Some Props Across
  4670. CHECKHR(hr = m_pRootNode->pBody->MoveProps(ARRAYSIZE(g_rgszToMultipart), g_rgszToMultipart, pNode->pBody));
  4671. }
  4672. // Otherwise, create a body that takes the place of pNode
  4673. else
  4674. {
  4675. // Create a body object
  4676. CHECKHR(hr = _HrCreateTreeNode(&pNew));
  4677. // DON'T FAIL FROM HERE TO END OF FUNCTION
  4678. // Return New Multipart Handle
  4679. if (phMultipart)
  4680. *phMultipart = pNew->hBody;
  4681. // Assume the position of pNode
  4682. pNew->pParent = pNode->pParent;
  4683. pNew->pPrev = pNode->pPrev;
  4684. pNew->pNext = pNode->pNext;
  4685. pNew->pChildHead = pNode;
  4686. pNew->pChildTail = pNode;
  4687. pNew->cChildren = 1;
  4688. // Set pParnet
  4689. pParent = pNode->pParent;
  4690. // Fix up parent head and child
  4691. if (pParent->pChildHead == pNode)
  4692. pParent->pChildHead = pNew;
  4693. if (pParent->pChildTail == pNode)
  4694. pParent->pChildTail = pNew;
  4695. // Set pNode Parent
  4696. pNode->pParent = pNew;
  4697. // Fixup pNext and pPrev
  4698. pNext = pNode->pNext;
  4699. pPrev = pNode->pPrev;
  4700. if (pNext)
  4701. pNext->pPrev = pNew;
  4702. if (pPrev)
  4703. pPrev->pNext = pNew;
  4704. // Clear pNext and pPrev
  4705. pNode->pNext = NULL;
  4706. pNode->pPrev = NULL;
  4707. }
  4708. // Change this nodes content type
  4709. CHECKHR(hr = pNew->pContainer->SetProp(SYM_ATT_PRITYPE, STR_CNT_MULTIPART));
  4710. CHECKHR(hr = pNew->pContainer->SetProp(SYM_ATT_SUBTYPE, pszSubType));
  4711. pNode->pBody->CopyOptionsTo(pNew->pBody);
  4712. exit:
  4713. // Create Worked
  4714. _PostCreateTreeNode(hr, pNew);
  4715. // Thread Safety
  4716. LeaveCriticalSection(&m_cs);
  4717. // Done
  4718. return hr;
  4719. }
  4720. // --------------------------------------------------------------------------------
  4721. // CMessageTree::_HrNodeFromHandle
  4722. // --------------------------------------------------------------------------------
  4723. HRESULT CMessageTree::_HrNodeFromHandle(HBODY hBody, LPTREENODEINFO *ppBody)
  4724. {
  4725. // Invalid Arg
  4726. Assert(hBody && ppBody);
  4727. // Root ?
  4728. if ((HBODY)HBODY_ROOT == hBody)
  4729. {
  4730. // No Root
  4731. if (NULL == m_pRootNode)
  4732. return MIME_E_NO_DATA;
  4733. // Otherwise, use root
  4734. *ppBody = m_pRootNode;
  4735. }
  4736. // Otherwise
  4737. else
  4738. {
  4739. // Validate handle
  4740. if (_FIsValidHandle(hBody) == FALSE)
  4741. return TrapError(MIME_E_INVALID_HANDLE);
  4742. // Get Node
  4743. *ppBody = _PNodeFromHBody(hBody);
  4744. }
  4745. // Done
  4746. return S_OK;
  4747. }
  4748. // --------------------------------------------------------------------------------
  4749. // CMessageTree::IsBodyType
  4750. // --------------------------------------------------------------------------------
  4751. HRESULT CMessageTree::IsBodyType(HBODY hBody, IMSGBODYTYPE bodytype)
  4752. {
  4753. // Locals
  4754. HRESULT hr=S_OK;
  4755. LPTREENODEINFO pNode;
  4756. // check params
  4757. if (NULL == hBody)
  4758. return TrapError(E_INVALIDARG);
  4759. // Thread Safety
  4760. EnterCriticalSection(&m_cs);
  4761. // Get body
  4762. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4763. // Call into body object
  4764. hr = pNode->pBody->IsType(bodytype);
  4765. exit:
  4766. // Thread Safety
  4767. LeaveCriticalSection(&m_cs);
  4768. // Done
  4769. return hr;
  4770. }
  4771. // --------------------------------------------------------------------------------
  4772. // CMessageTree::IsContentType
  4773. // --------------------------------------------------------------------------------
  4774. STDMETHODIMP CMessageTree::IsContentType(HBODY hBody, LPCSTR pszPriType, LPCSTR pszSubType)
  4775. {
  4776. // Locals
  4777. HRESULT hr=S_OK;
  4778. LPTREENODEINFO pNode;
  4779. // check params
  4780. if (NULL == hBody)
  4781. return TrapError(E_INVALIDARG);
  4782. // Thread Safety
  4783. EnterCriticalSection(&m_cs);
  4784. // Get body
  4785. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4786. // Call into body object
  4787. hr = pNode->pContainer->IsContentType(pszPriType, pszSubType);
  4788. exit:
  4789. // Thread Safety
  4790. LeaveCriticalSection(&m_cs);
  4791. // Done
  4792. return hr;
  4793. }
  4794. // --------------------------------------------------------------------------------
  4795. // CMessageTree::QueryBodyProp
  4796. // --------------------------------------------------------------------------------
  4797. STDMETHODIMP CMessageTree::QueryBodyProp(HBODY hBody, LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive)
  4798. {
  4799. // Locals
  4800. HRESULT hr=S_OK;
  4801. LPTREENODEINFO pNode;
  4802. // check params
  4803. if (NULL == hBody)
  4804. return TrapError(E_INVALIDARG);
  4805. // Thread Safety
  4806. EnterCriticalSection(&m_cs);
  4807. // Get body
  4808. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4809. // Call into body object
  4810. hr = pNode->pContainer->QueryProp(pszName, pszCriteria, fSubString, fCaseSensitive);
  4811. exit:
  4812. // Thread Safety
  4813. LeaveCriticalSection(&m_cs);
  4814. // Done
  4815. return hr;
  4816. }
  4817. // --------------------------------------------------------------------------------
  4818. // CMessageTree::GetBodyProp
  4819. // --------------------------------------------------------------------------------
  4820. STDMETHODIMP CMessageTree::GetBodyProp(HBODY hBody, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue)
  4821. {
  4822. // Locals
  4823. HRESULT hr=S_OK;
  4824. LPTREENODEINFO pNode;
  4825. // check params
  4826. if (NULL == hBody)
  4827. return TrapError(E_INVALIDARG);
  4828. // Thread Safety
  4829. EnterCriticalSection(&m_cs);
  4830. // Get body
  4831. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4832. // Call into body object
  4833. hr = pNode->pContainer->GetProp(pszName, dwFlags, pValue);
  4834. exit:
  4835. // Thread Safety
  4836. LeaveCriticalSection(&m_cs);
  4837. // Done
  4838. return hr;
  4839. }
  4840. // --------------------------------------------------------------------------------
  4841. // CMessageTree::SetBodyProp
  4842. // --------------------------------------------------------------------------------
  4843. STDMETHODIMP CMessageTree::SetBodyProp(HBODY hBody, LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue)
  4844. {
  4845. // Locals
  4846. HRESULT hr=S_OK;
  4847. LPTREENODEINFO pNode;
  4848. // check params
  4849. if (NULL == hBody)
  4850. return TrapError(E_INVALIDARG);
  4851. // Thread Safety
  4852. EnterCriticalSection(&m_cs);
  4853. // Get body
  4854. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4855. // Call into body object
  4856. hr = pNode->pContainer->SetProp(pszName, dwFlags, pValue);
  4857. exit:
  4858. // Thread Safety
  4859. LeaveCriticalSection(&m_cs);
  4860. // Done
  4861. return hr;
  4862. }
  4863. // --------------------------------------------------------------------------------
  4864. // CMessageTree::DeleteBodyProp
  4865. // --------------------------------------------------------------------------------
  4866. STDMETHODIMP CMessageTree::DeleteBodyProp(HBODY hBody, LPCSTR pszName)
  4867. {
  4868. // Locals
  4869. HRESULT hr=S_OK;
  4870. LPTREENODEINFO pNode;
  4871. // check params
  4872. if (NULL == hBody)
  4873. return TrapError(E_INVALIDARG);
  4874. // Thread Safety
  4875. EnterCriticalSection(&m_cs);
  4876. // Get body
  4877. CHECKHR(hr = _HrNodeFromHandle(hBody, &pNode));
  4878. // Call into body object
  4879. hr = pNode->pContainer->DeleteProp(pszName);
  4880. exit:
  4881. // Thread Safety
  4882. LeaveCriticalSection(&m_cs);
  4883. // Done
  4884. return hr;
  4885. }
  4886. // --------------------------------------------------------------------------------
  4887. // CMessageTree::_FIsUuencodeBegin
  4888. // --------------------------------------------------------------------------------
  4889. BOOL CMessageTree::_FIsUuencodeBegin(LPPROPSTRINGA pLine, LPSTR *ppszFileName)
  4890. {
  4891. // Locals
  4892. ULONG i;
  4893. // check params
  4894. Assert(ISVALIDSTRINGA(pLine));
  4895. // Length must be at least 11 to accomodate "begin 666 " and the first character of a filename.
  4896. if (pLine->cchVal < 11)
  4897. return FALSE;
  4898. // First 6 characters must be "begin ", or we're not a valid line.
  4899. if (StrCmpN(pLine->pszVal, "begin ", 6) != 0)
  4900. return FALSE;
  4901. // Check characters 6-8 for valid Unix filemode. They must all be digits between 0 and 7.
  4902. for (i=6; i<pLine->cchVal; i++)
  4903. {
  4904. if (pLine->pszVal[i] < '0' || pLine->pszVal[i] > '7')
  4905. break;
  4906. }
  4907. // Not a begin line
  4908. if (pLine->pszVal[i] != ' ')
  4909. return FALSE;
  4910. // Get File Name
  4911. if (ppszFileName)
  4912. {
  4913. *ppszFileName = PszDupA(pLine->pszVal + i + 1);
  4914. ULONG cbLine = lstrlen (*ppszFileName);
  4915. StripCRLF(*ppszFileName, &cbLine);
  4916. }
  4917. // Done
  4918. return TRUE;
  4919. }
  4920. // --------------------------------------------------------------------------------
  4921. // CMessageTree::_GetMimeBoundaryType
  4922. // --------------------------------------------------------------------------------
  4923. BOUNDARYTYPE CMessageTree::_GetMimeBoundaryType(LPPROPSTRINGA pLine, LPPROPSTRINGA pBoundary)
  4924. {
  4925. // Locals
  4926. BOUNDARYTYPE boundary=BOUNDARY_NONE;
  4927. CHAR ch;
  4928. ULONG cchLine=pLine->cchVal;
  4929. LPSTR psz1, psz2;
  4930. // Check Params
  4931. Assert(ISVALIDSTRINGA(pBoundary) && ISVALIDSTRINGA(pLine));
  4932. // Check First two chars of the line
  4933. if ('-' != pLine->pszVal[0] || '-' != pLine->pszVal[1])
  4934. goto exit;
  4935. // Removes trailing white spaces
  4936. while(pLine->cchVal > 0)
  4937. {
  4938. // Get the last character
  4939. ch = *(pLine->pszVal + (pLine->cchVal - 1));
  4940. // No LWSP or CRLF
  4941. if (' ' != ch && '\t' != ch && chCR != ch && chLF != ch)
  4942. break;
  4943. // Decrement Length
  4944. pLine->cchVal--;
  4945. }
  4946. // Decrement two for --
  4947. pLine->cchVal -= 2;
  4948. // Checks line length against boundary length
  4949. if (pLine->cchVal != pBoundary->cchVal && pLine->cchVal != pBoundary->cchVal + 2)
  4950. goto exit;
  4951. // Compare the line with the boundary
  4952. if (StrCmpN(pLine->pszVal + 2, pBoundary->pszVal, (size_t)pBoundary->cchVal) == 0)
  4953. {
  4954. // BOUNDARY_MIMEEND
  4955. if ((pLine->cchVal > pBoundary->cchVal) && (pLine->pszVal[pBoundary->cchVal+2] == '-') && (pLine->pszVal[pBoundary->cchVal+3] == '-'))
  4956. boundary = BOUNDARY_MIMEEND;
  4957. // BOUNDARY_MIMENEXT
  4958. else if (pLine->cchVal == pBoundary->cchVal)
  4959. boundary = BOUNDARY_MIMENEXT;
  4960. }
  4961. exit:
  4962. // Relace the Length
  4963. pLine->cchVal = cchLine;
  4964. // Done
  4965. return boundary;
  4966. }
  4967. // --------------------------------------------------------------------------------
  4968. // CMessageTree::ResolveURL
  4969. // --------------------------------------------------------------------------------
  4970. STDMETHODIMP CMessageTree::ResolveURL(HBODY hRelated, LPCSTR pszBase, LPCSTR pszURL,
  4971. DWORD dwFlags, LPHBODY phBody)
  4972. {
  4973. // Locals
  4974. HRESULT hr=S_OK;
  4975. LPTREENODEINFO pSearchRoot;
  4976. RESOLVEURLINFO rInfo;
  4977. HBODY hBody=NULL;
  4978. PROPSTRINGA rBaseUrl;
  4979. LPSTR pszFree=NULL;
  4980. LPSTR pszFree2=NULL;
  4981. BOOL fMultipartBase=FALSE;
  4982. // InvalidArg
  4983. if (NULL == pszURL)
  4984. return TrapError(E_INVALIDARG);
  4985. // Init
  4986. if (phBody)
  4987. *phBody = NULL;
  4988. // Thread Safety
  4989. EnterCriticalSection(&m_cs);
  4990. // If hRelated is NULL, find the first multipart/related
  4991. if (NULL == hRelated)
  4992. {
  4993. // Find the Related
  4994. if (FAILED(MimeOleGetRelatedSection(this, FALSE, &hRelated, NULL)))
  4995. {
  4996. // Use Root
  4997. hRelated = m_pRootNode->hBody;
  4998. }
  4999. }
  5000. // Get Default Base
  5001. if (NULL == pszBase && FALSE == ISFLAGSET(dwFlags, URL_RESULVE_NO_BASE))
  5002. {
  5003. // Compute the content-base
  5004. if (SUCCEEDED(MimeOleComputeContentBase(this, hRelated, &pszFree, &fMultipartBase)))
  5005. pszBase = pszFree;
  5006. }
  5007. // Setup Resolve URL Info
  5008. ZeroMemory(&rInfo, sizeof(RESOLVEURLINFO));
  5009. // This is the base that we will use to absolutify URLs that are in the text/html body
  5010. rInfo.pszBase = pszBase;
  5011. // Set the url that we are looking for, could be combined with rInfo.pszBase
  5012. rInfo.pszURL = pszURL;
  5013. // Are we searching for a CID type URL.
  5014. if (StrCmpNI(pszURL, c_szCID, lstrlen(c_szCID)) == 0)
  5015. {
  5016. rInfo.fIsCID = TRUE;
  5017. rInfo.pszURL += 4;
  5018. }
  5019. else
  5020. rInfo.fIsCID = FALSE;
  5021. // Raid-62579: Athena: Need to support MHTML content-base inheritance
  5022. if (hRelated)
  5023. {
  5024. // Did pszBase come from the multipart/related section ?
  5025. if (fMultipartBase)
  5026. rInfo.pszInheritBase = pszBase;
  5027. // Otherwise, lookup the multipart/related base header
  5028. else
  5029. rInfo.pszInheritBase = pszFree2 = MimeOleContentBaseFromBody(this, hRelated);
  5030. }
  5031. // Get a Body from the Handle
  5032. CHECKHR(hr = _HrNodeFromHandle(rInfo.fIsCID ? HBODY_ROOT : hRelated, &pSearchRoot));
  5033. // Recurse the Tree
  5034. CHECKHR(hr = _HrRecurseResolveURL(pSearchRoot, &rInfo, &hBody));
  5035. // Not found
  5036. if (NULL == hBody)
  5037. {
  5038. hr = TrapError(MIME_E_NOT_FOUND);
  5039. goto exit;
  5040. }
  5041. // Return It ?
  5042. if (phBody)
  5043. *phBody = hBody;
  5044. // Mark as Resolved ?
  5045. if (ISFLAGSET(dwFlags, URL_RESOLVE_RENDERED))
  5046. {
  5047. // Defref the body
  5048. LPTREENODEINFO pNode = _PNodeFromHBody(hBody);
  5049. // Set Rendered
  5050. PROPVARIANT rVariant;
  5051. rVariant.vt = VT_UI4;
  5052. rVariant.ulVal = TRUE;
  5053. // Set the Property
  5054. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  5055. }
  5056. exit:
  5057. // Thread Safety
  5058. LeaveCriticalSection(&m_cs);
  5059. // Cleanup
  5060. SafeMemFree(pszFree);
  5061. SafeMemFree(pszFree2);
  5062. // Done
  5063. return hr;
  5064. }
  5065. // --------------------------------------------------------------------------------
  5066. // CMessageTree::_HrRecurseResolveURL
  5067. // --------------------------------------------------------------------------------
  5068. HRESULT CMessageTree::_HrRecurseResolveURL(LPTREENODEINFO pNode, LPRESOLVEURLINFO pInfo, LPHBODY phBody)
  5069. {
  5070. // Locals
  5071. HRESULT hr=S_OK;
  5072. LPTREENODEINFO pChild;
  5073. // Invalid Arg
  5074. Assert(pNode && pInfo && phBody);
  5075. // We must have not found the body yet ?
  5076. Assert(NULL == *phBody);
  5077. // If this is a multipart item, lets search it's children
  5078. if (_IsMultiPart(pNode))
  5079. {
  5080. // Loop Children
  5081. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  5082. {
  5083. // Check body
  5084. Assert(pChild->pParent == pNode);
  5085. // Bind the body table for this dude
  5086. CHECKHR(hr = _HrRecurseResolveURL(pChild, pInfo, phBody));
  5087. // Done
  5088. if (NULL != *phBody)
  5089. break;
  5090. }
  5091. }
  5092. // Get Character Set Information
  5093. else
  5094. {
  5095. // Ask the container to do the resolution
  5096. if (SUCCEEDED(pNode->pContainer->HrResolveURL(pInfo)))
  5097. {
  5098. // Cool we found the body, we resolved the URL
  5099. *phBody = pNode->hBody;
  5100. }
  5101. }
  5102. exit:
  5103. // Done
  5104. return hr;
  5105. }
  5106. // --------------------------------------------------------------------------------
  5107. // CMessageTree::GetProp
  5108. // --------------------------------------------------------------------------------
  5109. STDMETHODIMP CMessageTree::GetProp(LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue)
  5110. {
  5111. EnterCriticalSection(&m_cs);
  5112. Assert(m_pRootNode && m_pRootNode->pContainer);
  5113. HRESULT hr = m_pRootNode->pContainer->GetProp(pszName, dwFlags, pValue);
  5114. LeaveCriticalSection(&m_cs);
  5115. return hr;
  5116. }
  5117. STDMETHODIMP CMessageTree::GetPropW(LPCWSTR pwszName, DWORD dwFlags, LPPROPVARIANT pValue)
  5118. {
  5119. return TraceResult(E_NOTIMPL);
  5120. }
  5121. // --------------------------------------------------------------------------------
  5122. // CMessageTree::SetProp
  5123. // --------------------------------------------------------------------------------
  5124. STDMETHODIMP CMessageTree::SetProp(LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue)
  5125. {
  5126. EnterCriticalSection(&m_cs);
  5127. Assert(m_pRootNode && m_pRootNode->pContainer);
  5128. HRESULT hr = m_pRootNode->pContainer->SetProp(pszName, dwFlags, pValue);
  5129. LeaveCriticalSection(&m_cs);
  5130. return hr;
  5131. }
  5132. STDMETHODIMP CMessageTree::SetPropW(LPCWSTR pwszName, DWORD dwFlags, LPCPROPVARIANT pValue)
  5133. {
  5134. return TraceResult(E_NOTIMPL);
  5135. }
  5136. // --------------------------------------------------------------------------------
  5137. // CMessageTree::DeleteProp
  5138. // --------------------------------------------------------------------------------
  5139. STDMETHODIMP CMessageTree::DeleteProp(LPCSTR pszName)
  5140. {
  5141. EnterCriticalSection(&m_cs);
  5142. Assert(m_pRootNode && m_pRootNode->pContainer);
  5143. HRESULT hr = m_pRootNode->pContainer->DeleteProp(pszName);
  5144. LeaveCriticalSection(&m_cs);
  5145. return hr;
  5146. }
  5147. STDMETHODIMP CMessageTree::DeletePropW(LPCWSTR pwszName)
  5148. {
  5149. return TraceResult(E_NOTIMPL);
  5150. }
  5151. // --------------------------------------------------------------------------------
  5152. // CMessageTree::QueryProp
  5153. // --------------------------------------------------------------------------------
  5154. STDMETHODIMP CMessageTree::QueryProp(LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive)
  5155. {
  5156. EnterCriticalSection(&m_cs);
  5157. Assert(m_pRootNode && m_pRootNode->pContainer);
  5158. HRESULT hr = m_pRootNode->pContainer->QueryProp(pszName, pszCriteria, fSubString, fCaseSensitive);
  5159. LeaveCriticalSection(&m_cs);
  5160. return hr;
  5161. }
  5162. STDMETHODIMP CMessageTree::QueryPropW(LPCWSTR pwszName, LPCWSTR pwszCriteria, boolean fSubString, boolean fCaseSensitive)
  5163. {
  5164. return TraceResult(E_NOTIMPL);
  5165. }
  5166. // --------------------------------------------------------------------------------
  5167. // CMessageTree::GetAddressTable
  5168. // --------------------------------------------------------------------------------
  5169. STDMETHODIMP CMessageTree::GetAddressTable(IMimeAddressTable **ppTable)
  5170. {
  5171. EnterCriticalSection(&m_cs);
  5172. Assert(m_pRootNode && m_pRootNode->pContainer);
  5173. HRESULT hr = m_pRootNode->pContainer->BindToObject(IID_IMimeAddressTable, (LPVOID *)ppTable);
  5174. LeaveCriticalSection(&m_cs);
  5175. return hr;
  5176. }
  5177. // --------------------------------------------------------------------------------
  5178. // CMessageTree::GetSender
  5179. // --------------------------------------------------------------------------------
  5180. STDMETHODIMP CMessageTree::GetSender(LPADDRESSPROPS pAddress)
  5181. {
  5182. EnterCriticalSection(&m_cs);
  5183. Assert(m_pRootNode && m_pRootNode->pContainer);
  5184. HRESULT hr = m_pRootNode->pContainer->GetSender(pAddress);
  5185. LeaveCriticalSection(&m_cs);
  5186. return hr;
  5187. }
  5188. // --------------------------------------------------------------------------------
  5189. // CMessageTree::GetAddressTypes
  5190. // --------------------------------------------------------------------------------
  5191. STDMETHODIMP CMessageTree::GetAddressTypes(DWORD dwAdrTypes, DWORD dwProps, LPADDRESSLIST pList)
  5192. {
  5193. EnterCriticalSection(&m_cs);
  5194. Assert(m_pRootNode && m_pRootNode->pContainer);
  5195. HRESULT hr = m_pRootNode->pContainer->GetTypes(dwAdrTypes, dwProps, pList);
  5196. LeaveCriticalSection(&m_cs);
  5197. return hr;
  5198. }
  5199. // --------------------------------------------------------------------------------
  5200. // CMessageTree::GetAddressFormat
  5201. // --------------------------------------------------------------------------------
  5202. STDMETHODIMP CMessageTree::GetAddressFormat(DWORD dwAdrType, ADDRESSFORMAT format, LPSTR *ppszFormat)
  5203. {
  5204. EnterCriticalSection(&m_cs);
  5205. Assert(m_pRootNode && m_pRootNode->pContainer);
  5206. HRESULT hr = m_pRootNode->pContainer->GetFormat(dwAdrType, format, ppszFormat);
  5207. LeaveCriticalSection(&m_cs);
  5208. return hr;
  5209. }
  5210. // --------------------------------------------------------------------------------
  5211. // CMessageTree::GetAddressFormatW
  5212. // --------------------------------------------------------------------------------
  5213. STDMETHODIMP CMessageTree::GetAddressFormatW(DWORD dwAdrType, ADDRESSFORMAT format, LPWSTR *ppszFormat)
  5214. {
  5215. EnterCriticalSection(&m_cs);
  5216. Assert(m_pRootNode && m_pRootNode->pContainer);
  5217. HRESULT hr = m_pRootNode->pContainer->GetFormatW(dwAdrType, format, ppszFormat);
  5218. LeaveCriticalSection(&m_cs);
  5219. return hr;
  5220. }
  5221. // --------------------------------------------------------------------------------
  5222. // CMessageTree::EnumAddressTypes
  5223. // --------------------------------------------------------------------------------
  5224. STDMETHODIMP CMessageTree::EnumAddressTypes(DWORD dwAdrTypes, DWORD dwProps, IMimeEnumAddressTypes **ppEnum)
  5225. {
  5226. EnterCriticalSection(&m_cs);
  5227. Assert(m_pRootNode && m_pRootNode->pContainer);
  5228. HRESULT hr = m_pRootNode->pContainer->EnumTypes(dwAdrTypes, dwProps, ppEnum);
  5229. LeaveCriticalSection(&m_cs);
  5230. return hr;
  5231. }
  5232. // --------------------------------------------------------------------------------
  5233. // CMessageTree::_HrGetTextTypeInfo
  5234. // --------------------------------------------------------------------------------
  5235. HRESULT CMessageTree::_HrGetTextTypeInfo(DWORD dwTxtType, LPTEXTTYPEINFO *ppTextInfo)
  5236. {
  5237. // Invalid Arg
  5238. Assert(ppTextInfo);
  5239. // Init
  5240. *ppTextInfo = NULL;
  5241. // Locate the text type
  5242. for (ULONG i=0; i<ARRAYSIZE(g_rgTextInfo); i++)
  5243. {
  5244. // Desired Text Type
  5245. if (g_rgTextInfo[i].dwTxtType == dwTxtType)
  5246. {
  5247. // Found It
  5248. *ppTextInfo = (LPTEXTTYPEINFO)&g_rgTextInfo[i];
  5249. return S_OK;
  5250. }
  5251. }
  5252. // Un-identified text type
  5253. if (NULL == *ppTextInfo)
  5254. return TrapError(MIME_E_INVALID_TEXT_TYPE);
  5255. // Done
  5256. return S_OK;
  5257. }
  5258. // --------------------------------------------------------------------------------
  5259. // CMessageTree::_FindDisplayableTextBody
  5260. // --------------------------------------------------------------------------------
  5261. HRESULT CMessageTree::_FindDisplayableTextBody(LPCSTR pszSubType,
  5262. LPTREENODEINFO pNode, LPHBODY phBody)
  5263. {
  5264. // Locals
  5265. HRESULT hr=S_OK;
  5266. ULONG cBodies;
  5267. LPTREENODEINFO pChild;
  5268. // Invalid Arg
  5269. Assert(pNode && phBody && pszSubType && NULL == *phBody);
  5270. // If this is a multipart item, lets search it's children
  5271. if (_IsMultiPart(pNode))
  5272. {
  5273. // Loop Children
  5274. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  5275. {
  5276. // Check body
  5277. Assert(pChild->pParent == pNode);
  5278. // Bind the body table for this dude
  5279. hr = _FindDisplayableTextBody(pszSubType, pChild, phBody);
  5280. // Done ?
  5281. if (SUCCEEDED(hr))
  5282. {
  5283. Assert(*phBody);
  5284. goto exit;
  5285. }
  5286. }
  5287. }
  5288. // Otherwise...
  5289. else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, pszSubType))
  5290. {
  5291. // If not an attachment...
  5292. if (S_FALSE == IsBodyType(pNode->hBody, IBT_ATTACHMENT))
  5293. {
  5294. *phBody = pNode->hBody;
  5295. goto exit;
  5296. }
  5297. // Otherwise...Raid 43444: Inbox Direct messages showing as attachments
  5298. else
  5299. {
  5300. // Count Bodies
  5301. CHECKHR(hr = CountBodies(NULL, TRUE, &cBodies));
  5302. // Only one body...
  5303. if (cBodies == 1)
  5304. {
  5305. // Inline or Disposition is not set
  5306. if (m_pRootNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_INLINE, FALSE, FALSE) == S_OK ||
  5307. m_pRootNode->pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTDISP)) == S_FALSE)
  5308. {
  5309. *phBody = pNode->hBody;
  5310. goto exit;
  5311. }
  5312. }
  5313. }
  5314. }
  5315. // Not Found
  5316. hr = MIME_E_NOT_FOUND;
  5317. exit:
  5318. // Done
  5319. return(hr);
  5320. }
  5321. // --------------------------------------------------------------------------------
  5322. // CMessageTree::GetTextBody
  5323. // --------------------------------------------------------------------------------
  5324. STDMETHODIMP CMessageTree::GetTextBody(DWORD dwTxtType, ENCODINGTYPE ietEncoding,
  5325. IStream **ppStream, LPHBODY phBody)
  5326. {
  5327. // Locals
  5328. HRESULT hr=S_OK;
  5329. HRESULT hrFind;
  5330. LPTEXTTYPEINFO pTextInfo=NULL;
  5331. FINDBODY rFind;
  5332. IMimeBody *pBody=NULL;
  5333. PROPVARIANT rStart;
  5334. PROPVARIANT rVariant;
  5335. HBODY hAlternativeParent;
  5336. HBODY hFirst=NULL;
  5337. HBODY hChild;
  5338. HBODY hBody=NULL;
  5339. HBODY hRelated;
  5340. LPSTR pszStartCID=NULL;
  5341. BOOL fMarkRendered=TRUE;
  5342. // Thread Safety
  5343. EnterCriticalSection(&m_cs);
  5344. // Init
  5345. if (phBody)
  5346. *phBody = NULL;
  5347. if (ppStream)
  5348. *ppStream = NULL;
  5349. // Init
  5350. MimeOleVariantInit(&rStart);
  5351. // Get the Text Info
  5352. CHECKHR(hr = _HrGetTextTypeInfo(dwTxtType, &pTextInfo));
  5353. // MimeHTML
  5354. if (SUCCEEDED(MimeOleGetRelatedSection(this, FALSE, &hRelated, NULL)))
  5355. {
  5356. // Get start= parameter
  5357. if (SUCCEEDED(GetBodyProp(hRelated, STR_PAR_START, 0, &rStart)))
  5358. {
  5359. // Raid 63823: Mail : Content-Location Href's inside the message do not work if there is a Start Parameter in headers
  5360. // The start parameter can only specify a CID.
  5361. // I need to prefix cid: onto the front of rStart
  5362. DWORD cchSize = (lstrlen(rStart.pszVal) + lstrlen(c_szCID) + 1);
  5363. CHECKALLOC(pszStartCID = PszAllocA(cchSize));
  5364. // Format the CID
  5365. wnsprintfA(pszStartCID, cchSize, "%s%s", c_szCID, rStart.pszVal);
  5366. // Resolve this URL
  5367. ResolveURL(hRelated, NULL, pszStartCID, URL_RESULVE_NO_BASE, &hBody);
  5368. }
  5369. }
  5370. // Still haven't found a text body ?
  5371. if (NULL == hBody)
  5372. {
  5373. // FindTextBody
  5374. hr = _FindDisplayableTextBody(pTextInfo->pszSubType, m_pRootNode, &hBody);
  5375. // If that failed and we were looking for html, try to get enriched text...
  5376. if (FAILED(hr) && ISFLAGSET(dwTxtType, TXT_HTML))
  5377. {
  5378. // Looking for text/html, lets try to find text/enriched
  5379. hr = _FindDisplayableTextBody(STR_SUB_ENRICHED, m_pRootNode, &hBody);
  5380. }
  5381. // Not Found
  5382. if (FAILED(hr))
  5383. {
  5384. hr = TrapError(MIME_E_NOT_FOUND);
  5385. goto exit;
  5386. }
  5387. // Reset hr
  5388. hr = S_OK;
  5389. }
  5390. // Get the stream...
  5391. CHECKHR(hr = BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody));
  5392. // If Empty...
  5393. if (pBody->IsType(IBT_EMPTY) == S_OK)
  5394. {
  5395. hr = MIME_E_NO_DATA;
  5396. goto exit;
  5397. }
  5398. // User Wants the Data
  5399. if (ppStream)
  5400. {
  5401. // If content-type is text/enriched, convert to html
  5402. if (pBody->IsContentType(STR_CNT_TEXT, STR_SUB_ENRICHED) == S_OK)
  5403. {
  5404. // Better be asking for html
  5405. Assert(ISFLAGSET(dwTxtType, TXT_HTML));
  5406. // Do the Conversion
  5407. CHECKHR(hr = MimeOleConvertEnrichedToHTMLEx(pBody, ietEncoding, ppStream));
  5408. }
  5409. // Otherwise, non-text enriched case
  5410. else
  5411. {
  5412. // Get Data
  5413. CHECKHR(hr = pBody->GetData(ietEncoding, ppStream));
  5414. }
  5415. }
  5416. // If we are in OE5 compat mode...
  5417. if (TRUE == ISFLAGSET(g_dwCompatMode, MIMEOLE_COMPAT_OE5))
  5418. {
  5419. // If there is no stream requested, then don't mark rendered
  5420. if (NULL == ppStream)
  5421. {
  5422. // Don't Mark Rendered
  5423. fMarkRendered = FALSE;
  5424. }
  5425. }
  5426. // Mark Rendered
  5427. if (fMarkRendered)
  5428. {
  5429. // Rendered
  5430. rVariant.vt = VT_UI4;
  5431. rVariant.ulVal = TRUE;
  5432. // Lets set the resourl flag
  5433. SideAssert(SUCCEEDED(pBody->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  5434. // Raid-45116: new text attachment contains message body on Communicator inline image message
  5435. if (FAILED(GetBody(IBL_PARENT, hBody, &hAlternativeParent)))
  5436. hAlternativeParent = NULL;
  5437. // Try to find an alternative parent...
  5438. while(hAlternativeParent)
  5439. {
  5440. // If multipart/alternative, were done
  5441. if (IsContentType(hAlternativeParent, STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_OK)
  5442. break;
  5443. // Get Next Parent
  5444. if (FAILED(GetBody(IBL_PARENT, hAlternativeParent, &hAlternativeParent)))
  5445. hAlternativeParent = NULL;
  5446. }
  5447. // Get Parent
  5448. if (hAlternativeParent)
  5449. {
  5450. // Resolve all first level children
  5451. hrFind = GetBody(IBL_FIRST, hAlternativeParent, &hChild);
  5452. while(SUCCEEDED(hrFind) && hChild)
  5453. {
  5454. // Set Resolve Property
  5455. SideAssert(SUCCEEDED(SetBodyProp(hChild, PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  5456. // Find Next
  5457. hrFind = GetBody(IBL_NEXT, hChild, &hChild);
  5458. }
  5459. }
  5460. }
  5461. // Return the hBody
  5462. if (phBody)
  5463. *phBody = hBody;
  5464. exit:
  5465. // Cleanup
  5466. SafeRelease(pBody);
  5467. SafeMemFree(pszStartCID);
  5468. MimeOleVariantFree(&rStart);
  5469. // Thread Safety
  5470. LeaveCriticalSection(&m_cs);
  5471. // Done
  5472. return hr;
  5473. }
  5474. // --------------------------------------------------------------------------------
  5475. // CMessageTree::SetTextBody
  5476. // --------------------------------------------------------------------------------
  5477. STDMETHODIMP CMessageTree::SetTextBody(DWORD dwTxtType, ENCODINGTYPE ietEncoding,
  5478. HBODY hAlternative, IStream *pStream, LPHBODY phBody)
  5479. {
  5480. // Locals
  5481. HRESULT hr=S_OK,
  5482. hrFind;
  5483. HBODY hRoot,
  5484. hBody,
  5485. hTextBody=NULL,
  5486. hSection,
  5487. hParent,
  5488. hPrevious,
  5489. hInsertAfter;
  5490. LPTEXTTYPEINFO pTextInfo=NULL;
  5491. BOOL fFound,
  5492. fFoundInsertLocation;
  5493. DWORD dwWeightBody;
  5494. ULONG i;
  5495. IMimeBody *pBody=NULL;
  5496. PROPVARIANT rVariant;
  5497. // Invalid Arg
  5498. if (NULL == pStream)
  5499. return TrapError(E_INVALIDARG);
  5500. // Thread Safety
  5501. EnterCriticalSection(&m_cs);
  5502. // Init
  5503. if (phBody)
  5504. *phBody = NULL;
  5505. // Debug Dump
  5506. // DebugDumpTree("SetTextBody", TRUE);
  5507. // Get the Text Info
  5508. CHECKHR(hr = _HrGetTextTypeInfo(dwTxtType, &pTextInfo));
  5509. // Raid-45369: message from Eudora Pro comes in .txt attachment which is lost when forwarded.
  5510. // If hAlternative is NULL, then this means that the client wants to replace all text bodies
  5511. // with this new text body. If hAlternative is not NULL, then the client has already inserted
  5512. // a text body and created a alternative section, no more deleting.
  5513. if (NULL == hAlternative)
  5514. {
  5515. // Loop through the text type
  5516. for (i=0; i<ARRAYSIZE(g_rgTextInfo); i++)
  5517. {
  5518. // Get the Current Text Body Associated with this type
  5519. if (SUCCEEDED(GetTextBody(g_rgTextInfo[i].dwTxtType, IET_BINARY, NULL, &hBody)))
  5520. {
  5521. // If the parent of hBody is an alternative section, delete the alternative
  5522. if (SUCCEEDED(GetBody(IBL_PARENT, hBody, &hParent)) && IsContentType(hParent, STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_OK)
  5523. {
  5524. // Delete multipart/alternative
  5525. hBody = hParent;
  5526. }
  5527. // Not if hBody is equal to hAlternative
  5528. if (hBody != hAlternative)
  5529. {
  5530. // Locals
  5531. HRESULT hrFind;
  5532. HBODY hFind;
  5533. // Raid-54277: Mail : Inline replying losses inline images sent from Nav4 using Plain text & HTML format
  5534. hrFind = GetBody(IBL_FIRST, hBody, &hFind);
  5535. while(SUCCEEDED(hrFind) && hFind)
  5536. {
  5537. // If not a multipart/related, delete it
  5538. if (S_FALSE == IsContentType(hFind, STR_CNT_MULTIPART, STR_SUB_RELATED))
  5539. {
  5540. // Delete this body
  5541. CHECKHR(hr = DeleteBody(hFind, 0));
  5542. // Use the hPrevious
  5543. hrFind = GetBody(IBL_FIRST, hBody, &hFind);
  5544. }
  5545. // Get Next
  5546. else
  5547. {
  5548. // Find Next
  5549. hrFind = GetBody(IBL_NEXT, hFind, &hFind);
  5550. }
  5551. }
  5552. // Delete the multipart/alternative section, promoting any multipart/related section
  5553. CHECKHR(hr = DeleteBody(hBody, DELETE_PROMOTE_CHILDREN));
  5554. // Done
  5555. break;
  5556. }
  5557. }
  5558. }
  5559. }
  5560. // Get Root
  5561. CHECKHR(hr = GetBody(IBL_ROOT, NULL, &hRoot));
  5562. // If only one body..
  5563. if (IsBodyType(hRoot, IBT_EMPTY) == S_OK)
  5564. {
  5565. // Just use the root
  5566. hTextBody = hRoot;
  5567. }
  5568. // Otherwise, if not inserting an alternative body, we must need a multipart/mixed or multipart/related section
  5569. else if (NULL == hAlternative)
  5570. {
  5571. // Better not be an alternative section
  5572. Assert(FAILED(MimeOleGetAlternativeSection(this, &hSection, NULL)));
  5573. // If there is a related section use it
  5574. if (FAILED(MimeOleGetRelatedSection(this, FALSE, &hSection, NULL)))
  5575. {
  5576. // Find or Create a multipart/mixed section
  5577. CHECKHR(hr = MimeOleGetMixedSection(this, TRUE, &hSection, NULL));
  5578. }
  5579. // Insert an element at the head of this section...
  5580. CHECKHR(hr = InsertBody(IBL_FIRST, hSection, &hTextBody));
  5581. }
  5582. // Otherwise, if inserting an alternative body
  5583. else if (hAlternative != NULL)
  5584. {
  5585. // Verify pBody is STR_CNT_TEXT
  5586. Assert(IsContentType(hAlternative, STR_CNT_TEXT, NULL) == S_OK);
  5587. // Get hAlternative's parent
  5588. if (FAILED(GetBody(IBL_PARENT, hAlternative, &hParent)))
  5589. hParent = NULL;
  5590. // If hAlternative is the root
  5591. if (hRoot == hAlternative || NULL == hParent || IsContentType(hParent, STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_FALSE)
  5592. {
  5593. // Convert this body to a multipart/alternative
  5594. CHECKHR(hr = ToMultipart(hAlternative, STR_SUB_ALTERNATIVE, &hSection));
  5595. }
  5596. // Otherwise, hSection is equal to hParent
  5597. else
  5598. hSection = hParent;
  5599. // We better have an alternative section now...
  5600. Assert(IsContentType(hSection, STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_OK);
  5601. // Init Search
  5602. hPrevious = NULL;
  5603. fFound = FALSE;
  5604. fFoundInsertLocation = FALSE;
  5605. dwWeightBody = 0;
  5606. hInsertAfter = NULL;
  5607. // Lets enum the children of rLayout.hAlternative and verify that hAlternative is still a child...and decide what alternative body to insert after
  5608. hrFind = GetBody(IBL_FIRST, hSection, &hBody);
  5609. while(SUCCEEDED(hrFind) && hBody)
  5610. {
  5611. // Default dwWeightBody
  5612. dwWeightBody = 0xffffffff;
  5613. // Get Weight of hBody
  5614. for (i=0; i<ARRAYSIZE(g_rgTextInfo); i++)
  5615. {
  5616. // Compare Content Type
  5617. if (IsContentType(hBody, STR_CNT_TEXT, g_rgTextInfo[i].pszSubType) == S_OK)
  5618. {
  5619. dwWeightBody = g_rgTextInfo[i].dwWeight;
  5620. break;
  5621. }
  5622. }
  5623. // Get Alternative Weight of the body we are inserting
  5624. if (pTextInfo->dwWeight <= dwWeightBody && FALSE == fFoundInsertLocation)
  5625. {
  5626. fFoundInsertLocation = TRUE;
  5627. hInsertAfter = hPrevious;
  5628. }
  5629. // Is this the alternative brother...
  5630. if (hAlternative == hBody)
  5631. fFound = TRUE;
  5632. // Set hPrev
  5633. hPrevious = hBody;
  5634. // Find Next
  5635. hrFind = GetBody(IBL_NEXT, hBody, &hBody);
  5636. }
  5637. // If we didn't find hAlternative, we're hosed
  5638. if (FALSE == fFound)
  5639. {
  5640. Assert(FALSE);
  5641. hr = TrapError(E_FAIL);
  5642. goto exit;
  5643. }
  5644. // If no after was found... insert first..
  5645. if (NULL == hInsertAfter)
  5646. {
  5647. // BODY_LAST_CHILD
  5648. if (pTextInfo->dwWeight > dwWeightBody)
  5649. {
  5650. // Insert a new body...
  5651. CHECKHR(hr = InsertBody(IBL_LAST, hSection, &hTextBody));
  5652. }
  5653. // BODY_FIRST_CHILD
  5654. else
  5655. {
  5656. // Insert a new body...
  5657. CHECKHR(hr = InsertBody(IBL_FIRST, hSection, &hTextBody));
  5658. }
  5659. }
  5660. // Otherwise insert after hInsertAfter
  5661. else
  5662. {
  5663. // Insert a new body...
  5664. CHECKHR(hr = InsertBody(IBL_NEXT, hInsertAfter, &hTextBody));
  5665. }
  5666. }
  5667. // Open the object
  5668. Assert(hTextBody);
  5669. CHECKHR(hr = BindToObject(hTextBody, IID_IMimeBody, (LPVOID *)&pBody));
  5670. // Set the root...
  5671. CHECKHR(hr = pBody->SetData(ietEncoding, STR_CNT_TEXT, pTextInfo->pszSubType, IID_IStream, (LPVOID)pStream));
  5672. // Release This
  5673. SafeRelease(pBody);
  5674. // Set multipart/related; type=...
  5675. if (SUCCEEDED(GetBody(IBL_PARENT, hTextBody, &hParent)))
  5676. {
  5677. // If parent is multipart/related, set type
  5678. if (IsContentType(hParent, STR_CNT_MULTIPART, STR_SUB_RELATED) == S_OK)
  5679. {
  5680. // Get Parent
  5681. CHECKHR(hr = BindToObject(hParent, IID_IMimeBody, (LPVOID *)&pBody));
  5682. // type = text/plain
  5683. if (ISFLAGSET(dwTxtType, TXT_PLAIN))
  5684. {
  5685. // Setup Variant
  5686. rVariant.vt = VT_LPSTR;
  5687. rVariant.pszVal = (LPSTR)STR_MIME_TEXT_PLAIN;
  5688. // Set the Properyt
  5689. CHECKHR(hr = pBody->SetProp(STR_PAR_TYPE, 0, &rVariant));
  5690. }
  5691. // type = text/plain
  5692. else if (ISFLAGSET(dwTxtType, TXT_HTML))
  5693. {
  5694. // Setup Variant
  5695. rVariant.vt = VT_LPSTR;
  5696. rVariant.pszVal = (LPSTR)STR_MIME_TEXT_HTML;
  5697. // Set the Properyt
  5698. CHECKHR(hr = pBody->SetProp(STR_PAR_TYPE, 0, &rVariant));
  5699. }
  5700. else
  5701. AssertSz(FALSE, "UnKnown dwTxtType passed to IMimeMessage::SetTextBody");
  5702. }
  5703. // Otherwise, if hParent is multipart/alternative
  5704. else if (IsContentType(hParent, STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE) == S_OK)
  5705. {
  5706. // Set multipart/related; type=...
  5707. if (SUCCEEDED(GetBody(IBL_PARENT, hParent, &hParent)))
  5708. {
  5709. // If parent is multipart/related, set type
  5710. if (IsContentType(hParent, STR_CNT_MULTIPART, STR_SUB_RELATED) == S_OK)
  5711. {
  5712. // Get Parent
  5713. CHECKHR(hr = BindToObject(hParent, IID_IMimeBody, (LPVOID *)&pBody));
  5714. // Setup Variant
  5715. rVariant.vt = VT_LPSTR;
  5716. rVariant.pszVal = (LPSTR)STR_MIME_MPART_ALT;
  5717. // Set the Properyt
  5718. CHECKHR(hr = pBody->SetProp(STR_PAR_TYPE, 0, &rVariant));
  5719. }
  5720. }
  5721. }
  5722. }
  5723. // Set body handle
  5724. if (phBody)
  5725. *phBody = hTextBody;
  5726. exit:
  5727. // Cleanup
  5728. SafeRelease(pBody);
  5729. // Thread Safety
  5730. LeaveCriticalSection(&m_cs);
  5731. // Done
  5732. return hr;
  5733. }
  5734. // --------------------------------------------------------------------------------
  5735. // CMessageTree::AttachObject
  5736. // --------------------------------------------------------------------------------
  5737. STDMETHODIMP CMessageTree::AttachObject(REFIID riid, void *pvObject, LPHBODY phBody)
  5738. {
  5739. // Locals
  5740. HRESULT hr=S_OK;
  5741. HBODY hBody,
  5742. hMixed;
  5743. IMimeBody *pBody=NULL;
  5744. PROPVARIANT rVariant;
  5745. // Invalid Arg
  5746. if (NULL == pvObject || FALSE == FBODYSETDATAIID(riid))
  5747. return TrapError(E_INVALIDARG);
  5748. // Thread Safety
  5749. EnterCriticalSection(&m_cs);
  5750. // Init
  5751. if (phBody)
  5752. *phBody = NULL;
  5753. // Get Mixed Section
  5754. CHECKHR(hr = MimeOleGetMixedSection(this, TRUE, &hMixed, NULL));
  5755. // Append a child to the mixed part...
  5756. CHECKHR(hr = InsertBody(IBL_LAST, hMixed, &hBody));
  5757. // Bind to the Body Object
  5758. CHECKHR(hr = BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody));
  5759. // Set Data Object
  5760. CHECKHR(hr = pBody->SetData(IET_INETCSET, NULL, NULL, riid, pvObject));
  5761. // Setup Variant
  5762. rVariant.vt = VT_LPSTR;
  5763. rVariant.pszVal = (LPSTR)STR_DIS_ATTACHMENT;
  5764. // Mark as Attachment
  5765. CHECKHR(hr = SetBodyProp(hBody, PIDTOSTR(PID_HDR_CNTDISP), 0, &rVariant));
  5766. // Return hBody
  5767. if (phBody)
  5768. *phBody = hBody;
  5769. exit:
  5770. // Cleanup
  5771. SafeRelease(pBody);
  5772. // Thread Safety
  5773. LeaveCriticalSection(&m_cs);
  5774. // Done
  5775. return hr;
  5776. }
  5777. // --------------------------------------------------------------------------------
  5778. // CMessageTree::AttachFile
  5779. // --------------------------------------------------------------------------------
  5780. STDMETHODIMP CMessageTree::AttachFile(LPCSTR pszFilePath, IStream *pstmFile, LPHBODY phBody)
  5781. {
  5782. LPWSTR pwszFilePath;
  5783. HRESULT hr = S_OK;
  5784. IF_NULLEXIT(pwszFilePath = PszToUnicode(CP_ACP, pszFilePath));
  5785. IF_FAILEXIT(hr = AttachFileW(pwszFilePath, pstmFile, phBody));
  5786. exit:
  5787. MemFree(pwszFilePath);
  5788. return hr;
  5789. }
  5790. STDMETHODIMP CMessageTree::AttachFileW(LPCWSTR pszFilePath, IStream *pstmFile, LPHBODY phBody)
  5791. {
  5792. // Locals
  5793. HRESULT hr=S_OK;
  5794. IStream *pstmTemp=NULL;
  5795. LPWSTR pszCntType=NULL,
  5796. pszSubType=NULL,
  5797. pszFName=NULL;
  5798. HBODY hBody;
  5799. PROPVARIANT rVariant;
  5800. // Invalid Arg
  5801. if (NULL == pszFilePath)
  5802. return TrapError(E_INVALIDARG);
  5803. // Thread Safety
  5804. EnterCriticalSection(&m_cs);
  5805. // Init
  5806. if (phBody)
  5807. *phBody = NULL;
  5808. // Did the user give me a file stream
  5809. if (NULL == pstmFile)
  5810. {
  5811. // Get File Stream
  5812. CHECKHR(hr = OpenFileStreamW((LPWSTR)pszFilePath, OPEN_EXISTING, GENERIC_READ, &pstmTemp));
  5813. // Set The File Stream
  5814. pstmFile = pstmTemp;
  5815. }
  5816. // Attach as object
  5817. CHECKHR(hr = AttachObject(IID_IStream, (LPVOID)pstmFile, &hBody));
  5818. // Get mime file info
  5819. hr = MimeOleGetFileInfoW((LPWSTR)pszFilePath, &pszCntType, &pszSubType, NULL, &pszFName, NULL);
  5820. // Failure
  5821. if (FAILED(hr) && NULL == pszFName)
  5822. {
  5823. Assert(FALSE);
  5824. hr = TrapError(hr);
  5825. goto exit;
  5826. }
  5827. // Success
  5828. hr = S_OK;
  5829. // Attachment FileName
  5830. if (pszFName)
  5831. {
  5832. rVariant.vt = VT_LPWSTR;
  5833. rVariant.pwszVal = pszFName;
  5834. CHECKHR(hr = SetBodyProp(hBody, PIDTOSTR(PID_ATT_FILENAME), 0, &rVariant));
  5835. }
  5836. // ContentType
  5837. if (pszCntType && pszSubType)
  5838. {
  5839. // PriType
  5840. rVariant.vt = VT_LPWSTR;
  5841. rVariant.pwszVal = pszCntType;
  5842. CHECKHR(hr = SetBodyProp(hBody, PIDTOSTR(PID_ATT_PRITYPE), 0, &rVariant));
  5843. // SubType
  5844. rVariant.vt = VT_LPWSTR;
  5845. rVariant.pwszVal = pszSubType;
  5846. CHECKHR(hr = SetBodyProp(hBody, PIDTOSTR(PID_ATT_SUBTYPE), 0, &rVariant));
  5847. }
  5848. // Otherwise, default content type
  5849. else
  5850. {
  5851. // Default to text/plain
  5852. rVariant.vt = VT_LPSTR;
  5853. rVariant.pszVal = (LPSTR)STR_MIME_TEXT_PLAIN;
  5854. CHECKHR(hr = SetBodyProp(hBody, PIDTOSTR(PID_HDR_CNTTYPE), 0, &rVariant));
  5855. }
  5856. // Return hBody
  5857. if (phBody)
  5858. *phBody = hBody;
  5859. exit:
  5860. // Cleanup
  5861. ReleaseObj(pstmTemp);
  5862. MemFree(pszCntType);
  5863. MemFree(pszSubType);
  5864. MemFree(pszFName);
  5865. // Thread Safety
  5866. LeaveCriticalSection(&m_cs);
  5867. // Done
  5868. return hr;
  5869. }
  5870. // --------------------------------------------------------------------------------
  5871. // CMessageTree::GetAttachments
  5872. // --------------------------------------------------------------------------------
  5873. STDMETHODIMP CMessageTree::GetAttachments(ULONG *pcAttach, LPHBODY *pprghAttach)
  5874. {
  5875. // Locals
  5876. HRESULT hr=S_OK;
  5877. LPHBODY prghBody=NULL;
  5878. ULONG cAlloc=0;
  5879. ULONG cCount=0;
  5880. ULONG i;
  5881. PROPVARIANT rVariant;
  5882. // Invalid Arg
  5883. if (NULL == pcAttach)
  5884. return TrapError(E_INVALIDARG);
  5885. // Thread Safety
  5886. EnterCriticalSection(&m_cs);
  5887. // Init
  5888. if (pprghAttach)
  5889. *pprghAttach = NULL;
  5890. *pcAttach = 0;
  5891. // Setup Variant
  5892. rVariant.vt = VT_UI4;
  5893. // Walk through the tree and look for unrendered bodies
  5894. for (i=0; i<m_rTree.cNodes; i++)
  5895. {
  5896. // Better have it
  5897. if (NULL == m_rTree.prgpNode[i])
  5898. continue;
  5899. // Not a multipart
  5900. if (_IsMultiPart(m_rTree.prgpNode[i]))
  5901. continue;
  5902. // Raid-44193: reply to multipart/digest message yields text attachment
  5903. if (m_rTree.prgpNode[i]->pBody->IsType(IBT_EMPTY) == S_OK)
  5904. continue;
  5905. // Raid-56665: We are showing tnef attachments again
  5906. if (TRUE == m_rOptions.fHideTnef && S_OK == m_rTree.prgpNode[i]->pBody->IsContentType(STR_CNT_APPLICATION, STR_SUB_MSTNEF))
  5907. continue;
  5908. // an attachment shows up in the attachment well if it has NOT been rendered yet, OR it has been renderd but was auto inlined
  5909. // eg: if (!r || a) (as a implies r)
  5910. if ( (!(m_rTree.prgpNode[i]->pContainer->GetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant) == S_OK && TRUE == rVariant.ulVal)) ||
  5911. (m_rTree.prgpNode[i]->pContainer->GetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)==S_OK && TRUE == rVariant.ulVal))
  5912. {
  5913. // Realloc
  5914. if (cCount + 1 > cAlloc)
  5915. {
  5916. // Realloc
  5917. CHECKHR(hr = HrRealloc((LPVOID *)&prghBody, sizeof(HBODY) * (cAlloc + 10)));
  5918. // Increment cAlloc
  5919. cAlloc += 10;
  5920. }
  5921. // Insert the hBody
  5922. prghBody[cCount] = m_rTree.prgpNode[i]->hBody;
  5923. // Increment Count
  5924. cCount++;
  5925. }
  5926. }
  5927. // Done
  5928. *pcAttach = cCount;
  5929. // Return hBody Array
  5930. if (pprghAttach)
  5931. {
  5932. *pprghAttach = prghBody;
  5933. prghBody = NULL;
  5934. }
  5935. exit:
  5936. // Cleanup
  5937. SafeMemFree(prghBody);
  5938. // Thread Safety
  5939. LeaveCriticalSection(&m_cs);
  5940. // Done
  5941. return hr;
  5942. }
  5943. #if 0
  5944. // --------------------------------------------------------------------------------
  5945. // CMessageTree::GetAttachments
  5946. // --------------------------------------------------------------------------------
  5947. STDMETHODIMP CMessageTree::GetAttachments(ULONG *pcAttach, LPHBODY *pprghAttach)
  5948. {
  5949. // Locals
  5950. HRESULT hr=S_OK;
  5951. ULONG cBodies;
  5952. LPHBODY prghBody=NULL;
  5953. HBODY hRoot;
  5954. // Invalid Arg
  5955. if (NULL == pcAttach)
  5956. return TrapError(E_INVALIDARG);
  5957. // Thread Safety
  5958. EnterCriticalSection(&m_cs);
  5959. // Init
  5960. if (pprghAttach)
  5961. *pprghAttach = NULL;
  5962. *pcAttach = 0;
  5963. // Count the number of items in the tree
  5964. CHECKHR(hr = CountBodies(NULL, TRUE, &cBodies));
  5965. // No Data
  5966. if (0 == cBodies)
  5967. {
  5968. hr = MIME_E_NO_DATA;
  5969. goto exit;
  5970. }
  5971. // Get the root body
  5972. CHECKHR(hr = GetBody(IBL_ROOT, NULL, &hRoot));
  5973. // Allocate an array that can old the handle to all text items
  5974. CHECKALLOC(prghBody = (LPHBODY)g_pMalloc->Alloc(sizeof(HBODY) * cBodies));
  5975. // Zero Init
  5976. ZeroMemory(prghBody, sizeof(HBODY) * cBodies);
  5977. // Get Content
  5978. CHECKHR(hr = _HrEnumeratAttachments(hRoot, pcAttach, prghBody));
  5979. // Return this array
  5980. if (pprghAttach && *pcAttach > 0)
  5981. {
  5982. *pprghAttach = prghBody;
  5983. prghBody = NULL;
  5984. }
  5985. exit:
  5986. // Cleanup
  5987. SafeMemFree(prghBody);
  5988. // Thread Safety
  5989. LeaveCriticalSection(&m_cs);
  5990. // Done
  5991. return hr;
  5992. }
  5993. // --------------------------------------------------------------------------------
  5994. // CMessageTree::_HrEnumeratAttachments
  5995. // --------------------------------------------------------------------------------
  5996. HRESULT CMessageTree::_HrEnumeratAttachments(HBODY hBody, ULONG *pcBodies, LPHBODY prghBody)
  5997. {
  5998. // Locals
  5999. HRESULT hr=S_OK,
  6000. hrFind;
  6001. HBODY hChild;
  6002. ULONG i;
  6003. // multipart/alternative
  6004. if (IsContentType(hBody, STR_CNT_MULTIPART, NULL) == S_OK)
  6005. {
  6006. // Is Alternative
  6007. if (IsContentType(hBody, NULL, STR_SUB_ALTERNATIVE) == S_OK)
  6008. {
  6009. // Get First Child
  6010. hrFind = GetBody(IBL_FIRST, hBody, &hChild);
  6011. while(SUCCEEDED(hrFind) && NULL != hChild)
  6012. {
  6013. // If this text type is support by the client, then
  6014. for (i=0; i<ARRAYSIZE(g_rgTextInfo); i++)
  6015. {
  6016. // text/XXXX
  6017. if (IsContentType(hChild, STR_CNT_TEXT, g_rgTextInfo[i].pszSubType) == S_OK)
  6018. goto exit;
  6019. }
  6020. // Next Child
  6021. hrFind = GetBody(IBL_NEXT, hChild, &hChild);
  6022. }
  6023. }
  6024. // Get First Child
  6025. hrFind = GetBody(IBL_FIRST, hBody, &hChild);
  6026. while(SUCCEEDED(hrFind) && hChild)
  6027. {
  6028. // Bind the body table for this dude
  6029. CHECKHR(hr = _HrEnumeratAttachments(hChild, pcBodies, prghBody));
  6030. // Next Child
  6031. hrFind = GetBody(IBL_NEXT, hChild, &hChild);
  6032. }
  6033. }
  6034. // Otherwise, is it an attachment
  6035. else if (IsBodyType(hBody, IBT_ATTACHMENT) == S_OK)
  6036. {
  6037. // Insert as an attachment
  6038. prghBody[(*pcBodies)] = hBody;
  6039. (*pcBodies)++;
  6040. }
  6041. exit:
  6042. // Done
  6043. return hr;
  6044. }
  6045. #endif
  6046. // --------------------------------------------------------------------------------
  6047. // CMessageTree::AttachURL
  6048. // --------------------------------------------------------------------------------
  6049. STDMETHODIMP CMessageTree::AttachURL(LPCSTR pszBase, LPCSTR pszURL, DWORD dwFlags,
  6050. IStream *pstmURL, LPSTR *ppszCIDURL, LPHBODY phBody)
  6051. {
  6052. // Locals
  6053. HRESULT hr=S_OK;
  6054. HBODY hRoot,
  6055. hBody=NULL,
  6056. hSection;
  6057. CHAR szCID[CCHMAX_CID];
  6058. LPSTR pszFree=NULL;
  6059. LPSTR pszBaseFree=NULL;
  6060. IMimeBody *pBody=NULL;
  6061. LPWSTR pwszUrl=NULL;
  6062. IMimeWebDocument *pWebDocument=NULL;
  6063. // Thread Safety
  6064. EnterCriticalSection(&m_cs);
  6065. // Get the Root Body
  6066. CHECKHR(hr = GetBody(IBL_ROOT, NULL, &hRoot));
  6067. // multipart/mixed
  6068. if (ISFLAGSET(dwFlags, URL_ATTACH_INTO_MIXED))
  6069. {
  6070. // Get Mixed Section
  6071. CHECKHR(hr = MimeOleGetMixedSection(this, TRUE, &hSection, NULL));
  6072. }
  6073. // multipart/related
  6074. else
  6075. {
  6076. // Get Mixed Section
  6077. CHECKHR(hr = MimeOleGetRelatedSection(this, TRUE, &hSection, NULL));
  6078. }
  6079. // Get Default Base
  6080. if (NULL == pszBase && SUCCEEDED(MimeOleComputeContentBase(this, hSection, &pszBaseFree, NULL)))
  6081. pszBase = pszBaseFree;
  6082. // Append a child to the mixed part...
  6083. CHECKHR(hr = InsertBody(IBL_LAST, hSection, &hBody));
  6084. // Bind to IMimeBody
  6085. CHECKHR(hr = BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody));
  6086. // If I have a stream
  6087. if (pstmURL)
  6088. {
  6089. // Set the data
  6090. CHECKHR(hr = pBody->SetData(IET_INETCSET, NULL, NULL, IID_IStream, (LPVOID)pstmURL));
  6091. }
  6092. // Otherwise, Set the content type
  6093. else
  6094. {
  6095. // Create a WebDocument
  6096. CHECKHR(hr = MimeOleCreateWebDocument(pszBase, pszURL, &pWebDocument));
  6097. // Set Web Document on the body object
  6098. CHECKHR(hr = pBody->SetData(IET_BINARY, NULL, NULL, IID_IMimeWebDocument, (LPVOID)pWebDocument));
  6099. }
  6100. // URL_ATTACH_SET_CNTTYPE
  6101. if (ISFLAGSET(dwFlags, URL_ATTACH_SET_CNTTYPE))
  6102. {
  6103. // Locals
  6104. LPSTR pszCntType=(LPSTR)STR_MIME_APPL_STREAM;
  6105. PROPVARIANT rVariant;
  6106. // Get the Content Type from the Url
  6107. if (SUCCEEDED(MimeOleContentTypeFromUrl(pszBase, pszURL, &pszFree)))
  6108. pszCntType = pszFree;
  6109. // Setup the Variant
  6110. rVariant.vt = VT_LPSTR;
  6111. rVariant.pszVal = pszCntType;
  6112. // Set the Content Type
  6113. CHECKHR(hr = pBody->SetProp(PIDTOSTR(PID_HDR_CNTTYPE), 0, &rVariant));
  6114. }
  6115. // Set Content-Base
  6116. if (pszBase && pszBase != pszBaseFree)
  6117. {
  6118. // Set Base
  6119. CHECKHR(hr = MimeOleSetPropA(pBody, PIDTOSTR(PID_HDR_CNTBASE), 0, pszBase));
  6120. }
  6121. // User Wants a CID: URL Back
  6122. if (ISFLAGSET(dwFlags, URL_ATTACH_GENERATE_CID))
  6123. {
  6124. // Generate CID
  6125. CHECKHR(hr = MimeOleGenerateCID(szCID, CCHMAX_CID, FALSE));
  6126. // Set the Body Property
  6127. CHECKHR(hr = MimeOleSetPropA(pBody, PIDTOSTR(PID_HDR_CNTID), 0, szCID));
  6128. // User Wants CID Back...
  6129. if (ppszCIDURL)
  6130. {
  6131. DWORD cchSize = (lstrlen(szCID) + 5);
  6132. CHECKALLOC(MemAlloc((LPVOID *)ppszCIDURL, cchSize));
  6133. StrCpyN(*ppszCIDURL, "cid:", cchSize);
  6134. StrCatBuff(*ppszCIDURL, szCID, cchSize);
  6135. }
  6136. }
  6137. else
  6138. {
  6139. if (pszURL)
  6140. // Setup Content-Location
  6141. CHECKHR(hr = MimeOleSetPropA(pBody, PIDTOSTR(PID_HDR_CNTLOC), 0, pszURL));
  6142. }
  6143. // Return the hBody
  6144. if (phBody)
  6145. *phBody = hBody;
  6146. exit:
  6147. // Cleanup
  6148. SafeMemFree(pszFree);
  6149. SafeMemFree(pszBaseFree);
  6150. SafeMemFree(pwszUrl);
  6151. SafeRelease(pBody);
  6152. // Thread Safety
  6153. LeaveCriticalSection(&m_cs);
  6154. // Done
  6155. return hr;
  6156. }
  6157. STDMETHODIMP CMessageTree::AttachURLW(LPCWSTR pwszBase, LPCWSTR pwszURL, DWORD dwFlags,
  6158. IStream *pstmURL, LPWSTR *ppwszCIDURL, LPHBODY phBody)
  6159. {
  6160. return TraceResult(E_NOTIMPL);
  6161. }
  6162. STDMETHODIMP CMessageTree::ResolveURLW(HBODY hRelated, LPCWSTR pwszBase, LPCWSTR pwszURL,
  6163. DWORD dwFlags, LPHBODY phBody)
  6164. {
  6165. return TraceResult(E_NOTIMPL);
  6166. }
  6167. // --------------------------------------------------------------------------------
  6168. // CMessageTree::SplitMessage
  6169. // --------------------------------------------------------------------------------
  6170. STDMETHODIMP CMessageTree::SplitMessage(ULONG cbMaxPart, IMimeMessageParts **ppParts)
  6171. {
  6172. EnterCriticalSection(&m_cs);
  6173. HRESULT hr = MimeOleSplitMessage(this, cbMaxPart, ppParts);
  6174. LeaveCriticalSection(&m_cs);
  6175. return hr;
  6176. }
  6177. // --------------------------------------------------------------------------------
  6178. // CMessageTree::EnumFormatEtc
  6179. // --------------------------------------------------------------------------------
  6180. STDMETHODIMP CMessageTree::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnum)
  6181. {
  6182. // Locals
  6183. HRESULT hr=S_OK;
  6184. ULONG cFormat=0;
  6185. DATAOBJINFO rgFormat[CFORMATS_IDATAOBJECT];
  6186. ULONG cBodies;
  6187. IEnumFORMATETC *pEnum=NULL;
  6188. DWORD dwFlags;
  6189. // Invalid Arg
  6190. if (NULL == ppEnum)
  6191. return TrapError(E_INVALIDARG);
  6192. if (DATADIR_SET == dwDirection)
  6193. return TrapError(E_NOTIMPL);
  6194. else if (DATADIR_GET != dwDirection)
  6195. return TrapError(E_INVALIDARG);
  6196. // Thread Safety
  6197. EnterCriticalSection(&m_cs);
  6198. // Init
  6199. *ppEnum = NULL;
  6200. // No Data...
  6201. CHECKHR(hr = CountBodies(NULL, TRUE, &cBodies));
  6202. // If there are bodies...
  6203. if (cBodies)
  6204. {
  6205. // I can always give CF_INETMSG now...
  6206. SETDefFormatEtc(rgFormat[cFormat].fe, CF_INETMSG, TYMED_ISTREAM | TYMED_HGLOBAL);
  6207. cFormat++;
  6208. // Get Some Flags
  6209. dwFlags = DwGetFlags();
  6210. // Get the HTML body stream
  6211. if (ISFLAGSET(dwFlags, IMF_HTML))
  6212. {
  6213. SETDefFormatEtc(rgFormat[cFormat].fe, CF_HTML, TYMED_ISTREAM | TYMED_HGLOBAL);
  6214. cFormat++;
  6215. }
  6216. // Get the TEXT body stream
  6217. if (ISFLAGSET(dwFlags, IMF_PLAIN))
  6218. {
  6219. // Unicode Text
  6220. SETDefFormatEtc(rgFormat[cFormat].fe, CF_UNICODETEXT, TYMED_ISTREAM | TYMED_HGLOBAL);
  6221. cFormat++;
  6222. // Plain Text
  6223. SETDefFormatEtc(rgFormat[cFormat].fe, CF_TEXT, TYMED_ISTREAM | TYMED_HGLOBAL);
  6224. cFormat++;
  6225. }
  6226. }
  6227. // Create the enumerator
  6228. CHECKHR(hr = CreateEnumFormatEtc(GetInner(), cFormat, rgFormat, NULL, &pEnum));
  6229. // Set Return
  6230. *ppEnum = pEnum;
  6231. (*ppEnum)->AddRef();
  6232. exit:
  6233. // Cleanup
  6234. SafeRelease(pEnum);
  6235. // Thread Safety
  6236. LeaveCriticalSection(&m_cs);
  6237. // Done
  6238. return hr;
  6239. }
  6240. // --------------------------------------------------------------------------------
  6241. // CMessageTree::GetCanonicalFormatEtc
  6242. // --------------------------------------------------------------------------------
  6243. STDMETHODIMP CMessageTree::GetCanonicalFormatEtc(FORMATETC *pFormatIn, FORMATETC *pFormatOut)
  6244. {
  6245. // E_INVALIDARG
  6246. if (NULL == pFormatOut)
  6247. return E_INVALIDARG;
  6248. // Target device independent
  6249. pFormatOut->ptd = NULL;
  6250. // Done
  6251. return DATA_S_SAMEFORMATETC;
  6252. }
  6253. // --------------------------------------------------------------------------------
  6254. // CMessageTree::GetData
  6255. // --------------------------------------------------------------------------------
  6256. STDMETHODIMP CMessageTree::GetData(FORMATETC *pFormat, STGMEDIUM *pMedium)
  6257. {
  6258. // Locals
  6259. HRESULT hr=S_OK;
  6260. LPSTREAM pstmData=NULL;
  6261. BOOL fFreeGlobal=FALSE;
  6262. // E_INVALIDARG
  6263. if (NULL == pFormat || NULL == pMedium)
  6264. return TrapError(E_INVALIDARG);
  6265. // Thread Safety
  6266. EnterCriticalSection(&m_cs);
  6267. // TYMED_ISTREAM
  6268. if (ISFLAGSET(pFormat->tymed, TYMED_ISTREAM))
  6269. {
  6270. // Use a fast IStream
  6271. if (FAILED(MimeOleCreateVirtualStream(&pstmData)))
  6272. {
  6273. hr = TrapError(STG_E_MEDIUMFULL);
  6274. goto exit;
  6275. }
  6276. // Get data object source
  6277. if (FAILED(hr = _HrDataObjectGetSource(pFormat->cfFormat, pstmData)))
  6278. goto exit;
  6279. // Set pmedium
  6280. pMedium->tymed = TYMED_ISTREAM;
  6281. pMedium->pstm = pstmData;
  6282. pstmData->AddRef();
  6283. }
  6284. // TYMED_HGLOBAL
  6285. else if (ISFLAGSET(pFormat->tymed, TYMED_HGLOBAL))
  6286. {
  6287. fFreeGlobal = TRUE;
  6288. // don't have the stream release the global
  6289. if (FAILED(CreateStreamOnHGlobal(NULL, FALSE, &pstmData)))
  6290. {
  6291. hr = TrapError(STG_E_MEDIUMFULL);
  6292. goto exit;
  6293. }
  6294. // Get data object source
  6295. if (FAILED(hr = _HrDataObjectGetSource(pFormat->cfFormat, pstmData)))
  6296. goto exit;
  6297. // Create HGLOBAL from stream
  6298. if (FAILED(GetHGlobalFromStream(pstmData, &pMedium->hGlobal)))
  6299. {
  6300. hr = TrapError(STG_E_MEDIUMFULL);
  6301. goto exit;
  6302. }
  6303. // Set pmedium type
  6304. pMedium->tymed = TYMED_HGLOBAL;
  6305. // Release the strema
  6306. pstmData->Release();
  6307. pstmData = NULL;
  6308. fFreeGlobal = FALSE;
  6309. }
  6310. // Bad Medium Type
  6311. else
  6312. {
  6313. hr = TrapError(DV_E_TYMED);
  6314. goto exit;
  6315. }
  6316. exit:
  6317. // Cleanup
  6318. if (pstmData)
  6319. {
  6320. if (fFreeGlobal)
  6321. {
  6322. // we may fail had have to free the hglobal
  6323. HGLOBAL hGlobal;
  6324. // Free the underlying HGLOBAL
  6325. if (SUCCEEDED(GetHGlobalFromStream(pstmData, &hGlobal)))
  6326. GlobalFree(hGlobal);
  6327. }
  6328. // Release the Stream
  6329. pstmData->Release();
  6330. }
  6331. // Thread Safety
  6332. LeaveCriticalSection(&m_cs);
  6333. // Done
  6334. return hr;
  6335. }
  6336. // --------------------------------------------------------------------------------
  6337. // CMessageTree::GetDataHere
  6338. // --------------------------------------------------------------------------------
  6339. STDMETHODIMP CMessageTree::GetDataHere(FORMATETC *pFormat, STGMEDIUM *pMedium)
  6340. {
  6341. // Locals
  6342. HRESULT hr=S_OK;
  6343. LPSTREAM pstmData=NULL;
  6344. ULONG cb;
  6345. LPVOID pv=NULL;
  6346. // E_INVALIDARG
  6347. if (NULL == pFormat || NULL == pMedium)
  6348. return TrapError(E_INVALIDARG);
  6349. // Thread Safety
  6350. EnterCriticalSection(&m_cs);
  6351. // TYMED_ISTREAM
  6352. if (ISFLAGSET(pFormat->tymed, TYMED_ISTREAM))
  6353. {
  6354. // No dest stream...
  6355. if (NULL == pMedium->pstm)
  6356. {
  6357. hr = TrapError(E_INVALIDARG);
  6358. goto exit;
  6359. }
  6360. // Set pmedium
  6361. pMedium->tymed = TYMED_ISTREAM;
  6362. // Get the data
  6363. CHECKHR(hr = _HrDataObjectGetSource(pFormat->cfFormat, pMedium->pstm));
  6364. }
  6365. // TYMED_HGLOBAL
  6366. else if (ISFLAGSET(pFormat->tymed, TYMED_HGLOBAL))
  6367. {
  6368. // No dest stream...
  6369. if (NULL == pMedium->hGlobal)
  6370. {
  6371. hr = TrapError(E_INVALIDARG);
  6372. goto exit;
  6373. }
  6374. // Set pmedium type
  6375. pMedium->tymed = TYMED_HGLOBAL;
  6376. // Create a place to store the data
  6377. if (FAILED(MimeOleCreateVirtualStream(&pstmData)))
  6378. {
  6379. hr = TrapError(STG_E_MEDIUMFULL);
  6380. goto exit;
  6381. }
  6382. // Get data object source
  6383. CHECKHR(hr = _HrDataObjectGetSource(pFormat->cfFormat, pstmData));
  6384. // Get Size
  6385. CHECKHR(hr = HrGetStreamSize(pstmData, &cb));
  6386. // Is it big enought ?
  6387. if (cb > GlobalSize(pMedium->hGlobal))
  6388. {
  6389. hr = TrapError(STG_E_MEDIUMFULL);
  6390. goto exit;
  6391. }
  6392. // Lock the hglobal
  6393. pv = GlobalLock(pMedium->hGlobal);
  6394. if (NULL == pv)
  6395. {
  6396. hr = TrapError(STG_E_MEDIUMFULL);
  6397. goto exit;
  6398. }
  6399. // Copy the Data
  6400. CHECKHR(hr = HrCopyStreamToByte(pstmData, (LPBYTE)pv, NULL));
  6401. // Unlock it
  6402. GlobalUnlock(pMedium->hGlobal);
  6403. }
  6404. // Bad Medium Type
  6405. else
  6406. {
  6407. hr = TrapError(DV_E_TYMED);
  6408. goto exit;
  6409. }
  6410. exit:
  6411. // Cleanup
  6412. SafeRelease(pstmData);
  6413. // Thread Safety
  6414. LeaveCriticalSection(&m_cs);
  6415. // Done
  6416. return hr;
  6417. }
  6418. // --------------------------------------------------------------------------------
  6419. // CMessageTree::_HrDataObjectWriteHeaderA
  6420. // --------------------------------------------------------------------------------
  6421. HRESULT CMessageTree::_HrDataObjectWriteHeaderA(LPSTREAM pStream, UINT idsHeader, LPSTR pszData)
  6422. {
  6423. // Locals
  6424. HRESULT hr=S_OK;
  6425. CHAR szRes[CCHMAX_STRINGRES];
  6426. // Invalid Arg
  6427. Assert(idsHeader && pStream && pszData);
  6428. // Load Localized Header Name
  6429. LoadString(g_hLocRes, idsHeader, szRes, ARRAYSIZE(szRes));
  6430. // Write Header Name
  6431. CHECKHR(hr = pStream->Write(szRes, lstrlen(szRes), NULL));
  6432. // Write space
  6433. CHECKHR(hr = pStream->Write(c_szColonSpace, lstrlen(c_szColonSpace), NULL));
  6434. // Write Data
  6435. CHECKHR(hr = pStream->Write(pszData, lstrlen(pszData), NULL));
  6436. // Final CRLF
  6437. CHECKHR(hr = pStream->Write(g_szCRLF, lstrlen(g_szCRLF), NULL));
  6438. exit:
  6439. // Done
  6440. return hr;
  6441. }
  6442. // --------------------------------------------------------------------------------
  6443. // CMessageTree::_HrDataObjectGetHeaderA
  6444. // --------------------------------------------------------------------------------
  6445. HRESULT CMessageTree::_HrDataObjectGetHeaderA(LPSTREAM pStream)
  6446. {
  6447. // Locals
  6448. HRESULT hr=S_OK;
  6449. PROPVARIANT rVariant;
  6450. // Init
  6451. MimeOleVariantInit(&rVariant);
  6452. // Init Variant
  6453. rVariant.vt = VT_LPSTR;
  6454. // Get address table from header...
  6455. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_FROM), 0, &rVariant)))
  6456. {
  6457. // Write it
  6458. CHECKHR(hr = _HrDataObjectWriteHeaderA(pStream, IDS_FROM, rVariant.pszVal));
  6459. // Free It
  6460. MimeOleVariantFree(&rVariant);
  6461. }
  6462. // Init Variant
  6463. rVariant.vt = VT_LPSTR;
  6464. // Get address table from header...
  6465. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_TO), 0, &rVariant)))
  6466. {
  6467. // Write it
  6468. CHECKHR(hr = _HrDataObjectWriteHeaderA(pStream, IDS_TO, rVariant.pszVal));
  6469. // Free It
  6470. MimeOleVariantFree(&rVariant);
  6471. }
  6472. // Init Variant
  6473. rVariant.vt = VT_LPSTR;
  6474. // Get address table from header...
  6475. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_CC), 0, &rVariant)))
  6476. {
  6477. // Write it
  6478. CHECKHR(hr = _HrDataObjectWriteHeaderA(pStream, IDS_CC, rVariant.pszVal));
  6479. // Free It
  6480. MimeOleVariantFree(&rVariant);
  6481. }
  6482. // Init Variant
  6483. rVariant.vt = VT_LPSTR;
  6484. // Get address table from header...
  6485. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), 0, &rVariant)))
  6486. {
  6487. // Write it
  6488. CHECKHR(hr = _HrDataObjectWriteHeaderA(pStream, IDS_SUBJECT, rVariant.pszVal));
  6489. // Free It
  6490. MimeOleVariantFree(&rVariant);
  6491. }
  6492. // Init Variant
  6493. rVariant.vt = VT_FILETIME;
  6494. // Get address table from header...
  6495. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_ATT_RECVTIME), 0, &rVariant)))
  6496. {
  6497. // Locals
  6498. CHAR szDate[CCHMAX_STRINGRES];
  6499. // Convert to user friendly date format
  6500. CchFileTimeToDateTimeSz(&rVariant.filetime, szDate, ARRAYSIZE(szDate), DTM_NOSECONDS | DTM_LONGDATE);
  6501. // Write it
  6502. CHECKHR(hr = _HrDataObjectWriteHeaderA(pStream, IDS_DATE, szDate));
  6503. }
  6504. // Final CRLF
  6505. CHECKHR(hr = pStream->Write(g_szCRLF, lstrlen(g_szCRLF), NULL));
  6506. exit:
  6507. // Cleanup
  6508. MimeOleVariantFree(&rVariant);
  6509. // Done
  6510. return hr;
  6511. }
  6512. // --------------------------------------------------------------------------------
  6513. // CMessageTree::_HrDataObjectWriteHeaderW
  6514. // --------------------------------------------------------------------------------
  6515. HRESULT CMessageTree::_HrDataObjectWriteHeaderW(LPSTREAM pStream, UINT idsHeader, LPWSTR pwszData)
  6516. {
  6517. // Locals
  6518. HRESULT hr=S_OK;
  6519. CHAR szRes[CCHMAX_STRINGRES];
  6520. LPWSTR pwszRes=NULL;
  6521. // Invalid Arg
  6522. Assert(idsHeader && pStream && pwszData);
  6523. // Load Localized Header Name
  6524. LoadString(g_hLocRes, idsHeader, szRes, ARRAYSIZE(szRes));
  6525. // Convert to Unicode
  6526. IF_NULLEXIT(pwszRes = PszToUnicode(CP_ACP, szRes));
  6527. // Write Header Name
  6528. CHECKHR(hr = pStream->Write(pwszRes, (lstrlenW(pwszRes) * sizeof(WCHAR)), NULL));
  6529. // Write space
  6530. CHECKHR(hr = pStream->Write(L": ", (lstrlenW(L": ") * sizeof(WCHAR)), NULL));
  6531. // Write Data
  6532. CHECKHR(hr = pStream->Write(pwszData, (lstrlenW(pwszData) * sizeof(WCHAR)), NULL));
  6533. // Final CRLF
  6534. CHECKHR(hr = pStream->Write(L"\r\n", (lstrlenW(L"\r\n") * sizeof(WCHAR)), NULL));
  6535. exit:
  6536. // Cleanup
  6537. SafeMemFree(pwszRes);
  6538. // Done
  6539. return hr;
  6540. }
  6541. // --------------------------------------------------------------------------------
  6542. // CMessageTree::_HrDataObjectGetHeaderW
  6543. // --------------------------------------------------------------------------------
  6544. HRESULT CMessageTree::_HrDataObjectGetHeaderW(LPSTREAM pStream)
  6545. {
  6546. // Locals
  6547. HRESULT hr=S_OK;
  6548. LPWSTR pwszDate=NULL;
  6549. PROPVARIANT rVariant;
  6550. // Init
  6551. MimeOleVariantInit(&rVariant);
  6552. // Init Variant
  6553. rVariant.vt = VT_LPWSTR;
  6554. // Get address table from header...
  6555. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_FROM), 0, &rVariant)))
  6556. {
  6557. // Write it
  6558. CHECKHR(hr = _HrDataObjectWriteHeaderW(pStream, IDS_FROM, rVariant.pwszVal));
  6559. // Free It
  6560. MimeOleVariantFree(&rVariant);
  6561. }
  6562. // Init Variant
  6563. rVariant.vt = VT_LPWSTR;
  6564. // Get address table from header...
  6565. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_TO), 0, &rVariant)))
  6566. {
  6567. // Write it
  6568. CHECKHR(hr = _HrDataObjectWriteHeaderW(pStream, IDS_TO, rVariant.pwszVal));
  6569. // Free It
  6570. MimeOleVariantFree(&rVariant);
  6571. }
  6572. // Init Variant
  6573. rVariant.vt = VT_LPWSTR;
  6574. // Get address table from header...
  6575. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_CC), 0, &rVariant)))
  6576. {
  6577. // Write it
  6578. CHECKHR(hr = _HrDataObjectWriteHeaderW(pStream, IDS_CC, rVariant.pwszVal));
  6579. // Free It
  6580. MimeOleVariantFree(&rVariant);
  6581. }
  6582. // Init Variant
  6583. rVariant.vt = VT_LPWSTR;
  6584. // Get address table from header...
  6585. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), 0, &rVariant)))
  6586. {
  6587. // Write it
  6588. CHECKHR(hr = _HrDataObjectWriteHeaderW(pStream, IDS_SUBJECT, rVariant.pwszVal));
  6589. // Free It
  6590. MimeOleVariantFree(&rVariant);
  6591. }
  6592. // Init Variant
  6593. rVariant.vt = VT_FILETIME;
  6594. // Get address table from header...
  6595. if (SUCCEEDED(GetBodyProp(HBODY_ROOT, PIDTOSTR(PID_ATT_RECVTIME), 0, &rVariant)))
  6596. {
  6597. // Locals
  6598. WCHAR wszDate[CCHMAX_STRINGRES];
  6599. // Convert to user friendly date format
  6600. AthFileTimeToDateTimeW(&rVariant.filetime, wszDate, ARRAYSIZE(wszDate), DTM_NOSECONDS | DTM_LONGDATE);
  6601. // Write it
  6602. CHECKHR(hr = _HrDataObjectWriteHeaderW(pStream, IDS_DATE, wszDate));
  6603. }
  6604. // Final CRLF
  6605. CHECKHR(hr = pStream->Write(L"\r\n", (lstrlenW(L"\r\n") * sizeof(WCHAR)), NULL));
  6606. exit:
  6607. // Cleanup
  6608. MimeOleVariantFree(&rVariant);
  6609. // Done
  6610. return hr;
  6611. }
  6612. // --------------------------------------------------------------------------------
  6613. // CMessageTree::_HrDataObjectGetSource
  6614. // --------------------------------------------------------------------------------
  6615. HRESULT CMessageTree::_HrDataObjectGetSource(CLIPFORMAT cfFormat, LPSTREAM pStream)
  6616. {
  6617. // Locals
  6618. HRESULT hr=S_OK;
  6619. LPSTREAM pstmSrc=NULL;
  6620. // Invalid Arg
  6621. Assert(pStream);
  6622. // text body
  6623. if (CF_TEXT == cfFormat || CF_UNICODETEXT == cfFormat)
  6624. {
  6625. // Get Plain Text Source
  6626. CHECKHR(hr = GetTextBody(TXT_PLAIN, (cfFormat == CF_UNICODETEXT) ? IET_UNICODE : IET_BINARY, &pstmSrc, NULL));
  6627. }
  6628. // HTML Body
  6629. else if (CF_HTML == cfFormat)
  6630. {
  6631. // Get HTML Text Source
  6632. CHECKHR(hr = GetTextBody(TXT_HTML, IET_INETCSET, &pstmSrc, NULL));
  6633. }
  6634. // Raw Message Stream
  6635. else if (CF_INETMSG == cfFormat)
  6636. {
  6637. // Get source
  6638. CHECKHR(hr = GetMessageSource(&pstmSrc, COMMIT_ONLYIFDIRTY));
  6639. }
  6640. // Format not handled
  6641. else
  6642. {
  6643. hr = DV_E_FORMATETC;
  6644. goto exit;
  6645. }
  6646. // No Data
  6647. if (NULL == pstmSrc)
  6648. {
  6649. hr = TrapError(E_FAIL);
  6650. goto exit;
  6651. }
  6652. // Rewind Source
  6653. CHECKHR(hr = HrRewindStream(pstmSrc));
  6654. // If TEXT, put in friendly header
  6655. if (CF_TEXT == cfFormat)
  6656. {
  6657. CHECKHR(hr = _HrDataObjectGetHeaderA(pStream));
  6658. }
  6659. // Otherwise, unicode
  6660. else if (CF_UNICODETEXT == cfFormat)
  6661. {
  6662. CHECKHR(hr = _HrDataObjectGetHeaderW(pStream));
  6663. }
  6664. // Copy Source to destination
  6665. CHECKHR(hr = HrCopyStream(pstmSrc, pStream, NULL));
  6666. // Write a NULL
  6667. if (CF_TEXT == cfFormat)
  6668. {
  6669. CHECKHR(hr = pStream->Write(c_szEmpty, 1, NULL));
  6670. }
  6671. // Otherwise, unicode
  6672. else if (CF_UNICODETEXT == cfFormat)
  6673. {
  6674. CHECKHR(hr = pStream->Write(L"", 2, NULL));
  6675. }
  6676. // Commit
  6677. CHECKHR(hr = pStream->Commit(STGC_DEFAULT));
  6678. // Rewind it
  6679. CHECKHR(hr = HrRewindStream(pStream));
  6680. exit:
  6681. // Cleanup
  6682. SafeRelease(pstmSrc);
  6683. // Done
  6684. return hr;
  6685. }
  6686. // --------------------------------------------------------------------------------
  6687. // CMessageTree::QueryGetData
  6688. // --------------------------------------------------------------------------------
  6689. STDMETHODIMP CMessageTree::QueryGetData(FORMATETC *pFormat)
  6690. {
  6691. // Invalid Arg
  6692. if (NULL == pFormat)
  6693. return TrapError(E_INVALIDARG);
  6694. // Bad Medium
  6695. if (!(TYMED_ISTREAM & pFormat->tymed) && !(TYMED_HGLOBAL & pFormat->tymed))
  6696. return DV_E_TYMED;
  6697. // Bad format
  6698. if (CF_TEXT != pFormat->cfFormat && CF_HTML != pFormat->cfFormat &&
  6699. CF_UNICODETEXT != pFormat->cfFormat && CF_INETMSG != pFormat->cfFormat)
  6700. return DV_E_FORMATETC;
  6701. // Success
  6702. return S_OK;
  6703. }
  6704. // --------------------------------------------------------------------------------
  6705. // CMessageTree::OnStartBinding
  6706. // --------------------------------------------------------------------------------
  6707. STDMETHODIMP CMessageTree::OnStartBinding(DWORD dwReserved, IBinding *pBinding)
  6708. {
  6709. // Locals
  6710. HBODY hBody;
  6711. // Thread Safety
  6712. EnterCriticalSection(&m_cs);
  6713. // I Should not have a current binding
  6714. Assert(NULL == m_pBinding);
  6715. // Remove Bind Finished Flag
  6716. FLAGCLEAR(m_dwState, TREESTATE_BINDDONE);
  6717. // Assume the Binding
  6718. if (pBinding)
  6719. {
  6720. // Assume It
  6721. m_pBinding = pBinding;
  6722. m_pBinding->AddRef();
  6723. }
  6724. // Get the Root Body
  6725. Assert(m_pRootNode);
  6726. // Current Bind Result
  6727. m_hrBind = S_OK;
  6728. // Bind to that object
  6729. m_pBindNode = m_pRootNode;
  6730. // Set Bound Start
  6731. m_pBindNode->boundary = BOUNDARY_ROOT;
  6732. // Set Node Bind State
  6733. m_pBindNode->bindstate = BINDSTATE_PARSING_HEADER;
  6734. // Thread Safety
  6735. LeaveCriticalSection(&m_cs);
  6736. // Done
  6737. return S_OK;
  6738. }
  6739. // --------------------------------------------------------------------------------
  6740. // CMessageTree::GetPriority
  6741. // --------------------------------------------------------------------------------
  6742. STDMETHODIMP CMessageTree::GetPriority(LONG *plPriority)
  6743. {
  6744. // Normal Priority
  6745. *plPriority = THREAD_PRIORITY_NORMAL;
  6746. // Done
  6747. return S_OK;
  6748. }
  6749. // --------------------------------------------------------------------------------
  6750. // CMessageTree::OnLowResource
  6751. // --------------------------------------------------------------------------------
  6752. STDMETHODIMP CMessageTree::OnLowResource(DWORD reserved)
  6753. {
  6754. // Thread Safety
  6755. EnterCriticalSection(&m_cs);
  6756. // If we have a binding operation, try to abort it
  6757. if (m_pBinding)
  6758. m_pBinding->Abort();
  6759. // Thread Safety
  6760. LeaveCriticalSection(&m_cs);
  6761. // Done
  6762. return S_OK;
  6763. }
  6764. // --------------------------------------------------------------------------------
  6765. // CMessageTree::OnProgress
  6766. // --------------------------------------------------------------------------------
  6767. STDMETHODIMP CMessageTree::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pszStatusText)
  6768. {
  6769. // Debuging
  6770. //DebugTrace("CMessageTree::OnProgress - %d of %d Bytes\n", ulProgress, ulProgressMax);
  6771. // Done
  6772. return S_OK;
  6773. }
  6774. // --------------------------------------------------------------------------------
  6775. // CMessageTree::OnStopBinding
  6776. // --------------------------------------------------------------------------------
  6777. STDMETHODIMP CMessageTree::OnStopBinding(HRESULT hrResult, LPCWSTR pszError)
  6778. {
  6779. // Thread Safety
  6780. EnterCriticalSection(&m_cs);
  6781. // Release the Binding Object
  6782. SafeRelease(m_pBinding);
  6783. // Bind Finished
  6784. FLAGSET(m_dwState, TREESTATE_BINDDONE);
  6785. // Nuke the no cache flag....
  6786. FLAGCLEAR(m_dwState, TREESTATE_RESYNCHRONIZE);
  6787. // No m_pInternet Object ?
  6788. if (NULL == m_pInternet)
  6789. {
  6790. m_hrBind = TrapError(E_FAIL);
  6791. goto exit;
  6792. }
  6793. // It must be fully available
  6794. m_pInternet->SetFullyAvailable(TRUE);
  6795. // Make sure we have read all the way to the end of the stream
  6796. m_pInternet->HrReadToEnd();
  6797. // Keep Saving Total
  6798. m_cbMessage = m_pInternet->DwGetOffset();
  6799. #ifdef DEBUG
  6800. STATSTG rStat;
  6801. SideAssert(SUCCEEDED(m_pStmLock->Stat(&rStat, STATFLAG_NONAME)));
  6802. if (rStat.cbSize.QuadPart != m_cbMessage)
  6803. DebugTrace("CMessageTree Size Difference m_pStmLock::Stat = %d, m_cbMessage = %d\n", rStat.cbSize.QuadPart, m_cbMessage);
  6804. #endif
  6805. // Terminate current parsing state
  6806. if (m_pBindNode)
  6807. {
  6808. // Set Error
  6809. if (SUCCEEDED(m_hrBind))
  6810. m_hrBind = TrapError(E_FAIL);
  6811. // Mark remaining bodies as incomplete
  6812. while(m_pBindNode)
  6813. {
  6814. // Must not be complete
  6815. FLAGSET(m_pBindNode->dwType, NODETYPE_INCOMPLETE);
  6816. // Must not have found the end
  6817. Assert(0 == m_pBindNode->cbBodyEnd);
  6818. // cbBodyEnd
  6819. m_pBindNode->cbBodyEnd = m_cbMessage;
  6820. // Pop the stack
  6821. m_pBindNode = m_pBindNode->pBindParent;
  6822. }
  6823. }
  6824. // Check hrResult
  6825. if (FAILED(hrResult) && SUCCEEDED(m_hrBind))
  6826. m_hrBind = hrResult;
  6827. // DispatchBindRequest
  6828. _HrProcessPendingUrlRequests();
  6829. // Tell the webpage that we are done
  6830. if (m_pWebPage)
  6831. {
  6832. m_pWebPage->OnBindComplete(this);
  6833. m_pWebPage->Release();
  6834. m_pWebPage = NULL;
  6835. }
  6836. // Bind Node Better be Null
  6837. m_pBindNode = NULL;
  6838. // Release the Internet Stream Object
  6839. SafeRelease(m_pInternet);
  6840. // If we have a bind stream...
  6841. if (m_pStmBind)
  6842. {
  6843. #ifdef DEBUG
  6844. // m_pStmBind->DebugDumpDestStream("d:\\binddst.txt");
  6845. #endif
  6846. // Get hands off source
  6847. m_pStmBind->HandsOffSource();
  6848. // Release, m_pStmLock should still have this object
  6849. SideAssert(m_pStmBind->Release() > 0);
  6850. // Don't release it again
  6851. m_pStmBind = NULL;
  6852. }
  6853. // HandleCanInlineTextOption
  6854. _HandleCanInlineTextOption();
  6855. exit:
  6856. if (m_pBC)
  6857. {
  6858. // we only regiser our own bscb is m_pbc is set
  6859. RevokeBindStatusCallback(m_pBC, (IBindStatusCallback *)this);
  6860. SafeRelease(m_pBC);
  6861. }
  6862. // Thread Safety
  6863. LeaveCriticalSection(&m_cs);
  6864. // Done
  6865. return m_hrBind;
  6866. }
  6867. // --------------------------------------------------------------------------------
  6868. // CMessageTree::GetBindInfo
  6869. // --------------------------------------------------------------------------------
  6870. STDMETHODIMP CMessageTree::GetBindInfo(DWORD *grfBINDF, BINDINFO *pBindInfo)
  6871. {
  6872. // Setup the BindInfo
  6873. *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
  6874. // No CACHE?
  6875. if (ISFLAGSET(m_dwState, TREESTATE_RESYNCHRONIZE))
  6876. {
  6877. // Don't load from cache
  6878. FLAGSET(*grfBINDF, BINDF_RESYNCHRONIZE);
  6879. }
  6880. // Done
  6881. return S_OK;
  6882. }
  6883. // --------------------------------------------------------------------------------
  6884. // CMessageTree::_HrInitializeStorage
  6885. // --------------------------------------------------------------------------------
  6886. HRESULT CMessageTree::_HrInitializeStorage(IStream *pStream)
  6887. {
  6888. // Locals
  6889. HRESULT hr=S_OK;
  6890. DWORD dwOffset;
  6891. // Invalid Arg
  6892. Assert(pStream && NULL == m_pInternet && NULL == m_pStmLock && NULL == m_pStmBind);
  6893. // TREESTATE_BINDUSEFILE
  6894. if (ISFLAGSET(m_dwState, TREESTATE_BINDUSEFILE))
  6895. {
  6896. // Create a Binding Stream
  6897. CHECKALLOC(m_pStmBind = new CBindStream(pStream));
  6898. // Set pStmSource
  6899. pStream = (IStream *)m_pStmBind;
  6900. }
  6901. // $$BUGBUG$$ Urlmon fails on getting the current position of a stream
  6902. if (FAILED(HrGetStreamPos(pStream, &dwOffset)))
  6903. dwOffset = 0;
  6904. // Create a ILockBytes
  6905. CHECKALLOC(m_pStmLock = new CStreamLockBytes(pStream));
  6906. // Create a Text Stream
  6907. CHECKALLOC(m_pInternet = new CInternetStream);
  6908. // Initialize the TextStream
  6909. m_pInternet->InitNew(dwOffset, m_pStmLock);
  6910. exit:
  6911. // Failure
  6912. if (FAILED(hr))
  6913. {
  6914. SafeRelease(m_pStmLock);
  6915. SafeRelease(m_pInternet);
  6916. }
  6917. // Done
  6918. return hr;
  6919. }
  6920. // --------------------------------------------------------------------------------
  6921. // CMessageTree::OnDataAvailable
  6922. // --------------------------------------------------------------------------------
  6923. STDMETHODIMP CMessageTree::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFormat, STGMEDIUM *pMedium)
  6924. {
  6925. // Locals
  6926. HRESULT hr=S_OK;
  6927. // No Storage Medium
  6928. if (NULL == pMedium || TYMED_ISTREAM != pMedium->tymed || NULL == pMedium->pstm)
  6929. return TrapError(E_INVALIDARG);
  6930. // Thread Safety
  6931. EnterCriticalSection(&m_cs);
  6932. // Trace
  6933. // DebugTrace("CMessageTree::OnDataAvailable - Nodes=%d, m_pBindNode=%0x, dwSize = %d\n", m_rTree.cNodes, m_pBindNode, dwSize);
  6934. // Do I have an internal lock bytes yet ?
  6935. if (NULL == m_pStmLock)
  6936. {
  6937. // InitializeStorage
  6938. CHECKHR(hr = _HrInitializeStorage(pMedium->pstm));
  6939. // Assume not fully available
  6940. if (BINDSTATUS_ENDDOWNLOADDATA == grfBSCF)
  6941. m_pInternet->SetFullyAvailable(TRUE);
  6942. else
  6943. m_pInternet->SetFullyAvailable(FALSE);
  6944. }
  6945. // Done downloading the data
  6946. else if (BINDSTATUS_ENDDOWNLOADDATA == grfBSCF)
  6947. m_pInternet->SetFullyAvailable(TRUE);
  6948. // If we are in a failed read state
  6949. if (SUCCEEDED(m_hrBind))
  6950. {
  6951. // State Pumper
  6952. while(m_pBindNode)
  6953. {
  6954. // Execute current - could return E_PENDING
  6955. hr = ((this->*m_rgBindStates[m_pBindNode->bindstate])());
  6956. // Failure
  6957. if (FAILED(hr))
  6958. {
  6959. // E_PENDING
  6960. if (E_PENDING == hr)
  6961. goto exit;
  6962. // Otherwise, set m_hrBind
  6963. m_hrBind = hr;
  6964. // Done
  6965. break;
  6966. }
  6967. }
  6968. }
  6969. // If m_hrBind has failed, read until endof stream
  6970. if (FAILED(m_hrBind))
  6971. {
  6972. // Read to the end of the internet stream
  6973. CHECKHR(hr = m_pInternet->HrReadToEnd());
  6974. }
  6975. exit:
  6976. // Thread Safety
  6977. LeaveCriticalSection(&m_cs);
  6978. // Done
  6979. return hr;
  6980. }
  6981. // --------------------------------------------------------------------------------
  6982. // CMessageTree::_HrBindParsingHeader
  6983. // --------------------------------------------------------------------------------
  6984. HRESULT CMessageTree::_HrBindParsingHeader(void)
  6985. {
  6986. // Locals
  6987. HRESULT hr=S_OK;
  6988. MIMEVARIANT rVariant;
  6989. // Invalid Arg
  6990. BINDASSERTARGS(BINDSTATE_PARSING_HEADER, FALSE);
  6991. // Load the Current Body with the header
  6992. CHECKHR(hr = m_pBindNode->pContainer->Load(m_pInternet));
  6993. // End of the Header
  6994. m_pBindNode->cbBodyStart = m_pInternet->DwGetOffset();
  6995. // Multipart ?
  6996. if (_IsMultiPart(m_pBindNode))
  6997. {
  6998. // Setup the variant
  6999. rVariant.type = MVT_STRINGA;
  7000. // Get the boundary String
  7001. hr = m_pBindNode->pContainer->GetProp(SYM_PAR_BOUNDARY, 0, &rVariant);
  7002. // Raid-63150: Athena version 1 MSN issue: unable to download messages from SCSPromo
  7003. if (FAILED(hr))
  7004. {
  7005. // Incomplete Body
  7006. FLAGSET(m_pBindNode->dwType, NODETYPE_INCOMPLETE);
  7007. // Convert to a text part only if we read more than two bytes from body start
  7008. m_pBindNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN);
  7009. // Boundary Mismatch
  7010. hr = TrapError(MIME_E_BOUNDARY_MISMATCH);
  7011. // Done
  7012. goto exit;
  7013. }
  7014. // Set PropStringA
  7015. m_pBindNode->rBoundary.pszVal = rVariant.rStringA.pszVal;
  7016. m_pBindNode->rBoundary.cchVal = rVariant.rStringA.cchVal;
  7017. // Free this boundary later
  7018. FLAGCLEAR(m_pBindNode->dwState, NODESTATE_BOUNDNOFREE);
  7019. // Modify Bind Parser State
  7020. m_pBindNode->bindstate = BINDSTATE_FINDING_MIMEFIRST;
  7021. }
  7022. // Otherwise
  7023. else
  7024. {
  7025. // Message In a Message
  7026. if (m_pBindNode->pContainer->IsContentType(STR_CNT_MESSAGE, NULL) == S_OK)
  7027. {
  7028. // We are parsing a message attachment
  7029. FLAGSET(m_pBindNode->dwState, NODESTATE_MESSAGE);
  7030. }
  7031. // Otherwise, if parent and parent is a multipart/digest
  7032. else if (m_pBindNode->pParent && m_pBindNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_DIGEST) == S_OK &&
  7033. m_pBindNode->pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTTYPE)) == S_FALSE)
  7034. {
  7035. // Change the Content Type
  7036. m_pBindNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MSG_RFC822);
  7037. // This is a message
  7038. FLAGSET(m_pBindNode->dwState, NODESTATE_MESSAGE);
  7039. }
  7040. // If parsing a body inside of a parent multipart section
  7041. if (m_pBindNode->pParent && !ISFLAGSET(m_pBindNode->pParent->dwType, NODETYPE_FAKEMULTIPART))
  7042. {
  7043. // Find Next Mime Part
  7044. m_pBindNode->bindstate = BINDSTATE_FINDING_MIMENEXT;
  7045. }
  7046. // Otherwise, Reading Body and Looking for a uuencode begin boundary
  7047. else
  7048. {
  7049. // Parse the RFC1154 header
  7050. _DecodeRfc1154();
  7051. if (m_pBT1154)
  7052. {
  7053. HBODY hBody;
  7054. // This is an RFC1154 message. We convert the root node
  7055. // to a multi-part, and create a new node for the first
  7056. // of the body parts.
  7057. Assert(m_pBindNode == m_pRootNode);
  7058. m_pBindNode->bindstate = BINDSTATE_PARSING_RFC1154;
  7059. m_pBindNode->cbBodyEnd = m_pBindNode->cbBodyStart;
  7060. FLAGSET(m_pBindNode->dwType, NODETYPE_FAKEMULTIPART);
  7061. FLAGSET(m_pBindNode->dwType, NODETYPE_RFC1154_ROOT);
  7062. CHECKHR(hr = m_pBindNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MPART_MIXED));
  7063. CHECKHR(hr = InsertBody(IBL_LAST,m_pBindNode->hBody,&hBody));
  7064. m_pBindNode = _PNodeFromHBody(hBody);
  7065. m_pBindNode->bindstate = BINDSTATE_PARSING_RFC1154;
  7066. }
  7067. else
  7068. {
  7069. // Search for nested uuencoded block of data
  7070. m_pBindNode->bindstate = BINDSTATE_FINDING_UUBEGIN;
  7071. }
  7072. }
  7073. }
  7074. exit:
  7075. // Done
  7076. return hr;
  7077. }
  7078. // --------------------------------------------------------------------------------
  7079. // CMessageTree::_HrOnFoundNodeEnd
  7080. // --------------------------------------------------------------------------------
  7081. HRESULT CMessageTree::_HrOnFoundNodeEnd(DWORD cbBoundaryStart, HRESULT hrBind /* =S_OK */)
  7082. {
  7083. // Locals
  7084. HRESULT hr =S_OK;
  7085. // Compute the real body end
  7086. if (cbBoundaryStart < 2 || cbBoundaryStart == m_pBindNode->cbBodyStart)
  7087. m_pBindNode->cbBodyEnd = m_pBindNode->cbBodyStart;
  7088. else
  7089. m_pBindNode->cbBodyEnd = cbBoundaryStart - 2;
  7090. // This node is finished binding
  7091. CHECKHR(hr = _HrBindNodeComplete(m_pBindNode, hrBind));
  7092. // POP the stack
  7093. m_pBindNode = m_pBindNode->pBindParent;
  7094. exit:
  7095. // Done
  7096. return hr;
  7097. }
  7098. // --------------------------------------------------------------------------------
  7099. // CMessageTree::_HrOnFoundMultipartEnd
  7100. // --------------------------------------------------------------------------------
  7101. HRESULT CMessageTree::_HrOnFoundMultipartEnd(void)
  7102. {
  7103. // Locals
  7104. HRESULT hr=S_OK;
  7105. // Set m_pBindNode which is a multipart, end
  7106. m_pBindNode->cbBodyEnd = m_pInternet->DwGetOffset();
  7107. // This node is finished binding
  7108. CHECKHR(hr = _HrBindNodeComplete(m_pBindNode, S_OK));
  7109. // Finished with the multipart, pop it off the stack
  7110. m_pBindNode = m_pBindNode->pBindParent;
  7111. // If I still have a bind node, it should now be looking for a mime first boundary
  7112. if (m_pBindNode)
  7113. {
  7114. // New Bind State
  7115. m_pBindNode->bindstate = BINDSTATE_FINDING_MIMEFIRST;
  7116. }
  7117. exit:
  7118. // Done
  7119. return hr;
  7120. }
  7121. // --------------------------------------------------------------------------------
  7122. // CMessageTree::_HrBindFindingMimeFirst
  7123. // --------------------------------------------------------------------------------
  7124. HRESULT CMessageTree::_HrBindFindingMimeFirst(void)
  7125. {
  7126. // Locals
  7127. HRESULT hr=S_OK;
  7128. DWORD cbBoundaryStart;
  7129. PROPSTRINGA rLine;
  7130. BOUNDARYTYPE boundary=BOUNDARY_NONE;
  7131. // Invalid Arg
  7132. BINDASSERTARGS(BINDSTATE_FINDING_MIMEFIRST, TRUE);
  7133. // Sit and Spin
  7134. while(BOUNDARY_NONE == boundary)
  7135. {
  7136. // Mark Boundary Start
  7137. cbBoundaryStart = m_pInternet->DwGetOffset();
  7138. // Read a line
  7139. CHECKHR(hr = m_pInternet->HrReadLine(&rLine));
  7140. // Zero bytes read, were done, but this should not happen, we should find a boundary first
  7141. if (0 == rLine.cchVal)
  7142. break;
  7143. // Is MimeBoundary
  7144. boundary = _GetMimeBoundaryType(&rLine, &m_pBindNode->rBoundary);
  7145. }
  7146. // BOUNDARY_MIMENEXT
  7147. if (BOUNDARY_MIMENEXT == boundary)
  7148. {
  7149. // MultipartMimeNext
  7150. CHECKHR(hr = _HrMultipartMimeNext(cbBoundaryStart));
  7151. }
  7152. // RAID-38241: Mail: some attached files not getting parsed from Communicator to OE
  7153. // RAID-31255: multipart/mixed with single child which is multipart/alternative
  7154. else if (BOUNDARY_MIMEEND == boundary)
  7155. {
  7156. // Finished with a multipart
  7157. if (_IsMultiPart(m_pBindNode))
  7158. {
  7159. // Done
  7160. CHECKHR(hr = _HrOnFoundMultipartEnd());
  7161. }
  7162. // Found end of current node
  7163. else
  7164. {
  7165. // Done
  7166. CHECKHR(hr = _HrOnFoundNodeEnd(cbBoundaryStart));
  7167. }
  7168. }
  7169. else
  7170. {
  7171. // Incomplete Body
  7172. FLAGSET(m_pBindNode->dwType, NODETYPE_INCOMPLETE);
  7173. // Get Offset
  7174. DWORD dwOffset = m_pInternet->DwGetOffset();
  7175. // Convert to a text part only if we read more than two bytes from body start
  7176. if (dwOffset > m_pBindNode->cbBodyStart && dwOffset - m_pBindNode->cbBodyStart > 2)
  7177. m_pBindNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN);
  7178. // Boundary Mismatch
  7179. hr = TrapError(MIME_E_BOUNDARY_MISMATCH);
  7180. // This node is finished binding
  7181. _HrOnFoundNodeEnd(dwOffset, hr);
  7182. // Done
  7183. goto exit;
  7184. }
  7185. exit:
  7186. // Done
  7187. return hr;
  7188. }
  7189. // --------------------------------------------------------------------------------
  7190. // CMessageTree::_HrMultipartMimeNext
  7191. // --------------------------------------------------------------------------------
  7192. HRESULT CMessageTree::_HrMultipartMimeNext(DWORD cbBoundaryStart)
  7193. {
  7194. // Locals
  7195. HRESULT hr=S_OK;
  7196. HBODY hBody;
  7197. LPTREENODEINFO pChild;
  7198. // Get the Root Body
  7199. CHECKHR(hr = InsertBody(IBL_LAST, m_pBindNode->hBody, &hBody));
  7200. // Bind to that object
  7201. pChild = _PNodeFromHBody(hBody);
  7202. // Align the stack correctly
  7203. pChild->pBindParent = m_pBindNode;
  7204. // Setup Offset Information
  7205. pChild->boundary = BOUNDARY_MIMENEXT;
  7206. pChild->cbBoundaryStart = cbBoundaryStart;
  7207. pChild->cbHeaderStart = m_pInternet->DwGetOffset();
  7208. // Assume the Boundary
  7209. pChild->rBoundary.pszVal = m_pBindNode->rBoundary.pszVal;
  7210. pChild->rBoundary.cchVal = m_pBindNode->rBoundary.cchVal;
  7211. // Don't Free this string...
  7212. FLAGSET(pChild->dwState, NODESTATE_BOUNDNOFREE);
  7213. // New State for parent
  7214. m_pBindNode->bindstate = BINDSTATE_FINDING_MIMENEXT;
  7215. // Set New Current Node
  7216. m_pBindNode = pChild;
  7217. // Change State
  7218. m_pBindNode->bindstate = BINDSTATE_PARSING_HEADER;
  7219. exit:
  7220. // Done
  7221. return hr;
  7222. }
  7223. // --------------------------------------------------------------------------------
  7224. // CMessageTree::_HrBindFindingMimeNext
  7225. // --------------------------------------------------------------------------------
  7226. HRESULT CMessageTree::_HrBindFindingMimeNext(void)
  7227. {
  7228. // Locals
  7229. HRESULT hr=S_OK;
  7230. DWORD cbBoundaryStart;
  7231. PROPSTRINGA rLine;
  7232. BOUNDARYTYPE boundary=BOUNDARY_NONE;
  7233. // Invalid Arg
  7234. BINDASSERTARGS(BINDSTATE_FINDING_MIMENEXT, TRUE);
  7235. // Sit and Spin
  7236. while(BOUNDARY_NONE == boundary)
  7237. {
  7238. // Mark Boundary Start
  7239. cbBoundaryStart = m_pInternet->DwGetOffset();
  7240. // Read a line
  7241. CHECKHR(hr = m_pInternet->HrReadLine(&rLine));
  7242. // Zero bytes read, were done, but this should not happen, we should find a boundary first
  7243. if (0 == rLine.cchVal)
  7244. break;
  7245. // Next or Ending Mime Boundary
  7246. boundary = _GetMimeBoundaryType(&rLine, &m_pBindNode->rBoundary);
  7247. }
  7248. // Not found
  7249. if (BOUNDARY_NONE == boundary)
  7250. {
  7251. // Incomplete Body
  7252. FLAGSET(m_pBindNode->dwType, NODETYPE_INCOMPLETE);
  7253. // Boundary Mismatch
  7254. hr = TrapError(MIME_E_BOUNDARY_MISMATCH);
  7255. // This node is finished binding
  7256. _HrOnFoundNodeEnd(m_pInternet->DwGetOffset(), hr);
  7257. // Done
  7258. goto exit;
  7259. }
  7260. // Compute Ending Offset
  7261. CHECKHR(hr = _HrOnFoundNodeEnd(cbBoundaryStart));
  7262. // If BOUNDARY_MIMEEND
  7263. if (BOUNDARY_MIMEEND == boundary)
  7264. {
  7265. // OnFoundMultipartEnd
  7266. CHECKHR(hr = _HrOnFoundMultipartEnd());
  7267. }
  7268. // BOUNDARY_MIMENEXT
  7269. else
  7270. {
  7271. // MultipartMimeNext
  7272. CHECKHR(hr = _HrMultipartMimeNext(cbBoundaryStart));
  7273. }
  7274. exit:
  7275. // Done
  7276. return hr;
  7277. }
  7278. // --------------------------------------------------------------------------------
  7279. // _FIsUuencodeEnd
  7280. // --------------------------------------------------------------------------------
  7281. BOOL _FIsUuencodeEnd(LPCSTR pszVal)
  7282. {
  7283. // UU Encode End
  7284. if (StrCmpN(pszVal, "end", 3) == 0)
  7285. {
  7286. // Skip the first three chars
  7287. pszVal += 3;
  7288. // Make sure there is only space after the word end
  7289. while (*pszVal)
  7290. {
  7291. // LWSP or CRLF
  7292. if (' ' != *pszVal && '\t' != *pszVal && chCR != *pszVal && chLF != *pszVal)
  7293. {
  7294. // Oops, this isn't the end
  7295. return (FALSE);
  7296. // Done
  7297. break;
  7298. }
  7299. // Next Char
  7300. pszVal++;
  7301. }
  7302. return (TRUE);
  7303. }
  7304. return (FALSE);
  7305. }
  7306. // --------------------------------------------------------------------------------
  7307. // CMessageTree::_HrBindRfc1154
  7308. // --------------------------------------------------------------------------------
  7309. HRESULT CMessageTree::_HrBindRfc1154(void)
  7310. {
  7311. static CHAR szBINHEXSTART[] = "(This file must be converted with BinHex";
  7312. HRESULT hr=S_OK;
  7313. ULONG cbThisLine;
  7314. PROPSTRINGA rLine;
  7315. BT1154BODY *pCurrBody;
  7316. ULONG cbLastLine=0;
  7317. BINDASSERTARGS(BINDSTATE_PARSING_RFC1154, FALSE);
  7318. Assert(m_pBT1154 != NULL);
  7319. Assert(m_pBT1154->cBodies > m_pBT1154->cCurrentBody);
  7320. pCurrBody = &m_pBT1154->aBody[m_pBT1154->cCurrentBody];
  7321. Assert((BT1154ENC_MINIMUM <= pCurrBody->encEncoding) &&
  7322. (BT1154ENC_MAXIMUM >= pCurrBody->encEncoding));
  7323. // Sit and Spin
  7324. while (1)
  7325. {
  7326. // Get the current offset, and read a line
  7327. cbThisLine = m_pInternet->DwGetOffset();
  7328. CHECKHR(hr = m_pInternet->HrReadLine(&rLine));
  7329. if (0 == m_pBT1154->cCurrentLine)
  7330. {
  7331. // This is the first line in the body.
  7332. m_pBindNode->cbBoundaryStart = cbThisLine;
  7333. m_pBindNode->cbHeaderStart = cbThisLine;
  7334. switch (pCurrBody->encEncoding)
  7335. {
  7336. case BT1154ENC_TEXT:
  7337. // For a TEXT body, the "body start" and the "boundary start"
  7338. // are the same thing.
  7339. m_pBindNode->cbBodyStart = cbThisLine;
  7340. m_pBindNode->boundary = BOUNDARY_NONE;
  7341. _HrComputeDefaultContent(m_pBindNode,NULL);
  7342. break;
  7343. case BT1154ENC_UUENCODE:
  7344. // This is UUENCODE - we won't know the "content type" until we
  7345. // see the filename.
  7346. m_pBindNode->boundary = BOUNDARY_UUBEGIN;
  7347. break;
  7348. case BT1154ENC_BINHEX:
  7349. // For a BINHEX body, we set the "body start" and "boundary start"
  7350. // to the same thing - the "body start" will be set forward later
  7351. // if we see the BINHEX start line. We set the "content disposition"
  7352. // to "attachment", the "content type" to be "application/mac-binhex40",
  7353. // and HrBindNodeComplete will end up setting the "content transfer
  7354. // encoding" to "mac-binhex40".
  7355. m_pBindNode->cbBodyStart = cbThisLine;
  7356. m_pBindNode->boundary = BOUNDARY_NONE;
  7357. FLAGSET(m_pBindNode->dwType,NODETYPE_RFC1154_BINHEX);
  7358. CHECKHR(hr = m_pBindNode->pContainer->SetProp(SYM_HDR_CNTDISP, STR_DIS_ATTACHMENT));
  7359. CHECKHR(hr = m_pBindNode->pContainer->SetProp(PIDTOSTR(PID_ATT_PRITYPE), STR_CNT_APPLICATION));
  7360. CHECKHR(hr = m_pBindNode->pContainer->SetProp(PIDTOSTR(PID_ATT_SUBTYPE), STR_SUB_BINHEX));
  7361. break;
  7362. default:
  7363. AssertSz(FALSE,"Unknown encoding type.");
  7364. break;
  7365. }
  7366. }
  7367. if (0 == rLine.cchVal)
  7368. {
  7369. // Zero bytes read, we're done
  7370. if ((pCurrBody->cLines != 0xffffffff) &&
  7371. (m_pBT1154->cCurrentLine+1 <= pCurrBody->cLines))
  7372. {
  7373. // We weren't in the special "read as many lines as
  7374. // we can" state, and we haven't consumed all of
  7375. // the lines yet for this body part. So, we need to
  7376. // go into the "There were parsing errors" state.
  7377. m_pBT1154->hrLoadResult = MIME_E_NO_DATA;
  7378. }
  7379. break;
  7380. }
  7381. if (m_pBT1154->cCurrentLine == pCurrBody->cLines)
  7382. {
  7383. // We have just read the line past the end
  7384. // of the body. Let's remember this spot...
  7385. cbLastLine = cbThisLine;
  7386. }
  7387. m_pBT1154->cCurrentLine++;
  7388. if (m_pBT1154->cCurrentLine > pCurrBody->cLines)
  7389. {
  7390. // We are reading lines past the end of a body part.
  7391. if ((rLine.cchVal != 2) || (rLine.pszVal[0] != '\r') || (rLine.pszVal[1] != '\n'))
  7392. {
  7393. // All of the lines past the end of a body part (i.e. lines that are
  7394. // either between body parts, or at the end of the message) should be
  7395. // blank - and this one isn't. Since it isn't, we go into the "There
  7396. // were parsing errors" state.
  7397. m_pBT1154->hrLoadResult = MIME_E_NO_MULTIPART_BOUNDARY;
  7398. }
  7399. if (m_pBT1154->cCurrentBody+1 < m_pBT1154->cBodies)
  7400. {
  7401. // We are *between* body parts, which means we just
  7402. // consumed the single (blank) line which is between
  7403. // them. So we break out so we can add this body part
  7404. // and move on to the next one.
  7405. break;
  7406. }
  7407. // If we get to this point, it means that we are consuming the
  7408. // (blank) lines which are beyond the end of the last body
  7409. // part. We continue consuming all of those lines until they
  7410. // are gone. If any of them were non-blank, then we will have
  7411. // set the m_pBT1154->hrLoadResult member to MIME_E_NO_MULTIPART_BOUNDARY
  7412. // (above).
  7413. Assert(m_pBT1154->cCurrentBody+1 == m_pBT1154->cBodies);
  7414. }
  7415. else if (BT1154ENC_UUENCODE == pCurrBody->encEncoding)
  7416. {
  7417. // This is an else-if clause because we never look for the UUENCODE
  7418. // begin and end keywords past the end of the body part.
  7419. LPSTR pszFileName = NULL;
  7420. // We are dealing with UUENCODE.
  7421. if ((0 == m_pBindNode->cbBodyStart) && _FIsUuencodeBegin(&rLine, &pszFileName))
  7422. {
  7423. // We are looking for the start of UUENCODE - and this is it! We set
  7424. // the boundary start to be at the begin marker, and the body start to be
  7425. // *after* the begin marker.
  7426. m_pBindNode->cbBoundaryStart = cbThisLine;
  7427. m_pBindNode->cbHeaderStart = cbThisLine;
  7428. m_pBindNode->cbBodyStart = m_pInternet->DwGetOffset();
  7429. _HrComputeDefaultContent(m_pBindNode, pszFileName);
  7430. SafeMemFree(pszFileName);
  7431. }
  7432. else if ((0 != m_pBindNode->cbBodyStart) &&
  7433. (0 == m_pBindNode->cbBodyEnd) &&
  7434. _FIsUuencodeEnd(rLine.pszVal))
  7435. {
  7436. // We are looking for the end of UUENCODE - and this is it! We set
  7437. // the body end to be *before* the end marker.
  7438. m_pBindNode->cbBodyEnd = cbThisLine;
  7439. // We *don't* break out - we keep reading until we've consumed all
  7440. // of the lines for this body.
  7441. }
  7442. }
  7443. else if (BT1154ENC_BINHEX == pCurrBody->encEncoding)
  7444. {
  7445. // This is an else-if clause because we never look for the BINHEX
  7446. // start line past the end of the body part.
  7447. if (m_pBindNode->cbBodyStart == m_pBindNode->cbBoundaryStart)
  7448. {
  7449. // We haven't found the BINHEX start line yet.
  7450. if (StrCmpNI(szBINHEXSTART,rLine.pszVal,sizeof(szBINHEXSTART)-1) == 0)
  7451. {
  7452. // And this is it! So set the body start to this line.
  7453. m_pBindNode->cbBodyStart = cbThisLine;
  7454. }
  7455. }
  7456. }
  7457. }
  7458. // We only get to this point when we are at the end of a body - either
  7459. // by having consumed the correct number of lines (plus the blank line
  7460. // between bodies), or by having run off the end of the message.
  7461. Assert((0 == rLine.cchVal) || (m_pBT1154->cCurrentLine == pCurrBody->cLines+1));
  7462. // The only way we should have set the body end is if we are UUENCODE.
  7463. Assert((BT1154ENC_UUENCODE == pCurrBody->encEncoding) || (0 == m_pBindNode->cbBodyEnd));
  7464. if (0 == m_pBindNode->cbBodyEnd)
  7465. {
  7466. // We are either a TEXT or BINHEX body, or we are a UUENCODE and we
  7467. // didn't find the end.
  7468. if (BT1154ENC_UUENCODE == pCurrBody->encEncoding)
  7469. {
  7470. // We are doing UUENCODE, and we haven't seen the end keyword (and
  7471. // maybe not even the begin keyword). So we go into the "There
  7472. // were parsing errors" state.
  7473. if (0 == m_pBindNode->cbBodyStart)
  7474. {
  7475. // We haven't seen the begin keyword - so set the
  7476. // body start to be the same as the boundary start.
  7477. m_pBindNode->cbBodyStart = m_pBindNode->cbBoundaryStart;
  7478. }
  7479. m_pBT1154->hrLoadResult = MIME_E_BOUNDARY_MISMATCH;
  7480. }
  7481. // We need to set the body end...
  7482. if (0 != cbLastLine)
  7483. {
  7484. // We found the "last line" above, so we set the
  7485. // body end to that line.
  7486. m_pBindNode->cbBodyEnd = cbLastLine;
  7487. }
  7488. else
  7489. {
  7490. // Since we didn't find the "last line" above, we set the
  7491. // body end to this line.
  7492. m_pBindNode->cbBodyEnd = cbThisLine;
  7493. }
  7494. }
  7495. // We're done with this body part, so bind it.
  7496. _HrBindNodeComplete(m_pBindNode, m_pBT1154->hrLoadResult);
  7497. if (0 == rLine.cchVal)
  7498. {
  7499. // We have consumed the entire message - so clean everything up.
  7500. // ****************************************************
  7501. // NOTE - We set hr to the return value for the binding
  7502. // operation. Don't change hr between this point and
  7503. // where we return.
  7504. // ****************************************************
  7505. m_pRootNode->cbBodyEnd = m_pInternet->DwGetOffset();
  7506. _HrBindNodeComplete(m_pRootNode,S_OK);
  7507. hr = m_pBT1154->hrLoadResult;
  7508. SafeMemFree(m_pBT1154);
  7509. m_pBindNode = NULL;
  7510. }
  7511. else
  7512. {
  7513. HBODY hBody;
  7514. // When we are processing the last body part, we consume all
  7515. // of the lines after the last body part. So, if we haven't
  7516. // consumed the entire message, that means that we must have
  7517. // some bodies left to process...
  7518. Assert(m_pBT1154->cBodies > m_pBT1154->cCurrentBody+1);
  7519. m_pBT1154->cCurrentBody++;
  7520. m_pBT1154->cCurrentLine = 0;
  7521. Assert(m_pBindNode != m_pRootNode);
  7522. m_pBindNode = NULL; // set this to NULL in case we get an error from InsertBody
  7523. CHECKHR(hr = InsertBody(IBL_LAST, m_pRootNode->hBody, &hBody));
  7524. m_pBindNode = _PNodeFromHBody(hBody);
  7525. m_pBindNode->bindstate = BINDSTATE_PARSING_RFC1154;
  7526. }
  7527. // *********************************************************
  7528. // NOTE - Don't change hr below this point. See NOTE above.
  7529. // *********************************************************
  7530. exit:
  7531. return hr;
  7532. }
  7533. // --------------------------------------------------------------------------------
  7534. // CMessageTree::_HrBindFindingUuencodeBegin
  7535. // --------------------------------------------------------------------------------
  7536. HRESULT CMessageTree::_HrBindFindingUuencodeBegin(void)
  7537. {
  7538. // Locals
  7539. HRESULT hr=S_OK;
  7540. ULONG cbBoundaryStart;
  7541. PROPSTRINGA rLine;
  7542. BOUNDARYTYPE boundary=BOUNDARY_NONE;
  7543. LPTREENODEINFO pChild;
  7544. LPSTR pszFileName=NULL;
  7545. HBODY hBody;
  7546. BOOL fAddTextBody=FALSE;
  7547. ULONG cbTextBodyStart=0;
  7548. // Invalid Arg
  7549. BINDASSERTARGS(BINDSTATE_FINDING_UUBEGIN, FALSE);
  7550. // Sit and Spin
  7551. while(1)
  7552. {
  7553. // Mark Boundary Start
  7554. cbBoundaryStart = m_pInternet->DwGetOffset();
  7555. // Read a line
  7556. CHECKHR(hr = m_pInternet->HrReadLine(&rLine));
  7557. // Zero bytes read, were done, but this should not happen, we should find a boundary first
  7558. if (0 == rLine.cchVal)
  7559. break;
  7560. // If not parsing a message
  7561. if (!ISFLAGSET(m_pBindNode->dwState, NODESTATE_MESSAGE))
  7562. {
  7563. // Is uuencode begine line
  7564. if (_FIsUuencodeBegin(&rLine, &pszFileName) == TRUE)
  7565. {
  7566. boundary = BOUNDARY_UUBEGIN;
  7567. break;
  7568. }
  7569. }
  7570. }
  7571. // No Boundary
  7572. if (BOUNDARY_NONE == boundary)
  7573. {
  7574. // Stuff after the last UUENCODED body must be appended as a text body
  7575. if (m_pBindNode->pChildTail)
  7576. {
  7577. // De-ref Last Child
  7578. pChild = m_pBindNode->pChildTail;
  7579. // Artificial text body start
  7580. cbTextBodyStart = pChild->cbBodyEnd;
  7581. // AddTextBody ? lstrlen(end\r\n) = 5
  7582. if (BOUNDARY_UUBEGIN == pChild->boundary && !ISFLAGSET(pChild->dwType, NODETYPE_INCOMPLETE))
  7583. cbTextBodyStart += 5;
  7584. // Space between last body end and boundary start is greater than sizeof(crlf)
  7585. if (cbBoundaryStart > cbTextBodyStart && cbBoundaryStart - cbTextBodyStart > 2)
  7586. {
  7587. // Create Root Body Node
  7588. CHECKHR(hr = InsertBody(IBL_LAST, m_pBindNode->hBody, &hBody));
  7589. // Bind to that object
  7590. pChild = _PNodeFromHBody(hBody);
  7591. // Fixup the STack
  7592. pChild->pBindParent = m_pBindNode;
  7593. // This body should assume the new text offsets
  7594. CHECKHR(hr = pChild->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN));
  7595. // Set Encoding
  7596. CHECKHR(hr = pChild->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_7BIT));
  7597. // Set Offsets
  7598. pChild->boundary = BOUNDARY_NONE;
  7599. pChild->cbBoundaryStart = cbTextBodyStart;
  7600. pChild->cbHeaderStart = cbTextBodyStart;
  7601. pChild->cbBodyStart = cbTextBodyStart;
  7602. pChild->cbBodyEnd = cbBoundaryStart;
  7603. // This node is finished binding
  7604. CHECKHR(hr = _HrBindNodeComplete(pChild, S_OK));
  7605. }
  7606. }
  7607. // Body Offset Information
  7608. m_pBindNode->cbBodyEnd = m_pInternet->DwGetOffset();
  7609. // This node is finished binding
  7610. CHECKHR(hr = _HrBindNodeComplete(m_pBindNode, S_OK));
  7611. // Pop the parsing Stack
  7612. m_pBindNode = m_pBindNode->pBindParent;
  7613. }
  7614. // Otherwise, if we hit a uuencode boundary
  7615. else
  7616. {
  7617. // If not a fake multipart yet...
  7618. if (!ISFLAGSET(m_pBindNode->dwType, NODETYPE_FAKEMULTIPART))
  7619. {
  7620. // Its a faked multipart
  7621. FLAGSET(m_pBindNode->dwType, NODETYPE_FAKEMULTIPART);
  7622. // Free current content type
  7623. CHECKHR(hr = m_pBindNode->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MPART_MIXED));
  7624. // Modify this dudes bound start
  7625. Assert(m_pBindNode->boundary == BOUNDARY_ROOT);
  7626. // Set the parse state
  7627. m_pBindNode->bindstate = BINDSTATE_FINDING_UUBEGIN;
  7628. }
  7629. // ------------------------------------------------------------------------------------
  7630. // \/ \/ \/ Raid 41599 - lost/munged attachments on forward/uuencode \/ \/ \/
  7631. // If root node and body size is greater than sizeof(crlf)
  7632. if (NULL == m_pBindNode->pChildTail && cbBoundaryStart - m_pBindNode->cbBodyStart > 2)
  7633. {
  7634. // Validate bind node
  7635. Assert(m_pRootNode == m_pBindNode && m_pBindNode->cChildren == 0);
  7636. // Set artificial text body start
  7637. cbTextBodyStart = m_pBindNode->cbBodyStart;
  7638. // Yes, add artificial text body
  7639. fAddTextBody = TRUE;
  7640. }
  7641. // Otherwise, if last child parsed had an ending boundary of UUEND, and body size is greater than sizeof(crlf)
  7642. else if (m_pBindNode->pChildTail)
  7643. {
  7644. // De-ref Last Child
  7645. pChild = m_pBindNode->pChildTail;
  7646. // Artificial text body start
  7647. cbTextBodyStart = pChild->cbBodyEnd;
  7648. // AddTextBody ? lstrlen(end\r\n) = 5
  7649. if (BOUNDARY_UUBEGIN == pChild->boundary && !ISFLAGSET(pChild->dwType, NODETYPE_INCOMPLETE))
  7650. cbTextBodyStart += 5;
  7651. // Otherwise, what was the ending boundary
  7652. else
  7653. AssertSz(FALSE, "I should have only seen and uuencoded ending boundary.");
  7654. // Space between last body end and boundary start is greater than sizeof(crlf)
  7655. if (cbBoundaryStart > cbTextBodyStart && cbBoundaryStart - cbTextBodyStart > 2)
  7656. fAddTextBody = TRUE;
  7657. }
  7658. // /\ /\ /\ Raid 41599 - lost/munged attachments on forward/uuencode /\ /\ /\
  7659. // ------------------------------------------------------------------------------------
  7660. // Create Root Body Node
  7661. CHECKHR(hr = InsertBody(IBL_LAST, m_pBindNode->hBody, &hBody));
  7662. // Bind to that object
  7663. pChild = _PNodeFromHBody(hBody);
  7664. // Fixup the STack
  7665. pChild->pBindParent = m_pBindNode;
  7666. // Enough text to create a text/plain body ?
  7667. if (fAddTextBody)
  7668. {
  7669. // This body should assume the new text offsets
  7670. CHECKHR(hr = pChild->pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN));
  7671. // Set Encoding
  7672. CHECKHR(hr = pChild->pContainer->SetProp(SYM_HDR_CNTXFER, STR_ENC_7BIT));
  7673. // Set Offsets
  7674. pChild->boundary = BOUNDARY_NONE;
  7675. pChild->cbBoundaryStart = cbTextBodyStart;
  7676. pChild->cbHeaderStart = cbTextBodyStart;
  7677. pChild->cbBodyStart = cbTextBodyStart;
  7678. pChild->cbBodyEnd = cbBoundaryStart;
  7679. // This node is finished binding
  7680. CHECKHR(hr = _HrBindNodeComplete(pChild, S_OK));
  7681. // Create Root Body Node
  7682. CHECKHR(hr = InsertBody(IBL_LAST, m_pBindNode->hBody, &hBody));
  7683. // Bind to that object
  7684. pChild = _PNodeFromHBody(hBody);
  7685. // Fixup the STack
  7686. pChild->pBindParent = m_pBindNode;
  7687. }
  7688. // Set Offsets
  7689. pChild->boundary = BOUNDARY_UUBEGIN;
  7690. pChild->cbBoundaryStart = cbBoundaryStart;
  7691. pChild->cbHeaderStart = cbBoundaryStart;
  7692. pChild->cbBodyStart = m_pInternet->DwGetOffset();
  7693. // Update m_pBindNode
  7694. Assert(m_pBindNode->bindstate == BINDSTATE_FINDING_UUBEGIN);
  7695. m_pBindNode = pChild;
  7696. // Default Node Content Type
  7697. _HrComputeDefaultContent(m_pBindNode, pszFileName);
  7698. // New Node BindState
  7699. m_pBindNode->bindstate = BINDSTATE_FINDING_UUEND;
  7700. }
  7701. exit:
  7702. // Cleanup
  7703. SafeMemFree(pszFileName);
  7704. // Done
  7705. return hr;
  7706. }
  7707. // --------------------------------------------------------------------------------
  7708. // CMessageTree::_HrBindFindingUuencodeEnd
  7709. // --------------------------------------------------------------------------------
  7710. HRESULT CMessageTree::_HrBindFindingUuencodeEnd(void)
  7711. {
  7712. // Locals
  7713. HRESULT hr=S_OK;
  7714. PROPSTRINGA rLine;
  7715. DWORD cbBoundaryStart;
  7716. BOUNDARYTYPE boundary=BOUNDARY_NONE;
  7717. // Invalid Arg
  7718. BINDASSERTARGS(BINDSTATE_FINDING_UUEND, FALSE);
  7719. // Sit and Spin
  7720. while(BOUNDARY_NONE == boundary)
  7721. {
  7722. // Mark Boundary Start
  7723. cbBoundaryStart = m_pInternet->DwGetOffset();
  7724. // Read a line
  7725. CHECKHR(hr = m_pInternet->HrReadLine(&rLine));
  7726. // Zero bytes read, were done, but this should not happen, we should find a boundary first
  7727. if (0 == rLine.cchVal)
  7728. break;
  7729. // UU Encode End
  7730. if (_FIsUuencodeEnd(rLine.pszVal))
  7731. {
  7732. boundary = BOUNDARY_UUEND;
  7733. }
  7734. }
  7735. // Incomplete
  7736. if (BOUNDARY_UUEND != boundary)
  7737. {
  7738. // Incomplete Body
  7739. FLAGSET(m_pBindNode->dwType, NODETYPE_INCOMPLETE);
  7740. // Adjust body start to boundary start
  7741. m_pBindNode->cbBodyStart = m_pBindNode->cbBoundaryStart;
  7742. // Body End
  7743. m_pBindNode->cbBodyEnd = m_pInternet->DwGetOffset();
  7744. // This node is finished binding
  7745. CHECKHR(hr = _HrBindNodeComplete(m_pBindNode, S_OK));
  7746. // Pop the tree
  7747. m_pBindNode = m_pBindNode->pBindParent;
  7748. // Done
  7749. goto exit;
  7750. }
  7751. // Get the offset
  7752. m_pBindNode->cbBodyEnd = cbBoundaryStart;
  7753. // POP the stack
  7754. m_pBindNode = m_pBindNode->pBindParent;
  7755. // Should now be looking for next uubegin
  7756. Assert(m_pBindNode ? m_pBindNode->bindstate == BINDSTATE_FINDING_UUBEGIN : TRUE);
  7757. exit:
  7758. // Done
  7759. return hr;
  7760. }
  7761. // --------------------------------------------------------------------------------
  7762. // CMessageTree::_HrBindNodeComplete
  7763. // --------------------------------------------------------------------------------
  7764. HRESULT CMessageTree::_HrBindNodeComplete(LPTREENODEINFO pNode, HRESULT hrResult)
  7765. {
  7766. // Locals
  7767. HRESULT hr=S_OK;
  7768. LPURLREQUEST pRequest;
  7769. LPURLREQUEST pNext;
  7770. // The bind for this node is complete
  7771. pNode->bindstate = BINDSTATE_COMPLETE;
  7772. // Save the bind result
  7773. pNode->hrBind = hrResult;
  7774. // If pNode has not been bound yet, lets do it
  7775. if (!ISFLAGSET(pNode->dwState, NODESTATE_BOUNDTOTREE))
  7776. {
  7777. // Bind it to the tree
  7778. hr = pNode->pBody->HrBindToTree(m_pStmLock, pNode);
  7779. // If HrBindToTree failed
  7780. if (SUCCEEDED(pNode->hrBind) && FAILED(hr))
  7781. pNode->hrBind = hr;
  7782. // Process the bind Request Table
  7783. CHECKHR(hr = _HrProcessPendingUrlRequests());
  7784. // If there is a WebPage being built, lets add this body
  7785. if (m_pWebPage)
  7786. {
  7787. // Add the Body
  7788. m_pWebPage->OnBodyBoundToTree(this, pNode);
  7789. }
  7790. }
  7791. // Init the Loop
  7792. pRequest = pNode->pResolved;
  7793. // Loop
  7794. while(pRequest)
  7795. {
  7796. // Set Next
  7797. pNext = pRequest->m_pNext;
  7798. // Unlink this pending request
  7799. _RelinkUrlRequest(pRequest, &pNode->pResolved, &m_pComplete);
  7800. // OnComplete
  7801. pRequest->OnBindingComplete(pNode->hrBind);
  7802. // Set pRequest
  7803. pRequest = pNext;
  7804. }
  7805. exit:
  7806. // Done
  7807. return hr;
  7808. }
  7809. // --------------------------------------------------------------------------------
  7810. // CMessageTree::HrRegisterRequest
  7811. // --------------------------------------------------------------------------------
  7812. HRESULT CMessageTree::HrActiveUrlRequest(LPURLREQUEST pRequest)
  7813. {
  7814. // Locals
  7815. HRESULT hr=S_OK;
  7816. // Invalid Arg
  7817. if (NULL == pRequest)
  7818. return TrapError(E_INVALIDARG);
  7819. // Thread Safety
  7820. EnterCriticalSection(&m_cs);
  7821. // Check State
  7822. Assert(m_rRootUrl.pszVal);
  7823. // AddRef the Request
  7824. pRequest->GetInner()->AddRef();
  7825. // Put the Request into the pending list
  7826. _LinkUrlRequest(pRequest, &m_pPending);
  7827. // Process Pending Url Requests
  7828. CHECKHR(hr = _HrProcessPendingUrlRequests());
  7829. exit:
  7830. // Thread Safety
  7831. LeaveCriticalSection(&m_cs);
  7832. // Done
  7833. return hr;
  7834. }
  7835. // --------------------------------------------------------------------------------
  7836. // CMessageTree::_HrProcessPendingUrlRequests
  7837. // --------------------------------------------------------------------------------
  7838. HRESULT CMessageTree::_HrProcessPendingUrlRequests(void)
  7839. {
  7840. // Locals
  7841. HRESULT hr=S_OK;
  7842. LPURLREQUEST pRequest=m_pPending;
  7843. LPURLREQUEST pNext;
  7844. HBODY hBody;
  7845. BOOL fResolved;
  7846. // Loop the request
  7847. while(pRequest)
  7848. {
  7849. // Set Next
  7850. pNext = pRequest->m_pNext;
  7851. // Try to resolve the request
  7852. CHECKHR(hr = _HrResolveUrlRequest(pRequest, &fResolved));
  7853. // Resolved
  7854. if (FALSE == fResolved && ISFLAGSET(m_dwState, TREESTATE_BINDDONE))
  7855. {
  7856. // Unlink this pending request
  7857. _RelinkUrlRequest(pRequest, &m_pPending, &m_pComplete);
  7858. // Not found, use default protocol
  7859. pRequest->OnBindingComplete(E_FAIL);
  7860. }
  7861. // Next
  7862. pRequest = pNext;
  7863. }
  7864. exit:
  7865. // Done
  7866. return hr;
  7867. }
  7868. // --------------------------------------------------------------------------------
  7869. // CMessageTree::_HrResolveUrlRequest
  7870. // --------------------------------------------------------------------------------
  7871. HRESULT CMessageTree::_HrResolveUrlRequest(LPURLREQUEST pRequest, BOOL *pfResolved)
  7872. {
  7873. // Locals
  7874. HRESULT hr=S_OK;
  7875. HBODY hBody=NULL;
  7876. LPTREENODEINFO pNode;
  7877. LPWSTR pwszCntType=NULL;
  7878. IStream *pStream=NULL;
  7879. // Invalid Arg
  7880. Assert(pfResolved);
  7881. // Initialize
  7882. *pfResolved = FALSE;
  7883. // Is this the root request ?
  7884. if (NULL == pRequest->m_pszBodyUrl)
  7885. {
  7886. // Do I have a user supplied root data stream...I assume its html
  7887. if (m_pRootStm)
  7888. {
  7889. // Unlink this pending request
  7890. _RelinkUrlRequest(pRequest, &m_pPending, &m_pComplete);
  7891. // Use client driven root html stream
  7892. pRequest->OnFullyAvailable(STR_TEXTHTML, m_pRootStm, this, NULL);
  7893. // Resolved
  7894. *pfResolved = TRUE;
  7895. // Done
  7896. goto exit;
  7897. }
  7898. // Otherwise, try to resolve the text/html body
  7899. else
  7900. {
  7901. // We should not have a webpage object yet...
  7902. Assert(NULL == m_pWebPage);
  7903. // Create a CMessageWebPage Object
  7904. CHECKALLOC(m_pWebPage = new CMessageWebPage(pRequest));
  7905. // Unlink this pending request
  7906. _RelinkUrlRequest(pRequest, &m_pPending, &m_pComplete);
  7907. // Feed the current amount of data read into the binder
  7908. pRequest->OnStartBinding(STR_TEXTHTML, (IStream *)m_pWebPage, this, HBODY_ROOT);
  7909. // Initialize
  7910. CHECKHR(hr = m_pWebPage->Initialize(m_pCallback, this, &m_rWebPageOpt));
  7911. // I need to feed all bound nodes to the web page for generation...
  7912. CHECKHR(hr = _HrSychronizeWebPage(m_pRootNode));
  7913. // If the entire tree bind is complete, then tell the webpage we are complete
  7914. if (ISFLAGSET(m_dwState, TREESTATE_BINDDONE))
  7915. {
  7916. // Tell the webpage that we are done
  7917. m_pWebPage->OnBindComplete(this);
  7918. m_pWebPage->Release();
  7919. m_pWebPage = NULL;
  7920. }
  7921. // Resolved
  7922. *pfResolved = TRUE;
  7923. // Done
  7924. goto exit;
  7925. }
  7926. }
  7927. // Otherwise, look for the Url
  7928. else if (FAILED(ResolveURL(NULL, NULL, pRequest->m_pszBodyUrl, URL_RESOLVE_RENDERED, &hBody)))
  7929. goto exit;
  7930. // We better have a body handle by now
  7931. Assert(_FIsValidHandle(hBody) && pRequest);
  7932. // Dereference the body
  7933. pNode = _PNodeFromHBody(hBody);
  7934. // Get the Content Type
  7935. MimeOleGetPropW(pNode->pBody, PIDTOSTR(PID_HDR_CNTTYPE), 0, &pwszCntType);
  7936. // Get the BodyStream
  7937. if (FAILED(pNode->pBody->GetData(IET_INETCSET, &pStream)))
  7938. goto exit;
  7939. // Complete
  7940. if (BINDSTATE_COMPLETE == pNode->bindstate)
  7941. {
  7942. // Unlink this pending request
  7943. _RelinkUrlRequest(pRequest, &m_pPending, &m_pComplete);
  7944. // OnComplete
  7945. pRequest->OnFullyAvailable(pwszCntType, pStream, this, pNode->hBody);
  7946. // Resolved
  7947. *pfResolved = TRUE;
  7948. // Done
  7949. goto exit;
  7950. }
  7951. // Otherwise, start binding
  7952. else if (ISFLAGSET(pNode->dwState, NODESTATE_BOUNDTOTREE))
  7953. {
  7954. // Should have pNode->pLockBytes
  7955. Assert(pNode->pLockBytes);
  7956. // Relink Request into the Node
  7957. _RelinkUrlRequest(pRequest, &m_pPending, &pNode->pResolved);
  7958. // Feed the current amount of data read into the binder
  7959. pRequest->OnStartBinding(pwszCntType, pStream, this, pNode->hBody);
  7960. // Resolved
  7961. *pfResolved = TRUE;
  7962. // Done
  7963. goto exit;
  7964. }
  7965. exit:
  7966. // Cleanup
  7967. SafeRelease(pStream);
  7968. SafeMemFree(pwszCntType);
  7969. // Done
  7970. return hr;
  7971. }
  7972. // --------------------------------------------------------------------------------
  7973. // CMessageTree::_HrSychronizeWebPage
  7974. // --------------------------------------------------------------------------------
  7975. HRESULT CMessageTree::_HrSychronizeWebPage(LPTREENODEINFO pNode)
  7976. {
  7977. // Locals
  7978. HRESULT hr=S_OK;
  7979. LPTREENODEINFO pChild;
  7980. // Invalid Arg
  7981. Assert(m_pWebPage && pNode);
  7982. // Clear the "OnWebPage" Flag, we are re-generating the webpage
  7983. FLAGCLEAR(pNode->dwState, WEBPAGE_NODESTATE_BITS);
  7984. // If this is a multipart item, lets search it's children
  7985. if (_IsMultiPart(pNode))
  7986. {
  7987. // Loop Children
  7988. for (pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  7989. {
  7990. // Check body
  7991. Assert(pChild->pParent == pNode);
  7992. // Get the flags for this child node
  7993. CHECKHR(hr = _HrSychronizeWebPage(pChild));
  7994. }
  7995. // Bind the multipart to the webpage
  7996. m_pWebPage->OnBodyBoundToTree(this, pNode);
  7997. }
  7998. // Otherwise, if the node is bound and gagged...
  7999. else if (BINDSTATE_COMPLETE == pNode->bindstate)
  8000. {
  8001. // Append to the WebPage
  8002. m_pWebPage->OnBodyBoundToTree(this, pNode);
  8003. }
  8004. exit:
  8005. // Done
  8006. return hr;
  8007. }
  8008. // --------------------------------------------------------------------------------
  8009. // CMessageTree::_UnlinkUrlRequest
  8010. // --------------------------------------------------------------------------------
  8011. void CMessageTree::_RelinkUrlRequest(LPURLREQUEST pRequest, LPURLREQUEST *ppSource,
  8012. LPURLREQUEST *ppDest)
  8013. {
  8014. // Unlink this pending request
  8015. _UnlinkUrlRequest(pRequest, ppSource);
  8016. // Link the bind request into pNode
  8017. _LinkUrlRequest(pRequest, ppDest);
  8018. }
  8019. // --------------------------------------------------------------------------------
  8020. // CMessageTree::_UnlinkUrlRequest
  8021. // --------------------------------------------------------------------------------
  8022. void CMessageTree::_UnlinkUrlRequest(LPURLREQUEST pRequest, LPURLREQUEST *ppHead)
  8023. {
  8024. // Invalid Arg
  8025. Assert(pRequest && ppHead);
  8026. // Debug make sure pRequest is part of *ppHead chain
  8027. #ifdef DEBUG
  8028. for(LPURLREQUEST pCurr=*ppHead; pCurr!=NULL; pCurr=pCurr->m_pNext)
  8029. if (pCurr == pRequest)
  8030. break;
  8031. AssertSz(pCurr, "pRequest is not part of *ppHead linked list");
  8032. #endif
  8033. // Fixup Previous and Next
  8034. LPURLREQUEST pNext = pRequest->m_pNext;
  8035. LPURLREQUEST pPrev = pRequest->m_pPrev;
  8036. // Fixup Links
  8037. if (pNext)
  8038. pNext->m_pPrev = pPrev;
  8039. if (pPrev)
  8040. pPrev->m_pNext = pNext;
  8041. // Fixup ppHead
  8042. if (pRequest == *ppHead)
  8043. {
  8044. Assert(pPrev == NULL);
  8045. *ppHead = pNext;
  8046. }
  8047. // Set Next and Prev
  8048. pRequest->m_pNext = NULL;
  8049. pRequest->m_pPrev = NULL;
  8050. }
  8051. // --------------------------------------------------------------------------------
  8052. // CMessageTree::_LinkUrlRequest
  8053. // --------------------------------------------------------------------------------
  8054. void CMessageTree::_LinkUrlRequest(LPURLREQUEST pRequest, LPURLREQUEST *ppHead)
  8055. {
  8056. // Invalid Arg
  8057. Assert(pRequest && ppHead);
  8058. // Is the head set
  8059. if (NULL != *ppHead)
  8060. {
  8061. // Set Next
  8062. pRequest->m_pNext = *ppHead;
  8063. // Set Previous
  8064. (*ppHead)->m_pPrev = pRequest;
  8065. }
  8066. // Set the head
  8067. (*ppHead) = pRequest;
  8068. }
  8069. // --------------------------------------------------------------------------------
  8070. // CMessageTree::_ReleaseUrlRequestList
  8071. // --------------------------------------------------------------------------------
  8072. void CMessageTree::_ReleaseUrlRequestList(LPURLREQUEST *ppHead)
  8073. {
  8074. // Locals
  8075. LPURLREQUEST pCurr;
  8076. LPURLREQUEST pNext;
  8077. // Invalid Arg
  8078. Assert(ppHead);
  8079. // Init
  8080. pCurr = *ppHead;
  8081. // Loop the Elements
  8082. while(pCurr)
  8083. {
  8084. // Set Next
  8085. pNext = pCurr->m_pNext;
  8086. // Free pCurr
  8087. pCurr->GetInner()->Release();
  8088. // Next
  8089. pCurr = pNext;
  8090. }
  8091. // Done
  8092. *ppHead = NULL;
  8093. }
  8094. // --------------------------------------------------------------------------------
  8095. // IsRfc1154Token
  8096. //
  8097. // --------------------------------------------------------------------------------
  8098. inline BOOL IsRfc1154Token(LPSTR pszDesired, LPSTR pszEndPtr, ULONG cchLen)
  8099. {
  8100. if (StrCmpNI(pszDesired,pszEndPtr,cchLen) != 0)
  8101. {
  8102. return (FALSE);
  8103. }
  8104. if ((pszEndPtr[cchLen] != '\0') &&
  8105. (pszEndPtr[cchLen] != ' ') &&
  8106. (pszEndPtr[cchLen] != '\t') &&
  8107. (pszEndPtr[cchLen] != ','))
  8108. {
  8109. return (FALSE);
  8110. }
  8111. return (TRUE);
  8112. }
  8113. // --------------------------------------------------------------------------------
  8114. // CMessageTree::_DecodeRfc1154
  8115. // --------------------------------------------------------------------------------
  8116. void CMessageTree::_DecodeRfc1154() {
  8117. BOOL bRes = FALSE;
  8118. HRESULT hr;
  8119. LPSTR pszEncoding = NULL;
  8120. LPSTR pszEndPtr;
  8121. ULONG cAlloc = 0;
  8122. if (!m_rOptions.fDecodeRfc1154)
  8123. {
  8124. goto exit;
  8125. }
  8126. hr = m_pBindNode->pContainer->GetProp(SYM_HDR_ENCODING, &pszEncoding);
  8127. if (!SUCCEEDED(hr))
  8128. {
  8129. goto exit;
  8130. }
  8131. pszEndPtr = pszEncoding;
  8132. // Each time we enter this loop, pszEndPtr points to the start of the
  8133. // next subfield. Each subfield is something like "103 TEXT". The
  8134. // number is always decimal, and the number is optional in the last
  8135. // subfield.
  8136. while (1)
  8137. {
  8138. LPSTR pszTmp;
  8139. ULONG cLines;
  8140. BOOL bNumberFound;
  8141. BT1154ENCODING encEncoding;
  8142. // ------------------------------------
  8143. // " 103 TEXT , ..."
  8144. // ^
  8145. // |-- pszEndPtr
  8146. //
  8147. // or (if there isn't a number)
  8148. //
  8149. // " TEXT , ..."
  8150. // ^
  8151. // |-- pszEndPtr
  8152. // ------------------------------------
  8153. bNumberFound = FALSE;
  8154. // Skip past any leading whitespace.
  8155. while ((*pszEndPtr==' ')||(*pszEndPtr=='\t'))
  8156. {
  8157. pszEndPtr++;
  8158. }
  8159. // ------------------------------------
  8160. // " 103 TEXT , ..."
  8161. // ^
  8162. // |-- pszEndPtr
  8163. //
  8164. // or (if there isn't a number)
  8165. //
  8166. // " TEXT , ..."
  8167. // ^
  8168. // |-- pszEndPtr
  8169. // ------------------------------------
  8170. pszTmp = pszEndPtr;
  8171. // We use strtoul to convert a decimal number.
  8172. // pszEndPtr will be left pointing at the
  8173. // character which terminated the number.
  8174. cLines = strtoul(pszTmp, &pszEndPtr, 10);
  8175. if (0xffffffff == cLines)
  8176. {
  8177. // We don't allow this - we use cLines == 0xffffffff to signal
  8178. // the case where the body part didn't include a line count and
  8179. // thus should consume all remaining lines. So we'll (silently)
  8180. // convert this to 0xfffffffe...
  8181. cLines = 0xfffffffe;
  8182. }
  8183. // ------------------------------------
  8184. // " 103 TEXT , ..."
  8185. // ^
  8186. // |-- pszEndPtr
  8187. //
  8188. // or (if there isn't a number)
  8189. //
  8190. // " TEXT , ..."
  8191. // ^
  8192. // |-- pszEndPtr
  8193. // ------------------------------------
  8194. if (cLines && !((*pszEndPtr==' ')||(*pszEndPtr=='\t')))
  8195. {
  8196. // Malformed - if the subfield specifies a number, then
  8197. // the number *must* be followed by whitespace.
  8198. goto exit;
  8199. }
  8200. // Now we skip past any whitespace...
  8201. while ((*pszEndPtr==' ') || (*pszEndPtr=='\t'))
  8202. {
  8203. bNumberFound = TRUE;
  8204. pszEndPtr++;
  8205. }
  8206. // ------------------------------------
  8207. // " 103 TEXT , ..."
  8208. // ^
  8209. // |-- pszEndPtr
  8210. // ------------------------------------
  8211. // We should now be pointing at the body type.
  8212. if (IsRfc1154Token("text",pszEndPtr,4))
  8213. {
  8214. encEncoding = BT1154ENC_TEXT;
  8215. pszEndPtr += 4;
  8216. }
  8217. else if (IsRfc1154Token("uuencode",pszEndPtr,8))
  8218. {
  8219. encEncoding = BT1154ENC_UUENCODE;
  8220. pszEndPtr += 8;
  8221. }
  8222. else if (IsRfc1154Token("x-binhex",pszEndPtr,8))
  8223. {
  8224. encEncoding = BT1154ENC_BINHEX;
  8225. pszEndPtr += 8;
  8226. }
  8227. else
  8228. {
  8229. // Malformed - we don't really support anything except
  8230. // TEXT, UUENCODE, and X-BINHEX. But, instead of
  8231. // falling back to "fake multipart" handling, we'll just
  8232. // pretend that this body part is TEXT...
  8233. encEncoding = BT1154ENC_TEXT;
  8234. // We need to consume the body part from the Encoding: string - that means
  8235. // that we advance until we see a NULL, a space, a tab, or a comma.
  8236. while ((*pszEndPtr != '\0') &&
  8237. (*pszEndPtr != ' ') &&
  8238. (*pszEndPtr != '\t') &&
  8239. (*pszEndPtr != ','))
  8240. {
  8241. pszEndPtr++;
  8242. }
  8243. // TBD - We could add the body type as a property on the
  8244. // body part. To do that, we would need to save it in the
  8245. // m_pBT1154 structure. We'd also have to figure out which
  8246. // property to set it as.
  8247. }
  8248. // ------------------------------------
  8249. // " 103 TEXT , ..."
  8250. // ^
  8251. // |-- pszEndPtr
  8252. // ------------------------------------
  8253. // Now we skip past any whitespace...
  8254. while ((*pszEndPtr==' ') || (*pszEndPtr=='\t'))
  8255. {
  8256. pszEndPtr++;
  8257. }
  8258. // ------------------------------------
  8259. // " 103 TEXT , ..."
  8260. // ^
  8261. // |-- pszEndPtr
  8262. // ------------------------------------
  8263. if ((*pszEndPtr!='\0') && (*pszEndPtr!=','))
  8264. {
  8265. // Malformed - a subfield is terminated either by a comma,
  8266. // or by a NULL.
  8267. goto exit;
  8268. }
  8269. if (*pszEndPtr != '\0' && !bNumberFound)
  8270. {
  8271. // Malformed - only the *last* subfield can get away with
  8272. // not specifying a line count.
  8273. goto exit;
  8274. }
  8275. if (*pszEndPtr == '\0' && !bNumberFound)
  8276. {
  8277. // This is the last subfield, and there wasn't
  8278. // a line count specified. This means that the
  8279. // last body part should consume all of the remaining
  8280. // lines - so we'll set the line count really high...
  8281. cLines = 0xffffffff;
  8282. }
  8283. if (!m_pBT1154 || (m_pBT1154->cBodies == cAlloc))
  8284. {
  8285. ULONG cbCurrSize = offsetof(BOOKTREE1154, aBody) + (sizeof(BT1154BODY) * cAlloc);
  8286. ULONG cbAllocSize = cbCurrSize + sizeof(BT1154BODY) * 4;
  8287. LPBOOKTREE1154 pTmp;
  8288. CHECKALLOC(pTmp = (LPBOOKTREE1154)g_pMalloc->Alloc(cbAllocSize));
  8289. if (!m_pBT1154)
  8290. {
  8291. ZeroMemory(pTmp, cbAllocSize);
  8292. }
  8293. else
  8294. {
  8295. CopyMemory(pTmp, m_pBT1154, cbCurrSize);
  8296. ZeroMemory(((LPBYTE) pTmp) + cbCurrSize, cbAllocSize - cbCurrSize);
  8297. }
  8298. SafeMemFree(m_pBT1154);
  8299. m_pBT1154 = pTmp;
  8300. cAlloc += 4;
  8301. }
  8302. Assert(0 == m_pBT1154->aBody[m_pBT1154->cBodies].encEncoding);
  8303. Assert(0 == m_pBT1154->aBody[m_pBT1154->cBodies].cLines);
  8304. m_pBT1154->aBody[m_pBT1154->cBodies].encEncoding = encEncoding;
  8305. m_pBT1154->aBody[m_pBT1154->cBodies].cLines = cLines;
  8306. m_pBT1154->cBodies++;
  8307. if (*pszEndPtr == '\0')
  8308. {
  8309. // The end of the line...
  8310. break;
  8311. }
  8312. // Skip past the comma.
  8313. Assert(*pszEndPtr==',');
  8314. Assert(bNumberFound);
  8315. pszEndPtr++;
  8316. // ------------------------------------
  8317. // " ... , 975 UUENCODE"
  8318. // ^
  8319. // |-- pszEndPtr
  8320. // ------------------------------------
  8321. }
  8322. Assert(m_pBT1154);
  8323. Assert(m_pBT1154->cBodies);
  8324. Assert(!m_pBT1154->cCurrentBody);
  8325. Assert(!m_pBT1154->cCurrentLine);
  8326. Assert(S_OK == m_pBT1154->hrLoadResult);
  8327. bRes = TRUE;
  8328. exit:
  8329. SafeMemFree(pszEncoding);
  8330. if (!bRes)
  8331. {
  8332. SafeMemFree(m_pBT1154);
  8333. }
  8334. }
  8335. #endif // !WIN16
  8336. #ifdef SMIME_V3
  8337. // --------------------------------------------------------------------------------
  8338. // CMessageTree::Encode
  8339. // --------------------------------------------------------------------------------
  8340. HRESULT CMessageTree::Encode(HWND hwnd, DWORD dwFlags)
  8341. {
  8342. HRESULT hr;
  8343. CSMime * pSMime = NULL;
  8344. // Create the object
  8345. CHECKALLOC(pSMime = new CSMime);
  8346. // Initialize the object
  8347. CHECKHR(hr = pSMime->InitNew());
  8348. // Set the state flag to tell us about re-use of boundaries
  8349. FLAGSET(m_dwState, TREESTATE_REUSESIGNBOUND);
  8350. // Encode the message
  8351. CHECKHR(hr = pSMime->EncodeMessage2(this, m_rOptions.ulSecIgnoreMask |
  8352. dwFlags, hwnd));
  8353. exit:
  8354. ReleaseObj(pSMime);
  8355. return hr;
  8356. }
  8357. // --------------------------------------------------------------------------------
  8358. // CMessageTree::Decode
  8359. // --------------------------------------------------------------------------------
  8360. HRESULT CMessageTree::Decode(HWND hwnd, DWORD dwFlags, IMimeSecurityCallback * pCallback)
  8361. {
  8362. HRESULT hr;
  8363. CSMime * pSMime = NULL;
  8364. // Create the object
  8365. CHECKALLOC(pSMime = new CSMime);
  8366. // Initialize the object
  8367. CHECKHR(hr = pSMime->InitNew());
  8368. // Encode the message
  8369. CHECKHR(hr = pSMime->DecodeMessage2(this, m_rOptions.ulSecIgnoreMask |
  8370. dwFlags, hwnd, pCallback));
  8371. exit:
  8372. ReleaseObj(pSMime);
  8373. return hr;
  8374. }
  8375. // --------------------------------------------------------------------------------
  8376. // CMessageTree::GetRecipientCount
  8377. // --------------------------------------------------------------------------------
  8378. HRESULT CMessageTree::GetRecipientCount(DWORD dwFlags, DWORD * pdwRecipCount)
  8379. {
  8380. HRESULT hr;
  8381. IMimeSecurity2 * pms2 = NULL;
  8382. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8383. CHECKHR(hr = pms2->GetRecipientCount(dwFlags, pdwRecipCount));
  8384. exit:
  8385. if (pms2 != NULL) pms2->Release();
  8386. return hr;
  8387. }
  8388. // --------------------------------------------------------------------------------
  8389. // CMessageTree::AddRecipient
  8390. // --------------------------------------------------------------------------------
  8391. HRESULT CMessageTree::AddRecipient(DWORD dwFlags, DWORD cRecipData,
  8392. PCMS_RECIPIENT_INFO precipData)
  8393. {
  8394. HRESULT hr;
  8395. IMimeSecurity2 * pms2 = NULL;
  8396. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8397. CHECKHR(hr = pms2->AddRecipient(dwFlags, cRecipData, precipData));
  8398. exit:
  8399. if (pms2 != NULL) pms2->Release();
  8400. return hr;
  8401. }
  8402. // ------------------------------------------------------------------------------
  8403. // CMessageTree::GetRecipient
  8404. // ------------------------------------------------------------------------------
  8405. HRESULT CMessageTree::GetRecipient(DWORD dwFlags, DWORD iRecipient, DWORD cRecipients, PCMS_RECIPIENT_INFO pRecipData)
  8406. {
  8407. HRESULT hr;
  8408. IMimeSecurity2 * pms2 = NULL;
  8409. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8410. CHECKHR(hr = pms2->GetRecipient(dwFlags, iRecipient, cRecipients, pRecipData));
  8411. exit:
  8412. if (pms2 != NULL) pms2->Release();
  8413. return hr;
  8414. }
  8415. // --------------------------------------------------------------------------------
  8416. // CMessageTree::DeleteRecipient
  8417. // --------------------------------------------------------------------------------
  8418. HRESULT CMessageTree::DeleteRecipient(DWORD dwFlags, DWORD iRecipient, DWORD cRecipients)
  8419. {
  8420. HRESULT hr;
  8421. IMimeSecurity2 * pms2 = NULL;
  8422. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8423. CHECKHR(hr = pms2->DeleteRecipient(dwFlags, iRecipient, cRecipients));
  8424. exit:
  8425. if (pms2 != NULL) pms2->Release();
  8426. return hr;
  8427. }
  8428. // --------------------------------------------------------------------------------
  8429. // CMessageTree::GetAttribute
  8430. // --------------------------------------------------------------------------------
  8431. HRESULT CMessageTree::GetAttribute(DWORD dwFlags, DWORD iSigner, DWORD iAttribSet,
  8432. DWORD iInstance, LPCSTR pszObjId,
  8433. CRYPT_ATTRIBUTE ** ppattr)
  8434. {
  8435. HRESULT hr;
  8436. IMimeSecurity2 * pms2 = NULL;
  8437. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8438. CHECKHR(hr = pms2->GetAttribute(dwFlags, iSigner, iAttribSet, iInstance,
  8439. pszObjId, ppattr));
  8440. exit:
  8441. if (pms2 != NULL) pms2->Release();
  8442. return hr;
  8443. }
  8444. // --------------------------------------------------------------------------------
  8445. // CMessageTree::SetAttribute
  8446. // --------------------------------------------------------------------------------
  8447. HRESULT CMessageTree::SetAttribute(DWORD dwFlags, DWORD iSigner, DWORD iAttribSet,
  8448. const CRYPT_ATTRIBUTE * ppattr)
  8449. {
  8450. HRESULT hr;
  8451. IMimeSecurity2 * pms2 = NULL;
  8452. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8453. CHECKHR(hr = pms2->SetAttribute(dwFlags, iSigner, iAttribSet, ppattr));
  8454. exit:
  8455. if (pms2 != NULL) pms2->Release();
  8456. return hr;
  8457. }
  8458. // --------------------------------------------------------------------------------
  8459. // CMessageBody::DeleteAttribute
  8460. // --------------------------------------------------------------------------------
  8461. HRESULT CMessageTree::DeleteAttribute(DWORD dwFlags, DWORD iSigner,
  8462. DWORD iAttributeSet, DWORD iInstance,
  8463. LPCSTR pszObjId)
  8464. {
  8465. HRESULT hr;
  8466. IMimeSecurity2 * pms2 = NULL;
  8467. CHECKHR(hr = BindToObject(HBODY_ROOT, IID_IMimeSecurity2, (LPVOID *) &pms2));
  8468. CHECKHR(hr = pms2->DeleteAttribute(dwFlags, iSigner, iAttributeSet,
  8469. iInstance, pszObjId));
  8470. exit:
  8471. if (pms2 != NULL) pms2->Release();
  8472. return hr;
  8473. }
  8474. // --------------------------------------------------------------------------------
  8475. // CMessageTree::CreateReceipt
  8476. // --------------------------------------------------------------------------------
  8477. HRESULT CMessageTree::CreateReceipt(DWORD dwFlags, DWORD cbFromNames,
  8478. const BYTE *pbFromNames, DWORD cSignerCertificates,
  8479. PCCERT_CONTEXT *rgSignerCertificates,
  8480. IMimeMessage ** ppMimeMessageReceipt)
  8481. {
  8482. return E_FAIL;
  8483. }
  8484. // --------------------------------------------------------------------------------
  8485. // CMessageTree::GetReceiptSendersList
  8486. // --------------------------------------------------------------------------------
  8487. HRESULT CMessageTree::GetReceiptSendersList(DWORD dwFlags, DWORD *pcSendersList,
  8488. CERT_NAME_BLOB * *rgSendersList)
  8489. {
  8490. return E_FAIL;
  8491. }
  8492. // --------------------------------------------------------------------------------
  8493. // CMessageTree::VerifyReceipt
  8494. // --------------------------------------------------------------------------------
  8495. HRESULT CMessageTree::VerifyReceipt(DWORD dwFlags,
  8496. IMimeMessage * pMimeMessageReceipt)
  8497. {
  8498. return E_FAIL;
  8499. }
  8500. // --------------------------------------------------------------------------------
  8501. // CMessageTree::CapabilitiesSupported
  8502. // --------------------------------------------------------------------------------
  8503. HRESULT CMessageTree::CapabilitiesSupported(DWORD * pdwFlags)
  8504. {
  8505. // Assume no capabilities
  8506. *pdwFlags = 0;
  8507. // If we have msasn1.dll on the system, then we can support labels
  8508. if (FIsMsasn1Loaded()) *pdwFlags |= SMIME_SUPPORT_LABELS;
  8509. // If we have a correct crypt32, then we can support receipts & key agreement
  8510. DemandLoadCrypt32();
  8511. if (g_FSupportV3 && FIsMsasn1Loaded())
  8512. *pdwFlags |= SMIME_SUPPORT_RECEIPTS;
  8513. if (g_FSupportV3)
  8514. *pdwFlags |= SMIME_SUPPORT_KEY_AGREE;
  8515. // If we have a correct advapi32, then we can support maillist keys
  8516. DemandLoadAdvApi32();
  8517. if (VAR_CryptContextAddRef != MY_CryptContextAddRef)
  8518. *pdwFlags |= SMIME_SUPPORT_MAILLIST;
  8519. return S_OK;
  8520. }
  8521. #endif // SMIME_V3