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.

535 lines
14 KiB

  1. // File: mail.cpp
  2. #include "precomp.h"
  3. #include "resource.h"
  4. #include <mapi.h>
  5. #include <clinkid.h>
  6. #include <clink.h>
  7. #include "mail.h"
  8. #include "ConfUtil.h"
  9. typedef struct _tagMAIL_ADDRESS
  10. {
  11. LPTSTR pszAddress;
  12. LPTSTR pszDisplayName;
  13. } MAIL_ADDRESS, *LPMAIL_ADDRESS;
  14. // Send an e-mail message using the default mail provider
  15. HRESULT SendMailMessage(LPMAIL_ADDRESS pmaTo, LPCTSTR pcszSubject,
  16. LPCTSTR pcszText, LPCTSTR pcszFile);
  17. /* Given an existing Conference Shortcut, bring up a mail message with */
  18. /* it included as an attachment. The Conference Shortcut should have */
  19. /* been saved to disk prior to this call. */
  20. BOOL SendConfLinkMail(LPMAIL_ADDRESS pmaTo, IConferenceLink* pconflink, LPCTSTR pcszNoteText);
  21. /////////////////////////////////////////////////////////////////////////////////////
  22. // These variables are only used in this module, so we will make them static...
  23. // This keeps it out of the global namespace but more importantly it tells
  24. // the person reading this code that they don't have to worry about any othur
  25. // source file changing the variables directly...
  26. static HANDLE s_hSendMailThread = NULL;
  27. static const TCHAR s_cszWinIniMail[] = _TEXT("Mail");
  28. static const TCHAR s_cszWinIniMAPI[] = _TEXT("MAPI");
  29. // MAPISendMail:
  30. typedef ULONG (FAR PASCAL *LPMSM)(LHANDLE,ULONG,lpMapiMessage,FLAGS,ULONG);
  31. BOOL IsSimpleMAPIInstalled()
  32. {
  33. return (BOOL) GetProfileInt(s_cszWinIniMail, s_cszWinIniMAPI, 0);
  34. }
  35. BOOL CreateInvitationMail(LPCTSTR pszMailAddr, LPCTSTR pszMailName,
  36. LPCTSTR pcszName, LPCTSTR pcszAddress,
  37. DWORD dwTransport, BOOL fMissedYou)
  38. {
  39. BOOL bRet = FALSE;
  40. TCHAR szTempFile[MAX_PATH];
  41. WCHAR wszTempFile[MAX_PATH];
  42. ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
  43. ASSERT(IS_VALID_STRING_PTR(pcszAddress, CSTR));
  44. // password not supported yet
  45. // ASSERT(IS_VALID_STRING_PTR(pcszPassword, CSTR));
  46. LPTSTR pszFileName = NULL;
  47. if (0 == GetTempPath(MAX_PATH, szTempFile))
  48. {
  49. ERROR_OUT(("GetTempPath failed!"));
  50. return FALSE;
  51. }
  52. pszFileName = szTempFile + lstrlen(szTempFile);
  53. // the +3 is for null terminators
  54. // append the conference name and the shortcut extension to the temp directory
  55. if (((lstrlen(pcszName) + lstrlen(szTempFile) + lstrlen(g_cszConfLinkExt) + 3)
  56. > sizeof(szTempFile)) ||
  57. (0 == lstrcat(szTempFile, pcszName)) ||
  58. (0 == lstrcat(szTempFile, g_cszConfLinkExt)))
  59. {
  60. ERROR_OUT(("Could not create temp file name!"));
  61. return FALSE;
  62. }
  63. // Filter names to allow only legal filename characters
  64. SanitizeFileName(pszFileName);
  65. // convert to UNICODE because IPersistFile interface expects UNICODE
  66. if (0 == MultiByteToWideChar(CP_ACP,
  67. 0L,
  68. szTempFile,
  69. sizeof(szTempFile),
  70. wszTempFile,
  71. sizeof(wszTempFile)))
  72. {
  73. ERROR_OUT(("Could not create wide temp file string!"));
  74. return FALSE;
  75. }
  76. IUnknown* punk = NULL;
  77. // Create a ConfLink object - try to obtain an IUnknown pointer
  78. HRESULT hr = CoCreateInstance( CLSID_ConfLink,
  79. NULL,
  80. CLSCTX_INPROC_SERVER |
  81. CLSCTX_INPROC_HANDLER |
  82. CLSCTX_LOCAL_SERVER,
  83. IID_IUnknown,
  84. (LPVOID*) &punk);
  85. if (FAILED(hr))
  86. {
  87. ERROR_OUT(("CoCreateInstance ret %lx", (DWORD)hr));
  88. return FALSE;
  89. }
  90. ASSERT(IS_VALID_INTERFACE_PTR(punk, IUnknown));
  91. // Try to obtain a IConferenceLink pointer
  92. IConferenceLink* pcl = NULL;
  93. hr = punk->QueryInterface(IID_IConferenceLink, (LPVOID*) &pcl);
  94. if (SUCCEEDED(hr))
  95. {
  96. ASSERT(IS_VALID_INTERFACE_PTR(pcl, IConferenceLink));
  97. // Set the conference name and address
  98. pcl->SetAddress(pcszAddress);
  99. pcl->SetName(pcszName);
  100. pcl->SetTransport(dwTransport);
  101. pcl->SetCallFlags(CRPCF_DEFAULT);
  102. // Try to obtain a IPersistFile pointer
  103. IPersistFile* ppf = NULL;
  104. hr = punk->QueryInterface(IID_IPersistFile, (LPVOID*) &ppf);
  105. if (SUCCEEDED(hr))
  106. {
  107. ASSERT(IS_VALID_INTERFACE_PTR(ppf, IPersistFile));
  108. // Save the object using the filename generated above
  109. hr = ppf->Save(wszTempFile, TRUE);
  110. // Release the IPersistFile pointer
  111. ppf->Release();
  112. ppf = NULL;
  113. TCHAR szNoteText[512];
  114. if (fMissedYou)
  115. {
  116. TCHAR szFormat[MAX_PATH];
  117. if (FLoadString(IDS_MISSED_YOU_FORMAT, szFormat, CCHMAX(szFormat)))
  118. {
  119. RegEntry reULS(ISAPI_CLIENT_KEY, HKEY_CURRENT_USER);
  120. wsprintf(szNoteText, szFormat, reULS.GetString(REGVAL_ULS_NAME));
  121. }
  122. }
  123. else
  124. {
  125. FLoadString(IDS_SEND_MAIL_NOTE_TEXT, szNoteText, CCHMAX(szNoteText));
  126. }
  127. MAIL_ADDRESS maDestAddress;
  128. maDestAddress.pszAddress = (LPTSTR) pszMailAddr;
  129. maDestAddress.pszDisplayName = (LPTSTR) pszMailName;
  130. // Send it using MAPI
  131. bRet = SendConfLinkMail(&maDestAddress, pcl, szNoteText);
  132. }
  133. // Release the IConferenceLink pointer
  134. pcl->Release();
  135. pcl = NULL;
  136. }
  137. // Release the IUnknown pointer
  138. punk->Release();
  139. punk = NULL;
  140. return bRet;
  141. }
  142. // SendConfLinkMail creates a mail message using Simple MAPI and attaches one
  143. // file to it - a Conference Shortcut which is passed in via the IConferenceLink
  144. // interface pointer.
  145. BOOL SendConfLinkMail(LPMAIL_ADDRESS pmaTo, IConferenceLink* pconflink, LPCTSTR pcszNoteText)
  146. {
  147. ASSERT(IS_VALID_INTERFACE_PTR((PCIConferenceLink)pconflink, IConferenceLink));
  148. HRESULT hr = E_FAIL;
  149. // File
  150. TCHAR szFile[MAX_PATH];
  151. LPOLESTR pwszFile = NULL;
  152. IPersistFile* pPersistFile = NULL;
  153. if (SUCCEEDED(pconflink->QueryInterface(IID_IPersistFile,
  154. (LPVOID*) &pPersistFile)))
  155. {
  156. if (SUCCEEDED(pPersistFile->GetCurFile(&pwszFile)))
  157. {
  158. #ifndef _UNICODE
  159. WideCharToMultiByte(CP_ACP,
  160. 0L,
  161. pwszFile,
  162. -1,
  163. szFile,
  164. sizeof(szFile),
  165. NULL,
  166. NULL);
  167. // Free the string using the Shell Allocator
  168. LPMALLOC pMalloc = NULL;
  169. if (SUCCEEDED(SHGetMalloc(&pMalloc)))
  170. {
  171. pMalloc->Free(pwszFile);
  172. pwszFile = NULL;
  173. pMalloc->Release();
  174. pMalloc = NULL;
  175. }
  176. #else // ndef _UNICODE
  177. #error Unicode not handled here!
  178. #endif // ndef _UNICODE
  179. hr = SendMailMessage(pmaTo, NULL, pcszNoteText, szFile);
  180. // BUGBUG: need unique ret val for this case
  181. // BUGBUG: should we move error UI out of this function?
  182. if (FAILED(hr))
  183. {
  184. ::PostConfMsgBox(IDS_CANT_SEND_SENDMAIL_IN_PROGRESS);
  185. }
  186. }
  187. else
  188. {
  189. ERROR_OUT(("GetCurFile failed - can't send message!"));
  190. pPersistFile->Release();
  191. return FALSE;
  192. }
  193. pPersistFile->Release();
  194. }
  195. else
  196. {
  197. ERROR_OUT(("Did not get IPersistFile pointer - can't send message!"));
  198. return FALSE;
  199. }
  200. return SUCCEEDED(hr);
  201. }
  202. //
  203. // BEGIN STOLEN CODE FROM IE 3.0 (sendmail.c) -------------------------------
  204. //
  205. const TCHAR g_cszAthenaV1Name[] = _TEXT("Internet Mail and News");
  206. const TCHAR g_cszAthenaV2Name[] = _TEXT("Outlook Express");
  207. const TCHAR g_cszAthenaV1DLLPath[] = _TEXT("mailnews.dll");
  208. BOOL IsAthenaDefault()
  209. {
  210. TCHAR szAthena[80];
  211. LONG cb = ARRAY_ELEMENTS(szAthena);
  212. return (ERROR_SUCCESS == RegQueryValue(HKEY_LOCAL_MACHINE, REGVAL_IE_CLIENTS_MAIL, szAthena, &cb)) &&
  213. ((lstrcmpi(szAthena, g_cszAthenaV1Name) == 0) ||
  214. (lstrcmpi(szAthena, g_cszAthenaV2Name) == 0));
  215. }
  216. HMODULE LoadMailProvider()
  217. {
  218. TCHAR szMAPIDLL[MAX_PATH];
  219. if (IsAthenaDefault())
  220. {
  221. RegEntry reMailClient(REGVAL_IE_CLIENTS_MAIL, HKEY_LOCAL_MACHINE);
  222. PTSTR pszMailClient = reMailClient.GetString(NULL);
  223. if ((NULL != pszMailClient) && (_T('\0') != pszMailClient[0]))
  224. {
  225. reMailClient.MoveToSubKey(pszMailClient);
  226. PTSTR pszDllPath = reMailClient.GetString(REGVAL_MAIL_DLLPATH);
  227. if ((NULL == pszDllPath) || (_T('\0') == pszDllPath[0]))
  228. {
  229. pszDllPath = (PTSTR) g_cszAthenaV1DLLPath;
  230. }
  231. return ::LoadLibraryEx(pszDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
  232. }
  233. else
  234. {
  235. ERROR_OUT(("No e-mail client in registry but IsAthenaDefault() returned TRUE"));
  236. }
  237. }
  238. // read win.ini (bogus hu!) for mapi dll provider
  239. if (GetProfileString( TEXT("Mail"), TEXT("CMCDLLName32"), TEXT(""),
  240. szMAPIDLL, ARRAY_ELEMENTS(szMAPIDLL)) <= 0)
  241. lstrcpy(szMAPIDLL, TEXT("mapi32.dll"));
  242. return ::LoadLibraryEx(szMAPIDLL, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
  243. }
  244. typedef struct {
  245. TCHAR szToAddress[MAX_PATH];
  246. TCHAR szToDisplayName[MAX_PATH];
  247. TCHAR szSubject[MAX_PATH];
  248. TCHAR szText[MAX_PATH];
  249. TCHAR szFile[MAX_PATH];
  250. BOOL fDeleteFile;
  251. MapiMessage mm;
  252. MapiRecipDesc mrd;
  253. MapiFileDesc mfd;
  254. } MAPI_FILES;
  255. MAPI_FILES* _AllocMapiFiles(LPMAIL_ADDRESS pmaTo, LPCTSTR pcszSubject,
  256. LPCTSTR pcszText, LPCTSTR pcszFile, BOOL fDeleteFile)
  257. {
  258. MAPI_FILES* pmf = new MAPI_FILES;
  259. if (pmf)
  260. {
  261. ::ZeroMemory(pmf, sizeof(MAPI_FILES));
  262. pmf->fDeleteFile = fDeleteFile;
  263. if (NULL != pcszSubject)
  264. {
  265. lstrcpyn(pmf->szSubject, pcszSubject, CCHMAX(pmf->szSubject));
  266. }
  267. else
  268. {
  269. pmf->szSubject[0] = _T('\0');
  270. }
  271. if (NULL != pcszText)
  272. {
  273. lstrcpyn(pmf->szText, pcszText, CCHMAX(pmf->szText));
  274. }
  275. else
  276. {
  277. pmf->szText[0] = _T('\0');
  278. }
  279. pmf->mm.nFileCount = (NULL != pcszFile) ? 1 : 0;
  280. if (pmf->mm.nFileCount)
  281. {
  282. lstrcpyn(pmf->szFile, pcszFile, CCHMAX(pmf->szFile));
  283. pmf->mm.lpFiles = &(pmf->mfd);
  284. pmf->mfd.lpszPathName = pmf->szFile;
  285. pmf->mfd.lpszFileName = pmf->szFile;
  286. pmf->mfd.nPosition = (UINT)-1;
  287. }
  288. pmf->mm.lpszSubject = pmf->szSubject;
  289. pmf->mm.lpszNoteText = pmf->szText;
  290. if( ( NULL != pmaTo ) && !FEmptySz(pmaTo->pszAddress ) )
  291. {
  292. pmf->mm.lpRecips = &(pmf->mrd);
  293. pmf->mrd.ulRecipClass = MAPI_TO;
  294. pmf->mm.nRecipCount = 1;
  295. // If we're sending via Athena and a friendly name is specified,
  296. // we pass both the friendly name and address. If we're sending
  297. // via Simple MAPI, we pass just the address in the name field.
  298. // This is necessary so that the email client can do the address
  299. // resolution as appropriate for the installed mail system. This
  300. // is not necessary for Athena since it assumes that all addresses
  301. // are SMTP addresses.
  302. if (IsAthenaDefault()
  303. && NULL != pmaTo->pszDisplayName && _T('\0') != pmaTo->pszDisplayName[0])
  304. {
  305. lstrcpyn(
  306. pmf->szToDisplayName,
  307. pmaTo->pszDisplayName,
  308. CCHMAX(pmf->szToDisplayName));
  309. pmf->mrd.lpszName = pmf->szToDisplayName;
  310. lstrcpyn(
  311. pmf->szToAddress,
  312. pmaTo->pszAddress,
  313. CCHMAX(pmf->szToAddress));
  314. pmf->mrd.lpszAddress = pmf->szToAddress;
  315. }
  316. else
  317. {
  318. lstrcpyn(
  319. pmf->szToDisplayName,
  320. pmaTo->pszAddress,
  321. CCHMAX(pmf->szToDisplayName));
  322. pmf->mrd.lpszName = pmf->szToDisplayName;
  323. pmf->mrd.lpszAddress = NULL;
  324. }
  325. }
  326. else
  327. {
  328. // No recepients
  329. pmf->mm.lpRecips = NULL;
  330. }
  331. }
  332. return pmf;
  333. }
  334. VOID _FreeMapiFiles(MAPI_FILES *pmf)
  335. {
  336. if (pmf->fDeleteFile)
  337. {
  338. ::DeleteFile(pmf->szFile);
  339. }
  340. delete pmf;
  341. }
  342. STDAPI_(DWORD) MailRecipientThreadProc(LPVOID pv)
  343. {
  344. DebugEntry(MailRecipientThreadProc);
  345. MAPI_FILES* pmf = (MAPI_FILES*) pv;
  346. DWORD dwRet = S_OK;
  347. if (pmf)
  348. {
  349. HMODULE hmodMail = LoadMailProvider();
  350. if (hmodMail)
  351. {
  352. LPMSM pfnSendMail;
  353. if (pfnSendMail = (LPMSM) ::GetProcAddress(hmodMail, "MAPISendMail"))
  354. {
  355. dwRet = pfnSendMail(0, 0, &pmf->mm, MAPI_LOGON_UI | MAPI_DIALOG, 0);
  356. }
  357. ::FreeLibrary(hmodMail);
  358. }
  359. _FreeMapiFiles(pmf);
  360. }
  361. // s_hSendMailThread can't be NULL because we don't resume this thread
  362. // until s_hSendMailThread is set to a non-null value, so this is a sanity check
  363. ASSERT(s_hSendMailThread);
  364. HANDLE hThread = s_hSendMailThread;
  365. s_hSendMailThread = NULL;
  366. ::CloseHandle(hThread);
  367. DebugExitULONG(MailRecipientThreadProc, dwRet);
  368. return dwRet;
  369. }
  370. //
  371. // END STOLEN CODE FROM IE 3.0 (sendmail.c) ---------------------------------
  372. //
  373. VOID SendMailMsg(LPTSTR pszAddr, LPTSTR pszName)
  374. {
  375. // Create Send Mail structure to pass on
  376. MAIL_ADDRESS maDestAddress;
  377. maDestAddress.pszAddress = pszAddr;
  378. maDestAddress.pszDisplayName = pszName;
  379. // We are adding the callto://pszName link
  380. // to the body of the e-mail message
  381. TCHAR sz[MAX_PATH];
  382. USES_RES2T
  383. lstrcpy( sz, RES2T(IDS_NMCALLTOMAILTEXT) );
  384. lstrcat( sz, pszAddr );
  385. // Only send the text part if pszName is not a NULL string
  386. HRESULT hr = SendMailMessage(&maDestAddress, NULL, ( *pszAddr ) ? sz : NULL, NULL);
  387. if (FAILED(hr))
  388. {
  389. ::PostConfMsgBox(IDS_CANT_SEND_SENDMAIL_IN_PROGRESS);
  390. }
  391. }
  392. const int MESSAGE_THREAD_SHUTDOWN_TIMEOUT = 5000; // milliseconds
  393. HRESULT SendMailMessage(LPMAIL_ADDRESS pmaTo, LPCTSTR pcszSubject,
  394. LPCTSTR pcszText, LPCTSTR pcszFile)
  395. {
  396. DebugEntry(SendMailMessage);
  397. HRESULT hr = E_FAIL;
  398. if (NULL != s_hSendMailThread)
  399. {
  400. // Athena takes a while to get out of MAPISendMail after the message is closed,
  401. // so we wait around a few seconds in case you just finished sending a message..
  402. HCURSOR hCurPrev = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
  403. ::WaitForSingleObject(s_hSendMailThread, MESSAGE_THREAD_SHUTDOWN_TIMEOUT);
  404. ::SetCursor(hCurPrev);
  405. }
  406. if (NULL == s_hSendMailThread)
  407. {
  408. MAPI_FILES* pmf = _AllocMapiFiles( pmaTo, pcszSubject, pcszText,
  409. pcszFile, TRUE);
  410. if (NULL != pmf)
  411. {
  412. DWORD dwThreadID;
  413. // We create the thread suspended because in the thread fn
  414. // we call closehandle on s_hSendMailThread...if we create
  415. // the thread not suspended there is a race condition where
  416. // s_hSendMailThread may not have been assigned the return
  417. // value of CreateThread before it is checked in the thread fn
  418. s_hSendMailThread = ::CreateThread( NULL,
  419. 0,
  420. MailRecipientThreadProc,
  421. pmf,
  422. CREATE_SUSPENDED,
  423. &dwThreadID);
  424. // If the thread was created, we have to call Resume Thread...
  425. if( s_hSendMailThread )
  426. {
  427. if( 0xFFFFFFFF != ResumeThread( s_hSendMailThread ) )
  428. {
  429. hr = S_OK;
  430. }
  431. else
  432. {
  433. // This would indicate an error...
  434. hr = HRESULT_FROM_WIN32(GetLastError());
  435. }
  436. }
  437. else
  438. {
  439. hr = HRESULT_FROM_WIN32(GetLastError());
  440. }
  441. }
  442. }
  443. else
  444. {
  445. WARNING_OUT(("can't send mail - mail thread already in progress"));
  446. }
  447. DebugExitHRESULT(SendMailMessage, hr);
  448. return hr;
  449. }
  450. BOOL IsSendMailInProgress()
  451. {
  452. return (NULL != s_hSendMailThread);
  453. }