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.
748 lines
22 KiB
748 lines
22 KiB
#include "stdafx.h"
|
|
#include "pubwiz.h"
|
|
#include "netplace.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
// this code works by building a multi-part post
|
|
|
|
LARGE_INTEGER g_li0 = {0};
|
|
|
|
|
|
// IStream class that wraps up the multi-part post into a single object.
|
|
|
|
#define BOUNDARY TEXT("------WindowsPublishWizard")
|
|
|
|
LPCTSTR c_pszBoundary = (TEXT("--") BOUNDARY);
|
|
LPCTSTR c_pszBoundaryEOF = (TEXT("\r\n") TEXT("--") BOUNDARY TEXT("--"));
|
|
LPWSTR c_pszContentType = (TEXT("multipart/form-data; boundary=") BOUNDARY);
|
|
|
|
LPCTSTR c_szFmtContent = (TEXT("content-disposition: form-data; name=\"%s\""));
|
|
LPCTSTR c_szFmtFilename = (TEXT("; filename=\"%s\""));
|
|
LPCTSTR c_szCRLF = (TEXT("\r\n"));
|
|
|
|
|
|
/* 8c1e9993-7a84-431d-8c03-527f0fb147c5 */
|
|
CLSID IID_IPostStream = {0x8c1e9993, 0x7a84, 0x431d, {0x8c, 0x03, 0x52, 0x7f, 0x0f, 0xb1, 0x47, 0xc5}};
|
|
|
|
DECLARE_INTERFACE_(IPostStream, IStream)
|
|
{
|
|
// *** IUnknown methods ***
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE;
|
|
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG,Release) (THIS) PURE;
|
|
|
|
// **** IPostStream ****
|
|
STDMETHOD(SetTransferSink)(ITransferAdviseSink *ptas, ULONGLONG ulTotal, ULONGLONG ulCurrent);
|
|
};
|
|
|
|
|
|
// stream wrapper that expoes the binary data for the file as a multi-part stream object
|
|
|
|
class CPostStream : public IPostStream
|
|
{
|
|
public:
|
|
CPostStream();
|
|
|
|
HRESULT Initialize(IStorage *pstg, TRANSFERITEM *pti);
|
|
|
|
// *** IUnknown methods ***
|
|
STDMETHOD(QueryInterface)( REFIID riid, void **ppv);
|
|
STDMETHOD_(ULONG,AddRef)();
|
|
STDMETHOD_(ULONG,Release)();
|
|
|
|
// *** IStream methods ***
|
|
STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead);
|
|
STDMETHOD(Write)(VOID const *pv, ULONG cb, ULONG *pcbWritten)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(Seek)(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(SetSize)(ULARGE_INTEGER libNewSize)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(CopyTo)(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(Commit)(DWORD grfCommitFlags)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(Revert)()
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(LockRegion)(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(UnlockRegion)(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHOD(Stat)(STATSTG *pstatstg, DWORD grfStatFlag);
|
|
STDMETHOD(Clone)(IStream **ppstm)
|
|
{ return E_NOTIMPL; }
|
|
|
|
STDMETHOD(SetTransferSink)(ITransferAdviseSink *ptas, ULONGLONG ulTotal, ULONGLONG ulCurrent);
|
|
|
|
protected:
|
|
~CPostStream();
|
|
|
|
static int s_ReleaseStream(IStream *pstrm, void *pv);
|
|
|
|
HRESULT _WriteString(IStream *pstrm, LPCTSTR pszString);
|
|
HRESULT _WriteStringCRLF(IStream *pstrm, LPCTSTR pszString);
|
|
HRESULT _AddBoundaryMarker(IStream *pstrm, BOOL fLeadingCRLF, LPCTSTR pszName, LPCTSTR pszFilename);
|
|
HRESULT _AddStream(IStream *pstrm);
|
|
HRESULT _CreateMemoryStream(REFIID riid, void **ppv);
|
|
|
|
LONG _cRef;
|
|
|
|
IShellItem *_psi;
|
|
ITransferAdviseSink *_ptas;
|
|
|
|
// stream array we use to transfer the bits
|
|
CDPA<IStream> _dpaStreams;
|
|
int _iCurStream;
|
|
|
|
// current seek pointers into the stream
|
|
ULONGLONG _ulCurrent;
|
|
ULONGLONG _ulTotal;
|
|
|
|
// current seek pointers overal into the transfer
|
|
ULONGLONG _ulOverallCurrent;
|
|
ULONGLONG _ulOverallTotal;
|
|
};
|
|
|
|
|
|
// unknown / qi handler
|
|
|
|
CPostStream::CPostStream() :
|
|
_cRef(1)
|
|
{
|
|
}
|
|
|
|
CPostStream::~CPostStream()
|
|
{
|
|
if (_dpaStreams != NULL)
|
|
{
|
|
_dpaStreams.DestroyCallback(s_ReleaseStream, this);
|
|
_iCurStream = 0;
|
|
}
|
|
|
|
if (_ptas)
|
|
_ptas->Release();
|
|
}
|
|
|
|
|
|
// handle IUnknown
|
|
|
|
ULONG CPostStream::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CPostStream::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
HRESULT CPostStream::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CPostStream, IStream), // IID_IStream
|
|
QITABENT(CPostStream, IPostStream), // IID_IPostStream
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
|
|
|
|
// handle writing data into a stream for building the post
|
|
|
|
HRESULT CPostStream::_WriteString(IStream *pstrm, LPCTSTR pszString)
|
|
{
|
|
// T2A conversion, can we do a UTF8 encode at this point?
|
|
USES_CONVERSION;
|
|
ULONG cb = lstrlen(pszString) * sizeof(CHAR);
|
|
return pstrm->Write(T2A(pszString), cb, NULL);
|
|
}
|
|
|
|
HRESULT CPostStream::_WriteStringCRLF(IStream *pstrm, LPCTSTR pszString)
|
|
{
|
|
HRESULT hr = _WriteString(pstrm, pszString);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _WriteString(pstrm, c_szCRLF);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPostStream::_AddBoundaryMarker(IStream *pstrm, BOOL fLeadingCRLF, LPCTSTR pszName, LPCTSTR pszFilename)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// add the boundary marker
|
|
if (fLeadingCRLF)
|
|
hr = _WriteString(pstrm, c_szCRLF);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _WriteStringCRLF(pstrm, c_pszBoundary);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
|
|
// format up the content disp + name attribute
|
|
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), c_szFmtContent, pszName);
|
|
hr = _WriteString(pstrm, szBuffer);
|
|
|
|
// if we have a filename then lets put that into the line also
|
|
if (SUCCEEDED(hr) && pszFilename)
|
|
{
|
|
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), c_szFmtFilename, pszFilename);
|
|
hr = _WriteString(pstrm, szBuffer);
|
|
}
|
|
|
|
// finish it off with a CR/LF
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_WriteString(pstrm, c_szCRLF);
|
|
_WriteString(pstrm, c_szCRLF);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// stream management functions
|
|
|
|
int CPostStream::s_ReleaseStream(IStream *pstrm, void *pv)
|
|
{
|
|
pstrm->Release();
|
|
return 1;
|
|
}
|
|
|
|
HRESULT CPostStream::_AddStream(IStream *pstrm)
|
|
{
|
|
HRESULT hr = (-1 == _dpaStreams.AppendPtr(pstrm)) ? E_FAIL:S_OK;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pstrm->AddRef();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPostStream::_CreateMemoryStream(REFIID riid, void **ppv)
|
|
{
|
|
IStream *pstrm = SHCreateMemStream(NULL, 0);
|
|
if (!pstrm)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// lets add it to our list and return a refernce if needed
|
|
|
|
HRESULT hr = _AddStream(pstrm);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pstrm->QueryInterface(riid, ppv);
|
|
}
|
|
pstrm->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// handle initialising the handler
|
|
|
|
HRESULT CPostStream::Initialize(IStorage *pstg, TRANSFERITEM *pti)
|
|
{
|
|
HRESULT hr = pti->psi->QueryInterface(IID_PPV_ARG(IShellItem, &_psi));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _dpaStreams.Create(4) ? S_OK:E_FAIL;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// first comes the file bits, this consists of two stream:
|
|
//
|
|
// 1) boundary marker
|
|
// 2) file bits (reference to real bits on file system)
|
|
|
|
IStream *pstrm;
|
|
hr = _CreateMemoryStream(IID_PPV_ARG(IStream, &pstrm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _AddBoundaryMarker(pstrm, FALSE, pti->szName, pti->szFilename);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IStream *pstrmFile;
|
|
|
|
// if we are recompressing this stream then apply it accordingly by
|
|
// creating an in memory stream that represents the file bits.
|
|
|
|
if (pti->fResizeOnUpload)
|
|
{
|
|
IImageRecompress *pir;
|
|
hr = CoCreateInstance(CLSID_ImageRecompress, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IImageRecompress, &pir));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pir->RecompressImage(_psi, pti->cxResize, pti->cyResize, pti->iQuality, pstg, &pstrmFile);
|
|
pir->Release();
|
|
}
|
|
}
|
|
|
|
if (!pti->fResizeOnUpload || (hr != S_OK))
|
|
hr = _psi->BindToHandler(NULL, BHID_Stream, IID_PPV_ARG(IStream, &pstrmFile));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _AddStream(pstrmFile);
|
|
pstrmFile->Release();
|
|
}
|
|
}
|
|
pstrm->Release();
|
|
}
|
|
|
|
// now do we have any form data we need to write into the stream?
|
|
|
|
if (pti->dsaFormData != NULL)
|
|
{
|
|
for (int iFormData = 0; SUCCEEDED(hr) && (iFormData < pti->dsaFormData.GetItemCount()); iFormData++)
|
|
{
|
|
FORMDATA *pfd = pti->dsaFormData.GetItemPtr(iFormData);
|
|
ASSERT(pfd != NULL);
|
|
|
|
IStream *pstrm;
|
|
hr = _CreateMemoryStream(IID_PPV_ARG(IStream, &pstrm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
|
|
// convert the variants - useful for passing across thread boundary
|
|
// to strings and form into a stream.
|
|
|
|
VariantToStr(&pfd->varName, szBuffer, ARRAYSIZE(szBuffer));
|
|
hr = _AddBoundaryMarker(pstrm, TRUE, szBuffer, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VariantToStr(&pfd->varValue, szBuffer, ARRAYSIZE(szBuffer));
|
|
hr = _WriteString(pstrm, szBuffer);
|
|
}
|
|
pstrm->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
// write EOF into a stream which will be returned.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IStream *pstrm;
|
|
hr = _CreateMemoryStream(IID_PPV_ARG(IStream, &pstrm));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _WriteStringCRLF(pstrm, c_pszBoundaryEOF);
|
|
pstrm->Release();
|
|
}
|
|
}
|
|
|
|
// now handle our prep for post, this consists of walking all the streams
|
|
// and processing the data.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// now get the total for the stream object that we are going to upload to the site
|
|
STATSTG ststg;
|
|
hr = this->Stat(&ststg, STATFLAG_NONAME);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ulTotal = ststg.cbSize.QuadPart;
|
|
}
|
|
|
|
// seek all the streams to the begining so that we can read from them
|
|
for (int iStream = 0; iStream < _dpaStreams.GetPtrCount(); iStream++)
|
|
{
|
|
IStream *pstrm = _dpaStreams.GetPtr(iStream);
|
|
ASSERT(pstrm != NULL);
|
|
|
|
pstrm->Seek(g_li0, 0, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPostStream::SetTransferSink(ITransferAdviseSink *ptas, ULONGLONG ulMax, ULONGLONG ulCurrent)
|
|
{
|
|
_ulOverallTotal = ulMax;
|
|
_ulOverallCurrent = ulCurrent;
|
|
|
|
return ptas->QueryInterface(IID_PPV_ARG(ITransferAdviseSink, &_ptas));
|
|
}
|
|
|
|
|
|
// IStream methods
|
|
|
|
HRESULT CPostStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cbReadTotal = 0;
|
|
ULONG cbLeftToRead = cb;
|
|
|
|
// cancel the stream
|
|
|
|
if (_ptas && (_ptas->QueryContinue() == S_FALSE))
|
|
{
|
|
hr = ERROR_CANCELLED;
|
|
}
|
|
|
|
// loop over the streams reading the bits from them
|
|
|
|
while ((SUCCEEDED(hr) && hr != S_FALSE) && cbLeftToRead && (_iCurStream < _dpaStreams.GetPtrCount()))
|
|
{
|
|
IStream *pstrm = _dpaStreams.GetPtr(_iCurStream);
|
|
ASSERT(pstrm != NULL);
|
|
|
|
ULONG cbReadThisStream;
|
|
hr = pstrm->Read(pv, cbLeftToRead, &cbReadThisStream);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
cbLeftToRead -= cbReadThisStream;
|
|
cbReadTotal += cbReadThisStream;
|
|
pv = (char *)pv + cbReadThisStream;
|
|
|
|
if (cbLeftToRead)
|
|
{
|
|
_iCurStream++;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update our seek pointer so we know where we are and notify the progress object
|
|
|
|
_ulCurrent = min(_ulTotal, (_ulCurrent + cbReadTotal));
|
|
_ulOverallCurrent = min(_ulOverallTotal, (_ulOverallCurrent + cbReadTotal));
|
|
|
|
if (_ptas)
|
|
{
|
|
_ptas->OperationProgress(STGOP_COPY, NULL, NULL, _ulOverallTotal, _ulOverallCurrent);
|
|
_ptas->OperationProgress(STGOP_COPY, _psi, NULL, _ulTotal, _ulCurrent);
|
|
}
|
|
|
|
// write back the count for the caller
|
|
if (pcbRead)
|
|
*pcbRead = cbReadTotal;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPostStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
if (grfStatFlag != STATFLAG_NONAME)
|
|
return E_INVALIDARG;
|
|
|
|
ZeroMemory(pstatstg, sizeof(*pstatstg));
|
|
|
|
HRESULT hr = S_OK;
|
|
for (int iStream = 0 ; SUCCEEDED(hr) && (iStream < _dpaStreams.GetPtrCount()); iStream++)
|
|
{
|
|
IStream *pstrm = _dpaStreams.GetPtr(iStream);
|
|
ASSERT(pstrm != NULL);
|
|
|
|
STATSTG ststg;
|
|
hr = pstrm->Stat(&ststg, STATFLAG_NONAME);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pstatstg->cbSize.QuadPart += ststg.cbSize.QuadPart;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// create wrapper, this initializes the object and returns a reference to it.
|
|
|
|
HRESULT CreatePostStream(TRANSFERITEM *pti, IStorage *pstg, IStream **ppstrm)
|
|
{
|
|
CPostStream *pps = new CPostStream();
|
|
if (!pps)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pps->Initialize(pstg, pti);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pps->QueryInterface(IID_PPV_ARG(IStream, ppstrm));
|
|
}
|
|
pps->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// this engine posts the files to the site using the manifest
|
|
|
|
class CPostThread : public IUnknown
|
|
{
|
|
public:
|
|
CPostThread(TRANSFERINFO *pti);
|
|
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
|
|
|
|
HRESULT BeginTransfer(CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas);
|
|
|
|
protected:
|
|
~CPostThread();
|
|
|
|
static DWORD CALLBACK s_ThreadProc(void *pv);
|
|
DWORD _ThreadProc();
|
|
|
|
LONG _cRef;
|
|
|
|
TRANSFERINFO _ti; // transfer info structure
|
|
CDPA<TRANSFERITEM> _dpaItems;
|
|
IStream *_pstrmSink;
|
|
IStorage *_pstg;
|
|
|
|
ULONGLONG _ulTotal;
|
|
ULONGLONG _ulCurrent;
|
|
};
|
|
|
|
|
|
// construction destruction
|
|
|
|
CPostThread::CPostThread(TRANSFERINFO *pti) :
|
|
_cRef(1),
|
|
_ti(*pti)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
CPostThread::~CPostThread()
|
|
{
|
|
if (_pstrmSink)
|
|
_pstrmSink->Release();
|
|
|
|
if (_pstg)
|
|
_pstg->Release();
|
|
|
|
_dpaItems.DestroyCallback(_FreeTransferItems, NULL);
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
ULONG CPostThread::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CPostThread::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
HRESULT CPostThread::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
|
|
// thread which handles the posting of the files to the site we walk the DPA that we
|
|
// have and post each individual file.
|
|
|
|
DWORD CPostThread::s_ThreadProc(void *pv)
|
|
{
|
|
CPostThread *ppt = (CPostThread*)pv;
|
|
return ppt->_ThreadProc();
|
|
}
|
|
|
|
DWORD CPostThread::_ThreadProc()
|
|
{
|
|
ITransferAdviseSink *ptas;
|
|
HRESULT hr = CoGetInterfaceAndReleaseStream(_pstrmSink, IID_PPV_ARG(ITransferAdviseSink, &ptas));
|
|
_pstrmSink = NULL;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ulTotal = 0;
|
|
_ulCurrent = 0;
|
|
|
|
// lets create a dyanmic storage that we can use for building the post
|
|
// data into, this will be passed to the stream creator to us.
|
|
|
|
hr = CoCreateInstance(CLSID_DynamicStorage, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IStorage, &_pstg));
|
|
|
|
// our pre flight sets the global size of the transfer and creates streams for
|
|
// the objects we want to move over. now get the advise sink and start
|
|
// processing the files.
|
|
|
|
for (int iItem = 0 ; SUCCEEDED(hr) && (iItem < _dpaItems.GetPtrCount()); iItem++)
|
|
{
|
|
TRANSFERITEM *pti = _dpaItems.GetPtr(iItem);
|
|
|
|
hr = SHCreateShellItem(NULL, NULL, pti->pidl, &pti->psi);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ptas->PreOperation(STGOP_STATS, pti->psi, NULL);
|
|
|
|
hr = CreatePostStream(pti, _pstg, &pti->pstrm);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pti->pstrm->Stat(&pti->ststg, STATFLAG_NONAME);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ulTotal += pti->ststg.cbSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
ptas->PostOperation(STGOP_STATS, pti->psi, NULL, hr);
|
|
}
|
|
}
|
|
|
|
for (int iItem = 0 ; SUCCEEDED(hr) && (iItem < _dpaItems.GetPtrCount()); iItem++)
|
|
{
|
|
TRANSFERITEM *pti = _dpaItems.GetPtr(iItem);
|
|
|
|
if (ptas->QueryContinue() == S_FALSE)
|
|
{
|
|
hr = STRESPONSE_CANCEL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// notify the object that we are going to transfer
|
|
ptas->PreOperation(STGOP_COPY, pti->psi, NULL);
|
|
|
|
IPostStream *pps;
|
|
if (ptas && SUCCEEDED(pti->pstrm->QueryInterface(IID_PPV_ARG(IPostStream, &pps))))
|
|
{
|
|
pps->SetTransferSink(ptas, _ulTotal, _ulCurrent);
|
|
pps->Release();
|
|
}
|
|
|
|
IXMLHttpRequest *preq;
|
|
hr = CoCreateInstance(CLSID_XMLHTTPRequest, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IXMLHttpRequest, &preq));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varNULL = {0};
|
|
VARIANT varAsync = {VT_BOOL};
|
|
varAsync.boolVal = VARIANT_FALSE;
|
|
|
|
// open a post request to the destination that we have
|
|
hr = preq->open(pti->szVerb, pti->szURL, varAsync, varNULL, varNULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// set it up to post with a multi-part
|
|
hr = preq->setRequestHeader(L"content-type", c_pszContentType);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varBody = {VT_UNKNOWN};
|
|
varBody.punkVal = pti->pstrm;
|
|
|
|
hr = preq->send(varBody);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
long lStatus;
|
|
hr = preq->get_status(&lStatus);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
switch (lStatus)
|
|
{
|
|
case HTTP_STATUS_OK:
|
|
case HTTP_STATUS_CREATED:
|
|
hr = S_OK;
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
preq->Release();
|
|
}
|
|
|
|
// notify the site that the transfer is complete
|
|
ptas->PostOperation(STGOP_COPY, pti->psi, NULL, hr);
|
|
|
|
// update our seek pointer for progress
|
|
_ulCurrent = min((_ulCurrent + pti->ststg.cbSize.QuadPart), _ulTotal);
|
|
}
|
|
}
|
|
|
|
// notify the foreground that the wizard has finished uploading the bits to the site.
|
|
|
|
PostMessage(_ti.hwnd, PWM_TRANSFERCOMPLETE, 0, (LPARAM)hr);
|
|
|
|
// if that succeeded then lets try and create a net place that points to the place
|
|
// we are uploading the files to. of course we can only do this if they place
|
|
// a shortcut entry into the
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
ptas->Release();
|
|
}
|
|
|
|
Release();
|
|
return 0L;
|
|
}
|
|
|
|
|
|
// handle initializing and kicking off the post thread which will handle the transter of the bits.
|
|
|
|
HRESULT CPostThread::BeginTransfer(CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas)
|
|
{
|
|
_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;
|
|
}
|
|
|
|
|
|
// create the posting object and initialize it
|
|
|
|
HRESULT PublishViaPost(TRANSFERINFO *pti, CDPA<TRANSFERITEM> *pdpaItems, ITransferAdviseSink *ptas)
|
|
{
|
|
CPostThread *ppt = new CPostThread(pti);
|
|
if (!ppt)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = ppt->BeginTransfer(pdpaItems, ptas);
|
|
ppt->Release();
|
|
return hr;
|
|
}
|