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

  1. /*
  2. * S C R P T M P S . C P P
  3. *
  4. * Scriptmaps cacheing
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davprs.h"
  9. #include "scrptmps.h"
  10. #include "instdata.h"
  11. // ========================================================================
  12. // class CScriptMap
  13. //
  14. // Contains the parsed set of scriptmaps for a single metabase entry.
  15. // Contains lookup functions to find scriptmaps that match certain
  16. // conditions.
  17. //
  18. class CScriptMap :
  19. public IScriptMap,
  20. public CMTRefCounted
  21. {
  22. typedef struct _inclusions {
  23. DWORD fdwInclusions;
  24. LPCWSTR pwszMethods;
  25. BOOL FAllMethodsIncluded ()
  26. {
  27. // When a script map has an empty inclusion verb list, it means all verbs included
  28. // (NOT all verbs excluded).
  29. //
  30. Assert (pwszMethods);
  31. return L'\0' == pwszMethods[0];
  32. }
  33. } INCLUSIONS, * PINCLUSIONS;
  34. typedef CCache<CRCWszi, PINCLUSIONS> CInclusionsCache;
  35. // INCLUSIONS data storage area.
  36. //
  37. ChainedBuffer<INCLUSIONS> m_bInclusions;
  38. // Cache of scriptmap entries
  39. //
  40. CInclusionsCache m_cache;
  41. // Pointer to first 'star' scriptmap in the list.
  42. // This is the one that IIS will have called to process
  43. // the request for this url. This is used by the virtual
  44. // root cache.
  45. //
  46. // Note that we should ignore any 'star' scriptmaps when
  47. // evaluating for matches. The first 'star' gets a crack
  48. // at it, and that's that.
  49. //
  50. LPCWSTR m_pwszStarScriptmap;
  51. // Private accessors
  52. //
  53. VOID AddMapping (LPCWSTR pwszMap, HDRITER_W* pit);
  54. BOOL FLoadScriptmaps (LPWSTR pwszScriptMaps);
  55. // CLASS CIsMatch --------------------------------------------------------
  56. //
  57. // Functional class to find if a given scriptmap applys to a URI
  58. //
  59. class CIsMatch : public CInclusionsCache::IOp
  60. {
  61. const CInclusionsCache& m_cache;
  62. const LPCWSTR m_pwszMethod;
  63. const METHOD_ID m_midMethod;
  64. BOOL m_fCGI;
  65. DWORD m_dwAccess;
  66. LPCWSTR m_pwszMatch;
  67. LPCWSTR m_pwszURI;
  68. SCODE m_sc;
  69. // NOT IMPLEMENTED
  70. //
  71. CIsMatch& operator=(const CIsMatch&);
  72. public:
  73. CIsMatch(const CInclusionsCache& cache,
  74. const LPCWSTR pwszMethod,
  75. const METHOD_ID midMethod,
  76. LPCWSTR pwszUri,
  77. LPCWSTR pwszMatch,
  78. DWORD dwAcc)
  79. : m_cache(cache),
  80. m_pwszMethod(pwszMethod),
  81. m_midMethod(midMethod),
  82. m_fCGI(FALSE),
  83. m_dwAccess(dwAcc),
  84. m_pwszURI(pwszUri),
  85. m_pwszMatch(pwszMatch),
  86. m_sc(S_OK)
  87. {}
  88. SCODE ScMatched() const { return m_sc; }
  89. BOOL FMatchIsCGI() const { return m_fCGI; }
  90. virtual BOOL operator()(const CRCWszi& crcwszi, const PINCLUSIONS& pin);
  91. };
  92. // NOT IMPLEMENTED
  93. //
  94. CScriptMap& operator=(const CScriptMap&);
  95. CScriptMap(const CScriptMap&);
  96. // Helper function
  97. //
  98. public:
  99. // CREATORS
  100. //
  101. CScriptMap() : m_pwszStarScriptmap(NULL)
  102. {
  103. // Use COM-style ref-counting. Start with 1.
  104. //
  105. m_cRef = 1;
  106. }
  107. BOOL FInit (LPWSTR pwszScriptMaps);
  108. // Implementation of IRefCounted members
  109. // Simply route them to our own CMTRefCounted members.
  110. //
  111. void AddRef()
  112. {
  113. CMTRefCounted::AddRef();
  114. }
  115. void Release()
  116. {
  117. CMTRefCounted::Release();
  118. }
  119. // ACCESSORS
  120. //
  121. SCODE ScMatched (LPCWSTR pwszMethod,
  122. METHOD_ID midMethod,
  123. LPCWSTR pwszMap,
  124. DWORD dwAccess,
  125. BOOL * pfCGI) const;
  126. // Used by MOVE/COPY/DELETE to check for star scriptmapping
  127. // overrides
  128. //
  129. BOOL FSameStarScriptmapping (const IScriptMap * pism) const
  130. {
  131. const CScriptMap* prhs = static_cast<const CScriptMap*>(pism);
  132. if (m_pwszStarScriptmap != prhs->m_pwszStarScriptmap)
  133. {
  134. if (m_pwszStarScriptmap && prhs->m_pwszStarScriptmap)
  135. if (0 == _wcsicmp (m_pwszStarScriptmap, prhs->m_pwszStarScriptmap))
  136. return TRUE;
  137. }
  138. else
  139. return TRUE;
  140. return FALSE;
  141. }
  142. };
  143. CScriptMap::FInit (LPWSTR pwszScriptMaps)
  144. {
  145. // Init the cache
  146. //
  147. if ( !m_cache.FInit() )
  148. return FALSE;
  149. // Load the scriptmaps
  150. //
  151. return FLoadScriptmaps(pwszScriptMaps);
  152. }
  153. void
  154. CScriptMap::AddMapping(LPCWSTR pwszMap, HDRITER_W * pitInclusions)
  155. {
  156. LPCWSTR pwszInclusion;
  157. METHOD_ID mid;
  158. PINCLUSIONS pin = NULL;
  159. Assert (pwszMap);
  160. // If there is a DLL, then we want to assemble an inclusions list
  161. //
  162. if (pitInclusions)
  163. {
  164. pin = m_bInclusions.Alloc (sizeof(INCLUSIONS));
  165. // Record the start of the inclusion list
  166. //
  167. pin->pwszMethods = pitInclusions->PszRaw();
  168. pin->fdwInclusions = 0;
  169. // Rip through the list and identify all the known
  170. // inclusions
  171. //
  172. while ((pwszInclusion = pitInclusions->PszNext()) != NULL)
  173. {
  174. mid = MidMethod (pwszInclusion);
  175. if (mid != MID_UNKNOWN)
  176. pin->fdwInclusions |= (1 << mid);
  177. }
  178. }
  179. // At this point, we can add the cache item...
  180. //
  181. ScriptMapTrace ("Dav: adding scriptmap for %S -- including %S\n",
  182. pwszMap,
  183. (pin && pin->pwszMethods) ? pin->pwszMethods : L"none");
  184. // CRC the mapping and stuff it into the cache.
  185. // Note that we are safe in using the actual parameter string
  186. // here because the CScriptMap object's lifetime is the same
  187. // as the lifetime of the metadata on which it operates. See
  188. // \cal\src\_davprs\davmb.cpp for details.
  189. //
  190. CRCWszi crcwszi(pwszMap);
  191. (void) m_cache.FSet (crcwszi, pin);
  192. }
  193. BOOL
  194. CScriptMap::FLoadScriptmaps (LPWSTR pwszScriptMaps)
  195. {
  196. HDRITER_W it(NULL);
  197. UINT cchDav = static_cast<UINT>(wcslen(gc_wszSignature));
  198. Assert (pwszScriptMaps);
  199. ScriptMapTrace ("Dav: loading scriptmap cache\n");
  200. // Add in the default CGI/BGI mappings
  201. //
  202. AddMapping (L".EXE", NULL);
  203. AddMapping (L".CGI", NULL);
  204. AddMapping (L".COM", NULL);
  205. AddMapping (L".DLL", NULL);
  206. AddMapping (L".ISA", NULL);
  207. //
  208. // Parse through the scriptmap list and build up the cache.
  209. //
  210. // Each mapping is a string of the form:
  211. //
  212. // "<ext>|<*>,<path>,<flags>[,<included verb>...]"
  213. //
  214. // Note that if any of the mappings is invalid we fail the whole call.
  215. // This is consistent with IIS' behavior.
  216. //
  217. UINT cchMapping = 0;
  218. for ( LPWSTR pwszMapping = pwszScriptMaps;
  219. *pwszMapping;
  220. pwszMapping += cchMapping )
  221. {
  222. enum {
  223. ISZ_SM_EXT = 0,
  224. ISZ_SM_PATH,
  225. ISZ_SM_FLAGS,
  226. ISZ_SM_INCLUSION_LIST,
  227. CSZ_SM_FIELDS
  228. };
  229. // Figure out the length of the mapping
  230. // including the null terminator
  231. //
  232. cchMapping = static_cast<UINT>(wcslen(pwszMapping) + 1);
  233. // Special case: star (wildcard) scriptmaps.
  234. //
  235. // These should mostly be ignored. We will never
  236. // forward to a star scriptmap. If we find a star
  237. // scriptmap, the only reason to keep track of it
  238. // is so that we can compare it against another
  239. // star scriptmap when checking the feasibility
  240. // of a trans-vroot MOVE/COPY/DELETE. And for this
  241. // comparsion, we check for EXACT equality between
  242. // the scriptmaps by checking the entire scriptmap
  243. // string.
  244. //
  245. // See the comments regarding m_pszStarScriptMap
  246. // above for more detail.
  247. //
  248. if (L'*' == *pwszMapping)
  249. {
  250. if (NULL == m_pwszStarScriptmap)
  251. m_pwszStarScriptmap = pwszMapping;
  252. continue;
  253. }
  254. // Digest the metadata.
  255. //
  256. LPWSTR rgpwsz[CSZ_SM_FIELDS];
  257. UINT cchUnused;
  258. if (!FParseMDData (pwszMapping,
  259. rgpwsz,
  260. CSZ_SM_FIELDS,
  261. &cchUnused))
  262. {
  263. // FParseMDData() will return FALSE if there is no verb
  264. // exclusion list because it is an optional parameter.
  265. // If all the other parameters exist though then it's
  266. // really ok.
  267. //
  268. if (!(rgpwsz[ISZ_SM_EXT] &&
  269. rgpwsz[ISZ_SM_PATH] &&
  270. rgpwsz[ISZ_SM_FLAGS]))
  271. {
  272. DebugTrace ("CScriptMap::FLoadScriptMaps() - Malformed scriptmaps\n");
  273. continue;
  274. }
  275. }
  276. // We belive that all the scriptmaps are
  277. // extension based. But other than that
  278. // there is no validation.
  279. //
  280. Assert (*rgpwsz[ISZ_SM_EXT] == L'.');
  281. // If the path refers to our DAV DLL then skip this mapping.
  282. //
  283. // The way this works is: If the length of the path is at least
  284. // as long as the length of our DLL name AND the final component
  285. // of that path is the name of our DLL then skip the mapping.
  286. // Eg. "HTTPEXT.DLL" will match the first condition of the if,
  287. // "c:\foo\bar\HTTPEXT.DLL" will match the second condition of the if.
  288. //
  289. static const UINT cchDll = CchConstString(L".DLL");
  290. UINT cchPath = static_cast<UINT>(wcslen(rgpwsz[ISZ_SM_PATH]));
  291. if (cchPath == cchDav + cchDll ||
  292. ((cchPath > cchDav + cchDll) &&
  293. *(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll - cchDav - 1) == L'\\'))
  294. {
  295. // Now we know the final piece of the path is the correct length.
  296. // Check the data! If it matches our dll name, skip this mapping.
  297. //
  298. if (!_wcsnicmp(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll - cchDav,
  299. gc_wszSignature,
  300. cchDav) &&
  301. !_wcsicmp(rgpwsz[ISZ_SM_PATH] + cchPath - cchDll,
  302. L".DLL"))
  303. {
  304. continue;
  305. }
  306. }
  307. // Feed the optional inclusion list into a header iterator
  308. // that AddMapping() will use to determine what verbs
  309. // are included for this mapping. If there is no inclusion
  310. // list then use an empty iterator.
  311. //
  312. // Adding a mapping with an empty iterator (vs. NULL)
  313. // allows the scriptmap matching code to distinguish
  314. // between a "real" scriptmap with an empty inclusion
  315. // list and a default CGI-style scriptmap like those
  316. // added at the beginning of this function.
  317. //
  318. it.NewHeader(rgpwsz[ISZ_SM_INCLUSION_LIST] ?
  319. rgpwsz[ISZ_SM_INCLUSION_LIST] :
  320. gc_wszEmpty);
  321. // Add the extension-based mapping
  322. //
  323. AddMapping (rgpwsz[ISZ_SM_EXT], &it);
  324. }
  325. return TRUE;
  326. }
  327. SCODE
  328. CScriptMap::ScMatched (
  329. LPCWSTR pwszMethod,
  330. METHOD_ID midMethod,
  331. LPCWSTR pwszURI,
  332. DWORD dwAccess,
  333. BOOL * pfCGI) const
  334. {
  335. LPCWSTR pwsz;
  336. SCODE sc = S_OK;
  337. Assert(pwszURI);
  338. //
  339. // Scan down the URI, looking for extensions. When one is found
  340. // zip through the list of mappings. While this may not seem the
  341. // most optimal, it really is. If we simply scaned the URI for
  342. // each mapping. We would be scaning the URI multiple times. In
  343. // this model, we scan the URI once.
  344. //
  345. if ((pwsz = wcsrchr(pwszURI, L'.')) != NULL)
  346. {
  347. // We have an extension so take a look
  348. //
  349. CIsMatch cim(m_cache, pwszMethod, midMethod, pwszURI, pwsz, dwAccess);
  350. m_cache.ForEach(cim);
  351. sc = cim.ScMatched();
  352. if (pfCGI && (sc != S_OK))
  353. *pfCGI = cim.FMatchIsCGI();
  354. }
  355. return sc;
  356. }
  357. // CLASS CIsMatch ------------------------------------------------------------
  358. //
  359. //$REVIEW: Does this code work for DBCS/UTF-8 map names? These are filenames....
  360. //$REVIEW: This function does not currently check the METHOD EXCLUSION LIST.
  361. //$REVIEW: This might cause us to report a match when actually there are NO matches.
  362. //
  363. BOOL
  364. CScriptMap::CIsMatch::operator()(const CRCWszi& crcwszi, const PINCLUSIONS& pin)
  365. {
  366. Assert (crcwszi.m_pwsz);
  367. // Every scriptmap in the cache should be an extension-based mapping.
  368. // Compare the extension vs. the part of the URI that we're looking at.
  369. // If they match then we have a scriptmap.
  370. //
  371. Assert (L'.' == *crcwszi.m_pwsz);
  372. UINT cch = static_cast<UINT>(wcslen (crcwszi.m_pwsz));
  373. if (!_wcsnicmp (crcwszi.m_pwsz, m_pwszMatch, cch) &&
  374. ((m_pwszMatch[cch] == '\0')
  375. || !wcscmp (m_pwszMatch+cch, L"/")
  376. || !wcscmp (m_pwszMatch+cch, L"\\")))
  377. {
  378. // Looks like we have a match
  379. //
  380. ScriptMapTrace ("Dav: %S matched scriptmap %S\n", m_pwszURI, crcwszi.m_pwsz);
  381. // However, we only allow execution of CGI type child
  382. // ISAPI's if EXECUTE priviledge is enabled
  383. //
  384. if ((pin != NULL) || (m_dwAccess & MD_ACCESS_EXECUTE))
  385. m_sc = W_DAV_SCRIPTMAP_MATCH_FOUND;
  386. m_fCGI = !pin;
  387. }
  388. // See if it is included
  389. // Note that, if all methods are included, no need to do further checking
  390. //
  391. if ((m_sc != S_OK) && pin && !pin->FAllMethodsIncluded())
  392. {
  393. ScriptMapTrace ("Dav: checking '%S' against scriptmap inclusions: %S\n",
  394. m_pwszMethod,
  395. pin->pwszMethods);
  396. // In the unknown method scenario, we just need
  397. // to iterate the set of methods that are included
  398. // and check it against the request method
  399. //
  400. if (m_midMethod == MID_UNKNOWN)
  401. {
  402. BOOL fIncluded = FALSE;
  403. HDRITER_W it(pin->pwszMethods);
  404. LPCWSTR pwsz;
  405. while ((pwsz = it.PszNext()) != NULL)
  406. {
  407. fIncluded = !wcscmp (pwsz, m_pwszMethod);
  408. if (fIncluded)
  409. break;
  410. }
  411. if (!fIncluded)
  412. {
  413. ScriptMapTrace ("Dav: unknown '%S' excluded from scriptmap\n",
  414. m_pwszMethod);
  415. m_sc = W_DAV_SCRIPTMAP_MATCH_EXCLUDED;
  416. }
  417. }
  418. //
  419. // Otherwise, the inclusions flags have the MID'th bit
  420. // set if it is excluded.
  421. //
  422. else if (!(pin->fdwInclusions & (1 << m_midMethod)))
  423. {
  424. ScriptMapTrace ("Dav: '%S' excluded from scriptmap\n",
  425. m_pwszMethod);
  426. m_sc = W_DAV_SCRIPTMAP_MATCH_EXCLUDED;
  427. }
  428. }
  429. return (m_sc == S_OK);
  430. }
  431. IScriptMap *
  432. NewScriptMap( LPWSTR pwszScriptMaps )
  433. {
  434. auto_ref_ptr<CScriptMap> pScriptMap;
  435. pScriptMap.take_ownership (new CScriptMap());
  436. if (pScriptMap->FInit(pwszScriptMaps))
  437. return pScriptMap.relinquish();
  438. return NULL;
  439. }