Windows2003-3790/inetcore/outlookexpress/inetcomm/mimeole/webpage.cpp
2020-09-30 16:53:55 +02:00

1772 lines
56 KiB
C++

// --------------------------------------------------------------------------------
// WebPage.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "mhtmlurl.h"
#include "webpage.h"
#include "vstream.h"
#include "booktree.h"
#include "strconst.h"
#include "containx.h"
#include "bookbody.h"
#include "mimeapi.h"
#include "plainstm.h"
#include "mimeutil.h"
#include "symcache.h"
#include "dllmain.h"
#include "internat.h"
#include "shlwapi.h"
#include "shlwapip.h"
#include "enriched.h"
#include "resource.h"
//#include "util.h"
#include "demand.h"
// From Util.h
HRESULT HrLoadStreamFileFromResourceW(ULONG uCodePage, LPCSTR lpszResourceName, LPSTREAM *ppstm);
// --------------------------------------------------------------------------------
// CMessageWebPage::CMessageWebPage
// --------------------------------------------------------------------------------
CMessageWebPage::CMessageWebPage(LPURLREQUEST pRequest) : m_pRequest(pRequest)
{
TraceCall("CMessageWebPage::CMessageWebPage");
Assert(m_pRequest);
m_cRef = 1;
m_pCallback = NULL;
m_pRequest->AddRef();
m_pHeadSegment = NULL;
m_pTailSegment = NULL;
m_pCurrSegment = NULL;
m_fComplete = FALSE;
m_cInline = 0;
m_cbOffset = 0;
m_cSlideShow = 0;
ZeroMemory(&m_rOptions, sizeof(WEBPAGEOPTIONS));
InitializeCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageWebPage::~CMessageWebPage
// --------------------------------------------------------------------------------
CMessageWebPage::~CMessageWebPage(void)
{
TraceCall("CMessageWebPage::~CMessageWebPage");
Assert(m_pRequest == NULL);
_VFreeSegmentList();
if (m_pCallback && m_pCallback != this)
m_pCallback->Release();
DeleteCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageWebPage::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageWebPage::QueryInterface(REFIID riid, LPVOID *ppv)
{
// Locals
HRESULT hr=S_OK;
// Tracing
TraceCall("CMessageWebPage::QueryInterface");
// check params
if (ppv == NULL)
return TrapError(E_INVALIDARG);
// Find IID
if (IID_IUnknown == riid)
*ppv = (IUnknown *)(IStream *)this;
else if (IID_IStream == riid)
*ppv = (IStream *)this;
else if (IID_IMimeMessageCallback == riid)
*ppv = (IMimeMessageCallback *)this;
else
{
*ppv = NULL;
hr = TrapError(E_NOINTERFACE);
goto exit;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CMessageWebPage::AddRef(void)
{
TraceCall("CMessageWebPage::AddRef");
return InterlockedIncrement(&m_cRef);
}
// --------------------------------------------------------------------------------
// CMessageWebPage::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CMessageWebPage::Release(void)
{
TraceCall("CMessageWebPage::Release");
LONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
delete this;
return (ULONG)cRef;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::Read
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageWebPage::Read(LPVOID pvData, ULONG cbData, ULONG *pcbRead)
{
// Locals
HRESULT hr=S_OK;
ULONG cbLeft=cbData;
ULONG cbRead=0;
ULONG cbSegmentRead;
LPPAGESEGMENT pSegment;
// Tracing
TraceCall("CMessageWebPage::Read");
// Invalid Ags
if (NULL == pvData)
return TraceResult(E_INVALIDARG);
// HrInitialize
if (pcbRead)
*pcbRead = 0;
// Thread Safety
EnterCriticalSection(&m_cs);
// Only if there is a current segment
if (m_pCurrSegment)
{
// HrInitialize Segment Loop
while (cbLeft)
{
// Is there data left to read in this segment ?
if (m_pCurrSegment->cbOffset == m_pCurrSegment->cbLength && TRUE == m_pCurrSegment->fLengthKnown)
{
// Are there no more segments ?
if (NULL == m_pCurrSegment->pNext)
break;
// Goto Next Segment
m_pCurrSegment = m_pCurrSegment->pNext;
}
// We should have a stream for the current segment
Assert(m_pCurrSegment->pStream);
// Compute the current position of the stream
#ifdef DEBUG
DWORD cbOffset;
SideAssert(SUCCEEDED(HrGetStreamPos(m_pCurrSegment->pStream, &cbOffset)));
Assert(cbOffset == m_pCurrSegment->cbOffset);
#endif
// If I have computed the length of this item yet?
IF_FAILEXIT(hr = m_pCurrSegment->pStream->Read((LPVOID)((LPBYTE)pvData + cbRead), cbLeft, &cbSegmentRead));
// Increment offset
m_pCurrSegment->cbOffset += cbSegmentRead;
// Compute Global Offset
m_cbOffset += cbSegmentRead;
// Adjust the size of this segment ?
if (m_pCurrSegment->cbOffset > m_pCurrSegment->cbLength)
{
Assert(FALSE == m_pCurrSegment->fLengthKnown);
m_pCurrSegment->cbLength = m_pCurrSegment->cbOffset;
}
// Decrement amount left
cbLeft -= cbSegmentRead;
// Increment Amount Actually Read
cbRead += cbSegmentRead;
// If we read zero...we must have read all the data in this segment
if (0 == cbSegmentRead)
{
Assert(m_pCurrSegment->cbLength == m_pCurrSegment->cbOffset);
m_pCurrSegment->fLengthKnown = TRUE;
}
}
}
// Return Amount Read
if (pcbRead)
*pcbRead = cbRead;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::Seek
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageWebPage::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNew)
{
// Locals
HRESULT hr=S_OK;
DWORD cbOffsetNew;
DWORD cbSize=0xffffffff;
// Tracing
TraceCall("CMessageWebPage::Seek");
// Invalid Args
Assert(dlibMove.HighPart == 0);
// Thread Safety
EnterCriticalSection(&m_cs);
// Relative to the beginning of the stream
if (STREAM_SEEK_SET == dwOrigin)
{
// If less than zero, its an error
// if (dlibMove.LowPart < 0)
// {
// hr = TraceResult(E_FAIL);
// goto exit;
// }
// else
// Otherwise, if past current offset...
if (dlibMove.LowPart > m_cbOffset)
{
// If not finished binding, return E_PENDING
if (FALSE == m_fComplete)
{
hr = TraceResult(E_PENDING);
goto exit;
}
// Compute Size of the Entire Stream
IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
// If past end of stream, error
if (dlibMove.LowPart > cbSize)
{
hr = TraceResult(E_FAIL);
goto exit;
}
}
// Set new offset
cbOffsetNew = (DWORD)dlibMove.LowPart;
}
// Relative to current offset
else if (STREAM_SEEK_CUR == dwOrigin)
{
// If less than zero, and absolute is greater than its an error
if ( (DWORD)(0 - dlibMove.LowPart) > m_cbOffset)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Otherwise, if past current offset...
else if (m_cbOffset + dlibMove.LowPart > m_cbOffset)
{
// If not finished binding, return E_PENDING
if (FALSE == m_fComplete)
{
hr = TraceResult(E_PENDING);
goto exit;
}
// Compute Size of the Entire Stream
IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
// If past end of stream, error
if (dlibMove.LowPart > cbSize)
{
hr = TraceResult(E_FAIL);
goto exit;
}
}
// Set new offset
cbOffsetNew = m_cbOffset + dlibMove.LowPart;
}
// Relative to the end of the stream
else if (STREAM_SEEK_END == dwOrigin)
{
// If not finished binding, return E_PENDING
if (FALSE == m_fComplete)
{
hr = TraceResult(E_PENDING);
goto exit;
}
// Compute Size of the Entire Stream
IF_FAILEXIT(hr = _ComputeStreamSize(&cbSize));
// If negative or greater than size, its an error
if ( (DWORD)dlibMove.LowPart > cbSize)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Set new offset
cbOffsetNew = cbSize - dlibMove.LowPart;
}
// Otherwise, its an error
else
{
hr = TraceResult(STG_E_INVALIDFUNCTION);
goto exit;
}
// Only if a change
if (m_cbOffset != cbOffsetNew)
{
// New offset greater than size...
m_cbOffset = cbOffsetNew;
// Walk through the segments
for (m_pCurrSegment=m_pHeadSegment; m_pCurrSegment!=NULL; m_pCurrSegment=m_pCurrSegment->pNext)
{
// Never Seeks beyond length
Assert(FALSE == m_pCurrSegment->fLengthKnown ? cbOffsetNew <= m_pCurrSegment->cbLength : TRUE);
// Offset falls into this segment ?
if (cbOffsetNew <= m_pCurrSegment->cbLength)
{
// Set Offset within m_pCurrSegment->pStream
m_pCurrSegment->cbOffset = cbOffsetNew;
// Should have a stream
Assert(m_pCurrSegment->pStream);
// Seek the stream
IF_FAILEXIT(hr = HrStreamSeekSet(m_pCurrSegment->pStream, m_pCurrSegment->cbOffset));
// Reset the Offsets of the remaining segments
for (LPPAGESEGMENT pSegment=m_pCurrSegment->pNext; pSegment!=NULL; pSegment=pSegment->pNext)
{
// At 0
pSegment->cbOffset = 0;
// Seek the stream
IF_FAILEXIT(hr = HrStreamSeekSet(pSegment->pStream, 0));
}
// Done
break;
}
// Otherwise, seek the stream to the end offset / length
else
{
// Must know the length
Assert(m_pCurrSegment->fLengthKnown);
// Set Offset
m_pCurrSegment->cbOffset = m_pCurrSegment->cbLength;
// Seek the stream
IF_FAILEXIT(hr = HrStreamSeekSet(m_pCurrSegment->pStream, m_pCurrSegment->cbOffset));
}
// Decrement cbOffsetNew
cbOffsetNew -= m_pCurrSegment->cbLength;
}
}
// Return Position
if (plibNew)
{
plibNew->HighPart = 0;
plibNew->LowPart = (LONG)m_cbOffset;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_ComputeStreamSize
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_ComputeStreamSize(LPDWORD pcbSize)
{
// Locals
HRESULT hr=S_OK;
LPPAGESEGMENT pCurrSegment;
// Tracing
TraceCall("CMessageWebPage::_ComputeStreamSize");
// Invalid Args
Assert(pcbSize && m_fComplete);
// Initialize
*pcbSize = 0;
// Walk through the segments
for (pCurrSegment=m_pHeadSegment; pCurrSegment!=NULL; pCurrSegment=pCurrSegment->pNext)
{
// If length is not known, then get its size
if (FALSE == pCurrSegment->fLengthKnown)
{
// There better be a stream
Assert(pCurrSegment->pStream);
// Get the size of the stream
IF_FAILEXIT(hr = HrGetStreamSize(pCurrSegment->pStream, &pCurrSegment->cbLength));
// Set Size is known
pCurrSegment->fLengthKnown = TRUE;
}
// Increment Size
(*pcbSize) += pCurrSegment->cbLength;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_AllocateSegment
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_AllocateSegment(LPPAGESEGMENT *ppSegment, BOOL fCreateStream)
{
// Locals
HRESULT hr=S_OK;
// Invalid Args
Assert(ppSegment);
// Tracing
TraceCall("CMessageWebPage::_AllocateSegment");
// Allocate It
IF_NULLEXIT(*ppSegment = (LPPAGESEGMENT)g_pMalloc->Alloc(sizeof(PAGESEGMENT)));
// Zero
ZeroMemory(*ppSegment, sizeof(PAGESEGMENT));
// Create a Stream ?
if (fCreateStream)
{
// Allocate a Stream
IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&(*ppSegment)->pStream));
}
exit:
// Failure ?
if (FAILED(hr) && *ppSegment != NULL)
{
SafeRelease((*ppSegment)->pStream);
SafeMemFree((*ppSegment));
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_VAppendSegment
// --------------------------------------------------------------------------------
void CMessageWebPage::_VAppendSegment(LPPAGESEGMENT pSegment)
{
// Invalid Args
Assert(pSegment);
// Tracing
TraceCall("CMessageWebPage::_VAppendSegment");
// Head is Null
if (NULL == m_pHeadSegment)
{
Assert(NULL == m_pTailSegment);
m_pCurrSegment = m_pHeadSegment = m_pTailSegment = pSegment;
}
// Otherwise, append to tail
else
{
Assert(m_pTailSegment);
m_pTailSegment->pNext = pSegment;
pSegment->pPrev = m_pTailSegment;
m_pTailSegment = pSegment;
}
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_VFreeSegmentList
// --------------------------------------------------------------------------------
void CMessageWebPage::_VFreeSegmentList(void)
{
// Locals
LPPAGESEGMENT pCurr;
LPPAGESEGMENT pNext;
// Tracing
TraceCall("CMessageWebPage::_VFreeSegmentList");
// HrInitialize Curr
pCurr = m_pHeadSegment;
// Loop
while(pCurr)
{
// Set pNext
pNext = pCurr->pNext;
// Free This One
_VFreeSegment(pCurr);
// Goto Next
pCurr = pNext;
}
// Set Head and Tail
m_pHeadSegment = m_pTailSegment = NULL;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_VFreeSegment
// --------------------------------------------------------------------------------
void CMessageWebPage::_VFreeSegment(LPPAGESEGMENT pSegment)
{
TraceCall("CMessageWebPage::_VFreeSegment");
SafeRelease(pSegment->pStream);
g_pMalloc->Free(pSegment);
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_VInitializeCharacterSet
// --------------------------------------------------------------------------------
void CMessageWebPage::_VInitializeCharacterSet(LPMESSAGETREE pTree)
{
// Locals
HRESULT hr=S_OK;
INETCSETINFO rCharset;
DWORD dwCodePage=0;
HCHARSET hCharset;
// Tracing
TraceCall("CMessageWebPage::_VInitializeCharacterSet");
// Get the Character Set
pTree->GetCharset(&m_hCharset);
// Raid-47838: Nav4 message in iso-2022-jp causes initialization error
if (NULL == m_hCharset)
{
// Get the default character set
if (SUCCEEDED(g_pInternat->GetDefaultCharset(&hCharset)))
m_hCharset = hCharset;
}
#ifdef BROKEN
// 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
MimeOleGetCharsetInfo(m_hCharset, &rCharset);
// Map Character set
if (rCharset.cpiInternet == 50220 || rCharset.cpiInternet == 50932)
{
// Raid-35230: hard-code to ISO-2022-JP-ESC or ISO-2022-JP-SIO
hCharset = GetJP_ISOControlCharset();
if (hCharset)
m_hCharset = hCharset;
}
#endif
// We better have a charset
Assert(m_hCharset);
// Done
return;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::Initialize
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::Initialize(IMimeMessageCallback *pCallback, LPMESSAGETREE pTree,
LPWEBPAGEOPTIONS pOptions)
{
// Locals
HRESULT hr=S_OK;
INETCSETINFO rCsetInfo;
CODEPAGEINFO rCodePage;
LPSTR pszCharset;
LPPAGESEGMENT pSegment=NULL;
// Tracing
TraceCall("CMessageWebPage::Initialize");
// No Options ?
Assert(pOptions);
// Thread Safety
EnterCriticalSection(&m_cs);
// Better have a request
Assert(m_pRequest);
// No WebPage Callback
if (pCallback)
{
m_pCallback = pCallback;
m_pCallback->AddRef();
}
else
m_pCallback = this;
// Save the Options
CopyMemory(&m_rOptions, pOptions, sizeof(WEBPAGEOPTIONS));
// Remap the Character Set ?
_VInitializeCharacterSet(pTree);
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
// Client wants meta-tag?
if (!ISFLAGSET(m_rOptions.dwFlags, WPF_NOMETACHARSET))
{
// Get the charset information
IF_FAILEXIT(hr = MimeOleGetCharsetInfo(m_hCharset, &rCsetInfo));
// Get the codepage information
IF_FAILEXIT(hr = MimeOleGetCodePageInfo(rCsetInfo.cpiInternet, &rCodePage));
// Set the charset to write into the meta tag
pszCharset = FIsEmpty(rCodePage.szWebCset) ? rCodePage.szBodyCset : rCodePage.szWebCset;
// If Still Empty, use iso-8859-1
if (FIsEmpty(pszCharset))
pszCharset = (LPSTR)STR_ISO88591;
// Write STR_METATAG_PREFIX
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_METATAG_PREFIX, lstrlen(STR_METATAG_PREFIX), NULL));
// Write the Charset
IF_FAILEXIT(hr = pSegment->pStream->Write(pszCharset, lstrlen(pszCharset), NULL));
// Write STR_METATAG_POSTFIX
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_METATAG_POSTFIX, lstrlen(STR_METATAG_POSTFIX), NULL));
}
// Only showing images ?
if (ISFLAGSET(m_rOptions.dwFlags, WPF_IMAGESONLY))
{
// Locals
CHAR szRes[255];
// Load the string
LoadString(g_hLocRes, idsImagesOnly, szRes, ARRAYSIZE(szRes));
// Write idsImagesOnly
IF_FAILEXIT(hr = pSegment->pStream->Write(szRes, lstrlen(szRes), NULL));
}
// Rewind the segment
IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
// Report that some data is available
m_pRequest->OnBindingDataAvailable();
exit:
// Cleanup
if (pSegment)
_VFreeSegment(pSegment);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_GetInlineHtmlStream
// --------------------------------------------------------------------------------
#define CCHMAX_SNIFFER 64
HRESULT CMessageWebPage::_GetInlineHtmlStream(LPMESSAGETREE pTree, LPTREENODEINFO pNode,
LPSTREAM *ppStream)
{
// Locals
HRESULT hr=S_OK;
BOOL fQuote;
IStream *pStmHtml=NULL;
IStream *pStmHtmlW=NULL;
IStream *pStmPlainW=NULL;
IStream *pStmEnriched=NULL;
ULONG cbRead;
LPWSTR pwszType=NULL;
WCHAR wszHeader[CCHMAX_SNIFFER];
CHAR szHeader[CCHMAX_SNIFFER];
// Tracing
TraceCall("CMessageWebPage::_GetInlineHtmlStream");
// Invalid Args
Assert(pTree && pNode && ppStream);
// HrInitialize
*ppStream = NULL;
// text/html ?
if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_HTML))
{
// Just get and return an HTML inetcset encoded stream
IF_FAILEXIT(hr = pNode->pBody->GetData(IET_INETCSET, &pStmHtml));
}
// text/enriched
else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_ENRICHED))
{
// Convert to HTML
IF_FAILEXIT(hr = MimeOleConvertEnrichedToHTMLEx((IMimeBody *)pNode->pBody, IET_INETCSET, &pStmHtml));
}
// text/*
else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, NULL))
{
// Get the data
IF_FAILEXIT(hr = pNode->pBody->GetData(IET_UNICODE, &pStmPlainW));
// Read Off the first 255 bytes
IF_FAILEXIT(hr = pStmPlainW->Read(wszHeader, (CCHMAX_SNIFFER * sizeof(WCHAR)), &cbRead));
// Did we read something
if (cbRead > 0)
{
// Null It
ULONG cchRead = (cbRead / sizeof(WCHAR)) - 1;
// Null It Out
wszHeader[cchRead] = L'\0';
// Convert to ANSI
szHeader[0] = L'\0';
if(WideCharToMultiByte(CP_ACP, 0, wszHeader, -1, szHeader, ARRAYSIZE(szHeader) - 1, NULL, NULL) == 0)
{
IF_FAILEXIT(hr = HrRewindStream(pStmPlainW));
}
else
{
// Lets Read the first "<x-rich>" bytes and see if it might be text/enriched
if (0 == StrCmpI(szHeader, "<x-rich>"))
{
// Convert to HTML
IF_FAILEXIT(hr = MimeOleConvertEnrichedToHTMLEx((IMimeBody *)pNode->pBody, IET_INETCSET, &pStmHtml));
}
// Is this html ?
else if (SUCCEEDED(FindMimeFromData(NULL, NULL, szHeader, cchRead, NULL, NULL, &pwszType, 0)) && pwszType && 0 == StrCmpIW(pwszType, L"text/html"))
{
// Release pStmPlainW
SafeRelease(pStmPlainW);
// Just get and return an HTML inetcset encoded stream
IF_FAILEXIT(hr = pNode->pBody->GetData(IET_INETCSET, &pStmHtml));
}
// Otherwise, rewind pStmPlainW
else
{
// Rewind
IF_FAILEXIT(hr = HrRewindStream(pStmPlainW));
}
}
}
}
// Otheriwse
else
{
hr = S_FALSE;
goto exit;
}
// We have HTML
if (pStmHtml)
{
// Client wants HTML
if (ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
{
// Return the Html Stream
*ppStream = pStmHtml;
pStmHtml = NULL;
goto exit;
}
// Otherwise, client wants plain text
else
{
// Convert to Plain text
IF_FAILEXIT(hr = HrConvertHTMLToFormat(pStmHtml, &pStmPlainW, CF_UNICODETEXT));
}
}
// Otherwise, if I have a plain stream
if (NULL == pStmPlainW)
{
hr = S_FALSE;
goto exit;
}
// Determine if we should quote (Can't quote QP)
fQuote = (IET_QP == pNode->pContainer->GetEncodingType()) ? FALSE : TRUE;
// Convert Unicode Plain stream to HTML
IF_FAILEXIT(hr = HrConvertPlainStreamW(pStmPlainW, fQuote ? m_rOptions.wchQuote : NULL, &pStmHtmlW));
// Convert from unicode back to internet character set
IF_FAILEXIT(hr = HrIStreamWToInetCset(pStmHtmlW, m_hCharset, &pStmHtml));
// Return pStmHtml
*ppStream = pStmHtml;
pStmHtml = NULL;
// #ifdef DEBUG
// WriteStreamToFile(*ppStream, "c:\\dump.htm", CREATE_ALWAYS, GENERIC_WRITE);
// #endif
exit:
// Cleanup
SafeRelease(pStmHtml);
SafeRelease(pStmHtmlW);
SafeRelease(pStmPlainW);
SafeRelease(pStmEnriched);
SafeMemFree(pwszType);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_DoSegmentSplitter
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_DoSegmentSplitter(void)
{
// Locals
HRESULT hr=S_OK;
LPPAGESEGMENT pSegment=NULL;
// Trace
TraceCall("CMessageWebPage::_DoSegmentSplitter");
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
// If more than one inline bodies ?
if (S_OK == m_pCallback->OnWebPageSplitter(m_cInline, pSegment->pStream))
{
// Rewind the stream
HrRewindStream(pSegment->pStream);
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
}
// Otherwise, free this segment
else
{
// Free It
_VFreeSegment(pSegment);
// Done Free it again
pSegment = NULL;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_InlineTextBody
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_InlineTextBody(LPMESSAGETREE pTree, LPTREENODEINFO pNode, BOOL fSetParents)
{
// Locals
HRESULT hr=S_OK;
PROPVARIANT rVariant;
LPSTREAM pStream=NULL;
LPPAGESEGMENT pSegment=NULL;
LPTREENODEINFO pCurrent;
LPSTR pszFileName=NULL;
// Tracing
TraceCall("CMessageWebPage::_InlineTextBody");
// This node better not already be on the webpage
Assert(FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE));
// Handle Text Types that I explicitly will never inline
if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_VCARD))
goto exit;
// Inline the body
if (S_OK != _GetInlineHtmlStream(pTree, pNode, &pStream))
goto exit;
// Setup a Variant
rVariant.vt = VT_LPSTR;
// If this body has a file name, lets also show it as an attachment
if (SUCCEEDED(pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_FILENAME), NOFLAGS, &rVariant)))
{
// Save the File name
pszFileName = rVariant.pszVal;
}
// Only showing images ?
if (FALSE == ISFLAGSET(m_rOptions.dwFlags, WPF_IMAGESONLY))
{
// Segment Split
_DoSegmentSplitter();
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, FALSE));
// Set pStream
pSegment->pStream = pStream;
pSegment->pStream->AddRef();
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
// Report that some data is available
m_pRequest->OnBindingDataAvailable();
// Increment number of inline bodies
m_cInline++;
}
// Mark the node as rendered
rVariant.vt = VT_UI4;
rVariant.ulVal = TRUE;
// If this has a filename
if (pszFileName)
{
// Mark it as auto-inlined
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
}
// Set the Property
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
// We have rendered this node on the webpage
FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
// Set parents are on webpage
if (fSetParents)
{
// Raid-45116: new text attachment contains message body on Communicator inline image message
pCurrent = pNode->pParent;
// Try to find an alternative parent...
while(pCurrent)
{
// If multipart/alternative, walk all of its children and mark them as being rendered
if (S_OK == pCurrent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE))
{
// Get Parent
for (LPTREENODEINFO pChild=pCurrent->pChildHead; pChild!=NULL; pChild=pChild->pNext)
{
// Set Resolve Property
SideAssert(SUCCEEDED(pChild->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
}
}
// Mark as being on the webpage
FLAGSET(pCurrent->dwState, NODESTATE_ONWEBPAGE);
// Get Next Parent
pCurrent = pCurrent->pParent;
}
}
exit:
// Cleanup
SafeRelease(pStream);
SafeMemFree(pszFileName);
if (pSegment)
_VFreeSegment(pSegment);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_SetContentId
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_SetContentId(LPTREENODEINFO pNode, LPSTR pszCID, ULONG cchCID)
{
// Locals
HRESULT hr=S_OK;
LPSTR pszContentId=NULL;
GUID guid;
WCHAR wszGUID[64];
LPSTR pszFile;
LPSTR pszGuid=0;
LPSTR pszT;
// Tracing
TraceCall("CMessageWebPage::_SetContentId");
// See of pNode already has a Content-ID
if (S_FALSE == pNode->pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTID)))
{
// $BUG #64186
// Create a content-id in the form:
// CID:{guid}/<filename>
// so that trident's save-as dialog has a meaningful
// filename to work with
// Create a guid
IF_FAILEXIT(hr = CoCreateGuid(&guid));
// Convert the GUID to a string
if (0 == StringFromGUID2(guid, wszGUID, ARRAYSIZE(wszGUID)))
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Convert to ANSI
pszGuid = PszToANSI(CP_ACP, wszGUID);
if (!pszGuid)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
// [PaulHi] 6/18/99. Raid 76531. Don't append the file name to the GUID ...
// it causes encoding problems with Trident International. In particular the
// DBCS characters in the filename can cause the HTML to contain both JIS and
// SHIFT-JIS encodings. I believe this is a Trident bug because we explicitly
// set the Trident to CP_JAUTODETECT (JIS) and it still performs SHIFT_JIS decoding
// if the attachment filename is long. However, the real fix is to make the entire
// HTML text a single (JIS) encoding, but this is difficult to do because the attachment
// code is single byte (DBCS) which equates to SHIFT-JIS. We need to convert fully to
// Unicode.
INETCSETINFO rCharset;
MimeOleGetCharsetInfo(m_hCharset, &rCharset);
if (rCharset.cpiInternet != CP_JAUTODETECT) // code page 50932
{
// If we have a file-name, append to guid
if (pNode->pContainer->GetProp(PIDTOSTR(STR_ATT_GENFNAME), &pszFile)==S_OK)
{
// Allocate Buffer
DWORD cchSize = (lstrlen(pszFile) + lstrlen(pszGuid) + 2);
pszT = PszAllocA(cchSize);
if (pszT)
{
// Copy contents and free old GUID
wnsprintfA(pszT, cchSize, "%s/%s", pszGuid, pszFile);
MemFree(pszGuid);
pszGuid = pszT;
}
MemFree(pszFile);
}
}
else
{
// @HACK [PaulHi] Preempt any JIS encoding problems by just appending "/.".
// This will allow right-click save image as operation to work without
// the user seeing the URL GUID.
DWORD cchSize = (lstrlen(pszGuid) + 3);
pszT = PszAllocA(cchSize);
if (pszT)
{
// Copy conents and free old GUID
wnsprintfA(pszT, cchSize, "%s/.", pszGuid);
MemFree(pszGuid);
pszGuid = pszT;
}
}
// copy GUID to output buffer
StrCpyNA(pszCID, pszGuid, cchCID);
// Store the content-id into the node
IF_FAILEXIT(hr = pNode->pContainer->SetProp(PIDTOSTR(PID_HDR_CNTID), pszCID));
}
// Otheriwse, get the Content-ID from this body
else
{
// Get the Content-Id
IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_HDR_CNTID), &pszContentId));
// Copy it into pszCID
Assert(lstrlen(pszContentId) <= (LONG)cchCID);
// Copy the cid to the outbound variable
StrCpyN(pszCID, pszContentId, cchCID);
}
exit:
// Cleanup
SafeMemFree(pszContentId);
SafeMemFree(pszGuid);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_InlineImageBody
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_InlineImageBody(LPMESSAGETREE pTree, LPTREENODEINFO pNode)
{
// Locals
HRESULT hr=S_OK;
LPSTR pszFile=NULL;
LPSTR pszExt;
CHAR szCID[CCHMAX_CID + 1];
PROPVARIANT rVariant;
LPPAGESEGMENT pSegment=NULL;
// Tracing
TraceCall("CMessageWebPage::_InlineImageBody");
// This node better not already be on the webpage
Assert(pTree && pNode && FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE));
// Setup the Variant
rVariant.vt = VT_UI4;
// If the body is marked as inline, or autoline attachments is enabled and (slideshow is disabled)
if (S_OK == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_INLINE, FALSE, FALSE) || ISFLAGSET(m_rOptions.dwFlags, WPF_AUTOINLINE))
{
// Get a generated filename from the body
IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_ATT_GENFNAME), &pszFile));
// Look up the file extension of the body
pszExt = PathFindExtension(pszFile);
// Do I support inlining this object ?
if (lstrcmpi(pszExt, c_szBmpExt) == 0 ||
lstrcmpi(pszExt, c_szJpgExt) == 0 ||
lstrcmpi(pszExt, c_szJpegExt) == 0 ||
lstrcmpi(pszExt, c_szGifExt) == 0 ||
lstrcmpi(pszExt, c_szIcoExt) == 0 ||
lstrcmpi(pszExt, c_szWmfExt) == 0 ||
lstrcmpi(pszExt, c_szPngExt) == 0 ||
lstrcmpi(pszExt, c_szEmfExt) == 0 ||
lstrcmpi(pszExt, c_szArtExt) == 0 ||
lstrcmpi(pszExt, c_szXbmExt) == 0)
{
// Generate a Content-Id for this body
IF_FAILEXIT(hr = _SetContentId(pNode, szCID, CCHMAX_CID));
// If the user wants a slide show, then lets mark this body as a slideshow image
if (ISFLAGSET(m_rOptions.dwFlags, WPF_SLIDESHOW))
{
// Mark the node as rendered
rVariant.vt = VT_UI4;
rVariant.ulVal = TRUE;
// Set the Property
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
// Count the number of items in the slide show
m_cSlideShow++;
// This node is in the slide show and will be processed at the end of the rendering
FLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW);
// Basically, we rendered this body
FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
// Done
goto exit;
}
// Otherwise, inline it and mark it as rendered
else
{
// Segment Splitter
_DoSegmentSplitter();
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
// Write the HTML for an inline image
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_INLINE_IMAGE1, lstrlen(STR_INLINE_IMAGE1), NULL));
// Write the CID
IF_FAILEXIT(hr = pSegment->pStream->Write(szCID, lstrlen(szCID), NULL));
// Write the HTML for an inline image
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_INLINE_IMAGE2, lstrlen(STR_INLINE_IMAGE2), NULL));
// Rewind the stream
IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
// Report that some data is available
m_pRequest->OnBindingDataAvailable();
// Mark the node as rendered
rVariant.vt = VT_UI4;
rVariant.ulVal = TRUE;
// Set the Property
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &rVariant)));
SideAssert(SUCCEEDED(pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &rVariant)));
// Basically, we rendered this body
FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
// Basically, we rendered this body
goto exit;
}
}
}
// If we got here, we didn't inline the iamge
hr = E_FAIL;
exit:
// Cleanup
SafeMemFree(pszFile);
if (pSegment)
_VFreeSegment(pSegment);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::OnBodyBoundToTree
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::OnBodyBoundToTree(LPMESSAGETREE pTree, LPTREENODEINFO pNode)
{
// Locals
HRESULT hr=S_OK;
LPSTR pszStart=NULL;
LPSTR pszType=NULL;
PROPVARIANT Variant;
RESOLVEURLINFO rInfo;
// Tracing
TraceCall("CMessageWebPage::OnBodyBoundToTree");
// Invalid Args
Assert(pTree && pNode && BINDSTATE_COMPLETE == pNode->bindstate);
// Thread Safety
EnterCriticalSection(&m_cs);
// Set Variant
Variant.vt = VT_UI4;
Variant.ulVal = FALSE;
// Remove PID_ATT_RENDERED and PID_ATT_AUTOINLINED Properties
pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_RENDERED), 0, &Variant);
pNode->pContainer->SetProp(PIDTOSTR(PID_ATT_AUTOINLINED), 0, &Variant);
// If pNode is a multipart...
if (S_OK == pNode->pContainer->IsContentType(STR_CNT_MULTIPART, NULL))
{
// Alternative
if (S_OK == pNode->pContainer->IsContentType(NULL, STR_SUB_ALTERNATIVE))
{
// Bound multipart/alternative and non of its bodies got displayed on the web page
if (FALSE == ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE))
{
// Loop through this multipart's alternative bodies...
for (LPTREENODEINFO pChild=pNode->pChildHead; pChild!=NULL; pChild=pChild->pNext)
{
// text/plain -> text/html
if (S_OK == pChild->pContainer->IsContentType(STR_CNT_TEXT, NULL))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pChild, TRUE));
// Done
break;
}
}
}
}
}
// Otherwise, non-multipart body
else
{
// If in multipart/mixed or not in a multipart
if (NULL == pNode->pParent ||
S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_MIXED) ||
S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, "report"))
{
// Try to inline as an image...
if (FAILED(_InlineImageBody(pTree, pNode)))
{
// If is inline body
if (S_FALSE == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_ATTACHMENT, FALSE, FALSE) || ISFLAGSET(pNode->dwState, NODESTATE_AUTOATTACH))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, FALSE));
}
}
}
// Otheriwse, is pNode inside of a multipart/related section
else if (S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_RELATED))
{
// If we haven't rendered a body for this multipart/related section yet ?
if (FALSE == ISFLAGSET(pNode->pParent->dwState, NODESTATE_ONWEBPAGE))
{
// Get the start parameter from pNode->pParent
if (SUCCEEDED(pNode->pParent->pContainer->GetProp(STR_PAR_START, &pszStart)))
{
// Setup Resolve URL Info
rInfo.pszInheritBase = NULL;
rInfo.pszBase = NULL;
rInfo.pszURL = pszStart;
rInfo.fIsCID = TRUE;
// See if pNode's Content-Id matches this...
if (SUCCEEDED(pNode->pContainer->HrResolveURL(&rInfo)))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
}
// Otherwise, fetch the type parameter
else if (SUCCEEDED(pNode->pParent->pContainer->GetProp(STR_PAR_TYPE, &pszType)))
{
// Is this the type ?
if (S_OK == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTTYPE), pszType, FALSE, FALSE))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
}
// Otherwise, if this is the first body in the multipart/related section
else if (pNode == pNode->pParent->pChildHead)
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
}
}
// Otheriwse, is pNode inside of a multipart/alternative section
else if (S_OK == pNode->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_ALTERNATIVE))
{
// If we haven't rendered a body for this multipart/related section yet ?
if (FALSE == ISFLAGSET(pNode->pParent->dwState, NODESTATE_ONWEBPAGE))
{
// Is there are start parameter ?
if (pNode->pParent->pParent)
{
// Is multipart/related ?
if (S_OK == pNode->pParent->pParent->pContainer->IsContentType(STR_CNT_MULTIPART, STR_SUB_RELATED))
{
// Get the Start Parameter
pNode->pParent->pParent->pContainer->GetProp(STR_PAR_START, &pszStart);
}
}
// Something that is not marked as an attachment ?
if (S_FALSE == pNode->pContainer->QueryProp(PIDTOSTR(PID_HDR_CNTDISP), STR_DIS_ATTACHMENT, FALSE, FALSE))
{
// Try to inline ?
BOOL fTryToInline = TRUE;
// If there is a start parameter and this nod'es Content-Id is equal to start...
if (pszStart)
{
// Setup Resolve URL Info
rInfo.pszInheritBase = NULL;
rInfo.pszBase = NULL;
rInfo.pszURL = pszStart;
rInfo.fIsCID = TRUE;
// See if pNode's Content-Id matches this...
if (!SUCCEEDED(pNode->pContainer->HrResolveURL(&rInfo)))
fTryToInline = FALSE;
}
// Try to inline
if (fTryToInline)
{
// If we are rendering HTML
if (ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
{
// If this body is HTML
if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_HTML))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
// We can convert text/enriched to html
else if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_ENRICHED))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
}
// Otherwise, we are rendering plain text, and this body is plain text
else if (FALSE == ISFLAGSET(m_rOptions.dwFlags, WPF_HTML))
{
// Is text/*
if (S_OK == pNode->pContainer->IsContentType(STR_CNT_TEXT, STR_SUB_PLAIN))
{
// Inline the body
IF_FAILEXIT(hr = _InlineTextBody(pTree, pNode, TRUE));
}
}
}
}
}
}
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Cleanup
SafeMemFree(pszStart);
SafeMemFree(pszType);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_DoAttachmentLinks
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_DoAttachmentLinks(LPMESSAGETREE pTree)
{
// Locals
HRESULT hr=S_OK;
LPHBODY prghAttach=NULL;
CHAR szRes[256];
LPPAGESEGMENT pSegment=NULL;
CHAR szCID[CCHMAX_CID];
LPTREENODEINFO pNode;
LPSTR pszDisplay=NULL;
DWORD cAttach;
DWORD i;
// Tracing
TraceCall("CMessageWebPage::_DoAttachmentLinks");
// Get all the un-rendered stuff from the message
IF_FAILEXIT(hr = pTree->GetAttachments(&cAttach, &prghAttach));
// No Attachments
if (0 == cAttach)
{
hr = E_FAIL;
goto exit;
}
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, TRUE));
// Load Attachment Title
LoadString(g_hLocRes, idsAttachTitleBegin, szRes, ARRAYSIZE(szRes));
// Write the HTML for the attachment section title...
IF_FAILEXIT(hr = pSegment->pStream->Write(szRes, lstrlen(szRes), NULL));
// Loop through the Attachments
for (i=0; i<cAttach; i++)
{
// Get the Node
pNode = pTree->_PNodeFromHBody(prghAttach[i]);
// Should not already be on the web page
Assert(!ISFLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE) && !ISFLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW));
// Get the display name
IF_FAILEXIT(hr = pNode->pBody->GetDisplayName(&pszDisplay));
// Generate a Content-Id for this body
IF_FAILEXIT(hr = _SetContentId(pNode, szCID, CCHMAX_CID));
// Write the HTML for a bulleted attachment
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_BEGIN, lstrlen(STR_ATTACH_BEGIN), NULL));
// Write the Content-Id
IF_FAILEXIT(hr = pSegment->pStream->Write(szCID, lstrlen(szCID), NULL));
// Write the HTML for a bulleted attachment
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_MIDDLE, lstrlen(STR_ATTACH_MIDDLE), NULL));
// Write the friendly name
IF_FAILEXIT(hr = pSegment->pStream->Write(pszDisplay, lstrlen(pszDisplay), NULL));
// Write the HTML for a bulleted attachment
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_END, lstrlen(STR_ATTACH_END), NULL));
// Cleanup
SafeMemFree(pszDisplay);
// This node is on the webpage
FLAGSET(pNode->dwState, NODESTATE_ONWEBPAGE);
}
// Write the HTML for the attachment title end
IF_FAILEXIT(hr = pSegment->pStream->Write(STR_ATTACH_TITLE_END, lstrlen(STR_ATTACH_TITLE_END), NULL));
// Rewind the stream
IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
// Report that some data is available
m_pRequest->OnBindingDataAvailable();
exit:
// Cleanup
SafeMemFree(prghAttach);
if (pSegment)
_VFreeSegment(pSegment);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::_DoSlideShow
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::_DoSlideShow(LPMESSAGETREE pTree)
{
// Locals
HRESULT hr=S_OK;
ULONG i;
LPTREENODEINFO pNode;
LPPAGESEGMENT pSegment=NULL;
CHAR szSlideEnd[255];
IStream *pStmHtmlW=NULL;
LPSTR pszValueA=NULL;
LPWSTR pszValueW=NULL;
// Tracing
TraceCall("CMessageWebPage::_DoSlideShow");
// Invalid Arg
Assert(pTree);
// No Slides
if (0 == m_cSlideShow)
return S_OK;
// Load the inline HTML
IF_FAILEXIT(hr = HrLoadStreamFileFromResourceW(GetACP(), "inline.htm", &pStmHtmlW));
// Walk through all the nodes and get the things that are marked for the slide show
for (i=0; i<pTree->m_rTree.cNodes; i++)
{
// Get the node
pNode = pTree->m_rTree.prgpNode[i];
if (NULL == pNode)
continue;
// If not marked NODESTATE_INSLIDESHOW
if (FALSE == ISFLAGSET(pNode->dwState, NODESTATE_INSLIDESHOW))
continue;
// Append the ssimage
IF_FAILEXIT(hr = pStmHtmlW->Write(STR_SLIDEIMG_BEGIN, lstrlenW(STR_SLIDEIMG_BEGIN) * sizeof(WCHAR), NULL));
// Get the ContentId
IF_FAILEXIT(hr = pNode->pContainer->GetProp(PIDTOSTR(PID_HDR_CNTID), &pszValueA));
// Convert to Unicode
IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), pszValueA));
// Append the Content-ID
IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
// Free pszValue
SafeMemFree(pszValueA);
SafeMemFree(pszValueW);
// Append the separator
IF_FAILEXIT(hr = pStmHtmlW->Write(STR_QUOTECOMMASPACEQUOTE, lstrlenW(STR_QUOTECOMMASPACEQUOTE) * sizeof(WCHAR), NULL));
// Get the Display Name
IF_FAILEXIT(hr = pNode->pBody->GetDisplayName(&pszValueA));
// Convert to Unicode
IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), pszValueA));
// Append the Display Name
IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
// Free pszValue
SafeMemFree(pszValueA);
SafeMemFree(pszValueW);
// Append the separator
IF_FAILEXIT(hr = pStmHtmlW->Write(STR_QUOTEPARASEMI, lstrlenW(STR_QUOTEPARASEMI) * sizeof(WCHAR), NULL));
}
// Format the Ending String
wnsprintf(szSlideEnd, ARRAYSIZE(szSlideEnd), "g_dwTimeOutSec=%d\r\n</SCRIPT>\r\n", (m_rOptions.dwDelay / 1000));
// Convert to Unicode
IF_NULLEXIT(pszValueW = PszToUnicode(MimeOleGetWindowsCP(m_hCharset), szSlideEnd));
// Append the separator
IF_FAILEXIT(hr = pStmHtmlW->Write(pszValueW, lstrlenW(pszValueW) * sizeof(WCHAR), NULL));
// Rewind the stream
IF_FAILEXIT(hr = HrRewindStream(pStmHtmlW));
// Append a PageSegment
IF_FAILEXIT(hr = _AllocateSegment(&pSegment, FALSE));
// Now we have a unicode stream, we have to convert back to internet charset for rootstream
IF_FAILEXIT(hr = HrIStreamWToInetCset(pStmHtmlW, m_hCharset, &pSegment->pStream));
// Rewind the stream
IF_FAILEXIT(hr = HrRewindStream(pSegment->pStream));
// Link Segment into list...
_VAppendSegment(pSegment);
// Don't Free It
pSegment = NULL;
// Report that some data is available
m_pRequest->OnBindingDataAvailable();
exit:
// Cleanup
SafeMemFree(pszValueA);
SafeMemFree(pszValueW);
SafeRelease(pStmHtmlW);
if (pSegment)
_VFreeSegment(pSegment);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::OnBindComplete
// --------------------------------------------------------------------------------
HRESULT CMessageWebPage::OnBindComplete(LPMESSAGETREE pTree)
{
// Locals
HRESULT hr=S_OK;
// Tracing
TraceCall("CMessageWebPage::OnBindComplete");
// Thread Safety
EnterCriticalSection(&m_cs);
// We Better have a Request
Assert(pTree && m_pRequest && FALSE == m_fComplete);
// Attachment Links ?
if (ISFLAGSET(m_rOptions.dwFlags, WPF_ATTACHLINKS))
_DoAttachmentLinks(pTree);
// Slide Show ?
if (ISFLAGSET(m_rOptions.dwFlags, WPF_SLIDESHOW))
_DoSlideShow(pTree);
// Complete
m_fComplete = TRUE;
// Tell the Request That we are done
m_pRequest->OnBindingComplete(S_OK);
// Release the Request
SafeRelease(m_pRequest);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageWebPage::OnWebPageSplitter
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageWebPage::OnWebPageSplitter(DWORD cInlined, IStream *pStream)
{
// Locals
HRESULT hr=S_OK;
// Tracing
TraceCall("CMessageWebPage::OnWebPageSplitter");
// I'm going to put a horizontal line between each segment
if (cInlined > 0)
{
// Write STR_METATAG_PREFIX
IF_FAILEXIT(hr = pStream->Write(STR_SEGMENT_SPLIT, lstrlen(STR_SEGMENT_SPLIT), NULL));
}
// Otherwise, I did nothing
else
hr = S_FALSE;
exit:
// Done
return hr;
}