windows-nt/Source/XPSP1/NT/shell/ext/netplwiz/pubcopy.cpp
2020-09-26 16:20:57 +08:00

604 lines
18 KiB
C++

#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<TRANSFERITEM> *_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<TRANSFERITEM> *_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<TRANSFERITEM> *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<TRANSFERITEM> *_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<TRANSFERITEM> *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<TRANSFERITEM> *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<TRANSFERITEM> *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<TRANSFERITEM> _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<TRANSFERITEM> *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<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas)
{
CTransferThread *ptt = new CTransferThread();
if (!ptt)
return E_OUTOFMEMORY;
HRESULT hr = ptt->BeginTransfer(pti, pdpaItems, ptas);
ptt->Release();
return hr;
}