You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1454 lines
42 KiB
1454 lines
42 KiB
// --------------------------------------------------------------------------------
|
|
// Mimeutil.cpp
|
|
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
|
|
// --------------------------------------------------------------------------------
|
|
#include "pch.hxx"
|
|
#include "demand.h"
|
|
#include "ipab.h"
|
|
#include "resource.h"
|
|
#include <mimeole.h>
|
|
#include "mimeutil.h"
|
|
#include "secutil.h"
|
|
#include "strconst.h"
|
|
#include "oleutil.h"
|
|
#include <error.h>
|
|
#include "options.h"
|
|
#include <shlwapi.h>
|
|
#include "fonts.h"
|
|
#include "multlang.h"
|
|
#include "xpcomm.h"
|
|
#include "multiusr.h"
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// Cached Current Default Character Set From Fonts Options Dialog
|
|
// --------------------------------------------------------------------------------
|
|
static HCHARSET g_hDefaultCharset=NULL; // default charset for reading
|
|
HCHARSET g_hDefaultCharsetForMail=NULL; // default charset for sending news
|
|
int g_iLastCharsetSelection=0; // last charset selection from View/Language
|
|
int g_iCurrentCharsetSelection=0; // current charset selection from View/Language
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// local function
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT HrGetCodePageTagName(ULONG uCodePage, BSTR *pbstr);
|
|
HRESULT GetSubTreeAttachCount(LPMIMEMESSAGE pMsg, HBODY hFirst, ULONG *cCount);
|
|
|
|
HRESULT HrSetMessageText(LPMIMEMESSAGE pMsg, LPTSTR pszText)
|
|
{
|
|
TCHAR rgchText[CCHMAX_STRINGRES];
|
|
HBODY hBody;
|
|
IStream *pstm;
|
|
|
|
// Is it a string resource id?
|
|
if (IS_INTRESOURCE(pszText))
|
|
{
|
|
if (0 == AthLoadString(PtrToUlong(pszText), rgchText, sizeof(rgchText)))
|
|
return E_FAIL;
|
|
|
|
pszText = rgchText;
|
|
}
|
|
|
|
if (SUCCEEDED(pMsg->GetBody(IBL_ROOT, 0, &hBody)))
|
|
pMsg->DeleteBody(hBody, 0);
|
|
|
|
if (MimeOleCreateVirtualStream(&pstm)==S_OK)
|
|
{
|
|
pstm->Write(pszText, lstrlen(rgchText)*sizeof(TCHAR), NULL);
|
|
pMsg->SetTextBody(TXT_HTML, IET_BINARY, NULL, pstm, NULL);
|
|
pstm->Release();
|
|
}
|
|
pMsg->Commit(0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT HrGetWabalFromMsg(LPMIMEMESSAGE pMsg, LPWABAL *ppWabal)
|
|
{
|
|
ADDRESSLIST addrList={0};
|
|
HRESULT hr = S_OK;
|
|
LPWABAL lpWabal = NULL;
|
|
LONG lRecipType;
|
|
ULONG i;
|
|
LONG lMapiType;
|
|
LPADDRESSPROPS pSender = NULL;
|
|
LPADDRESSPROPS pFrom = NULL;
|
|
LPWSTR pwszEmail = NULL;
|
|
|
|
if (!pMsg || !ppWabal)
|
|
IF_FAILEXIT(hr = E_INVALIDARG);
|
|
|
|
IF_FAILEXIT(hr = HrCreateWabalObject(&lpWabal));
|
|
|
|
lpWabal->SetAssociatedMessage(pMsg);
|
|
|
|
IF_FAILEXIT(hr=pMsg->GetAddressTypes(IAT_KNOWN, IAP_FRIENDLYW | IAP_EMAIL | IAP_ADRTYPE, &addrList));
|
|
|
|
for (i=0; i<addrList.cAdrs; i++)
|
|
{
|
|
// Raid 40730 - OE shows a message was sent by 2 people if the sender and from field do not match.
|
|
lMapiType = MimeOleRecipToMapi(addrList.prgAdr[i].dwAdrType);
|
|
|
|
// If Originator and IAT_FROM
|
|
if (MAPI_ORIG == lMapiType)
|
|
{
|
|
// If IAT_SENDER, remember this item but don't add it yet
|
|
if (ISFLAGSET(addrList.prgAdr[i].dwAdrType, IAT_SENDER))
|
|
pSender = &addrList.prgAdr[i];
|
|
|
|
// Have we seen the pFrom
|
|
if (ISFLAGSET(addrList.prgAdr[i].dwAdrType, IAT_FROM))
|
|
pFrom = &addrList.prgAdr[i];
|
|
}
|
|
|
|
// Don't add the IAT_SENDER
|
|
if (!ISFLAGSET(addrList.prgAdr[i].dwAdrType, IAT_SENDER))
|
|
{
|
|
pwszEmail = PszToUnicode(CP_ACP, addrList.prgAdr[i].pszEmail);
|
|
if (addrList.prgAdr[i].pszEmail && !pwszEmail)
|
|
IF_FAILEXIT(hr = E_OUTOFMEMORY);
|
|
|
|
// Add an Entry
|
|
IF_FAILEXIT(hr = lpWabal->HrAddEntry(addrList.prgAdr[i].pszFriendlyW, pwszEmail, lMapiType));
|
|
|
|
SafeMemFree(pwszEmail);
|
|
}
|
|
}
|
|
|
|
// If no pFrom, and we have a pSender, at the entry
|
|
if (NULL == pFrom && NULL != pSender)
|
|
{
|
|
pwszEmail = PszToUnicode(CP_ACP, addrList.prgAdr[i].pszEmail);
|
|
if (addrList.prgAdr[i].pszEmail && !pwszEmail)
|
|
IF_FAILEXIT(hr = E_OUTOFMEMORY);
|
|
|
|
// Add an Entry
|
|
IF_FAILEXIT(hr = lpWabal->HrAddEntry(pSender->pszFriendlyW, pwszEmail, MAPI_ORIG));
|
|
}
|
|
|
|
// success
|
|
*ppWabal = lpWabal;
|
|
lpWabal->AddRef();
|
|
|
|
exit:
|
|
ReleaseObj(lpWabal);
|
|
MemFree(pwszEmail);
|
|
g_pMoleAlloc->FreeAddressList(&addrList);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrCheckDisplayNames(LPWABAL lpWabal, CODEPAGEID cpID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ADRINFO wabInfo = {0};
|
|
LPWABAL lpWabalFlat = NULL;
|
|
IMimeBody *pBody = NULL;
|
|
IImnAccount *pAccount = NULL;
|
|
|
|
|
|
if (!lpWabal)
|
|
return E_INVALIDARG;
|
|
|
|
// flatten the distribution lists in the wabal...
|
|
IF_FAILEXIT(hr = HrCreateWabalObject(&lpWabalFlat));
|
|
|
|
IF_FAILEXIT(hr = lpWabal->HrExpandTo(lpWabalFlat));
|
|
|
|
// use the new flat-wabal
|
|
lpWabal = lpWabalFlat;
|
|
|
|
if (lpWabal->FGetFirst(&wabInfo))
|
|
{
|
|
do
|
|
{
|
|
IF_FAILEXIT((hr = HrSafeToEncodeToCP(wabInfo.lpwszDisplay, cpID)));
|
|
if (MIME_S_CHARSET_CONFLICT == hr)
|
|
goto exit;
|
|
}
|
|
while (lpWabal->FGetNext(&wabInfo));
|
|
}
|
|
|
|
exit:
|
|
ReleaseObj(lpWabalFlat);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSetWabalOnMsg(LPMIMEMESSAGE pMsg, LPWABAL lpWabal)
|
|
{
|
|
LPMIMEADDRESSTABLE pAddrTable = NULL;
|
|
LPMIMEADDRESSTABLEW pAddrTableW = NULL;
|
|
ADRINFO wabInfo = {0};
|
|
LPWABAL lpWabalFlat = NULL;
|
|
HADDRESS hAddress = NULL;
|
|
HRESULT hr = S_OK;
|
|
ADDRESSPROPS rAddress;
|
|
const DWORD dwSecurity = DwGetSecurityOfMessage(pMsg);
|
|
const BOOL fEncrypt = BOOL(MST_ENCRYPT_MASK & dwSecurity);
|
|
const BOOL fSigned = BOOL(MST_SIGN_MASK & dwSecurity);
|
|
ULONG cbSymCapsMe = 0;
|
|
LPBYTE pbSymCapsMe = NULL;
|
|
BOOL fFreeSymCapsMe = FALSE;
|
|
LPVOID pvSymCapsCookie = NULL;
|
|
PROPVARIANT var;
|
|
IMimeBody *pBody = NULL;
|
|
IImnAccount *pAccount = NULL;
|
|
|
|
|
|
if (!pMsg || !lpWabal)
|
|
return E_INVALIDARG;
|
|
|
|
IF_FAILEXIT(hr = pMsg->GetAddressTable(&pAddrTable));
|
|
IF_FAILEXIT(hr = pAddrTable->QueryInterface(IID_IMimeAddressTableW, (LPVOID*)&pAddrTableW));
|
|
|
|
pAddrTableW->DeleteTypes(IAT_ALL);
|
|
|
|
// flatten the distribution lists in the wabal...
|
|
IF_FAILEXIT(hr = HrCreateWabalObject(&lpWabalFlat));
|
|
|
|
lpWabal->SetAssociatedMessage(pMsg);
|
|
|
|
IF_FAILEXIT(hr = lpWabal->HrExpandTo(lpWabalFlat));
|
|
|
|
// use the new flat-wabal
|
|
lpWabal = lpWabalFlat;
|
|
|
|
|
|
if (fEncrypt || fSigned)
|
|
{
|
|
//
|
|
// Signed message gets OID_SECURITY_SYMCAPS
|
|
//
|
|
// Encrypted message uses SYMCAPS to prime the pump on the algorithm determination engine.
|
|
//
|
|
// Get the body object
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (void **)&pBody)))
|
|
{
|
|
// Get the default caps blob from the registry
|
|
var.vt = VT_LPSTR;
|
|
IF_FAILEXIT(hr = pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var));
|
|
|
|
hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pAccount);
|
|
SafeMemFree(var.pszVal);
|
|
IF_FAILEXIT(hr);
|
|
|
|
if(SUCCEEDED(hr = pAccount->GetProp(AP_SMTP_ENCRYPT_ALGTH, NULL, &cbSymCapsMe)))
|
|
{
|
|
if (! MemAlloc((LPVOID *)&pbSymCapsMe, cbSymCapsMe))
|
|
{
|
|
cbSymCapsMe = 0;
|
|
}
|
|
else
|
|
{
|
|
if(FAILED(hr = pAccount->GetProp(AP_SMTP_ENCRYPT_ALGTH, pbSymCapsMe, &cbSymCapsMe)))
|
|
{
|
|
Assert(FALSE); // Huh, now it fails?
|
|
SafeMemFree(pbSymCapsMe);
|
|
cbSymCapsMe = 0;
|
|
}
|
|
else
|
|
{
|
|
fFreeSymCapsMe = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
ReleaseObj(pAccount);
|
|
if(FAILED(hr))
|
|
{
|
|
// No Symcap option set for ME. Go get the default value.
|
|
if (SUCCEEDED(HrGetHighestSymcaps(&pbSymCapsMe, &cbSymCapsMe)))
|
|
{
|
|
fFreeSymCapsMe = TRUE;
|
|
}
|
|
}
|
|
|
|
// Set our SYMCAPS property on the message
|
|
if (fSigned && cbSymCapsMe)
|
|
{
|
|
var.vt = VT_BLOB;
|
|
var.blob.cbSize = cbSymCapsMe;
|
|
var.blob.pBlobData = pbSymCapsMe;
|
|
pBody->SetOption(OID_SECURITY_SYMCAPS, &var);
|
|
}
|
|
|
|
if (fEncrypt)
|
|
{
|
|
if (! cbSymCapsMe)
|
|
{
|
|
// Default value for algorithm determination
|
|
pbSymCapsMe = (LPBYTE)c_RC2_40_ALGORITHM_ID;
|
|
cbSymCapsMe = cbRC2_40_ALGORITHM_ID;
|
|
}
|
|
|
|
// Prime the pump for calculating encryption algorithm
|
|
if (FAILED(hr = MimeOleSMimeCapInit(pbSymCapsMe, cbSymCapsMe, &pvSymCapsCookie)))
|
|
{
|
|
DOUTL(DOUTL_CRYPT, "MimeOleSMimeCapInit -> %x", hr);
|
|
}
|
|
}
|
|
if (fFreeSymCapsMe)
|
|
{
|
|
SafeMemFree(pbSymCapsMe);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (lpWabal->FGetFirst(&wabInfo))
|
|
{
|
|
do
|
|
{
|
|
rAddress.dwProps = IAP_ENCRYPTION_PRINT;
|
|
|
|
LONG l;
|
|
|
|
l = MapiRecipToMimeOle(wabInfo.lRecipType);
|
|
|
|
IF_FAILEXIT(hr = pAddrTableW->AppendW(l, IET_DECODED, wabInfo.lpwszDisplay, wabInfo.lpwszAddress, &hAddress));
|
|
|
|
if (fEncrypt)
|
|
{
|
|
// need to treat sender differently since there is
|
|
// no cert in the wab for her
|
|
// it gets taken care of in _HrPrepSecureMsgForSending in secutil
|
|
if (MAPI_ORIG != wabInfo.lRecipType && MAPI_REPLYTO != wabInfo.lRecipType)
|
|
{
|
|
BLOB blSymCaps;
|
|
FILETIME ftSigningTime;
|
|
|
|
blSymCaps.cbSize = 0;
|
|
|
|
// if these fail, big deal. We just don't have a print then.
|
|
// we'll wait for the S/MIME engine to yell, since there may
|
|
// be other things wrong with other certs.
|
|
hr = HrGetThumbprint(lpWabal, &wabInfo, &(rAddress.tbEncryption), &blSymCaps, &ftSigningTime);
|
|
if (SUCCEEDED(hr) && rAddress.tbEncryption.pBlobData)
|
|
{
|
|
pAddrTableW->SetProps(hAddress, &rAddress);
|
|
SafeMemFree(rAddress.tbEncryption.pBlobData);
|
|
|
|
if (pvSymCapsCookie)
|
|
{
|
|
if (blSymCaps.cbSize && blSymCaps.pBlobData)
|
|
{
|
|
// Pass the recipient's SYMCAPS to the algorithm selection engine
|
|
hr = MimeOleSMimeCapAddSMimeCap(
|
|
blSymCaps.pBlobData,
|
|
blSymCaps.cbSize,
|
|
pvSymCapsCookie);
|
|
MemFree(blSymCaps.pBlobData);
|
|
}
|
|
else
|
|
{
|
|
LPBYTE pbCert = NULL;
|
|
ULONG cbCert = 0;
|
|
|
|
// Need to get the cert context for this cert
|
|
// BUGBUG: MimeOleSMimeCapAddCert currently doesn't even look
|
|
// at the cert. Why should we bother getting it from the thumbprint?
|
|
// It only looks at fParanoid
|
|
|
|
hr = MimeOleSMimeCapAddCert(pbCert,
|
|
cbCert,
|
|
FALSE, // fParanoid,
|
|
pvSymCapsCookie);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (lpWabal->FGetNext(&wabInfo));
|
|
}
|
|
|
|
if (fEncrypt)
|
|
{
|
|
LPBYTE pbEncode = NULL;
|
|
ULONG cbEncode = 0;
|
|
BOOL fFreeEncode = FALSE;
|
|
DWORD dwBits = 0;
|
|
|
|
if (pvSymCapsCookie)
|
|
{
|
|
// Finish up with SymCaps and save the ALG_BULK
|
|
MimeOleSMimeCapGetEncAlg(pvSymCapsCookie,
|
|
pbEncode,
|
|
&cbEncode,
|
|
&dwBits);
|
|
|
|
if (cbEncode)
|
|
{
|
|
if (! MemAlloc((LPVOID *)&pbEncode, cbEncode))
|
|
{
|
|
cbEncode = 0;
|
|
}
|
|
else
|
|
{
|
|
fFreeEncode = TRUE;
|
|
if (SUCCEEDED(hr = MimeOleSMimeCapGetEncAlg(
|
|
pvSymCapsCookie,
|
|
pbEncode,
|
|
&cbEncode,
|
|
&dwBits)))
|
|
{
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hey, there should ALWAYS be at least RC2 (40 bit). What happened?
|
|
AssertSz(cbEncode, "MimeOleSMimeCapGetEncAlg gave us no encoding algorithm");
|
|
}
|
|
}
|
|
if (! pbEncode)
|
|
{
|
|
// Stick in the RC2 value as a default
|
|
pbEncode = (LPBYTE)c_RC2_40_ALGORITHM_ID;
|
|
cbEncode = cbRC2_40_ALGORITHM_ID;
|
|
}
|
|
|
|
// Ah, finally, we have calculated the algorithm.
|
|
// Set it on the message body
|
|
var.vt = VT_BLOB;
|
|
var.blob.cbSize = cbEncode;
|
|
var.blob.pBlobData = pbEncode;
|
|
hr = pBody->SetOption(OID_SECURITY_ALG_BULK, &var);
|
|
|
|
if (fFreeEncode)
|
|
{
|
|
SafeMemFree(pbEncode);
|
|
}
|
|
}
|
|
|
|
// No more commits needed in the address table hr=pAddrTableW->Commit();
|
|
|
|
exit:
|
|
MemFree(pvSymCapsCookie);
|
|
ReleaseObj(pBody);
|
|
ReleaseObj(pAddrTable);
|
|
ReleaseObj(pAddrTableW);
|
|
ReleaseObj(lpWabalFlat);
|
|
|
|
return hr;
|
|
}
|
|
|
|
#if 0
|
|
HRESULT HrSetReplyTo(LPMIMEMESSAGE pMsg, LPSTR lpszEmail)
|
|
{
|
|
LPMIMEADDRESSTABLE pAddrTable=0;
|
|
HRESULT hr;
|
|
|
|
if (!pMsg)
|
|
return E_INVALIDARG;
|
|
|
|
hr=pMsg->GetAddressTable(&pAddrTable);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr=pAddrTable->Append(IAT_REPLYTO, IET_DECODED, NULL, lpszEmail, NULL);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
error:
|
|
ReleaseObj(pAddrTable);
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
LONG MimeOleRecipToMapi(IADDRESSTYPE addrtype)
|
|
{
|
|
LONG lRecipType = MAPI_ORIG;
|
|
|
|
AssertSz(addrtype & IAT_KNOWN, "Must be a known type for this to work!!");
|
|
|
|
switch (addrtype)
|
|
{
|
|
case IAT_FROM:
|
|
case IAT_SENDER:
|
|
lRecipType=MAPI_ORIG;
|
|
break;
|
|
case IAT_TO:
|
|
lRecipType=MAPI_TO;
|
|
break;
|
|
case IAT_CC:
|
|
lRecipType=MAPI_CC;
|
|
break;
|
|
case IAT_BCC:
|
|
lRecipType=MAPI_BCC;
|
|
break;
|
|
case IAT_REPLYTO:
|
|
lRecipType=MAPI_REPLYTO;
|
|
break;
|
|
default:
|
|
Assert(FALSE);
|
|
}
|
|
return lRecipType;
|
|
}
|
|
|
|
|
|
IADDRESSTYPE MapiRecipToMimeOle(LONG lRecip)
|
|
{
|
|
IADDRESSTYPE addrtype = IAT_UNKNOWN;
|
|
|
|
switch (lRecip)
|
|
{
|
|
case MAPI_ORIG:
|
|
addrtype=IAT_FROM;
|
|
break;
|
|
case MAPI_TO:
|
|
addrtype=IAT_TO;
|
|
break;
|
|
case MAPI_CC:
|
|
addrtype=IAT_CC;
|
|
break;
|
|
case MAPI_BCC:
|
|
addrtype=IAT_BCC;
|
|
break;
|
|
case MAPI_REPLYTO:
|
|
addrtype=IAT_REPLYTO;
|
|
break;
|
|
default:
|
|
Assert(FALSE);
|
|
}
|
|
return addrtype;
|
|
}
|
|
|
|
|
|
// CANDIDATES.
|
|
// This function is never called to dup a message that has all the security
|
|
// fully encoded into the message. Because of that, we always need to clear
|
|
// the security flags and then reset them into the
|
|
HRESULT HrDupeMsg(LPMIMEMESSAGE pMsg, LPMIMEMESSAGE *ppMsg)
|
|
{
|
|
LPMIMEMESSAGE pMsgDupe=0;
|
|
IMimePropertySet *pPropsSrc,
|
|
*pPropsDest;
|
|
LPSTREAM pstm=0;
|
|
HRESULT hr;
|
|
HCHARSET hCharset ;
|
|
DWORD dwSecFlags = MST_NONE;
|
|
SECSTATE secState = {0};
|
|
LPCSTR rgszHdrCopy[] = {
|
|
PIDTOSTR(PID_ATT_ACCOUNTID),
|
|
STR_ATT_ACCOUNTNAME,
|
|
PIDTOSTR(PID_ATT_STOREMSGID),
|
|
PIDTOSTR(PID_ATT_STOREFOLDERID) };
|
|
|
|
|
|
if (!ppMsg || !pMsg)
|
|
return E_INVALIDARG;
|
|
|
|
*ppMsg=0;
|
|
|
|
hr=HrCreateMessage(&pMsgDupe);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
HrGetSecurityState(pMsg, &secState, NULL);
|
|
if (IsSecure(secState.type))
|
|
{
|
|
dwSecFlags = MST_CLASS_SMIME_V1;
|
|
if (IsSigned(secState.type))
|
|
dwSecFlags |= ((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
|
|
if (IsEncrypted(secState.type))
|
|
dwSecFlags |= MST_THIS_ENCRYPT;
|
|
|
|
hr = HrInitSecurityOptions(pMsg, 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
hr = pMsg->GetMessageSource(&pstm, 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
pMsg->GetCharset(&hCharset);
|
|
|
|
hr= pMsgDupe->Load(pstm);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (hCharset) // for uuencode msg, we need to do this to carry over the charset
|
|
pMsgDupe->SetCharset(hCharset, CSET_APPLY_ALL);
|
|
|
|
if (pMsgDupe->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pPropsDest)==S_OK)
|
|
{
|
|
if (pMsg->BindToObject(HBODY_ROOT, IID_IMimePropertySet, (LPVOID *)&pPropsSrc)==S_OK)
|
|
{
|
|
pPropsSrc->CopyProps(ARRAYSIZE(rgszHdrCopy), rgszHdrCopy, pPropsDest);
|
|
pPropsSrc->Release();
|
|
}
|
|
pPropsDest->Release();
|
|
}
|
|
|
|
if (MST_NONE != dwSecFlags)
|
|
{
|
|
hr = HrInitSecurityOptions(pMsg, dwSecFlags);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = HrInitSecurityOptions(pMsgDupe, dwSecFlags);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
*ppMsg = pMsgDupe;
|
|
pMsgDupe->AddRef();
|
|
|
|
error:
|
|
ReleaseObj(pMsgDupe);
|
|
CleanupSECSTATE(&secState);
|
|
ReleaseObj(pstm);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT HrRemoveAttachments(LPMIMEMESSAGE pMsg, BOOL fKeepRelatedSection)
|
|
{
|
|
HRESULT hr;
|
|
ULONG cAttach,
|
|
uAttach;
|
|
LPHBODY rghAttach=0;
|
|
HBODY hBody;
|
|
|
|
if(!pMsg)
|
|
return E_INVALIDARG;
|
|
|
|
hr = pMsg->GetAttachments(&cAttach, &rghAttach);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
for(uAttach=0; uAttach<cAttach; uAttach++)
|
|
{
|
|
if (fKeepRelatedSection &&
|
|
HrIsInRelatedSection(pMsg, rghAttach[uAttach])==S_OK)
|
|
continue; // skip related content
|
|
|
|
hr = pMsg->DeleteBody(rghAttach[uAttach], 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
//N BUGBUG
|
|
/*this is to keep the tree (which may now be a
|
|
multipart with single child) in sync. We should
|
|
look at DeleteBody performing this. Additionally,
|
|
we could have the children whose parents are deleted
|
|
inherit their parents' inheritable properties*/
|
|
pMsg->Commit(0);
|
|
|
|
error:
|
|
SafeMimeOleFree(rghAttach);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrCreateMessage(IMimeMessage **ppMsg)
|
|
{
|
|
return MimeOleCreateMessage(NULL, ppMsg);
|
|
}
|
|
|
|
|
|
|
|
HRESULT GetAttachmentCount(LPMIMEMESSAGE pMsg, ULONG *pcCount)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
ULONG cCount = 0;
|
|
|
|
if (pMsg && pcCount)
|
|
{
|
|
HBODY hRootBody;
|
|
|
|
// WHY? because GetAttachments calculated from renderred body parts. If we call
|
|
// on a fresh stream it returns 2 - for the multi/alternate plain/html section
|
|
// GetTextBody will mark these body parts as inlinable and they won't show as
|
|
// 'attachments'
|
|
pMsg->GetTextBody(TXT_HTML, IET_UNICODE, NULL, &hRootBody);
|
|
pMsg->GetTextBody(TXT_PLAIN, IET_UNICODE, NULL, &hRootBody);
|
|
|
|
hr = pMsg->GetBody(IBL_ROOT, NULL, &hRootBody);
|
|
if (!FAILED(hr))
|
|
{
|
|
if(S_OK != pMsg->IsContentType(hRootBody, STR_CNT_MULTIPART, STR_SUB_RELATED))
|
|
hr = GetSubTreeAttachCount(pMsg, hRootBody, &cCount);
|
|
}
|
|
}
|
|
|
|
*pcCount = cCount;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT GetSubTreeAttachCount(LPMIMEMESSAGE pMsg, HBODY hFirst, ULONG *pcCount)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HBODY hIter = hFirst;
|
|
ULONG cCount = 0;
|
|
|
|
do
|
|
{
|
|
// Is multipart?
|
|
if(S_OK == pMsg->IsContentType(hIter, STR_CNT_MULTIPART, NULL))
|
|
{
|
|
// Only count the sub tree if is not multipart/related,
|
|
if (S_OK != pMsg->IsContentType(hIter, NULL, STR_SUB_RELATED))
|
|
{
|
|
ULONG cLocalCount;
|
|
HBODY hMultiPart;
|
|
hr = pMsg->GetBody(IBL_FIRST, hIter, &hMultiPart);
|
|
|
|
// If GetBody fails, just ignore this sub tree
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = GetSubTreeAttachCount(pMsg, hMultiPart, &cLocalCount);
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
cCount += cLocalCount;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PROPVARIANT rVariant;
|
|
|
|
rVariant.vt = VT_I4;
|
|
|
|
// See RAID-56665: Should ignore this type
|
|
if (S_OK != pMsg->IsContentType(hIter, STR_CNT_APPLICATION, STR_SUB_MSTNEF))
|
|
{
|
|
if (FAILED(pMsg->GetBodyProp(hIter, PIDTOSTR(PID_ATT_RENDERED), NOFLAGS, &rVariant)) || rVariant.ulVal==FALSE)
|
|
cCount++;
|
|
}
|
|
}
|
|
} while (S_OK == pMsg->GetBody(IBL_NEXT, hIter, &hIter));
|
|
|
|
Error:
|
|
*pcCount = cCount;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrSaveMsgToFile(LPMIMEMESSAGE pMsg, LPSTR lpszFile)
|
|
{
|
|
return HrIPersistFileSave((LPUNKNOWN)pMsg, lpszFile);
|
|
}
|
|
|
|
|
|
HRESULT HrSetServer(LPMIMEMESSAGE pMsg, LPSTR lpszServer)
|
|
{
|
|
PROPVARIANT rUserData;
|
|
|
|
if (!lpszServer)
|
|
return S_OK;
|
|
|
|
rUserData.vt = VT_LPSTR;
|
|
rUserData.pszVal = lpszServer;
|
|
return pMsg->SetProp(PIDTOSTR(PID_ATT_SERVER), 0, &rUserData);
|
|
}
|
|
|
|
HRESULT HrSetAccount(LPMIMEMESSAGE pMsg, LPSTR pszAcctName)
|
|
{
|
|
IImnAccount *pAccount;
|
|
PROPVARIANT rUserData;
|
|
|
|
if (!pszAcctName)
|
|
return S_OK;
|
|
|
|
if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_NAME, pszAcctName, &pAccount)))
|
|
{
|
|
CHAR szId[CCHMAX_ACCOUNT_NAME];
|
|
|
|
rUserData.vt = VT_LPSTR;
|
|
rUserData.pszVal = (LPSTR)pszAcctName;
|
|
pMsg->SetProp(STR_ATT_ACCOUNTNAME, 0, &rUserData);
|
|
|
|
if (SUCCEEDED(pAccount->GetPropSz(AP_ACCOUNT_ID, szId, sizeof(szId))))
|
|
{
|
|
rUserData.pszVal = szId;
|
|
pMsg->SetProp(PIDTOSTR(PID_ATT_ACCOUNTID), 0, &rUserData);
|
|
}
|
|
|
|
pAccount->Release();
|
|
}
|
|
else
|
|
return(E_FAIL);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT HrSetAccountByAccount(LPMIMEMESSAGE pMsg, IImnAccount *pAcct)
|
|
{
|
|
TCHAR szAccount[CCHMAX_ACCOUNT_NAME];
|
|
TCHAR szId[CCHMAX_ACCOUNT_NAME];
|
|
PROPVARIANT rUserData;
|
|
|
|
if (!pAcct)
|
|
return S_OK;
|
|
|
|
rUserData.vt = VT_LPSTR;
|
|
|
|
// Having the name in the msg is good, but not necessary
|
|
if (SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_NAME, szAccount, sizeof(szAccount))))
|
|
{
|
|
rUserData.pszVal = (LPSTR)szAccount;
|
|
pMsg->SetProp(STR_ATT_ACCOUNTNAME, 0, &rUserData);
|
|
}
|
|
|
|
// Must have the account ID in the msg
|
|
if (SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_ID, szId, sizeof(szId))))
|
|
{
|
|
rUserData.pszVal = szId;
|
|
pMsg->SetProp(PIDTOSTR(PID_ATT_ACCOUNTID), 0, &rUserData);
|
|
}
|
|
else
|
|
return(E_FAIL);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT HrLoadMsgFromFile(LPMIMEMESSAGE pMsg, LPSTR lpszFile)
|
|
{
|
|
return HrIPersistFileLoad((LPUNKNOWN)pMsg, lpszFile);
|
|
}
|
|
|
|
HRESULT HrLoadMsgFromFileW(LPMIMEMESSAGE pMsg, LPWSTR lpwszFile)
|
|
{
|
|
return HrIPersistFileLoadW((LPUNKNOWN)pMsg, lpwszFile);
|
|
}
|
|
|
|
|
|
#define CCH_COUNTBUFFER 4096
|
|
HRESULT HrComputeLineCount(LPMIMEMESSAGE pMsg, LPDWORD pdw)
|
|
{
|
|
HRESULT hr;
|
|
BODYOFFSETS rOffset;
|
|
LPSTREAM pstm=0;
|
|
TCHAR rgch[CCH_COUNTBUFFER+1];
|
|
ULONG cb,
|
|
i,
|
|
cLines=0;
|
|
|
|
if (!pdw)
|
|
return E_INVALIDARG;
|
|
|
|
*pdw=0;
|
|
|
|
hr = pMsg->GetMessageSource(&pstm, COMMIT_ONLYIFDIRTY);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr=pMsg->GetBodyOffsets(HBODY_ROOT, &rOffset);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr=HrStreamSeekSet(pstm, rOffset.cbBodyStart);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
while (pstm->Read(rgch, CCH_COUNTBUFFER, &cb)==S_OK && cb)
|
|
{
|
|
if (cLines==0) // if there's text, then there's atleast one line.
|
|
cLines++;
|
|
|
|
for (i=0; i<cb; i++)
|
|
{
|
|
if (rgch[i]=='\n')
|
|
cLines++;
|
|
}
|
|
}
|
|
|
|
error:
|
|
ReleaseObj(pstm);
|
|
*pdw=cLines;
|
|
return hr;
|
|
}
|
|
|
|
#if 0
|
|
// =====================================================================================
|
|
// HrEscapeQuotedString - quotes '"' and '\'
|
|
// =====================================================================================
|
|
HRESULT HrEscapeQuotedString (LPTSTR pszIn, LPTSTR *ppszOut)
|
|
{
|
|
LPTSTR pszOut;
|
|
TCHAR ch;
|
|
|
|
// worst case - escape every character, so use double original strlen
|
|
if (!MemAlloc((LPVOID*)ppszOut, (2 * lstrlen(pszIn) + 1) * sizeof(TCHAR)))
|
|
return E_OUTOFMEMORY;
|
|
pszOut = *ppszOut;
|
|
|
|
while (ch = *pszIn++)
|
|
{
|
|
if (ch == _T('"') || ch == _T('\\'))
|
|
*pszOut++ = _T('\\');
|
|
*pszOut++ = ch;
|
|
}
|
|
*pszOut = _T('\0');
|
|
return NOERROR;
|
|
}
|
|
#endif
|
|
|
|
|
|
HRESULT HrHasBodyParts(LPMIMEMESSAGE pMsg)
|
|
{
|
|
DWORD dwFlags=0;
|
|
|
|
if (pMsg)
|
|
pMsg->GetFlags(&dwFlags);
|
|
|
|
return (dwFlags&IMF_HTML || dwFlags & IMF_PLAIN)? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT HrHasEncodedBodyParts(LPMIMEMESSAGE pMsg, ULONG cBody, LPHBODY rghBody)
|
|
{
|
|
ULONG uBody;
|
|
|
|
if (cBody==0 || rghBody==NULL)
|
|
return S_FALSE;
|
|
|
|
for (uBody=0; uBody<cBody; uBody++)
|
|
{
|
|
if (HrIsBodyEncoded(pMsg, rghBody[uBody])==S_OK)
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* looks for non-7bit or non-8bit encoding
|
|
*/
|
|
HRESULT HrIsBodyEncoded(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
LPSTR lpsz;
|
|
HRESULT hr=S_FALSE;
|
|
|
|
if (!FAILED(MimeOleGetBodyPropA(pMsg, hBody, PIDTOSTR(PID_HDR_CNTXFER), NOFLAGS, &lpsz)))
|
|
{
|
|
if (lstrcmpi(lpsz, STR_ENC_7BIT)!=0 && lstrcmpi(lpsz, STR_ENC_8BIT)!=0)
|
|
hr=S_OK;
|
|
|
|
SafeMimeOleFree(lpsz);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// sizeof(lspzBuffer) needs to be == or > CCHMAX_CSET_NAME
|
|
HRESULT HrGetMetaTagName(HCHARSET hCharset, LPSTR pszBuffer, DWORD cchSize)
|
|
{
|
|
INETCSETINFO rCsetInfo;
|
|
CODEPAGEINFO rCodePage;
|
|
HRESULT hr;
|
|
LPSTR psz;
|
|
|
|
if (hCharset == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
hr = MimeOleGetCharsetInfo(hCharset, &rCsetInfo);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = MimeOleGetCodePageInfo(rCsetInfo.cpiInternet, &rCodePage);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
psz = rCodePage.szWebCset;
|
|
|
|
if (FIsEmpty(psz)) // if no webset, try the body cset
|
|
psz = rCodePage.szBodyCset;
|
|
|
|
if (FIsEmpty(psz))
|
|
{
|
|
hr = E_FAIL;
|
|
goto error;
|
|
}
|
|
|
|
StrCpyN(pszBuffer, psz, cchSize);
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// SetDefaultCharset
|
|
// --------------------------------------------------------------------------------
|
|
void SetDefaultCharset(HCHARSET hCharset)
|
|
{
|
|
g_hDefaultCharset = hCharset;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// HGetCharsetFromCodepage
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT HGetCharsetFromCodepage(CODEPAGEID cp, HCHARSET *phCharset)
|
|
{
|
|
CODEPAGEINFO rCodePage;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(!phCharset)
|
|
return E_INVALIDARG;
|
|
|
|
// Ask MimeOle for the CodePage Information
|
|
IF_FAILEXIT(hr = MimeOleGetCodePageInfo(cp, &rCodePage));
|
|
|
|
// Better Have a WebCharset
|
|
if (!(ILM_WEBCSET & rCodePage.dwMask))
|
|
{
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
// Find the body charset
|
|
hr = MimeOleFindCharset(rCodePage.szWebCset, phCharset);
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// HGetDefaultCharset
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT HGetDefaultCharset(HCHARSET *phCharset)
|
|
{
|
|
DWORD cb;
|
|
CODEPAGEID cpiWindows,
|
|
cpiInternet;
|
|
CHAR szCodePage[MAX_PATH];
|
|
HCHARSET hDefaultCharset = NULL; // default charset for reading
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// No Null...
|
|
if (g_hDefaultCharset)
|
|
{
|
|
if(phCharset)
|
|
*phCharset = g_hDefaultCharset;
|
|
return S_OK;
|
|
}
|
|
|
|
// Open Trident\International
|
|
cb = sizeof(cpiWindows);
|
|
if (ERROR_SUCCESS != SHGetValue(MU_GetCurrentUserHKey(), c_szRegInternational, c_szDefaultCodePage, NULL, (LPBYTE)&cpiWindows, &cb))
|
|
cpiWindows = GetACP();
|
|
|
|
// Open the CodePage Key
|
|
wnsprintf(szCodePage, ARRAYSIZE(szCodePage), "%s\\%d", c_szRegInternational, cpiWindows);
|
|
cb = sizeof(cpiInternet);
|
|
if (ERROR_SUCCESS != SHGetValue(MU_GetCurrentUserHKey(), szCodePage, c_szDefaultEncoding, NULL, (LPBYTE)&cpiInternet, &cb))
|
|
cpiInternet = GetICP(cpiWindows);
|
|
|
|
// If you can't get the charset, this could be because of user
|
|
// roaming or cset uninstall, retry with the default codepage
|
|
if(FAILED(HGetCharsetFromCodepage(cpiInternet, &hDefaultCharset)) && (cpiInternet != GetACP()))
|
|
{
|
|
cpiInternet = GetACP();
|
|
IF_FAILEXIT(hr = HGetCharsetFromCodepage(cpiInternet, &hDefaultCharset));
|
|
}
|
|
|
|
// for JP codepage 50221 and 50222, we have to check whether it is
|
|
// consistent with registry. If not, we need to override it.
|
|
// 50221 and 50222 both have same user friendly name "JIS-allow 1 byte Kana".
|
|
// but in registry, it defines which one should be used.
|
|
if (cpiInternet == 50222 || cpiInternet == 50221 )
|
|
hDefaultCharset = GetJP_ISOControlCharset();
|
|
|
|
// Set the default charset
|
|
g_hDefaultCharset = hDefaultCharset;
|
|
|
|
// Tell MimeOle About the New Default Charset...
|
|
IF_FAILEXIT(hr = MimeOleSetDefaultCharset(hDefaultCharset));
|
|
|
|
if(phCharset)
|
|
*phCharset = hDefaultCharset;
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrIsInRelatedSection(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
HBODY hBodyParent;
|
|
|
|
if (!FAILED(pMsg->GetBody(IBL_PARENT, hBody, &hBodyParent)) &&
|
|
(pMsg->IsContentType(hBodyParent, STR_CNT_MULTIPART, STR_SUB_RELATED)==S_OK))
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
#if 0
|
|
HRESULT HrMarkGhosted(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
PROPVARIANT pv;
|
|
|
|
Assert (pMsg && hBody);
|
|
|
|
pv.vt = VT_I4;
|
|
pv.lVal = TRUE;
|
|
return pMsg->SetBodyProp(hBody, PIDTOSTR(PID_ATT_GHOSTED), NOFLAGS, &pv);
|
|
}
|
|
|
|
|
|
HRESULT HrIsReferencedUrl(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
PROPVARIANT rVariant;
|
|
|
|
rVariant.vt = VT_I4;
|
|
|
|
if (!FAILED(pMsg->GetBodyProp(hBody, PIDTOSTR(PID_ATT_RENDERED), NOFLAGS, &rVariant)) && rVariant.ulVal)
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT HrIsGhosted(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
PROPVARIANT pv;
|
|
|
|
pv.vt = VT_I4;
|
|
|
|
|
|
|
|
if (pMsg->GetBodyProp(hBody, PIDTOSTR(PID_ATT_GHOSTED), NOFLAGS, &pv)==S_OK &&
|
|
pv.vt == VT_I4 && pv.lVal == TRUE)
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT HrGhostKids(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (pMsg && hBody)
|
|
{
|
|
if (!FAILED(pMsg->GetBody(IBL_FIRST, hBody, &hBody)))
|
|
{
|
|
do
|
|
{
|
|
if (HrIsReferencedUrl(pMsg, hBody)==S_OK)
|
|
{
|
|
hr = HrMarkGhosted(pMsg, hBody);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
}
|
|
while (!FAILED(pMsg->GetBody(IBL_NEXT, hBody, &hBody)));
|
|
}
|
|
}
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrDeleteGhostedKids(LPMIMEMESSAGE pMsg, HBODY hBody)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
ULONG cKids=0,
|
|
uKid;
|
|
LPHBODY rghKids=0;
|
|
|
|
pMsg->CountBodies(hBody, FALSE, &cKids);
|
|
if (cKids)
|
|
{
|
|
if (!MemAlloc((LPVOID *)&rghKids, sizeof(HBODY) * cKids))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
cKids = 0;
|
|
|
|
if (!FAILED(pMsg->GetBody(IBL_FIRST, hBody, &hBody)))
|
|
{
|
|
do
|
|
{
|
|
if (HrIsGhosted(pMsg, hBody)==S_OK)
|
|
{
|
|
rghKids[cKids++] = hBody;
|
|
}
|
|
}
|
|
while (!FAILED(pMsg->GetBody(IBL_NEXT, hBody, &hBody)));
|
|
}
|
|
|
|
for (uKid = 0; uKid < cKids; uKid++)
|
|
{
|
|
hr=pMsg->DeleteBody(rghKids[uKid], 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
}
|
|
|
|
error:
|
|
SafeMemFree(rghKids);
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// HrSetSentTimeProp
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT HrSetSentTimeProp(IMimeMessage *pMessage, LPSYSTEMTIME pst)
|
|
{
|
|
// Locals
|
|
PROPVARIANT rVariant;
|
|
SYSTEMTIME st;
|
|
|
|
// No time was passed in
|
|
if (NULL == pst)
|
|
{
|
|
GetSystemTime(&st);
|
|
pst = &st;
|
|
}
|
|
|
|
// Setup the Variant
|
|
rVariant.vt = VT_FILETIME;
|
|
SystemTimeToFileTime(&st, &rVariant.filetime);
|
|
|
|
// Set the property and return
|
|
return TrapError(pMessage->SetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &rVariant));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// HrSetMailOptionsOnMessage
|
|
// --------------------------------------------------------------------------------
|
|
HRESULT HrSetMailOptionsOnMessage(IMimeMessage *pMessage, HTMLOPT *pHtmlOpt, PLAINOPT *pPlainOpt,
|
|
HCHARSET hCharset, BOOL fHTML)
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
PROPVARIANT rVariant;
|
|
ENCODINGTYPE ietEncoding;
|
|
ULONG uSaveFmt,
|
|
uWrap;
|
|
BOOL f8Bit,
|
|
fWrap=FALSE;
|
|
|
|
// Invalid Arg
|
|
Assert(pMessage && pHtmlOpt && pPlainOpt);
|
|
|
|
// Html Mail
|
|
if (fHTML)
|
|
{
|
|
uSaveFmt = (ULONG)SAVE_RFC1521; // always MIME
|
|
ietEncoding = (IsSecure(pMessage) ? IET_QP : pHtmlOpt->ietEncoding);
|
|
// ietEncoding = pHtmlOpt->ietEncoding;
|
|
f8Bit = pHtmlOpt->f8Bit;
|
|
uWrap = pHtmlOpt->uWrap;
|
|
fWrap = (IET_7BIT == pHtmlOpt->ietEncoding && uWrap > 0) ? TRUE : FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Bug 44270: UUEncode on secure message doesn't make sense. If they asked for secure, they get Mime.
|
|
uSaveFmt = (ULONG)((pPlainOpt->fMime || IsSecure(pMessage)) ? SAVE_RFC1521 : SAVE_RFC822);
|
|
ietEncoding = pPlainOpt->ietEncoding;
|
|
f8Bit = pPlainOpt->f8Bit;
|
|
uWrap = pPlainOpt->uWrap;
|
|
fWrap = (IET_7BIT == pPlainOpt->ietEncoding && uWrap > 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
// Save Format
|
|
rVariant.vt = VT_UI4;
|
|
rVariant.ulVal = uSaveFmt;
|
|
CHECKHR(hr = pMessage->SetOption(OID_SAVE_FORMAT, &rVariant));
|
|
|
|
// Text body encoding
|
|
rVariant.ulVal = (ULONG)ietEncoding;
|
|
CHECKHR(hr = pMessage->SetOption(OID_TRANSMIT_TEXT_ENCODING, &rVariant));
|
|
|
|
// Plain Text body encoding
|
|
rVariant.ulVal = (ULONG)ietEncoding;
|
|
CHECKHR(hr = pMessage->SetOption(OID_XMIT_PLAIN_TEXT_ENCODING, &rVariant));
|
|
|
|
// HTML Text body encoding
|
|
rVariant.ulVal = (ULONG)((IET_7BIT == ietEncoding) ? IET_QP : ietEncoding);
|
|
CHECKHR(hr = pMessage->SetOption(OID_XMIT_HTML_TEXT_ENCODING, &rVariant));
|
|
|
|
// Wrapping Length
|
|
if (uWrap)
|
|
{
|
|
rVariant.ulVal = (ULONG)uWrap;
|
|
CHECKHR(hr = pMessage->SetOption(OID_CBMAX_BODY_LINE, &rVariant));
|
|
}
|
|
|
|
// Allow 8bit Header
|
|
rVariant.vt = VT_BOOL;
|
|
rVariant.boolVal = (VARIANT_BOOL)!!f8Bit;
|
|
CHECKHR(hr = pMessage->SetOption(OID_ALLOW_8BIT_HEADER, &rVariant));
|
|
|
|
// Wrapping
|
|
rVariant.boolVal = (VARIANT_BOOL)!!fWrap;
|
|
CHECKHR(hr = pMessage->SetOption(OID_WRAP_BODY_TEXT, &rVariant));
|
|
|
|
// set the character set also based on what's set in the fontUI.
|
|
if (hCharset)
|
|
CHECKHR(hr = pMessage->SetCharset(hCharset, CSET_APPLY_ALL));
|
|
|
|
exit:
|
|
// Done
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrSetMsgCodePage(LPMIMEMESSAGE pMsg, UINT uCodePage)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
HCHARSET hCharset;
|
|
|
|
if (pMsg == NULL || uCodePage == 0)
|
|
return E_INVALIDARG;
|
|
|
|
// use the WEB charset then the BODY charset, then default charset.
|
|
// EXCEPT for codepage 932 (shift-jis) where bug #61416 requires we ignore the webcarset
|
|
|
|
if (uCodePage != 932)
|
|
hr = MimeOleGetCodePageCharset(uCodePage, CHARSET_WEB, &hCharset);
|
|
|
|
if (FAILED(hr))
|
|
hr = MimeOleGetCodePageCharset(uCodePage, CHARSET_BODY, &hCharset);
|
|
|
|
if (!FAILED(hr))
|
|
hr = pMsg->SetCharset(hCharset, CSET_APPLY_ALL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
UINT uCodePageFromCharset(HCHARSET hCharset)
|
|
{
|
|
INETCSETINFO CsetInfo;
|
|
UINT uiCodePage = GetACP();
|
|
|
|
if (hCharset &&
|
|
(MimeOleGetCharsetInfo(hCharset, &CsetInfo)==S_OK))
|
|
uiCodePage = CsetInfo.cpiInternet ;
|
|
|
|
return uiCodePage;
|
|
}
|
|
|
|
UINT uCodePageFromMsg(LPMIMEMESSAGE pMsg)
|
|
{
|
|
HCHARSET hCharset=0;
|
|
|
|
if (pMsg)
|
|
pMsg->GetCharset(&hCharset);
|
|
return uCodePageFromCharset(hCharset);
|
|
}
|
|
|
|
HRESULT HrSafeToEncodeToCP(LPWSTR pwsz, CODEPAGEID cpID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
INT cbIn = (lstrlenW(pwsz)+1)*sizeof(WCHAR);
|
|
DWORD dwTemp = 0;
|
|
|
|
IF_FAILEXIT(hr = ConvertINetString(&dwTemp, CP_UNICODE, cpID, (LPCSTR)pwsz, &cbIn, NULL, NULL));
|
|
if (S_FALSE == hr)
|
|
hr = MIME_S_CHARSET_CONFLICT;
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrSafeToEncodeToCPA(LPCSTR psz, CODEPAGEID cpSrc, CODEPAGEID cpDest)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pwsz = NULL;
|
|
|
|
Assert(psz);
|
|
|
|
IF_NULLEXIT(pwsz = PszToUnicode(cpSrc, psz));
|
|
IF_FAILEXIT(hr = HrSafeToEncodeToCP(pwsz, cpDest));
|
|
|
|
exit:
|
|
MemFree(pwsz);
|
|
return hr;
|
|
}
|
|
|
|
|
|
#if 0
|
|
HRESULT HrIStreamWToInetCset(LPSTREAM pstmW, HCHARSET hCharset, LPSTREAM *ppstmOut)
|
|
{
|
|
IMimeBody *pBody;
|
|
HRESULT hr;
|
|
|
|
hr = MimeOleCreateBody(&pBody);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pBody->InitNew();
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pBody->SetData(IET_UNICODE, STR_CNT_TEXT, STR_SUB_HTML, IID_IStream, pstmW);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pBody->SetCharset(hCharset, CSET_APPLY_ALL);
|
|
if (!FAILED(hr))
|
|
hr = pBody->GetData(IET_INETCSET, ppstmOut);
|
|
}
|
|
}
|
|
pBody->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
HRESULT HrCopyHeader(LPMIMEMESSAGE pMsg, HBODY hBodyDest, HBODY hBodySrc, LPCSTR pszName)
|
|
{
|
|
LPSTR lpszProp;
|
|
HRESULT hr;
|
|
|
|
hr = MimeOleGetBodyPropA(pMsg, hBodySrc, pszName, NOFLAGS, &lpszProp);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = MimeOleSetBodyPropA(pMsg, hBodyDest, pszName, NOFLAGS, lpszProp);
|
|
SafeMimeOleFree(lpszProp);
|
|
}
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
HRESULT HrFindUrlInMsg(LPMIMEMESSAGE pMsg, LPSTR lpszUrl, LPSTREAM *ppstm)
|
|
{
|
|
HBODY hBody;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (MimeOleGetRelatedSection(pMsg, FALSE, &hBody, NULL)==S_OK && hBody)
|
|
{
|
|
if (!FAILED(hr = pMsg->ResolveURL(hBody, NULL, lpszUrl, 0, &hBody)))
|
|
hr = pMsg->BindToObject(hBody, IID_IStream, (LPVOID *)ppstm);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrSniffStreamFileExt(LPSTREAM pstm, LPSTR *lplpszExt)
|
|
{
|
|
BYTE pb[4096];
|
|
LPWSTR lpszW;
|
|
TCHAR rgch[MAX_PATH];
|
|
|
|
if (!FAILED(pstm->Read(&pb, 4096, NULL)))
|
|
{
|
|
if (!FAILED(FindMimeFromData(NULL, NULL, pb, 4096, NULL, NULL, &lpszW, 0)))
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, lpszW, -1, rgch, MAX_PATH, NULL, NULL);
|
|
return MimeOleGetContentTypeExt(rgch, lplpszExt);
|
|
}
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
#endif
|