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.

1099 lines
37 KiB

  1. #include "precomp.h" // pch file
  2. #include "sendto.h"
  3. #pragma hdrstop
  4. CLIPFORMAT g_cfShellURL = 0;
  5. CLIPFORMAT g_cfFileContents = 0;
  6. CLIPFORMAT g_cfFileDescA = 0;
  7. CLIPFORMAT g_cfFileDescW = 0;
  8. CLIPFORMAT g_cfHIDA = 0;
  9. // registry key for recompressing settings
  10. struct
  11. {
  12. int cx;
  13. int cy;
  14. int iQuality;
  15. }
  16. _aQuality[] =
  17. {
  18. { 640, 480, 80 }, // low quality
  19. { 800, 600, 80 }, // medium quality
  20. { 1024, 768, 80 }, // high quality
  21. };
  22. #define QUALITY_LOW 0
  23. #define QUALITY_MEDIUM 1
  24. #define QUALITY_HIGH 2
  25. #define RESPONSE_UNKNOWN 0
  26. #define RESPONSE_CANCEL 1
  27. #define RESPONSE_ORIGINAL 2
  28. #define RESPONSE_RECOMPRESS 3
  29. #define RECTWIDTH(rc) ((rc).right-(rc).left)
  30. #define RECTHEIGHT(rc) ((rc).bottom-(rc).top)
  31. // these bits are set by the user (holding down the keys) durring drag drop,
  32. // but more importantly, they are set in the SimulateDragDrop() call that the
  33. // browser implements to get the "Send Page..." vs "Send Link..." feature
  34. #define IS_FORCE_LINK(grfKeyState) ((grfKeyState == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) || \
  35. (grfKeyState == (MK_LBUTTON | MK_ALT)))
  36. #define IS_FORCE_COPY(grfKeyState) (grfKeyState == (MK_LBUTTON | MK_CONTROL))
  37. // constructor / destructor
  38. CSendTo::CSendTo(CLSID clsid) :
  39. _clsid(clsid), _cRef(1), _iRecompSetting(QUALITY_LOW)
  40. {
  41. DllAddRef();
  42. }
  43. CSendTo::~CSendTo()
  44. {
  45. if (_pStorageTemp)
  46. _pStorageTemp->Release();
  47. DllRelease();
  48. }
  49. STDMETHODIMP CSendTo::QueryInterface(REFIID riid, void **ppv)
  50. {
  51. static const QITAB qit[] =
  52. {
  53. QITABENT(CSendTo, IShellExtInit),
  54. QITABENT(CSendTo, IDropTarget),
  55. QITABENT(CSendTo, IPersistFile),
  56. { 0 },
  57. };
  58. return QISearch(this, qit, riid, ppv);
  59. }
  60. STDMETHODIMP_(ULONG) CSendTo::AddRef()
  61. {
  62. return InterlockedIncrement(&_cRef);
  63. }
  64. STDMETHODIMP_(ULONG) CSendTo::Release()
  65. {
  66. ASSERT( 0 != _cRef );
  67. ULONG cRef = InterlockedDecrement(&_cRef);
  68. if ( 0 == cRef )
  69. {
  70. delete this;
  71. }
  72. return cRef;
  73. }
  74. STDMETHODIMP CSendTo::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  75. {
  76. TraceMsg(DM_TRACE, "CSendTo::DragEnter");
  77. _grfKeyStateLast = grfKeyState;
  78. _dwEffectLast = *pdwEffect;
  79. return S_OK;
  80. }
  81. STDMETHODIMP CSendTo::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  82. {
  83. *pdwEffect &= ~DROPEFFECT_MOVE;
  84. if (IS_FORCE_COPY(grfKeyState))
  85. *pdwEffect &= DROPEFFECT_COPY;
  86. else if (IS_FORCE_LINK(grfKeyState))
  87. *pdwEffect &= DROPEFFECT_LINK;
  88. _grfKeyStateLast = grfKeyState;
  89. _dwEffectLast = *pdwEffect;
  90. return S_OK;
  91. }
  92. STDMETHODIMP CSendTo::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  93. {
  94. HRESULT hr = v_DropHandler(pdtobj, _grfKeyStateLast, _dwEffectLast);
  95. *pdwEffect = DROPEFFECT_COPY; // don't let source delete data
  96. return hr;
  97. }
  98. //
  99. // helper methods
  100. //
  101. int CSendTo::_PathCleanupSpec(/*IN OPTIONAL*/ LPCTSTR pszDir, /*IN OUT*/ LPTSTR pszSpec)
  102. {
  103. WCHAR wzDir[MAX_PATH];
  104. WCHAR wzSpec[MAX_PATH];
  105. LPWSTR pwszDir = wzDir;
  106. int iRet;
  107. if (pszDir)
  108. SHTCharToUnicode(pszDir, wzDir, ARRAYSIZE(wzDir));
  109. else
  110. pwszDir = NULL;
  111. SHTCharToUnicode(pszSpec, wzSpec, ARRAYSIZE(wzSpec));
  112. iRet = PathCleanupSpec((LPTSTR)pwszDir, (LPTSTR)wzSpec);
  113. SHUnicodeToTChar(wzSpec, pszSpec, MAX_PATH);
  114. return iRet;
  115. }
  116. HRESULT CSendTo::_CreateShortcutToPath(LPCTSTR pszPath, LPCTSTR pszTarget)
  117. {
  118. IUnknown *punk;
  119. HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk));
  120. if (SUCCEEDED(hr))
  121. {
  122. ShellLinkSetPath(punk, pszTarget);
  123. IPersistFile *ppf;
  124. hr = punk->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  125. if (SUCCEEDED(hr))
  126. {
  127. WCHAR wszPath[MAX_PATH];
  128. SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
  129. hr = ppf->Save(wszPath, TRUE);
  130. ppf->Release();
  131. }
  132. punk->Release();
  133. }
  134. return hr;
  135. }
  136. // thunk A/W funciton to access A/W FILEGROUPDESCRIPTOR
  137. // this relies on the fact that the first part of the A/W structures are
  138. // identical. only the string buffer part is different. so all accesses to the
  139. // cFileName field need to go through this function.
  140. FILEDESCRIPTOR* CSendTo::_GetFileDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, int nIndex, LPTSTR pszName)
  141. {
  142. if (fUnicode)
  143. {
  144. // Yes, so grab the data because it matches.
  145. FILEGROUPDESCRIPTORW * pfgdW = (FILEGROUPDESCRIPTORW *)pfgd; // cast to what this really is
  146. if (pszName)
  147. SHUnicodeToTChar(pfgdW->fgd[nIndex].cFileName, pszName, MAX_PATH);
  148. return (FILEDESCRIPTOR *)&pfgdW->fgd[nIndex]; // cast assume the non string parts are the same!
  149. }
  150. else
  151. {
  152. FILEGROUPDESCRIPTORA *pfgdA = (FILEGROUPDESCRIPTORA *)pfgd; // cast to what this really is
  153. if (pszName)
  154. SHAnsiToTChar(pfgdA->fgd[nIndex].cFileName, pszName, MAX_PATH);
  155. return (FILEDESCRIPTOR *)&pfgdA->fgd[nIndex]; // cast assume the non string parts are the same!
  156. }
  157. }
  158. // our own impl since URLMON IStream::CopyTo is busted, danpoz will be fixing this
  159. HRESULT CSendTo::_StreamCopyTo(IStream *pstmFrom, IStream *pstmTo, LARGE_INTEGER cb, LARGE_INTEGER *pcbRead, LARGE_INTEGER *pcbWritten)
  160. {
  161. BYTE buf[512];
  162. ULONG cbRead;
  163. HRESULT hr = S_OK;
  164. if (pcbRead)
  165. {
  166. pcbRead->LowPart = 0;
  167. pcbRead->HighPart = 0;
  168. }
  169. if (pcbWritten)
  170. {
  171. pcbWritten->LowPart = 0;
  172. pcbWritten->HighPart = 0;
  173. }
  174. ASSERT(cb.HighPart == 0);
  175. while (cb.LowPart)
  176. {
  177. hr = pstmFrom->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
  178. if (pcbRead)
  179. pcbRead->LowPart += cbRead;
  180. if (FAILED(hr) || (cbRead == 0))
  181. break;
  182. cb.LowPart -= cbRead;
  183. hr = pstmTo->Write(buf, cbRead, &cbRead);
  184. if (pcbWritten)
  185. pcbWritten->LowPart += cbRead;
  186. if (FAILED(hr) || (cbRead == 0))
  187. break;
  188. }
  189. return hr;
  190. }
  191. // create a temporary shortcut to a file
  192. // FEATURE: Colision is not handled here
  193. BOOL CSendTo::_CreateTempFileShortcut(LPCTSTR pszTarget, LPTSTR pszShortcut, int cchShortcut)
  194. {
  195. TCHAR szShortcutPath[MAX_PATH + 1];
  196. BOOL bSuccess = FALSE;
  197. if (GetTempPath(ARRAYSIZE(szShortcutPath), szShortcutPath))
  198. {
  199. PathAppend(szShortcutPath, PathFindFileName(pszTarget));
  200. if (IsShortcut(pszTarget))
  201. {
  202. TCHAR szTarget[MAX_PATH + 1];
  203. SHFILEOPSTRUCT shop = {0};
  204. shop.wFunc = FO_COPY;
  205. shop.pFrom = szTarget;
  206. shop.pTo = szShortcutPath;
  207. shop.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
  208. StrCpyN(szTarget, pszTarget, ARRAYSIZE(szTarget));
  209. szTarget[lstrlen(szTarget) + 1] = TEXT('\0');
  210. szShortcutPath[lstrlen(szShortcutPath) + 1] = TEXT('\0');
  211. bSuccess = (0 == SHFileOperation(&shop));
  212. }
  213. else
  214. {
  215. PathRenameExtension(szShortcutPath, TEXT(".lnk"));
  216. bSuccess = SUCCEEDED(_CreateShortcutToPath(szShortcutPath, pszTarget));
  217. }
  218. if (bSuccess)
  219. StrCpyN(pszShortcut, szShortcutPath, cchShortcut);
  220. }
  221. return bSuccess;
  222. }
  223. HRESULT CSendTo::_GetFileNameFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, LPTSTR pszDescription, int cchDescription)
  224. {
  225. STGMEDIUM medium;
  226. HRESULT hr = pdtobj->GetData(pfmtetc, &medium);
  227. if (SUCCEEDED(hr))
  228. {
  229. // NOTE: this is a TCHAR format, we depend on how we are compiled, we really
  230. // should test both the A and W formats
  231. FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(medium.hGlobal);
  232. if (pfgd)
  233. {
  234. TCHAR szFdName[MAX_PATH]; // pfd->cFileName
  235. FILEDESCRIPTOR *pfd;
  236. // &pfgd->fgd[0], w/ thunk
  237. ASSERT(pfmtetc->cfFormat == g_cfFileDescW
  238. || pfmtetc->cfFormat == g_cfFileDescA);
  239. // for now, all callers are ANSI (other untested)
  240. //ASSERT(pfmtetc->cfFormat == g_cfFileDescA);
  241. pfd = _GetFileDescriptor(pfgd, pfmtetc->cfFormat == g_cfFileDescW, 0, szFdName);
  242. StrCpyN(pszDescription, szFdName, cchDescription); // pfd->cFileName
  243. GlobalUnlock(medium.hGlobal);
  244. hr = S_OK;
  245. }
  246. ReleaseStgMedium(&medium);
  247. }
  248. return hr;
  249. }
  250. // construct a nice title "<File Name> (<File Type>)"
  251. void CSendTo::_GetFileAndTypeDescFromPath(LPCTSTR pszPath, LPTSTR pszDesc, int cchDesc)
  252. {
  253. SHFILEINFO sfi;
  254. if (!SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME | SHGFI_DISPLAYNAME))
  255. {
  256. StrCpyN(sfi.szDisplayName, PathFindFileName(pszPath), ARRAYSIZE(sfi.szDisplayName));
  257. sfi.szTypeName[0] = 0;
  258. }
  259. StrCpyN(pszDesc, sfi.szDisplayName, cchDesc);
  260. }
  261. // pcszURL -> "ftp://ftp.microsoft.com"
  262. // pcszPath -> "c:\windows\desktop\internet\Microsoft FTP.url"
  263. HRESULT CSendTo::_CreateNewURLShortcut(LPCTSTR pcszURL, LPCTSTR pcszURLFile)
  264. {
  265. IUniformResourceLocator *purl;
  266. HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUniformResourceLocator, &purl));
  267. if (SUCCEEDED(hr))
  268. {
  269. hr = purl->SetURL(pcszURL, 0);
  270. if (SUCCEEDED(hr))
  271. {
  272. IPersistFile *ppf;
  273. hr = purl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  274. if (SUCCEEDED(hr))
  275. {
  276. WCHAR wszFile[INTERNET_MAX_URL_LENGTH];
  277. SHTCharToUnicode(pcszURLFile, wszFile, ARRAYSIZE(wszFile));
  278. hr = ppf->Save(wszFile, TRUE);
  279. ppf->Release();
  280. }
  281. }
  282. purl->Release();
  283. }
  284. return hr;
  285. }
  286. HRESULT CSendTo::_CreateURLFileToSend(IDataObject *pdtobj, MRPARAM *pmp)
  287. {
  288. MRFILEENTRY *pFile = pmp->pFiles;
  289. HRESULT hr = CSendTo::_CreateNewURLShortcut(pFile->pszTitle, pFile->pszFileName);
  290. if (SUCCEEDED(hr))
  291. {
  292. _GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
  293. pFile->dwFlags |= MRFILE_DELETE;
  294. }
  295. return hr;
  296. }
  297. HRESULT CSendTo::_GetHDROPFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
  298. {
  299. HRESULT hr = E_FAIL;
  300. HDROP hDrop = (HDROP)pmedium->hGlobal;
  301. pmp->nFiles = DragQueryFile(hDrop, -1, NULL, 0);
  302. if (pmp->nFiles && AllocatePMP(pmp, MAX_PATH, MAX_PATH))
  303. {
  304. int i;
  305. CFileEnum MREnum(pmp, NULL);
  306. MRFILEENTRY *pFile;
  307. for (i = 0; (pFile = MREnum.Next()) && DragQueryFile(hDrop, i, pFile->pszFileName, pmp->cchFile); ++i)
  308. {
  309. if (IS_FORCE_LINK(grfKeyState) || PathIsDirectory(pFile->pszFileName))
  310. {
  311. // Want to send a link even for the real file, we will create links to the real files
  312. // and send it.
  313. _CreateTempFileShortcut(pFile->pszFileName, pFile->pszFileName, pmp->cchFile);
  314. pFile->dwFlags |= MRFILE_DELETE;
  315. }
  316. _GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
  317. }
  318. // If loop terminates early update our item count
  319. pmp->nFiles = i;
  320. hr = S_OK;
  321. }
  322. return hr;
  323. }
  324. // "Uniform Resource Locator" format
  325. HRESULT CSendTo::_GetURLFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
  326. {
  327. HRESULT hr = E_FAIL;
  328. // This DataObj is from the internet
  329. // NOTE: We only allow to send one file here.
  330. pmp->nFiles = 1;
  331. if (AllocatePMP(pmp, INTERNET_MAX_URL_LENGTH, MAX_PATH))
  332. {
  333. // n.b. STR not TSTR! since URLs only support ansi
  334. //lstrcpyn(pmp->pszTitle, (LPSTR)GlobalLock(pmedium->hGlobal), INTERNET_MAX_URL_LENGTH);
  335. MRFILEENTRY *pFile = pmp->pFiles;
  336. SHAnsiToTChar((LPSTR)GlobalLock(pmedium->hGlobal), pFile->pszTitle, INTERNET_MAX_URL_LENGTH);
  337. GlobalUnlock(pmedium->hGlobal);
  338. if (pFile->pszTitle[0])
  339. {
  340. // Note some of these functions depend on which OS we
  341. // are running on to know if we should pass ansi or unicode strings
  342. // to it Windows 95
  343. if (GetTempPath(MAX_PATH, pFile->pszFileName))
  344. {
  345. TCHAR szFileName[MAX_PATH];
  346. // it's an URL, which is always ANSI, but the filename
  347. // can still be wide (?)
  348. FORMATETC fmteW = {g_cfFileDescW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  349. FORMATETC fmteA = {g_cfFileDescA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  350. if (FAILED(_GetFileNameFromData(pdtobj, &fmteW, szFileName, ARRAYSIZE(szFileName))))
  351. {
  352. if (FAILED(_GetFileNameFromData(pdtobj, &fmteA, szFileName, ARRAYSIZE(szFileName))))
  353. {
  354. LoadString(g_hinst, IDS_SENDMAIL_URL_FILENAME, szFileName, ARRAYSIZE(szFileName));
  355. }
  356. }
  357. _PathCleanupSpec(pFile->pszFileName, szFileName);
  358. hr = _CreateURLFileToSend(pdtobj, pmp);
  359. }
  360. }
  361. }
  362. return hr;
  363. }
  364. // transfer FILECONTENTS/FILEGROUPDESCRIPTOR data to a temp file then send that in mail
  365. HRESULT CSendTo::_GetFileContentsFromData(IDataObject *pdtobj, FORMATETC *pfmtetc, STGMEDIUM *pmedium, DWORD grfKeyState, MRPARAM *pmp)
  366. {
  367. HRESULT hr = E_FAIL;
  368. MRFILEENTRY *pFile = NULL;
  369. // NOTE: We only allow to send one file here.
  370. pmp->nFiles = 1;
  371. if (AllocatePMP(pmp, INTERNET_MAX_URL_LENGTH, MAX_PATH))
  372. {
  373. pFile = pmp->pFiles;
  374. FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(pmedium->hGlobal);
  375. if (pfgd)
  376. {
  377. TCHAR szFdName[MAX_PATH]; // pfd->cFileName
  378. FILEDESCRIPTOR *pfd;
  379. // &pfgd->fgd[0], w/ thunk
  380. ASSERT((pfmtetc->cfFormat == g_cfFileDescW) || (pfmtetc->cfFormat == g_cfFileDescA));
  381. pfd = _GetFileDescriptor(pfgd, pfmtetc->cfFormat == g_cfFileDescW, 0, szFdName);
  382. // the file we're about to create has contents from the internet.
  383. // use the internet cache to mark it as "unsafe" so other pages that might know
  384. // the filename can't refer to it and elevate their privileges.
  385. // createurlcacheentry says first param has to be a unique string so just use this GUID.
  386. if (CreateUrlCacheEntry(L"67a3caff-bedc-4090-a21e-492dd8935102", 0, NULL, pFile->pszFileName, 0))
  387. {
  388. PathRemoveFileSpec(pFile->pszFileName); // only interested in the dir that it sits in.
  389. DeleteUrlCacheEntry(L"67a3caff-bedc-4090-a21e-492dd8935102");
  390. STGMEDIUM medium;
  391. FORMATETC fmte = {g_cfFileContents, NULL, pfmtetc->dwAspect, 0, TYMED_ISTREAM | TYMED_HGLOBAL};
  392. hr = pdtobj->GetData(&fmte, &medium);
  393. if (SUCCEEDED(hr))
  394. {
  395. PathAppend(pFile->pszFileName, szFdName); // pfd->cFileName
  396. _PathCleanupSpec(pFile->pszFileName, PathFindFileName(pFile->pszFileName));
  397. PathYetAnotherMakeUniqueName(pFile->pszFileName, pFile->pszFileName, NULL, NULL);
  398. IStream *pstmFile;
  399. hr = SHCreateStreamOnFile(pFile->pszFileName, STGM_WRITE | STGM_CREATE, &pstmFile);
  400. if (SUCCEEDED(hr))
  401. {
  402. switch (medium.tymed)
  403. {
  404. case TYMED_ISTREAM:
  405. {
  406. const LARGE_INTEGER li = {-1, 0}; // the whole thing
  407. hr = _StreamCopyTo(medium.pstm, pstmFile, li, NULL, NULL);
  408. break;
  409. }
  410. case TYMED_HGLOBAL:
  411. hr = pstmFile->Write(GlobalLock(medium.hGlobal),
  412. pfd->dwFlags & FD_FILESIZE ? pfd->nFileSizeLow:(DWORD)GlobalSize(medium.hGlobal),
  413. NULL);
  414. GlobalUnlock(medium.hGlobal);
  415. break;
  416. default:
  417. hr = E_FAIL;
  418. }
  419. pstmFile->Release();
  420. if (FAILED(hr))
  421. DeleteFile(pFile->pszFileName);
  422. }
  423. ReleaseStgMedium(&medium);
  424. }
  425. }
  426. GlobalUnlock(pmedium->hGlobal);
  427. }
  428. }
  429. if (SUCCEEDED(hr))
  430. {
  431. _GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
  432. pFile->dwFlags |= MRFILE_DELETE;
  433. if (pfmtetc->dwAspect == DVASPECT_COPY)
  434. {
  435. pmp->dwFlags |= MRPARAM_DOC; // we are sending the document
  436. // get the code page if there is one
  437. IQueryCodePage *pqcp;
  438. if (SUCCEEDED(pdtobj->QueryInterface(IID_PPV_ARG(IQueryCodePage, &pqcp))))
  439. {
  440. if (SUCCEEDED(pqcp->GetCodePage(&pmp->uiCodePage)))
  441. pmp->dwFlags |= MRPARAM_USECODEPAGE;
  442. pqcp->Release();
  443. }
  444. }
  445. }
  446. else if (pfmtetc->dwAspect == DVASPECT_COPY)
  447. {
  448. TCHAR szFailureMsg[MAX_PATH], szFailureMsgTitle[40];
  449. LoadString(g_hinst, IDS_SENDMAIL_FAILUREMSG, szFailureMsg, ARRAYSIZE(szFailureMsg));
  450. LoadString(g_hinst, IDS_SENDMAIL_TITLE, szFailureMsgTitle, ARRAYSIZE(szFailureMsgTitle));
  451. int iRet = MessageBox(NULL, szFailureMsg, szFailureMsgTitle, MB_YESNO);
  452. if (iRet == IDNO)
  453. hr = S_FALSE; // convert to success to we don't try DVASPECT_LINK
  454. }
  455. return hr;
  456. }
  457. // generate a set of files from the data object
  458. typedef struct
  459. {
  460. INT format;
  461. FORMATETC fmte;
  462. } DATA_HANDLER;
  463. #define GET_FILECONTENT 0
  464. #define GET_HDROP 1
  465. #define GET_URL 2
  466. // Note: If this function returns E_CANCELLED that tells the caller that the user requested us to cancel the
  467. // sendmail operation.
  468. HRESULT CSendTo::CreateSendToFilesFromDataObj(IDataObject *pdtobj, DWORD grfKeyState, MRPARAM *pmp)
  469. {
  470. HRESULT hr;
  471. DWORD dwAspectPrefered;
  472. IEnumFORMATETC *penum;
  473. if (g_cfShellURL == 0)
  474. {
  475. g_cfShellURL = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLURL); // URL is always ANSI
  476. g_cfFileContents = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILECONTENTS);
  477. g_cfFileDescA = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
  478. g_cfFileDescW = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
  479. }
  480. if (IS_FORCE_COPY(grfKeyState))
  481. dwAspectPrefered = DVASPECT_COPY;
  482. else if (IS_FORCE_LINK(grfKeyState))
  483. dwAspectPrefered = DVASPECT_LINK;
  484. else
  485. dwAspectPrefered = DVASPECT_CONTENT;
  486. hr = pdtobj->EnumFormatEtc(DATADIR_GET, &penum);
  487. if (SUCCEEDED(hr))
  488. {
  489. DATA_HANDLER rg_data_handlers[] = {
  490. GET_FILECONTENT, {g_cfFileDescW, NULL, dwAspectPrefered, -1, TYMED_HGLOBAL},
  491. GET_FILECONTENT, {g_cfFileDescW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  492. GET_FILECONTENT, {g_cfFileDescA, NULL, dwAspectPrefered, -1, TYMED_HGLOBAL},
  493. GET_FILECONTENT, {g_cfFileDescA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  494. GET_HDROP, {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  495. GET_URL, {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  496. };
  497. FORMATETC fmte;
  498. while (penum->Next(1, &fmte, NULL) == S_OK)
  499. {
  500. SHFree(fmte.ptd);
  501. fmte.ptd = NULL; // so nobody looks at it
  502. int i;
  503. for (i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
  504. {
  505. if (rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat &&
  506. rg_data_handlers[i].fmte.dwAspect == fmte.dwAspect)
  507. {
  508. STGMEDIUM medium;
  509. if (SUCCEEDED(pdtobj->GetData(&rg_data_handlers[i].fmte, &medium)))
  510. {
  511. switch ( rg_data_handlers[i].format )
  512. {
  513. case GET_FILECONTENT:
  514. hr = _GetFileContentsFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
  515. break;
  516. case GET_HDROP:
  517. hr = _GetHDROPFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
  518. break;
  519. case GET_URL:
  520. hr = _GetURLFromData(pdtobj, &fmte, &medium, grfKeyState, pmp);
  521. break;
  522. }
  523. ReleaseStgMedium(&medium);
  524. if (SUCCEEDED(hr))
  525. goto Done;
  526. }
  527. }
  528. }
  529. }
  530. Done:
  531. penum->Release();
  532. }
  533. if (SUCCEEDED(hr))
  534. hr = FilterPMP(pmp);
  535. return hr;
  536. }
  537. // allocate and free a file list. pmp->nFiles MUST be initialized before calling this function!
  538. BOOL CSendTo::AllocatePMP(MRPARAM *pmp, DWORD cchTitle, DWORD cchFiles)
  539. {
  540. // Remember the array sizes for overflow checks, etc.
  541. pmp->cchFile = cchFiles;
  542. pmp->cchTitle = cchTitle;
  543. // compute size of each file entry and allocate enough for the number of files we have. Also
  544. // add a TCHAR to the end of the buffer so we can do a double null termination safely while deleting files
  545. pmp->cbFileEntry = sizeof(MRFILEENTRY) + ((cchTitle + cchFiles) * sizeof(TCHAR));
  546. pmp->pFiles = (MRFILEENTRY *)GlobalAlloc(GPTR, (pmp->cbFileEntry * pmp->nFiles) + sizeof(TCHAR));
  547. if (!pmp->pFiles)
  548. return FALSE;
  549. CFileEnum MREnum(pmp, NULL);
  550. MRFILEENTRY *pFile;
  551. // Note: The use of the enumerator here is questionable since this is the loop that initializes the
  552. // data structure. If the implementation changes in the future be sure this assumption still holds.
  553. while (pFile = MREnum.Next())
  554. {
  555. pFile->pszFileName = (LPTSTR)pFile->chBuf;
  556. pFile->pszTitle = pFile->pszFileName + cchFiles;
  557. ASSERTMSG(pFile->dwFlags == 0, "Expected zero-inited memory allocation");
  558. ASSERTMSG(pFile->pszFileName[cchFiles-1] == 0, "Expected zero-inited memory allocation");
  559. ASSERTMSG(pFile->pszTitle[cchTitle-1] == 0, "Expected zero-inited memory allocation");
  560. ASSERTMSG(pFile->pStream == NULL, "Expected zero-inited memory allocation");
  561. }
  562. return TRUE;
  563. }
  564. BOOL CSendTo::CleanupPMP(MRPARAM *pmp)
  565. {
  566. CFileEnum MREnum(pmp, NULL);
  567. MRFILEENTRY *pFile;
  568. while (pFile = MREnum.Next())
  569. {
  570. // delete the file if we are supposed to
  571. if (pFile->dwFlags & MRFILE_DELETE)
  572. DeleteFile(pFile->pszFileName);
  573. // If we held on to a temporary stream release it so the underlying data will be deleted.
  574. ATOMICRELEASE(pFile->pStream);
  575. }
  576. if (pmp->pFiles)
  577. {
  578. GlobalFree((LPVOID)pmp->pFiles);
  579. pmp->pFiles = NULL;
  580. }
  581. GlobalFree(pmp);
  582. return TRUE;
  583. }
  584. // allow files to be massaged before sending
  585. HRESULT CSendTo::FilterPMP(MRPARAM *pmp)
  586. {
  587. // lets handle the initialization of the progress dialog
  588. IActionProgress *pap;
  589. HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActionProgress, &pap));
  590. if (SUCCEEDED(hr))
  591. {
  592. TCHAR szBuffer[MAX_PATH];
  593. IActionProgressDialog *papd;
  594. hr = pap->QueryInterface(IID_PPV_ARG(IActionProgressDialog, &papd));
  595. if (SUCCEEDED(hr))
  596. {
  597. LoadString(g_hinst, IDS_SENDMAIL_TITLE, szBuffer, ARRAYSIZE(szBuffer));
  598. hr = papd->Initialize(0x0, szBuffer, NULL);
  599. papd->Release();
  600. }
  601. if (SUCCEEDED(hr))
  602. {
  603. LoadString(g_hinst, IDS_SENDMAIL_RECOMPRESS, szBuffer, ARRAYSIZE(szBuffer));
  604. pap->UpdateText(SPTEXT_ACTIONDESCRIPTION, szBuffer, FALSE);
  605. pap->Begin(SPACTION_COPYING, SPBEGINF_NORMAL);
  606. }
  607. if (FAILED(hr))
  608. {
  609. pap->Release();
  610. pap = NULL;
  611. }
  612. }
  613. // walk the files and perform the recompress if we need to.
  614. int iResponse = RESPONSE_UNKNOWN;
  615. CFileEnum MREnum(pmp, pap);
  616. MRFILEENTRY *pFile;
  617. for (hr = S_OK; (pFile = MREnum.Next()) && SUCCEEDED(hr); )
  618. {
  619. if (pap)
  620. pap->UpdateText(SPTEXT_ACTIONDETAIL, pFile->pszFileName, TRUE);
  621. // if this is a picture then lets off the option to recompress the image
  622. if (PathIsImage(pFile->pszFileName))
  623. {
  624. LPITEMIDLIST pidl;
  625. hr = SHILCreateFromPath(pFile->pszFileName, &pidl, NULL);
  626. if (SUCCEEDED(hr))
  627. {
  628. hr = SHCreateShellItem(NULL, NULL, pidl, &_psi);
  629. if (SUCCEEDED(hr))
  630. {
  631. // if the response is unknown then we need to prompt for which type of optimization
  632. // needs to be performed.
  633. if (iResponse == RESPONSE_UNKNOWN)
  634. {
  635. // we need the link control window
  636. INITCOMMONCONTROLSEX initComctl32;
  637. initComctl32.dwSize = sizeof(initComctl32);
  638. initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
  639. InitCommonControlsEx(&initComctl32);
  640. // we need a parent window
  641. HWND hwnd = GetActiveWindow();
  642. if (pap)
  643. IUnknown_GetWindow(pap, &hwnd);
  644. iResponse = (int)DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_RECOMPRESS),
  645. hwnd, s_ConfirmDlgProc, (LPARAM)this);
  646. }
  647. // based on the response we either have cache or the dialog lets perform
  648. // that operation as needed.
  649. if (iResponse == RESPONSE_CANCEL)
  650. {
  651. hr = E_CANCELLED;
  652. }
  653. else if (iResponse == RESPONSE_RECOMPRESS)
  654. {
  655. IStorage *pstg;
  656. hr = _GetTempStorage(&pstg);
  657. if (SUCCEEDED(hr))
  658. {
  659. IImageRecompress *pir;
  660. hr = CoCreateInstance(CLSID_ImageRecompress, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImageRecompress, &pir));
  661. if (SUCCEEDED(hr))
  662. {
  663. IStream *pstrm;
  664. hr = pir->RecompressImage(_psi, _aQuality[_iRecompSetting].cx, _aQuality[_iRecompSetting].cy, _aQuality[_iRecompSetting].iQuality, pstg, &pstrm);
  665. if (hr == S_OK)
  666. {
  667. STATSTG stat;
  668. hr = pstrm->Stat(&stat, STATFLAG_DEFAULT);
  669. if (SUCCEEDED(hr))
  670. {
  671. // its OK to delete this file now, b/c we are going to replace it with the recompressed
  672. // stream we have just generated from the source.
  673. if (pFile->dwFlags & MRFILE_DELETE)
  674. DeleteFile(pFile->pszFileName);
  675. // get the information on the recompressed object.
  676. StrCpyNW(pFile->pszFileName, _szTempPath, pmp->cchFile);
  677. PathAppend(pFile->pszFileName, stat.pwcsName);
  678. _GetFileAndTypeDescFromPath(pFile->pszFileName, pFile->pszTitle, pmp->cchTitle);
  679. pFile->dwFlags |= MRFILE_DELETE;
  680. pstrm->QueryInterface(IID_PPV_ARG(IStream, &pFile->pStream));
  681. CoTaskMemFree(stat.pwcsName);
  682. }
  683. pstrm->Release();
  684. }
  685. pir->Release();
  686. }
  687. pstg->Release();
  688. }
  689. }
  690. if (SUCCEEDED(hr) && pap)
  691. {
  692. BOOL fCancelled;
  693. if (SUCCEEDED(pap->QueryCancel(&fCancelled)) && fCancelled)
  694. {
  695. hr = E_CANCELLED;
  696. }
  697. }
  698. _psi->Release();
  699. }
  700. ILFree(pidl);
  701. }
  702. }
  703. }
  704. if (pap)
  705. {
  706. pap->End();
  707. pap->Release();
  708. }
  709. return hr;
  710. }
  711. HRESULT CSendTo::_GetTempStorage(IStorage **ppStorage)
  712. {
  713. *ppStorage = NULL;
  714. HRESULT hr;
  715. if (_pStorageTemp == NULL)
  716. {
  717. if (GetTempPath(ARRAYSIZE(_szTempPath), _szTempPath))
  718. {
  719. LPITEMIDLIST pidl = NULL;
  720. hr = SHILCreateFromPath(_szTempPath, &pidl, NULL);
  721. if (SUCCEEDED(hr))
  722. {
  723. hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IStorage, ppStorage));
  724. if (SUCCEEDED(hr))
  725. {
  726. hr = (*ppStorage)->QueryInterface(IID_PPV_ARG(IStorage, &_pStorageTemp));
  727. }
  728. ILFree(pidl);
  729. }
  730. }
  731. else
  732. {
  733. hr = E_FAIL;
  734. }
  735. }
  736. else
  737. {
  738. hr = _pStorageTemp->QueryInterface(IID_PPV_ARG(IStorage, ppStorage));
  739. }
  740. return hr;
  741. }
  742. void CSendTo::_CollapseOptions(HWND hwnd, BOOL fHide)
  743. {
  744. _fOptionsHidden = fHide;
  745. RECT rc1, rc2;
  746. GetWindowRect(GetDlgItem(hwnd, IDC_RECOMPORIGINAL), &rc1);
  747. GetWindowRect(GetDlgItem(hwnd, IDC_RECOMPLARGE), &rc2);
  748. int cyAdjust = (rc2.top - rc1.top) * (fHide ? -1:1);
  749. // show/hide the controls we are not going to use
  750. UINT idHide[] = { IDC_RECOMPMAKETHEM, IDC_RECOMPSMALL, IDC_RECOMPMEDIUM, IDC_RECOMPLARGE };
  751. for (int i = 0; i < ARRAYSIZE(idHide); i++)
  752. {
  753. ShowWindow(GetDlgItem(hwnd, idHide[i]), fHide ? SW_HIDE:SW_SHOW);
  754. }
  755. // move the buttons at the bottom of the dialog
  756. UINT idMove[] = { IDC_RECOMPSHOWHIDE, IDOK, IDCANCEL };
  757. for (int i = 0; i < ARRAYSIZE(idMove); i++)
  758. {
  759. RECT rcItem;
  760. GetWindowRect(GetDlgItem(hwnd, idMove[i]), &rcItem);
  761. MapWindowPoints(NULL, hwnd, (LPPOINT)&rcItem, 2);
  762. SetWindowPos(GetDlgItem(hwnd, idMove[i]), NULL,
  763. rcItem.left, rcItem.top + cyAdjust, 0, 0,
  764. SWP_NOSIZE|SWP_NOZORDER);
  765. }
  766. // resize the dialog accordingly
  767. RECT rcWindow;
  768. GetWindowRect(hwnd, &rcWindow);
  769. SetWindowPos(hwnd, NULL,
  770. 0, 0, RECTWIDTH(rcWindow), RECTHEIGHT(rcWindow) + cyAdjust,
  771. SWP_NOZORDER|SWP_NOMOVE);
  772. // update the link control
  773. TCHAR szBuffer[MAX_PATH];
  774. LoadString(g_hinst, fHide ? IDS_SENDMAIL_SHOWMORE:IDS_SENDMAIL_SHOWLESS, szBuffer, ARRAYSIZE(szBuffer));
  775. SetDlgItemText(hwnd, IDC_RECOMPSHOWHIDE, szBuffer);
  776. }
  777. // dialog proc for the recompress prompt
  778. BOOL_PTR CSendTo::s_ConfirmDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  779. {
  780. CSendTo *pst = (CSendTo*)GetWindowLongPtr(hwnd, DWLP_USER);
  781. if (msg == WM_INITDIALOG)
  782. {
  783. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  784. pst = (CSendTo*)lParam;
  785. }
  786. return pst->_ConfirmDlgProc(hwnd, msg, wParam, lParam);
  787. }
  788. BOOL_PTR CSendTo::_ConfirmDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  789. {
  790. switch (msg)
  791. {
  792. case WM_INITDIALOG:
  793. {
  794. HWND hwndThumbnail = GetDlgItem(hwnd, IDC_RECOMPTHUMBNAIL);
  795. LONG_PTR dwStyle = GetWindowLongPtr(hwndThumbnail, GWL_STYLE);
  796. // set the default state of the dialog
  797. _CollapseOptions(hwnd, TRUE);
  798. // set the default state of the buttons
  799. CheckRadioButton(hwnd, IDC_RECOMPORIGINAL, IDC_RECOMPALL, IDC_RECOMPALL);
  800. CheckRadioButton(hwnd, IDC_RECOMPSMALL, IDC_RECOMPLARGE, IDC_RECOMPSMALL + _iRecompSetting);
  801. // get the thumbnail and show it.
  802. IExtractImage *pei;
  803. HRESULT hr = _psi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IExtractImage, &pei));
  804. if (SUCCEEDED(hr))
  805. {
  806. RECT rcThumbnail;
  807. GetClientRect(GetDlgItem(hwnd, IDC_RECOMPTHUMBNAIL), &rcThumbnail);
  808. SIZE sz = {RECTWIDTH(rcThumbnail), RECTHEIGHT(rcThumbnail)};
  809. WCHAR szImage[MAX_PATH];
  810. DWORD dwFlags = 0;
  811. hr = pei->GetLocation(szImage, ARRAYSIZE(szImage), NULL, &sz, 24, &dwFlags);
  812. if (SUCCEEDED(hr))
  813. {
  814. HBITMAP hbmp;
  815. hr = pei->Extract(&hbmp);
  816. if (SUCCEEDED(hr))
  817. {
  818. SetWindowLongPtr(hwndThumbnail, GWL_STYLE, dwStyle | SS_BITMAP);
  819. HBITMAP hbmp2 = (HBITMAP)SendMessage(hwndThumbnail, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
  820. if (hbmp2)
  821. {
  822. DeleteObject(hbmp2);
  823. }
  824. }
  825. }
  826. pei->Release();
  827. }
  828. // if that failed then lets get the icon for the file and place that into the dialog,
  829. // this is less likely to fail - I hope.
  830. if (FAILED(hr))
  831. {
  832. IPersistIDList *ppid;
  833. hr = _psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &ppid));
  834. if (SUCCEEDED(hr))
  835. {
  836. LPITEMIDLIST pidl;
  837. hr = ppid->GetIDList(&pidl);
  838. if (SUCCEEDED(hr))
  839. {
  840. SHFILEINFO sfi = {0};
  841. if (SHGetFileInfo((LPCWSTR)pidl, -1, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_PIDL|SHGFI_ADDOVERLAYS))
  842. {
  843. SetWindowLongPtr(hwndThumbnail, GWL_STYLE, dwStyle | SS_ICON);
  844. HICON hIcon = (HICON)SendMessage(hwndThumbnail, STM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)sfi.hIcon);
  845. if (hIcon)
  846. {
  847. DeleteObject(hIcon);
  848. }
  849. }
  850. ILFree(pidl);
  851. }
  852. ppid->Release();
  853. }
  854. }
  855. break;
  856. }
  857. case WM_NOTIFY:
  858. {
  859. // Did they click/keyboard on the alter settings link?
  860. NMHDR *pnmh = (NMHDR *)lParam;
  861. if ((wParam == IDC_RECOMPSHOWHIDE) &&
  862. (pnmh->code == NM_CLICK || pnmh->code == NM_RETURN))
  863. {
  864. _CollapseOptions(hwnd, !_fOptionsHidden);
  865. return TRUE;
  866. }
  867. break;
  868. }
  869. case WM_COMMAND:
  870. {
  871. switch (wParam)
  872. {
  873. case IDOK:
  874. {
  875. // read back the quality index and store
  876. if (IsDlgButtonChecked(hwnd, IDC_RECOMPSMALL))
  877. _iRecompSetting = QUALITY_LOW;
  878. else if (IsDlgButtonChecked(hwnd, IDC_RECOMPMEDIUM))
  879. _iRecompSetting = QUALITY_MEDIUM;
  880. else
  881. _iRecompSetting = QUALITY_HIGH;
  882. // dismiss the dialog, returning the radio button state
  883. EndDialog(hwnd,(IsDlgButtonChecked(hwnd, IDC_RECOMPALL)) ? RESPONSE_RECOMPRESS:RESPONSE_ORIGINAL);
  884. return FALSE;
  885. }
  886. case IDCANCEL:
  887. EndDialog(hwnd, RESPONSE_CANCEL);
  888. return FALSE;
  889. default:
  890. break;
  891. }
  892. break;
  893. }
  894. default:
  895. return FALSE;
  896. }
  897. return TRUE;
  898. }