📄 davlock.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: davlock.cpp
Abstract: WebDAV implementation of locking functions
--*/
#include "httpd.h"
#if defined (DEBUG)
inline void DebugCheckCacheManagerLocked(void) { DEBUGCHK(g_pFileLockManager->IsLocked()); }
inline void DebugCheckCacheManagerNotLocked(void) { DEBUGCHK(! g_pFileLockManager->IsLocked()); }
#else
inline void DebugCheckCacheManagerLocked(void) { ; }
inline void DebugCheckCacheManagerNotLocked(void) { ; }
#endif
// BUGBUG - Our filesys doesn't do the right thing in protecting MOVE files.
// This applies to both static and to ISAPIs loaded with LoadLibrary().
// *********************************************************
// Main VERB handeling routines.
// *********************************************************
BOOL CWebDav::DavLock(void) {
CWebDavFileNode *pFileNode = NULL;
BOOL fRet = FALSE;
PSTR szLockHeader;
if (! (m_pRequest->GetPerms() & HSE_URL_FLAGS_WRITE)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK fails, write permissions not turned on\r\n"));
SetStatus(STATUS_FORBIDDEN);
goto done;
}
if (IsCollection()) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK fails, WinCE DAV does not support locking collections\r\n"));
SetStatus(STATUS_FORBIDDEN);
goto done;
}
if (! CheckIfHeaders())
goto done;
if (! CheckLockHeader())
goto done;
if (NULL != (szLockHeader = m_pRequest->FindHttpHeader(cszIfToken,ccIfToken))) {
// If LOCK sets a "Lock-Token", the only supported scenario is refreshing
// the timeout on a lock.
DWORD dwTimeout = 0;
if (m_pRequest->m_dwContentLength) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDav LOCK sets Lock-Token but has a body, illegal\r\n"));
SetStatus(STATUS_UNSUPPORTED_MEDIA_TYPE);
goto done;
}
if (! GetLockTimeout(&dwTimeout) || (dwTimeout == 0)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDAV lock unable to set updated lock, failing request\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
if (m_nLockTokens != 1) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK refresh timeout sent %d lock tokens, need exactly 1\r\n",m_nLockTokens));
SetStatus(STATUS_BADREQ);
goto done;
}
g_pFileLockManager->Lock();
if (NULL != (pFileNode = g_pFileLockManager->GetNodeFromFileName(m_pRequest->m_wszPath))) {
CWebDavLock *pLock;
if (pFileNode->HasLockId(m_lockIds[0],&pLock)) {
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: LOCK updating timeout on LockID=%I64d, file=%s, timeout seconds=%d\r\n",m_lockIds[0],m_pRequest->m_wszPath,dwTimeout));
pLock->SetTimeout(dwTimeout);
SetStatusAndXMLBody(STATUS_OK);
SendLockTags(pFileNode);
fRet = TRUE;
}
else {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK update timer failed, file %s not associated with lockID %I64d\r\n",m_pRequest->m_wszPath,m_lockIds[0]));
SetStatus(STATUS_PRECONDITION_FAILED);
}
}
else {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK update timer failed, file %s does not have any locks associated with it\r\n",m_pRequest->m_wszPath));
SetStatus(STATUS_PRECONDITION_FAILED);
}
g_pFileLockManager->Unlock();
}
else {
// Request for a new lock.
CLockParse lockParse(this);
if (! GetDepth(DEPTH_ZERO) || ((m_Depth != DEPTH_INFINITY) && (m_Depth != DEPTH_ZERO))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDAV LOCK fails, depth not set to infinity or zero\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
#if defined (DEBUG)
// Like IIS's behavior in accepting depth infinity.
if (m_Depth == DEPTH_INFINITY)
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: Warning, LOCK accepting depth infinite tag, however it will be treated like depth: 0 because WinCE doesn't support locking collections.\r\n"));
#endif
if (! ParseXMLBody(&lockParse,FALSE))
goto done;
fRet = g_pFileLockManager->CreateLock(&lockParse,this);
}
done:
return fRet;
}
BOOL CWebDav::DavUnlock(void) {
BOOL fRet = FALSE;
__int64 iLockId;
if (! (m_pRequest->GetPerms() & HSE_URL_FLAGS_WRITE)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, write permissions not turned on\r\n"));
SetStatus(STATUS_FORBIDDEN);
goto done;
}
if (IsCollection()) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, WinCE DAV does not support locking collections\r\n"));
SetStatus(STATUS_FORBIDDEN);
goto done;
}
if (IsNull()) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, file %s does not exist\r\n",m_pRequest->m_wszPath));
SetStatus(STATUS_NOTFOUND);
goto done;
}
if (0 == (iLockId = CheckLockTokenHeader())) {
DEBUGCHK(m_pRequest->m_rs != STATUS_OK); //CheckLockTokenHeader sets errors
goto done;
}
if (! g_pFileLockManager->IsFileAssociatedWithLockId(m_pRequest->m_wszPath,iLockId)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: File <%s> is not associated with LockID %I64d\r\n",m_pRequest->m_wszPath,iLockId));
SetStatus(STATUS_PRECONDITION_FAILED);
goto done;
}
if (! g_pFileLockManager->DeleteLock(iLockId)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD UNLOCK fails, lockID %I64d does not exist\r\n",iLockId));
SetStatus(STATUS_PRECONDITION_FAILED);
goto done;
}
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: UNLOCK successfully deleted LockID %I64d\r\n",iLockId));
fRet = TRUE;
done:
if (fRet) {
DEBUGCHK(m_pRequest->m_rs == STATUS_OK);
CHttpResponse resp(m_pRequest,STATUS_NOCONTENT);
resp.SendResponse();
}
return fRet;
}
// *********************************************************
// Helper routines for LOCK, UNLOCK, and other verbs
// like MOVE and DELETE that query lock status.
// *********************************************************
BOOL CWebDav::GetLockTimeout(DWORD *pdwTimeout) {
DEBUGCHK((*pdwTimeout) == 0);
DEBUGCHK(m_pRequest->m_idMethod == VERB_LOCK);
CHAR *szTimeout = m_pRequest->FindHttpHeader(cszTimeOut,ccTimeOut);
CHAR *szTrav;
if (! szTimeout) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDAV did not send \"timeout:\" http header\r\n"));
return TRUE; // lack of header is not an error.
}
CHeaderIter header(szTimeout);
for (szTrav = header.GetNext(); szTrav; szTrav = header.GetNext()) {
if (0 == _strnicmp(szTrav,cszSecond,ccSecond)) {
szTrav += ccSecond;
if (0 == (*pdwTimeout = atoi(szTrav))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LOCK \"timeout:\" header not propertly formatted\r\n"));
SetStatus(STATUS_BADREQ);
return FALSE;
}
break;
}
else if (0 == _strnicmp(szTrav,cszInfinite,ccInfinite)) {
*pdwTimeout = INFINITE;
break;
}
#if defined (DEBUG)
else {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: Received unknown field in \"timeout:\" header. Ignoring, no error\r\n"));
}
#endif // DEBUG
}
if (*pdwTimeout > MAX_DAV_LOCK_TIMEOUT) {
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDAV LOCK requested timeout of %ud seconds, setting to maximum allowed %ud seconds\r\n",*pdwTimeout,MAX_DAV_LOCK_TIMEOUT));
*pdwTimeout = MAX_DAV_LOCK_TIMEOUT;
}
return TRUE;
}
PSTR CHeaderIter::GetNext(void) {
ResetSaved();
PSTR szTrav = m_szNextString;
SkipWhiteSpaceNonCRLF(szTrav);
if (*szTrav == 0 || *szTrav == '\r')
return NULL;
m_szStartString = szTrav;
// look for ','
while ((*szTrav != '\r') && (*szTrav != ','))
szTrav++;
m_szNextString = szTrav+1;
szTrav--;
// backup to remove existing spaces
while (IsNonCRLFSpace(*szTrav)) szTrav--;
m_szSave = (szTrav+1);
cSave = *m_szSave;
*m_szSave = 0;
return m_szStartString;
}
// Skip W/ in weak header parsing
inline PSTR SkipWeakTagIfNeeded(PSTR pszToken) {
if (*pszToken == 'W' && *(pszToken+1) == '/')
return pszToken+2;
return pszToken;
}
BOOL ETagEqualsHeader(PSTR szHeader, PSTR szETag) {
DEBUGCHK(szHeader);
PSTR szToken;
CHeaderIter headIter(szHeader);
for (szToken = headIter.GetNext(); szToken; szToken = headIter.GetNext()) {
szToken = SkipWeakTagIfNeeded(szToken);
if (*szToken == '*')
return TRUE;
else if (0 == strcmp(szETag,szToken))
return TRUE;
}
return FALSE;
}
int WriteOpaqueLockToken(PSTR szBuf, __int64 iLockId, BOOL fClosingBrackets) {
PSTR szWrite = szBuf;
if (fClosingBrackets)
*szWrite++ = '<';
strcpy(szWrite,cszOpaquelocktokenPrefix);
szWrite += ccOpaquelocktokenPrefix;
strcpy(szWrite,g_pFileLockManager->m_szLockGUID);
szWrite += SVSUTIL_GUID_STR_LENGTH;
*szWrite++ = ':';
szWrite += sprintf(szWrite,"%I64d",iLockId);
if (fClosingBrackets) {
*szWrite++ = '>';
*szWrite++ = 0;
}
return (szWrite - szBuf);
}
// Checks that szHeader is equal to "opaquelocktoken:CE_SERVER_GUID:" and removes the LockID after
// returns a pointer to next. Returns 0 on failure.
__int64 GetLockIDFromOpaqueHeader(PSTR szToken) {
__int64 iLockId;
if (0 != _strnicmp(szToken,cszOpaquelocktokenPrefix,ccOpaquelocktokenPrefix)) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: DAV Lock header check fails, '<' not followed by %s\r\n",cszOpaquelocktokenPrefix));
return 0;
}
szToken += ccOpaquelocktokenPrefix;
if (0 != _strnicmp(szToken,g_pFileLockManager->m_szLockGUID,SVSUTIL_GUID_STR_LENGTH)) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: DAV Lock header check fails, GUID sent does not match system's, which is <<%s>>\r\n",g_pFileLockManager->m_szLockGUID));
return 0;
}
szToken += SVSUTIL_GUID_STR_LENGTH;
if (*szToken != ':') {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: DAV Lock header check fails, GUID sent does not contain ':' at end\r\n"));
return 0;
}
szToken++; // skip ':'
if (0 == (iLockId = _atoi64(szToken))) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: DAV Lock header check fails, GUID does not contain 64 bit lockID\r\n"));
return 0;
}
return iLockId;
}
#define OPEN_URI '<'
#define CLOSE_URI '>'
#define OPEN_LIST '('
#define CLOSE_LIST ')'
#define OPEN_LOCK_TOKEN '<'
#define CLOSE_LOCK_TOKEN '>'
#define OPEN_ETAG '['
#define CLOSE_ETAG ']'
// Per RFC 2518, section 9.5:
// Lock-Token = "Lock-Token" ":" Coded-URL
// Coded-URL = "<" URI ">"
__int64 CWebDav::CheckLockTokenHeader(void) {
PSTR szLockToken = NULL;
PSTR szTrav;
PSTR szEndOfURI;
PSTR szEndOfHeader;
__int64 iLockId = 0;
DEBUGCHK(m_pRequest->m_idMethod == VERB_UNLOCK);
if (NULL == (szLockToken = m_pRequest->FindHttpHeader(cszLockToken,ccLockToken))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, \"Lock-token\" not sent and is required\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
szTrav = szLockToken;
svsutil_SkipWhiteSpace(szTrav);
if (*szTrav != OPEN_URI) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, \"Lock-token\" header does not begin with '<'\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
szTrav++; // skip '<'
szEndOfHeader = strchr(szLockToken,'\r');
szEndOfURI = strchr(szTrav,CLOSE_URI);
if (!szEndOfURI || (szEndOfURI > szEndOfHeader)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails,\"Lock-token\" did not terminate with '>'\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
iLockId = GetLockIDFromOpaqueHeader(szTrav);
if (iLockId == 0) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: UNLOCK fails, \"Lock-token\" fields formatted incorrectly\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
done:
return iLockId;
}
// Constants and logic in CheckLockHeader per RFC 2518, section 9.4 parsing of "If:" header rules
// Format of the If header
// If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
// No-tag-list = List
// Tagged-list = Resource 1*List
// Resource = Coded-url
// List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
// State-token = Coded-url
// Coded-url = "<" URI ">"
// Parses "If:" HTTP header to see what locks we have.
BOOL CWebDav::CheckLockHeader(void) {
PSTR szLockHeaderInBuf; // points inside main request class, do not free
PSTR szLockHeader = NULL;
PSTR szEndOfHeader = NULL;
PSTR szEndOfStatement = NULL;
BOOL fRet = FALSE;
// BOOL fFirstPass = TRUE;
WCHAR *wszPhysicalPath = NULL;
PSTR szTrav;
SVSSimpleBuffer urlBuf(512);
if (NULL == (szLockHeaderInBuf = m_pRequest->FindHttpHeader(cszIfToken,ccIfToken)))
return TRUE;
szEndOfHeader = strchr(szLockHeaderInBuf,'\r');
DEBUGCHK(szEndOfHeader); // otherwise invalid HTTP headers
*szEndOfHeader = 0;
szLockHeader = MySzDupA(szLockHeaderInBuf);
*szEndOfHeader = '\r';
if (!szLockHeader) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDAV out of memory\r\n"));
SetStatus(STATUS_INTERNALERR);
goto done;
}
szTrav = szLockHeader;
while (*szTrav) {
urlBuf.Reset();
svsutil_SkipWhiteSpace(szTrav);
if (*szTrav != OPEN_URI) {
#if 0 // New policy is to be more lenient, accept this case at any time. See RFC 2518, section 8.9.6 for example of why we accept this.
// On the first pass through loop, we'll treat a no-tag production
// as if it refers to the request URI
if (!fFirstPass) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDAV does not have '<' for tagged-list, skipping to end\r\n"));
fRet = TRUE;
goto done;
}
#endif
MyFreeNZ(wszPhysicalPath);
wszPhysicalPath = NULL;
}
else {
PSTR szEndOfUrl = NULL;
szTrav++; // skip '<'
szTrav = SkipHTTPPrefixAndHostName(szTrav);
if (szTrav)
szEndOfUrl = strchr(szTrav,CLOSE_URI);
if (NULL == szEndOfUrl) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDAV skipping host name and headers of tagged-URL failed in \"if:\" header\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
// need to copy results to a new buffer, as canonicilization changes data in place.
if (! urlBuf.AllocMem(szEndOfUrl-szTrav+1)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WebDAV out of memory\r\n"));
SetStatus(STATUS_INTERNALERR);
goto done;
}
*szEndOfUrl = 0;
svsutil_HttpCanonicalizeUrlA(szTrav,(CHAR*)urlBuf.pBuffer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -