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

6586 lines
208 KiB
C++

// --------------------------------------------------------------------------------
// BookBody.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "bookbody.h"
#include "dllmain.h"
#include "ibdylock.h"
#include "stmlock.h"
#include "ibdystm.h"
#include "resource.h"
#include "smime.h"
#include "objheap.h"
#include "internat.h"
#include "urlmon.h"
#include "symcache.h"
#include "booktree.h"
#include <demand.h>
#include "mimeapi.h"
#include <shlwapi.h>
#include "webdocs.h"
// --------------------------------------------------------------------------------
// ASSERTINIT
// --------------------------------------------------------------------------------
#define ASSERTINIT \
AssertSz(m_pContainer, "Object is being used before a call to InitNew")
// --------------------------------------------------------------------------------
// Default Body Options
// --------------------------------------------------------------------------------
static const BODYOPTIONS g_rDefBodyOptions = {
IET_UNKNOWN, // OID_TRANSMIT_BODY_FORMAT
DEF_CBMAX_BODY_LINE, // OID_CBMAX_BODY_LINE
DEF_WRAP_BODY_TEXT, // OID_WRAP_BODY_TEXT
DEF_BODY_REMOVE_NBSP, // OID_BODY_REMOVE_NBSP
DEF_DBCS_ESCAPE_IS_8BIT, // OID_DBCS_ESCAPE_IS_8BIT
DEF_HIDE_TNEF_ATTACHMENTS, // OID_HIDE_TNEF_ATTACHMENTS
MST_NONE, // OID_SECURITY_TYPE
NULL, // OID_SECURITY_ALG_HASH and OID_SECURITY_ALG_HASH_RG
{ 0, NULL }, // OID_SECURITY_ALG_BULK
NULL, // OID_SECURITY_CERT_SIGNING and OID_SECURITY_CERT_SIGNING_RG
0, // OID_SECURITY_CERT_DECRYPTION
NULL, // OID_SECURITY_HCERTSTORE and OID_SECURITY_HCERTSTORE_RG
{ 0, NULL }, // OID_SECURITY_SEARCHSTORES
0,
NULL, // OID_SECURITY_RG_IASN
#ifdef SMIME_V3
NULL, // OID_SECURITY_AUTHATTR and OID_SECURITY_AUTHATTR_RG
NULL, // OID_SECURITY_UNAUTHATTR and OID_SECURITY_UNAUTHATTR_RG
NULL, // OID_SECURITY_UNPROTECTATTR_RG
#else // !SMIME_V3
NULL, // OID_SECURITY_SYMCAPS and OID_SECURITY_SYMCAPS_RG
NULL, // OID_SECURITY_AUTHATTR and OID_SECURITY_AUTHATTR_RG
NULL, // OID_SECURITY_UNAUTHATTR and OID_SECURITY_UNAUTHATTR_RG
NULL, // OID_SECURITY_SIGNTIME and OID_SECURITY_SIGNTIME_RG
#endif // SMIME_V3
NULL, // OID_SECURITY_USER_VALIDITY and OID_SECURITY_USER_VALIDITY_RG
NULL, // OID_SECURITY_RO_MSG_VALIDITY and OID_SECURITY_RO_MSG_VALIDITY_RG
0, // OID_SECURITY_HCRYPTPROV
0, // OID_SECURITY_ENCODE_FLAGS
FALSE, // OID_SECURITY_CERT_INCLUDED
// This is NULL b/c default is generated at runtime
NULL, // OID_SECURITY_HWND_OWNER
// Base64 is the recommended value in the S/MIME spec
IET_BASE64, // OID_SECURITY_REQUESTED_CTE
#ifdef SMIME_V3
NULL, // OID_SECURITY_RECEIPT_RG
NULL, // OID_SECURITY_MESSAGE_HASH_RG
NULL, // OID_SECURITY_KEY_PROMPT
#endif // SMIME_V3
DEF_SHOW_MACBINARY, // OID_SHOW_MACBINARY
DEF_SUPPORT_EXTERNAL_BODY, // OID_SUPPORT_EXTERNAL_BODY
0, // cSecurityLayers (size of arrays of
// OID_SECURITY_ALG_HASH
// OID_SECURITY_CERT_SIGNING
// OID_SECURITY_HCERTSTORE
// OID_SECURITY_SYMCAPS
// OID_SECURITY_AUTHATTR
// OID_SECURITY_UNAUTHATTR
// OID_SECURITY_SIGNTIME
// OID_SECURITY_USER_VALIDITY
// OID_SECURITY_RO_MSG_VALIDITY)
FALSE, // OID_NOSECURITY_ON_SAVE
#ifdef SMIME_V3
0, 0, NULL, // cRecipients/rgRecipients
#endif // SMIME_V3
NULL, // OID_SECURITY_ENCRYPT_CERT_BAG
};
static const BLOB blobNULL = {0, NULL};
HRESULT HrCopyBlobArray(LPCBLOB pIn, ULONG cEntries, PROPVARIANT FAR * pvOut);
HRESULT HrCopyDwordArray(LPDWORD pIn, ULONG cEntries, PROPVARIANT FAR * pvOut);
HRESULT HrCopyIntoUlonglongArray(ULARGE_INTEGER * pIn, ULONG cEntries, PROPVARIANT FAR * pvOut);
HRESULT HrCopyFiletimeArray(LPFILETIME pIn, ULONG cEntries, PROPVARIANT FAR * pvOut);
DWORD MergeDWORDFlags(LPDWORD rgdw, ULONG cEntries);
extern HRESULT HrGetLastError(void);
extern BOOL FIsMsasn1Loaded();
// --------------------------------------------------------------------------------
// WebBookContentBody_CreateInstance
// --------------------------------------------------------------------------------
HRESULT WebBookContentBody_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppUnknown)
{
// Invalid Arg
Assert(ppUnknown);
// Initialize
*ppUnknown = NULL;
// Create me
CMessageBody *pNew = new CMessageBody(NULL, pUnkOuter);
if (NULL == pNew)
return TrapError(E_OUTOFMEMORY);
// Return the Innter
*ppUnknown = pNew->GetInner();
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::CMessageBody
// --------------------------------------------------------------------------------
CMessageBody::CMessageBody(LPTREENODEINFO pNode, IUnknown *pUnkOuter)
: m_pNode(pNode), CPrivateUnknown(pUnkOuter)
{
DllAddRef();
m_cRef = 1;
m_dwState = 0;
m_pszDisplay = NULL;
m_ietEncoding = IET_BINARY;
m_ietPrevious = IET_UNKNOWN;
m_pCharset = CIntlGlobals::GetDefBodyCset();
m_pCsetTagged = NULL;
m_pContainer = NULL;
m_cbExternal = 0xFFFFFFFF;
ZeroMemory(&m_rStorage, sizeof(BODYSTORAGE));
CopyMemory(&m_rOptions, &g_rDefBodyOptions, sizeof(BODYOPTIONS));
// (t-erikne) need to get this default at run time
m_rOptions.hwndOwner = HWND_DESKTOP;
InitializeCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::~CMessageBody
// --------------------------------------------------------------------------------
CMessageBody::~CMessageBody(void)
{
SafeRelease(m_pContainer);
SafeMemFree(m_pszDisplay);
SafeRelease(m_rStorage.pUnkRelease);
// Clear out the options
_FreeOptions();
DeleteCriticalSection(&m_cs);
DllRelease();
}
// --------------------------------------------------------------------------------
// CMessageBody::PrivateQueryInterface
// --------------------------------------------------------------------------------
HRESULT CMessageBody::PrivateQueryInterface(REFIID riid, LPVOID *ppv)
{
// check params
if (ppv == NULL)
return TrapError(E_INVALIDARG);
// Init
*ppv = NULL;
// Find IID
if (IID_IMimeBody == riid)
*ppv = (IMimeBody *)this;
else if (IID_IMimeBodyW == riid)
*ppv = (IMimeBodyW *)this;
else if (IID_IMimePropertySet == riid)
*ppv = (IMimePropertySet *)this;
else if (IID_IPersist == riid)
*ppv = (IPersist *)this;
else if (IID_IPersistStreamInit == riid)
*ppv = (IPersistStreamInit *)this;
else if (IID_CMessageBody == riid)
*ppv = (CMessageBody *)this;
#ifdef SMIME_V3
else if (IID_IMimeSecurity2 == riid)
*ppv = (IMimeSecurity2 *) this;
#endif
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::RevokeTreeNode
// --------------------------------------------------------------------------------
void CMessageBody::RevokeTreeNode(void)
{
EnterCriticalSection(&m_cs);
m_pNode = NULL;
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::HrBindToTree
// --------------------------------------------------------------------------------
HRESULT CMessageBody::HrBindToTree(CStreamLockBytes *pStmLock, LPTREENODEINFO pNode)
{
// Locals
HRESULT hr=S_OK;
HCHARSET hCharset=NULL;
IStream *pstmBody=NULL;
ASSERTINIT;
// Thread Safety
EnterCriticalSection(&m_cs);
// Invalid Arg
Assert(pNode && NULL == pNode->pLockBytes && pStmLock && m_pNode == pNode);
// Create the body lock bytes
CHECKALLOC(pNode->pLockBytes = new CBodyLockBytes(pStmLock, pNode));
// Just assume it
m_rStorage.riid = IID_ILockBytes;
m_rStorage.pLockBytes = (ILockBytes *)pNode->pLockBytes;
m_rStorage.pUnkRelease = m_rStorage.pLockBytes;
m_rStorage.pUnkRelease->AddRef();
// Test for binhex
if (S_FALSE == m_pContainer->IsPropSet(PIDTOSTR(PID_HDR_CNTXFER)) && m_pContainer->IsContentType(STR_CNT_APPLICATION, STR_SUB_BINHEX) == S_OK)
{
// Locals
PROPVARIANT rVariant;
// Setup the variant
rVariant.vt = VT_LPSTR;
// If there is a filename, lets re-compute the content-type
if (SUCCEEDED(m_pContainer->GetProp(PIDTOSTR(PID_ATT_FILENAME), 0, &rVariant)))
{
// Locals
LPSTR pszCntType;
LPSTR pszSubType;
// Get mime file information
if (SUCCEEDED(MimeOleGetFileInfo(rVariant.pszVal, &pszCntType, &pszSubType, NULL, NULL, NULL)))
{
// ContentType
if (pszCntType && pszSubType)
{
CHECKHR(hr = m_pContainer->SetProp(PIDTOSTR(PID_ATT_PRITYPE), pszCntType));
CHECKHR(hr = m_pContainer->SetProp(PIDTOSTR(PID_ATT_SUBTYPE), pszSubType));
}
// application/octet-stream
else
{
CHECKHR(hr = m_pContainer->SetProp(PIDTOSTR(PID_HDR_CNTTYPE), STR_MIME_APPL_STREAM));
}
// Cleanup
SafeMemFree(pszCntType);
SafeMemFree(pszSubType);
}
// Clenaup
SafeMemFree(rVariant.pszVal);
}
// Set the Content-Transfer-Encoding to binhex
m_pContainer->SetProp(PIDTOSTR(PID_HDR_CNTXFER), STR_ENC_BINHEX40);
// The encoding type better be binhex
Assert(m_pContainer->GetEncodingType() == IET_BINHEX40);
}
// Otherwise, test for message/external-body
else if (m_rOptions.fExternalBody && S_OK == m_pContainer->IsContentType(STR_CNT_MESSAGE, STR_SUB_EXTERNAL))
{
// Bind to External-Body
_BindToExternalBody();
}
// Save The Format
m_ietPrevious = m_ietEncoding = m_pContainer->GetEncodingType();
// Raid 2215: Map a CTE of binary to 8bit so that it gets decoded correctly from the internet character set
if (IET_BINARY == m_ietEncoding)
{
// Switch to 8bit because ibdystm.cpp will not do the charset translation right if the source is binary...
m_ietEncoding = IET_8BIT;
}
// LateTnef Check
if (ISFLAGSET(pNode->dwState, NODESTATE_VERIFYTNEF))
{
// Get the data stream
if (SUCCEEDED(GetData(IET_BINARY, &pstmBody)))
{
// If TNEF, apply content type...
if (MimeOleIsTnefStream(pstmBody) == S_OK)
{
// application/ms-tnef
CHECKHR(hr = m_pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_APPLY_MSTNEF));
}
}
// Clear the flag
FLAGCLEAR(pNode->dwState, NODESTATE_VERIFYTNEF);
}
// If I'm a message with crypto mime types, say I'm "secure"
if (IsSecureContentType(m_pContainer))
{
// TREENODE_SECURE
FLAGSET(m_dwState, BODYSTATE_SECURE);
}
// If the Header was tagged with a charset, use that charset
if (m_pContainer->IsState(COSTATE_CSETTAGGED) == S_OK)
{
// Get Internal Character Set
if (SUCCEEDED(m_pContainer->GetCharset(&hCharset)))
{
// Get Pointer
SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(hCharset, &m_pCharset)));
// Save this as m_pCsetTagged
m_pCsetTagged = m_pCharset;
}
// I was tagged with a charset
FLAGSET(m_dwState, BODYSTATE_CSETTAGGED);
}
// Bound to tree
FLAGSET(pNode->dwState, NODESTATE_BOUNDTOTREE);
exit:
// Cleanup
SafeRelease(pstmBody);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ---------------------------------------------------------------------------
// CMessageBody::_BindToExternalBody
// ---------------------------------------------------------------------------
void CMessageBody::_BindToExternalBody(void)
{
// Locals
HRESULT hr=S_OK;
LPSTR pszAccessType=NULL;
LPSTR pszUrl=NULL;
IStream *pstmBody=NULL;
DWORD cbSize=0xFFFFFFFF;
CMimeWebDocument *pWebDoc=NULL;
// Get par:content-type:access-type
CHECKHR(hr = m_pContainer->GetProp(STR_PAR_ACCESSTYPE, &pszAccessType));
// Handle Access-Types that I know about
if (lstrcmpi(pszAccessType, "X-URL") == 0)
{
// Locals
PROPVARIANT rSize;
// Get par:content-type:xurl
CHECKHR(hr = m_pContainer->GetProp(STR_PAR_XURL, &pszUrl));
// Create the WebDoc
CHECKALLOC(pWebDoc = new CMimeWebDocument);
// Initialize It
CHECKHR(hr = pWebDoc->HrInitialize(NULL, pszUrl));
// Setup Variant
rSize.vt = VT_UI4;
// Get par:content-type:size
if (SUCCEEDED(m_pContainer->GetProp(STR_PAR_SIZE, 0, &rSize)))
cbSize = rSize.ulVal;
}
// If we have a webdocument...
if (pWebDoc)
{
// Get the Body Data
if (SUCCEEDED(GetData(IET_BINARY, &pstmBody)))
{
// Locals
PROPVARIANT rOption;
// Setup the option variant
rOption.vt = VT_UI4;
rOption.ulVal = RELOAD_HEADER_REPLACE;
// Set special option since I'm realoding the header...
CHECKHR(hr = m_pContainer->SetOption(OID_HEADER_RELOAD_TYPE, &rOption));
// Load this body into the container
CHECKHR(hr = m_pContainer->Load(pstmBody));
// Reset the option variant
rOption.vt = VT_UI4;
rOption.ulVal = DEF_HEADER_RELOAD_TYPE_PROPSET;
// Set special option since I'm realoding the header...
CHECKHR(hr = m_pContainer->SetOption(OID_HEADER_RELOAD_TYPE, &rOption));
}
// SetData
CHECKHR(hr = SetData(IET_BINARY, NULL, NULL, IID_IMimeWebDocument, (LPVOID)pWebDoc));
// Create a External Body Info Structure: MUST BE SET AFTER CALL TO SETDATA
FLAGSET(m_dwState, BODYSTATE_EXTERNAL);
// Set Size: MUST BE SET AFTER CALL TO SETDATA
m_cbExternal = cbSize;
}
exit:
// Cleanup
SafeMemFree(pszAccessType);
SafeMemFree(pszUrl);
SafeRelease(pstmBody);
SafeRelease(pWebDoc);
// Done
return;
}
#if 0
// ---------------------------------------------------------------------------
// CMessageBody::UseOriginalCharset
// ---------------------------------------------------------------------------
void CMessageBody::UseOriginalCharset(void)
{
// Thread Safety
EnterCriticalSection(&m_cs);
// We should have m_pCsetTagged
Assert(m_pCsetTagged);
// Set the Charset
if (m_pCsetTagged)
SetCharset(m_pCsetTagged->hCharset, CSET_APPLY_ALL);
// Thread Safety
LeaveCriticalSection(&m_cs);
}
#endif
// ---------------------------------------------------------------------------
// CMessageBody::SetDisplayName
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetDisplayName(LPCSTR pszDisplayName)
{
LPWSTR pwszDispName;
HRESULT hr = S_OK;
Assert(pszDisplayName);
IF_NULLEXIT(pwszDispName = PszToUnicode(CP_ACP, pszDisplayName));
hr = SetDisplayNameW(pwszDispName);
exit:
MemFree(pwszDispName);
return hr;
}
#define DisplayMaxLen 64
STDMETHODIMP CMessageBody::SetDisplayNameW(LPCWSTR pszDisplayName)
{
// Locals
HRESULT hr=S_OK;
WCHAR szSize[30],
szScratch[30],
szBuf[MAX_PATH];
ULONG cbSize=0,
cAlloc,
cLen;
ASSERTINIT;
// check params
if (NULL == pszDisplayName)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Free current display name
SafeMemFree(m_pszDisplay);
// Get Data Size...
GetEstimatedSize(IET_BINARY, &cbSize);
// Format the Size
StrFormatByteSizeW(cbSize, szScratch, ARRAYSIZE(szScratch));
StrCpyNW(szSize, L"(", ARRAYSIZE(szSize));
StrCatBuffW(szSize, szScratch, ARRAYSIZE(szSize));
StrCatBuffW(szSize, L")", ARRAYSIZE(szSize));
cLen = lstrlenW(pszDisplayName);
if (cLen+1 > ARRAYSIZE(szBuf))
cLen = ARRAYSIZE(szBuf) - 1;
StrCpyNW(szBuf, pszDisplayName, cLen+1);
PathStripPathW(szBuf);
cLen = lstrlenW(szBuf);
if (cLen > DisplayMaxLen)
{
WCHAR szBuf2[MAX_PATH];
WCHAR *szExt;
szExt = PathFindExtensionW(szBuf);
if (*szExt)
{
int cExt = lstrlenW(szExt);
if (cExt < DisplayMaxLen-3)
{
WCHAR szExt2[DisplayMaxLen];
StrCpyNW(szExt2, szExt, ARRAYSIZE(szExt2));
PathCompactPathExW(szBuf2, szBuf, DisplayMaxLen-cExt, 0);
StrCatBuffW(szBuf2, szExt2, ARRAYSIZE(szBuf2));
}
else
PathCompactPathExW(szBuf2, szBuf, DisplayMaxLen, 0);
}
else
{
PathCompactPathExW(szBuf2, szBuf, DisplayMaxLen, 0);
}
StrCpyNW(szBuf, szBuf2, ARRAYSIZE(szBuf));
}
// Size to allocate: filename.dat (x)\0
cAlloc = lstrlenW(szBuf) + lstrlenW(szSize) + 2;
// Dup the display name
CHECKALLOC(m_pszDisplay = PszAllocW(cAlloc));
// Format the Display Name
StrCpyNW(m_pszDisplay, szBuf, cAlloc);
StrCatBuffW(m_pszDisplay, L" ", cAlloc);
StrCatBuffW(m_pszDisplay, szSize, cAlloc);
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ---------------------------------------------------------------------------
// CMessageBody::GetDisplayName
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetDisplayName(LPSTR *ppszDisplayName)
{
LPWSTR pwszDispName = NULL;
HRESULT hr;
Assert(ppszDisplayName);
*ppszDisplayName = NULL;
IF_FAILEXIT(hr = GetDisplayNameW(ppszDisplayName ? &pwszDispName : NULL));
IF_NULLEXIT(*ppszDisplayName = PszToANSI(CP_ACP, pwszDispName));
exit:
MemFree(pwszDispName);
return TraceResult(hr);
}
STDMETHODIMP CMessageBody::GetDisplayNameW(LPWSTR *ppszDisplayName)
{
// Locals
HRESULT hr=S_OK;
ASSERTINIT;
// check params
if (NULL == ppszDisplayName)
return TrapError(E_INVALIDARG);
// Init
*ppszDisplayName = NULL;
// Thread Safety
EnterCriticalSection(&m_cs);
// Do I have an internal displayname ?
if (NULL == m_pszDisplay)
{
LPSTR pszVal = NULL;
LPWSTR pwszVal = NULL;
// Use m_pszURL first
if (IID_IMimeWebDocument == m_rStorage.riid && SUCCEEDED(m_rStorage.pWebDocument->GetURL(&pszVal)))
SetDisplayName(pszVal);
// Raid-38681 - mail:file name is incorrect when attaching renamed saved message
// Raid-18813 - Single Bodies messages can have a filename and a subject.
else if (SUCCEEDED(m_pContainer->GetPropW(SYM_ATT_FILENAME, &pwszVal)) && pwszVal)
SetDisplayNameW(pwszVal);
// If I'm an message/rfc822
else if (m_pContainer->IsContentType(STR_CNT_MESSAGE, STR_SUB_RFC822) == S_OK && FExtractRfc822Subject(&pwszVal))
SetDisplayNameW(pwszVal);
// Parent is multipart/digest
else if (m_pNode && m_pNode->pParent && m_pNode->pParent->pBody && m_pNode->pParent->pBody->IsContentType(STR_CNT_MULTIPART, STR_SUB_DIGEST) == S_OK && FExtractRfc822Subject(&pwszVal))
SetDisplayNameW(pwszVal);
// Use Subject
else if (SUCCEEDED(m_pContainer->GetPropW(SYM_HDR_SUBJECT, &pwszVal)) && pwszVal)
SetDisplayNameW(pwszVal);
// Use Generated File Name...
else if (SUCCEEDED(m_pContainer->GetPropW(SYM_ATT_GENFNAME, &pwszVal)) && pwszVal)
SetDisplayNameW(pwszVal);
// Content Description
else if (SUCCEEDED(m_pContainer->GetPropW(SYM_HDR_CNTDESC, &pwszVal)) && pwszVal)
SetDisplayNameW(pwszVal);
SafeMemFree(pszVal);
SafeMemFree(pwszVal);
}
// If there is a display name now, then dup it.
if (m_pszDisplay)
CHECKALLOC(*ppszDisplayName = PszDupW(m_pszDisplay));
else
hr = MIME_E_NO_DATA;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ---------------------------------------------------------------------------
// CMessageBody::FExtractRfc822Subject
// ---------------------------------------------------------------------------
BOOL CMessageBody::FExtractRfc822Subject(LPWSTR *ppwszVal)
{
// Locals
HRESULT hr=S_OK;
IStream *pstmData=NULL;
IMimePropertySet *pPropertySet=NULL;
PROPVARIANT rSubject;
ASSERTINIT;
// Invalid Arg
Assert(ppwszVal);
// Init
MimeOleVariantInit(&rSubject);
*ppwszVal = NULL;
// Get the data
CHECKHR(hr = GetData(IET_BINARY, &pstmData));
// Lets create a header
CHECKHR(hr = MimeOleCreatePropertySet(NULL, &pPropertySet));
// Parse the header
CHECKHR(hr = pPropertySet->Load(pstmData));
// Init Variant
rSubject.vt = VT_LPWSTR;
// Get the subject and set the display name
CHECKHR(hr = pPropertySet->GetProp(PIDTOSTR(PID_HDR_SUBJECT), 0, &rSubject));
// Raid-38681 - mail:file name is incorrect when attaching renamed saved message
if (FIsEmptyW(rSubject.pwszVal))
{
SafeMemFree(rSubject.pwszVal);
goto exit;
}
// Set this subject on my self so that STR_ATT_GENFNAME works
m_pContainer->SetProp(PIDTOSTR(PID_HDR_CNTDESC), 0, &rSubject);
// Return It
*ppwszVal = rSubject.pwszVal;
exit:
// Cleanup
SafeRelease(pstmData);
SafeRelease(pPropertySet);
// Done
return (NULL == *ppwszVal) ? FALSE : TRUE;
}
// ---------------------------------------------------------------------------
// CMessageBody::SetOption
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetOption(const TYPEDID oid, LPCPROPVARIANT pValue)
{
// check params
if (NULL == pValue)
return TrapError(E_INVALIDARG);
return InternalSetOption(oid, pValue, FALSE, FALSE);
}
// ---------------------------------------------------------------------------
// CMessageBody::InternalSetOption
// ---------------------------------------------------------------------------
HRESULT CMessageBody::InternalSetOption(const TYPEDID oid, LPCPROPVARIANT pValue, BOOL fInternal, BOOL fNoDirty)
{
// Locals
#ifdef SMIME_V3
DWORD cb;
#endif // SMIME_V3
HRESULT hr=S_OK;
DWORD i;
ASSERTINIT;
CAPROPVARIANT capv;
CAUL caul;
CAUH cauh;
CAFILETIME cafiletime;
#ifdef SMIME_V3
BYTE rgb[50];
#endif // SMIME_V3
// Thread Safety
EnterCriticalSection(&m_cs);
// Handle Optid
switch(oid)
{
case OID_SUPPORT_EXTERNAL_BODY:
if (m_rOptions.fExternalBody != (pValue->boolVal ? TRUE : FALSE))
{
m_rOptions.fExternalBody = pValue->boolVal ? TRUE : FALSE;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SHOW_MACBINARY:
if (m_rOptions.fShowMacBin != (pValue->boolVal ? TRUE : FALSE))
{
m_rOptions.fShowMacBin = pValue->boolVal ? TRUE : FALSE;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_CBMAX_BODY_LINE:
if (pValue->ulVal < MIN_CBMAX_BODY_LINE || pValue->ulVal > MAX_CBMAX_BODY_LINE)
{
hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
goto exit;
}
if (m_rOptions.cbMaxLine != pValue->ulVal)
{
m_rOptions.cbMaxLine = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_TRANSMIT_BODY_ENCODING:
if (FALSE == FIsValidBodyEncoding((ENCODINGTYPE)pValue->ulVal))
{
hr = TrapError(MIME_E_INVALID_OPTION_VALUE);
goto exit;
}
if (m_rOptions.ietTransmit != (ENCODINGTYPE)pValue->ulVal)
{
m_rOptions.ietTransmit = (ENCODINGTYPE)pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_WRAP_BODY_TEXT:
if (m_rOptions.fWrapText != (pValue->boolVal ? TRUE : FALSE))
{
m_rOptions.fWrapText = pValue->boolVal ? TRUE : FALSE;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_HIDE_TNEF_ATTACHMENTS:
if (m_rOptions.fHideTNEF != (pValue->boolVal ? TRUE : FALSE))
{
m_rOptions.fHideTNEF = pValue->boolVal ? TRUE : FALSE;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_DBCS_ESCAPE_IS_8BIT:
if (m_rOptions.fDBCSEscape8 != (pValue->boolVal ? TRUE : FALSE))
{
m_rOptions.fDBCSEscape8 = pValue->boolVal ? TRUE : FALSE;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_TYPE:
if (m_rOptions.ulSecurityType != pValue->ulVal)
{
m_rOptions.ulSecurityType = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_ALG_HASH: // innermost signing algorithm
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (CompareBlob(&m_rOptions.rgblobHash[0], &pValue->blob))
{
ReleaseMem(m_rOptions.rgblobHash[0].pBlobData);
hr = HrCopyBlob(&pValue->blob, &m_rOptions.rgblobHash[0]);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_ALG_HASH_RG: // signing algorithms
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobHash, fNoDirty);
break;
case OID_SECURITY_ALG_BULK:
if (CompareBlob(&m_rOptions.blobBulk, &pValue->blob))
{
ReleaseMem(m_rOptions.blobBulk.pBlobData);
hr = HrCopyBlob(&pValue->blob, &m_rOptions.blobBulk);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
#ifndef _WIN64
case OID_SECURITY_CERT_SIGNING:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (m_rOptions.rgpcCertSigning[0] != (PCCERT_CONTEXT)pValue->ulVal)
{
if (m_rOptions.rgpcCertSigning[0])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[0]);
m_rOptions.rgpcCertSigning[0] = CertDuplicateCertificateContext((PCCERT_CONTEXT)pValue->ulVal);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_CERT_SIGNING_RG: // signing algorithms
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
caul = pValue->caul;
Assert(caul.cElems == m_rOptions.cSigners || 0 == m_rOptions.cSigners);
if (m_rOptions.cSigners != caul.cElems && 0 != m_rOptions.cSigners)
{
hr = E_INVALIDARG;
}
else
{
for (i = 0; i < caul.cElems; i++) {
if (m_rOptions.rgpcCertSigning[i] != (PCCERT_CONTEXT)caul.pElems[i])
{
if (m_rOptions.rgpcCertSigning[i])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[i]);
m_rOptions.rgpcCertSigning[i] = CertDuplicateCertificateContext((PCCERT_CONTEXT)caul.pElems[i]);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
break;
case OID_SECURITY_CERT_DECRYPTION:
if (m_rOptions.pcCertDecryption != (PCCERT_CONTEXT)pValue->ulVal)
{
if (m_rOptions.pcCertDecryption)
CertFreeCertificateContext(m_rOptions.pcCertDecryption);
m_rOptions.pcCertDecryption = CertDuplicateCertificateContext((PCCERT_CONTEXT)pValue->ulVal);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_RG_CERT_ENCRYPT:
#ifdef SMIME_V3
for (i=0; i<pValue->caul.cElems; i++)
{
CMS_RECIPIENT_INFO info = {0};
info.pccert = (PCCERT_CONTEXT) pValue->caul.pElems[i];
hr = AddRecipient((i == 0) ? SMIME_RECIPIENT_REPLACE_ALL : 0, 1, &info);
if (FAILED(hr))
{
break;
}
}
if (SUCCEEDED(hr) && !fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
#else // !SMIME_V3
if (SUCCEEDED(hr = _CAULToCERTARRAY(pValue->caul, &m_rOptions.caEncrypt)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
#endif // !SMIME_V3
break;
case OID_SECURITY_HCERTSTORE:
CertCloseStore(m_rOptions.hCertStore, 0);
if (pValue->ulVal)
m_rOptions.hCertStore = CertDuplicateStore((HCERTSTORE)pValue->ulVal);
else
m_rOptions.hCertStore = NULL;
break;
case OID_SECURITY_ENCRYPT_CERT_BAG:
CertCloseStore(m_rOptions.hstoreEncrypt, 0);
if (pValue->ulVal)
m_rOptions.hstoreEncrypt = CertDuplicateStore((HCERTSTORE) pValue->ulVal);
else
m_rOptions.hstoreEncrypt = NULL;
break;
case OID_SECURITY_RG_CERT_BAG:
if (SUCCEEDED(hr = _CAULToCertStore(pValue->caul, &m_rOptions.hCertStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_SEARCHSTORES:
if (SUCCEEDED(hr = _CAULToSTOREARRAY(pValue->caul, &m_rOptions.saSearchStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_RG_IASN:
//N TODO: OID_SECURITY_RG_IASN
if (fInternal)
{
}
else
{
hr = MIME_E_READ_ONLY;
}
break;
// 2 Key implementation
case OID_SECURITY_2KEY_CERT_BAG:
{
hr = S_OK;
// Create a new store if needed
if (m_rOptions.hCertStore == NULL)
{
m_rOptions.hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING,
NULL, 0, NULL);
}
if (m_rOptions.hCertStore == NULL)
{
hr = HrGetLastError();
}
for (i=0; i < (pValue->caul).cElems; i++)
{
if (!CertAddCertificateContextToStore(m_rOptions.hCertStore,
(PCCERT_CONTEXT) IntToPtr((pValue->caul).pElems[i]),
CERT_STORE_ADD_ALWAYS, NULL))
{
hr = HrGetLastError();
}
}
if(hr == S_OK)
{
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
break;
case OID_SECURITY_HCRYPTPROV:
if (m_rOptions.hCryptProv != pValue->ulVal)
{
if (m_rOptions.hCryptProv)
CryptReleaseContext(m_rOptions.hCryptProv, 0);
m_rOptions.hCryptProv = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
#else // _WIN64
case OID_SECURITY_CERT_SIGNING_64:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (m_rOptions.rgpcCertSigning[0] != (PCCERT_CONTEXT)pValue->pulVal)
{
if (m_rOptions.rgpcCertSigning[0])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[0]);
m_rOptions.rgpcCertSigning[0] = CertDuplicateCertificateContext((PCCERT_CONTEXT)pValue->pulVal);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_CERT_SIGNING_RG_64: // signing algorithms
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
cauh = pValue->cauh;
Assert(cauh.cElems == m_rOptions.cSigners || 0 == m_rOptions.cSigners);
if (m_rOptions.cSigners != cauh.cElems && 0 != m_rOptions.cSigners)
{
hr = E_INVALIDARG;
} else {
for (i = 0; i < cauh.cElems; i++)
{
PCCERT_CONTEXT pCert = *(PCCERT_CONTEXT *) (&(cauh.pElems[i]));
if (m_rOptions.rgpcCertSigning[i] != (PCCERT_CONTEXT)(pCert))
{
if (m_rOptions.rgpcCertSigning[i])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[i]);
m_rOptions.rgpcCertSigning[i] = CertDuplicateCertificateContext((PCCERT_CONTEXT)(pCert));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
break;
case OID_SECURITY_CERT_DECRYPTION_64:
if (m_rOptions.pcCertDecryption != (PCCERT_CONTEXT)pValue->pulVal)
{
if (m_rOptions.pcCertDecryption)
CertFreeCertificateContext(m_rOptions.pcCertDecryption);
m_rOptions.pcCertDecryption = CertDuplicateCertificateContext((PCCERT_CONTEXT)pValue->pulVal);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_RG_CERT_ENCRYPT_64:
#ifdef SMIME_V3
for (i=0; i<pValue->cauh.cElems; i++)
{
CMS_RECIPIENT_INFO info = {0};
info.pccert = *((PCCERT_CONTEXT*) &(pValue->cauh.pElems[i]));
hr = AddRecipient((i == 0) ? SMIME_RECIPIENT_REPLACE_ALL : 0, 1, &info);
if (FAILED(hr))
{
break;
}
}
if (SUCCEEDED(hr) && !fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
#else // !SMIME_V3
if (SUCCEEDED(hr = _CAUHToCERTARRAY(pValue->cauh, &m_rOptions.caEncrypt)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
#endif // !SMIME_V3
break;
case OID_SECURITY_HCERTSTORE_64:
CertCloseStore(m_rOptions.hCertStore, 0);
if (pValue->pulVal)
m_rOptions.hCertStore = CertDuplicateStore((HCERTSTORE)pValue->pulVal);
else
m_rOptions.hCertStore = NULL;
break;
case OID_SECURITY_ENCRYPT_CERT_BAG_64:
CertCloseStore(m_rOptions.hstoreEncrypt, 0);
if (pValue->pulVal)
m_rOptions.hstoreEncrypt = CertDuplicateStore((HCERTSTORE) pValue->pulVal);
else
m_rOptions.hstoreEncrypt = NULL;
break;
case OID_SECURITY_RG_CERT_BAG_64:
if (SUCCEEDED(hr = _CAUHToCertStore(pValue->cauh, &m_rOptions.hCertStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_SEARCHSTORES_64:
if (SUCCEEDED(hr = _CAUHToSTOREARRAY(pValue->cauh, &m_rOptions.saSearchStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_RG_IASN_64:
//N TODO: OID_SECURITY_RG_IASN
if (fInternal)
{
}
else
{
hr = MIME_E_READ_ONLY;
}
break;
// 2 Key implementation
case OID_SECURITY_2KEY_CERT_BAG_64:
{
hr = S_OK;
// Create a new store if needed
if (m_rOptions.hCertStore == NULL)
{
m_rOptions.hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING,
NULL, 0, NULL);
}
if (m_rOptions.hCertStore == NULL)
{
hr = HrGetLastError();
}
for (i=0; i < (pValue->cauh).cElems; i++)
{
if (!CertAddCertificateContextToStore(m_rOptions.hCertStore,
*((PCCERT_CONTEXT *) (&((pValue->cauh).pElems[i]))),
CERT_STORE_ADD_ALWAYS, NULL))
{
hr = HrGetLastError();
}
}
if(hr == S_OK)
{
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
break;
case OID_SECURITY_HCRYPTPROV_64:
if (m_rOptions.hCryptProv != (((HCRYPTPROV) (pValue->pulVal))))
{
if (m_rOptions.hCryptProv)
CryptReleaseContext(m_rOptions.hCryptProv, 0);
m_rOptions.hCryptProv = (HCRYPTPROV) pValue->pulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
#endif //_WIN64
case OID_SECURITY_CRL:
if (m_rOptions.hCertStore == NULL)
{
m_rOptions.hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
X509_ASN_ENCODING,
NULL, 0, NULL);
if (m_rOptions.hCertStore == NULL)
{
hr = HrGetLastError();
break;
}
}
if (!CertAddEncodedCRLToStore(m_rOptions.hCertStore, X509_ASN_ENCODING,
pValue->blob.pBlobData,
pValue->blob.cbSize,
CERT_STORE_ADD_ALWAYS, NULL))
{
hr = HrGetLastError();
}
else if (!fNoDirty)
{
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_SYMCAPS:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
#ifdef SMIME_V3
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0],
szOID_RSA_SMIMECapabilities,
pValue->blob.cbSize, pValue->blob.pBlobData);
#else // !SMIME_V3
if (CompareBlob(&m_rOptions.rgblobSymCaps[0], &pValue->blob))
{
ReleaseMem(m_rOptions.rgblobSymCaps[0].pBlobData);
hr = HrCopyBlob(&pValue->blob, &m_rOptions.rgblobSymCaps[0]);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
#endif // SMIME_V3
break;
case OID_SECURITY_SYMCAPS_RG: // symetric capabilities
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
#ifdef SMIME_V3
for (i=0; i<pValue->capropvar.cElems; i++)
{
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i],
szOID_RSA_SMIMECapabilities,
pValue->capropvar.pElems[i].blob.cbSize,
pValue->capropvar.pElems[i].blob.pBlobData);
if (FAILED(hr))
break;
}
#else // !SMIME_V3
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobSymCaps, fNoDirty);
#endif // SMIME_V3
break;
case OID_SECURITY_AUTHATTR:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
#ifdef SMIME_V3
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0],
NULL, pValue->blob.cbSize, pValue->blob.pBlobData);
#else // !SMIME_V3
if (CompareBlob(&m_rOptions.rgblobAuthAttr[0], &pValue->blob))
{
ReleaseMem(m_rOptions.rgblobAuthAttr[0].pBlobData);
hr = HrCopyBlob(&pValue->blob, &m_rOptions.rgblobAuthAttr[0]);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
#endif // SMIME_V3
break;
case OID_SECURITY_AUTHATTR_RG: // authenticated attributes
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
#ifdef SMIME_V3
for (i=0; i<pValue->capropvar.cElems; i++)
{
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i],
NULL, pValue->capropvar.pElems[i].blob.cbSize,
pValue->capropvar.pElems[i].blob.pBlobData);
if (FAILED(hr))
break;
}
#else // !SMIME_V3
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobAuthAttr, fNoDirty);
#endif // SMIME_V3
break;
case OID_SECURITY_UNAUTHATTR:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
#ifdef SMIME_V3
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][0],
NULL, pValue->blob.cbSize, pValue->blob.pBlobData);
#else // !SMIME_V3
if (CompareBlob(&m_rOptions.rgblobUnauthAttr[0], &pValue->blob))
{
ReleaseMem(m_rOptions.rgblobUnauthAttr[0].pBlobData);
hr = HrCopyBlob(&pValue->blob, &m_rOptions.rgblobUnauthAttr[0]);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
#endif // SMIME_V3
break;
case OID_SECURITY_UNAUTHATTR_RG: // unauthenticated attributes
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
#ifdef SMIME_V3
for (i=0; i<pValue->capropvar.cElems; i++)
{
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][i],
NULL, pValue->capropvar.pElems[i].blob.cbSize,
pValue->capropvar.pElems[i].blob.pBlobData);
if (FAILED(hr))
break;
}
#else // !SMIME_V3
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobUnauthAttr, fNoDirty);
#endif // SMIME_V3
break;
case OID_SECURITY_SIGNTIME:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
#ifdef SMIME_V3
if ((pValue->filetime.dwLowDateTime == 0) &&
(pValue->filetime.dwHighDateTime == 0))
{
hr = DeleteAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED, 0,
szOID_RSA_signingTime);
}
else
{
cb = sizeof(rgb);
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
&pValue->filetime, 0, NULL,
rgb, &cb))
{
hr = HrGetLastError();
break;
}
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0],
szOID_RSA_signingTime, cb, rgb);
}
#else // !SMIME_V3
if (CompareFileTime(&m_rOptions.rgftSigning[0], (FILETIME FAR*)&pValue->filetime))
{
CopyMemory(&m_rOptions.rgftSigning[0], &pValue->filetime, sizeof(FILETIME));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
#endif // SMIME_V3
break;
case OID_SECURITY_SIGNTIME_RG: // signing times
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
#ifdef SMIME_V3
for (i=0; i<pValue->cafiletime.cElems; i++)
{
if ((pValue->cafiletime.pElems[i].dwLowDateTime == 0) &&
(pValue->cafiletime.pElems[i].dwHighDateTime == 0))
{
hr = DeleteAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED, 0,
szOID_RSA_signingTime);
}
else
{
cb = sizeof(rgb);
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
&pValue->cafiletime.pElems[i], 0, NULL,
rgb, &cb))
{
hr = HrGetLastError();
break;
}
hr = _HrSetAttribute(0, &m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i],
szOID_RSA_signingTime, cb, rgb);
}
}
#else // !SMIME_V3
cafiletime = pValue->cafiletime;
Assert(cafiletime.cElems == m_rOptions.cSigners);
if (m_rOptions.cSigners != cafiletime.cElems)
{
hr = E_INVALIDARG;
} else {
for (i = 0; i < cafiletime.cElems; i++)
{
if (CompareFileTime(&m_rOptions.rgftSigning[i], (FILETIME FAR*)&cafiletime.pElems[i]))
{
CopyMemory(&m_rOptions.rgftSigning[i], &cafiletime.pElems[i], sizeof(FILETIME));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
#endif // SMIME_V3
break;
case OID_SECURITY_USER_VALIDITY:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (m_rOptions.rgulUserDef[0] != pValue->ulVal)
{
m_rOptions.rgulUserDef[0] = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_USER_VALIDITY_RG: // user validity flags
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
caul = pValue->caul;
Assert(caul.cElems == m_rOptions.cSigners || 0 == m_rOptions.cSigners);
if (m_rOptions.cSigners != caul.cElems && 0 != m_rOptions.cSigners)
{
hr = E_INVALIDARG;
} else {
for (i = 0; i < caul.cElems; i++)
{
if (m_rOptions.rgulUserDef[i] != caul.pElems[i])
{
m_rOptions.rgulUserDef[i] = caul.pElems[i];
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
break;
case OID_SECURITY_RO_MSG_VALIDITY:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (fInternal)
{
if (m_rOptions.rgulROValid[0] != pValue->ulVal)
{
m_rOptions.rgulROValid[0] = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
else
{
hr = MIME_E_READ_ONLY;
}
break;
case OID_SECURITY_RO_MSG_VALIDITY_RG: // message validity flags
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
caul = pValue->caul;
Assert(caul.cElems == m_rOptions.cSigners || 0 == m_rOptions.cSigners);
if (m_rOptions.cSigners != caul.cElems && 0 != m_rOptions.cSigners)
{
hr = E_INVALIDARG;
}
else
{
for (i = 0; i < caul.cElems; i++)
{
if (m_rOptions.rgulROValid[i] != caul.pElems[i])
{
m_rOptions.rgulROValid[i] = caul.pElems[i];
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
break;
case OID_SECURITY_ENCODE_FLAGS:
if (m_rOptions.ulEncodeFlags != pValue->ulVal)
{
m_rOptions.ulEncodeFlags = pValue->ulVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_CERT_INCLUDED:
if (fInternal)
{
if (m_rOptions.fCertWithMsg != pValue->boolVal)
{
m_rOptions.fCertWithMsg = pValue->boolVal;
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
else
{
hr = MIME_E_READ_ONLY;
}
break;
#ifndef _WIN64
case OID_SECURITY_HWND_OWNER:
m_rOptions.hwndOwner = HWND(pValue->ulVal);
break;
#endif
case OID_SECURITY_REQUESTED_CTE:
if (m_rOptions.ietRequested != pValue->lVal)
{
m_rOptions.ietRequested = ENCODINGTYPE(pValue->lVal);
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_SIGNATURE_COUNT:
// M00BUG - I just found out that if lVal >0 but lVal < m_rOptions.cSigners
// then we don't do any adjustments to handle this case.
if (pValue->lVal == 0)
{
if (m_rOptions.cSigners)
{
// OID_SECURITY_ALG_HASH
SafeMemFree(m_rOptions.rgblobHash[0].pBlobData);
// OID_SECURITY_CERT_SIGNING
for (i = 0; i < m_rOptions.cSigners; i++)
{
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[i]);
#ifdef SMIME_V3
// Attributes
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i]);
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][i]);
// OID_SECURITY_RECEIPT_RG
SafeMemFree(m_rOptions.rgblobReceipt[i].pBlobData);
// OID_SECURITY_MESSAGE_HASH_RG
SafeMemFree(m_rOptions.rgblobMsgHash[i].pBlobData);
// OID_SECURITY_KEY_PROMPT
SafeMemFree(m_rOptions.pwszKeyPrompt);
#else // !SMIME_V3
// OID_SECURITY_SYMCAPS
SafeMemFree(m_rOptions.rgblobSymCaps[i].pBlobData);
// OID_SECURITY_AUTHATTR
SafeMemFree(m_rOptions.rgblobAuthAttr[i].pBlobData);
// OID_SECURITY_UNAUTHATTR
SafeMemFree(m_rOptions.rgblobUnauthAttr[i].pBlobData);
#endif // SMIME_V3
}
// OID_SECURITY_HCERTSTORE
CertCloseStore(m_rOptions.hCertStore, 0);
m_rOptions.hCertStore = NULL;
_FreeLayerArrays();
m_rOptions.cSigners = 0;
}
}
else if (m_rOptions.cSigners <= pValue->ulVal)
{
hr = _HrEnsureBodyOptionLayers(pValue);
}
break;
#ifdef SMIME_V3
case OID_SECURITY_RECEIPT_RG:
if (fInternal)
{
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobReceipt, fNoDirty);
}
else
hr = MIME_E_READ_ONLY;
break;
case OID_SECURITY_MESSAGE_HASH_RG:
if (fInternal)
{
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
hr = _CompareCopyBlobArray(pValue, &m_rOptions.rgblobMsgHash, fNoDirty);
}
else
hr = MIME_E_READ_ONLY;
break;
case OID_SECURITY_KEY_PROMPT:
if ((m_rOptions.pwszKeyPrompt == NULL) ||
(pValue->pwszVal == NULL) ||
(lstrcmpW(m_rOptions.pwszKeyPrompt,pValue->pwszVal) != 0))
{
SafeMemFree(m_rOptions.pwszKeyPrompt);
if (pValue->pwszVal != NULL)
{
m_rOptions.pwszKeyPrompt = PszDupW(pValue->pwszVal);
if (NULL == m_rOptions.pwszKeyPrompt)
hr = E_OUTOFMEMORY;
}
}
break;
#endif // SMIME_V3
case OID_NOSECURITY_ONSAVE:
m_rOptions.fNoSecurityOnSave = !!pValue->boolVal;
break;
#ifdef _WIN65
// (YST) This was checked in by BriMo at 01/22/99
case OID_SECURITY_CERT_SIGNING2:
if (FAILED(hr = _HrEnsureBodyOptionLayers(1)))
{
break;
}
if (m_rOptions.rgpcCertSigning[0] != *(PCCERT_CONTEXT *)(&(pValue->uhVal)))
{
if (m_rOptions.rgpcCertSigning[0])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[0]);
m_rOptions.rgpcCertSigning[0] = CertDuplicateCertificateContext(*(PCCERT_CONTEXT *)(&(pValue->uhVal)));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_CERT_SIGNING_RG2: // signing algorithms
if (FAILED(hr = _HrEnsureBodyOptionLayers(pValue)))
{
break;
}
cauh = pValue->cauh;
Assert(cauh.cElems == m_rOptions.cSigners || 0 == m_rOptions.cSigners);
if (m_rOptions.cSigners != cauh.cElems && 0 != m_rOptions.cSigners)
{
hr = E_INVALIDARG;
} else {
for (i = 0; i < cauh.cElems; i++)
{
if (m_rOptions.rgpcCertSigning[i] != (PCCERT_CONTEXT)(cauh.pElems[i]))
{
if (m_rOptions.rgpcCertSigning[i])
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[i]);
m_rOptions.rgpcCertSigning[i] = CertDuplicateCertificateContext((PCCERT_CONTEXT )(cauh.pElems[i]));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
break;
case OID_SECURITY_CERT_DECRYPTION2:
if (m_rOptions.pcCertDecryption != *(PCCERT_CONTEXT *)(&(pValue->uhVal)))
{
if (m_rOptions.pcCertDecryption)
CertFreeCertificateContext(m_rOptions.pcCertDecryption);
m_rOptions.pcCertDecryption = CertDuplicateCertificateContext(*(PCCERT_CONTEXT *)(&(pValue->uhVal)));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
case OID_SECURITY_RG_CERT_ENCRYPT2:
if (SUCCEEDED(hr = _CAUHToCERTARRAY(pValue->cauh, &m_rOptions.caEncrypt)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_HCERTSTORE2:
CertCloseStore(m_rOptions.hCertStore, 0);
if (pValue->ulVal)
m_rOptions.hCertStore = CertDuplicateStore(*(HCERTSTORE *)(&(pValue->uhVal)));
else
m_rOptions.hCertStore = NULL;
break;
case OID_SECURITY_RG_CERT_BAG2:
if (SUCCEEDED(hr = _CAUHToCertStore(pValue->cauh, &m_rOptions.hCertStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_SEARCHSTORES2:
if (SUCCEEDED(hr = _CAUHToSTOREARRAY(pValue->cauh, &m_rOptions.saSearchStore)))
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
break;
case OID_SECURITY_RG_IASN2:
//N TODO: OID_SECURITY_RG_IASN2
if (fInternal)
{
}
else
{
hr = MIME_E_READ_ONLY;
}
break;
case OID_SECURITY_HCRYPTPROV2:
if (m_rOptions.hCryptProv != *(HCRYPTPROV *)(&(pValue->uhVal)))
{
if (m_rOptions.hCryptProv)
CryptReleaseContext(m_rOptions.hCryptProv, 0);
m_rOptions.hCryptProv = *(HCRYPTPROV *)(&(pValue->uhVal));
if (!fNoDirty)
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
break;
// End of BriMo checkin
#endif // _WIN65
#ifdef _WIN64
case OID_SECURITY_HWND_OWNER_64:
m_rOptions.hwndOwner = (HWND)(pValue->pulVal);
break;
#endif // _WIN64
default:
hr = m_pContainer->SetOption(oid, pValue);
break;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return TrapError(hr);
}
// ---------------------------------------------------------------------------
// CMessageBody::GetOption
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetOption(const TYPEDID oid, LPPROPVARIANT pValue)
{
// Locals
DWORD cb;
HRESULT hr=S_OK;
DWORD i;
LPBYTE pb;
ASSERTINIT;
ULONG iLayer;
#ifdef SMIME_V3
CRYPT_ATTRIBUTE UNALIGNED *pattr;
#endif // SMIME_V3
#ifdef _WIN64
void UNALIGNED *pv = NULL;
PCCERT_CONTEXT pTmpCert = NULL;
PCCERT_CONTEXT pcCert = NULL;
#endif // _WIN64
CRYPT_ATTR_BLOB UNALIGNED *pVal = NULL;
// check params
if (NULL == pValue)
return TrapError(E_INVALIDARG);
pValue->vt = TYPEDID_TYPE(oid);
// Thread Safety
EnterCriticalSection(&m_cs);
// Handle Optid
switch(oid)
{
case OID_SUPPORT_EXTERNAL_BODY:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fExternalBody;
break;
case OID_SHOW_MACBINARY:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fShowMacBin;
break;
case OID_CBMAX_BODY_LINE:
pValue->ulVal = m_rOptions.cbMaxLine;
break;
case OID_TRANSMIT_BODY_ENCODING:
pValue->ulVal = (ULONG)m_rOptions.ietTransmit;
break;
case OID_WRAP_BODY_TEXT:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fWrapText;
break;
case OID_HIDE_TNEF_ATTACHMENTS:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fHideTNEF;
break;
case OID_DBCS_ESCAPE_IS_8BIT:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fDBCSEscape8;
break;
case OID_SECURITY_TYPE:
pValue->ulVal = m_rOptions.ulSecurityType;
break;
case OID_SECURITY_ALG_HASH:
if (m_rOptions.cSigners)
{
hr = HrCopyBlob(&m_rOptions.rgblobHash[0], &pValue->blob);
}
else
{
hr = HrCopyBlob(&blobNULL, &pValue->blob);
}
break;
case OID_SECURITY_ALG_HASH_RG:
hr = HrCopyBlobArray(m_rOptions.rgblobHash, m_rOptions.cSigners, pValue);
break;
case OID_SECURITY_ALG_BULK:
hr = HrCopyBlob(&m_rOptions.blobBulk, &pValue->blob);
break;
#ifndef _WIN64
case OID_SECURITY_CERT_SIGNING:
if (m_rOptions.cSigners)
pValue->ulVal = (ULONG)CertDuplicateCertificateContext(m_rOptions.rgpcCertSigning[0]);
else
pValue->ulVal = 0; // ?
break;
case OID_SECURITY_CERT_SIGNING_RG:
hr = HrCopyDwordArray((ULONG*)m_rOptions.rgpcCertSigning, m_rOptions.cSigners, pValue);
// Duplicate the certs in place.
for (iLayer = 0; iLayer < m_rOptions.cSigners; iLayer++)
{
pValue->caul.pElems[iLayer] = (ULONG)CertDuplicateCertificateContext((PCCERT_CONTEXT)pValue->caul.pElems[iLayer]);
}
break;
case OID_SECURITY_CERT_DECRYPTION:
pValue->ulVal = (ULONG)CertDuplicateCertificateContext(m_rOptions.pcCertDecryption);
break;
#ifndef SMIME_V3
case OID_SECURITY_RG_CERT_ENCRYPT:
hr = _CERTARRAYToCAUL(m_rOptions.caEncrypt, &pValue->caul);
break;
#endif // !SMIEM_V3
case OID_SECURITY_HCERTSTORE:
pValue->ulVal = 0;
if (m_rOptions.hCertStore)
pValue->ulVal = (ULONG)CertDuplicateStore(m_rOptions.hCertStore);
break;
case OID_SECURITY_ENCRYPT_CERT_BAG:
pValue->ulVal = 0;
if (m_rOptions.hstoreEncrypt != NULL)
pValue->ulVal = (ULONG) CertDuplicateStore(m_rOptions.hstoreEncrypt);
break;
case OID_SECURITY_RG_CERT_BAG:
hr = _CertStoreToCAUL(m_rOptions.hCertStore, &pValue->caul);
break;
case OID_SECURITY_SEARCHSTORES:
hr = _STOREARRAYToCAUL(m_rOptions.saSearchStore, &pValue->caul);
break;
case OID_SECURITY_RG_IASN:
Assert(FALSE);
//N TODO: OID_SECURITY_RG_IASN
break;
case OID_SECURITY_HCRYPTPROV:
pValue->ulVal = m_rOptions.hCryptProv;
m_rOptions.hCryptProv = NULL; // read-once
break;
#else //_WIN64
case OID_SECURITY_CERT_SIGNING_64:
if (m_rOptions.cSigners)
pValue->pulVal = (ULONG *)CertDuplicateCertificateContext(m_rOptions.rgpcCertSigning[0]);
else
pValue->pulVal = 0; // ?
break;
case OID_SECURITY_CERT_SIGNING_RG_64:
hr = HrCopyIntoUlonglongArray((ULARGE_INTEGER *)m_rOptions.rgpcCertSigning, m_rOptions.cSigners, pValue);
// Duplicate the certs in place.
if(m_rOptions.cSigners > 0)
{
for (iLayer = 0; iLayer < m_rOptions.cSigners; iLayer++)
{
pv = (void*) (&(pValue->cauh.pElems[iLayer]));
pTmpCert = *((PCCERT_CONTEXT *) pv);
pcCert = CertDuplicateCertificateContext(pTmpCert);
pValue->cauh.pElems[iLayer] = *((ULARGE_INTEGER *)(&(pcCert)));
}
}
break;
case OID_SECURITY_CERT_DECRYPTION_64:
pValue->pulVal = (ULONG *)CertDuplicateCertificateContext(m_rOptions.pcCertDecryption);
break;
#ifndef SMIME_V3
case OID_SECURITY_RG_CERT_ENCRYPT_64:
hr = _CERTARRAYToCAUH(m_rOptions.caEncrypt, &pValue->cauh);
break;
#endif // !SMIEM_V3
case OID_SECURITY_HCERTSTORE_64:
pValue->pulVal = 0;
if (m_rOptions.hCertStore)
pValue->pulVal = (ULONG *)CertDuplicateStore(m_rOptions.hCertStore);
break;
case OID_SECURITY_ENCRYPT_CERT_BAG_64:
pValue->pulVal = 0;
if (m_rOptions.hstoreEncrypt != NULL)
pValue->pulVal = (ULONG *) CertDuplicateStore(m_rOptions.hstoreEncrypt);
break;
case OID_SECURITY_RG_CERT_BAG_64:
hr = _CertStoreToCAUH(m_rOptions.hCertStore, &pValue->cauh);
break;
case OID_SECURITY_SEARCHSTORES_64:
hr = _STOREARRAYToCAUH(m_rOptions.saSearchStore, &pValue->cauh);
break;
case OID_SECURITY_RG_IASN_64:
Assert(FALSE);
//N TODO: OID_SECURITY_RG_IASN
break;
case OID_SECURITY_HCRYPTPROV_64:
pValue->pulVal = (ULONG *) (m_rOptions.hCryptProv);
m_rOptions.hCryptProv = NULL; // read-once
break;
#endif //_WIN64
case OID_SECURITY_CRL:
// hr = HrCopyBlob(&m_rOptions.blobCRL, &pValue->blob);
Assert(FALSE);
// M00BUG -- MUST IMPLEMENT THIS
break;
case OID_SECURITY_SYMCAPS:
#ifdef SMIME_V3
pattr = _FindAttribute(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0],
szOID_RSA_SMIMECapabilities, 0);
if (pattr != NULL)
{
pVal = &(pattr->rgValue[0]);
Assert(pattr->cValue == 1);
if (!MemAlloc((LPVOID UNALIGNED *) &pValue->blob.pBlobData,
pVal->cbData))
{
hr = E_OUTOFMEMORY;
break;
}
pValue->blob.cbSize = pVal->cbData;
memcpy(pValue->blob.pBlobData, pVal->pbData,
pVal->cbData);
}
else {
pValue->blob.cbSize = 0;
pValue->blob.pBlobData = NULL;
}
pValue->vt = VT_BLOB;
#else // !SMIME_V3
if (m_rOptions.cSigners)
{
hr = HrCopyBlob(&m_rOptions.rgblobSymCaps[0], &pValue->blob);
} else {
hr = HrCopyBlob(&blobNULL, &pValue->blob);
}
#endif // SMIME_V3
break;
case OID_SECURITY_SYMCAPS_RG:
#ifdef SMIME_V3
hr = _HrGetAttrs(m_rOptions.cSigners, m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED],
szOID_RSA_SMIMECapabilities, pValue);
#else // !SMIME_V3
hr = HrCopyBlobArray(m_rOptions.rgblobSymCaps, m_rOptions.cSigners, pValue);
#endif // SMIME_V3
break;
case OID_SECURITY_AUTHATTR:
if (m_rOptions.cSigners)
{
#ifdef SMIME_V3
memset(pValue, 0, sizeof(*pValue));
pValue->vt = VT_BLOB;
if ((m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0] != NULL) &&
!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_Microsoft_Attribute_Sequence,
m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0],
CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &pValue->blob.pBlobData,
&pValue->blob.cbSize))
{
hr = HrGetLastError();
}
#else // !SMIME_V3
hr = HrCopyBlob(&m_rOptions.rgblobAuthAttr[0], &pValue->blob);
#endif // SMIME_V3
}
else
{
hr = HrCopyBlob(&blobNULL, &pValue->blob);
}
break;
case OID_SECURITY_AUTHATTR_RG:
#ifdef SMIME_V3
hr = _HrGetAttrs(m_rOptions.cSigners, m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED], NULL, pValue);
#else // !SMIME_V3
hr = HrCopyBlobArray(m_rOptions.rgblobAuthAttr, m_rOptions.cSigners, pValue);
#endif // SMIME_V3
break;
case OID_SECURITY_UNAUTHATTR:
if (m_rOptions.cSigners)
{
#ifdef SMIME_V3
memset(pValue, 0, sizeof(*pValue));
pValue->vt = VT_BLOB;
if ((m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][0] != NULL) &&
!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_Microsoft_Attribute_Sequence,
m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][0],
CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &pValue->blob.pBlobData,
&pValue->blob.cbSize))
{
hr = HrGetLastError();
}
#else // !SMIME_V3
hr = HrCopyBlob(&m_rOptions.rgblobUnauthAttr[0], &pValue->blob);
#endif // SMIME_V3
} else {
hr = HrCopyBlob(&blobNULL, &pValue->blob);
}
break;
case OID_SECURITY_UNAUTHATTR_RG:
#ifdef SMIME_V3
hr = _HrGetAttrs(m_rOptions.cSigners, m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED], NULL, pValue);
#else // !SMIME_V3
hr = HrCopyBlobArray(m_rOptions.rgblobUnauthAttr, m_rOptions.cSigners, pValue);
#endif // SMIME_V3
break;
case OID_SECURITY_SIGNTIME:
#ifdef SMIME_V3
pattr = _FindAttribute(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][0], szOID_RSA_signingTime, 0);
pValue->vt = VT_FILETIME;
if (pattr == NULL)
{
pValue->filetime.dwLowDateTime = 0;
pValue->filetime.dwHighDateTime = 0;
}
else
{
cb = sizeof(pValue->filetime);
Assert(pattr->cValue == 1);
pVal = &(pattr->rgValue[0]);
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
pVal->pbData,
pVal->cbData,
0, NULL, &pValue->filetime, &cb))
{
hr = HrGetLastError();
break;
}
}
#else // !SMIME_V3
if (m_rOptions.cSigners)
{
CopyMemory(&pValue->filetime, &m_rOptions.rgftSigning[0], sizeof(FILETIME));
}
else
{
hr = HrCopyBlob(&blobNULL, &pValue->blob);
}
#endif // SMIME_V3
break;
case OID_SECURITY_SIGNTIME_RG:
#ifdef SMIME_V3
pValue->vt = VT_VECTOR | VT_VARIANT;
pValue->capropvar.cElems = m_rOptions.cSigners;
if (m_rOptions.cSigners > 0)
{
hr = HrAlloc((LPVOID *) &pValue->capropvar.pElems,
m_rOptions.cSigners * sizeof(PROPVARIANT));
if (FAILED(hr))
{
break;
}
memset(pValue->capropvar.pElems, 0, m_rOptions.cSigners * sizeof(PROPVARIANT));
for (i=0; i<m_rOptions.cSigners; i++)
{
pValue->capropvar.pElems[i].vt = VT_FILETIME;
pattr = _FindAttribute(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i],
szOID_RSA_signingTime, 0);
if (pattr != NULL)
{
cb = sizeof(pValue->filetime);
Assert(pattr->cValue == 1);
pVal = &(pattr->rgValue[0]);
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
pVal->pbData,
pVal->cbData, 0, NULL,
&pValue->capropvar.pElems[i].filetime,
&cb))
{
hr = HrGetLastError();
MemFree(pValue->capropvar.pElems);
pValue->capropvar.pElems = NULL;
break;
}
}
}
}
#else // !SMIME_V3
hr = HrCopyFiletimeArray(m_rOptions.rgftSigning, m_rOptions.cSigners, pValue);
#endif // SMIME_V3
break;
case OID_SECURITY_USER_VALIDITY:
if (m_rOptions.cSigners)
{
pValue->ulVal = m_rOptions.rgulUserDef[0];
} else {
pValue->ulVal = 0;
}
break;
case OID_SECURITY_USER_VALIDITY_RG:
hr = HrCopyDwordArray(m_rOptions.rgulUserDef, m_rOptions.cSigners, pValue);
break;
case OID_SECURITY_RO_MSG_VALIDITY:
if (m_rOptions.cSigners)
{
pValue->ulVal = m_rOptions.rgulROValid[0];
} else {
pValue->ulVal = 0;
}
break;
case OID_SECURITY_RO_MSG_VALIDITY_RG:
hr = HrCopyDwordArray(m_rOptions.rgulROValid, m_rOptions.cSigners, pValue);
break;
case OID_SECURITY_ENCODE_FLAGS:
pValue->ulVal = m_rOptions.ulEncodeFlags;
break;
case OID_SECURITY_CERT_INCLUDED:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fCertWithMsg;
break;
#ifndef _WIN64
case OID_SECURITY_HWND_OWNER:
pValue->ulVal = ULONG(m_rOptions.hwndOwner);
break;
#endif // _WIN64
case OID_SECURITY_REQUESTED_CTE:
pValue->lVal = m_rOptions.ietRequested;
break;
case OID_SECURITY_SIGNATURE_COUNT:
pValue->ulVal = ULONG(m_rOptions.cSigners);
break;
#ifdef SMIME_V3
case OID_SECURITY_RECEIPT_RG:
hr = HrCopyBlobArray(m_rOptions.rgblobReceipt, m_rOptions.cSigners, pValue);
break;
case OID_SECURITY_MESSAGE_HASH_RG:
hr = HrCopyBlobArray(m_rOptions.rgblobMsgHash, m_rOptions.cSigners, pValue);
break;
case OID_SECURITY_KEY_PROMPT:
ZeroMemory(&(pValue->pwszVal), sizeof(pValue->pwszVal));
if (m_rOptions.pwszKeyPrompt != NULL)
{
pValue->pwszVal = PszDupW(m_rOptions.pwszKeyPrompt);
if (NULL == pValue->pwszVal)
hr = E_OUTOFMEMORY;
}
break;
#endif // SMIME_V3
case OID_NOSECURITY_ONSAVE:
pValue->boolVal = (VARIANT_BOOL) !!m_rOptions.fNoSecurityOnSave;
break;
#ifdef _WIN65
// BriMo checkin at 01/22/99
case OID_SECURITY_CERT_SIGNING2:
if (m_rOptions.cSigners)
{
PCCERT_CONTEXT pcCert = CertDuplicateCertificateContext(m_rOptions.rgpcCertSigning[0]);
pValue->uhVal = *(ULARGE_INTEGER *)(&(pcCert));
} else {
ZeroMemory(&(pValue->uhVal), sizeof(pValue->uhVal));
}
break;
case OID_SECURITY_CERT_SIGNING_RG2:
hr = HrCopyIntoUlonglongArray((ULARGE_INTEGER *)m_rOptions.rgpcCertSigning, m_rOptions.cSigners, pValue);
// Duplicate the certs in place.
for (iLayer = 0; iLayer < m_rOptions.cSigners; iLayer++)
{
PCCERT_CONTEXT pcCert = CertDuplicateCertificateContext((PCCERT_CONTEXT)(pValue->cauh.pElems[iLayer]));
pValue->cauh.pElems[iLayer] = *(ULARGE_INTEGER *)(&(pcCert));
}
break;
case OID_SECURITY_CERT_DECRYPTION2:
{
PCCERT_CONTEXT pcCert = CertDuplicateCertificateContext(m_rOptions.pcCertDecryption);
pValue->uhVal = *(ULARGE_INTEGER *)(&(pcCert));
}
break;
case OID_SECURITY_RG_CERT_ENCRYPT2:
hr = _CERTARRAYToCAUH(m_rOptions.caEncrypt, &pValue->cauh);
break;
case OID_SECURITY_HCERTSTORE2:
ZeroMemory(&(pValue->uhVal), sizeof(pValue->uhVal));
if (m_rOptions.hCertStore)
{
HCERTSTORE hCertStore = CertDuplicateStore(m_rOptions.hCertStore);
pValue->uhVal = *(ULARGE_INTEGER *)(&(hCertStore));
}
break;
case OID_SECURITY_RG_CERT_BAG2:
hr = _CertStoreToCAUH(m_rOptions.hCertStore, &pValue->cauh);
break;
case OID_SECURITY_SEARCHSTORES2:
hr = _STOREARRAYToCAUH(m_rOptions.saSearchStore, &pValue->cauh);
break;
case OID_SECURITY_RG_IASN2:
Assert(FALSE);
//N TODO: OID_SECURITY_RG_IASN
break;
case OID_SECURITY_HCRYPTPROV2:
pValue->uhVal = *(ULARGE_INTEGER *)(&(m_rOptions.hCryptProv));
m_rOptions.hCryptProv = NULL; // read-once
break;
// End of BriMo check-in
#endif // _WIN65
#ifdef _WIN64
case OID_SECURITY_HWND_OWNER_64:
pValue->pulVal = (ULONG *)(m_rOptions.hwndOwner);
break;
#endif _WIN64
default:
hr = m_pContainer->GetOption(oid, pValue);
break;
}
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::InitNew
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::InitNew(void)
{
// Locals
HRESULT hr=S_OK;
// Thread Safety
EnterCriticalSection(&m_cs);
// Release Lock Bytes
EmptyData();
// These flags are based on the data objects
m_dwState = 0;
// Change Size
m_cbExternal = 0xFFFFFFFF;
// Have I Created my property set yet ?
if (NULL == m_pContainer)
{
// Create Init
CHECKALLOC(m_pContainer = new CMimePropertyContainer);
}
// Reset the property set
CHECKHR(hr = m_pContainer->InitNew());
// Reset m_pCsetTagged
m_pCsetTagged = NULL;
// Reset Options
_FreeOptions();
// Reset to default options (this is probably a bug)
CopyMemory(&m_rOptions, &g_rDefBodyOptions, sizeof(BODYOPTIONS));
// (t-erikne) need to get this default at run time
m_rOptions.hwndOwner = HWND_DESKTOP;
// Reset Charset
m_pCharset = CIntlGlobals::GetDefBodyCset();
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::EmptyData
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::EmptyData(void)
{
// Thread Safety
EnterCriticalSection(&m_cs);
// Free current display name
SafeMemFree(m_pszDisplay);
// Do I have Data
SafeRelease(m_rStorage.pUnkRelease);
// Zero
ZeroMemory(&m_rStorage, sizeof(BODYSTORAGE));
// Removed CSETTAGGED state
FLAGCLEAR(m_dwState, BODYSTATE_CSETTAGGED);
FLAGCLEAR(m_dwState, BODYSTATE_EXTERNAL);
// Change Size
m_cbExternal = 0xFFFFFFFF;
// Reset Encoding
m_ietEncoding = IET_7BIT;
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::SetState
// --------------------------------------------------------------------------------
void CMessageBody::SetState(DWORD dwState)
{
EnterCriticalSection(&m_cs);
FLAGSET(m_dwState, dwState);
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::ClearState
// --------------------------------------------------------------------------------
void CMessageBody::ClearState(DWORD dwState)
{
EnterCriticalSection(&m_cs);
FLAGCLEAR(m_dwState, dwState);
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::ClearDirty
// --------------------------------------------------------------------------------
void CMessageBody::ClearDirty(void)
{
ASSERTINIT;
EnterCriticalSection(&m_cs);
FLAGCLEAR(m_dwState, BODYSTATE_DIRTY);
m_pContainer->ClearState(COSTATE_DIRTY);
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::IsType
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::IsType(IMSGBODYTYPE type)
{
// Locals
HRESULT hr;
STATSTG rStat;
ASSERTINIT;
// Thread Safety
EnterCriticalSection(&m_cs);
// Handle Type
if (IBT_SECURE == type)
{
// Is Secure Flag Set
hr = (ISFLAGSET(m_dwState, BODYSTATE_SECURE)) ? S_OK : S_FALSE;
}
// Charset Tagged
else if (IBT_CSETTAGGED == type)
{
hr = ISFLAGSET(m_dwState, BODYSTATE_CSETTAGGED) ? S_OK : S_FALSE;
}
// Is this an attachment
else if (IBT_ATTACHMENT == type)
{
// If container returns IMF_ATTACHMENTS, it must be an attachment
DWORD dw = m_pContainer->DwGetMessageFlags(m_rOptions.fHideTNEF);
hr = (ISFLAGSET(dw, IMF_ATTACHMENTS) || ISFLAGSET(dw, IMF_HASVCARD)) ? S_OK : S_FALSE;
}
// Was AUTOATTACH
else if (IBT_AUTOATTACH == type)
{
hr = (m_pNode && ISFLAGSET(m_pNode->dwState, NODESTATE_AUTOATTACH)) ? S_OK : S_FALSE;
}
// Is the body empty
else if (IBT_EMPTY == type)
{
// Body is not empty if it is a multipart with children
if (m_pContainer->IsContentType(STR_CNT_MULTIPART, NULL) == S_OK && m_pNode && m_pNode->cChildren > 0)
hr = S_FALSE;
else if (m_rStorage.pUnkRelease)
hr = S_FALSE;
else
hr = S_OK;
}
// Error
else
{
hr = TrapError(MIME_E_INVALID_BODYTYPE);
goto exit;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::IsDirty
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::IsDirty(void)
{
ASSERTINIT;
EnterCriticalSection(&m_cs);
HRESULT hr = (ISFLAGSET(m_dwState, BODYSTATE_DIRTY) || m_pContainer->IsDirty() == S_OK) ? S_OK : S_FALSE;
LeaveCriticalSection(&m_cs);
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetOffsets
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetOffsets(LPBODYOFFSETS pOffsets)
{
// Locals
HRESULT hr=S_OK;
ASSERTINIT;
// Invalid Arg
if (NULL == pOffsets)
return TrapError(E_INVALIDARG);
// Init
ZeroMemory(pOffsets, sizeof(BODYOFFSETS));
// Thread Safety
EnterCriticalSection(&m_cs);
// Not Bound
if (NULL == m_pNode || (0 == m_pNode->cbBodyStart && 0 == pOffsets->cbBodyEnd))
{
hr = TrapError(MIME_E_NO_DATA);
goto exit;
}
// Get Offset Info
pOffsets->cbBoundaryStart = m_pNode->cbBoundaryStart;
pOffsets->cbHeaderStart = m_pNode->cbHeaderStart;
pOffsets->cbBodyStart = m_pNode->cbBodyStart;
pOffsets->cbBodyEnd = m_pNode->cbBodyEnd;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetEstimatedSize
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetEstimatedSize(ENCODINGTYPE ietEncoding, ULONG *pcbSize)
{
// Locals
HRESULT hr=S_OK;
ULONG cbSize=0;
STATSTG rStat;
DOCCONVTYPE dctConvert;
ILockBytes *plb=NULL;
ASSERTINIT;
// Parameter Check
if (NULL == pcbSize)
return TrapError(E_INVALIDARG);
if (ietEncoding >= IET_UNKNOWN)
return TrapError(MIME_E_INVALID_ENCODINGTYPE);
// Thread Safety
EnterCriticalSection(&m_cs);
// If external..
if (ISFLAGSET(m_dwState, BODYSTATE_EXTERNAL) && TRUE == m_rOptions.fExternalBody && 0xFFFFFFFF != m_cbExternal)
{
*pcbSize = m_cbExternal;
goto exit;
}
// No internal lockbytes yet ?
CHECKHR(hr = HrGetLockBytes(&plb));
// Query m_pLockBytes Size
CHECKHR(hr = plb->Stat(&rStat, STATFLAG_NONAME));
cbSize = (ULONG)rStat.cbSize.QuadPart;
// Otheriwse
if (IET_CURRENT != ietEncoding)
{
// Compute ietEncoding type...
dctConvert = g_rgConversionMap[m_pContainer->GetEncodingType()].rgDestType[ietEncoding];
// Handle Conversion type
if (DCT_ENCODE == dctConvert)
cbSize = (ULONG)((cbSize * 4) / 3);
else if (DCT_DECODE == dctConvert)
cbSize = (ULONG)((cbSize * 3) / 4);
}
// Set Return Size
*pcbSize = cbSize;
exit:
// Cleanup
SafeRelease(plb);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::SaveToFile
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SaveToFile(ENCODINGTYPE ietEncoding, LPCSTR pszFilePath)
{
// Locals
HRESULT hr=S_OK;
LPWSTR pszFilePathW=NULL;
// Trace
TraceCall("CMessageBody::SaveToFile");
// Convert
IF_NULLEXIT(pszFilePathW = PszToUnicode(CP_ACP, pszFilePath));
// Do it as unicode
IF_FAILEXIT(hr = SaveToFileW(ietEncoding, pszFilePathW));
exit:
// Cleanup
MemFree(pszFilePathW);
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CMessageBody::SaveToFileW
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SaveToFileW(ENCODINGTYPE ietEncoding, LPCWSTR pszFilePath)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
LPSTREAM pstmFile=NULL,
pstmBody=NULL;
ASSERTINIT;
// Parameter Check
if (NULL == pszFilePath)
return TrapError(E_INVALIDARG);
if (ietEncoding >= IET_UNKNOWN)
return TrapError(MIME_E_INVALID_ENCODINGTYPE);
// Thread Safety
EnterCriticalSection(&m_cs);
// Call Get Data
CHECKHR(hr = GetData(ietEncoding, &pstmBody));
// Open File Stream
CHECKHR(hr = OpenFileStreamW((LPWSTR)pszFilePath, CREATE_ALWAYS, GENERIC_WRITE, &pstmFile));
// Copy Stream
CHECKHR(hr = _HrCopyDataStream(pstmBody, pstmFile));
if (S_OK != hr)
hrWarnings = TrapError(hr);
// Commit
CHECKHR(hr = pstmFile->Commit(STGC_DEFAULT));
exit:
// Cleanup
SafeRelease(pstmFile);
SafeRelease(pstmBody);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetData
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetData(ENCODINGTYPE ietEncoding, IStream **ppStream)
{
// Locals
HRESULT hr=S_OK;
BODYSTREAMINIT rStreamInit;
CBodyStream *pBodyStream=NULL;
ASSERTINIT;
// Parameter Check
if (NULL == ppStream)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Init StreamInit
ZeroMemory(&rStreamInit, sizeof(BODYSTREAMINIT));
// Initialzie
rStreamInit.ietExternal = ietEncoding;
rStreamInit.ietInternal = m_ietEncoding;
rStreamInit.fRemoveNBSP = m_rOptions.fRemoveNBSP;
rStreamInit.pCharset = m_pCharset;
// Create a new body stream...
CHECKALLOC(pBodyStream = new CBodyStream());
// Initialize the body stream
CHECKHR(hr = pBodyStream->HrInitialize(&rStreamInit, this));
// Set return
*ppStream = (IStream *)pBodyStream;
(*ppStream)->AddRef();
exit:
// Cleanup
SafeRelease(pBodyStream);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetDataHere
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetDataHere(ENCODINGTYPE ietEncoding, IStream *pStream)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
IStream *pBodyStream=NULL;
ASSERTINIT;
// Parameter Check
if (NULL == pStream)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Get the body stream
CHECKHR(hr = GetData(ietEncoding, &pBodyStream));
// Copy Stream
CHECKHR(hr = _HrCopyDataStream(pBodyStream, pStream));
if (S_OK != hr)
hrWarnings = TrapError(hr);
exit:
// Cleanup
SafeRelease(pBodyStream);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::_HrCopyDataStream
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_HrCopyDataStream(IStream *pstmSource, IStream *pstmDest)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BYTE rgBuffer[4096];
ULONG cbRead;
DWORD cbStream = 0;
STATSTG statstg;
// Invalid Arg
Assert(pstmSource && pstmDest);
// get the size of the stream
CHECKHR(pstmSource->Stat(&statstg, STATFLAG_NONAME));
cbStream = statstg.cbSize.LowPart;
// Loop for ever
while(cbStream)
{
// Read a buffer
CHECKHR(hr = pstmSource->Read(rgBuffer, ARRAYSIZE(rgBuffer), &cbRead));
if (S_OK != hr)
hrWarnings = TrapError(hr);
// Done
if (0 == cbRead)
break;
cbStream = cbStream - cbRead;
// Write It
CHECKHR(hr = pstmDest->Write(rgBuffer, cbRead, NULL));
}
exit:
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::HrGetLockBytes
// --------------------------------------------------------------------------------
HRESULT CMessageBody::HrGetLockBytes(ILockBytes **ppLockBytes)
{
// Locals
HRESULT hr=S_OK;
ASSERTINIT;
// Invalid Arg
Assert(ppLockBytes);
// Init
*ppLockBytes = NULL;
// Thread Safety
EnterCriticalSection(&m_cs);
// No Data
if (NULL == m_rStorage.pUnkRelease)
{
hr = TrapError(MIME_E_NO_DATA);
goto exit;
}
// IID_ILockBytes
if (IID_ILockBytes == m_rStorage.riid)
{
// AddRef It
*ppLockBytes = m_rStorage.pLockBytes;
(*ppLockBytes)->AddRef();
}
// IID_IMimeWebDocument
else if (IID_IMimeWebDocument == m_rStorage.riid)
{
// BindToStorage
CHECKHR(hr = m_rStorage.pWebDocument->BindToStorage(IID_ILockBytes, (LPVOID *)ppLockBytes));
// Lets Cache ppLockBytes
SafeRelease(m_rStorage.pUnkRelease);
// Assume the lock bytes
m_rStorage.pLockBytes = (*ppLockBytes);
m_rStorage.pLockBytes->AddRef();
// Set pUnkRelease
m_rStorage.pUnkRelease = m_rStorage.pLockBytes;
// Set Storage Id
m_rStorage.riid = IID_ILockBytes;
}
// Big Problem
else
Assert(FALSE);
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::SetData
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetData(ENCODINGTYPE ietEncoding, LPCSTR pszPriType, LPCSTR pszSubType,
REFIID riid, LPVOID pvObject)
{
// Locals
HRESULT hr=S_OK;
IStream *pStream=NULL;
ASSERTINIT;
// Parameter Check
if (NULL == pvObject || ietEncoding >= IET_UNKNOWN || FALSE == FBODYSETDATAIID(riid))
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// If multipart...
if (m_pContainer->IsContentType(STR_CNT_MULTIPART, NULL) == S_OK)
{
// RAID-29817: Only fail if there are children
if (m_pNode && m_pNode->cChildren > 0)
{
hr = TrapError(MIME_E_MULTIPART_NO_DATA);
goto exit;
}
// Lets adjust the Content-Type to application/octet-stream now so that things don't get confused
CHECKHR(hr = m_pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_APPL_STREAM));
}
// Release current m_pLockBytes
EmptyData();
// IID_IStream
if (IID_IStream == riid)
{
// New CBodyLockBytes
CHECKALLOC(m_rStorage.pLockBytes = new CStreamLockBytes((IStream *)pvObject));
// Set m_rStorage type
m_rStorage.riid = IID_ILockBytes;
m_rStorage.pUnkRelease = m_rStorage.pLockBytes;
}
// IID_ILockBytes
else if (IID_ILockBytes == riid)
{
// Just assume it
m_rStorage.pLockBytes = (ILockBytes *)pvObject;
m_rStorage.pLockBytes->AddRef();
// Set m_rStorage type
m_rStorage.riid = IID_ILockBytes;
m_rStorage.pUnkRelease = m_rStorage.pLockBytes;
}
// IID_IMimeBody
else if (IID_IMimeBody == riid)
{
// CopyTo
CHECKHR(hr = ((IMimeBody *)pvObject)->CopyTo(this));
}
// IID_IMimeMessage
else if (IID_IMimeMessage == riid)
{
// Locals
IMimePropertySet *pProps;
IMimeMessage *pMessage=(IMimeMessage *)pvObject;
// Get the message source
CHECKHR(hr = pMessage->GetMessageSource(&pStream, 0));
// New CBodyLockBytes
CHECKALLOC(m_rStorage.pLockBytes = new CStreamLockBytes(pStream));
// Set m_rStorage type
m_rStorage.riid = IID_ILockBytes;
m_rStorage.pUnkRelease = m_rStorage.pLockBytes;
// Set Content Type message/rfc822
CHECKHR(hr = m_pContainer->SetProp(SYM_HDR_CNTTYPE, STR_MIME_MSG_RFC822));
// Get Root Property Container
if (SUCCEEDED(pMessage->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pProps)))
{
// Locals
MIMEPROPINFO rPropInfo;
// I don't actualy want any props, just want to know if its set
rPropInfo.dwMask = 0;
// News Message ?
if (SUCCEEDED(pProps->GetPropInfo(PIDTOSTR(PID_HDR_NEWSGROUPS), &rPropInfo)) ||
SUCCEEDED(pProps->GetPropInfo(PIDTOSTR(PID_HDR_XNEWSRDR), &rPropInfo)) ||
SUCCEEDED(pProps->GetPropInfo(PIDTOSTR(PID_HDR_NEWSGROUP), &rPropInfo)))
m_pContainer->SetState(COSTATE_RFC822NEWS);
else
m_pContainer->ClearState(COSTATE_RFC822NEWS);
// Release
pProps->Release();
}
}
// IID_IMimeWebDocument
else if (IID_IMimeWebDocument == riid)
{
// Just assume it
m_rStorage.pWebDocument = (IMimeWebDocument *)pvObject;
m_rStorage.pWebDocument->AddRef();
// Set m_rStorage type
m_rStorage.riid = IID_IMimeWebDocument;
m_rStorage.pUnkRelease = m_rStorage.pWebDocument;
}
// Save The Format
m_ietEncoding = ietEncoding;
// Save Format per bert
if (g_rgEncodingMap[ietEncoding].pszName)
CHECKHR(hr = m_pContainer->SetProp(SYM_HDR_CNTXFER, g_rgEncodingMap[ietEncoding].pszName));
// Set PriType
if (pszPriType)
CHECKHR(hr = m_pContainer->SetProp(SYM_ATT_PRITYPE, pszPriType));
// Set SubType
if (pszSubType)
CHECKHR(hr = m_pContainer->SetProp(SYM_ATT_SUBTYPE, pszSubType));
// Assume The User is now controlling the Character Set Properties of this body
FLAGCLEAR(m_dwState, BODYSTATE_CSETTAGGED);
// We are now dirty
FLAGSET(m_dwState, BODYSTATE_DIRTY);
exit:
// Cleanup
SafeRelease(pStream);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
STDMETHODIMP CMessageBody::SetDataW(ENCODINGTYPE ietEncoding, LPCWSTR pwszPriType, LPCWSTR pwszSubType,
REFIID riid, LPVOID pvObject)
{
return TraceResult(E_NOTIMPL);
}
// --------------------------------------------------------------------------------
// CMessageBody::CopyTo
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::CopyTo(IMimeBody *pBodyIn)
{
// Locals
HRESULT hr=S_OK;
LPMESSAGEBODY pBody=NULL;
LPCONTAINER pContainer=NULL;
// Invalid Arg
if (NULL == pBodyIn)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// QI for Private CMessageBody
CHECKHR(hr = pBodyIn->QueryInterface(IID_CMessageBody, (LPVOID *)&pBody));
// Thread Safety
EnterCriticalSection(&pBody->m_cs);
// Copy Data Over
pBody->m_dwState = m_dwState;
// Release Current Data
pBody->EmptyData();
// Copy body props
CHECKHR(hr = m_pContainer->Clone(&pContainer));
// Release the Bodies' Current Container
SafeRelease(pBody->m_pContainer);
// Reset the Container
pBody->m_pContainer = pContainer;
pBody->m_pContainer->AddRef();
// Assume new container in pNode
if (pBody->m_pNode)
{
SafeRelease(pBody->m_pNode->pContainer);
pBody->m_pNode->pContainer = pContainer;
pContainer->AddRef();
}
// Copy Display Name
if (m_pszDisplay)
CHECKALLOC(pBody->m_pszDisplay = PszDupW(m_pszDisplay));
// Copy Options
// BUGBUG: This is pretty iffy. BODYOPTIONS contains pointers, each of which should
// be duplicated. Caller beware!
CopyMemory(&pBody->m_rOptions, &m_rOptions, sizeof(BODYOPTIONS));
// Current Encoding
pBody->m_ietEncoding = m_ietEncoding;
// Charset
pBody->m_pCharset = m_pCharset;
// If we have data
if (m_rStorage.pUnkRelease)
{
// IID_ILockBytes
if (IID_ILockBytes == m_rStorage.riid)
{
pBody->m_rStorage.pLockBytes = m_rStorage.pLockBytes;
pBody->m_rStorage.pLockBytes->AddRef();
pBody->m_rStorage.pUnkRelease = pBody->m_rStorage.pLockBytes;
pBody->m_rStorage.riid = IID_ILockBytes;
}
// IID_IMimeWebDocument
else if (IID_IMimeWebDocument == m_rStorage.riid)
{
pBody->m_rStorage.pWebDocument = m_rStorage.pWebDocument;
pBody->m_rStorage.pWebDocument->AddRef();
pBody->m_rStorage.pUnkRelease = pBody->m_rStorage.pWebDocument;
pBody->m_rStorage.riid = IID_IMimeWebDocument;
}
// Big Problem
else
Assert(FALSE);
}
exit:
// Release Thread Safety
if (pBody)
LeaveCriticalSection(&pBody->m_cs);
// Cleanup
SafeRelease(pBody);
SafeRelease(pContainer);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::SetCurrentEncoding
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetCurrentEncoding(ENCODINGTYPE ietEncoding)
{
// Parameter Check
if (ietEncoding >= IET_UNKNOWN)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Set the current encoding
m_ietEncoding = ietEncoding;
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetCurrentEncoding
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetCurrentEncoding(ENCODINGTYPE *pietEncoding)
{
// Invalid Arg
if (NULL == pietEncoding)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Set Return
*pietEncoding = m_ietEncoding;
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetTransmitInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetTransmitInfo(LPTRANSMITINFO pTransmit)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
ULONG cbRead,
cbLine=0,
cEscapeChars=0,
i;
BYTE rgBuffer[4096];
LPSTREAM pStream=NULL;
BYTE bPrev='\0';
BOOL fBadEOL=FALSE;
DWORD cbStream = 0;
STATSTG statstg;
ASSERTINIT;
// Parmaeters
if (NULL == pTransmit)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Init
ZeroMemory(pTransmit, sizeof(TRANSMITINFO));
// Set the current code page
pTransmit->ietCurrent = m_ietEncoding;
// Init Format
pTransmit->ietXmitMime = IET_7BIT;
pTransmit->ietXmit822 = IET_7BIT;
// Don't call for multipart types...
if (m_pContainer->IsContentType(STR_CNT_MULTIPART, NULL) == S_OK)
{
Assert(FALSE);
hr = TrapError(MIME_E_MULTIPART_NO_DATA);
goto exit;
}
// Lets a binary stream of the data
CHECKHR(hr = GetData(IET_INETCSET, &pStream));
// get the size of the stream
CHECKHR(pStream->Stat(&statstg, STATFLAG_NONAME));
cbStream = statstg.cbSize.LowPart;
// Scan It
while(cbStream)
{
// Read a buffer
CHECKHR(hr = pStream->Read(rgBuffer, sizeof(rgBuffer), &cbRead));
if (S_OK != hr)
hrWarnings = TrapError(hr);
// Done
if (0 == cbRead)
break;
cbStream = cbStream - cbRead;
// Scan the buffer
for (i=0; i<cbRead; i++, cbLine++)
{
// If End of line, reset line length
if (chLF == rgBuffer[i])
{
// Count lines
pTransmit->cLines++;
cbLine = 0;
// Raid-41839: x-bitmap images are not correctly transferred via Schotzie
// Don't write out lines that end with only a \n, not legal
if (chCR != bPrev)
fBadEOL = TRUE;
}
// Line too long
if (cbLine > pTransmit->cbLongestLine)
pTransmit->cbLongestLine++;
// Tests for extended and control characters
if (IS_EXTENDED(rgBuffer[i]))
{
// Count Extneded
pTransmit->cExtended++;
// Count Escape Characters
if (0x1B == rgBuffer[i])
cEscapeChars++;
}
// Save Previous
bPrev = rgBuffer[i];
}
// Increment Total
pTransmit->cbSize += cbRead;
}
// No Data ?
if (0 == pTransmit->cbSize)
{
pTransmit->ulPercentExt = 0;
goto exit;
}
// RAID-22542: FE-J:Athena:mail:sending mail with text/plain has Content-transfer-encording:8bit
if (FALSE == m_rOptions.fDBCSEscape8 && cEscapeChars > 0 && m_pCharset && g_pInternat->IsDBCSCharset(m_pCharset->hCharset) == S_OK)
{
// Subtract the Number of EscapeChars off of the number of extended chars
Assert(cEscapeChars <= pTransmit->cExtended);
pTransmit->cExtended -= cEscapeChars;
}
if (IET_UNKNOWN == m_rOptions.ietTransmit)
{
// More than 25% extended characters
pTransmit->ulPercentExt = ((pTransmit->cExtended * 100) / pTransmit->cbSize);
// Raid-41839: x-bitmap images are not correctly transferred via Schotzie
// More than 17 percent extended
if (pTransmit->ulPercentExt > 17)
{
pTransmit->ietXmitMime = IET_BASE64;
pTransmit->ietXmit822 = IET_UUENCODE;
}
// Some Extended Characters or line longer than max
else if (pTransmit->cExtended || pTransmit->cbLongestLine > m_rOptions.cbMaxLine || TRUE == fBadEOL)
{
pTransmit->ietXmitMime = IET_QP;
pTransmit->ietXmit822 = IET_7BIT;
}
// Otherwise, 7bit
else
{
pTransmit->ietXmitMime = IET_7BIT;
pTransmit->ietXmit822 = IET_7BIT;
}
}
else
{
// the client specifically set this option, so honor it
pTransmit->ietXmitMime = m_rOptions.ietTransmit;
// we may need to fixup ietXmit822 if the XmitMime
// option does not make sense for it
switch (m_rOptions.ietTransmit)
{
case IET_BASE64:
pTransmit->ietXmit822 = IET_UUENCODE;
break;
case IET_QP:
pTransmit->ietXmit822 = IET_7BIT;
break;
default:
pTransmit->ietXmit822 = m_rOptions.ietTransmit;
break;
}
}
exit:
// Cleanup
SafeRelease(pStream);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::SwitchContainers
// --------------------------------------------------------------------------------
void CMessageBody::SwitchContainers(CMessageBody *pBody)
{
// Thread Safety
EnterCriticalSection(&m_cs);
EnterCriticalSection(&pBody->m_cs);
// Invalid Arg
Assert(pBody && pBody->m_pContainer && m_pContainer && m_pNode && pBody->m_pNode);
// Simple Varialbe Swap
LPCONTAINER pTemp = m_pContainer;
m_pNode->pContainer = m_pContainer = pBody->m_pContainer;
pBody->m_pNode->pContainer = pBody->m_pContainer = pTemp;
// Thread Safety
LeaveCriticalSection(&pBody->m_cs);
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CMessageBody::GetHandle
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetHandle(LPHBODY phBody)
{
// Local
HRESULT hr=S_OK;
ASSERTINIT;
// Invalid Arg
if (NULL == phBody)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Init
*phBody = NULL;
// Not Bound...
if (NULL == m_pNode || NULL == m_pNode->hBody)
{
hr = MIME_E_NO_DATA;
goto exit;
}
// Set Handle
*phBody = m_pNode->hBody;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetClassID
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetClassID(CLSID *pClassID)
{
ASSERTINIT;
// Parmaeters
if (NULL == pClassID)
return TrapError(E_INVALIDARG);
// Copy Class Id
CopyMemory(pClassID, &IID_IMimeBody, sizeof(CLSID));
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetSizeMax
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetSizeMax(ULARGE_INTEGER* pcbSize)
{
// Locals
HRESULT hr=S_OK;
ULONG cbSize;
ASSERTINIT;
// Get The Size
CHECKHR(hr = GetEstimatedSize(IET_BINARY, &cbSize));
// Return the Size
pcbSize->QuadPart = cbSize;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::Load
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::Load(LPSTREAM pStream)
{
ASSERTINIT;
return TrapError(m_pContainer->Load(pStream));
}
// ---------------------------------------------------------------------------
// CMessageBody::Load
// ---------------------------------------------------------------------------
HRESULT CMessageBody::Load(CInternetStream *pInternet)
{
ASSERTINIT;
return TrapError(m_pContainer->Load(pInternet));
}
// --------------------------------------------------------------------------------
// CMessageBody::Save
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::Save(LPSTREAM pStream, BOOL fClearDirty)
{
ASSERTINIT;
return TrapError(m_pContainer->Save(pStream, fClearDirty));
}
// --------------------------------------------------------------------------------
// CMessageBody::IsContentType
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::IsContentType(LPCSTR pszPriType, LPCSTR pszSubType)
{
ASSERTINIT;
return TrapError(m_pContainer->IsContentType(pszPriType, pszSubType));
}
// --------------------------------------------------------------------------------
// CMessageBody::GetCharset
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetCharset(LPHCHARSET phCharset)
{
ASSERTINIT;
return TrapError(m_pContainer->GetCharset(phCharset));
}
// --------------------------------------------------------------------------------
// CMessageBody::SetCharset
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetCharset(HCHARSET hCharset, CSETAPPLYTYPE applytype)
{
// Locals
HRESULT hr=S_OK;
ASSERTINIT;
// Invalid Arg
if (NULL == hCharset)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Lookiup Charset Info
if (FALSE == g_pInternat->FIsValidHandle(hCharset))
{
hr = TrapError(MIME_E_INVALID_HANDLE);
goto exit;
}
// If I'm already tagged with a charset, and applytype is CSET_APPLY_UNTAGGED, then leave
if (ISFLAGSET(m_dwState, BODYSTATE_SKIPCSET) == TRUE && CSET_APPLY_UNTAGGED == applytype)
goto exit;
// Pass it into the property set
CHECKHR(hr = m_pContainer->SetCharset(hCharset, applytype));
// If I'm already tagged with a charset, and applytype is CSET_APPLY_UNTAGGED, then leave
if (ISFLAGSET(m_dwState, BODYSTATE_CSETTAGGED) == TRUE && CSET_APPLY_UNTAGGED == applytype)
goto exit;
// Save the character set...
SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(hCharset, &m_pCharset)));
// Mark as Tagged
if (CSET_APPLY_TAG_ALL == applytype)
{
// Mark the body as being tagged with a charset
// Get Internal Character Set
if (SUCCEEDED(m_pContainer->GetCharset(&hCharset)))
{
// Get Pointer
SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(hCharset, &m_pCharset)));
// Save this as m_pCsetTagged
m_pCsetTagged = m_pCharset;
}
// I was tagged with a charset
FLAGSET(m_dwState, BODYSTATE_CSETTAGGED);
// Mark the charset as explicitly set
FLAGSET(m_dwState, BODYSTATE_SKIPCSET);
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetProp
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetProp(LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue)
{
ASSERTINIT;
return TrapError(m_pContainer->GetProp(pszName, dwFlags, pValue));
}
// ---------------------------------------------------------------------------
// CMessageBody::AppendProp
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::AppendProp(LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue)
{
ASSERTINIT;
return TrapError(m_pContainer->AppendProp(pszName, dwFlags, pValue));
}
// --------------------------------------------------------------------------------
// CMessageBody::SetProp
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetProp(LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue)
{
ASSERTINIT;
return TrapError(m_pContainer->SetProp(pszName, dwFlags, pValue));
}
// --------------------------------------------------------------------------------
// CMessageBody::DeleteProp
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::DeleteProp(LPCSTR pszName)
{
ASSERTINIT;
return TrapError(m_pContainer->DeleteProp(pszName));
}
// ---------------------------------------------------------------------------
// CMessageBody::QueryProp
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::QueryProp(LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive)
{
ASSERTINIT;
return TrapError(m_pContainer->QueryProp(pszName, pszCriteria, fSubString, fCaseSensitive));
}
// --------------------------------------------------------------------------------
// CMessageBody::Clone
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::Clone(IMimePropertySet **ppPropertySet)
{
ASSERTINIT;
return TrapError(m_pContainer->Clone(ppPropertySet));
}
// --------------------------------------------------------------------------------
// CMessageBody::BindToObject
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::BindToObject(REFIID riid, void **ppvObject)
{
// Locals
HRESULT hr=S_OK;
ASSERTINIT;
// Thread Safety
EnterCriticalSection(&m_cs);
// IID_IStream
if (IID_IStream == riid)
{
// Get Data...
CHECKHR(hr = GetData(IET_INETCSET, (IStream **)ppvObject));
}
// IID_IUnicodeStream
else if (IID_IUnicodeStream == riid)
{
// Get Data...
CHECKHR(hr = GetData(IET_UNICODE, (IStream **)ppvObject));
}
// IID_IMimeMessage... (returns message/rfc822 or message/partial bodies)
else if (IID_IMimeMessage == riid)
{
// Better be message/rfc822 or message/partial...
if (m_pContainer->IsContentType(STR_CNT_MESSAGE, STR_SUB_RFC822) == S_OK ||
m_pContainer->IsContentType(STR_CNT_MESSAGE, STR_SUB_PARTIAL) == S_OK)
{
// Locals
LPSTREAM pstmMsg=NULL;
IMimeMessage *pMessage=NULL;
// Create a message object
CHECKHR(hr = MimeOleCreateMessage(NULL, &pMessage));
// Get the body stream...
hr = GetData(IET_BINARY, &pstmMsg);
if (FAILED(hr))
{
pMessage->Release();
TrapError(hr);
goto exit;
}
// Load the message...
hr = pMessage->Load(pstmMsg);
if (FAILED(hr))
{
pstmMsg->Release();
pMessage->Release();
TrapError(hr);
goto exit;
}
// Return the message
*ppvObject = pMessage;
((IUnknown *)*ppvObject)->AddRef();
// Cleanup
SafeRelease(pstmMsg);
SafeRelease(pMessage);
}
// Otherwise, fail
else
{
hr = TrapError(E_FAIL);
goto exit;
}
}
// Otheriwse, do a normal QI of the body/property set
else if (SUCCEEDED(QueryInterface(riid, ppvObject)))
goto exit;
// Otherwise, try to do a bindtoobject on the container
else if (SUCCEEDED(m_pContainer->BindToObject(riid, ppvObject)))
goto exit;
// Not Found
else
hr = TrapError(MIME_E_NOT_FOUND);
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::DeleteExcept
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::DeleteExcept(ULONG cNames, LPCSTR *prgszName)
{
ASSERTINIT;
return TrapError(m_pContainer->DeleteExcept(cNames, prgszName));
}
// ---------------------------------------------------------------------------
// CMessageBody::GetParameters
// ---------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetParameters(LPCSTR pszName, ULONG *pcParams, LPMIMEPARAMINFO *pprgParam)
{
ASSERTINIT;
return TrapError(m_pContainer->GetParameters(pszName, pcParams, pprgParam));
}
// --------------------------------------------------------------------------------
// CMessageBody::CopyProps
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::CopyProps(ULONG cNames, LPCSTR *prgszName, IMimePropertySet *pPropSet)
{
ASSERTINIT;
return TrapError(m_pContainer->CopyProps(cNames, prgszName, pPropSet));
}
// --------------------------------------------------------------------------------
// CMessageBody::MoveProps
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::MoveProps(ULONG cNames, LPCSTR *prgszName, IMimePropertySet *pPropSet)
{
ASSERTINIT;
return TrapError(m_pContainer->MoveProps(cNames, prgszName, pPropSet));
}
// --------------------------------------------------------------------------------
// CMessageBody::GetPropInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::GetPropInfo(LPCSTR pszName, LPMIMEPROPINFO pInfo)
{
ASSERTINIT;
return TrapError(m_pContainer->GetPropInfo(pszName, pInfo));
}
// --------------------------------------------------------------------------------
// CMessageBody::SetPropInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::SetPropInfo(LPCSTR pszName, LPCMIMEPROPINFO pInfo)
{
ASSERTINIT;
return TrapError(m_pContainer->SetPropInfo(pszName, pInfo));
}
// --------------------------------------------------------------------------------
// CMessageBody::EnumProps
// --------------------------------------------------------------------------------
STDMETHODIMP CMessageBody::EnumProps(DWORD dwFlags, IMimeEnumProperties **ppEnum)
{
ASSERTINIT;
return TrapError(m_pContainer->EnumProps(dwFlags, ppEnum));
}
// --------------------------------------------------------------------------------
// CMessageBody::DwGetFlags
// --------------------------------------------------------------------------------
DWORD CMessageBody::DwGetFlags(BOOL fHideTnef)
{
DWORD dwFlags = 0;
ASSERTINIT;
dwFlags |= m_pContainer->DwGetMessageFlags(fHideTnef);
// if it isn't x-pkcs7-mime, we already know the flags, so no need for the call
// check the flag first b/c it is cheap
if (dwFlags & IMF_SECURE &&
(S_OK == m_pContainer->IsContentType(STR_CNT_APPLICATION, STR_SUB_XPKCS7MIME) ||
S_OK == m_pContainer->IsContentType(STR_CNT_APPLICATION, STR_SUB_PKCS7MIME)))
dwFlags |= _GetSecureTypeFlags();
return dwFlags;
}
// --------------------------------------------------------------------------------
// CMessageBody::_GetSecureTypeFlags
// --------------------------------------------------------------------------------
DWORD CMessageBody::_GetSecureTypeFlags()
{
DWORD dwSecType;
DWORD dwFlags;
// if the data isn't ASN.1, then this call should fail
if (SUCCEEDED(TrapError(CSMime::StaticGetMessageType(m_rOptions.hwndOwner,
(IMimeBody *)this, &dwSecType))))
{
dwFlags = IMF_SECURE;
if (MST_THIS_SIGN & dwSecType)
dwFlags |= IMF_SIGNED;
if (MST_THIS_ENCRYPT & dwSecType)
dwFlags |= IMF_ENCRYPTED;
}
else
dwFlags = 0;
return dwFlags;
}
// --------------------------------------------------------------------------------
// CMessageBody::CopyOptionsTo
// --------------------------------------------------------------------------------
void CMessageBody::CopyOptionsTo(CMessageBody *pBody, BOOL fNewOnwer /*=FALSE*/)
{
// Locals
ENCODINGTYPE ietTransmit;
ASSERTINIT;
// Valid State
Assert(pBody);
// critsec both bodies since we are using information on both
EnterCriticalSection(&m_cs);
EnterCriticalSection(&pBody->m_cs);
// Copy Body Options
pBody->m_rOptions = m_rOptions;
// Raid 33207 - Preserve transmit body encoding
ietTransmit = m_rOptions.ietTransmit;
// If pBody->m_rOptions is the new owner, then clear my structure
CopyMemory(&m_rOptions, &g_rDefBodyOptions, sizeof(BODYOPTIONS));
// Raid 33207 - Restore transmit body encoding
m_rOptions.ietTransmit = ietTransmit;
LeaveCriticalSection(&pBody->m_cs);
LeaveCriticalSection(&m_cs);
}
#ifndef _WIN64
// -----------------------------------------------------------------------------
// CMessageBody::_CAULToCertStore
// -----------------------------------------------------------------------------
HRESULT CMessageBody::_CAULToCertStore(const CAUL caul, HCERTSTORE * phcertstor)
{
DWORD i;
// Release the old store -- we don't want it anymore
if (*phcertstor != NULL)
{
CertCloseStore(*phcertstor, 0);
*phcertstor = NULL;
}
// Create a new store if needed
if (*phcertstor == NULL)
{
*phcertstor = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING,
NULL, 0, NULL);
}
if (*phcertstor == NULL)
{
return HrGetLastError();
}
for (i=0; i<caul.cElems; i++)
{
if (!CertAddCertificateContextToStore(*phcertstor,
(PCCERT_CONTEXT) caul.pElems[i],
CERT_STORE_ADD_ALWAYS, NULL))
{
return HrGetLastError();
}
}
return S_OK;
}
#endif // _WIN64
// -----------------------------------------------------------------------------
// CMessageBody::_CertStoreToCAUL
// -----------------------------------------------------------------------------
HRESULT CMessageBody::_CertStoreToCAUL(const HCERTSTORE hcertstor, CAUL * pcaul)
{
DWORD cCerts = 0;
PCCERT_CONTEXT pccert = NULL;
PCCERT_CONTEXT * rgpccert;
if (hcertstor != NULL)
{
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL)
{
cCerts += 1;
}
}
if (cCerts == 0)
{
pcaul->pElems = NULL;
pcaul->cElems = 0;
return S_OK;
}
pcaul->pElems = (ULONG *) g_pMalloc->Alloc(cCerts*sizeof(PCCERT_CONTEXT *));
if (pcaul->pElems == NULL)
{
return TrapError(E_OUTOFMEMORY);
}
rgpccert = (PCCERT_CONTEXT *) pcaul->pElems;
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL)
{
*rgpccert = CertDuplicateCertificateContext(pccert);
rgpccert++;
}
pcaul->cElems = cCerts;
return S_OK;
}
#ifndef SMIME_V3
// --------------------------------------------------------------------------------
// CMessageBody::_CAULToCERTARRAY
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CAULToCERTARRAY(const CAUL caul, CERTARRAY *pca)
{
// Locals
HRESULT hr;
register DWORD i;
if (SUCCEEDED(hr = HrRealloc((void**)&pca->rgpCerts, caul.cElems*sizeof(PCCERT_CONTEXT))))
{
for (i = 0; i < pca->cCerts; i++)
CertFreeCertificateContext(pca->rgpCerts[i]);
pca->cCerts = caul.cElems;
for (i = 0; i < caul.cElems; i++)
pca->rgpCerts[i] = CertDuplicateCertificateContext((PCCERT_CONTEXT)caul.pElems[i]);
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::_CERTARRAYToCAUL
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CERTARRAYToCAUL(const CERTARRAY ca, CAUL *pcaul)
{
// Locals
HRESULT hr = S_OK;
register DWORD i;
if (ca.cCerts)
{
if ((pcaul->pElems = (ULONG*)(PCCERT_CONTEXT*)g_pMalloc->Alloc(ca.cCerts*sizeof(PCCERT_CONTEXT))))
{
pcaul->cElems = ca.cCerts;
for (i = 0; i < ca.cCerts; i++)
pcaul->pElems[i] = (ULONG)CertDuplicateCertificateContext(ca.rgpCerts[i]);
}
else
hr = TrapError(E_OUTOFMEMORY);
}
else
{
pcaul->pElems = NULL;
pcaul->cElems = 0;
}
// Done
return hr;
}
#endif // !SMIEM_V3
#ifndef _WIN64
// --------------------------------------------------------------------------------
// CMessageBody::_CAULToSTOREARRAY
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CAULToSTOREARRAY(const CAUL caul, STOREARRAY *psa)
{
// Locals
HRESULT hr;
register DWORD i;
if (SUCCEEDED(hr = HrRealloc((void**)&psa->rgStores, caul.cElems*sizeof(HCERTSTORE))))
{
for (i = 0; i < psa->cStores; i++)
CertCloseStore(psa->rgStores[i], 0);
psa->cStores = caul.cElems;
for (i = 0; i < caul.cElems; i++)
psa->rgStores[i] = CertDuplicateStore((HCERTSTORE)caul.pElems[i]);
}
// Done
return hr;
}
#endif // _WIN64
#ifndef _WIN64
// --------------------------------------------------------------------------------
// CMessageBody::_STOREARRAYToCAUL
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_STOREARRAYToCAUL(const STOREARRAY sa, CAUL *pcaul)
{
// Locals
HRESULT hr = S_OK;
register DWORD i;
if (sa.cStores)
{
if ((pcaul->pElems = (ULONG*)(HCERTSTORE*)g_pMalloc->Alloc(sa.cStores*sizeof(HCERTSTORE))))
{
pcaul->cElems = sa.cStores;
for (i = 0; i < sa.cStores; i++)
pcaul->pElems[i] = (ULONG)CertDuplicateStore(sa.rgStores[i]);
}
else
hr = TrapError(E_OUTOFMEMORY);
}
else
{
pcaul->pElems = NULL;
pcaul->cElems = 0;
}
// Done
return hr;
}
#endif // _WIN64
// -----------------------------------------------------------------------------
// CMessageBody::_CAUHToCertStore
// -----------------------------------------------------------------------------
HRESULT CMessageBody::_CAUHToCertStore(const CAUH cauh, HCERTSTORE * phcertstor)
{
DWORD i;
#ifndef NEED // This prevent us from send 2 certificates.
// Release the old store -- we don't want it anymore
if (*phcertstor != NULL)
{
CertCloseStore(*phcertstor, 0);
*phcertstor = NULL;
}
#endif
// Create a new store if needed
if (*phcertstor == NULL)
{
*phcertstor = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING,
NULL, 0, NULL);
}
if (*phcertstor == NULL)
{
return HrGetLastError();
}
for (i=0; i<cauh.cElems; i++)
{
if (!CertAddCertificateContextToStore(*phcertstor,
*(PCCERT_CONTEXT *) (&(cauh.pElems[i])),
CERT_STORE_ADD_ALWAYS, NULL))
{
return HrGetLastError();
}
}
return S_OK;
}
// -----------------------------------------------------------------------------
// CMessageBody::_CertStoreToCAUH
// -----------------------------------------------------------------------------
HRESULT CMessageBody::_CertStoreToCAUH(const HCERTSTORE hcertstor, CAUH * pcauh)
{
DWORD cCerts = 0;
PCCERT_CONTEXT pccert = NULL;
PCCERT_CONTEXT * rgpccert;
if (hcertstor != NULL)
{
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL)
{
cCerts += 1;
}
}
if (cCerts == 0)
{
pcauh->pElems = NULL;
pcauh->cElems = 0;
return S_OK;
}
pcauh->pElems = (ULARGE_INTEGER *) g_pMalloc->Alloc(cCerts*sizeof(PCCERT_CONTEXT *));
if (pcauh->pElems == NULL)
{
return TrapError(E_OUTOFMEMORY);
}
rgpccert = (PCCERT_CONTEXT *) pcauh->pElems;
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL)
{
*rgpccert = CertDuplicateCertificateContext(pccert);
rgpccert++;
}
pcauh->cElems = cCerts;
return S_OK;
}
#ifdef _WIN65
// --------------------------------------------------------------------------------
// CMessageBody::_CAUHToCERTARRAY
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CAUHToCERTARRAY(const CAUH cauh, CERTARRAY *pca)
{
// Locals
HRESULT hr;
register DWORD i;
if (SUCCEEDED(hr = HrRealloc((void**)&pca->rgpCerts, cauh.cElems*sizeof(PCCERT_CONTEXT))))
{
for (i = 0; i < pca->cCerts; i++)
CertFreeCertificateContext(pca->rgpCerts[i]);
pca->cCerts = cauh.cElems;
for (i = 0; i < cauh.cElems; i++)
pca->rgpCerts[i] = CertDuplicateCertificateContext(*(PCCERT_CONTEXT *)(&(cauh.pElems[i])));
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::_CERTARRAYToCAUH
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CERTARRAYToCAUH(const CERTARRAY ca, CAUH *pcauh)
{
// Locals
HRESULT hr = S_OK;
register DWORD i;
PCCERT_CONTEXT * rgpccert = NULL;
if (ca.cCerts)
{
if ((pcauh->pElems = (ULARGE_INTEGER *)g_pMalloc->Alloc(ca.cCerts*sizeof(PCCERT_CONTEXT))))
{
pcauh->cElems = ca.cCerts;
rgpccert = *(PCCERT_CONTEXT **)(&(pcauh->pElems));
for (i = 0; i < ca.cCerts; i++)
rgpccert[i] = CertDuplicateCertificateContext(ca.rgpCerts[i]);
}
else
hr = TrapError(E_OUTOFMEMORY);
}
else
{
pcauh->pElems = NULL;
pcauh->cElems = 0;
}
// Done
return hr;
}
#endif // _WIN65
// --------------------------------------------------------------------------------
// CMessageBody::_CAUHToSTOREARRAY
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_CAUHToSTOREARRAY(const CAUH cauh, STOREARRAY *psa)
{
// Locals
HRESULT hr;
register DWORD i;
if (SUCCEEDED(hr = HrRealloc((void**)&psa->rgStores, cauh.cElems*sizeof(HCERTSTORE))))
{
for (i = 0; i < psa->cStores; i++)
CertCloseStore(psa->rgStores[i], 0);
psa->cStores = cauh.cElems;
for (i = 0; i < cauh.cElems; i++)
psa->rgStores[i] = CertDuplicateStore(*(HCERTSTORE *)(&(cauh.pElems[i])));
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::_STOREARRAYToCAUH
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_STOREARRAYToCAUH(const STOREARRAY sa, CAUH *pcauh)
{
// Locals
HRESULT hr = S_OK;
register DWORD i;
HCERTSTORE * rgStores = NULL;
if (sa.cStores)
{
if ((pcauh->pElems = (ULARGE_INTEGER *)g_pMalloc->Alloc(sa.cStores*sizeof(HCERTSTORE))))
{
pcauh->cElems = sa.cStores;
rgStores = *(HCERTSTORE **)(&(pcauh->pElems));
for (i = 0; i < sa.cStores; i++)
rgStores[i] = CertDuplicateStore(sa.rgStores[i]);
}
else
hr = TrapError(E_OUTOFMEMORY);
}
else
{
pcauh->pElems = NULL;
pcauh->cElems = 0;
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::_FreeOptions
// --------------------------------------------------------------------------------
void CMessageBody::_FreeOptions()
{
DWORD i;
if (m_rOptions.cSigners)
{
// OID_SECURITY_ALG_HASH
SafeMemFree(m_rOptions.rgblobHash[0].pBlobData);
// OID_SECURITY_CERT_SIGNING
for (i = 0; i < m_rOptions.cSigners; i++)
{
CertFreeCertificateContext(m_rOptions.rgpcCertSigning[i]);
#ifdef SMIME_V3
// Attributes
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][i]);
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED][i]);
// OID_SECURITY_RECEIPT_RG
SafeMemFree(m_rOptions.rgblobReceipt[i].pBlobData);
// OID_SECURITY_MESSAGE_HASH_RG
SafeMemFree(m_rOptions.rgblobMsgHash[i].pBlobData);
// OID_SECURITY_KEY_PROMPT
SafeMemFree(m_rOptions.pwszKeyPrompt);
#else // !SMIME_V3
// OID_SECURITY_SYMCAPS
SafeMemFree(m_rOptions.rgblobSymCaps[i].pBlobData);
// OID_SECURITY_AUTHATTR
SafeMemFree(m_rOptions.rgblobAuthAttr[i].pBlobData);
// OID_SECURITY_UNAUTHATTR
SafeMemFree(m_rOptions.rgblobUnauthAttr[i].pBlobData);
#endif // SMIME_V3
}
// OID_SECURITY_HCERTSTORE
CertCloseStore(m_rOptions.hCertStore, 0);
_FreeLayerArrays();
if (m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNPROTECTED] != NULL) {
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNPROTECTED][0]);
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNPROTECTED]);
}
}
// OID_SECURITY_ALG_BULK
SafeMemFree(m_rOptions.blobBulk.pBlobData);
// OID_SECURITY_CERT_DECRYPTION
if (m_rOptions.pcCertDecryption)
CertFreeCertificateContext(m_rOptions.pcCertDecryption);
#ifdef SMIME_V3
if (m_rOptions.rgRecipients != 0)
{
for (i=0; i<m_rOptions.cRecipients; i++)
{
FreeRecipientInfoContent(&m_rOptions.rgRecipients[i]);
}
}
SafeMemFree(m_rOptions.rgRecipients);
m_rOptions.cRecipients = 0;
m_rOptions.cRecipsAllocated = 0;
#else // SMIME_V3
// OID_SECURITY_RG_CERT_ENCRYPT
for (i=0; i<m_rOptions.caEncrypt.cCerts; i++)
CertFreeCertificateContext(m_rOptions.caEncrypt.rgpCerts[i]);
SafeMemFree(m_rOptions.caEncrypt.rgpCerts);
#endif // !SMIEM_V3
// OID_SECURITY_SEARCHSTORES
for (i=0; i<m_rOptions.saSearchStore.cStores; i++)
CertCloseStore(m_rOptions.saSearchStore.rgStores[i], 0);
SafeMemFree(m_rOptions.saSearchStore.rgStores);
// OID_SECURITY_RG_IASN
// nyi
// OID_SECURITY_HCRYPTPROV
if (m_rOptions.hCryptProv)
CryptReleaseContext(m_rOptions.hCryptProv, 0);
return;
}
#ifdef _WIN64
#define REALLOC_AND_INIT_OPTION(Option) \
if (FAILED(hr = HrRealloc((void**)&Option, LcbAlignLcb(ulLayers * sizeof(*Option))))) { \
goto exit; \
} \
ZeroMemory(&Option[m_rOptions.cSigners], LcbAlignLcb((ulLayersNew) * sizeof(*Option)));
#else
#define REALLOC_AND_INIT_OPTION(Option) \
if (FAILED(hr = HrRealloc((void**)&Option, ulLayers * sizeof(*Option)))) { \
goto exit; \
} \
ZeroMemory(&Option[m_rOptions.cSigners], (ulLayersNew) * sizeof(*Option));
#endif //_WIN64
// --------------------------------------------------------------------------------
// CMessageBody::_HrEnsureBodyOptionLayers
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_HrEnsureBodyOptionLayers(ULONG ulLayers)
{
HRESULT hr = S_OK;
ULONG ulLayersNew = ulLayers - m_rOptions.cSigners;
if (m_rOptions.cSigners < ulLayers)
{
// Time to grow the arrays
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobHash);
REALLOC_AND_INIT_OPTION(m_rOptions.rgpcCertSigning);
#ifdef SMIME_V3
REALLOC_AND_INIT_OPTION(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED]);
REALLOC_AND_INIT_OPTION(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED]);
#else // !SMIME_V3
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobSymCaps);
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobAuthAttr);
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobUnauthAttr);
REALLOC_AND_INIT_OPTION(m_rOptions.rgftSigning);
#endif // SMIME_V3
REALLOC_AND_INIT_OPTION(m_rOptions.rgulUserDef);
REALLOC_AND_INIT_OPTION(m_rOptions.rgulROValid);
#ifdef SMIME_V3
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobReceipt);
REALLOC_AND_INIT_OPTION(m_rOptions.rgblobMsgHash);
#endif // SMIME_V3
m_rOptions.cSigners = ulLayers;
}
exit:
return(hr);
}
HRESULT CMessageBody::_HrEnsureBodyOptionLayers(LPCPROPVARIANT ppv)
{
CAUL * pcaul = (CAUL *)&ppv->caul;
return(_HrEnsureBodyOptionLayers(pcaul->cElems));
}
// --------------------------------------------------------------------------------
// CMessageBody::_HrEnsureBodyOptionLayers
// --------------------------------------------------------------------------------
void CMessageBody::_FreeLayerArrays(void)
{
if (m_rOptions.cSigners)
{
SafeMemFree(m_rOptions.rgblobHash);
SafeMemFree(m_rOptions.rgpcCertSigning);
#ifdef SMIME_V3
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED]);
SafeMemFree(m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_UNSIGNED]);
#else // !SMIME_V3
SafeMemFree(m_rOptions.rgblobSymCaps);
SafeMemFree(m_rOptions.rgblobAuthAttr);
SafeMemFree(m_rOptions.rgblobUnauthAttr);
SafeMemFree(m_rOptions.rgftSigning);
#endif // SMIME_V3
SafeMemFree(m_rOptions.rgulUserDef);
SafeMemFree(m_rOptions.rgulROValid);
#ifdef SMIME_V3
SafeMemFree(m_rOptions.rgblobReceipt);
SafeMemFree(m_rOptions.rgblobMsgHash);
#endif // SMIME_V3
m_rOptions.cSigners = 0;
}
}
HRESULT CMessageBody::_CompareCopyBlobArray(const PROPVARIANT FAR * pvSource, BLOB FAR * FAR * prgblDestination, BOOL fNoDirty)
{
HRESULT hr = S_OK;
ULONG i;
BOOL fReplace;
CAPROPVARIANT capropvar = pvSource->capropvar;
ULONG ulSize = capropvar.cElems;
Assert(ulSize == m_rOptions.cSigners);
Assert(*prgblDestination);
if (ulSize == m_rOptions.cSigners)
{
for (i = 0; i < ulSize; i++)
{
if ((*prgblDestination)[i].pBlobData)
{
if (fReplace = CompareBlob(&(*prgblDestination)[i], (BLOB FAR *)&capropvar.pElems[i]))
{
ReleaseMem((VOID FAR *)(*prgblDestination)[i].pBlobData);
}
} else {
fReplace = TRUE;
}
if (fReplace)
{
if (FAILED(hr = HrCopyBlob(&(capropvar.pElems[i].blob), &(*prgblDestination)[i])))
{
goto exit;
}
if (!fNoDirty)
{
FLAGSET(m_dwState, BODYSTATE_DIRTY);
}
}
}
} else {
hr = E_INVALIDARG;
}
exit:
return(hr);
}
// --------------------------------------------------------------------------------
// HrCopyBlobArray
//
// Allocate a new propvariant blob array, copy the original to it.
// --------------------------------------------------------------------------------
HRESULT HrCopyBlobArray(LPCBLOB pIn, ULONG cEntries, PROPVARIANT FAR * pvOut)
{
// Locals
HRESULT hr = S_OK;
CAPROPVARIANT * pcapropvar;
PROPVARIANT * ppv;
ULONG i;
pvOut->vt = VT_VECTOR | VT_VARIANT;
pcapropvar = &pvOut->capropvar;
pcapropvar->cElems = cEntries;
if (cEntries)
{
Assert(pIn && pvOut);
// Allocate the array of VT_BLOB propvariants
if (FAILED(hr = HrAlloc((LPVOID *)&ppv, cEntries * sizeof(PROPVARIANT))))
{
goto exit;
}
pcapropvar->pElems = ppv;
// Fill in the array of BLOBs
for (i = 0; i < cEntries; i++)
{
ppv[i].vt = VT_BLOB;
// HrCopyBlob allocates memory for the blob data.
if (FAILED(hr = HrCopyBlob((BLOB FAR *)&pIn[i] , &ppv[i].blob)))
{
goto exit;
}
}
} else {
pcapropvar->pElems = NULL;
}
exit:
// BUGBUG: Should clean up allocations on failure
return(hr);
}
// --------------------------------------------------------------------------------
// HrCopyArray
//
// Allocate a new array, copy the original to it.
// --------------------------------------------------------------------------------
HRESULT HrCopyArray(LPBYTE pIn, ULONG cEntries, PROPVARIANT FAR * pvOut, ULONG cbElement)
{
// Locals
HRESULT hr = S_OK;
BYTE * pb;
ULONG i;
ULONG cbArray = cEntries * cbElement;
CAUL * pcaul = &pvOut->caul;
pcaul->cElems = cEntries;
if (cEntries)
{
Assert(pIn && pvOut);
// Allocate the array of VT_BLOB propvariants
if (FAILED(hr = HrAlloc((LPVOID *)&pb, cbArray)))
{
goto exit;
}
pcaul->pElems = (PULONG)pb;
// Fill in the array of BLOBs
memcpy(pb, pIn, cbArray);
} else {
pcaul->pElems = NULL;
}
exit:
// BUGBUG: Should clean up allocations on failure
return(hr);
}
// --------------------------------------------------------------------------------
// HrCopyDwordArray
//
// Allocate a new dword array, copy the original to it.
// --------------------------------------------------------------------------------
HRESULT HrCopyDwordArray(LPDWORD pIn, ULONG cEntries, PROPVARIANT FAR * pvOut)
{
pvOut->vt = VT_VECTOR | VT_UI4;
return(HrCopyArray((LPBYTE)pIn, cEntries, pvOut, sizeof(DWORD)));
}
// --------------------------------------------------------------------------------
// HrCopyIntoUlonglongArray
//
// Allocate a new Ulonglong array, copy the original to it.
// --------------------------------------------------------------------------------
HRESULT HrCopyIntoUlonglongArray(ULARGE_INTEGER *pIn, ULONG cEntries, PROPVARIANT FAR * pvOut)
{
// Locals
HRESULT hr = S_OK;
ULARGE_INTEGER * pullBuff;
CAUH * pcauh = &pvOut->cauh;
pvOut->vt = VT_VECTOR | VT_UI8;
pcauh->cElems = cEntries;
if (cEntries)
{
Assert(pIn && pvOut);
// Allocate the array of VT_BLOB propvariants
if (FAILED(hr = HrAlloc((LPVOID *)&pullBuff, cEntries * sizeof(ULARGE_INTEGER *))))
{
goto exit;
}
pcauh->pElems = (ULARGE_INTEGER *) pullBuff;
// Fill in the array of BLOBs
for (; cEntries > 0; cEntries--, pullBuff++, pIn++)
{
*pullBuff = *pIn;
}
}
else
{
pcauh->pElems = NULL;
}
exit:
// BUGBUG: Should clean up allocations on failure
return(hr);
}
// --------------------------------------------------------------------------------
// HrCopyFiletimeArray
//
// Allocate a new filetime array, copy the original to it.
// --------------------------------------------------------------------------------
HRESULT HrCopyFiletimeArray(LPFILETIME pIn, ULONG cEntries, PROPVARIANT FAR * pvOut)
{
pvOut->vt = VT_VECTOR | VT_FILETIME;
return(HrCopyArray((LPBYTE)pIn, cEntries, pvOut, sizeof(FILETIME)));
}
// --------------------------------------------------------------------------------
// MergeDWORDFlags
//
// Merge the flags from an array of DWORDs into one DWORD
// --------------------------------------------------------------------------------
DWORD MergeDWORDFlags(LPDWORD rgdw, ULONG cEntries)
{
DWORD dwReturn = 0;
for (ULONG i = 0; i < cEntries; i++)
{
dwReturn |= rgdw[i];
}
return(dwReturn);
}
#ifdef SMIME_V3
// --------------------------------------------------------------------------------
// CMessageBody::Encode
// --------------------------------------------------------------------------------
HRESULT CMessageBody::Encode(HWND hwnd, DWORD dwFlags)
{
return E_FAIL;
}
// --------------------------------------------------------------------------------
// CMessageBody::Decode
// --------------------------------------------------------------------------------
HRESULT CMessageBody::Decode(HWND hwnd, DWORD dwFlags, IMimeSecurityCallback * pCallback)
{
return E_FAIL;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetRecipientCount
// --------------------------------------------------------------------------------
HRESULT CMessageBody::GetRecipientCount(DWORD dwFlags, DWORD * pdwRecipCount)
{
Assert(dwFlags == 0);
if (dwFlags != 0) return E_INVALIDARG;
*pdwRecipCount = m_rOptions.cRecipients;
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessageBody::AddRecipient
// --------------------------------------------------------------------------------
HRESULT CMessageBody::AddRecipient(DWORD dwFlags, DWORD cRecipData,
PCMS_RECIPIENT_INFO precipData)
{
DWORD cbExtra;
DWORD dw;
DWORD dwCaps;
HRESULT hr;
DWORD i;
Assert((dwFlags & ~(SMIME_RECIPIENT_REPLACE_ALL)) == 0);
if ((dwFlags & (~SMIME_RECIPIENT_REPLACE_ALL)) != 0) return E_INVALIDARG;
//
// Query capabilities
//
CHECKHR(hr = CapabilitiesSupported(&dwCaps));
//
// Start by running the verification code on the structures.
//
for (i=0; i<cRecipData; i++)
{
switch(precipData[i].dwRecipientType)
{
//
// If you tell use that you don't know whats going on, then you must
// 1. Give us a certificate
// 2. If you don't specify an alg, we must recognize the alg in the cert
//
case CMS_RECIPIENT_INFO_TYPE_UNKNOWN:
if (precipData[i].pccert == NULL)
{
hr = E_INVALIDARG;
goto exit;
}
if (precipData[i].KeyEncryptionAlgorithm.pszObjId == NULL)
{
hr = _HrMapPublicKeyAlg(&precipData[i].pccert->pCertInfo->SubjectPublicKeyInfo,
&dw, NULL);
if (FAILED(hr))
{
goto exit;
}
if (dw == CMS_RECIPIENT_INFO_TYPE_KEYTRANS) {
;
}
else if (dw == CMS_RECIPIENT_INFO_TYPE_KEYAGREE) {
if (!(dwCaps & SMIME_SUPPORT_KEY_AGREE)) {
return MIME_E_SECURITY_NOTIMPLEMENTED;
}
}
else {
return E_INVALIDARG;
}
}
break;
//
// We have no requirements here.
//
case CMS_RECIPIENT_INFO_TYPE_KEYTRANS:
break;
case CMS_RECIPIENT_INFO_TYPE_KEYAGREE:
if (!(dwCaps & SMIME_SUPPORT_KEY_AGREE)) {
return MIME_E_SECURITY_NOTIMPLEMENTED;
}
break;
//
// If you give use a mail list key, you must also tell us the
// public key identifier and the mail list key or we are going
// to fail big time
//
case CMS_RECIPIENT_INFO_TYPE_MAIL_LIST:
if (!(dwCaps & SMIME_SUPPORT_MAILLIST)) {
return MIME_E_SECURITY_NOTIMPLEMENTED;
}
if ((precipData[i].dwU1 != CMS_RECIPIENT_INFO_PUBKEY_PROVIDER) ||
(precipData[i].dwU3 != CMS_RECIPIENT_INFO_KEYID_KEY_ID))
{
hr = E_INVALIDARG;
goto exit;
}
break;
default:
Assert(FALSE);
hr = E_INVALIDARG;
goto exit;
}
}
//
//
if (dwFlags & SMIME_RECIPIENT_REPLACE_ALL)
{
for (i=0; i<m_rOptions.cRecipients; i++)
{
FreeRecipientInfoContent(&m_rOptions.rgRecipients[i]);
}
m_rOptions.cRecipients = 0;
}
//
Assert(m_rOptions.cRecipients <= m_rOptions.cRecipsAllocated);
if (m_rOptions.cRecipients >= m_rOptions.cRecipsAllocated)
{
hr = HrRealloc((LPVOID *) &m_rOptions.rgRecipients,
(m_rOptions.cRecipsAllocated+cRecipData+5)*sizeof(CMS_RECIPIENT_INFO));
if (FAILED(hr))
{
goto exit;
}
m_rOptions.cRecipsAllocated += 5 + cRecipData;
}
//
// Do the actual copy of the data
//
CHECKHR(hr = _HrCopyRecipInfos(cRecipData, precipData,
&m_rOptions.rgRecipients[m_rOptions.cRecipients]));
m_rOptions.cRecipients += cRecipData;
hr = S_OK;
exit:
return hr;
}
// ------------------------------------------------------------------------------
// CMessageBody::_HrMapPublicKeyAlg
// ------------------------------------------------------------------------------
HRESULT CMessageBody::_HrMapPublicKeyAlg(CERT_PUBLIC_KEY_INFO * pkey, DWORD * pdw,
CRYPT_ALGORITHM_IDENTIFIER ** ppalg)
{
HRESULT hr = E_INVALIDARG;
static CRYPT_ALGORITHM_IDENTIFIER rgAlgs[] = {
{szOID_RSA_RSA, {0, 0}},
{szOID_RSA_SMIMEalgESDH, {0, 0}}
};
if (lstrcmp(pkey->Algorithm.pszObjId, szOID_RSA_RSA) == 0)
{
*pdw = CMS_RECIPIENT_INFO_TYPE_KEYTRANS;
if (ppalg != NULL)
{
*ppalg = &rgAlgs[0];
}
hr = S_OK;
}
else if (lstrcmp(pkey->Algorithm.pszObjId, szOID_ANSI_X942_DH) == 0)
{
*pdw = CMS_RECIPIENT_INFO_TYPE_KEYAGREE;
if (ppalg != NULL)
{
*ppalg = &rgAlgs[1];
}
hr = S_OK;
}
else {
*pdw = CMS_RECIPIENT_INFO_TYPE_UNKNOWN;
}
return hr;
}
// ------------------------------------------------------------------------------
// CMessageBody::_HrCopyRecipInfos
// ------------------------------------------------------------------------------
HRESULT CMessageBody::_HrCopyRecipInfos(DWORD cItems,
const CMS_RECIPIENT_INFO * precipSrc,
PCMS_RECIPIENT_INFO precipDst)
{
DWORD cb;
DWORD dw;
HRESULT hr;
DWORD i;
CRYPT_ALGORITHM_IDENTIFIER * palg;
memset(precipDst, 0, cItems*sizeof(*precipDst));
for (i=0; i<cItems; i++, precipSrc++, precipDst++)
{
//
// Now copy over the information
//
precipDst->dwRecipientType = precipSrc->dwRecipientType;
if (precipSrc->pccert != NULL)
{
precipDst->pccert = CertDuplicateCertificateContext(
(PCCERT_CONTEXT) precipSrc->pccert);
}
// Move over the key encryption alg if it exists
if (precipSrc->KeyEncryptionAlgorithm.pszObjId != NULL)
{
CHECKHR(hr = HrCopyOID(precipSrc->KeyEncryptionAlgorithm.pszObjId,
&precipDst->KeyEncryptionAlgorithm.pszObjId));
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->KeyEncryptionAlgorithm.Parameters,
&precipDst->KeyEncryptionAlgorithm.Parameters));
}
else {
//
// If the data does not exist, then we need to create the data
// from scratch. We do this by pulling information out of the
// certificate and create the algorithm information about this.
//
// Maps RSA->RSA; DH -> smimeAlgESDH
//
Assert(precipSrc->pccert != NULL);
hr = _HrMapPublicKeyAlg(&precipDst->pccert->pCertInfo->SubjectPublicKeyInfo,
&dw, &palg);
Assert(hr == S_OK);
precipDst->dwRecipientType = dw;
CHECKHR(hr = HrCopyCryptAlgorithm(palg, &precipDst->KeyEncryptionAlgorithm));
}
//
// Move over the aux encryption info if it exists. The length has already
// been copied over
//
if (precipSrc->cbKeyEncryptionAuxInfo != 0)
{
CHECKHR(hr = HrAlloc(&precipDst->pvKeyEncryptionAuxInfo,
precipSrc->cbKeyEncryptionAuxInfo));
}
//
// Copy over the subject key id information
//
precipDst->dwU1 = precipSrc->dwU1;
switch (precipSrc->dwU1)
{
case CMS_RECIPIENT_INFO_PUBKEY_CERTIFICATE:
if (precipDst->dwRecipientType == CMS_RECIPIENT_INFO_TYPE_KEYTRANS)
{
precipDst->dwU1 = CMS_RECIPIENT_INFO_PUBKEY_KEYTRANS;
CHECKHR(hr = HrCopyCryptBitBlob(
&precipSrc->pccert->pCertInfo->SubjectPublicKeyInfo.PublicKey,
&precipDst->u1.SubjectPublicKey));
}
else if (precipDst->dwRecipientType == CMS_RECIPIENT_INFO_TYPE_KEYAGREE)
{
precipDst->dwU1 = CMS_RECIPIENT_INFO_PUBKEY_EPHEMERAL_KEYAGREE;
CHECKHR(hr = HrCopyCryptBitBlob(
&precipSrc->pccert->pCertInfo->SubjectPublicKeyInfo.PublicKey,
&precipDst->u1.u3.SubjectPublicKey));
// precipDst->u1.u3.UserKeyingMaterial = NULL;
CHECKHR(hr = HrCopyCryptAlgorithm(
&precipSrc->pccert->pCertInfo->SubjectPublicKeyInfo.Algorithm,
&precipDst->u1.u3.EphemeralAlgorithm));
}
else {
hr = E_INVALIDARG;
goto exit;
}
break;
case CMS_RECIPIENT_INFO_PUBKEY_KEYTRANS:
CHECKHR(hr = HrCopyCryptBitBlob(&precipSrc->u1.SubjectPublicKey,
&precipDst->u1.SubjectPublicKey));
break;
case CMS_RECIPIENT_INFO_PUBKEY_EPHEMERAL_KEYAGREE:
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->u1.u3.UserKeyingMaterial,
&precipDst->u1.u3.UserKeyingMaterial));
CHECKHR(hr = HrCopyCryptAlgorithm(&precipSrc->u1.u3.EphemeralAlgorithm,
&precipDst->u1.u3.EphemeralAlgorithm));
CHECKHR(hr = HrCopyCryptBitBlob(&precipSrc->u1.u3.SubjectPublicKey,
&precipDst->u1.u3.SubjectPublicKey));
break;
case CMS_RECIPIENT_INFO_PUBKEY_STATIC_KEYAGREE:
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->u1.u4.UserKeyingMaterial,
&precipDst->u1.u4.UserKeyingMaterial));
if (!CryptContextAddRef(precipSrc->u1.u4.hprov, 0, 0))
{
hr = HrGetLastError();
goto exit;
}
precipDst->u1.u4.hprov = precipSrc->u1.u4.hprov;
precipDst->u1.u4.dwKeySpec = precipSrc->u1.u4.dwKeySpec;
CHECKHR(hr = HrCopyCertId(&precipSrc->u1.u4.senderCertId,
&precipDst->u1.u4.senderCertId));
CHECKHR(hr = HrCopyCryptBitBlob(&precipSrc->u1.u4.SubjectPublicKey,
&precipDst->u1.u4.SubjectPublicKey));
break;
// hprov & hkey are already copied over
case CMS_RECIPIENT_INFO_PUBKEY_PROVIDER:
if (!CryptContextAddRef(precipDst->u1.u2.hprov, 0, 0))
{
hr = HrGetLastError();
goto exit;
}
precipDst->u1.u2.hprov = precipSrc->u1.u2.hprov;
if (!CryptDuplicateKey(precipSrc->u1.u2.hkey, 0, 0,
&precipDst->u1.u2.hkey))
{
hr = HrGetLastError();
goto exit;
}
precipDst->u1.u2.hkey = precipSrc->u1.u2.hkey;
break;
}
precipDst->dwU3 = precipSrc->dwU3;
switch (precipSrc->dwU3)
{
case CMS_RECIPIENT_INFO_KEYID_CERTIFICATE:
precipDst->dwU3 = CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL;
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->pccert->pCertInfo->Issuer,
&precipDst->u3.IssuerSerial.Issuer));
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->pccert->pCertInfo->SerialNumber,
&precipDst->u3.IssuerSerial.SerialNumber));
break;
case CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL:
CHECKHR(hr = HrCopyCryptDataBlob( &precipSrc->u3.IssuerSerial.Issuer,
&precipDst->u3.IssuerSerial.Issuer));
CHECKHR(hr = HrCopyCryptDataBlob( &precipSrc->u3.IssuerSerial.SerialNumber,
&precipDst->u3.IssuerSerial.SerialNumber));
break;
case CMS_RECIPIENT_INFO_KEYID_KEY_ID:
CHECKHR(hr = HrCopyCryptDataBlob(&precipSrc->u3.KeyId, &precipDst->u3.KeyId));
break;
}
precipDst->filetime = precipSrc->filetime;
if (precipSrc->pOtherAttr != NULL)
{
Assert(FALSE);
}
}
hr = S_OK;
exit:
return hr;
}
// ------------------------------------------------------------------------------
// CMessageBody::GetRecipient
// ------------------------------------------------------------------------------
HRESULT CMessageBody::GetRecipient(DWORD dwFlags, DWORD iRecipient, DWORD cRecipients, PCMS_RECIPIENT_INFO pRecipData)
{
DWORD cbAlloc;
HRESULT hr;
LPBYTE pb;
PCMS_RECIPIENT_INFO precip;
if (iRecipient+cRecipients > m_rOptions.cRecipients)
{
hr = E_INVALIDARG;
goto exit;
}
precip = &m_rOptions.rgRecipients[iRecipient];
//
// Copy the buffer
//
CHECKHR(hr = _HrCopyRecipInfos(cRecipients, precip, pRecipData));
hr = S_OK;
exit:
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::DeleteRecipient
// --------------------------------------------------------------------------------
HRESULT CMessageBody::DeleteRecipient(DWORD dwFlags, DWORD iRecipient, DWORD cRecipients)
{
return E_FAIL;
}
// --------------------------------------------------------------------------------
// CMessageBody::GetAttribute
// --------------------------------------------------------------------------------
HRESULT CMessageBody::GetAttribute(DWORD dwFlags, DWORD iSigner, DWORD iAttribSet,
DWORD iInstance, LPCSTR pszObjId,
CRYPT_ATTRIBUTE ** ppattr)
{
DWORD cb;
HRESULT hr;
DWORD i;
DWORD i1;
PCRYPT_ATTRIBUTE pattr = NULL;
PCRYPT_ATTRIBUTES pattrs;
PCRYPT_ATTRIBUTE pattrSrc;
LPBYTE pb;
LPBLOB pblob;
//
// Start with some simple parameter checks
//
if ( // (iAttribSet < SMIME_ATTRIBUTE_SET_SIGNED) ||
(iAttribSet > SMIME_ATTRIBUTE_SET_UNPROTECTED))
{
return E_INVALIDARG;
}
if (dwFlags & ~(SMIME_RECIPIENT_REPLACE_ALL))
{
return E_INVALIDARG;
}
if (iAttribSet == SMIME_ATTRIBUTE_SET_UNPROTECTED)
{
if ((iSigner != 0) || !g_FSupportV3)
return E_INVALIDARG;
if (m_rOptions.rgrgpattrs[iAttribSet] == NULL)
return S_FALSE;
}
else if (iSigner >= m_rOptions.cSigners)
{
return E_INVALIDARG;
}
//
// Special case of getting every single attribute on record
//
if (pszObjId == NULL) {
if (!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_Microsoft_Attribute_Sequence,
m_rOptions.rgrgpattrs[iAttribSet][iSigner],
0, NULL, NULL, &cb))
{
hr = HrGetLastError();
goto exit;
}
if (!MemAlloc((LPVOID *) &pattr, cb + sizeof(CRYPT_ATTRIBUTE) +
sizeof(CRYPT_ATTR_BLOB))) {
hr = E_OUTOFMEMORY;
goto exit;
}
pattr->cValue = 1;
pattr->rgValue = (PCRYPT_ATTR_BLOB) &pattr[1];
pb = (LPBYTE) &pattr->rgValue[1];
pattr->pszObjId = NULL;
pattr->rgValue[0].pbData = pb;
pattr->rgValue[0].cbData = cb;
if (!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_Microsoft_Attribute_Sequence,
m_rOptions.rgrgpattrs[iAttribSet][iSigner],
0, NULL, pb, &cb))
{
hr = HrGetLastError();
goto exit;
}
*ppattr = pattr;
pattr = NULL;
hr = S_OK;
goto exit;
}
//
//
pattrSrc = _FindAttribute(m_rOptions.rgrgpattrs[iAttribSet][iSigner], pszObjId,
iInstance);
if (pattrSrc != NULL)
{
#ifdef _WIN64
cb = sizeof(CRYPT_ATTRIBUTE) + LcbAlignLcb(strlen(pszObjId) + 1);
#else
cb = sizeof(CRYPT_ATTRIBUTE) + strlen(pszObjId) + 1;
#endif
for (i1=0; i1<pattrSrc->cValue; i1++)
{
cb += sizeof(CRYPT_ATTR_BLOB);
#ifdef _WIN64
cb += LcbAlignLcb(pattrSrc->rgValue[i1].cbData);
#else
cb += pattrSrc->rgValue[i1].cbData;
#endif
}
if (!MemAlloc((LPVOID *) &pattr, cb))
{
hr = E_OUTOFMEMORY;
goto exit;
}
pattr->cValue = pattrSrc->cValue;
pattr->rgValue = (PCRYPT_ATTR_BLOB) &pattr[1];
pb = (LPBYTE) &pattr->rgValue[pattrSrc->cValue];
pattr->pszObjId = (LPSTR) pb;
cb = strlen(pszObjId)+1;
memcpy(pattr->pszObjId, pszObjId, cb);
#ifdef _WIN64
pb += LcbAlignLcb(cb);
#else
pb += cb;
#endif //_WIN64
for (i1=0; i1<pattrSrc->cValue; i1++)
{
pattr->rgValue[i1].pbData = pb;
pattr->rgValue[i1].cbData = pattrSrc->rgValue[i1].cbData;
memcpy(pb, pattrSrc->rgValue[i1].pbData,
pattrSrc->rgValue[i1].cbData);
#ifdef _WIN64
pb += LcbAlignLcb(pattrSrc->rgValue[i1].cbData);
#else
pb += pattrSrc->rgValue[i1].cbData;
#endif //_WIN64
}
*ppattr = pattr;
pattr = NULL;
hr = S_OK;
}
else {
hr = S_FALSE;
}
exit:
if (pattr != NULL) MemFree(pattr);
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::SetAttribute
// --------------------------------------------------------------------------------
HRESULT CMessageBody::SetAttribute(DWORD dwFlags, DWORD iSigner, DWORD iAttribSet,
const CRYPT_ATTRIBUTE * pattr)
{
HRESULT hr;
DWORD i;
//
// Start with some simple parameter checks
//
if (// (iAttribSet < SMIME_ATTRIBUTE_SET_SIGNED) ||
(iAttribSet > SMIME_ATTRIBUTE_SET_UNPROTECTED))
{
return E_INVALIDARG;
}
if (dwFlags & ~(SMIME_ATTR_ADD_TO_EXISTING |
SMIME_ATTR_ADD_IF_NOT_EXISTS))
{
return E_INVALIDARG;
}
if (iAttribSet == SMIME_ATTRIBUTE_SET_UNPROTECTED) {
if ((iSigner != 0) || !g_FSupportV3)
return E_INVALIDARG;
if (m_rOptions.rgrgpattrs[iAttribSet] == NULL) {
if (!MemAlloc((LPVOID *) &m_rOptions.rgrgpattrs[iAttribSet],
sizeof(LPVOID))) {
return E_OUTOFMEMORY;
}
m_rOptions.rgrgpattrs[iAttribSet][0] = NULL;
}
}
else if ((iSigner >= m_rOptions.cSigners) && (iSigner != (DWORD) -1))
{
return E_INVALIDARG;
}
//
// Now make the correct utility call to put things right
//
Assert(pattr->cValue == 1);
if (iSigner == (DWORD) -1)
{
Assert(iAttribSet != SMIME_ATTRIBUTE_SET_UNPROTECTED);
for (i=0; i<m_rOptions.cSigners; i++)
{
hr = _HrSetAttribute(dwFlags, &m_rOptions.rgrgpattrs[iAttribSet][i],
pattr->pszObjId, pattr->rgValue[0].cbData,
pattr->rgValue[0].pbData);
if (FAILED(hr))
{
break;
}
}
}
else {
hr = _HrSetAttribute(dwFlags, &m_rOptions.rgrgpattrs[iAttribSet][iSigner],
pattr->pszObjId, pattr->rgValue[0].cbData,
pattr->rgValue[0].pbData);
}
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::DeleteAttribute
// --------------------------------------------------------------------------------
HRESULT CMessageBody::DeleteAttribute(DWORD dwFlags, DWORD iSigner,
DWORD iAttribSet, DWORD iInstance,
LPCSTR pszObjId)
{
UINT i;
PCRYPT_ATTRIBUTE pattr;
PCRYPT_ATTRIBUTES pattrs;
//
// Start with some simple parameter checks
//
if (// (iAttribSet < SMIME_ATTRIBUTE_SET_SIGNED) ||
(iAttribSet > SMIME_ATTRIBUTE_SET_UNPROTECTED))
{
return E_INVALIDARG;
}
if (dwFlags & ~(SMIME_RECIPIENT_REPLACE_ALL))
{
return E_INVALIDARG;
}
if (iAttribSet == SMIME_ATTRIBUTE_SET_UNPROTECTED) {
if ((iSigner != 0) || !g_FSupportV3)
return E_INVALIDARG;
if (m_rOptions.rgrgpattrs[iAttribSet] == NULL)
return S_OK;
}
else if (iSigner >= m_rOptions.cSigners)
{
return E_INVALIDARG;
}
//
pattrs = m_rOptions.rgrgpattrs[iAttribSet][iSigner];
pattr = _FindAttribute(pattrs, pszObjId, iInstance);
if (pattr != NULL)
{
i = (UINT) (pattr - &pattrs->rgAttr[0]);
Assert( (0 <= ((int) i)) && (((int) i) < pattrs->cAttr));
memcpy(pattr, pattr+1, (pattrs->cAttr - i - 1) * sizeof(CRYPT_ATTRIBUTE));
pattrs->cAttr -= 1;
}
else {
return S_FALSE;
}
return S_OK;
}
HRESULT CMessageBody::_SetNames(ReceiptNames * pnames, DWORD cNames, CERT_NAME_BLOB * rgNames)
{
DWORD cb;
HRESULT hr = S_OK;
DWORD i;
LPBYTE pb;
if (pnames->rgNames != NULL)
{
MemFree(pnames->rgNames);
pnames->rgNames = NULL;
pnames->cNames = 0;
}
for (i=0, cb=cNames*sizeof(CERT_NAME_BLOB); i<cNames; i++)
#ifdef _WIN64
cb += LcbAlignLcb(rgNames[i].cbData);
#else
cb += rgNames[i].cbData;
#endif //_WIN64
CHECKHR(hr = HrAlloc((LPVOID *)&pnames->rgNames, cb));
pb = (LPBYTE) &pnames->rgNames[cNames];
for (i=0; i<cNames; i++)
{
pnames->rgNames[i].pbData = pb;
pnames->rgNames[i].cbData = rgNames[i].cbData;
memcpy(pb, rgNames[i].pbData, rgNames[i].cbData);
#ifdef _WIN64
pb += LcbAlignLcb(rgNames[i].cbData);
#else
pb += rgNames[i].cbData;
#endif //_WIN64
}
pnames->cNames = cNames;
exit:
return hr;
}
HRESULT CMessageBody::_MergeNames(ReceiptNames * pnames, DWORD cNames, CERT_NAME_BLOB * rgNames)
{
DWORD cb;
HRESULT hr = S_OK;
DWORD i;
DWORD i1;
LPBYTE pb;
CERT_NAME_BLOB * p;
for (i=0, cb=0; i<pnames->cNames; i++)
#ifdef _WIN64
cb += LcbAlignLcb(pnames->rgNames[i].cbData);
#else
cb += pnames->rgNames[i].cbData;
#endif //_WIN64
for (i=0; i<cNames; i++)
#ifdef _WIN64
cb += LcbAlignLcb(rgNames[i].cbData);
#else
cb += rgNames[i].cbData;
#endif //_WIN64
CHECKHR(hr = HrAlloc((LPVOID *)&p, cb + (pnames->cNames + cNames) *
sizeof(CERT_NAME_BLOB)));
pb = (LPBYTE) &p[pnames->cNames + cNames];
for (i=0, i1=0; i<pnames->cNames; i++, i1++)
{
p[i1].pbData = pb;
p[i1].cbData = pnames->rgNames[i].cbData;
memcpy(pb, pnames->rgNames[i].pbData, pnames->rgNames[i].cbData);
#ifdef _WIN64
pb += LcbAlignLcb(pnames->rgNames[i].cbData);
#else
pb += pnames->rgNames[i].cbData;
#endif //_WIN64
}
for (i=0; i<pnames->cNames; i++, i1++)
{
p[i1].pbData = pb;
p[i1].cbData = rgNames[i].cbData;
memcpy(pb, rgNames[i].pbData, rgNames[i].cbData);
#ifdef _WIN64
pb += LcbAlignLcb(rgNames[i].cbData);
#else
pb += rgNames[i].cbData;
#endif //_WIN64
}
MemFree(pnames->rgNames);
pnames->rgNames = p;
pnames->cNames = i1;
exit:
return hr;
}
/////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------------
// CMessageBody::_GetReceiptRequest
//--------------------------------------------------------------------------------
HRESULT CMessageBody::_GetReceiptRequest(DWORD dwFlags,
PSMIME_RECEIPT_REQUEST *ppreq,
ReceiptNames *pReceiptsTo,
DWORD *pcbReceipt,
LPBYTE *ppbReceipt,
DWORD *pcbMsgHash,
LPBYTE *ppbMsgHash)
{
DWORD cb;
DWORD cbMLHistory = 0;
DWORD cbMsgHash = 0;
DWORD cbReceipt = 0;
DWORD cSendersList = 0;
DWORD dwReciptsFrom;
HRESULT hr = S_OK;
DWORD i;
DWORD iAttr;
DWORD iSigner;
PCRYPT_ATTRIBUTES pattrs = NULL;
LPBYTE pb;
LPBYTE pbMLHistory = NULL;
LPBYTE pbMsgHash = NULL;
LPBYTE pbReceipt = NULL;
PSMIME_RECEIPT_REQUEST preq = NULL;
ReceiptNames receiptsTo = {0, NULL};
CERT_NAME_BLOB *rgSendersList = NULL;
PROPVARIANT var;
*ppreq = NULL;
if (pReceiptsTo != NULL)
{
pReceiptsTo->cNames = 0;
pReceiptsTo->rgNames = NULL;
}
if (pcbReceipt != NULL) *pcbReceipt = 0;
if (ppbReceipt != NULL) *ppbReceipt = NULL;
Assert(((pcbReceipt == NULL) && (ppbReceipt == NULL)) ||
((pcbReceipt != NULL) && (ppbReceipt != NULL)));
if (pcbMsgHash != NULL) *pcbMsgHash = 0;
if (ppbMsgHash != NULL) *ppbMsgHash = NULL;
Assert(((pcbMsgHash == NULL) && (ppbMsgHash == NULL)) ||
((pcbMsgHash != NULL) && (ppbMsgHash != NULL)));
//
// If this is the bottom layer then
// find and Decode Receipt Request
// Set ReceiptsTo from the request
// if not then
// check lower layers
// if mlExpansion in this layer? No -- Skip to next layer
// Receipt for First Tier only? Yes - return S_FALSE
// Policy override on mlExpansion?
// None - return S_FALSE
// return S_OK
// If this is not the bottom layer then look for MLHistory
if (IsContentType(STR_CNT_MULTIPART, "y-security") == S_OK)
{
Assert(m_pNode->cChildren == 1);
hr = m_pNode->pChildHead->pBody->_GetReceiptRequest(
dwFlags,
&preq,
(pReceiptsTo) ? &receiptsTo : NULL,
(pcbReceipt) ? &cbReceipt : NULL,
(ppbReceipt) ? &pbReceipt : NULL,
(pcbMsgHash) ? &cbMsgHash : NULL,
(ppbMsgHash) ? &pbMsgHash : NULL);
if (hr)
goto exit;
//
// Walk through each signer's authenticated attributes processing the
// relevant attribute.
//
for (iSigner=0; iSigner<m_rOptions.cSigners; iSigner++)
{
//
// Walk through each attribute looking for
// a Mail List expansion history
//
pattrs = m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][iSigner];
for (iAttr=0; iAttr<pattrs->cAttr; iAttr++)
{
if (lstrcmp(pattrs->rgAttr[iAttr].pszObjId,
szOID_SMIME_MLExpansion_History) == 0)
{
//
// If receipts are from first tier only and we are at
// this layer then we are not first tier by definition.
//
if (preq->ReceiptsFrom.AllOrFirstTier == SMIME_RECEIPTS_FROM_FIRST_TIER)
{
hr = S_FALSE;
goto exit;
}
if (cbMLHistory == 0)
{
CHECKHR(hr = HrAlloc((LPVOID *)&pbMLHistory,
pattrs->rgAttr[iAttr].rgValue[0].cbData));
memcpy(pbMLHistory,pattrs->rgAttr[iAttr].rgValue[0].pbData,
pattrs->rgAttr[iAttr].rgValue[0].cbData);
cbMLHistory = pattrs->rgAttr[iAttr].rgValue[0].cbData;
}
else if ((pattrs->rgAttr[iAttr].rgValue[0].cbData != cbMLHistory) ||
(memcmp(pattrs->rgAttr[iAttr].rgValue[0].pbData,
pbMLHistory, cbMLHistory)))
{
// Hey, all MLHistorys should match
hr = S_FALSE;
goto exit;
}
break;
}
}
}
// Decode and respect the MLHistory
if (cbMLHistory != 0)
{
PSMIME_ML_EXPANSION_HISTORY pmlhist = NULL;
//
// Crack the attribute
//
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
szOID_SMIME_MLExpansion_History,
pbMLHistory,
cbMLHistory,
CRYPT_ENCODE_ALLOC_FLAG,
&CryptDecodeAlloc, &pmlhist, &cb))
goto GeneralFail;
PSMIME_MLDATA pMLData = &pmlhist->rgMLData[pmlhist->cMLData-1];
switch( pMLData->dwPolicy)
{
// No receipt is to be returned
case SMIME_MLPOLICY_NONE:
hr = S_FALSE;
SafeMemFree(pmlhist);
goto exit;
// Return receipt to a new list
case SMIME_MLPOLICY_INSTEAD_OF:
if (pReceiptsTo != NULL)
_SetNames(&receiptsTo, pMLData->cNames, pMLData->rgNames);
break;
case SMIME_MLPOLICY_IN_ADDITION_TO:
if (pReceiptsTo != NULL)
_MergeNames(&receiptsTo, pMLData->cNames, pMLData->rgNames);
break;
case SMIME_MLPOLICY_NO_CHANGE:
break;
default:
SafeMemFree(pmlhist);
goto GeneralFail;
}
SafeMemFree(pmlhist);
}
}
else
{
// Else this is the bottom layer so look for receipt request
//
// Walk through each signer's authenticated attributes processing the
// two relevant attributes.
//
for (iSigner=0; iSigner<m_rOptions.cSigners; iSigner++)
{
// Check if receipt was created for this signer if not then
// it's signature must have been bad or it had not request
if (m_rOptions.rgblobReceipt[iSigner].cbSize == 0)
continue;
// if we have a receipt we should also have a message hash
if (m_rOptions.rgblobMsgHash[iSigner].cbSize == 0)
{
Assert(FALSE);
continue;
}
if (m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][iSigner] == NULL)
{
// Hey how did we get a receipt without any attributes
Assert(FALSE);
goto GeneralFail;
}
pattrs = m_rOptions.rgrgpattrs[SMIME_ATTRIBUTE_SET_SIGNED][iSigner];
//
// Walk through each attribute looking for the receipt request
//
for (iAttr=0; iAttr<pattrs->cAttr; iAttr++)
{
if (lstrcmp(pattrs->rgAttr[iAttr].pszObjId,
szOID_SMIME_MLExpansion_History) == 0)
{
// We are at the bottom so we should not have found a ML History
hr = S_FALSE;
goto exit;
}
if (lstrcmp(pattrs->rgAttr[iAttr].pszObjId,
szOID_SMIME_Receipt_Request) == 0)
{
//
// Crack the contents of the receipt request
//
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
szOID_SMIME_Receipt_Request,
pattrs->rgAttr[iAttr].rgValue[0].pbData,
pattrs->rgAttr[iAttr].rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
&CryptDecodeAlloc, &preq, &cb) ||
(preq->cReceiptsTo == 0))
goto GeneralFail;
//
// Initialize the ReceiptsTo list
//
if (pReceiptsTo != NULL)
_SetNames(&receiptsTo, preq->cReceiptsTo, preq->rgReceiptsTo);
if (ppbReceipt != NULL)
{
CHECKHR(hr = HrAlloc((LPVOID *)&pbReceipt,
m_rOptions.rgblobReceipt[iSigner].cbSize));
memcpy(pbReceipt,m_rOptions.rgblobReceipt[iSigner].pBlobData,
m_rOptions.rgblobReceipt[iSigner].cbSize);
}
if (pcbReceipt != NULL) cbReceipt = m_rOptions.rgblobReceipt[iSigner].cbSize;
if (ppbMsgHash != NULL)
{
CHECKHR(hr = HrAlloc((LPVOID *)&pbMsgHash,
m_rOptions.rgblobMsgHash[iSigner].cbSize));
memcpy(pbMsgHash,m_rOptions.rgblobMsgHash[iSigner].pBlobData,
m_rOptions.rgblobMsgHash[iSigner].cbSize);
}
if (pcbMsgHash != NULL) cbMsgHash = m_rOptions.rgblobMsgHash[iSigner].cbSize;
break;
}
}
}
if (preq == NULL)
{
// We are at the bottom so we should have found a receipt request
hr = S_FALSE;
goto exit;
}
}
*ppreq = preq;
preq = NULL;
if (pReceiptsTo != NULL)
{
pReceiptsTo->cNames = receiptsTo.cNames;
pReceiptsTo->rgNames = receiptsTo.rgNames;
receiptsTo.cNames = 0;
receiptsTo.rgNames = NULL;
}
if (pcbReceipt != NULL)
{
*pcbReceipt = cbReceipt;
cbReceipt = 0;
}
if (ppbReceipt != NULL)
{
*ppbReceipt = pbReceipt;
pbReceipt = NULL;
}
if (pcbMsgHash != NULL)
{
*pcbMsgHash = cbMsgHash;
cbMsgHash = 0;
}
if (ppbMsgHash != NULL)
{
*ppbMsgHash = pbMsgHash;
pbMsgHash = NULL;
}
exit:
SafeMemFree(preq);
SafeMemFree(receiptsTo.rgNames);
SafeMemFree(pbReceipt);
SafeMemFree(pbMsgHash);
SafeMemFree(pbMLHistory);
return hr;
GeneralFail:
hr = E_FAIL;
goto exit;
}
//--------------------------------------------------------------------------------
// CMessageBody::CreateReceipt
//--------------------------------------------------------------------------------
HRESULT CMessageBody::CreateReceipt(DWORD dwFlags, DWORD cbFromNames,
const BYTE *pbFromNames, DWORD cSignerCertificates,
PCCERT_CONTEXT *rgSignerCertificates,
IMimeMessage ** ppMimeMessageReceipt)
{
CRYPT_ATTRIBUTE attrMsgHash;
DWORD cb;
DWORD cbEncodedMsgHash = 0;
DWORD cbMsgHash = 0;
DWORD cbReceipt = 0;
DWORD cLayers;
DWORD dwReceiptsFrom;
BOOL fAddedAddress = FALSE;
HRESULT hr;
DWORD i;
DWORD i1;
DWORD i2;
DWORD iLayer;
LPBYTE pbEncodedMsgHash = NULL;
LPBYTE pbMsgHash = NULL;
LPBYTE pbReceipt = NULL;
IMimeAddressTable * pmatbl = NULL;
IMimeBody * pmb = NULL;
IMimeMessage * pmm = NULL;
IMimeSecurity2 * pms = NULL;
PSMIME_RECEIPT_REQUEST preq = NULL;
LPSTREAM pstm = NULL;
ReceiptNames receiptsTo = {0, NULL};
PROPVARIANT var;
CRYPT_ATTR_BLOB valMsgHash;
hr = _GetReceiptRequest(dwFlags,
&preq,
&receiptsTo,
&cbReceipt,
&pbReceipt,
&cbMsgHash,
&pbMsgHash);
if (hr)
goto exit;
//
// Am I on the ReceiptsFrom List --
//
if (preq->ReceiptsFrom.cNames != 0)
{
BOOL fFoundMe = FALSE;
CERT_ALT_NAME_INFO * pname2 = NULL;
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
pbFromNames,
cbFromNames,
CRYPT_ENCODE_ALLOC_FLAG,
&CryptDecodeAlloc, &pname2, &cb))
goto GeneralFail;
for (i=0; !fFoundMe && (i<preq->ReceiptsFrom.cNames); i++)
{
CERT_ALT_NAME_INFO * pname = NULL;
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
preq->ReceiptsFrom.rgNames[i].pbData,
preq->ReceiptsFrom.rgNames[i].cbData,
CRYPT_ENCODE_ALLOC_FLAG,
&CryptDecodeAlloc, &pname, &cb))
goto GeneralFail;
for (i1=0; i1<pname->cAltEntry; i1++)
{
for (i2=0; i2<pname2->cAltEntry; i2++)
{
if (pname->rgAltEntry[i1].dwAltNameChoice !=
pname2->rgAltEntry[i2].dwAltNameChoice)
continue;
switch (pname->rgAltEntry[i1].dwAltNameChoice)
{
case CERT_ALT_NAME_RFC822_NAME:
if (lstrcmpiW(pname->rgAltEntry[i1].pwszRfc822Name,
pname2->rgAltEntry[i2].pwszRfc822Name) == 0)
{
fFoundMe = TRUE;
goto FoundMe;
}
}
}
}
FoundMe:
SafeMemFree(pname);
}
SafeMemFree(pname2);
if (!fFoundMe)
{
hr = S_FALSE;
goto exit;
}
}
// Create a stream object to hold the receipt and put the receipt into the
// stream -- this supplies the body of the receipt message.
CHECKHR(hr = MimeOleCreateVirtualStream(&pstm));
CHECKHR(hr = pstm->Write(pbReceipt, cbReceipt, NULL));
CHECKHR(hr = MimeOleCreateMessage(NULL, &pmm));
CHECKHR(hr = pmm->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pmb));
CHECKHR(hr = pmb->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &pms));
CHECKHR(hr = pmb->SetData(IET_BINARY, "OID", szOID_SMIME_ContentType_Receipt,
IID_IStream, pstm));
//
// Address the receipt back to the receipients
//
CHECKHR(hr = pmm->GetAddressTable(&pmatbl));
for (i=0; i<receiptsTo.cNames; i++)
{
CERT_ALT_NAME_INFO * pname = NULL;
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
receiptsTo.rgNames[i].pbData,
receiptsTo.rgNames[i].cbData,
CRYPT_ENCODE_ALLOC_FLAG,
&CryptDecodeAlloc, &pname, &cb))
goto GeneralFail;
for (i1=0; i1<pname->cAltEntry; i1++)
{
int cch;
TCHAR rgch[256];
if (pname->rgAltEntry[i1].dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME)
{
cch = WideCharToMultiByte(CP_ACP, 0,
pname->rgAltEntry[i1].pwszRfc822Name,
-1, rgch, sizeof(rgch), NULL, NULL);
if (cch > 0)
{
CHECKHR(hr = pmatbl->AppendRfc822(IAT_TO, IET_DECODED,
rgch));
fAddedAddress = TRUE;
}
break;
}
}
SafeMemFree(pname);
}
if (!fAddedAddress)
{
hr = S_FALSE;
goto exit;
}
var.vt = VT_UI4;
var.ulVal = MST_CLASS_SMIME_V1 | MST_THIS_BLOBSIGN;
CHECKHR(hr = pmb->SetOption(OID_SECURITY_TYPE, &var));
#ifndef _WIN64
var.vt = (VT_VECTOR | VT_UI4);
var.caul.cElems = cSignerCertificates;
var.caul.pElems = (DWORD *) rgSignerCertificates;
CHECKHR(hr = pmb->SetOption(OID_SECURITY_CERT_SIGNING_RG, &var));
#else
var.vt = (VT_VECTOR | VT_UI8);
var.cauh.cElems = cSignerCertificates;
var.cauh.pElems = (ULARGE_INTEGER *) rgSignerCertificates;
CHECKHR(hr = pmb->SetOption(OID_SECURITY_CERT_SIGNING_RG_64, &var));
#endif
//
// Setup the authorized attribute block so that
// we can get the correct information transfered. At present we
// are only looking at one item to be included here.
// 1. The Message Hash of the message requesting the receipt
valMsgHash.cbData = cbMsgHash;
valMsgHash.pbData = pbMsgHash;
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
&valMsgHash, CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &pbEncodedMsgHash, &cbEncodedMsgHash))
goto GeneralFail;
attrMsgHash.pszObjId = szOID_SMIME_Msg_Sig_Digest;
attrMsgHash.cValue = 1;
attrMsgHash.rgValue = &valMsgHash;
valMsgHash.cbData = cbEncodedMsgHash;
valMsgHash.pbData = pbEncodedMsgHash;
CHECKHR(hr = pms->SetAttribute(0, -1, SMIME_ATTRIBUTE_SET_SIGNED, &attrMsgHash));
hr = S_OK;
*ppMimeMessageReceipt = pmm;
pmm->AddRef();
exit:
SafeMemFree(preq);
SafeMemFree(receiptsTo.rgNames);
SafeMemFree(pbEncodedMsgHash);
SafeMemFree(pbMsgHash);
SafeMemFree(pbReceipt);
if (pstm != NULL) pstm->Release();
if (pmatbl != NULL) pmatbl->Release();
if (pmb != NULL) pmb->Release();
if (pms != NULL) pms->Release();
if (pmm != NULL) pmm->Release();
return hr;
GeneralFail:
hr = E_FAIL;
goto exit;
}
//--------------------------------------------------------------------------------
// CMessageBody::GetReceiptSendersList
//--------------------------------------------------------------------------------
HRESULT CMessageBody::GetReceiptSendersList(DWORD dwFlags, DWORD *pcSendersList,
CERT_NAME_BLOB * *rgSendersList)
{
DWORD cb;
HRESULT hr;
DWORD i;
LPBYTE pb;
PSMIME_RECEIPT_REQUEST preq = NULL;
hr = _GetReceiptRequest(dwFlags,
&preq,
NULL, NULL, NULL, NULL, NULL);
if (hr)
goto exit;
if (preq->ReceiptsFrom.cNames == 0)
{
*rgSendersList = NULL;
*pcSendersList = preq->ReceiptsFrom.cNames;
goto exit;
}
cb = 0;
for (i =0; i < preq->ReceiptsFrom.cNames; i++)
#ifdef _WIN64
cb += sizeof(CERT_NAME_BLOB) + LcbAlignLcb(preq->ReceiptsFrom.rgNames[i].cbData);
#else
cb += sizeof(CERT_NAME_BLOB) + preq->ReceiptsFrom.rgNames[i].cbData;
#endif // _WIN64
CHECKHR(hr = HrAlloc((LPVOID *)rgSendersList, cb));
*pcSendersList = preq->ReceiptsFrom.cNames;
pb = (LPBYTE)*rgSendersList + (sizeof(CERT_NAME_BLOB) * (*pcSendersList));
for (i =0; i < preq->ReceiptsFrom.cNames; i++)
{
(*rgSendersList)[i].cbData = preq->ReceiptsFrom.rgNames[i].cbData;
(*rgSendersList)[i].pbData = pb;
memcpy(pb, preq->ReceiptsFrom.rgNames[i].pbData, preq->ReceiptsFrom.rgNames[i].cbData);
#ifdef _WIN64
pb += LcbAlignLcb((*rgSendersList)[i].cbData);
#else
pb += (*rgSendersList)[i].cbData;
#endif // _WIN64
}
exit:
SafeMemFree(preq);
return hr;
}
// --------------------------------------------------------------------------------
// CMessageBody::VerifyReceipt
//
// Assumes the passed in pMimeMessageReceipt has been decoded and
// it's signature verified.
//
// This function verifies that the values in the Receipt content are
// identical to those i the original sigendData signerInfo that
// requested the receipt and that the message hash of the original message
// is identical to the msgSigDigest sigend Attribute of the receipt
//
// --------------------------------------------------------------------------------
HRESULT CMessageBody::VerifyReceipt(DWORD dwFlags,
IMimeMessage * pMimeMessageReceipt)
{
PCRYPT_ATTRIBUTE pattrMsgHash = NULL;
PCRYPT_ATTRIBUTE pattrMsgHash2 = NULL;
DWORD cb;
DWORD cbMsgHash = 0;
DWORD cbData;
HRESULT hr;
DWORD iReceiptSigner;
DWORD iSigner;
LPBYTE pb;
LPBYTE pbData = NULL;
PCRYPT_ATTR_BLOB pblobMsgHash = NULL;
IMimeBody * pmb = NULL;
IMimeSecurity2 * pms = NULL;
PSMIME_RECEIPT_REQUEST preq = NULL;
PSMIME_RECEIPT pSecReceipt = NULL;
LPSTREAM pstm = NULL;
STATSTG statstg;
PROPVARIANT var;
// If this is not the bottom layer then ask child to verify receipt
if (IsContentType(STR_CNT_MULTIPART, "y-security") == S_OK)
{
Assert(m_pNode->cChildren == 1);
hr = m_pNode->pChildHead->pBody->VerifyReceipt(
dwFlags,
pMimeMessageReceipt);
goto exit;
}
CHECKHR(hr = pMimeMessageReceipt->BindToObject(HBODY_ROOT, IID_IMimeBody,
(LPVOID *) &pmb));
CHECKHR(hr = pmb->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &pms));
CHECKHR(hr = pmb->GetData(IET_BINARY, &pstm));
CHECKHR(hr = pstm->Stat(&statstg,STATFLAG_NONAME));
Assert(statstg.cbSize.HighPart == 0);
CHECKHR(hr = HrAlloc((LPVOID *)&pbData, statstg.cbSize.LowPart));
CHECKHR(hr = pstm->Read(pbData, statstg.cbSize.LowPart, &cbData));
// Check if receipt is the receipt we expect
for (iSigner=0; iSigner<m_rOptions.cSigners; iSigner++)
{
if ((m_rOptions.rgblobReceipt[iSigner].cbSize == cbData) &&
!memcmp(m_rOptions.rgblobReceipt[iSigner].pBlobData,pbData,cbData))
break;
}
// check if we found a matching receipt
if (iSigner == m_rOptions.cSigners)
{
hr = MIME_E_SECURITY_RECEIPT_NOMATCHINGRECEIPTBODY;
goto exit;
}
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_SMIME_ContentType_Receipt,
pbData,
cbData,
CRYPT_ENCODE_ALLOC_FLAG, &CryptDecodeAlloc,
&pSecReceipt, &cb))
goto GeneralFail;
//
// Get the first signatures MsgSigDigest
//
CHECKHR(hr = pms->GetAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED,
0, szOID_SMIME_Msg_Sig_Digest,
&pattrMsgHash));
if ((hr == S_FALSE) ||
(!CryptDecodeObjectEx(X509_ASN_ENCODING,
X509_OCTET_STRING,
pattrMsgHash->rgValue[0].pbData,
pattrMsgHash->rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
&CryptDecodeAlloc, &pblobMsgHash, &cbMsgHash)))
{
hr = MIME_E_SECURITY_RECEIPT_MSGHASHMISMATCH;
goto exit;
}
if ((m_rOptions.rgblobMsgHash[iSigner].cbSize != pblobMsgHash->cbData) ||
memcmp(m_rOptions.rgblobMsgHash[iSigner].pBlobData,
pblobMsgHash->pbData, pblobMsgHash->cbData))
{
hr = MIME_E_SECURITY_RECEIPT_MSGHASHMISMATCH;
goto exit;
}
CHECKHR(hr = pmb->GetOption(OID_SECURITY_SIGNATURE_COUNT, &var));
for (iReceiptSigner = 1; iReceiptSigner < var.ulVal; iReceiptSigner++)
{
CHECKHR(hr = pms->GetAttribute(0, iReceiptSigner, SMIME_ATTRIBUTE_SET_SIGNED,
0, szOID_SMIME_Msg_Sig_Digest,
&pattrMsgHash2));
if ((hr == S_FALSE) ||
(pattrMsgHash->rgValue[0].cbData != pattrMsgHash2->rgValue[0].cbData) ||
memcmp(pattrMsgHash->rgValue[0].pbData,
pattrMsgHash2->rgValue[0].pbData,
pattrMsgHash->rgValue[0].cbData))
{
hr = MIME_E_SECURITY_RECEIPT_MSGHASHMISMATCH;
goto exit;
}
SafeMemFree(pattrMsgHash2);
}
exit:
SafeMemFree(pblobMsgHash);
SafeMemFree(pbData);
SafeMemFree(pSecReceipt);
SafeMemFree(pattrMsgHash);
SafeMemFree(pattrMsgHash2);
if (pstm != NULL) pstm->Release();
if (pmb != NULL) pmb->Release();
if (pms != NULL) pms->Release();
return hr;
GeneralFail:
hr = E_FAIL;
goto exit;
}
// --------------------------------------------------------------------------------
// CMessageBody::CapabilitiesSupported
// --------------------------------------------------------------------------------
HRESULT CMessageBody::CapabilitiesSupported(DWORD * pdwFlags)
{
// Assume no capabilities
*pdwFlags = 0;
// If we have msasn1.dll on the system, then we can support labels
if (FIsMsasn1Loaded()) *pdwFlags |= SMIME_SUPPORT_LABELS;
// If we have a correct crypt32, then we can support receipts & key agreement
DemandLoadCrypt32();
if (g_FSupportV3 && FIsMsasn1Loaded())
*pdwFlags |= SMIME_SUPPORT_RECEIPTS;
if (g_FSupportV3)
*pdwFlags |= SMIME_SUPPORT_KEY_AGREE;
// If we have a correct advapi32, then we can support maillist keys
DemandLoadAdvApi32();
if (VAR_CryptContextAddRef != MY_CryptContextAddRef)
*pdwFlags |= SMIME_SUPPORT_MAILLIST;
return S_OK;
}
// --------------------------------------------------------------------------------
// CMessage::_HrGetAttrs
//
// Utility function to retrieve attributes
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_HrGetAttrs(DWORD cSigners, PCRYPT_ATTRIBUTES * rgpattrs,
LPCSTR pszObjId, PROPVARIANT FAR * pvOut)
{
// Locals
CRYPT_ATTRIBUTES attrs;
HRESULT hr = S_OK;
DWORD i;
DWORD i1;
CRYPT_ATTRIBUTES UNALIGNED *pattrs;
CAPROPVARIANT UNALIGNED *pcapropvar;
PROPVARIANT *ppv = NULL;
pvOut->vt = VT_VECTOR | VT_VARIANT;
pcapropvar = &pvOut->capropvar;
pcapropvar->cElems = cSigners;
if (cSigners > 0)
{
Assert(rgpattrs && pvOut);
// Allocate the array of VT_BLOB propvariants
if (FAILED(hr = HrAlloc((LPVOID *)&ppv, cSigners * sizeof(PROPVARIANT))))
{
goto exit;
}
memset(ppv, 0, cSigners * sizeof(PROPVARIANT));
pcapropvar->pElems = ppv;
// Fill in the array of BLOBs
for (i = 0; i < cSigners; i++)
{
ppv[i].vt = VT_BLOB;
// HrCopyBlob allocates memory for the blob data.
if (rgpattrs[i] == NULL)
continue;
if (pszObjId == NULL)
{
pattrs = rgpattrs[i];
}
else
{
pattrs = NULL;
for (i1=0; i1<rgpattrs[i]->cAttr; i1++)
{
if (lstrcmp(rgpattrs[i]->rgAttr[i1].pszObjId, pszObjId) == NULL)
{
pattrs = &attrs;
attrs.cAttr = 1;
attrs.rgAttr = &rgpattrs[i]->rgAttr[i1];
break;
}
}
if (pattrs == NULL)
continue;
}
if (!CryptEncodeObjectEx(X509_ASN_ENCODING,
szOID_Microsoft_Attribute_Sequence,
pattrs, CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &ppv[i].blob.pBlobData,
&ppv[i].blob.cbSize))
{
hr = HrGetLastError();
goto exit;
}
}
} else {
pcapropvar->pElems = NULL;
}
hr = S_OK;
exit:
if (FAILED(hr) && (ppv != NULL))
{
for (i=0; i<cSigners; i++)
{
MemFree(&ppv[i].blob.pBlobData);
}
MemFree(ppv);
}
return(hr);
}
// --------------------------------------------------------------------------------
// CMessageBody::_FindAttribute
//
// Utility function designed to find attributes in an attribute set
// --------------------------------------------------------------------------------
PCRYPT_ATTRIBUTE CMessageBody::_FindAttribute(PCRYPT_ATTRIBUTES pattrs,
LPCSTR pszObjId, DWORD iInstance)
{
DWORD i;
if (pattrs == NULL)
return NULL;
for (i=0; i<pattrs->cAttr; i++)
{
if (lstrcmp(pattrs->rgAttr[i].pszObjId, pszObjId) == 0)
{
if (iInstance == 0)
{
return &pattrs->rgAttr[i];
}
else
{
iInstance -= 1;
}
}
}
return NULL;
}
// --------------------------------------------------------------------------------
// CMessageBody::_HrSetAttribute
//
// Utility function designed to set attributes. Called from set property as well
// as public interface set attribute function
// --------------------------------------------------------------------------------
HRESULT CMessageBody::_HrSetAttribute(DWORD dwFlags, PCRYPT_ATTRIBUTES * ppattrs,
LPCSTR pszObjId, DWORD cbNew, const BYTE * pbNew)
{
DWORD cAttr =0;
DWORD cb;
HRESULT hr;
DWORD i;
PCRYPT_ATTRIBUTES pattrs = *ppattrs;
PCRYPT_ATTRIBUTES pattrs2 = NULL;
LPBYTE pb;
CRYPT_ATTR_BLOB UNALIGNED *pVal = NULL;
//
// We have a special case of pszObjId == NULL, in this case the entire
// encoded item is passed in
//
if (pszObjId == NULL)
{
hr = HrDecodeObject(pbNew, cbNew, szOID_Microsoft_Attribute_Sequence, 0,
&cb, (LPVOID *) &pattrs2);
if (SUCCEEDED(hr))
{
#ifdef _WIN64
*ppattrs = (PCRYPT_ATTRIBUTES) MyPbAlignPb(*ppattrs);
#endif //_WIN64
MemFree(*ppattrs);
*ppattrs = NULL;
*ppattrs = pattrs2;
pattrs2 = NULL;
}
goto exit;
}
//
// Compute size of buffer we are going to need to hold the result
//
if (pattrs == NULL)
{
cb = sizeof(CRYPT_ATTRIBUTES);
}
else
{
cb = sizeof(CRYPT_ATTRIBUTES);
for (i=0; i<pattrs->cAttr; i++)
{
Assert(pattrs->rgAttr[i].cValue == 1);
//
// If we are going to replace something, then set it's oid to NULL
//
if ((lstrcmp(pattrs->rgAttr[i].pszObjId, pszObjId) == 0) &&
!(dwFlags & SMIME_ATTR_ADD_TO_EXISTING))
{
if (dwFlags & SMIME_ATTR_ADD_IF_NOT_EXISTS)
{
return S_OK;
}
pattrs->rgAttr[i].pszObjId = NULL;
continue;
}
pVal = &(pattrs->rgAttr[i].rgValue[0]);
cb += ((DWORD) (sizeof(CRYPT_ATTRIBUTE) + sizeof(CRYPT_ATTR_BLOB) +
strlen(pattrs->rgAttr[i].pszObjId) + 1 +
pVal->cbData));
#ifdef _WIN64
cb = LcbAlignLcb(cb);
#endif // _WIN64
cAttr += 1;
}
}
//
// Add room for the one we are about to include
//
#ifdef _WIN64
cb = LcbAlignLcb(cb);
#endif // _WIN64
cb += (DWORD)(sizeof(CRYPT_ATTRIBUTE) + sizeof(CRYPT_ATTR_BLOB) +
#ifdef _WIN64
LcbAlignLcb(strlen(pszObjId) + 1) + cbNew);
#else
strlen(pszObjId) + 1 + cbNew);
#endif // _WIN64
cAttr += 1;
//
// Allocate Memory to hold the result
//
pattrs2 = (PCRYPT_ATTRIBUTES) g_pMalloc->Alloc(cb);
if (pattrs2 == NULL)
{
hr = E_OUTOFMEMORY;
goto exit;
}
//
// Now copy over the items appending our item at the end
//
pattrs2->rgAttr = (PCRYPT_ATTRIBUTE) &pattrs2[1];
pb = (LPBYTE) &pattrs2->rgAttr[cAttr];
cAttr = 0;
if (pattrs != NULL)
{
for (i=0; i<pattrs->cAttr; i++)
{
if (pattrs->rgAttr[i].pszObjId == NULL) continue;
pattrs2->rgAttr[cAttr].pszObjId = (LPSTR) pb;
#ifdef _WIN64
cb = LcbAlignLcb(strlen(pattrs->rgAttr[i].pszObjId) + 1);
#else
cb = strlen(pattrs->rgAttr[i].pszObjId) + 1;
#endif // _WIN64
memcpy(pb, pattrs->rgAttr[i].pszObjId, cb);
pb += cb;
pattrs2->rgAttr[cAttr].cValue = 1;
pattrs2->rgAttr[cAttr].rgValue = (PCRYPT_ATTR_BLOB) pb;
#ifdef _WIN64
pb += LcbAlignLcb(sizeof(CRYPT_ATTR_BLOB));
#else
pb += sizeof(CRYPT_ATTR_BLOB);
#endif
pVal = &(pattrs->rgAttr[i].rgValue[0]);
cb = ((DWORD) (pVal->cbData));
#ifdef _WIN64
// cb = LcbAlignLcb(cb);
#endif // _WIN64
pVal = &(pattrs2->rgAttr[cAttr].rgValue[0]);
pVal->pbData = pb;
pVal->cbData = cb;
pVal = &(pattrs->rgAttr[i].rgValue[0]);
memcpy(pb, pVal->pbData, cb);
#ifdef _WIN64
pb += LcbAlignLcb(cb);
#else
pb += cb;
#endif
cAttr += 1;
}
}
//
// Append the new one
//
#ifdef _WIN64
cb = LcbAlignLcb(strlen(pszObjId) + 1);
#else
cb = strlen(pszObjId) + 1;
#endif // _WIN64
pattrs2->rgAttr[cAttr].pszObjId = (LPSTR) pb;
memcpy(pb, pszObjId, cb);
pb += cb;
pattrs2->rgAttr[cAttr].cValue = (DWORD) 1;
pattrs2->rgAttr[cAttr].rgValue = (PCRYPT_ATTR_BLOB) pb;
#ifdef _WIN64
pb += LcbAlignLcb(sizeof(CRYPT_ATTR_BLOB));
#else
pb += sizeof(CRYPT_ATTR_BLOB);
#endif
pVal = &(pattrs2->rgAttr[cAttr].rgValue[0]);
pVal->cbData = (DWORD) cbNew;
pVal->pbData = pb;
memcpy(pb, pbNew, cbNew);
#ifdef _WIN64
pb += LcbAlignLcb(cbNew);
#else
pb += cbNew;
#endif
pattrs2->cAttr = cAttr + 1;
MemFree(*ppattrs);
*ppattrs = NULL;
*ppattrs = pattrs2;
pattrs2 = NULL;
hr = S_OK;
exit:
if (pattrs2 != NULL) MemFree(pattrs2);
return hr;
}
#endif // SMIME_V3