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.

2601 lines
75 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: DragDrop.cpp
  5. //
  6. // PURPOSE: Implements some common IDropTarget derived interfaces
  7. //
  8. #include "pch.hxx"
  9. #include "dragdrop.h"
  10. #include "dllmain.h"
  11. #include "shlobj.h"
  12. #include <storutil.h>
  13. #include <storecb.h>
  14. #include "instance.h"
  15. #include "demand.h"
  16. #include "mimeutil.h"
  17. #include "storecb.h"
  18. #include "bodyutil.h"
  19. #include "imsgsite.h"
  20. #include "note.h"
  21. #include "shlwapip.h"
  22. #include "secutil.h"
  23. BOOL FIsFileInsertable(HDROP hDrop, LPSTREAM *ppStream, BOOL* fHTML);
  24. HRESULT HrAttachHDrop(HWND hwnd, IMimeMessage *pMessage, HDROP hDrop, BOOL fMakeLinks);
  25. HRESULT HrAddAttachment(IMimeMessage *pMessage, LPWSTR pszName, LPSTREAM pStream, BOOL fLink);
  26. //
  27. // FUNCTION: CDropTarget::CDropTarget
  28. //
  29. // PURPOSE: Simple constructor, initializes everything to NULL or zero.
  30. //
  31. CDropTarget::CDropTarget()
  32. {
  33. m_cRef = 1;
  34. m_hwndOwner = NULL;
  35. m_idFolder = FOLDERID_INVALID;
  36. m_fOutbox = FALSE;
  37. m_pDataObject = NULL;
  38. m_cf = 0;
  39. m_hwndDlg = 0;
  40. m_hDrop = 0;
  41. m_cFiles = 0;
  42. m_iFileCur = 0;
  43. m_pFolder = 0;
  44. m_pStoreCB = 0;
  45. }
  46. //
  47. // FUNCTION: CDropTarget::~CDropTarget
  48. //
  49. // PURPOSE: Cleans up any leftover data.
  50. //
  51. CDropTarget::~CDropTarget()
  52. {
  53. SafeRelease(m_pDataObject);
  54. }
  55. //
  56. // FUNCTION: CDropTarget::Initialize()
  57. //
  58. // PURPOSE: Initializes the drop target with the ID of the folder that will
  59. // be the target and a window handle that can parent any UI we
  60. // need to display.
  61. //
  62. // PARAMETERS:
  63. // [in] hwndOwner - Handle of a window we can parent UI to.
  64. // [in] idFolder - ID of the folder that will be the target.
  65. //
  66. // RETURN VALUE:
  67. // E_INVALIDARG - Bogus parameter passed in
  68. // S_OK - Happiness abounds
  69. //
  70. HRESULT CDropTarget::Initialize(HWND hwndOwner, FOLDERID idFolder)
  71. {
  72. TraceCall("CDropTarget::Initialize");
  73. if (!IsWindow(hwndOwner) || idFolder == FOLDERID_INVALID)
  74. return (E_INVALIDARG);
  75. m_hwndOwner = hwndOwner;
  76. m_idFolder = idFolder;
  77. FOLDERINFO fi;
  78. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &fi)))
  79. {
  80. m_fOutbox = (fi.tySpecial == FOLDER_OUTBOX);
  81. g_pStore->FreeRecord(&fi);
  82. }
  83. return (S_OK);
  84. }
  85. //
  86. // FUNCTION: CDropTarget::QueryInterface()
  87. //
  88. // PURPOSE: Returns a the requested interface if supported.
  89. //
  90. HRESULT CDropTarget::QueryInterface(REFIID riid, LPVOID *ppvObj)
  91. {
  92. if (IsEqualIID(riid, IID_IUnknown))
  93. *ppvObj = (LPVOID) (IUnknown*)(IDropTarget*) this;
  94. else if (IsEqualIID(riid, IID_IDropTarget))
  95. *ppvObj = (LPVOID) (IDropTarget*) this;
  96. else
  97. *ppvObj = NULL;
  98. if (*ppvObj)
  99. {
  100. AddRef();
  101. return (S_OK);
  102. }
  103. return (E_NOINTERFACE);
  104. }
  105. //
  106. // FUNCTION: CBaseDropTarget::AddRef()
  107. //
  108. // PURPOSE: Increments the object reference count.
  109. //
  110. ULONG CDropTarget::AddRef(void)
  111. {
  112. return (++m_cRef);
  113. }
  114. //
  115. // FUNCTION: CDropTarget::Release()
  116. //
  117. // PURPOSE: Decrements the object's ref count. If the ref count hit's zero
  118. // the object is freed.
  119. //
  120. ULONG CDropTarget::Release(void)
  121. {
  122. m_cRef--;
  123. if (m_cRef == 0)
  124. {
  125. delete this;
  126. return (0);
  127. }
  128. return (m_cRef);
  129. }
  130. //
  131. // FUNCTION: CDropTarget::DragEnter()
  132. //
  133. // PURPOSE: This get's called when the user starts dragging an object
  134. // over our target area.
  135. //
  136. // PARAMETERS:
  137. // [in] pDataObject - Pointer to the data object being dragged
  138. // [in] grfKeyState - Pointer to the current key states
  139. // [in] pt - Point in screen coordinates of the mouse
  140. // [out] pdwEffect - Where we return whether this is a valid place for
  141. // pDataObject to be dropped and if so what type of
  142. // drop.
  143. //
  144. // RETURN VALUE:
  145. // S_OK - The function succeeded.
  146. //
  147. HRESULT CDropTarget::DragEnter(IDataObject* pDataObject, DWORD grfKeyState,
  148. POINTL pt, DWORD* pdwEffect)
  149. {
  150. IEnumFORMATETC *pEnum;
  151. FORMATETC fe;
  152. ULONG celtFetched;
  153. DWORD dwEffectOut = DROPEFFECT_NONE;
  154. Assert(m_pDataObject == NULL);
  155. // Get the FORMATETC enumerator for this object
  156. if (SUCCEEDED(pDataObject->EnumFormatEtc(DATADIR_GET, &pEnum)))
  157. {
  158. // Walk through the data types available to see if there is one we
  159. // understand
  160. pEnum->Reset();
  161. while (S_OK == pEnum->Next(1, &fe, &celtFetched))
  162. {
  163. Assert(celtFetched == 1);
  164. if (_ValidateDropType(fe.cfFormat, pDataObject))
  165. {
  166. // Figure out what the right drag effect is
  167. dwEffectOut = _DragEffectFromFormat(pDataObject, *pdwEffect, fe.cfFormat, grfKeyState);
  168. break;
  169. }
  170. }
  171. pEnum->Release();
  172. }
  173. // If we allow this to be dropped on us, then keep a copy of the data object
  174. if (dwEffectOut != DROPEFFECT_NONE)
  175. {
  176. m_pDataObject = pDataObject;
  177. m_pDataObject->AddRef();
  178. m_cf = fe.cfFormat;
  179. }
  180. *pdwEffect = dwEffectOut;
  181. return (S_OK);
  182. }
  183. //
  184. // FUNCTION: CDropTarget::DragOver()
  185. //
  186. // PURPOSE: This is called as the user drags an object over our target.
  187. // If we allow this object to be dropped on us, then we will have
  188. // a pointer in m_pDataObject.
  189. //
  190. // PARAMETERS:
  191. // [in] grfKeyState - Pointer to the current key states
  192. // [in] pt - Point in screen coordinates of the mouse
  193. // [out] pdwEffect - Where we return whether this is a valid place for
  194. // pDataObject to be dropped and if so what type of
  195. // drop.
  196. //
  197. // RETURN VALUE:
  198. // S_OK - The function succeeded.
  199. //
  200. HRESULT CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  201. {
  202. // If we don't have a stored data object from CMDT::DragEnter(), then this
  203. // isn't a data object we have any interest in.
  204. if (NULL == m_pDataObject)
  205. {
  206. *pdwEffect = DROPEFFECT_NONE;
  207. return (S_OK);
  208. }
  209. // We don't care about _where_ the drop happens, just what type of effect
  210. // should be displayed.
  211. *pdwEffect = _DragEffectFromFormat(m_pDataObject, *pdwEffect, m_cf, grfKeyState);
  212. return (S_OK);
  213. }
  214. //
  215. // FUNCTION: CDropTarget::DragLeave()
  216. //
  217. // PURPOSE: Allows us to release any stored data we have from a successful
  218. // DragEnter()
  219. //
  220. // RETURN VALUE:
  221. // S_OK - Everything is groovy
  222. //
  223. HRESULT CDropTarget::DragLeave(void)
  224. {
  225. // Free everything up at this point.
  226. if (NULL != m_pDataObject)
  227. {
  228. m_pDataObject->Release();
  229. m_pDataObject = 0;
  230. m_cf = 0;
  231. }
  232. return (S_OK);
  233. }
  234. //
  235. // FUNCTION: CDropTarget::Drop()
  236. //
  237. // PURPOSE: The user has let go of the object over our target. If we
  238. // can accept this object we will already have the pDataObject
  239. // stored in m_pDataObject.
  240. //
  241. // PARAMETERS:
  242. // [in] pDataObject - Pointer to the data object being dragged
  243. // [in] grfKeyState - Pointer to the current key states
  244. // [in] pt - Point in screen coordinates of the mouse
  245. // [out] pdwEffect - Where we return whether this is a valid place for
  246. // pDataObject to be dropped and if so what type of
  247. // drop.
  248. //
  249. // RETURN VALUE:
  250. // S_OK - Everything worked OK
  251. //
  252. HRESULT CDropTarget::Drop(IDataObject* pDataObject, DWORD grfKeyState,
  253. POINTL pt, DWORD* pdwEffect)
  254. {
  255. IEnumFORMATETC *pEnum;
  256. FORMATETC fe;
  257. ULONG celtFetched;
  258. HRESULT hr;
  259. if (!pDataObject)
  260. return (E_INVALIDARG);
  261. *pdwEffect = _DragEffectFromFormat(pDataObject, *pdwEffect, m_cf, grfKeyState);
  262. hr = _HandleDrop(m_pDataObject, *pdwEffect, m_cf, grfKeyState);
  263. SafeRelease(m_pDataObject);
  264. return (hr);
  265. }
  266. //
  267. // FUNCTION: CDropTarget::_CheckRoundtrip()
  268. //
  269. // PURPOSE: Checks to see if the source and the target are the same.
  270. //
  271. // PARAMETERS:
  272. // [in] pDataObject - Object being dragged over us
  273. //
  274. // RETURNS:
  275. // TRUE if the source and destination are the same, FALSE otherwise.
  276. //
  277. BOOL CDropTarget::_CheckRoundtrip(IDataObject *pDataObject)
  278. {
  279. AssertSz(FALSE, "CDropTarget::_CheckRoundtrip() - NYI");
  280. return (FALSE);
  281. }
  282. //
  283. // FUNCTION: CDropTarget::_ValidateDropType()
  284. //
  285. // PURPOSE: Examines the the specified clipboard format to see if we can
  286. // accept this data type.
  287. //
  288. // PARAMETERS:
  289. // <in> cf - Clipboard format
  290. //
  291. // RETURN VALUE:
  292. // TRUE if we understand, FALSE otherwise.
  293. //
  294. BOOL CDropTarget::_ValidateDropType(CLIPFORMAT cf, IDataObject *pDataObject)
  295. {
  296. if (!pDataObject)
  297. return (FALSE);
  298. // OE Folders
  299. if (cf == CF_OEFOLDER)
  300. return (_IsValidOEFolder(pDataObject));
  301. // Messages
  302. if (cf == CF_OEMESSAGES)
  303. return (_IsValidOEMessages(pDataObject));
  304. // Files
  305. if (cf == CF_HDROP && !m_fOutbox)
  306. return (TRUE);
  307. // Text
  308. if ((cf == CF_TEXT || cf == CF_HTML || cf == CF_UNICODETEXT) && !m_fOutbox)
  309. return (TRUE);
  310. return (FALSE);
  311. }
  312. //
  313. // FUNCTION: CDropTarget::_IsValidOEFolder()
  314. //
  315. // PURPOSE: Checks to see if the data object contains valid OE Folder
  316. // information for this target.
  317. //
  318. // PARAMETERS:
  319. // [in] pDataObject - Data Object to check
  320. //
  321. // RETURN VALUE:
  322. // Returns TRUE if it's OK to drop this here, FALSE otherwise.
  323. //
  324. BOOL CDropTarget::_IsValidOEFolder(IDataObject *pDataObject)
  325. {
  326. FORMATETC fe;
  327. STGMEDIUM stm = {0};
  328. FOLDERID *pidFolder;
  329. FOLDERINFO rInfoSrc = {0};
  330. FOLDERINFO rInfoDest = {0};
  331. BOOL fReturn = FALSE;
  332. TraceCall("CDropTarget::_IsValidOEFolder");
  333. // Get the folder information from the object
  334. SETDefFormatEtc(fe, CF_OEFOLDER, TYMED_HGLOBAL);
  335. if (FAILED(pDataObject->GetData(&fe, &stm)))
  336. return (FALSE);
  337. pidFolder = (FOLDERID *) GlobalLock(stm.hGlobal);
  338. // Moving a folder onto itself would be bad
  339. if (*pidFolder == m_idFolder)
  340. goto exit;
  341. // Figure out the store type of the folder
  342. if (FAILED(g_pStore->GetFolderInfo(*pidFolder, &rInfoSrc)))
  343. goto exit;
  344. // You simply cannot move news or special folders
  345. if (rInfoSrc.tyFolder == FOLDER_NEWS || rInfoSrc.tySpecial != FOLDER_NOTSPECIAL)
  346. goto exit;
  347. // If it's not news, we need information about the destination
  348. if (FAILED(g_pStore->GetFolderInfo(m_idFolder, &rInfoDest)))
  349. goto exit;
  350. // Local to Local OK
  351. if (rInfoSrc.tyFolder == FOLDER_LOCAL && rInfoDest.tyFolder == FOLDER_LOCAL)
  352. {
  353. fReturn = TRUE;
  354. goto exit;
  355. }
  356. // According to Ray, IMAP folders can't be moved. I don't know about HTTP.
  357. if (rInfoSrc.tyFolder == FOLDER_IMAP || rInfoSrc.tyFolder == FOLDER_HTTPMAIL)
  358. goto exit;
  359. exit:
  360. if (rInfoDest.pAllocated)
  361. g_pStore->FreeRecord(&rInfoDest);
  362. if (rInfoSrc.pAllocated)
  363. g_pStore->FreeRecord(&rInfoSrc);
  364. GlobalUnlock(stm.hGlobal);
  365. ReleaseStgMedium(&stm);
  366. return (fReturn);
  367. }
  368. //
  369. // FUNCTION: CDropTarget::_IsValidOEMessages()
  370. //
  371. // PURPOSE: Checks to see if the data object contains OE Messages that can
  372. // be dropped here.
  373. //
  374. // PARAMETERS:
  375. // [in] pDataObject - Data object to verify.
  376. //
  377. // RETURN VALUE:
  378. // Returns TRUE if the object contains data that can be dropped here.
  379. //
  380. BOOL CDropTarget::_IsValidOEMessages(IDataObject *pDataObject)
  381. {
  382. FORMATETC fe;
  383. STGMEDIUM stm;
  384. FOLDERID *pidFolder;
  385. FOLDERINFO rInfoDest = {0};
  386. BOOL fReturn = FALSE;
  387. TraceCall("CDropTarget::_IsValidOEMessages");
  388. // We don't allow dropping messages on the outbox, on server nodes,
  389. // or on the root.
  390. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &rInfoDest)))
  391. {
  392. fReturn = (0 == (rInfoDest.dwFlags & FOLDER_SERVER)) &&
  393. (FOLDERID_ROOT != m_idFolder) &&
  394. (FOLDER_OUTBOX != rInfoDest.tySpecial) &&
  395. (FOLDER_NEWS != GetFolderType(m_idFolder));
  396. g_pStore->FreeRecord(&rInfoDest);
  397. }
  398. return (fReturn);
  399. }
  400. //
  401. // FUNCTION: CDropTarget::_DragEffectFromFormat()
  402. //
  403. // PURPOSE: Examines the keyboard state and the specified clipboard format
  404. // and determines what the right drag effect would be.
  405. //
  406. // PARAMETERS:
  407. // <in> cf - Clipboard format
  408. // <in> grfKeyState - State of the keyboard
  409. //
  410. // RETURN VALUE:
  411. // Returns one of the drag effects defined by OLE, ie DRAGEFFECT_COPY, etc.
  412. //
  413. DWORD CDropTarget::_DragEffectFromFormat(IDataObject *pDataObject, DWORD dwEffectOk,
  414. CLIPFORMAT cf, DWORD grfKeyState)
  415. {
  416. FORMATETC fe;
  417. STGMEDIUM stm;
  418. BOOL fRoundTrip = FALSE;
  419. FOLDERID *pidFolder;
  420. // Folders are always a move
  421. if (cf == CF_OEFOLDER)
  422. return (DROPEFFECT_MOVE);
  423. // Messages move or copy
  424. if (cf == CF_OEMESSAGES)
  425. {
  426. SETDefFormatEtc(fe, CF_OEMESSAGES, TYMED_HGLOBAL);
  427. if (SUCCEEDED(pDataObject->GetData(&fe, &stm)))
  428. {
  429. pidFolder = (FOLDERID *) GlobalLock(stm.hGlobal);
  430. fRoundTrip = (*pidFolder == m_idFolder);
  431. GlobalUnlock(stm.hGlobal);
  432. ReleaseStgMedium(&stm);
  433. }
  434. if (fRoundTrip)
  435. return (DROPEFFECT_NONE);
  436. else if ((dwEffectOk & DROPEFFECT_MOVE) && !(grfKeyState & MK_CONTROL))
  437. return (DROPEFFECT_MOVE);
  438. else
  439. return (DROPEFFECT_COPY);
  440. }
  441. // Files
  442. if (cf == CF_HDROP)
  443. {
  444. if (grfKeyState & MK_SHIFT && grfKeyState & MK_CONTROL)
  445. return (DROPEFFECT_LINK);
  446. else
  447. return (DROPEFFECT_COPY);
  448. }
  449. // If it's text or HTML, create a new note with the body filled with the
  450. // contents
  451. if (CF_TEXT == cf || CF_HTML == cf || CF_UNICODETEXT == cf)
  452. return (DROPEFFECT_COPY);
  453. return (DROPEFFECT_NONE);
  454. }
  455. //
  456. // FUNCTION: CDropTarget::_HandleDrop()
  457. //
  458. // PURPOSE: Takes the dropped object and get's the data out of it that
  459. // we care about.
  460. //
  461. // PARAMETERS:
  462. // <in> pDataObject - Object being dropped on us.
  463. // <in> cf - Format to render
  464. // <in> grfKeyState - Keyboard state when the object was dropped.
  465. //
  466. // RETURN VALUE:
  467. // S_OK if we jam on it.
  468. //
  469. HRESULT CDropTarget::_HandleDrop(IDataObject *pDataObject, DWORD dwEffectOk,
  470. CLIPFORMAT cf, DWORD grfKeyState)
  471. {
  472. DWORD dw;
  473. if (cf == CF_OEFOLDER)
  474. return (_HandleFolderDrop(pDataObject));
  475. if (cf == CF_OEMESSAGES)
  476. {
  477. dw = _DragEffectFromFormat(pDataObject, dwEffectOk, cf, grfKeyState);
  478. Assert(dw == DROPEFFECT_MOVE || dw == DROPEFFECT_COPY);
  479. return (_HandleMessageDrop(pDataObject, dw == DROPEFFECT_MOVE));
  480. }
  481. if (cf == CF_HDROP)
  482. return (_HandleHDrop(pDataObject, cf, grfKeyState));
  483. if (cf == CF_TEXT || cf == CF_HTML || cf == CF_UNICODETEXT)
  484. return (_CreateMessageFromDrop(m_hwndOwner, pDataObject, grfKeyState));
  485. return (DV_E_FORMATETC);
  486. }
  487. HRESULT CDropTarget::_HandleFolderDrop(IDataObject *pDataObject)
  488. {
  489. FORMATETC fe;
  490. STGMEDIUM stm;
  491. FOLDERID *pidFolder;
  492. HRESULT hr = E_UNEXPECTED;
  493. if (!pDataObject)
  494. return (E_INVALIDARG);
  495. // Get the data from the data object
  496. SETDefFormatEtc(fe, CF_OEFOLDER, TYMED_HGLOBAL);
  497. if (SUCCEEDED(pDataObject->GetData(&fe, &stm)))
  498. {
  499. pidFolder = (FOLDERID *) GlobalLock(stm.hGlobal);
  500. // Tell the store to move
  501. hr = MoveFolderProgress(m_hwndOwner, *pidFolder, m_idFolder);
  502. GlobalUnlock(stm.hGlobal);
  503. ReleaseStgMedium(&stm);
  504. }
  505. return (hr);
  506. }
  507. HRESULT CDropTarget::_HandleMessageDrop(IDataObject *pDataObject, BOOL fMove)
  508. {
  509. FORMATETC fe;
  510. STGMEDIUM stm;
  511. OEMESSAGES *pMsgs = 0;
  512. HRESULT hr = E_UNEXPECTED;
  513. // Get the data from the data object
  514. SETDefFormatEtc(fe, CF_OEMESSAGES, TYMED_HGLOBAL);
  515. if (SUCCEEDED(hr = pDataObject->GetData(&fe, &stm)))
  516. {
  517. pMsgs = (OEMESSAGES *) GlobalLock(stm.hGlobal);
  518. hr = CopyMoveMessages(m_hwndOwner, pMsgs->idSource, m_idFolder, &pMsgs->rMsgIDList, fMove ? COPY_MESSAGE_MOVE : 0);
  519. if (FAILED(hr))
  520. AthErrorMessageW(m_hwndOwner, MAKEINTRESOURCEW(idsAthena), fMove ? MAKEINTRESOURCEW(idsErrMoveMsgs) : MAKEINTRESOURCEW(idsErrCopyMsgs), hr);
  521. if (NULL != g_pInstance)
  522. {
  523. HRESULT hrTemp;
  524. FOLDERINFO fiFolderInfo;
  525. hrTemp = g_pStore->GetFolderInfo(pMsgs->idSource, &fiFolderInfo);
  526. if (SUCCEEDED(hrTemp))
  527. {
  528. if (FOLDER_INBOX == fiFolderInfo.tySpecial)
  529. g_pInstance->UpdateTrayIcon(TRAYICONACTION_REMOVE);
  530. g_pStore->FreeRecord(&fiFolderInfo);
  531. }
  532. }
  533. GlobalUnlock(stm.hGlobal);
  534. ReleaseStgMedium(&stm);
  535. }
  536. return (hr);
  537. }
  538. //
  539. // FUNCTION: CDropTarget::_HandleHDrop()
  540. //
  541. // PURPOSE: Examines the contents of the drop to see if these are just files
  542. // or are they .eml or .nws files.
  543. //
  544. // PARAMETERS:
  545. // [in] pDataObject
  546. // [in] cf
  547. // [in] grfKeyState
  548. //
  549. // RETURN VALUE:
  550. // HRESULT
  551. //
  552. HRESULT CDropTarget::_HandleHDrop(IDataObject *pDataObject, CLIPFORMAT cf, DWORD grfKeyState)
  553. {
  554. FORMATETC fe;
  555. STGMEDIUM stg = {0};
  556. HDROP hDrop;
  557. HRESULT hr;
  558. BOOL fRelease = FALSE;
  559. UINT cFiles;
  560. BOOL fMessages = TRUE;
  561. UINT i;
  562. TraceCall("CDropTarget::_HandleHDrop");
  563. // Get the data from the object
  564. SETDefFormatEtc(fe, CF_HDROP, TYMED_HGLOBAL);
  565. if (FAILED(hr = pDataObject->GetData(&fe, &stg)))
  566. {
  567. AssertSz(SUCCEEDED(hr), "CDropTarget::_HandleHDrop() - GetData() failed.");
  568. goto exit;
  569. }
  570. fRelease = TRUE;
  571. hDrop = (HDROP) GlobalLock(stg.hGlobal);
  572. if (FOLDER_NEWS != GetFolderType(m_idFolder) && (FOLDERID_ROOT != m_idFolder) &&
  573. (FOLDERID_LOCAL_STORE != m_idFolder) && !(FOLDER_IMAP == GetFolderType(m_idFolder) && FFolderIsServer(m_idFolder)))
  574. {
  575. // Look inside the data to see if any of the files are .eml or .nws
  576. cFiles = DragQueryFileWrapW(hDrop, (UINT) -1, NULL, 0);
  577. for (i = 0; i < cFiles; i++)
  578. {
  579. WCHAR wszFile[MAX_PATH];
  580. LPWSTR pwszExt;
  581. // Get the name of the i'th file in the drop
  582. DragQueryFileWrapW(hDrop, i, wszFile, ARRAYSIZE(wszFile));
  583. // Get the extension for the file
  584. pwszExt = PathFindExtensionW(wszFile);
  585. if (*pwszExt)
  586. {
  587. // Once we find the first file that isn't one of our messages, we
  588. // can give up.
  589. if (0 != StrCmpIW(pwszExt, c_wszEmlExt) && 0 != StrCmpIW(pwszExt, c_wszNwsExt))
  590. {
  591. fMessages = FALSE;
  592. break;
  593. }
  594. }
  595. }
  596. }
  597. else
  598. {
  599. fMessages = FALSE;
  600. }
  601. // If all of the messages were news or mail messages, we can just copy them
  602. // into our store. If even one were normal files, then we create a new message
  603. // with everything attached.
  604. if (fMessages)
  605. hr = _InsertMessagesInStore(hDrop);
  606. else
  607. hr = _CreateMessageFromDrop(m_hwndOwner, pDataObject, grfKeyState);
  608. exit:
  609. if (fRelease)
  610. ReleaseStgMedium(&stg);
  611. return (hr);
  612. }
  613. //
  614. // FUNCTION: CDropTarget::_InsertMessagesInStore()
  615. //
  616. // PURPOSE: When the user drops messages that are stored as .nws or .eml files
  617. // onto us, we need to integrate those files into our store.
  618. //
  619. // PARAMETERS:
  620. // [in] hDrop - Contains the information needed to get the files
  621. //
  622. // RETURN VALUE:
  623. // HRESULT
  624. //
  625. HRESULT CDropTarget::_InsertMessagesInStore(HDROP hDrop)
  626. {
  627. HRESULT hr;
  628. TraceCall("CDropTarget::_InsertMessagesInStore");
  629. // Open the folder we're saving into
  630. if (FAILED(hr = g_pStore->OpenFolder(m_idFolder, NULL, 0, &m_pFolder)))
  631. return (hr);
  632. if (0 == (m_pStoreCB = new CStoreDlgCB()))
  633. {
  634. m_pFolder->Release();
  635. return (E_OUTOFMEMORY);
  636. }
  637. // Get the count of files
  638. m_cFiles = DragQueryFileWrapW(hDrop, (UINT) -1, NULL, 0);
  639. m_hDrop = hDrop;
  640. m_iFileCur = 0;
  641. // Do the dialog
  642. DialogBoxParamWrapW(g_hLocRes, MAKEINTRESOURCEW(iddCopyMoveMessages), m_hwndOwner,
  643. _ProgDlgProcExt, (LPARAM) this);
  644. // Free some stuff up
  645. m_cFiles = 0;
  646. m_hDrop = 0;
  647. m_pFolder->Release();
  648. m_pStoreCB->Release();
  649. return (S_OK);
  650. }
  651. INT_PTR CALLBACK CDropTarget::_ProgDlgProcExt(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  652. {
  653. CDropTarget *pThis;
  654. if (msg == WM_INITDIALOG)
  655. {
  656. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  657. pThis = (CDropTarget*) lParam;
  658. }
  659. else
  660. pThis = (CDropTarget*) GetWindowLongPtr(hwnd, DWLP_USER);
  661. if (pThis)
  662. return pThis->_ProgDlgProc(hwnd, msg, wParam, lParam);
  663. return FALSE;
  664. }
  665. //
  666. // FUNCTION: CDropTarget::DlgProc()
  667. //
  668. // PURPOSE: Groovy dialog proc.
  669. //
  670. INT_PTR CDropTarget::_ProgDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  671. {
  672. HWND hwndT;
  673. switch (msg)
  674. {
  675. case WM_INITDIALOG:
  676. return (BOOL)HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, _OnInitDialog);
  677. case WM_COMMAND:
  678. HANDLE_WM_COMMAND(hwnd, wParam, lParam, _OnCommand);
  679. return TRUE;
  680. case WM_STORE_COMPLETE:
  681. m_iFileCur++;
  682. _SaveNextMessage();
  683. return (TRUE);
  684. case WM_STORE_PROGRESS:
  685. return (TRUE);
  686. }
  687. return FALSE;
  688. }
  689. //
  690. // FUNCTION: CDropTarget::_OnInitDialog()
  691. //
  692. // PURPOSE: Initializes the progress dialog
  693. //
  694. BOOL CDropTarget::_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  695. {
  696. Assert(m_pStoreCB);
  697. m_hwndDlg = hwnd;
  698. m_pStoreCB->Initialize(hwnd);
  699. m_pStoreCB->Reset();
  700. // Open and save the first message
  701. _SaveNextMessage();
  702. return (TRUE);
  703. }
  704. //
  705. // FUNCTION: CDropTarget::_OnCommand()
  706. //
  707. // PURPOSE: Handle the cancel button
  708. //
  709. void CDropTarget::_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  710. {
  711. // User's have been known to press cancel once in a while
  712. if (id == IDCANCEL)
  713. {
  714. m_pStoreCB->Cancel();
  715. }
  716. }
  717. //
  718. // FUNCTION: CDropTarget::_SaveNextMessage()
  719. //
  720. // PURPOSE: Opens the next message in the drop and saves it.
  721. //
  722. void CDropTarget::_SaveNextMessage()
  723. {
  724. WCHAR wszFile[MAX_PATH],
  725. wszRes[CCHMAX_STRINGRES],
  726. wszBuf[CCHMAX_STRINGRES + MAX_PATH];
  727. HRESULT hr;
  728. IMimeMessage *pMsg = 0;
  729. TraceCall("CDropTarget::_SaveNextMessage");
  730. // See if we're done
  731. if (m_iFileCur >= m_cFiles)
  732. {
  733. EndDialog(m_hwndDlg, 0);
  734. return;
  735. }
  736. // Get the name of the i'th file in the drop
  737. DragQueryFileWrapW(m_hDrop, m_iFileCur, wszFile, ARRAYSIZE(wszFile));
  738. // Create a new, empty message.
  739. if (FAILED(hr = HrCreateMessage(&pMsg)))
  740. {
  741. AthErrorMessageW(m_hwndDlg, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsMemory), E_OUTOFMEMORY);
  742. EndDialog(m_hwndDlg, 0);
  743. }
  744. // Load the message from the file
  745. hr = HrLoadMsgFromFileW(pMsg, wszFile);
  746. if (FAILED(hr))
  747. {
  748. AthLoadStringW(IDS_ERROR_FILE_NOEXIST, wszRes, ARRAYSIZE(wszRes));
  749. wnsprintfW(wszBuf, ARRAYSIZE(wszBuf), wszRes, wszFile);
  750. AthErrorMessageW(m_hwndDlg, MAKEINTRESOURCEW(idsAthena), wszBuf, hr);
  751. PostMessage(m_hwndDlg, WM_STORE_COMPLETE, 0, 0);
  752. goto exit;
  753. }
  754. // Progress
  755. AthLoadStringW(idsSavingFmt, wszRes, ARRAYSIZE(wszRes));
  756. wnsprintfW(wszBuf, ARRAYSIZE(wszBuf), wszRes, wszFile);
  757. SetDlgItemTextWrapW(m_hwndDlg, idcStatic1, wszBuf);
  758. // Tell the store to save it
  759. hr = m_pFolder->SaveMessage(NULL, SAVE_MESSAGE_GENID, ARF_READ, 0, pMsg, m_pStoreCB);
  760. if (SUCCEEDED(hr))
  761. {
  762. PostMessage(m_hwndDlg, WM_STORE_COMPLETE, 0, 0);
  763. goto exit;
  764. }
  765. if (FAILED(hr) && E_PENDING != hr)
  766. {
  767. AthErrorMessageW(m_hwndDlg, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsUnableToSaveMessage), hr);
  768. PostMessage(m_hwndDlg, WM_STORE_COMPLETE, 0, 0);
  769. }
  770. exit:
  771. SafeRelease(pMsg);
  772. }
  773. //
  774. // FUNCTION: CDropTarget::_CreateMessageFromDrop()
  775. //
  776. // PURPOSE: This function takes an IDataObject which was dropped on Athena
  777. // and creates a new mail message from that dropped object. If
  778. // the dropped object supports either CF_TEXT or CF_HTML, then
  779. // that will be streamed into the body of the message. Otherwise,
  780. // if it supports CF_HDROP we'll add it as an attachment.
  781. //
  782. // PARAMETERS:
  783. // <in> pDataObject -
  784. // <in> pStore -
  785. // <in> grfKeyState -
  786. // <in> pidl -
  787. //
  788. // RETURN VALUE:
  789. // E_INVALIDARG -
  790. //
  791. // COMMENTS:
  792. // <???>
  793. //
  794. HRESULT CDropTarget::_CreateMessageFromDrop(HWND hwnd, IDataObject *pDataObject,
  795. DWORD grfKeyState)
  796. {
  797. IEnumFORMATETC *pEnum = NULL;
  798. FORMATETC fe;
  799. DWORD celtFetched;
  800. CLIPFORMAT cf = 0;
  801. DWORD tymed = 0;
  802. IMimeMessage *pMessage = NULL;
  803. HRESULT hr = S_OK;
  804. STGMEDIUM stg;
  805. IStream *pStream = NULL;
  806. BOOL fRelease = TRUE;
  807. BOOL fIsRealCFHTML=FALSE;
  808. ZeroMemory(&stg, sizeof(STGMEDIUM));
  809. if (!pDataObject)
  810. {
  811. Assert(pDataObject);
  812. return (E_INVALIDARG);
  813. }
  814. // Enumerate the formats this object supports to decide which if any we
  815. // are going to use.
  816. if (SUCCEEDED(pDataObject->EnumFormatEtc(DATADIR_GET, &pEnum)))
  817. {
  818. pEnum->Reset();
  819. while (S_OK == pEnum->Next(1, &fe, &celtFetched))
  820. {
  821. // HTML is the richest format we understand. If we find that, then
  822. // we can stop looking.
  823. if (fe.cfFormat == CF_HTML &&
  824. (fe.tymed & TYMED_HGLOBAL || fe.tymed & TYMED_ISTREAM))
  825. {
  826. DOUTL(32, _T("HrNewMailFromDrop() - Accepting CF_HTML."));
  827. cf = (CLIPFORMAT) CF_HTML;
  828. tymed = fe.tymed;
  829. break;
  830. }
  831. // UNICODETEXT is great, but only if we can't find anything richer. So we
  832. // accept this, but keep looking.
  833. else if (fe.cfFormat == CF_UNICODETEXT &&
  834. (fe.tymed & TYMED_HGLOBAL || fe.tymed & TYMED_ISTREAM))
  835. {
  836. DOUTL(32,_T("HrNewMailFromDrop() - Accepting CF_UNICODETEXT."));
  837. cf = CF_UNICODETEXT;
  838. tymed = fe.tymed;
  839. }
  840. // TEXT is cool, but only if we can't find anything richer. So we
  841. // accept this, but keep looking.
  842. else if (fe.cfFormat == CF_TEXT && cf != CF_UNICODETEXT &&
  843. (fe.tymed & TYMED_HGLOBAL || fe.tymed & TYMED_ISTREAM))
  844. {
  845. DOUTL(32,_T("HrNewMailFromDrop() - Accepting CF_TEXT."));
  846. cf = CF_TEXT;
  847. tymed = fe.tymed;
  848. }
  849. // If we find HDROP, then we can create an attachment. However, we
  850. // want to find something richer so we only accept this if we haven't
  851. // found anything else we like.
  852. else if (fe.cfFormat == CF_HDROP && cf == 0 && fe.tymed & TYMED_HGLOBAL)
  853. {
  854. cf = CF_HDROP;
  855. tymed = fe.tymed;
  856. }
  857. }
  858. pEnum->Release();
  859. }
  860. // Make sure we found something useful
  861. if (0 == cf)
  862. {
  863. AssertSz(cf, _T("HrNewMailFromDrop() - Did not find an acceptable data")
  864. _T(" format to create a message from."));
  865. hr = E_UNEXPECTED;
  866. goto exit;
  867. }
  868. // Set the preferred TYMED to ISTREAM and set the FORMATETC struct up to
  869. // retrieve the data type we determined above.
  870. if (tymed & TYMED_ISTREAM)
  871. tymed = TYMED_ISTREAM;
  872. else
  873. tymed = TYMED_HGLOBAL;
  874. SETDefFormatEtc(fe, cf, tymed);
  875. // Get the data from the object
  876. if (FAILED(hr = pDataObject->GetData(&fe, &stg)))
  877. {
  878. AssertSz(SUCCEEDED(hr), _T("HrNewMailFromDrop() - pDataObject->GetData() failed."));
  879. goto exit;
  880. }
  881. // Create the Message Object
  882. hr = HrCreateMessage(&pMessage);
  883. if (FAILED(hr))
  884. {
  885. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsMemory),
  886. 0, MB_ICONSTOP | MB_OK);
  887. goto exit;
  888. }
  889. // Set the body appropriately
  890. if (cf == CF_HTML || cf == CF_TEXT || cf == CF_UNICODETEXT)
  891. {
  892. if (fe.tymed == TYMED_HGLOBAL)
  893. {
  894. if (FAILED(hr = CreateStreamOnHGlobal(stg.hGlobal, TRUE, &pStream)))
  895. {
  896. AssertSz(FALSE, _T("HrNewMailFromDrop() - Failed CreateStreamOnHGlobal()"));
  897. goto exit;
  898. }
  899. // Bug #24846 - Need to find the actual size of the stream instead of the size
  900. // of the HGLOBAL.
  901. LPBYTE pb = (LPBYTE) GlobalLock(stg.hGlobal);
  902. ULARGE_INTEGER uliSize;
  903. uliSize.QuadPart = 0;
  904. // Raid 77624: OE mishandles CF_UNICODETEXT / TYMED_GLOBAL
  905. if (cf == CF_TEXT || cf == CF_HTML)
  906. uliSize.QuadPart = lstrlen((LPSTR)pb);
  907. else if (cf == CF_UNICODETEXT)
  908. uliSize.QuadPart = (lstrlenW((LPWSTR)pb) * sizeof(WCHAR));
  909. GlobalUnlock(stg.hGlobal);
  910. pStream->SetSize(uliSize);
  911. fRelease = FALSE;
  912. }
  913. else
  914. pStream = stg.pstm;
  915. if (cf == CF_HTML && FAILED(hr = HrStripHTMLClipboardHeader(pStream, &fIsRealCFHTML)))
  916. goto exit;
  917. // Set Unicode Text Body
  918. if (cf == CF_UNICODETEXT)
  919. {
  920. HCHARSET hCharset;
  921. if (SUCCEEDED(MimeOleFindCharset("UTF-8", &hCharset)))
  922. {
  923. pMessage->SetCharset(hCharset, CSET_APPLY_ALL);
  924. }
  925. pMessage->SetTextBody(TXT_PLAIN, IET_UNICODE, NULL, pStream, NULL);
  926. }
  927. // Set HTML Text Body
  928. else if (cf == CF_HTML)
  929. {
  930. // Real CF_HTML, or OE's version of CF_HTML?
  931. if (fIsRealCFHTML)
  932. {
  933. // Locals
  934. HCHARSET hCharset;
  935. // Map to HCHARSET - Real CF_HTML is always UTF-8
  936. if (SUCCEEDED(MimeOleFindCharset("utf-8", &hCharset)))
  937. {
  938. // Set It
  939. pMessage->SetCharset(hCharset, CSET_APPLY_ALL);
  940. }
  941. }
  942. // Otherwise...
  943. else
  944. {
  945. // Locals
  946. LPSTR pszCharset=NULL;
  947. // Sniff the charset
  948. if (SUCCEEDED(GetHtmlCharset(pStream, &pszCharset)))
  949. {
  950. // Locals
  951. HCHARSET hCharset;
  952. // Map to HCHARSET
  953. if (SUCCEEDED(MimeOleFindCharset(pszCharset, &hCharset)))
  954. {
  955. // Set It
  956. pMessage->SetCharset(hCharset, CSET_APPLY_ALL);
  957. }
  958. // Cleanup
  959. SafeMemFree(pszCharset);
  960. }
  961. }
  962. // We should sniff the charset from the html document and call pMessage->SetCharset
  963. pMessage->SetTextBody(TXT_HTML, IET_INETCSET, NULL, pStream, NULL);
  964. }
  965. // Set plain text, non-unicode text body
  966. else
  967. pMessage->SetTextBody(TXT_PLAIN, IET_BINARY, NULL, pStream, NULL);
  968. }
  969. // If there is a single file and the content is text/plain or text/html, then
  970. // we insert the contents of the message as the body. Otherwise, we add each
  971. // file as an attachment.
  972. if (cf == CF_HDROP)
  973. {
  974. HDROP hDrop = (HDROP) GlobalLock(stg.hGlobal);
  975. BOOL fHTML = FALSE,
  976. fSetCharset = FALSE,
  977. fUnicode = FALSE,
  978. fLittleEndian;
  979. if (FIsFileInsertable(hDrop, &pStream, &fHTML))
  980. {
  981. if(fHTML)
  982. {
  983. LPSTR pszCharset = NULL;
  984. // Sniff the charset
  985. if (FAILED(GetHtmlCharset(pStream, &pszCharset)) && (S_OK == HrIsStreamUnicode(pStream, &fLittleEndian)))
  986. pszCharset = StrDupA("utf-8");
  987. if(pszCharset)
  988. {
  989. HCHARSET hCharset = NULL;
  990. // Map to HCHARSET
  991. if (SUCCEEDED(MimeOleFindCharset(pszCharset, &hCharset)))
  992. {
  993. // Set It
  994. if(SUCCEEDED(pMessage->SetCharset(hCharset, CSET_APPLY_ALL)))
  995. fSetCharset = TRUE;
  996. }
  997. // Cleanup
  998. SafeMemFree(pszCharset);
  999. }
  1000. }
  1001. else if(S_OK == HrIsStreamUnicode(pStream, &fLittleEndian))
  1002. {
  1003. HCHARSET hCharset;
  1004. if (SUCCEEDED(MimeOleFindCharset("UTF-8", &hCharset)))
  1005. {
  1006. pMessage->SetCharset(hCharset, CSET_APPLY_ALL);
  1007. }
  1008. fUnicode = TRUE;
  1009. }
  1010. pMessage->SetTextBody((fHTML ? TXT_HTML : TXT_PLAIN),
  1011. (fSetCharset ? IET_INETCSET : (fUnicode ? IET_UNICODE : IET_DECODED)),
  1012. NULL, pStream, NULL);
  1013. SafeRelease(pStream);
  1014. }
  1015. else
  1016. {
  1017. // Get the drop handle and add it's contents to the message
  1018. hr = HrAttachHDrop(hwnd, pMessage, hDrop, grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT);
  1019. }
  1020. GlobalUnlock(stg.hGlobal);
  1021. }
  1022. // Last step is to instantiate the send note
  1023. INIT_MSGSITE_STRUCT initStruct;
  1024. BOOL fNews;
  1025. fNews = FALSE;
  1026. initStruct.dwInitType = OEMSIT_MSG;
  1027. initStruct.folderID = 0;
  1028. initStruct.pMsg = pMessage;
  1029. if (FOLDER_NEWS == GetFolderType(m_idFolder))
  1030. {
  1031. FOLDERINFO fi = {0};
  1032. TCHAR sz[1024];
  1033. fNews = TRUE;
  1034. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &fi)))
  1035. {
  1036. // Set some news-specific fields on the message
  1037. if ((FOLDER_SERVER & fi.dwFlags) == 0)
  1038. MimeOleSetBodyPropA(pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, fi.pszName);
  1039. FOLDERINFO fiServer = {0};
  1040. if (SUCCEEDED(GetFolderServer(m_idFolder, &fiServer)))
  1041. {
  1042. HrSetAccount(pMessage, fiServer.pszName);
  1043. g_pStore->FreeRecord(&fiServer);
  1044. }
  1045. g_pStore->FreeRecord(&fi);
  1046. }
  1047. }
  1048. CreateAndShowNote(OENA_COMPOSE, fNews ? OENCF_NEWSFIRST : 0, &initStruct, m_hwndOwner);
  1049. exit:
  1050. SafeRelease(pMessage);
  1051. if (fRelease)
  1052. ReleaseStgMedium(&stg);
  1053. return (hr);
  1054. }
  1055. //
  1056. // FUNCTION: CBaseDataObject::CBaseDataObject
  1057. //
  1058. // PURPOSE: Simple constructor, initializes everything to NULL or zero.
  1059. //
  1060. CBaseDataObject::CBaseDataObject()
  1061. {
  1062. m_cRef = 1;
  1063. ZeroMemory(m_rgFormatEtc, sizeof(m_rgFormatEtc));
  1064. m_cFormatEtc = 0;
  1065. }
  1066. //
  1067. // FUNCTION: CBaseDataObject::~CBaseDataObject
  1068. //
  1069. // PURPOSE: Cleans up any leftover data.
  1070. //
  1071. CBaseDataObject::~CBaseDataObject()
  1072. {
  1073. Assert(m_cRef == 0);
  1074. }
  1075. //
  1076. // FUNCTION: CBaseDataObject::QueryInterface()
  1077. //
  1078. // PURPOSE: Returns a the requested interface if supported.
  1079. //
  1080. STDMETHODIMP CBaseDataObject::QueryInterface(REFIID riid, LPVOID* ppv)
  1081. {
  1082. *ppv = NULL;
  1083. if (IsEqualIID(riid, IID_IUnknown))
  1084. *ppv = (LPVOID)(IUnknown*) this;
  1085. else if (IsEqualIID(riid, IID_IDataObject))
  1086. *ppv = (LPVOID)(IDataObject*) this;
  1087. if (NULL == *ppv)
  1088. return (E_NOINTERFACE);
  1089. AddRef();
  1090. return (S_OK);
  1091. }
  1092. //
  1093. // FUNCTION: CBaseDataObject::AddRef()
  1094. //
  1095. // PURPOSE: Increments the object reference count.
  1096. //
  1097. STDMETHODIMP_(ULONG) CBaseDataObject::AddRef(void)
  1098. {
  1099. return (++m_cRef);
  1100. }
  1101. //
  1102. // FUNCTION: CBaseDataObject::Release()
  1103. //
  1104. // PURPOSE: Decrements the object's ref count. If the ref count hit's zero
  1105. // the object is freed.
  1106. //
  1107. STDMETHODIMP_(ULONG) CBaseDataObject::Release(void)
  1108. {
  1109. m_cRef--;
  1110. if (0 == m_cRef)
  1111. {
  1112. delete this;
  1113. return (0);
  1114. }
  1115. return (m_cRef);
  1116. }
  1117. //
  1118. // FUNCTION: CBaseDataObject::GetDataHere
  1119. //
  1120. // PURPOSE: Returns the object's data in a requested format on the storage
  1121. // the caller allocated.
  1122. //
  1123. // PARAMETERS:
  1124. // pFE - Pointer to the FORMATETC structure that specifies the
  1125. // format the data is requested in.
  1126. // pStgMedium - Pointer to the STGMEDIUM structure that contains the
  1127. // medium the caller allocated and the object provides the
  1128. // data on.
  1129. //
  1130. // RETURN VALUE:
  1131. // Returns an HRESULT indicating success or failure.
  1132. //
  1133. STDMETHODIMP CBaseDataObject::GetDataHere (LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1134. {
  1135. return E_NOTIMPL;
  1136. }
  1137. //
  1138. // FUNCTION: CBaseDataObject::GetCanonicalFormatEtc
  1139. //
  1140. // PURPOSE: Communicates to the caller which FORMATETC data structures
  1141. // produce the same output data.
  1142. //
  1143. // PARAMETERS:
  1144. // pFEIn - Points to the FORMATETC in which the caller wants the returned
  1145. // data.
  1146. // pFEOut - Points to the FORMATETC which is the canonical equivalent of
  1147. // pFEIn.
  1148. //
  1149. // RETURN VALUE:
  1150. // Returns an HRESULT signifying success or failure.
  1151. //
  1152. STDMETHODIMP CBaseDataObject::GetCanonicalFormatEtc(LPFORMATETC pFEIn,
  1153. LPFORMATETC pFEOut)
  1154. {
  1155. if (NULL == pFEOut)
  1156. return (E_INVALIDARG);
  1157. pFEOut->ptd = NULL;
  1158. return (DATA_S_SAMEFORMATETC);
  1159. }
  1160. //
  1161. // FUNCTION: CBaseDataObject::EnumFormatEtc
  1162. //
  1163. // PURPOSE: Provides an interface the caller can use to enumerate the
  1164. // FORMATETC's that the data object supports.
  1165. //
  1166. // PARAMETERS:
  1167. // dwDirection - DATADIR_GET if the caller wants to enumerate the formats
  1168. // he can get, or DATADIR_SET if he wants to enumerate the
  1169. // formats he can set.
  1170. // ppEnum - Points to the enumerator the caller can use to enumerate.
  1171. //
  1172. // RETURN VALUE:
  1173. // Returns S_OK if ppEnum contains the enumerator, or E_NOTIMPL if the
  1174. // direction dwDirection is not supported.
  1175. //
  1176. STDMETHODIMP CBaseDataObject::EnumFormatEtc(DWORD dwDirection,
  1177. IEnumFORMATETC** ppEnum)
  1178. {
  1179. LPFORMATETC pFE = 0;
  1180. ULONG cFE = 0;
  1181. if (DATADIR_GET == dwDirection)
  1182. {
  1183. // Create the enumerator and give it our list of formats
  1184. if (SUCCEEDED(_BuildFormatEtc(NULL, NULL)))
  1185. {
  1186. if (SUCCEEDED(CreateEnumFormatEtc(this, m_cFormatEtc, NULL, m_rgFormatEtc, ppEnum)))
  1187. return (S_OK);
  1188. }
  1189. *ppEnum = NULL;
  1190. return (E_FAIL);
  1191. }
  1192. else
  1193. {
  1194. *ppEnum = NULL;
  1195. return (E_NOTIMPL);
  1196. }
  1197. }
  1198. //
  1199. // FUNCTION: CBaseDataObject::SetData
  1200. //
  1201. // PURPOSE: pStgMedium contains data that the caller wants us to store.
  1202. // This data object does not support a caller changing our data.
  1203. //
  1204. STDMETHODIMP CBaseDataObject::SetData(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium,
  1205. BOOL fRelease)
  1206. {
  1207. return (E_NOTIMPL);
  1208. }
  1209. //
  1210. // FUNCTION: CBaseDataObject::DAdvise
  1211. //
  1212. // PURPOSE: Creates a connection between the data-transfer object and an
  1213. // advisory sink through which the sink can be informed when the
  1214. // object's data changes. This object does not support advises.
  1215. STDMETHODIMP CBaseDataObject::DAdvise(LPFORMATETC pFE, DWORD advf,
  1216. IAdviseSink* ppAdviseSink,
  1217. LPDWORD pdwConnection)
  1218. {
  1219. return (E_NOTIMPL);
  1220. }
  1221. //
  1222. // FUNCTION: CBaseDataObject::DUnadvise
  1223. //
  1224. // PURPOSE: Deletes an advisory connect previously established via DAdvise.
  1225. // This object doesn't support advises.
  1226. //
  1227. STDMETHODIMP CBaseDataObject::DUnadvise(DWORD dwConnection)
  1228. {
  1229. return (E_NOTIMPL);
  1230. }
  1231. //
  1232. // FUNCTION: CBaseDataObject::EnumDAdvise
  1233. //
  1234. // PURPOSE: Enumerates the advisory connections previously established via
  1235. // DAdvise. This object doesn't support advises.
  1236. //
  1237. STDMETHODIMP CBaseDataObject::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise)
  1238. {
  1239. return (E_NOTIMPL);
  1240. }
  1241. //
  1242. // FUNCTION: CFolderDataObject::GetData
  1243. //
  1244. // PURPOSE: Returns the object's data in a requested format in the
  1245. // specified storage medium which the object allocates.
  1246. //
  1247. // PARAMETERS:
  1248. // pFE - Pointer to the FORMATETC structure that specifies the
  1249. // format the data is requested in.
  1250. // pStgMedium - Pointer to the STGMEDIUM structure that contains the
  1251. // medium the object allocates and provides the data on.
  1252. //
  1253. // RETURN VALUE:
  1254. // Returns an HRESULT indicating success or failure.
  1255. //
  1256. STDMETHODIMP CFolderDataObject::GetData(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1257. {
  1258. HRESULT hr;
  1259. // Initialize this
  1260. ZeroMemory(pStgMedium, sizeof(STGMEDIUM));
  1261. if (CF_OEFOLDER == pFE->cfFormat)
  1262. return (_RenderOEFolder(pFE, pStgMedium));
  1263. else if ((CF_TEXT == pFE->cfFormat) || (CF_SHELLURL == pFE->cfFormat))
  1264. return (_RenderTextOrShellURL(pFE, pStgMedium));
  1265. else if ((CF_FILEDESCRIPTORW == pFE->cfFormat) ||
  1266. (CF_FILECONTENTS == pFE->cfFormat) ||
  1267. (CF_FILEDESCRIPTORA == pFE->cfFormat))
  1268. {
  1269. AssertSz(FALSE, "These cases not implemented");
  1270. return (E_NOTIMPL);
  1271. }
  1272. else
  1273. return (DV_E_FORMATETC);
  1274. }
  1275. //
  1276. // FUNCTION: CFolderDataObject::QueryGetData
  1277. //
  1278. // PURPOSE: Determines if a call to GetData() would succeed if it were
  1279. // passed pFE.
  1280. //
  1281. // PARAMETERS:
  1282. // pFE - Pointer to the FORMATETC structure to check to see if the data
  1283. // object supports a particular format.
  1284. //
  1285. // RETURN VALUE:
  1286. // Returns an HRESULT signifying success or failure.
  1287. //
  1288. STDMETHODIMP CFolderDataObject::QueryGetData(LPFORMATETC pFE)
  1289. {
  1290. // Make sure this is already built
  1291. _BuildFormatEtc(NULL, NULL);
  1292. // Loop through our formats until we find a match
  1293. for (UINT i = 0; i < m_cFormatEtc; i++)
  1294. {
  1295. if (pFE->cfFormat == m_rgFormatEtc[i].cfFormat &&
  1296. pFE->tymed & m_rgFormatEtc[i].tymed)
  1297. {
  1298. return (S_OK);
  1299. }
  1300. }
  1301. return (DV_E_FORMATETC);
  1302. }
  1303. //
  1304. // FUNCTION: CFolderDataObject::_RenderOEFolder()
  1305. //
  1306. // PURPOSE: Renders the data into the CF_OEFOLDER format
  1307. //
  1308. HRESULT CFolderDataObject::_RenderOEFolder(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1309. {
  1310. TraceCall("CFolderDataObject::_RenderOEFolder");
  1311. HGLOBAL hGlobal;
  1312. FOLDERID *pFolderID;
  1313. // We only support HGLOBALs
  1314. if (pFE->tymed & TYMED_HGLOBAL)
  1315. {
  1316. // I just love allocating 4 bytes
  1317. hGlobal = GlobalAlloc(GHND, sizeof(FOLDERID));
  1318. if (NULL == hGlobal)
  1319. return (E_OUTOFMEMORY);
  1320. // Local the memory
  1321. pFolderID = (FOLDERID *) GlobalLock(hGlobal);
  1322. // Set the value
  1323. *pFolderID = m_idFolder;
  1324. // Unlock
  1325. GlobalUnlock(hGlobal);
  1326. // Set up the return value
  1327. pStgMedium->hGlobal = hGlobal;
  1328. pStgMedium->pUnkForRelease = 0;
  1329. pStgMedium->tymed = TYMED_HGLOBAL;
  1330. return (S_OK);
  1331. }
  1332. return (DV_E_TYMED);
  1333. }
  1334. //
  1335. // FUNCTION: CFolderDataObject::_RenderShellURL()
  1336. //
  1337. // PURPOSE: Renders the data into the CF_SHELLURL format
  1338. //
  1339. HRESULT CFolderDataObject::_RenderTextOrShellURL(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1340. {
  1341. TraceCall("CFolderDataObject::_RenderOEFolder");
  1342. HGLOBAL hGlobal;
  1343. FOLDERID *pFolderID;
  1344. FOLDERINFO rInfo = { 0 };
  1345. TCHAR szNewsPrefix[] = _T("news://");
  1346. LPTSTR pszURL;
  1347. DWORD cch;
  1348. HRESULT hr;
  1349. // We only support HGLOBALs
  1350. if (!(pFE->tymed & TYMED_HGLOBAL))
  1351. {
  1352. hr = DV_E_TYMED;
  1353. goto exit;
  1354. }
  1355. // Get information about the source folder
  1356. Assert(g_pStore);
  1357. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &rInfo)))
  1358. {
  1359. cch = lstrlen(rInfo.pszName) + lstrlen(szNewsPrefix) + 2;
  1360. // Allocate a buffer for the generated URL
  1361. hGlobal = GlobalAlloc(GHND, sizeof(TCHAR) * cch);
  1362. if (!hGlobal)
  1363. {
  1364. hr = E_OUTOFMEMORY;
  1365. goto exit;
  1366. }
  1367. // Double check that our source file is indeed a newsgroup
  1368. if (pFE->cfFormat == CF_SHELLURL)
  1369. {
  1370. if (!(rInfo.tyFolder == FOLDER_NEWS && !(rInfo.dwFlags & FOLDER_SERVER)))
  1371. {
  1372. hr = E_UNEXPECTED;
  1373. goto exit;
  1374. }
  1375. // Generate a URL from the newsgroup name
  1376. pszURL = (LPTSTR) GlobalLock(hGlobal);
  1377. wnsprintf(pszURL, cch, TEXT("%s%s"), szNewsPrefix, rInfo.pszName);
  1378. GlobalUnlock(hGlobal);
  1379. }
  1380. else if (pFE->cfFormat == CF_TEXT)
  1381. {
  1382. // Copy the folder name to the buffer
  1383. pszURL = (LPTSTR) GlobalLock(hGlobal);
  1384. StrCpyN(pszURL, rInfo.pszName, cch);
  1385. GlobalUnlock(hGlobal);
  1386. }
  1387. else
  1388. {
  1389. AssertSz(FALSE, "CFolderDataObject::_RenderTextOrShellURL() - How did we get here?");
  1390. hr = DV_E_FORMATETC;
  1391. goto exit;
  1392. }
  1393. // Set up the return value
  1394. pStgMedium->hGlobal = hGlobal;
  1395. pStgMedium->pUnkForRelease = 0;
  1396. pStgMedium->tymed = TYMED_HGLOBAL;
  1397. hr = S_OK;
  1398. }
  1399. exit:
  1400. if (rInfo.pAllocated)
  1401. g_pStore->FreeRecord(&rInfo);
  1402. return (hr);
  1403. }
  1404. //
  1405. // FUNCTION: CMsgDataObject::HrBuildFormatEtc()
  1406. //
  1407. // PURPOSE: Builds the list of FORMATETC structs that this object will
  1408. // support. This list is stored in m_rgFormatEtc[].
  1409. //
  1410. // PARAMTERS:
  1411. // [out] ppFE - Returns the arrray of FORMATETC structures
  1412. // [out] pcElt - Returns the size of ppFE
  1413. //
  1414. // RETURN VALUE:
  1415. // S_OK
  1416. //
  1417. HRESULT CFolderDataObject::_BuildFormatEtc(LPFORMATETC *ppFE, ULONG *pcElt)
  1418. {
  1419. BOOL fNews = FALSE;
  1420. FOLDERINFO rInfo = { 0 };
  1421. // If we haven't built our format list yet, do so first.
  1422. if (FALSE == m_fBuildFE)
  1423. {
  1424. ZeroMemory(&m_rgFormatEtc, sizeof(m_rgFormatEtc));
  1425. m_cFormatEtc = 0;
  1426. // Figure out if this is a news group first
  1427. if (SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &rInfo)))
  1428. {
  1429. fNews = rInfo.tyFolder == FOLDER_NEWS && !(rInfo.dwFlags & FOLDER_SERVER);
  1430. g_pStore->FreeRecord(&rInfo);
  1431. }
  1432. if (fNews)
  1433. {
  1434. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_SHELLURL, TYMED_HGLOBAL);
  1435. m_cFormatEtc++;
  1436. }
  1437. // Since you can't move a newsgroup, we don't support this format
  1438. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_OEFOLDER, TYMED_HGLOBAL);
  1439. m_cFormatEtc++;
  1440. /*
  1441. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_FILEDESCRIPTOR, TYMED_HGLOBAL);
  1442. m_cFormatEtc++;
  1443. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_FILECONTENTS, TYMED_HGLOBAL);
  1444. m_cFormatEtc++;
  1445. */
  1446. m_fBuildFE = TRUE;
  1447. }
  1448. // Return what we have if the caller cares
  1449. if (ppFE && pcElt)
  1450. {
  1451. *ppFE = m_rgFormatEtc;
  1452. *pcElt = m_cFormatEtc;
  1453. }
  1454. return (S_OK);
  1455. }
  1456. CMessageDataObject::CMessageDataObject()
  1457. {
  1458. m_pMsgIDList = NULL;
  1459. m_idSource = FOLDERID_INVALID;
  1460. m_fBuildFE = FALSE;
  1461. m_fDownloaded = FALSE;
  1462. }
  1463. CMessageDataObject::~CMessageDataObject()
  1464. {
  1465. }
  1466. HRESULT CMessageDataObject::Initialize(LPMESSAGEIDLIST pMsgs, FOLDERID idSource)
  1467. {
  1468. IMessageFolder *pFolder = 0;
  1469. HRESULT hr;
  1470. DWORD i;
  1471. MESSAGEINFO rInfo;
  1472. DWORD cDownloaded = 0;
  1473. m_pMsgIDList = pMsgs;
  1474. m_idSource = idSource;
  1475. // Open the folder that contains the message
  1476. if (SUCCEEDED(hr = g_pStore->OpenFolder(m_idSource, NULL, NOFLAGS, &pFolder)))
  1477. {
  1478. // See if they have bodies or not. All must.
  1479. for (i = 0; i < m_pMsgIDList->cMsgs; i++)
  1480. {
  1481. if (SUCCEEDED(GetMessageInfo(pFolder, m_pMsgIDList->prgidMsg[i], &rInfo)))
  1482. {
  1483. if (rInfo.dwFlags & ARF_HASBODY)
  1484. cDownloaded++;
  1485. pFolder->FreeRecord(&rInfo);
  1486. }
  1487. }
  1488. pFolder->Release();
  1489. }
  1490. m_fDownloaded = (cDownloaded == m_pMsgIDList->cMsgs);
  1491. return (S_OK);
  1492. }
  1493. //
  1494. // FUNCTION: CMessageDataObject::GetData()
  1495. //
  1496. // PURPOSE: Called by the drop target to get the data from the data object.
  1497. //
  1498. // PARAMETERS:
  1499. // LPFORMATETC pFE
  1500. // LPSTGMEDIUM pStgMedium
  1501. //
  1502. // RETURN VALUE:
  1503. // HRESULT
  1504. //
  1505. HRESULT CMessageDataObject::GetData(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1506. {
  1507. HRESULT hr = DV_E_FORMATETC;
  1508. TraceCall("CMessageDataObject::GetData");
  1509. if (pFE->cfFormat == CF_OEMESSAGES)
  1510. return (_RenderOEMessages(pFE, pStgMedium));
  1511. if (m_fDownloaded)
  1512. {
  1513. IMimeMessage *pMsg = NULL;
  1514. if ((CF_FILEDESCRIPTORW == pFE->cfFormat) ||
  1515. (CF_FILEDESCRIPTORA == pFE->cfFormat))
  1516. return (_RenderFileGroupDescriptor(pFE, pStgMedium));
  1517. if (CF_FILECONTENTS == pFE->cfFormat)
  1518. return (_RenderFileContents(pFE, pStgMedium));
  1519. // Otherwise, do the default action
  1520. if (m_pMsgIDList->cMsgs == 1)
  1521. {
  1522. if (SUCCEEDED(_LoadMessage(0, &pMsg, NULL)))
  1523. {
  1524. IDataObject *pDataObject = NULL;
  1525. if (SUCCEEDED(pMsg->QueryInterface(IID_IDataObject, (LPVOID *) &pDataObject)))
  1526. {
  1527. hr = pDataObject->GetData(pFE, pStgMedium);
  1528. pDataObject->Release();
  1529. }
  1530. pMsg->Release();
  1531. }
  1532. }
  1533. }
  1534. return (hr);
  1535. }
  1536. //
  1537. // FUNCTION: CMessageDataObject::QueryGetData
  1538. //
  1539. // PURPOSE: Determines if a call to GetData() would succeed if it were
  1540. // passed pFE.
  1541. //
  1542. // PARAMETERS:
  1543. // pFE - Pointer to the FORMATETC structure to check to see if the data
  1544. // object supports a particular format.
  1545. //
  1546. // RETURN VALUE:
  1547. // Returns an HRESULT signifying success or failure.
  1548. //
  1549. HRESULT CMessageDataObject::QueryGetData(LPFORMATETC pFE)
  1550. {
  1551. IMimeMessage *pMsg = 0;
  1552. IDataObject *pDataObject = 0;
  1553. HRESULT hr = DV_E_FORMATETC;
  1554. // Walk through the formats we support to see if any match
  1555. if (SUCCEEDED(_BuildFormatEtc(NULL, NULL)))
  1556. {
  1557. for (UINT i = 0; i < m_cFormatEtc; i++)
  1558. {
  1559. if (pFE->cfFormat == m_rgFormatEtc[i].cfFormat &&
  1560. pFE->tymed == m_rgFormatEtc[i].tymed)
  1561. {
  1562. hr = S_OK;
  1563. goto exit;
  1564. }
  1565. }
  1566. // If we have a message object, then ask it for it's formats as well.
  1567. if (m_pMsgIDList->cMsgs == 1)
  1568. {
  1569. if (SUCCEEDED(_LoadMessage(0, &pMsg, NULL)))
  1570. {
  1571. if (SUCCEEDED(pMsg->QueryInterface(IID_IDataObject, (LPVOID *) &pDataObject)))
  1572. {
  1573. hr = pDataObject->QueryGetData(pFE);
  1574. pDataObject->Release();
  1575. }
  1576. pMsg->Release();
  1577. }
  1578. }
  1579. }
  1580. exit:
  1581. return (hr);
  1582. }
  1583. HRESULT CMessageDataObject::_BuildFormatEtc(LPFORMATETC *ppFE, ULONG *pcElt)
  1584. {
  1585. IEnumFORMATETC *pEnum = 0;
  1586. FORMATETC fe;
  1587. ULONG celtFetched;
  1588. IMimeMessage *pMessage = 0;
  1589. HRESULT hr = S_OK;
  1590. // Only build our internal list of formats once
  1591. if (FALSE == m_fBuildFE)
  1592. {
  1593. m_cFormatEtc = 0;
  1594. // We always support these formats
  1595. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_OEMESSAGES, TYMED_HGLOBAL);
  1596. m_cFormatEtc++;
  1597. // We support these if the messages are downloaded
  1598. if (m_fDownloaded)
  1599. {
  1600. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_FILEDESCRIPTORA, TYMED_HGLOBAL);
  1601. m_cFormatEtc++;
  1602. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_FILEDESCRIPTORW, TYMED_HGLOBAL);
  1603. m_cFormatEtc++;
  1604. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_FILECONTENTS, TYMED_ISTREAM);
  1605. m_cFormatEtc++;
  1606. }
  1607. // If we're holding just one message, then have the message add it's
  1608. // own formats too.
  1609. if (m_fDownloaded && m_pMsgIDList->cMsgs == 1 && SUCCEEDED(_LoadMessage(0, &pMessage, NULL)))
  1610. {
  1611. IDataObject *pDataObject = 0;
  1612. // Get the data object interface from the message
  1613. if (SUCCEEDED(hr = pMessage->QueryInterface(IID_IDataObject, (LPVOID *) &pDataObject)))
  1614. {
  1615. if (SUCCEEDED(hr = pDataObject->EnumFormatEtc(DATADIR_GET, &pEnum)))
  1616. {
  1617. pEnum->Reset();
  1618. while (S_OK == pEnum->Next(1, &fe, &celtFetched))
  1619. {
  1620. m_rgFormatEtc[m_cFormatEtc] = fe;
  1621. m_cFormatEtc++;
  1622. }
  1623. pEnum->Release();
  1624. }
  1625. pDataObject->Release();
  1626. }
  1627. pMessage->Release();
  1628. }
  1629. }
  1630. // Return what we have
  1631. if (ppFE && pcElt)
  1632. {
  1633. *ppFE = m_rgFormatEtc;
  1634. *pcElt = m_cFormatEtc;
  1635. }
  1636. return (hr);
  1637. }
  1638. //
  1639. // FUNCTION: CMessageDataObject::_LoadMessage()
  1640. //
  1641. // PURPOSE: This function loads the specified message from the store.
  1642. //
  1643. // PARAMETERS:
  1644. // iMsg - Index into m_rgMsgs of the message desired
  1645. // ppMsg - Returns a pointer to the message
  1646. //
  1647. // RETURN VALUE:
  1648. // HRESULT
  1649. //
  1650. HRESULT CMessageDataObject::_LoadMessage(DWORD iMsg, IMimeMessage **ppMsg, LPWSTR pwszFileExt)
  1651. {
  1652. TraceCall("CMessageDataObject::_LoadMessage");
  1653. IMessageFolder *pFolder = NULL;
  1654. IDataObject *pDataObj = NULL;
  1655. HRESULT hr;
  1656. // Open the folder that contains the message
  1657. *ppMsg = NULL;
  1658. hr = g_pStore->OpenFolder(m_idSource, NULL, NOFLAGS, &pFolder);
  1659. if (FAILED(hr))
  1660. goto exit;
  1661. // Open the message from the folder
  1662. hr = pFolder->OpenMessage(m_pMsgIDList->prgidMsg[iMsg],
  1663. OPEN_MESSAGE_SECURE | OPEN_MESSAGE_CACHEDONLY,
  1664. ppMsg, NOSTORECALLBACK);
  1665. if (FAILED(hr))
  1666. goto exit;
  1667. if (pwszFileExt)
  1668. {
  1669. LPTSTR pszNewsgroups = NULL;
  1670. if (SUCCEEDED(MimeOleGetBodyPropA(*ppMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, &pszNewsgroups)))
  1671. {
  1672. MemFree(pszNewsgroups);
  1673. AthLoadStringW(idsDefNewsExt, pwszFileExt, 32);
  1674. }
  1675. else
  1676. AthLoadStringW(idsDefMailExt, pwszFileExt, 32);
  1677. }
  1678. exit:
  1679. SafeRelease(pFolder);
  1680. if (FAILED(hr))
  1681. SafeRelease((*ppMsg));
  1682. return (hr);
  1683. }
  1684. //
  1685. // FUNCTION: CMessageDataObject::_RenderOEMessages()
  1686. //
  1687. // PURPOSE: Renders the data from our object into the CF_OEMESSAGES format.
  1688. //
  1689. // PARAMETERS:
  1690. // pFE
  1691. // pStgMedium
  1692. //
  1693. // RETURN VALUE:
  1694. // HRESULT
  1695. //
  1696. HRESULT CMessageDataObject::_RenderOEMessages(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1697. {
  1698. TraceCall("CMessageDataObject::_RenderOEMessages");
  1699. // Make sure the caller want's an HGLOBAL
  1700. if (pFE->tymed != TYMED_HGLOBAL)
  1701. return (DV_E_TYMED);
  1702. // Figure out how much memory to allocate for our returned information
  1703. DWORD cb = sizeof(OEMESSAGES) + (sizeof(MESSAGEID) * m_pMsgIDList->cMsgs);
  1704. // Allocate the memory
  1705. HGLOBAL hGlobal = GlobalAlloc(GHND | GMEM_SHARE, cb);
  1706. if (NULL == hGlobal)
  1707. return (E_OUTOFMEMORY);
  1708. OEMESSAGES *pOEMessages = (OEMESSAGES *) GlobalLock(hGlobal);
  1709. // Fill in the basic fields
  1710. pOEMessages->idSource = m_idSource;
  1711. pOEMessages->rMsgIDList = *m_pMsgIDList;
  1712. pOEMessages->rMsgIDList.prgidMsg = (MESSAGEID *) ((LPBYTE) pOEMessages + sizeof(OEMESSAGES));
  1713. // Copy the message ID's
  1714. CopyMemory(pOEMessages->rMsgIDList.prgidMsg, m_pMsgIDList->prgidMsg, sizeof(MESSAGEID) * m_pMsgIDList->cMsgs);
  1715. GlobalUnlock(hGlobal);
  1716. // Fill out the return value info
  1717. pStgMedium->tymed = TYMED_HGLOBAL;
  1718. pStgMedium->hGlobal = hGlobal;
  1719. pStgMedium->pUnkForRelease = 0;
  1720. return (S_OK);
  1721. }
  1722. //
  1723. // FUNCTION: CMessageDataObject::_RenderFileContents()
  1724. //
  1725. // PURPOSE: Takes the data that this object contains and renders it into an
  1726. // IStream.
  1727. //
  1728. // PARAMETERS:
  1729. // <in> pFE - Pointer to the FORMATETC struct that describes the
  1730. // type of data requested.
  1731. // <out> pStgMedium - Pointer to where we should return the rendered data.
  1732. //
  1733. // RETURN VALUE:
  1734. // DV_E_TYMED - A tymed was requested that we don't support
  1735. // E_OUTOFMEMORY - not enough memory
  1736. // S_OK - Everything succeeded
  1737. //
  1738. HRESULT CMessageDataObject::_RenderFileContents(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1739. {
  1740. IMimeMessage *pMsg = 0;
  1741. IDataObject *pDataObject = 0;
  1742. FORMATETC feMsg;
  1743. HRESULT hr = E_FAIL;
  1744. DWORD dwFlags = 0;
  1745. Assert(pFE->lindex < (int) m_pMsgIDList->cMsgs);
  1746. // Get the IDataObject for the specific message
  1747. if (SUCCEEDED(_LoadMessage(pFE->lindex, &pMsg, NULL)))
  1748. {
  1749. // prevent save to file message with label
  1750. pMsg->GetFlags(&dwFlags);
  1751. if (IMF_SECURE & dwFlags)
  1752. {
  1753. hr = HandleSecurity(NULL, pMsg);
  1754. if(FAILED(hr))
  1755. goto exit;
  1756. SafeRelease(pMsg);
  1757. if (FAILED(_LoadMessage(pFE->lindex, &pMsg, NULL)))
  1758. goto exit;
  1759. }
  1760. if (SUCCEEDED(pMsg->QueryInterface(IID_IDataObject, (LPVOID *)&pDataObject)))
  1761. {
  1762. SETDefFormatEtc(feMsg, CF_INETMSG, TYMED_ISTREAM);
  1763. hr = pDataObject->GetData(&feMsg, pStgMedium);
  1764. }
  1765. }
  1766. exit:
  1767. // Cleanup
  1768. SafeRelease(pMsg);
  1769. SafeRelease(pDataObject);
  1770. return (hr);
  1771. }
  1772. //
  1773. // FUNCTION: CMessageDataObject::_RenderFileGroupDescriptor()
  1774. //
  1775. // PURPOSE: Takes the data that this object contains and renders it into a
  1776. // FILEGROUPDESCRIPTOR struct.
  1777. //
  1778. // PARAMETERS:
  1779. // <in> pFE - Pointer to the FORMATETC struct that describes the
  1780. // type of data requested.
  1781. // <out> pStgMedium - Pointer to where we should return the rendered data.
  1782. //
  1783. // RETURN VALUE:
  1784. // DV_E_TYMED - A tymed was requested that we don't support
  1785. // E_OUTOFMEMORY - not enough memory
  1786. // S_OK - Everything succeeded
  1787. //
  1788. HRESULT CMessageDataObject::_RenderFileGroupDescriptor(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1789. {
  1790. HGLOBAL hGlobal = 0;
  1791. FILEGROUPDESCRIPTORA *pFileGDA = NULL;
  1792. FILEGROUPDESCRIPTORW *pFileGDW = NULL;
  1793. IMimeMessage *pMsg = 0;
  1794. PROPVARIANT pv;
  1795. DWORD cMsgs = m_pMsgIDList->cMsgs;
  1796. BOOL fWide = (CF_FILEDESCRIPTORW == pFE->cfFormat);
  1797. WCHAR wszFileExt[32];
  1798. UINT cbDescSize = 0;
  1799. LPWSTR pwszSubject = NULL;
  1800. LPSTR pszSubject = NULL,
  1801. pszFileExt = NULL;
  1802. HRESULT hr = S_OK;
  1803. UINT i = 0;
  1804. // Allocate a FILEGROUPDESCRIPTOR struct large enough to contain the names
  1805. // of all the messages we contain.
  1806. cbDescSize = (fWide ? (sizeof(FILEGROUPDESCRIPTORW) + (sizeof(FILEDESCRIPTORW) * (cMsgs - 1))) :
  1807. (sizeof(FILEGROUPDESCRIPTORA) + (sizeof(FILEDESCRIPTORA) * (cMsgs - 1))));
  1808. IF_NULLEXIT(hGlobal = GlobalAlloc(GHND, cbDescSize));
  1809. pFileGDA = (FILEGROUPDESCRIPTORA *) GlobalLock(hGlobal);
  1810. pFileGDA->cItems = cMsgs;
  1811. pFileGDW = (FILEGROUPDESCRIPTORW *)pFileGDA;
  1812. // Copy the file names one at a time into the pFileGDA struct
  1813. for (; i < cMsgs; i++)
  1814. {
  1815. if (SUCCEEDED(_LoadMessage(i, &pMsg, wszFileExt)))
  1816. {
  1817. if (fWide)
  1818. {
  1819. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &pwszSubject)) && pwszSubject && *pwszSubject)
  1820. {
  1821. wnsprintfW(pFileGDW->fgd[i].cFileName, ARRAYSIZE(pFileGDW->fgd[i].cFileName), L"%.180s.%s", pwszSubject, wszFileExt);
  1822. }
  1823. else
  1824. {
  1825. WCHAR wszBuf[CCHMAX_STRINGRES];
  1826. AthLoadStringW(idsMessageFileName, wszBuf, ARRAYSIZE(wszBuf));
  1827. wnsprintfW(pFileGDW->fgd[i].cFileName, ARRAYSIZE(pFileGDW->fgd[i].cFileName), wszBuf, i + 1, wszFileExt);
  1828. }
  1829. }
  1830. else
  1831. {
  1832. IF_NULLEXIT(pszFileExt = PszToANSI(CP_ACP, wszFileExt));
  1833. if (SUCCEEDED(MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &pwszSubject)) && pwszSubject && *pwszSubject)
  1834. {
  1835. IF_NULLEXIT(pszSubject = PszToANSI(CP_ACP, pwszSubject));
  1836. wnsprintf(pFileGDA->fgd[i].cFileName, ARRAYSIZE(pFileGDW->fgd[i].cFileName), TEXT("%.180s.%s"), pszSubject, pszFileExt);
  1837. }
  1838. else
  1839. {
  1840. TCHAR szBuf[CCHMAX_STRINGRES];
  1841. AthLoadString(idsMessageFileName, szBuf, ARRAYSIZE(szBuf));
  1842. wnsprintf(pFileGDA->fgd[i].cFileName, ARRAYSIZE(pFileGDW->fgd[i].cFileName), szBuf, i + 1, pszFileExt);
  1843. }
  1844. }
  1845. SafeMemFree(pszSubject);
  1846. SafeMemFree(pwszSubject);
  1847. SafeMemFree(pszFileExt);
  1848. if (fWide)
  1849. {
  1850. pFileGDW->fgd[i].dwFlags = FD_FILESIZE | FD_WRITESTIME;
  1851. pFileGDW->fgd[i].nFileSizeHigh = 0;
  1852. pMsg->GetMessageSize(&pFileGDW->fgd[i].nFileSizeLow, 0);
  1853. pv.vt = VT_FILETIME;
  1854. pMsg->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &pv);
  1855. pFileGDW->fgd[i].ftLastWriteTime = pv.filetime;
  1856. CleanupFileNameInPlaceW(pFileGDW->fgd[i].cFileName);
  1857. }
  1858. else
  1859. {
  1860. pFileGDA->fgd[i].dwFlags = FD_FILESIZE | FD_WRITESTIME;
  1861. pFileGDA->fgd[i].nFileSizeHigh = 0;
  1862. pMsg->GetMessageSize(&pFileGDA->fgd[i].nFileSizeLow, 0);
  1863. pv.vt = VT_FILETIME;
  1864. pMsg->GetProp(PIDTOSTR(PID_ATT_SENTTIME), 0, &pv);
  1865. pFileGDA->fgd[i].ftLastWriteTime = pv.filetime;
  1866. CleanupFileNameInPlaceA(CP_ACP, pFileGDA->fgd[i].cFileName);
  1867. }
  1868. SafeRelease(pMsg);
  1869. }
  1870. }
  1871. exit:
  1872. // Put the structure into the STGMEDIUM
  1873. if (hGlobal)
  1874. GlobalUnlock(hGlobal);
  1875. if (SUCCEEDED(hr))
  1876. {
  1877. pStgMedium->hGlobal = hGlobal;
  1878. pStgMedium->pUnkForRelease = NULL;
  1879. pStgMedium->tymed = TYMED_HGLOBAL;
  1880. }
  1881. MemFree(pszSubject);
  1882. MemFree(pwszSubject);
  1883. MemFree(pszFileExt);
  1884. return hr;
  1885. }
  1886. //
  1887. // FUNCTION: CShortcutDataObject::GetData
  1888. //
  1889. // PURPOSE: Returns the object's data in a requested format in the
  1890. // specified storage medium which the object allocates.
  1891. //
  1892. // PARAMETERS:
  1893. // pFE - Pointer to the FORMATETC structure that specifies the
  1894. // format the data is requested in.
  1895. // pStgMedium - Pointer to the STGMEDIUM structure that contains the
  1896. // medium the object allocates and provides the data on.
  1897. //
  1898. // RETURN VALUE:
  1899. // Returns an HRESULT indicating success or failure.
  1900. //
  1901. STDMETHODIMP CShortcutDataObject::GetData(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1902. {
  1903. HRESULT hr;
  1904. // Initialize this
  1905. ZeroMemory(pStgMedium, sizeof(STGMEDIUM));
  1906. // Loop through the format array to see if any of the elements are the
  1907. // same as pFE.
  1908. if (pFE->cfFormat == CF_OESHORTCUT)
  1909. return (_RenderOEShortcut(pFE, pStgMedium));
  1910. else
  1911. return (DV_E_FORMATETC);
  1912. }
  1913. //
  1914. // FUNCTION: CShortcutDataObject::QueryGetData
  1915. //
  1916. // PURPOSE: Determines if a call to GetData() would succeed if it were
  1917. // passed pFE.
  1918. //
  1919. // PARAMETERS:
  1920. // pFE - Pointer to the FORMATETC structure to check to see if the data
  1921. // object supports a particular format.
  1922. //
  1923. // RETURN VALUE:
  1924. // Returns an HRESULT signifying success or failure.
  1925. //
  1926. STDMETHODIMP CShortcutDataObject::QueryGetData(LPFORMATETC pFE)
  1927. {
  1928. // Make sure this is already built
  1929. _BuildFormatEtc(NULL, NULL);
  1930. // Loop through our formats until we find a match
  1931. for (UINT i = 0; i < m_cFormatEtc; i++)
  1932. {
  1933. if (pFE->cfFormat == m_rgFormatEtc[i].cfFormat &&
  1934. pFE->tymed & m_rgFormatEtc[i].tymed)
  1935. {
  1936. return (S_OK);
  1937. }
  1938. }
  1939. return (DV_E_FORMATETC);
  1940. }
  1941. //
  1942. // FUNCTION: CShortcutDataObject::_RenderOEFolder()
  1943. //
  1944. // PURPOSE: Renders the data into the CF_OESHORTCUT format
  1945. //
  1946. HRESULT CShortcutDataObject::_RenderOEShortcut(LPFORMATETC pFE, LPSTGMEDIUM pStgMedium)
  1947. {
  1948. TraceCall("CShortcutDataObject::_RenderOEShortcut");
  1949. HGLOBAL hGlobal;
  1950. UINT *piPos;
  1951. // We only support HGLOBALs
  1952. if (pFE->tymed & TYMED_HGLOBAL)
  1953. {
  1954. // I just love allocating 4 bytes
  1955. hGlobal = GlobalAlloc(GHND, sizeof(FOLDERID));
  1956. if (NULL == hGlobal)
  1957. return (E_OUTOFMEMORY);
  1958. // Local the memory
  1959. piPos = (UINT *) GlobalLock(hGlobal);
  1960. // Set the value
  1961. *piPos = m_iPos;
  1962. // Unlock
  1963. GlobalUnlock(hGlobal);
  1964. // Set up the return value
  1965. pStgMedium->hGlobal = hGlobal;
  1966. pStgMedium->pUnkForRelease = 0;
  1967. pStgMedium->tymed = TYMED_HGLOBAL;
  1968. return (S_OK);
  1969. }
  1970. return (DV_E_TYMED);
  1971. }
  1972. //
  1973. // FUNCTION: CShortcutDataObject::HrBuildFormatEtc()
  1974. //
  1975. // PURPOSE: Builds the list of FORMATETC structs that this object will
  1976. // support. This list is stored in m_rgFormatEtc[].
  1977. //
  1978. // PARAMTERS:
  1979. // [out] ppFE - Returns the arrray of FORMATETC structures
  1980. // [out] pcElt - Returns the size of ppFE
  1981. //
  1982. // RETURN VALUE:
  1983. // S_OK
  1984. //
  1985. HRESULT CShortcutDataObject::_BuildFormatEtc(LPFORMATETC *ppFE, ULONG *pcElt)
  1986. {
  1987. BOOL fNews = FALSE;
  1988. FOLDERINFO rInfo = { 0 };
  1989. // If we haven't built our format list yet, do so first.
  1990. if (FALSE == m_fBuildFE)
  1991. {
  1992. ZeroMemory(&m_rgFormatEtc, sizeof(m_rgFormatEtc));
  1993. m_cFormatEtc = 0;
  1994. SETDefFormatEtc(m_rgFormatEtc[m_cFormatEtc], CF_OESHORTCUT, TYMED_HGLOBAL);
  1995. m_cFormatEtc++;
  1996. m_fBuildFE = TRUE;
  1997. }
  1998. // Return what we have if the caller cares
  1999. if (ppFE && pcElt)
  2000. {
  2001. *ppFE = m_rgFormatEtc;
  2002. *pcElt = m_cFormatEtc;
  2003. }
  2004. return (S_OK);
  2005. }
  2006. //
  2007. // FUNCTION: FIsFileInsertable()
  2008. //
  2009. // PURPOSE: If there is a single file in the specified HDROP, then we check
  2010. // the mime content type of the file to see if it's text/html or
  2011. // text/plain. If so, we open the file and return an IStream on
  2012. // that file.
  2013. //
  2014. // PARAMETERS:
  2015. // <in> hDrop - HDROP handle to check.
  2016. // <out> ppStream - If not NULL and the checks listed above succeed, then
  2017. // this returns a stream pointer on the file.
  2018. //
  2019. // RETURN VALUE:
  2020. // TRUE if the file is insertable as the body of a message
  2021. // FALSE otherwise.
  2022. //
  2023. BOOL FIsFileInsertable(HDROP hDrop, LPSTREAM *ppStream, BOOL* fHTML)
  2024. {
  2025. WCHAR wszFile[MAX_PATH];
  2026. LPWSTR pwszCntType = 0,
  2027. pwszCntSubType = 0,
  2028. pwszCntDesc = 0,
  2029. pwszFName = 0;
  2030. BOOL fReturn = FALSE;
  2031. // If there is more than one file then we attach instead
  2032. if (1 == DragQueryFileWrapW(hDrop, (UINT) -1, NULL, 0))
  2033. {
  2034. // Get the file name
  2035. DragQueryFileWrapW(hDrop, 0, wszFile, ARRAYSIZE(wszFile));
  2036. // Find out it's content type
  2037. MimeOleGetFileInfoW(wszFile, &pwszCntType, &pwszCntSubType, &pwszCntDesc,
  2038. &pwszFName, NULL);
  2039. // See if the content is text/plain or text/html
  2040. if ((0 == StrCmpIW(pwszCntType, L"text")) &&
  2041. ((0 == StrCmpIW(pwszCntSubType, L"plain")) ||
  2042. (0 == StrCmpIW(pwszCntSubType, L"html"))))
  2043. {
  2044. if (ppStream)
  2045. {
  2046. // Get a stream on that file
  2047. if (SUCCEEDED(CreateStreamOnHFileW(wszFile, GENERIC_READ, FILE_SHARE_READ,
  2048. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
  2049. 0, ppStream)))
  2050. {
  2051. fReturn = TRUE;
  2052. *fHTML = !StrCmpIW(pwszCntSubType, L"html");
  2053. }
  2054. }
  2055. }
  2056. }
  2057. MemFree(pwszCntType);
  2058. MemFree(pwszCntSubType);
  2059. MemFree(pwszCntDesc);
  2060. MemFree(pwszFName);
  2061. return (fReturn);
  2062. }
  2063. //
  2064. // FUNCTION: HrAttachHDrop()
  2065. //
  2066. // PURPOSE: Takes an HDROP handle and attaches the files specified by the
  2067. // HDROP to the specified message object.
  2068. //
  2069. // PARAMETERS:
  2070. // <in> pAttList - Pointer to the attachment list of the object we want
  2071. // to attach to.
  2072. // <in> hDrop - HDROP handle with the file information.
  2073. // <in> fMakeLinks - TRUE if the caller wants us to create shortcuts.
  2074. //
  2075. // RETURN VALUE:
  2076. // HRESULT
  2077. //
  2078. HRESULT HrAttachHDrop(HWND hwnd, IMimeMessage *pMessage, HDROP hDrop, BOOL fMakeLinks)
  2079. {
  2080. HRESULT hr = S_OK;
  2081. WCHAR szFile[MAX_PATH];
  2082. UINT cFiles;
  2083. BOOL fFirstDirectory = TRUE;
  2084. HCURSOR hCursor;
  2085. int id;
  2086. // This might take a bit of time
  2087. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2088. // Walk through the drop information
  2089. cFiles = DragQueryFileWrapW(hDrop, (UINT) -1, NULL, 0);
  2090. for (UINT i = 0; i < cFiles; i++)
  2091. {
  2092. // Get the name of the i'th file in the drop
  2093. DragQueryFileWrapW(hDrop, i, szFile, ARRAYSIZE(szFile));
  2094. // We don't allow the user to attach an entire directory, only link.
  2095. if (!fMakeLinks && PathIsDirectoryW(szFile))
  2096. {
  2097. // Only show this error once
  2098. if (fFirstDirectory)
  2099. {
  2100. id = AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena),
  2101. MAKEINTRESOURCEW(idsDropLinkDirs), 0,
  2102. MB_ICONEXCLAMATION | MB_YESNOCANCEL);
  2103. if (id == IDCANCEL)
  2104. {
  2105. hr = E_FAIL;
  2106. goto exit;
  2107. }
  2108. // If we can create a link to directories, then add one now.
  2109. if (id == IDYES)
  2110. HrAddAttachment(pMessage, szFile, NULL, TRUE);
  2111. fFirstDirectory = FALSE;
  2112. }
  2113. }
  2114. else
  2115. HrAddAttachment(pMessage, szFile, NULL, fMakeLinks);
  2116. }
  2117. exit:
  2118. SetCursor(hCursor);
  2119. return (hr);
  2120. }
  2121. //
  2122. // FUNCTION: HrAddAttachment()
  2123. //
  2124. // PURPOSE: Adds a file or stream as an attachment to a message object.
  2125. //
  2126. // PARAMETERS:
  2127. // <in> pAttList - Attachment list for the message to add to.
  2128. // <in> pszName - Name of the file to attach. This is used if pStream
  2129. // is NULL.
  2130. // <in> pStream - Stream to add to the message as an attachment.
  2131. // <in> fLink - TRUE if the caller wants to attach the file as a
  2132. // shortcut.
  2133. //
  2134. // RETURN VALUE:
  2135. // S_OK - The file/stream was attached OK.
  2136. //
  2137. HRESULT HrAddAttachment(IMimeMessage *pMessage, LPWSTR pszName, LPSTREAM pStream, BOOL fLink)
  2138. {
  2139. HRESULT hr;
  2140. HBODY hBody;
  2141. IMimeBodyW *pBody = NULL;
  2142. ULONG cbSize = 0;
  2143. WCHAR szLinkPath[MAX_PATH];
  2144. LPWSTR pszFileNameToUse;
  2145. *szLinkPath = 0;
  2146. // If we need to create a link, then call off and do that
  2147. if(fLink)
  2148. CreateNewShortCut(pszName, szLinkPath, ARRAYSIZE(szLinkPath));
  2149. pszFileNameToUse = *szLinkPath ? szLinkPath : pszName;
  2150. // Add the attachment based on whether it's a stream or file
  2151. if (pStream)
  2152. {
  2153. hr = pMessage->AttachObject(IID_IStream, (LPVOID)pStream, &hBody);
  2154. if (FAILED(hr))
  2155. return hr;
  2156. }
  2157. else
  2158. {
  2159. LPMIMEMESSAGEW pMsgW = NULL;
  2160. hr = pMessage->QueryInterface(IID_IMimeMessageW, (LPVOID*)&pMsgW);
  2161. if (SUCCEEDED(hr))
  2162. hr = pMsgW->AttachFileW(pszFileNameToUse, NULL, &hBody);
  2163. ReleaseObj(pMsgW);
  2164. if (FAILED(hr))
  2165. return hr;
  2166. }
  2167. // Set the display name...
  2168. Assert(hBody);
  2169. hr = pMessage->BindToObject(hBody, IID_IMimeBodyW, (LPVOID *)&pBody);
  2170. if (FAILED(hr))
  2171. return hr;
  2172. pBody->SetDisplayNameW(pszFileNameToUse);
  2173. pBody->Release();
  2174. // Done
  2175. return (S_OK);
  2176. }