windows-nt/Source/XPSP1/NT/shell/ext/sendmail/mail.cpp
2020-09-26 16:20:57 +08:00

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;
}