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.
 
 
 
 
 
 

516 lines
13 KiB

/*
* S C R P T M P S . C P P
*
* Scriptmaps cacheing
*
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
*/
#include "_davprs.h"
#include "scrptmps.h"
#include "instdata.h"
// ========================================================================
// class CScriptMap
//
// Contains the parsed set of scriptmaps for a single metabase entry.
// Contains lookup functions to find scriptmaps that match certain
// conditions.
//
class CScriptMap :
public IScriptMap,
public CMTRefCounted
{
typedef struct _inclusions {
DWORD fdwInclusions;
LPCWSTR pwszMethods;
BOOL FAllMethodsIncluded ()
{
// When a script map has an empty inclusion verb list, it means all verbs included
// (NOT all verbs excluded).
//
Assert (pwszMethods);
return L'\0' == pwszMethods[0];
}
} INCLUSIONS, * PINCLUSIONS;
typedef CCache<CRCWszi, PINCLUSIONS> CInclusionsCache;
// INCLUSIONS data storage area.
//
ChainedBuffer<INCLUSIONS> m_bInclusions;
// Cache of scriptmap entries
//
CInclusionsCache m_cache;
// Pointer to first 'star' scriptmap in the list.
// This is the one that IIS will have called to process
// the request for this url. This is used by the virtual
// root cache.
//
// Note that we should ignore any 'star' scriptmaps when
// evaluating for matches. The first 'star' gets a crack
// at it, and that's that.
//
LPCWSTR m_pwszStarScriptmap;
// Private accessors
//
VOID AddMapping (LPCWSTR pwszMap, HDRITER_W* pit);
BOOL FLoadScriptmaps (LPWSTR pwszScriptMaps);
// CLASS CIsMatch --------------------------------------------------------
//
// Functional class to find if a given scriptmap applys to a URI
//
class CIsMatch : public CInclusionsCache::IOp
{
const CInclusionsCache& m_cache;
const LPCWSTR m_pwszMethod;
const METHOD_ID m_midMethod;
BOOL m_fCGI;
DWORD m_dwAccess;
LPCWSTR m_pwszMatch;
LPCWSTR m_pwszURI;
SCODE m_sc;
// NOT IMPLEMENTED
//
CIsMatch& operator=(const CIsMatch&);
public:
CIsMatch(const CInclusionsCache& cache,
const LPCWSTR pwszMethod,
const METHOD_ID midMethod,
LPCWSTR pwszUri,
LPCWSTR pwszMatch,
DWORD dwAcc)
: m_cache(cache),
m_pwszMethod(pwszMethod),
m_midMethod(midMethod),
m_fCGI(FALSE),
m_dwAccess(dwAcc),
m_pwszURI(pwszUri),
m_pwszMatch(pwszMatch),
m_sc(S_OK)
{}
SCODE ScMatched() const { return m_sc; }
BOOL FMatchIsCGI() const { return m_fCGI; }
virtual BOOL operator()(const CRCWszi& crcwszi, const PINCLUSIONS& pin);
};
// NOT IMPLEMENTED
//
CScriptMap& operator=(const CScriptMap&);
CScriptMap(const CScriptMap&);
// Helper function
//
public:
// CREATORS
//
CScriptMap() : m_pwszStarScriptmap(NULL)
{
// Use COM-style ref-counting. Start with 1.
//
m_cRef = 1;
}
BOOL FInit (LPWSTR pwszScriptMaps);
// Implementation of IRefCounted members
// Simply route them to our own CMTRefCounted members.
//
void AddRef()
{
CMTRefCounted::AddRef();
}
void Release()
{
CMTRefCounted::Release();
}
// ACCESSORS
//
SCODE ScMatched (LPCWSTR pwszMethod,
METHOD_ID midMethod,
LPCWSTR pwszMap,
DWORD dwAccess,
BOOL * pfCGI) const;
// Used by MOVE/COPY/DELETE to check for star scriptmapping
// overrides
//
BOOL FSameStarScriptmapping (const IScriptMap * pism) const
{
const CScriptMap* prhs = static_cast<const CScriptMap*>(pism);
if (m_pwszStarScriptmap != prhs->m_pwszStarScriptmap)
{
if (m_pwszStarScriptmap && prhs->m_pwszStarScriptmap)
if (0 == _wcsicmp (m_pwszStarScriptmap, prhs->m_pwszStarScriptmap))
return TRUE;
}
else
return TRUE;
return FALSE;
}
};
CScriptMap::FInit (LPWSTR pwszScriptMaps)
{
// Init the cache
//
if ( !m_cache.FInit() )
return FALSE;
// Load the scriptmaps
//
return FLoadScriptmaps(pwszScriptMaps);
}
void
CScriptMap::AddMapping(LPCWSTR pwszMap, HDRITER_W * pitInclusions)
{
LPCWSTR pwszInclusion;
METHOD_ID mid;
PINCLUSIONS pin = NULL;
Assert (pwszMap);
// If there is a DLL, then we want to assemble an inclusions list
//
if (pitInclusions)
{
pin = m_bInclusions.Alloc (sizeof(INCLUSIONS));
// Record the start of the inclusion list
//
pin->pwszMethods = pitInclusions->PszRaw();
pin->fdwInclusions = 0;
// Rip through the list and identify all the known
// inclusions
//
while ((pwszInclusion = pitInclusions->PszNext()) != NULL)
{
mid = MidMethod (pwszInclusion);
if (mid != MID_UNKNOWN)
pin->fdwInclusions |= (1 << mid);
}
}
// At this point, we can add the cache item...
//
ScriptMapTrace ("Dav: adding scriptmap for %S -- including %S\n",
pwszMap,
(pin && pin->pwszMethods) ? pin->pwszMethods : L"none");
// CRC the mapping and stuff it into the cache.
// Note that we are safe in using the actual parameter string
// here because the CScriptMap object's lifetime is the same
// as the lifetime of the metadata on which it operates. See
// \cal\src\_davprs\davmb.cpp for details.
//
CRCWszi crcwszi(pwszMap);
(void) m_cache.FSet (crcwszi, pin);
}
BOOL
CScriptMap::FLoadScriptmaps (LPWSTR pwszScriptMaps)
{
HDRITER_W it(NULL);
UINT cchDav = static_cast<UINT>(wcslen(gc_wszSignature));
Assert (pwszScriptMaps);
ScriptMapTrace ("Dav: loading scriptmap cache\n");
// Add in the default CGI/BGI mappings
//
AddMapping (L".EXE", NULL);
AddMapping (L".CGI", NULL);
AddMapping (L".COM", NULL);
AddMapping (L".DLL", NULL);
AddMapping (L".ISA", NULL);
//
// Parse through the scriptmap list and build up the cache.
//
// Each mapping is a string of the form:
//
// "<ext>|<*>,<path>,<flags>[,<included verb>...]"
//
// Note that if any of the mappings is invalid we fail the whole call.
// This is consistent with IIS' behavior.
//
UINT cchMapping = 0;
for ( LPWSTR pwszMapping = pwszScriptMaps;
*pwszMapping;
pwszMapping += cchMapping )
{
enum {
ISZ_SM_EXT = 0,
ISZ_SM_PATH,
ISZ_SM_FLAGS,
ISZ_SM_INCLUSION_LIST,
CSZ_SM_FIELDS
};
// Figure out the length of the mapping
// including the null terminator
//
cchMapping = static_cast<UINT>(wcslen(pwszMapping) + 1);
// Special case: star (wildcard) scriptmaps.
//
// These should mostly be ignored. We will never
// forward to a star scriptmap. If we find a star
// scriptmap, the only reason to keep track of it
// is so that we can compare it against another
// star scriptmap when checking the feasibility
// of a trans-vroot MOVE/COPY/DELETE. And for this
// comparsion, we check for EXACT equality between
// the scriptmaps by checking the entire scriptmap
// string.
//
// See the comments regarding m_pszStarScriptMap
// above for more detail.
//
if (L'*' == *pwszMapping)
{
if (NULL == m_pwszStarScriptmap)
m_pwszStarScriptmap = pwszMapping;
continue;
}
// Digest the metadata.
//
LPWSTR rgpwsz[CSZ_SM_FIELDS];
UINT cchUnused;
if (!FParseMDData (pwszMapping,
rgpwsz,
CSZ_SM_FIELDS,
&cchUnused))
{
// FParseMDData() will return FALSE if there is no verb
// exclusion list because it is an optional parameter.
// If all the other parameters exist though then it's
// really ok.
//
if (!(rgpwsz[ISZ_SM_EXT] &&
rgpwsz[ISZ_SM_PATH] &&
rgpwsz[ISZ_SM_FLAGS]))
{
DebugTrace ("CScriptMap::FLoadScriptMaps() - Malformed scriptmaps\n");
continue;
}
}
// We belive that all the scriptmaps are
// extension based. But other than that
// there is no validation.
//
Assert (*rgpwsz[ISZ_SM_EXT] == L'.');
// If the path refers to our DAV DLL then skip this mapping.
//
// The way this works is: If the length of the path is at least
// as long as the length of our DLL name AND the final component
// of that path is the name of our DLL then skip the mapping.
// Eg. "HTTPEXT.DLL" will match the first condition of the if,
// "c:\foo\bar\HTTPEXT.DLL" will match the second condition of the if.
//
static const UINT cchDll = CchConstString(L".DLL");
UINT cchPath = static_cast<UINT>(wcslen(rgpwsz[ISZ_SM_PATH]));
if (cchPath == cchDav + cchDll ||
((cchPath > cchDav + cchDll) &&
*(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll - cchDav - 1) == L'\\'))
{
// Now we know the final piece of the path is the correct length.
// Check the data! If it matches our dll name, skip this mapping.
//
if (!_wcsnicmp(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll - cchDav,
gc_wszSignature,
cchDav) &&
!_wcsicmp(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll,
L".DLL"))
{
continue;
}
}
// Feed the optional inclusion list into a header iterator
// that AddMapping() will use to determine what verbs
// are included for this mapping. If there is no inclusion
// list then use an empty iterator.
//
// Adding a mapping with an empty iterator (vs. NULL)
// allows the scriptmap matching code to distinguish
// between a "real" scriptmap with an empty inclusion
// list and a default CGI-style scriptmap like those
// added at the beginning of this function.
//
it.NewHeader(rgpwsz[ISZ_SM_INCLUSION_LIST] ?
rgpwsz[ISZ_SM_INCLUSION_LIST] :
gc_wszEmpty);
// Add the extension-based mapping
//
AddMapping (rgpwsz[ISZ_SM_EXT], &it);
}
return TRUE;
}
SCODE
CScriptMap::ScMatched (
LPCWSTR pwszMethod,
METHOD_ID midMethod,
LPCWSTR pwszURI,
DWORD dwAccess,
BOOL * pfCGI) const
{
LPCWSTR pwsz;
SCODE sc = S_OK;
Assert(pwszURI);
//
// Scan down the URI, looking for extensions. When one is found
// zip through the list of mappings. While this may not seem the
// most optimal, it really is. If we simply scaned the URI for
// each mapping. We would be scaning the URI multiple times. In
// this model, we scan the URI once.
//
if ((pwsz = wcsrchr(pwszURI, L'.')) != NULL)
{
// We have an extension so take a look
//
CIsMatch cim(m_cache, pwszMethod, midMethod, pwszURI, pwsz, dwAccess);
m_cache.ForEach(cim);
sc = cim.ScMatched();
if (pfCGI && (sc != S_OK))
*pfCGI = cim.FMatchIsCGI();
}
return sc;
}
// CLASS CIsMatch ------------------------------------------------------------
//
//$REVIEW: Does this code work for DBCS/UTF-8 map names? These are filenames....
//$REVIEW: This function does not currently check the METHOD EXCLUSION LIST.
//$REVIEW: This might cause us to report a match when actually there are NO matches.
//
BOOL
CScriptMap::CIsMatch::operator()(const CRCWszi& crcwszi, const PINCLUSIONS& pin)
{
Assert (crcwszi.m_pwsz);
// Every scriptmap in the cache should be an extension-based mapping.
// Compare the extension vs. the part of the URI that we're looking at.
// If they match then we have a scriptmap.
//
Assert (L'.' == *crcwszi.m_pwsz);
UINT cch = static_cast<UINT>(wcslen (crcwszi.m_pwsz));
if (!_wcsnicmp (crcwszi.m_pwsz, m_pwszMatch, cch) &&
((m_pwszMatch[cch] == '\0')
|| !wcscmp (m_pwszMatch+cch, L"/")
|| !wcscmp (m_pwszMatch+cch, L"\\")))
{
// Looks like we have a match
//
ScriptMapTrace ("Dav: %S matched scriptmap %S\n", m_pwszURI, crcwszi.m_pwsz);
// However, we only allow execution of CGI type child
// ISAPI's if EXECUTE priviledge is enabled
//
if ((pin != NULL) || (m_dwAccess & MD_ACCESS_EXECUTE))
m_sc = W_DAV_SCRIPTMAP_MATCH_FOUND;
m_fCGI = !pin;
}
// See if it is included
// Note that, if all methods are included, no need to do further checking
//
if ((m_sc != S_OK) && pin && !pin->FAllMethodsIncluded())
{
ScriptMapTrace ("Dav: checking '%S' against scriptmap inclusions: %S\n",
m_pwszMethod,
pin->pwszMethods);
// In the unknown method scenario, we just need
// to iterate the set of methods that are included
// and check it against the request method
//
if (m_midMethod == MID_UNKNOWN)
{
BOOL fIncluded = FALSE;
HDRITER_W it(pin->pwszMethods);
LPCWSTR pwsz;
while ((pwsz = it.PszNext()) != NULL)
{
fIncluded = !wcscmp (pwsz, m_pwszMethod);
if (fIncluded)
break;
}
if (!fIncluded)
{
ScriptMapTrace ("Dav: unknown '%S' excluded from scriptmap\n",
m_pwszMethod);
m_sc = W_DAV_SCRIPTMAP_MATCH_EXCLUDED;
}
}
//
// Otherwise, the inclusions flags have the MID'th bit
// set if it is excluded.
//
else if (!(pin->fdwInclusions & (1 << m_midMethod)))
{
ScriptMapTrace ("Dav: '%S' excluded from scriptmap\n",
m_pwszMethod);
m_sc = W_DAV_SCRIPTMAP_MATCH_EXCLUDED;
}
}
return (m_sc == S_OK);
}
IScriptMap *
NewScriptMap( LPWSTR pwszScriptMaps )
{
auto_ref_ptr<CScriptMap> pScriptMap;
pScriptMap.take_ownership (new CScriptMap());
if (pScriptMap->FInit(pwszScriptMaps))
return pScriptMap.relinquish();
return NULL;
}