2025-04-27 07:49:33 -04:00

661 lines
21 KiB
C++

#include <objbase.h>
#include <assert.h>
#include "davinet.clsid.h"
#include "httpstrm.clsid.h"
#include "asyncwnt.clsid.h"
#include "qxml.h"
#include "iasyncwnt.h"
#include "davinet.h"
#include "httpstrm.h"
#include "asynccall.h"
#include "propreqs.h"
#include "strutil.h"
#include "mischlpr.h"
///////////////////////////////////////
CDavInetImpl::CDavInetImpl(): _pwszUserName(NULL), _pasyncInet(NULL), _pwszPassword(NULL), _pwszLogFilePath(NULL)
{
}
///////////////////////////////////////
CDavInetImpl::~CDavInetImpl()
{
if (_pasyncInet != NULL)
{
_pasyncInet->Release();
}
if (_pwszUserName != NULL)
{
free(_pwszUserName);
}
if (_pwszPassword != NULL)
{
free(_pwszPassword);
}
if (_pwszLogFilePath != NULL)
{
free(_pwszLogFilePath);
}
}
///////////////////////////////////////
// pseudo-constructor: called before we do any operation,
// doesn't do anything if already initialized
STDMETHODIMP CDavInetImpl::_Init()
{
HRESULT hr = S_OK;
if (_pasyncInet == NULL)
{
hr = ::CoCreateInstance(CLSID_AsyncWnt,
NULL,
CLSCTX_INPROC_SERVER,
IID_IAsyncWnt,
(LPVOID*)&_pasyncInet);
if (FAILED(hr))
{
_pasyncInet = NULL;
}
}
return hr;
}
///////////////////////////////////////
///////////////////////////////////////
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::SetUserAgent(/* [in] */ LPCWSTR pwszUserAgent)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = _pasyncInet->SetUserAgent(pwszUserAgent);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::SetLogFilePath(/* [in] */ LPCWSTR pwszLogFilePath)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = _pasyncInet->SetLogFilePath(pwszLogFilePath);
}
return hr;
}
///////////////////////////////////////
// note that setting username/password to null will clear that field, not leave it as it was
STDMETHODIMP CDavInetImpl::SetAuthentication(/* [optional][in] */ LPCWSTR pwszUserName,
/* [optional][in] */ LPCWSTR pwszPassword)
{
// local
HRESULT hr = S_OK;
UINT cchUserName;
UINT cchPassword;
// check params
if (pwszUserName == NULL && pwszPassword != NULL)
{
// only combination that is illegal is a blank username and a non-blank password
hr = E_INVALIDARG;
}
else
{
// code
hr = this->_Init();
if (SUCCEEDED(hr))
{
// clear previous username/password if appropriate
if (_pwszUserName != NULL)
{
free(_pwszUserName);
_pwszUserName = NULL;
}
if (_pwszPassword != NULL)
{
free(_pwszPassword);
_pwszPassword = NULL;
}
// set new username/password if appropriate
if (pwszUserName != NULL && ((cchUserName = lstrlen(pwszUserName)) > 0))
{
_pwszUserName = AllocateStringW(cchUserName);
if (_pwszUserName == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
lstrcpy(_pwszUserName, pwszUserName);
}
}
if (SUCCEEDED(hr))
{
if (pwszPassword != NULL && ((cchPassword = lstrlen(pwszPassword)) > 0))
{
_pwszPassword = AllocateStringW(cchPassword);
if (_pwszPassword == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
lstrcpy(_pwszPassword, pwszPassword);
}
}
}
}
}
// return value
return hr;
}
///////////////////////////////////////
///////////////////////////////////////
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::_Command(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ LPCWSTR pwszVerb,
/* [in] */ LPCWSTR pwszHeaders,
/* [in] */ ULONG nAcceptTypes,
/* [in] */ LPCWSTR rgwszAcceptTypes[],
/* [in] */ IStream* pStream,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
IAsyncWntCallback* pAsyncCallback = NULL;
BOOL fNoRoot = FALSE;
UINT cchHeaders;
LPWSTR pwszHeadersLocal;
hr = ::CoCreateInstance(CLSID_AsyncWntCallback,
NULL,
CLSCTX_INPROC_SERVER,
IID_IAsyncWntCallback,
(LPVOID*)&pAsyncCallback);
if (SUCCEEDED(hr))
{
pwszHeadersLocal = (LPWSTR)pwszHeaders;
// BUGBUG: we need to test this code
// initialize the AsyncCallback with the DavCallback and auth info
// pCallback may well be null, but we still need to pass along the auth info
if (lstrcmp(pwszVerb, L"PROPFIND") == 0)
{
// BUGBUG: ugly special case detection of noroot
// note: don't need to do this if we're talking to an exchange-enabled server
cchHeaders = lstrlen(pwszHeaders);
if (LStrStr((LPWSTR)pwszHeaders, L",noroot") == (pwszHeaders + cchHeaders - 7))
{
fNoRoot = TRUE;
pwszHeadersLocal = (LPWSTR)malloc((cchHeaders - 7 + 1) * sizeof(WCHAR)); // get rid of ,noroot
if (pwszHeadersLocal == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
lstrcpyn(pwszHeadersLocal, pwszHeaders, cchHeaders - 7 + 1);
}
}
}
if (SUCCEEDED(hr))
{
hr = ((CAsyncWntCallback*)pAsyncCallback)->Init(pCallback, dwCallbackParam, _pwszUserName, _pwszPassword, fNoRoot);
if (SUCCEEDED(hr))
{
if (pStream == NULL)
{
hr = _pasyncInet->Request(pwszUrl, pwszVerb, pwszHeadersLocal, nAcceptTypes,
rgwszAcceptTypes, pAsyncCallback, 0);
}
else
{
hr = _pasyncInet->RequestWithStream(pwszUrl, pwszVerb, pwszHeadersLocal, nAcceptTypes,
rgwszAcceptTypes, pStream, pAsyncCallback, 0);
}
}
if (pwszHeadersLocal != pwszHeaders)
{
free(pwszHeadersLocal);
}
}
pAsyncCallback->Release();
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandGET(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ ULONG nAcceptTypes,
/* [size_is][in] */ LPCWSTR rgwszAcceptTypes[],
/* [in] */ BOOL UNREF_PARAM(fTranslate),
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
// GET does not require a callback,
// but if no callback is specified,
// GET really only tests for whether the file is found
hr = this->_Command(pwszUrl,
L"GET",
NULL,
nAcceptTypes,
rgwszAcceptTypes,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandOPTIONS(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IDavCallback __RPC_FAR *pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
// OPTIONS requires a callback
if (pCallback == NULL)
{
hr = E_INVALIDARG;
}
else
{
hr = this->_Command(pwszUrl,
L"OPTIONS",
NULL,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandHEAD(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
// HEAD does not require a callback,
// but if no callback is specified,
// HEAD really only tests for whether the file is found
hr = this->_Command(pwszUrl,
L"HEAD",
NULL,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandPUT(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IStream* pStream,
/* [in] */ LPCWSTR pwszContentType,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
hr = this->_Init();
if (SUCCEEDED(hr))
{
// PUT doesn't require a callback
hr = this->_Command(pwszUrl,
L"PUT",
pwszContentType,
0,
NULL,
pStream,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandPOST(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IStream* pStream,
/* [in] */ LPCWSTR pwszContentType,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
// DAV supports the DAV PUT verb
// This POST method is simply here for people who are used to using GET and POST
HRESULT hr;
hr = this->_Init();
if (SUCCEEDED(hr))
{
// POST doesn't require a callback
hr = this->CommandPUT(pwszUrl,
pStream,
pwszContentType,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandMKCOL(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
// MKCOL doesn't require a callback
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = this->_Command(pwszUrl,
L"MKCOL",
NULL,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandDELETE(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
// DELETE doesn't require a callback
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = this->_Command(pwszUrl,
L"DELETE",
NULL,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandCOPY(/* [in] */ LPCWSTR pwszUrlSource,
/* [in] */ LPCWSTR pwszUrlDest,
/* [in] */ DWORD dwDepth,
/* [in] */ BOOL fOverwrite,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
WCHAR wszHeader[100];
// COPY doesn't require a callback
hr = this->_Init();
if (SUCCEEDED(hr))
{
if (dwDepth == DEPTH_INFINITY)
{
lstrcpy(wszHeader, L"Depth: infinity");
}
else
{
wsprintf(wszHeader, L"Depth: %d", dwDepth);
}
lstrcat(wszHeader, L"Destination: ");
lstrcat(wszHeader, pwszUrlDest);
if (fOverwrite)
{
lstrcat(wszHeader, L"\nOverwrite: T");
}
else
{
lstrcat(wszHeader, L"\nOverwrite: F");
}
hr = this->_Command(pwszUrlSource,
L"COPY",
wszHeader,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandMOVE(/* [in] */ LPCWSTR pwszUrlSource,
/* [in] */ LPCWSTR pwszUrlDest,
/* [in] */ BOOL fOverwrite,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
WCHAR wszHeader[100];
// MOVE doesn't require a callback
hr = this->_Init();
if (SUCCEEDED(hr))
{
lstrcpy(wszHeader, L"Destination: ");
lstrcat(wszHeader, pwszUrlDest);
if (fOverwrite)
{
lstrcat(wszHeader, L"\nOverwrite: T");
}
else
{
lstrcat(wszHeader, L"\nOverwrite: F");
}
hr = this->_Command(pwszUrlSource,
L"MOVE",
wszHeader,
0,
NULL,
NULL,
pCallback,
dwCallbackParam);
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandPROPFIND(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IPropFindRequest* pRequest,
/* [in] */ DWORD dwDepth,
/* [in] */ BOOL fNoRoot,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
IStream* pStream = NULL;
WCHAR wszHeader[100];
// PROPFIND requires a callback
if (pCallback == NULL)
{
hr = E_INVALIDARG;
}
else
{
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = pRequest->GetXmlUtf8(&pStream);
if (SUCCEEDED(hr))
{
if (dwDepth == DEPTH_INFINITY)
{
lstrcpy(wszHeader, L"Depth: infinity");
}
else
{
wsprintf(wszHeader, L"Depth: %d", dwDepth);
}
if (fNoRoot)
{
lstrcat(wszHeader, L",noroot"); // exchange-specific
}
hr = this->_Command(pwszUrl,
L"PROPFIND",
wszHeader,
0,
NULL,
pStream,
pCallback,
dwCallbackParam);
pStream->Release();
}
}
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandPROPPATCH(/* [in] */ LPCWSTR pwszUrl,
/* [in] */ IPropPatchRequest* pRequest,
/* [in] */ LPCWSTR pwszHeaders,
/* [in] */ IDavCallback* pCallback,
/* [in] */ DWORD dwCallbackParam)
{
HRESULT hr = S_OK;
IStream* pStream = NULL;
// PROPPATCH requires a callback
if (pCallback == NULL)
{
hr = E_INVALIDARG;
}
else
{
hr = this->_Init();
if (SUCCEEDED(hr))
{
hr = pRequest->GetXmlUtf8(&pStream);
if (SUCCEEDED(hr))
{
hr = this->_Command(pwszUrl,
L"PROPPATCH",
pwszHeaders,
0,
NULL,
pStream,
pCallback,
dwCallbackParam);
pStream->Release();
}
}
}
return hr;
}
///////////////////////////////////////
STDMETHODIMP CDavInetImpl::CommandSEARCH(/* [in] */ LPCWSTR UNREF_PARAM(pwszUrl),
/* [in] */ IDavCallback* UNREF_PARAM(pCallback),
/* [in] */ DWORD UNREF_PARAM(dwCallbackParam))
{
return E_NOTIMPL;
}
STDMETHODIMP CDavInetImpl::CommandREPLSEARCH(/* [in] */ LPCWSTR UNREF_PARAM(pwszUrl),
/* [in] */ ULONG UNREF_PARAM(cbCollblob),
/* [size_is][in] */ BYTE* UNREF_PARAM(pbCollblob),
/* [in] */ IDavCallback* UNREF_PARAM(pCallback),
/* [in] */ DWORD UNREF_PARAM(dwCallbackParam))
{
return E_NOTIMPL;
}
//////////////////////////////////////////////////////////