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.

804 lines
23 KiB

  1. /*
  2. * a t t a c h . c p p
  3. *
  4. * Purpose:
  5. * Attachment utilities
  6. *
  7. * History
  8. *
  9. * Copyright (C) Microsoft Corp. 1995, 1996.
  10. */
  11. #include <pch.hxx>
  12. #include "dllmain.h"
  13. #include "resource.h"
  14. #include "error.h"
  15. #include "mimeolep.h"
  16. #include "shellapi.h"
  17. #include "shlobj.h"
  18. #include "shlwapi.h"
  19. #include "shlwapip.h"
  20. #include "mimeole.h"
  21. #include "commdlg.h"
  22. #include "demand.h"
  23. #include "saferun.h"
  24. ASSERTDATA
  25. /*
  26. * t y p e d e f s
  27. */
  28. /*
  29. * m a c r o s
  30. */
  31. /*
  32. * c o n s t a n t s
  33. */
  34. #define MAX_CHARS_FOR_NUM 20
  35. static const CHAR c_szWebMark[] = "<!-- saved from url=(0022)http://internet.e-mail -->\r\n";
  36. static const WCHAR c_wszWebMark[] = L"<!-- saved from url=(0022)http://internet.e-mail -->\r\n";
  37. /*
  38. * g l o b a l s
  39. */
  40. /*
  41. * p r o t o t y p e s
  42. */
  43. HRESULT HrCleanTempFile(LPATTACHDATA pAttach);
  44. HRESULT HrGetTempFile(IMimeMessage *pMsg, LPATTACHDATA lpAttach);
  45. DWORD AthGetShortPathName(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer);
  46. STDAPI HrGetAttachIconByFile(LPWSTR szFilename, BOOL fLargeIcon, HICON *phIcon)
  47. {
  48. HICON hIcon = NULL;
  49. LPWSTR lpszExt;
  50. SHFILEINFOW rShFileInfo;
  51. if (szFilename)
  52. {
  53. // Does the file exist already ?
  54. if ((UINT)GetFileAttributesWrapW(szFilename) != (UINT)-1)
  55. {
  56. // Try to get the icon out of the file
  57. SHGetFileInfoWrapW(szFilename, 0, &rShFileInfo, sizeof(rShFileInfo), SHGFI_ICON |(fLargeIcon ? 0 : SHGFI_SMALLICON));
  58. hIcon=rShFileInfo.hIcon;
  59. }
  60. else
  61. if (lpszExt = PathFindExtensionW(szFilename))
  62. {
  63. // Lookup the icon for lpszExt
  64. SHGetFileInfoWrapW(lpszExt, FILE_ATTRIBUTE_NORMAL, &rShFileInfo, sizeof (rShFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | (fLargeIcon ? 0 : SHGFI_SMALLICON));
  65. hIcon=rShFileInfo.hIcon;
  66. }
  67. }
  68. if (!hIcon)
  69. hIcon = CopyIcon(LoadIcon (g_hLocRes, MAKEINTRESOURCE (idiDefaultAtt)));
  70. *phIcon=hIcon;
  71. return S_OK;
  72. }
  73. STDAPI HrGetAttachIcon(IMimeMessage *pMsg, HBODY hAttach, BOOL fLargeIcon, HICON *phIcon)
  74. {
  75. // Locals
  76. HRESULT hr = S_OK;
  77. LPWSTR lpszFile=0;
  78. if (!phIcon || !hAttach || !pMsg)
  79. return E_INVALIDARG;
  80. *phIcon=NULL;
  81. // Get file name for body part. If get an error, doesn't matter. Still use
  82. // a default icon that will be provided through HrGetAttachIconByFile
  83. MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &lpszFile);
  84. hr = HrGetAttachIconByFile(lpszFile, fLargeIcon, phIcon);
  85. SafeMemFree(lpszFile);
  86. return hr;
  87. }
  88. //
  89. // GetUIVersion()
  90. //
  91. // returns the version of shell32
  92. // 3 == win95 gold / NT4
  93. // 4 == IE4 Integ / win98
  94. // 5 == win2k / millennium
  95. //
  96. UINT GetUIVersion()
  97. {
  98. static UINT s_uiShell32 = 0;
  99. if (s_uiShell32 == 0)
  100. {
  101. HINSTANCE hinst = LoadLibrary("SHELL32.DLL");
  102. if (hinst)
  103. {
  104. DLLGETVERSIONPROC pfnGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
  105. DLLVERSIONINFO dllinfo;
  106. dllinfo.cbSize = sizeof(DLLVERSIONINFO);
  107. if (pfnGetVersion && pfnGetVersion(&dllinfo) == NOERROR)
  108. s_uiShell32 = dllinfo.dwMajorVersion;
  109. else
  110. s_uiShell32 = 3;
  111. FreeLibrary(hinst);
  112. }
  113. }
  114. return s_uiShell32;
  115. }
  116. STDAPI HrDoAttachmentVerb(HWND hwnd, ULONG uVerb, IMimeMessage *pMsg, LPATTACHDATA pAttach)
  117. {
  118. HRESULT hr = S_OK;
  119. SHELLEXECUTEINFOW rShellExec;
  120. LPSTREAM lpstmAtt = NULL;
  121. LPWSTR lpszShortName = NULL,
  122. lpszParameters = NULL,
  123. lpszExt = NULL;
  124. DWORD dwFlags;
  125. WCHAR szCommand[MAX_PATH];
  126. HKEY hKeyAssoc = NULL;
  127. AssertSz( uVerb < AV_MAX, "Bad Verb");
  128. if (uVerb == AV_PROPERTIES)
  129. {
  130. AssertSz(FALSE, "AV_PROPERTIES is NYI!!");
  131. return E_NOTIMPL;
  132. }
  133. if (!pAttach)
  134. return E_INVALIDARG;
  135. // Save As - much simpler case
  136. if (uVerb == AV_SAVEAS)
  137. {
  138. hr=HrSaveAttachmentAs(hwnd, pMsg, pAttach);
  139. goto error; // done
  140. }
  141. // If opening attachment, lets verify thsi
  142. if (uVerb == AV_OPEN)
  143. {
  144. // If nVerb is to open the attachment, lets verify that with the user
  145. hr = IsSafeToRun(hwnd, pAttach->szFileName, TRUE);
  146. if (hr == MIMEEDIT_E_USERCANCEL) // map mimeedit error to athena error
  147. hr = hrUserCancel;
  148. if (FAILED(hr)) // user doesn't want to do this
  149. goto error;
  150. if (hr == MIMEEDIT_S_SAVEFILE)
  151. {
  152. hr=HrSaveAttachmentAs(hwnd, pMsg, pAttach);
  153. // done
  154. goto error;
  155. }
  156. }
  157. // get temp file
  158. hr = HrGetTempFile(pMsg, pAttach);
  159. if (FAILED(hr))
  160. goto error;
  161. Assert(lstrlenW(pAttach->szTempFile));
  162. // Setup Shell Execute Info Struct
  163. ZeroMemory (&rShellExec, sizeof (rShellExec));
  164. rShellExec.cbSize = sizeof (rShellExec);
  165. rShellExec.fMask = SEE_MASK_NOCLOSEPROCESS;
  166. rShellExec.hwnd = hwnd;
  167. rShellExec.nShow = SW_SHOWNORMAL;
  168. // Verb
  169. if (uVerb == AV_OPEN)
  170. {
  171. // Were going to run the file
  172. hr = VerifyTrust(hwnd, pAttach->szFileName, pAttach->szTempFile);
  173. if (FAILED(hr))
  174. {
  175. hr = hrUserCancel;
  176. goto error;
  177. }
  178. StrCpyNW(szCommand, pAttach->szTempFile, ARRAYSIZE(szCommand));
  179. rShellExec.lpFile = szCommand;
  180. // If the file does not have an associated type, do an OpenAs.
  181. // We're not testing the result of AssocQueryKeyW because even if it fails,
  182. // we want to try to open the file.
  183. lpszExt = PathFindExtensionW(pAttach->szTempFile);
  184. AssocQueryKeyW(NULL, ASSOCKEY_CLASS, lpszExt, NULL, &hKeyAssoc);
  185. if((hKeyAssoc == NULL) && (GetUIVersion() != 5))
  186. rShellExec.lpVerb = L"OpenAs";
  187. else
  188. RegCloseKey(hKeyAssoc);
  189. }
  190. else if (uVerb == AV_PRINT)
  191. {
  192. StrCpyNW(szCommand, pAttach->szTempFile, ARRAYSIZE(szCommand));
  193. rShellExec.lpFile = szCommand;
  194. rShellExec.lpVerb = L"Print";
  195. }
  196. else if (uVerb == AV_QUICKVIEW)
  197. {
  198. UINT uiSysDirLen;
  199. const WCHAR c_szSubDir[] = L"\\VIEWERS\\QUIKVIEW.EXE";
  200. // Find out where the viewer lives
  201. uiSysDirLen = GetSystemDirectoryWrapW(szCommand, ARRAYSIZE(szCommand));
  202. if (0 == uiSysDirLen || uiSysDirLen >= ARRAYSIZE(szCommand) ||
  203. uiSysDirLen + ARRAYSIZE(c_szSubDir) > ARRAYSIZE(szCommand))
  204. {
  205. hr = E_FAIL;
  206. goto error;
  207. }
  208. StrCpyNW(szCommand + uiSysDirLen, c_szSubDir, ARRAYSIZE(szCommand) - uiSysDirLen);
  209. // Alloc for short file name
  210. ULONG cchShortName = MAX_PATH + lstrlenW(pAttach->szTempFile) + 1;
  211. if (!MemAlloc ((LPVOID *)&lpszShortName, cchShortName * sizeof (WCHAR)))
  212. {
  213. hr = E_OUTOFMEMORY;
  214. goto error;
  215. }
  216. // Alloc a string for the parameters
  217. ULONG cchParameters = 30 + cchShortName;
  218. if (!MemAlloc ((LPVOID *)&lpszParameters, cchParameters * sizeof (WCHAR)))
  219. {
  220. hr = E_OUTOFMEMORY;
  221. goto error;
  222. }
  223. // Get Short File Name
  224. if (0 == AthGetShortPathName(pAttach->szTempFile, lpszShortName, cchShortName))
  225. StrCpyNW(lpszShortName, pAttach->szTempFile, cchShortName);
  226. // Put together the paramenters for QVSTUB.EXE
  227. Assert(cchParameters > cchShortName);
  228. StrCpyNW(lpszParameters, L"-v -f:", cchParameters);
  229. StrCatBuffW(lpszParameters, lpszShortName, cchParameters);
  230. // Setup Shellexec
  231. rShellExec.lpParameters = lpszParameters;
  232. rShellExec.lpFile = szCommand;
  233. }
  234. else
  235. Assert (FALSE);
  236. if (fIsNT5()) // quoted paths cause problems downlevel
  237. PathQuoteSpacesW(szCommand);
  238. // Execute it - even if this fails, we handled it - it will give a good error
  239. ShellExecuteExWrapW(&rShellExec);
  240. pAttach->hProcess = rShellExec.hProcess;
  241. error:
  242. MemFree(lpszParameters);
  243. MemFree(lpszShortName);
  244. return hr;
  245. }
  246. DWORD AthGetShortPathName(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cchBuffer)
  247. {
  248. CHAR szShortPath[MAX_PATH*2]; // Each Unicode char might go multibyte
  249. LPSTR pszLongPath = NULL;
  250. DWORD result = 0;
  251. Assert(pwszLongPath);
  252. pszLongPath = PszToANSI(CP_ACP, pwszLongPath);
  253. if (pszLongPath)
  254. {
  255. result = GetShortPathName(pszLongPath, szShortPath, ARRAYSIZE(szShortPath));
  256. if (result && result <= ARRAYSIZE(szShortPath))
  257. {
  258. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szShortPath, lstrlen(szShortPath), pwszShortPath, cchBuffer);
  259. pwszShortPath[cchBuffer - 1] = 0;
  260. }
  261. MemFree(pszLongPath);
  262. }
  263. return result;
  264. }
  265. STDAPI HrAttachDataFromBodyPart(IMimeMessage *pMsg, HBODY hAttach, LPATTACHDATA *ppAttach)
  266. {
  267. LPATTACHDATA pAttach;
  268. LPWSTR pszW;
  269. IMimeBodyW *pBody;
  270. Assert (pMsg && ppAttach && hAttach);
  271. if (!MemAlloc((LPVOID *)&pAttach, sizeof(ATTACHDATA)))
  272. return E_OUTOFMEMORY;
  273. // fill in attachment data
  274. ZeroMemory(pAttach, sizeof(ATTACHDATA));
  275. if (pMsg->BindToObject(hAttach, IID_IMimeBodyW, (LPVOID *)&pBody)==S_OK)
  276. {
  277. if (pBody->GetDisplayNameW(&pszW)==S_OK)
  278. {
  279. StrCpyNW(pAttach->szDisplay, pszW, ARRAYSIZE(pAttach->szDisplay));
  280. MemFree(pszW);
  281. }
  282. ReleaseObj(pBody);
  283. }
  284. if (MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &pszW)==S_OK)
  285. {
  286. if(IsPlatformWinNT() != S_OK)
  287. {
  288. CHAR pszFile[MAX_PATH*2];
  289. DWORD cchSizeW = (lstrlenW(pszW) + 1);
  290. WideCharToMultiByte(CP_ACP, 0, pszW, -1, pszFile, ARRAYSIZE(pszFile), NULL, NULL);
  291. pszFile[ARRAYSIZE(pszFile) - 1] = '\0';
  292. MultiByteToWideChar(CP_ACP, 0, pszFile, -1, pszW, cchSizeW);
  293. pszW[cchSizeW - 1] = 0;
  294. CleanupFileNameInPlaceW(pszW);
  295. }
  296. StrCpyNW(pAttach->szFileName, pszW, ARRAYSIZE(pAttach->szFileName));
  297. MemFree(pszW);
  298. }
  299. SideAssert(HrGetAttachIcon(pMsg, hAttach, FALSE, &pAttach->hIcon)==S_OK);
  300. pAttach->hAttach = hAttach;
  301. pAttach->fSafe = (IsSafeToRun(NULL, pAttach->szFileName, FALSE) == MIMEEDIT_S_OPENFILE);
  302. *ppAttach = pAttach;
  303. return S_OK;
  304. }
  305. STDAPI HrAttachDataFromFile(IStream *pstm, LPWSTR pszFileName, LPATTACHDATA *ppAttach)
  306. {
  307. LPATTACHDATA pAttach=0;
  308. LPMIMEBODY pBody=0;
  309. HRESULT hr;
  310. int cFileNameLength;
  311. Assert(ppAttach);
  312. if (!MemAlloc((LPVOID *)&pAttach, sizeof(ATTACHDATA)))
  313. return E_OUTOFMEMORY;
  314. // fill in attachment data
  315. ZeroMemory(pAttach, sizeof(ATTACHDATA));
  316. HrGetDisplayNameWithSizeForFile(pszFileName, pAttach->szDisplay, ARRAYSIZE(pAttach->szDisplay));
  317. StrCpyNW(pAttach->szFileName, pszFileName, ARRAYSIZE(pAttach->szFileName));
  318. if (!pstm)
  319. {
  320. // for new attachments set tempfile to be the same as filename
  321. // note: if creating from an IStream then pszFileName is not a valid path
  322. // we can create a tempfile later
  323. StrCpyNW(pAttach->szTempFile, pszFileName, ARRAYSIZE(pAttach->szTempFile));
  324. }
  325. ReplaceInterface(pAttach->pstm, pstm);
  326. SideAssert(HrGetAttachIconByFile(pszFileName, FALSE, &pAttach->hIcon)==S_OK);
  327. if (ppAttach)
  328. *ppAttach=pAttach;
  329. return S_OK;
  330. }
  331. STDAPI HrFreeAttachData(LPATTACHDATA pAttach)
  332. {
  333. if (pAttach)
  334. {
  335. HrCleanTempFile(pAttach);
  336. ReleaseObj(pAttach->pstm);
  337. if (pAttach->hIcon)
  338. DestroyIcon(pAttach->hIcon);
  339. MemFree(pAttach);
  340. }
  341. return NOERROR;
  342. }
  343. HRESULT HrCleanTempFile(LPATTACHDATA pAttach)
  344. {
  345. if (pAttach)
  346. {
  347. if (*pAttach->szTempFile && pAttach->hAttach)
  348. {
  349. // we only clean the tempfile if hAttach != NULL. Otherwise we assume it's a new attachment
  350. // and szTempFile points to the source file
  351. // If the file was launched, don't delete the temp file if the process still has it open
  352. if (pAttach->hProcess)
  353. {
  354. if (WaitForSingleObject (pAttach->hProcess, 0) == WAIT_OBJECT_0)
  355. DeleteFileWrapW(pAttach->szTempFile);
  356. }
  357. else
  358. DeleteFileWrapW(pAttach->szTempFile);
  359. }
  360. *pAttach->szTempFile = NULL;
  361. pAttach->hProcess=NULL;
  362. }
  363. return NOERROR;
  364. }
  365. HRESULT HrAttachSafetyFromBodyPart(IMimeMessage *pMsg, HBODY hAttach, BOOL *pfSafe)
  366. {
  367. LPWSTR pszW = NULL;
  368. Assert (pMsg && pfSafe && hAttach);
  369. *pfSafe = FALSE;
  370. if (MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &pszW)==S_OK)
  371. {
  372. if(IsPlatformWinNT() != S_OK)
  373. {
  374. CHAR pszFile[MAX_PATH*2];
  375. DWORD cchSizeW = lstrlenW(pszW)+1;
  376. WideCharToMultiByte(CP_ACP, 0, pszW, -1, pszFile, ARRAYSIZE(pszFile), NULL, NULL);
  377. pszFile[ARRAYSIZE(pszFile) - 1] = 0;
  378. MultiByteToWideChar(CP_ACP, 0, pszFile, -1, pszW, cchSizeW);
  379. pszW[cchSizeW - 1] = 0;
  380. CleanupFileNameInPlaceW(pszW);
  381. }
  382. *pfSafe = (IsSafeToRun(NULL, pszW, FALSE) == MIMEEDIT_S_OPENFILE);
  383. MemFree(pszW);
  384. }
  385. return S_OK;
  386. }
  387. STDAPI HrGetDisplayNameWithSizeForFile(LPWSTR pszPathName, LPWSTR pszDisplayName, int cchMaxDisplayName)
  388. {
  389. HANDLE hFile;
  390. DWORD uFileSize;
  391. WCHAR szSize[MAX_CHARS_FOR_NUM+1+3],
  392. szBuff[MAX_CHARS_FOR_NUM+1];
  393. LPWSTR pszFileName = NULL,
  394. pszFirst;
  395. int iSizeLen = 0,
  396. iLenFirst;
  397. szSize[0] = L'\0';
  398. hFile = CreateFileWrapW(pszPathName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  399. if (INVALID_HANDLE_VALUE != hFile)
  400. {
  401. uFileSize = GetFileSize(hFile, NULL);
  402. CloseHandle(hFile);
  403. if ((uFileSize != 0xffffffff) && StrFormatByteSizeW(uFileSize, szBuff, ARRAYSIZE(szBuff) - 1))
  404. {
  405. StrCpyNW(szSize, L" (", ARRAYSIZE(szSize));
  406. StrCatBuffW(szSize, szBuff, ARRAYSIZE(szSize));
  407. StrCatBuffW(szSize, L")", ARRAYSIZE(szSize));
  408. }
  409. }
  410. pszFileName = PathFindFileNameW(pszPathName);
  411. iSizeLen = lstrlenW(szSize);
  412. if (pszFileName)
  413. pszFirst = pszFileName;
  414. else
  415. pszFirst = pszPathName;
  416. iLenFirst = lstrlenW(pszFirst);
  417. StrCpyNW(pszDisplayName, pszFirst, cchMaxDisplayName);
  418. if (iLenFirst + iSizeLen + 1 > cchMaxDisplayName)
  419. return E_FAIL;
  420. StrCpyNW(pszDisplayName + iLenFirst, szSize, cchMaxDisplayName - iLenFirst);
  421. return S_OK;
  422. }
  423. STDAPI HrSaveAttachmentAs(HWND hwnd, IMimeMessage *pMsg, LPATTACHDATA lpAttach)
  424. {
  425. HRESULT hr = S_OK;
  426. OPENFILENAMEW ofn;
  427. WCHAR szTitle[CCHMAX_STRINGRES],
  428. szFilter[CCHMAX_STRINGRES],
  429. szFile[MAX_PATH];
  430. *szFile=0;
  431. *szFilter=0;
  432. *szTitle=0;
  433. Assert (lpAttach->szFileName);
  434. if (lpAttach->szFileName)
  435. StrCpyNW(szFile, lpAttach->szFileName, ARRAYSIZE(szFile));
  436. ZeroMemory (&ofn, sizeof(ofn));
  437. ofn.lStructSize = sizeof(ofn);
  438. ofn.hwndOwner = hwnd;
  439. LoadStringWrapW(g_hLocRes, idsFilterAttSave, szFilter, ARRAYSIZE(szFilter));
  440. ReplaceCharsW(szFilter, L'|', L'\0');
  441. ofn.lpstrFilter = szFilter;
  442. ofn.nFilterIndex = 1;
  443. ofn.lpstrFile = szFile;
  444. ofn.nMaxFile = ARRAYSIZE(szFile);
  445. LoadStringWrapW(g_hLocRes, idsSaveAttachmentAs, szTitle, ARRAYSIZE(szTitle));
  446. ofn.lpstrTitle = szTitle;
  447. ofn.Flags = OFN_NOCHANGEDIR | OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  448. // Show SaveAs Dialog
  449. if (HrAthGetFileNameW(&ofn, FALSE) != S_OK)
  450. {
  451. hr = hrUserCancel;
  452. goto error;
  453. }
  454. if (lpAttach->hAttach == NULL)
  455. {
  456. if (!PathFileExistsW(lpAttach->szFileName))
  457. {
  458. hr = hrNotFound;
  459. goto error;
  460. }
  461. // if hAttach == NULL then try and copy the file
  462. CopyFileWrapW(lpAttach->szFileName, szFile, TRUE);
  463. }
  464. else
  465. {
  466. // Verify the Attachment's Stream
  467. hr=HrSaveAttachToFile(pMsg, lpAttach->hAttach, szFile);
  468. if (FAILED(hr))
  469. goto error;
  470. }
  471. error:
  472. return hr;
  473. }
  474. HRESULT HrGetTempFile(IMimeMessage *pMsg, LPATTACHDATA lpAttach)
  475. {
  476. HRESULT hr;
  477. if (*lpAttach->szTempFile)
  478. return S_OK;
  479. if (!FBuildTempPathW(lpAttach->szFileName, lpAttach->szTempFile, ARRAYSIZE(lpAttach->szTempFile), FALSE))
  480. {
  481. hr = E_FAIL;
  482. goto error;
  483. }
  484. if (lpAttach->hAttach)
  485. {
  486. hr=HrSaveAttachToFile(pMsg, lpAttach->hAttach, lpAttach->szTempFile);
  487. if (FAILED(hr))
  488. goto error;
  489. }
  490. else
  491. {
  492. AssertSz(lpAttach->pstm, "if no hAttach then pstm should be set");
  493. hr = WriteStreamToFileW(lpAttach->pstm, lpAttach->szTempFile, CREATE_ALWAYS, GENERIC_WRITE);
  494. if (FAILED(hr))
  495. goto error;
  496. }
  497. error:
  498. if (FAILED(hr))
  499. {
  500. // Null out temp file as we didn't really create it
  501. *(lpAttach->szTempFile)=0;
  502. }
  503. return hr;
  504. }
  505. const BYTE c_rgbUTF[3] = {0xEF, 0xBB, 0xBF};
  506. STDAPI HrSaveAttachToFile(IMimeMessage *pMsg, HBODY hAttach, LPWSTR lpszFileName)
  507. {
  508. HRESULT hr;
  509. LPSTREAM pstm=NULL,
  510. pstmOut=NULL;
  511. BOOL fEndian,
  512. fUTFEncoded = FALSE;
  513. BYTE rgbBOM[2];
  514. BYTE rgbUTF[3];
  515. DWORD cbRead;
  516. if (pMsg == NULL || hAttach == NULL)
  517. IF_FAILEXIT(hr = E_INVALIDARG);
  518. // bind to the attachment data
  519. IF_FAILEXIT(hr=pMsg->BindToObject(hAttach, IID_IStream, (LPVOID *)&pstm));
  520. // create a file stream
  521. IF_FAILEXIT(hr = OpenFileStreamW(lpszFileName, CREATE_ALWAYS, GENERIC_WRITE, &pstmOut));
  522. // if we have an .HTM file, then pre-pend 'mark of the web' comment
  523. // If we are a unicode file, the BOM will be "0xFFFE"
  524. // If we are a UTF8 file, the BOM will be "0xEFBBBF"
  525. if (PathIsHTMLFileW(lpszFileName))
  526. {
  527. if (S_OK == HrIsStreamUnicode(pstm, &fEndian))
  528. {
  529. // Don't rewind otherwise end up with BOM after 'mark of the web' as well.
  530. IF_FAILEXIT(hr = pstm->Read(rgbBOM, sizeof(rgbBOM), &cbRead));
  531. // Since HrIsStreamUnicode succeeded, there should be at least two
  532. Assert(sizeof(rgbBOM) == cbRead);
  533. IF_FAILEXIT(hr = pstmOut->Write(rgbBOM, cbRead, NULL));
  534. IF_FAILEXIT(hr = pstmOut->Write(c_wszWebMark, sizeof(c_wszWebMark)-sizeof(WCHAR), NULL));
  535. }
  536. else
  537. {
  538. // Check for UTF8 file BOM
  539. IF_FAILEXIT(hr = pstm->Read(rgbUTF, sizeof(rgbUTF), &cbRead));
  540. if (sizeof(rgbUTF) == cbRead)
  541. {
  542. fUTFEncoded = (0 == memcmp(c_rgbUTF, rgbUTF, sizeof(rgbUTF)));
  543. }
  544. // If we are not UTF8 encoded, then rewind, else write out BOM
  545. if (!fUTFEncoded)
  546. IF_FAILEXIT(hr = HrRewindStream(pstm));
  547. else
  548. IF_FAILEXIT(hr = pstmOut->Write(c_rgbUTF, sizeof(c_rgbUTF), NULL));
  549. IF_FAILEXIT(hr = pstmOut->Write(c_szWebMark, sizeof(c_szWebMark)-sizeof(CHAR), NULL));
  550. }
  551. }
  552. // write out the actual file data
  553. IF_FAILEXIT(hr = HrCopyStream(pstm, pstmOut, NULL));
  554. exit:
  555. ReleaseObj(pstm);
  556. ReleaseObj(pstmOut);
  557. return hr;
  558. }
  559. /*
  560. * Why?
  561. *
  562. * we wrap this as if we don't use NOCHANGEDIR, then things like ShellExec
  563. * fail if the current directory is nolonger valid. Eg: attach a file from a:\
  564. * and remove the floppy. Then all ShellExec's fail. So we maintain our own
  565. * last directory buffer. We should use thread-local for this, as it is possible
  566. * that two thread could whack the same buffer, it is unlikley that a user action
  567. * could cause two open file dialogs in the note and the view to open at exactly the
  568. * same time.
  569. */
  570. WCHAR g_wszLastDir[MAX_PATH];
  571. HRESULT SetDefaultSpecialFolderPath()
  572. {
  573. LPITEMIDLIST pidl = NULL;
  574. HRESULT hr = E_FAIL;
  575. if (SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl)==S_OK)
  576. {
  577. if (SHGetPathFromIDListWrapW(pidl, g_wszLastDir))
  578. hr = S_OK;
  579. SHFree(pidl);
  580. }
  581. return hr;
  582. }
  583. STDAPI HrAthGetFileName(OPENFILENAME *pofn, BOOL fOpen)
  584. {
  585. BOOL fRet;
  586. LPSTR pszDir = NULL;
  587. HRESULT hr = S_OK;
  588. Assert(pofn != NULL);
  589. // force NOCHANGEDIR
  590. pofn->Flags |= OFN_NOCHANGEDIR;
  591. if (pofn->lpstrInitialDir == NULL)
  592. {
  593. if (!PathFileExistsW(g_wszLastDir))
  594. SideAssert(SetDefaultSpecialFolderPath()==S_OK);
  595. IF_NULLEXIT(pszDir = PszToANSI(CP_ACP, g_wszLastDir));
  596. pofn->lpstrInitialDir = pszDir;
  597. }
  598. if (fOpen)
  599. fRet = GetOpenFileName(pofn);
  600. else
  601. fRet = GetSaveFileName(pofn);
  602. if (fRet)
  603. {
  604. // store the last path
  605. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pofn->lpstrFile, lstrlen(pofn->lpstrFile), g_wszLastDir, ARRAYSIZE(g_wszLastDir));
  606. if (!PathIsDirectoryW(g_wszLastDir))
  607. PathRemoveFileSpecW(g_wszLastDir);
  608. }
  609. else
  610. TraceResult(hr = E_FAIL);
  611. exit:
  612. MemFree(pszDir);
  613. return hr;
  614. }
  615. STDAPI HrAthGetFileNameW(OPENFILENAMEW *pofn, BOOL fOpen)
  616. {
  617. BOOL fRet;
  618. Assert(pofn != NULL);
  619. // force NOCHANGEDIR
  620. pofn->Flags |= OFN_NOCHANGEDIR;
  621. if (pofn->lpstrInitialDir == NULL)
  622. {
  623. if (!PathFileExistsW(g_wszLastDir))
  624. SideAssert(SetDefaultSpecialFolderPath()==S_OK);
  625. pofn->lpstrInitialDir = g_wszLastDir;
  626. }
  627. if (fOpen)
  628. fRet = GetOpenFileNameWrapW(pofn);
  629. else
  630. fRet = GetSaveFileNameWrapW(pofn);
  631. if (fRet)
  632. {
  633. // store the last path
  634. StrCpyNW(g_wszLastDir, pofn->lpstrFile, ARRAYSIZE(g_wszLastDir));
  635. if (!PathIsDirectoryW(g_wszLastDir))
  636. PathRemoveFileSpecW(g_wszLastDir);
  637. }
  638. return fRet?S_OK:E_FAIL;
  639. }
  640. STDAPI HrGetLastOpenFileDirectory(int cchMax, LPSTR lpsz)
  641. {
  642. if (!PathFileExistsW(g_wszLastDir))
  643. SideAssert(SetDefaultSpecialFolderPath()==S_OK);
  644. WideCharToMultiByte(CP_ACP, 0, g_wszLastDir, lstrlenW(g_wszLastDir), lpsz, cchMax, NULL, NULL);
  645. lpsz[cchMax - 1] = 0;
  646. return S_OK;
  647. }
  648. STDAPI HrGetLastOpenFileDirectoryW(int cchMax, LPWSTR lpsz)
  649. {
  650. if (!PathFileExistsW(g_wszLastDir))
  651. SideAssert(SetDefaultSpecialFolderPath()==S_OK);
  652. StrCpyNW(lpsz, g_wszLastDir, cchMax);
  653. return S_OK;
  654. }