Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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