|
|
/*++
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; }
|