2025-04-27 07:49:33 -04:00

674 lines
15 KiB
C++

/*
* m s g m o n . c p p
*
* Purpose:
* IMoniker implementation for messages.
*
* History
* September '96: brettm - created
* March '97: moved from mailnews to inetcomm
*
* Copyright (C) Microsoft Corp. 1995, 1996.
*/
#include "pch.hxx"
#include "dllmain.h"
#include "resource.h"
#include "strconst.h"
#include "msgmon.h"
#ifdef MAC
#include "commdlg.h"
#else // !MAC
#include <shlwapi.h>
#endif // MAC
#include "demand.h"
ASSERTDATA
/*
* m a c r o s
*/
#define AssertThread AssertSz(m_dwThread == GetCurrentThreadId(), "Moniker called on thread other than it's owner!!")
/*
* t y p e d e f s
*/
/*
* c o n s t a n t s
*/
/*
* g l o b a l s
*/
/*
* f u n c t i o n p r o t y p e s
*/
HRESULT HrGetObjectParam(LPBC pbc, LPOLESTR pszKey, REFIID riid, LPUNKNOWN *ppUnk);
void DebugDumpStreamToFile(LPSTREAM pstm, LPSTR lpszFile)
{
LPSTREAM pstmFile;
if (!FAILED(OpenFileStream(lpszFile, CREATE_ALWAYS, GENERIC_WRITE, &pstmFile)))
{
HrRewindStream(pstm);
HrCopyStream (pstm, pstmFile, NULL);
pstmFile->Release();
HrRewindStream(pstm);
}
};
/*
* f u n c t i o n s
*/
CMsgMon::CMsgMon()
{
m_cRef=1;
m_pbsc=0;
m_pstmRoot=0;
m_pbc = 0;
m_pUnk=0;
ZeroMemory(&m_iid, sizeof(IID));
#ifdef DEBUG
m_dwThread = GetCurrentThreadId();
#endif
}
CMsgMon::~CMsgMon()
{
AssertThread;
ReleaseObj(m_pbsc);
ReleaseObj(m_pstmRoot);
ReleaseObj(m_pUnk);
if (m_pbc)
{
m_pbc->RevokeObjectParam((LPOLESTR)REG_BC_ATHENAMESSAGE);
m_pbc->Release();
}
}
HRESULT CMsgMon::QueryInterface(REFIID riid, LPVOID *lplpObj)
{
AssertThread;
if(!lplpObj)
return E_INVALIDARG;
*lplpObj = NULL; // set to NULL, in case we fail.
if (IsEqualIID(riid, IID_IUnknown))
*lplpObj = (LPVOID)this;
if (IsEqualIID(riid, IID_IMoniker))
{
DOUTL(64, "CMsgMon::QI - IID_IMoniker");
*lplpObj = (LPVOID)(LPMONIKER)this;
}
if (IsEqualIID(riid, IID_IBinding))
{
DOUTL(64, "CMsgMon::QI - IID_IBinding");
*lplpObj = (LPVOID)(LPBINDING)this;
}
if(!*lplpObj)
{
#ifdef DEBUG
LPOLESTR szW=0;
char sz[MAX_PATH];
char szOut[512];
char *pszIFName=0;
StringFromIID(riid, &szW);
#ifdef MAC
wsprintf(szOut, "CMsgMon::QI [not supported]:%s", pszIFName?pszIFName:szW);
#else // !MAC
WideCharToMultiByte(CP_ACP, 0, szW, -1, sz, MAX_PATH, NULL, NULL);
wsprintf(szOut, "CMsgMon::QI [not supported]:%s", pszIFName?pszIFName:sz);
#endif // MAC
DOUTL(64, szOut);
if(szW)
CoTaskMemFree(szW);
#endif
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG CMsgMon::AddRef()
{
AssertThread;
return ++m_cRef;
}
ULONG CMsgMon::Release()
{
AssertThread;
if (--m_cRef==0)
{
delete this;
return 0;
}
return m_cRef;
}
// *** IPersist ***
HRESULT CMsgMon::GetClassID (LPCLSID pCLSDID)
{
AssertThread;
return E_NOTIMPL;
}
// *** IPersistStream methods ***
HRESULT CMsgMon::IsDirty()
{
AssertThread;
return S_FALSE;
}
HRESULT CMsgMon::Load (LPSTREAM pstm)
{
AssertThread;
if (!pstm)
return E_INVALIDARG;
if (m_fRootMoniker)
{
// if we're a root moniker, allow setting of the root stream
SafeRelease(m_pstmRoot);
m_pstmRoot = pstm;
pstm->AddRef();
return S_OK;
}
return E_NOTIMPL;
}
HRESULT CMsgMon::Save (LPSTREAM pstm, BOOL fClearDirty)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
AssertThread;
return E_NOTIMPL;
}
// *** IMoniker methods ***
HRESULT CMsgMon::BindToObject(LPBC pbc, LPMONIKER pmkToLeft, REFIID riidResult, LPVOID *ppvResult)
{
AssertThread;
DOUTL(64, "CMsgMon::BindToObject");
return E_NOTIMPL;
}
HRESULT CMsgMon::BindToStorage (LPBC pbc, LPMONIKER pmkToLeft, REFIID riid, LPVOID *ppvObj)
{
HRESULT hr;
AssertThread;
if(ppvObj==NULL || pbc==NULL)
return E_INVALIDARG;
*ppvObj=NULL;
if(!IsEqualIID(riid, IID_IStream)) // can't handle bind to a non-stream
return E_FAIL;
// async bind: NB: this is actually fake async right now, so no need for custom mashalling
// across threads. This code needs to change when we need true asyncronisity
hr = HrPrepareForBind(pbc);
if (FAILED(hr))
goto error;
hr=HrStartBinding();
if(FAILED(hr))
goto error;
if (m_fRootMoniker)
{
hr = HrBindToMessage();
if(FAILED(hr))
goto error;
}
else
{
hr = HrBindToBodyPart();
if (FAILED(hr))
goto error;
}
hr=HrStopBinding();
if(FAILED(hr))
goto error;
// if all went well, return ASYNC so the bindoperation
// continues
if(SUCCEEDED(hr))
hr=MK_S_ASYNCHRONOUS;
error:
return hr;
}
HRESULT CMsgMon::Reduce(LPBC pbc, DWORD dwReduceHowFar, LPMONIKER *ppmkToLeft, LPMONIKER *ppmkReduced)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::ComposeWith (LPMONIKER pmkRight, BOOL fOnlyIfNotGeneric, LPMONIKER *ppmkComposite)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::Enum(BOOL fForward, LPENUMMONIKER *ppenumMoniker)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::IsEqual(LPMONIKER pmkOtherMoniker)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::Hash(LPDWORD pdwHash)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::IsRunning(LPBC pbc, LPMONIKER pmkToLeft, LPMONIKER pmkNewlyRunning)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::GetTimeOfLastChange(LPBC pbc, LPMONIKER pmkToLeft, LPFILETIME pFileTime)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::Inverse(LPMONIKER * ppmk)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::CommonPrefixWith(LPMONIKER pmkOther, LPMONIKER *ppmkPrefix)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::RelativePathTo(LPMONIKER pmkOther, LPMONIKER *ppmkRelPath)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::GetDisplayName(LPBC pbc, LPMONIKER pmkToLeft, LPOLESTR *ppszDisplayName)
{
AssertThread;
TCHAR sz[MAX_PATH];
DOUTL(64, "CMsgMon::GetDisplayName");
if(ppszDisplayName==NULL)
return E_NOTIMPL;
*ppszDisplayName = (LPOLESTR)CoTaskMemAlloc(MAX_PATH * sizeof(OLECHAR));
if (!*ppszDisplayName)
return E_OUTOFMEMORY;
**ppszDisplayName=0;
#ifdef MAC
lstrcpy(*ppszDisplayName, "mailnews://");
#else // !MAC
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "mailnews://", -1, *ppszDisplayName, MAX_PATH);
#endif // MAC
return NOERROR;
}
HRESULT CMsgMon::ParseDisplayName(LPBC pbc, LPMONIKER pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, LPMONIKER *ppmkOut)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::IsSystemMoniker(DWORD *pdwMksys)
{
AssertThread;
return E_NOTIMPL;
}
HRESULT CMsgMon::HrInit(REFIID riid, LPUNKNOWN pUnk)
{
AssertThread;
if(pUnk==NULL)
return E_INVALIDARG;
if (!IsEqualIID(riid, IID_IMimeMessage) && !IsEqualIID(riid, IID_IMimeBody))
return E_NOINTERFACE;
m_pUnk=pUnk;
pUnk->AddRef();
CopyMemory((LPVOID)&m_iid, (LPCVOID)&riid, sizeof(IID));
m_fRootMoniker=IsEqualIID(m_iid, IID_IMimeMessage);
return NOERROR;
}
HRESULT CMsgMon::Abort()
{
return E_NOTIMPL;
}
HRESULT CMsgMon::Suspend()
{
AssertSz(FALSE, "IBinding::Suspend - why did we get called if we're synchronous?");
return NOERROR;
}
HRESULT CMsgMon::Resume()
{
AssertSz(FALSE, "IBinding::Resume - why did we get called if we're synchronous?");
return NOERROR;
}
HRESULT CMsgMon::SetPriority(LONG nPriority)
{
AssertSz(FALSE, "IBinding::SetPriority - why did we get called if we're synchronous?");
return NOERROR;
}
HRESULT CMsgMon::GetPriority(LPLONG pnPriority)
{
AssertSz(FALSE, "IBinding::GetPriority - why did we get called if we're synchronous?");
if(pnPriority==NULL)
return E_INVALIDARG;
*pnPriority = GetThreadPriority(GetCurrentThread());
return NOERROR;
}
HRESULT CMsgMon::GetBindResult(LPCLSID pclsidProtocol, LPDWORD pdwResult, LPWSTR *pszResult, LPDWORD pdwReserved)
{
AssertSz(FALSE, "IBinding::GetBindResult - NYI.");
return E_NOTIMPL;
}
HRESULT CMsgMon::HrHandOffDataStream(LPSTREAM pstm, CLIPFORMAT cf)
{
HRESULT hr;
FORMATETC fetc = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
STGMEDIUM stgmed;
ULONG cb=0;
if(pstm==NULL)
return E_INVALIDARG;
AssertSz(m_pbsc, "this should never be called without one!!");
stgmed.tymed = TYMED_ISTREAM;
stgmed.pstm=pstm;
stgmed.pUnkForRelease=pstm;
HrGetStreamSize(pstm, &cb);
HrRewindStream(pstm);
hr=m_pbsc->OnDataAvailable(BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION, cb, &fetc, &stgmed);
return hr;
}
HRESULT CMsgMon::HrPrepareForBind(LPBC pbc)
{
HRESULT hr;
AssertSz(pbc!=NULL, "This should be checked higher up the foodchain");
SafeRelease(m_pbc);
m_pbc = pbc;
m_pbc->AddRef();
SafeRelease(m_pbsc);
hr = HrGetObjectParam(pbc, (LPOLESTR)REG_BC_BINDSTATUSCALLBACK, IID_IBindStatusCallback, (LPUNKNOWN *)&m_pbsc);
if(FAILED(hr))
goto error;
if (m_fRootMoniker)
{
// if we are the root moniker we make sure to register our IMimeMessage in the bind context
// if this fails, don't fail here, fail in the next msgmon bind when GetObjParam will fail.
pbc->RegisterObjectParam((LPOLESTR)REG_BC_ATHENAMESSAGE, m_pUnk);
}
error:
return hr;
}
HRESULT CMsgMon::HrStartBinding()
{
HRESULT hr;
BINDINFO bi;
DWORD dwBind=0;
AssertSz(m_pbc!=NULL, "This should be checked higher up the foodchain");
AssertSz(m_pbsc!=NULL, "This should be checked higher up the foodchain");
ZeroMemory(&bi, sizeof(bi));
bi.cbSize=sizeof(BINDINFO);
hr=m_pbsc->GetBindInfo(&dwBind, &bi);
if(FAILED(hr))
goto error;
hr=m_pbsc->OnStartBinding(0, (LPBINDING)this);
if(FAILED(hr))
goto error;
error:
return hr;
}
HRESULT CMsgMon::HrStopBinding()
{
HRESULT hr;
AssertSz(m_pbsc!=NULL, "This should be checked higher up the foodchain");
#ifndef WIN16
hr=m_pbsc->OnStopBinding(NOERROR, L"");
#else
hr=m_pbsc->OnStopBinding(NOERROR, "");
#endif // !WIN16
SafeRelease(m_pbsc);
return hr;
}
HRESULT CMsgMon::HrBindToBodyPart()
{
HRESULT hr;
TCHAR *pszExt;
LPSTR lpszFileName=0,
lpszCntType=0,
lpszCF=0;
LPSTREAM pstm=0;
IMimeBody *pBody = (IMimeBody *)m_pUnk;
Assert (m_pUnk != NULL && IsEqualIID(m_iid, IID_IMimeBody));
hr = MimeOleGetPropA(pBody, PIDTOSTR(PID_ATT_FILENAME), NOFLAGS, &lpszFileName);
if (!FAILED(hr))
{
// if there's a filename, try and infer type from filename
pszExt = PathFindExtension(lpszFileName);
MimeOleGetExtContentType(pszExt, &lpszCntType);
}
// if no filename, or couldn't infer type, try to get from header
if (!lpszCntType)
{
hr = MimeOleGetPropA(pBody, PIDTOSTR(PID_HDR_CNTTYPE), NOFLAGS, &lpszCntType);
if (FAILED(hr))
goto error;
}
// if no content-type then give raw-data
if (!lpszCntType)
lpszCF = CFSTR_MIME_RAWDATA;
else
lpszCF = lpszCntType;
hr = pBody->BindToObject(IID_IStream, (LPVOID *)&pstm);
if (FAILED(hr))
goto error;
hr = HrHandOffDataStream(pstm, (CLIPFORMAT) RegisterClipboardFormat(lpszCF));
if (FAILED(hr))
goto error;
error:
ReleaseObj(pstm);
SafeMemFree(lpszCntType);
SafeMemFree(lpszFileName);
return hr;
}
HRESULT CMsgMon::HrBindToMessage()
{
HRESULT hr=NOERROR;
LPSTREAM pstmLoad=0;
LPCSTR lpszCF=CFSTR_MIME_HTML;
IMimeMessage *pMsg = (IMimeMessage *)m_pUnk;
Assert (m_pUnk != NULL && IsEqualIID(m_iid, IID_IMimeMessage));
if (m_pstmRoot)
{
pstmLoad=m_pstmRoot;
m_pstmRoot->AddRef();
}
else
{
// if no root-stream override, then get html or plain text body part
hr = pMsg->GetTextBody(TXT_HTML, IET_DECODED, &pstmLoad, NULL);
if (FAILED(hr))
{
lpszCF = CFSTR_MIME_TEXT;
hr = pMsg->GetTextBody(TXT_PLAIN, IET_DECODED, &pstmLoad, NULL);
if (FAILED(hr))
goto error;
}
}
// when we implement Async binding, figure out the body part we need and put into
// pstmLoad.
hr=HrHandOffDataStream(pstmLoad, (CLIPFORMAT) RegisterClipboardFormat(lpszCF));
if(FAILED(hr))
goto error;
error:
ReleaseObj(pstmLoad);
return hr;
}
/*
* pMsg
* - should always be valid
*
* fIsRootMoniker
* - if this is set, then we require special handling. This is the root moniker of the bind, in this case it
* will register the pMsg in the bind context in the call to BindToStorage. This will enable the BindHost to
* perform IMimeMessage lookup from the context and quickly bind to a subpart. Also, it will determine the richest
* format of text to display - html or plaintext and pump it to the bindstatuscallback, unless pstmRoot is set
* in which case it will use this as the root stream. pstmRoot can't be set if fIsRoot is False.
*
* hBody
* - if we're not a root moniker, then we're a body moniker, in which case hBody will be set to the body part it is,
* related to, in theory this could be HBODY_ROOT, which would mean it would pump "message/rfc822" at the bscb.
*
*/
HRESULT HrCreateMsgMoniker(REFIID riid, LPUNKNOWN pUnk, LPMONIKER *ppmk)
{
LPMSGMONIKER pMsgMon=0;
HRESULT hr;
if(!ppmk)
return E_INVALIDARG;
*ppmk = 0;
pMsgMon = new CMsgMon();
if(!pMsgMon)
return E_OUTOFMEMORY;
hr=pMsgMon->HrInit(riid, pUnk);
if(FAILED(hr))
goto error;
hr=pMsgMon->QueryInterface(IID_IMoniker, (LPVOID *)ppmk);
if(FAILED(hr))
goto error;
error:
ReleaseObj(pMsgMon);
return hr;
}
HRESULT HrGetObjectParam(LPBC pbc, LPOLESTR pszKey, REFIID riid, LPUNKNOWN *ppUnk)
{
HRESULT hr;
LPUNKNOWN pUnk=0;
if(pbc==NULL)
return E_INVALIDARG;
// Try to get an IUnknown pointer from the bind context
hr = pbc->GetObjectParam(pszKey, &pUnk);
if (FAILED(hr))
return hr;
// Query for riid
hr = pUnk->QueryInterface(riid, (void **)ppUnk);
ReleaseObj(pUnk);
Assert(SUCCEEDED(hr));
return hr;
}