|
|
#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 cchMAPIDLL, 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 cchMAPIDLL, 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];
StrCpyN(szProgKey, MAIL_HANDLER TEXT("\\"), ARRAYSIZE(szProgKey)); StrCpyN(szProgKey, szDefaultProg, ARRAYSIZE(szProgKey));
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szProgKey, 0, KEY_QUERY_VALUE, &hkey)) { // ugly, hard code this for OE
*pbWantsCodePageInfo = (StrCmpI(szDefaultProg, MAIL_ATHENA_V2) == 0);
cb = sizeof(*pszMAPIDLL)*cchMAPIDLL; if (ERROR_SUCCESS != SHQueryValueEx(hkey, TEXT("DLLPath"), 0, NULL, (LPBYTE)pszMAPIDLL, &cb)) { if (StrCmpI(szDefaultProg, MAIL_ATHENA_V1) == 0) { StrCpyN(pszMAPIDLL, TEXT("mailnews.dll"), cchMAPIDLL); } } 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, ARRAYSIZE(szMAPIDLL), pbWantsCodePageInfo)) { // read win.ini (bogus hu!) for mapi dll provider
if (GetProfileString(TEXT("Mail"), TEXT("CMCDLLName32"), TEXT(""), szMAPIDLL, ARRAYSIZE(szMAPIDLL)) <= 0) { StrCpyN(szMAPIDLL, TEXT("mapi32.dll"), ARRAYSIZE(szMAPIDLL)); } } 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);
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.
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[4096+512] ={0}; // body text (with enough room for prefix/postfix)
TCHAR szTemp[4096] = {0}; TCHAR szTitle[256] = {0}; CHAR szTextA[ARRAYSIZE(szText)], szTitleA[ARRAYSIZE(szTitle)]; 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(szTemp, TEXT("\n\r"), ARRAYSIZE(szTemp)); } TCHAR szTarget[MAX_PATH]; hr = GetShortcutTarget(pFile->pszFileName, szTarget, ARRAYSIZE(szTarget)); if (SUCCEEDED(hr)) { TCHAR szFmt[128], szString[MAX_PATH+ARRAYSIZE(szFmt)]; LoadString(g_hinst, IDS_SENDMAIL_SHORTCUTTO, szFmt, ARRAYSIZE(szFmt)); StringCchPrintf(szString, ARRAYSIZE(szString), szFmt, szTarget); StrCatBuff(szTemp, szString, ARRAYSIZE(szTemp)); } else { StrCatBuff(szTemp, pFile->pszTitle, ARRAYSIZE(szTemp)); }
StrCatBuff(szTitle, pFile->pszTitle, ARRAYSIZE(szTitle));
// can change this logic once CFileStream supports STGM_DELETE_ON_RELEASE
ATOMICRELEASE(pFile->pStream); } LoadString(g_hinst, IDS_SENDMAIL_MSGBODY, szText, ARRAYSIZE(szText)); // prefix
StrCatBuff(szText, szTemp, ARRAYSIZE(szText)); // file list
LoadString(g_hinst, IDS_SENDMAIL_MSGPOSTFIX, szTemp, ARRAYSIZE(szTemp)); StrCatBuff(szText, szTemp, ARRAYSIZE(szText)); // postfix
// 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 (RegCreateKeyEx(hkCLSID, DEFAULTICON, 0, NULL, 0, KEY_SET_VALUE,NULL, &hk, NULL) == ERROR_SUCCESS) { TCHAR szIcon[MAX_PATH + 10]; StringCchPrintf(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, ARRAYSIZE(szFile), IDS_MAIL_FILENAME, EXCHANGE_EXTENSION))) { SetFileAttributes(szFile, FILE_ATTRIBUTE_HIDDEN); } } else { if (SUCCEEDED(GetDropTargetPath(szFile, ARRAYSIZE(szFile), IDS_MAIL_FILENAME, SENDMAIL_EXTENSION))) { DeleteFile(szFile); }
// unhide the exchange shortcut
if (SUCCEEDED(GetDropTargetPath(szFile, ARRAYSIZE(szFile), IDS_MAIL_FILENAME, EXCHANGE_EXTENSION))) { SetFileAttributes(szFile, FILE_ATTRIBUTE_NORMAL); } } return S_OK; }
|