Leaked source code of windows server 2003
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.
 
 
 
 
 
 

325 lines
7.7 KiB

/*
* I F . C P P
*
* If-xxx header processing and ETags for DAV resources
*
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
*/
#include "_davprs.h"
//$ REVIEW: This file was once the same as \exdav\davif.cpp.
//$ REVIEW: These two files should really be merged. They share a lot of
//$ REVIEW: common functionality, but they have been evolving separately.
//$ REVIEW: We need to be very careful because different bug fixes have
//$ REVIEW: been going into each of them.
// ETag formation ------------------------------------------------------------
//
/*
* FETagFromFiletime()
*
* Purpose:
*
* Derives an ETag for a given resource or a given last modification
* time.
*
* Parameters:
*
* pft [in] last modification time
* pwszETag [out] ETag buffer
* pecb [in] ecb so that we can access the metabase
*
* Returns:
*
* TRUE if ETag was created.
*/
BOOL
FETagFromFiletime (FILETIME * pft, LPWSTR pwszEtag, const IEcb * pecb)
{
Assert (pwszEtag);
Assert (pecb);
swprintf (pwszEtag,
L"\"%x%x%x%x%x%x%x%x:%x\"",
(DWORD)(((PUCHAR)pft)[0]),
(DWORD)(((PUCHAR)pft)[1]),
(DWORD)(((PUCHAR)pft)[2]),
(DWORD)(((PUCHAR)pft)[3]),
(DWORD)(((PUCHAR)pft)[4]),
(DWORD)(((PUCHAR)pft)[5]),
(DWORD)(((PUCHAR)pft)[6]),
(DWORD)(((PUCHAR)pft)[7]),
DwMDChangeNumber(pecb));
return TRUE;
}
// If-xxx header processing --------------------------------------------------
//
SCODE
ScCheckEtagAgainstHeader (LPCWSTR pwszEtag, LPCWSTR pwszHeader)
{
LPCWSTR pwsz;
Assert (pwszHeader);
// Get the ETag we are going to compare against, and then
// look at what was passed it. It should either be an ETAG
// or an '*'. We fail if the value does not exist or the
// ETag does not match.
//
HDRITER_W hdri(pwszHeader);
for (pwsz = hdri.PszNext(); pwsz; pwsz = hdri.PszNext())
{
// Since we do not do week ETAG checking, if the
// ETAG starts with "W/" skip those bits
//
if (L'W' == *pwsz)
{
Assert (L'/' == pwsz[1]);
pwsz += 2;
}
// If we see stars, then we match
//
if (L'*' == *pwsz)
return S_OK;
else
{
// For DAVFS, we don't do weak matching today
//
if (pwszEtag && !wcscmp (pwsz, pwszEtag))
return S_OK;
}
}
return E_DAV_IF_HEADER_FAILURE;
}
SCODE
ScCheckFileTimeAgainstHeader (FILETIME * pft, LPCWSTR pwszHeader)
{
FILETIME ftHeader;
FILETIME ftTmp;
SYSTEMTIME st;
Assert (pft);
Assert (pwszHeader);
// The header passed in here should be an HTTP-date
// of the format "ddd, dd, mmm yyyy HH:mm:ss GMT".
// We can spit this into a SYSTEMTIME and then compare
// it against the filetime for the resource.
//
DebugTrace ("DAV: evaluating If-Unmodified-Since header\n");
memset (&st, 0, sizeof(SYSTEMTIME));
if (SUCCEEDED (HrHTTPDateToFileTime(pwszHeader, &ftHeader)))
{
FILETIME ftCur;
// The filetime retrieved from FGetLastModTime is acurate
// down to 100-nanosecond increments. The converted date
// is acurate down to seconds. Adjust for that.
//
FileTimeToSystemTime (pft, &st);
st.wMilliseconds = 0;
SystemTimeToFileTime (&st, &ftTmp);
// Get current time
//
GetSystemTimeAsFileTime(&ftCur);
// Compare the two filetimes
// Note that we also need to make sure the Modified-Since time is
// less than our current time
//
if ((CompareFileTime (&ftHeader, &ftTmp) >= 0) &&
(CompareFileTime (&ftHeader, &ftCur) < 0))
return S_OK;
return E_DAV_IF_HEADER_FAILURE;
}
return S_FALSE;
}
SCODE
ScCheckIfHeaders(IMethUtil * pmu,
FILETIME * pft,
BOOL fGetMethod)
{
Assert(pmu);
WCHAR pwszEtag[CCH_ETAG];
SideAssert(FETagFromFiletime (pft, pwszEtag, pmu->GetEcb()));
return ScCheckIfHeadersFromEtag (pmu,
pft,
fGetMethod,
pwszEtag);
}
SCODE
ScCheckIfHeadersFromEtag (IMethUtil * pmu,
FILETIME* pft,
BOOL fGetMethod,
LPCWSTR pwszEtag)
{
SCODE sc = S_OK;
LPCWSTR pwsz;
Assert (pmu);
Assert (pft);
Assert (pwszEtag);
// There're several bugs related with DAV not matching IIS behavior
// on these If-xxx header processing.
// So we now just copy their logic over
// Check the 'if-match' header
//
if ((pwsz = pmu->LpwszGetRequestHeader (gc_szIf_Match, FALSE)) != NULL)
{
DebugTrace ("DAV: evaluating 'if-match' header\n");
sc = ScCheckEtagAgainstHeader (pwszEtag, pwsz);
if (FAILED (sc))
goto ret;
}
// Now see if we have an If-None-Match, and if so handle that.
//
BOOL fIsNoneMatchPassed = TRUE;
BOOL fSkipIfModifiedSince = FALSE;
if ((pwsz = pmu->LpwszGetRequestHeader (gc_szIf_None_Match, FALSE)) != NULL)
{
DebugTrace ("DAV: evaluating 'if-none-match' header\n");
if (!FAILED (ScCheckEtagAgainstHeader (pwszEtag, pwsz)))
{
// Etag match, so nonmatch test is NOT passed
//
fIsNoneMatchPassed = FALSE;
}
else
{
fSkipIfModifiedSince = TRUE;
}
}
// The "if-modified-since" really only applies to GET-type
// requests
//
if (!fSkipIfModifiedSince && fGetMethod)
{
if ((pwsz = pmu->LpwszGetRequestHeader (gc_szIf_Modified_Since, FALSE)) != NULL)
{
DebugTrace ("DAV: evaluating 'if-none-match' header\n");
if (S_OK == ScCheckFileTimeAgainstHeader (pft, pwsz))
{
sc = fGetMethod
? E_DAV_ENTITY_NOT_MODIFIED
: E_DAV_IF_HEADER_FAILURE;
goto ret;
}
fIsNoneMatchPassed = TRUE;
}
}
if (!fIsNoneMatchPassed)
{
sc = fGetMethod
? E_DAV_ENTITY_NOT_MODIFIED
: E_DAV_IF_HEADER_FAILURE;
goto ret;
}
// Made it through that, handle If-Unmodified-Since if we have that.
//
if ((pwsz = pmu->LpwszGetRequestHeader (gc_szIf_Unmodified_Since, FALSE)) != NULL)
{
DebugTrace ("DAV: evaluating 'if-unmodified-since' header\n");
sc = ScCheckFileTimeAgainstHeader (pft, pwsz);
if (FAILED (sc))
goto ret;
}
ret:
if (sc == E_DAV_ENTITY_NOT_MODIFIED)
{
// Let me quote from the HTTP/1.1 draft...
//
// "The response MUST include the following header fields:
//
// ...
//
// . ETag and/or Content-Location, if the header would have been sent in
// a 200 response to the same request
//
// ..."
//
// So what that means, is that we really just want to do is suppress the
// body of the response, set a 304 error code and do everything else as
// normal. All of which is done by setting a hsc of 304.
//
DebugTrace ("Dav: suppressing body for 304 response\n");
pmu->SetResponseCode (HSC_NOT_MODIFIED, NULL, 0);
sc = S_OK;
}
return sc;
}
SCODE
ScCheckIfRangeHeader (IMethUtil * pmu, FILETIME * pft)
{
Assert(pmu);
WCHAR pwszEtag[CCH_ETAG];
SideAssert(FETagFromFiletime (pft, pwszEtag, pmu->GetEcb()));
return ScCheckIfRangeHeaderFromEtag (pmu, pft, pwszEtag);
}
SCODE
ScCheckIfRangeHeaderFromEtag (IMethUtil * pmu, FILETIME * pft, LPCWSTR pwszEtag)
{
SCODE sc = S_OK;
LPCWSTR pwsz;
Assert (pmu);
Assert (pft);
Assert (pwszEtag);
// Check "If-Range". Do not apply URL conversion rules to this header
//
if ((pwsz = pmu->LpwszGetRequestHeader (gc_szIf_Range, FALSE)) != NULL)
{
DebugTrace ("DAV: evaluating 'if-range' header\n");
// The format of this header is either an ETAG or a
// date. Process accordingly...
//
if ((L'"' == *pwsz) || (L'"' == *(pwsz + 2)))
{
if (L'W' == *pwsz)
{
Assert (L'/' == *(pwsz + 1));
pwsz += 2;
}
sc = ScCheckEtagAgainstHeader (pwszEtag, pwsz);
if (FAILED (sc))
goto ret;
}
else
{
sc = ScCheckFileTimeAgainstHeader (pft, pwsz);
goto ret;
}
}
ret:
return sc;
}