📄 davimpl.cpp
字号:
while (*szTrav) {
DWORD ccTrav = strlen(szTrav);
if (! m_bufResp.BeginBraceNoNS() ||
! m_bufResp.Append(szTrav,ccTrav) ||
! m_bufResp.Append("/>",2)) {
return FALSE;
}
szTrav += ccTrav + 1;
}
if (! m_bufResp.EndTag(cszProp) ||
! m_bufResp.EndTag(cszPropstat))
return FALSE;
}
return TRUE;
}
// When an unknown XML tag is hit during processing a PROPPATCH or PROPFIND request,
// save that tag (converting to ASCII first) in a buffer so that
BOOL CWebDav::AddUnknownXMLElement(const WCHAR *wszTagName, const DWORD ccTagName) {
SVSUTIL_ASSERT((m_pRequest->m_idMethod == VERB_PROPFIND) || (m_pRequest->m_idMethod == VERB_PROPPATCH));
UINT uiCodePage = GetUTF8OrACP();
int iLen = WideCharToMultiByte(uiCodePage, 0, wszTagName, ccTagName, NULL, 0, 0, 0);
if (iLen == 0) {
DEBUGCHK(0);
return TRUE;
}
if (! m_bufUnknownTags.AllocMem(iLen+1))
return FALSE;
WideCharToMultiByte(uiCodePage, 0,wszTagName, ccTagName, (CHAR*)m_bufUnknownTags.pBuffer+m_bufUnknownTags.uiNextIn, iLen, 0, 0);
m_bufUnknownTags.uiNextIn += iLen;
m_bufUnknownTags.AppendCHAR('\0'); // always succeeds because we alloc'd an extra byte for it above.
return TRUE;
}
// Only returns property names associated with resource, not their values.
BOOL CWebDav::SendPropertyNamesVisitFcn(WIN32_FIND_DATA *pFindData, DWORD dwContext, WCHAR *szTraversalPath, BOOL fRootDir) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav PROPFIND returning all property names\r\n"));
DEBUGCHK(m_pRequest->m_rs == STATUS_MULTISTATUS);
BOOL fIsRoot = (NULL==szTraversalPath);
BOOL fRet = FALSE;
if (! m_bufResp.StartTag(cszResponse))
goto done;
// <a:href>http://CEDevice/fileName/...</a:href>
if (! SetHREFTagFromDestinationPath(pFindData,szTraversalPath,fRootDir,NULL,fIsRoot))
goto done;
// <a:propstat>
if (! m_bufResp.StartTag(cszPropstat))
goto done;
// <a:status>
if (! m_bufResp.SetStatusTag(STATUS_OK))
goto done;
// <a:prop>
if (! m_bufResp.StartTag(cszProp))
goto done;
if (! m_bufResp.SetEmptyElementNoEncode(cszGetcontentLengthNS,ccGetcontentLengthNS) ||
! m_bufResp.SetEmptyElementNoEncode(cszCreationDateNS,ccCreationDateNS) ||
! m_bufResp.SetEmptyElementNoEncode(cszDisplayName,ccDisplayName) ||
! m_bufResp.SetEmptyElementNoEncode(cszGetETag,ccGetETag) ||
! m_bufResp.SetEmptyElementNoEncode(cszLastModifiedNS,ccLastModifiedNS) ||
! m_bufResp.SetEmptyElementNoEncode(cszSupportedLock,ccSupportedLock) ||
! m_bufResp.SetEmptyElementNoEncode(cszLockDiscovery,ccLockDiscovery) ||
! m_bufResp.SetEmptyElementNoEncode(cszIsHiddenNS,ccIsHiddenNS) ||
! m_bufResp.SetEmptyElementNoEncode(cszIsCollectionNS,ccIsCollectionNS))
goto done;
// close up XML tags
if (! m_bufResp.EndTag(cszProp) ||
! m_bufResp.EndTag(cszPropstat) ||
! m_bufResp.EndTag(cszResponse))
goto done;
fRet = TRUE;
done:
return fRet;
}
// Send all properties
BOOL CWebDav::SendProperties(DWORD dwDavPropFlags, BOOL fSendNamesOnly) {
PFN_RECURSIVE_VISIT pfnVisit = fSendNamesOnly ? ::SendPropertyNamesVisitFcn : (::SendPropertiesVisitFcn);
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDav PROPFIND sending properties for %s\r\n",m_pRequest->m_wszPath));
DEBUGCHK(m_pRequest->m_rs == STATUS_OK);
if (! DavGetFileAttributesEx()) {
DEBUGCHK(0); // earlier GetFileAttributes() worked, this should too.
SetStatus(STATUS_INTERNALERR);
return FALSE;
}
SetMultiStatusResponse();
// Print out the root node first.
if (! pfnVisit(this,(WIN32_FIND_DATA*)&m_fileAttribs,dwDavPropFlags,NULL,FALSE))
goto done;
// If we're not a directory or if depth=0 on directory, we're done.
if (! IsCollection() || (m_Depth == DEPTH_ZERO)) {
return TRUE;
}
if (IsCollection() && (! m_pRequest->AllowDirBrowse())) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: PROPFIND requests collection info but dir brows turned off, only returning info on root dir\r\n"));
return TRUE;
}
RecursiveVisitDirs(m_pRequest->m_wszPath,dwDavPropFlags,pfnVisit,FALSE);
done:
// always return TRUE because we've set "Multistatus" error code.
DEBUGCHK(m_pRequest->m_rs == STATUS_MULTISTATUS);
return TRUE;
}
// Client has given us a restricted list of properties and/or files it wants
BOOL CWebDav::SendSubsetOfProperties(void) {
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDav PROPFIND will send subset of properties based on XML body\r\n"));
DEBUGCHK(m_pRequest->m_rs == STATUS_OK);
BOOL fRet = FALSE;
CPropFindParse cPropFind(this);
return ParseXMLBody(&cPropFind);
}
void BuildFileName(WCHAR *szWriteBuf, WCHAR *szPath, WCHAR *szFileName) {
DWORD ccFile;
wcscpy(szWriteBuf,szPath);
ccFile = wcslen(szWriteBuf);
szWriteBuf[ccFile++] = '\\';
wcscpy(szWriteBuf+ccFile,szFileName);
DEBUGCHK(wcslen(szWriteBuf) < MAX_PATH);
}
//
// PROPPATCH
//
// We'll pretend like we can handle all Win32 properties (we can only do Win32FileAttributes),
// and will return "403" for other properties.
BOOL CWebDav::SendPropPatchResponse(RESPONSESTATUS rs) {
if ((m_dwPropPatchUpdate == 0) && (NULL == m_bufUnknownTags.pBuffer)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: PROPPATCH did not set any elements, bad request\r\n"));
SetStatus(STATUS_BADREQ);
return FALSE;
}
if (! SetMultiStatusResponse() ||
! m_bufResp.StartTag(cszResponse) ||
! SetHREFTag()) {
return FALSE;
}
if (m_dwPropPatchUpdate) {
if ( ! m_bufResp.StartTag(cszPropstat) ||
! m_bufResp.SetStatusTag(rs) ||
! m_bufResp.StartTag(cszProp))
{
return FALSE;
}
if ((m_dwPropPatchUpdate & DAV_PROP_W32_FILE_ATTRIBUTES) && ! SetEmptyTagWin32NS(&m_bufResp,cszWin32FileAttribs,ccWin32FileAttribs))
return FALSE;
if ((m_dwPropPatchUpdate & DAV_PROP_W32_LAST_MODIFY_TIME) && ! SetEmptyTagWin32NS(&m_bufResp,cszWin32LastModifiedTime,ccWin32LastModifiedTime))
return FALSE;
if ((m_dwPropPatchUpdate & DAV_PROP_W32_CREATION_TIME) && ! SetEmptyTagWin32NS(&m_bufResp,cszWin32CreationTime,ccWin32CreationTime))
return FALSE;
if ((m_dwPropPatchUpdate & DAV_PROP_W32_LAST_ACCESS_TIME) && ! SetEmptyTagWin32NS(&m_bufResp,cszWin32LastAccessTime,ccWin32LastAccessTime))
return FALSE;
if (! m_bufResp.EndTag(cszProp) ||
! m_bufResp.EndTag(cszPropstat))
return FALSE;
}
if (! SendUnknownXMLElementsIfNeeded(STATUS_FORBIDDEN))
return FALSE;
if (! m_bufResp.EndTag(cszResponse))
return FALSE;
return TRUE;
}
// Effectively does a memset(0) on pFindData, but don't call memset to avoid
// having to zero out long cFileName struct, can have same effect by setting its first byte = 0.
void ZeroW32FData(WIN32_FIND_DATAW *pFindData) {
pFindData->dwFileAttributes = 0;
pFindData->ftCreationTime.dwLowDateTime = pFindData->ftLastAccessTime.dwLowDateTime = pFindData->ftLastWriteTime.dwLowDateTime = 0;
pFindData->ftCreationTime.dwHighDateTime = pFindData->ftLastAccessTime.dwHighDateTime = pFindData->ftLastWriteTime.dwHighDateTime = 0;
pFindData->nFileSizeHigh = pFindData->nFileSizeLow = pFindData->dwOID = 0;
pFindData->cFileName[0] = 0;
}
//
// DELETE
//
BOOL CWebDav::DeleteVisitFcn(WIN32_FIND_DATAW *pFindData, DWORD dwContext, WCHAR *szTraversalPath, BOOL fRootDir) {
CDavDeleteContext *pContext = (CDavDeleteContext*)dwContext;
DEBUGCHK(! URLHasTrailingSlashW(szTraversalPath));
DEBUGMSG_VISIT_FCN("DeleteVisitFcn");
RESPONSESTATUS rs = STATUS_MAX;
BOOL fRet = FALSE;
if (IsDirectory(pFindData)) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav removing directory %s\r\n",szTraversalPath));
fRet = RemoveDirectory(szTraversalPath);
if (!fRet) {
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: RemoveDirectory(%s) failed, GLE=0x%08x\r\n",szTraversalPath,GetLastError()));
// On non-empty dir, lower level set error
if (SendErrorOnRemoveDirectoryFailure()) {
// We don't use existing pFindData because it has the node directory name encoded in it,
// but szTraversalPath also has this info in it, so we'd end up duplicating final file name.
WIN32_FIND_DATAW w32FData;
ZeroW32FData(&w32FData);
SetMultiStatusErrorFromDestinationPath(GLEtoStatus(),&w32FData,szTraversalPath,fRootDir,pContext->szURL);
return TRUE;
}
else {
fRet = TRUE;
}
}
}
else {
WCHAR szFile[MAX_PATH*2];
if (!(m_pRequest->GetPerms() & HSE_URL_FLAGS_SCRIPT_SOURCE) && IsPathScript(pFindData->cFileName)) {
#if defined (DEBUG)
BuildFileName(szFile,szTraversalPath,pFindData->cFileName);
DEBUGMSG(ZONE_ERROR,(L"HTTPD: Cannot MOVE/COPY %s, it is a script but SCRIPT_SOURCE permissions not granted\r\n",szFile));
#endif
rs = STATUS_FORBIDDEN;
}
else {
BuildFileName(szFile,szTraversalPath,pFindData->cFileName);
fRet = DavDeleteFile(szFile);
if (IsFileLocked(szFile)) {
rs = STATUS_LOCKED;
}
}
}
if (!fRet && pContext->fSendErrorsToClient) {
rs = (rs == STATUS_MAX) ? GLEtoStatus() : rs;
SetMultiStatusErrorFromDestinationPath(rs,pFindData,szTraversalPath,fRootDir,pContext->szURL);
}
return TRUE;
}
BOOL CWebDav::DeleteCollection(WCHAR *wszPath, CHAR *szURL) {
CDavDeleteContext cDel(szURL,TRUE);
BOOL fRet = FALSE;
// per spec, depth must be infinite
if (! GetDepth() || m_Depth != DEPTH_INFINITY) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: DELETE fails, depth not valid or not set to \"infinity\"\r\n"));
SetStatus(STATUS_BADREQ);
return FALSE;
}
if (RecursiveVisitDirs(wszPath,(DWORD)&cDel,::DeleteVisitFcn,TRUE)) {
if (! RemoveDirectory(wszPath)) {
DEBUGMSG(ZONE_WEBDAV,(L"HTTPD: WebDav removing directory %s, GLE=0x%08x\r\n",wszPath,GetLastError()));
if (SendErrorOnRemoveDirectoryFailure())
SetMultiStatusErrorFromURL(GLEtoStatus(),szURL);
}
else
fRet = TRUE;
}
return fRet;
}
//
// MOVE/COPY
//
// Returns a new buffer containing the canonicalized URL. ppszDestinationPrefix is
// pointer to raw headers containing http://... part of URL, ppszDestinationURL
// points to the URL portion.
PSTR CWebDav::GetDestinationHeader(PSTR *ppszDestinationPrefix, PSTR *ppszDestinationURL, PSTR *ppszSave) {
PSTR szDestinationPrefix = NULL;
PSTR szDestinationURL = NULL;
PSTR szDestination = NULL;
// Destination: header contains location of resource to write to. May
// be in form "http://CEDevice/destination", so need to crack this out, canonicalize,
// and then find VRoot on the device it corresponds to (not necessarily the
// vroot of m_pszURL).
if (NULL == (szDestinationPrefix = m_pRequest->FindHttpHeader(cszDestination,ccDestination))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: MOVE/COPY fails, \"Destination:\" request header not present\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
*ppszSave = strchr(szDestinationPrefix,'\r');
DEBUGCHK(*(*ppszSave)); // request wouldn't have past had it been wrongly formatted
*(*ppszSave) = 0;
m_pRequest->DebugSetHeadersInvalid(TRUE);
if (NULL == (szDestinationURL = SkipHTTPPrefixAndHostName(szDestinationPrefix))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: MOVE/COPY fails, \"Destination:\" header not formatted corectly.\r\n"));
SetStatus(STATUS_BADREQ);
goto done;
}
// szDestinationURL is pointer into m_pRequest->m_bufRequest. Need
// a new copy of buffer for canonicalization. +1 to store possible '/' at end.
if (NULL == (szDestination = MySzAllocA(strlen(szDestinationURL)+1))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: MOVE/COPY fails, server out of memory\r\n"));
SetStatus(STATUS_INTERNALERR);
goto done;
}
svsutil_HttpCanonicalizeUrlA(szDestinationURL,szDestination);
RemoveTrailingSlashIfNeededA(szDestination);
done:
if (szDestination) {
*ppszDestinationPrefix = szDestinationPrefix;
*ppszDestinationURL = szDestinationURL;
}
#if defined (DEBUG)
if (m_pRequest->m_rs == STATUS_OK)
DEBUGMSG(ZONE_WEBDAV_VERBOSE,(L"HTTPD: WebDav GetDestinationHeader sets canonicalized dest root = <<%a>>, destURL = <<%a>>\r\n",
szDestination,szDestinationURL));
#endif
return szDestination;
}
// Determines whether we have write access to "Destination:" URL. Parameters passed in are the
// access permisions of the vroot in destination.
BOOL CWebDav::HasAccessToDestVRoot(CHAR *szDestURL, WCHAR *szPath, PVROOTINFO pVRoot) {
AUTHLEVEL authGranted = AUTH_PUBLIC;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -