#include "stdafx.h" #include "netplace.h" #include "pubwiz.h" #pragma hdrstop // IEnumShellItems - used to expose the transfer list as a set of IShellItems class CTransferItemEnum : public IEnumShellItems { public: CTransferItemEnum(LPCTSTR pszPath, IStorage *pstg, CDPA *_pdpaItems); ~CTransferItemEnum(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG,AddRef)(void); STDMETHOD_(ULONG,Release)(void); STDMETHOD(Next)(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched); STDMETHOD(Skip)(ULONG celt) { return S_OK; } STDMETHOD(Reset)() { _iItem = 0; return S_OK; } STDMETHOD(Clone)(IEnumShellItems **ppenum) { return S_OK; } private: long _cRef; TCHAR _szPath[MAX_PATH]; int _cchPath; IStorage *_pstg; CDPA *_pdpaItems; int _iItem; BOOL _GetNextItem(TRANSFERITEM **ppti); LPTSTR _GetNextComponent(LPTSTR pszPath); }; // A IShellItem that represents an IStorage to the copy engine - limited functionality class CTransferStgItem : public IShellItem { public: CTransferStgItem(LPCTSTR pszPath, int cchName, IStorage *pstg, CDPA *pdpaItems); ~CTransferStgItem(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG,AddRef)(void); STDMETHOD_(ULONG,Release)(void); // IShellItem STDMETHODIMP BindToHandler(IBindCtx *pbc, REFGUID rguidHandler, REFIID riid, void **ppv); STDMETHODIMP GetParent(IShellItem **ppsi) { return E_NOTIMPL; } STDMETHODIMP GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName); STDMETHODIMP GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags); STDMETHODIMP Compare(IShellItem *psi, SICHINTF hint, int *piOrder) { return E_NOTIMPL; } private: long _cRef; TCHAR _szPath[MAX_PATH]; IStorage *_pstg; CDPA *_pdpaItems; }; // IShellItem implementation that will return a storage to anybody // querying it. We generate the in folder name from the path // we are initialized from, and the attributes are fixed for the items. CTransferStgItem::CTransferStgItem(LPCTSTR pszPath, int cchName, IStorage *pstg, CDPA *pdpaItems) : _cRef(1), _pstg(pstg), _pdpaItems(pdpaItems) { StrCpyN(_szPath, pszPath, (int)min(ARRAYSIZE(_szPath), cchName)); _pstg->AddRef(); } CTransferStgItem::~CTransferStgItem() { _pstg->Release(); } ULONG CTransferStgItem::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTransferStgItem::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CTransferStgItem::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CTransferStgItem, IShellItem), // IID_IShellItem {0, 0 }, }; return QISearch(this, qit, riid, ppv); } HRESULT CTransferStgItem::BindToHandler(IBindCtx *pbc, REFGUID rguidHandler, REFIID riid, void **ppv) { HRESULT hr = E_UNEXPECTED; if (rguidHandler == BHID_StorageEnum) { CTransferItemEnum *ptie = new CTransferItemEnum(_szPath, _pstg, _pdpaItems); if (ptie) { hr = ptie->QueryInterface(riid, ppv); ptie->Release(); } else { hr = E_OUTOFMEMORY; } } return hr; } HRESULT CTransferStgItem::GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName) { HRESULT hr = E_UNEXPECTED; if ((sigdnName == SIGDN_PARENTRELATIVEPARSING) || (sigdnName == SIGDN_PARENTRELATIVEEDITING) || (sigdnName == SIGDN_PARENTRELATIVEFORADDRESSBAR)) { hr = SHStrDupW(PathFindFileName(_szPath), ppszName); } return hr; } HRESULT CTransferStgItem::GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags) { *psfgaoFlags = SFGAO_STORAGE; return S_OK; } // enumerator, this takes a DPA and returns IShellItems for the streams and // storages it finds. the storages are contructed dynamically based on // the destination paths specified. CTransferItemEnum::CTransferItemEnum(LPCTSTR pszPath, IStorage *pstg, CDPA *pdpaItems) : _cRef(1), _iItem(0), _pstg(pstg), _pdpaItems(pdpaItems) { StrCpyN(_szPath, pszPath, ARRAYSIZE(_szPath)); _cchPath = lstrlen(_szPath); _pstg->AddRef(); } CTransferItemEnum::~CTransferItemEnum() { _pstg->Release(); } ULONG CTransferItemEnum::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTransferItemEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CTransferItemEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CTransferItemEnum, IEnumShellItems), // IID_IEnumShellItems {0, 0 }, }; return QISearch(this, qit, riid, ppv); } // next enumerator for the items that we have in the DPA, this works by comparing the root // that we have against the items in our list. those who match that criteria can then BOOL CTransferItemEnum::_GetNextItem(TRANSFERITEM **ppti) { BOOL fResult = FALSE; if (_iItem < _pdpaItems->GetPtrCount()) { TRANSFERITEM *pti = _pdpaItems->GetPtr(_iItem); if (StrCmpNI(_szPath, pti->szFilename, _cchPath) == 0) { *ppti = pti; fResult = TRUE; } _iItem++; } return fResult; } LPTSTR CTransferItemEnum::_GetNextComponent(LPTSTR pszPath) { LPTSTR pszResult = pszPath; if (*pszResult == TEXT('\\')) pszResult++; while (*pszResult && (*pszResult != TEXT('\\'))) pszResult++; if (*pszResult == TEXT('\\')) pszResult++; return pszResult; } HRESULT CTransferItemEnum::Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched) { if (!celt || !rgelt) return E_INVALIDARG; // fail bad mojo if (pceltFetched) *pceltFetched = 0; HRESULT hr = S_FALSE; while (SUCCEEDED(hr) && (celt > 0) && (_iItem < _pdpaItems->GetPtrCount())) { // we still have some space in the buffer, and we haven't returned all // the items yet, so we can still itterate over the data set that we have // we have. TRANSFERITEM *pti; if (_GetNextItem(&pti)) { TCHAR szFilename[MAX_PATH]; StrCpy(szFilename, pti->szFilename); // storage or a stream, storages have trailing component names, if we // dont have that then we can assume its a create and pass out a IShellItem. LPTSTR pszNextComponent = _GetNextComponent(szFilename+_cchPath); if (!*pszNextComponent) { // create a wrapped shell item so that we can return the compressed // object back to the caller. if (!pti->psi) { hr = SHCreateShellItem(NULL, NULL, pti->pidl, &pti->psi); if (SUCCEEDED(hr) && pti->fResizeOnUpload) { IImageRecompress *pir; hr = CoCreateInstance(CLSID_ImageRecompress, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImageRecompress, &pir)); if (SUCCEEDED(hr)) { IStream *pstrm; hr = pir->RecompressImage(pti->psi, pti->cxResize, pti->cyResize, pti->iQuality, _pstg, &pstrm); if (hr == S_OK) { STATSTG stat; hr = pstrm->Stat(&stat, STATFLAG_DEFAULT); if (SUCCEEDED(hr)) { IDynamicStorage *pdstg; hr = _pstg->QueryInterface(IID_PPV_ARG(IDynamicStorage, &pdstg)); if (SUCCEEDED(hr)) { IShellItem *psi; hr = pdstg->BindToItem(stat.pwcsName, IID_PPV_ARG(IShellItem, &psi)); if (SUCCEEDED(hr)) { IUnknown_Set((IUnknown**)&pti->psi, psi); } pdstg->Release(); } CoTaskMemFree(stat.pwcsName); } pstrm->Release(); } pir->Release(); } } } if (SUCCEEDED(hr)) { hr = pti->psi->QueryInterface(IID_PPV_ARG(IShellItem, rgelt)); if (SUCCEEDED(hr)) { rgelt++; celt--; if (pceltFetched) { (*pceltFetched)++; } } } } else { // Its a storage, so lets create a dummy IShellItem that represents this // and pass it to the caller. Then walk forward until we have skipped // all the items in this storage. int cchName = (int)(pszNextComponent-szFilename); CTransferStgItem *ptsi = new CTransferStgItem(szFilename, cchName, _pstg, _pdpaItems); if (ptsi) { hr = ptsi->QueryInterface(IID_PPV_ARG(IShellItem, rgelt++)); if (SUCCEEDED(hr)) { celt--; if (pceltFetched) { (*pceltFetched)++; } } ptsi->Release(); } else { hr = E_OUTOFMEMORY; } // Skip the children of this storage TRANSFERITEM *ptiNext; while (_GetNextItem(&ptiNext)) { if (0 != StrCmpNI(ptiNext->szFilename, szFilename, cchName)) { _iItem--; // we hit an item that doesn't match the criteria break; } } } } } return hr; } // all this code relates to using the RDR to transfer items to the destination site // rather than using the manifest to handle the transfer via a HTTP POST. class CTransferThread : IUnknown { public: CTransferThread(); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); HRESULT BeginTransfer(TRANSFERINFO *pti, CDPA *pdpaItems, ITransferAdviseSink *ptas); protected: ~CTransferThread(); static DWORD CALLBACK s_ThreadProc(void *pv); DWORD _ThreadProc(); HRESULT _FixUpDestination(); HRESULT _InitSourceEnum(IEnumShellItems **ppesi); HRESULT _SetProgress(DWORD dwCompleted, DWORD dwTotal); LONG _cRef; TRANSFERINFO _ti; CDPA _dpaItems; IStream *_pstrmSink; CNetworkPlace _np; }; // Main transfer thread object, this calls the shell item processor to copy // items using the manifest we received back from the site. CTransferThread::CTransferThread() : _cRef(1) { DllAddRef(); } CTransferThread::~CTransferThread() { ATOMICRELEASE(_pstrmSink); _dpaItems.DestroyCallback(_FreeTransferItems, NULL); DllRelease(); } ULONG CTransferThread::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTransferThread::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CTransferThread::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { { 0 }, }; return QISearch(this, qit, riid, ppv); } // handle fixing up the server information to the correct site, this handles the case // wher ethe server name is www.msnusers.com and we need to redirect to the correct place. HRESULT CTransferThread::_FixUpDestination() { LPCTSTR pszMSN = TEXT("http://www.msnusers.com/"); int cchMSN = lstrlen(pszMSN); HRESULT hr = S_OK; if (0 == StrCmpNI(_ti.szFileTarget, pszMSN, cchMSN)) { // this is the MSN server, therefore we need to perform // a PROPFIND to the root of the community to force it to create // the folders that we are going to be publishing into, if we don't // do this then we end up getting a file not found error back from the // DAV RDR - we should get the MSN dudes to fix this so we don't need to // keep this around. WCHAR szURL[INTERNET_MAX_URL_LENGTH]; StrCpy(szURL, _ti.szFileTarget); *StrChr(szURL + cchMSN, L'/') = L'\0'; CNetworkPlace np; hr = np.SetTarget(_ti.hwnd, _ti.szFileTarget, NPTF_SILENT|NPTF_VALIDATE); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = np.GetIDList(_ti.hwnd, &pidl); if (SUCCEEDED(hr)) { ILFree(pidl); } } } return hr; } // being the transfer of items, by creating a background thread which handles the upload. HRESULT CTransferThread::BeginTransfer(TRANSFERINFO *pti, CDPA *pdpaItems, ITransferAdviseSink *ptas) { _ti = *pti; _dpaItems.Attach(pdpaItems->Detach()); // we have ownership of the DPA now HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_ITransferAdviseSink, ptas, &_pstrmSink); if (SUCCEEDED(hr)) { AddRef(); hr = SHCreateThread(s_ThreadProc, this, CTF_INSIST | CTF_COINIT, NULL) ? S_OK:E_FAIL; if (FAILED(hr)) { Release(); } } return hr; } DWORD CALLBACK CTransferThread::s_ThreadProc(void *pv) { CTransferThread *pTransfer = (CTransferThread*)pv; return pTransfer->_ThreadProc(); } HRESULT CTransferThread::_InitSourceEnum(IEnumShellItems **ppesi) { IStorage *pstg; HRESULT hr = CoCreateInstance(CLSID_DynamicStorage, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStorage, &pstg)); { CTransferItemEnum *ptie = new CTransferItemEnum(L"", pstg, &_dpaItems); if (ptie) { hr = ptie->QueryInterface(IID_PPV_ARG(IEnumShellItems, ppesi)); ptie->Release(); } else { hr = E_OUTOFMEMORY; } pstg->Release(); } return hr; } DWORD CTransferThread::_ThreadProc() { IEnumShellItems *penum =NULL; HRESULT hr = _InitSourceEnum(&penum); if (SUCCEEDED(hr)) { hr = _FixUpDestination(); // apply fixup for MSN etc. if (SUCCEEDED(hr)) { hr = _np.SetTarget(_ti.hwnd, _ti.szFileTarget, NPTF_SILENT|NPTF_VALIDATE); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = _np.GetIDList(_ti.hwnd, &pidl); if (SUCCEEDED(hr)) { IShellItem *psiDest; hr = SHCreateShellItem(NULL, NULL, pidl, &psiDest); if (SUCCEEDED(hr)) { IStorageProcessor *psp; hr = CoCreateInstance(CLSID_StorageProcessor, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStorageProcessor, &psp)); if (SUCCEEDED(hr)) { DWORD dwCookie = 0; ITransferAdviseSink *ptas; hr = CoGetInterfaceAndReleaseStream(_pstrmSink, IID_PPV_ARG(ITransferAdviseSink, &ptas)); _pstrmSink = NULL; if (SUCCEEDED(hr)) { hr = psp->Advise(ptas, &dwCookie); ptas->Release(); } hr = psp->Run(penum, psiDest, STGOP_COPY, STOPT_NOPROGRESSUI); if (dwCookie) psp->Unadvise(dwCookie); psp->Release(); } psiDest->Release(); } ILFree(pidl); } } } penum->Release(); } // notify the fg thread that this has happened. PostMessage(_ti.hwnd, PWM_TRANSFERCOMPLETE, 0, (LPARAM)hr); // were done transfering the files so lets start to clear up - in particular // lets attempt to create the net work place. if (_ti.szLinkTarget[0] && !(_ti.dwFlags & SHPWHF_NONETPLACECREATE)) { CNetworkPlace np; if (SUCCEEDED(np.SetTarget(_ti.hwnd, _ti.szLinkTarget, 0x0))) { if (_ti.szLinkName[0]) np.SetName(NULL, _ti.szLinkName); if (_ti.szLinkDesc[0]) np.SetDescription(_ti.szLinkDesc); np.CreatePlace(_ti.hwnd, FALSE); } } Release(); return 0; } // helper to create and initialize the transfer engine HRESULT PublishViaCopyEngine(TRANSFERINFO *pti, CDPA *pdpaItems, ITransferAdviseSink *ptas) { CTransferThread *ptt = new CTransferThread(); if (!ptt) return E_OUTOFMEMORY; HRESULT hr = ptt->BeginTransfer(pti, pdpaItems, ptas); ptt->Release(); return hr; }