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
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;
|
|
}
|