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.
1275 lines
37 KiB
1275 lines
37 KiB
#ifndef _DAVIMPL_H_
|
|
#define _DAVIMPL_H_
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// DAVIMPL.H
|
|
//
|
|
// Header for DAV implementation methods interface
|
|
//
|
|
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
|
|
#include <objbase.h> // For common C/C++ interface macros
|
|
|
|
#include <httpext.h>
|
|
#include <ex\oldhack.h> // This file is so that we build as there are some definitions in old headers that are not in new. Will go away.
|
|
|
|
#include <autoptr.h> // For CMTRefCounted base class
|
|
#include <ecb.h>
|
|
#include <request.h>
|
|
#include <response.h>
|
|
#include <sgstruct.h>
|
|
#include <vrenum.h>
|
|
#include <davsc.h>
|
|
#include <body.h> // For async stream interfaces, CRefHandle, etc.
|
|
#include <sz.h>
|
|
#include <ex\calcom.h>
|
|
#include <url.h>
|
|
#include <ex\xml.h>
|
|
|
|
// Resource types ------------------------------------------------------------
|
|
//
|
|
typedef enum {
|
|
|
|
RT_NULL = 0,
|
|
RT_DOCUMENT,
|
|
RT_STRUCTURED_DOCUMENT,
|
|
RT_COLLECTION
|
|
|
|
} RESOURCE_TYPE;
|
|
|
|
enum { MAX_VERSION_LEN = 20*4 };
|
|
|
|
// Access control scope ------------------------------------------------------
|
|
//
|
|
// The acl scope is used when asking the metabase for the IIS access applied
|
|
// to a specific resource.
|
|
//
|
|
// The following enum actually represents bit flags.
|
|
//
|
|
// STRICT: resource access must have all requested access bits
|
|
// LOOSE: resource access must have at least one requested access bit
|
|
// INHERIT: resource access may be inherited from parent.
|
|
//
|
|
enum {
|
|
|
|
ACS_STRICT = 0x00,
|
|
ACS_LOOSE = 0x01,
|
|
ACS_INHERIT = 0x10
|
|
};
|
|
|
|
// Implementation-defined items ----------------------------------------------
|
|
//
|
|
#include <impldef.h>
|
|
#include <implstub.h>
|
|
|
|
// Parser-Defined items ------------------------------------------------------
|
|
//
|
|
|
|
// Content-type --------------------------------------------------------------
|
|
//
|
|
// Content-type is stored in the metabase for HTTPEXT. Each resource, if it
|
|
// has a content-type that is different than the default content-type, is store
|
|
// explicitly on the resource. If the content-type is the default value, then
|
|
// there is no explicit setting in the metabase. Instead, the mapping of a
|
|
// resource's extension to the default content-type is stored.
|
|
//
|
|
BOOL FGetContentTypeFromURI(
|
|
/* [in] */ const IEcb& ecb,
|
|
/* [in] */ LPCWSTR pwszUrl,
|
|
/* [i/o] */ LPWSTR pwszContentType,
|
|
/* [i/o] */ UINT * pcchContentType,
|
|
/* [i/o] */ BOOL * pfIsGlobalMapping = NULL );
|
|
|
|
BOOL FGetContentTypeFromPath( const IEcb& ecb,
|
|
LPCWSTR pwszPath,
|
|
LPWSTR pwszContentType,
|
|
UINT * pcchContentType );
|
|
|
|
SCODE ScSetContentType(
|
|
/* [in] */ const IEcb& ecb,
|
|
/* [in] */ LPCWSTR pwszUrl,
|
|
/* [in] */ LPCWSTR pwszContentType );
|
|
|
|
// Method ID's ---------------------------------------------------------------
|
|
//
|
|
// Each DAV method has its own ID for use in scriptmap inclusion lists.
|
|
//
|
|
typedef enum {
|
|
|
|
MID_UNKNOWN = -1,
|
|
MID_OPTIONS,
|
|
MID_GET,
|
|
MID_HEAD,
|
|
MID_PUT,
|
|
MID_POST,
|
|
MID_DELETE,
|
|
MID_MOVE,
|
|
MID_COPY,
|
|
MID_MKCOL,
|
|
MID_PROPFIND,
|
|
MID_PROPPATCH,
|
|
MID_SEARCH,
|
|
MID_LOCK,
|
|
MID_UNLOCK,
|
|
MID_SUBSCRIBE,
|
|
MID_UNSUBSCRIBE,
|
|
MID_POLL,
|
|
MID_BATCHDELETE,
|
|
MID_BATCHCOPY,
|
|
MID_BATCHMOVE,
|
|
MID_BATCHPROPFIND,
|
|
MID_BATCHPROPPATCH,
|
|
MID_X_MS_ENUMATTS
|
|
} METHOD_ID;
|
|
|
|
// Note: The method name handed to us from IIS is skinny, and there is no
|
|
// real reason why it should be widened for this call. However, when the
|
|
// scriptmap metabase cache goes wide, we may want to do something smarter
|
|
// here.
|
|
//
|
|
METHOD_ID MidMethod (LPCSTR pszMethod);
|
|
METHOD_ID MidMethod (LPCWSTR pwszMethod);
|
|
|
|
// Custom error suberrors ----------------------------------------------------
|
|
//
|
|
typedef enum {
|
|
|
|
// Default
|
|
//
|
|
CSE_NONE = 0,
|
|
|
|
// 401
|
|
//
|
|
CSE_401_LOGON_FAILED = 1, // "Logon Failed"
|
|
CSE_401_SERVER_CONFIG = 2, // "Logon Failed due to server configuration"
|
|
CSE_401_ACL = 3, // "Unauthorized access due to ACL on resource"
|
|
CSE_401_FILTER = 4, // "Authorization failed by filter"
|
|
CSE_401_ISAPI = 5, // "Authorization failed by ISAPI/CGI app"
|
|
|
|
// 403
|
|
//
|
|
CSE_403_EXECUTE = 1, // "Execute Access Forbidden"
|
|
CSE_403_READ = 2, // "Read Access Forbidden"
|
|
CSE_403_WRITE = 3, // "Write Access Forbidden"
|
|
CSE_403_SSL = 4, // "SSL Required"
|
|
CSE_403_SSL_128 = 5, // "SSL 128 Required"
|
|
CSE_403_IP = 6, // "IP Address Rejected"
|
|
CSE_403_CERT_REQUIRED = 7, // "Client Certificate Required"
|
|
CSE_403_SITE = 8, // "Site Access Denied"
|
|
CSE_403_TOO_MANY_USERS = 9, // "Too many users are connected"
|
|
CSE_403_INVALID_CONFIG = 10, // "Invalid configuration"
|
|
CSE_403_PASSWORD_CHANGE = 11, // "Password change"
|
|
CSE_403_MAPPER = 12, // "Mapper access denied"
|
|
CSE_403_CERT_REVOKED = 13, // "Client certificate revoked"
|
|
CSE_403_FOURTEEN, // There is no suberror for this one.
|
|
CSE_403_CALS_EXCEEDED = 15, // "Client Access Licenses exceeded"
|
|
CSE_403_CERT_INVALID = 16, // "Client certificate untrusted or invalid"
|
|
CSE_403_CERT_EXPIRED = 17, // "Client certificate expired"
|
|
|
|
// 500
|
|
//
|
|
CSE_500_SHUTDOWN = 11, // "Server shutting down"
|
|
CSE_500_RESTART = 12, // "Application restarting"
|
|
CSE_500_TOO_BUSY = 13, // "Server too busy"
|
|
CSE_500_INVALID_APP = 14, // "Invalid application"
|
|
CSE_500_GLOBAL_ASA = 15, // "Requests for global.asa not allowed"
|
|
CSE_500_ASP_ERROR = 100, // "ASP error"
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncIStreamObserver
|
|
//
|
|
// Interface to async I/O completion mechanism for IStreams capable of
|
|
// returning E_PENDING from IStream::Read() and IStream::CopyTo().
|
|
// A client would typically associate an IAsyncIStreamObserver with
|
|
// an IStream when creating the latter. When one of the IStream calls
|
|
// returns E_PENDING, the IAsyncIStreamObserver will be called when
|
|
// the pending operation completes.
|
|
//
|
|
class IAsyncIStreamObserver
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncIStreamObserver& operator=( const IAsyncIStreamObserver& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IAsyncIStreamObserver() = 0;
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID AsyncIOComplete() = 0;
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// DAV IMPL UTILITY CLASS
|
|
//
|
|
// Utility class for use by the DAV impl.
|
|
// ALL methods here are inlined on retail builds. DON'T ADD NON-TRIVIAL METHODS!
|
|
//
|
|
class IBodyPart;
|
|
|
|
class IMethUtilBase : public CMTRefCounted
|
|
{
|
|
private:
|
|
// NOT IMPLEMENTED
|
|
IMethUtilBase(const IMethUtilBase&);
|
|
IMethUtilBase& operator=(const IMethUtilBase&);
|
|
protected:
|
|
auto_ref_ptr<IEcbBase> m_pecb;
|
|
auto_ref_ptr<IResponseBase> m_presponse;
|
|
IMethUtilBase(IEcbBase& ecb, IResponseBase& response) :
|
|
m_pecb(&ecb),
|
|
m_presponse(&response)
|
|
{
|
|
}
|
|
public:
|
|
void AddResponseText( UINT cbText,
|
|
LPCSTR pszText )
|
|
{
|
|
m_presponse->AddBodyText(cbText, pszText);
|
|
}
|
|
|
|
void AddResponseText( UINT cchText,
|
|
LPCWSTR pwszText )
|
|
{
|
|
m_presponse->AddBodyText(cchText, pwszText);
|
|
}
|
|
|
|
void AddResponseFile( const auto_ref_handle& hf,
|
|
UINT64 ib64 = 0,
|
|
UINT64 cb64 = 0xFFFFFFFFFFFFFFFF )
|
|
{
|
|
m_presponse->AddBodyFile(hf, ib64, cb64);
|
|
}
|
|
ULONG LcidAccepted() const { return m_pecb->LcidAccepted(); }
|
|
VOID SetLcidAccepted(LCID lcid) { m_pecb->SetLcidAccepted(lcid); }
|
|
LPCWSTR LpwszRequestUrl() const { return m_pecb->LpwszRequestUrl(); }
|
|
BOOL FSsl() const { return m_pecb->FSsl(); }
|
|
BOOL FFrontEndSecured() const { return m_pecb->FFrontEndSecured(); }
|
|
UINT CchUrlPrefix( LPCSTR * ppszPrefix ) const
|
|
{
|
|
return m_pecb->CchUrlPrefix( ppszPrefix );
|
|
}
|
|
UINT CchServerName( LPCSTR * ppszServer ) const
|
|
{
|
|
return m_pecb->CchGetServerName( ppszServer );
|
|
}
|
|
UINT CchGetVirtualRootW( LPCWSTR * ppwszVroot ) const
|
|
{
|
|
return m_pecb->CchGetVirtualRootW(ppwszVroot);
|
|
}
|
|
UINT CchGetMatchingPathW( LPCWSTR * ppwszVroot ) const
|
|
{
|
|
return m_pecb->CchGetMatchingPathW(ppwszVroot);
|
|
}
|
|
LPCWSTR LpwszPathTranslated() const { return m_pecb->LpwszPathTranslated(); }
|
|
SCODE ScUrlFromStoragePath( LPCWSTR pwszPath,
|
|
LPWSTR pwszUrl,
|
|
UINT* pcch )
|
|
{
|
|
return ::ScUrlFromStoragePath( *m_pecb,
|
|
pwszPath,
|
|
pwszUrl,
|
|
pcch );
|
|
}
|
|
|
|
// Instance data lookup --------------------------------------------------
|
|
//
|
|
CInstData& InstData() const
|
|
{
|
|
return m_pecb->InstData();
|
|
}
|
|
|
|
//$NOTE
|
|
//$REVIEW
|
|
// I had to pull HitUser into here as pure virtual because we needed access
|
|
// to it in CWMRenderer class (which has IMethBaseUtil * as a member) in
|
|
// order to do a Revert. This was the only way to get davex and the forms
|
|
// stuff to build without touching lots core headers this late in the Beta3
|
|
// game. We should revisit this in RC1 to try and remove this from the vtable.
|
|
// (9/1/99 - russsi)
|
|
//
|
|
virtual HANDLE HitUser() const = 0;
|
|
|
|
virtual LPCSTR LpszServerName() const = 0;
|
|
|
|
virtual BOOL FAuthenticated() const = 0;
|
|
|
|
};
|
|
|
|
class CMethUtil : public IMethUtilBase
|
|
{
|
|
private:
|
|
auto_ref_ptr<IEcb> m_pecb;
|
|
auto_ref_ptr<IRequest> m_prequest;
|
|
auto_ref_ptr<IResponse> m_presponse;
|
|
|
|
// Method ID
|
|
//
|
|
METHOD_ID m_mid;
|
|
|
|
// Translation
|
|
//
|
|
enum { TRANS_UNKNOWN = -1, TRANS_FALSE, TRANS_TRUE };
|
|
mutable LONG m_lTrans;
|
|
|
|
// Overwrite
|
|
//
|
|
mutable LONG m_lOverwrite;
|
|
|
|
// Depth
|
|
//
|
|
// The values for this member are defined in EX\CALCOM.H
|
|
//
|
|
mutable LONG m_lDepth;
|
|
|
|
// Destination url
|
|
//
|
|
mutable auto_heap_ptr<WCHAR> m_pwszDestinationUrl;
|
|
mutable auto_heap_ptr<WCHAR> m_pwszDestinationPath;
|
|
mutable UINT m_cchDestinationPath;
|
|
mutable auto_ref_ptr<CVRoot> m_pcvrDestination;
|
|
|
|
// CREATORS
|
|
//
|
|
// Only create this object through the construction function!
|
|
//
|
|
CMethUtil( IEcb& ecb, IRequest& request, IResponse& response, METHOD_ID mid ) :
|
|
IMethUtilBase(ecb,response),
|
|
m_pecb(&ecb),
|
|
m_prequest(&request),
|
|
m_presponse(&response),
|
|
m_mid(mid),
|
|
m_lTrans(TRANS_UNKNOWN),
|
|
m_lOverwrite(OVERWRITE_UNKNOWN),
|
|
m_lDepth(DEPTH_UNKNOWN),
|
|
m_cchDestinationPath(0)
|
|
{ }
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CMethUtil( const CMethUtil& );
|
|
CMethUtil& operator=( const CMethUtil& );
|
|
|
|
public:
|
|
|
|
// CREATORS
|
|
//
|
|
// NOTE: Virtual destructor already provided by parent CMTRefCounted.
|
|
//
|
|
static CMethUtil * NewMethUtil( IEcb& ecb,
|
|
IRequest& request,
|
|
IResponse& response,
|
|
METHOD_ID mid )
|
|
{
|
|
return new CMethUtil( ecb, request, response, mid );
|
|
}
|
|
|
|
// Get the pointer to ECB so the we could pass it to the objects that
|
|
// will need to query it for data. This object holds a ref on ECB, so
|
|
// make sure it is used only as long as this object is alive.
|
|
//
|
|
const IEcb * GetEcb() { return m_pecb.get(); }
|
|
|
|
// REQUEST ACCESSORS -----------------------------------------------------
|
|
//
|
|
// Common header evaluation ----------------------------------------------
|
|
//
|
|
// FTranslate():
|
|
//
|
|
// Gets the value of the translate header.
|
|
//
|
|
BOOL FTranslated () const
|
|
{
|
|
// If we don't have a value yet...
|
|
//
|
|
if (TRANS_UNKNOWN == m_lTrans)
|
|
{
|
|
// Translation is expected when:
|
|
//
|
|
// The "translate" header is not present or
|
|
// The "translate" header has a value other than "f" or "F"
|
|
//
|
|
// NOTE: The draft says the valid values are "t" or "f". So, we
|
|
// are draft compliant if we check only the first char. This way
|
|
// we are faster and/or more flexible.
|
|
//
|
|
// BTW: this is also an IIS-approved way to check boolean strings.
|
|
// -- BeckyAn (BA:js)
|
|
//
|
|
LPCSTR psz = m_prequest->LpszGetHeader (gc_szTranslate);
|
|
if (!psz || (*psz != 'f' && *psz != 'F'))
|
|
m_lTrans = TRANS_TRUE;
|
|
else
|
|
m_lTrans = TRANS_FALSE;
|
|
}
|
|
return (TRANS_TRUE == m_lTrans);
|
|
}
|
|
|
|
// LOverwrite():
|
|
//
|
|
// Gets the enumerated value of the Overwrite/Allow-Rename headers.
|
|
//
|
|
LONG LOverwrite () const
|
|
{
|
|
// If we don't have a value yet...
|
|
//
|
|
if (OVERWRITE_UNKNOWN == m_lOverwrite)
|
|
{
|
|
// Overwrite is expected when:
|
|
//
|
|
// The "overwrite" header has a value of "t"
|
|
//
|
|
// NOTE: The draft says the valid values are "t" or "f". So, we
|
|
// are draft compliant if we check only the first char. This way
|
|
// we are faster and/or more flexible.
|
|
//
|
|
// BTW: this is also an IIS-approved way to check boolean strings.
|
|
// -- BeckyAn (BA:js)
|
|
//
|
|
// NOTE also: the default value if there is no Overwrite: header
|
|
// is TRUE -- DO overwrite. Allow-rename if "f", when header is
|
|
// absent.
|
|
//
|
|
LPCSTR pszOverWrite = m_prequest->LpszGetHeader (gc_szOverwrite);
|
|
if ((!pszOverWrite) || (*pszOverWrite == 't' || *pszOverWrite == 'T'))
|
|
{
|
|
m_lOverwrite |= OVERWRITE_YES;
|
|
}
|
|
LPCSTR pszAllowRename = m_prequest->LpszGetHeader (gc_szAllowRename);
|
|
if (pszAllowRename && (*pszAllowRename == 't' || *pszAllowRename == 'T'))
|
|
m_lOverwrite |= OVERWRITE_RENAME;
|
|
}
|
|
return (m_lOverwrite);
|
|
}
|
|
|
|
// LDepth ():
|
|
//
|
|
// Returns an enumerated value identifying the contents of the
|
|
// depth header.
|
|
//
|
|
// The values for the enumeration are defined in EX\CALCOM.H
|
|
//
|
|
LONG __fastcall LDepth (LONG lDefault) const
|
|
{
|
|
// If we do not have a value yet...
|
|
//
|
|
if (DEPTH_UNKNOWN == m_lDepth)
|
|
{
|
|
// Depth can have several values:
|
|
//
|
|
// DEPTH_ZERO corresponds to "0"
|
|
// DEPTH_ONE corresponds to "1"
|
|
// DEPTH_INFINITY corresponds to "Infinity"
|
|
// DEPTH_ONE_NOROOT corresponds to "1,NoRoot"
|
|
// DEPTH_INFINITY_NOROOT corresponds to "Infinty,NoRoot"
|
|
//
|
|
// In the case there is no depth header specified, there
|
|
// is a default that applies to each method. The default
|
|
// is not the same from method to method, so the caller
|
|
// must pass in a default value.
|
|
//
|
|
LPCSTR psz = m_prequest->LpszGetHeader (gc_szDepth);
|
|
|
|
if (NULL == psz)
|
|
{
|
|
m_lDepth = lDefault;
|
|
}
|
|
else
|
|
{
|
|
switch (*psz)
|
|
{
|
|
case '0':
|
|
|
|
if (!_stricmp (psz, gc_sz0))
|
|
m_lDepth = DEPTH_ZERO;
|
|
break;
|
|
|
|
case '1':
|
|
|
|
if (!_stricmp(psz, gc_sz1))
|
|
m_lDepth = DEPTH_ONE;
|
|
else if (!_stricmp(psz, gc_sz1NoRoot))
|
|
m_lDepth = DEPTH_ONE_NOROOT;
|
|
break;
|
|
|
|
case 'i':
|
|
case 'I':
|
|
|
|
if (!_stricmp(psz, gc_szInfinity))
|
|
m_lDepth = DEPTH_INFINITY;
|
|
else if (!_stricmp(psz, gc_szInfinityNoRoot))
|
|
m_lDepth = DEPTH_INFINITY_NOROOT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return m_lDepth;
|
|
}
|
|
|
|
// FBrief():
|
|
//
|
|
// Gets the value of the Brief header.
|
|
//
|
|
BOOL FBrief () const { return m_pecb->FBrief(); }
|
|
|
|
// FIsOffice9Request()
|
|
// Finds out if the request is comming from Rosebud as shipped
|
|
// in Office9.
|
|
//
|
|
BOOL FIsOffice9Request () const
|
|
{
|
|
// Get the User-Agent header
|
|
//
|
|
LPCSTR pszUserAgent = m_prequest->LpszGetHeader(gc_szUser_Agent);
|
|
|
|
// If there is User-Agent header search for the product token of Office9
|
|
//
|
|
if (pszUserAgent)
|
|
{
|
|
LPCSTR pszProductToken = strstr(pszUserAgent, gc_szOffice9UserAgent);
|
|
|
|
// If we have found the Office9 product token, and it is the
|
|
// last token in the string, then the request is from Office9.
|
|
//
|
|
// Important Note: Office9's entire User-Agent is "Microsoft Data
|
|
// Access Internet Publishing Provider DAV". We want to make sure
|
|
// and NOT match "Microsoft Data Access Internet Publishing Provider
|
|
// DAV 1.1 (for instance). So we require the token on the end of
|
|
// the string. NOTE: Exprox currently adds itself BEFORE any
|
|
// User-Agent string, so we're okay here.
|
|
//
|
|
if (pszProductToken &&
|
|
((pszProductToken == pszUserAgent) || FIsWhiteSpace(pszProductToken - 1)) &&
|
|
('\0' == (pszProductToken[gc_cchOffice9UserAgent])))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// FIsRosebudNT5Request()
|
|
// Finds out if the request is comming from Rosebud as shipped
|
|
// in NT5.
|
|
//
|
|
BOOL FIsRosebudNT5Request () const
|
|
{
|
|
// Get the User-Agent header
|
|
//
|
|
LPCSTR pszUserAgent = m_prequest->LpszGetHeader(gc_szUser_Agent);
|
|
|
|
// If there is User-Agent header search for the product token of Rosebud-NT5.
|
|
//
|
|
if (pszUserAgent)
|
|
{
|
|
LPCSTR pszProductToken = strstr(pszUserAgent, gc_szRosebudNT5UserAgent);
|
|
|
|
// If we have found the Rosebud product token, and it is the
|
|
// last token in the string, then the request is from Rosebud.
|
|
//
|
|
// Important Note: Rosebud-NT5's entire User-Agent is "Microsoft Data
|
|
// Access Internet Publishing Provider DAV 1.1". We want to make sure
|
|
// and NOT match "Microsoft Data Access Internet Publishing Provider
|
|
// DAV 1.1 refresh" (for instance). So we require the token on the end of
|
|
// the string. NOTE: Exprox currently adds itself BEFORE any
|
|
// User-Agent string, so we're okay here.
|
|
//
|
|
if (pszProductToken &&
|
|
((pszProductToken == pszUserAgent) || FIsWhiteSpace(pszProductToken - 1)) &&
|
|
('\0' == (pszProductToken[gc_cchRosebudNT5UserAgent])))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Request item access ---------------------------------------------------
|
|
//
|
|
HANDLE HitUser() const { return m_pecb->HitUser(); }
|
|
LPCSTR LpszMethod() const { return m_pecb->LpszMethod(); }
|
|
LPCWSTR LpwszMethod() const { return m_pecb->LpwszMethod(); }
|
|
METHOD_ID MidMethod() const { return m_mid; }
|
|
LPCSTR LpszQueryString() const { return m_pecb->LpszQueryString(); }
|
|
LPCSTR LpszServerName() const
|
|
{
|
|
LPCSTR pszServer;
|
|
(void) m_pecb->CchGetServerName(&pszServer);
|
|
return pszServer;
|
|
}
|
|
LPCSTR LpszVersion() const { return m_pecb->LpszVersion(); }
|
|
BOOL FAuthenticated() const { return m_pecb->FAuthenticated(); }
|
|
|
|
BOOL FGetServerVariable(LPCSTR pszName,
|
|
LPSTR pszValue,
|
|
DWORD * pcbValue) const
|
|
{ return m_pecb->FGetServerVariable(pszName, pszValue, pcbValue); }
|
|
|
|
DWORD CbTotalRequestBytes() const { return m_pecb->CbTotalBytes(); }
|
|
DWORD CbAvailableRequestBytes() const { return m_pecb->CbAvailable(); }
|
|
|
|
// Destination url access ------------------------------------------------
|
|
//
|
|
SCODE __fastcall ScGetDestination( LPCWSTR* ppwszUrl,
|
|
LPCWSTR* ppwszPath,
|
|
UINT* pcchPath,
|
|
CVRoot** ppcvr = NULL) const;
|
|
|
|
// Uncommon header access wide -------------------------------------------
|
|
//
|
|
LPCWSTR LpwszGetRequestHeader( LPCSTR pszName, BOOL fUrlConversion ) const
|
|
{
|
|
// Assert that this is not one of the common headers handled above
|
|
//
|
|
Assert (_stricmp (gc_szTranslate, pszName));
|
|
Assert (_stricmp (gc_szOverwrite, pszName));
|
|
Assert (_stricmp (gc_szDepth, pszName));
|
|
Assert (_stricmp (gc_szDestination, pszName));
|
|
|
|
return m_prequest->LpwszGetHeader(pszName, fUrlConversion);
|
|
}
|
|
|
|
// IIS Access ------------------------------------------------------------
|
|
//
|
|
SCODE ScIISAccess( LPCWSTR pwszURI,
|
|
DWORD dwAccessRequested,
|
|
DWORD* pdwAccessOut = NULL) const;
|
|
|
|
// Utility function to tell whether the scriptmap has an
|
|
// applicable entry for a given URI and access.
|
|
//
|
|
BOOL FInScriptMap( LPCWSTR pwszURI,
|
|
DWORD dwAccess,
|
|
BOOL * pfCGI = NULL,
|
|
SCODE * pscMatch = NULL) const;
|
|
|
|
// Child ISAPI invocation ------------------------------------------------
|
|
//
|
|
// fForward = FALSE means just check if there's a scriptmap entry
|
|
// fCheckISAPIAccess means do extra ACL checking (workaround the ASP access bug)
|
|
// fKeepQueryString should only be set to FALSE on default doc processing
|
|
// pszQueryPrefix allows the query string to be prefixed with new data
|
|
// fIgnoreTFAccess if set to TRUE will ignore the access bits checking in translate: f case,
|
|
// is handy when the acces bits are to be ignored by security checking functions
|
|
// and function is used solely to redirect to the child ISAPI.
|
|
// Example: we have real urls, constructed both from request URL
|
|
// and relative URL parts from XML body (like B* methods). The object specified: t
|
|
// by the request URL might need to be redirected to child ISAPI in the translate
|
|
// case, while actual (constructed) URL-s might look like:
|
|
//
|
|
// /exchange/user1/Inbox.asp/message.eml (where message.eml was relative part)
|
|
//
|
|
// if we do not disable the security checking on the request URL in translate: f case
|
|
// we might be failed out up front in case for example script source access was disabled
|
|
// and it turned to be that directory was named INBOX.ASP.
|
|
// NOTE: of course later the security is being checked on each constructed URL separately,
|
|
// that is why we do not open the security hole.
|
|
//
|
|
// fDoNotForward if set to TRUE instead of forwarding request to child ISAPI it will return bad gateway,
|
|
// which is necessary in the case there would be an attempt to execute child ISAPI on the
|
|
// URL that is a construct of the request URL and the relative URL that comes in the request
|
|
// body (like in B* methods)
|
|
//
|
|
SCODE ScApplyChildISAPI( LPCWSTR pwszURI,
|
|
DWORD dwAccess,
|
|
BOOL fCheckISAPIAccess = FALSE,
|
|
BOOL fKeepQueryString = TRUE) const;
|
|
|
|
// Apply child ISAPI if necessary, if not, verify if desired access
|
|
// is granted
|
|
//
|
|
SCODE ScIISCheck ( LPCWSTR pwszURI,
|
|
DWORD dwDesired = 0,
|
|
BOOL fCheckISAPIAccess = FALSE) const;
|
|
|
|
// Move/Copy/Delete access
|
|
//
|
|
SCODE ScCheckMoveCopyDeleteAccess (
|
|
/* [in] */ LPCWSTR pwszUrl,
|
|
/* [in] */ CVRoot* pcvr,
|
|
/* [in] */ BOOL fDirectory,
|
|
/* [in] */ BOOL fCheckScriptmaps,
|
|
/* [in] */ DWORD dwAccess);
|
|
|
|
// Url parsing/construction ----------------------------------------------
|
|
//
|
|
BOOL __fastcall FIsVRoot (LPCWSTR pwszURI);
|
|
|
|
// Exchange and FS uses different URL to path mappers.
|
|
//
|
|
SCODE ScStoragePathFromUrl( LPCWSTR pwszUrl,
|
|
LPWSTR pwszPath,
|
|
UINT * pcch ) const
|
|
{
|
|
return ::ScStoragePathFromUrl(
|
|
*m_pecb,
|
|
pwszUrl,
|
|
pwszPath,
|
|
pcch );
|
|
}
|
|
|
|
// Construct the redirect url given the server name
|
|
//
|
|
SCODE ScConstructRedirectUrl( BOOL fNeedSlash,
|
|
LPSTR * ppszUrl,
|
|
LPCWSTR pwszServer = NULL ) const
|
|
{
|
|
return ::ScConstructRedirectUrl( *m_pecb,
|
|
fNeedSlash,
|
|
ppszUrl,
|
|
pwszServer );
|
|
}
|
|
|
|
SCODE ScStripAndCheckHttpPrefix( LPCWSTR * ppwszUrl ) const
|
|
{
|
|
return ::ScStripAndCheckHttpPrefix( *m_pecb,
|
|
ppwszUrl );
|
|
}
|
|
|
|
|
|
// Fetch the metadata for the request URI
|
|
//
|
|
IMDData& MetaData() const
|
|
{
|
|
return m_pecb->MetaData();
|
|
}
|
|
|
|
// Fetch the metadata for an aribtrary URI.
|
|
// Note: use the MetaData() accessor above
|
|
// to get the metadata for the request URI.
|
|
//
|
|
HRESULT HrMDGetData( LPCWSTR pwszURI,
|
|
IMDData ** ppMDData )
|
|
{
|
|
Assert(m_pecb.get());
|
|
return ::HrMDGetData( *m_pecb,
|
|
pwszURI,
|
|
ppMDData );
|
|
}
|
|
|
|
HRESULT HrMDGetData( LPCWSTR pwszMDPathAccess,
|
|
LPCWSTR pwszMDPathOpen,
|
|
IMDData ** ppMDData )
|
|
{
|
|
Assert(m_pecb.get());
|
|
return ::HrMDGetData( *m_pecb,
|
|
pwszMDPathAccess,
|
|
pwszMDPathOpen,
|
|
ppMDData );
|
|
}
|
|
|
|
HRESULT HrMDIsAuthorViaFrontPageNeeded( BOOL * pfFrontPageWeb ) const
|
|
{
|
|
Assert( pfFrontPageWeb );
|
|
|
|
return ::HrMDIsAuthorViaFrontPageNeeded(*m_pecb,
|
|
m_pecb->PwszMDPathVroot(),
|
|
pfFrontPageWeb);
|
|
}
|
|
|
|
BOOL FGetContentType( LPCWSTR pwszURI,
|
|
LPWSTR pwszContentType,
|
|
UINT * pcchContentType ) const
|
|
{
|
|
return ::FGetContentTypeFromURI( *m_pecb,
|
|
pwszURI,
|
|
pwszContentType,
|
|
pcchContentType );
|
|
}
|
|
|
|
SCODE ScSetContentType( LPCWSTR pwszURI,
|
|
LPCWSTR pwszContentType )
|
|
{
|
|
return ::ScSetContentType( *m_pecb,
|
|
pwszURI,
|
|
pwszContentType );
|
|
}
|
|
|
|
// Url and child virtual directories -------------------------------------
|
|
//
|
|
SCODE ScFindChildVRoots( LPCWSTR pwszUri,
|
|
ChainedStringBuffer<WCHAR>& sb,
|
|
CVRList& vrl )
|
|
{
|
|
// Get the wide metapath, and make sure the URL is
|
|
// stripped before we call into the MDPath processing
|
|
//
|
|
Assert (pwszUri == PwszUrlStrippedOfPrefix (pwszUri));
|
|
UINT cb = ::CbMDPathW(*m_pecb, pwszUri);
|
|
CStackBuffer<WCHAR,MAX_PATH> pwszMetaPath;
|
|
if (NULL == pwszMetaPath.resize(cb))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Find the vroot
|
|
//
|
|
MDPathFromURIW (*m_pecb, pwszUri, pwszMetaPath.get());
|
|
return CChildVRCache::ScFindChildren( *m_pecb, pwszMetaPath.get(), sb, vrl );
|
|
}
|
|
|
|
BOOL FGetChildVRoot( LPCWSTR pwszMetaPath, auto_ref_ptr<CVRoot>& cvr )
|
|
{
|
|
return CChildVRCache::FFindVroot( *m_pecb, pwszMetaPath, cvr );
|
|
}
|
|
|
|
BOOL FFindVRootFromUrl( LPCWSTR pwszUri, auto_ref_ptr<CVRoot>& cvr )
|
|
{
|
|
// Get the wide metapath, and make sure the URL is
|
|
// stripped before we call into the MDPath processing
|
|
//
|
|
Assert (pwszUri == PwszUrlStrippedOfPrefix (pwszUri));
|
|
UINT cb = ::CbMDPathW(*m_pecb, pwszUri);
|
|
CStackBuffer<WCHAR,MAX_PATH> pwszMetaPath(cb);
|
|
if (NULL == pwszMetaPath.resize(cb))
|
|
return FALSE;
|
|
|
|
// Build the path and go...
|
|
//
|
|
MDPathFromURIW (*m_pecb, pwszUri, pwszMetaPath.get());
|
|
_wcslwr (pwszMetaPath.get());
|
|
|
|
// If the last char of the metabase path is a slash, trim
|
|
// it.
|
|
//
|
|
cb = static_cast<UINT>(wcslen(pwszMetaPath.get()));
|
|
if (L'/' == pwszMetaPath[cb - 1])
|
|
pwszMetaPath[cb - 1] = L'\0';
|
|
|
|
// Find the vroot
|
|
//
|
|
return CChildVRCache::FFindVroot( *m_pecb, pwszMetaPath.get(), cvr );
|
|
}
|
|
|
|
// Exception handler -----------------------------------------------------
|
|
//
|
|
// Impls must call this function whenever they catch an exception on
|
|
// a thread other than the thread on which the request initially
|
|
// executed. This call causes a 500 Server Error response to be sent
|
|
// if no other response is already in the process of being sent (only
|
|
// a problem for chunked responses). It also ensures that the
|
|
// EXTENSION_CONTROL_BLOCK from IIS will be properly cleaned up
|
|
// regardless whether the pmu or any other object gets leaked as a result
|
|
// of the exception. This last function keeps IIS from hanging on
|
|
// shutdown.
|
|
//
|
|
void HandleException()
|
|
{
|
|
//
|
|
// Just forward the exception handling to the ECB and hope it works.
|
|
// If it doesn't then there is nothing we can do about it -- we
|
|
// may leak the ECB which would cause IIS to hang on shutdown.
|
|
//
|
|
(VOID) m_pecb->HSEHandleException();
|
|
}
|
|
|
|
// Async error response handler ------------------------------------------
|
|
//
|
|
// Used to handle non-exception asynchronous error responses. The main
|
|
// distinction between the exception and non-exception case is that in
|
|
// the exception case we force a cleanup of the ECB, but here we don't.
|
|
// Also, the exception case is hardwired to 500 Internal Server Error,
|
|
// but this function can be used to send any 500 level error (e.g. a
|
|
// 503 Service Unavailable).
|
|
//
|
|
VOID SendAsyncErrorResponse( DWORD dwStatusCode,
|
|
LPCSTR pszBody = NULL,
|
|
DWORD cchzBody = 0,
|
|
LPCSTR pszStatusDescription = NULL,
|
|
DWORD cchzStatusDescription = 0 )
|
|
{
|
|
m_pecb->SendAsyncErrorResponse( dwStatusCode,
|
|
pszBody,
|
|
cchzBody,
|
|
pszStatusDescription,
|
|
cchzStatusDescription );
|
|
}
|
|
|
|
// Request body access ---------------------------------------------------
|
|
//
|
|
BOOL FExistsRequestBody() const
|
|
{
|
|
return m_prequest->FExistsBody();
|
|
}
|
|
|
|
IStream * GetRequestBodyIStream( IAsyncIStreamObserver& obs ) const
|
|
{
|
|
return m_prequest->GetBodyIStream(obs);
|
|
}
|
|
|
|
VOID AsyncPersistRequestBody( IAsyncStream& stm,
|
|
IAsyncPersistObserver& obs ) const
|
|
{
|
|
m_prequest->AsyncImplPersistBody( stm, obs );
|
|
}
|
|
|
|
// Response manipulators -------------------------------------------------
|
|
//
|
|
SCODE ScRedirect( LPCSTR pszURI )
|
|
{
|
|
return m_presponse->ScRedirect(pszURI);
|
|
}
|
|
|
|
void RestartResponse()
|
|
{
|
|
m_presponse->ClearHeaders();
|
|
m_presponse->ClearBody();
|
|
}
|
|
|
|
void SupressBody()
|
|
{
|
|
// This should only be called by an IMPL. in response to a HEAD
|
|
// request...
|
|
//
|
|
Assert (MID_HEAD == MidMethod());
|
|
m_presponse->SupressBody();
|
|
}
|
|
|
|
void SetResponseCode( ULONG ulCode,
|
|
LPCSTR lpszBodyDetail,
|
|
UINT uiBodyDetail,
|
|
UINT uiCustomSubError = CSE_NONE )
|
|
{
|
|
m_presponse->SetStatus( ulCode,
|
|
NULL,
|
|
uiCustomSubError,
|
|
lpszBodyDetail,
|
|
uiBodyDetail );
|
|
}
|
|
|
|
void SetResponseHeader( LPCSTR pszName,
|
|
LPCSTR pszValue,
|
|
BOOL fMultiple = FALSE )
|
|
{
|
|
m_presponse->SetHeader(pszName, pszValue, fMultiple);
|
|
}
|
|
|
|
void SetResponseHeader( LPCSTR pszName,
|
|
LPCWSTR pwszValue,
|
|
BOOL fMultiple = FALSE )
|
|
{
|
|
m_presponse->SetHeader(pszName, pwszValue, fMultiple);
|
|
}
|
|
|
|
void AddResponseStream( LPSTREAM pstm )
|
|
{
|
|
Assert( !IsBadReadPtr(pstm, sizeof(IStream)) );
|
|
m_presponse->AddBodyStream(*pstm);
|
|
}
|
|
|
|
void AddResponseStream( LPSTREAM pstm,
|
|
UINT ibOffset,
|
|
UINT cbSize )
|
|
{
|
|
Assert( cbSize > 0 );
|
|
Assert( !IsBadReadPtr(pstm, sizeof(IStream)) );
|
|
m_presponse->AddBodyStream(*pstm, ibOffset, cbSize);
|
|
}
|
|
|
|
void AddResponseBodyPart( IBodyPart * pBodyPart )
|
|
{
|
|
m_presponse->AddBodyPart( pBodyPart );
|
|
}
|
|
|
|
// Common response emission routines -------------------------------------
|
|
//
|
|
void __fastcall EmitLocation ( LPCSTR pszHeader,
|
|
LPCWSTR pwszURI,
|
|
BOOL fCollection);
|
|
|
|
void __fastcall EmitLastModified (FILETIME * pft);
|
|
void __fastcall EmitCacheControlAndExpires (LPCWSTR pwszUrl);
|
|
|
|
SCODE __fastcall ScEmitHeader (LPCWSTR pwszContent,
|
|
LPCWSTR pwszURI = NULL,
|
|
FILETIME* pftLastModification = NULL);
|
|
|
|
|
|
// Etags -----------------------------------------------------------------
|
|
//
|
|
void __fastcall EmitETag (FILETIME * pft);
|
|
void __fastcall EmitETag (LPCWSTR pwszPath);
|
|
|
|
// Deferred Sends --------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
// DeferResponse()
|
|
//
|
|
// If called in an implementation method, this function prevents the
|
|
// default automatic sending of the response upon the implementation
|
|
// method's return.
|
|
//
|
|
// After calling this function the implementation must call either
|
|
// SendPartialResponse() or SendCompleteResponse() to send the response.
|
|
//
|
|
void DeferResponse() { m_presponse->Defer(); }
|
|
|
|
//
|
|
// SendPartialResponse()
|
|
//
|
|
// Starts sending accumulated response data. The impl is expected to
|
|
// continue adding response data after calling this function. The impl
|
|
// must call SendCompleteResponse() to indicate when it is done adding
|
|
// response data.
|
|
//
|
|
void SendPartialResponse() { m_presponse->SendPartial(); }
|
|
|
|
//
|
|
// SendCompleteResponse()
|
|
//
|
|
// Starts sending accumulated response data. Indicates that the impl
|
|
// is done adding response data. The impl must not add response data
|
|
// after calling this function.
|
|
//
|
|
void SendCompleteResponse() { m_presponse->SendComplete(); }
|
|
|
|
// Expiration/Cache-Control ----------------------------------------------
|
|
//
|
|
SCODE ScGetExpirationTime( IN LPCWSTR pwszURI,
|
|
IN LPWSTR pwszExpire,
|
|
IN OUT UINT * pcch);
|
|
|
|
// Allow header ----------------------------------------------------------
|
|
//
|
|
void SetAllowHeader (RESOURCE_TYPE rt);
|
|
|
|
// Metadata helpers ------------------------------------------------------
|
|
//
|
|
UINT CbMDPathW(LPCWSTR pwszUrl) const { return ::CbMDPathW(*m_pecb, pwszUrl); }
|
|
VOID MDPathFromUrlW( LPCWSTR pwszUrl, LPWSTR pwszMDPath )
|
|
{
|
|
::MDPathFromURIW (*m_pecb, pwszUrl, pwszMDPath);
|
|
}
|
|
};
|
|
|
|
typedef CMethUtil * LPMETHUTIL;
|
|
typedef CMethUtil IMethUtil;
|
|
|
|
// ========================================================================
|
|
//
|
|
// STRUCT SImplMethods
|
|
//
|
|
// Implementation methods
|
|
//
|
|
typedef void (DAVMETHOD)( LPMETHUTIL );
|
|
|
|
extern DAVMETHOD DAVOptions;
|
|
extern DAVMETHOD DAVGet;
|
|
extern DAVMETHOD DAVHead;
|
|
extern DAVMETHOD DAVPut;
|
|
extern DAVMETHOD DAVPost;
|
|
extern DAVMETHOD DAVDelete;
|
|
extern DAVMETHOD DAVMove;
|
|
extern DAVMETHOD DAVCopy;
|
|
extern DAVMETHOD DAVMkCol;
|
|
extern DAVMETHOD DAVPropFind;
|
|
extern DAVMETHOD DAVPropPatch;
|
|
extern DAVMETHOD DAVSearch;
|
|
extern DAVMETHOD DAVLock;
|
|
extern DAVMETHOD DAVUnlock;
|
|
extern DAVMETHOD DAVSubscribe;
|
|
extern DAVMETHOD DAVUnsubscribe;
|
|
extern DAVMETHOD DAVPoll;
|
|
extern DAVMETHOD DAVBatchDelete;
|
|
extern DAVMETHOD DAVBatchMove;
|
|
extern DAVMETHOD DAVBatchCopy;
|
|
extern DAVMETHOD DAVBatchPropFind;
|
|
extern DAVMETHOD DAVBatchPropPatch;
|
|
extern DAVMETHOD DAVEnumAtts;
|
|
extern DAVMETHOD DAVUnsupported; // Returns 501 Not Supported
|
|
|
|
// ========================================================================
|
|
//
|
|
// IIS ISAPI Extension interface
|
|
//
|
|
class CDAVExt
|
|
{
|
|
public:
|
|
static BOOL FInitializeDll( HINSTANCE, DWORD );
|
|
static BOOL FVersion ( HSE_VERSION_INFO * );
|
|
static BOOL FTerminate();
|
|
static VOID LogECBString( LPEXTENSION_CONTROL_BLOCK, LONG, LPCSTR );
|
|
static DWORD DwMain( LPEXTENSION_CONTROL_BLOCK, BOOL fUseRawUrlMappings = FALSE );
|
|
};
|
|
|
|
// Map last error to HTTP response code --------------------------------------
|
|
//
|
|
UINT HscFromLastError (DWORD dwErr);
|
|
UINT HscFromHresult (HRESULT hr);
|
|
UINT CSEFromHresult (HRESULT hr);
|
|
|
|
// Virtual root mappings -----------------------------------------------------
|
|
//
|
|
BOOL FWchFromHex (LPCWSTR pwsz, WCHAR * pwch);
|
|
|
|
// Lock header lookup --------------------------------------------------------
|
|
//
|
|
BOOL FGetLockTimeout (LPMETHUTIL pmu, DWORD * pdwSeconds, DWORD dwMaxOverride = 0);
|
|
|
|
// Content type mappings -----------------------------------------------------
|
|
//
|
|
SCODE ScIsAcceptable (IMethUtil * pmu, LPCWSTR pwszContent);
|
|
SCODE ScIsContentType (IMethUtil * pmu, LPCWSTR pwszType, LPCWSTR pwszTypeAnother = NULL);
|
|
inline SCODE ScIsContentTypeXML(IMethUtil * pmu)
|
|
{
|
|
return ScIsContentType(pmu, gc_wszText_XML, gc_wszApplication_XML);
|
|
}
|
|
|
|
// Range header processors ---------------------------------------------------
|
|
//
|
|
class CRangeParser;
|
|
|
|
SCODE
|
|
ScProcessByteRanges(
|
|
/* [in] */ IMethUtil * pmu,
|
|
/* [in] */ LPCWSTR pwszPath,
|
|
/* [in] */ DWORD dwSizeLow,
|
|
/* [in] */ DWORD dwSizeHigh,
|
|
/* [in] */ CRangeParser * pByteRange );
|
|
|
|
SCODE
|
|
ScProcessByteRangesFromEtagAndTime (
|
|
/* [in] */ IMethUtil * pmu,
|
|
/* [in] */ DWORD dwSizeLow,
|
|
/* [in] */ DWORD dwSizeHigh,
|
|
/* [in] */ CRangeParser *pByteRange,
|
|
/* [in] */ LPCWSTR pwszEtag,
|
|
/* [in] */ FILETIME * pft );
|
|
|
|
// Non-Async IO on Top of Overlapped Files -----------------------------------
|
|
//
|
|
BOOL ReadFromOverlapped (HANDLE hf,
|
|
LPVOID pvBuf,
|
|
ULONG cbToRead,
|
|
ULONG * pcbRead,
|
|
OVERLAPPED * povl);
|
|
BOOL WriteToOverlapped (HANDLE hf,
|
|
const void * pvBuf,
|
|
ULONG cbToRead,
|
|
ULONG * pcbRead,
|
|
OVERLAPPED * povl);
|
|
|
|
// DAVEX LOCK Support routines -----------------------------------------------
|
|
//
|
|
class CXMLEmitter;
|
|
class CEmitterNode;
|
|
SCODE ScBuildLockDiscovery (CXMLEmitter& emitter,
|
|
CEmitterNode& en,
|
|
LPCWSTR wszLockToken,
|
|
LPCWSTR wszLockType,
|
|
LPCWSTR wszLockScope,
|
|
BOOL fRollback,
|
|
BOOL fDepthInfinity,
|
|
DWORD dwTimeout,
|
|
LPCWSTR pwszOwnerComment,
|
|
LPCWSTR pwszSubType);
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CXMLBody
|
|
// This class is wrapper around CTextBodyPart, it collects small XML pieces
|
|
// and save them in a CTextBodyPart, the body part will be added to body part
|
|
// list when it grow large enough. This avoid contructing CTextBodyPart too
|
|
// frequently.
|
|
//
|
|
class CXMLBody : public IXMLBody
|
|
{
|
|
private:
|
|
auto_ptr<CTextBodyPart> m_ptbp;
|
|
auto_ref_ptr<IMethUtil> m_pmu;
|
|
BOOL m_fChunked;
|
|
|
|
// non-implemented
|
|
//
|
|
CXMLBody(const CXMLBody& p);
|
|
CXMLBody& operator=(const CXMLBody& p);
|
|
|
|
// Helper
|
|
//
|
|
VOID SendCurrentChunk()
|
|
{
|
|
XmlTrace ("Dav: Xml: adding %ld bytes to body\n", m_ptbp->CbSize64());
|
|
m_pmu->AddResponseBodyPart (m_ptbp.relinquish());
|
|
|
|
//$REVIEW: The auto_ptr clas defined in \inc\autoptr.h is different from
|
|
//$REVIEW: the one defined in \inc\ex\autoptr.h. it does not set px
|
|
//$REVIEW: to zero when it relinquish. I believe this is a bug. I am not
|
|
//$REVIEW: sure if anyone is relying on this behavior, so I did not go ahead
|
|
//$REVIEW: fix the relinquish(), a better/complete fix will be moving
|
|
//$REVIEW: everyoen to \inc\ex\autoptr.h
|
|
//$REVIEW:
|
|
m_ptbp.clear();
|
|
|
|
// Send the data from this chunk back to the client before
|
|
// we go fetch the next chunk.
|
|
//
|
|
if (m_fChunked)
|
|
m_pmu->SendPartialResponse();
|
|
}
|
|
|
|
public:
|
|
// ctor & dtor
|
|
//
|
|
CXMLBody (IMethUtil * pmu, BOOL fChunked = TRUE)
|
|
: m_pmu(pmu),
|
|
m_fChunked(fChunked)
|
|
{
|
|
}
|
|
|
|
~CXMLBody ()
|
|
{
|
|
}
|
|
|
|
// IXMLBody methods
|
|
//
|
|
SCODE ScAddTextBytes ( UINT cbText, LPCSTR lpszText );
|
|
|
|
VOID Done()
|
|
{
|
|
if (m_ptbp.get())
|
|
SendCurrentChunk();
|
|
}
|
|
};
|
|
|
|
SCODE ScAddTitledHref (CEmitterNode& enParent,
|
|
IMethUtil * pmu,
|
|
LPCWSTR pwszTag,
|
|
LPCWSTR pwszPath,
|
|
BOOL fCollection = FALSE,
|
|
CVRoot* pcvrTranslate = NULL);
|
|
|
|
inline
|
|
SCODE ScAddHref (CEmitterNode& enParent,
|
|
IMethUtil * pmu,
|
|
LPCWSTR pwszPath,
|
|
BOOL fCollection = FALSE,
|
|
CVRoot* pcvrTranslate = NULL)
|
|
{
|
|
return ScAddTitledHref (enParent,
|
|
pmu,
|
|
gc_wszXML__Href,
|
|
pwszPath,
|
|
fCollection,
|
|
pcvrTranslate);
|
|
}
|
|
|
|
//$HACK:ROSEBUD_TIMEOUT_HACK
|
|
// For the bug where rosebud waits until the last second
|
|
// before issueing the refresh. Need to filter out this check with
|
|
// the user agent string. The hack is to increase the timeout
|
|
// by 30 seconds and send back the requested timeout.
|
|
//
|
|
DEC_CONST gc_dwSecondsHackTimeoutForRosebud = 120;
|
|
|
|
//$HACK:END:ROSEBUD_TIMEOUT_HACK
|
|
//
|
|
|
|
#endif // !defined(_DAVIMPL_H_)
|