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.

1771 lines
56 KiB

  1. // --------------------------------------------------------------------------------
  2. // WebPage.cpp
  3. // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  4. // Steven J. Bailey
  5. // --------------------------------------------------------------------------------
  6. #include "pch.hxx"
  7. #include "mhtmlurl.h"
  8. #include "webpage.h"
  9. #include "vstream.h"
  10. #include "booktree.h"
  11. #include "strconst.h"
  12. #include "containx.h"
  13. #include "bookbody.h"
  14. #include "mimeapi.h"
  15. #include "plainstm.h"
  16. #include "mimeutil.h"
  17. #include "symcache.h"
  18. #include "dllmain.h"
  19. #include "internat.h"
  20. #include "shlwapi.h"
  21. #include "shlwapip.h"
  22. #include "enriched.h"
  23. #include "resource.h"
  24. //#include "util.h"
  25. #include "demand.h"
  26. // From Util.h
  27. HRESULT HrLoadStreamFileFromResourceW(ULONG uCodePage, LPCSTR lpszResourceName, LPSTREAM *ppstm);
  28. // --------------------------------------------------------------------------------
  29. // CMessageWebPage::CMessageWebPage
  30. // --------------------------------------------------------------------------------
  31. CMessageWebPage::CMessageWebPage(LPURLREQUEST pRequest) : m_pRequest(pRequest)
  32. {
  33. TraceCall("CMessageWebPage::CMessageWebPage");
  34. Assert(m_pRequest);
  35. m_cRef = 1;
  36. m_pCallback = NULL;
  37. m_pRequest->AddRef();
  38. m_pHeadSegment = NULL;
  39. m_pTailSegment = NULL;
  40. m_pCurrSegment = NULL;
  41. m_fComplete = FALSE;
  42. m_cInline = 0;
  43. m_cbOffset = 0;
  44. m_cSlideShow = 0;
  45. ZeroMemory(&m_rOptions, sizeof(WEBPAGEOPTIONS));
  46. InitializeCriticalSection(&m_cs);
  47. }
  48. // --------------------------------------------------------------------------------
  49. // CMessageWebPage::~CMessageWebPage
  50. // --------------------------------------------------------------------------------
  51. CMessageWebPage::~CMessageWebPage(void)
  52. {
  53. TraceCall("CMessageWebPage::~CMessageWebPage");
  54. Assert(m_pRequest == NULL);
  55. _VFreeSegmentList();
  56. if (m_pCallback && m_pCallback != this)
  57. m_pCallback->Release();
  58. DeleteCriticalSection(&m_cs);
  59. }
  60. // --------------------------------------------------------------------------------
  61. // CMessageWebPage::QueryInterface
  62. // --------------------------------------------------------------------------------
  63. STDMETHODIMP CMessageWebPage::QueryInterface(REFIID riid, LPVOID *ppv)
  64. {
  65. // Locals
  66. HRESULT hr=S_OK;
  67. // Tracing
  68. TraceCall("CMessageWebPage::QueryInterface");
  69. // check params
  70. if (ppv == NULL)
  71. return TrapError(E_INVALIDARG);
  72. // Find IID
  73. if (IID_IUnknown == riid)
  74. *ppv = (IUnknown *)(IStream *)this;
  75. else if (IID_IStream == riid)
  76. *ppv = (IStream *)this;
  77. else if (IID_IMimeMessageCallback == riid)
  78. *ppv = (IMimeMessageCallback *)this;
  79. else
  80. {
  81. *ppv = NULL;
  82. hr = TrapError(E_NOINTERFACE);
  83. goto exit;
  84. }
  85. // AddRef It
  86. ((IUnknown *)*ppv)->AddRef();
  87. exit:
  88. // Done
  89. return hr;
  90. }
  91. // --------------------------------------------------------------------------------
  92. // CMessageWebPage::AddRef
  93. // --------------------------------------------------------------------------------
  94. STDMETHODIMP_(ULONG) CMessageWebPage::AddRef(void)
  95. {
  96. TraceCall("CMessageWebPage::AddRef");
  97. return InterlockedIncrement(&m_cRef);
  98. }
  99. // --------------------------------------------------------------------------------
  100. // CMessageWebPage::Release
  101. // --------------------------------------------------------------------------------
  102. STDMETHODIMP_(ULONG) CMessageWebPage::Release(void)
  103. {
  104. TraceCall("CMessageWebPage::Release");
  105. LONG cRef = InterlockedDecrement(&m_cRef);
  106. if (0 == cRef)
  107. delete this;
  108. return (ULONG)cRef;
  109. }
  110. // --------------------------------------------------------------------------------
  111. // CMessageWebPage::Read
  112. // --------------------------------------------------------------------------------
  113. STDMETHODIMP CMessageWebPage::Read(LPVOID pvData, ULONG cbData, ULONG *pcbRead)
  114. {
  115. // Locals
  116. HRESULT hr=S_OK;
  117. ULONG cbLeft=cbData;
  118. ULONG cbRead=0;
  119. ULONG cbSegmentRead;
  120. LPPAGESEGMENT pSegment;
  121. // Tracing
  122. TraceCall("CMessageWebPage::Read");
  123. // Invalid Ags
  124. if (NULL == pvData)
  125. return TraceResult(E_INVALIDARG);
  126. // HrInitialize
  127. if (pcbRead)
  128. *pcbRead = 0;
  129. // Thread Safety
  130. EnterCriticalSection(&m_cs);
  131. // Only if there is a current segment
  132. if (m_pCurrSegment)
  133. {
  134. // HrInitialize Segment Loop
  135. while (cbLeft)
  136. {
  137. // Is there data left to read in this segment ?
  138. if (m_pCurrSegment->cbOffset == m_pCurrSegment->cbLength && TRUE == m_pCurrSegment->fLengthKnown)
  139. {
  140. // Are there no more segments ?
  141. if (NULL == m_pCurrSegment->pNext)
  142. break;
  143. // Goto Next Segment
  144. m_pCurrSegment = m_pCurrSegment->pNext;
  145. }
  146. // We should have a stream for the current segment
  147. Assert(m_pCurrSegment->pStream);
  148. // Compute the current position of the stream
  149. #ifdef DEBUG
  150. DWORD cbOffset;
  151. SideAssert(SUCCEEDED(HrGetStreamPos(m_pCurrSegment->pStream, &cbOffset)));
  152. Assert(cbOffset == m_pCurrSegment->cbOffset);
  153. #endif
  154. // If I have computed the length of this item yet?
  155. IF_FAILEXIT(hr = m_pCurrSegment->pStream->Read((LPVOID)((LPBYTE)pvData + cbRead), cbLeft, &cbSegmentRead));
  156. // Increment offset
  157. m_pCurrSegment->cbOffset += cbSegmentRead;
  158. // Compute Global Offset
  159. m_cbOffset += cbSegmentRead;
  160. // Adjust the size of this segment ?
  161. if (m_pCurrSegment->cbOffset > m_pCurrSegment->cbLength)
  162. {
  163. Assert(FALSE == m_pCurrSegment->fLengthKnown);
  164. m_pCurrSegment->cbLength = m_pCurrSegment->cbOffset;
  165. }
  166. // Decrement amount left
  167. cbLeft -= cbSegmentRead;
  168. // Increment Amount Actually Read
  169. cbRead += cbSegmentRead;
  170. // If we read zero...we must have read all the data in this segment
  171. if (0 == cbSegmentRead)
  172. {
  173. Assert(m_pCurrSegment->cbLength == m_pCurrSegment->cbOffset);
  174. m_pCurrSegment->fLengthKnown = TRUE;
  175. }
  176. }
  177. }
  178. // Return Amount Read
  179. if (pcbRead)
  180. *pcbRead = cbRead;
  181. exit:
  182. // Thread Safety
  183. LeaveCriticalSection(&m_cs);
  184. // Done
  185. return hr;
  186. }
  187. // --------------------------------------------------------------------------------
  188. // CMessageWebPage::Seek
  189. // --------------------------------------------------------------------------------
  190. STDMETHODIMP CMessageWebPage::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNew)
  191. {
  192. // Locals
  193. HRESULT hr=S_OK;
  194. DWORD cbOffsetNew;
  195. DWORD cbSize=0xffffffff;
  196. // Tracing
  197. TraceCall("CMessageWebPage::Seek");
  198. // Invalid Args
  199. Assert(dlibMove.HighPart == 0);
  200. // Thread Safety
  201. EnterCriticalSection(&m_cs);
  202. // Relative to the beginning of the stream
  203. if (STREAM_SEEK_SET == dwOrigin)
  204. {
  205. // If less than zero, its an error
  206. // if (dlibMove.LowPart < 0)
  207. // {
  208. // hr = TraceResult(E_FAIL);
  209. // goto exit;
  210. // }
  211. // else
  212. // Otherwise, if past current offset...
  213. if (dlibMove.LowPart > m_cbOffset)
  214. {
  215. // If not finished binding, return E_PENDING
  216. if (FALSE == m_fComplete)
  217. {
  218. hr = TraceResult(E_PENDING);
  219. goto exit;
  220. }
  221. // Compute Size of the Entire Stream
  222. IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
  223. // If past end of stream, error
  224. if (dlibMove.LowPart > cbSize)
  225. {
  226. hr = TraceResult(E_FAIL);
  227. goto exit;
  228. }
  229. }
  230. // Set new offset
  231. cbOffsetNew = (DWORD)dlibMove.LowPart;
  232. }
  233. // Relative to current offset
  234. else if (STREAM_SEEK_CUR == dwOrigin)
  235. {
  236. // If less than zero, and absolute is greater than its an error
  237. if ( (DWORD)(0 - dlibMove.LowPart) > m_cbOffset)
  238. {
  239. hr = TraceResult(E_FAIL);
  240. goto exit;
  241. }
  242. // Otherwise, if past current offset...
  243. else if (m_cbOffset + dlibMove.LowPart > m_cbOffset)
  244. {
  245. // If not finished binding, return E_PENDING
  246. if (FALSE == m_fComplete)
  247. {
  248. hr = TraceResult(E_PENDING);
  249. goto exit;
  250. }
  251. // Compute Size of the Entire Stream
  252. IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
  253. // If past end of stream, error
  254. if (dlibMove.LowPart > cbSize)
  255. {
  256. hr = TraceResult(E_FAIL);
  257. goto exit;
  258. }
  259. }
  260. // Set new offset
  261. cbOffsetNew = m_cbOffset + dlibMove.LowPart;
  262. }
  263. // Relative to the end of the stream
  264. else if (STREAM_SEEK_END == dwOrigin)
  265. {
  266. // If not finished binding, return E_PENDING
  267. if (FALSE == m_fComplete)
  268. {
  269. hr = TraceResult(E_PENDING);
  270. goto exit;
  271. }
  272. // Compute Size of the Entire Stream
  273. IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
  274. // If negative or greater than size, its an error
  275. if ( (DWORD)dlibMove.LowPart > cbSize)
  276. {
  277. hr = TraceResult(E_FAIL);
  278. goto exit;
  279. }
  280. // Set new offset
  281. cbOffsetNew = cbSize - dlibMove.LowPart;
  282. }
  283. // Otherwise, its an error
  284. else
  285. {
  286. hr = TraceResult(STG_E_INVALIDFUNCTION);
  287. goto exit;
  288. }
  289. // Only if a change
  290. if (m_cbOffset != cbOffsetNew)
  291. {
  292. // New offset greater than size...
  293. m_cbOffset = cbOffsetNew;
  294. // Walk through the segments
  295. for (m_pCurrSegment=m_pHeadSegment; m_pCurrSegment!=NULL; m_pCurrSegment=m_pCurrSegment->pNext)
  296. {
  297. // Never Seeks beyond length
  298. Assert(FALSE == m_pCurrSegment->fLengthKnown ? cbOffsetNew <= m_pCurrSegment->cbLength : TRUE);
  299. // Offset falls into this segment ?
  300. if (cbOffsetNew <= m_pCurrSegment->cbLength)
  301. {
  302. // Set Offset within m_pCurrSegment->pStream
  303. m_pCurrSegment->cbOffset = cbOffsetNew;
  304. // Should have a stream
  305. Assert(m_pCurrSegment->pStream);
  306. // Seek the stream
  307. IF_FAILEXIT(hr = HrStreamSeekSet(m_pCurrSegment->pStream, m_pCurrSegment->cbOffset));
  308. // Reset the Offsets of the remaining segments
  309. for (LPPAGESEGMENT pSegment=m_pCurrSegment->pNext; pSegment!=NULL; pSegment=pSegment->pNext)
  310. {
  311. // At 0
  312. pSegment->cbOffset = 0;
  313. // Seek the stream
  314. IF_FAILEXIT(hr = HrStreamSeekSet(pSegment->pStream, 0));
  315. }
  316. // Done
  317. break;
  318. }
  319. // Otherwise, seek the stream to the end offset / length
  320. else
  321. {
  322. // Must know the length
  323. Assert(m_pCurrSegment->fLengthKnown);
  324. // Set Offset
  325. m_pCurrSegment->cbOffset = m_pCurrSegment->cbLength;
  326. // Seek the stream
  327. IF_FAILEXIT(hr = HrStreamSeekSet(m_pCurrSegment->pStream, m_pCurrSegment->cbOffset));
  328. }
  329. // Decrement cbOffsetNew
  330. cbOffsetNew -= m_pCurrSegment->cbLength;
  331. }
  332. }
  333. // Return Position
  334. if (plibNew)
  335. {
  336. plibNew->HighPart = 0;
  337. plibNew->LowPart = (LONG)m_cbOffset;
  338. }
  339. exit:
  340. // Thread Safety
  341. LeaveCriticalSection(&m_cs);
  342. // Done
  343. return hr;
  344. }
  345. // --------------------------------------------------------------------------------
  346. // CMessageWebPage::_ComputeStreamSize
  347. // --------------------------------------------------------------------------------
  348. HRESULT CMessageWebPage::_ComputeStreamSize(LPDWORD pcbSize)
  349. {
  350. // Locals
  351. HRESULT hr=S_OK;
  352. LPPAGESEGMENT pCurrSegment;
  353. // Tracing
  354. TraceCall("CMessageWebPage::_ComputeStreamSize");
  355. // Invalid Args
  356. Assert(pcbSize && m_fComplete);
  357. // Initialize
  358. *pcbSize = 0;
  359. // Walk through the segments
  360. for (pCurrSegment=m_pHeadSegment; pCurrSegment!=NULL; pCurrSegment=pCurrSegment->pNext)
  361. {
  362. // If length is not known, then get its size
  363. if (FALSE == pCurrSegment->fLengthKnown)
  364. {
  365. // There better be a stream
  366. Assert(pCurrSegment->pStream);
  367. // Get the size of the stream
  368. IF_FAILEXIT(hr = HrGetStreamSize(pCurrSegment->pStream, &pCurrSegment->cbLength));
  369. // Set Size is known
  370. pCurrSegment->fLengthKnown = TRUE;
  371. }
  372. // Increment Size
  373. (*pcbSize) += pCurrSegment->cbLength;
  374. }
  375. exit:
  376. // Done
  377. return hr;
  378. }
  379. // --------------------------------------------------------------------------------
  380. // CMessageWebPage::_AllocateSegment
  381. // --------------------------------------------------------------------------------
  382. HRESULT CMessageWebPage::_AllocateSegment(LPPAGESEGMENT *ppSegment, BOOL fCreateStream)
  383. {
  384. // Locals
  385. HRESULT hr=S_OK;
  386. // Invalid Args
  387. Assert(ppSegment);
  388. // Tracing
  389. TraceCall("CMessageWebPage::_AllocateSegment");
  390. // Allocate It
  391. IF_NULLEXIT(*ppSegment = (LPPAGESEGMENT)g_pMalloc->Alloc(sizeof(PAGESEGMENT)));
  392. // Zero
  393. ZeroMemory(*ppSegment, sizeof(PAGESEGMENT));
  394. // Create a Stream ?
  395. if (fCreateStream)
  396. {
  397. // Allocate a Stream
  398. IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&(*ppSegment)->pStream));
  399. }
  400. exit:
  401. // Failure ?
  402. if (FAILED(hr) && *ppSegment != NULL)
  403. {
  404. SafeRelease((*ppSegment)->pStream);
  405. SafeMemFree((*ppSegment));
  406. }
  407. // Done
  408. return hr;
  409. }
  410. // --------------------------------------------------------------------------------
  411. // CMessageWebPage::_VAppendSegment
  412. // --------------------------------------------------------------------------------
  413. void CMessageWebPage::_VAppendSegment(LPPAGESEGMENT pSegment)
  414. {
  415. // Invalid Args
  416. Assert(pSegment);
  417. // Tracing
  418. TraceCall("CMessageWebPage::_VAppendSegment");
  419. // Head is Null
  420. if (NULL == m_pHeadSegment)
  421. {
  422. Assert(NULL == m_pTailSegment);
  423. m_pCurrSegment = m_pHeadSegment = m_pTailSegment = pSegment;
  424. }
  425. // Otherwise, append to tail
  426. else
  427. {
  428. Assert(m_pTailSegment);
  429. m_pTailSegment->pNext = pSegment;
  430. pSegment->pPrev = m_pTailSegment;
  431. m_pTailSegment = pSegment;
  432. }
  433. }
  434. // --------------------------------------------------------------------------------
  435. // CMessageWebPage::_VFreeSegmentList
  436. // --------------------------------------------------------------------------------
  437. void CMessageWebPage::_VFreeSegmentList(void)
  438. {
  439. // Locals
  440. LPPAGESEGMENT pCurr;
  441. LPPAGESEGMENT pNext;
  442. // Tracing
  443. TraceCall("CMessageWebPage::_VFreeSegmentList");
  444. // HrInitialize Curr
  445. pCurr = m_pHeadSegment;
  446. // Loop
  447. while(pCurr)
  448. {
  449. // Set pNext
  450. pNext = pCurr->pNext;
  451. // Free This One
  452. _VFreeSegment(pCurr);
  453. // Goto Next
  454. pCurr = pNext;
  455. }
  456. // Set Head and Tail
  457. m_pHeadSegment = m_pTailSegment = NULL;
  458. }
  459. // --------------------------------------------------------------------------------
  460. // CMessageWebPage::_VFreeSegment
  461. // --------------------------------------------------------------------------------
  462. void CMessageWebPage::_VFreeSegment(LPPAGESEGMENT pSegment)
  463. {
  464. TraceCall("CMessageWebPage::_VFreeSegment");
  465. SafeRelease(pSegment->pStream);
  466. g_pMalloc->Free(pSegment);
  467. }
  468. // --------------------------------------------------------------------------------
  469. // CMessageWebPage::_VInitializeCharacterSet
  470. // --------------------------------------------------------------------------------
  471. void CMessageWebPage::_VInitializeCharacterSet(LPMESSAGETREE pTree)
  472. {
  473. // Locals
  474. HRESULT hr=S_OK;
  475. INETCSETINFO rCharset;
  476. DWORD dwCodePage=0;
  477. HCHARSET hCharset;
  478. // Tracing
  479. TraceCall("CMessageWebPage::_VInitializeCharacterSet");
  480. // Get the Character Set
  481. pTree->GetCharset(&m_hCharset);
  482. // Raid-47838: Nav4 message in iso-2022-jp causes initialization error
  483. if (NULL == m_hCharset)
  484. {
  485. // Get the default character set
  486. if (SUCCEEDED(g_pInternat->GetDefaultCharset(&hCharset)))
  487. m_hCharset = hCharset;
  488. }
  489. #ifdef BROKEN
  490. // Raid-43580: Special case for codepage 50220 - iso-2022-jp and 50932 - JP auto use JP windows codepage instead to preserve half width Kana data
  491. MimeOleGetCharsetInfo(m_hCharset, &rCharset);
  492. // Map Character set
  493. if (rCharset.cpiInternet == 50220 || rCharset.cpiInternet == 50932)
  494. {
  495. // Raid-35230: hard-code to ISO-2022-JP-ESC or ISO-2022-JP-SIO
  496. hCharset = GetJP_ISOControlCharset();
  497. if (hCharset)
  498. m_hCharset = hCharset;
  499. }
  500. #endif
  501. // We better have a charset
  502. Assert(m_hCharset);
  503. // Done
  504. return;
  505. }
  506. // --------------------------------------------------------------------------------
  507. // CMessageWebPage::Initialize
  508. // --------------------------------------------------------------------------------
  509. HRESULT CMessageWebPage::Initialize(IMimeMessageCallback *pCallback, LPMESSAGETREE pTree,
  510. LPWEBPAGEOPTIONS pOptions)
  511. {
  512. // Locals
  513. HRESULT hr=S_OK;
  514. INETCSETINFO rCsetInfo;
  515. CODEPAGEINFO rCodePage;
  516. LPSTR pszCharset;
  517. LPPAGESEGMENT pSegment=NULL;
  518. // Tracing
  519. TraceCall("CMessageWebPage::Initialize");
  520. // No Options ?
  521. Assert(pOptions);
  522. // Thread Safety
  523. EnterCriticalSection(&m_cs);
  524. // Better have a request
  525. Assert(m_pRequest);
  526. // No WebPage Callback
  527. if (pCallback)
  528. {
  529. m_pCallback = pCallback;
  530. m_pCallback->AddRef();
  531. }
  532. else
  533. m_pCallback = this;
  534. // Save the Options
  535. CopyMemory(&m_rOptions, pOptions, sizeof(WEBPAGEOPTIONS));
  536. // Remap the Character Set ?
  537. _VInitializeCharacterSet(pTree);
  538. // Append a PageSegment
  539. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
  540. // Client wants meta-tag?
  541. if (!ISFLAGSET(m_rOptions.dwFlags, WPF_NOMETACHARSET))
  542. {
  543. // Get the charset information
  544. IF_FAILEXIT(hr = MimeOleGetCharsetInfo(m_hCharset, &rCsetInfo));
  545. // Get the codepage information
  546. IF_FAILEXIT(hr = MimeOleGetCodePageInfo(rCsetInfo.cpiInternet, &rCodePage));
  547. // Set the charset to write into the meta tag
  548. pszCharset = FIsEmpty(rCodePage.szWebCset) ? rCodePage.szBodyCset : rCodePage.szWebCset;
  549. // If Still Empty, use iso-8859-1
  550. if (FIsEmpty(pszCharset))
  551. pszCharset = (LPSTR)STR_ISO88591;
  552. // Write STR_METATAG_PREFIX
  553. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_METATAG_PREFIX, lstrlen(STR_METATAG_PREFIX), NULL));
  554. // Write the Charset
  555. IF_FAILEXIT(hr = pSegment->pStream->Write(pszCharset, lstrlen(pszCharset), NULL));
  556. // Write STR_METATAG_POSTFIX
  557. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_METATAG_POSTFIX, lstrlen(STR_METATAG_POSTFIX), NULL));
  558. }
  559. // Only showing images ?
  560. if (ISFLAGSET(m_rOptions.dwFlags, WPF_IMAGESONLY))
  561. {
  562. // Locals
  563. CHAR szRes[255];
  564. // Load the string
  565. LoadString(g_hLocRes, idsImagesOnly, szRes, ARRAYSIZE(szRes));
  566. // Write idsImagesOnly
  567. IF_FAILEXIT(hr = pSegment->pStream->Write(szRes, lstrlen(szRes), NULL));
  568. }
  569. // Rewind the segment
  570. IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
  571. // Link Segment into list...
  572. _VAppendSegment(pSegment);
  573. // Don't Free It
  574. pSegment = NULL;
  575. // Report that some data is available
  576. m_pRequest->OnBindingDataAvailable();
  577. exit:
  578. // Cleanup
  579. if (pSegment)
  580. _VFreeSegment(pSegment);
  581. // Thread Safety
  582. LeaveCriticalSection(&m_cs);
  583. // Done
  584. return hr;
  585. }
  586. // --------------------------------------------------------------------------------
  587. // CMessageWebPage::_GetInlineHtmlStream
  588. // --------------------------------------------------------------------------------
  589. #define CCHMAX_SNIFFER 64
  590. HRESULT CMessageWebPage::_GetInlineHtmlStream(LPMESSAGETREE pTree, LPTREENODEINFO pNode,
  591. LPSTREAM *ppStream)
  592. {
  593. // Locals
  594. HRESULT hr=S_OK;
  595. BOOL fQuote;
  596. IStream *pStmHtml=NULL;
  597. IStream *pStmHtmlW=NULL;
  598. IStream *pStmPlainW=NULL;
  599. IStream *pStmEnriched=NULL;
  600. ULONG cbRead;
  601. LPWSTR pwszType=NULL;
  602. WCHAR wszHeader[CCHMAX_SNIFFER];
  603. CHAR szHeader[CCHMAX_SNIFFER];
  604. // Tracing
  605. TraceCall("CMessageWebPage::_GetInlineHtmlStream");
  606. // Invalid Args
  607. Assert(pTree && pNode && ppStream);
  608. // HrInitialize
  609. *ppStream = NULL;
  610. // text/html ?
  611. if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_HTML))
  612. {
  613. // Just get and return an HTML inetcset encoded stream
  614. IF_FAILEXIT(hr = pNode->pBody->GetData(IET_INETCSET, &pStmHtml));
  615. }
  616. // text/enriched
  617. else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_ENRICHED))
  618. {
  619. // Convert to HTML
  620. IF_FAILEXIT(hr = MimeOleConvertEnrichedToHTMLEx((IMimeBody *)pNode->pBody, IET_INETCSET, &pStmHtml));
  621. }
  622. // text/*
  623. else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, NULL))
  624. {
  625. // Get the data
  626. IF_FAILEXIT(hr = pNode->pBody->GetData(IET_UNICODE, &pStmPlainW));
  627. // Read Off the first 255 bytes
  628. IF_FAILEXIT(hr = pStmPlainW->Read(wszHeader, (CCHMAX_SNIFFER * sizeof(WCHAR)), &cbRead));
  629. // Did we read something
  630. if (cbRead > 0)
  631. {
  632. // Null It
  633. ULONG cchRead = (cbRead / sizeof(WCHAR)) - 1;
  634. // Null It Out
  635. wszHeader[cchRead] = L'\0';
  636. // Convert to ANSI
  637. szHeader[0] = L'\0';
  638. if(WideCharToMultiByte(CP_ACP, 0, wszHeader, -1, szHeader, ARRAYSIZE(szHeader) - 1, NULL, NULL) == 0)
  639. {
  640. IF_FAILEXIT(hr = HrRewindStream(pStmPlainW));
  641. }
  642. else
  643. {
  644. // Lets Read the first "<x-rich>" bytes and see if it might be text/enriched
  645. if (0 == StrCmpI(szHeader, "<x-rich>"))
  646. {
  647. // Convert to HTML
  648. IF_FAILEXIT(hr = MimeOleConvertEnrichedToHTMLEx((IMimeBody *)pNode->pBody, IET_INETCSET, &pStmHtml));
  649. }
  650. // Is this html ?
  651. else if (SUCCEEDED(FindMimeFromData(NULL, NULL, szHeader, cchRead, NULL, NULL, &pwszType, 0)) && pwszType && 0 == StrCmpIW(pwszType, L"text/html"))
  652. {
  653. // Release pStmPlainW
  654. SafeRelease(pStmPlainW);
  655. // Just get and return an HTML inetcset encoded stream
  656. IF_FAILEXIT(hr = pNode->pBody->GetData(IET_INETCSET, &pStmHtml));
  657. }
  658. // Otherwise, rewind pStmPlainW
  659. else
  660. {
  661. // Rewind
  662. IF_FAILEXIT(hr = HrRewindStream(pStmPlainW));
  663. }
  664. }
  665. }
  666. }
  667. // Otheriwse
  668. else
  669. {
  670. hr = S_FALSE;
  671. goto exit;
  672. }
  673. // We have HTML
  674. if (pStmHtml)
  675. {
  676. // Client wants HTML
  677. if (ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
  678. {
  679. // Return the Html Stream
  680. *ppStream = pStmHtml;
  681. pStmHtml = NULL;
  682. goto exit;
  683. }
  684. // Otherwise, client wants plain text
  685. else
  686. {
  687. // Convert to Plain text
  688. IF_FAILEXIT(hr = HrConvertHTMLToFormat(pStmHtml, &pStmPlainW, CF_UNICODETEXT));
  689. }
  690. }
  691. // Otherwise, if I have a plain stream
  692. if (NULL == pStmPlainW)
  693. {
  694. hr = S_FALSE;
  695. goto exit;
  696. }
  697. // Determine if we should quote (Can't quote QP)
  698. fQuote = (IET_QP == pNode->pContainer->GetEncodingType()) ? FALSE : TRUE;
  699. // Convert Unicode Plain stream to HTML
  700. IF_FAILEXIT(hr = HrConvertPlainStreamW(pStmPlainW, fQuote ? m_rOptions.wchQuote : NULL, &pStmHtmlW));
  701. // Convert from unicode back to internet character set
  702. IF_FAILEXIT(hr = HrIStreamWToInetCset(pStmHtmlW, m_hCharset, &pStmHtml));
  703. // Return pStmHtml
  704. *ppStream = pStmHtml;
  705. pStmHtml = NULL;
  706. // #ifdef DEBUG
  707. // WriteStreamToFile(*ppStream, "c:\\dump.htm", CREATE_ALWAYS, GENERIC_WRITE);
  708. // #endif
  709. exit:
  710. // Cleanup
  711. SafeRelease(pStmHtml);
  712. SafeRelease(pStmHtmlW);
  713. SafeRelease(pStmPlainW);
  714. SafeRelease(pStmEnriched);
  715. SafeMemFree(pwszType);
  716. // Done
  717. return hr;
  718. }
  719. // --------------------------------------------------------------------------------
  720. // CMessageWebPage::_DoSegmentSplitter
  721. // --------------------------------------------------------------------------------
  722. HRESULT CMessageWebPage::_DoSegmentSplitter(void)
  723. {
  724. // Locals
  725. HRESULT hr=S_OK;
  726. LPPAGESEGMENT pSegment=NULL;
  727. // Trace
  728. TraceCall("CMessageWebPage::_DoSegmentSplitter");
  729. // Append a PageSegment
  730. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
  731. // If more than one inline bodies ?
  732. if (S_OK == m_pCallback->OnWebPageSplitter(m_cInline, pSegment->pStream))
  733. {
  734. // Rewind the stream
  735. HrRewindStream(pSegment->pStream);
  736. // Link Segment into list...
  737. _VAppendSegment(pSegment);
  738. // Don't Free It
  739. pSegment = NULL;
  740. }
  741. // Otherwise, free this segment
  742. else
  743. {
  744. // Free It
  745. _VFreeSegment(pSegment);
  746. // Done Free it again
  747. pSegment = NULL;
  748. }
  749. exit:
  750. // Done
  751. return hr;
  752. }
  753. // --------------------------------------------------------------------------------
  754. // CMessageWebPage::_InlineTextBody
  755. // --------------------------------------------------------------------------------
  756. HRESULT CMessageWebPage::_InlineTextBody(LPMESSAGETREE pTree, LPTREENODEINFO pNode, BOOL fSetParents)
  757. {
  758. // Locals
  759. HRESULT hr=S_OK;
  760. PROPVARIANT rVariant;
  761. LPSTREAM pStream=NULL;
  762. LPPAGESEGMENT pSegment=NULL;
  763. LPTREENODEINFO pCurrent;
  764. LPSTR pszFileName=NULL;
  765. // Tracing
  766. TraceCall("CMessageWebPage::_InlineTextBody");
  767. // This node better not already be on the webpage
  768. Assert(FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE));
  769. // Handle Text Types that I explicitly will never inline
  770. if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_VCARD))
  771. goto exit;
  772. // Inline the body
  773. if (S_OK != _GetInlineHtmlStream(pTree, pNode, &pStream))
  774. goto exit;
  775. // Setup a Variant
  776. rVariant.vt = VT_LPSTR;
  777. // If this body has a file name, lets also show it as an attachment
  778. if (SUCCEEDED(pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_FILENAME), NOFLAGS, &rVariant)))
  779. {
  780. // Save the File name
  781. pszFileName = rVariant.pszVal;
  782. }
  783. // Only showing images ?
  784. if (FALSE == ISFLAGSET(m_rOptions.dwFlags, WPF_IMAGESONLY))
  785. {
  786. // Segment Split
  787. _DoSegmentSplitter();
  788. // Append a PageSegment
  789. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, FALSE));
  790. // Set pStream
  791. pSegment->pStream = pStream;
  792. pSegment->pStream->AddRef();
  793. // Link Segment into list...
  794. _VAppendSegment(pSegment);
  795. // Don't Free It
  796. pSegment = NULL;
  797. // Report that some data is available
  798. m_pRequest->OnBindingDataAvailable();
  799. // Increment number of inline bodies
  800. m_cInline++;
  801. }
  802. // Mark the node as rendered
  803. rVariant.vt = VT_UI4;
  804. rVariant.ulVal = TRUE;
  805. // If this has a filename
  806. if (pszFileName)
  807. {
  808. // Mark it as auto-inlined
  809. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
  810. }
  811. // Set the Property
  812. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  813. // We have rendered this node on the webpage
  814. FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
  815. // Set parents are on webpage
  816. if (fSetParents)
  817. {
  818. // Raid-45116: new text attachment contains message body on Communicator inline image message
  819. pCurrent = pNode->pParent;
  820. // Try to find an alternative parent...
  821. while(pCurrent)
  822. {
  823. // If multipart/alternative, walk all of its children and mark them as being rendered
  824. if (S_OK == pCurrent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE))
  825. {
  826. // Get Parent
  827. for (LPTREENODEINFO pChild=pCurrent->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  828. {
  829. // Set Resolve Property
  830. SideAssert(SUCCEEDED(pChild->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  831. }
  832. }
  833. // Mark as being on the webpage
  834. FLAGSET(pCurrent->dwState, NODESTATE_ONWEBPAGE);
  835. // Get Next Parent
  836. pCurrent = pCurrent->pParent;
  837. }
  838. }
  839. exit:
  840. // Cleanup
  841. SafeRelease(pStream);
  842. SafeMemFree(pszFileName);
  843. if (pSegment)
  844. _VFreeSegment(pSegment);
  845. // Done
  846. return hr;
  847. }
  848. // --------------------------------------------------------------------------------
  849. // CMessageWebPage::_SetContentId
  850. // --------------------------------------------------------------------------------
  851. HRESULT CMessageWebPage::_SetContentId(LPTREENODEINFO pNode, LPSTR pszCID, ULONG cchCID)
  852. {
  853. // Locals
  854. HRESULT hr=S_OK;
  855. LPSTR pszContentId=NULL;
  856. GUID guid;
  857. WCHAR wszGUID[64];
  858. LPSTR pszFile;
  859. LPSTR pszGuid=0;
  860. LPSTR pszT;
  861. // Tracing
  862. TraceCall("CMessageWebPage::_SetContentId");
  863. // See of pNode already has a Content-ID
  864. if (S_FALSE == pNode->pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTID)))
  865. {
  866. // $BUG #64186
  867. // Create a content-id in the form:
  868. // CID:{guid}/<filename>
  869. // so that trident's save-as dialog has a meaningful
  870. // filename to work with
  871. // Create a guid
  872. IF_FAILEXIT(hr = CoCreateGuid(&guid));
  873. // Convert the GUID to a string
  874. if (0 == StringFromGUID2(guid, wszGUID, ARRAYSIZE(wszGUID)))
  875. {
  876. hr = TraceResult(E_FAIL);
  877. goto exit;
  878. }
  879. // Convert to ANSI
  880. pszGuid = PszToANSI(CP_ACP, wszGUID);
  881. if (!pszGuid)
  882. {
  883. hr = TraceResult(E_OUTOFMEMORY);
  884. goto exit;
  885. }
  886. // [PaulHi] 6/18/99. Raid 76531. Don't append the file name to the GUID ...
  887. // it causes encoding problems with Trident International. In particular the
  888. // DBCS characters in the filename can cause the HTML to contain both JIS and
  889. // SHIFT-JIS encodings. I believe this is a Trident bug because we explicitly
  890. // set the Trident to CP_JAUTODETECT (JIS) and it still performs SHIFT_JIS decoding
  891. // if the attachment filename is long. However, the real fix is to make the entire
  892. // HTML text a single (JIS) encoding, but this is difficult to do because the attachment
  893. // code is single byte (DBCS) which equates to SHIFT-JIS. We need to convert fully to
  894. // Unicode.
  895. INETCSETINFO rCharset;
  896. MimeOleGetCharsetInfo(m_hCharset, &rCharset);
  897. if (rCharset.cpiInternet != CP_JAUTODETECT) // code page 50932
  898. {
  899. // If we have a file-name, append to guid
  900. if (pNode->pContainer->GetProp(PIDTOSTR(STR_ATT_GENFNAME), &pszFile)==S_OK)
  901. {
  902. // Allocate Buffer
  903. DWORD cchSize = (lstrlen(pszFile) + lstrlen(pszGuid) + 2);
  904. pszT = PszAllocA(cchSize);
  905. if (pszT)
  906. {
  907. // Copy contents and free old GUID
  908. wnsprintfA(pszT, cchSize, "%s/%s", pszGuid, pszFile);
  909. MemFree(pszGuid);
  910. pszGuid = pszT;
  911. }
  912. MemFree(pszFile);
  913. }
  914. }
  915. else
  916. {
  917. // @HACK [PaulHi] Preempt any JIS encoding problems by just appending "/.".
  918. // This will allow right-click save image as operation to work without
  919. // the user seeing the URL GUID.
  920. DWORD cchSize = (lstrlen(pszGuid) + 3);
  921. pszT = PszAllocA(cchSize);
  922. if (pszT)
  923. {
  924. // Copy conents and free old GUID
  925. wnsprintfA(pszT, cchSize, "%s/.", pszGuid);
  926. MemFree(pszGuid);
  927. pszGuid = pszT;
  928. }
  929. }
  930. // copy GUID to output buffer
  931. StrCpyNA(pszCID, pszGuid, cchCID);
  932. // Store the content-id into the node
  933. IF_FAILEXIT(hr = pNode->pContainer->SetProp(PIDTOSTR(PID_HDR_CNTID), pszCID));
  934. }
  935. // Otheriwse, get the Content-ID from this body
  936. else
  937. {
  938. // Get the Content-Id
  939. IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_HDR_CNTID), &pszContentId));
  940. // Copy it into pszCID
  941. Assert(lstrlen(pszContentId) <= (LONG)cchCID);
  942. // Copy the cid to the outbound variable
  943. StrCpyN(pszCID, pszContentId, cchCID);
  944. }
  945. exit:
  946. // Cleanup
  947. SafeMemFree(pszContentId);
  948. SafeMemFree(pszGuid);
  949. // Done
  950. return hr;
  951. }
  952. // --------------------------------------------------------------------------------
  953. // CMessageWebPage::_InlineImageBody
  954. // --------------------------------------------------------------------------------
  955. HRESULT CMessageWebPage::_InlineImageBody(LPMESSAGETREE pTree, LPTREENODEINFO pNode)
  956. {
  957. // Locals
  958. HRESULT hr=S_OK;
  959. LPSTR pszFile=NULL;
  960. LPSTR pszExt;
  961. CHAR szCID[CCHMAX_CID + 1];
  962. PROPVARIANT rVariant;
  963. LPPAGESEGMENT pSegment=NULL;
  964. // Tracing
  965. TraceCall("CMessageWebPage::_InlineImageBody");
  966. // This node better not already be on the webpage
  967. Assert(pTree && pNode && FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE));
  968. // Setup the Variant
  969. rVariant.vt = VT_UI4;
  970. // If the body is marked as inline, or autoline attachments is enabled and (slideshow is disabled)
  971. if (S_OK == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_INLINE, FALSE, FALSE) || ISFLAGSET(m_rOptions.dwFlags, WPF_AUTOINLINE))
  972. {
  973. // Get a generated filename from the body
  974. IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_GENFNAME), &pszFile));
  975. // Look up the file extension of the body
  976. pszExt = PathFindExtension(pszFile);
  977. // Do I support inlining this object ?
  978. if (lstrcmpi(pszExt, c_szBmpExt) == 0 ||
  979. lstrcmpi(pszExt, c_szJpgExt) == 0 ||
  980. lstrcmpi(pszExt, c_szJpegExt) == 0 ||
  981. lstrcmpi(pszExt, c_szGifExt) == 0 ||
  982. lstrcmpi(pszExt, c_szIcoExt) == 0 ||
  983. lstrcmpi(pszExt, c_szWmfExt) == 0 ||
  984. lstrcmpi(pszExt, c_szPngExt) == 0 ||
  985. lstrcmpi(pszExt, c_szEmfExt) == 0 ||
  986. lstrcmpi(pszExt, c_szArtExt) == 0 ||
  987. lstrcmpi(pszExt, c_szXbmExt) == 0)
  988. {
  989. // Generate a Content-Id for this body
  990. IF_FAILEXIT(hr = _SetContentId(pNode, szCID, CCHMAX_CID));
  991. // If the user wants a slide show, then lets mark this body as a slideshow image
  992. if (ISFLAGSET(m_rOptions.dwFlags, WPF_SLIDESHOW))
  993. {
  994. // Mark the node as rendered
  995. rVariant.vt = VT_UI4;
  996. rVariant.ulVal = TRUE;
  997. // Set the Property
  998. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  999. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
  1000. // Count the number of items in the slide show
  1001. m_cSlideShow++;
  1002. // This node is in the slide show and will be processed at the end of the rendering
  1003. FLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW);
  1004. // Basically, we rendered this body
  1005. FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
  1006. // Done
  1007. goto exit;
  1008. }
  1009. // Otherwise, inline it and mark it as rendered
  1010. else
  1011. {
  1012. // Segment Splitter
  1013. _DoSegmentSplitter();
  1014. // Append a PageSegment
  1015. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
  1016. // Write the HTML for an inline image
  1017. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_INLINE_IMAGE1, lstrlen(STR_INLINE_IMAGE1), NULL));
  1018. // Write the CID
  1019. IF_FAILEXIT(hr = pSegment->pStream->Write(szCID, lstrlen(szCID), NULL));
  1020. // Write the HTML for an inline image
  1021. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_INLINE_IMAGE2, lstrlen(STR_INLINE_IMAGE2), NULL));
  1022. // Rewind the stream
  1023. IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
  1024. // Link Segment into list...
  1025. _VAppendSegment(pSegment);
  1026. // Don't Free It
  1027. pSegment = NULL;
  1028. // Report that some data is available
  1029. m_pRequest->OnBindingDataAvailable();
  1030. // Mark the node as rendered
  1031. rVariant.vt = VT_UI4;
  1032. rVariant.ulVal = TRUE;
  1033. // Set the Property
  1034. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
  1035. SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
  1036. // Basically, we rendered this body
  1037. FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
  1038. // Basically, we rendered this body
  1039. goto exit;
  1040. }
  1041. }
  1042. }
  1043. // If we got here, we didn't inline the iamge
  1044. hr = E_FAIL;
  1045. exit:
  1046. // Cleanup
  1047. SafeMemFree(pszFile);
  1048. if (pSegment)
  1049. _VFreeSegment(pSegment);
  1050. // Done
  1051. return hr;
  1052. }
  1053. // --------------------------------------------------------------------------------
  1054. // CMessageWebPage::OnBodyBoundToTree
  1055. // --------------------------------------------------------------------------------
  1056. HRESULT CMessageWebPage::OnBodyBoundToTree(LPMESSAGETREE pTree, LPTREENODEINFO pNode)
  1057. {
  1058. // Locals
  1059. HRESULT hr=S_OK;
  1060. LPSTR pszStart=NULL;
  1061. LPSTR pszType=NULL;
  1062. PROPVARIANT Variant;
  1063. RESOLVEURLINFO rInfo;
  1064. // Tracing
  1065. TraceCall("CMessageWebPage::OnBodyBoundToTree");
  1066. // Invalid Args
  1067. Assert(pTree && pNode && BINDSTATE_COMPLETE == pNode->bindstate);
  1068. // Thread Safety
  1069. EnterCriticalSection(&m_cs);
  1070. // Set Variant
  1071. Variant.vt = VT_UI4;
  1072. Variant.ulVal = FALSE;
  1073. // Remove PID_ATT_RENDERED and PID_ATT_AUTOINLINED Properties
  1074. pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &Variant);
  1075. pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &Variant);
  1076. // If pNode is a multipart...
  1077. if (S_OK == pNode->pContainer->IsContentType(STR_CNT_MULTIPART, NULL))
  1078. {
  1079. // Alternative
  1080. if (S_OK == pNode->pContainer->IsContentType(NULL, STR_SUB_ALTERNATIVE))
  1081. {
  1082. // Bound multipart/alternative and non of its bodies got displayed on the web page
  1083. if (FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE))
  1084. {
  1085. // Loop through this multipart's alternative bodies...
  1086. for (LPTREENODEINFO pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
  1087. {
  1088. // text/plain -> text/html
  1089. if (S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, NULL))
  1090. {
  1091. // Inline the body
  1092. IF_FAILEXIT(hr = _InlineTextBody(pTree, pChild, TRUE));
  1093. // Done
  1094. break;
  1095. }
  1096. }
  1097. }
  1098. }
  1099. }
  1100. // Otherwise, non-multipart body
  1101. else
  1102. {
  1103. // If in multipart/mixed or not in a multipart
  1104. if (NULL == pNode->pParent ||
  1105. S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_MIXED) ||
  1106. S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, "report"))
  1107. {
  1108. // Try to inline as an image...
  1109. if (FAILED(_InlineImageBody(pTree, pNode)))
  1110. {
  1111. // If is inline body
  1112. if (S_FALSE == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_ATTACHMENT, FALSE, FALSE) || ISFLAGSET(pNode->dwState, NODESTATE_AUTOATTACH))
  1113. {
  1114. // Inline the body
  1115. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, FALSE));
  1116. }
  1117. }
  1118. }
  1119. // Otheriwse, is pNode inside of a multipart/related section
  1120. else if (S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_RELATED))
  1121. {
  1122. // If we haven't rendered a body for this multipart/related section yet ?
  1123. if (FALSE == ISFLAGSET(pNode->pParent->dwState, NODESTATE_ONWEBPAGE))
  1124. {
  1125. // Get the start parameter from pNode->pParent
  1126. if (SUCCEEDED(pNode->pParent->pContainer->GetProp(STR_PAR_START, &pszStart)))
  1127. {
  1128. // Setup Resolve URL Info
  1129. rInfo.pszInheritBase = NULL;
  1130. rInfo.pszBase = NULL;
  1131. rInfo.pszURL = pszStart;
  1132. rInfo.fIsCID = TRUE;
  1133. // See if pNode's Content-Id matches this...
  1134. if (SUCCEEDED(pNode->pContainer->HrResolveURL(&rInfo)))
  1135. {
  1136. // Inline the body
  1137. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1138. }
  1139. }
  1140. // Otherwise, fetch the type parameter
  1141. else if (SUCCEEDED(pNode->pParent->pContainer->GetProp(STR_PAR_TYPE, &pszType)))
  1142. {
  1143. // Is this the type ?
  1144. if (S_OK == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTTYPE), pszType, FALSE, FALSE))
  1145. {
  1146. // Inline the body
  1147. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1148. }
  1149. }
  1150. // Otherwise, if this is the first body in the multipart/related section
  1151. else if (pNode == pNode->pParent->pChildHead)
  1152. {
  1153. // Inline the body
  1154. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1155. }
  1156. }
  1157. }
  1158. // Otheriwse, is pNode inside of a multipart/alternative section
  1159. else if (S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE))
  1160. {
  1161. // If we haven't rendered a body for this multipart/related section yet ?
  1162. if (FALSE == ISFLAGSET(pNode->pParent->dwState, NODESTATE_ONWEBPAGE))
  1163. {
  1164. // Is there are start parameter ?
  1165. if (pNode->pParent->pParent)
  1166. {
  1167. // Is multipart/related ?
  1168. if (S_OK == pNode->pParent->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_RELATED))
  1169. {
  1170. // Get the Start Parameter
  1171. pNode->pParent->pParent->pContainer->GetProp(STR_PAR_START, &pszStart);
  1172. }
  1173. }
  1174. // Something that is not marked as an attachment ?
  1175. if (S_FALSE == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_ATTACHMENT, FALSE, FALSE))
  1176. {
  1177. // Try to inline ?
  1178. BOOL fTryToInline = TRUE;
  1179. // If there is a start parameter and this nod'es Content-Id is equal to start...
  1180. if (pszStart)
  1181. {
  1182. // Setup Resolve URL Info
  1183. rInfo.pszInheritBase = NULL;
  1184. rInfo.pszBase = NULL;
  1185. rInfo.pszURL = pszStart;
  1186. rInfo.fIsCID = TRUE;
  1187. // See if pNode's Content-Id matches this...
  1188. if (!SUCCEEDED(pNode->pContainer->HrResolveURL(&rInfo)))
  1189. fTryToInline = FALSE;
  1190. }
  1191. // Try to inline
  1192. if (fTryToInline)
  1193. {
  1194. // If we are rendering HTML
  1195. if (ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
  1196. {
  1197. // If this body is HTML
  1198. if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_HTML))
  1199. {
  1200. // Inline the body
  1201. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1202. }
  1203. // We can convert text/enriched to html
  1204. else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_ENRICHED))
  1205. {
  1206. // Inline the body
  1207. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1208. }
  1209. }
  1210. // Otherwise, we are rendering plain text, and this body is plain text
  1211. else if (FALSE == ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
  1212. {
  1213. // Is text/*
  1214. if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN))
  1215. {
  1216. // Inline the body
  1217. IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
  1218. }
  1219. }
  1220. }
  1221. }
  1222. }
  1223. }
  1224. }
  1225. exit:
  1226. // Thread Safety
  1227. LeaveCriticalSection(&m_cs);
  1228. // Cleanup
  1229. SafeMemFree(pszStart);
  1230. SafeMemFree(pszType);
  1231. // Done
  1232. return hr;
  1233. }
  1234. // --------------------------------------------------------------------------------
  1235. // CMessageWebPage::_DoAttachmentLinks
  1236. // --------------------------------------------------------------------------------
  1237. HRESULT CMessageWebPage::_DoAttachmentLinks(LPMESSAGETREE pTree)
  1238. {
  1239. // Locals
  1240. HRESULT hr=S_OK;
  1241. LPHBODY prghAttach=NULL;
  1242. CHAR szRes[256];
  1243. LPPAGESEGMENT pSegment=NULL;
  1244. CHAR szCID[CCHMAX_CID];
  1245. LPTREENODEINFO pNode;
  1246. LPSTR pszDisplay=NULL;
  1247. DWORD cAttach;
  1248. DWORD i;
  1249. // Tracing
  1250. TraceCall("CMessageWebPage::_DoAttachmentLinks");
  1251. // Get all the un-rendered stuff from the message
  1252. IF_FAILEXIT(hr = pTree->GetAttachments(&cAttach, &prghAttach));
  1253. // No Attachments
  1254. if (0 == cAttach)
  1255. {
  1256. hr = E_FAIL;
  1257. goto exit;
  1258. }
  1259. // Append a PageSegment
  1260. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
  1261. // Load Attachment Title
  1262. LoadString(g_hLocRes, idsAttachTitleBegin, szRes, ARRAYSIZE(szRes));
  1263. // Write the HTML for the attachment section title...
  1264. IF_FAILEXIT(hr = pSegment->pStream->Write(szRes, lstrlen(szRes), NULL));
  1265. // Loop through the Attachments
  1266. for (i=0; i<cAttach; i++)
  1267. {
  1268. // Get the Node
  1269. pNode = pTree->_PNodeFromHBody(prghAttach[i]);
  1270. // Should not already be on the web page
  1271. Assert(!ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE) && !ISFLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW));
  1272. // Get the display name
  1273. IF_FAILEXIT(hr = pNode->pBody->GetDisplayName(&pszDisplay));
  1274. // Generate a Content-Id for this body
  1275. IF_FAILEXIT(hr = _SetContentId(pNode, szCID, CCHMAX_CID));
  1276. // Write the HTML for a bulleted attachment
  1277. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_BEGIN, lstrlen(STR_ATTACH_BEGIN), NULL));
  1278. // Write the Content-Id
  1279. IF_FAILEXIT(hr = pSegment->pStream->Write(szCID, lstrlen(szCID), NULL));
  1280. // Write the HTML for a bulleted attachment
  1281. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_MIDDLE, lstrlen(STR_ATTACH_MIDDLE), NULL));
  1282. // Write the friendly name
  1283. IF_FAILEXIT(hr = pSegment->pStream->Write(pszDisplay, lstrlen(pszDisplay), NULL));
  1284. // Write the HTML for a bulleted attachment
  1285. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_END, lstrlen(STR_ATTACH_END), NULL));
  1286. // Cleanup
  1287. SafeMemFree(pszDisplay);
  1288. // This node is on the webpage
  1289. FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
  1290. }
  1291. // Write the HTML for the attachment title end
  1292. IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_TITLE_END, lstrlen(STR_ATTACH_TITLE_END), NULL));
  1293. // Rewind the stream
  1294. IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
  1295. // Link Segment into list...
  1296. _VAppendSegment(pSegment);
  1297. // Don't Free It
  1298. pSegment = NULL;
  1299. // Report that some data is available
  1300. m_pRequest->OnBindingDataAvailable();
  1301. exit:
  1302. // Cleanup
  1303. SafeMemFree(prghAttach);
  1304. if (pSegment)
  1305. _VFreeSegment(pSegment);
  1306. // Done
  1307. return hr;
  1308. }
  1309. // --------------------------------------------------------------------------------
  1310. // CMessageWebPage::_DoSlideShow
  1311. // --------------------------------------------------------------------------------
  1312. HRESULT CMessageWebPage::_DoSlideShow(LPMESSAGETREE pTree)
  1313. {
  1314. // Locals
  1315. HRESULT hr=S_OK;
  1316. ULONG i;
  1317. LPTREENODEINFO pNode;
  1318. LPPAGESEGMENT pSegment=NULL;
  1319. CHAR szSlideEnd[255];
  1320. IStream *pStmHtmlW=NULL;
  1321. LPSTR pszValueA=NULL;
  1322. LPWSTR pszValueW=NULL;
  1323. // Tracing
  1324. TraceCall("CMessageWebPage::_DoSlideShow");
  1325. // Invalid Arg
  1326. Assert(pTree);
  1327. // No Slides
  1328. if (0 == m_cSlideShow)
  1329. return S_OK;
  1330. // Load the inline HTML
  1331. IF_FAILEXIT(hr = HrLoadStreamFileFromResourceW(GetACP(), "inline.htm", &pStmHtmlW));
  1332. // Walk through all the nodes and get the things that are marked for the slide show
  1333. for (i=0; i<pTree->m_rTree.cNodes; i++)
  1334. {
  1335. // Get the node
  1336. pNode = pTree->m_rTree.prgpNode[i];
  1337. if (NULL == pNode)
  1338. continue;
  1339. // If not marked NODESTATE_INSLIDESHOW
  1340. if (FALSE == ISFLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW))
  1341. continue;
  1342. // Append the ssimage
  1343. IF_FAILEXIT(hr = pStmHtmlW->Write(STR_SLIDEIMG_BEGIN, lstrlenW(STR_SLIDEIMG_BEGIN) * sizeof(WCHAR), NULL));
  1344. // Get the ContentId
  1345. IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_HDR_CNTID), &pszValueA));
  1346. // Convert to Unicode
  1347. IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), pszValueA));
  1348. // Append the Content-ID
  1349. IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
  1350. // Free pszValue
  1351. SafeMemFree(pszValueA);
  1352. SafeMemFree(pszValueW);
  1353. // Append the separator
  1354. IF_FAILEXIT(hr = pStmHtmlW->Write(STR_QUOTECOMMASPACEQUOTE, lstrlenW(STR_QUOTECOMMASPACEQUOTE) * sizeof(WCHAR), NULL));
  1355. // Get the Display Name
  1356. IF_FAILEXIT(hr = pNode->pBody->GetDisplayName(&pszValueA));
  1357. // Convert to Unicode
  1358. IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), pszValueA));
  1359. // Append the Display Name
  1360. IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
  1361. // Free pszValue
  1362. SafeMemFree(pszValueA);
  1363. SafeMemFree(pszValueW);
  1364. // Append the separator
  1365. IF_FAILEXIT(hr = pStmHtmlW->Write(STR_QUOTEPARASEMI, lstrlenW(STR_QUOTEPARASEMI) * sizeof(WCHAR), NULL));
  1366. }
  1367. // Format the Ending String
  1368. wnsprintf(szSlideEnd, ARRAYSIZE(szSlideEnd), "g_dwTimeOutSec=%d\r\n</SCRIPT>\r\n", (m_rOptions.dwDelay / 1000));
  1369. // Convert to Unicode
  1370. IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), szSlideEnd));
  1371. // Append the separator
  1372. IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
  1373. // Rewind the stream
  1374. IF_FAILEXIT(hr = HrRewindStream(pStmHtmlW));
  1375. // Append a PageSegment
  1376. IF_FAILEXIT(hr = _AllocateSegment(&pSegment, FALSE));
  1377. // Now we have a unicode stream, we have to convert back to internet charset for rootstream
  1378. IF_FAILEXIT(hr = HrIStreamWToInetCset(pStmHtmlW, m_hCharset, &pSegment->pStream));
  1379. // Rewind the stream
  1380. IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
  1381. // Link Segment into list...
  1382. _VAppendSegment(pSegment);
  1383. // Don't Free It
  1384. pSegment = NULL;
  1385. // Report that some data is available
  1386. m_pRequest->OnBindingDataAvailable();
  1387. exit:
  1388. // Cleanup
  1389. SafeMemFree(pszValueA);
  1390. SafeMemFree(pszValueW);
  1391. SafeRelease(pStmHtmlW);
  1392. if (pSegment)
  1393. _VFreeSegment(pSegment);
  1394. // Done
  1395. return hr;
  1396. }
  1397. // --------------------------------------------------------------------------------
  1398. // CMessageWebPage::OnBindComplete
  1399. // --------------------------------------------------------------------------------
  1400. HRESULT CMessageWebPage::OnBindComplete(LPMESSAGETREE pTree)
  1401. {
  1402. // Locals
  1403. HRESULT hr=S_OK;
  1404. // Tracing
  1405. TraceCall("CMessageWebPage::OnBindComplete");
  1406. // Thread Safety
  1407. EnterCriticalSection(&m_cs);
  1408. // We Better have a Request
  1409. Assert(pTree && m_pRequest && FALSE == m_fComplete);
  1410. // Attachment Links ?
  1411. if (ISFLAGSET(m_rOptions.dwFlags, WPF_ATTACHLINKS))
  1412. _DoAttachmentLinks(pTree);
  1413. // Slide Show ?
  1414. if (ISFLAGSET(m_rOptions.dwFlags, WPF_SLIDESHOW))
  1415. _DoSlideShow(pTree);
  1416. // Complete
  1417. m_fComplete = TRUE;
  1418. // Tell the Request That we are done
  1419. m_pRequest->OnBindingComplete(S_OK);
  1420. // Release the Request
  1421. SafeRelease(m_pRequest);
  1422. // Thread Safety
  1423. LeaveCriticalSection(&m_cs);
  1424. // Done
  1425. return hr;
  1426. }
  1427. // --------------------------------------------------------------------------------
  1428. // CMessageWebPage::OnWebPageSplitter
  1429. // --------------------------------------------------------------------------------
  1430. STDMETHODIMP CMessageWebPage::OnWebPageSplitter(DWORD cInlined, IStream *pStream)
  1431. {
  1432. // Locals
  1433. HRESULT hr=S_OK;
  1434. // Tracing
  1435. TraceCall("CMessageWebPage::OnWebPageSplitter");
  1436. // I'm going to put a horizontal line between each segment
  1437. if (cInlined > 0)
  1438. {
  1439. // Write STR_METATAG_PREFIX
  1440. IF_FAILEXIT(hr = pStream->Write(STR_SEGMENT_SPLIT, lstrlen(STR_SEGMENT_SPLIT), NULL));
  1441. }
  1442. // Otherwise, I did nothing
  1443. else
  1444. hr = S_FALSE;
  1445. exit:
  1446. // Done
  1447. return hr;
  1448. }