|
|
/**********************************************************************/ /** Microsoft Windows NT **/ /** Copyright(c) Microsoft Corp., 1995 **/ /**********************************************************************/
/*
extmap.cxx
This module contains the extension mapping to CGI or BGI scripts.
FILE HISTORY: Johnl 22-Sep-1995 Created
*/
#include "w3p.hxx"
#include <rpc.h>
#include <rpcndr.h>
#include <mbstring.h>
//
// Name of the value under the parameters key containing the list of
// script extension to BGI/CGI binaries.
//
#define HTTP_EXT_MAPS "Script Map"
char Get[] = "GET";
#define GET_SIZE (sizeof("GET"))
//
// This is the maximum size for a script map extension
//
#define MAX_EXT_LEN 128
//
// This list is the extension maps found in the registry - they always get
// appended to the end of all extension mappings that are added to a particular
// URI.
//
static BOOL fInitialized = FALSE;
class EXT_MAP_ITEM { public:
EXT_MAP_ITEM( const char * pszExtension, const char * pszImage, const DWORD dwFlags, const CHAR * pszExclusions, const DWORD dwExclusionLength) : _strExt ( pszExtension ), _strImage ( pszImage ), _GatewayType( GATEWAY_UNKNOWN ), _cchExt ( 0 ), _dwFlags ( dwFlags ), _fGetValid (0), _dwInclusionCount ( 0 ), _ppszInclusionTable ( NULL ), _pszInclusions ( NULL ) { DWORD cch; _fValid = _strExt.IsValid() && _strImage.IsValid() && ExpandImage();
if ( _fValid ) { const CHAR * pchtmp = pszImage;
_cchExt = _strExt.QueryCCH();
//
// Determine if this is a CGI or BGI gateway
//
while ( pchtmp = strchr( pchtmp + 1, '.' )) { if ( !_strnicmp( pchtmp, ".exe", 4 )) { _GatewayType = GATEWAY_CGI; } else if ( !_strnicmp( pchtmp, ".dll", 4 )) { _GatewayType = GATEWAY_BGI; } }
if (!strcmp(pszExtension, "*")) { _fWildcard = TRUE; _dwFlags |= MD_SCRIPTMAPFLAG_WILDCARD; } else { _fWildcard = FALSE; _dwFlags &= ~MD_SCRIPTMAPFLAG_WILDCARD; }
if (pszExclusions != NULL) { const CHAR *pszTemp; char *pszDest;
_pszInclusions = new char[dwExclusionLength];
if (_pszInclusions == NULL) { _fValid = FALSE; return; }
_dwInclusionCount = 1; pszTemp = pszExclusions; pszDest = _pszInclusions;
while (*pszTemp != '\0') { CHAR ch;
ch = *pszTemp; pszTemp++;
if (ch != ',') { *pszDest = ch; } else { *pszDest = '\0';
_dwInclusionCount++; }
pszDest++;
}
*pszDest = '\0';
_ppszInclusionTable = new char *[_dwInclusionCount];
if (_ppszInclusionTable == NULL) { _fValid = FALSE; return; } else { DWORD i; DWORD dwPos;
pszTemp = _pszInclusions;
i = 0;
do {
_ppszInclusionTable[i] = (CHAR *)pszTemp;
if ( _fGetValid ) { dwPos = 0; }
while (*pszTemp != '\0') { if (dwPos < GET_SIZE) { if (*pszTemp == Get[dwPos]) { dwPos++;
if (dwPos == (GET_SIZE - 1)) { if (*(pszTemp+1) == '\0') { _fGetValid = TRUE; } else { dwPos = GET_SIZE; } } } else { dwPos = GET_SIZE; } }
pszTemp++; }
DBG_ASSERT(*pszTemp == '\0');
pszTemp++;
i++;
} while (i < _dwInclusionCount ); } } else { _dwInclusionCount = 0; _ppszInclusionTable = NULL; _pszInclusions = NULL; } } }
~EXT_MAP_ITEM( ) { delete _ppszInclusionTable; delete _pszInclusions; }
GATEWAY_TYPE QueryGatewayType( VOID ) const { return _GatewayType; }
const CHAR * QueryScript( VOID ) const { return _strImage.QueryStr(); }
const CHAR * QueryExtension( VOID ) const { return _strExt.QueryStr(); }
const BOOL AllowedOnReadDir() { return _dwFlags & MD_SCRIPTMAPFLAG_SCRIPT; }
const DWORD QueryFlags() { return _dwFlags; }
DWORD QueryCCHExt( VOID ) const { return _cchExt; }
BOOL IsValid( VOID ) const { return _fValid; }
BOOL IsWildCard( VOID ) const { return _fWildcard; }
BOOL IsGetValid( VOID ) const { return _fGetValid; }
BOOL ExpandImage( VOID );
BOOL CheckInclusions( const CHAR *pszVerb );
LIST_ENTRY _ListEntry;
private:
STR _strExt; STR _strImage; DWORD _cchExt; GATEWAY_TYPE _GatewayType; DWORD _fValid:1; DWORD _fWildcard:1; DWORD _fGetValid:1; DWORD _dwFlags; DWORD _dwInclusionCount; CHAR **_ppszInclusionTable; CHAR *_pszInclusions; };
BOOL EXT_MAP_ITEM::ExpandImage( VOID ) /*++
Routine Description:
Expand any embedded environment variables in the image.
Return Value:
TRUE if successful, FALSE if not.
--*/ { DWORD cbRet = 0; TCHAR achBuffer[ MAX_PATH + 1 ]; DWORD cbBufLen = sizeof( achBuffer );
cbRet = ExpandEnvironmentStringsA( _strImage.QueryStr(), achBuffer, cbBufLen ); if ( !cbRet || ( cbRet > cbBufLen ) ) { return FALSE; } else { return _strImage.Copy( achBuffer ); } }
//
// Private globals.
//
BOOL W3_METADATA::BuildExtMap( CHAR *pszExtMapList ) /*++
Routine Description:
Builds the extension mapping into the metadata. The input string is a multi-sz of comma seperated ext, image name strings.
Return Value:
TRUE if successfull, FALSE if not.
--*/ { EXT_MAP_ITEM * pExtMap; EXT_MAP_ITEM * pWCExtMapItem; LIST_ENTRY * pEntry;
m_fAnyExtAllowedOnReadDir = FALSE;
m_dwMaxExtLen = 0;
do {
CHAR *pszExt; CHAR *pszImage; CHAR *pszFlags; CHAR *pszExclusions; CHAR *pszTemp; CHAR *pszTemp2; CHAR *pszTemp3; DWORD dwExclusionSize; DWORD dwExtSize;
pszExt = pszExtMapList;
// Find the end of the extension, and temporarily NULL terminate it.
pszImage = strchr(pszExt, ',');
if (pszImage == NULL) { // Bad script map entry
SetLastError(ERROR_INVALID_DATA); return FALSE; }
pszTemp = pszImage++; *pszTemp = '\0';
pszFlags = strchr(pszImage, ',');
if (pszFlags == NULL) { // Bad script map entry
SetLastError(ERROR_INVALID_DATA); return FALSE; }
pszTemp2 = pszFlags++; *pszTemp2 = '\0';
//
// HOTFIX: make sure the extension is not too long to be copied to
// our static buffer.
//
dwExtSize = strlen(pszExt); if (dwExtSize > MAX_EXT_LEN) { // Bad script map entry
SetLastError(ERROR_INVALID_DATA); return FALSE; } else if (dwExtSize > m_dwMaxExtLen) { m_dwMaxExtLen = dwExtSize; }
//
// See if there's any excluded methods. If there are, break them out.
//
pszExclusions = strchr(pszFlags, ',');
if (pszExclusions != NULL) { pszTemp3 = pszExclusions++; *pszTemp3 = '\0'; dwExclusionSize = strlen(pszExclusions) + 1; pszExtMapList = pszExclusions + dwExclusionSize; } else { pszExtMapList = pszFlags + strlen(pszFlags) + 1; dwExclusionSize = 0; }
//
// HOTFIX: Now convert extension to lower case so we can avoid
// multi-byte string compares that cause lock contention.
//
IISstrlwr( (PUCHAR) pszExt ); //
// Note we OR in the notransmit flag on *all* script mappings!
//
pExtMap = new EXT_MAP_ITEM( pszExt, pszImage, ((DWORD) atoi( pszFlags )), pszExclusions, dwExclusionSize);
*pszTemp = ','; *pszTemp2 = ',';
if (pszExclusions != NULL) { *pszTemp3 = '\0'; }
if ( !pExtMap || !pExtMap->IsValid() ) { delete pExtMap;
return FALSE; }
if (!pExtMap->IsWildCard()) { InsertTailList( &m_ExtMapHead, &pExtMap->_ListEntry ); } else {
pWCExtMapItem = (EXT_MAP_ITEM *)QueryWildcardMapping();
if (pWCExtMapItem != NULL) { delete pWCExtMapItem; }
SetWildcardMapping( pExtMap ); }
if ( pExtMap->QueryFlags() ) { m_fAnyExtAllowedOnReadDir = TRUE; }
} while ( *pszExtMapList != '\0');
return TRUE;
} // W3_METADATA::BuildExtMap
VOID W3_METADATA::TerminateExtMap( VOID ) /*++
Routine Description:
Cleans up the extension map list
--*/ { LIST_ENTRY * pEntry; EXT_MAP_ITEM * pExtMap;
while ( !IsListEmpty( &m_ExtMapHead )) { pExtMap = CONTAINING_RECORD( m_ExtMapHead.Flink, EXT_MAP_ITEM, _ListEntry );
RemoveEntryList( &pExtMap->_ListEntry );
delete pExtMap; }
pExtMap = (EXT_MAP_ITEM *)QueryWildcardMapping();
if (pExtMap != NULL) { delete pExtMap; }
} // W3_METADATA::TerminateExtMap
BOOL EXT_MAP_ITEM::CheckInclusions( const CHAR *pszVerb ) /*++
Routine Description:
Check the extension map list to see if the input verb is included. If it is, we return TRUE, otherwise we return FALSE. --*/ { DWORD i;
//
// Special case for EMPTY script map. Allow all verbs
//
if (0 == _dwInclusionCount) { return TRUE; } for (i = 0; i < _dwInclusionCount; i++) { if (!_stricmp(pszVerb, _ppszInclusionTable[i])) { return TRUE; } }
return FALSE;
} // EXT_MAP_ITEM::CheckInclusions
BOOL W3_METADATA::LookupExtMap( IN const CHAR * pchExt, IN BOOL fNoWildcards, OUT STR * pstrGatewayImage, OUT GATEWAY_TYPE * pGatewayType, OUT DWORD * pcchExt, OUT BOOL * pfImageInURL, OUT BOOL * pfVerbExcluded, OUT DWORD * pdwFlags, IN const CHAR *pszVerb, IN enum HTTP_VERB Verb, IN OUT PVOID * ppvExtMapInfo ) /*++
Routine Description:
Finds the admin specified mapping between a script extension and the associated CGI or BGI binary to run (or load).
Arguments:
pchExt - Pointer to possible extension to be mapped (i.e., '.pl') pstrGatewayImage - Receives the mapped binary image name pGatewayType - Specifies whether this is a BGI, CGI or MAP extension type pcchExt - Returns length of extension (including dot) pfImageInURL - Indicates an image was found encoded in the URL and not from a script extension mapping pdwFlags - Returns extension flags ppvExtMapInfo - Cached extension map info. If *ppvExtMapInfo is NULL on input, then set to the matched PEXT_MAP_ITEM. If not NULL on input, then it is used instead of doing lookup.
--*/ { EXT_MAP_ITEM * pExtMapItem; DWORD cchTillEOS; BOOL fRet; LIST_ENTRY * pEntry; BOOL bFoundMatch = FALSE; BOOL fUseExtMapInfo = *ppvExtMapInfo != NULL;
*pGatewayType = GATEWAY_UNKNOWN; *pfVerbExcluded = FALSE;
//
// Check for wildcard mapping first
//
if (!fNoWildcards) { pExtMapItem = (EXT_MAP_ITEM *)QueryWildcardMapping();
if (pExtMapItem != NULL) { if (Verb == HTV_GET && pExtMapItem->IsGetValid()) { bFoundMatch = TRUE; } else { // If verb is not included, don't return the * script map.
// Instead, continue to look for a script map match.
bFoundMatch = pExtMapItem->CheckInclusions( pszVerb ); } } }
//
// Look for the exact extension mapping if there's no wildcard
//
if (!bFoundMatch && pchExt != NULL) { if ( fUseExtMapInfo ) { //
// If caller passed in a non-NULL pExtMapInfo, then use it
// instead of doing a manual lookup
//
if ( *ppvExtMapInfo != EXTMAP_UNKNOWN_PTR ) { pExtMapItem = (EXT_MAP_ITEM*) *ppvExtMapInfo; bFoundMatch = TRUE; } } else { //
// This buffer, rgchExtBuffer, holds a copy of a portion of a URL
// which we are testing to see if it's a known extension. We copy
// into this buffer so we can convert the extension to lower case
// without disrupting the original.
//
CHAR rgchExtBuffer[MAX_EXT_LEN + 4]; DWORD dwLength; DBG_ASSERT( *pchExt == '.' ); //
// HOTFIX: Now convert extension to lower case so we can avoid
// multi-byte string compares that cause lock contention.
//
// Since we don't want to risk modifying the orignal URL, we
// copy it into another buffer first.
//
cchTillEOS = strlen( pchExt );
dwLength = min(cchTillEOS, m_dwMaxExtLen + 1); memcpy(rgchExtBuffer, pchExt, dwLength); rgchExtBuffer[ dwLength ] = 0; IISstrlwr( (PUCHAR) rgchExtBuffer );
//
// Look through the list of mappings
//
for ( pEntry = m_ExtMapHead.Flink; !bFoundMatch && pEntry != &m_ExtMapHead; pEntry = pEntry->Flink ) { pExtMapItem = CONTAINING_RECORD( pEntry, EXT_MAP_ITEM, _ListEntry );
if ( cchTillEOS >= pExtMapItem->QueryCCHExt() && (pchExt[pExtMapItem->QueryCCHExt()] == '/' || pchExt[pExtMapItem->QueryCCHExt()] == '\0' ) && !memcmp( rgchExtBuffer, pExtMapItem->QueryExtension(), pExtMapItem->QueryCCHExt()) ) { bFoundMatch = TRUE; *ppvExtMapInfo = pExtMapItem; } } } }
if (bFoundMatch) { *pGatewayType = pExtMapItem->QueryGatewayType(); *pcchExt = pExtMapItem->QueryCCHExt(); *pfImageInURL = FALSE; *pdwFlags = pExtMapItem->QueryFlags();
//
// Check that verb is included. If it isn't, we're still going to return
// this item. Just indicate that the verb was excluded.
//
if (Verb != HTV_GET || !pExtMapItem->IsGetValid()) { *pfVerbExcluded = !pExtMapItem->CheckInclusions(pszVerb); }
fRet = pstrGatewayImage->Copy( pExtMapItem->QueryScript() ); if ( !_stricmp( pExtMapItem->QueryScript(), "nogateway" ) ) { *pGatewayType = GATEWAY_NONE; }
return fRet; }
if ( !pchExt || fUseExtMapInfo ) { if ( !fUseExtMapInfo ) { *ppvExtMapInfo = EXTMAP_UNKNOWN_PTR; } return TRUE; }
//
// Either the image will be specified in the URL or not found, so
// just indicate it's in the URL. Not found has precedence.
//
*pfImageInURL = TRUE; *pdwFlags = 0;
//
// Look for CGI or BGI scripts in the URL itself
//
if ( cchTillEOS >= 4 && (*(pchExt+4) == TEXT('/') || *(pchExt+4) == TEXT('\0')) ) { *pcchExt = 4;
//
// Don't confuse a menu map request with a gateway request
//
if ( !::_tcsnicmp( TEXT(".MAP"), pchExt, 4 )) { *pGatewayType = GATEWAY_MAP; return TRUE; }
if ( !::_tcsnicmp( TEXT(".EXE"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".CGI"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".COM"), pchExt, 4 )) { *pGatewayType = GATEWAY_CGI; return TRUE; } else if (!::_tcsnicmp( TEXT(".DLL"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".ISA"), pchExt, 4 ) ) { *pGatewayType = GATEWAY_BGI; return TRUE; } } *ppvExtMapInfo = EXTMAP_UNKNOWN_PTR;
return TRUE; } // W3_METADATA::LookupExtMap
APIERR ReadRegistryExtMap( VOID ) /*++
Routine Description:
Builds the extension mapping from the registry
Return Value:
NO_ERROR if successful, win32 error code on failure
--*/ { HKEY hkeyParam; DWORD dwDisposition; LIST_ENTRY pEntry; APIERR err; DWORD i = 0; DWORD dwRegType; MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() ); MULTISZ msz; BOOL fNeedToWrite = FALSE;
if ( !fInitialized ) { fInitialized = TRUE; }
//
// Get the list
//
err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, W3_PARAMETERS_KEY "\\" HTTP_EXT_MAPS, 0, 0, 0, KEY_READ, NULL, &hkeyParam, &dwDisposition );
if( err != NO_ERROR ) { TCP_PRINT(( DBG_CONTEXT, "cannot open registry key, error %lu\n", err ));
return NO_ERROR; }
if ( !mb.Open( "/LM/W3SVC/", METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) || !mb.GetMultisz( "", MD_SCRIPT_MAPS, IIS_MD_UT_FILE, &msz )) { TCP_PRINT(( DBG_CONTEXT, "cannot get the script maps from the metabase, error %d\n", GetLastError() ));
return NO_ERROR; }
while ( TRUE ) { CHAR achExt[MAX_PATH+1]; CHAR achImage[MAX_PATH+1]; DWORD cchExt = sizeof( achExt ); DWORD cchImage = sizeof( achImage );
err = RegEnumValue( hkeyParam, i++, achExt, &cchExt, NULL, &dwRegType, (LPBYTE) achImage, &cchImage );
if ( err == ERROR_NO_MORE_ITEMS ) { err = NO_ERROR; break; }
if ( dwRegType == REG_SZ ) { const CHAR * psz; BOOL fFound = FALSE;
//
// Look for this script map in the metabase, if not found, add it
//
for ( psz = msz.First(); psz != NULL; psz = msz.Next( psz ) ) { if ( !IISstrnicmp( (PUCHAR)achExt, (PUCHAR)psz, cchExt )) { fFound = TRUE; break; } }
if ( !fFound ) { STR str; CHAR achFlags[32];
//
// Note these scripts are added w/o the Script bit and with
// the "never download" bit. In addition, we leave the
// method exclusion list blank.
//
_itoa( 0, achFlags, 10 );
if ( !str.Append( achExt ) || !str.Append( "," ) || !str.Append( achImage ) || !str.Append( "," ) || !str.Append( achFlags ) || !msz.Append( str.QueryStr() )) { return err = GetLastError(); break; }
TCP_PRINT(( DBG_CONTEXT, "Added \"%s\" from registry as script map\n", str.QueryStr() ));
fNeedToWrite = TRUE; } } }
if ( fNeedToWrite ) { if ( !mb.SetMultiSZ( "", MD_SCRIPT_MAPS, IIS_MD_UT_FILE, msz.QueryStr() )) { TCP_PRINT(( DBG_CONTEXT, "Failed to write MD_SCRIPT_MAPS back to metabase, error %d\n", GetLastError() )); } }
RegCloseKey( hkeyParam );
return err; }
VOID FreeRegistryExtMap( VOID ) { if ( !fInitialized ) { return; }
fInitialized = FALSE; }
|