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.
943 lines
21 KiB
943 lines
21 KiB
/*
|
|
* D A V C O M . C P P
|
|
*
|
|
* Common routines used by both DAVFS and DAVOWS.
|
|
*
|
|
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
*/
|
|
|
|
#include "_davprs.h"
|
|
#include <iiscnfg.h>
|
|
#include "instdata.h"
|
|
#include <mapicode.h>
|
|
#include <mimeole.h>
|
|
#include <dav.rh>
|
|
#include <ex\rgiter.h>
|
|
|
|
// Map last error to HTTP response code --------------------------------------
|
|
//
|
|
/*
|
|
* HscFromLastError()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Maps the value returned from GetLastError() to
|
|
* an HTTP 1.1 response status code.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* err [in] system error code
|
|
*
|
|
* Returns:
|
|
*
|
|
* Mapped system error code
|
|
*/
|
|
UINT
|
|
HscFromLastError (DWORD dwErr)
|
|
{
|
|
UINT hsc = HSC_INTERNAL_SERVER_ERROR;
|
|
|
|
switch (dwErr)
|
|
{
|
|
// Successes ---------------------------------------------------------
|
|
//
|
|
case NO_ERROR:
|
|
|
|
return HSC_OK;
|
|
|
|
// Parial Success
|
|
//
|
|
case ERROR_PARTIAL_COPY:
|
|
|
|
hsc = HSC_MULTI_STATUS;
|
|
break;
|
|
|
|
// Errors ------------------------------------------------------------
|
|
//
|
|
// Not Implemented
|
|
//
|
|
case ERROR_NOT_SUPPORTED:
|
|
case ERROR_INVALID_FUNCTION:
|
|
|
|
hsc = HSC_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
// Not Found
|
|
//
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_INVALID_NAME:
|
|
|
|
hsc = HSC_NOT_FOUND;
|
|
break;
|
|
|
|
// Unathorized Access
|
|
//
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
hsc = HSC_UNAUTHORIZED;
|
|
break;
|
|
|
|
// Forbidden access
|
|
//
|
|
case ERROR_DRIVE_LOCKED:
|
|
case ERROR_INVALID_ACCESS:
|
|
case ERROR_INVALID_PASSWORD:
|
|
case ERROR_LOCK_VIOLATION:
|
|
case ERROR_WRITE_PROTECT:
|
|
|
|
hsc = HSC_FORBIDDEN;
|
|
break;
|
|
|
|
// LOCKING -- this is the error when a resource is
|
|
// already locked.
|
|
//
|
|
case ERROR_SHARING_VIOLATION:
|
|
|
|
hsc = HSC_LOCKED;
|
|
#ifdef DBG
|
|
{
|
|
static LONG s_lAssert = -1;
|
|
if (s_lAssert == -1)
|
|
{
|
|
LONG lAss = GetPrivateProfileIntA ("general",
|
|
"Assert_423s",
|
|
0,
|
|
gc_szDbgIni);
|
|
InterlockedCompareExchange (&s_lAssert, lAss, -1);
|
|
}
|
|
if (s_lAssert != 0)
|
|
TrapSz ("GetLastError() maps to 423");
|
|
}
|
|
#endif // DBG
|
|
break;
|
|
|
|
// Bad Requests
|
|
//
|
|
case ERROR_BAD_COMMAND:
|
|
case ERROR_BAD_FORMAT:
|
|
case ERROR_INVALID_DRIVE:
|
|
case ERROR_INVALID_PARAMETER:
|
|
case ERROR_NO_UNICODE_TRANSLATION:
|
|
|
|
hsc = HSC_BAD_REQUEST;
|
|
break;
|
|
|
|
// Errors generated when the client drops the connection
|
|
// on us or when we timeout waiting for the client to send
|
|
// us additional data. These errors should map to a response
|
|
// status code of 400 Bad Request even though we can't actually
|
|
// send back the response. IIS logs the response status code
|
|
// and we want to indicate that the error is the client's,
|
|
// not ours. K2 logs a 400, so this is for compatibility.
|
|
//
|
|
case WSAECONNRESET:
|
|
case ERROR_NETNAME_DELETED:
|
|
case ERROR_SEM_TIMEOUT:
|
|
|
|
hsc = HSC_BAD_REQUEST;
|
|
break;
|
|
|
|
// Method Failure
|
|
//
|
|
case ERROR_DIR_NOT_EMPTY:
|
|
|
|
hsc = HSC_METHOD_FAILURE;
|
|
break;
|
|
|
|
// Conflict
|
|
//
|
|
case ERROR_FILE_EXISTS:
|
|
case ERROR_ALREADY_EXISTS:
|
|
|
|
hsc = HSC_CONFLICT;
|
|
break;
|
|
|
|
// Unavailable Services (SMB access)
|
|
//
|
|
case ERROR_NETWORK_UNREACHABLE:
|
|
case ERROR_UNEXP_NET_ERR:
|
|
|
|
hsc = HSC_SERVICE_UNAVAILABLE;
|
|
break;
|
|
|
|
// Returned by metabase when the server is too busy.
|
|
// Do NOT map this to HSC_SERVICE_UNAVAILABLE. The
|
|
// "too busy" scenario has an IIS custom suberror
|
|
// which assumes a 500 (not 503) status code.
|
|
//
|
|
case ERROR_PATH_BUSY:
|
|
|
|
hsc = HSC_INTERNAL_SERVER_ERROR;
|
|
break;
|
|
|
|
// This error code (ERROR_OUTOFMEMORY) has been added for DAVEX.
|
|
// The exchange store returns this error when we try to retrieve
|
|
// the message body property. When we fetch properties on a
|
|
// message, the store does not return the message body property
|
|
// if the message body is greater than a certain length. The
|
|
// general idea is that we don't wan't huge message bodies
|
|
// returned along with the other properties. We'd much rather fetch
|
|
// the message body separately.
|
|
//
|
|
case ERROR_OUTOFMEMORY:
|
|
|
|
hsc = HSC_INSUFFICIENT_SPACE;
|
|
break;
|
|
|
|
default:
|
|
|
|
hsc = HSC_INTERNAL_SERVER_ERROR;
|
|
#ifdef DBG
|
|
{
|
|
static LONG s_lAssert = -1;
|
|
if (s_lAssert == -1)
|
|
{
|
|
LONG lAss = GetPrivateProfileIntA ("general",
|
|
"Assert_500s",
|
|
0,
|
|
gc_szDbgIni);
|
|
InterlockedCompareExchange (&s_lAssert, lAss, -1);
|
|
}
|
|
if (s_lAssert != 0)
|
|
TrapSz ("GetLastError() maps to 500");
|
|
}
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
|
|
DebugTrace ("DAV: sys error (%ld) mapped to hsc (%ld)\n", dwErr, hsc);
|
|
return hsc;
|
|
}
|
|
|
|
/*
|
|
* CSEFromHresult()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Maps an hresult to an IIS custom error suberror
|
|
*
|
|
* Parameters:
|
|
*
|
|
* hr [in] HRESULT error code
|
|
*
|
|
* Returns:
|
|
*
|
|
* Mapped suberror
|
|
*/
|
|
UINT
|
|
CSEFromHresult (HRESULT hr)
|
|
{
|
|
UINT cse = CSE_NONE;
|
|
|
|
switch (hr)
|
|
{
|
|
// Read Access Forbidden
|
|
//
|
|
case E_DAV_NO_IIS_READ_ACCESS:
|
|
|
|
Assert( HscFromHresult(hr) == HSC_FORBIDDEN );
|
|
cse = CSE_403_READ;
|
|
break;
|
|
|
|
// Write Access Forbidden
|
|
//
|
|
case E_DAV_NO_IIS_WRITE_ACCESS:
|
|
|
|
Assert( HscFromHresult(hr) == HSC_FORBIDDEN );
|
|
cse = CSE_403_WRITE;
|
|
break;
|
|
|
|
// Execute Access Forbidden
|
|
//
|
|
case E_DAV_NO_IIS_EXECUTE_ACCESS:
|
|
|
|
Assert( HscFromHresult(hr) == HSC_FORBIDDEN );
|
|
cse = CSE_403_EXECUTE;
|
|
break;
|
|
|
|
// Access denied due to ACL
|
|
//
|
|
case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
|
|
|
|
Assert( HscFromHresult(hr) == HSC_UNAUTHORIZED );
|
|
cse = CSE_401_ACL;
|
|
break;
|
|
|
|
// Server too busy
|
|
//
|
|
case HRESULT_FROM_WIN32(ERROR_PATH_BUSY):
|
|
|
|
Assert( HscFromHresult(hr) == HSC_INTERNAL_SERVER_ERROR );
|
|
cse = CSE_500_TOO_BUSY;
|
|
break;
|
|
}
|
|
|
|
return cse;
|
|
}
|
|
|
|
/*
|
|
* HscFromHresult()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Maps an hresult to an HTTP 1.1 response status code.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* hr [in] HRESULT error code
|
|
*
|
|
* Returns:
|
|
*
|
|
* Mapped error code
|
|
*/
|
|
UINT
|
|
HscFromHresult (HRESULT hr)
|
|
{
|
|
UINT hsc = HSC_INTERNAL_SERVER_ERROR;
|
|
|
|
// If the facility of the hr is WIN32,
|
|
// parse out the error bits and send it to HscFromLastError.
|
|
//
|
|
if (FACILITY_WIN32 == HRESULT_FACILITY(hr))
|
|
return HscFromLastError (HRESULT_CODE(hr));
|
|
|
|
switch (hr)
|
|
{
|
|
// Successes ---------------------------------------------------------
|
|
//
|
|
case S_OK:
|
|
case S_FALSE:
|
|
case W_DAV_SCRIPTMAP_MATCH_FOUND:
|
|
|
|
return HSC_OK;
|
|
|
|
// No Content
|
|
//
|
|
case W_DAV_NO_CONTENT:
|
|
|
|
hsc = HSC_NO_CONTENT;
|
|
break;
|
|
|
|
// Created
|
|
//
|
|
case W_DAV_CREATED:
|
|
|
|
hsc = HSC_CREATED;
|
|
break;
|
|
|
|
// Partial content
|
|
//
|
|
case W_DAV_PARTIAL_CONTENT:
|
|
|
|
hsc = HSC_PARTIAL_CONTENT;
|
|
break;
|
|
|
|
// Multi-status
|
|
//
|
|
case W_DAV_PARTIAL_SUCCESS:
|
|
|
|
hsc = HSC_MULTI_STATUS;
|
|
break;
|
|
|
|
// Moved temporarily
|
|
//
|
|
case W_DAV_MOVED_TEMPORARILY:
|
|
|
|
hsc = HSC_MOVED_TEMPORARILY;
|
|
break;
|
|
|
|
// Errors ------------------------------------------------------------
|
|
//
|
|
// Not modified
|
|
//
|
|
case E_DAV_ENTITY_NOT_MODIFIED:
|
|
|
|
hsc = HSC_NOT_MODIFIED;
|
|
break;
|
|
|
|
// Pre-condition failed
|
|
//
|
|
case E_DAV_IF_HEADER_FAILURE:
|
|
case E_DAV_NOTALLOWED_WITHIN_TRANSACTION:
|
|
case E_DAV_OVERWRITE_REQUIRED:
|
|
case E_DAV_CANT_SATISFY_LOCK_REQUEST:
|
|
case E_DAV_NOTIF_SUBID_ERROR:
|
|
|
|
hsc = HSC_PRECONDITION_FAILED;
|
|
break;
|
|
|
|
// Not Implemented
|
|
//
|
|
case E_NOTIMPL:
|
|
case E_DAV_NO_PARTIAL_UPDATE:
|
|
case STG_E_UNIMPLEMENTEDFUNCTION:
|
|
case STG_E_INVALIDFUNCTION:
|
|
case E_DAV_STORE_CHECK_FOLDER_NAME:
|
|
case E_DAV_MKCOL_NOT_ALLOWED_ON_NULL_RESOURCE:
|
|
case E_DAV_STORE_SEARCH_UNSUPPORTED:
|
|
|
|
hsc = HSC_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
// Not Found
|
|
//
|
|
case E_DAV_ALT_FILESTREAM:
|
|
case E_DAV_SHORT_FILENAME:
|
|
case MK_E_NOOBJECT:
|
|
case STG_E_FILENOTFOUND:
|
|
case STG_E_INVALIDNAME:
|
|
case STG_E_PATHNOTFOUND:
|
|
case E_DAV_HIDDEN_OBJECT:
|
|
case E_DAV_STORE_BAD_PATH:
|
|
case E_DAV_STORE_NOT_FOUND:
|
|
|
|
hsc = HSC_NOT_FOUND;
|
|
break;
|
|
|
|
// Unathorized Access
|
|
//
|
|
case E_DAV_ENTITY_TYPE_CONFLICT:
|
|
case E_ACCESSDENIED:
|
|
case STG_E_ACCESSDENIED:
|
|
|
|
hsc = HSC_UNAUTHORIZED;
|
|
break;
|
|
|
|
case E_DAV_SMB_PROPERTY_ERROR:
|
|
case E_DAV_NO_IIS_ACCESS_RIGHTS:
|
|
case E_DAV_NO_IIS_READ_ACCESS:
|
|
case E_DAV_NO_IIS_WRITE_ACCESS:
|
|
case E_DAV_NO_IIS_EXECUTE_ACCESS:
|
|
case E_DAV_NO_ACL_ACCESS:
|
|
case E_DAV_PROTECTED_ENTITY:
|
|
case E_DAV_CONFLICTING_PATHS:
|
|
case E_DAV_FORBIDDEN:
|
|
case STG_E_DISKISWRITEPROTECTED:
|
|
case STG_E_LOCKVIOLATION:
|
|
case E_DAV_STORE_MAIL_SUBMISSION:
|
|
case E_DAV_STORE_REVISION_ID_FAILURE:
|
|
case E_DAV_MAIL_SUBMISSION_FORBIDDEN:
|
|
case E_DAV_MKCOL_REVISION_ID_FORBIDDEN:
|
|
case E_ABORT:
|
|
|
|
hsc = HSC_FORBIDDEN;
|
|
break;
|
|
|
|
case E_DAV_SEARCH_COULD_NOT_RESTRICT:
|
|
case E_DAV_UNSUPPORTED_SQL:
|
|
case MIME_E_NO_DATA: // empty 822 message, returned from IMail. It is nothing wrong with
|
|
// request that attempts to create empty message. Semantics are wrong.
|
|
|
|
hsc = HSC_UNPROCESSABLE;
|
|
break;
|
|
|
|
// LOCKING errors when a resource is already locked.
|
|
//
|
|
case E_DAV_LOCKED:
|
|
case STG_E_SHAREVIOLATION:
|
|
|
|
hsc = HSC_LOCKED;
|
|
#ifdef DBG
|
|
{
|
|
static LONG s_lAssert = -1;
|
|
if (s_lAssert == -1)
|
|
{
|
|
LONG lAss = GetPrivateProfileIntA ("general",
|
|
"Assert_423s",
|
|
0,
|
|
gc_szDbgIni);
|
|
InterlockedCompareExchange (&s_lAssert, lAss, -1);
|
|
}
|
|
if (s_lAssert != 0)
|
|
TrapSz ("HRESULT maps to 423");
|
|
}
|
|
#endif // DBG
|
|
break;
|
|
|
|
// Bad Requests
|
|
//
|
|
case E_DAV_EMPTY_FIND_REQUEST:
|
|
case E_DAV_EMPTY_PATCH_REQUEST:
|
|
case E_DAV_INCOMPLETE_SQL_STATEMENT:
|
|
case E_DAV_INVALID_HEADER:
|
|
case E_DAV_LOCK_NOT_FOUND:
|
|
case E_DAV_MALFORMED_PATH:
|
|
case E_DAV_METHOD_FAILURE_STAR_URL:
|
|
case E_DAV_MISSING_CONTENT_TYPE:
|
|
case E_DAV_NAMED_PROPERTY_ERROR:
|
|
case E_DAV_NO_DESTINATION:
|
|
case E_DAV_NO_QUERY:
|
|
case E_DAV_PATCH_TYPE_MISMATCH:
|
|
case E_DAV_READ_REQUEST_TIMEOUT:
|
|
case E_DAV_SEARCH_SCOPE_ERROR:
|
|
case E_DAV_UNEXPECTED_TYPE:
|
|
case E_DAV_XML_PARSE_ERROR:
|
|
case E_DAV_XML_BAD_DATA:
|
|
case E_INVALIDARG:
|
|
case MK_E_NOSTORAGE:
|
|
case MK_E_SYNTAX:
|
|
case STG_E_INVALIDPARAMETER:
|
|
|
|
hsc = HSC_BAD_REQUEST;
|
|
break;
|
|
|
|
// Length required
|
|
//
|
|
case E_DAV_MISSING_LENGTH:
|
|
|
|
hsc = HSC_LENGTH_REQUIRED;
|
|
break;
|
|
|
|
// Unknown content-types
|
|
//
|
|
case E_DAV_UNKNOWN_CONTENT:
|
|
|
|
hsc = HSC_UNSUPPORTED_MEDIA_TYPE;
|
|
break;
|
|
|
|
// Content errors
|
|
//
|
|
case E_DAV_BASE64_ENCODING_ERROR:
|
|
case E_DAV_RESPONSE_TYPE_UNACCEPTED:
|
|
|
|
hsc = HSC_NOT_ACCEPTABLE;
|
|
break;
|
|
|
|
// Bad Gateway
|
|
//
|
|
case E_DAV_BAD_DESTINATION:
|
|
case W_DAV_SPANS_VIRTUAL_ROOTS:
|
|
case E_DAV_STAR_SCRIPTMAPING_MISMATCH:
|
|
|
|
hsc = HSC_BAD_GATEWAY;
|
|
break;
|
|
|
|
// Methods not allowed
|
|
//
|
|
case E_DAV_COLLECTION_EXISTS:
|
|
case E_DAV_VOLUME_NOT_NTFS:
|
|
case E_NOINTERFACE:
|
|
case E_DAV_STORE_ALREADY_EXISTS:
|
|
case E_DAV_MKCOL_OBJECT_ALREADY_EXISTS:
|
|
|
|
hsc = HSC_METHOD_NOT_ALLOWED;
|
|
break;
|
|
|
|
// Conflict
|
|
//
|
|
case E_DAV_NONEXISTING_PARENT:
|
|
case STG_E_FILEALREADYEXISTS:
|
|
case E_DAV_CONFLICT:
|
|
case E_DAV_NATIVE_CONTENT_NOT_MAPI:
|
|
|
|
hsc = HSC_CONFLICT;
|
|
break;
|
|
|
|
// Unsatisfiable byte range requests
|
|
//
|
|
case E_DAV_RANGE_NOT_SATISFIABLE:
|
|
|
|
hsc = HSC_RANGE_NOT_SATISFIABLE;
|
|
break;
|
|
|
|
// 424 Method Failure
|
|
//
|
|
case E_DAV_STORE_COMMIT_GOP:
|
|
|
|
hsc = HSC_METHOD_FAILURE;
|
|
break;
|
|
|
|
case E_DAV_IPC_CONNECT_FAILED:
|
|
case E_DAV_EXPROX_CONNECT_FAILED:
|
|
case E_DAV_MDB_DOWN:
|
|
case E_DAV_STORE_MDB_UNAVAILABLE:
|
|
|
|
hsc = HSC_SERVICE_UNAVAILABLE;
|
|
break;
|
|
|
|
case E_DAV_RSRC_INSUFFICIENT_BUFFER:
|
|
|
|
hsc = HSC_INSUFFICIENT_SPACE;
|
|
break;
|
|
|
|
default:
|
|
case E_DAV_METHOD_FORWARDED:
|
|
case E_DAV_GET_DB_HELPER_FAILURE:
|
|
case E_DAV_NOTIF_POLL_FAILURE:
|
|
|
|
hsc = HSC_INTERNAL_SERVER_ERROR;
|
|
#ifdef DBG
|
|
{
|
|
static LONG s_lAssert = -1;
|
|
if (s_lAssert == -1)
|
|
{
|
|
LONG lAss = GetPrivateProfileIntA ("general",
|
|
"Assert_500s",
|
|
0,
|
|
gc_szDbgIni);
|
|
InterlockedCompareExchange (&s_lAssert, lAss, -1);
|
|
}
|
|
if (s_lAssert != 0)
|
|
TrapSz ("HRESULT maps to 500");
|
|
}
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
|
|
DebugTrace ("DAV: HRESULT error (0x%08x) mapped to hsc (%ld)\n", hr, hsc);
|
|
return hsc;
|
|
}
|
|
|
|
BOOL
|
|
FWchFromHex (LPCWSTR pwsz, WCHAR * pwch)
|
|
{
|
|
INT iwch;
|
|
WCHAR wch;
|
|
WCHAR wchX = 0;
|
|
|
|
Assert (pwch);
|
|
for (iwch = 0; iwch < 2; iwch++)
|
|
{
|
|
// Shift whats there up a diget
|
|
//
|
|
wchX = (WCHAR)(wchX << 4);
|
|
|
|
// Parse the next char
|
|
//
|
|
wch = pwsz[iwch];
|
|
|
|
// Make sure we don't exceed the sequence.
|
|
//
|
|
if (!wch)
|
|
return FALSE;
|
|
|
|
#pragma warning(disable:4244)
|
|
if ((wch >= L'0') && (wch <= L'9'))
|
|
wchX += (WCHAR)(wch - L'0');
|
|
else if ((wch >= L'A') && (wch <= L'F'))
|
|
wchX += (WCHAR)(wch - L'A' + 10);
|
|
else if ((wch >= L'a') && (wch <= L'f'))
|
|
wchX += (WCHAR)(wch - L'a' + 10);
|
|
else
|
|
return FALSE; // bad sequence
|
|
#pragma warning(default:4244)
|
|
}
|
|
|
|
*pwch = wchX;
|
|
return TRUE;
|
|
}
|
|
|
|
// Byte Range Checking and Header Emission -----------------------------------------------------------
|
|
//
|
|
/*
|
|
* ScProcessByteRanges()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Helper function used to process byte ranges and emit the header
|
|
* information for GET responses.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* pmu [in] pointer to the method util obj
|
|
* pwszPath [in] path of request entity
|
|
* dwSizeLow [in] size of get request entity (low byte)
|
|
* dwSizeHigh [in] size of get request entity (high byte)
|
|
* pByteRange [out] given a pointer to a RangeIter obj, the
|
|
* function fills in byte range information
|
|
* if the request contains a Range header
|
|
* pszEtagOverride [in, opt.] pointer to an Etag, overrides the Etag
|
|
* generated from the last modification
|
|
* time
|
|
* pftOverride [in, opt.] pointer to a FILETIME structure, overrides
|
|
* call to FGetLastModTime
|
|
*
|
|
* Returns: SCODE
|
|
*
|
|
* S_OK indicates success(ordinary response).
|
|
* W_DAV_PARTIAL_CONTENT (206) indicates success(byte range response).
|
|
* E_DAV_RANGE_NOT_SATISFIABLE (416) indicates all of requested
|
|
* byte ranges were beyond the size of the entity.
|
|
*/
|
|
SCODE
|
|
ScProcessByteRanges (IMethUtil * pmu,
|
|
LPCWSTR pwszPath,
|
|
DWORD dwSizeLow,
|
|
DWORD dwSizeHigh,
|
|
CRangeParser * pByteRange)
|
|
{
|
|
FILETIME ft;
|
|
WCHAR pwszEtag[CCH_ETAG];
|
|
|
|
// Check the validity of the inputs
|
|
//
|
|
Assert (pmu);
|
|
Assert (pwszPath);
|
|
Assert (pByteRange);
|
|
|
|
SideAssert(FGetLastModTime (pmu, pwszPath, &ft));
|
|
SideAssert(FETagFromFiletime (&ft, pwszEtag, pmu->GetEcb()));
|
|
|
|
return ScProcessByteRangesFromEtagAndTime (pmu,
|
|
dwSizeLow,
|
|
dwSizeHigh,
|
|
pByteRange,
|
|
pwszEtag,
|
|
&ft);
|
|
}
|
|
|
|
SCODE
|
|
ScProcessByteRangesFromEtagAndTime (IMethUtil * pmu,
|
|
DWORD dwSizeLow,
|
|
DWORD dwSizeHigh,
|
|
CRangeParser *pByteRange,
|
|
LPCWSTR pwszEtag,
|
|
FILETIME * pft)
|
|
{
|
|
SCODE sc = S_OK;
|
|
LPCWSTR pwszRangeHeader;
|
|
WCHAR rgwchBuf[128] = L"";
|
|
|
|
// Check the validity of the inputs
|
|
//
|
|
Assert (pmu);
|
|
Assert (pByteRange);
|
|
Assert (pwszEtag);
|
|
Assert (pft);
|
|
|
|
// Check to see if we have a Range header and the If-Range condition( if
|
|
// there is one) is satisfied. Do not apply URL conversion rules while
|
|
// fetching the header.
|
|
//
|
|
pwszRangeHeader = pmu->LpwszGetRequestHeader (gc_szRange, FALSE);
|
|
if ( pwszRangeHeader && !FAILED (ScCheckIfRangeHeaderFromEtag (pmu,
|
|
pft,
|
|
pwszEtag)) )
|
|
{
|
|
// Limit the maximum size of range headers we will process
|
|
//
|
|
if ( MAX_PATH < wcslen(pwszRangeHeader) )
|
|
{
|
|
sc = E_DAV_RANGE_NOT_SATISFIABLE;
|
|
goto ret;
|
|
}
|
|
|
|
// We have no means of handling byte ranges for files larger than 4GB,
|
|
// due to limitations of _HSE_TF_INFO that takes DWORD values for sizes
|
|
// and offsets. So if we are geting byterange request on that large file
|
|
// just fail out - with some error that maps to 405 Method Not Allowed
|
|
//
|
|
if (dwSizeHigh)
|
|
{
|
|
sc = E_NOINTERFACE;
|
|
goto ret;
|
|
}
|
|
|
|
// OK, we have a byte range. Parse the byte ranges from the header.
|
|
// The function takes the size of the request entity to make
|
|
// sure the byte ranges are consistent with the entity size (no byte
|
|
// ranges beyond the size).
|
|
//
|
|
sc = pByteRange->ScParseByteRangeHdr(pwszRangeHeader, dwSizeLow);
|
|
|
|
switch (sc)
|
|
{
|
|
case W_DAV_PARTIAL_CONTENT:
|
|
|
|
// We have a byte range (206 partial content). Send back
|
|
// this return code.
|
|
//
|
|
break;
|
|
|
|
case E_DAV_RANGE_NOT_SATISFIABLE:
|
|
|
|
// We don't have any satisfiable ranges (all our ranges had a
|
|
// start byte greater than the size of the file or they
|
|
// requested a zero-sized range). Our behaviour here depends on
|
|
// the presence of the If-Range header.
|
|
// If we have an If-Range header, return the default response
|
|
// S_OK (the entire file). If we don't have one,
|
|
// we need to return a 416 (Requested Range Not Satisfiable).
|
|
// Would look like it is more performant to ask for the skinny
|
|
// version here, but at this moment wide header value is already
|
|
// cached so it makes no difference. And do not apply URL conversion
|
|
// rules to the header.
|
|
//
|
|
if (!pmu->LpwszGetRequestHeader(gc_szIf_Range, FALSE))
|
|
{
|
|
// NO If-Range header found.
|
|
// Set the Content-Range header to say "bytes *"
|
|
// (meaning the whole file was sent).
|
|
//
|
|
wsprintfW(rgwchBuf, L"%ls */%d", gc_wszBytes, dwSizeLow);
|
|
pmu->SetResponseHeader(gc_szContent_Range, rgwchBuf);
|
|
|
|
// Send back this return code (E_DAV_RANGE_NOT_SATISFIABLE).
|
|
//
|
|
}
|
|
else
|
|
{
|
|
// We DO have an If-Range header.
|
|
// Return 200 OK, and send the whole file.
|
|
//
|
|
sc = S_OK;
|
|
}
|
|
break;
|
|
|
|
case E_INVALIDARG:
|
|
|
|
// If the parsing function returned S_FALSE we have a syntax
|
|
// error, so we ignore the Range header and send the entire
|
|
// file/stream.
|
|
// Reset our return code to S_OK.
|
|
//
|
|
sc = S_OK;
|
|
break;
|
|
|
|
default:
|
|
|
|
// Unrecognizable error. We should never see anything but
|
|
// the three values in the case statement. Assert (TrapSz),
|
|
// and return this sc.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Either we didn't have a Range header or the If-Range condition
|
|
// was false. Its an ordinary GET and we need to send the entire
|
|
// file. Our response (S_OK) is already set by default.
|
|
//
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
// Generating a boundary for multipartpart mime like responses------------------
|
|
//
|
|
/*
|
|
* GenerateBoundary()
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Helper function used to generate a separator boundary for multipart
|
|
* mime like responses
|
|
*
|
|
* Parameters:
|
|
*
|
|
* rgwchBoundary [out] boundary for multipart responses
|
|
* cch [in] size of the rgwchBoundary parameter
|
|
*/
|
|
void
|
|
GenerateBoundary(LPWSTR rgwchBoundary, UINT cch)
|
|
{
|
|
UINT cchMin;
|
|
UINT iIter;
|
|
|
|
// Assert that we've been given a buffer of at least size 2 (a minimum
|
|
// null terminated boundary of one byte).
|
|
//
|
|
Assert (cch > 1);
|
|
Assert (rgwchBoundary);
|
|
|
|
// The boundary size is the smaller of the size passed in to us or the default
|
|
//
|
|
cchMin = min(gc_ulDefaultBoundarySz, cch - 1);
|
|
|
|
// We are going to randomly use characters from the boundary alphabet.
|
|
// The rand() function is seeded by the current time
|
|
//
|
|
srand(GetTickCount());
|
|
|
|
// Now to generate the actual boundary
|
|
//
|
|
for (iIter = 0; iIter < cchMin; iIter++)
|
|
{
|
|
rgwchBoundary[iIter] = gc_wszBoundaryAlphabet[ rand() % gc_ulAlphabetSz ];
|
|
}
|
|
rgwchBoundary[cchMin] = L'\0';
|
|
}
|
|
|
|
|
|
// Non-Async IO on Top of Overlapped Files -----------------------------------
|
|
//
|
|
BOOL
|
|
ReadFromOverlapped (HANDLE hf,
|
|
LPVOID pvBuf,
|
|
ULONG cbToRead,
|
|
ULONG * pcbRead,
|
|
OVERLAPPED * povl)
|
|
{
|
|
Assert (povl);
|
|
|
|
// Start reading
|
|
//
|
|
if ( !ReadFile( hf, pvBuf, cbToRead, pcbRead, povl ) )
|
|
{
|
|
if ( GetLastError() == ERROR_IO_PENDING )
|
|
{
|
|
if ( !GetOverlappedResult( hf, povl, pcbRead, TRUE ) )
|
|
{
|
|
if ( GetLastError() != ERROR_HANDLE_EOF )
|
|
{
|
|
DebugTrace( "ReadFromOverlapped(): "
|
|
"GetOverlappedResult() failed (%d)\n",
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if ( GetLastError() != ERROR_HANDLE_EOF )
|
|
{
|
|
DebugTrace( "ReadFromOverlapped(): "
|
|
"ReadFile() failed (%d)\n",
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WriteToOverlapped (HANDLE hf,
|
|
const void * pvBuf,
|
|
ULONG cbToWrite,
|
|
ULONG * pcbWritten,
|
|
OVERLAPPED * povl)
|
|
{
|
|
Assert (povl);
|
|
|
|
// Start writting
|
|
//
|
|
if ( !WriteFile( hf, pvBuf, cbToWrite, pcbWritten, povl ) )
|
|
{
|
|
if ( GetLastError() == ERROR_IO_PENDING )
|
|
{
|
|
if ( !GetOverlappedResult( hf, povl, pcbWritten, TRUE ) )
|
|
{
|
|
if ( GetLastError() != ERROR_HANDLE_EOF )
|
|
{
|
|
DebugTrace( "WriteToOverlapped(): "
|
|
"GetOverlappedResult() failed (%d)\n",
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if ( GetLastError() != ERROR_HANDLE_EOF )
|
|
{
|
|
DebugTrace( "WriteToOverlapped(): "
|
|
"WriteFile() failed (%d)\n",
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|