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.
2884 lines
68 KiB
2884 lines
68 KiB
/*
|
|
* F S M E T A . C P P
|
|
*
|
|
* Sources file system implementation of DAV-Meta
|
|
*
|
|
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
*/
|
|
|
|
#include "_davfs.h"
|
|
|
|
// CFSFind -------------------------------------------------------------------
|
|
//
|
|
SCODE
|
|
CFSFind::ScAddProp (LPCWSTR, LPCWSTR pwszProp, BOOL)
|
|
{
|
|
enum { cProps = 8 };
|
|
|
|
// If this is our first time in, we will need to allocate
|
|
// space for all of the properties we are expecting to get
|
|
// added over the coarse of this operation.
|
|
//
|
|
if (m_ft == FIND_NONE)
|
|
{
|
|
// Note that we have started requesting specific properties
|
|
//
|
|
m_ft = FIND_SPECIFIC;
|
|
}
|
|
else if (m_ft != FIND_SPECIFIC)
|
|
{
|
|
// If we are not finding specfic properties and somebody asked
|
|
// for one, then bts (by the spec) this should consititute an
|
|
// error.
|
|
//
|
|
return E_DAV_PROPFIND_TYPE_UNEXPECTED;
|
|
}
|
|
|
|
// See if there is room at the in...
|
|
//
|
|
if (m_cMaxProps == m_cProps)
|
|
{
|
|
UINT cb;
|
|
|
|
// Allocate enough space for the next block of properties
|
|
//
|
|
m_cMaxProps = m_cProps + cProps;
|
|
cb = m_cMaxProps * sizeof(PROPVARIANT);
|
|
m_rgwszProps.realloc (cb);
|
|
}
|
|
|
|
// If this is the getcontenttype property, then we need to remember
|
|
// its location for use when providing default values...
|
|
//
|
|
if (!wcscmp (pwszProp, gc_wszProp_iana_getcontenttype))
|
|
m_ip_getcontenttype = m_cProps;
|
|
|
|
// Set the property up as one to process.
|
|
//
|
|
Assert (m_cProps < m_cMaxProps);
|
|
m_rgwszProps[m_cProps++] = AppendChainedSz (m_csb, pwszProp);
|
|
return S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CFSFind::ScFind (CXMLEmitter& msr,
|
|
IMethUtil * pmu,
|
|
CFSProp& fpt)
|
|
{
|
|
SCODE scFind;
|
|
SCODE sc = S_OK;
|
|
|
|
// Setup the emitting of the response. This will construct
|
|
// an XML node that looks like:
|
|
//
|
|
// <multistatus>
|
|
// <response>
|
|
// <href>http:/www....</>
|
|
//
|
|
//
|
|
CEmitterNode enItem;
|
|
CEmitterNode en;
|
|
|
|
sc = msr.ScSetRoot (gc_wszMultiResponse);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
sc = enItem.ScConstructNode (msr, msr.PxnRoot(), gc_wszResponse);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// If they havent asked for anything, then we should return an
|
|
// error
|
|
//
|
|
if (m_ft == FIND_NONE)
|
|
{
|
|
//$REVIEW: is it really correct to NOT add the HREF node here? --BeckyAn 6July1999
|
|
return E_DAV_EMPTY_FIND_REQUEST;
|
|
}
|
|
// If the request is an for a specific set of properties, then
|
|
// this is pretty easy...
|
|
//
|
|
else if (m_ft == FIND_SPECIFIC)
|
|
{
|
|
Assert (m_cProps);
|
|
Assert (m_rgwszProps);
|
|
|
|
sc = ScAddHref (enItem,
|
|
pmu,
|
|
fpt.PwszPath(),
|
|
fpt.FCollection(),
|
|
fpt.PcvrTranslation());
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// Get all the properties by name
|
|
//
|
|
scFind = fpt.ScGetSpecificProps (msr,
|
|
enItem,
|
|
m_cProps,
|
|
(LPCWSTR*)m_rgwszProps.get(),
|
|
m_ip_getcontenttype);
|
|
if (FAILED (scFind))
|
|
{
|
|
(void) ScAddStatus (&enItem, HscFromHresult(scFind));
|
|
goto ret;
|
|
}
|
|
}
|
|
// If the request is an for all properties or all names, then again,
|
|
// this is pretty easy...
|
|
//
|
|
else
|
|
{
|
|
Assert ((m_ft == FIND_ALL) || (m_ft == FIND_NAMES));
|
|
|
|
// Get all props or all names
|
|
//
|
|
scFind = fpt.ScGetAllProps (msr, enItem, m_ft == FIND_ALL);
|
|
if (FAILED (scFind) && (scFind != E_DAV_SMB_PROPERTY_ERROR))
|
|
{
|
|
(void) ScAddStatus (&enItem, HscFromHresult(scFind));
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
|
|
// IPreloadNamespaces
|
|
//
|
|
SCODE
|
|
CFSFind::ScLoadNamespaces(CXMLEmitter * pmsr)
|
|
{
|
|
SCODE sc = S_OK;
|
|
UINT iProp;
|
|
|
|
// Load common namespaces
|
|
//
|
|
sc = pmsr->ScPreloadNamespace (gc_wszDav);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
sc = pmsr->ScPreloadNamespace (gc_wszLexType);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
sc = pmsr->ScPreloadNamespace (gc_wszXml_V);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Add more namespaces
|
|
|
|
switch (m_ft)
|
|
{
|
|
case FIND_SPECIFIC:
|
|
for (iProp = 0; iProp < m_cProps; iProp++)
|
|
{
|
|
sc = pmsr->ScPreloadNamespace (m_rgwszProps[iProp]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
break;
|
|
|
|
case FIND_ALL:
|
|
case FIND_NAMES:
|
|
// Now that we don't have a way to predict what namespaces to
|
|
// be used.
|
|
// Per resource level namespaces will be added on <DAV:response>
|
|
// node later
|
|
break;
|
|
|
|
default:
|
|
AssertSz (FALSE, "Unknown propfind type");
|
|
// fall through
|
|
|
|
case FIND_NONE:
|
|
sc = E_DAV_EMPTY_FIND_REQUEST;
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
|
|
// CFSPatch ------------------------------------------------------------------
|
|
//
|
|
SCODE
|
|
CFSPatch::ScDeleteProp (LPCWSTR, LPCWSTR pwszProp)
|
|
{
|
|
enum { cProps = 8 };
|
|
UINT irp;
|
|
|
|
// We cannot delete any reserved properties, so let's
|
|
// just shortcut this here and now...
|
|
//
|
|
if (CFSProp::FReservedProperty (pwszProp,
|
|
CFSProp::RESERVED_SET,
|
|
&irp))
|
|
{
|
|
// Take ownership of the bstr as well
|
|
//
|
|
return m_csn.ScAddErrorStatus (HSC_FORBIDDEN, pwszProp);
|
|
}
|
|
|
|
// Make sure there is room at the inn...
|
|
//
|
|
if (m_cMaxDeleteProps == m_cDeleteProps)
|
|
{
|
|
UINT cb;
|
|
|
|
// Allocate enough space for all the properties names
|
|
// we want to delete.
|
|
//
|
|
m_cMaxDeleteProps = m_cDeleteProps + cProps;
|
|
cb = m_cMaxDeleteProps * sizeof(BSTR);
|
|
m_rgwszDeleteProps.realloc (cb);
|
|
}
|
|
|
|
// Set the property up as one to process.
|
|
//
|
|
Assert (m_cDeleteProps < m_cMaxDeleteProps);
|
|
m_rgwszDeleteProps[m_cDeleteProps++] = AppendChainedSz(m_csb, pwszProp);
|
|
return S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CFSPatch::ScSetProp (LPCWSTR,
|
|
LPCWSTR pwszProp,
|
|
auto_ref_ptr<CPropContext>& pPropCtx)
|
|
{
|
|
enum { cProps = 8 };
|
|
UINT irp;
|
|
|
|
// We cannot set any reserved properties, so let's
|
|
// just shortcut this here and now...
|
|
//
|
|
if (CFSProp::FReservedProperty (pwszProp,
|
|
CFSProp::RESERVED_SET,
|
|
&irp))
|
|
{
|
|
// Take ownership of the bstr as well
|
|
//
|
|
return m_csn.ScAddErrorStatus (HSC_FORBIDDEN, pwszProp);
|
|
}
|
|
|
|
// Make sure there is room at the inn...
|
|
//
|
|
if (m_cMaxSetProps == m_cSetProps)
|
|
{
|
|
UINT cb;
|
|
|
|
// Allocate enough space for all the properties we
|
|
// might want to set
|
|
//
|
|
m_cMaxSetProps = m_cSetProps + cProps;
|
|
cb = m_cMaxSetProps * sizeof(PROPVARIANT);
|
|
m_rgvSetProps.realloc (cb);
|
|
|
|
// Make sure the VARIANT are properly initialized
|
|
// (only initialize the newly added space).
|
|
//
|
|
ZeroMemory (&m_rgvSetProps[m_cSetProps],
|
|
sizeof(PROPVARIANT) * cProps);
|
|
|
|
// ... and their names.
|
|
//
|
|
cb = m_cMaxSetProps * sizeof(LPCWSTR);
|
|
m_rgwszSetProps.realloc (cb);
|
|
}
|
|
|
|
// Set the property up as one to process.
|
|
//
|
|
Assert (m_cSetProps < m_cMaxSetProps);
|
|
m_rgwszSetProps[m_cSetProps] = AppendChainedSz(m_csb, pwszProp);
|
|
pPropCtx = new CFSPropContext(&m_rgvSetProps[m_cSetProps]);
|
|
m_cSetProps++;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CFSPatch::ScPatch (CXMLEmitter& msr,
|
|
IMethUtil * pmu,
|
|
CFSProp& fpt)
|
|
{
|
|
SCODE sc = S_OK;
|
|
SCODE scSet = S_OK;
|
|
SCODE scDelete = S_OK;
|
|
|
|
CEmitterNode enItem;
|
|
|
|
// If there are no properties at all, reserved or otherwise,
|
|
// we want to fail the call with BAD_REQUEST
|
|
//
|
|
if ((m_cSetProps == 0) &&
|
|
(m_cDeleteProps == 0) &&
|
|
m_csn.FEmpty())
|
|
{
|
|
return E_DAV_EMPTY_PATCH_REQUEST;
|
|
}
|
|
|
|
// Setup the emitting of the response. This will construct
|
|
// an XML node that looks like:
|
|
//
|
|
// <multistatus>
|
|
// <response>
|
|
// <href>http:/www....</>
|
|
//
|
|
//
|
|
sc = msr.ScSetRoot (gc_wszMultiResponse);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
sc = enItem.ScConstructNode (msr, msr.PxnRoot(), gc_wszResponse);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
sc = ScAddHref (enItem,
|
|
pmu,
|
|
fpt.PwszPath(),
|
|
fpt.FCollection(),
|
|
fpt.PcvrTranslation());
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// If the client requested any of the reserved properties, we know
|
|
// that they will fail and we also know that everything else will fail
|
|
// as well, so we might as well handle that here...
|
|
//
|
|
if (!m_csn.FEmpty())
|
|
{
|
|
//$ REVIEW:
|
|
//
|
|
// If the possibly successful properties need to be
|
|
// marked as a failure as well (HSC_METHOD_FAILURE),
|
|
// then that would happen here.
|
|
//
|
|
|
|
//NT242086: Now that we've got a reponse node, we should
|
|
//added to the response.
|
|
//
|
|
sc = m_csn.ScEmitErrorStatus (enItem);
|
|
goto ret;
|
|
}
|
|
|
|
// If there are no reserved properties we have a pretty good bet
|
|
// at setting these props...
|
|
//
|
|
scSet = fpt.ScSetProps (m_csn,
|
|
m_cSetProps,
|
|
m_rgwszSetProps.get(),
|
|
m_rgvSetProps);
|
|
if (FAILED (scSet))
|
|
{
|
|
sc = scSet;
|
|
goto ret;
|
|
}
|
|
|
|
// ... and deleting these props.
|
|
//
|
|
scDelete = fpt.ScDeleteProps (m_csn,
|
|
m_cDeleteProps,
|
|
m_rgwszDeleteProps.get());
|
|
if (FAILED (scDelete))
|
|
{
|
|
sc = scDelete;
|
|
goto ret;
|
|
}
|
|
|
|
// If the possibly successful properties need to be
|
|
// marked as a failure as well (HSC_METHOD_FAILURE),
|
|
// then that would happen here. Either way, if there
|
|
// is a failure, then we do not want to commit the
|
|
// changes.
|
|
//
|
|
if ((scSet == S_FALSE) || (scDelete == S_FALSE))
|
|
goto ret;
|
|
|
|
// Commit the changes to the property container
|
|
//
|
|
sc = fpt.ScPersist();
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// Emit the response,
|
|
//
|
|
sc = m_csn.ScEmitErrorStatus (enItem);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
CFSPatch::~CFSPatch()
|
|
{
|
|
// Make sure all the propvariants are cleaned up...
|
|
//
|
|
for (UINT i = 0; i < m_cSetProps; i++)
|
|
PropVariantClear (&m_rgvSetProps[i]);
|
|
}
|
|
|
|
SCODE
|
|
CFSPatch::ScLoadNamespaces (CXMLEmitter * pmsr)
|
|
{
|
|
SCODE sc = S_OK;
|
|
UINT iProp;
|
|
|
|
// Load common namespaces
|
|
//
|
|
sc = pmsr->ScPreloadNamespace (gc_wszDav);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Add namespaces for set props
|
|
//
|
|
for (iProp = 0; iProp < m_cSetProps; iProp++)
|
|
{
|
|
sc = pmsr->ScPreloadNamespace (m_rgwszSetProps[iProp]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
// And delete props
|
|
//
|
|
for (iProp = 0; iProp < m_cDeleteProps; iProp++)
|
|
{
|
|
sc = pmsr->ScPreloadNamespace (m_rgwszDeleteProps[iProp]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
// CFSProp -------------------------------------------------------------------
|
|
//
|
|
SCODE
|
|
CFSProp::ScGetPropsInternal (ULONG cProps,
|
|
LPCWSTR* rgwszPropNames,
|
|
PROPVARIANT* rgvar,
|
|
LONG ip_getcontenttype)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// There really should only be one scenario where this could happen
|
|
// -- and it is a cheap test, so it is worth doing. The case where
|
|
// we might see an invalid pbag is when the document extisted, but
|
|
// there was no existing property set to impose the pbag on. Other
|
|
// than that, OLE is always giving us a property bag, regardless of
|
|
// whether the target drive can support it.
|
|
//
|
|
if (FInvalidPbag())
|
|
return sc;
|
|
|
|
// We better be good to go...
|
|
//
|
|
sc = m_pbag->ReadMultiple (cProps,
|
|
rgwszPropNames,
|
|
rgvar,
|
|
NULL);
|
|
|
|
// If we succeeded, and the getcontenttype property was requested,
|
|
// we may need to do some special processing
|
|
//
|
|
if (SUCCEEDED (sc) && (ip_getcontenttype != -1))
|
|
{
|
|
// We want to make sure that getcontenttype gets filled in
|
|
//
|
|
if (rgvar[ip_getcontenttype].vt == VT_EMPTY)
|
|
{
|
|
CStackBuffer<WCHAR> pwszT;
|
|
LPWSTR pwszContentType;
|
|
UINT cch = 40;
|
|
|
|
// No content type was explicitly set in the props.
|
|
// Fetch the default based on the file extension
|
|
// (fetching from our metabase-content-type-cache).
|
|
//
|
|
do {
|
|
|
|
if (NULL == pwszT.resize(CbSizeWsz(cch)))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
goto ret;
|
|
}
|
|
|
|
} while (!m_pmu->FGetContentType (m_pwszURI, pwszT.get(), &cch));
|
|
|
|
// Return the mapped content type
|
|
//
|
|
rgvar[ip_getcontenttype].vt = VT_LPWSTR;
|
|
|
|
// Must use task memory, as it will be freed by PropVariantClear
|
|
//
|
|
pwszContentType = (LPWSTR) CoTaskMemAlloc (cch * sizeof(WCHAR));
|
|
if (NULL == pwszContentType)
|
|
{
|
|
MCDTrace ("Dav: MCD: CFSProp::ScGetPropsInternal() - CoTaskMemAlloc() failed to allocate %d bytes\n", cch * sizeof(WCHAR));
|
|
|
|
sc = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto ret;
|
|
}
|
|
|
|
rgvar[ip_getcontenttype].pwszVal = pwszContentType;
|
|
memcpy(pwszContentType, pwszT.get(), cch * sizeof(WCHAR));
|
|
|
|
// In the case where this was the only property requested, make
|
|
// sure that our return code it correct.
|
|
//
|
|
if (cProps == 1)
|
|
{
|
|
Assert (ip_getcontenttype == 0);
|
|
Assert (sc == S_FALSE);
|
|
sc = S_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is the common path for when we are trying to access
|
|
// something over an SMB, but the host cannot support the
|
|
// request (it is not an NT5 NTFS machine).
|
|
//
|
|
if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
|
|
sc = E_DAV_SMB_PROPERTY_ERROR;
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
BOOL
|
|
CFSProp::FReservedProperty (LPCWSTR pwszProp, RESERVED_TYPE rt, UINT* prp)
|
|
{
|
|
UINT irp;
|
|
CRCWsz wsz(pwszProp);
|
|
|
|
// Search for the property in the list of local
|
|
// properties.
|
|
//
|
|
Assert (CElems(sc_rp) == sc_crp_set_reserved);
|
|
for (irp = 0; irp < sc_crp_set_reserved; irp++)
|
|
{
|
|
// If the crc and the strings match...
|
|
//
|
|
if ((wsz.m_dwCRC == sc_rp[irp].dwCRC) &&
|
|
!wcscmp (wsz.m_pwsz, sc_rp[irp].pwsz))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Setup the return
|
|
//
|
|
Assert (sc_crp_set_reserved != iana_rp_content_type);
|
|
*prp = irp;
|
|
|
|
return (irp < static_cast<UINT>((rt == RESERVED_GET) ? sc_crp_get_reserved : sc_crp_set_reserved));
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScGetReservedProp (CXMLEmitter& xml,
|
|
CEmitterNode& enParent,
|
|
UINT irp,
|
|
BOOL fGetValues)
|
|
{
|
|
CEmitterNode en;
|
|
CStackBuffer<WCHAR> wszBuf;
|
|
LARGE_INTEGER li;
|
|
LPCWSTR pwszType = NULL;
|
|
LPWSTR pwsz = NULL;
|
|
SCODE sc = S_OK;
|
|
SYSTEMTIME st;
|
|
|
|
Assert (irp <= sc_crp_get_reserved);
|
|
Assert (sc_crp_get_reserved == iana_rp_content_type);
|
|
Assert (CElems(sc_rp) == sc_crp_set_reserved);
|
|
|
|
// Only generate values if the caller wants them
|
|
//
|
|
if (fGetValues)
|
|
{
|
|
// Switch across the reserved properties generating
|
|
// a value for the property
|
|
//
|
|
switch (irp)
|
|
{
|
|
case iana_rp_etag:
|
|
|
|
Assert(m_pmu);
|
|
if (FETagFromFiletime (m_cri.PftLastModified(), wszBuf.get(), m_pmu->GetEcb()))
|
|
{
|
|
pwsz = wszBuf.get();
|
|
}
|
|
break;
|
|
|
|
case iana_rp_displayname:
|
|
|
|
// The filename/displayname is simply the name of the file
|
|
// and we should be able to pick it off from the path with
|
|
// little and/or no trouble at all. However, we will use
|
|
// the URI instead. We do this such that the displayname
|
|
// for a vroot is the name of the vroot and not the name of
|
|
// the physical disk directory.
|
|
//
|
|
pwsz = wcsrchr (m_pwszURI, L'/');
|
|
if (NULL == pwsz)
|
|
{
|
|
// Arrgh. If there was no path separator in the filename
|
|
// I don't know that we can really give a reasonable value
|
|
// for this file.
|
|
//
|
|
TrapSz ("resource path has no slashes....");
|
|
return S_FALSE;
|
|
}
|
|
|
|
// One more check. If this is a directory path,
|
|
// they might have a trailing slash. If that is what we're
|
|
// pointing to right now (next char is NULL), back up to the
|
|
// next delimiter to get the real item name.
|
|
//
|
|
if (L'\0' == pwsz[1])
|
|
{
|
|
// This better be a collection. Although it may not
|
|
// be if the client mis-terminated his/her url
|
|
//
|
|
// There is a special case that we need to check for
|
|
// here. It is possible that the URI was strictly "/"
|
|
// which means that if we continue this processing, the
|
|
// displayname and/or the filename would be empty or
|
|
// non-existant. In this case only, return "/" as the
|
|
// display name.
|
|
//
|
|
if (m_pwszURI == pwsz)
|
|
{
|
|
pwsz = L"/";
|
|
}
|
|
else
|
|
{
|
|
// Now we have to copy the string, to rip off that
|
|
// trailing slash we found in the step above.
|
|
// We want to remove the final slash because this is the
|
|
// displayname, not a URI.
|
|
//
|
|
LPCWSTR pwszEnd;
|
|
UINT cchNew;
|
|
|
|
for (pwszEnd = pwsz--; pwsz > m_pwszURI; pwsz--)
|
|
if (L'/' == *pwsz)
|
|
break;
|
|
|
|
if (L'/' != *pwsz)
|
|
{
|
|
// Arrgh. If there was no path separator in the
|
|
// filename I don't know that we can really give
|
|
// a reasonable value for this file.
|
|
//
|
|
TrapSz ("resource path has no slashes (redux)....");
|
|
return S_FALSE;
|
|
}
|
|
|
|
// At this point, the segment defined by (pwsz + 1, pwszEnd)
|
|
// names the resource.
|
|
//
|
|
cchNew = static_cast<UINT>(pwszEnd - ++pwsz);
|
|
if (NULL == wszBuf.resize(CbSizeWsz(cchNew)))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
goto ret;
|
|
}
|
|
memcpy(wszBuf.get(), pwsz, cchNew * sizeof(WCHAR));
|
|
wszBuf[cchNew] = L'\0';
|
|
pwsz = wszBuf.get();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// At this point, the segment defined by (pwsz + 1, '\0'] names
|
|
// the resource.
|
|
//
|
|
pwsz++;
|
|
}
|
|
break;
|
|
|
|
case iana_rp_resourcetype:
|
|
|
|
// Create the element to pass back
|
|
//
|
|
sc = en.ScConstructNode (xml, enParent.Pxn(), sc_rp[irp].pwsz);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
if (m_cri.FCollection())
|
|
{
|
|
CEmitterNode enSub;
|
|
sc = en.ScAddNode (gc_wszCollection, enSub);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
goto ret;
|
|
|
|
case iana_rp_content_length:
|
|
|
|
m_cri.FileSize(li);
|
|
pwszType = gc_wszDavType_Int;
|
|
|
|
//$ REVIEW: negative values of _int64 seem to have problems in
|
|
// the __i64tow() API. Handle those cases ourselves.
|
|
//
|
|
// In this instance, we shouldn't have to worry about it because
|
|
// the content-length *shouldn't* ever be negative. We'll assert
|
|
// that this is the case.
|
|
//
|
|
Assert (li.QuadPart >= 0);
|
|
_i64tow (li.QuadPart, wszBuf.get(), 10);
|
|
pwsz = wszBuf.get();
|
|
break;
|
|
|
|
case iana_rp_creation_date:
|
|
|
|
FileTimeToSystemTime (m_cri.PftCreation(), &st);
|
|
if (FGetDateIso8601FromSystime (&st, wszBuf.get(), wszBuf.size()))
|
|
{
|
|
pwszType = gc_wszDavType_Date_ISO8601;
|
|
pwsz = wszBuf.get();
|
|
}
|
|
break;
|
|
|
|
case iana_rp_last_modified:
|
|
|
|
FileTimeToSystemTime (m_cri.PftLastModified(), &st);
|
|
if (FGetDateRfc1123FromSystime (&st, wszBuf.get(), wszBuf.size()))
|
|
{
|
|
pwszType = gc_wszDavType_Date_Rfc1123;
|
|
pwsz = wszBuf.get();
|
|
}
|
|
break;
|
|
|
|
case iana_rp_supportedlock:
|
|
case iana_rp_lockdiscovery:
|
|
|
|
// Get the prop from the lock cache (and related subsystem calls).
|
|
//
|
|
sc = HrGetLockProp (m_pmu,
|
|
sc_rp[irp].pwsz,
|
|
m_pwszPath,
|
|
m_cri.FCollection() ? RT_COLLECTION : RT_DOCUMENT,
|
|
xml,
|
|
enParent);
|
|
|
|
// Regardless of error or success, we are done here. If we
|
|
// succeeded, then the pel has already been constructed and
|
|
// is ready to pass back. Otherwise, we just want to report
|
|
// the error.
|
|
//
|
|
goto ret;
|
|
|
|
case iana_rp_ishidden:
|
|
|
|
pwszType = gc_wszDavType_Boolean;
|
|
_itow (!!m_cri.FHidden(), wszBuf.get(), 10);
|
|
pwsz = wszBuf.get();
|
|
break;
|
|
|
|
case iana_rp_iscollection:
|
|
|
|
pwszType = gc_wszDavType_Boolean;
|
|
_itow (!!m_cri.FCollection(), wszBuf.get(), 10);
|
|
pwsz = wszBuf.get();
|
|
break;
|
|
|
|
// Special case: getcontenttype should really be stored, but there
|
|
// are some cases where the file may live in such a place as there
|
|
// would be no property stream available to store the value in.
|
|
//
|
|
case iana_rp_content_type:
|
|
|
|
// Get the content-type if it was not stored in the property
|
|
// stream.
|
|
//
|
|
for (UINT cch = wszBuf.celems();;)
|
|
{
|
|
if (NULL == wszBuf.resize(CbSizeWsz(cch)))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
goto ret;
|
|
}
|
|
if (m_pmu->FGetContentType(m_pwszURI, wszBuf.get(), &cch))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the element to pass back
|
|
//
|
|
sc = en.ScConstructNode (xml, enParent.Pxn(), sc_rp[irp].pwsz, pwsz, pwszType);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScGetSpecificProps (CXMLEmitter& msr,
|
|
CEmitterNode& enItem,
|
|
ULONG cProps,
|
|
LPCWSTR* rgwszPropNames,
|
|
LONG ip_getcontenttype)
|
|
{
|
|
// safe_propvariant_array ----------------------------------------------------
|
|
//
|
|
// Used to make sure the array of VARIANT can always be safely freed
|
|
//
|
|
class safe_propvariant_array
|
|
{
|
|
PROPVARIANT * m_rgv;
|
|
ULONG m_cv;
|
|
|
|
public:
|
|
|
|
safe_propvariant_array (PROPVARIANT* rgv, ULONG cv)
|
|
: m_rgv(rgv),
|
|
m_cv(cv)
|
|
{
|
|
memset (rgv, 0, sizeof(PROPVARIANT) * cv);
|
|
}
|
|
|
|
~safe_propvariant_array ()
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < m_cv; i++)
|
|
PropVariantClear(&m_rgv[i]);
|
|
}
|
|
};
|
|
|
|
SCODE sc = S_OK;
|
|
CStackBuffer<PROPVARIANT> rgv;
|
|
UINT iv;
|
|
CStatusCache csn;
|
|
CEmitterNode enPropStat;
|
|
CEmitterNode enPropOK;
|
|
|
|
|
|
// allocate space to hold an array of variants and stuff it into
|
|
// a safe_variant_array to ensure cleanup
|
|
//
|
|
rgv.resize(sizeof(PROPVARIANT) * cProps);
|
|
safe_propvariant_array sva(rgv.get(), cProps);
|
|
|
|
sc = csn.ScInit();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Get the properties
|
|
//
|
|
sc = ScGetPropsInternal (cProps, rgwszPropNames, rgv.get(), ip_getcontenttype);
|
|
if (FAILED(sc))
|
|
{
|
|
// When getting properties, it is perfectly OK to ignore SMB errors
|
|
// and treat the file as if it were hosted on a FAT drive
|
|
//
|
|
if (sc == E_DAV_SMB_PROPERTY_ERROR)
|
|
sc = S_OK;
|
|
|
|
// What this means is that the default not-found processing should
|
|
// kick in.
|
|
//
|
|
}
|
|
|
|
// Rip through the returned properties, adding to the response as we go
|
|
//
|
|
for (iv = 0; iv < cProps; iv++)
|
|
{
|
|
// If there is a value to the property, write the variant as
|
|
// an XML element and add it to the response
|
|
//
|
|
if (rgv[iv].vt != VT_EMPTY)
|
|
{
|
|
if (!enPropOK.Pxn())
|
|
{
|
|
// Get the insert point for props
|
|
//
|
|
sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enPropOK);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
// Write the variant as an XML element
|
|
//
|
|
sc = ScEmitFromVariant (msr,
|
|
enPropOK,
|
|
rgwszPropNames[iv],
|
|
rgv[iv]);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
else
|
|
{
|
|
UINT irp;
|
|
|
|
// Check if it's a reserved property
|
|
//
|
|
if (FReservedProperty (rgwszPropNames[iv], RESERVED_GET, &irp) ||
|
|
(irp == iana_rp_content_type))
|
|
{
|
|
if (!enPropOK.Pxn())
|
|
{
|
|
// Get the insert point for props
|
|
//
|
|
sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enPropOK);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
// If the property was reserved, then extract it from
|
|
// the property class directly
|
|
//
|
|
sc = ScGetReservedProp (msr, enPropOK, irp);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
continue;
|
|
}
|
|
|
|
// Now, if we got here, then for CFSProp, the property
|
|
// must not have existed.
|
|
//
|
|
sc = csn.ScAddErrorStatus (HSC_NOT_FOUND, rgwszPropNames[iv]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
// Need to close the previous prop stat before more status node to be emitted
|
|
//
|
|
if (!csn.FEmpty())
|
|
{
|
|
// The order is important, inner node must be closed first
|
|
//
|
|
sc = enPropOK.ScDone();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
sc = enPropStat.ScDone();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
sc = csn.ScEmitErrorStatus (enItem);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScGetAllProps (CXMLEmitter& msr,
|
|
CEmitterNode& enItem,
|
|
BOOL fFindValues)
|
|
{
|
|
auto_com_ptr<IEnumSTATPROPBAG> penum;
|
|
BOOL fContentType = FALSE;
|
|
BOOL fHrefAdded = FALSE;
|
|
SCODE sc = S_OK;
|
|
UINT irp;
|
|
CEmitterNode enPropStat;
|
|
CEmitterNode enProp;
|
|
|
|
// There really should only be one scenario where this could happen
|
|
// -- and it is a cheap test, so it is worth doing. The case where
|
|
// we might see an invalid pbag is when the document extisted, but
|
|
// there was no existing property set to impose the pbag on. Other
|
|
// than that, OLE is always giving us a property bag, regardless of
|
|
// whether the target drive can support it.
|
|
//
|
|
if (!FInvalidPbag())
|
|
{
|
|
sc = m_pbag->Enum (NULL, 0, &penum);
|
|
if (FAILED(sc))
|
|
{
|
|
// AddHref was delayed to be done after local namespace is loaded
|
|
// but in this case, we know there'll be no local namespaces at all.
|
|
// so add href now
|
|
//
|
|
(void) ScAddHref (enItem,
|
|
m_pmu,
|
|
PwszPath(),
|
|
FCollection(),
|
|
PcvrTranslation());
|
|
if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
|
|
{
|
|
// This is the common path for when we are trying to access
|
|
// something over an SMB, but the host cannot support the
|
|
// request (it is not an NT5 NTFS machine). We want to treat
|
|
// this as if the operation was against a FAT drive
|
|
//
|
|
sc = E_DAV_SMB_PROPERTY_ERROR;
|
|
goto get_reserved;
|
|
}
|
|
goto ret;
|
|
}
|
|
|
|
// We must preload all the potential namespaces in the <response> node,
|
|
// Note that the namespace for all reserved properties is "DAV:", which
|
|
// has been added already in CFSFind::ScLoadNamespace()
|
|
//
|
|
do
|
|
{
|
|
safe_statpropbag ssp[PROP_CHUNK_SIZE];
|
|
ULONG csp = 0;
|
|
UINT isp;
|
|
|
|
// Get next chunk of props
|
|
//
|
|
sc = penum->Next (PROP_CHUNK_SIZE, ssp[0].load(), &csp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// At this point, we either want to call the underlying
|
|
// property container to retrieve all the property data
|
|
// or we just want to emit the names.
|
|
//
|
|
for (isp = 0; isp < csp; isp++)
|
|
{
|
|
Assert (ssp[isp].get().lpwstrName);
|
|
|
|
sc = msr.ScPreloadLocalNamespace (enItem.Pxn(), ssp[isp].get().lpwstrName);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
} while (sc != S_FALSE);
|
|
|
|
|
|
// Addhref must be done after all the local nmespaces has been emitted
|
|
//
|
|
sc = ScAddHref (enItem,
|
|
m_pmu,
|
|
PwszPath(),
|
|
FCollection(),
|
|
PcvrTranslation());
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
fHrefAdded = TRUE;
|
|
|
|
// Reset the enumerator back to the beginning
|
|
//
|
|
sc = penum->Reset();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Get the insert point for props
|
|
//
|
|
sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enProp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Enumerate the props and emit
|
|
//
|
|
do
|
|
{
|
|
safe_statpropbag ssp[PROP_CHUNK_SIZE];
|
|
safe_propvariant propvar[PROP_CHUNK_SIZE];
|
|
LPWSTR rglpwstr[PROP_CHUNK_SIZE] = {0};
|
|
ULONG csp = 0;
|
|
UINT isp;
|
|
|
|
// Get next chunk of props
|
|
//
|
|
sc = penum->Next (PROP_CHUNK_SIZE, ssp[0].load(), &csp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// At this point, we either want to call the underlying
|
|
// property container to retrieve all the property data
|
|
// or we just want to emit the names.
|
|
//
|
|
for (isp = 0; isp < csp; isp++)
|
|
{
|
|
Assert (ssp[isp].get().lpwstrName);
|
|
|
|
// We need to track whether or not the getcontenttype
|
|
// property was actually stored or not. If it wasn't,
|
|
// then, we will want to default it at a later time.
|
|
//
|
|
if (!fContentType)
|
|
{
|
|
if (!wcscmp (ssp[isp].get().lpwstrName,
|
|
gc_wszProp_iana_getcontenttype))
|
|
{
|
|
// Note that content-type is included
|
|
//
|
|
fContentType = TRUE;
|
|
}
|
|
}
|
|
|
|
// If we are just asking for names, then add the
|
|
// name to the list now...
|
|
//
|
|
if (!fFindValues)
|
|
{
|
|
CEmitterNode en;
|
|
|
|
// Add the result to the response
|
|
//
|
|
sc = enProp.ScAddNode (ssp[isp].get().lpwstrName, en);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
else
|
|
rglpwstr[isp] = ssp[isp].get().lpwstrName;
|
|
}
|
|
|
|
// If we are just asking about names, then we really
|
|
// are done with this group of properties, otherwise
|
|
// we need to generate the values and emit them.
|
|
//
|
|
if (!fFindValues)
|
|
continue;
|
|
|
|
// Read properties in chunk
|
|
//
|
|
if (csp)
|
|
{
|
|
sc = m_pbag->ReadMultiple (csp,
|
|
rglpwstr,
|
|
propvar[0].addressof(),
|
|
NULL);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
// Emit properties
|
|
//
|
|
for (isp = 0; isp < csp; isp++)
|
|
{
|
|
// Contstruct the pel from the variant
|
|
//
|
|
sc = ScEmitFromVariant (msr,
|
|
enProp,
|
|
ssp[isp].get().lpwstrName,
|
|
const_cast<PROPVARIANT&>(propvar[isp].get()));
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
} while (sc != S_FALSE);
|
|
}
|
|
|
|
get_reserved:
|
|
|
|
// Render all the reserved properties, this relies on the fact that
|
|
// the first non-GET reserved property is "DAV:getcontenttype".
|
|
//
|
|
Assert (iana_rp_content_type == sc_crp_get_reserved);
|
|
|
|
if (!fHrefAdded)
|
|
{
|
|
// Need to build the HREF node because it wasn't built above.
|
|
// This can happen when we don't have a pbag (like on FAT16).
|
|
//
|
|
sc = ScAddHref (enItem,
|
|
m_pmu,
|
|
PwszPath(),
|
|
FCollection(),
|
|
PcvrTranslation());
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
if (!enProp.Pxn())
|
|
{
|
|
// Get the insert point for props
|
|
//
|
|
sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enProp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
}
|
|
|
|
for (irp = 0; irp <= sc_crp_get_reserved; irp++)
|
|
{
|
|
// If the content-type has already been processed, then
|
|
// don't do it here.
|
|
//
|
|
if ((irp == sc_crp_get_reserved) && fContentType)
|
|
break;
|
|
|
|
// Construct the pel from the reserved property
|
|
//
|
|
sc = ScGetReservedProp (msr, enProp, irp, fFindValues);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
// We are done with all the local namespaces
|
|
//
|
|
msr.DoneWithLocalNamespace();
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScSetProps (CStatusCache& csn,
|
|
ULONG cProps,
|
|
LPCWSTR* rgwszProps,
|
|
PROPVARIANT* rgvProps)
|
|
{
|
|
UINT ip;
|
|
SCODE sc = S_OK;
|
|
ULONG hsc;
|
|
|
|
// Zero props is a no-op
|
|
//
|
|
if (!cProps)
|
|
return S_OK;
|
|
|
|
Assert (!FInvalidPbag());
|
|
sc = m_pbag->WriteMultiple (cProps, rgwszProps, rgvProps);
|
|
if (FAILED(sc))
|
|
{
|
|
// This is the common path for when we are trying to access
|
|
// something over an SMB, but the host cannot support the
|
|
// request (it is not an NT5 NTFS machine).
|
|
//
|
|
if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
|
|
return E_DAV_SMB_PROPERTY_ERROR;
|
|
}
|
|
|
|
// we don't know exactly which prop failed,
|
|
// return same error for all props
|
|
//
|
|
hsc = HscFromHresult(sc);
|
|
for (ip = 0; ip < cProps; ip++)
|
|
{
|
|
sc = csn.ScAddErrorStatus (hsc, rgwszProps[ip]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return FAILED(sc) ? S_FALSE : S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScDeleteProps (CStatusCache& csn,
|
|
ULONG cProps,
|
|
LPCWSTR* rgwszProps)
|
|
{
|
|
UINT ip;
|
|
SCODE sc = S_OK;
|
|
ULONG hsc;
|
|
|
|
// Zero props is a no-op
|
|
//
|
|
if (!cProps)
|
|
return S_OK;
|
|
|
|
Assert (!FInvalidPbag());
|
|
sc = m_pbag->DeleteMultiple (cProps, rgwszProps, 0);
|
|
if (FAILED(sc))
|
|
{
|
|
// This is the common path for when we are trying to access
|
|
// something over an SMB, but the host cannot support the
|
|
// request (it is not an NT5 NTFS machine).
|
|
//
|
|
if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
|
|
return E_DAV_SMB_PROPERTY_ERROR;
|
|
}
|
|
|
|
// we don't know exactly which prop failed,
|
|
// return same error for all props
|
|
//
|
|
hsc = HscFromHresult(sc);
|
|
for (ip = 0; ip < cProps; ip++)
|
|
{
|
|
sc = csn.ScAddErrorStatus (hsc, rgwszProps[ip]);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return FAILED(sc) ? S_FALSE : S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CFSProp::ScPersist ()
|
|
{
|
|
// We are not transacted now, just
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
// Content properties --------------------------------------------------------
|
|
//
|
|
SCODE
|
|
ScSetContentProperties (IMethUtil * pmu, LPCWSTR pwszPath, HANDLE hFile)
|
|
{
|
|
LPCWSTR pwszContentType;
|
|
LPCWSTR pwszContentLanguage;
|
|
LPCWSTR pwszContentEncoding;
|
|
|
|
SCODE sc = S_OK;
|
|
|
|
// Figure out which content properties we have
|
|
//
|
|
pwszContentType = pmu->LpwszGetRequestHeader (gc_szContent_Type, FALSE);
|
|
pwszContentLanguage = pmu->LpwszGetRequestHeader (gc_szContent_Language, FALSE);
|
|
pwszContentEncoding = pmu->LpwszGetRequestHeader (gc_szContent_Encoding, FALSE);
|
|
|
|
// Content-Type is special -- it is always set in the metabase.
|
|
// It should be set *before* setting any properties in the property bag
|
|
// since it's OK for the property bag stuff to fail.
|
|
//
|
|
if (NULL != pwszContentType)
|
|
{
|
|
// Setting the content-type will not work if the metabase is read only,
|
|
// which is exactly what is happening in .NET server. So we need to
|
|
// ignore the error -- if any.
|
|
//
|
|
(void) pmu->ScSetContentType (pmu->LpwszRequestUrl(), pwszContentType);
|
|
}
|
|
|
|
// Set any content properties we have in the property bag
|
|
//
|
|
if (pwszContentLanguage || pwszContentEncoding)
|
|
{
|
|
auto_com_ptr<IPropertyBagEx> pbe;
|
|
CStackBuffer<WCHAR> pwsz;
|
|
|
|
// Try to open the property bag. If this fails because we're not
|
|
// on an NTFS filesystem, that's OK. We just won't set the properties
|
|
// there.
|
|
//
|
|
// We need to open propertybag by handle as it the main stream might
|
|
// be locked.
|
|
//
|
|
sc = ScGetPropertyBag (pwszPath,
|
|
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
|
|
&pbe,
|
|
FALSE, // not a collection
|
|
hFile);
|
|
if (FAILED(sc))
|
|
{
|
|
//$ REVIEW:
|
|
//
|
|
// We did our best here. For DocFiles this will fail because
|
|
// of how we have to open the files. What this means is that we
|
|
// could potentially lose the content-encoding and content-language
|
|
// which would put us on par with IIS (they don't store these either).
|
|
//
|
|
sc = S_OK;
|
|
goto ret;
|
|
}
|
|
|
|
CResourceInfo cri;
|
|
CFSProp xpt(pmu,
|
|
pbe,
|
|
pmu->LpwszRequestUrl(),
|
|
pwszPath,
|
|
NULL,
|
|
cri);
|
|
|
|
// Content-Type
|
|
//
|
|
if (NULL != pwszContentType)
|
|
{
|
|
sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_type].pwsz, pwszContentType);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
// Content-Language
|
|
//
|
|
if (NULL != pwszContentLanguage)
|
|
{
|
|
sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_language].pwsz, pwszContentLanguage);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
// Content-Encoding
|
|
//
|
|
if (NULL != pwszContentEncoding)
|
|
{
|
|
sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_encoding].pwsz, pwszContentEncoding);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
}
|
|
|
|
// Persist the changes
|
|
//
|
|
sc = xpt.ScPersist();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
|
|
// It is perfectly OK to ignore SMB errors when setting content properties.
|
|
//
|
|
if (sc == E_DAV_SMB_PROPERTY_ERROR)
|
|
sc = S_OK;
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
// ScFindFileProps -----------------------------------------------------------
|
|
//
|
|
SCODE
|
|
ScFindFileProps (IMethUtil* pmu,
|
|
CFSFind& cfc,
|
|
CXMLEmitter& msr,
|
|
LPCWSTR pwszUri,
|
|
LPCWSTR pwszPath,
|
|
CVRoot* pcvrTranslation,
|
|
CResourceInfo& cri,
|
|
BOOL fEmbedErrorsInResponse)
|
|
{
|
|
auto_com_ptr<IPropertyBagEx> pbag;
|
|
CFSProp fsp(pmu, pbag, pwszUri, pwszPath, pcvrTranslation, cri);
|
|
SCODE sc = S_OK;
|
|
|
|
// Check access permission
|
|
//
|
|
sc = pmu->ScCheckMoveCopyDeleteAccess (pwszUri,
|
|
pcvrTranslation,
|
|
cri.FCollection(),
|
|
FALSE, // do not check against scriptmaps
|
|
MD_ACCESS_READ);
|
|
if (FAILED (sc))
|
|
{
|
|
// No permission to read, we certainly do not want
|
|
// to try and traverse down into the directory (if
|
|
// it was one), we do this by returning S_FALSE.
|
|
//
|
|
if (fEmbedErrorsInResponse)
|
|
{
|
|
sc = cfc.ScErrorAllProps (msr,
|
|
pmu,
|
|
pwszPath,
|
|
cri.FCollection(),
|
|
pcvrTranslation,
|
|
sc);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// Pass back S_FALSE so that no further traversal of
|
|
// this resource is performed
|
|
//
|
|
sc = S_FALSE;
|
|
}
|
|
if (S_OK != sc)
|
|
goto ret;
|
|
}
|
|
|
|
// Don't get pbag for remote files. This would cause the files to
|
|
// be recalled, etc.
|
|
//
|
|
if (!cri.FRemote())
|
|
{
|
|
// Get the IPropertyBagEx interface
|
|
//
|
|
// Before call into this function, we've checked we have read access to
|
|
// this file. so we should always be able to read the proerties however,
|
|
// if the file is write locked, there may be some problems from the OLE
|
|
// properties code.
|
|
//
|
|
sc = ScGetPropertyBag (pwszPath,
|
|
STGM_READ | STGM_SHARE_DENY_WRITE,
|
|
&pbag,
|
|
cri.FCollection());
|
|
if (FAILED (sc))
|
|
{
|
|
// We need to check the volume of the file we are trying
|
|
// to read.
|
|
//
|
|
if (VOLTYPE_NTFS == VolumeType (pwszPath, pmu->HitUser()))
|
|
{
|
|
// Report the errors for this file and come on back...
|
|
//
|
|
if (fEmbedErrorsInResponse)
|
|
{
|
|
sc = cfc.ScErrorAllProps (msr,
|
|
pmu,
|
|
pwszPath,
|
|
cri.FCollection(),
|
|
pcvrTranslation,
|
|
sc);
|
|
}
|
|
goto ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the properties
|
|
//
|
|
sc = cfc.ScFind (msr, pmu, fsp);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
ScFindFilePropsDeep (IMethUtil* pmu,
|
|
CFSFind& cfc,
|
|
CXMLEmitter& msr,
|
|
LPCWSTR pwszUri,
|
|
LPCWSTR pwszPath,
|
|
CVRoot* pcvrTranslation,
|
|
LONG lDepth)
|
|
{
|
|
BOOL fSubDirectoryAccess = TRUE;
|
|
SCODE sc = S_OK;
|
|
|
|
// Query subdirs when do deep query
|
|
//
|
|
Assert ((lDepth == DEPTH_ONE) ||
|
|
(lDepth == DEPTH_ONE_NOROOT) ||
|
|
(lDepth == DEPTH_INFINITY));
|
|
|
|
CDirIter di(pwszUri,
|
|
pwszPath,
|
|
NULL, // no destination url
|
|
NULL, // no destination path
|
|
NULL, // no destination translation
|
|
lDepth == DEPTH_INFINITY);
|
|
|
|
while (S_OK == (sc = di.ScGetNext (fSubDirectoryAccess)))
|
|
{
|
|
CResourceInfo cri;
|
|
|
|
// If we found another directory, then iterate on it
|
|
//
|
|
fSubDirectoryAccess = FALSE;
|
|
if (di.FDirectory())
|
|
{
|
|
auto_ref_ptr<CVRoot> arp;
|
|
|
|
// Skip the special and/or hidden directories
|
|
//
|
|
if (di.FSpecial())
|
|
continue;
|
|
|
|
// If we happen to traverse into a directory
|
|
// that happens to be a vroot (as identified
|
|
// by url), then there is another entry in
|
|
// the list of child vroots that will refer
|
|
// to this directory. Let that processing
|
|
// handle this directory instead of the
|
|
// doing it here.
|
|
//
|
|
// This means that the file hierarchy is not
|
|
// strictly preserved, but I think that this
|
|
// is OK.
|
|
//
|
|
if (pmu->FFindVRootFromUrl (di.PwszUri(), arp))
|
|
continue;
|
|
|
|
// Check the directory browsing bit and see
|
|
// if it is enabled. And only progess down
|
|
// if it is set.
|
|
//
|
|
{
|
|
auto_ref_ptr<IMDData> pMDData;
|
|
if (SUCCEEDED(pmu->HrMDGetData (di.PwszUri(), pMDData.load())) &&
|
|
(pMDData->DwDirBrowsing() & MD_DIRBROW_ENABLED))
|
|
{
|
|
// Prepare to go into the subdir
|
|
//
|
|
fSubDirectoryAccess = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the properties for the resource
|
|
//
|
|
*cri.PfdLoad() = di.FindData();
|
|
sc = ScFindFileProps (pmu,
|
|
cfc,
|
|
msr,
|
|
di.PwszUri(),
|
|
di.PwszSource(),
|
|
pcvrTranslation,
|
|
cri,
|
|
TRUE /*fEmbedErrorsInResponse*/);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// S_FALSE is a special return code that
|
|
// means we did not have access to read the
|
|
// resource...
|
|
//
|
|
if (sc == S_FALSE)
|
|
{
|
|
// ... and since we really didn't have access,
|
|
// we don't want to delve into the children of
|
|
// the resource.
|
|
//
|
|
fSubDirectoryAccess = FALSE;
|
|
}
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
// ScCopyProps ---------------------------------------------------------------
|
|
//
|
|
/*
|
|
* ScCopyProps()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Copies the properties from one resource to another. This is
|
|
* really only useful for copying full directories. Standard file
|
|
* copies do the dirty work for us, but for directories, we need to
|
|
* do it ourselves.
|
|
* If we don't find any propstream on the source, we DELETE
|
|
* any propstream on the destination.
|
|
*/
|
|
SCODE
|
|
ScCopyProps (IMethUtil* pmu, LPCWSTR pwszSrc, LPCWSTR pwszDst,
|
|
BOOL fCollection, HANDLE hSource, HANDLE hDest)
|
|
{
|
|
enum { CHUNK_SIZE = 16 };
|
|
|
|
auto_com_ptr<IPropertyBagEx> pbeSrc;
|
|
auto_com_ptr<IPropertyBagEx> pbeDst;
|
|
auto_com_ptr<IEnumSTATPROPBAG> penumSrc;
|
|
auto_com_ptr<IEnumSTATPROPBAG> penumDst;
|
|
|
|
SCODE sc;
|
|
SCODE scEnum;
|
|
ULONG cProp;
|
|
|
|
MCDTrace ("Dav: MCD: copying props manually: %ws -> %ws\n", pwszSrc, pwszDst);
|
|
|
|
// Get the IPropertyBagEx on the source
|
|
//
|
|
sc = ScGetPropertyBag (pwszSrc,
|
|
STGM_READ | STGM_SHARE_DENY_WRITE,
|
|
&pbeSrc,
|
|
fCollection,
|
|
hSource);
|
|
if (sc != S_OK)
|
|
goto ret;
|
|
|
|
MCDTrace ("Dav: MCD: opened source property bag: %ws\n", pwszSrc);
|
|
|
|
// Get the IPropertyBagEx on the destination
|
|
//
|
|
sc = ScGetPropertyBag (pwszDst,
|
|
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
|
|
&pbeDst,
|
|
fCollection,
|
|
hDest);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
MCDTrace ("Dav: MCD: opened destination property bag: %ws\n", pwszDst);
|
|
|
|
// Get the IEnumSTATPROPBAG interface on source
|
|
//
|
|
sc = pbeSrc->Enum (NULL, 0, &penumSrc);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Get the IEnumSTATPROPBAG interface on destination
|
|
//
|
|
sc = pbeDst->Enum (NULL, 0, &penumDst);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Delete all props from destination if there's any
|
|
//$ COME BACK
|
|
//$ Instead of delete props one by one, we can just delete the
|
|
// prop stream.
|
|
//
|
|
for (;;)
|
|
{
|
|
safe_statpropbag ssp[CHUNK_SIZE];
|
|
safe_propvariant propvar[CHUNK_SIZE];
|
|
ULONG csp = 0;
|
|
|
|
// Get next chunk of props
|
|
//
|
|
Assert (sizeof(safe_statpropbag) == sizeof(STATPROPBAG));
|
|
scEnum = penumDst->Next(CHUNK_SIZE,
|
|
reinterpret_cast<STATPROPBAG *>(&ssp[0]),
|
|
&csp);
|
|
if (FAILED(scEnum))
|
|
{
|
|
sc = scEnum;
|
|
goto ret;
|
|
}
|
|
|
|
MCDTrace ("Dav: MCD: copying %ld props\n", csp);
|
|
|
|
// Delete one by one
|
|
//
|
|
for (cProp = 0; cProp < csp; cProp++)
|
|
{
|
|
Assert (ssp[cProp].get().lpwstrName);
|
|
|
|
// Write to the destination
|
|
//
|
|
LPCWSTR pwsz = ssp[cProp].get().lpwstrName;
|
|
sc = pbeDst->DeleteMultiple (1, &pwsz, 0);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
if (scEnum == S_FALSE)
|
|
break;
|
|
}
|
|
|
|
// Enumerate the props and emit
|
|
//
|
|
for (;;)
|
|
{
|
|
safe_statpropbag ssp[CHUNK_SIZE];
|
|
safe_propvariant propvar[CHUNK_SIZE];
|
|
LPWSTR rglpwstr[CHUNK_SIZE] = {0};
|
|
ULONG csp = 0;
|
|
|
|
// Get next chunk of props
|
|
//
|
|
Assert (sizeof(safe_statpropbag) == sizeof(STATPROPBAG));
|
|
scEnum = penumSrc->Next (CHUNK_SIZE,
|
|
reinterpret_cast<STATPROPBAG *>(&ssp[0]),
|
|
&csp);
|
|
if (FAILED(scEnum))
|
|
{
|
|
sc = scEnum;
|
|
goto ret;
|
|
}
|
|
|
|
// Prepare to call read multiple props
|
|
//
|
|
for (cProp=0; cProp<csp; cProp++)
|
|
{
|
|
Assert (ssp[cProp].get().lpwstrName);
|
|
rglpwstr[cProp] = ssp[cProp].get().lpwstrName;
|
|
}
|
|
|
|
if (csp)
|
|
{
|
|
// Read properties in chunk from source
|
|
//
|
|
sc = pbeSrc->ReadMultiple (csp, rglpwstr, &propvar[0], NULL);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// Write to the destination
|
|
//
|
|
sc = pbeDst->WriteMultiple (csp, rglpwstr, propvar[0].addressof());
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
if (scEnum == S_FALSE)
|
|
break;
|
|
}
|
|
|
|
|
|
ret:
|
|
|
|
// Copying properties is a harmless failure that
|
|
// we should feel free to ignore if we are not on
|
|
// an NFTS volume
|
|
//
|
|
if (FAILED(sc))
|
|
{
|
|
if ((sc == STG_E_INVALIDNAME) ||
|
|
VOLTYPE_NTFS != VolumeType (pwszSrc, pmu->HitUser()) ||
|
|
VOLTYPE_NTFS != VolumeType (pwszDst, pmu->HitUser()))
|
|
{
|
|
// This is the common path for when we are trying to access
|
|
// something over an SMB, but the host cannot support the
|
|
// request (it is not an NT5 NTFS machine).
|
|
//
|
|
sc = S_OK;
|
|
}
|
|
}
|
|
return sc;
|
|
}
|
|
|
|
// OLE 32 IPropertyBagEx Access ----------------------------------------------
|
|
//
|
|
// StgOpenStorageOnHandle() and StgCreateStorageOnHandle() are implemented
|
|
// in OLE32.DLL but not exported. We must load the library and get the proc
|
|
// instances ourselves. We wrap the calls to these functions with this small
|
|
// wrapper such that we can catch when the API changes.
|
|
//
|
|
STDAPI
|
|
StgOpenStorageOnHandle (
|
|
IN HANDLE hStream,
|
|
IN DWORD grfMode,
|
|
IN void *reserved1,
|
|
IN void *reserved2,
|
|
IN REFIID riid,
|
|
OUT void **ppObjectOpen )
|
|
{
|
|
Assert (g_pfnStgOpenStorageOnHandle);
|
|
|
|
// Yes, we've asserted.
|
|
// However, if it does happen, we don't want to fail and we can
|
|
// just treat this like we are on a FAT. (i.e. no property support)
|
|
//
|
|
if (!g_pfnStgOpenStorageOnHandle)
|
|
return E_DAV_SMB_PROPERTY_ERROR;
|
|
|
|
return (*g_pfnStgOpenStorageOnHandle) (hStream,
|
|
grfMode,
|
|
reserved1,
|
|
reserved2,
|
|
riid,
|
|
ppObjectOpen);
|
|
}
|
|
|
|
|
|
// ScGetPropertyBag() --------------------------------------------------------
|
|
//
|
|
// Helper function used to get IPropertyBagEx interface. The important
|
|
// thing to know about this function is that there are three interesting
|
|
// return values:
|
|
//
|
|
// S_OK means everything was OK, and there should be a
|
|
// propertybag associated with the file in the out param.
|
|
//
|
|
// S_FALSE means that the file did not exist. There will
|
|
// not be an associated property bag in that scenario.
|
|
//
|
|
// FAILED(sc) means that there was a failure of some sort,
|
|
// not all of which are fatal. In many cases, we will simply
|
|
// treat the file as if it was hosted on a FAT file system.
|
|
//
|
|
SCODE
|
|
ScGetPropertyBag (LPCWSTR pwszPath,
|
|
DWORD dwAccessDesired,
|
|
IPropertyBagEx** ppbe,
|
|
BOOL fCollection,
|
|
HANDLE hLockFile)
|
|
{
|
|
SCODE sc = S_OK;
|
|
auto_handle<HANDLE> hAlt;
|
|
|
|
// READ!!
|
|
//
|
|
// The storage of property bag is different between docfile and flat file,
|
|
// In a flat file, the property bag is stored in an alternative file stream,
|
|
// (currently, ":Docf_\005Bagaaqy23kudbhchAaq5u2chNd"), in a docfile, the
|
|
// property bag is stored as a substream under the root storage.
|
|
//
|
|
// We should not be concerned with where the pbag is stored. The API with
|
|
// which we implement our IPropertyBagEx access is designed to have the
|
|
// behavior of...
|
|
//
|
|
// We pass in a handle to the file that we want to get a property bag
|
|
// on. If the file is a docfile, then OLE32 will dup the file handle
|
|
// and impose a IPropertyBagEx on the appropriate substorage. If the
|
|
// file is a flat file -- directories included -- then OLE32 opens up
|
|
// a handle on the alternate file stream relative to the handle given
|
|
// in the call.
|
|
//
|
|
// These are the only two combinations allowed, and we rely on this in the
|
|
// following flag checks.
|
|
//
|
|
Assert ((dwAccessDesired == (STGM_READWRITE | STGM_SHARE_EXCLUSIVE)) ||
|
|
(dwAccessDesired == (STGM_READ | STGM_SHARE_DENY_WRITE)));
|
|
|
|
if (hLockFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ULONG dwShare = 0;
|
|
ULONG dwAccess;
|
|
ULONG dwOpen;
|
|
ULONG dwFile;
|
|
|
|
//$ REVIEW: Directories are special critters and we need to
|
|
// open the directory with special access as not to conflict
|
|
// with IIS and/or ASP and their directory change notification
|
|
// stuff
|
|
//
|
|
if (fCollection)
|
|
{
|
|
dwAccess = 1; // FILE_LIST_DIRECTORY
|
|
dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
|
|
dwOpen = OPEN_EXISTING;
|
|
|
|
// The FILE_FLAG_BACKUP_SEMANTICS is used to open a directory handle
|
|
//
|
|
dwFile = FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED;
|
|
}
|
|
else
|
|
{
|
|
// Adjust access/open mode based on desired operation
|
|
//
|
|
dwAccess = GENERIC_READ;
|
|
dwFile = FILE_ATTRIBUTE_NORMAL;
|
|
if (dwAccessDesired & STGM_READWRITE)
|
|
{
|
|
dwAccess |= GENERIC_WRITE;
|
|
dwOpen = OPEN_ALWAYS;
|
|
}
|
|
else
|
|
dwOpen = OPEN_EXISTING;
|
|
|
|
// Adjust the sharing modes as well
|
|
//
|
|
if (dwAccessDesired & STGM_SHARE_DENY_WRITE)
|
|
dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
|
|
}
|
|
hAlt = DavCreateFile (pwszPath,
|
|
dwAccess,
|
|
dwShare,
|
|
NULL,
|
|
dwOpen,
|
|
dwFile,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hAlt.get())
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
|
|
// When open the property bag for read (PROPFIND), ERROR_FILE/PATH_NOT_FOUND
|
|
// could be returned if the file does not exists.
|
|
//
|
|
// When open the property bag for write (PROPPATCH), ERROR_FILE/PATH_NOT_FOUND
|
|
// Could still be returned if the parent of the path does not exists
|
|
// (Such as c:\x\y\z and \x\y does not exist).
|
|
//
|
|
// We need to differenciate the above two cases, when reading a file,
|
|
// it's not a fatal error as you could still try to read reserved
|
|
// properties, when write, we should treat this as a fatal error.
|
|
//
|
|
if ((dwErr == ERROR_FILE_NOT_FOUND) || (dwErr == ERROR_PATH_NOT_FOUND))
|
|
{
|
|
// It's not a fatal error when read
|
|
//
|
|
if (dwAccessDesired == (STGM_READ|STGM_SHARE_DENY_WRITE))
|
|
sc = S_FALSE;
|
|
else
|
|
{
|
|
// This is consistent with Mkcol, it will be mapped to 409
|
|
//
|
|
Assert (dwAccessDesired == (STGM_READWRITE|STGM_SHARE_EXCLUSIVE));
|
|
sc = E_DAV_NONEXISTING_PARENT;
|
|
}
|
|
}
|
|
else
|
|
sc = HRESULT_FROM_WIN32 (dwErr);
|
|
|
|
goto ret;
|
|
}
|
|
|
|
// Setup the handle to use
|
|
//
|
|
hLockFile = hAlt.get();
|
|
}
|
|
|
|
// Try to open the propertybag.
|
|
//
|
|
Assert (hLockFile != 0);
|
|
Assert (hLockFile != INVALID_HANDLE_VALUE);
|
|
sc = StgOpenStorageOnHandle (hLockFile,
|
|
dwAccessDesired,
|
|
NULL,
|
|
NULL,
|
|
IID_IPropertyBagEx,
|
|
reinterpret_cast<LPVOID *>(ppbe));
|
|
if (FAILED (sc))
|
|
{
|
|
goto ret;
|
|
}
|
|
|
|
//$ WARNING
|
|
//
|
|
// Argh! The current implementation of OLE32 returns a non-failure
|
|
// with a NULL property bag!
|
|
//
|
|
if (*ppbe == NULL)
|
|
{
|
|
DebugTrace ("WARNING! OLE32 returned success w/NULL object!\n");
|
|
sc = E_DAV_SMB_PROPERTY_ERROR;
|
|
}
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
// DAV-Properties Implementation ---------------------------------------------------
|
|
//
|
|
|
|
// CPropFindRequest ----------------------------------------------------------------
|
|
//
|
|
class CPropFindRequest :
|
|
public CMTRefCounted,
|
|
private IAsyncIStreamObserver
|
|
{
|
|
//
|
|
// Reference to the CMethUtil
|
|
//
|
|
auto_ref_ptr<CMethUtil> m_pmu;
|
|
|
|
//
|
|
// Translated URI path
|
|
//
|
|
LPCWSTR m_pwszPath;
|
|
|
|
// Resource info
|
|
//
|
|
CResourceInfo m_cri;
|
|
|
|
// Depth
|
|
//
|
|
LONG m_lDepth;
|
|
|
|
// Contexts
|
|
//
|
|
CFSFind m_cfc;
|
|
auto_ref_ptr<CNFFind> m_pcpf;
|
|
|
|
// Request body as an IStream. This stream is async -- it can
|
|
// return E_PENDING from Read() calls.
|
|
//
|
|
auto_ref_ptr<IStream> m_pstmRequest;
|
|
|
|
// The XML parser used to parse the request body using
|
|
// the node factory above.
|
|
//
|
|
auto_ref_ptr<IXMLParser> m_pxprs;
|
|
|
|
// IAsyncIStreamObserver
|
|
//
|
|
VOID AsyncIOComplete();
|
|
|
|
// State functions
|
|
//
|
|
VOID ParseBody();
|
|
VOID DoFind();
|
|
VOID SendResponse( SCODE sc );
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CPropFindRequest (const CPropFindRequest&);
|
|
CPropFindRequest& operator= (const CPropFindRequest&);
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
CPropFindRequest(LPMETHUTIL pmu) :
|
|
m_pmu(pmu),
|
|
m_pwszPath(m_pmu->LpwszPathTranslated()),
|
|
m_lDepth(DEPTH_INFINITY)
|
|
{
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
VOID Execute();
|
|
};
|
|
|
|
VOID
|
|
CPropFindRequest::Execute()
|
|
{
|
|
auto_ref_handle hf;
|
|
LPCWSTR pwsz;
|
|
SCODE sc = S_OK;
|
|
|
|
//
|
|
// First off, tell the pmu that we want to defer the response.
|
|
// Even if we send it synchronously (i.e. due to an error in
|
|
// this function), we still want to use the same mechanism that
|
|
// we would use for async.
|
|
//
|
|
m_pmu->DeferResponse();
|
|
|
|
// Do ISAPI application and IIS access bits checking
|
|
//
|
|
sc = m_pmu->ScIISCheck (m_pmu->LpwszRequestUrl(), MD_ACCESS_READ);
|
|
if (FAILED(sc))
|
|
{
|
|
// Either the request has been forwarded, or some bad error occurred.
|
|
// In either case, quit here and map the error!
|
|
//
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// For PropFind, content-length is required
|
|
//
|
|
//
|
|
if (NULL == m_pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE))
|
|
{
|
|
pwsz = m_pmu->LpwszGetRequestHeader (gc_szTransfer_Encoding, FALSE);
|
|
if (!pwsz || _wcsicmp (pwsz, gc_wszChunked))
|
|
{
|
|
DavTrace ("Dav: PUT: missing content-length in request\n");
|
|
SendResponse(E_DAV_MISSING_LENGTH);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Ensure the resource exists
|
|
//
|
|
sc = m_cri.ScGetResourceInfo (m_pwszPath);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Depth header only applies on directories
|
|
//
|
|
if (m_cri.FCollection())
|
|
{
|
|
// Check the depth header only on collections
|
|
//
|
|
if (!FGetDepth (m_pmu.get(), &m_lDepth))
|
|
{
|
|
// If Depth header is wrong, fail the operation
|
|
//
|
|
SendResponse(E_INVALIDARG);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// This method is gated by If-xxx headers
|
|
//
|
|
sc = ScCheckIfHeaders (m_pmu.get(), m_cri.PftLastModified(), FALSE);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Ensure the URI and resource match
|
|
//
|
|
(void) ScCheckForLocationCorrectness (m_pmu.get(), m_cri, NO_REDIRECT);
|
|
|
|
// Check state headers here.
|
|
//
|
|
// For PROPFIND, when we check the state headers,
|
|
// we want to treat the request as if it were a
|
|
// GET-type request.
|
|
//
|
|
sc = HrCheckStateHeaders (m_pmu.get(), m_pwszPath, TRUE);
|
|
if (FAILED (sc))
|
|
{
|
|
DebugTrace ("DavFS: If-State checking failed.\n");
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Handle locktokens and check for locks on this resource.
|
|
// Our locks don't lock the "secondary file stream" where we keep
|
|
// the properties, so we have to check manually before we do anything else.
|
|
//$REVIEW: Joels, will this change when we switch to NT5 properties?
|
|
//$REVIEW: If so, we need to change this code!
|
|
//
|
|
// If we have a locktoken, try to get the lock handle from the cache.
|
|
// If this fails, fall through and do the normal processing.
|
|
//
|
|
pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
|
|
if (!pwsz || !FGetLockHandle (m_pmu.get(), m_pwszPath, GENERIC_READ, pwsz, &hf))
|
|
{
|
|
// Manually check for locks on this resource.
|
|
// (see if someone ELSE has it locked...)
|
|
// If a read lock exists, tell the caller that it's locked.
|
|
//
|
|
if (FLockViolation (m_pmu.get(), ERROR_SHARING_VIOLATION, m_pwszPath, GENERIC_READ))
|
|
{
|
|
SendResponse(E_DAV_LOCKED);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If there was no request body, we want to get all props
|
|
//
|
|
if (!m_pmu->FExistsRequestBody())
|
|
{
|
|
sc = m_cfc.ScGetAllProps (m_pwszPath);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
DoFind();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// If there's a body, there must be a content-type header
|
|
// and the value must be text/xml
|
|
//
|
|
sc = ScIsContentTypeXML (m_pmu.get());
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace ("Dav: PROPFIND specific fails without specifying a text/xml contenttype\n");
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Instantiate the XML parser
|
|
//
|
|
m_pcpf.take_ownership(new CNFFind(m_cfc));
|
|
m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
|
|
|
|
sc = ScNewXMLParser( m_pcpf.get(),
|
|
m_pstmRequest.get(),
|
|
m_pxprs.load() );
|
|
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace( "CPropFindRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Parse the body
|
|
//
|
|
ParseBody();
|
|
}
|
|
|
|
VOID
|
|
CPropFindRequest::ParseBody()
|
|
{
|
|
SCODE sc;
|
|
|
|
Assert( m_pxprs.get() );
|
|
Assert( m_pcpf.get() );
|
|
Assert( m_pstmRequest.get() );
|
|
|
|
// Parse XML from the request body stream.
|
|
//
|
|
// Add a ref for the following async operation.
|
|
// Use auto_ref_ptr rather than AddRef() for exception safety.
|
|
//
|
|
auto_ref_ptr<CPropFindRequest> pRef(this);
|
|
|
|
sc = ScParseXML (m_pxprs.get(), m_pcpf.get());
|
|
|
|
if ( SUCCEEDED(sc) )
|
|
{
|
|
Assert( S_OK == sc || S_FALSE == sc );
|
|
|
|
DoFind();
|
|
}
|
|
else if ( E_PENDING == sc )
|
|
{
|
|
//
|
|
// The operation is pending -- AsyncIOComplete() will take ownership
|
|
// ownership of the reference when it is called.
|
|
//
|
|
pRef.relinquish();
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( "CPropFindRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
|
|
SendResponse(sc);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CPropFindRequest::AsyncIOComplete()
|
|
{
|
|
// Take ownership of the reference added for the async operation.
|
|
//
|
|
auto_ref_ptr<CPropFindRequest> pRef;
|
|
pRef.take_ownership(this);
|
|
|
|
ParseBody();
|
|
}
|
|
|
|
VOID
|
|
CPropFindRequest::DoFind()
|
|
{
|
|
LPCWSTR pwszUrl = m_pmu->LpwszRequestUrl();
|
|
SCODE sc;
|
|
|
|
// At this point, make sure that they support text/xml
|
|
//
|
|
sc = ScIsAcceptable (m_pmu.get(), gc_wszText_XML);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// All header must be emitted before chunked XML emitting starts
|
|
//
|
|
m_pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
|
|
|
|
// Set the response code and go
|
|
//
|
|
m_pmu->SetResponseCode( HscFromHresult(W_DAV_PARTIAL_SUCCESS),
|
|
NULL,
|
|
0,
|
|
CSEFromHresult(W_DAV_PARTIAL_SUCCESS) );
|
|
|
|
// Find the properties...
|
|
//
|
|
auto_ref_ptr<CXMLEmitter> pmsr;
|
|
auto_ref_ptr<CXMLBody> pxb;
|
|
|
|
pxb.take_ownership (new CXMLBody(m_pmu.get()));
|
|
pmsr.take_ownership (new CXMLEmitter(pxb.get(), &m_cfc));
|
|
|
|
if (DEPTH_ONE_NOROOT != m_lDepth)
|
|
{
|
|
// Get properties for root if it is not a noroot case
|
|
// Depth infinity,noroot is a bad request, that is why
|
|
// check above is valid.
|
|
//
|
|
sc = ScFindFileProps (m_pmu.get(),
|
|
m_cfc,
|
|
*pmsr,
|
|
pwszUrl,
|
|
m_pwszPath,
|
|
NULL,
|
|
m_cri,
|
|
FALSE /*fEmbeddErrorsInResponse*/);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ScFindFilePropsDeep initializes the emitter root only
|
|
// when it sees there's an entry to emit. so we crash
|
|
// in the noroot empty response case, when we try to emit
|
|
// the response, as we have no entry to emit and the
|
|
// root is still NULL.
|
|
// so we here manually initialize the root,
|
|
//
|
|
sc = pmsr->ScSetRoot (gc_wszMultiResponse);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// And then, if apropriate, go deep...
|
|
//
|
|
if (m_cri.FCollection() &&
|
|
(m_lDepth != DEPTH_ZERO) &&
|
|
(m_pmu->MetaData().DwDirBrowsing() & MD_DIRBROW_ENABLED))
|
|
{
|
|
ChainedStringBuffer<WCHAR> sb;
|
|
CVRList vrl;
|
|
|
|
// Apply the property request across all the physical children
|
|
//
|
|
sc = ScFindFilePropsDeep (m_pmu.get(),
|
|
m_cfc,
|
|
*pmsr,
|
|
pwszUrl,
|
|
m_pwszPath,
|
|
NULL,
|
|
m_lDepth);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Enumerate the child vroots and perform the
|
|
// deletion of those directories as well
|
|
//
|
|
m_pmu->ScFindChildVRoots (pwszUrl, sb, vrl);
|
|
for ( ; (!FAILED (sc) && !vrl.empty()); vrl.pop_front())
|
|
{
|
|
auto_ref_ptr<CVRoot> cvr;
|
|
LPCWSTR pwszChildUrl;
|
|
LPCWSTR pwszChildPath;
|
|
|
|
if (m_pmu->FGetChildVRoot (vrl.front().m_pwsz, cvr))
|
|
{
|
|
// Put the url into a multibyte string
|
|
//
|
|
cvr->CchGetVRoot (&pwszChildUrl);
|
|
|
|
// Only process the sub-vroot if we are
|
|
// truely are going deep or if the sub-vroot
|
|
// is the immediate child of the request URI
|
|
//
|
|
if ((m_lDepth == DEPTH_INFINITY) ||
|
|
FIsImmediateParentUrl (pwszUrl, pwszChildUrl))
|
|
{
|
|
CResourceInfo criSub;
|
|
|
|
// Crack the vroot and go...
|
|
//
|
|
cvr->CchGetVRPath (&pwszChildPath);
|
|
sc = criSub.ScGetResourceInfo (pwszChildPath);
|
|
if (!FAILED (sc))
|
|
{
|
|
// Find the properties on the vroot root
|
|
//
|
|
sc = ScFindFileProps (m_pmu.get(),
|
|
m_cfc,
|
|
*pmsr,
|
|
pwszChildUrl,
|
|
pwszChildPath,
|
|
cvr.get(),
|
|
criSub,
|
|
TRUE /*fEmbedErrorsInResponse*/);
|
|
}
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
else if (S_FALSE == sc)
|
|
continue;
|
|
|
|
// Find the properties on the vroot kids
|
|
//
|
|
if (m_lDepth == DEPTH_INFINITY)
|
|
{
|
|
auto_ref_ptr<IMDData> pMDData;
|
|
|
|
// See if we have directory browsing...
|
|
//
|
|
if (SUCCEEDED(m_pmu->HrMDGetData (pwszChildUrl, pMDData.load())) &&
|
|
(pMDData->DwDirBrowsing() & MD_DIRBROW_ENABLED))
|
|
{
|
|
sc = ScFindFilePropsDeep (m_pmu.get(),
|
|
m_cfc,
|
|
*pmsr,
|
|
pwszChildUrl,
|
|
pwszChildPath,
|
|
cvr.get(),
|
|
m_lDepth);
|
|
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Done with the reponse
|
|
//
|
|
pmsr->Done();
|
|
m_pmu->SendCompleteResponse();
|
|
}
|
|
|
|
VOID
|
|
CPropFindRequest::SendResponse( SCODE sc )
|
|
{
|
|
//
|
|
// Set the response code and go
|
|
//
|
|
m_pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
|
|
m_pmu->SendCompleteResponse();
|
|
}
|
|
|
|
/*
|
|
* DAVPropFind()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Win32 file system implementation of the DAV PROPGET method. The
|
|
* PROPGET method responds with a fully constructed XML that provides
|
|
* the values of the resources property/properties.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method utility object
|
|
*/
|
|
void
|
|
DAVPropFind (LPMETHUTIL pmu)
|
|
{
|
|
auto_ref_ptr<CPropFindRequest> pRequest(new CPropFindRequest(pmu));
|
|
|
|
pRequest->Execute();
|
|
}
|
|
|
|
|
|
// CPropPatchRequest ----------------------------------------------------------------
|
|
//
|
|
class CPropPatchRequest :
|
|
public CMTRefCounted,
|
|
private IAsyncIStreamObserver
|
|
{
|
|
//
|
|
// Reference to the CMethUtil
|
|
//
|
|
auto_ref_ptr<CMethUtil> m_pmu;
|
|
|
|
//
|
|
// Translated URI path
|
|
//
|
|
LPCWSTR m_pwszPath;
|
|
|
|
// Holds a handle owned by the lock cache.
|
|
//
|
|
auto_ref_handle m_hf;
|
|
|
|
// Resource info
|
|
//
|
|
CResourceInfo m_cri;
|
|
|
|
// Contexts
|
|
//
|
|
CFSPatch m_cpc;
|
|
auto_ref_ptr<CNFPatch> m_pnfp;
|
|
|
|
// Request body as an IStream. This stream is async -- it can
|
|
// return E_PENDING from Read() calls.
|
|
//
|
|
auto_ref_ptr<IStream> m_pstmRequest;
|
|
|
|
// The XML parser used to parse the request body using
|
|
// the node factory above.
|
|
//
|
|
auto_ref_ptr<IXMLParser> m_pxprs;
|
|
|
|
// IAsyncIStreamObserver
|
|
//
|
|
VOID AsyncIOComplete();
|
|
|
|
// State functions
|
|
//
|
|
VOID ParseBody();
|
|
VOID DoPatch();
|
|
VOID SendResponse( SCODE sc );
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CPropPatchRequest (const CPropPatchRequest&);
|
|
CPropPatchRequest& operator= (const CPropPatchRequest&);
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
CPropPatchRequest(LPMETHUTIL pmu) :
|
|
m_pmu(pmu),
|
|
m_pwszPath(m_pmu->LpwszPathTranslated())
|
|
{
|
|
}
|
|
|
|
SCODE ScInit() { return m_cpc.ScInit(); }
|
|
|
|
// MANIPULATORS
|
|
//
|
|
VOID Execute();
|
|
};
|
|
|
|
VOID
|
|
CPropPatchRequest::Execute()
|
|
{
|
|
LPCWSTR pwsz;
|
|
SCODE sc = S_OK;
|
|
|
|
//
|
|
// First off, tell the pmu that we want to defer the response.
|
|
// Even if we send it synchronously (i.e. due to an error in
|
|
// this function), we still want to use the same mechanism that
|
|
// we would use for async.
|
|
//
|
|
m_pmu->DeferResponse();
|
|
|
|
// Do ISAPI application and IIS access bits checking
|
|
//
|
|
sc = m_pmu->ScIISCheck (m_pmu->LpwszRequestUrl(), MD_ACCESS_WRITE);
|
|
if (FAILED(sc))
|
|
{
|
|
// Either the request has been forwarded, or some bad error occurred.
|
|
// In either case, quit here and map the error!
|
|
//
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// PropPatch must have a content-type header and the value must be text/xml
|
|
//
|
|
sc = ScIsContentTypeXML (m_pmu.get());
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace ("Dav: PROPPATCH fails without specifying a text/xml contenttype\n");
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Look to see the Content-length - required for this operation
|
|
// to continue.
|
|
//
|
|
if (NULL == m_pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE))
|
|
{
|
|
DebugTrace ("Dav: PROPPATCH fails without content\n");
|
|
SendResponse(E_DAV_MISSING_LENGTH);
|
|
return;
|
|
}
|
|
if (!m_pmu->FExistsRequestBody())
|
|
{
|
|
DebugTrace ("Dav: PROPPATCH fails without content\n");
|
|
SendResponse(E_INVALIDARG);
|
|
return;
|
|
}
|
|
|
|
// This method is gated by If-xxx headers
|
|
//
|
|
if (!FAILED (m_cri.ScGetResourceInfo (m_pwszPath)))
|
|
{
|
|
// Ensure the URI and resource match...
|
|
//
|
|
(void) ScCheckForLocationCorrectness (m_pmu.get(), m_cri, NO_REDIRECT);
|
|
|
|
// ... then check the headers
|
|
//
|
|
sc = ScCheckIfHeaders (m_pmu.get(), m_cri.PftLastModified(), FALSE);
|
|
}
|
|
else
|
|
sc = ScCheckIfHeaders (m_pmu.get(), m_pwszPath, FALSE);
|
|
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Check state headers here.
|
|
//
|
|
sc = HrCheckStateHeaders (m_pmu.get(), m_pwszPath, FALSE);
|
|
if (FAILED (sc))
|
|
{
|
|
DebugTrace ("DavFS: If-State checking failed.\n");
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Handle locktokens and check for locks on this resource.
|
|
// Our locks don't lock the "secondary file stream" where we keep
|
|
// the properties, so we have to check manually before we do anything else.
|
|
//$REVIEW: Joels, will this change when we switch to NT5 properties?
|
|
//$REVIEW: If so, we need to change this code!
|
|
//
|
|
// If we have a locktoken, try to get the lock handle from the cache.
|
|
// If this fails, fall through and do the normal processing.
|
|
//
|
|
pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
|
|
if (!pwsz || !FGetLockHandle (m_pmu.get(), m_pwszPath, GENERIC_WRITE, pwsz, &m_hf))
|
|
{
|
|
// Manually check for any write locks on this resource.
|
|
// If a write lock exists, don't process the request.
|
|
//
|
|
if (FLockViolation (m_pmu.get(), ERROR_SHARING_VIOLATION, m_pwszPath, GENERIC_WRITE))
|
|
{
|
|
SendResponse(E_DAV_LOCKED);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Instantiate the XML parser
|
|
//
|
|
m_pnfp.take_ownership(new CNFPatch(m_cpc));
|
|
m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
|
|
|
|
sc = ScNewXMLParser( m_pnfp.get(),
|
|
m_pstmRequest.get(),
|
|
m_pxprs.load() );
|
|
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace( "CPropPatchRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Start parsing it into the context
|
|
//
|
|
ParseBody();
|
|
}
|
|
|
|
VOID
|
|
CPropPatchRequest::ParseBody()
|
|
{
|
|
Assert( m_pxprs.get() );
|
|
Assert( m_pnfp.get() );
|
|
Assert( m_pstmRequest.get() );
|
|
|
|
// Parse XML from the request body stream.
|
|
//
|
|
// Add a ref for the following async operation.
|
|
// Use auto_ref_ptr rather than AddRef() for exception safety.
|
|
//
|
|
auto_ref_ptr<CPropPatchRequest> pRef(this);
|
|
|
|
SCODE sc = ScParseXML (m_pxprs.get(), m_pnfp.get());
|
|
|
|
if ( SUCCEEDED(sc) )
|
|
{
|
|
Assert( S_OK == sc || S_FALSE == sc );
|
|
|
|
DoPatch();
|
|
}
|
|
else if ( E_PENDING == sc )
|
|
{
|
|
//
|
|
// The operation is pending -- AsyncIOComplete() will take ownership
|
|
// ownership of the reference when it is called.
|
|
//
|
|
pRef.relinquish();
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( "CPropPatchRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
|
|
SendResponse(sc);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CPropPatchRequest::AsyncIOComplete()
|
|
{
|
|
// Take ownership of the reference added for the async operation.
|
|
//
|
|
auto_ref_ptr<CPropPatchRequest> pRef;
|
|
pRef.take_ownership(this);
|
|
|
|
ParseBody();
|
|
}
|
|
|
|
VOID
|
|
CPropPatchRequest::DoPatch()
|
|
{
|
|
SCODE sc;
|
|
|
|
// At this point, make sure that they support text/xml
|
|
//
|
|
sc = ScIsAcceptable (m_pmu.get(), gc_wszText_XML);
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Get the IPropertyBagEx on the resource
|
|
// If the file is locked, we must use its handle to
|
|
// get the inteface. otherwise, access will be denied
|
|
//
|
|
auto_com_ptr<IPropertyBagEx> pbag;
|
|
|
|
sc = ScGetPropertyBag (m_pwszPath,
|
|
STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
|
|
&pbag,
|
|
m_cri.FLoaded() ? m_cri.FCollection() : FALSE,
|
|
m_hf.get() ? m_hf.get() : INVALID_HANDLE_VALUE);
|
|
if (FAILED (sc))
|
|
{
|
|
// You can't set properties without a property bag...
|
|
//
|
|
if (VOLTYPE_NTFS != VolumeType (m_pwszPath, m_pmu->HitUser()))
|
|
sc = E_DAV_VOLUME_NOT_NTFS;
|
|
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// All header must be emitted before chunked XML emitting starts
|
|
//
|
|
m_pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
|
|
|
|
// Set the response code and go
|
|
//
|
|
m_pmu->SetResponseCode( HscFromHresult(W_DAV_PARTIAL_SUCCESS),
|
|
NULL,
|
|
0,
|
|
CSEFromHresult(W_DAV_PARTIAL_SUCCESS) );
|
|
|
|
// Apply the request
|
|
//
|
|
auto_ref_ptr<CXMLEmitter> pmsr;
|
|
auto_ref_ptr<CXMLBody> pxb;
|
|
|
|
pxb.take_ownership (new CXMLBody(m_pmu.get()));
|
|
pmsr.take_ownership (new CXMLEmitter(pxb.get(), &m_cpc));
|
|
|
|
CFSProp fsp(m_pmu.get(),
|
|
pbag,
|
|
m_pmu->LpwszRequestUrl(),
|
|
m_pwszPath,
|
|
NULL,
|
|
m_cri);
|
|
|
|
sc = m_cpc.ScPatch (*pmsr, m_pmu.get(), fsp);
|
|
|
|
// Make sure we close the file before sending any response
|
|
//
|
|
pbag.clear();
|
|
|
|
if (FAILED (sc))
|
|
{
|
|
SendResponse(sc);
|
|
return;
|
|
}
|
|
|
|
// Done with the reponse
|
|
//
|
|
pmsr->Done();
|
|
|
|
m_pmu->SendCompleteResponse();
|
|
}
|
|
|
|
VOID
|
|
CPropPatchRequest::SendResponse( SCODE sc )
|
|
{
|
|
//
|
|
// Set the response code and go
|
|
//
|
|
m_pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
|
|
m_pmu->SendCompleteResponse();
|
|
}
|
|
|
|
/*
|
|
* DAVPropPatch()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Win32 file system implementation of the DAV PROPPATCH method. The
|
|
* PROPPATCH method responds with a fully constructed XML that identifies
|
|
* the success of each prop request.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method utility object
|
|
*/
|
|
void
|
|
DAVPropPatch (LPMETHUTIL pmu)
|
|
{
|
|
SCODE sc;
|
|
auto_ref_ptr<CPropPatchRequest> pRequest(new CPropPatchRequest(pmu));
|
|
|
|
sc = pRequest->ScInit();
|
|
if (FAILED(sc))
|
|
{
|
|
pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
|
|
pmu->SendCompleteResponse();
|
|
}
|
|
|
|
pRequest->Execute();
|
|
}
|