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.
 
 
 
 
 
 

1150 lines
39 KiB

// --------------------------------------------------------------------------------
// Ibdystm.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "ibdystm.h"
#include "dllmain.h"
#include "inetconv.h"
#include "internat.h"
#include "symcache.h"
#include "bookbody.h"
#include "strconst.h"
#include "demand.h"
// --------------------------------------------------------------------------------
// Encoding Type Names
// --------------------------------------------------------------------------------
const ENCODINGMAP g_rgEncodingMap[IET_LAST] = {
{ IET_BINARY, STR_ENC_BINARY, TRUE },
{ IET_BASE64, STR_ENC_BASE64, TRUE },
{ IET_UUENCODE, STR_ENC_UUENCODE, TRUE },
{ IET_QP, STR_ENC_QP, TRUE },
{ IET_7BIT, STR_ENC_7BIT, TRUE },
{ IET_8BIT, STR_ENC_8BIT, TRUE },
{ IET_INETCSET, NULL, FALSE },
{ IET_UNICODE, NULL, FALSE },
{ IET_RFC1522, NULL, FALSE },
{ IET_ENCODED, NULL, FALSE },
{ IET_CURRENT, NULL, FALSE },
{ IET_UNKNOWN, NULL, FALSE },
{ IET_BINHEX40, STR_ENC_BINHEX40, TRUE }
};
// --------------------------------------------------------------------------------
// Document Covnert Map
// --------------------------------------------------------------------------------
const CONVERSIONMAP g_rgConversionMap[IET_LAST] = {
{ DCT_NONE, DCT_ENCODE, DCT_ENCODE, DCT_ENCODE, DCT_ENCODE, DCT_ENCODE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_ENCODE }, // IET_BINARY
{ DCT_DECODE, DCT_NONE, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_DECENC }, // IET_BASE64
{ DCT_DECODE, DCT_DECENC, DCT_NONE, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_DECENC }, // IET_UUENCODE
{ DCT_DECODE, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_DECENC }, // IET_QP
{ DCT_DECODE, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_DECENC }, // IET_7BIT
{ DCT_DECODE, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_DECENC }, // IET_8BIT
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_INETCSET
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_UNICODE
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_RFC1522
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_ENCODED
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_CURRENT
{ DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_UNKNOWN
{ DCT_DECODE, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_DECENC, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE, DCT_NONE }, // IET_BINHEX40
// BINARY BASE64 UUENCODE QP 7BIT 8BIT INETCSET UNICODE RFC1522 ENCODED CURRENT UNKNOWN BINHEX40
};
// --------------------------------------------------------------------------------
// FIsValidBodyEncoding
// --------------------------------------------------------------------------------
BOOL FIsValidBodyEncoding(ENCODINGTYPE ietEncoding)
{
// Try to find the correct body encoding
for (ULONG i=0; i<IET_LAST; i++)
{
// Is this it?
if (ietEncoding == g_rgEncodingMap[i].ietEncoding)
return g_rgEncodingMap[i].fValidBodyEncoding;
}
// Failure
return FALSE;
}
// --------------------------------------------------------------------------------
// CBodyStream::CBodyStream
// --------------------------------------------------------------------------------
CBodyStream::CBodyStream(void)
{
DllAddRef();
m_cRef = 1;
m_pszFileName = NULL;
m_uliIntOffset.QuadPart = 0;
m_uliIntSize.QuadPart = 0;
m_liLastWrite.QuadPart = 0;
m_pLockBytes = NULL;
m_dctConvert = DCT_NONE;
m_pEncoder = NULL;
m_pDecoder = NULL;
InitializeCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CBodyStream::~CBodyStream
// --------------------------------------------------------------------------------
CBodyStream::~CBodyStream(void)
{
SafeMemFree(m_pszFileName);
SafeRelease(m_pLockBytes);
SafeRelease(m_pEncoder);
SafeRelease(m_pDecoder);
DeleteCriticalSection(&m_cs);
DllRelease();
}
// --------------------------------------------------------------------------------
// CBodyStream::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CBodyStream::QueryInterface(REFIID riid, LPVOID *ppv)
{
// check params
if (ppv == NULL)
return TrapError(E_INVALIDARG);
// Init
*ppv = NULL;
// Find IID
if (IID_IUnknown == riid)
*ppv = (IUnknown *)this;
else if (IID_IStream == riid)
*ppv = (IStream *)this;
else if (IID_CBodyStream == riid)
*ppv = (CBodyStream *)this;
else
{
*ppv = NULL;
return TrapError(E_NOINTERFACE);
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CBodyStream::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CBodyStream::AddRef(void)
{
return (ULONG)InterlockedIncrement(&m_cRef);
}
// --------------------------------------------------------------------------------
// CBodyStream::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CBodyStream::Release(void)
{
LONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
delete this;
return (ULONG)cRef;
}
// --------------------------------------------------------------------------------
// CBodyStream::GetEncodeWrapInfo
// --------------------------------------------------------------------------------
void CBodyStream::GetEncodeWrapInfo(LPCONVINITINFO pInitInfo, LPMESSAGEBODY pBody)
{
// Locals
PROPVARIANT rOption;
// Export 7bit or 8bit
if (IET_7BIT != pInitInfo->ietEncoding && IET_8BIT != pInitInfo->ietEncoding)
return;
// OID_WRAP_BODY_TEXT
if (FAILED(pBody->GetOption(OID_WRAP_BODY_TEXT, &rOption)) || FALSE == rOption.boolVal)
return;
// Get Wrapping Width
if (FAILED(pBody->GetOption(OID_CBMAX_BODY_LINE, &rOption)))
rOption.ulVal = DEF_CBMAX_BODY_LINE;
// We are wrapping
pInitInfo->dwFlags |= ICF_WRAPTEXT;
// Max Line
pInitInfo->cchMaxLine = rOption.ulVal;
// Done
return;
}
// --------------------------------------------------------------------------------
// CBodyStream::GetCodePageInfo
// --------------------------------------------------------------------------------
void CBodyStream::GetCodePageInfo(LPCONVINITINFO pInitInfo, BOOL fDoCharset, CODEPAGEID cpiSource, CODEPAGEID cpiDest)
{
// Decide if we should do a code page conversion
if (TRUE == fDoCharset && cpiSource != cpiDest)
{
// No Conversions between 28591 and 1252
if ((1252 == cpiSource || 28591 == cpiSource) && (1252 == cpiDest || 28591 == cpiDest))
{
// Do a code page conversion
FLAGCLEAR(pInitInfo->dwFlags, ICF_CODEPAGE);
}
// Can the conversion between the internal and external code pages be performed ?
else if (g_pInternat->CanConvertCodePages(cpiSource, cpiDest) == S_OK)
{
// Do a code page conversion
FLAGSET(pInitInfo->dwFlags, ICF_CODEPAGE);
}
// Otherwise...
else
{
// Time to Whine
DOUTL(4, "MLANG.DLL Can't Convert CodePage %d to CodePage %d\n", cpiSource, cpiDest);
// If cpiSource is unicode and cpiDest is not unicode, we need to set cpiDest to a legal multibyte code page
if (CP_UNICODE == cpiSource && CP_UNICODE != cpiDest)
{
// Default to system acp
cpiDest = GetACP();
// We better be able to do this conversion...
Assert(g_pInternat->CanConvertCodePages(cpiSource, cpiDest) == S_OK);
// Do the conversion...
FLAGSET(pInitInfo->dwFlags, ICF_CODEPAGE);
// Whine some more
DOUTL(4, "Modified: CODEPAGE(%d -> %d, 0x%0X -> 0x%0X)\n", cpiSource, cpiDest, cpiSource, cpiDest);
}
// multibyte to unicode
else if (CP_UNICODE != cpiSource && CP_UNICODE == cpiDest)
{
// Default to system acp
cpiSource = GetACP();
// We better be able to do this conversion...
Assert(g_pInternat->CanConvertCodePages(cpiSource, cpiDest) == S_OK);
// Do the conversion...
FLAGSET(pInitInfo->dwFlags, ICF_CODEPAGE);
// Whine some more
DOUTL(4, "Modified: CODEPAGE(%d -> %d, 0x%0X -> 0x%0X)\n", cpiSource, cpiDest, cpiSource, cpiDest);
}
}
}
// Set Source Code Page
pInitInfo->cpiSource = cpiSource;
// Set Destination Code Page
pInitInfo->cpiDest = cpiDest;
// Done
return;
}
// --------------------------------------------------------------------------------
// CBodyStream::ComputeCodePageMapping
// --------------------------------------------------------------------------------
void CBodyStream::ComputeCodePageMapping(LPBODYSTREAMINIT pInitInfo)
{
// We should always have a charset...
Assert(pInitInfo->pCharset && g_pInternat);
// External is IET_UNICODE
if (IET_UNICODE == pInitInfo->ietExternal)
{
pInitInfo->cpiExternal = CP_UNICODE;
pInitInfo->ietExternal = IET_BINARY;
}
// External is in Windows CodePage
else if (IET_BINARY == pInitInfo->ietExternal)
{
// RAID-32777: User does not want unicode, so make sure we return multibyte
pInitInfo->cpiExternal = (CP_UNICODE == pInitInfo->pCharset->cpiWindows) ? GetACP() : pInitInfo->pCharset->cpiWindows;
// Map outof autodetect
if (CP_JAUTODETECT == pInitInfo->cpiExternal)
pInitInfo->cpiExternal = 932;
}
// External is in Internet CodePage
else
{
// RAID-25300 - FE-J:Athena: Newsgroup article and mail sent with charset=_autodetect
pInitInfo->cpiExternal = (CP_JAUTODETECT == pInitInfo->pCharset->cpiInternet) ? 50220 : pInitInfo->pCharset->cpiInternet;
// Better not be unicode - Removed because of Raid 40228
/// Assert(CP_UNICODE != pInitInfo->cpiExternal);
// Adjust ietExternal
if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pInitInfo->ietExternal))
pInitInfo->ietExternal = IET_BINARY;
}
// Internal is IET_UNICODE
if (IET_UNICODE == pInitInfo->ietInternal)
{
pInitInfo->cpiInternal = CP_UNICODE;
pInitInfo->ietInternal = IET_BINARY;
}
// Internal is in Windows CodePage
else if (IET_BINARY == pInitInfo->ietInternal)
{
// The internal data is not unicode so make sure we don't say its unicode
pInitInfo->cpiInternal = (CP_UNICODE == pInitInfo->pCharset->cpiWindows) ? GetACP() : pInitInfo->pCharset->cpiWindows;
}
// Internal is in Internet CodePage
else
{
// Internet CodePage
pInitInfo->cpiInternal = pInitInfo->pCharset->cpiInternet;
// Adjust ietExternal
if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pInitInfo->ietInternal))
pInitInfo->ietInternal = IET_BINARY;
}
}
// --------------------------------------------------------------------------------
// CBodyStream::GenerateDefaultMacBinaryHeader
// --------------------------------------------------------------------------------
void CBodyStream::GenerateDefaultMacBinaryHeader(LPMACBINARY pMacBinary)
{
// ZeroInit
ZeroMemory(pMacBinary, sizeof(MACBINARY));
}
// --------------------------------------------------------------------------------
// CBodyStream::HrInitialize
// --------------------------------------------------------------------------------
HRESULT CBodyStream::HrInitialize(LPBODYSTREAMINIT pInitInfo, LPMESSAGEBODY pBody)
{
// Locals
HRESULT hr=S_OK;
STATSTG rStat;
BOOL fDoCharset=FALSE;
BOOL fIsText;
CONVINITINFO rEncodeInit;
CONVINITINFO rDecodeInit;
PROPVARIANT rVariant;
// Parameters
Assert(pBody);
// Thread Safety
EnterCriticalSection(&m_cs);
// Get Code Page Mappings
ComputeCodePageMapping(pInitInfo);
// Debug
//DebugTrace("IBodyStream: ENCODING(%s -> %s) CODEPAGE(%d -> %d)\n", g_rgEncodingMap[pInitInfo->ietInternal].pszName,g_rgEncodingMap[pInitInfo->ietExternal].pszName, pInitInfo->cpiInternal, pInitInfo->cpiExternal);
// Initialize Convert Init Structs
ZeroMemory(&rEncodeInit, sizeof(CONVINITINFO));
rEncodeInit.fEncoder = TRUE;
ZeroMemory(&rDecodeInit, sizeof(CONVINITINFO));
rDecodeInit.fEncoder = FALSE;
// Get the class id of this stream
rVariant.vt = VT_LPSTR;
if (SUCCEEDED(pBody->GetProp(PIDTOSTR(PID_ATT_GENFNAME), 0, &rVariant)))
m_pszFileName = rVariant.pszVal;
// Lets test my IBodyStream::Stat function
#ifdef DEBUG
Stat(&rStat, STATFLAG_NONAME);
#endif
// Get LockBytes from the body
hr = pBody->HrGetLockBytes(&m_pLockBytes);
if (FAILED(hr) && MIME_E_NO_DATA != hr)
{
TrapError(hr);
goto exit;
}
// Else, ok
hr = S_OK;
// Otherwise, query the size
if (m_pLockBytes)
{
// Get the size of the lockbytes object...
CHECKHR(hr = m_pLockBytes->Stat(&rStat, STATFLAG_NONAME));
m_uliIntSize.QuadPart = rStat.cbSize.QuadPart;
}
// Special Case for IET_CURRENT
if (IET_CURRENT == pInitInfo->ietExternal)
{
// No Conversion
m_dctConvert = DCT_NONE;
// Done
goto exit;
}
// Otheriwse, lookup conversion type
Assert(FIsValidBodyEncoding(pInitInfo->ietInternal) && FIsValidBodyEncoding(pInitInfo->ietExternal));
m_dctConvert = (DOCCONVTYPE)(g_rgConversionMap[pInitInfo->ietInternal].rgDestType[pInitInfo->ietExternal]);
// If we have data...
if (m_uliIntSize.QuadPart > 0)
{
// Is Text
fIsText = (pBody->IsContentType(STR_CNT_TEXT, NULL) == S_OK) ? TRUE : FALSE;
// Get the character set for this body...
if (fIsText)
{
// Raid-6832: Mail : We fail to display Plain Text messages when they have a name parameter in the Content Type header
// If multibyte to unicode or unicode to multibyte, then we must do a charset translation
if ((CP_UNICODE == pInitInfo->cpiInternal && CP_UNICODE != pInitInfo->cpiExternal) ||
(CP_UNICODE != pInitInfo->cpiInternal && CP_UNICODE == pInitInfo->cpiExternal))
fDoCharset = TRUE;
// If Tagged with a Character Set, then always apply charset dencode/encode
else if (pBody->IsType(IBT_CSETTAGGED) == S_OK)
fDoCharset = TRUE;
// Otherwise, if its not an attachment, then always apply charset dencode/encode
else if (pBody->IsType(IBT_AUTOATTACH) == S_OK || pBody->IsType(IBT_ATTACHMENT) == S_FALSE)
fDoCharset = TRUE;
}
// If currently no conversion, see if we are translating between code pages
if (fDoCharset && DCT_NONE == m_dctConvert && pInitInfo->cpiInternal != pInitInfo->cpiExternal)
m_dctConvert = DCT_DECODE;
// Encode
if (DCT_ENCODE == m_dctConvert)
{
// Do Code Page Convsion
GetCodePageInfo(&rEncodeInit, fDoCharset, pInitInfo->cpiInternal, pInitInfo->cpiExternal);
// Remove NBSPs
if ((TRUE == fIsText) && (CP_UNICODE == pInitInfo->cpiInternal) && (TRUE == pInitInfo->fRemoveNBSP))
rEncodeInit.dwFlags |= ICF_KILLNBSP;
// Set Encoding Type
rEncodeInit.ietEncoding = pInitInfo->ietExternal;
// BinHex Encoding...
if (IET_BINHEX40 == rEncodeInit.ietEncoding)
{
// Locals
PROPVARIANT rOption;
// Init rOption
rOption.vt = VT_BLOB;
rOption.blob.cbSize = sizeof(MACBINARY);
rOption.blob.pBlobData = (LPBYTE)&rEncodeInit.rMacBinary;
GenerateDefaultMacBinaryHeader(&rEncodeInit.rMacBinary);
}
// GetEncodeWrapInfo
if (fIsText)
GetEncodeWrapInfo(&rEncodeInit, pBody);
// Create Internet Encoder
CHECKHR(hr = HrCreateInternetConverter(&rEncodeInit, &m_pEncoder));
}
// Decode
else if (DCT_DECODE == m_dctConvert)
{
// Do Code Page Convsion
GetCodePageInfo(&rDecodeInit, fDoCharset, pInitInfo->cpiInternal, pInitInfo->cpiExternal);
// Remove NBSPs
if ((TRUE == fIsText) && (CP_UNICODE == pInitInfo->cpiInternal) && (TRUE == pInitInfo->fRemoveNBSP))
rDecodeInit.dwFlags |= ICF_KILLNBSP;
// Set Encoding Type
rDecodeInit.ietEncoding = pInitInfo->ietInternal;
// BinHex Dencoding...
if (IET_BINHEX40 == rDecodeInit.ietEncoding)
{
// Locals
PROPVARIANT rOption;
// OID_SHOW_MACBINARY
if (SUCCEEDED(pBody->GetOption(OID_SHOW_MACBINARY, &rOption)))
{
rDecodeInit.fShowMacBinary = rOption.boolVal;
}
}
// Create Internet DeCoder
CHECKHR(hr = HrCreateInternetConverter(&rDecodeInit, &m_pDecoder));
}
// Decode -> Encode
else if (DCT_DECENC == m_dctConvert)
{
// Do Code Page Convsion
if (pInitInfo->cpiInternal != pInitInfo->cpiExternal)
{
// Internal -> Unicode
GetCodePageInfo(&rDecodeInit, fDoCharset, pInitInfo->cpiInternal, CP_UNICODE);
// Unicode -> Extnernal
GetCodePageInfo(&rEncodeInit, fDoCharset, CP_UNICODE, pInitInfo->cpiExternal);
}
// Set Encoding Type
rDecodeInit.ietEncoding = pInitInfo->ietInternal;
// Create Internet Decoder
CHECKHR(hr = HrCreateInternetConverter(&rDecodeInit, &m_pDecoder));
// Set Encoding Type
rEncodeInit.ietEncoding = pInitInfo->ietExternal;
// GetEncodeWrapInfo
if (fIsText)
GetEncodeWrapInfo(&rEncodeInit, pBody);
// Create Internet Encoder
CHECKHR(hr = HrCreateInternetConverter(&rEncodeInit, &m_pEncoder));
}
// No Conversion
else
Assert(DCT_NONE == m_dctConvert);
}
// Otherwise, handle zero length data
else
{
// Converting to IET_UUENCODE
if (IET_UUENCODE == pInitInfo->ietExternal)
{
// Save Size
m_uliIntOffset.QuadPart = m_uliIntSize.QuadPart = lstrlen(c_szUUEncodeZeroLength);
// Single byte
CHECKHR(hr = m_cVirtualStream.Write(c_szUUEncodeZeroLength, lstrlen(c_szUUEncodeZeroLength), NULL));
// Rewind that stream
HrRewindStream(&m_cVirtualStream);
// Change the DC type to any other type
m_dctConvert = DCT_ENCODE;
}
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::HrConvertDataLast
// --------------------------------------------------------------------------------
HRESULT CBodyStream::HrConvertDataLast(void)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
// Handle Conversion type
switch(m_dctConvert)
{
// ----------------------------------------------------------------------------
case DCT_ENCODE:
// Encode
CHECKHR(hr = m_pEncoder->HrInternetEncode(TRUE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pEncoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
case DCT_DECODE:
// Convert
CHECKHR(hr = m_pDecoder->HrInternetDecode(TRUE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pDecoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
case DCT_DECENC:
// Convert
CHECKHR(hr = m_pDecoder->HrInternetDecode(TRUE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Fill Buffer
CHECKHR(hr = m_pDecoder->HrWriteConverted(m_pEncoder));
// Convert
CHECKHR(hr = m_pEncoder->HrInternetEncode(TRUE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pEncoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
default:
AssertSz(FALSE, "I should be fired and shot if this line of code executes.");
break;
}
exit:
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::HrConvertData
// --------------------------------------------------------------------------------
HRESULT CBodyStream::HrConvertData(LPBLOB pConvert)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
// Handle Conversion type
switch(m_dctConvert)
{
// ----------------------------------------------------------------------------
case DCT_ENCODE:
// Fill Buffer
CHECKHR(hr = m_pEncoder->HrFillAppend(pConvert));
// Encode
CHECKHR(hr = m_pEncoder->HrInternetEncode(FALSE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pEncoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
case DCT_DECODE:
// Fill Buffer
CHECKHR(hr = m_pDecoder->HrFillAppend(pConvert));
// Convert
CHECKHR(hr = m_pDecoder->HrInternetDecode(FALSE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pDecoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
case DCT_DECENC:
// Fill Buffer
CHECKHR(hr = m_pDecoder->HrFillAppend(pConvert));
// Convert
CHECKHR(hr = m_pDecoder->HrInternetDecode(FALSE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Fill Buffer
CHECKHR(hr = m_pDecoder->HrWriteConverted(m_pEncoder));
// Convert
CHECKHR(hr = m_pEncoder->HrInternetEncode(FALSE));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Write
CHECKHR(hr = m_pEncoder->HrWriteConverted(&m_cVirtualStream));
break;
// ----------------------------------------------------------------------------
default:
AssertSz(FALSE, "I should be fired and shot if this line of code executes.");
break;
}
exit:
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::HrConvertToOffset
// --------------------------------------------------------------------------------
HRESULT CBodyStream::HrConvertToOffset(ULARGE_INTEGER uliOffset)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BYTE rgbBuffer[4096];
ULONG cbBuffer=0;
ULONG cb;
ULARGE_INTEGER uliCur, uliSize;
LARGE_INTEGER liStart;
BLOB rConvert;
DWORD dwSize = 0;
// Big Problems...
Assert(m_pLockBytes);
// Fatal Error: This can happen if we are persisting an IMimeMessage back into its original source.
if (NULL == m_pLockBytes)
{
hr = TrapError(E_FAIL);
goto exit;
}
// Query Current position and uliSize
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
liStart.QuadPart = uliCur.QuadPart;
Assert(m_liLastWrite.QuadPart == liStart.QuadPart);
dwSize = m_uliIntSize.LowPart - m_uliIntOffset.LowPart;
// Convert until no more to read or virtual offset is correct
while(dwSize)
{
// Done
if (uliCur.QuadPart >= uliOffset.QuadPart)
break;
// Read a buffer
Assert(m_uliIntOffset.QuadPart <= m_uliIntSize.QuadPart);
CHECKHR(hr = m_pLockBytes->ReadAt(m_uliIntOffset, rgbBuffer, sizeof(rgbBuffer), &cbBuffer));
// Done
if (0 == cbBuffer)
break;
dwSize = dwSize - cbBuffer;
// Last Buffer ?
Assert(m_uliIntOffset.QuadPart + cbBuffer <= m_uliIntSize.QuadPart);
// Setup Blob to Convert
rConvert.cbSize = cbBuffer;
rConvert.pBlobData = rgbBuffer;
// Convert the buffer
CHECKHR(hr = HrConvertData(&rConvert));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Increment internal offset...
m_uliIntOffset.QuadPart += cbBuffer;
// Get current virtual offset
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Save position as last write position...
m_liLastWrite.QuadPart = uliCur.QuadPart;
}
// Done ?
if (0 == cbBuffer || m_uliIntOffset.QuadPart == m_uliIntSize.QuadPart)
{
// Well we don't need m_pLockBytes anymore, its all in m_cVirtualStream
Assert(m_uliIntOffset.QuadPart == m_uliIntSize.QuadPart);
// Do the last one
CHECKHR(hr = HrConvertDataLast());
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Get current virtual offset
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Save position as last write position...
m_liLastWrite.QuadPart = uliCur.QuadPart;
// Release objects
SafeRelease(m_pLockBytes);
SafeRelease(m_pEncoder);
SafeRelease(m_pDecoder);
}
// Rewind virtual stream back to
CHECKHR(hr = m_cVirtualStream.Seek(liStart, STREAM_SEEK_SET, NULL));
exit:
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::HrConvertToEnd
// --------------------------------------------------------------------------------
HRESULT CBodyStream::HrConvertToEnd(void)
{
// Locals
HRESULT hr=S_OK;
BYTE rgbBuffer[4096];
ULONG cbRead;
// Thread Safety
EnterCriticalSection(&m_cs);
// Move virtual stream to last write position
CHECKHR(hr = m_cVirtualStream.Seek(m_liLastWrite, STREAM_SEEK_SET, NULL));
// Copy m_pLockBytes to pstmTemp
while(1)
{
// Read
CHECKHR(hr = Read(rgbBuffer, sizeof(rgbBuffer), &cbRead));
// Done
if (0 == cbRead)
{
// Well we don't need m_pLockBytes anymore, its all in m_cVirtualStream
Assert(m_uliIntOffset.QuadPart == m_uliIntSize.QuadPart);
break;
}
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::Read
// --------------------------------------------------------------------------------
#ifndef WIN16
STDMETHODIMP CBodyStream::Read(LPVOID pv, ULONG cb, ULONG *pcbRead)
#else
STDMETHODIMP CBodyStream::Read(VOID HUGEP *pv, ULONG cb, ULONG *pcbRead)
#endif // !WIN16
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
ULARGE_INTEGER uliCur,
uliSize;
ULONG cbRead=0,
cbLeft=0,
cbGet;
// Thread Safety
EnterCriticalSection(&m_cs);
// No Convert Case
if (DCT_NONE == m_dctConvert)
{
// Do I Have data
if (m_pLockBytes)
{
// Read Buffer
CHECKHR(hr = m_pLockBytes->ReadAt(m_uliIntOffset, pv, cb, &cbRead));
// Done
m_uliIntOffset.QuadPart += cbRead;
}
}
// Otherwise
else
{
// Read until we have cb
cbLeft = cb;
while(cbLeft)
{
// Query Current position and uliSize
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Convert more into the virtual stream
if (uliCur.QuadPart == uliSize.QuadPart)
{
// Done
if (m_uliIntOffset.QuadPart == m_uliIntSize.QuadPart)
break;
// Grow
uliCur.QuadPart += g_dwSysPageSize;
// Convert to offset...
CHECKHR(hr = HrConvertToOffset(uliCur));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
}
// Compute amount that can be read from the current cache
CHECKHR(hr = m_cVirtualStream.Read((LPVOID)((LPBYTE)pv + cbRead), cbLeft, &cbGet));
// Increment cbRead
cbRead+=cbGet;
cbLeft-=cbGet;
}
}
// Return amount read
if (pcbRead)
*pcbRead = cbRead;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::Seek
// --------------------------------------------------------------------------------
STDMETHODIMP CBodyStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
ULARGE_INTEGER uliNew;
// Thread Safety
EnterCriticalSection(&m_cs);
// No Convert Case
if (DCT_NONE == m_dctConvert)
{
// Seek the file pointer
switch (dwOrigin)
{
// --------------------------------------------------------
case STREAM_SEEK_SET:
uliNew.QuadPart = (DWORDLONG)dlibMove.QuadPart;
break;
// --------------------------------------------------------
case STREAM_SEEK_CUR:
if (dlibMove.QuadPart < 0)
{
if ((DWORDLONG)(0 - dlibMove.QuadPart) > m_uliIntOffset.QuadPart)
{
hr = TrapError(E_FAIL);
goto exit;
}
}
uliNew.QuadPart = m_uliIntOffset.QuadPart + dlibMove.QuadPart;
break;
// --------------------------------------------------------
case STREAM_SEEK_END:
if (dlibMove.QuadPart < 0 || (DWORDLONG)dlibMove.QuadPart > m_uliIntSize.QuadPart)
{
hr = TrapError(E_FAIL);
goto exit;
}
uliNew.QuadPart = m_uliIntSize.QuadPart - dlibMove.QuadPart;
break;
// --------------------------------------------------------
default:
hr = TrapError(STG_E_INVALIDFUNCTION);
goto exit;
}
// New offset greater than size...
m_uliIntOffset.QuadPart = min(uliNew.QuadPart, m_uliIntSize.QuadPart);
// Return Position
if (plibNewPosition)
plibNewPosition->QuadPart = (LONGLONG)m_uliIntOffset.QuadPart;
}
// Otherwise
else
{
// Locals
ULARGE_INTEGER uliCur, uliSize;
LARGE_INTEGER liNew;
// Query Current position and uliSize
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Seek the file pointer
switch (dwOrigin)
{
// --------------------------------------------------------
case STREAM_SEEK_SET:
// Assume new position
uliNew.QuadPart = (DWORDLONG)dlibMove.QuadPart;
break;
// --------------------------------------------------------
case STREAM_SEEK_CUR:
if (dlibMove.QuadPart < 0)
{
if ((DWORDLONG)(0 - dlibMove.QuadPart) > uliCur.QuadPart)
{
hr = TrapError(E_FAIL);
goto exit;
}
}
uliNew.QuadPart = uliCur.QuadPart + dlibMove.QuadPart;
break;
// --------------------------------------------------------
case STREAM_SEEK_END:
// Do I need to convert any more ?
if (m_uliIntOffset.QuadPart < m_uliIntSize.QuadPart)
{
// Then Convert to the end
CHECKHR(hr = HrConvertToEnd());
// Better be done...
Assert(m_uliIntOffset.QuadPart == m_uliIntSize.QuadPart && NULL == m_pLockBytes);
}
// Query Current Position
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Can I really move there
if (dlibMove.QuadPart < 0 || (DWORDLONG)dlibMove.QuadPart > uliSize.QuadPart)
{
hr = TrapError(E_FAIL);
goto exit;
}
// Assume new position
uliNew.QuadPart = uliSize.QuadPart - dlibMove.QuadPart;
break;
// --------------------------------------------------------
default:
hr = TrapError(STG_E_INVALIDFUNCTION);
goto exit;
}
// New offset greater than size...
if (uliNew.QuadPart > uliSize.QuadPart)
{
// Do I need to convert any more ?
if (m_uliIntOffset.QuadPart < m_uliIntSize.QuadPart)
{
// Seek m_cVirtualStream to m_uliIntOffset
CHECKHR(hr = m_cVirtualStream.Seek(m_liLastWrite, STREAM_SEEK_SET, NULL));
// Convert to offset
CHECKHR(hr = HrConvertToOffset(uliNew));
if ( S_OK != hr )
hrWarnings = TrapError(hr);
}
// Query Current position and uliSize
m_cVirtualStream.QueryStat(&uliCur, &uliSize);
// Reposition uliNew.QuadPart
uliNew.QuadPart = (uliNew.QuadPart > uliSize.QuadPart) ? uliSize.QuadPart : uliNew.QuadPart;
}
// Otherwise, seek m_cVirtualStream to new location
liNew.QuadPart = uliNew.QuadPart;
CHECKHR(hr = m_cVirtualStream.Seek(liNew, STREAM_SEEK_SET, NULL));
// Return Position
if (plibNewPosition)
plibNewPosition->QuadPart = (LONGLONG)uliNew.QuadPart;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CBodyStream::CopyTo
// --------------------------------------------------------------------------------
STDMETHODIMP CBodyStream::CopyTo(IStream *pstmDest, ULARGE_INTEGER cb, ULARGE_INTEGER *puliRead, ULARGE_INTEGER *puliWritten)
{
return HrCopyStreamCB((IStream *)this, pstmDest, cb, puliRead, puliWritten);
}
// --------------------------------------------------------------------------------
// CBodyStream::Stat
// --------------------------------------------------------------------------------
STDMETHODIMP CBodyStream::Stat(STATSTG *pStat, DWORD grfStatFlag)
{
// Locals
HRESULT hr=S_OK;
LARGE_INTEGER liSeek;
ULARGE_INTEGER uliCurrent;
// Invalid Arg
if (NULL == pStat)
return TrapError(E_INVALIDARG);
// Init
ZeroMemory(pStat, sizeof(STATSTG));
// Set Storage Type
pStat->type = STGTY_STREAM;
// Set the name ?
if (m_pszFileName && !(grfStatFlag & STATFLAG_NONAME))
pStat->pwcsName = PszToUnicode(CP_ACP, m_pszFileName);
// Seek current position
liSeek.QuadPart = 0;
CHECKHR(hr = Seek(liSeek, STREAM_SEEK_CUR, &uliCurrent));
// Seek to the end
liSeek.QuadPart = 0;
CHECKHR(hr = Seek(liSeek, STREAM_SEEK_END, &pStat->cbSize));
// Seek back to current position
liSeek.QuadPart = uliCurrent.QuadPart;
CHECKHR(hr = Seek(liSeek, STREAM_SEEK_SET, &uliCurrent));
Assert(uliCurrent.QuadPart == (DWORDLONG)liSeek.QuadPart);
// init clsid
pStat->clsid = CLSID_NULL;
// If we have a filename, get the class id...
if (m_pszFileName)
{
// Locals
CHAR szExt[MAX_PATH];
// Split the filename
if (SUCCEEDED(MimeOleGetFileExtension(m_pszFileName, szExt, ARRAYSIZE(szExt))))
MimeOleGetExtClassId(szExt, &pStat->clsid);
}
exit:
// Done
return hr;
}