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.

3352 lines
84 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. w3metadata.cxx
  5. Abstract:
  6. Code to read metadata and generate W3_METADATA objects
  7. Author:
  8. Bilal Alam (balam) 23-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "redirect.hxx"
  16. ALLOC_CACHE_HANDLER * W3_METADATA::sm_pachW3MetaData;
  17. W3_METADATA::W3_METADATA( OBJECT_CACHE * pObjectCache )
  18. : CACHE_ENTRY ( pObjectCache ),
  19. _dwAccessPerm ( MD_ACCESS_READ ),
  20. _dwSslAccessPerm ( 0 ),
  21. _cbIpAccessCheck ( 0 ),
  22. _fUseAnonSubAuth ( FALSE ),
  23. _dwLogonMethod ( LOGON32_LOGON_NETWORK_CLEARTEXT ),
  24. _dwVrLevel ( 0 ),
  25. _dwVrLen ( 0 ),
  26. _pRedirectBlob ( NULL ),
  27. _dwDirBrowseFlags ( MD_DIRBROW_LOADDEFAULT ),
  28. _dwAuthentication ( 0 ),
  29. _dwAuthPersistence ( 0 ),
  30. _strVrUserName ( _rgVrUserName, sizeof( _rgVrUserName ) ),
  31. _strVrPasswd ( _rgVrPasswd, sizeof( _rgVrPasswd ) ),
  32. _strUserName ( _rgUserName, sizeof( _rgUserName ) ),
  33. _strPasswd ( _rgPasswd, sizeof( _rgPasswd ) ),
  34. _strDomainName ( _rgDomainName, sizeof( _rgDomainName ) ),
  35. _strRealm ( _rgRealm, sizeof( _rgRealm ) ),
  36. _mstrAuthProviders (),
  37. _pctVrToken ( NULL ),
  38. _pctAnonymousToken ( NULL ),
  39. _fCreateProcessAsUser ( TRUE ),
  40. _fCreateProcessNewConsole ( FALSE ),
  41. _fDoStaticCompression ( HTTP_COMPRESSION::QueryDoStaticCompression() ),
  42. _fDoDynamicCompression ( HTTP_COMPRESSION::QueryDoDynamicCompression() ),
  43. _dwCGIScriptTimeout ( DEFAULT_SCRIPT_TIMEOUT ),
  44. _ScriptMap (),
  45. _pMimeMap ( NULL ),
  46. _fSSIExecDisabled ( FALSE ),
  47. _fDontLog ( FALSE ),
  48. _fFooterEnabled ( FALSE ),
  49. _dwExpireMode ( EXPIRE_MODE_NONE ),
  50. _fHaveNoCache ( FALSE ),
  51. _fHaveMaxAge ( FALSE ),
  52. _fDoReverseDNS ( FALSE ),
  53. _cbEntityReadAhead ( DEFAULT_ENTITY_READ_AHEAD ),
  54. _dwMaxRequestEntityAllowed ( DEFAULT_MAX_REQUEST_ENTITY_ALLOWED ),
  55. _fNoCache ( FALSE ),
  56. _dwAppIsolated ( 0 ),
  57. _dwAppOopRecoverLimit ( 0 ),
  58. _fKeepAliveEnabled ( TRUE ),
  59. _cGetAllRecords ( NULL ),
  60. _pGetAllBuffer ( NULL ),
  61. _fAppPoolMatches ( FALSE ),
  62. _dwRequireMapping ( MD_PASSPORT_TRY_MAPPING ),
  63. _fUNCUserInvalid ( FALSE ),
  64. _cbMatchingUrlA ( 0 ),
  65. _cbMatchingPathA ( 0 )
  66. {
  67. //
  68. // Hmmm, since most of these values aren't getting initialized, if
  69. // somebody went and deleted all the metadata items from the tree,
  70. // then bad things could happen. We should initialize with defaults
  71. // things that might screw us
  72. //
  73. _dirmonConfig.hToken = NULL;
  74. _dirmonConfig.pszDirPath = NULL;
  75. }
  76. W3_METADATA::~W3_METADATA()
  77. {
  78. if ( _pctVrToken != NULL )
  79. {
  80. _pctVrToken->DereferenceCacheEntry();
  81. _pctVrToken = NULL;
  82. }
  83. if ( _pctAnonymousToken != NULL )
  84. {
  85. _pctAnonymousToken->DereferenceCacheEntry();
  86. _pctAnonymousToken = NULL;
  87. }
  88. if ( _pMimeMap != NULL )
  89. {
  90. delete _pMimeMap;
  91. _pMimeMap = NULL;
  92. }
  93. if ( _pRedirectBlob != NULL )
  94. {
  95. delete _pRedirectBlob;
  96. _pRedirectBlob = NULL;
  97. }
  98. if ( _pGetAllBuffer != NULL )
  99. {
  100. delete _pGetAllBuffer;
  101. _pGetAllBuffer = NULL;
  102. }
  103. DBG_ASSERT(CheckSignature());
  104. }
  105. //static
  106. HRESULT
  107. W3_METADATA::Initialize(
  108. VOID
  109. )
  110. /*++
  111. Routine Description:
  112. Initialize metadata lookaside
  113. Arguments:
  114. None
  115. Return Value:
  116. HRESULT
  117. --*/
  118. {
  119. ALLOC_CACHE_CONFIGURATION acConfig;
  120. HRESULT hr;
  121. //
  122. // Initialize allocation lookaside
  123. //
  124. acConfig.nConcurrency = 1;
  125. acConfig.nThreshold = 100;
  126. acConfig.cbSize = sizeof( W3_METADATA );
  127. DBG_ASSERT( sm_pachW3MetaData == NULL );
  128. sm_pachW3MetaData = new ALLOC_CACHE_HANDLER( "W3_METADATA",
  129. &acConfig );
  130. if ( sm_pachW3MetaData == NULL )
  131. {
  132. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  133. DBGPRINTF(( DBG_CONTEXT,
  134. "Error initializing sm_pachW3MetaData. hr = 0x%x\n",
  135. hr ));
  136. return hr;
  137. }
  138. DATA_SET_CACHE::Initialize();
  139. return NO_ERROR;
  140. }
  141. //static
  142. VOID
  143. W3_METADATA::Terminate(
  144. VOID
  145. )
  146. {
  147. DATA_SET_CACHE::Terminate();
  148. if ( sm_pachW3MetaData != NULL )
  149. {
  150. delete sm_pachW3MetaData;
  151. sm_pachW3MetaData = NULL;
  152. }
  153. }
  154. //
  155. // Private constants.
  156. //
  157. #define DEFAULT_MD_RECORDS 40
  158. #define DEFAULT_RECORD_SIZE 50
  159. # define DEF_MD_REC_SIZE ((1 + DEFAULT_MD_RECORDS) * \
  160. (sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE))
  161. HRESULT
  162. W3_METADATA::ReadMetaData(
  163. const STRU & strMetabasePath,
  164. const STRU & strURL
  165. )
  166. /*++
  167. Routine Description:
  168. Reads the metabase (directly) to get the metadata for the given URL
  169. Arguments:
  170. strMetabasePath - The preceding service/instance goo (like "LM/W3SVC/1/ROOT" )
  171. strURL - URL in question
  172. Return Value:
  173. HRESULT
  174. --*/
  175. {
  176. PMETADATA_GETALL_RECORD pMDRecord;
  177. DWORD dwNumMDRecords;
  178. DWORD dwNumWamRecords;
  179. BYTE tmpBuffer[ DEF_MD_REC_SIZE];
  180. BYTE tmpWamBuffer[ DEF_MD_REC_SIZE];
  181. BUFFER TempBuff( tmpBuffer, DEF_MD_REC_SIZE);
  182. BUFFER WamBuff( tmpWamBuffer, DEF_MD_REC_SIZE);
  183. DWORD i;
  184. DWORD dwDataSetNumber;
  185. DWORD dwWamDataSetNumber;
  186. WCHAR ch;
  187. LPWSTR pszInVr;
  188. LPWSTR pszMinInVr;
  189. LPWSTR pszURL;
  190. DWORD dwNeed;
  191. DWORD dwL;
  192. DWORD dwVRLen;
  193. BYTE tmpPrivateBuffer[ 20 ];
  194. BUFFER PrivateBuffer( tmpPrivateBuffer, 20 );
  195. DWORD dwPrivateBufferUsed;
  196. MB mb( g_pW3Server->QueryMDObject() );
  197. MB * pmb = &mb;
  198. HRESULT hr = NO_ERROR;
  199. WCHAR * pszStart = NULL;
  200. DWORD cchLength = 0;
  201. STACK_STRU( strAppPoolId, 256 );
  202. STACK_STRU( strMatchingUrl, 64 );
  203. WCHAR * pszProcessAppPoolId;
  204. BOOL fRet;
  205. //
  206. // We lie about modifying the input path
  207. //
  208. pszURL = (LPWSTR)strURL.QueryStr();
  209. DBG_ASSERT( pszURL != NULL );
  210. DBG_ASSERT( TempBuff.QuerySize() >=
  211. (DEFAULT_MD_RECORDS *
  212. (sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE))
  213. );
  214. DBG_ASSERT( WamBuff.QuerySize() >=
  215. (DEFAULT_MD_RECORDS *
  216. (sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE))
  217. );
  218. //
  219. // Read the metabase
  220. //
  221. if ( !pmb->Open( strMetabasePath.QueryStr() ))
  222. {
  223. hr = HRESULT_FROM_WIN32( GetLastError() );
  224. goto Failure;
  225. }
  226. //
  227. // Get the UT_FILE info
  228. //
  229. if ( !pmb->GetAll( pszURL,
  230. METADATA_INHERIT | METADATA_PARTIAL_PATH,
  231. IIS_MD_UT_FILE,
  232. &TempBuff,
  233. &dwNumMDRecords,
  234. &dwDataSetNumber ))
  235. {
  236. hr = HRESULT_FROM_WIN32( GetLastError() );
  237. goto Failure;
  238. }
  239. //
  240. // Get the UT_WAM info
  241. //
  242. if ( !pmb->GetAll( pszURL,
  243. METADATA_INHERIT | METADATA_PARTIAL_PATH,
  244. IIS_MD_UT_WAM,
  245. &WamBuff,
  246. &dwNumWamRecords,
  247. &dwWamDataSetNumber ))
  248. {
  249. hr = HRESULT_FROM_WIN32( GetLastError() );
  250. goto Failure;
  251. }
  252. //
  253. // Aarrggh. MD_APP_APPPOOL_ID is UT_SERVER. We want to get this property
  254. // so that we can validate that we don't execute AppPool A's stuff in a
  255. // process running as AppPool B (A != B)
  256. //
  257. // Note that it is OK if this call fails. WAS by default uses
  258. // DefaultAppPool as apppool
  259. //
  260. pmb->GetStr( pszURL,
  261. MD_APP_APPPOOL_ID,
  262. IIS_MD_UT_SERVER,
  263. &strAppPoolId,
  264. METADATA_INHERIT | METADATA_PARTIAL_PATH );
  265. pszProcessAppPoolId = (WCHAR*) UlAtqGetContextProperty( NULL,
  266. ULATQ_PROPERTY_APP_POOL_ID );
  267. DBG_ASSERT( pszProcessAppPoolId != NULL );
  268. //
  269. // For efficiency sake, we'll store whether the AppPools match. That way, we don't have
  270. // to repeat the determination per request.
  271. //
  272. if ( strAppPoolId.IsEmpty() )
  273. {
  274. _fAppPoolMatches = _wcsicmp( pszProcessAppPoolId, L"DefaultAppPool" ) == 0;
  275. }
  276. else
  277. {
  278. _fAppPoolMatches = _wcsicmp( pszProcessAppPoolId,
  279. strAppPoolId.QueryStr() ) == 0;
  280. }
  281. //
  282. // Both sets of data better have the same data set number
  283. //
  284. DBG_ASSERT( dwDataSetNumber == dwWamDataSetNumber );
  285. //
  286. // Set the data set number, so that this object is metadata cachable
  287. //
  288. _cacheKey.SetDataSetNumber( dwDataSetNumber );
  289. //
  290. // Grok the buffer containing all the records
  291. //
  292. pMDRecord = (PMETADATA_GETALL_RECORD)TempBuff.QueryPtr();
  293. i = 0;
  294. //
  295. // Check from where we got VR_PATH
  296. //
  297. pszMinInVr = pszURL ;
  298. if ( *pszURL )
  299. {
  300. for ( pszInVr = pszMinInVr + wcslen(pszMinInVr) ;; )
  301. {
  302. ch = *pszInVr;
  303. *pszInVr = L'\0';
  304. dwNeed = 0;
  305. if ( !pmb->GetString( pszURL,
  306. MD_VR_PATH,
  307. IIS_MD_UT_FILE,
  308. NULL,
  309. &dwNeed,
  310. 0 ) &&
  311. GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  312. {
  313. *pszInVr = ch;
  314. // VR_PATH was defined at this level !
  315. break;
  316. }
  317. *pszInVr = ch;
  318. if ( ch )
  319. {
  320. if ( pszInVr > pszMinInVr )
  321. {
  322. pszInVr--;
  323. }
  324. else
  325. {
  326. //
  327. // VR_PATH was defined above Instance vroot
  328. // or not at all. If defined above, then the reference
  329. // path is empty, so we can claim we found it.
  330. // if not defined, then this will be catch later.
  331. //
  332. break;
  333. }
  334. }
  335. // scan for previous delimiter
  336. while ( *pszInVr != L'/' && *pszInVr != L'\\' )
  337. {
  338. if ( pszInVr > pszMinInVr )
  339. {
  340. pszInVr--;
  341. }
  342. else
  343. {
  344. //
  345. // VR_PATH was defined above Instance vroot
  346. // or not at all. If defined above, then the reference
  347. // path is empty, so we can claim we found it.
  348. // if not defined, then this will be catch later.
  349. //
  350. break;
  351. }
  352. }
  353. }
  354. dwVRLen = DIFF(pszInVr - pszMinInVr);
  355. }
  356. else
  357. {
  358. dwVRLen = 0;
  359. pszInVr = pszMinInVr;
  360. }
  361. // Close this now to minimize lock contention.
  362. DBG_REQUIRE(pmb->Close());
  363. for ( dwL = 0 ; pszMinInVr < pszInVr - 1 ; pszMinInVr++ )
  364. {
  365. if ( *pszMinInVr == L'/' || *pszMinInVr == L'\\' )
  366. {
  367. ++dwL;
  368. }
  369. }
  370. //
  371. // Store away an ANSI copy of the matching URL
  372. //
  373. hr = strMatchingUrl.Copy( pszURL );
  374. if ( FAILED( hr ) )
  375. {
  376. goto Failure;
  377. }
  378. DBG_ASSERT( strMatchingUrl.QueryCCH() >= dwVRLen );
  379. strMatchingUrl.SetLen( dwVRLen );
  380. hr = _strMatchingUrlA.CopyW( strMatchingUrl.QueryStr() );
  381. if ( FAILED( hr ) )
  382. {
  383. goto Failure;
  384. }
  385. _cbMatchingUrlA = strlen( _strMatchingUrlA.QueryStr() );
  386. //
  387. // Now walk through the array of returned metadata objects and format
  388. // each one into our predigested form.
  389. //
  390. _dwVrLevel = dwL;
  391. _dwVrLen = dwVRLen;
  392. dwPrivateBufferUsed = 0;
  393. for ( ; i < dwNumMDRecords; i++, pMDRecord++ ) {
  394. PVOID pDataPointer;
  395. pDataPointer = (PVOID) ((PCHAR)TempBuff.QueryPtr() +
  396. pMDRecord->dwMDDataOffset);
  397. DBG_ASSERT(pMDRecord->dwMDDataTag == 0);
  398. switch ( pMDRecord->dwMDIdentifier )
  399. {
  400. case MD_ALLOW_KEEPALIVES:
  401. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  402. {
  403. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  404. goto Failure;
  405. }
  406. _fKeepAliveEnabled = *(DWORD *)pDataPointer;
  407. break;
  408. case MD_FOOTER_DOCUMENT:
  409. if (pMDRecord->dwMDDataType != STRING_METADATA)
  410. {
  411. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  412. goto Failure;
  413. }
  414. if (*(WCHAR *)pDataPointer != L'\0')
  415. {
  416. if (FAILED(hr = ReadCustomFooter((WCHAR *)pDataPointer)))
  417. {
  418. goto Failure;
  419. }
  420. }
  421. break;
  422. case MD_FOOTER_ENABLED:
  423. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  424. {
  425. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  426. goto Failure;
  427. }
  428. _fFooterEnabled = !!*((DWORD *) pDataPointer );
  429. if ( _fFooterEnabled )
  430. {
  431. //
  432. // If we have footers for a static file, we cannot do static
  433. // compression on it
  434. //
  435. _fDoStaticCompression = FALSE;
  436. }
  437. break;
  438. case MD_HTTP_EXPIRES:
  439. if (pMDRecord->dwMDDataType != STRING_METADATA)
  440. {
  441. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  442. goto Failure;
  443. }
  444. if (FAILED(hr = SetExpire((WCHAR *)pDataPointer)))
  445. {
  446. goto Failure;
  447. }
  448. break;
  449. case MD_CC_NO_CACHE:
  450. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  451. {
  452. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  453. goto Failure;
  454. }
  455. if (*(BOOL *)pDataPointer)
  456. {
  457. _fHaveNoCache = TRUE;
  458. }
  459. break;
  460. case MD_CC_MAX_AGE:
  461. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  462. {
  463. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  464. goto Failure;
  465. }
  466. _dwMaxAge = *(DWORD *)pDataPointer;
  467. _fHaveMaxAge = TRUE;
  468. break;
  469. case MD_CC_OTHER:
  470. if (pMDRecord->dwMDDataType != STRING_METADATA)
  471. {
  472. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  473. goto Failure;
  474. }
  475. if (FAILED(hr = _strCacheControlHeader.CopyWTruncate((WCHAR *)pDataPointer)))
  476. {
  477. goto Failure;
  478. }
  479. break;
  480. case MD_HTTP_REDIRECT:
  481. {
  482. if (pMDRecord->dwMDDataType != STRING_METADATA)
  483. {
  484. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  485. goto Failure;
  486. }
  487. STACK_STRU( strRealSource, MAX_PATH );
  488. STACK_STRU( strDestination, MAX_PATH );
  489. if (FAILED(hr = strDestination.Copy((WCHAR *)pDataPointer)) ||
  490. FAILED(hr = GetTrueRedirectionSource(
  491. pszURL,
  492. strMetabasePath.QueryStr(),
  493. &strRealSource)) ||
  494. FAILED(hr = SetRedirectionBlob(strRealSource,
  495. strDestination)))
  496. {
  497. goto Failure;
  498. }
  499. break;
  500. }
  501. case MD_DONT_LOG:
  502. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  503. {
  504. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  505. goto Failure;
  506. }
  507. _fDontLog = *(BOOL *)pDataPointer;
  508. break;
  509. case MD_CREATE_PROCESS_AS_USER:
  510. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  511. {
  512. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  513. goto Failure;
  514. }
  515. _fCreateProcessAsUser = *(BOOL *)pDataPointer;
  516. break;
  517. case MD_CREATE_PROC_NEW_CONSOLE:
  518. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  519. {
  520. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  521. goto Failure;
  522. }
  523. _fCreateProcessNewConsole = *(BOOL *)pDataPointer;
  524. break;
  525. case MD_SCRIPT_TIMEOUT:
  526. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  527. {
  528. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  529. goto Failure;
  530. }
  531. _dwCGIScriptTimeout = *(DWORD *)pDataPointer;
  532. break;
  533. case MD_MIME_MAP:
  534. if (pMDRecord->dwMDDataType != MULTISZ_METADATA)
  535. {
  536. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  537. goto Failure;
  538. }
  539. if (*(WCHAR *)pDataPointer)
  540. {
  541. _pMimeMap = new MIME_MAP((WCHAR *)pDataPointer);
  542. if (_pMimeMap == NULL)
  543. {
  544. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  545. goto Failure;
  546. }
  547. }
  548. break;
  549. case MD_HC_DO_NAMESPACE_DYNAMIC_COMPRESSION:
  550. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  551. {
  552. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  553. goto Failure;
  554. }
  555. _fDoDynamicCompression = *(BOOL *)pDataPointer;
  556. break;
  557. case MD_HC_DO_NAMESPACE_STATIC_COMPRESSION:
  558. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  559. {
  560. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  561. goto Failure;
  562. }
  563. _fDoStaticCompression = *(BOOL *)pDataPointer;
  564. break;
  565. case MD_ANONYMOUS_USER_NAME:
  566. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  567. {
  568. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  569. goto Failure;
  570. }
  571. if ( FAILED( hr = _strUserName.Copy(
  572. (WCHAR *)pDataPointer) ) )
  573. {
  574. goto Failure;
  575. }
  576. break;
  577. case MD_ANONYMOUS_PWD:
  578. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  579. {
  580. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  581. goto Failure;
  582. }
  583. if ( FAILED( hr = _strPasswd.Copy(
  584. (WCHAR *)pDataPointer) ) )
  585. {
  586. goto Failure;
  587. }
  588. break;
  589. case MD_ANONYMOUS_USE_SUBAUTH:
  590. if (pMDRecord->dwMDDataType != DWORD_METADATA)
  591. {
  592. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  593. goto Failure;
  594. }
  595. _fUseAnonSubAuth = *(BOOL *)pDataPointer;
  596. break;
  597. case MD_DEFAULT_LOGON_DOMAIN:
  598. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  599. {
  600. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  601. goto Failure;
  602. }
  603. if ( FAILED( hr = _strDomainName.Copy(
  604. (WCHAR *)pDataPointer) ) )
  605. {
  606. goto Failure;
  607. }
  608. break;
  609. case MD_HTTP_PICS:
  610. case MD_HTTP_CUSTOM:
  611. if ( pMDRecord->dwMDDataType != MULTISZ_METADATA )
  612. {
  613. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  614. goto Failure;
  615. }
  616. //
  617. // Copy all the specified headers into our header buffer
  618. //
  619. pszStart = (WCHAR*) pDataPointer;
  620. while ( *pszStart != L'\0' )
  621. {
  622. cchLength = wcslen( pszStart );
  623. hr = _strCustomHeaders.AppendW( pszStart );
  624. if ( FAILED( hr ) )
  625. {
  626. goto Failure;
  627. }
  628. hr = _strCustomHeaders.Append( "\r\n", 2 );
  629. if ( FAILED( hr ) )
  630. {
  631. goto Failure;
  632. }
  633. pszStart += ( cchLength + 1 );
  634. }
  635. break;
  636. case MD_LOGON_METHOD:
  637. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  638. {
  639. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  640. goto Failure;
  641. }
  642. //
  643. // The MD_LOGON_METHOD values in the metabase don't match
  644. // the NT logon values, so we'll convert them
  645. //
  646. switch ( *((DWORD *) pDataPointer ) )
  647. {
  648. case MD_LOGON_BATCH:
  649. _dwLogonMethod = LOGON32_LOGON_BATCH;
  650. break;
  651. case MD_LOGON_INTERACTIVE:
  652. _dwLogonMethod = LOGON32_LOGON_INTERACTIVE;
  653. break;
  654. case MD_LOGON_NETWORK:
  655. _dwLogonMethod = LOGON32_LOGON_NETWORK;
  656. break;
  657. case MD_LOGON_NETWORK_CLEARTEXT:
  658. _dwLogonMethod = LOGON32_LOGON_NETWORK_CLEARTEXT;
  659. break;
  660. default:
  661. break;
  662. }
  663. break;
  664. case MD_AUTHORIZATION:
  665. if( pMDRecord->dwMDDataType != DWORD_METADATA )
  666. {
  667. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  668. goto Failure;
  669. }
  670. _dwAuthentication = *((DWORD *) pDataPointer );
  671. break;
  672. case MD_REALM:
  673. if( pMDRecord->dwMDDataType != STRING_METADATA )
  674. {
  675. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  676. goto Failure;
  677. }
  678. if( FAILED( hr = _strRealm.Copy( ( WCHAR* )pDataPointer ) ) )
  679. {
  680. goto Failure;
  681. }
  682. break;
  683. case MD_NTAUTHENTICATION_PROVIDERS:
  684. if( pMDRecord->dwMDDataType != STRING_METADATA )
  685. {
  686. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  687. goto Failure;
  688. }
  689. hr = BuildProviderList( ( WCHAR* )pDataPointer );
  690. if ( FAILED( hr ) )
  691. {
  692. goto Failure;
  693. }
  694. break;
  695. case MD_AUTHORIZATION_PERSISTENCE:
  696. if( pMDRecord->dwMDDataType != DWORD_METADATA )
  697. {
  698. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  699. goto Failure;
  700. }
  701. _dwAuthPersistence = *((DWORD *) pDataPointer );
  702. break;
  703. case MD_IP_SEC:
  704. if( pMDRecord->dwMDDataType != BINARY_METADATA )
  705. {
  706. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  707. goto Failure;
  708. }
  709. if ( pMDRecord->dwMDDataLen )
  710. {
  711. hr = SetIpAccessCheck( pDataPointer,
  712. pMDRecord->dwMDDataLen );
  713. if ( FAILED( hr ) )
  714. {
  715. goto Failure;
  716. }
  717. }
  718. break;
  719. case MD_ACCESS_PERM:
  720. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  721. {
  722. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  723. goto Failure;
  724. }
  725. _dwAccessPerm = *((DWORD*) pDataPointer);
  726. break;
  727. case MD_SSL_ACCESS_PERM:
  728. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  729. {
  730. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  731. goto Failure;
  732. }
  733. _dwSslAccessPerm = *((DWORD*)pDataPointer) & MD_SSL_ACCESS_MASK;
  734. break;
  735. case MD_VR_PATH:
  736. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  737. {
  738. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  739. goto Failure;
  740. }
  741. if ( FAILED( hr = _strVrPath.Copy((WCHAR *)pDataPointer) ) )
  742. {
  743. goto Failure;
  744. }
  745. if ( FAILED( hr = _strMatchingPathA.CopyW((WCHAR *)pDataPointer) ) )
  746. {
  747. goto Failure;
  748. }
  749. _cbMatchingPathA = strlen( _strMatchingPathA.QueryStr() );
  750. break;
  751. case MD_APP_ROOT:
  752. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  753. {
  754. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  755. goto Failure;
  756. }
  757. if ( FAILED( hr = _strAppMetaPath.Copy((WCHAR *)pDataPointer) ) )
  758. {
  759. goto Failure;
  760. }
  761. break;
  762. case MD_VR_USERNAME:
  763. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  764. {
  765. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  766. goto Failure;
  767. }
  768. if ( FAILED( hr = _strVrUserName.Copy(
  769. (WCHAR *)pDataPointer) ) )
  770. {
  771. goto Failure;
  772. }
  773. break;
  774. case MD_VR_PASSWORD:
  775. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  776. {
  777. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  778. goto Failure;
  779. }
  780. if ( FAILED( hr = _strVrPasswd.Copy(
  781. (WCHAR *)pDataPointer) ) )
  782. {
  783. goto Failure;
  784. }
  785. break;
  786. case MD_REDIRECT_HEADERS:
  787. if ( pMDRecord->dwMDDataType != MULTISZ_METADATA )
  788. {
  789. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  790. goto Failure;
  791. }
  792. //
  793. // Copy all the specified headers into our header buffer
  794. //
  795. pszStart = (WCHAR*) pDataPointer;
  796. while ( *pszStart != L'\0' )
  797. {
  798. cchLength = wcslen( pszStart );
  799. hr = _strRedirectHeaders.AppendW( pszStart );
  800. if ( FAILED( hr ) )
  801. {
  802. goto Failure;
  803. }
  804. hr = _strRedirectHeaders.Append( "\r\n", 2 );
  805. if ( FAILED( hr ) )
  806. {
  807. goto Failure;
  808. }
  809. pszStart += ( cchLength + 1 );
  810. }
  811. break;
  812. case MD_DIRECTORY_BROWSING:
  813. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  814. {
  815. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  816. goto Failure;
  817. }
  818. _dwDirBrowseFlags = *((DWORD *) pDataPointer );
  819. break;
  820. case MD_DEFAULT_LOAD_FILE:
  821. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  822. {
  823. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  824. goto Failure;
  825. }
  826. hr = _strDefaultLoad.Copy( (WCHAR*) pDataPointer );
  827. if ( FAILED( hr ) )
  828. {
  829. goto Failure;
  830. }
  831. break;
  832. case MD_SCRIPT_MAPS:
  833. if ( pMDRecord->dwMDDataType != MULTISZ_METADATA )
  834. {
  835. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  836. goto Failure;
  837. }
  838. hr = _ScriptMap.Initialize( (WCHAR *)pDataPointer );
  839. if( FAILED(hr) )
  840. {
  841. goto Failure;
  842. }
  843. break;
  844. case MD_CUSTOM_ERROR:
  845. if ( pMDRecord->dwMDDataType != MULTISZ_METADATA )
  846. {
  847. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  848. goto Failure;
  849. }
  850. //
  851. // An empty string means use hard-coded errors
  852. //
  853. if ( *((WCHAR*)pDataPointer) == L'\0' )
  854. {
  855. break;
  856. }
  857. hr = _customErrorTable.BuildTable( (WCHAR*) pDataPointer );
  858. if ( FAILED( hr ) )
  859. {
  860. goto Failure;
  861. }
  862. break;
  863. case MD_SSI_EXEC_DISABLED:
  864. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  865. {
  866. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  867. goto Failure;
  868. }
  869. _fSSIExecDisabled = !!*( ( DWORD * ) pDataPointer );
  870. break;
  871. case MD_DO_REVERSE_DNS:
  872. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  873. {
  874. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  875. goto Failure;
  876. }
  877. _fDoReverseDNS = !!*((DWORD*)pDataPointer);
  878. break;
  879. case MD_UPLOAD_READAHEAD_SIZE:
  880. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  881. {
  882. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  883. goto Failure;
  884. }
  885. _cbEntityReadAhead = *(DWORD*)pDataPointer;
  886. break;
  887. case MD_MAX_REQUEST_ENTITY_ALLOWED:
  888. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  889. {
  890. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  891. goto Failure;
  892. }
  893. _dwMaxRequestEntityAllowed = *(DWORD*)pDataPointer;
  894. break;
  895. case MD_VR_NO_CACHE:
  896. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  897. {
  898. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  899. goto Failure;
  900. }
  901. _fNoCache = !!*((DWORD*)pDataPointer);
  902. break;
  903. case MD_PASSPORT_REQUIRE_AD_MAPPING:
  904. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  905. {
  906. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  907. goto Failure;
  908. }
  909. _dwRequireMapping = *((DWORD*)pDataPointer);
  910. break;
  911. default:
  912. // BUGBUGBUG
  913. DBG_ASSERT(CheckSignature());
  914. break;
  915. }
  916. }
  917. //
  918. // Walk through the WAM data
  919. //
  920. pMDRecord = (PMETADATA_GETALL_RECORD)WamBuff.QueryPtr();
  921. i = 0;
  922. for ( ; i < dwNumWamRecords; i++, pMDRecord++ ) {
  923. PVOID pDataPointer;
  924. pDataPointer = (PVOID) ((PCHAR)WamBuff.QueryPtr() +
  925. pMDRecord->dwMDDataOffset);
  926. DBG_ASSERT(pMDRecord->dwMDDataTag == 0);
  927. switch ( pMDRecord->dwMDIdentifier )
  928. {
  929. case MD_APP_ISOLATED:
  930. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  931. {
  932. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  933. goto Failure;
  934. }
  935. _dwAppIsolated = *(DWORD*)pDataPointer;
  936. break;
  937. case MD_APP_WAM_CLSID:
  938. if ( pMDRecord->dwMDDataType != STRING_METADATA )
  939. {
  940. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  941. goto Failure;
  942. }
  943. hr = _strWamClsId.Copy( (WCHAR*)pDataPointer );
  944. break;
  945. case MD_APP_OOP_RECOVER_LIMIT:
  946. if ( pMDRecord->dwMDDataType != DWORD_METADATA )
  947. {
  948. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  949. goto Failure;
  950. }
  951. _dwAppOopRecoverLimit = *(DWORD*)pDataPointer;
  952. break;
  953. default:
  954. break;
  955. }
  956. }
  957. //
  958. // If no-cache, max-age or a dynamic expires directive is present, add
  959. // it to Cache-Control header
  960. //
  961. if (FAILED(hr = SetCacheControlHeader()))
  962. {
  963. goto Failure;
  964. }
  965. if ( _strVrPath.IsEmpty() )
  966. {
  967. DBGPRINTF(( DBG_CONTEXT,
  968. "[ReadMetaData] Virtual Dir Path mapping not found\n" ));
  969. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  970. goto Failure;
  971. }
  972. //
  973. // If this is an UNC share, logon using associated credentials
  974. // keep a reference to this access token in the cache
  975. //
  976. if ( _strVrPath.QueryStr()[0] == L'\\' &&
  977. _strVrPath.QueryStr()[1] == L'\\' )
  978. {
  979. if ( _strVrUserName.QueryStr() != NULL &&
  980. _strVrPasswd.QueryStr() != NULL &&
  981. _strVrUserName.QueryStr()[0] )
  982. {
  983. hr = CreateUNCVrToken( _strVrUserName.QueryStr(),
  984. _strVrPasswd.QueryStr() );
  985. if( SUCCEEDED( hr ) )
  986. {
  987. hr = EncryptMemoryPassword( &_strVrPasswd );
  988. if( FAILED( hr ) )
  989. {
  990. goto Failure;
  991. }
  992. }
  993. else
  994. {
  995. //
  996. // Continue metadata creation, but remember the failure
  997. //
  998. hr = NO_ERROR;
  999. _fUNCUserInvalid = TRUE;
  1000. }
  1001. }
  1002. }
  1003. //
  1004. // Setup the dirmon configuration
  1005. //
  1006. if ( _pctVrToken != NULL )
  1007. {
  1008. _dirmonConfig.hToken = _pctVrToken->QueryImpersonationToken();
  1009. }
  1010. else
  1011. {
  1012. _dirmonConfig.hToken = NULL;
  1013. }
  1014. _dirmonConfig.pszDirPath = _strVrPath.QueryStr();
  1015. //
  1016. // Build a default provider list for SSPI auth if the
  1017. // NTAuthenticationProviders is not set in the metabase.
  1018. //
  1019. if( _mstrAuthProviders.IsEmpty() )
  1020. {
  1021. hr = BuildDefaultProviderList();
  1022. if( FAILED( hr ) )
  1023. {
  1024. goto Failure;
  1025. }
  1026. }
  1027. //
  1028. // Get anonymous user token
  1029. //
  1030. hr = CreateAnonymousToken( _strUserName.QueryStr(),
  1031. _strPasswd.QueryStr() );
  1032. if ( FAILED( hr ) )
  1033. {
  1034. goto Failure;
  1035. }
  1036. hr = EncryptMemoryPassword( &_strPasswd );
  1037. if( FAILED( hr ) )
  1038. {
  1039. goto Failure;
  1040. }
  1041. DBG_ASSERT( CheckSignature() );
  1042. return S_OK;
  1043. Failure:
  1044. return hr;
  1045. }
  1046. HRESULT
  1047. W3_METADATA::BuildPhysicalPath(
  1048. STRU & strUrl,
  1049. STRU * pstrPhysicalPath
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. From the current metadata, convert a URL to a physical path
  1054. (using the MD_VR_ROOT property and inheritance level calculated on read)
  1055. Arguments:
  1056. strUrl - Virtual path
  1057. pstrPhysicalPath - String filled with physical path of strURL
  1058. Return Value:
  1059. BOOL
  1060. --*/
  1061. {
  1062. LPWSTR pszInVr;
  1063. DWORD dwL;
  1064. WCHAR ch;
  1065. HRESULT hr = S_OK;
  1066. DBG_ASSERT(CheckSignature());
  1067. //
  1068. // Build physical path from VR_PATH & portion of URI not used to define VR_PATH
  1069. //
  1070. //
  1071. // skip the URI components used to locate the virtual root
  1072. //
  1073. pszInVr = strUrl.QueryStr();
  1074. dwL = _dwVrLevel;
  1075. while ( dwL-- )
  1076. {
  1077. if ( *pszInVr )
  1078. {
  1079. DBG_ASSERT( *pszInVr == L'/' || *pszInVr == L'\\' );
  1080. ++pszInVr;
  1081. while ( (ch = *pszInVr) && ch != L'/' && ch !=L'\\' )
  1082. {
  1083. pszInVr++;
  1084. }
  1085. }
  1086. }
  1087. DBG_ASSERT( dwL == (DWORD)-1 );
  1088. if ( FAILED(hr = pstrPhysicalPath->Copy( _strVrPath ) ) )
  1089. {
  1090. return hr;
  1091. }
  1092. //
  1093. // Add a path delimiter char between virtual root mount point & significant part of URI
  1094. //
  1095. if ( pstrPhysicalPath->QueryCCH() )
  1096. {
  1097. ch = pstrPhysicalPath->QueryStr()[ pstrPhysicalPath->QueryCCH() - 1 ];
  1098. if ( (ch == L'/' || ch == L'\\') && *pszInVr )
  1099. {
  1100. ++pszInVr;
  1101. }
  1102. }
  1103. if ( FAILED(hr = pstrPhysicalPath->Append( pszInVr ) ) )
  1104. {
  1105. return hr;
  1106. }
  1107. //
  1108. // insure physical path last char uses standard directory delimiter
  1109. //
  1110. FlipSlashes( pstrPhysicalPath->QueryStr() );
  1111. return NO_ERROR;
  1112. }
  1113. HRESULT
  1114. W3_METADATA::BuildProviderList(
  1115. IN WCHAR * pszProviders
  1116. )
  1117. /*++
  1118. Description:
  1119. Builds a name array of SSPI Authentication providers
  1120. Arguments:
  1121. pszProviders - Comma separated list of providers
  1122. Returns:
  1123. HRESULT
  1124. --*/
  1125. {
  1126. WCHAR * pszCursor;
  1127. WCHAR * pszEnd;
  1128. BOOL fFinished = FALSE;
  1129. DBG_ASSERT(CheckSignature());
  1130. if ( pszProviders == NULL )
  1131. {
  1132. DBG_ASSERT( FALSE );
  1133. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1134. }
  1135. //
  1136. // Parse comma delimited list of providers, removing white space
  1137. //
  1138. pszCursor = SkipWhite( pszProviders );
  1139. for( ; ; )
  1140. {
  1141. pszEnd = wcschr( pszCursor, L',' );
  1142. if ( pszEnd == NULL )
  1143. {
  1144. fFinished = TRUE;
  1145. pszEnd = pszCursor + wcslen( pszCursor );
  1146. }
  1147. while ( pszEnd > pszCursor )
  1148. {
  1149. if ( !ISWHITEW( *pszEnd ) )
  1150. {
  1151. break;
  1152. }
  1153. pszEnd--;
  1154. }
  1155. *pszEnd = L'\0';
  1156. if ( !_mstrAuthProviders.AppendW( pszCursor ) )
  1157. {
  1158. return HRESULT_FROM_WIN32( GetLastError() );
  1159. }
  1160. if ( fFinished )
  1161. {
  1162. break;
  1163. }
  1164. //
  1165. // Advance to next provider
  1166. //
  1167. pszCursor = SkipWhite( pszEnd + 1 );
  1168. }
  1169. return NO_ERROR;
  1170. }
  1171. HRESULT
  1172. W3_METADATA::BuildDefaultProviderList(
  1173. VOID
  1174. )
  1175. /*++
  1176. Description:
  1177. Builds a default name array of SSPI Authentication providers
  1178. if NTAuthenticationProviders is not set in metabase
  1179. Arguments:
  1180. NONE
  1181. Returns:
  1182. HRESULT
  1183. --*/
  1184. {
  1185. DBG_ASSERT( CheckSignature() );
  1186. DBG_ASSERT( _mstrAuthProviders.IsEmpty() );
  1187. if ( !_mstrAuthProviders.Append( "Negotiate" ) )
  1188. {
  1189. return HRESULT_FROM_WIN32( GetLastError() );
  1190. }
  1191. if ( !_mstrAuthProviders.Append( "NTLM" ) )
  1192. {
  1193. return HRESULT_FROM_WIN32( GetLastError() );
  1194. }
  1195. return NO_ERROR;
  1196. }
  1197. BOOL
  1198. W3_METADATA::CheckAuthProvider(
  1199. IN const CHAR * pszPkgName
  1200. )
  1201. /*++
  1202. Description:
  1203. Check if we support the SSP package of name pszPkgName
  1204. Arguments:
  1205. pszPkgName - Name of the package we check against
  1206. Returns:
  1207. TRUE if we support the package, FALSE if we don't.
  1208. --*/
  1209. {
  1210. const CHAR * pszProvider;
  1211. DBG_ASSERT( CheckSignature() );
  1212. if ( pszPkgName == NULL )
  1213. {
  1214. DBG_ASSERT( FALSE );
  1215. return FALSE;
  1216. }
  1217. pszProvider = _mstrAuthProviders.First();
  1218. while ( pszProvider != NULL )
  1219. {
  1220. if ( _stricmp( pszPkgName, pszProvider ) == 0 )
  1221. {
  1222. return TRUE;
  1223. }
  1224. pszProvider = _mstrAuthProviders.Next( pszProvider );
  1225. }
  1226. return FALSE;
  1227. }
  1228. HRESULT
  1229. W3_METADATA::CreateUNCVrToken(
  1230. IN LPWSTR pszUserName,
  1231. IN LPWSTR pszPasswd
  1232. )
  1233. /*++
  1234. Description:
  1235. Logon the user account for the UNC virtual path
  1236. Arguments:
  1237. pszUserName - User name of the account in format domain\username
  1238. pszPasswd - Passwd of the account
  1239. Returns:
  1240. HRESULT
  1241. --*/
  1242. {
  1243. STACK_STRU( strUserName, UNLEN + 1 );
  1244. STACK_STRU( strDomainName, IIS_DNLEN + 1 );
  1245. // add 1 to strUserDomain for separator "\"
  1246. STACK_STRU( strUserDomain, UNLEN + IIS_DNLEN + 1 + 1);
  1247. HRESULT hr;
  1248. DWORD dwError;
  1249. BOOL fPossibleUPNLogon = FALSE;
  1250. hr = strUserDomain.Copy( pszUserName );
  1251. if ( FAILED( hr ) )
  1252. {
  1253. return hr;
  1254. }
  1255. hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomain,
  1256. &strUserName,
  1257. &strDomainName,
  1258. NULL,
  1259. &fPossibleUPNLogon );
  1260. if ( FAILED( hr ) )
  1261. {
  1262. return hr;
  1263. }
  1264. DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
  1265. hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
  1266. strUserName.QueryStr(),
  1267. strDomainName.QueryStr(),
  1268. pszPasswd,
  1269. QueryLogonMethod(),
  1270. FALSE,
  1271. fPossibleUPNLogon,
  1272. NULL,
  1273. &_pctVrToken,
  1274. &dwError );
  1275. if ( FAILED( hr ) )
  1276. {
  1277. return hr;
  1278. }
  1279. //
  1280. // If we didn't get a token, this is a fatal error. It means bad config
  1281. // so we will prop back an error which means Error 500
  1282. //
  1283. if ( _pctVrToken == NULL )
  1284. {
  1285. DBG_ASSERT( dwError != ERROR_SUCCESS );
  1286. //
  1287. // Make the error ERROR_LOGON_FAILURE so that we can generate the appropriate
  1288. // custom error response
  1289. //
  1290. hr = HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE );
  1291. return hr;
  1292. }
  1293. return NO_ERROR;
  1294. }
  1295. HRESULT
  1296. W3_METADATA::CreateAnonymousToken(
  1297. IN LPWSTR pszUserName,
  1298. IN LPWSTR pszPasswd
  1299. )
  1300. /*++
  1301. Description:
  1302. Logon the user account for the UNC virtual path
  1303. Arguments:
  1304. pszUserName - User name of the account in format domain\username
  1305. pszPasswd - Passwd of the account
  1306. Returns:
  1307. HRESULT
  1308. --*/
  1309. {
  1310. STACK_STRU( strUserName, UNLEN );
  1311. STACK_STRU( strDomainName, IIS_DNLEN );
  1312. // add 1 to strUserDomain for separator "\"
  1313. STACK_STRU( strUserDomain, UNLEN + IIS_DNLEN + 1 );
  1314. HRESULT hr;
  1315. DWORD dwLogonError;
  1316. BOOL fPossibleUPNLogon = FALSE;
  1317. BOOL fUseAnonSubAuth = FALSE;
  1318. hr = strUserDomain.Copy( pszUserName );
  1319. if ( FAILED( hr ) )
  1320. {
  1321. return hr;
  1322. }
  1323. hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomain,
  1324. &strUserName,
  1325. &strDomainName,
  1326. NULL,
  1327. &fPossibleUPNLogon );
  1328. if ( FAILED( hr ) )
  1329. {
  1330. return hr;
  1331. }
  1332. DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
  1333. if( _fUseAnonSubAuth )
  1334. {
  1335. if( !W3_STATE_AUTHENTICATION::sm_fSubAuthConfigured )
  1336. {
  1337. if( W3_STATE_AUTHENTICATION::sm_lSubAuthAnonEvent == 0 )
  1338. {
  1339. if( !InterlockedExchange( &W3_STATE_AUTHENTICATION::sm_lSubAuthAnonEvent, 1 ) )
  1340. {
  1341. //
  1342. // The registry key for iissuba is not configured correctly on local machine
  1343. //
  1344. g_pW3Server->LogEvent( W3_EVENT_SUBAUTH_REGISTRY_CONFIGURATION_LOCAL,
  1345. 0,
  1346. NULL );
  1347. }
  1348. }
  1349. }
  1350. else if( !W3_STATE_AUTHENTICATION::sm_fLocalSystem )
  1351. {
  1352. if( W3_STATE_AUTHENTICATION::sm_lLocalSystemEvent == 0 )
  1353. {
  1354. if( !InterlockedExchange( &W3_STATE_AUTHENTICATION::sm_lLocalSystemEvent, 1 ) )
  1355. {
  1356. //
  1357. // The process token does not have SeTcbPrivilege
  1358. //
  1359. g_pW3Server->LogEvent( W3_EVENT_SUBAUTH_LOCAL_SYSTEM,
  1360. 0,
  1361. NULL );
  1362. }
  1363. }
  1364. }
  1365. else
  1366. {
  1367. fUseAnonSubAuth = TRUE;
  1368. }
  1369. }
  1370. hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
  1371. strUserName.QueryStr(),
  1372. strDomainName.QueryStr(),
  1373. pszPasswd,
  1374. QueryLogonMethod(),
  1375. fUseAnonSubAuth,
  1376. fPossibleUPNLogon,
  1377. NULL,
  1378. &_pctAnonymousToken,
  1379. &dwLogonError );
  1380. if ( FAILED( hr ) )
  1381. {
  1382. return hr;
  1383. }
  1384. return NO_ERROR;
  1385. }
  1386. HRESULT
  1387. W3_METADATA::GetAndRefAnonymousToken(
  1388. TOKEN_CACHE_ENTRY ** ppCachedToken
  1389. )
  1390. /*++
  1391. Description:
  1392. Get the anonymous token for the current request.
  1393. Everybody that calls this function needs to call
  1394. deref when it's done.
  1395. Arguments:
  1396. NONE
  1397. Returns:
  1398. HRESULT
  1399. --*/
  1400. {
  1401. HRESULT hr = S_OK;
  1402. BOOL fReadLocked = TRUE;
  1403. //
  1404. // If you want to modify the stack buffer length, make sure
  1405. // it's a multiple of 16 bytes( CRYPTPROTECTMEMORY_BLOCK_SIZE )
  1406. //
  1407. STACK_BUFFER( bufDecryptedPassword, PWLEN * sizeof( WCHAR ) );
  1408. if( ppCachedToken == NULL )
  1409. {
  1410. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1411. }
  1412. *ppCachedToken = NULL;
  1413. //
  1414. // Do a quick check for an existing token
  1415. //
  1416. if ( _pctAnonymousToken == NULL )
  1417. {
  1418. return NO_ERROR;
  1419. }
  1420. //
  1421. // First see if we even have a token to return?
  1422. //
  1423. _TokenLock.ReadLock();
  1424. if ( _pctAnonymousToken != NULL )
  1425. {
  1426. //
  1427. // Checkout the token. This insures we don't use a
  1428. // flushed token
  1429. //
  1430. if ( !_pctAnonymousToken->Checkout( NULL ) )
  1431. {
  1432. //
  1433. // OK. Get rid of associated token and build a new one
  1434. //
  1435. _TokenLock.ConvertSharedToExclusive();
  1436. fReadLocked = FALSE;
  1437. if ( _pctAnonymousToken != NULL )
  1438. {
  1439. _pctAnonymousToken->DereferenceCacheEntry();
  1440. _pctAnonymousToken = NULL;
  1441. }
  1442. //
  1443. // Do the logon
  1444. //
  1445. hr = DecryptMemoryPassword( &_strPasswd,
  1446. &bufDecryptedPassword );
  1447. if ( SUCCEEDED( hr ) )
  1448. {
  1449. hr = CreateAnonymousToken(
  1450. _strUserName.QueryStr(),
  1451. ( WCHAR * )bufDecryptedPassword.QueryPtr() );
  1452. if ( SUCCEEDED( hr ) )
  1453. {
  1454. if ( _pctAnonymousToken != NULL )
  1455. {
  1456. _pctAnonymousToken->ReferenceCacheEntry();
  1457. }
  1458. *ppCachedToken = _pctAnonymousToken;
  1459. }
  1460. SecureZeroMemory( bufDecryptedPassword.QueryPtr(),
  1461. bufDecryptedPassword.QuerySize() );
  1462. }
  1463. }
  1464. else
  1465. {
  1466. *ppCachedToken = _pctAnonymousToken;
  1467. }
  1468. }
  1469. if ( fReadLocked )
  1470. {
  1471. _TokenLock.ReadUnlock();
  1472. }
  1473. else
  1474. {
  1475. _TokenLock.WriteUnlock();
  1476. }
  1477. return hr;
  1478. }
  1479. HRESULT
  1480. W3_METADATA::GetAndRefVrAccessToken(
  1481. TOKEN_CACHE_ENTRY ** ppCachedToken
  1482. )
  1483. /*++
  1484. Description:
  1485. Get the UNC token for the current request.
  1486. Everybody that calls this function needs to call
  1487. deref when it's done.
  1488. Arguments:
  1489. NONE
  1490. Returns:
  1491. HRESULT
  1492. --*/
  1493. {
  1494. HRESULT hr = S_OK;
  1495. BOOL fReadLocked = TRUE;
  1496. STACK_BUFFER( bufDecryptedPassword, PWLEN * sizeof( WCHAR ) );
  1497. if( ppCachedToken == NULL )
  1498. {
  1499. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1500. }
  1501. *ppCachedToken = NULL;
  1502. //
  1503. // Do a quick check for an existing token
  1504. //
  1505. if ( _pctVrToken == NULL )
  1506. {
  1507. return NO_ERROR;
  1508. }
  1509. //
  1510. // First see if we even have a token to return?
  1511. //
  1512. _TokenLock.ReadLock();
  1513. if ( _pctVrToken != NULL )
  1514. {
  1515. //
  1516. // Checkout the token. This insures we don't use a
  1517. // flushed token
  1518. //
  1519. if ( !_pctVrToken->Checkout( NULL ) )
  1520. {
  1521. //
  1522. // OK. Get rid of associated token and build a new one
  1523. //
  1524. _TokenLock.ConvertSharedToExclusive();
  1525. fReadLocked = FALSE;
  1526. if ( _pctVrToken != NULL )
  1527. {
  1528. _pctVrToken->DereferenceCacheEntry();
  1529. _pctVrToken = NULL;
  1530. }
  1531. //
  1532. // Do the logon
  1533. //
  1534. if ( _strVrUserName.QueryStr() != NULL &&
  1535. _strVrPasswd.QueryStr() != NULL &&
  1536. _strVrUserName.QueryStr()[0] )
  1537. {
  1538. hr = DecryptMemoryPassword( &_strVrPasswd,
  1539. &bufDecryptedPassword );
  1540. if( SUCCEEDED( hr ) )
  1541. {
  1542. hr = CreateUNCVrToken( _strVrUserName.QueryStr(),
  1543. (WCHAR*) bufDecryptedPassword.QueryPtr() );
  1544. if( SUCCEEDED( hr ) )
  1545. {
  1546. _dirmonConfig.hToken = _pctVrToken->QueryImpersonationToken();
  1547. _pctVrToken->ReferenceCacheEntry();
  1548. *ppCachedToken = _pctVrToken;
  1549. }
  1550. else
  1551. {
  1552. _fUNCUserInvalid = TRUE;
  1553. hr = NO_ERROR;
  1554. }
  1555. SecureZeroMemory( bufDecryptedPassword.QueryPtr(),
  1556. bufDecryptedPassword.QuerySize() );
  1557. }
  1558. }
  1559. }
  1560. else
  1561. {
  1562. *ppCachedToken = _pctVrToken;
  1563. }
  1564. }
  1565. if ( fReadLocked )
  1566. {
  1567. _TokenLock.ReadUnlock();
  1568. }
  1569. else
  1570. {
  1571. _TokenLock.WriteUnlock();
  1572. }
  1573. return hr;
  1574. }
  1575. HRESULT
  1576. W3_METADATA::SetIpAccessCheck(
  1577. LPVOID pMDData,
  1578. DWORD dwDataLen
  1579. )
  1580. /*++
  1581. Description:
  1582. Store away the IP DNS list
  1583. Arguments:
  1584. pMDData - Beginning of binary blob to store
  1585. dwDataLen - Length of binary data
  1586. Returns:
  1587. HRESULT
  1588. --*/
  1589. {
  1590. if ( !_buffIpAccessCheck.Resize( dwDataLen ) )
  1591. {
  1592. return HRESULT_FROM_WIN32( GetLastError() );
  1593. }
  1594. memcpy( _buffIpAccessCheck.QueryPtr(),
  1595. pMDData,
  1596. dwDataLen );
  1597. _cbIpAccessCheck = dwDataLen;
  1598. return NO_ERROR;
  1599. }
  1600. HRESULT
  1601. W3_METADATA::GetMetadataProperty(
  1602. W3_CONTEXT * pW3Context,
  1603. DWORD dwPropertyId,
  1604. PBYTE pbBuffer,
  1605. DWORD cbBuffer,
  1606. DWORD * pcbBufferRequired
  1607. )
  1608. /*++
  1609. Routine Description:
  1610. Retrieve and serialize the requested metabase property.
  1611. Arguments:
  1612. pW3Context - W3 Context
  1613. dwPropertyId - Property ID
  1614. pbBuffer - Buffer (OPTIONAL)
  1615. cbBuffer - Size of given buffer
  1616. pcbBufferRequired - Set to size of buffer required
  1617. Returns:
  1618. HRESULT
  1619. --*/
  1620. {
  1621. HRESULT hr;
  1622. DWORD dwDataSetNumber;
  1623. DWORD dwNumMDRecords;
  1624. METADATA_GETALL_RECORD* pRecord;
  1625. VOID * pDataPointer;
  1626. DWORD cbNeeded = 0;
  1627. METADATA_RECORD * pOutputRecord;
  1628. if ( pcbBufferRequired == NULL ||
  1629. pW3Context == NULL )
  1630. {
  1631. DBG_ASSERT( FALSE );
  1632. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1633. }
  1634. //
  1635. // First check whether we have already read the raw buffer. If not,
  1636. // read it in
  1637. //
  1638. if ( _pGetAllBuffer == NULL )
  1639. {
  1640. BUFFER * pBuffer;
  1641. MB mb( g_pW3Server->QueryMDObject() );
  1642. BOOL fRet;
  1643. STACK_STRU( strUrl, 256 );
  1644. HANDLE hToken = NULL;
  1645. pBuffer = new BUFFER;
  1646. if ( pBuffer == NULL )
  1647. {
  1648. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1649. }
  1650. hr = pW3Context->QueryRequest()->GetUrl( &strUrl );
  1651. if ( FAILED( hr ) )
  1652. {
  1653. return hr;
  1654. }
  1655. fRet = OpenThreadToken( GetCurrentThread(),
  1656. TOKEN_IMPERSONATE,
  1657. TRUE,
  1658. &hToken );
  1659. if ( fRet )
  1660. {
  1661. DBG_ASSERT( hToken != NULL );
  1662. RevertToSelf();
  1663. }
  1664. //
  1665. // Open the metabase at the site level
  1666. //
  1667. fRet = mb.Open( pW3Context->QuerySite()->QueryMBRoot()->QueryStr() );
  1668. if ( fRet )
  1669. {
  1670. fRet = mb.GetAll( strUrl.QueryStr(),
  1671. METADATA_INHERIT | METADATA_PARTIAL_PATH,
  1672. IIS_MD_UT_FILE,
  1673. pBuffer,
  1674. &dwNumMDRecords,
  1675. &dwDataSetNumber );
  1676. mb.Close();
  1677. }
  1678. if ( hToken != NULL )
  1679. {
  1680. SetThreadToken( NULL, hToken );
  1681. CloseHandle( hToken );
  1682. hToken = NULL;
  1683. }
  1684. if ( !fRet )
  1685. {
  1686. return HRESULT_FROM_WIN32( GetLastError() );
  1687. }
  1688. //
  1689. // Try to stuff our buffer into this cache entry
  1690. //
  1691. LockCacheEntry();
  1692. if ( _pGetAllBuffer == NULL )
  1693. {
  1694. _pGetAllBuffer = pBuffer;
  1695. _cGetAllRecords = dwNumMDRecords;
  1696. }
  1697. else
  1698. {
  1699. //
  1700. // We already have one in here. Kill ours.
  1701. //
  1702. delete pBuffer;
  1703. pBuffer = NULL;
  1704. }
  1705. UnlockCacheEntry();
  1706. }
  1707. DBG_ASSERT( _pGetAllBuffer != NULL );
  1708. //
  1709. // Lookup the property in question
  1710. //
  1711. for ( DWORD i = 0; i < _cGetAllRecords; i++ )
  1712. {
  1713. pRecord = &(((METADATA_GETALL_RECORD*) _pGetAllBuffer->QueryPtr())[ i ]);
  1714. pDataPointer = (PVOID) ((PCHAR) _pGetAllBuffer->QueryPtr() + pRecord->dwMDDataOffset );
  1715. DBG_ASSERT( pRecord != NULL );
  1716. DBG_ASSERT( pDataPointer != NULL );
  1717. if ( pRecord->dwMDIdentifier == dwPropertyId )
  1718. {
  1719. //
  1720. // Found it!
  1721. //
  1722. switch( pRecord->dwMDDataType )
  1723. {
  1724. case DWORD_METADATA:
  1725. cbNeeded = sizeof( DWORD );
  1726. break;
  1727. case STRING_METADATA:
  1728. cbNeeded = ( wcslen( (WCHAR*) pDataPointer ) + 1 ) * sizeof( WCHAR );
  1729. break;
  1730. default:
  1731. // CODEWORK: Handle the other data types
  1732. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  1733. }
  1734. //
  1735. // Account for the METADATA_RECORD
  1736. //
  1737. cbNeeded += sizeof( METADATA_RECORD );
  1738. //
  1739. // Do the copy and other return values
  1740. //
  1741. DBG_ASSERT( cbNeeded != 0 );
  1742. *pcbBufferRequired = cbNeeded;
  1743. if ( pbBuffer == NULL ||
  1744. cbBuffer < *pcbBufferRequired )
  1745. {
  1746. return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
  1747. }
  1748. pOutputRecord = (METADATA_RECORD*) pbBuffer;
  1749. pOutputRecord->dwMDIdentifier = dwPropertyId;
  1750. pOutputRecord->dwMDAttributes = pRecord->dwMDAttributes;
  1751. pOutputRecord->dwMDUserType = pRecord->dwMDUserType;
  1752. pOutputRecord->dwMDDataLen = cbNeeded - sizeof( METADATA_RECORD );
  1753. pOutputRecord->dwMDDataType = pRecord->dwMDDataType;
  1754. pOutputRecord->pbMDData = pbBuffer + sizeof( METADATA_RECORD );
  1755. memcpy( pbBuffer + sizeof( METADATA_RECORD ),
  1756. pDataPointer,
  1757. pOutputRecord->dwMDDataLen );
  1758. return NO_ERROR;
  1759. }
  1760. }
  1761. return HRESULT_FROM_WIN32( ERROR_INVALID_INDEX );
  1762. }
  1763. HRESULT
  1764. W3_METADATA::ReadCustomFooter(
  1765. WCHAR * pszFooter
  1766. )
  1767. /*++
  1768. Routine Description:
  1769. Process a footer string, either reading the file or copying the string
  1770. to the buffer.
  1771. Arguments:
  1772. pszFooter - The footer string, which may be a string or a file name.
  1773. It looks like "STRING : some-string" or "FILE : file-name"
  1774. Returns:
  1775. HRESULT
  1776. --*/
  1777. {
  1778. HRESULT hr;
  1779. STACK_STRU( strFooter, MAX_PATH );
  1780. BOOL fFooterIsString = FALSE;
  1781. // First thing to do is to determine if this is a string or a file name.
  1782. // Skip preceding whitespace and then strcmp.
  1783. while (iswspace(*pszFooter))
  1784. {
  1785. pszFooter++;
  1786. }
  1787. if (!_wcsnicmp(pszFooter, L"STRING", 6))
  1788. {
  1789. fFooterIsString = TRUE;
  1790. pszFooter += 6;
  1791. }
  1792. else if (!_wcsnicmp(pszFooter, L"FILE", 4))
  1793. {
  1794. fFooterIsString = FALSE;
  1795. pszFooter += 4;
  1796. }
  1797. else
  1798. {
  1799. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1800. }
  1801. // Now we look for 0 or more white space, followed by a colon, followed by
  1802. // more white space.
  1803. while (iswspace(*pszFooter))
  1804. {
  1805. pszFooter++;
  1806. }
  1807. if (*pszFooter != L':')
  1808. {
  1809. // No colon seperator, error.
  1810. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1811. }
  1812. pszFooter++;
  1813. //
  1814. // OK, now if this is a string we take everything after the colon to the
  1815. // end for the string. If this is a file name then we'll open and read the
  1816. // file.
  1817. //
  1818. if (fFooterIsString)
  1819. {
  1820. return _strFooterString.CopyW(pszFooter);
  1821. }
  1822. else
  1823. {
  1824. //
  1825. // For files, we'll skip any more white space before the name.
  1826. //
  1827. while (iswspace(*pszFooter))
  1828. {
  1829. pszFooter++;
  1830. }
  1831. hr = _strFooterDocument.Copy( pszFooter );
  1832. if ( FAILED( hr ) )
  1833. {
  1834. return hr;
  1835. }
  1836. }
  1837. return S_OK;
  1838. }
  1839. HRESULT
  1840. W3_METADATA::EncryptMemoryPassword(
  1841. IN OUT STRU * strPassword
  1842. )
  1843. /*++
  1844. Routine Description:
  1845. Encrypt the password within the current process
  1846. Arguments:
  1847. strPassword - The password to be encrypted. The encrypted password
  1848. is in the buffer in the STRU, and is not null
  1849. terminated. So QueryCB and QueryCCH don't mean
  1850. anything here.
  1851. Returns:
  1852. HRESULT
  1853. --*/
  1854. {
  1855. HRESULT hr;
  1856. hr = strPassword->Resize(
  1857. ( ( strPassword->QueryCCH() + 1 ) * sizeof( WCHAR )
  1858. / CRYPTPROTECTMEMORY_BLOCK_SIZE + 1 )
  1859. * CRYPTPROTECTMEMORY_BLOCK_SIZE );
  1860. if( SUCCEEDED( hr ) )
  1861. {
  1862. if( !CryptProtectMemory( strPassword->QueryBuffer()->QueryPtr(),
  1863. strPassword->QueryBuffer()->QuerySize(),
  1864. CRYPTPROTECTMEMORY_SAME_PROCESS ) )
  1865. {
  1866. hr = HRESULT_FROM_WIN32( GetLastError() );
  1867. }
  1868. }
  1869. return hr;
  1870. }
  1871. HRESULT
  1872. W3_METADATA::DecryptMemoryPassword(
  1873. IN STRU * strProtectedPassword,
  1874. OUT BUFFER * bufDecryptedPassword
  1875. )
  1876. /*++
  1877. Routine Description:
  1878. Decrypt the password with the current process
  1879. Arguments:
  1880. strProtectedPassword - The protected password to be decrypted.
  1881. bufDecryptedPassword - Holds the decrypted password.
  1882. Returns:
  1883. HRESULT
  1884. --*/
  1885. {
  1886. HRESULT hr = S_OK;
  1887. if( !bufDecryptedPassword->Resize(
  1888. strProtectedPassword->QueryBuffer()->QuerySize() ) )
  1889. {
  1890. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1891. }
  1892. memcpy( bufDecryptedPassword->QueryPtr(),
  1893. strProtectedPassword->QueryStr(),
  1894. bufDecryptedPassword->QuerySize() );
  1895. if( !CryptUnprotectMemory( bufDecryptedPassword->QueryPtr(),
  1896. bufDecryptedPassword->QuerySize(),
  1897. CRYPTPROTECTMEMORY_SAME_PROCESS ) )
  1898. {
  1899. hr = HRESULT_FROM_WIN32( GetLastError() );
  1900. }
  1901. return hr;
  1902. }
  1903. HRESULT W3_METADATA::SetExpire(WCHAR *pszExpire)
  1904. /*++
  1905. Routine Description:
  1906. Set the expire header to be used on all responses
  1907. Arguments:
  1908. pszExpire: the string containing the description. It could have the form
  1909. empty: no expire
  1910. "s, some-date" : expire on this date"
  1911. "d, some-number" : expire after this many seconds
  1912. Returns:
  1913. HRESULT
  1914. --*/
  1915. {
  1916. while (iswspace(*pszExpire))
  1917. {
  1918. pszExpire++;
  1919. }
  1920. LPWSTR pszParam;
  1921. if ((pszParam = wcschr(pszExpire, L',')) == NULL)
  1922. {
  1923. if (*pszExpire == L'\0')
  1924. {
  1925. _dwExpireMode = EXPIRE_MODE_OFF;
  1926. return S_OK;
  1927. }
  1928. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1929. }
  1930. pszParam++;
  1931. while (iswspace(*pszParam))
  1932. {
  1933. pszParam++;
  1934. }
  1935. HRESULT hr;
  1936. switch (*pszExpire)
  1937. {
  1938. case L's':
  1939. case L'S':
  1940. if (FAILED(hr = _strExpireHeader.CopyWTruncate(pszParam)))
  1941. {
  1942. return hr;
  1943. }
  1944. _dwExpireMode = EXPIRE_MODE_STATIC;
  1945. break;
  1946. case L'd':
  1947. case L'D':
  1948. LPWSTR endPtr;
  1949. DWORD dwExpire;
  1950. if (pszParam[0] == L'0' && pszParam[1] == L'x')
  1951. {
  1952. dwExpire = wcstoul(pszParam + 2, &endPtr, 16);
  1953. }
  1954. else
  1955. {
  1956. dwExpire = wcstoul(pszParam, &endPtr, 10);
  1957. }
  1958. if (!iswspace(*endPtr) &&
  1959. *endPtr != L'\0')
  1960. {
  1961. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1962. }
  1963. if (dwExpire != ULONG_MAX)
  1964. {
  1965. if (dwExpire > MAX_GLOBAL_EXPIRE)
  1966. {
  1967. dwExpire = MAX_GLOBAL_EXPIRE;
  1968. }
  1969. _dwExpireMode = EXPIRE_MODE_DYNAMIC;
  1970. _dwExpireDelta = dwExpire;
  1971. }
  1972. break;
  1973. default:
  1974. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1975. }
  1976. return S_OK;
  1977. }
  1978. HRESULT W3_METADATA::SetCacheControlHeader()
  1979. /*++
  1980. Routine Description:
  1981. If no-cache, max-age or a dynamic expires directive is present, add
  1982. it to Cache-Control header
  1983. Arguments:
  1984. None
  1985. Returns:
  1986. HRESULT
  1987. --*/
  1988. {
  1989. switch (QueryExpireMode())
  1990. {
  1991. case EXPIRE_MODE_NONE:
  1992. _fHaveNoCache = FALSE;
  1993. _fHaveMaxAge = FALSE;
  1994. break;
  1995. case EXPIRE_MODE_DYNAMIC:
  1996. // If we have a dynamic Expires header, create a max-age directive
  1997. if (_dwExpireDelta != 0)
  1998. {
  1999. _fHaveNoCache = FALSE;
  2000. if (!_fHaveMaxAge)
  2001. {
  2002. _fHaveMaxAge = TRUE;
  2003. _dwMaxAge = _dwExpireDelta;
  2004. }
  2005. }
  2006. else
  2007. {
  2008. _fHaveNoCache = TRUE;
  2009. _fHaveMaxAge = FALSE;
  2010. }
  2011. break;
  2012. default:
  2013. break;
  2014. }
  2015. BOOL fHaveCCHeader = !_strCacheControlHeader.IsEmpty();
  2016. HRESULT hr;
  2017. if (_fHaveNoCache)
  2018. {
  2019. if (FAILED(hr = _strCacheControlHeader.Append(
  2020. fHaveCCHeader ? ",no-cache" : "no-cache")))
  2021. {
  2022. return hr;
  2023. }
  2024. }
  2025. else if (_fHaveMaxAge)
  2026. {
  2027. CHAR pszMaxAgeBuffer[16];
  2028. _itoa(_dwMaxAge, pszMaxAgeBuffer, 10);
  2029. if (FAILED(hr = _strCacheControlHeader.Append(
  2030. fHaveCCHeader ? ",max-age=" : "max-age=")) ||
  2031. FAILED(hr = _strCacheControlHeader.Append(pszMaxAgeBuffer)))
  2032. {
  2033. return hr;
  2034. }
  2035. }
  2036. return S_OK;
  2037. }
  2038. HRESULT
  2039. META_SCRIPT_MAP::Initialize(
  2040. IN WCHAR * szScriptMapData
  2041. )
  2042. /*++
  2043. Routine Description:
  2044. Initialize the collection of META_SCRIPT_MAP_ENTRIES from the
  2045. metadata.
  2046. This routine will modify the multisz it works with (by replacing
  2047. some ',' with '\0' ).
  2048. Currently it modifies the in parameter, which is kindof icky.
  2049. We could avoid this by copying the buffer.
  2050. Arguments:
  2051. szScriptMapData - A multi-sz of script map entries.
  2052. Return Value:
  2053. Notes:
  2054. Script map is a multi-sz with each string being a comma separated list
  2055. <extension>,<executable>,<flags>,<verb list>
  2056. <extension>:
  2057. .xyz - Maximum of 128 characters
  2058. * - Star script map - routes all requests though the executable
  2059. <executable>
  2060. - Extension to invoke
  2061. <flags>:
  2062. 1 - Allow run in script access directory ( MD_SCRIPTMAPFLAG_SCRIPT )
  2063. 4 - Check for pathinfo file ( MD_SCRIPTMAPFLAG_CHECK_PATH_INFO )
  2064. <verb list>:
  2065. <verb>,<verb>,<verb>
  2066. - Allowed verbs
  2067. - If no verbs are listed, a value of "all verbs" is assumed.
  2068. --*/
  2069. {
  2070. DBG_ASSERT( szScriptMapData );
  2071. HRESULT hr = NOERROR;
  2072. // Iterate over multisz
  2073. WCHAR * pszEntryIterator;
  2074. // Current mapping
  2075. WCHAR * pszExtension;
  2076. WCHAR * pszExecutable;
  2077. WCHAR * pszFlags;
  2078. DWORD Flags;
  2079. WCHAR * pszVerbs;
  2080. DWORD cchVerbs;
  2081. WCHAR * pszDest;
  2082. //
  2083. // Iterate over each mapping
  2084. //
  2085. pszEntryIterator = szScriptMapData;
  2086. while( *pszEntryIterator != L'\0' )
  2087. {
  2088. //
  2089. // Get the extension
  2090. //
  2091. pszExtension = pszEntryIterator;
  2092. //
  2093. // Get the executable
  2094. //
  2095. pszEntryIterator = wcschr( pszEntryIterator, L',' );
  2096. if( pszEntryIterator == NULL )
  2097. {
  2098. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  2099. return hr;
  2100. }
  2101. *pszEntryIterator++ = L'\0';
  2102. pszExecutable = pszEntryIterator;
  2103. if( pszExecutable == L'\0' )
  2104. {
  2105. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  2106. return hr;
  2107. }
  2108. //
  2109. // Get the flags
  2110. //
  2111. pszEntryIterator = wcschr( pszEntryIterator, L',' );
  2112. if( pszEntryIterator == NULL )
  2113. {
  2114. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  2115. return hr;
  2116. }
  2117. *pszEntryIterator++ = L'\0';
  2118. //
  2119. // We don't need pszFlags here, but we will need it if
  2120. // there is an empty verb list, to reset our iterator.
  2121. //
  2122. pszFlags = pszEntryIterator;
  2123. Flags = wcstoul( pszFlags, NULL, 10 );
  2124. //
  2125. // Get the verbs
  2126. //
  2127. pszEntryIterator = wcschr( pszEntryIterator, L',' );
  2128. if( pszEntryIterator != NULL )
  2129. {
  2130. //
  2131. // There is a list of verbs
  2132. //
  2133. *pszEntryIterator++ = L'\0';
  2134. pszDest = pszVerbs = pszEntryIterator;
  2135. //
  2136. // Format verb list as a multi-sz for each entry
  2137. //
  2138. cchVerbs = 1;
  2139. while( *pszEntryIterator != L'\0')
  2140. {
  2141. if( *pszEntryIterator == L',' || *pszEntryIterator == L' ' )
  2142. {
  2143. while( *pszEntryIterator == L',' || *pszEntryIterator == L' ' )
  2144. {
  2145. pszEntryIterator++;
  2146. }
  2147. *pszDest++ = L'\0';
  2148. }
  2149. else
  2150. {
  2151. *pszDest++ = *pszEntryIterator++;
  2152. }
  2153. cchVerbs++;
  2154. }
  2155. }
  2156. else
  2157. {
  2158. //
  2159. // Empty Verb List
  2160. //
  2161. //
  2162. // We've lost our iterator so we need to get it back.
  2163. // Point to the terminator.
  2164. //
  2165. pszEntryIterator = pszFlags + wcslen( pszFlags );
  2166. pszVerbs = pszEntryIterator;
  2167. cchVerbs = 1;
  2168. }
  2169. //
  2170. // Create and add the entry object to our list
  2171. //
  2172. META_SCRIPT_MAP_ENTRY *
  2173. pNewEntry = new META_SCRIPT_MAP_ENTRY();
  2174. if( pNewEntry == NULL )
  2175. {
  2176. hr = E_OUTOFMEMORY;
  2177. return hr;
  2178. }
  2179. hr = pNewEntry->Create( pszExtension,
  2180. pszExecutable,
  2181. Flags,
  2182. pszVerbs,
  2183. cchVerbs
  2184. );
  2185. if( FAILED(hr) )
  2186. {
  2187. delete pNewEntry;
  2188. return hr;
  2189. }
  2190. if (pNewEntry->QueryIsStarScriptMap())
  2191. {
  2192. InsertTailList( &m_StarScriptMapListHead, &pNewEntry->m_ListEntry );
  2193. }
  2194. else
  2195. {
  2196. InsertTailList( &m_ListHead, &pNewEntry->m_ListEntry );
  2197. }
  2198. //
  2199. // Move to the next entry.
  2200. //
  2201. pszEntryIterator++;
  2202. }
  2203. return hr;
  2204. }
  2205. BOOL
  2206. META_SCRIPT_MAP::FindEntry(
  2207. IN const STRU & strExtension,
  2208. OUT META_SCRIPT_MAP_ENTRY * * ppScriptMapEntry
  2209. )
  2210. /*++
  2211. Routine Description:
  2212. Arguments:
  2213. Return Value:
  2214. --*/
  2215. {
  2216. *ppScriptMapEntry = NULL;
  2217. PLIST_ENTRY pEntry;
  2218. META_SCRIPT_MAP_ENTRY * pScriptMapEntry = NULL;
  2219. for( pEntry = m_ListHead.Flink;
  2220. pEntry != &m_ListHead;
  2221. pEntry = pEntry->Flink )
  2222. {
  2223. pScriptMapEntry = CONTAINING_RECORD( pEntry,
  2224. META_SCRIPT_MAP_ENTRY,
  2225. m_ListEntry
  2226. );
  2227. if( strExtension.Equals( pScriptMapEntry->m_strExtension ) )
  2228. {
  2229. *ppScriptMapEntry = pScriptMapEntry;
  2230. return TRUE;
  2231. }
  2232. }
  2233. return FALSE;
  2234. }
  2235. VOID
  2236. META_SCRIPT_MAP::Terminate( VOID )
  2237. /*++
  2238. Routine Description:
  2239. Arguments:
  2240. Return Value:
  2241. --*/
  2242. {
  2243. META_SCRIPT_MAP_ENTRY * pScriptMapEntry;
  2244. while( !IsListEmpty( &m_StarScriptMapListHead ) )
  2245. {
  2246. pScriptMapEntry = CONTAINING_RECORD( m_StarScriptMapListHead.Flink,
  2247. META_SCRIPT_MAP_ENTRY,
  2248. m_ListEntry );
  2249. RemoveEntryList( &pScriptMapEntry->m_ListEntry );
  2250. delete pScriptMapEntry;
  2251. }
  2252. while( !IsListEmpty( &m_ListHead ) )
  2253. {
  2254. pScriptMapEntry = CONTAINING_RECORD( m_ListHead.Flink,
  2255. META_SCRIPT_MAP_ENTRY,
  2256. m_ListEntry );
  2257. RemoveEntryList( &pScriptMapEntry->m_ListEntry );
  2258. delete pScriptMapEntry;
  2259. }
  2260. }
  2261. HRESULT
  2262. META_SCRIPT_MAP_ENTRY::Create(
  2263. IN const WCHAR * szExtension,
  2264. IN const WCHAR * szExecutable,
  2265. IN DWORD Flags,
  2266. IN const WCHAR * szVerbs,
  2267. IN DWORD cchVerbs
  2268. )
  2269. /*++
  2270. Routine Description:
  2271. Arguments:
  2272. Return Value:
  2273. --*/
  2274. {
  2275. HRESULT hr = NOERROR;
  2276. DWORD cchExecutable;
  2277. //
  2278. // Capture initialization parameters
  2279. //
  2280. m_Flags = Flags;
  2281. hr = m_strExtension.Copy( szExtension );
  2282. if( FAILED(hr) )
  2283. {
  2284. return hr;
  2285. }
  2286. //
  2287. // Lower-case to allow for case insensitive comparisons
  2288. //
  2289. _wcslwr( m_strExtension.QueryStr() );
  2290. if (szExtension[0] == L'*' && szExtension[1] == L'\0')
  2291. {
  2292. m_fIsStarScriptMapEntry = TRUE;
  2293. }
  2294. //
  2295. // We treat the executable name as an ExpandSz, so expand it
  2296. //
  2297. WCHAR szExpand[MAX_PATH + 1];
  2298. if (!ExpandEnvironmentStrings(szExecutable,
  2299. szExpand,
  2300. sizeof szExpand/sizeof WCHAR))
  2301. {
  2302. return HRESULT_FROM_WIN32(GetLastError());
  2303. }
  2304. if (FAILED(hr = m_strExecutable.Copy( szExpand )))
  2305. {
  2306. return hr;
  2307. }
  2308. //
  2309. // If the executable is quoted, remove the quotes now
  2310. //
  2311. // Note that we can't just remove the first and last
  2312. // quote, as it's possible that the script map entry
  2313. // could look like this:
  2314. //
  2315. // "c:\program files\test\test.exe" "%s" "%s"
  2316. //
  2317. if ( m_strExecutable.QueryStr()[ 0 ] == L'\"' &&
  2318. wcschr( m_strExecutable.QueryStr() + 1, L'\"' ) )
  2319. {
  2320. LPWSTR pCursor;
  2321. cchExecutable = m_strExecutable.QueryCCH();
  2322. pCursor = m_strExecutable.QueryStr();
  2323. while ( *(pCursor+1) != L'\"' )
  2324. {
  2325. *pCursor = *(pCursor+1);
  2326. pCursor++;
  2327. }
  2328. while ( *(pCursor+2) != L'\0' )
  2329. {
  2330. *pCursor = *(pCursor+2);
  2331. pCursor++;
  2332. }
  2333. *pCursor = L'\0';
  2334. m_strExecutable.SetLen( cchExecutable - 2 );
  2335. }
  2336. if (m_strExecutable.QueryCCH() > 4)
  2337. {
  2338. if (!_wcsicmp(
  2339. m_strExecutable.QueryStr() + m_strExecutable.QueryCCH() - 4,
  2340. L".dll"))
  2341. {
  2342. m_Gateway = GATEWAY_ISAPI;
  2343. }
  2344. else
  2345. {
  2346. m_Gateway = GATEWAY_CGI;
  2347. }
  2348. }
  2349. else
  2350. {
  2351. return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  2352. }
  2353. if (!m_Verbs.AppendW( szVerbs, cchVerbs ))
  2354. {
  2355. hr = HRESULT_FROM_WIN32(GetLastError());
  2356. return hr;
  2357. }
  2358. return S_OK;
  2359. }
  2360. HRESULT
  2361. W3_METADATA_CACHE::Initialize(
  2362. VOID
  2363. )
  2364. /*++
  2365. Description:
  2366. Initialize metadata cache
  2367. Arguments:
  2368. None
  2369. Return:
  2370. HRESULT
  2371. --*/
  2372. {
  2373. HRESULT hr;
  2374. DWORD dwData;
  2375. DWORD dwType;
  2376. DWORD cbData = sizeof( DWORD );
  2377. DWORD csecTTL = DEFAULT_W3_METADATA_CACHE_TTL;
  2378. HKEY hKey;
  2379. //
  2380. // What is the TTL for the URI cache
  2381. //
  2382. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2383. L"System\\CurrentControlSet\\Services\\w3svc\\Parameters",
  2384. 0,
  2385. KEY_READ,
  2386. &hKey ) == ERROR_SUCCESS )
  2387. {
  2388. DBG_ASSERT( hKey != NULL );
  2389. if ( RegQueryValueEx( hKey,
  2390. L"MetadataCacheTTL",
  2391. NULL,
  2392. &dwType,
  2393. (LPBYTE) &dwData,
  2394. &cbData ) == ERROR_SUCCESS &&
  2395. dwType == REG_DWORD )
  2396. {
  2397. csecTTL = dwData;
  2398. }
  2399. RegCloseKey( hKey );
  2400. }
  2401. //
  2402. // We'll use TTL for scavenge period, and expect two inactive periods to
  2403. // flush
  2404. //
  2405. hr = SetCacheConfiguration( csecTTL * 1000,
  2406. csecTTL * 1000,
  2407. CACHE_INVALIDATION_METADATA,
  2408. NULL );
  2409. if ( FAILED( hr ) )
  2410. {
  2411. return hr;
  2412. }
  2413. return W3_METADATA::Initialize();
  2414. }
  2415. HRESULT
  2416. W3_METADATA_CACHE::GetMetaData(
  2417. W3_CONTEXT * pW3Context,
  2418. STRU & strUrl,
  2419. W3_METADATA ** ppMetaData
  2420. )
  2421. /*++
  2422. Routine Description:
  2423. Retrieve a W3_METADATA, creating if necessary
  2424. Arguments:
  2425. pW3Context - W3 context
  2426. strUrl - Url
  2427. ppMetaData - Set to point to metadata on success
  2428. Return Value:
  2429. HRESULT
  2430. --*/
  2431. {
  2432. W3_METADATA_KEY metaKey;
  2433. W3_METADATA * pMetaData;
  2434. DWORD dwDataSetNumber;
  2435. HRESULT hr;
  2436. STACK_STRU( strFullPath, 128 );
  2437. DATA_SET_CACHE * pDataSetCache;
  2438. MB mb( g_pW3Server->QueryMDObject() );
  2439. HANDLE hToken = NULL;
  2440. if ( pW3Context == NULL ||
  2441. ppMetaData == NULL )
  2442. {
  2443. DBG_ASSERT( FALSE );
  2444. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2445. goto Failed;
  2446. }
  2447. *ppMetaData = NULL;
  2448. //
  2449. // Setup a key to lookup by determining data set number
  2450. //
  2451. hr = GetFullMetadataPath( pW3Context,
  2452. strUrl,
  2453. &strFullPath );
  2454. if ( FAILED( hr ) )
  2455. {
  2456. goto Failed;
  2457. }
  2458. //
  2459. // Get a data set cache
  2460. //
  2461. hr = pW3Context->QuerySite()->GetDataSetCache( &pDataSetCache );
  2462. if ( FAILED( hr ) )
  2463. {
  2464. goto Failed;
  2465. }
  2466. DBG_ASSERT( pDataSetCache != NULL );
  2467. hr = pDataSetCache->GetDataSetNumber( strFullPath,
  2468. &dwDataSetNumber );
  2469. pDataSetCache->DereferenceDataSetCache();
  2470. pDataSetCache = NULL;
  2471. if ( FAILED( hr ) )
  2472. {
  2473. goto Failed;
  2474. }
  2475. metaKey.SetDataSetNumber( dwDataSetNumber );
  2476. //
  2477. // Look it up
  2478. //
  2479. hr = FindCacheEntry( &metaKey, (CACHE_ENTRY**) &pMetaData );
  2480. if ( SUCCEEDED( hr ) )
  2481. {
  2482. DBG_ASSERT( pMetaData != NULL );
  2483. *ppMetaData = pMetaData;
  2484. goto Succeeded;
  2485. }
  2486. //
  2487. // We need to create a metadata entry and add it
  2488. //
  2489. if ( OpenThreadToken( GetCurrentThread(),
  2490. TOKEN_IMPERSONATE,
  2491. TRUE,
  2492. &hToken ) )
  2493. {
  2494. DBG_ASSERT( hToken != NULL );
  2495. DBG_REQUIRE( RevertToSelf() );
  2496. }
  2497. hr = CreateNewMetaData( pW3Context,
  2498. strUrl,
  2499. strFullPath,
  2500. &pMetaData );
  2501. if ( FAILED( hr ) )
  2502. {
  2503. goto Failed;
  2504. }
  2505. if ( hToken != NULL )
  2506. {
  2507. DBG_REQUIRE( SetThreadToken( NULL, hToken ) );
  2508. DBG_REQUIRE( CloseHandle( hToken ) );
  2509. hToken = NULL;
  2510. }
  2511. DBG_ASSERT( pMetaData != NULL );
  2512. //
  2513. // Add to the cache
  2514. //
  2515. AddCacheEntry( pMetaData );
  2516. *ppMetaData = pMetaData;
  2517. Succeeded:
  2518. DBG_ASSERT( SUCCEEDED( hr ) );
  2519. return NO_ERROR;
  2520. Failed:
  2521. if ( hToken != NULL )
  2522. {
  2523. DBG_REQUIRE( SetThreadToken( NULL, hToken ) );
  2524. DBG_REQUIRE( CloseHandle( hToken ) );
  2525. hToken = NULL;
  2526. }
  2527. DBG_ASSERT( FAILED( hr ) );
  2528. return hr;
  2529. }
  2530. HRESULT
  2531. W3_METADATA_CACHE::CreateNewMetaData(
  2532. W3_CONTEXT * pW3Context,
  2533. STRU & strUrl,
  2534. STRU & strFullMetadataPath,
  2535. W3_METADATA ** ppMetaData
  2536. )
  2537. /*++
  2538. Routine Description:
  2539. Create a new W3_METADATA and initializes it
  2540. Arguments:
  2541. pW3Context - context
  2542. strUrl - URL
  2543. strFullMetadataPath - Full metabase path to open
  2544. ppMetaData - Set to new metadata entry on success
  2545. Return Value:
  2546. HRESULT
  2547. --*/
  2548. {
  2549. HRESULT hr;
  2550. W3_METADATA * pMetaData = NULL;
  2551. if ( pW3Context == NULL ||
  2552. ppMetaData == NULL )
  2553. {
  2554. DBG_ASSERT( FALSE );
  2555. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2556. }
  2557. *ppMetaData = NULL;
  2558. //
  2559. // Create the metadata object
  2560. //
  2561. pMetaData = new W3_METADATA( this );
  2562. if ( pMetaData == NULL )
  2563. {
  2564. return HRESULT_FROM_WIN32( GetLastError() );
  2565. }
  2566. //
  2567. // Set full URL for flushing purposes
  2568. //
  2569. hr = pMetaData->QueryMetadataPath()->Copy( strFullMetadataPath );
  2570. if ( FAILED( hr ) )
  2571. {
  2572. pMetaData->DereferenceCacheEntry();
  2573. return hr;
  2574. }
  2575. //
  2576. // Initialize it
  2577. //
  2578. hr = pMetaData->ReadMetaData( *(pW3Context->QuerySite()->QueryMBRoot()),
  2579. strUrl );
  2580. if( FAILED(hr) )
  2581. {
  2582. pMetaData->DereferenceCacheEntry();
  2583. return hr;
  2584. }
  2585. *ppMetaData = pMetaData;
  2586. return NO_ERROR;
  2587. }
  2588. HRESULT
  2589. W3_METADATA_CACHE::GetFullMetadataPath(
  2590. W3_CONTEXT * pW3Context,
  2591. STRU & strUrl,
  2592. STRU * pstrFullPath
  2593. )
  2594. /*++
  2595. Routine Description:
  2596. Get the full metadata given the URL and site
  2597. Arguments:
  2598. pW3Context - Used to get the site
  2599. strUrl - Url
  2600. pstrFullPath - Filled with full path on success
  2601. Return Value:
  2602. HRESULT
  2603. --*/
  2604. {
  2605. HRESULT hr;
  2606. WCHAR * pszSource;
  2607. DWORD cchSource;
  2608. if ( pW3Context == NULL ||
  2609. pstrFullPath == NULL )
  2610. {
  2611. DBG_ASSERT( FALSE );
  2612. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2613. }
  2614. //
  2615. // Build up full metabase path (including instance)
  2616. //
  2617. hr = pstrFullPath->Copy( *(pW3Context->QuerySite()->QueryMBRoot()) );
  2618. if ( FAILED( hr ) )
  2619. {
  2620. return hr;
  2621. }
  2622. //
  2623. // Don't copy two slashes
  2624. //
  2625. if ( strUrl.QueryStr()[ 0 ] == L'/' )
  2626. {
  2627. pszSource = strUrl.QueryStr() + 1;
  2628. cchSource = strUrl.QueryCCH() - 1;
  2629. }
  2630. else
  2631. {
  2632. pszSource = strUrl.QueryStr();
  2633. cchSource = strUrl.QueryCCH();
  2634. }
  2635. hr = pstrFullPath->Append( pszSource, cchSource );
  2636. if ( FAILED( hr ) )
  2637. {
  2638. return hr;
  2639. }
  2640. return NO_ERROR;
  2641. }