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.
758 lines
20 KiB
758 lines
20 KiB
/*
|
|
* F S G E T . C P P
|
|
*
|
|
* Sources file system implementation of DAV-Base
|
|
*
|
|
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
*/
|
|
|
|
#include "_davfs.h"
|
|
#include <htmlmap.h>
|
|
#include <ex\rgiter.h>
|
|
|
|
/*
|
|
* ScEmitFile()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Helper function used to open and transmit a given
|
|
* file from the local dav namespace.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method util obj
|
|
* pwszFile [in] name of file to emit
|
|
* pwszContent [in] content type of the file, we need it if it is a multipart response
|
|
*
|
|
* Returns:
|
|
*
|
|
* SCODE.
|
|
* S_OK (0) indicates success, and the WHOLE file was sent.
|
|
* W_DAV_PARTIAL_CONTENT indicates success, but only PARTIAL content was
|
|
* sent because of a Content-Range header.
|
|
* An error (FAILED(sc)) means that the file was not setn.
|
|
*/
|
|
SCODE
|
|
ScEmitFile (LPMETHUTIL pmu,
|
|
LPCWSTR pwszFile,
|
|
LPCWSTR pwszContent)
|
|
{
|
|
auto_ref_handle hf;
|
|
BOOL fMap = FALSE;
|
|
BY_HANDLE_FILE_INFORMATION fi;
|
|
CRangeParser riByteRange;
|
|
LPCWSTR pwsz;
|
|
SCODE sc = S_OK;
|
|
UINT cch;
|
|
|
|
// Check validity of input
|
|
//
|
|
Assert (pwszFile);
|
|
Assert (pwszContent);
|
|
|
|
// Check to see if we have a map file
|
|
//
|
|
cch = static_cast<UINT>(wcslen (pwszFile));
|
|
if ((cch >= 4) && !_wcsicmp (L".map", pwszFile + cch - 4))
|
|
fMap = TRUE;
|
|
|
|
// If we have a locktoken, try to get the lock handle from the cache.
|
|
// If this fails, fall through and do the normal processing.
|
|
// DO NOT put LOCK handles into an auto-object!! The CACHE still owns it!!!
|
|
//
|
|
pwsz = pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
|
|
if (!pwsz ||
|
|
!FGetLockHandle (pmu, pmu->LpwszPathTranslated(), GENERIC_READ, pwsz, &hf))
|
|
{
|
|
// Open the file and go to it
|
|
//
|
|
if (!hf.FCreate(
|
|
DavCreateFile (pwszFile, // filename
|
|
GENERIC_READ, // dwAccess
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, // lpSecurityAttributes
|
|
OPEN_EXISTING, // creation flags
|
|
FILE_ATTRIBUTE_NORMAL |
|
|
FILE_FLAG_SEQUENTIAL_SCAN |
|
|
FILE_FLAG_OVERLAPPED, // attributes
|
|
NULL))) // template
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
sc = HRESULT_FROM_WIN32 (dwErr);
|
|
|
|
// Special work for 416 Locked responses -- fetch the
|
|
// comment & set that as the response body.
|
|
//
|
|
if (FLockViolation (pmu, dwErr, pwszFile, GENERIC_READ))
|
|
{
|
|
sc = E_DAV_LOCKED;
|
|
}
|
|
|
|
DebugTrace ("Dav: failed to open the file for retrieval\n");
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
// We better have a valid handle.
|
|
Assert (hf.get() != INVALID_HANDLE_VALUE);
|
|
|
|
// We are going to need the file size for both map files and
|
|
// ordinary files. For map files, we read the entire file into
|
|
// memory. For ordinary files, we need the file size to do
|
|
// byte range validation.
|
|
//
|
|
if (!GetFileInformationByHandle(hf.get(), &fi))
|
|
{
|
|
sc = HRESULT_FROM_WIN32 (GetLastError());
|
|
goto ret;
|
|
}
|
|
|
|
// Again, if it is a mapfile, we need to parse the map and
|
|
// find the right URL to redirect to, otherwise, we can just
|
|
// emit the file back to the client.
|
|
//
|
|
if (fMap && pmu->FTranslated())
|
|
{
|
|
auto_handle<HANDLE> hevt(CreateEvent(NULL, TRUE, FALSE, NULL));
|
|
auto_heap_ptr<CHAR> pszBuf;
|
|
BOOL fRedirect = FALSE;
|
|
LPCSTR pszPrefix;
|
|
CHAR pszRedirect[MAX_PATH];
|
|
OVERLAPPED ov;
|
|
ULONG cb;
|
|
|
|
// The common case is that these map files are not very large.
|
|
// We may want to put a physical upper bound on the size of the
|
|
// file, but I don't see that as an imperitive thing at this
|
|
// point.
|
|
//
|
|
// Since we are going to need to parse the whole thing, we are
|
|
// going to read the whole thing into memory at once. Read the file
|
|
// in, and then parse it out.
|
|
//
|
|
// Allocate space for the file.
|
|
// Lets put an uper bound of 64K on it.
|
|
//
|
|
if ((fi.nFileSizeHigh != 0) || (fi.nFileSizeLow > (128 * 1024)))
|
|
{
|
|
// Mapping is too large for our tastes
|
|
//
|
|
DavTrace ("Dav: mapping file too large\n");
|
|
sc = HRESULT_FROM_WIN32 (ERROR_MORE_DATA);
|
|
goto ret;
|
|
}
|
|
pszBuf = (CHAR *)g_heap.Alloc (fi.nFileSizeLow + 1);
|
|
|
|
// Read it in
|
|
//
|
|
ov.hEvent = hevt;
|
|
ov.Offset = 0;
|
|
ov.OffsetHigh = 0;
|
|
if (!ReadFromOverlapped (hf.get(), pszBuf, fi.nFileSizeLow, &cb, &ov))
|
|
{
|
|
sc = HRESULT_FROM_WIN32 (GetLastError());
|
|
goto ret;
|
|
}
|
|
Assert (cb == fi.nFileSizeLow);
|
|
|
|
// Ensure the file data is NULL terminated
|
|
//
|
|
*(pszBuf + cb) = 0;
|
|
|
|
// Check the map...
|
|
//
|
|
pmu->CchUrlPrefix(&pszPrefix);
|
|
if (FIsMapProcessed (pmu->LpszQueryString(),
|
|
pszPrefix,
|
|
pmu->LpszServerName(),
|
|
pszBuf.get(),
|
|
&fRedirect,
|
|
pszRedirect,
|
|
MAX_PATH))
|
|
{
|
|
// Redirect the request
|
|
//
|
|
if (fRedirect)
|
|
{
|
|
sc = pmu->ScRedirect (pszRedirect);
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
// if not redirect, we should rewind the file pointer
|
|
// back to the beginning.
|
|
//
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer (hf.get(), 0, NULL, FILE_BEGIN))
|
|
{
|
|
sc = HRESULT_FROM_WIN32 (GetLastError());
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
// Do any byte range (206 Partial Content) processing. The function will fail out if the
|
|
// we are trying to do byte ranges on the file larger than 4GB.
|
|
//
|
|
sc = ScProcessByteRanges (pmu, pwszFile, fi.nFileSizeLow, fi.nFileSizeHigh, &riByteRange);
|
|
|
|
// Tell the parser to transmit the file
|
|
//
|
|
// We need to transmit the entire file
|
|
//
|
|
if (S_OK == sc)
|
|
{
|
|
// Just add the file
|
|
//
|
|
pmu->AddResponseFile (hf);
|
|
}
|
|
else if (W_DAV_PARTIAL_CONTENT == sc)
|
|
{
|
|
// It is a byte range transmission. Tranmsit the ranges.
|
|
//
|
|
Assert(0 == fi.nFileSizeHigh);
|
|
TransmitFileRanges(pmu, hf, fi.nFileSizeLow, &riByteRange, pwszContent);
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*
|
|
* TransmitFileRanges()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Helper function used to transmit a byte range
|
|
* file from the local dav namespace.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method util obj
|
|
* hf [in] handle of file to emit
|
|
* dwSize [in] size of file
|
|
* priRanges [in] the ranges
|
|
* pszContent [in] content type of the file, we need it if it is a multipart response
|
|
*
|
|
*/
|
|
VOID
|
|
TransmitFileRanges (LPMETHUTIL pmu,
|
|
const auto_ref_handle& hf,
|
|
DWORD dwSize,
|
|
CRangeBase * priRanges,
|
|
LPCWSTR pwszContent)
|
|
{
|
|
auto_heap_ptr<WCHAR> pwszPreamble;
|
|
WCHAR rgwchBoundary[75];
|
|
const RGITEM * prgi = NULL;
|
|
DWORD dwTotalRanges;
|
|
|
|
// Create a buffer for the preamble we tramsit before each part
|
|
// of the response.
|
|
//
|
|
pwszPreamble = static_cast<LPWSTR>(g_heap.Alloc
|
|
((2 + CElems(rgwchBoundary) + 2 +
|
|
gc_cchContent_Type + 2 + wcslen(pwszContent) + 2 +
|
|
gc_cchContent_Range + 2 + gc_cchBytes + 40) * sizeof(WCHAR)));
|
|
|
|
// Assert that we have a at least one range to transmit
|
|
//
|
|
Assert (priRanges);
|
|
dwTotalRanges = priRanges->UlTotalRanges();
|
|
Assert (dwTotalRanges > 0);
|
|
|
|
// Assert that we have a content type.
|
|
//
|
|
Assert (pwszContent);
|
|
|
|
// Rewind to the first range. This is only a precautionary measure.
|
|
//
|
|
priRanges->Rewind();
|
|
prgi = priRanges->PrgiNextRange();
|
|
|
|
// Is it a singlepart response
|
|
//
|
|
if ((1 == dwTotalRanges) && prgi && (RANGE_ROW == prgi->uRT))
|
|
{
|
|
// Set the content range header
|
|
//
|
|
wsprintfW(pwszPreamble, L"%ls %u-%u/%u",
|
|
gc_wszBytes,
|
|
prgi->dwrgi.dwFirst,
|
|
prgi->dwrgi.dwLast,
|
|
dwSize);
|
|
|
|
pmu->SetResponseHeader (gc_szContent_Range, pwszPreamble);
|
|
|
|
// Add the file
|
|
//
|
|
pmu->AddResponseFile (hf,
|
|
prgi->dwrgi.dwFirst,
|
|
prgi->dwrgi.dwLast - prgi->dwrgi.dwFirst + 1);
|
|
}
|
|
else
|
|
{
|
|
// We have multiple byte ranges, then we need to generate a
|
|
// boundary and set the Content-Type header with the multipart
|
|
// content type and boundary.
|
|
//
|
|
// Generate a boundary
|
|
//
|
|
GenerateBoundary (rgwchBoundary, CElems(rgwchBoundary));
|
|
|
|
// Create the content type header with the boundary generated
|
|
//
|
|
wsprintfW(pwszPreamble, L"%ls; %ls=\"%ls\"",
|
|
gc_wszMultipart_Byterange,
|
|
gc_wszBoundary,
|
|
rgwchBoundary);
|
|
|
|
// Reset the content type header with the new content type
|
|
//
|
|
pmu->SetResponseHeader (gc_szContent_Type, pwszPreamble);
|
|
|
|
do {
|
|
|
|
if (RANGE_ROW == prgi->uRT)
|
|
{
|
|
// Create preamble.
|
|
//
|
|
wsprintfW(pwszPreamble, L"--%ls%ls%ls: %ls%ls%ls: %ls %u-%u/%u%ls%ls",
|
|
rgwchBoundary,
|
|
gc_wszCRLF,
|
|
gc_wszContent_Type,
|
|
pwszContent,
|
|
gc_wszCRLF,
|
|
gc_wszContent_Range,
|
|
gc_wszBytes,
|
|
prgi->dwrgi.dwFirst,
|
|
prgi->dwrgi.dwLast,
|
|
dwSize,
|
|
gc_wszCRLF,
|
|
gc_wszCRLF);
|
|
|
|
pmu->AddResponseText (static_cast<UINT>(wcslen(pwszPreamble)), pwszPreamble);
|
|
pmu->AddResponseFile (hf,
|
|
prgi->dwrgi.dwFirst,
|
|
prgi->dwrgi.dwLast - prgi->dwrgi.dwFirst + 1);
|
|
|
|
// Add the CRLF
|
|
//
|
|
pmu->AddResponseText (gc_cchCRLF, gc_szCRLF);
|
|
}
|
|
prgi = priRanges->PrgiNextRange();
|
|
|
|
} while (prgi);
|
|
|
|
// Add the last end of response text
|
|
//
|
|
wsprintfW(pwszPreamble, L"--%ls--", rgwchBoundary);
|
|
pmu->AddResponseText (static_cast<UINT>(wcslen(pwszPreamble)), pwszPreamble);
|
|
}
|
|
}
|
|
|
|
SCODE
|
|
ScGetFile (LPMETHUTIL pmu,
|
|
LPWSTR pwszFile,
|
|
LPCWSTR pwszURI)
|
|
{
|
|
SCODE sc;
|
|
WCHAR rgwszContent[MAX_PATH];
|
|
FILETIME ft;
|
|
|
|
// Get the Content-Type of the file
|
|
//
|
|
UINT cchContent = CElems(rgwszContent);
|
|
if (!pmu->FGetContentType(pwszURI, rgwszContent, &cchContent))
|
|
{
|
|
sc = E_FAIL;
|
|
goto ret;
|
|
}
|
|
|
|
// This method is gated by If-xxx headers
|
|
//
|
|
sc = ScCheckIfHeaders (pmu, pwszFile, TRUE);
|
|
if (FAILED (sc))
|
|
{
|
|
DebugTrace ("Dav: If-xxx failed their check\n");
|
|
goto ret;
|
|
}
|
|
|
|
sc = HrCheckStateHeaders (pmu, // methutil
|
|
pwszFile, // path
|
|
TRUE); // fGetMeth
|
|
if (FAILED (sc))
|
|
{
|
|
DebugTrace ("DavFS: If-State checking failed.\n");
|
|
// 304 returns from get should really have an ETag....
|
|
//SideAssert(FGetLastModTime (pmu, pwszFile, &ft));
|
|
//hsc = HscEmitHeader (pmu, pszContent, &ft);
|
|
goto ret;
|
|
}
|
|
|
|
// Emit the headers for the file
|
|
//
|
|
if (FGetLastModTime (pmu, pwszFile, &ft))
|
|
{
|
|
sc = pmu->ScEmitHeader (rgwszContent, pwszURI, &ft);
|
|
if (sc != S_OK)
|
|
{
|
|
DebugTrace ("Dav: failed to emit headers\n");
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
// Emit the file
|
|
//
|
|
sc = ScEmitFile (pmu, pwszFile, rgwszContent);
|
|
if ( (sc != S_OK) && (sc != W_DAV_PARTIAL_CONTENT) )
|
|
{
|
|
DebugTrace ("Dav: failed to emit file\n");
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
void
|
|
GetDirectory (LPMETHUTIL pmu, LPCWSTR pwszUrl)
|
|
{
|
|
auto_ref_ptr<IMDData> pMDData;
|
|
ULONG ulDirBrowsing = 0;
|
|
LPCWSTR pwszDftDocList = NULL;
|
|
SCODE sc = S_OK;
|
|
UINT cchUrl = static_cast<UINT>(wcslen(pwszUrl));
|
|
|
|
// Before we decide to do anything, we need to check to see what
|
|
// kind of default behavior is expected for a directory. Values
|
|
// for this level of access are cached from the metabase. Look
|
|
// them up and see what to do.
|
|
|
|
// Get the metabase doc attributes
|
|
//
|
|
if (FAILED(pmu->HrMDGetData (pwszUrl, pMDData.load())))
|
|
{
|
|
//
|
|
//$REVIEW HrMDGetData() can fail for timeout reasons,
|
|
//$REVIEW shouldn't we just pass back the hr that it returns?
|
|
//
|
|
sc = E_DAV_NO_IIS_READ_ACCESS;
|
|
goto ret;
|
|
}
|
|
|
|
ulDirBrowsing = pMDData->DwDirBrowsing();
|
|
pwszDftDocList = pMDData->PwszDefaultDocList();
|
|
|
|
// Try to load default file if allowed, do this only when translate: t
|
|
//
|
|
if ((ulDirBrowsing & MD_DIRBROW_LOADDEFAULT) && pwszDftDocList && pmu->FTranslated())
|
|
{
|
|
HDRITER_W hit(pwszDftDocList);
|
|
LPCWSTR pwszDoc;
|
|
|
|
while (NULL != (pwszDoc = hit.PszNext()))
|
|
{
|
|
auto_com_ptr<IStream> pstm;
|
|
CStackBuffer<WCHAR> pwszDocUrl;
|
|
CStackBuffer<WCHAR> pwszDocUrlNormalized;
|
|
CStackBuffer<WCHAR,MAX_PATH> pwszDocPath;
|
|
UINT cchDocUrlNormalized;
|
|
UINT cchDoc;
|
|
|
|
// What happens here is that for EVERY possible default
|
|
// document, we are going to see if this document is something
|
|
// we can legitimately process.
|
|
//
|
|
// So first, we need to extend our url and normalize it
|
|
// in such a way that we can pin-point the document it uses.
|
|
//
|
|
cchDoc = static_cast<UINT>(wcslen(pwszDoc));
|
|
pwszDocUrl.resize(CbSizeWsz(cchUrl + cchDoc));
|
|
memcpy (pwszDocUrl.get(), pwszUrl, cchUrl * sizeof(WCHAR));
|
|
memcpy (pwszDocUrl.get() + cchUrl, pwszDoc, (cchDoc + 1) * sizeof(WCHAR));
|
|
|
|
// Now, someone could have been evil and stuffed path modifiers
|
|
// or escaped characters in the default document name. So we
|
|
// need to parse those out here -- do both in place. You can't
|
|
// do this per-say in the MMC snap in, but MDUTIL does (maybe
|
|
// ASDUTIL too).
|
|
//
|
|
cchDocUrlNormalized = cchUrl + cchDoc + 1;
|
|
pwszDocUrlNormalized.resize(cchDocUrlNormalized * sizeof(WCHAR));
|
|
|
|
sc = ScNormalizeUrl (pwszDocUrl.get(),
|
|
&cchDocUrlNormalized,
|
|
pwszDocUrlNormalized.get(),
|
|
NULL);
|
|
if (S_FALSE == sc)
|
|
{
|
|
pwszDocUrlNormalized.resize(cchDocUrlNormalized * sizeof(WCHAR));
|
|
sc = ScNormalizeUrl (pwszDocUrl.get(),
|
|
&cchDocUrlNormalized,
|
|
pwszDocUrlNormalized.get(),
|
|
NULL);
|
|
|
|
// Since we've given ScNormalizeUrl() the space it asked for,
|
|
// we should never get S_FALSE again. Assert this!
|
|
//
|
|
Assert(S_FALSE != sc);
|
|
}
|
|
|
|
if (FAILED (sc))
|
|
continue;
|
|
|
|
// Translate this into a local path. We should be able to
|
|
//
|
|
// At most we should go through the processing below twice, as the byte
|
|
// count required is an out param.
|
|
//
|
|
do {
|
|
|
|
pwszDocPath.resize(cchDocUrlNormalized * sizeof(WCHAR));
|
|
sc = pmu->ScStoragePathFromUrl (pwszDocUrlNormalized.get(),
|
|
pwszDocPath.get(),
|
|
&cchDocUrlNormalized);
|
|
|
|
} while (sc == S_FALSE);
|
|
if (FAILED (sc) || (W_DAV_SPANS_VIRTUAL_ROOTS == sc))
|
|
continue;
|
|
|
|
//$ SECURITY:
|
|
//
|
|
// Check to see if the destination is really a short
|
|
// filename.
|
|
//
|
|
sc = ScCheckIfShortFileName (pwszDocPath.get(), pmu->HitUser());
|
|
if (FAILED (sc))
|
|
continue;
|
|
|
|
//$ SECURITY:
|
|
//
|
|
// Check to see if the destination is really the default
|
|
// data stream via alternate file access.
|
|
//
|
|
sc = ScCheckForAltFileStream (pwszDocPath.get());
|
|
if (FAILED (sc))
|
|
continue;
|
|
|
|
if (static_cast<DWORD>(-1) != GetFileAttributesW (pwszDocPath.get()))
|
|
{
|
|
DWORD dwAcc = 0;
|
|
|
|
// See if we have the right access...
|
|
//
|
|
(void) pmu->ScIISAccess (pwszDocUrlNormalized.get(), MD_ACCESS_READ, &dwAcc);
|
|
|
|
// Found the default doc, if a child ISAPI doesn't want it,
|
|
// then we will handle it.
|
|
//
|
|
// NOTE: Pass in TRUE for fCheckISAPIAccess to tell this
|
|
// function to do all the special access checking.
|
|
//
|
|
// NOTE: Also pass in FALSE to fKeepQueryString 'cause
|
|
// we're re-routing this request to a whole new URI.
|
|
//
|
|
sc = pmu->ScApplyChildISAPI (pwszDocUrlNormalized.get(), dwAcc, TRUE, FALSE);
|
|
if (FAILED(sc))
|
|
{
|
|
// Either the request has been forwarded, or some bad error occurred.
|
|
// In either case, quit here and map the error!
|
|
//
|
|
goto ret;
|
|
}
|
|
|
|
// Emit the location of the default document
|
|
//
|
|
pmu->EmitLocation (gc_szContent_Location, pwszDocUrlNormalized.get(), FALSE);
|
|
|
|
// If we don't have any read access, then there
|
|
// is no point in continuing the request.
|
|
//
|
|
if (0 == (dwAcc & MD_ACCESS_READ))
|
|
{
|
|
sc = E_DAV_NO_IIS_READ_ACCESS;
|
|
goto ret;
|
|
}
|
|
|
|
// Get the file
|
|
//
|
|
// NOTE: This function can give a WARNING that needs to be mapped
|
|
// to get our 207 Partial Content in some cases.
|
|
//
|
|
sc = ScGetFile (pmu, pwszDocPath.get(), pwszDocUrlNormalized.get());
|
|
goto ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we haven't emitted any other way, see if HTML
|
|
// is allowed...
|
|
//
|
|
if (ulDirBrowsing & MD_DIRBROW_ENABLED)
|
|
{
|
|
// At one point in time we would generate our own HTML rendering
|
|
// of the directories, but at the beginning of NT beta3, the change
|
|
// was made to behave the same in HTTPExt as in DavEX, etc.
|
|
//
|
|
sc = W_DAV_NO_CONTENT;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, report forbidden
|
|
// We weren't allowed to browse the dir and there was no
|
|
// default doc. IIS maps this scenario to a specific
|
|
// suberror (403.2).
|
|
//
|
|
sc = E_DAV_NO_IIS_READ_ACCESS;
|
|
}
|
|
|
|
ret:
|
|
|
|
pmu->SetResponseCode (HscFromHresult(sc), NULL, 0, CSEFromHresult(sc));
|
|
}
|
|
|
|
/*
|
|
* GetInt()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Win32 file system implementation of the DAV GET method. The
|
|
* GET method returns a file from the DAV name space and populates
|
|
* the headers with the info found in the file and its meta data. The
|
|
* response created indicates the success of the call and contains
|
|
* the data from the file.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method utility object
|
|
*/
|
|
void
|
|
GetInt (LPMETHUTIL pmu)
|
|
{
|
|
CResourceInfo cri;
|
|
LPCWSTR pwszUrl = pmu->LpwszRequestUrl();
|
|
LPCWSTR pwszPath = pmu->LpwszPathTranslated();
|
|
SCODE sc = S_OK;
|
|
|
|
// Do ISAPI application and IIS access bits checking
|
|
//
|
|
sc = pmu->ScIISCheck (pwszUrl, MD_ACCESS_READ, TRUE);
|
|
if (FAILED(sc))
|
|
{
|
|
// Either the request has been forwarded, or some bad error occurred.
|
|
// In either case, quit here and map the error!
|
|
//
|
|
goto ret;
|
|
}
|
|
|
|
// If we have a directory, we are going to return
|
|
// HTML, otherwise the extenstion of the file gives
|
|
// us what we need.
|
|
//
|
|
sc = cri.ScGetResourceInfo (pwszPath);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// If this is a hidden object, fail with 404 Resource Not Found. This is to be
|
|
// consistent with IIS (See NTRAID #247218). They do not allow GET on resources that
|
|
// have the HIDDEN bit set.
|
|
//
|
|
if (cri.FHidden())
|
|
{
|
|
sc = E_DAV_HIDDEN_OBJECT;
|
|
goto ret;
|
|
}
|
|
|
|
// If this is a directory, process it as such, otherwise
|
|
// handle the request as if the resource was a file
|
|
//
|
|
if (cri.FCollection())
|
|
{
|
|
// GET allows for request url's that end in a trailing slash
|
|
// when getting data from a directory. Otherwise it is a bad
|
|
// request. If it does not have a trailing slash and refers
|
|
// to a directory, then we want to redirect.
|
|
//
|
|
sc = ScCheckForLocationCorrectness (pmu, cri, REDIRECT);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// A return of S_FALSE from above means that a redirect happened
|
|
// so we can forego trying to do a GET on the directory.
|
|
//
|
|
if (S_FALSE == sc)
|
|
return;
|
|
|
|
GetDirectory (pmu, pwszUrl);
|
|
return;
|
|
}
|
|
|
|
// GET allows for request url's that end in a trailing slash
|
|
// when getting data from a directory. Otherwise it is not found.
|
|
// This matches IIS's behavior as of 9/28/98.
|
|
//
|
|
if (FTrailingSlash (pwszUrl))
|
|
{
|
|
// Trailing slash on a non-directory just doesn't work
|
|
//
|
|
sc = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
|
goto ret;
|
|
}
|
|
|
|
// Emit the file
|
|
//
|
|
sc = ScGetFile (pmu, const_cast<LPWSTR>(pwszPath), pwszUrl);
|
|
|
|
ret:
|
|
|
|
pmu->SetResponseCode (HscFromHresult(sc), NULL, 0, CSEFromHresult(sc));
|
|
}
|
|
|
|
/*
|
|
* DAVGet()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Win32 file system implementation of the DAV GET method. The
|
|
* GET method returns a file from the DAV name space and populates
|
|
* the headers with the info found in the file and its meta data. The
|
|
* response created indicates the success of the call and contains
|
|
* the data from the file.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method utility object
|
|
*/
|
|
void
|
|
DAVGet (LPMETHUTIL pmu)
|
|
{
|
|
GetInt (pmu);
|
|
}
|
|
|
|
/*
|
|
* DAVHead()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Win32 file system implementation of the DAV HEAD method. The
|
|
* HEAD method returns a file from the DAV name space and populates
|
|
* the headers with the info found in the file and its meta data.
|
|
* The response created indicates the success of the call and contains
|
|
* the data from the file.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method utility object
|
|
*/
|
|
void
|
|
DAVHead (LPMETHUTIL pmu)
|
|
{
|
|
// The HEAD method should never return any body what so ever...
|
|
//
|
|
pmu->SupressBody();
|
|
|
|
// Otherwise, it is just the same as a GET
|
|
//
|
|
GetInt (pmu);
|
|
}
|