Leaked source code of windows server 2003
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.

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