Source code of Windows XP (NT5)
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.

603 lines
18 KiB

  1. #include "stdafx.h"
  2. #include "netplace.h"
  3. #include "pubwiz.h"
  4. #pragma hdrstop
  5. // IEnumShellItems - used to expose the transfer list as a set of IShellItems
  6. class CTransferItemEnum : public IEnumShellItems
  7. {
  8. public:
  9. CTransferItemEnum(LPCTSTR pszPath, IStorage *pstg, CDPA<TRANSFERITEM> *_pdpaItems);
  10. ~CTransferItemEnum();
  11. // IUnknown
  12. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  13. STDMETHOD_(ULONG,AddRef)(void);
  14. STDMETHOD_(ULONG,Release)(void);
  15. STDMETHOD(Next)(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched);
  16. STDMETHOD(Skip)(ULONG celt)
  17. { return S_OK; }
  18. STDMETHOD(Reset)()
  19. { _iItem = 0; return S_OK; }
  20. STDMETHOD(Clone)(IEnumShellItems **ppenum)
  21. { return S_OK; }
  22. private:
  23. long _cRef;
  24. TCHAR _szPath[MAX_PATH];
  25. int _cchPath;
  26. IStorage *_pstg;
  27. CDPA<TRANSFERITEM> *_pdpaItems;
  28. int _iItem;
  29. BOOL _GetNextItem(TRANSFERITEM **ppti);
  30. LPTSTR _GetNextComponent(LPTSTR pszPath);
  31. };
  32. // A IShellItem that represents an IStorage to the copy engine - limited functionality
  33. class CTransferStgItem : public IShellItem
  34. {
  35. public:
  36. CTransferStgItem(LPCTSTR pszPath, int cchName, IStorage *pstg, CDPA<TRANSFERITEM> *pdpaItems);
  37. ~CTransferStgItem();
  38. // IUnknown
  39. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  40. STDMETHOD_(ULONG,AddRef)(void);
  41. STDMETHOD_(ULONG,Release)(void);
  42. // IShellItem
  43. STDMETHODIMP BindToHandler(IBindCtx *pbc, REFGUID rguidHandler, REFIID riid, void **ppv);
  44. STDMETHODIMP GetParent(IShellItem **ppsi)
  45. { return E_NOTIMPL; }
  46. STDMETHODIMP GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName);
  47. STDMETHODIMP GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags);
  48. STDMETHODIMP Compare(IShellItem *psi, SICHINTF hint, int *piOrder)
  49. { return E_NOTIMPL; }
  50. private:
  51. long _cRef;
  52. TCHAR _szPath[MAX_PATH];
  53. IStorage *_pstg;
  54. CDPA<TRANSFERITEM> *_pdpaItems;
  55. };
  56. // IShellItem implementation that will return a storage to anybody
  57. // querying it. We generate the in folder name from the path
  58. // we are initialized from, and the attributes are fixed for the items.
  59. CTransferStgItem::CTransferStgItem(LPCTSTR pszPath, int cchName, IStorage *pstg, CDPA<TRANSFERITEM> *pdpaItems) :
  60. _cRef(1), _pstg(pstg), _pdpaItems(pdpaItems)
  61. {
  62. StrCpyN(_szPath, pszPath, (int)min(ARRAYSIZE(_szPath), cchName));
  63. _pstg->AddRef();
  64. }
  65. CTransferStgItem::~CTransferStgItem()
  66. {
  67. _pstg->Release();
  68. }
  69. ULONG CTransferStgItem::AddRef()
  70. {
  71. return InterlockedIncrement(&_cRef);
  72. }
  73. ULONG CTransferStgItem::Release()
  74. {
  75. if (InterlockedDecrement(&_cRef))
  76. return _cRef;
  77. delete this;
  78. return 0;
  79. }
  80. HRESULT CTransferStgItem::QueryInterface(REFIID riid, void **ppv)
  81. {
  82. static const QITAB qit[] =
  83. {
  84. QITABENT(CTransferStgItem, IShellItem), // IID_IShellItem
  85. {0, 0 },
  86. };
  87. return QISearch(this, qit, riid, ppv);
  88. }
  89. HRESULT CTransferStgItem::BindToHandler(IBindCtx *pbc, REFGUID rguidHandler, REFIID riid, void **ppv)
  90. {
  91. HRESULT hr = E_UNEXPECTED;
  92. if (rguidHandler == BHID_StorageEnum)
  93. {
  94. CTransferItemEnum *ptie = new CTransferItemEnum(_szPath, _pstg, _pdpaItems);
  95. if (ptie)
  96. {
  97. hr = ptie->QueryInterface(riid, ppv);
  98. ptie->Release();
  99. }
  100. else
  101. {
  102. hr = E_OUTOFMEMORY;
  103. }
  104. }
  105. return hr;
  106. }
  107. HRESULT CTransferStgItem::GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName)
  108. {
  109. HRESULT hr = E_UNEXPECTED;
  110. if ((sigdnName == SIGDN_PARENTRELATIVEPARSING) ||
  111. (sigdnName == SIGDN_PARENTRELATIVEEDITING) ||
  112. (sigdnName == SIGDN_PARENTRELATIVEFORADDRESSBAR))
  113. {
  114. hr = SHStrDupW(PathFindFileName(_szPath), ppszName);
  115. }
  116. return hr;
  117. }
  118. HRESULT CTransferStgItem::GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags)
  119. {
  120. *psfgaoFlags = SFGAO_STORAGE;
  121. return S_OK;
  122. }
  123. // enumerator, this takes a DPA and returns IShellItems for the streams and
  124. // storages it finds. the storages are contructed dynamically based on
  125. // the destination paths specified.
  126. CTransferItemEnum::CTransferItemEnum(LPCTSTR pszPath, IStorage *pstg, CDPA<TRANSFERITEM> *pdpaItems) :
  127. _cRef(1), _iItem(0), _pstg(pstg), _pdpaItems(pdpaItems)
  128. {
  129. StrCpyN(_szPath, pszPath, ARRAYSIZE(_szPath));
  130. _cchPath = lstrlen(_szPath);
  131. _pstg->AddRef();
  132. }
  133. CTransferItemEnum::~CTransferItemEnum()
  134. {
  135. _pstg->Release();
  136. }
  137. ULONG CTransferItemEnum::AddRef()
  138. {
  139. return InterlockedIncrement(&_cRef);
  140. }
  141. ULONG CTransferItemEnum::Release()
  142. {
  143. if (InterlockedDecrement(&_cRef))
  144. return _cRef;
  145. delete this;
  146. return 0;
  147. }
  148. HRESULT CTransferItemEnum::QueryInterface(REFIID riid, void **ppv)
  149. {
  150. static const QITAB qit[] =
  151. {
  152. QITABENT(CTransferItemEnum, IEnumShellItems), // IID_IEnumShellItems
  153. {0, 0 },
  154. };
  155. return QISearch(this, qit, riid, ppv);
  156. }
  157. // next enumerator for the items that we have in the DPA, this works by comparing the root
  158. // that we have against the items in our list. those who match that criteria can then
  159. BOOL CTransferItemEnum::_GetNextItem(TRANSFERITEM **ppti)
  160. {
  161. BOOL fResult = FALSE;
  162. if (_iItem < _pdpaItems->GetPtrCount())
  163. {
  164. TRANSFERITEM *pti = _pdpaItems->GetPtr(_iItem);
  165. if (StrCmpNI(_szPath, pti->szFilename, _cchPath) == 0)
  166. {
  167. *ppti = pti;
  168. fResult = TRUE;
  169. }
  170. _iItem++;
  171. }
  172. return fResult;
  173. }
  174. LPTSTR CTransferItemEnum::_GetNextComponent(LPTSTR pszPath)
  175. {
  176. LPTSTR pszResult = pszPath;
  177. if (*pszResult == TEXT('\\'))
  178. pszResult++;
  179. while (*pszResult && (*pszResult != TEXT('\\')))
  180. pszResult++;
  181. if (*pszResult == TEXT('\\'))
  182. pszResult++;
  183. return pszResult;
  184. }
  185. HRESULT CTransferItemEnum::Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched)
  186. {
  187. if (!celt || !rgelt)
  188. return E_INVALIDARG; // fail bad mojo
  189. if (pceltFetched)
  190. *pceltFetched = 0;
  191. HRESULT hr = S_FALSE;
  192. while (SUCCEEDED(hr) && (celt > 0) && (_iItem < _pdpaItems->GetPtrCount()))
  193. {
  194. // we still have some space in the buffer, and we haven't returned all
  195. // the items yet, so we can still itterate over the data set that we have
  196. // we have.
  197. TRANSFERITEM *pti;
  198. if (_GetNextItem(&pti))
  199. {
  200. TCHAR szFilename[MAX_PATH];
  201. StrCpy(szFilename, pti->szFilename);
  202. // storage or a stream, storages have trailing component names, if we
  203. // dont have that then we can assume its a create and pass out a IShellItem.
  204. LPTSTR pszNextComponent = _GetNextComponent(szFilename+_cchPath);
  205. if (!*pszNextComponent)
  206. {
  207. // create a wrapped shell item so that we can return the compressed
  208. // object back to the caller.
  209. if (!pti->psi)
  210. {
  211. hr = SHCreateShellItem(NULL, NULL, pti->pidl, &pti->psi);
  212. if (SUCCEEDED(hr) && pti->fResizeOnUpload)
  213. {
  214. IImageRecompress *pir;
  215. hr = CoCreateInstance(CLSID_ImageRecompress, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImageRecompress, &pir));
  216. if (SUCCEEDED(hr))
  217. {
  218. IStream *pstrm;
  219. hr = pir->RecompressImage(pti->psi, pti->cxResize, pti->cyResize, pti->iQuality, _pstg, &pstrm);
  220. if (hr == S_OK)
  221. {
  222. STATSTG stat;
  223. hr = pstrm->Stat(&stat, STATFLAG_DEFAULT);
  224. if (SUCCEEDED(hr))
  225. {
  226. IDynamicStorage *pdstg;
  227. hr = _pstg->QueryInterface(IID_PPV_ARG(IDynamicStorage, &pdstg));
  228. if (SUCCEEDED(hr))
  229. {
  230. IShellItem *psi;
  231. hr = pdstg->BindToItem(stat.pwcsName, IID_PPV_ARG(IShellItem, &psi));
  232. if (SUCCEEDED(hr))
  233. {
  234. IUnknown_Set((IUnknown**)&pti->psi, psi);
  235. }
  236. pdstg->Release();
  237. }
  238. CoTaskMemFree(stat.pwcsName);
  239. }
  240. pstrm->Release();
  241. }
  242. pir->Release();
  243. }
  244. }
  245. }
  246. if (SUCCEEDED(hr))
  247. {
  248. hr = pti->psi->QueryInterface(IID_PPV_ARG(IShellItem, rgelt));
  249. if (SUCCEEDED(hr))
  250. {
  251. rgelt++;
  252. celt--;
  253. if (pceltFetched)
  254. {
  255. (*pceltFetched)++;
  256. }
  257. }
  258. }
  259. }
  260. else
  261. {
  262. // Its a storage, so lets create a dummy IShellItem that represents this
  263. // and pass it to the caller. Then walk forward until we have skipped
  264. // all the items in this storage.
  265. int cchName = (int)(pszNextComponent-szFilename);
  266. CTransferStgItem *ptsi = new CTransferStgItem(szFilename, cchName, _pstg, _pdpaItems);
  267. if (ptsi)
  268. {
  269. hr = ptsi->QueryInterface(IID_PPV_ARG(IShellItem, rgelt++));
  270. if (SUCCEEDED(hr))
  271. {
  272. celt--;
  273. if (pceltFetched)
  274. {
  275. (*pceltFetched)++;
  276. }
  277. }
  278. ptsi->Release();
  279. }
  280. else
  281. {
  282. hr = E_OUTOFMEMORY;
  283. }
  284. // Skip the children of this storage
  285. TRANSFERITEM *ptiNext;
  286. while (_GetNextItem(&ptiNext))
  287. {
  288. if (0 != StrCmpNI(ptiNext->szFilename, szFilename, cchName))
  289. {
  290. _iItem--; // we hit an item that doesn't match the criteria
  291. break;
  292. }
  293. }
  294. }
  295. }
  296. }
  297. return hr;
  298. }
  299. // all this code relates to using the RDR to transfer items to the destination site
  300. // rather than using the manifest to handle the transfer via a HTTP POST.
  301. class CTransferThread : IUnknown
  302. {
  303. public:
  304. CTransferThread();
  305. STDMETHODIMP_(ULONG) AddRef(void);
  306. STDMETHODIMP_(ULONG) Release(void);
  307. STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  308. HRESULT BeginTransfer(TRANSFERINFO *pti, CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas);
  309. protected:
  310. ~CTransferThread();
  311. static DWORD CALLBACK s_ThreadProc(void *pv);
  312. DWORD _ThreadProc();
  313. HRESULT _FixUpDestination();
  314. HRESULT _InitSourceEnum(IEnumShellItems **ppesi);
  315. HRESULT _SetProgress(DWORD dwCompleted, DWORD dwTotal);
  316. LONG _cRef;
  317. TRANSFERINFO _ti;
  318. CDPA<TRANSFERITEM> _dpaItems;
  319. IStream *_pstrmSink;
  320. CNetworkPlace _np;
  321. };
  322. // Main transfer thread object, this calls the shell item processor to copy
  323. // items using the manifest we received back from the site.
  324. CTransferThread::CTransferThread() :
  325. _cRef(1)
  326. {
  327. DllAddRef();
  328. }
  329. CTransferThread::~CTransferThread()
  330. {
  331. ATOMICRELEASE(_pstrmSink);
  332. _dpaItems.DestroyCallback(_FreeTransferItems, NULL);
  333. DllRelease();
  334. }
  335. ULONG CTransferThread::AddRef()
  336. {
  337. return InterlockedIncrement(&_cRef);
  338. }
  339. ULONG CTransferThread::Release()
  340. {
  341. if (InterlockedDecrement(&_cRef))
  342. return _cRef;
  343. delete this;
  344. return 0;
  345. }
  346. HRESULT CTransferThread::QueryInterface(REFIID riid, void **ppv)
  347. {
  348. static const QITAB qit[] =
  349. {
  350. { 0 },
  351. };
  352. return QISearch(this, qit, riid, ppv);
  353. }
  354. // handle fixing up the server information to the correct site, this handles the case
  355. // wher ethe server name is www.msnusers.com and we need to redirect to the correct place.
  356. HRESULT CTransferThread::_FixUpDestination()
  357. {
  358. LPCTSTR pszMSN = TEXT("http://www.msnusers.com/");
  359. int cchMSN = lstrlen(pszMSN);
  360. HRESULT hr = S_OK;
  361. if (0 == StrCmpNI(_ti.szFileTarget, pszMSN, cchMSN))
  362. {
  363. // this is the MSN server, therefore we need to perform
  364. // a PROPFIND to the root of the community to force it to create
  365. // the folders that we are going to be publishing into, if we don't
  366. // do this then we end up getting a file not found error back from the
  367. // DAV RDR - we should get the MSN dudes to fix this so we don't need to
  368. // keep this around.
  369. WCHAR szURL[INTERNET_MAX_URL_LENGTH];
  370. StrCpy(szURL, _ti.szFileTarget);
  371. *StrChr(szURL + cchMSN, L'/') = L'\0';
  372. CNetworkPlace np;
  373. hr = np.SetTarget(_ti.hwnd, _ti.szFileTarget, NPTF_SILENT|NPTF_VALIDATE);
  374. if (SUCCEEDED(hr))
  375. {
  376. LPITEMIDLIST pidl;
  377. hr = np.GetIDList(_ti.hwnd, &pidl);
  378. if (SUCCEEDED(hr))
  379. {
  380. ILFree(pidl);
  381. }
  382. }
  383. }
  384. return hr;
  385. }
  386. // being the transfer of items, by creating a background thread which handles the upload.
  387. HRESULT CTransferThread::BeginTransfer(TRANSFERINFO *pti, CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas)
  388. {
  389. _ti = *pti;
  390. _dpaItems.Attach(pdpaItems->Detach()); // we have ownership of the DPA now
  391. HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_ITransferAdviseSink, ptas, &_pstrmSink);
  392. if (SUCCEEDED(hr))
  393. {
  394. AddRef();
  395. hr = SHCreateThread(s_ThreadProc, this, CTF_INSIST | CTF_COINIT, NULL) ? S_OK:E_FAIL;
  396. if (FAILED(hr))
  397. {
  398. Release();
  399. }
  400. }
  401. return hr;
  402. }
  403. DWORD CALLBACK CTransferThread::s_ThreadProc(void *pv)
  404. {
  405. CTransferThread *pTransfer = (CTransferThread*)pv;
  406. return pTransfer->_ThreadProc();
  407. }
  408. HRESULT CTransferThread::_InitSourceEnum(IEnumShellItems **ppesi)
  409. {
  410. IStorage *pstg;
  411. HRESULT hr = CoCreateInstance(CLSID_DynamicStorage, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStorage, &pstg));
  412. {
  413. CTransferItemEnum *ptie = new CTransferItemEnum(L"", pstg, &_dpaItems);
  414. if (ptie)
  415. {
  416. hr = ptie->QueryInterface(IID_PPV_ARG(IEnumShellItems, ppesi));
  417. ptie->Release();
  418. }
  419. else
  420. {
  421. hr = E_OUTOFMEMORY;
  422. }
  423. pstg->Release();
  424. }
  425. return hr;
  426. }
  427. DWORD CTransferThread::_ThreadProc()
  428. {
  429. IEnumShellItems *penum =NULL;
  430. HRESULT hr = _InitSourceEnum(&penum);
  431. if (SUCCEEDED(hr))
  432. {
  433. hr = _FixUpDestination(); // apply fixup for MSN etc.
  434. if (SUCCEEDED(hr))
  435. {
  436. hr = _np.SetTarget(_ti.hwnd, _ti.szFileTarget, NPTF_SILENT|NPTF_VALIDATE);
  437. if (SUCCEEDED(hr))
  438. {
  439. LPITEMIDLIST pidl;
  440. hr = _np.GetIDList(_ti.hwnd, &pidl);
  441. if (SUCCEEDED(hr))
  442. {
  443. IShellItem *psiDest;
  444. hr = SHCreateShellItem(NULL, NULL, pidl, &psiDest);
  445. if (SUCCEEDED(hr))
  446. {
  447. IStorageProcessor *psp;
  448. hr = CoCreateInstance(CLSID_StorageProcessor, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStorageProcessor, &psp));
  449. if (SUCCEEDED(hr))
  450. {
  451. DWORD dwCookie = 0;
  452. ITransferAdviseSink *ptas;
  453. hr = CoGetInterfaceAndReleaseStream(_pstrmSink, IID_PPV_ARG(ITransferAdviseSink, &ptas));
  454. _pstrmSink = NULL;
  455. if (SUCCEEDED(hr))
  456. {
  457. hr = psp->Advise(ptas, &dwCookie);
  458. ptas->Release();
  459. }
  460. hr = psp->Run(penum, psiDest, STGOP_COPY, STOPT_NOPROGRESSUI);
  461. if (dwCookie)
  462. psp->Unadvise(dwCookie);
  463. psp->Release();
  464. }
  465. psiDest->Release();
  466. }
  467. ILFree(pidl);
  468. }
  469. }
  470. }
  471. penum->Release();
  472. }
  473. // notify the fg thread that this has happened.
  474. PostMessage(_ti.hwnd, PWM_TRANSFERCOMPLETE, 0, (LPARAM)hr);
  475. // were done transfering the files so lets start to clear up - in particular
  476. // lets attempt to create the net work place.
  477. if (_ti.szLinkTarget[0] && !(_ti.dwFlags & SHPWHF_NONETPLACECREATE))
  478. {
  479. CNetworkPlace np;
  480. if (SUCCEEDED(np.SetTarget(_ti.hwnd, _ti.szLinkTarget, 0x0)))
  481. {
  482. if (_ti.szLinkName[0])
  483. np.SetName(NULL, _ti.szLinkName);
  484. if (_ti.szLinkDesc[0])
  485. np.SetDescription(_ti.szLinkDesc);
  486. np.CreatePlace(_ti.hwnd, FALSE);
  487. }
  488. }
  489. Release();
  490. return 0;
  491. }
  492. // helper to create and initialize the transfer engine
  493. HRESULT PublishViaCopyEngine(TRANSFERINFO *pti, CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas)
  494. {
  495. CTransferThread *ptt = new CTransferThread();
  496. if (!ptt)
  497. return E_OUTOFMEMORY;
  498. HRESULT hr = ptt->BeginTransfer(pti, pdpaItems, ptas);
  499. ptt->Release();
  500. return hr;
  501. }