📄 davmain.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: davmain.cpp
Abstract: WebDAV main verb handeling routines
--*/
#include "httpd.h"
const DWORD g_fWebDavModule = TRUE;
//
// Global initialization and deinitialization routines
//
BOOL CGlobalVariables::InitWebDav(CReg *pWebsite) {
// WebDAV is enabled by default, but can be disabled via registry.
if (!pWebsite->ValueDW(RV_WEBDAV,TRUE))
return FALSE;
if (m_fRootSite && !g_pFileLockManager)
g_pFileLockManager = new CWebDavFileLockManager;
if (! (g_pFileLockManager && g_pFileLockManager->IsInitialized()))
return FALSE;
m_dwDavDefaultLockTimeout = pWebsite->ValueDW(RV_DEFAULT_DAV_LOCK,DEFAULT_DAV_LOCK_TIMEOUT);
m_dwDavDefaultLockTimeout = m_dwDavDefaultLockTimeout / 1000; // store number in seconds.
return TRUE;
}
void CGlobalVariables::DeInitWebDav(void) {
DEBUGCHK(m_fRootSite);
DEBUGCHK(g_fState == SERVICE_STATE_UNLOADING);
if (g_pFileLockManager)
delete g_pFileLockManager;
return;
}
//
// Map a verb request into appropriate DAV handler
//
typedef BOOL (WINAPI *PFN_DAVEXECUTE)(CWebDav *pThis);
BOOL DavUnsupported(CWebDav *pThis) { return pThis->DavUnsupported(); }
BOOL DavOptions(CWebDav *pThis) { return pThis->DavOptions() ; }
BOOL DavPut(CWebDav *pThis) { return pThis->DavPut() ; }
BOOL DavMove(CWebDav *pThis) { return pThis->DavMove() ; }
BOOL DavCopy(CWebDav *pThis) { return pThis->DavCopy() ; }
BOOL DavDelete(CWebDav *pThis) { return pThis->DavDelete() ; }
BOOL DavMkcol(CWebDav *pThis) { return pThis->DavMkcol() ; }
BOOL DavPropfind(CWebDav *pThis) { return pThis->DavPropfind() ; }
BOOL DavProppatch(CWebDav *pThis) { return pThis->DavProppatch() ; }
BOOL DavLock(CWebDav *pThis) { return pThis->DavLock() ; }
BOOL DavUnlock(CWebDav *pThis) { return pThis->DavUnlock() ; }
#if 0
BOOL DavSearch(CWebDav *pThis) { return pThis->DavSearch() ; }
BOOL DavSubscribe(CWebDav *pThis) { return pThis->DavUnsubscribe() ; }
BOOL DavUnsubscribe(CWebDav *pThis) { return pThis->DavUnsubscribe() ; }
BOOL DavPoll(CWebDav *pThis) { return pThis->DavPoll() ; }
BOOL DavBDelete(CWebDav *pThis) { return pThis->DavBDelete() ; }
BOOL DavBCopy(CWebDav *pThis) { return pThis->DavBCopy() ; }
BOOL DavBMove(CWebDav *pThis) { return pThis->DavBMove() ; }
BOOL DavBProppatch(CWebDav *pThis) { return pThis->DavBProppatch() ; }
BOOL DavBPropfind(CWebDav *pThis) { return pThis->DavBPropfind() ; }
BOOL DavX_MS_Enumatts(CWebDav *pThis) { return pThis->DavX_MS_Enumatts() ; }
#endif // 0
// NOTE: this follows same order as "enum VERB" in request.h
PFN_DAVEXECUTE davTable[] = {
DavUnsupported,
DavUnsupported,
DavUnsupported,
DavUnsupported,
DavOptions,
DavPut,
DavMove,
DavCopy,
DavDelete,
DavMkcol,
DavPropfind,
DavProppatch,
// DavSearch, // SharePoint + IIS Specific
DavLock,
DavUnlock,
#if 0 // unsupported DAV vebs
DavSubscribe,
DavUnsubscribe,
DavPoll,
DavBDelete,
DavBCopy,
DavBMove,
DavBProppatch,
DavBPropfind,
DavX_MS_Enumatts,
#endif // 0
};
// Webdav entry point web server calls into.
// Note that WebDav handles the vast majority of errors it hits with descriptive
// XML tags and a "207 Multistatus" response. HandleWebDav() only returns
// FALSE when it needs the web server to send a 4XX or 5XX error code to the client.
BOOL CHttpRequest::HandleWebDav(void) {
CWebDav dav(this);
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDAV called to handle HTTP verb <<%a>>\r\n",m_pszMethod));
DEBUGCHK(m_idMethod <= VERB_LAST_VALID && m_idMethod != VERB_UNKNOWN);
DEBUGCHK(m_idMethod <= ARRAYSIZEOF(davTable));
DEBUGCHK(m_bufRespBody.m_iNextIn == 0);
DEBUGCHK(m_rs == STATUS_OK);
if (dav.IsCollection() || dav.IsNull())
RemoveTrailingSlashToPhysicalPath();
m_fResponseHTTP11 = MAKELONG(1,1) <= m_dwVersion;
if (m_fDisabledNagle == FALSE) {
// To increase performance of WebDAV, disable Nagling.
BOOL fDisable = TRUE;
int iErr;
if (ERROR_SUCCESS != (iErr = setsockopt(m_socket,IPPROTO_TCP,TCP_NODELAY,(const char*)&fDisable,sizeof(BOOL)))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: setsockopt(0x%08x,TCP_NODELAY) failed, err = 0x%08x\r\n",m_socket,iErr));
DEBUGCHK(0);
}
m_fDisabledNagle = TRUE;
}
BOOL fRet = (davTable[m_idMethod])(&dav);
if (dav.m_fXMLBody)
dav.FinalizeMultiStatusResponse();
dav.FinalConsistencyCheck(fRet);
DEBUGCHK(! g_pFileLockManager->IsLocked());
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDav processing completed. Returning %d to HTTPD, status code = %d\r\n",fRet,rgStatus[m_rs].dwStatusNumber));
return fRet;
}
//
// Handle HTTP verbs
//
//
// DavOptions
// OPTIONS requests the capabilities of the server or the available operations on a given resource.
//
const CHAR cszDavCompliance[] = "DAV:";
const CHAR cszCompliance[] = "1, 2";
const CHAR cszCache_Control[] = "Cache-Control:";
const CHAR cszCache_Control_Private[] = "private";
// const CHAR cszCache_Control_NoCache[] = "no-cache";
BOOL CWebDav::DavOptions(void) {
BOOL fRet = FALSE;
BOOL fWriteAccess = (m_pRequest->GetPerms() & HSE_URL_FLAGS_WRITE);
// alloc big enough buffer now so we don't have to check every AddHeader() call.
if (!m_pRequest->m_bufRespHeaders.AllocMem(HEADERBUFSIZE)) {
SetStatus(STATUS_INTERNALERR);
goto done;
}
// "*" requests options for the entire server
if (0 == strcmp(m_pRequest->m_pszURL,"*") || 0 == strcmp(m_pRequest->m_pszURL,"/*")) {
m_pRequest->m_bufRespHeaders.AppendData(cszAllow,ccAllow);
WritePublicOptions();
m_pRequest->m_bufRespHeaders.AddHeader(cszAccept_Ranges,cszBytes);
fRet = TRUE;
goto done; // make sure common case headers are handled
}
if (m_pRequest->GetPerms() & HSE_URL_FLAGS_READ) {
if (! CheckIfHeaders()) {
SetStatus(STATUS_BADREQ);
fRet = FALSE;
goto done;
}
}
// It doesn't make sense to check against LOCKs in this case, as OPTIONS doesn't modify resource in any way.
// Print out "Allow: " header
m_pRequest->m_bufRespHeaders.AppendData(cszAllow,ccAllow);
m_pRequest->m_bufRespHeaders.AppendCHAR(' ');
WriteSupportedVerb(VERB_OPTIONS,TRUE);
WriteSupportedVerb(VERB_GET);
WriteSupportedVerb(VERB_HEAD);
if (fWriteAccess) {
WriteSupportedVerb(VERB_DELETE);
if (!IsCollection())
WriteSupportedVerb(VERB_PUT);
}
if (m_pRequest->IsScript(TRUE))
WriteSupportedVerb(VERB_POST);
if (! IsCollection())
WriteSupportedVerb(VERB_MKCOL);
if (! IsNull()) {
WriteSupportedVerb(VERB_COPY);
if (fWriteAccess) {
WriteSupportedVerb(VERB_MOVE);
WriteSupportedVerb(VERB_PROPPATCH);
// m_pRequest->m_bufRespHeaders.AppendData(cszDavSearch,SVSUTIL_CONSTSTRLEN(cszDavSearch));
}
}
WriteSupportedVerb(VERB_LOCK);
WriteSupportedVerb(VERB_UNLOCK);
m_pRequest->m_bufRespHeaders.AppendData(cszCRLF,ccCRLF);
m_pRequest->m_bufRespHeaders.AddHeader(cszAccept_Ranges,(m_rt == RT_COLLECTION) ? cszNone : cszBytes);
fRet = TRUE;
done:
if (fRet) {
// common headers.
// m_pRequest->m_bufRespHeaders.AddHeader(cszDasl,cszSqlQuery);
m_pRequest->m_bufRespHeaders.AppendData(cszPublic, ccPublic);
WritePublicOptions();
m_pRequest->m_bufRespHeaders.AddHeader(cszDavCompliance, cszCompliance);
m_pRequest->m_bufRespHeaders.AddHeader(cszCache_Control, cszCache_Control_Private);
CHttpResponse resp(m_pRequest);
resp.SetZeroLenBody();
resp.SendResponse();
}
DEBUGCHK(m_pRequest->m_bufRespHeaders.m_iNextIn < HEADERBUFSIZE); // make sure we didn't use more than we claimed we would...
return fRet;
}
BOOL CWebDav::DavPropfind(void) {
BOOL fRet = FALSE;
if (! (m_pRequest->GetPerms() & HSE_URL_FLAGS_READ)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: PROPFIND returns access denied because HSE_URL_FLAGS_READ is not set\r\n"));
SetStatus(STATUS_FORBIDDEN);
goto done;
}
#if 0
if (! m_pRequest->m_dwContentLength) {
// BUGBUG : No chunked encoding support currently, so this won't help. Look at other clients
// and how frequently POST is used to see if we need it. If add this, review every place!!
if (! m_pRequest->FindHttpHeader(cszTransfer_Encoding,ccTransfer_Encoding)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: PROPFIND fails because no content-length or transfer-encoding was set\r\n"));
SetStatus(STATUS_REQUEST_TOO_LARGE);
goto done;
}
}
#endif
if (m_rt == RT_NULL) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: PROPFIND fails because file %s does not exist\r\n",m_pRequest->m_wszPath));
SetStatus(STATUS_NOTFOUND);
goto done;
}
if (m_rt == RT_COLLECTION) {
// depth only matters for directories
if (! GetDepth()) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: PROPFIND fails because depth not set for directory\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
}
if (! CheckIfHeaders()) {
SetStatus(STATUS_PRECONDITION_FAILED);
goto done;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -