366 lines
11 KiB
C++
366 lines
11 KiB
C++
#include "precomp.h" // pch file
|
|
#include "mapi.h"
|
|
#include "sendto.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
// class that implement the MAPI send mail handler
|
|
|
|
typedef struct
|
|
{
|
|
TCHAR szTempShortcut[MAX_PATH];
|
|
MapiMessage mm;
|
|
MapiFileDesc mfd[0];
|
|
} MAPIFILES;
|
|
|
|
class CMailRecipient : public CSendTo
|
|
{
|
|
public:
|
|
CMailRecipient();
|
|
|
|
private:
|
|
BOOL _GetDefaultMailHandler(LPTSTR pszMAPIDLL, DWORD cbMAPIDLL, BOOL *pbWantsCodePageInfo);
|
|
HMODULE _LoadMailProvider(BOOL *pbWantsCodePageInfo);
|
|
MAPIFILES *_AllocMAPIFiles(MRPARAM *pmp);
|
|
void _FreeMAPIFiles(MAPIFILES *pmf);
|
|
|
|
DWORD _grfKeyState;
|
|
IStream *_pstrmDataObj; // marshalled IDataObject
|
|
|
|
static DWORD CALLBACK s_MAPISendMailThread(void *pv);
|
|
DWORD _MAPISendMailThread();
|
|
|
|
protected:
|
|
HRESULT v_DropHandler(IDataObject *pdtobj, DWORD grfKeyState, DWORD dwEffect);
|
|
};
|
|
|
|
|
|
// construct the sendto object with the appropriate CLSID.
|
|
|
|
CMailRecipient::CMailRecipient() :
|
|
CSendTo(CLSID_MailRecipient)
|
|
{
|
|
}
|
|
|
|
|
|
// read the default mail handler from the regstiry
|
|
|
|
#define MAIL_HANDLER TEXT("Software\\Clients\\Mail")
|
|
#define MAIL_ATHENA_V1 TEXT("Internet Mail and News")
|
|
#define MAIL_ATHENA_V2 TEXT("Outlook Express")
|
|
|
|
BOOL CMailRecipient::_GetDefaultMailHandler(LPTSTR pszMAPIDLL, DWORD cbMAPIDLL, BOOL *pbWantsCodePageInfo)
|
|
{
|
|
TCHAR szDefaultProg[80];
|
|
DWORD cb = SIZEOF(szDefaultProg);
|
|
|
|
*pbWantsCodePageInfo = FALSE;
|
|
|
|
*pszMAPIDLL = 0;
|
|
if (ERROR_SUCCESS == SHRegGetUSValue(MAIL_HANDLER, TEXT(""), NULL, szDefaultProg, &cb, FALSE, NULL, 0))
|
|
{
|
|
HKEY hkey;
|
|
TCHAR szProgKey[128];
|
|
|
|
lstrcpy(szProgKey, MAIL_HANDLER TEXT("\\"));
|
|
lstrcat(szProgKey, szDefaultProg);
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szProgKey,
|
|
0, KEY_QUERY_VALUE, &hkey))
|
|
{
|
|
// ugly, hard code this for OE
|
|
*pbWantsCodePageInfo = (lstrcmpi(szDefaultProg, MAIL_ATHENA_V2) == 0);
|
|
|
|
cb = cbMAPIDLL;
|
|
if (ERROR_SUCCESS != SHQueryValueEx(hkey, TEXT("DLLPath"), 0, NULL, (LPBYTE)pszMAPIDLL, &cb))
|
|
{
|
|
if (lstrcmpi(szDefaultProg, MAIL_ATHENA_V1) == 0)
|
|
{
|
|
lstrcpyn(pszMAPIDLL, TEXT("mailnews.dll"), cbMAPIDLL);
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
return *pszMAPIDLL;
|
|
}
|
|
|
|
|
|
// load the mail provider, returning a suitable default if we can't read it from the registry
|
|
|
|
HMODULE CMailRecipient::_LoadMailProvider(BOOL *pbWantsCodePageInfo)
|
|
{
|
|
TCHAR szMAPIDLL[MAX_PATH];
|
|
|
|
if (!_GetDefaultMailHandler(szMAPIDLL, sizeof(szMAPIDLL), pbWantsCodePageInfo))
|
|
{
|
|
// read win.ini (bogus hu!) for mapi dll provider
|
|
if (GetProfileString(TEXT("Mail"), TEXT("CMCDLLName32"), TEXT(""), szMAPIDLL, ARRAYSIZE(szMAPIDLL)) <= 0)
|
|
{
|
|
lstrcpy(szMAPIDLL, TEXT("mapi32.dll"));
|
|
}
|
|
}
|
|
return LoadLibrary(szMAPIDLL);
|
|
}
|
|
|
|
|
|
// allocate a list of MAPI files
|
|
|
|
MAPIFILES* CMailRecipient::_AllocMAPIFiles(MRPARAM *pmp)
|
|
{
|
|
MAPIFILES *pmf;
|
|
int n = SIZEOF(*pmf) + (pmp->nFiles * SIZEOF(pmf->mfd[0]));;
|
|
|
|
pmf = (MAPIFILES*)GlobalAlloc(GPTR, n + (pmp->nFiles * pmp->cchFile * 2));
|
|
if (pmf)
|
|
{
|
|
pmf->mm.nFileCount = pmp->nFiles;
|
|
if (pmp->nFiles)
|
|
{
|
|
int i;
|
|
LPSTR pszA = (CHAR *)pmf + n; // thunk buffer
|
|
|
|
pmf->mm.lpFiles = pmf->mfd;
|
|
|
|
CFileEnum MREnum(pmp, NULL);
|
|
MRFILEENTRY *pFile;
|
|
|
|
i = 0;
|
|
while (pFile = MREnum.Next())
|
|
{
|
|
// if the first item is a folder, we will create a shortcut to
|
|
// that instead of trying to mail the folder (that MAPI does
|
|
// not support)
|
|
|
|
SHPathToAnsi(pFile->pszFileName, pszA, pmp->cchFile * sizeof(TCHAR));
|
|
|
|
pmf->mfd[i].lpszPathName = pszA;
|
|
pmf->mfd[i].lpszFileName = PathFindFileNameA(pszA);
|
|
pmf->mfd[i].nPosition = (UINT)-1;
|
|
|
|
pszA += lstrlenA(pszA) + 1;
|
|
++i;
|
|
}
|
|
|
|
}
|
|
}
|
|
return pmf;
|
|
}
|
|
|
|
|
|
// free the list of mapi files
|
|
|
|
void CMailRecipient::_FreeMAPIFiles(MAPIFILES *pmf)
|
|
{
|
|
if (pmf->szTempShortcut[0])
|
|
DeleteFile(pmf->szTempShortcut);
|
|
GlobalFree(pmf);
|
|
}
|
|
|
|
|
|
// package up the parameters and then kick off a background thread which will do
|
|
// the processing for the send mail.
|
|
|
|
HRESULT CMailRecipient::v_DropHandler(IDataObject *pdo, DWORD grfKeyState, DWORD dwEffect)
|
|
{
|
|
_grfKeyState = grfKeyState;
|
|
_pstrmDataObj = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (pdo)
|
|
hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdo, &_pstrmDataObj);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
AddRef();
|
|
if (!SHCreateThread(s_MAPISendMailThread, this, CTF_PROCESS_REF|CTF_FREELIBANDEXIT|CTF_COINIT, NULL))
|
|
{
|
|
Release();
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) && _pstrmDataObj)
|
|
{
|
|
_pstrmDataObj->Release();
|
|
_pstrmDataObj = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
DWORD CALLBACK CMailRecipient::s_MAPISendMailThread(void *pv)
|
|
{
|
|
CMailRecipient *pmr = (CMailRecipient *)pv;
|
|
return pmr->_MAPISendMailThread();
|
|
}
|
|
|
|
|
|
// handler for the drop. this creates a list of files and then passes the object to
|
|
// another thread which inturn releases it.
|
|
|
|
const TCHAR c_szPad[] = TEXT(" \r\n ");
|
|
|
|
DWORD CMailRecipient::_MAPISendMailThread()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
MRPARAM *pmp = (MRPARAM*)GlobalAlloc(GPTR, SIZEOF(*pmp));
|
|
if (pmp)
|
|
{
|
|
// if we have an IDataObject stream then lets unmarshall it and
|
|
// create the file list from it.
|
|
|
|
if (_pstrmDataObj)
|
|
{
|
|
IDataObject *pdo;
|
|
hr = CoGetInterfaceAndReleaseStream(_pstrmDataObj, IID_PPV_ARG(IDataObject, &pdo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateSendToFilesFromDataObj(pdo, _grfKeyState, pmp);
|
|
pdo->Release();
|
|
}
|
|
_pstrmDataObj = NULL;
|
|
|
|
}
|
|
|
|
// lets build the MAPI information so that we can send the files.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MAPIFILES *pmf = _AllocMAPIFiles(pmp);
|
|
if (pmf)
|
|
{
|
|
TCHAR szText[MAX_PATH] ={0}; // body text.
|
|
TCHAR szTitle[2048] ={0};
|
|
CHAR szTextA[ARRAYSIZE(szText)]; // ...
|
|
CHAR szTitleA[ARRAYSIZE(szTitle)]; // because the title is supposed to be non-const (and ansi)
|
|
|
|
if (pmf->mm.nFileCount)
|
|
{
|
|
CFileEnum MREnum(pmp, NULL);
|
|
MRFILEENTRY *pFile;
|
|
|
|
LoadString(g_hinst, IDS_SENDMAIL_MSGTITLE, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
// release our stream objects
|
|
for (int iFile = 0; (NULL != (pFile = MREnum.Next())); iFile++)
|
|
{
|
|
if (iFile>0)
|
|
{
|
|
StrCatBuff(szTitle, TEXT(", "), ARRAYSIZE(szTitle));
|
|
}
|
|
StrCatBuff(szTitle, pFile->pszTitle, ARRAYSIZE(szTitle));
|
|
|
|
// can change this logic once CFileStream supports STGM_DELETE_ON_RELEASE
|
|
ATOMICRELEASE(pFile->pStream);
|
|
}
|
|
|
|
// lets set the body text
|
|
LoadString(g_hinst, IDS_SENDMAIL_MSGBODY, szText, ARRAYSIZE(szText));
|
|
|
|
// Don't fill in lpszNoteText if we know we are sending documents because OE will puke on it
|
|
SHTCharToAnsi(szText, szTextA, ARRAYSIZE(szTextA));
|
|
if (!(pmp->dwFlags & MRPARAM_DOC))
|
|
{
|
|
pmf->mm.lpszNoteText = szTextA;
|
|
}
|
|
else
|
|
{
|
|
Assert(pmf->mm.lpszNoteText == NULL);
|
|
}
|
|
|
|
SHTCharToAnsi(szTitle, szTitleA, ARRAYSIZE(szTitleA));
|
|
pmf->mm.lpszSubject = szTitleA;
|
|
}
|
|
|
|
BOOL bWantsCodePageInfo = FALSE;
|
|
HMODULE hmodMail = _LoadMailProvider(&bWantsCodePageInfo);
|
|
if (bWantsCodePageInfo && (pmp->dwFlags & MRPARAM_USECODEPAGE))
|
|
{
|
|
// When this flag is set, we know that we have just one file to send and we have a code page
|
|
// Athena will then look at ulReserved for the code page
|
|
// Will the other MAPI handlers puke on this? -- dli
|
|
ASSERT(pmf->mm.nFileCount == 1);
|
|
pmf->mfd[0].ulReserved = ((MRPARAM *)pmp)->uiCodePage;
|
|
}
|
|
|
|
if (hmodMail)
|
|
{
|
|
LPMAPISENDMAIL pfnSendMail = (LPMAPISENDMAIL)GetProcAddress(hmodMail, "MAPISendMail");
|
|
if (pfnSendMail)
|
|
{
|
|
pfnSendMail(0, 0, &pmf->mm, MAPI_LOGON_UI | MAPI_DIALOG, 0);
|
|
}
|
|
FreeLibrary(hmodMail);
|
|
}
|
|
_FreeMAPIFiles(pmf);
|
|
}
|
|
}
|
|
CleanupPMP(pmp);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
Release();
|
|
return 0;
|
|
}
|
|
|
|
|
|
// construct the send to mail recipient object
|
|
|
|
STDAPI MailRecipient_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
*ppunk = NULL; // assume failure
|
|
|
|
if ( punkOuter )
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
CMailRecipient *psm = new CMailRecipient;
|
|
if ( !psm )
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = psm->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
|
|
psm->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// handle registration / link creation for the send mail verb
|
|
|
|
#define SENDMAIL_EXTENSION TEXT("MAPIMail")
|
|
#define EXCHANGE_EXTENSION TEXT("lnk")
|
|
|
|
STDAPI MailRecipient_RegUnReg(BOOL bReg, HKEY hkCLSID, LPCTSTR pszCLSID, LPCTSTR pszModule)
|
|
{
|
|
TCHAR szFile[MAX_PATH];
|
|
if (bReg)
|
|
{
|
|
HKEY hk;
|
|
CommonRegister(hkCLSID, pszCLSID, SENDMAIL_EXTENSION, IDS_MAIL_FILENAME);
|
|
|
|
if (RegCreateKey(hkCLSID, DEFAULTICON, &hk) == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szIcon[MAX_PATH + 10];
|
|
wnsprintf(szIcon, ARRAYSIZE(szIcon), TEXT("%s,-%d"), pszModule, IDI_MAIL);
|
|
RegSetValueEx(hk, NULL, 0, REG_SZ, (LPBYTE)szIcon, (lstrlen(szIcon) + 1) * SIZEOF(TCHAR));
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
// hide the exchange shortcut
|
|
if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, EXCHANGE_EXTENSION)))
|
|
SetFileAttributes(szFile, FILE_ATTRIBUTE_HIDDEN);
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, SENDMAIL_EXTENSION)))
|
|
DeleteFile(szFile);
|
|
|
|
// unhide the exchange shortcut
|
|
if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, EXCHANGE_EXTENSION)))
|
|
SetFileAttributes(szFile, FILE_ATTRIBUTE_NORMAL);
|
|
}
|
|
return S_OK;
|
|
}
|