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.
 
 
 
 
 
 

948 lines
31 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
cachelogic.cxx
Abstract:
This file contains the implementation of the HTTPCACHE request object public interface and a few
miscellaneous global variables and classes
Author:
Revision History:
--*/
#include <wininetp.h>
#define __CACHE_INCLUDE__
#include "..\urlcache\cache.hxx"
#include "..\urlcache\hndlmgr.hxx"
#include "cachelogic.hxx"
#include "internalapi.hxx"
/////////////////////////////////////////////////////////////////////////
//
// Global variables specific to HttpCache
//
//
/////////////////////////////////////////////////////////////////////////
#define CACHE_ENTRY_INFOEX_SIZE 1024 * 5 // we need this much to prevent buffer overrun
/////////////////////////////////////////////////////////////////////////
//
// HTTPCACHE_REQUEST Constructors and destructors
//
//
/////////////////////////////////////////////////////////////////////////
HTTPCACHE_REQUEST::HTTPCACHE_REQUEST(HINTERNET hRequest)
{
_hRequest = hRequest;
InternalQueryOptionA(_hRequest, WINHTTP_OPTION_CACHE_FLAGS, &_dwCacheFlags);
InternalQueryOptionA(_hRequest, WINHTTP_OPTION_REQUEST_FLAGS, &_dwRequestFlags);
_lpszFileName = NULL;
_lpszFileExtension = NULL;
_lpszCacheWriteLocalFilename = NULL;
_fIsPartialCache = FALSE;
_fCacheWriteInProgress = FALSE;
_fDeleteWriteFile = FALSE;
_fCacheReadInProgress = FALSE;
_fLazyUpdate = FALSE;
_fHasExpiry = FALSE;
_fHasLastModTime = FALSE;
_fHasPostCheck = FALSE;
_fMustRevalidate = FALSE;
_pCacheEntryInfo = (CACHE_ENTRY_INFOEX *) ALLOCATE_FIXED_MEMORY(CACHE_ENTRY_INFOEX_SIZE);
// Set the URL for this object
// if you drill down to the defn of GetURL(), you'll see that it's returning
// _CacheUrlName from the class. This variable has NOTHING to do with the
// caching layer. They just have a misleading variable name there!
DWORD dwSize = INTERNET_MAX_URL_LENGTH;
InternetQueryOptionA(_hRequest, WINHTTP_OPTION_URL, _szUrl, &dwSize);
// This is where _nextState is set
_nextState = CHECK_IF_IN_CACHE;
if (_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_READ)
_nextState = PREPARE_DOWNLOAD_FROM_INET;
}
HTTPCACHE_REQUEST::~HTTPCACHE_REQUEST()
{
// If there's a file that we're using to write to the cache,
// but fails to commit to the cache index, then we entirely
// get rid of the file here
if (_fDeleteWriteFile == TRUE)
{
CloseHandle(_hCacheWriteFile);
DeleteFile(_lpszCacheWriteLocalFilename);
}
if (_lpszFileName)
FREE_MEMORY(_lpszFileName);
if (_lpszFileExtension)
FREE_MEMORY(_lpszFileExtension);
if (_lpszCacheWriteLocalFilename)
FREE_MEMORY(_lpszCacheWriteLocalFilename);
if (_pCacheEntryInfo)
FREE_MEMORY(_pCacheEntryInfo);
}
/////////////////////////////////////////////////////////////////////////
//
// HTTPCACHE_REQUEST Public interface:
// SendRequest
// ReceiveResponse
// QueryDataAvailable
// ReadData
//
// Essentially these public interfaces keep track of a state variable (_nextState)
// and manipulate the states based on the results returned from the private
// functions.
//
// It is structured so that ONLY the public interface should manipulate the state
// variables
//
// The lpszHeader and lpOptional parameters are being ignored
// by the cache, unless the cache fails (cache lookup fails or IMS request
// returns 200 OK), in which case we call the net SendRequest with
// the passed-in parameters
PUBLIC BOOL HTTPCACHE_REQUEST::SendRequest(
IN LPCWSTR lpszHeaders,
IN DWORD dwHeadersLength,
IN LPVOID lpOptional,
IN DWORD dwOptionalLength
)
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"HTTPCACHE_REQUEST::SendRequest",
NULL
));
BOOL fResult = FALSE;
BOOL fFinish = FALSE;
do
{
switch(_nextState)
{
case CHECK_IF_IN_CACHE:
DEBUG_PRINT(CACHE,
INFO,
("CHECK_IF_IN_CACHE state in HTTPCACHE_REQUEST::SendRequest\n"
));
fResult = OpenCacheReadStream();
if (fResult)
{
_fCacheReadInProgress = TRUE;
DEBUG_PRINT(CACHE, INFO, ("%s coming from the cache\n", GetUrl()));
if (IsPartialCacheEntry())
_nextState = ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER;
else
_nextState = ADD_NORMAL_CONTENT_IMS_HEADER;
}
else
{
_fCacheReadInProgress = FALSE;
_nextState = PREPARE_DOWNLOAD_FROM_INET;
}
break;
case ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER:
DEBUG_PRINT(CACHE,
INFO,
("ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER state in HTTPCACHE_REQUEST::SendRequest\n"
));
// we don't need to check for expiry if it's a partial content
LockPartialCacheEntry();
AddRangeRequestHeaders();
_nextState = SEND_REQUEST;
break;
case ADD_NORMAL_CONTENT_IMS_HEADER:
DEBUG_PRINT(CACHE,
INFO,
("ADD_NORMAL_CONTENT_IMS_HEADER state in HTTPCACHE_REQUEST::SendRequest\n"
));
if (_dwCacheFlags & CACHE_FLAG_ALWAYS_RESYNCHRONIZE ||
IsExpired() == TRUE)
{
AddIfModifiedSinceHeaders();
_nextState = SEND_REQUEST;
}
else
{
_nextState = PREPARE_READ_FROM_CACHE;
}
break;
case SEND_REQUEST:
DEBUG_PRINT(CACHE,
INFO,
("SEND_REQUEST state in HTTPCACHE_REQUEST::SendRequest\n"
));
DWORD dwStatusCode;
DWORD dwAction;
fResult = TransmitRequest(&dwStatusCode);
if (fResult)
{
CheckResponseAfterIMS(dwStatusCode);
// If I get a 304 back, then it means it's not modified, and I can grab it
// from the cache
if (dwStatusCode == HTTP_STATUS_NOT_MODIFIED)
_nextState = PREPARE_READ_FROM_CACHE;
// If I get back a 200 then I can start reading data from the net;
// but I don't have to do a SendRequets again caz it's already done in
// the Transmit request call
else if (dwStatusCode == HTTP_STATUS_OK)
_nextState = PREPARE_READ_FROM_INET_AFTER_200_RESPONSE;
// If we get a 206, then do the partial read
else if (dwStatusCode == HTTP_STATUS_PARTIAL_CONTENT &&
_pCacheEntryInfo->CacheEntryType & SPARSE_CACHE_ENTRY)
_nextState = PARTIAL_READ;
// Otherwise, I'll have to clear all the headers, reset the request object
// and redo the full SendRequest again (CACHE_SEND_ERROR state)
}
else
{
_nextState = CACHE_SEND_ERROR;
}
break;
case PARTIAL_READ:
DEBUG_PRINT(CACHE,
INFO,
("PARTIAL_READ state in HTTPCACHE_REQUEST::SendRequest\n"
));
fResult = FakePartialCacheResponseHeaders();
_nextState = PREPARE_READ_FROM_CACHE;
fFinish = TRUE;
break;
case PREPARE_READ_FROM_CACHE:
DEBUG_PRINT(CACHE,
INFO,
("PREPARE_READ_FROM_CACHE state in HTTPCACHE_REQUEST::SendRequest\n"
));
// If the request can be satisifed by the cache, we complete the SendRequest
// transaction by recovering the response headers from the cache so that the
// user is not aware that the content is coming from the cache
FakeCacheResponseHeaders();
fResult = TRUE;
fFinish = TRUE;
break;
case PREPARE_READ_FROM_INET_AFTER_200_RESPONSE:
DEBUG_PRINT(CACHE,
INFO,
("PREPARE_READ_FROM_INET_AFTER_200_RESPONSE state in HTTPCACHE_REQUEST::SendRequest\n"
));
if (IsPartialCacheEntry())
DeletePartialCacheFile();
CloseCacheReadStream();
_nextState = PREPARE_DOWNLOAD_FROM_INET;
fFinish = TRUE;
break;
// somehow adding the request header fails, so we fall back to downloading from inet
case CACHE_SEND_ERROR:
DEBUG_PRINT(CACHE,
INFO,
("CACHE_SEND_ERROR state in HTTPCACHE_REQUEST::SendRequest\n"
));
if (IsPartialCacheEntry())
DeletePartialCacheFile();
CloseCacheReadStream();
// Reset the request handle object (clear any previous request headers, etc...)
// so it can be used to send new requests again
InternalReuseHTTP_Request_Handle_Object(_hRequest);
_nextState = PREPARE_DOWNLOAD_FROM_INET;
break;
case PREPARE_DOWNLOAD_FROM_INET:
DEBUG_PRINT(CACHE,
INFO,
("PREPARE_DOWNLOAD_FROM_INET state in HTTPCACHE_REQUEST::SendRequest\n"
));
fResult = WinHttpSendRequest(
_hRequest,
lpszHeaders,
dwHeadersLength,
lpOptional,
dwOptionalLength,
0,
0);
fFinish = TRUE;
break;
default:
fResult = FALSE;
fFinish = TRUE;
break;
}
} while (!fFinish);
DEBUG_LEAVE(fResult);
return fResult;
}
PUBLIC BOOL HTTPCACHE_REQUEST::ReceiveResponse(LPVOID lpBuffersOut)
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"HTTPCACHE_REQUEST::ReceiveResponse",
NULL
));
BOOL fResult = FALSE;
switch (_nextState)
{
case PREPARE_READ_FROM_CACHE:
_nextState = BEGIN_CACHE_READ;
fResult = TRUE;
break;
case PREPARE_DOWNLOAD_FROM_INET:
_nextState = BEGIN_DOWNLOAD_FROM_INET;
fResult = WinHttpReceiveResponse(_hRequest, lpBuffersOut);
break;
default:
fResult = FALSE;
break;
}
DEBUG_LEAVE(fResult);
return fResult;
}
PUBLIC BOOL HTTPCACHE_REQUEST::QueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable)
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"HTTPCACHE_REQUEST::QueryDataAvailable",
NULL
));
BOOL fResult = FALSE;
switch (_nextState)
{
case END_CACHE_READ:
case END_READ_DATA:
*lpdwNumberOfBytesAvailable = 0;
fResult = TRUE;
break;
case BEGIN_CACHE_READ:
// We assume that the cached file size is less than 4 GB (= 2^32). If the cached file is
// really that big we might as well not cache it
*lpdwNumberOfBytesAvailable = _pCacheEntryInfo->dwSizeLow;
fResult = TRUE;
break;
case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
case DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE:
case BEGIN_DOWNLOAD_FROM_INET:
fResult = WinHttpQueryDataAvailable(_hRequest, lpdwNumberOfBytesAvailable);
break;
default:
break;
}
DEBUG_LEAVE(fResult);
return fResult;
}
PUBLIC BOOL HTTPCACHE_REQUEST::ReadData(LPVOID lpBuffer,
DWORD dwNumberOfBytesToRead,
LPDWORD lpdwNumberOfBytesRead)
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"HTTPCACHE_REQUEST::ReadData",
NULL
));
BOOL fFinish = FALSE;
BOOL fResult = FALSE;
do
{
switch (_nextState)
{
case BEGIN_CACHE_READ:
DEBUG_PRINT(CACHE,
INFO,
("BEGIN_CACHE_READ state in HTTPCACHE_REQUEST::ReadData\n"
));
fResult = ReadDataFromCache(lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead);
if (fResult == FALSE)
_nextState = BEGIN_DOWNLOAD_FROM_INET;
else if (*lpdwNumberOfBytesRead == 0)
{
_nextState = END_CACHE_READ;
}
else
{
_nextState = BEGIN_CACHE_READ; // just to be more clear
fFinish = TRUE;
}
break;
case BEGIN_DOWNLOAD_FROM_INET:
DEBUG_PRINT(CACHE,
INFO,
("BEGIN_DOWNLOAD_FROM_INET state in HTTPCACHE_REQUEST::ReadData\n"
));
if (_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_WRITE)
_nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
else
_nextState = PREP_FOR_CACHE_WRITE;
if (GetScheme() == INTERNET_SCHEME_HTTPS &&
_dwCacheFlags & CACHE_FLAG_DISABLE_SSL_CACHING)
{
_nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE; // SetPerUserItem(TRUE) ??;
}
break;
case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
DEBUG_PRINT(CACHE,
INFO,
("DOWNLOAD_FROM_INET_WITH_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
));
fResult = WinHttpReadData(_hRequest,
lpBuffer,
dwNumberOfBytesToRead,
lpdwNumberOfBytesRead);
if (fResult)
_nextState = WRITE_TO_CACHE_ENTRY;
else
{
_nextState = COMMIT_PARTIAL_CACHE_ENTRY;
fFinish = TRUE;
}
break;
case DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE:
DEBUG_PRINT(CACHE,
INFO,
("DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
));
fResult = WinHttpReadData(_hRequest,
lpBuffer,
dwNumberOfBytesToRead,
lpdwNumberOfBytesRead);
fFinish = TRUE;
break;
case PREP_FOR_CACHE_WRITE:
DEBUG_PRINT(CACHE,
INFO,
("PREP_FOR_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
));
INET_ASSERT((_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_WRITE) == FALSE);
BOOL fNoCache;
if (FCanWriteHTTP1_1ResponseToCache(&fNoCache))
{
// fNoCache indicates that a cache-control: no-store
// or cache-control: no-cache is present, so we need
// to make sure that the cache does not keep any
// previous copies of the file as well
if (fNoCache)
DeleteUrlCacheEntryA(GetUrl());
SetFilenameAndExtForCacheWrite();
_RealCacheFileSize = 0;
if (CreateCacheWriteFile())
{
_fCacheWriteInProgress = TRUE;
_nextState = DOWNLOAD_FROM_INET_WITH_CACHE_WRITE;
}
else
{
_nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
}
}
else
{
_nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
}
break;
case WRITE_TO_CACHE_ENTRY:
DEBUG_PRINT(CACHE,
INFO,
("WRITE_TO_CACHE_ENTRY state in HTTPCACHE_REQUEST::ReadData\n"
));
if (*lpdwNumberOfBytesRead == 0)
_nextState = END_CACHE_WRITE;
else
{
if ((fResult = WriteToCacheFile((LPBYTE) lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead)) == TRUE)
{
_nextState = DOWNLOAD_FROM_INET_WITH_CACHE_WRITE;
fFinish = TRUE;
}
else
{
_nextState = COMMIT_PARTIAL_CACHE_ENTRY;
}
}
break;
case COMMIT_PARTIAL_CACHE_ENTRY:
DEBUG_PRINT(CACHE,
INFO,
("COMMIT_PARTIAL_CACHE_ENTRY state in HTTPCACHE_REQUEST::ReadData\n"
));
if (!CommitCacheFileEntry(FALSE))
// The partial entry cannot be committed to the cache, so let's
// delete it in the destructor so it'll not become a stale entry
_fDeleteWriteFile = TRUE;
else
_fDeleteWriteFile = FALSE;
_fCacheWriteInProgress = FALSE;
_nextState = END_READ_DATA;
fResult = TRUE;
fFinish = TRUE;
break;
case END_CACHE_WRITE:
DEBUG_PRINT(CACHE,
INFO,
("END_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
));
if (!CommitCacheFileEntry(TRUE))
// The file cannot be committed to the cache, so let's
// delete it in the destructor so it'll not become a stale entry
_fDeleteWriteFile = TRUE;
else
_fDeleteWriteFile = FALSE;
_fCacheWriteInProgress = FALSE;
_nextState = END_READ_DATA;
fResult = TRUE;
fFinish = TRUE;
break;
case END_CACHE_READ:
DEBUG_PRINT(CACHE,
INFO,
("END_CACHE_READ state in HTTPCACHE_REQUEST::ReadData\n"
));
// Close the cache read file handle
CloseCacheReadStream();
_fCacheReadInProgress = FALSE;
if (_fIsPartialCache == TRUE)
{
_nextState = BEGIN_DOWNLOAD_FROM_INET;
_fIsPartialCache = FALSE;
}
else
_nextState = END_READ_DATA;
fResult = TRUE;
fFinish = TRUE;
break;
case END_READ_DATA:
DEBUG_PRINT(CACHE,
INFO,
("END_READ_DATA state\n in HTTPCACHE_REQUEST::ReadData"
));
lpBuffer = (LPVOID)'\0';
*lpdwNumberOfBytesRead = 0;
fResult = TRUE;
fFinish = TRUE;
break;
// If we ever got here, we REALLY SHOULD PANIC!! Fix this later
default:
DEBUG_PRINT(CACHE,
INFO,
("HTTPCACHE_REQUEST::ReadData FSM is in bogus state\n"
));
fResult = WinHttpReadData(_hRequest, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead);
fFinish = TRUE;
break;
}
} while (!fFinish);
// Before you exit this loop, make sure you set fResult to the intended value
DEBUG_LEAVE(fResult);
return fResult;
}
PUBLIC BOOL HTTPCACHE_REQUEST::CloseRequestHandle()
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"HTTPCACHE_REQUEST::CloseHandle",
NULL
));
BOOL fResult = FALSE;
switch (_nextState)
{
case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
// The idea here is that if the user calls WinHttpCloseHandle
// before the full content has been downloaded, then we
// should try to commit it as a partial entry for later retrieval
if (!CommitCacheFileEntry(FALSE))
// The file cannot be committed to the cache, so let's
// delete it in the destructor so it'll not become a stale entry
_fDeleteWriteFile = TRUE;
else
_fDeleteWriteFile = FALSE;
_fCacheWriteInProgress = FALSE;
// intentional fall through;
case END_READ_DATA:
default:
fResult = WinHttpCloseHandle(_hRequest);
break;
}
DEBUG_LEAVE(fResult);
return fResult;
}
////////////////////////////////////////////////////////////////////////
//
// Miscelleneous utility functions
//
//
//
/***
*char *StrTokEx(pstring, control) - tokenize string with delimiter in control
*
*Purpose:
* StrTokEx considers the string to consist of a sequence of zero or more
* text tokens separated by spans of one or more control chars. the first
* call, with string specified, returns a pointer to the first char of the
* first token, and will write a null char into pstring immediately
* following the returned token. when no tokens remain
* in pstring a NULL pointer is returned. remember the control chars with a
* bit map, one bit per ascii char. the null char is always a control char.
*
*Entry:
* char **pstring - ptr to ptr to string to tokenize
* char *control - string of characters to use as delimiters
*
*Exit:
* returns pointer to first token in string,
* returns NULL when no more tokens remain.
* pstring points to the beginning of the next token.
*
*WARNING!!!
* upon exit, the first delimiter in the input string will be replaced with '\0'
*
*******************************************************************************/
char * StrTokExA (char ** pstring, const char * control)
{
unsigned char *str;
const unsigned char *ctrl = (const unsigned char *)control;
unsigned char map[32];
int count;
char *tokenstr;
if(*pstring == NULL)
return NULL;
/* Clear control map */
for (count = 0; count < 32; count++)
map[count] = 0;
/* Set bits in delimiter table */
do
{
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
} while (*ctrl++);
/* Initialize str. */
str = (unsigned char *)*pstring;
/* Find beginning of token (skip over leading delimiters). Note that
* there is no token if this loop sets str to point to the terminal
* null (*str == '\0') */
while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
str++;
tokenstr = (char *)str;
/* Find the end of the token. If it is not the end of the string,
* put a null there. */
for ( ; *str ; str++ )
{
if ( map[*str >> 3] & (1 << (*str & 7)) )
{
*str++ = '\0';
break;
}
}
/* string now points to beginning of next token */
*pstring = (char *)str;
/* Determine if a token has been found. */
if ( tokenstr == (char *)str )
return NULL;
else
return tokenstr;
}
#define EXE_EXTENSION TEXT(".exe")
#define DLL_EXTENSION TEXT(".dll")
#define CGI_EXTENSION TEXT(".cgi")
LPSTR GetFileExtensionFromUrl(
IN LPSTR lpszUrl,
IN OUT LPDWORD lpdwLength)
/*++
Routine Description:
This routine returns a possible file extension from a URL
It does this by walking back from the end till the first dot.
Arguments:
lpszUrl Url to derive the extension from
lpdwLength max length of the extension expected
Returns:
NULL if no dot within the passed in length or a forward slash or a
backward slash encountered before the dot. Otherwise returns a pointer
pointing past the dot in the url string
Comments:
--*/
{
const char vszInvalidFilenameChars[] = "<>\\\"/:|?*";
INET_ASSERT(lpszUrl && lpdwLength);
if (!lpszUrl)
{
*lpdwLength = 0;
return NULL;
}
LPSTR pszPeriod = NULL;
BOOL fContinue = TRUE;
// Scanning from left to right, note where we last saw a period.
// If we see a character that cannot be in an extension, and we've seen a period, forget
// about the period.
// Repeat this until we've reached the end of the url, a question mark (query) or hash (fragment)
// 1.6.98: _However_, if the file extension we've discovered is either .dll or .exe,
// we'll continue to scan beyond the query mark for a file extension.
// 1.20.98: And if we find no extension before the question mark, we'll look after it, then.
while (fContinue)
{
switch (*lpszUrl)
{
case TEXT('.'):
pszPeriod = lpszUrl;
break;
case TEXT('?'):
if (pszPeriod)
{
if ((!StrCmpNI(pszPeriod, EXE_EXTENSION, ARRAY_ELEMENTS(EXE_EXTENSION)-1))
|| (!StrCmpNI(pszPeriod, DLL_EXTENSION, ARRAY_ELEMENTS(DLL_EXTENSION)-1))
|| (!StrCmpNI(pszPeriod, CGI_EXTENSION, ARRAY_ELEMENTS(CGI_EXTENSION)-1)))
{
pszPeriod = NULL;
break;
}
}
else
{
break;
}
case TEXT('#'):
case TEXT('\0'):
fContinue = FALSE;
break;
default:
if (pszPeriod && strchr(vszInvalidFilenameChars, *lpszUrl))
{
pszPeriod = NULL;
}
}
lpszUrl++;
}
// This will be off by one
lpszUrl--;
if (pszPeriod)
{
if (*lpdwLength < (DWORD)(lpszUrl-pszPeriod))
{
pszPeriod = NULL;
}
else
{
pszPeriod++;
*lpdwLength = (DWORD)(lpszUrl-pszPeriod);
}
}
return pszPeriod;
}
// This function and the #define should be moved to registry.cxx
#define MIME_TO_FILE_EXTENSION_KEY "MIME\\Database\\Content Type\\"
#define EXTENSION_VALUE "Extension"
PRIVATE BOOL GetFileExtensionFromMimeType(
LPCSTR lpszMimeType,
DWORD dwMimeLen,
LPSTR lpszFileExtension,
LPDWORD lpdwExtLen
)
{
HKEY hKey = NULL;
LPSTR lpszMimeKey = (LPSTR)_alloca(sizeof(MIME_TO_FILE_EXTENSION_KEY)+dwMimeLen);
memcpy(lpszMimeKey, MIME_TO_FILE_EXTENSION_KEY,
sizeof(MIME_TO_FILE_EXTENSION_KEY)-1);
memcpy(lpszMimeKey + sizeof(MIME_TO_FILE_EXTENSION_KEY) - 1, lpszMimeType,
dwMimeLen);
lpszMimeKey[sizeof(MIME_TO_FILE_EXTENSION_KEY) + dwMimeLen - 1] = '\0';
if (REGOPENKEYEX(HKEY_CLASSES_ROOT,
lpszMimeKey,
0,
KEY_QUERY_VALUE,
&hKey)==ERROR_SUCCESS)
{
DWORD dwType, dwError = RegQueryValueEx(hKey,
EXTENSION_VALUE,
NULL,
&dwType,
(LPBYTE)lpszFileExtension,
lpdwExtLen);
REGCLOSEKEY(hKey);
return (dwError==ERROR_SUCCESS);
}
return FALSE;
}
PRIVATE BOOL FExcludedMimeType(
IN LPSTR lpszMimeType,
IN DWORD dwMimeTypeSize
)
{
LPCSTR rgszExcludedMimeTypes[] = {
"multipart/mixed",
"multipart/x-mixed-replace",
"multipart/x-byteranges"
};
const DWORD rgdwExcludedMimeTypeSizes[] = {
sizeof("multipart/mixed") - 1,
sizeof("multipart/x-mixed-replace") - 1,
sizeof("multipart/x-byteranges") - 1
};
DWORD i;
LPCSTR * lprgszMimeExcludeTable = rgszExcludedMimeTypes;
DWORD dwMimeExcludeCount = (sizeof(rgszExcludedMimeTypes)/sizeof(LPSTR));
const DWORD *lprgdwMimeExcludeTableOfSizes = rgdwExcludedMimeTypeSizes;
for (i = 0; i < dwMimeExcludeCount; ++i) {
if ((dwMimeTypeSize == lprgdwMimeExcludeTableOfSizes[i]) &&
!strnicmp(lpszMimeType,
lprgszMimeExcludeTable[i],
lprgdwMimeExcludeTableOfSizes[i])) {
return TRUE;
}
}
return FALSE;
}