📄 davutil.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: davutil.cpp
Abstract: WebDAV utility functions
--*/
#include "httpd.h"
// Returns pointer to value part of the HTTP header, skipping past white space between ":" and value.
// NOTE: this string is not NULL terminated, but terminated by \r\n.
// Skip past "http(s)://CEHostName/".
PSTR SkipHTTPPrefixAndHostName(PSTR szURL) {
PSTR szSlash;
PSTR szFinalURL;
if (*szURL == '/')
return szURL; // URL is relative, no http://.../ stuff to begin with
if (NULL == (szSlash = strchr(szURL,'/')))
return NULL;
// skip opening 2 /'s.
szSlash += (szSlash[1] == '/') ? 2 : 1;
// skip the host name
szFinalURL = strchr(szSlash,'/');
return szFinalURL ? szFinalURL : szSlash;
}
// Retrieves "Depth:" HTTP header and maps to approrpiate enumeration
BOOL CWebDav::GetDepth(DEPTH_TYPE depthDefault) {
PSTR szHeader;
// If we have depth already we're done.
if (m_Depth != DEPTH_UNKNOWN)
return TRUE;
if (NULL == (szHeader = m_pRequest->FindHttpHeader(cszDepth,ccDepth))) {
m_Depth = depthDefault;
return TRUE;
}
if (0 == _strnicmp(szHeader,csz0,cc0))
m_Depth = DEPTH_ZERO;
else if (0 == _strnicmp(szHeader,csz1,cc1))
m_Depth = DEPTH_ONE;
else if (0 == _strnicmp(szHeader,cszInfinity,ccInfinity))
m_Depth = DEPTH_INFINITY;
else if (0 == _strnicmp(szHeader,csz1NoRoot,cc1NoRoot))
m_Depth = DEPTH_ONE_NOROOT;
else
return FALSE;
return TRUE;
}
void CWebDav::GetOverwrite(void) {
PSTR szHeader = m_pRequest->FindHttpHeader(cszOverwrite,ccOverwrite);
if ((NULL == szHeader) || (*szHeader == 't') || (*szHeader == 'T'))
m_Overwrite = OVERWRITE_YES;
else
m_Overwrite = OVERWRITE_NO;
szHeader = m_pRequest->FindHttpHeader(cszAllowRename,ccAllowRename);
if (szHeader && (*szHeader == 't' || *szHeader == 'T'))
m_Overwrite |= OVERWRITE_RENAME;
}
// szSrc and szDest are physical paths of source and destination of a move/copy.
// They cannot be a subset of one another, i.e. 'move \a \a\b' is disallowed.
BOOL IsPathSubDir(WCHAR *szSrc, DWORD ccSrc, WCHAR *szDest, DWORD ccDest) {
if ((ccSrc == 0) || (ccDest == 0))
return TRUE;
if (ccSrc == ccDest)
return (0 == PathNameCompare(szSrc,szDest));
WCHAR *szShorter;
WCHAR *szLonger;
DWORD ccShorter;
DWORD ccLonger;
if (ccSrc > ccDest) {
szShorter = szDest;
szLonger = szSrc;
ccShorter = ccDest;
ccLonger = ccSrc;
}
else {
szShorter = szSrc;
szLonger = szDest;
ccShorter = ccSrc;
ccLonger = ccDest;
}
// look for shorter path. We check back-slashes to make sure things are really
// equal, i.e. without backslash check '\windows\foo' and '\windows\foobar'
// would match, which isn't what we're looking for.
if (0 == PathNameCompareN(szSrc,szDest,ccShorter)) {
if ( (L'\\' == szShorter[ccShorter-1]) ||
(L'/' == szShorter[ccShorter-1]) ||
(L'\\' == szLonger [ccShorter]) ||
(L'/' == szLonger [ccShorter])) {
return TRUE;
}
}
return FALSE;
}
inline void SetFiletime(FILETIME *pftDest, FILETIME *pftSrc) {
memcpy(pftDest,pftSrc,sizeof(FILETIME));
}
BOOL CWebDav::DavGetFileAttributesEx() {
if (! m_fRetrievedFileAttribs) {
if (!GetFileAttributesEx(m_pRequest->m_wszPath,GetFileExInfoStandard,&m_fileAttribs)) {
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: GetFileAttributesEx(%s) fails, GLE=0x%08x\r\n",m_pRequest->m_wszPath,GetLastError()));
return FALSE;
}
m_fRetrievedFileAttribs = TRUE;
return TRUE;
}
return TRUE;
}
// Make a copy of this data here rather than (possibly) having to alloc 1 KB of RAM
// in m_bufRespHeaders for every response.
const CHAR cszDefaultXMLHttpHeader[] = "Content-Type: text/xml\r\nTransfer-Encoding: chunked\r\n\r\n";
#define ADD_CRLF(sz,ccWrite) (sz)[(ccWrite++)] = '\r'; (sz)[(ccWrite++)] = '\n';
DWORD AddHttpHeader(CHAR *szBuf, PCSTR szHeaderName, DWORD ccHeaderName, PCSTR szHeaderValue, DWORD ccHeaderValue) {
DWORD ccWritten = 0;
strcpy(szBuf,szHeaderName);
ccWritten = ccHeaderName;
szBuf[ccWritten++] = ' ';
strcpy(szBuf+ccWritten,szHeaderValue);
ccWritten += ccHeaderValue;
ADD_CRLF(szBuf,ccWritten);
return ccWritten;
}
// For success cases (i.e. 2XX) where we also need to send XML in body.
BOOL CWebDav::SetStatusAndXMLBody(RESPONSESTATUS rs) {
if (!m_fSetStatus) {
CHAR szHeaders[MINBUFSIZE];
DWORD ccWritten;
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav setting response %d headers\r\n",rgStatus[rs].dwStatusNumber));
ccWritten = AddHttpHeader(szHeaders,cszContent_Type,ccContent_Type,
cszTextXML,ccTextXML);
if (m_pRequest->m_fResponseHTTP11) {
ccWritten += AddHttpHeader(szHeaders+ccWritten,cszTransfer_Encoding,ccTransfer_Encoding,
cszChunked,ccChunked);
ADD_CRLF(szHeaders,ccWritten); // closing CRLF at end of HTTP headers
szHeaders[ccWritten++] = 0;
CHttpResponse resp(m_pRequest, rs);
resp.SendHeadersAndDefaultBodyIfAvailable(szHeaders,NULL);
}
else {
m_pRequest->m_rs = rs;
m_pRequest->m_bufRespHeaders.AppendData(szHeaders,ccWritten);
}
m_fSetStatus = TRUE;
if (! m_bufResp.Append(cszXMLVersionA, ccXMLVersionA))
return FALSE;
if ((m_pRequest->m_rs == STATUS_MULTISTATUS) && !m_bufResp.StartTagNoEncode(cszMultiStatusNS,ccMultiStatusNS))
return FALSE;
DEBUGCHK(ccWritten < sizeof(szHeaders));
m_fXMLBody = TRUE;
}
return TRUE;
}
BOOL CWebDav::FinalizeMultiStatusResponse(void) {
DEBUGCHK(m_fSetStatus && m_fXMLBody);
m_bufResp.BufferConsistencyChecks();
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav flushing buffer for the final time\r\n"));
if ((m_pRequest->m_rs == STATUS_MULTISTATUS) && !m_bufResp.EndTag(cszMultiStatus))
return FALSE;
return m_bufResp.FlushBuffer(TRUE);
}
// Creates string in format "http(s)://CeServerName/[szRoot]". Calling function
// will optionally add remainder of URL
BOOL CWebDav::BuildBaseURL(PCSTR szRoot, BOOL fHttpHeader, BOOL fAppendSlash) {
BOOL fRet = FALSE;
PCSTR szHostName;
#if defined (DEBUG)
if (fAppendSlash)
DEBUGCHK(!URLHasTrailingSlashA(szRoot));
#endif
if (m_pRequest->m_pszHost) {
szHostName = m_pRequest->m_pszHost;
m_ccHostName = strlen(m_pRequest->m_pszHost);
}
else {
if (m_ccHostName == 0) {
if (0 != gethostname(m_szHostName, sizeof(m_szHostName)))
goto done;
m_ccHostName = strlen(m_szHostName);
}
szHostName = m_szHostName;
}
if (fHttpHeader) {
if (! m_pRequest->m_bufRespHeaders.AppendData(GetHttpPrefix(),GetHttpPrefixLen()) ||
! m_pRequest->m_bufRespHeaders.AppendData(szHostName,m_ccHostName) ||
! m_pRequest->m_bufRespHeaders.EncodeURL(szRoot) ||
(fAppendSlash && !m_pRequest->m_bufRespHeaders.AppendCHAR('/')) ||
! m_pRequest->m_bufRespHeaders.AppendData(cszCRLF,2)) {
goto done;
}
}
else {
if (! m_bufResp.Append(GetHttpPrefix(),GetHttpPrefixLen()) ||
! m_bufResp.Encode(szHostName) ||
! m_bufResp.EncodeURL(szRoot) ||
(fAppendSlash && !m_bufResp.AppendCHAR('/'))) {
goto done;
}
}
fRet = TRUE;
done:
return fRet;
}
// Creates string in format "http(s)://CeServerName/[Root]/", where Root
// is the originally requested URL. The URL is cleaned up for sending if needed.
BOOL CWebDav::BuildBaseURLFromSourceURL(BOOL fHttpHeader) {
// For directories, tack trailing '/' to end of string if not there. For files,
// remove it.
BOOL fAddSlash = IsCollection() && ! m_pRequest->URLHasTrailingSlash();
return BuildBaseURL(m_pRequest->m_pszURL,fHttpHeader,fAddSlash);
}
// Adds "Content-Location:" HTTP response headers.
BOOL CWebDav::AddContentLocationHeader(void) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: Adding \"Content-Location:\" headers\r\n"));
if (! m_pRequest->m_bufRespHeaders.AppendData(cszContent_Location,ccContent_Location) ||
! m_pRequest->m_bufRespHeaders.AppendData(" ",1) ||
! BuildBaseURLFromSourceURL(TRUE))
{
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Out of memory, unable to add http headers\r\n"));
m_pRequest->m_bufRespHeaders.Reset();
SetStatus(STATUS_INTERNALERR);
return FALSE;
}
return TRUE;
}
// Adds "Content-Location:" to response headers when no trailing "/" on directories, effectively a redirect
BOOL CWebDav::AddContentLocationHeaderIfNeeded(void) {
int iLen = strlen(m_pRequest->m_pszURL);
DEBUGCHK(iLen >= 1);
// If directory and no trailing "/", or a file and a trailing "/",
if (m_pRequest->URLHasTrailingSlash() == IsCollection())
return TRUE; // no redirection needed.
return AddContentLocationHeader();
}
// Writes contents of HTTP body into hFile
BOOL CWebDav::WriteBodyToFile(HANDLE hFile, BOOL fChunked) {
CHAR szBuf[HEADERBUFSIZE];
DWORD cbBuf;
DWORD dwWritten;
DEBUGCHK(hFile != INVALID_HANDLE_VALUE);
DEBUGCHK(!fChunked); // Chunked NYI...
m_rcvBody.fChunked = fChunked;
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDav WriteBodyToFile called, hFile=0x%08x, fChunked=%d\r\n",hFile,fChunked));
// write any data we already have into the file immediatly.
if (!fChunked) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav WriteBodyToFile writes initial %d bytes to file\r\n",m_pRequest->m_bufRequest.Count()));
if (! WriteFile(hFile,m_pRequest->m_bufRequest.Data(),m_pRequest->m_bufRequest.Count(),&dwWritten,NULL)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WriteFile fails, GLE=0x%08x\r\n",GetLastError()));
return FALSE;
}
// Request read in everything
if (m_pRequest->m_dwContentLength == m_pRequest->m_bufRequest.Count())
return TRUE;
}
// There's additional data that needs to be recv()'d
while (1) {
cbBuf = sizeof(szBuf);
if (! ReadNextBodySegment(szBuf,&cbBuf))
return FALSE;
if (cbBuf == 0)
return TRUE;
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav WriteBodyToFile writes extra %d bytes to file\r\n",cbBuf));
if (! WriteFile(hFile,szBuf,cbBuf,&dwWritten,NULL)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: WriteFile fails, GLE=0x%08x\r\n",GetLastError()));
return FALSE;
}
}
DEBUGCHK(0);
return FALSE;
}
// Reads body segment, keeping track of state as it goes along.
// Returns FALSE on errors or timeouts. When done reading request, sets *pcbRead=0.
BOOL CWebDav::ReadNextBodySegment(PSTR szBuf, DWORD *pcbRead) {
BOOL fRet;
if (m_rcvBody.dwRemaining == RECEIVE_SIZE_UNINITIALIZED) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -