Source code of Windows XP (NT5)
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.

365 lines
11 KiB

  1. #include "precomp.h" // pch file
  2. #include "mapi.h"
  3. #include "sendto.h"
  4. #pragma hdrstop
  5. // class that implement the MAPI send mail handler
  6. typedef struct
  7. {
  8. TCHAR szTempShortcut[MAX_PATH];
  9. MapiMessage mm;
  10. MapiFileDesc mfd[0];
  11. } MAPIFILES;
  12. class CMailRecipient : public CSendTo
  13. {
  14. public:
  15. CMailRecipient();
  16. private:
  17. BOOL _GetDefaultMailHandler(LPTSTR pszMAPIDLL, DWORD cbMAPIDLL, BOOL *pbWantsCodePageInfo);
  18. HMODULE _LoadMailProvider(BOOL *pbWantsCodePageInfo);
  19. MAPIFILES *_AllocMAPIFiles(MRPARAM *pmp);
  20. void _FreeMAPIFiles(MAPIFILES *pmf);
  21. DWORD _grfKeyState;
  22. IStream *_pstrmDataObj; // marshalled IDataObject
  23. static DWORD CALLBACK s_MAPISendMailThread(void *pv);
  24. DWORD _MAPISendMailThread();
  25. protected:
  26. HRESULT v_DropHandler(IDataObject *pdtobj, DWORD grfKeyState, DWORD dwEffect);
  27. };
  28. // construct the sendto object with the appropriate CLSID.
  29. CMailRecipient::CMailRecipient() :
  30. CSendTo(CLSID_MailRecipient)
  31. {
  32. }
  33. // read the default mail handler from the regstiry
  34. #define MAIL_HANDLER TEXT("Software\\Clients\\Mail")
  35. #define MAIL_ATHENA_V1 TEXT("Internet Mail and News")
  36. #define MAIL_ATHENA_V2 TEXT("Outlook Express")
  37. BOOL CMailRecipient::_GetDefaultMailHandler(LPTSTR pszMAPIDLL, DWORD cbMAPIDLL, BOOL *pbWantsCodePageInfo)
  38. {
  39. TCHAR szDefaultProg[80];
  40. DWORD cb = SIZEOF(szDefaultProg);
  41. *pbWantsCodePageInfo = FALSE;
  42. *pszMAPIDLL = 0;
  43. if (ERROR_SUCCESS == SHRegGetUSValue(MAIL_HANDLER, TEXT(""), NULL, szDefaultProg, &cb, FALSE, NULL, 0))
  44. {
  45. HKEY hkey;
  46. TCHAR szProgKey[128];
  47. lstrcpy(szProgKey, MAIL_HANDLER TEXT("\\"));
  48. lstrcat(szProgKey, szDefaultProg);
  49. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szProgKey,
  50. 0, KEY_QUERY_VALUE, &hkey))
  51. {
  52. // ugly, hard code this for OE
  53. *pbWantsCodePageInfo = (lstrcmpi(szDefaultProg, MAIL_ATHENA_V2) == 0);
  54. cb = cbMAPIDLL;
  55. if (ERROR_SUCCESS != SHQueryValueEx(hkey, TEXT("DLLPath"), 0, NULL, (LPBYTE)pszMAPIDLL, &cb))
  56. {
  57. if (lstrcmpi(szDefaultProg, MAIL_ATHENA_V1) == 0)
  58. {
  59. lstrcpyn(pszMAPIDLL, TEXT("mailnews.dll"), cbMAPIDLL);
  60. }
  61. }
  62. RegCloseKey(hkey);
  63. }
  64. }
  65. return *pszMAPIDLL;
  66. }
  67. // load the mail provider, returning a suitable default if we can't read it from the registry
  68. HMODULE CMailRecipient::_LoadMailProvider(BOOL *pbWantsCodePageInfo)
  69. {
  70. TCHAR szMAPIDLL[MAX_PATH];
  71. if (!_GetDefaultMailHandler(szMAPIDLL, sizeof(szMAPIDLL), pbWantsCodePageInfo))
  72. {
  73. // read win.ini (bogus hu!) for mapi dll provider
  74. if (GetProfileString(TEXT("Mail"), TEXT("CMCDLLName32"), TEXT(""), szMAPIDLL, ARRAYSIZE(szMAPIDLL)) <= 0)
  75. {
  76. lstrcpy(szMAPIDLL, TEXT("mapi32.dll"));
  77. }
  78. }
  79. return LoadLibrary(szMAPIDLL);
  80. }
  81. // allocate a list of MAPI files
  82. MAPIFILES* CMailRecipient::_AllocMAPIFiles(MRPARAM *pmp)
  83. {
  84. MAPIFILES *pmf;
  85. int n = SIZEOF(*pmf) + (pmp->nFiles * SIZEOF(pmf->mfd[0]));;
  86. pmf = (MAPIFILES*)GlobalAlloc(GPTR, n + (pmp->nFiles * pmp->cchFile * 2));
  87. if (pmf)
  88. {
  89. pmf->mm.nFileCount = pmp->nFiles;
  90. if (pmp->nFiles)
  91. {
  92. int i;
  93. LPSTR pszA = (CHAR *)pmf + n; // thunk buffer
  94. pmf->mm.lpFiles = pmf->mfd;
  95. CFileEnum MREnum(pmp, NULL);
  96. MRFILEENTRY *pFile;
  97. i = 0;
  98. while (pFile = MREnum.Next())
  99. {
  100. // if the first item is a folder, we will create a shortcut to
  101. // that instead of trying to mail the folder (that MAPI does
  102. // not support)
  103. SHPathToAnsi(pFile->pszFileName, pszA, pmp->cchFile * sizeof(TCHAR));
  104. pmf->mfd[i].lpszPathName = pszA;
  105. pmf->mfd[i].lpszFileName = PathFindFileNameA(pszA);
  106. pmf->mfd[i].nPosition = (UINT)-1;
  107. pszA += lstrlenA(pszA) + 1;
  108. ++i;
  109. }
  110. }
  111. }
  112. return pmf;
  113. }
  114. // free the list of mapi files
  115. void CMailRecipient::_FreeMAPIFiles(MAPIFILES *pmf)
  116. {
  117. if (pmf->szTempShortcut[0])
  118. DeleteFile(pmf->szTempShortcut);
  119. GlobalFree(pmf);
  120. }
  121. // package up the parameters and then kick off a background thread which will do
  122. // the processing for the send mail.
  123. HRESULT CMailRecipient::v_DropHandler(IDataObject *pdo, DWORD grfKeyState, DWORD dwEffect)
  124. {
  125. _grfKeyState = grfKeyState;
  126. _pstrmDataObj = NULL;
  127. HRESULT hr = S_OK;
  128. if (pdo)
  129. hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdo, &_pstrmDataObj);
  130. if (SUCCEEDED(hr))
  131. {
  132. AddRef();
  133. if (!SHCreateThread(s_MAPISendMailThread, this, CTF_PROCESS_REF|CTF_FREELIBANDEXIT|CTF_COINIT, NULL))
  134. {
  135. Release();
  136. hr = E_FAIL;
  137. }
  138. }
  139. if (FAILED(hr) && _pstrmDataObj)
  140. {
  141. _pstrmDataObj->Release();
  142. _pstrmDataObj = NULL;
  143. }
  144. return hr;
  145. }
  146. DWORD CALLBACK CMailRecipient::s_MAPISendMailThread(void *pv)
  147. {
  148. CMailRecipient *pmr = (CMailRecipient *)pv;
  149. return pmr->_MAPISendMailThread();
  150. }
  151. // handler for the drop. this creates a list of files and then passes the object to
  152. // another thread which inturn releases it.
  153. const TCHAR c_szPad[] = TEXT(" \r\n ");
  154. DWORD CMailRecipient::_MAPISendMailThread()
  155. {
  156. HRESULT hr = S_OK;
  157. MRPARAM *pmp = (MRPARAM*)GlobalAlloc(GPTR, SIZEOF(*pmp));
  158. if (pmp)
  159. {
  160. // if we have an IDataObject stream then lets unmarshall it and
  161. // create the file list from it.
  162. if (_pstrmDataObj)
  163. {
  164. IDataObject *pdo;
  165. hr = CoGetInterfaceAndReleaseStream(_pstrmDataObj, IID_PPV_ARG(IDataObject, &pdo));
  166. if (SUCCEEDED(hr))
  167. {
  168. hr = CreateSendToFilesFromDataObj(pdo, _grfKeyState, pmp);
  169. pdo->Release();
  170. }
  171. _pstrmDataObj = NULL;
  172. }
  173. // lets build the MAPI information so that we can send the files.
  174. if (SUCCEEDED(hr))
  175. {
  176. MAPIFILES *pmf = _AllocMAPIFiles(pmp);
  177. if (pmf)
  178. {
  179. TCHAR szText[MAX_PATH] ={0}; // body text.
  180. TCHAR szTitle[2048] ={0};
  181. CHAR szTextA[ARRAYSIZE(szText)]; // ...
  182. CHAR szTitleA[ARRAYSIZE(szTitle)]; // because the title is supposed to be non-const (and ansi)
  183. if (pmf->mm.nFileCount)
  184. {
  185. CFileEnum MREnum(pmp, NULL);
  186. MRFILEENTRY *pFile;
  187. LoadString(g_hinst, IDS_SENDMAIL_MSGTITLE, szTitle, ARRAYSIZE(szTitle));
  188. // release our stream objects
  189. for (int iFile = 0; (NULL != (pFile = MREnum.Next())); iFile++)
  190. {
  191. if (iFile>0)
  192. {
  193. StrCatBuff(szTitle, TEXT(", "), ARRAYSIZE(szTitle));
  194. }
  195. StrCatBuff(szTitle, pFile->pszTitle, ARRAYSIZE(szTitle));
  196. // can change this logic once CFileStream supports STGM_DELETE_ON_RELEASE
  197. ATOMICRELEASE(pFile->pStream);
  198. }
  199. // lets set the body text
  200. LoadString(g_hinst, IDS_SENDMAIL_MSGBODY, szText, ARRAYSIZE(szText));
  201. // Don't fill in lpszNoteText if we know we are sending documents because OE will puke on it
  202. SHTCharToAnsi(szText, szTextA, ARRAYSIZE(szTextA));
  203. if (!(pmp->dwFlags & MRPARAM_DOC))
  204. {
  205. pmf->mm.lpszNoteText = szTextA;
  206. }
  207. else
  208. {
  209. Assert(pmf->mm.lpszNoteText == NULL);
  210. }
  211. SHTCharToAnsi(szTitle, szTitleA, ARRAYSIZE(szTitleA));
  212. pmf->mm.lpszSubject = szTitleA;
  213. }
  214. BOOL bWantsCodePageInfo = FALSE;
  215. HMODULE hmodMail = _LoadMailProvider(&bWantsCodePageInfo);
  216. if (bWantsCodePageInfo && (pmp->dwFlags & MRPARAM_USECODEPAGE))
  217. {
  218. // When this flag is set, we know that we have just one file to send and we have a code page
  219. // Athena will then look at ulReserved for the code page
  220. // Will the other MAPI handlers puke on this? -- dli
  221. ASSERT(pmf->mm.nFileCount == 1);
  222. pmf->mfd[0].ulReserved = ((MRPARAM *)pmp)->uiCodePage;
  223. }
  224. if (hmodMail)
  225. {
  226. LPMAPISENDMAIL pfnSendMail = (LPMAPISENDMAIL)GetProcAddress(hmodMail, "MAPISendMail");
  227. if (pfnSendMail)
  228. {
  229. pfnSendMail(0, 0, &pmf->mm, MAPI_LOGON_UI | MAPI_DIALOG, 0);
  230. }
  231. FreeLibrary(hmodMail);
  232. }
  233. _FreeMAPIFiles(pmf);
  234. }
  235. }
  236. CleanupPMP(pmp);
  237. }
  238. else
  239. {
  240. hr = E_OUTOFMEMORY;
  241. }
  242. Release();
  243. return 0;
  244. }
  245. // construct the send to mail recipient object
  246. STDAPI MailRecipient_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  247. {
  248. *ppunk = NULL; // assume failure
  249. if ( punkOuter )
  250. return CLASS_E_NOAGGREGATION;
  251. CMailRecipient *psm = new CMailRecipient;
  252. if ( !psm )
  253. return E_OUTOFMEMORY;
  254. HRESULT hr = psm->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  255. psm->Release();
  256. return hr;
  257. }
  258. // handle registration / link creation for the send mail verb
  259. #define SENDMAIL_EXTENSION TEXT("MAPIMail")
  260. #define EXCHANGE_EXTENSION TEXT("lnk")
  261. STDAPI MailRecipient_RegUnReg(BOOL bReg, HKEY hkCLSID, LPCTSTR pszCLSID, LPCTSTR pszModule)
  262. {
  263. TCHAR szFile[MAX_PATH];
  264. if (bReg)
  265. {
  266. HKEY hk;
  267. CommonRegister(hkCLSID, pszCLSID, SENDMAIL_EXTENSION, IDS_MAIL_FILENAME);
  268. if (RegCreateKey(hkCLSID, DEFAULTICON, &hk) == ERROR_SUCCESS)
  269. {
  270. TCHAR szIcon[MAX_PATH + 10];
  271. wnsprintf(szIcon, ARRAYSIZE(szIcon), TEXT("%s,-%d"), pszModule, IDI_MAIL);
  272. RegSetValueEx(hk, NULL, 0, REG_SZ, (LPBYTE)szIcon, (lstrlen(szIcon) + 1) * SIZEOF(TCHAR));
  273. RegCloseKey(hk);
  274. }
  275. // hide the exchange shortcut
  276. if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, EXCHANGE_EXTENSION)))
  277. SetFileAttributes(szFile, FILE_ATTRIBUTE_HIDDEN);
  278. }
  279. else
  280. {
  281. if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, SENDMAIL_EXTENSION)))
  282. DeleteFile(szFile);
  283. // unhide the exchange shortcut
  284. if (SUCCEEDED(GetDropTargetPath(szFile, IDS_MAIL_FILENAME, EXCHANGE_EXTENSION)))
  285. SetFileAttributes(szFile, FILE_ATTRIBUTE_NORMAL);
  286. }
  287. return S_OK;
  288. }