Source code of Windows XP (NT5)
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.

4584 lines
116 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. w3context.cxx
  5. Abstract:
  6. Drive the state machine
  7. Author:
  8. Bilal Alam (balam) 10-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "anonymousprovider.hxx"
  16. #include "customprovider.hxx"
  17. #include "staticfile.hxx"
  18. #include "isapi_handler.h"
  19. #include "cgi_handler.h"
  20. #include "trace_handler.h"
  21. #include "dav_handler.h"
  22. #include "options_handler.hxx"
  23. #include "generalhandler.hxx"
  24. //
  25. // Global context list
  26. //
  27. CHAR W3_CONTEXT::sm_achRedirectMessage[ 512 ];
  28. DWORD W3_CONTEXT::sm_cbRedirectMessage;
  29. CHAR * W3_CONTEXT::sm_pszAccessDeniedMessage;
  30. ALLOC_CACHE_HANDLER * EXECUTE_CONTEXT::sm_pachExecuteContexts;
  31. HRESULT
  32. EXECUTE_CONTEXT::InitializeFromExecUrlInfo(
  33. HSE_EXEC_URL_INFO * pExecUrlInfo
  34. )
  35. /*++
  36. Routine Description:
  37. Initialize an execution context from an HSE_EXEC_URL_INFO structure.
  38. This function does the necessary copying
  39. Arguments:
  40. pExecUrlInfo - Describes the child request to execute
  41. Return Value:
  42. HRESULT
  43. --*/
  44. {
  45. HRESULT hr;
  46. if ( pExecUrlInfo == NULL )
  47. {
  48. DBG_ASSERT( FALSE );
  49. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  50. }
  51. //
  52. // Copy entity if (any)
  53. //
  54. if ( pExecUrlInfo->pEntity != NULL )
  55. {
  56. _EntityInfo.cbAvailable = pExecUrlInfo->pEntity->cbAvailable;
  57. _EntityInfo.lpbData = pExecUrlInfo->pEntity->lpbData;
  58. _ExecUrlInfo.pEntity = &_EntityInfo;
  59. }
  60. //
  61. // Copy user (if any)
  62. //
  63. if ( pExecUrlInfo->pUserInfo != NULL )
  64. {
  65. _UserInfo.hImpersonationToken = pExecUrlInfo->pUserInfo->hImpersonationToken;
  66. hr = _HeaderBuffer.AllocateSpace(
  67. pExecUrlInfo->pUserInfo->pszCustomUserName,
  68. strlen( pExecUrlInfo->pUserInfo->pszCustomUserName ),
  69. &( _UserInfo.pszCustomUserName ) );
  70. if ( FAILED( hr ) )
  71. {
  72. return hr;
  73. }
  74. hr = _HeaderBuffer.AllocateSpace(
  75. pExecUrlInfo->pUserInfo->pszCustomAuthType,
  76. strlen( pExecUrlInfo->pUserInfo->pszCustomAuthType ),
  77. &( _UserInfo.pszCustomAuthType ) );
  78. if ( FAILED( hr ) )
  79. {
  80. return hr;
  81. }
  82. _ExecUrlInfo.pUserInfo = &_UserInfo;
  83. }
  84. //
  85. // Copy URL (if any)
  86. //
  87. if ( pExecUrlInfo->pszUrl != NULL )
  88. {
  89. hr = _HeaderBuffer.AllocateSpace(
  90. pExecUrlInfo->pszUrl,
  91. strlen( pExecUrlInfo->pszUrl ),
  92. &( _ExecUrlInfo.pszUrl ) );
  93. if ( FAILED( hr ) )
  94. {
  95. return hr;
  96. }
  97. }
  98. //
  99. // Copy method (if any)
  100. //
  101. if ( pExecUrlInfo->pszMethod != NULL )
  102. {
  103. hr = _HeaderBuffer.AllocateSpace(
  104. pExecUrlInfo->pszMethod,
  105. strlen( pExecUrlInfo->pszMethod ),
  106. &( _ExecUrlInfo.pszMethod ) );
  107. if ( FAILED( hr ) )
  108. {
  109. return hr;
  110. }
  111. }
  112. //
  113. // Copy child headers
  114. //
  115. if ( pExecUrlInfo->pszChildHeaders != NULL )
  116. {
  117. hr = _HeaderBuffer.AllocateSpace(
  118. pExecUrlInfo->pszChildHeaders,
  119. strlen( pExecUrlInfo->pszChildHeaders ),
  120. &( _ExecUrlInfo.pszChildHeaders ) );
  121. if ( FAILED( hr ) )
  122. {
  123. return hr;
  124. }
  125. }
  126. _ExecUrlInfo.dwExecUrlFlags = pExecUrlInfo->dwExecUrlFlags;
  127. return NO_ERROR;
  128. }
  129. //static
  130. HRESULT
  131. EXECUTE_CONTEXT::Initialize(
  132. VOID
  133. )
  134. /*++
  135. Routine Description:
  136. Initialize execute_context lookaside
  137. Arguments:
  138. None
  139. Return Value:
  140. HRESULT
  141. --*/
  142. {
  143. ALLOC_CACHE_CONFIGURATION acConfig;
  144. //
  145. // Setup allocation lookaside
  146. //
  147. acConfig.nConcurrency = 1;
  148. acConfig.nThreshold = 100;
  149. acConfig.cbSize = sizeof( EXECUTE_CONTEXT );
  150. DBG_ASSERT( sm_pachExecuteContexts == NULL );
  151. sm_pachExecuteContexts = new ALLOC_CACHE_HANDLER( "EXECUTE_CONTEXT",
  152. &acConfig );
  153. if ( sm_pachExecuteContexts == NULL )
  154. {
  155. return HRESULT_FROM_WIN32( GetLastError() );
  156. }
  157. return NO_ERROR;
  158. }
  159. //static
  160. VOID
  161. EXECUTE_CONTEXT::Terminate(
  162. VOID
  163. )
  164. /*++
  165. Routine Description:
  166. Cleanup execute_context lookaside
  167. Arguments:
  168. None
  169. Return Value:
  170. None
  171. --*/
  172. {
  173. if ( sm_pachExecuteContexts != NULL )
  174. {
  175. delete sm_pachExecuteContexts;
  176. sm_pachExecuteContexts = NULL;
  177. }
  178. }
  179. HRESULT
  180. W3_CONTEXT::IsapiExecuteUrl(
  181. HSE_EXEC_URL_INFO * pExecUrlInfo
  182. )
  183. /*++
  184. Routine Description:
  185. Do an ISAPI execute URL request.
  186. SO WHY THE HELL IS THIS CODE NOT IN THE ISAPI_CORE?
  187. Because I don't want to preclude the same child execute API exposed to
  188. ISAPI filters (though of course just the synchronous version)
  189. Arguments:
  190. pExecUrlInfo - Describes the child request to execute
  191. Return Value:
  192. HRESULT
  193. --*/
  194. {
  195. DWORD dwExecFlags = 0;
  196. WCHAR * pszRequiredAppPool = NULL;
  197. BOOL fIgnoreValidation = FALSE;
  198. W3_REQUEST * pChildRequest = NULL;
  199. W3_CHILD_CONTEXT * pChildContext = NULL;
  200. HRESULT hr = NO_ERROR;
  201. DWORD dwCloneRequestFlags = 0;
  202. BOOL fFinished = FALSE;
  203. STACK_STRU( strNewUrl, 256 );
  204. STACK_STRA( strNewVerb, 10 );
  205. BOOL fIsSSICommandExecution = FALSE;
  206. W3_HANDLER * pSSICommandHandler = NULL;
  207. HANDLE hImpersonationToken = NULL;
  208. CUSTOM_USER_CONTEXT* pCustomUser = NULL;
  209. STACK_STRA( strAuthorization, 24 );
  210. STACK_STRA( strCustomAuthType, 32 );
  211. STACK_STRA( strCustomUserName, 64 );
  212. BOOL fEnableWildcardMapping = TRUE;
  213. if ( pExecUrlInfo == NULL )
  214. {
  215. DBG_ASSERT( FALSE );
  216. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  217. goto Finished;
  218. }
  219. //
  220. // Is this a crappy SSI hack
  221. //
  222. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_SSI_CMD )
  223. {
  224. //
  225. // Issue-10-11-2000-BAlam
  226. //
  227. // This is a really dangerous code path (executing a raw command)
  228. // so verify that SSI is calling us
  229. //
  230. fIsSSICommandExecution = TRUE;
  231. }
  232. //
  233. // Should we execute the request synchronously?
  234. //
  235. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_SYNC )
  236. {
  237. dwExecFlags |= W3_FLAG_SYNC;
  238. }
  239. else
  240. {
  241. dwExecFlags |= W3_FLAG_ASYNC;
  242. }
  243. //
  244. // Should we suppress headers in the response (useful for SSI)
  245. //
  246. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_NO_HEADERS )
  247. {
  248. dwExecFlags |= W3_FLAG_NO_HEADERS;
  249. }
  250. //
  251. // Should we disable custom errors
  252. //
  253. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_DISABLE_CUSTOM_ERROR )
  254. {
  255. dwExecFlags |= W3_FLAG_NO_ERROR_BODY;
  256. }
  257. //
  258. // Should we disable * script map?
  259. //
  260. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR )
  261. {
  262. fEnableWildcardMapping = FALSE;
  263. dwCloneRequestFlags |= W3_REQUEST_CLONE_NO_DAV;
  264. }
  265. //
  266. // In every case we will clone the basics
  267. //
  268. dwCloneRequestFlags = W3_REQUEST_CLONE_BASICS;
  269. //
  270. // If the client specified headers, then we don't want to clone any
  271. // headers
  272. //
  273. if ( pExecUrlInfo->pszChildHeaders == NULL )
  274. {
  275. dwCloneRequestFlags |= W3_REQUEST_CLONE_HEADERS;
  276. }
  277. //
  278. // Now, should we also clone the precondition headers?
  279. //
  280. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE )
  281. {
  282. dwCloneRequestFlags |= W3_REQUEST_CLONE_NO_PRECONDITION;
  283. }
  284. //
  285. // Now, should we also clone the preloaded entity?
  286. //
  287. if ( pExecUrlInfo->pEntity == NULL )
  288. {
  289. dwCloneRequestFlags |= W3_REQUEST_CLONE_ENTITY;
  290. }
  291. //
  292. // OK. Start the fun by cloning the current request (as much as needed)
  293. //
  294. DBG_ASSERT( QueryRequest() != NULL );
  295. hr = QueryRequest()->CloneRequest( dwCloneRequestFlags,
  296. &pChildRequest );
  297. if ( FAILED( hr ) )
  298. {
  299. goto Finished;
  300. }
  301. DBG_ASSERT( pChildRequest != NULL );
  302. //
  303. // If the parent specified headers, add them now
  304. //
  305. if ( pExecUrlInfo->pszChildHeaders != NULL )
  306. {
  307. hr = pChildRequest->SetHeadersByStream( pExecUrlInfo->pszChildHeaders );
  308. if ( FAILED( hr ) )
  309. {
  310. goto Finished;
  311. }
  312. }
  313. //
  314. // If we have entity to stuff in, do so now
  315. //
  316. if ( pExecUrlInfo->pEntity != NULL )
  317. {
  318. hr = pChildRequest->AppendEntityBody( pExecUrlInfo->pEntity->lpbData,
  319. pExecUrlInfo->pEntity->cbAvailable );
  320. if ( FAILED( hr ) )
  321. {
  322. goto Finished;
  323. }
  324. }
  325. //
  326. // Setup the URL for the request, if specified (it can contain a
  327. // query string)
  328. //
  329. // If this is a command execution, then ignore the URL since it really
  330. // isn't a URL in this case (it is a command to execute)
  331. //
  332. if ( pExecUrlInfo->pszUrl != NULL &&
  333. !fIsSSICommandExecution )
  334. {
  335. hr = strNewUrl.CopyA( pExecUrlInfo->pszUrl );
  336. if ( FAILED( hr ) )
  337. {
  338. goto Finished;
  339. }
  340. //
  341. // Now set the new URL/Querystring
  342. //
  343. hr = pChildRequest->SetUrl( strNewUrl );
  344. if ( FAILED( hr ) )
  345. {
  346. goto Finished;
  347. }
  348. }
  349. //
  350. // Set the verb for this request if specified
  351. //
  352. if ( pExecUrlInfo->pszMethod != NULL )
  353. {
  354. hr = strNewVerb.Copy( pExecUrlInfo->pszMethod );
  355. if ( FAILED( hr ) )
  356. {
  357. goto Finished;
  358. }
  359. hr = pChildRequest->SetVerb( strNewVerb );
  360. if ( FAILED( hr ) )
  361. {
  362. goto Finished;
  363. }
  364. }
  365. //
  366. // If a user context was set for this child request, then create a
  367. // custom user context and do the necessary hookup
  368. //
  369. if ( pExecUrlInfo->pUserInfo != NULL )
  370. {
  371. if ( pExecUrlInfo->pUserInfo->hImpersonationToken != NULL )
  372. {
  373. hImpersonationToken = pExecUrlInfo->pUserInfo->hImpersonationToken;
  374. }
  375. else
  376. {
  377. hImpersonationToken = QueryImpersonationToken();
  378. }
  379. DBG_ASSERT( hImpersonationToken != NULL );
  380. //
  381. // Create the user context
  382. //
  383. pCustomUser = new CUSTOM_USER_CONTEXT(
  384. W3_STATE_AUTHENTICATION::QueryCustomProvider() );
  385. if ( pCustomUser == NULL )
  386. {
  387. goto Finished;
  388. }
  389. hr = pCustomUser->Create( hImpersonationToken,
  390. pExecUrlInfo->pUserInfo->pszCustomUserName,
  391. QueryUserContext()->QueryAuthType() );
  392. if ( FAILED( hr ) )
  393. {
  394. goto Finished;
  395. }
  396. //
  397. // Make sure REMOTE_USER is calculated properly
  398. //
  399. hr = strCustomUserName.Copy( pExecUrlInfo->pUserInfo->pszCustomUserName );
  400. if ( FAILED( hr ) )
  401. {
  402. goto Finished;
  403. }
  404. hr = pChildRequest->SetRequestUserName( strCustomUserName );
  405. if ( FAILED( hr ) )
  406. {
  407. goto Finished;
  408. }
  409. //
  410. // Note that AUTH_TYPE server variable is a request'ism. So stuff it
  411. // into the request now indirectly by setting authorization header
  412. //
  413. hr = strAuthorization.Copy( "Authorization" );
  414. if ( FAILED( hr ) )
  415. {
  416. goto Finished;
  417. }
  418. hr = strCustomAuthType.Copy( pExecUrlInfo->pUserInfo->pszCustomAuthType );
  419. if ( FAILED( hr ) )
  420. {
  421. goto Finished;
  422. }
  423. hr = pChildRequest->SetHeader( strAuthorization,
  424. strCustomAuthType,
  425. FALSE );
  426. if ( FAILED( hr ) )
  427. {
  428. goto Finished;
  429. }
  430. }
  431. else
  432. {
  433. //
  434. // No custom user
  435. //
  436. pCustomUser = NULL;
  437. }
  438. //
  439. // Now we can create a child context
  440. //
  441. pChildContext = new W3_CHILD_CONTEXT( QueryMainContext(),
  442. this, // parent
  443. pChildRequest,
  444. TRUE, // child owns
  445. pCustomUser,
  446. dwExecFlags );
  447. if ( pChildContext == NULL )
  448. {
  449. hr = HRESULT_FROM_WIN32( GetLastError() );
  450. goto Finished;
  451. }
  452. //
  453. // Get the URL context for this new context
  454. //
  455. hr = pChildContext->RetrieveUrlContext( &fFinished );
  456. if ( FAILED( hr ) )
  457. {
  458. goto Finished;
  459. }
  460. if ( fFinished )
  461. {
  462. //
  463. // Filter wants to bail.
  464. //
  465. hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  466. goto Finished;
  467. }
  468. //
  469. // Now validate that the app pool name is correct, if necessary
  470. //
  471. if ( !( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_IGNORE_APPPOOL ) )
  472. {
  473. if ( QueryUrlContext() == NULL )
  474. {
  475. DBG_ASSERT( FALSE );
  476. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  477. goto Finished;
  478. }
  479. DBG_ASSERT( QueryUrlContext()->QueryMetaData() );
  480. pszRequiredAppPool = QueryUrlContext()->QueryMetaData()->QueryAppPoolId();
  481. if ( wcscmp( pszRequiredAppPool,
  482. pChildContext->QueryUrlContext()->QueryMetaData()->QueryAppPoolId() ) )
  483. {
  484. //
  485. // App pool name doesn't match. Bail
  486. //
  487. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  488. goto Finished;
  489. }
  490. }
  491. //
  492. // ULTRA-GROSS. If this is an explicit command execution from SSI, then
  493. // we explicitly setup the CGI handler. Otherwise, we determine the
  494. // handler using sane rules
  495. //
  496. if ( fIsSSICommandExecution )
  497. {
  498. pSSICommandHandler = new W3_CGI_HANDLER( pChildContext,
  499. NULL,
  500. pExecUrlInfo->pszUrl );
  501. if ( pSSICommandHandler == NULL )
  502. {
  503. hr = HRESULT_FROM_WIN32( GetLastError() );
  504. goto Finished;
  505. }
  506. pChildContext->SetSSICommandHandler( pSSICommandHandler );
  507. }
  508. else
  509. {
  510. //
  511. // Properly find a handler for this request
  512. //
  513. hr = pChildContext->DetermineHandler( fEnableWildcardMapping );
  514. if ( FAILED( hr ) )
  515. {
  516. goto Finished;
  517. }
  518. }
  519. DBG_ASSERT( pChildContext->QueryHandler() != NULL );
  520. hr = pChildContext->ExecuteHandler( dwExecFlags );
  521. //
  522. // If we failed, then we should just cleanup and report the error.
  523. //
  524. // NOTE: Failure means failure to spawn the child request. Not that
  525. // the child request encountered a failure.
  526. //
  527. Finished:
  528. //
  529. // If we spawned this async, and there was no failure, then return out
  530. //
  531. if ( !FAILED( hr ) && ( dwExecFlags & W3_FLAG_ASYNC ) )
  532. {
  533. return NO_ERROR;
  534. }
  535. //
  536. // If we're here, we either failed, or succeeded synchronously
  537. //
  538. //
  539. // If the urlcontext/child-request was attached to the child context,
  540. // the child context will clean it up
  541. //
  542. if ( pChildContext != NULL )
  543. {
  544. delete pChildContext;
  545. }
  546. else
  547. {
  548. if ( pChildRequest != NULL )
  549. {
  550. delete pChildRequest;
  551. }
  552. if ( pCustomUser != NULL )
  553. {
  554. pCustomUser->DereferenceUserContext();
  555. pCustomUser = NULL;
  556. }
  557. }
  558. return hr;
  559. }
  560. HRESULT
  561. W3_CONTEXT::ExecuteChildRequest(
  562. W3_REQUEST * pNewRequest,
  563. BOOL fOwnRequest,
  564. DWORD dwFlags
  565. )
  566. /*++
  567. Routine Description:
  568. Execute a child request (for internal W3CORE use only)
  569. Arguments:
  570. pNewRequest - W3_REQUEST * representing the new request
  571. fOwnRequest - Should the child context be responsible for cleaning up
  572. request?
  573. dwFlags - W3_FLAG_ASYNC async
  574. W3_FLAG_SYNC sync
  575. W3_FLAG_MORE_DATA caller needs to send data too,
  576. W3_FLAG_NO_CUSTOM_ERROR do not execute custom errors
  577. Return Value:
  578. HRESULT
  579. --*/
  580. {
  581. HRESULT hr;
  582. W3_CHILD_CONTEXT * pChildContext = NULL;
  583. BOOL fFinished = FALSE;
  584. if ( !VALID_W3_FLAGS( dwFlags ) )
  585. {
  586. DBG_ASSERT( FALSE );
  587. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  588. }
  589. //
  590. // Ignore the fFinished value on a child
  591. //
  592. pChildContext = new W3_CHILD_CONTEXT( QueryMainContext(),
  593. this,
  594. pNewRequest,
  595. fOwnRequest,
  596. NULL,
  597. dwFlags );
  598. if ( pChildContext == NULL )
  599. {
  600. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  601. goto Finished;
  602. }
  603. //
  604. // First read the metadata for this new request
  605. //
  606. hr = pChildContext->RetrieveUrlContext( &fFinished );
  607. if ( FAILED( hr ) )
  608. {
  609. goto Finished;
  610. }
  611. if ( fFinished )
  612. {
  613. //
  614. // Filter wants to bail.
  615. //
  616. hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  617. goto Finished;
  618. }
  619. //
  620. // Find handler
  621. //
  622. hr = pChildContext->DetermineHandler( TRUE );
  623. if ( FAILED( hr ) )
  624. {
  625. goto Finished;
  626. }
  627. DBG_ASSERT( pChildContext->QueryHandler() );
  628. //
  629. // Execute the handler and take the error in any non-pending case
  630. // (we can't get the status on pending since the value we would be
  631. // getting is instable)
  632. //
  633. hr = pChildContext->ExecuteHandler( dwFlags );
  634. Finished:
  635. //
  636. // If we spawned this async, and there was no failure, then return out
  637. //
  638. if ( !FAILED( hr ) && ( dwFlags & W3_FLAG_ASYNC ) )
  639. {
  640. return NO_ERROR;
  641. }
  642. //
  643. // If we got here, then either something bad happened OR a synchronous
  644. // child execute is complete. Either way, we can delete the context
  645. // here
  646. //
  647. if ( pChildContext != NULL )
  648. {
  649. delete pChildContext;
  650. }
  651. else
  652. {
  653. if ( pNewRequest && fOwnRequest )
  654. {
  655. delete pNewRequest;
  656. }
  657. }
  658. return hr;
  659. }
  660. HRESULT
  661. W3_CONTEXT::SetupAllowHeader(
  662. VOID
  663. )
  664. /*++
  665. Routine Description:
  666. Setup a 405 response. This means setting the response status as well
  667. as setting up an appropriate Allow: header
  668. Arguments:
  669. None
  670. Return Value:
  671. HRESULT
  672. --*/
  673. {
  674. STACK_STRA( strAllowHeader, 256 );
  675. URL_CONTEXT * pUrlContext;
  676. HRESULT hr;
  677. W3_URL_INFO * pUrlInfo;
  678. W3_METADATA * pMetaData;
  679. MULTISZA * pAllowedVerbs;
  680. const CHAR * pszAllowedVerb;
  681. pUrlContext = QueryUrlContext();
  682. if ( pUrlContext == NULL )
  683. {
  684. //
  685. // If we have no metadata, then don't send an Allow: header. The
  686. // only case this would happen is if a filter decided to send the
  687. // response and in that case it is up to it to send an appropriate
  688. // Allow: header
  689. //
  690. return NO_ERROR;
  691. }
  692. pUrlInfo = pUrlContext->QueryUrlInfo();
  693. if ( pUrlInfo == NULL )
  694. {
  695. DBG_ASSERT( FALSE );
  696. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  697. }
  698. pMetaData = pUrlContext->QueryMetaData();
  699. if ( pMetaData == NULL )
  700. {
  701. DBG_ASSERT( FALSE );
  702. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  703. }
  704. //
  705. // We always support OPTIONS and TRACE
  706. //
  707. hr = strAllowHeader.Append( "OPTIONS, TRACE" );
  708. if ( FAILED( hr ) )
  709. {
  710. return hr;
  711. }
  712. //
  713. // Is this URL script mapped? If so, then the verbs allowed is equal to
  714. // to the verbs specified in the script map, provided that we have
  715. // script execute permissions
  716. //
  717. if ( pUrlInfo->QueryScriptMapEntry() != NULL )
  718. {
  719. if ( IS_ACCESS_ALLOWED( QueryRequest(),
  720. pMetaData->QueryAccessPerms(),
  721. SCRIPT ) )
  722. {
  723. pAllowedVerbs = pUrlInfo->QueryScriptMapEntry()->QueryAllowedVerbs();
  724. DBG_ASSERT( pAllowedVerbs != NULL );
  725. pszAllowedVerb = pAllowedVerbs->First();
  726. while ( pszAllowedVerb != NULL )
  727. {
  728. hr = strAllowHeader.Append( ", " );
  729. if ( FAILED( hr ) )
  730. {
  731. return hr;
  732. }
  733. hr = strAllowHeader.Append( pszAllowedVerb );
  734. if (FAILED(hr))
  735. {
  736. return hr;
  737. }
  738. pszAllowedVerb = pAllowedVerbs->Next( pszAllowedVerb );
  739. }
  740. }
  741. }
  742. else
  743. {
  744. //
  745. // Must be a static file or a explicit gateway
  746. //
  747. switch( pUrlInfo->QueryGateway() )
  748. {
  749. case GATEWAY_UNKNOWN:
  750. case GATEWAY_MAP:
  751. hr = strAllowHeader.Append( ", GET, HEAD" );
  752. break;
  753. case GATEWAY_CGI:
  754. case GATEWAY_ISAPI:
  755. hr = strAllowHeader.Append( ", GET, HEAD, POST" );
  756. break;
  757. }
  758. if ( FAILED( hr ) )
  759. {
  760. return hr;
  761. }
  762. }
  763. //
  764. // Now add some DAV verbs (man, this is broken)
  765. //
  766. if ( IS_ACCESS_ALLOWED( QueryRequest(),
  767. pMetaData->QueryAccessPerms(),
  768. WRITE ) &&
  769. !pMetaData->QueryIsDavDisabled() )
  770. {
  771. hr = strAllowHeader.Append( ", DELETE, PUT" );
  772. if ( FAILED( hr ) )
  773. {
  774. return hr;
  775. }
  776. }
  777. //
  778. // Finally, set the header
  779. //
  780. hr = QueryResponse()->SetHeader( HttpHeaderAllow,
  781. strAllowHeader.QueryStr(),
  782. strAllowHeader.QueryCCH() );
  783. return hr;
  784. }
  785. HRESULT
  786. W3_CONTEXT::SetupCustomErrorFileResponse(
  787. STRU & strErrorFile
  788. )
  789. /*++
  790. Routine Description:
  791. Open the custom error file (nor URL) from cache if possible and setup
  792. the response (which means, fill in entity body with file)
  793. Arguments:
  794. strErrorFile - Custom Error File
  795. Return Value:
  796. HRESULT (HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND) to indicate to caller
  797. that it should just generate hard coded custom error.
  798. --*/
  799. {
  800. W3_FILE_INFO * pOpenFile = NULL;
  801. HRESULT hr = NO_ERROR;
  802. DWORD dwFlags;
  803. W3_RESPONSE * pResponse = QueryResponse();
  804. FILE_CACHE_USER fileUser;
  805. LARGE_INTEGER liFileSize;
  806. W3_METADATA * pMetaData;
  807. URL_CONTEXT * pUrlContext;
  808. STACK_STRA( strContentType, 32 );
  809. DBG_ASSERT( pResponse != NULL );
  810. DBG_ASSERT( !pResponse->QueryEntityExists() );
  811. DBG_ASSERT( pResponse->QueryStatusCode() >= 400 );
  812. //
  813. // Get the file from the cache. We will enable deferred directory
  814. // monitor changes. This means that we will register for the appropriate
  815. // parent directory changes as needed and remove as the cached file
  816. // object goes away.
  817. //
  818. DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL );
  819. hr = g_pW3Server->QueryFileCache()->GetFileInfo( strErrorFile,
  820. NULL,
  821. &fileUser,
  822. TRUE,
  823. &pOpenFile );
  824. if ( FAILED( hr ) )
  825. {
  826. //
  827. // Switch some known errors back to file not found
  828. //
  829. switch( hr )
  830. {
  831. case HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ):
  832. case HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ):
  833. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  834. break;
  835. }
  836. return hr;
  837. }
  838. DBG_ASSERT( pOpenFile != NULL );
  839. //
  840. // We might already have a custom error file IF we encountered an error
  841. // setting up the last custom error response
  842. //
  843. if ( _pCustomErrorFile != NULL )
  844. {
  845. _pCustomErrorFile->DereferenceCacheEntry();
  846. _pCustomErrorFile = NULL;
  847. }
  848. DBG_ASSERT( pOpenFile != NULL );
  849. _pCustomErrorFile = pOpenFile;
  850. //
  851. // Determine the content-type and set the header
  852. //
  853. pUrlContext = QueryUrlContext();
  854. DBG_ASSERT( pUrlContext != NULL );
  855. pMetaData = pUrlContext->QueryMetaData();
  856. DBG_ASSERT( pMetaData != NULL );
  857. hr = SelectMimeMappingForFileExt( strErrorFile.QueryStr(),
  858. pMetaData->QueryMimeMap(),
  859. &strContentType );
  860. if ( SUCCEEDED( hr ) )
  861. {
  862. hr = pResponse->SetHeader( HttpHeaderContentType,
  863. strContentType.QueryStr(),
  864. strContentType.QueryCCH() );
  865. if ( FAILED( hr ) )
  866. {
  867. return hr;
  868. }
  869. }
  870. //
  871. // Now setup the response chunk
  872. //
  873. pOpenFile->QuerySize( &liFileSize );
  874. if ( pOpenFile->QueryFileBuffer() != NULL )
  875. {
  876. hr = pResponse->AddMemoryChunkByReference(
  877. pOpenFile->QueryFileBuffer(),
  878. liFileSize.LowPart );
  879. }
  880. else
  881. {
  882. hr = pResponse->AddFileHandleChunk(pOpenFile->QueryFileHandle(),
  883. 0,
  884. liFileSize.LowPart );
  885. }
  886. return hr;
  887. }
  888. HANDLE
  889. W3_CONTEXT::QueryImpersonationToken(
  890. BOOL * pfIsVrToken
  891. )
  892. /*++
  893. Routine Description:
  894. Get the impersonation token for this request. This routine will
  895. choose correctly based on whether there is a VRoot token, passthru is
  896. enabled, etc.
  897. Arguments:
  898. None
  899. Return Value:
  900. HANDLE
  901. --*/
  902. {
  903. W3_METADATA * pMetaData;
  904. TOKEN_CACHE_ENTRY * pVrToken;
  905. W3_USER_CONTEXT * pUserContext;
  906. DBG_ASSERT( QueryUrlContext() != NULL );
  907. pMetaData = QueryUrlContext()->QueryMetaData();
  908. DBG_ASSERT( pMetaData != NULL );
  909. pUserContext = QueryUserContext();
  910. DBG_ASSERT( pUserContext != NULL );
  911. pVrToken = pMetaData->QueryVrAccessToken();
  912. if ( pVrToken == NULL ||
  913. ( pMetaData->QueryVrPassThrough() &&
  914. pUserContext->QueryDelegatable() ) )
  915. {
  916. if ( pfIsVrToken )
  917. {
  918. *pfIsVrToken = FALSE;
  919. }
  920. return pUserContext->QueryImpersonationToken();
  921. }
  922. else
  923. {
  924. DBG_ASSERT( pVrToken != NULL );
  925. if ( pfIsVrToken )
  926. {
  927. *pfIsVrToken = TRUE;
  928. }
  929. return pVrToken->QueryImpersonationToken();
  930. }
  931. }
  932. VOID
  933. W3_CONTEXT::QueryFileCacheUser(
  934. FILE_CACHE_USER * pFileCacheUser
  935. )
  936. /*++
  937. Routine Description:
  938. Get a user descriptor suitable for use with file cache
  939. Arguments:
  940. pFileCacheUser - Filled with file cache user information
  941. Return Value:
  942. None
  943. --*/
  944. {
  945. W3_METADATA * pMetaData;
  946. TOKEN_CACHE_ENTRY * pVrToken;
  947. W3_USER_CONTEXT * pUserContext;
  948. HANDLE hReturnToken = NULL;
  949. PSID pReturnSid = NULL;
  950. DBG_ASSERT( pFileCacheUser != NULL );
  951. DBG_ASSERT( QueryUrlContext() != NULL );
  952. pMetaData = QueryUrlContext()->QueryMetaData();
  953. DBG_ASSERT( pMetaData != NULL );
  954. pUserContext = QueryUserContext();
  955. DBG_ASSERT( pUserContext != NULL );
  956. pVrToken = pMetaData->QueryVrAccessToken();
  957. if ( pVrToken == NULL ||
  958. ( pMetaData->QueryVrPassThrough() &&
  959. pUserContext->QueryDelegatable() ) )
  960. {
  961. hReturnToken = pUserContext->QueryImpersonationToken();
  962. pReturnSid = pUserContext->QuerySid();
  963. }
  964. else
  965. {
  966. DBG_ASSERT( pVrToken != NULL );
  967. pReturnSid = pVrToken->QuerySid();
  968. hReturnToken = pVrToken->QueryImpersonationToken();
  969. }
  970. //
  971. // Setup the file cache user
  972. //
  973. pFileCacheUser->_hToken = hReturnToken;
  974. pFileCacheUser->_pSid = pReturnSid;
  975. }
  976. HANDLE
  977. W3_CONTEXT::QueryPrimaryToken(
  978. VOID
  979. )
  980. /*++
  981. Routine Description:
  982. Get the primary token for this request. This routine will
  983. choose correctly based on whether there is a VRoot token, passthru is
  984. enabled, etc.
  985. Arguments:
  986. None
  987. Return Value:
  988. HANDLE
  989. --*/
  990. {
  991. W3_METADATA * pMetaData;
  992. TOKEN_CACHE_ENTRY * pVrToken;
  993. W3_USER_CONTEXT * pUserContext;
  994. DBG_ASSERT( QueryUrlContext() != NULL );
  995. pMetaData = QueryUrlContext()->QueryMetaData();
  996. DBG_ASSERT( pMetaData != NULL );
  997. pUserContext = QueryUserContext();
  998. DBG_ASSERT( pUserContext != NULL );
  999. pVrToken = pMetaData->QueryVrAccessToken();
  1000. if ( pVrToken == NULL ||
  1001. ( pMetaData->QueryVrPassThrough() &&
  1002. pUserContext->QueryDelegatable() ) )
  1003. {
  1004. return pUserContext->QueryPrimaryToken();
  1005. }
  1006. else
  1007. {
  1008. DBG_ASSERT( pVrToken != NULL );
  1009. return pVrToken->QueryPrimaryToken();
  1010. }
  1011. }
  1012. HRESULT
  1013. W3_CONTEXT::GetCertificateInfoEx(
  1014. IN DWORD cbAllocated,
  1015. OUT DWORD * pdwCertEncodingType,
  1016. OUT unsigned char * pbCertEncoded,
  1017. OUT DWORD * pcbCertEncoded,
  1018. OUT DWORD * pdwCertificateFlags
  1019. )
  1020. /*++
  1021. Routine Description:
  1022. Retrieve certificate info if available
  1023. Arguments:
  1024. Return Value:
  1025. HRESULT
  1026. --*/
  1027. {
  1028. CERTIFICATE_CONTEXT * pCertContext = NULL;
  1029. DWORD cbCert = 0;
  1030. PVOID pvCert = NULL;
  1031. if ( pdwCertEncodingType == NULL ||
  1032. pbCertEncoded == NULL ||
  1033. pcbCertEncoded == NULL ||
  1034. pdwCertificateFlags == NULL )
  1035. {
  1036. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1037. }
  1038. pCertContext = QueryCertificateContext();
  1039. if ( pCertContext == NULL )
  1040. {
  1041. //
  1042. // If we don't have a certificate, then just succeed with nothing
  1043. // to keep in line with IIS 5 behaviour
  1044. //
  1045. *pbCertEncoded = NULL;
  1046. *pcbCertEncoded = 0;
  1047. return NO_ERROR;
  1048. }
  1049. pCertContext->QueryEncodedCertificate( &pvCert, &cbCert );
  1050. DBG_ASSERT( pvCert != NULL );
  1051. DBG_ASSERT( cbCert != 0 );
  1052. //
  1053. // Fill in the parameters
  1054. //
  1055. *pcbCertEncoded = cbCert;
  1056. if ( cbAllocated < *pcbCertEncoded )
  1057. {
  1058. //
  1059. // Not enough space
  1060. //
  1061. return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
  1062. }
  1063. else
  1064. {
  1065. memcpy( pbCertEncoded,
  1066. pvCert,
  1067. *pcbCertEncoded );
  1068. *pdwCertificateFlags = pCertContext->QueryFlags();
  1069. *pdwCertEncodingType = X509_ASN_ENCODING;
  1070. return NO_ERROR;
  1071. }
  1072. }
  1073. HRESULT
  1074. W3_CONTEXT::SendResponse(
  1075. DWORD dwFlags
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. Sends a response to the client thru ULATQ.
  1080. Arguments:
  1081. dwFlags - Flags
  1082. W3_FLAG_ASYNC - Send response asynchronously
  1083. W3_FLAG_SYNC - Send response synchronously
  1084. W3_FLAG_MORE_DATA - Send more data
  1085. W3_FLAG_NO_ERROR_BODY - Don't generate error body
  1086. W3_FLAG_GENERATE_CONTENT_LENGTH - generate content length
  1087. Return Value:
  1088. HRESULT
  1089. --*/
  1090. {
  1091. HRESULT hr = NO_ERROR;
  1092. BOOL fIsFileError = FALSE;
  1093. BOOL fFinished = FALSE;
  1094. DWORD cbSent = 0;
  1095. BOOL fAddContentLength = FALSE;
  1096. BOOL fSendHeaders = FALSE;
  1097. BOOL fHandlerManagesHead = FALSE;
  1098. BOOL fEnableUlCache = FALSE;
  1099. BOOL fSuppressEntity = FALSE;
  1100. DWORD dwResponseFlags = 0;
  1101. AUTH_PROVIDER * pAnonymousProvider = NULL;
  1102. W3_RESPONSE * pResponse;
  1103. W3_FILTER_CONTEXT * pFilterContext;
  1104. if ( !VALID_W3_FLAGS( dwFlags ) )
  1105. {
  1106. DBG_ASSERT( FALSE );
  1107. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1108. }
  1109. pResponse = QueryResponse();
  1110. DBG_ASSERT( pResponse != NULL );
  1111. //
  1112. // Should we be sending headers at all?
  1113. //
  1114. fSendHeaders = QuerySendHeaders();
  1115. //
  1116. // Do a quick precheck to see whether we require an END_OF_REQUEST
  1117. // notification. If so, then this won't be our last send
  1118. //
  1119. if ( fSendHeaders &&
  1120. ( IsNotificationNeeded( SF_NOTIFY_END_OF_REQUEST ) ||
  1121. IsNotificationNeeded( SF_NOTIFY_LOG ) ) )
  1122. {
  1123. if ( !( dwFlags & W3_FLAG_MORE_DATA ) &&
  1124. !QueryNeedFinalDone() )
  1125. {
  1126. //
  1127. // If the only reason this is NOT the final send is because of
  1128. // an END_OF_REQUEST or LOG filter, then act like IIS 5.0 and
  1129. // still send a content length
  1130. //
  1131. fAddContentLength = TRUE;
  1132. }
  1133. dwFlags |= W3_FLAG_MORE_DATA;
  1134. }
  1135. //
  1136. // Remember whether we need to send an empty done on our own at the end
  1137. //
  1138. if ( dwFlags & W3_FLAG_MORE_DATA )
  1139. {
  1140. SetNeedFinalDone();
  1141. }
  1142. //
  1143. // Was this an access denial not handled by an authentication provider?
  1144. //
  1145. if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode &&
  1146. !QueryProviderHandled() &&
  1147. QueryUrlContext() != NULL )
  1148. {
  1149. //
  1150. // First call access denied filters
  1151. //
  1152. if ( IsNotificationNeeded( SF_NOTIFY_ACCESS_DENIED ) )
  1153. {
  1154. STACK_STRA( straUrl, 256 );
  1155. STACK_STRA( straPhys, 256 );
  1156. STACK_STRU( strUrl, 256 );
  1157. STRU * pstrPhysicalPath;
  1158. hr = QueryRequest()->GetUrl( &strUrl );
  1159. if ( FAILED( hr ) )
  1160. {
  1161. return hr;
  1162. }
  1163. pstrPhysicalPath = QueryUrlContext()->QueryPhysicalPath();
  1164. DBG_ASSERT( pstrPhysicalPath != NULL );
  1165. if (FAILED(hr = straUrl.CopyW(strUrl.QueryStr())) ||
  1166. FAILED(hr = straPhys.CopyW(pstrPhysicalPath->QueryStr())))
  1167. {
  1168. return hr;
  1169. }
  1170. if ( !QueryFilterContext()->NotifyAccessDenied(
  1171. straUrl.QueryStr(),
  1172. straPhys.QueryStr(),
  1173. &fFinished ) )
  1174. {
  1175. return HRESULT_FROM_WIN32( GetLastError() );
  1176. }
  1177. if ( fFinished )
  1178. {
  1179. if ( dwFlags & W3_FLAG_ASYNC )
  1180. {
  1181. ThreadPoolPostCompletion( 0,
  1182. W3_MAIN_CONTEXT::OnPostedCompletion,
  1183. (LPOVERLAPPED) QueryMainContext() );
  1184. }
  1185. return NO_ERROR;
  1186. }
  1187. }
  1188. //
  1189. // Now, notify all authentication providers so they can add headers
  1190. // if necessary
  1191. //
  1192. hr = W3_STATE_AUTHENTICATION::CallAllAccessDenied( QueryMainContext() );
  1193. if ( FAILED( hr ) )
  1194. {
  1195. return hr;
  1196. }
  1197. }
  1198. //
  1199. // Send a custom error for this response if all of following are true:
  1200. //
  1201. // a) The caller wants an error body
  1202. // b) This request (and all children) actually allow an error body
  1203. // c) The response code is greater than 400 (error)
  1204. //
  1205. if ( !(dwFlags & W3_FLAG_NO_ERROR_BODY ) &&
  1206. QuerySendErrorBody() &&
  1207. pResponse->QueryStatusCode() >= 400 )
  1208. {
  1209. STACK_STRU( strError, 64 );
  1210. STACK_STRU( strFullUrl, 64 );
  1211. WCHAR achNum[ 32 ];
  1212. HTTP_SUB_ERROR subError;
  1213. W3_REQUEST * pChildRequest = NULL;
  1214. pResponse->Clear( TRUE );
  1215. DisableUlCache();
  1216. //
  1217. // Get the sub error for this response (if any)
  1218. //
  1219. hr = pResponse->QuerySubError( &subError );
  1220. if ( FAILED( hr ) )
  1221. {
  1222. return hr;
  1223. }
  1224. //
  1225. // Check for a configured custom error. This check is only possible
  1226. // if we have read metadata. We may not have read metadata if
  1227. // some fatal error occurred early in the pipeline (for example,
  1228. // an out-of-mem error in the URLINFO state)
  1229. //
  1230. if ( QueryUrlContext() != NULL &&
  1231. QuerySendCustomError() )
  1232. {
  1233. W3_METADATA * pMetaData = NULL;
  1234. pMetaData = QueryUrlContext()->QueryMetaData();
  1235. DBG_ASSERT( pMetaData != NULL );
  1236. hr = pMetaData->FindCustomError( pResponse->QueryStatusCode(),
  1237. subError.mdSubError,
  1238. &fIsFileError,
  1239. &strError );
  1240. //
  1241. // If this is a file error, check for file existence now. If it
  1242. // does not exist, then act as if there is no custom error set
  1243. //
  1244. if ( SUCCEEDED( hr ) )
  1245. {
  1246. if ( fIsFileError )
  1247. {
  1248. hr = SetupCustomErrorFileResponse( strError );
  1249. }
  1250. else
  1251. {
  1252. //
  1253. // This is a custom error URL
  1254. //
  1255. //
  1256. // If there is no user context, then executing a child
  1257. // request would be tough.
  1258. //
  1259. // But we can still try to get the anonymous token, if
  1260. // it exists and use it
  1261. //
  1262. if ( QueryUserContext() == NULL )
  1263. {
  1264. pAnonymousProvider = W3_STATE_AUTHENTICATION::QueryAnonymousProvider();
  1265. DBG_ASSERT( pAnonymousProvider != NULL );
  1266. hr = pAnonymousProvider->DoAuthenticate( QueryMainContext() );
  1267. if ( FAILED( hr ) ||
  1268. QueryMainContext()->QueryUserContext() == NULL )
  1269. {
  1270. //
  1271. // Ok. We really can't get anonymous user.
  1272. // No custom error URL
  1273. //
  1274. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1275. }
  1276. }
  1277. }
  1278. }
  1279. }
  1280. else
  1281. {
  1282. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1283. }
  1284. if ( SUCCEEDED( hr ) )
  1285. {
  1286. //
  1287. // Great. Send the configured custom error
  1288. //
  1289. if ( !fIsFileError )
  1290. {
  1291. //
  1292. // This is a custom error URL. Reset and launch custom URL
  1293. //
  1294. // The URL is of the form:
  1295. //
  1296. // /customerrorurl?<status>;http://<server>/<original url>
  1297. //
  1298. hr = QueryRequest()->CloneRequest(
  1299. W3_REQUEST_CLONE_BASICS |
  1300. W3_REQUEST_CLONE_HEADERS |
  1301. W3_REQUEST_CLONE_NO_PRECONDITION |
  1302. W3_REQUEST_CLONE_NO_DAV,
  1303. &pChildRequest );
  1304. if ( FAILED( hr ) )
  1305. {
  1306. return hr;
  1307. }
  1308. DBG_ASSERT( pChildRequest != NULL );
  1309. hr = strError.Append( L"?" );
  1310. if ( FAILED( hr ) )
  1311. {
  1312. return hr;
  1313. }
  1314. _itow( pResponse->QueryStatusCode(), achNum, 10 );
  1315. hr = strError.Append( achNum );
  1316. if ( FAILED( hr ) )
  1317. {
  1318. return hr;
  1319. }
  1320. hr = strError.Append( L";" );
  1321. if ( FAILED( hr ) )
  1322. {
  1323. return hr;
  1324. }
  1325. hr = QueryRequest()->GetFullUrl( &strFullUrl );
  1326. if ( FAILED( hr ) )
  1327. {
  1328. return hr;
  1329. }
  1330. hr = strError.Append( strFullUrl );
  1331. if ( FAILED( hr ) )
  1332. {
  1333. return hr;
  1334. }
  1335. //
  1336. // Change the URL and verb of the request
  1337. //
  1338. hr = pChildRequest->SetUrl( strError );
  1339. if ( FAILED( hr ) )
  1340. {
  1341. return hr;
  1342. }
  1343. //
  1344. // Remove DAVFS'ness
  1345. //
  1346. pChildRequest->RemoveDav();
  1347. //
  1348. // All custom error URLs are GET/HEADs
  1349. //
  1350. if ( pChildRequest->QueryVerbType() == HttpVerbHEAD )
  1351. {
  1352. pChildRequest->SetVerbType( HttpVerbHEAD );
  1353. }
  1354. else
  1355. {
  1356. pChildRequest->SetVerbType( HttpVerbGET );
  1357. }
  1358. pResponse->SetStatus( HttpStatusOk );
  1359. hr = ExecuteChildRequest( pChildRequest,
  1360. TRUE, // child context cleans up
  1361. dwFlags | W3_FLAG_NO_CUSTOM_ERROR );
  1362. if ( FAILED( hr ) )
  1363. {
  1364. return hr;
  1365. }
  1366. return NO_ERROR;
  1367. }
  1368. }
  1369. else
  1370. {
  1371. //
  1372. // Not finding a custom error is OK, but any other error is
  1373. // fatal
  1374. //
  1375. DBG_ASSERT( FAILED( hr ) );
  1376. if ( hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  1377. {
  1378. return hr;
  1379. }
  1380. CHAR *achImageError;
  1381. DWORD cbImageError = 512;
  1382. if (FAILED(hr = QueryHeaderBuffer()->AllocateSpace(
  1383. cbImageError,
  1384. &achImageError)))
  1385. {
  1386. return hr;
  1387. }
  1388. //
  1389. // Check the image for a resource string which may apply.
  1390. //
  1391. if ( subError.dwStringId != 0 )
  1392. {
  1393. hr = g_pW3Server->LoadString( subError.dwStringId,
  1394. achImageError,
  1395. &cbImageError );
  1396. }
  1397. else if ( pResponse->QueryStatusCode() ==
  1398. HttpStatusUnauthorized.statusCode &&
  1399. sm_pszAccessDeniedMessage != NULL )
  1400. {
  1401. //
  1402. // Note: 401 message is actually configured in the registry
  1403. //
  1404. strncpy( achImageError,
  1405. sm_pszAccessDeniedMessage,
  1406. cbImageError - 1 );
  1407. achImageError[ cbImageError - 1 ] = '\0';
  1408. cbImageError = strlen( achImageError );
  1409. hr = NO_ERROR;
  1410. }
  1411. else
  1412. {
  1413. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1414. }
  1415. if (FAILED(hr) &&
  1416. FAILED(QueryErrorStatus()))
  1417. {
  1418. cbImageError = 512;
  1419. //
  1420. // If there is a particular error status set, find the
  1421. // message for that error and send it. This is that last
  1422. // thing we can do to attempt to send a useful message to
  1423. // client
  1424. //
  1425. cbImageError = FormatMessageA(
  1426. FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1427. NULL,
  1428. QueryErrorStatus(),
  1429. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1430. achImageError,
  1431. cbImageError,
  1432. NULL);
  1433. if (cbImageError == 0)
  1434. {
  1435. hr = HRESULT_FROM_WIN32(GetLastError());
  1436. }
  1437. else
  1438. {
  1439. hr = S_OK;
  1440. }
  1441. }
  1442. if ( SUCCEEDED( hr ) )
  1443. {
  1444. //
  1445. // Add content type
  1446. //
  1447. hr = pResponse->SetHeader( HttpHeaderContentType,
  1448. "text/html",
  1449. 9 );
  1450. if ( FAILED( hr ) )
  1451. {
  1452. return hr;
  1453. }
  1454. hr = pResponse->AddMemoryChunkByReference(
  1455. achImageError,
  1456. cbImageError );
  1457. if ( FAILED( hr ) )
  1458. {
  1459. return hr;
  1460. }
  1461. }
  1462. }
  1463. }
  1464. //
  1465. // Send custom headers if any
  1466. //
  1467. if ( fSendHeaders &&
  1468. QueryUrlContext() != NULL )
  1469. {
  1470. W3_METADATA *pMetaData = QueryUrlContext()->QueryMetaData();
  1471. DBG_ASSERT( pMetaData != NULL );
  1472. //
  1473. // Add Custom HTTP Headers if defined in the metabase.
  1474. //
  1475. if ( !pMetaData->QueryCustomHeaders()->IsEmpty() )
  1476. {
  1477. hr = pResponse->AppendResponseHeaders( *(pMetaData->QueryCustomHeaders()) );
  1478. if ( FAILED( hr ) )
  1479. {
  1480. pResponse->Clear();
  1481. return hr;
  1482. }
  1483. }
  1484. //
  1485. // Add Cache-Control and Expires header if so configured
  1486. //
  1487. STRA *pstrCacheControlHeader = pMetaData->QueryCacheControlHeader();
  1488. if (!pstrCacheControlHeader->IsEmpty())
  1489. {
  1490. if (FAILED(hr = pResponse->SetHeader(
  1491. HttpHeaderCacheControl,
  1492. pstrCacheControlHeader->QueryStr(),
  1493. pstrCacheControlHeader->QueryCCH(),
  1494. TRUE)))
  1495. {
  1496. pResponse->Clear();
  1497. return hr;
  1498. }
  1499. }
  1500. if (pMetaData->QueryExpireMode() == EXPIRE_MODE_STATIC)
  1501. {
  1502. STRA *pstrExpireHeader = pMetaData->QueryExpireHeader();
  1503. if (FAILED(hr = pResponse->SetHeaderByReference(
  1504. HttpHeaderExpires,
  1505. pstrExpireHeader->QueryStr(),
  1506. pstrExpireHeader->QueryCCH())))
  1507. {
  1508. pResponse->Clear();
  1509. return hr;
  1510. }
  1511. }
  1512. }
  1513. //
  1514. // Does the current handler manage content length on its own?
  1515. // We'll assume (i.e. the choice which means we do nothing to supply
  1516. // a content-length header or suppress HEADs)
  1517. //
  1518. if ( QueryHandler() != NULL )
  1519. {
  1520. fHandlerManagesHead = QueryHandler()->QueryManagesOwnHead();
  1521. }
  1522. //
  1523. // Should we be adding a content-length header?
  1524. //
  1525. // Note we get the original verb (not the current request's verb).
  1526. // This is because the original verb determines whether or not UL
  1527. // will be sending a content-length for us.
  1528. //
  1529. if ( ( QueryMainContext()->QueryShouldGenerateContentLength() &&
  1530. !fHandlerManagesHead &&
  1531. !QueryNeedFinalDone() ) ||
  1532. fAddContentLength ||
  1533. (dwFlags & W3_FLAG_GENERATE_CONTENT_LENGTH) )
  1534. {
  1535. CHAR achNum[ 32 ];
  1536. //
  1537. // If there is already a Content-Length header, then no need to
  1538. // make one (this must mean the handler handled HEAD themselves)
  1539. //
  1540. if ( pResponse->GetHeader( HttpHeaderContentLength ) == NULL )
  1541. {
  1542. _ui64toa( pResponse->QueryContentLength(),
  1543. achNum,
  1544. 10 );
  1545. hr = pResponse->SetHeader( HttpHeaderContentLength,
  1546. achNum,
  1547. strlen( achNum ) );
  1548. if ( FAILED( hr ) )
  1549. {
  1550. SetErrorStatus( hr );
  1551. pResponse->SetStatus( HttpStatusServerError );
  1552. return hr;
  1553. }
  1554. }
  1555. }
  1556. //
  1557. // Should we be suppressing entity. This should be done if this is a
  1558. // HEAD request and the current handler doesn't take responsibility
  1559. // to handle HEADs
  1560. //
  1561. if ( QueryRequest()->QueryVerbType() == HttpVerbHEAD &&
  1562. !fHandlerManagesHead )
  1563. {
  1564. DisableUlCache();
  1565. fSuppressEntity = TRUE;
  1566. }
  1567. //
  1568. // Remember what type of operation this is, so that the completion
  1569. // can do the proper book keeping
  1570. //
  1571. if ( dwFlags & W3_FLAG_ASYNC )
  1572. {
  1573. SetLastIOPending( LOG_WRITE_IO );
  1574. }
  1575. //
  1576. // Setup logging information
  1577. //
  1578. BOOL fDoLogging = FALSE;
  1579. if ( fSendHeaders &&
  1580. !QueryNeedFinalDone() &&
  1581. QueryDoUlLogging() )
  1582. {
  1583. fDoLogging = TRUE;
  1584. if ( FAILED( hr = QueryMainContext()->CollectLoggingData( TRUE ) ) )
  1585. {
  1586. return hr;
  1587. }
  1588. }
  1589. //
  1590. // If a filter added response headers (either for regular responses or
  1591. // for denial responses), then add them now
  1592. //
  1593. pFilterContext = QueryFilterContext( FALSE );
  1594. if ( pFilterContext != NULL )
  1595. {
  1596. //
  1597. // Add denial headers if this is a denial
  1598. //
  1599. if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
  1600. {
  1601. //
  1602. // Add filter denied headers
  1603. //
  1604. hr = pResponse->AppendResponseHeaders(
  1605. *pFilterContext->QueryAddDenialHeaders() );
  1606. if ( FAILED( hr ) )
  1607. {
  1608. return hr;
  1609. }
  1610. pFilterContext->QueryAddDenialHeaders()->Reset();
  1611. }
  1612. //
  1613. // Add regular headers
  1614. //
  1615. hr = pResponse->AppendResponseHeaders(
  1616. *pFilterContext->QueryAddResponseHeaders() );
  1617. if ( FAILED( hr ) )
  1618. {
  1619. return hr;
  1620. }
  1621. pFilterContext->QueryAddResponseHeaders()->Reset();
  1622. }
  1623. //
  1624. // Notify Response header filters
  1625. //
  1626. if ( fSendHeaders &&
  1627. IsNotificationNeeded( SF_NOTIFY_SEND_RESPONSE ) )
  1628. {
  1629. HTTP_FILTER_SEND_RESPONSE sendResponse;
  1630. sendResponse.GetHeader = GetSendHeader;
  1631. sendResponse.SetHeader = SetSendHeader;
  1632. sendResponse.AddHeader = AddSendHeader;
  1633. sendResponse.HttpStatus = pResponse->QueryStatusCode();
  1634. if ( !NotifyFilters( SF_NOTIFY_SEND_RESPONSE,
  1635. &sendResponse,
  1636. &fFinished ) )
  1637. {
  1638. return HRESULT_FROM_WIN32( GetLastError() ); // BUGBUG
  1639. }
  1640. if ( fFinished )
  1641. {
  1642. if ( dwFlags & W3_FLAG_ASYNC )
  1643. {
  1644. ThreadPoolPostCompletion( 0,
  1645. W3_MAIN_CONTEXT::OnPostedCompletion,
  1646. (LPOVERLAPPED) QueryMainContext() );
  1647. }
  1648. return NO_ERROR;
  1649. }
  1650. }
  1651. // perf ctr
  1652. if (QuerySite() != NULL)
  1653. {
  1654. if ( pResponse->QueryStatusCode() == HttpStatusNotFound.statusCode )
  1655. {
  1656. QuerySite()->IncNotFound();
  1657. }
  1658. else if ( pResponse->QueryStatusCode() == HttpStatusLockedError.statusCode )
  1659. {
  1660. QuerySite()->IncLockedError();
  1661. }
  1662. }
  1663. //
  1664. // If any one sees a reason why this response and this kernel mode cache
  1665. // should not be together, speak now or forever hold your peace (or
  1666. // at least until the UL cache flushes the response)
  1667. //
  1668. DBG_ASSERT( g_pW3Server->QueryUlCache() != NULL );
  1669. if ( g_pW3Server->QueryUlCache()->CheckUlCacheability( this ) )
  1670. {
  1671. //
  1672. // The response is UL cacheable, so setup a ul cache entry token
  1673. //
  1674. if ( _pHandler != NULL )
  1675. {
  1676. DBG_ASSERT( _pHandler->QueryIsUlCacheable() );
  1677. hr = _pHandler->SetupUlCachedResponse( this );
  1678. if ( SUCCEEDED( hr ) )
  1679. {
  1680. fEnableUlCache = TRUE;
  1681. }
  1682. }
  1683. }
  1684. //
  1685. // Generate response flags now
  1686. //
  1687. if ( QueryNeedFinalDone() )
  1688. {
  1689. dwResponseFlags |= W3_RESPONSE_MORE_DATA;
  1690. }
  1691. if ( QueryDisconnect() )
  1692. {
  1693. dwResponseFlags |= W3_RESPONSE_DISCONNECT;
  1694. }
  1695. if ( fEnableUlCache )
  1696. {
  1697. dwResponseFlags |= W3_RESPONSE_UL_CACHEABLE;
  1698. }
  1699. if ( !fSendHeaders )
  1700. {
  1701. dwResponseFlags |= W3_RESPONSE_SUPPRESS_HEADERS;
  1702. }
  1703. if ( fSuppressEntity )
  1704. {
  1705. dwResponseFlags |= W3_RESPONSE_SUPPRESS_ENTITY;
  1706. }
  1707. if ( dwFlags & W3_FLAG_ASYNC )
  1708. {
  1709. dwResponseFlags |= W3_RESPONSE_ASYNC;
  1710. }
  1711. //
  1712. // Send out the full response now
  1713. //
  1714. hr = pResponse->SendResponse( this,
  1715. dwResponseFlags,
  1716. &cbSent,
  1717. fDoLogging ? QueryUlLogData() : NULL );
  1718. if (!(dwFlags & W3_FLAG_ASYNC))
  1719. {
  1720. IncrementBytesSent(cbSent);
  1721. }
  1722. if (FAILED(hr))
  1723. {
  1724. if (_pHandler == NULL)
  1725. {
  1726. DBGPRINTF((DBG_CONTEXT,
  1727. "SendResponse failed: no handler, hr %x\n",
  1728. hr));
  1729. }
  1730. else
  1731. {
  1732. DBGPRINTF((DBG_CONTEXT,
  1733. "SendResponse failed: handler %S, hr %x\n",
  1734. _pHandler->QueryName(),
  1735. hr));
  1736. }
  1737. }
  1738. return hr;
  1739. }
  1740. HRESULT
  1741. W3_CONTEXT::SendEntity(
  1742. DWORD dwFlags
  1743. )
  1744. /*++
  1745. Routine Description:
  1746. Sends a response entity to the client thru ULATQ.
  1747. Arguments:
  1748. dwFlags - Flags
  1749. W3_FLAG_ASYNC - Send response asynchronously
  1750. W3_FLAG_SYNC - Send response synchronously
  1751. W3_FLAG_PAST_END_OF_REQ - Send after END_OF_REQUEST notification
  1752. to let UL know we are done
  1753. Return Value:
  1754. HRESULT
  1755. --*/
  1756. {
  1757. DWORD dwResponseFlags = 0;
  1758. if ( !VALID_W3_FLAGS( dwFlags ) ||
  1759. ( dwFlags & W3_FLAG_NO_CUSTOM_ERROR ) )
  1760. {
  1761. DBG_ASSERT( FALSE );
  1762. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1763. }
  1764. //
  1765. // Set the final send just in case we never did a SendResponse (which
  1766. // would normally have set this).
  1767. //
  1768. SetNeedFinalDone();
  1769. if (dwFlags & W3_FLAG_ASYNC)
  1770. {
  1771. SetLastIOPending(LOG_WRITE_IO);
  1772. }
  1773. BOOL fDoLogging = FALSE;
  1774. HRESULT hr;
  1775. if ((dwFlags & W3_FLAG_PAST_END_OF_REQ) &&
  1776. QueryDoUlLogging())
  1777. {
  1778. fDoLogging = TRUE;
  1779. if (FAILED(hr = QueryMainContext()->CollectLoggingData(TRUE)))
  1780. {
  1781. return hr;
  1782. }
  1783. }
  1784. //
  1785. // Setup response flags
  1786. //
  1787. if ( !( dwFlags & W3_FLAG_PAST_END_OF_REQ ) )
  1788. {
  1789. dwResponseFlags |= W3_RESPONSE_MORE_DATA;
  1790. }
  1791. if ( dwFlags & W3_FLAG_ASYNC )
  1792. {
  1793. dwResponseFlags |= W3_RESPONSE_ASYNC;
  1794. }
  1795. if ( QueryDisconnect() )
  1796. {
  1797. dwResponseFlags |= W3_RESPONSE_DISCONNECT;
  1798. }
  1799. if ( QueryRequest()->QueryVerbType() == HttpVerbHEAD &&
  1800. ( QueryHandler() == NULL ||
  1801. !QueryHandler()->QueryManagesOwnHead() ) )
  1802. {
  1803. dwResponseFlags |= W3_RESPONSE_SUPPRESS_ENTITY;
  1804. }
  1805. //
  1806. // Do the send now
  1807. //
  1808. DWORD cbSent = 0;
  1809. hr = QueryResponse()->SendEntity(
  1810. this,
  1811. dwResponseFlags,
  1812. &cbSent,
  1813. fDoLogging ? QueryUlLogData() : NULL);
  1814. if (!(dwFlags & W3_FLAG_ASYNC))
  1815. {
  1816. IncrementBytesSent(cbSent);
  1817. }
  1818. if (FAILED(hr))
  1819. {
  1820. if (_pHandler == NULL)
  1821. {
  1822. DBGPRINTF((DBG_CONTEXT,
  1823. "SendEntity failed: no handler, hr %x\n",
  1824. hr));
  1825. }
  1826. else
  1827. {
  1828. DBGPRINTF((DBG_CONTEXT,
  1829. "SendEntity failed: handler %S, hr %x\n",
  1830. _pHandler->QueryName(),
  1831. hr));
  1832. }
  1833. }
  1834. return hr;
  1835. }
  1836. DWORD
  1837. W3_CONTEXT::QueryRemainingEntityFromUl(
  1838. VOID
  1839. )
  1840. /*++
  1841. Routine Description:
  1842. Returns how much entity can be read from UL
  1843. Arguments:
  1844. None
  1845. Return Value:
  1846. Number of bytes available (INFINITE if chunked)
  1847. --*/
  1848. {
  1849. return QueryMainContext()->QueryRemainingEntityFromUl();
  1850. }
  1851. VOID
  1852. W3_CONTEXT::SetRemainingEntityFromUl(
  1853. DWORD cbRemaining
  1854. )
  1855. /*++
  1856. Routine Description:
  1857. Sets how much entity can be read from UL
  1858. Arguments:
  1859. DWORD
  1860. Return Value:
  1861. None
  1862. --*/
  1863. {
  1864. QueryMainContext()->SetRemainingEntityFromUl(cbRemaining);
  1865. }
  1866. VOID
  1867. W3_CONTEXT::QueryAlreadyAvailableEntity(
  1868. VOID ** ppvBuffer,
  1869. DWORD * pcbBuffer
  1870. )
  1871. /*++
  1872. Routine Description:
  1873. Returns the already preloaded entity found in the current request
  1874. Arguments:
  1875. ppvBuffer - filled with pointer to available entity
  1876. pcbBuffer - filled with size of buffer pointed to by *ppvBuffer
  1877. Return Value:
  1878. None
  1879. --*/
  1880. {
  1881. W3_REQUEST * pRequest;
  1882. DBG_ASSERT( ppvBuffer != NULL );
  1883. DBG_ASSERT( pcbBuffer != NULL );
  1884. pRequest = QueryRequest();
  1885. DBG_ASSERT( pRequest != NULL );
  1886. *ppvBuffer = pRequest->QueryEntityBody();
  1887. *pcbBuffer = pRequest->QueryAvailableBytes();
  1888. }
  1889. HRESULT
  1890. W3_CONTEXT::ReceiveEntity(
  1891. DWORD dwFlags,
  1892. VOID * pBuffer,
  1893. DWORD cbBuffer,
  1894. DWORD * pBytesReceived
  1895. )
  1896. /*++
  1897. Routine Description:
  1898. Receives request entity from the client thru ULATQ.
  1899. Arguments:
  1900. dwFlags - W3_FLAG_ASYNC or W3_FLAG_SYNC
  1901. pBuffer - Buffer to contain the data
  1902. cbBuffer - The size of the buffer
  1903. pBytesReceived - Upon return, the number of bytes
  1904. copied to the buffer
  1905. Return Value:
  1906. HRESULT
  1907. --*/
  1908. {
  1909. W3_MAIN_CONTEXT * pMainContext;
  1910. HRESULT hr;
  1911. if (dwFlags & W3_FLAG_ASYNC)
  1912. {
  1913. SetLastIOPending(LOG_READ_IO);
  1914. }
  1915. pMainContext = QueryMainContext();
  1916. DBG_ASSERT( pMainContext != NULL );
  1917. hr = pMainContext->ReceiveEntityBody( !!(dwFlags & W3_FLAG_ASYNC),
  1918. pBuffer,
  1919. cbBuffer,
  1920. pBytesReceived );
  1921. if (!(dwFlags & W3_FLAG_ASYNC))
  1922. {
  1923. IncrementBytesRecvd(*pBytesReceived);
  1924. }
  1925. if (FAILED(hr))
  1926. {
  1927. if (_pHandler == NULL)
  1928. {
  1929. DBGPRINTF((DBG_CONTEXT,
  1930. "ReceiveEntity failed: no handler, hr %x\n",
  1931. hr));
  1932. }
  1933. else if (hr != HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
  1934. {
  1935. DBGPRINTF((DBG_CONTEXT,
  1936. "ReceiveEntity failed: handler %S, hr %x\n",
  1937. _pHandler->QueryName(),
  1938. hr));
  1939. }
  1940. }
  1941. return hr;
  1942. }
  1943. HRESULT
  1944. W3_CONTEXT::CleanIsapiExecuteUrl(
  1945. HSE_EXEC_URL_INFO * pExecUrlInfo
  1946. )
  1947. /*++
  1948. Routine Description:
  1949. Wrapper for IsapiExecuteUrl() which insured it is called on a clean
  1950. thread (non-coinited). COM bites
  1951. Arguments:
  1952. pExecUrlInfo - HSE_EXEC_URL_INFO
  1953. Return Value:
  1954. HRESULT
  1955. --*/
  1956. {
  1957. EXECUTE_CONTEXT * pExecuteContext;
  1958. HRESULT hr;
  1959. BOOL fRet;
  1960. if ( pExecUrlInfo == NULL )
  1961. {
  1962. DBG_ASSERT( FALSE );
  1963. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1964. }
  1965. pExecuteContext = new EXECUTE_CONTEXT( this );
  1966. if ( pExecuteContext == NULL )
  1967. {
  1968. return HRESULT_FROM_WIN32( GetLastError() );
  1969. }
  1970. hr = pExecuteContext->InitializeFromExecUrlInfo( pExecUrlInfo );
  1971. if ( FAILED( hr ) )
  1972. {
  1973. delete pExecuteContext;
  1974. return hr;
  1975. }
  1976. fRet = ThreadPoolPostCompletion( 0,
  1977. W3_CONTEXT::OnCleanIsapiExecuteUrl,
  1978. (LPOVERLAPPED) pExecuteContext );
  1979. if ( !fRet )
  1980. {
  1981. hr = HRESULT_FROM_WIN32( GetLastError() );
  1982. delete pExecuteContext;
  1983. return hr;
  1984. }
  1985. return NO_ERROR;
  1986. }
  1987. HRESULT
  1988. W3_CONTEXT::CleanIsapiSendCustomError(
  1989. HSE_CUSTOM_ERROR_INFO * pCustomErrorInfo
  1990. )
  1991. /*++
  1992. Routine Description:
  1993. Wrapper for IsapiSendCustomError() which insured it is called on a clean
  1994. thread (non-coinited). COM bites
  1995. Arguments:
  1996. pCustomErrorInfo - Custom error info
  1997. Return Value:
  1998. HRESULT
  1999. --*/
  2000. {
  2001. EXECUTE_CONTEXT * pExecuteContext;
  2002. HRESULT hr;
  2003. HANDLE hEvent = NULL;
  2004. BOOL fRet;
  2005. W3_RESPONSE * pResponse = QueryResponse();
  2006. W3_METADATA * pMetaData = NULL;
  2007. STACK_STRU( strError, 256 );
  2008. BOOL fIsFileError;
  2009. HTTP_SUB_ERROR subError;
  2010. if ( pCustomErrorInfo == NULL )
  2011. {
  2012. DBG_ASSERT( FALSE );
  2013. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2014. }
  2015. DBG_ASSERT( pResponse != NULL );
  2016. //
  2017. // If we're already sending a custom error, then stop the recursion
  2018. //
  2019. if ( !QuerySendCustomError() )
  2020. {
  2021. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  2022. }
  2023. pResponse->Clear();
  2024. //
  2025. // We don't expect an empty status line here (we expect an error)
  2026. //
  2027. if ( pCustomErrorInfo->pszStatus == NULL ||
  2028. pCustomErrorInfo->pszStatus[ 0 ] == '\0' )
  2029. {
  2030. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2031. }
  2032. //
  2033. // Setup the response status/substatus
  2034. //
  2035. hr = pResponse->BuildStatusFromIsapi( pCustomErrorInfo->pszStatus );
  2036. if ( FAILED( hr ) )
  2037. {
  2038. return hr;
  2039. }
  2040. subError.mdSubError = pCustomErrorInfo->uHttpSubError;
  2041. subError.dwStringId = 0;
  2042. pResponse->SetSubError( &subError );
  2043. //
  2044. // An error better have been sent
  2045. //
  2046. if ( pResponse->QueryStatusCode() < 400 )
  2047. {
  2048. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  2049. }
  2050. //
  2051. // Now try to find a custom error for the given response. If it doesn't
  2052. // exist then we error our
  2053. //
  2054. DBG_ASSERT( QueryUrlContext() != NULL );
  2055. pMetaData = QueryUrlContext()->QueryMetaData();
  2056. DBG_ASSERT( pMetaData != NULL );
  2057. hr = pMetaData->FindCustomError( QueryResponse()->QueryStatusCode(),
  2058. subError.mdSubError,
  2059. &fIsFileError,
  2060. &strError );
  2061. if ( FAILED( hr ) )
  2062. {
  2063. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  2064. }
  2065. //
  2066. // Now start executing the custom error
  2067. //
  2068. if ( pCustomErrorInfo->fAsync == FALSE )
  2069. {
  2070. hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  2071. if ( hEvent == NULL )
  2072. {
  2073. return HRESULT_FROM_WIN32( GetLastError() );
  2074. }
  2075. }
  2076. pExecuteContext = new EXECUTE_CONTEXT( this );
  2077. if ( pExecuteContext == NULL )
  2078. {
  2079. return HRESULT_FROM_WIN32( GetLastError() );
  2080. }
  2081. pExecuteContext->SetCompleteEvent( hEvent );
  2082. //
  2083. // Post the call
  2084. //
  2085. fRet = ThreadPoolPostCompletion( 0,
  2086. W3_CONTEXT::OnCleanIsapiSendCustomError,
  2087. (LPOVERLAPPED) pExecuteContext );
  2088. if ( !fRet )
  2089. {
  2090. hr = HRESULT_FROM_WIN32( GetLastError() );
  2091. CloseHandle( hEvent );
  2092. pExecuteContext->SetCompleteEvent( NULL );
  2093. delete pExecuteContext;
  2094. return hr;
  2095. }
  2096. if ( pCustomErrorInfo->fAsync == FALSE )
  2097. {
  2098. WaitForSingleObject( hEvent, INFINITE );
  2099. CloseHandle( hEvent );
  2100. }
  2101. return NO_ERROR;
  2102. }
  2103. HRESULT
  2104. W3_CONTEXT::ExecuteHandler(
  2105. DWORD dwFlags,
  2106. BOOL * pfDidImmediateFinish
  2107. )
  2108. /*++
  2109. Routine Description:
  2110. Run the context's handler
  2111. Arguments:
  2112. dwFlags - W3_FLAG_ASYNC async
  2113. W3_FLAG_SYNC sync
  2114. W3_FLAG_MORE_DATA caller needs to send data too
  2115. pfDidImmediateFinish - If the caller sets this value (i.e. non NULL) AND
  2116. the callers asks for async execution, then in the
  2117. case where the handler executes synchronously, we
  2118. will set *pfDidImmediateFinish to TRUE and not
  2119. bother with faking a completion. This is an
  2120. optimization to handle the synchronous ISAPI
  2121. execution (non-child) case.
  2122. Return Value:
  2123. HRESULT
  2124. --*/
  2125. {
  2126. CONTEXT_STATUS status;
  2127. W3_HANDLER * pHandler;
  2128. HTTP_SUB_ERROR subError;
  2129. HRESULT hr;
  2130. if ( pfDidImmediateFinish != NULL )
  2131. {
  2132. *pfDidImmediateFinish = FALSE;
  2133. }
  2134. //
  2135. // If this is synchronous execution, then setup an event to signal
  2136. // upon the handler completion
  2137. //
  2138. if ( dwFlags & W3_FLAG_SYNC )
  2139. {
  2140. SetupCompletionEvent();
  2141. }
  2142. //
  2143. // Clear any response lingering
  2144. //
  2145. hr = QueryResponse()->Clear();
  2146. if ( FAILED( hr ) )
  2147. {
  2148. return hr;
  2149. }
  2150. //
  2151. // Redirect all completions to the current context (this)
  2152. //
  2153. QueryMainContext()->PushCurrentContext( this );
  2154. //
  2155. // Execute the handler
  2156. //
  2157. status = ExecuteCurrentHandler();
  2158. if ( status == CONTEXT_STATUS_CONTINUE )
  2159. {
  2160. //
  2161. // Remember not to execute completion routine for handler
  2162. //
  2163. DBG_ASSERT( _pHandler != NULL );
  2164. //
  2165. // We don't need the handler any more
  2166. //
  2167. delete _pHandler;
  2168. _pHandler = NULL;
  2169. //
  2170. // The handler completed synchronously. If async call was required
  2171. // then post a fake completion, unless the caller set
  2172. // pfDidImmediateFinish in which case we'll finish now and set the
  2173. // flag to TRUE
  2174. //
  2175. if ( dwFlags & W3_FLAG_ASYNC &&
  2176. pfDidImmediateFinish == NULL )
  2177. {
  2178. if ( ThreadPoolPostCompletion( 0,
  2179. W3_MAIN_CONTEXT::OnPostedCompletion,
  2180. (LPOVERLAPPED) QueryMainContext() ) )
  2181. {
  2182. return NO_ERROR;
  2183. }
  2184. DBG_ASSERT( FALSE );
  2185. }
  2186. else
  2187. {
  2188. //
  2189. // We indicate the "success/failure" of this child context
  2190. // to the parent context, so that the parent can send this
  2191. // info to ISAPIs if needed
  2192. //
  2193. if ( QueryParentContext() != NULL )
  2194. {
  2195. QueryResponse()->QuerySubError( &subError );
  2196. QueryParentContext()->SetChildStatusAndError(
  2197. QueryResponse()->QueryStatusCode(),
  2198. subError.mdSubError,
  2199. QueryErrorStatus() );
  2200. }
  2201. //
  2202. // Don't both signalling completion event since there is nothing
  2203. // to wait for. (Might revisit this decision later)
  2204. //
  2205. //
  2206. // Current context is no longer needed in completion stack
  2207. //
  2208. QueryMainContext()->PopCurrentContext();
  2209. //
  2210. // If caller wanted status, tell them now
  2211. //
  2212. if ( pfDidImmediateFinish != NULL )
  2213. {
  2214. *pfDidImmediateFinish = TRUE;
  2215. }
  2216. }
  2217. }
  2218. else
  2219. {
  2220. DBG_ASSERT( status == CONTEXT_STATUS_PENDING );
  2221. //
  2222. // The handler will complete asynchronsouly. But we are asked for
  2223. // synchronous execution. So wait here until complete
  2224. //
  2225. if ( dwFlags & W3_FLAG_SYNC )
  2226. {
  2227. WaitForCompletion();
  2228. }
  2229. }
  2230. return NO_ERROR;
  2231. }
  2232. CONTEXT_STATUS
  2233. W3_CONTEXT::ExecuteCurrentHandler(
  2234. VOID
  2235. )
  2236. /*++
  2237. Routine Description:
  2238. Execute the handler for this context
  2239. Arguments:
  2240. None
  2241. Return Value:
  2242. CONTEXT_STATUS_PENDING if async pending, else CONTEXT_STATUS_CONTINUE
  2243. --*/
  2244. {
  2245. HRESULT hr;
  2246. DBG_ASSERT( _pHandler != NULL );
  2247. //
  2248. // Now check if we should be doing redirection for this request
  2249. //
  2250. if (QueryRequest()->QueryVerbType() != HttpVerbTRACE &&
  2251. QueryRequest()->QueryVerbType() != HttpVerbTRACK)
  2252. {
  2253. BOOL fRedirected = FALSE;
  2254. if (FAILED(DoUrlRedirection(&fRedirected)) ||
  2255. fRedirected)
  2256. {
  2257. return CONTEXT_STATUS_CONTINUE;
  2258. }
  2259. }
  2260. //
  2261. // Access is allowed and no URL redirection. Let'r rip!
  2262. //
  2263. return _pHandler->MainDoWork();
  2264. }
  2265. CONTEXT_STATUS
  2266. W3_CONTEXT::ExecuteHandlerCompletion(
  2267. DWORD cbCompletion,
  2268. DWORD dwCompletionStatus
  2269. )
  2270. /*++
  2271. Routine Description:
  2272. Execute the current handler's completion.
  2273. Arguments:
  2274. cbCompletion - Completion bytes
  2275. dwCompletionStatus - Completion error
  2276. Return Value:
  2277. CONTEXT_STATUS_PENDING if async pending, else CONTEXT_STATUS_CONTINUE
  2278. --*/
  2279. {
  2280. CONTEXT_STATUS status;
  2281. W3_HANDLER * pHandler;
  2282. W3_CONTEXT * pParentContext;
  2283. DWORD dwChildStatus;
  2284. BOOL fAccessAllowed = FALSE;
  2285. HRESULT hr;
  2286. HTTP_SUB_ERROR subError;
  2287. //
  2288. // This is a completion for the handler.
  2289. //
  2290. // If this is a faked completion, the handler is already cleaned up
  2291. //
  2292. if ( _pHandler != NULL )
  2293. {
  2294. status = _pHandler->MainOnCompletion( cbCompletion,
  2295. dwCompletionStatus );
  2296. }
  2297. else
  2298. {
  2299. status = CONTEXT_STATUS_CONTINUE;
  2300. }
  2301. if ( status == CONTEXT_STATUS_PENDING )
  2302. {
  2303. return status;
  2304. }
  2305. else
  2306. {
  2307. //
  2308. // Cleanup the current context if necessary
  2309. //
  2310. DBG_ASSERT( status == CONTEXT_STATUS_CONTINUE );
  2311. //
  2312. // Current handler execute is complete. Delete it
  2313. //
  2314. if ( _pHandler != NULL )
  2315. {
  2316. delete _pHandler;
  2317. _pHandler = NULL;
  2318. }
  2319. //
  2320. // The current handler is complete. But if this is a child
  2321. // request we must first indicate handler completion to the caller
  2322. //
  2323. pParentContext = QueryParentContext();
  2324. if ( pParentContext != NULL )
  2325. {
  2326. //
  2327. // We indicate the "success/failure" of this child context
  2328. // to the parent context, so that the parent can send this
  2329. // info to ISAPIs if needed
  2330. //
  2331. QueryResponse()->QuerySubError( &subError );
  2332. pParentContext->SetChildStatusAndError(
  2333. QueryResponse()->QueryStatusCode(),
  2334. subError.mdSubError,
  2335. QueryErrorStatus() );
  2336. //
  2337. // Setup all future completions to indicate to parent
  2338. //
  2339. QueryMainContext()->PopCurrentContext();
  2340. //
  2341. // We are a child execute
  2342. //
  2343. if ( QueryIsSynchronous() )
  2344. {
  2345. IndicateCompletion();
  2346. //
  2347. // We assume the thread waiting on completion will be
  2348. // responsible for advancing the state machine
  2349. //
  2350. //
  2351. // The waiting thread will also cleanup the child context
  2352. //
  2353. return CONTEXT_STATUS_PENDING;
  2354. }
  2355. else
  2356. {
  2357. dwChildStatus = WIN32_FROM_HRESULT( QueryErrorStatus() );
  2358. //
  2359. // We can destroy the current context now. In the case of
  2360. // synchronous execution, it is the caller that cleans up
  2361. // the new context
  2362. //
  2363. delete this;
  2364. return pParentContext->ExecuteHandlerCompletion(
  2365. 0,
  2366. dwChildStatus );
  2367. }
  2368. }
  2369. return CONTEXT_STATUS_CONTINUE;
  2370. }
  2371. }
  2372. W3_CONTEXT::W3_CONTEXT( DWORD dwExecFlags )
  2373. : _pHandler ( NULL ),
  2374. _hCompletion ( NULL ),
  2375. _errorStatus ( S_OK ),
  2376. _pCustomErrorFile ( NULL ),
  2377. _dwExecFlags ( dwExecFlags ),
  2378. _accessState ( ACCESS_STATE_START ),
  2379. _fDNSRequiredForAccess ( FALSE ),
  2380. _fAuthAccessCheckRequired ( TRUE ),
  2381. _childStatusCode ( 200 ),
  2382. _childSubErrorCode ( 0 ),
  2383. _childError ( S_OK )
  2384. {
  2385. _dwSignature = W3_CONTEXT_SIGNATURE;
  2386. }
  2387. W3_CONTEXT::~W3_CONTEXT()
  2388. {
  2389. _dwSignature = W3_CONTEXT_SIGNATURE_FREE;
  2390. if ( _pHandler != NULL )
  2391. {
  2392. delete _pHandler;
  2393. _pHandler = NULL;
  2394. }
  2395. if ( _hCompletion != NULL )
  2396. {
  2397. CloseHandle( _hCompletion );
  2398. _hCompletion = NULL;
  2399. }
  2400. if ( _pCustomErrorFile != NULL )
  2401. {
  2402. _pCustomErrorFile->DereferenceCacheEntry();
  2403. _pCustomErrorFile = NULL;
  2404. }
  2405. }
  2406. // static
  2407. HRESULT
  2408. W3_CONTEXT::Initialize(
  2409. VOID
  2410. )
  2411. /*++
  2412. Routine Description:
  2413. Global initialization routine for W3_CONTEXTs
  2414. Arguments:
  2415. None
  2416. Return Value:
  2417. HRESULT
  2418. --*/
  2419. {
  2420. HRESULT hr = NO_ERROR;
  2421. HKEY hKey = NULL;
  2422. DWORD dwError;
  2423. DWORD dwType;
  2424. DWORD cbBuffer;
  2425. BYTE bUnused;
  2426. //
  2427. // Read in the 302 message once
  2428. //
  2429. DBG_ASSERT( g_pW3Server != NULL );
  2430. sm_cbRedirectMessage = sizeof( sm_achRedirectMessage );
  2431. hr = g_pW3Server->LoadString( IDS_URL_MOVED,
  2432. sm_achRedirectMessage,
  2433. &sm_cbRedirectMessage );
  2434. if ( FAILED( hr ) )
  2435. {
  2436. return hr;
  2437. }
  2438. DBG_ASSERT( sm_cbRedirectMessage > 0 );
  2439. //
  2440. // Read the Access-Denied message from registry
  2441. //
  2442. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2443. W3_PARAMETERS_KEY,
  2444. 0,
  2445. KEY_READ,
  2446. &hKey );
  2447. if ( dwError == ERROR_SUCCESS )
  2448. {
  2449. DBG_ASSERT( hKey != NULL );
  2450. cbBuffer = 0;
  2451. dwType = 0;
  2452. dwError = RegQueryValueExA( hKey,
  2453. "AccessDeniedMessage",
  2454. NULL,
  2455. &dwType,
  2456. &bUnused,
  2457. &cbBuffer );
  2458. if ( dwError == ERROR_MORE_DATA && dwType == REG_SZ )
  2459. {
  2460. DBG_ASSERT( cbBuffer > 0 );
  2461. sm_pszAccessDeniedMessage = (CHAR*) LocalAlloc( LPTR, cbBuffer );
  2462. if ( sm_pszAccessDeniedMessage == NULL )
  2463. {
  2464. return HRESULT_FROM_WIN32( GetLastError() );
  2465. }
  2466. dwError = RegQueryValueExA( hKey,
  2467. "AccessDeniedMessage",
  2468. NULL,
  2469. NULL,
  2470. (LPBYTE) sm_pszAccessDeniedMessage,
  2471. &cbBuffer );
  2472. if ( dwError != ERROR_SUCCESS )
  2473. {
  2474. DBG_ASSERT( FALSE );
  2475. LocalFree( sm_pszAccessDeniedMessage );
  2476. sm_pszAccessDeniedMessage = NULL;
  2477. }
  2478. }
  2479. }
  2480. //
  2481. // Setup child contexts
  2482. //
  2483. hr = W3_CHILD_CONTEXT::Initialize();
  2484. if ( FAILED( hr ) )
  2485. {
  2486. return hr;
  2487. }
  2488. //
  2489. // Setup main contexts
  2490. //
  2491. hr = W3_MAIN_CONTEXT::Initialize();
  2492. if ( FAILED( hr ) )
  2493. {
  2494. W3_CHILD_CONTEXT::Terminate();
  2495. return hr;
  2496. }
  2497. //
  2498. // Setup execute contexts
  2499. //
  2500. hr = EXECUTE_CONTEXT::Initialize();
  2501. if ( FAILED( hr ) )
  2502. {
  2503. W3_MAIN_CONTEXT::Terminate();
  2504. W3_CHILD_CONTEXT::Terminate();
  2505. return hr;
  2506. }
  2507. return NO_ERROR;
  2508. }
  2509. // static
  2510. VOID
  2511. W3_CONTEXT::Terminate(
  2512. VOID
  2513. )
  2514. /*++
  2515. Routine Description:
  2516. Terminate W3_CONTEXT globals
  2517. Arguments:
  2518. None
  2519. Return Value:
  2520. None
  2521. --*/
  2522. {
  2523. EXECUTE_CONTEXT::Terminate();
  2524. W3_CHILD_CONTEXT::Terminate();
  2525. W3_MAIN_CONTEXT::Terminate();
  2526. if ( sm_pszAccessDeniedMessage != NULL )
  2527. {
  2528. LocalFree( sm_pszAccessDeniedMessage );
  2529. sm_pszAccessDeniedMessage = NULL;
  2530. }
  2531. }
  2532. //static
  2533. VOID
  2534. W3_CONTEXT::OnCleanIsapiExecuteUrl(
  2535. DWORD dwCompletionStatus,
  2536. DWORD cbWritten,
  2537. LPOVERLAPPED lpo
  2538. )
  2539. /*++
  2540. Routine Description:
  2541. Actually calls ExecuteHandler() on behalf of a thread which may have
  2542. already been CoInited(). The thread this this guy runs on should not
  2543. have been (COM sucks)
  2544. Arguments:
  2545. dwCompletionStatus - Completion status (ignored)
  2546. cbWritten - Bytes written (ignored)
  2547. lpo - Pointer to EXECUTE_CONTEXT which contains info needed for
  2548. ExecuteHandler() call
  2549. Return Value:
  2550. None
  2551. --*/
  2552. {
  2553. EXECUTE_CONTEXT * pExecuteContext;
  2554. W3_CONTEXT * pW3Context;
  2555. HRESULT hr;
  2556. DBG_ASSERT( lpo != NULL );
  2557. pExecuteContext = (EXECUTE_CONTEXT*) lpo;
  2558. DBG_ASSERT( pExecuteContext->CheckSignature() );
  2559. pW3Context = pExecuteContext->QueryW3Context();
  2560. DBG_ASSERT( pW3Context != NULL );
  2561. DBG_ASSERT( pW3Context->CheckSignature() );
  2562. //
  2563. // Make the call (this is an async call)
  2564. //
  2565. hr = pW3Context->IsapiExecuteUrl( pExecuteContext->QueryExecUrlInfo() );
  2566. delete pExecuteContext;
  2567. if ( FAILED( hr ) )
  2568. {
  2569. //
  2570. // We failed before posting async operation. It is up to us to
  2571. // fake a completion
  2572. //
  2573. DBG_ASSERT( pW3Context->QueryHandler() != NULL );
  2574. pW3Context->SetErrorStatus( hr );
  2575. pW3Context->QueryHandler()->MainOnCompletion( 0,
  2576. WIN32_FROM_HRESULT( hr ) );
  2577. }
  2578. }
  2579. //static
  2580. VOID
  2581. W3_CONTEXT::OnCleanIsapiSendCustomError(
  2582. DWORD dwCompletionStatus,
  2583. DWORD cbWritten,
  2584. LPOVERLAPPED lpo
  2585. )
  2586. /*++
  2587. Routine Description:
  2588. Actually calls ExecuteHandler() on behalf of a thread which may have
  2589. already been CoInited(). The thread this this guy runs on should not
  2590. have been (COM sucks)
  2591. Arguments:
  2592. dwCompletionStatus - Completion status (ignored)
  2593. cbWritten - Bytes written (ignored)
  2594. lpo - Pointer to SEND_CUSTOM_ERROR_CONTEXT which contains info needed for
  2595. ExecuteHandler() call
  2596. Return Value:
  2597. None
  2598. --*/
  2599. {
  2600. EXECUTE_CONTEXT * pExecuteContext;
  2601. W3_CONTEXT * pW3Context;
  2602. HRESULT hr;
  2603. BOOL fAsync;
  2604. DBG_ASSERT( lpo != NULL );
  2605. pExecuteContext = (EXECUTE_CONTEXT*) lpo;
  2606. DBG_ASSERT( pExecuteContext->CheckSignature() );
  2607. pW3Context = pExecuteContext->QueryW3Context();
  2608. DBG_ASSERT( pW3Context != NULL );
  2609. DBG_ASSERT( pW3Context->CheckSignature() );
  2610. fAsync = pExecuteContext->QueryCompleteEvent() == NULL;
  2611. hr = pW3Context->SendResponse( fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC );
  2612. delete pExecuteContext;
  2613. if ( FAILED( hr ) )
  2614. {
  2615. if ( fAsync )
  2616. {
  2617. //
  2618. // We failed before posting async operation. It is up to us to
  2619. // fake a completion
  2620. //
  2621. DBG_ASSERT( pW3Context->QueryHandler() != NULL );
  2622. pW3Context->SetErrorStatus( hr );
  2623. pW3Context->QueryHandler()->MainOnCompletion( 0,
  2624. WIN32_FROM_HRESULT( hr ) );
  2625. }
  2626. }
  2627. }
  2628. CHUNK_BUFFER *
  2629. W3_CONTEXT::QueryHeaderBuffer(
  2630. VOID
  2631. )
  2632. /*++
  2633. Routine Description:
  2634. Get header buffer to store data which must be around until
  2635. request is finished
  2636. Arguments:
  2637. None
  2638. Return Value:
  2639. CHUNK_BUFFER pointer
  2640. --*/
  2641. {
  2642. DBG_ASSERT( QueryMainContext() != NULL );
  2643. return QueryMainContext()->QueryHeaderBuffer();
  2644. }
  2645. CONTEXT_STATUS
  2646. W3_CONTEXT::CheckAccess(
  2647. BOOL fCompletion,
  2648. DWORD dwCompletionStatus,
  2649. BOOL * pfAccessAllowed
  2650. )
  2651. /*++
  2652. Routine Description:
  2653. Check whether the given request is allowed for the current metadata
  2654. settings. In particular check:
  2655. 1) Is SSL required?
  2656. 2) Are IP address restrictions in force?
  2657. 3) Is a client certificate required?
  2658. 4) Is the authentication mechanism allowed? Optionally
  2659. If access is not allowed, then this routine will send the appropriate
  2660. response asychronously
  2661. Arguments:
  2662. fCompletion - Are we being called on a completion (i.e.
  2663. is this a subsequent call to CheckAccess())
  2664. dwCompletionStatus - Completion status (if fCompletion is TRUE)
  2665. pfAccessAllowed - Set to TRUE if access is allowed, else FALSE
  2666. Return Value:
  2667. CONTEXT_STATUS_PENDING if we're not finished the check yet
  2668. CONTEXT_STATUS_CONTINUE if we are finished.
  2669. --*/
  2670. {
  2671. W3_METADATA * pMetaData;
  2672. URL_CONTEXT * pUrlContext;
  2673. HTTP_SSL_INFO * pSslInfo;
  2674. HTTP_SSL_CLIENT_CERT_INFO * pClientCertInfo = NULL;
  2675. DWORD dwAccessPerms;
  2676. HRESULT hr = NO_ERROR;
  2677. BOOL fAccessAllowed = TRUE;
  2678. ADDRESS_CHECK * pAddressCheck = NULL;
  2679. AC_RESULT acResult;
  2680. BOOL fSyncDNS = FALSE;
  2681. LPSTR pszTemp;
  2682. sockaddr_in remoteAddress;
  2683. BOOL fDoCertMap;
  2684. if ( pfAccessAllowed == NULL )
  2685. {
  2686. DBG_ASSERT( FALSE );
  2687. SetErrorStatus( HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ) );
  2688. return CONTEXT_STATUS_CONTINUE;
  2689. }
  2690. *pfAccessAllowed = FALSE;
  2691. pUrlContext = QueryUrlContext();
  2692. DBG_ASSERT( pUrlContext != NULL );
  2693. pMetaData = pUrlContext->QueryMetaData();
  2694. DBG_ASSERT( pMetaData != NULL );
  2695. dwAccessPerms = pMetaData->QueryAccessPerms();
  2696. switch ( _accessState )
  2697. {
  2698. case ACCESS_STATE_SENDING_ERROR:
  2699. //
  2700. // We have sent an error. Just chill
  2701. //
  2702. fAccessAllowed = FALSE;
  2703. break;
  2704. case ACCESS_STATE_START:
  2705. //
  2706. // Is SSL required?
  2707. //
  2708. if ( dwAccessPerms & VROOT_MASK_SSL &&
  2709. !QueryRequest()->IsSecureRequest() )
  2710. {
  2711. QueryResponse()->SetStatus( HttpStatusForbidden,
  2712. Http403SSLRequired );
  2713. goto AccessDenied;
  2714. }
  2715. //
  2716. // Is 128bit required?
  2717. //
  2718. if ( dwAccessPerms & VROOT_MASK_SSL128 )
  2719. {
  2720. pSslInfo = QueryRequest()->QuerySslInfo();
  2721. if ( pSslInfo == NULL ||
  2722. pSslInfo->ConnectionKeySize < 128 )
  2723. {
  2724. QueryResponse()->SetStatus( HttpStatusForbidden,
  2725. Http403SSL128Required );
  2726. goto AccessDenied;
  2727. }
  2728. }
  2729. //
  2730. // Fall through to next phase in access check
  2731. //
  2732. case ACCESS_STATE_CLIENT_CERT:
  2733. _accessState = ACCESS_STATE_CLIENT_CERT;
  2734. //
  2735. // Is a client certificate possible? First check is we even allow
  2736. // a client cert to be negotiated and that this is a secure request
  2737. //
  2738. if ( dwAccessPerms & VROOT_MASK_NEGO_CERT &&
  2739. QueryRequest()->IsSecureRequest() )
  2740. {
  2741. //
  2742. // Try for a client cert if we don't already have one associated
  2743. // with the request
  2744. //
  2745. if ( QueryCertificateContext() == NULL )
  2746. {
  2747. if ( fCompletion )
  2748. {
  2749. fCompletion = FALSE;
  2750. //
  2751. // If we got an error, that's OK. We will fall thru
  2752. // and check whether we actually need a client cert
  2753. //
  2754. if ( dwCompletionStatus == NO_ERROR )
  2755. {
  2756. //
  2757. // Are we cert mapping?
  2758. //
  2759. fDoCertMap = !!(dwAccessPerms & VROOT_MASK_MAP_CERT);
  2760. //
  2761. // All is well. Make a synchronous call to get
  2762. // the certificate
  2763. //
  2764. hr = UlAtqReceiveClientCertificate(
  2765. QueryUlatqContext(),
  2766. FALSE, // sync
  2767. fDoCertMap,
  2768. &pClientCertInfo );
  2769. if ( FAILED( hr ) )
  2770. {
  2771. QueryResponse()->SetStatus( HttpStatusServerError );
  2772. goto AccessDenied;
  2773. }
  2774. DBG_ASSERT( pClientCertInfo != NULL );
  2775. //
  2776. // Setup a client cert context for this request
  2777. //
  2778. hr = QueryMainContext()->SetupCertificateContext( pClientCertInfo );
  2779. if ( FAILED( hr ) )
  2780. {
  2781. QueryResponse()->SetStatus( HttpStatusServerError );
  2782. goto AccessDenied;
  2783. }
  2784. //
  2785. // Just because we got a client cert, doesn't mean
  2786. // it is acceptable. It might be revoked, time
  2787. // expired, etc. The policy of what to do when
  2788. // certs are non-totally-valid is metabase driven.
  2789. // (in case you are wondering why the stream filter
  2790. // doesn't do these checks and just fail the
  2791. // renegotiation)
  2792. //
  2793. if ( !CheckClientCertificateAccess() )
  2794. {
  2795. goto AccessDenied;
  2796. }
  2797. }
  2798. }
  2799. else
  2800. {
  2801. //
  2802. // Are we cert mapping?
  2803. //
  2804. fDoCertMap = !!(dwAccessPerms & VROOT_MASK_MAP_CERT);
  2805. //
  2806. // First time asking for a client cert. Do it
  2807. // async
  2808. //
  2809. fCompletion = FALSE;
  2810. hr = UlAtqReceiveClientCertificate(
  2811. QueryUlatqContext(),
  2812. TRUE, // async
  2813. fDoCertMap,
  2814. &pClientCertInfo );
  2815. if ( FAILED( hr ) )
  2816. {
  2817. QueryResponse()->SetStatus( HttpStatusServerError );
  2818. goto AccessDenied;
  2819. }
  2820. return CONTEXT_STATUS_PENDING;
  2821. }
  2822. }
  2823. }
  2824. //
  2825. // Ok. We're done the certificate crud. If we needed a client
  2826. // cert and didn't get it, then setup 403.7
  2827. //
  2828. if ( dwAccessPerms & VROOT_MASK_NEGO_MANDATORY &&
  2829. QueryCertificateContext() == NULL )
  2830. {
  2831. QueryResponse()->SetStatus( HttpStatusForbidden,
  2832. Http403CertRequired );
  2833. goto AccessDenied;
  2834. }
  2835. //
  2836. // Fall through to next phase in access check
  2837. //
  2838. case ACCESS_STATE_RDNS:
  2839. _accessState = ACCESS_STATE_RDNS;
  2840. pAddressCheck = QueryMainContext()->QueryAddressCheck();
  2841. DBG_ASSERT( pAddressCheck != NULL );
  2842. if ( !fCompletion )
  2843. {
  2844. if ( pMetaData->QueryIpAccessCheckSize() != 0 ||
  2845. pMetaData->QueryDoReverseDNS() )
  2846. {
  2847. //
  2848. // Setup the RDNS crud (joy)
  2849. //
  2850. remoteAddress.sin_family = AF_INET;
  2851. remoteAddress.sin_port = QueryRequest()->QueryRemotePort();
  2852. remoteAddress.sin_addr.s_addr = ntohl( QueryRequest()->QueryRemoteAddress() );
  2853. ZeroMemory( remoteAddress.sin_zero, sizeof( remoteAddress.sin_zero ) );
  2854. pAddressCheck->BindAddr( (sockaddr*) &remoteAddress );
  2855. }
  2856. //
  2857. // Ok. If there is an access check set in metabase, then
  2858. // do the check.
  2859. //
  2860. if ( pMetaData->QueryIpAccessCheckSize() != 0 )
  2861. {
  2862. //
  2863. // Set the metadata IP blob
  2864. //
  2865. pAddressCheck->BindCheckList(
  2866. pMetaData->QueryIpAccessCheckBuffer(),
  2867. pMetaData->QueryIpAccessCheckSize() );
  2868. //
  2869. // Check access
  2870. //
  2871. acResult = pAddressCheck->CheckIpAccess( &_fDNSRequiredForAccess );
  2872. if ( !_fDNSRequiredForAccess )
  2873. {
  2874. pAddressCheck->UnbindCheckList();
  2875. }
  2876. }
  2877. else
  2878. {
  2879. //
  2880. // Fake a valid access check since there was no checks
  2881. // configured in metabase
  2882. //
  2883. acResult = AC_NO_LIST;
  2884. }
  2885. //
  2886. // Check if we now know our access status now
  2887. //
  2888. if ( acResult == AC_IN_DENY_LIST ||
  2889. ( acResult == AC_NOT_IN_GRANT_LIST &&
  2890. !_fDNSRequiredForAccess ) )
  2891. {
  2892. //
  2893. // We know we're rejected
  2894. //
  2895. QueryResponse()->SetStatus( HttpStatusForbidden,
  2896. Http403IPAddressReject );
  2897. goto AccessDenied;
  2898. }
  2899. //
  2900. // Do we need to do a DNS check to determine access?
  2901. // Do we need to do DNS check for logging/servervar purposes?
  2902. //
  2903. // In either case, we will do an async DNS check
  2904. //
  2905. if ( _fDNSRequiredForAccess ||
  2906. pMetaData->QueryDoReverseDNS() )
  2907. {
  2908. fSyncDNS = TRUE;
  2909. if ( !pAddressCheck->QueryDnsName( &fSyncDNS,
  2910. W3_MAIN_CONTEXT::AddressResolutionCallback,
  2911. QueryMainContext(),
  2912. &pszTemp ) )
  2913. {
  2914. //
  2915. // Only error if DNS was required for access check
  2916. // purposes
  2917. //
  2918. if ( _fDNSRequiredForAccess )
  2919. {
  2920. QueryResponse()->SetStatus( HttpStatusForbidden,
  2921. Http403IPAddressReject );
  2922. goto AccessDenied;
  2923. }
  2924. }
  2925. if ( fSyncDNS )
  2926. {
  2927. //
  2928. // Fake a completion if needed. This just prevents us from
  2929. // posting one more time to the thread pool
  2930. //
  2931. fCompletion = TRUE;
  2932. }
  2933. else
  2934. {
  2935. return CONTEXT_STATUS_PENDING;
  2936. }
  2937. }
  2938. }
  2939. if ( fCompletion )
  2940. {
  2941. fCompletion = FALSE;
  2942. //
  2943. // This is the completion for DNS check
  2944. //
  2945. if ( _fDNSRequiredForAccess )
  2946. {
  2947. _fDNSRequiredForAccess = FALSE;
  2948. acResult = pAddressCheck->CheckDnsAccess();
  2949. pAddressCheck->UnbindCheckList();
  2950. if ( acResult == AC_NOT_CHECKED ||
  2951. acResult == AC_IN_DENY_LIST ||
  2952. acResult == AC_NOT_IN_GRANT_LIST )
  2953. {
  2954. QueryResponse()->SetStatus( HttpStatusForbidden,
  2955. Http403IPAddressReject );
  2956. goto AccessDenied;
  2957. }
  2958. }
  2959. }
  2960. //
  2961. // Fall through to next phase in access check
  2962. //
  2963. case ACCESS_STATE_AUTHENTICATION:
  2964. //
  2965. // Is the authentication method valid? We may not have a user
  2966. // context available at this point if this was an early
  2967. // custom error URL invocation
  2968. //
  2969. _accessState = ACCESS_STATE_AUTHENTICATION;
  2970. if ( QueryAuthAccessCheckRequired() &&
  2971. QueryUserContext() != NULL )
  2972. {
  2973. if ( !( pMetaData->QueryAuthentication() &
  2974. QueryUserContext()->QueryAuthType() ) )
  2975. {
  2976. QueryResponse()->SetStatus( HttpStatusUnauthorized,
  2977. Http401Config );
  2978. goto AccessDenied;
  2979. }
  2980. }
  2981. }
  2982. //
  2983. // We know the access status (allowed or denied)
  2984. //
  2985. _accessState = ACCESS_STATE_DONE;
  2986. *pfAccessAllowed = fAccessAllowed;
  2987. return CONTEXT_STATUS_CONTINUE;
  2988. AccessDenied:
  2989. *pfAccessAllowed = FALSE;
  2990. _accessState = ACCESS_STATE_SENDING_ERROR;
  2991. //
  2992. // Send back the bad access response
  2993. //
  2994. hr = SendResponse( W3_FLAG_ASYNC );
  2995. if ( FAILED( hr ) )
  2996. {
  2997. _accessState = ACCESS_STATE_DONE;
  2998. return CONTEXT_STATUS_CONTINUE;
  2999. }
  3000. else
  3001. {
  3002. return CONTEXT_STATUS_PENDING;
  3003. }
  3004. }
  3005. CERTIFICATE_CONTEXT *
  3006. W3_CONTEXT::QueryCertificateContext(
  3007. VOID
  3008. )
  3009. /*++
  3010. Routine Description:
  3011. Get client cert info object. We get it from the main context
  3012. Arguments:
  3013. None
  3014. Return Value:
  3015. Pointer to client cert object
  3016. --*/
  3017. {
  3018. return QueryMainContext()->QueryCertificateContext();
  3019. }
  3020. BOOL
  3021. W3_CONTEXT::CheckClientCertificateAccess(
  3022. VOID
  3023. )
  3024. /*++
  3025. Routine Description:
  3026. Check the context's certificate for validity
  3027. Arguments:
  3028. None
  3029. Return Value:
  3030. TRUE if the cert is valid, else FALSE
  3031. --*/
  3032. {
  3033. W3_MAIN_CONTEXT * pMainContext;
  3034. CERTIFICATE_CONTEXT * pCertificateContext;
  3035. DWORD dwCertFlags;
  3036. pMainContext = QueryMainContext();
  3037. DBG_ASSERT( pMainContext != NULL );
  3038. pCertificateContext = pMainContext->QueryCertificateContext();
  3039. if ( pCertificateContext == NULL )
  3040. {
  3041. return FALSE;
  3042. }
  3043. dwCertFlags = pCertificateContext->QueryFlags();
  3044. if ( dwCertFlags & CERT_TRUST_IS_UNTRUSTED_ROOT ||
  3045. dwCertFlags & CERT_TRUST_IS_NOT_SIGNATURE_VALID ||
  3046. dwCertFlags & CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID ||
  3047. dwCertFlags & CERT_TRUST_IS_NOT_VALID_FOR_USAGE )
  3048. {
  3049. QueryResponse()->SetStatus( HttpStatusForbidden,
  3050. Http403CertInvalid );
  3051. return FALSE;
  3052. }
  3053. else if ( dwCertFlags & CERT_TRUST_IS_NOT_TIME_VALID ||
  3054. dwCertFlags & CERT_TRUST_CTL_IS_NOT_TIME_VALID )
  3055. {
  3056. QueryResponse()->SetStatus( HttpStatusForbidden,
  3057. Http403CertTimeInvalid );
  3058. return FALSE;
  3059. }
  3060. else if ( dwCertFlags & CERT_TRUST_IS_REVOKED )
  3061. {
  3062. QueryResponse()->SetStatus( HttpStatusForbidden,
  3063. Http403CertRevoked );
  3064. return FALSE;
  3065. }
  3066. else if ( dwCertFlags != 0 )
  3067. {
  3068. //
  3069. // this case is to prevent new error flags added by CAPI to be ignored by IIS
  3070. // if there is a flag IIS doesn't explicitly recognize, let's return Http403CertInvalid
  3071. //
  3072. QueryResponse()->SetStatus( HttpStatusForbidden,
  3073. Http403CertInvalid );
  3074. return FALSE;
  3075. }
  3076. return TRUE;
  3077. }
  3078. HRESULT
  3079. W3_CONTEXT::CheckPathInfoExists(
  3080. W3_HANDLER ** ppHandler
  3081. )
  3082. /*++
  3083. Routine Description:
  3084. Utility to check whether path info exists. If it does exist, this
  3085. function succeeds without setting the handler.
  3086. If the file doesn't exist, this function succeeds but sets a general
  3087. handler to send the error
  3088. Arguments:
  3089. ppHandler - Set to handler
  3090. Return Value:
  3091. HRESULT
  3092. --*/
  3093. {
  3094. HRESULT hr = NO_ERROR;
  3095. W3_FILE_INFO * pOpenFile = NULL;
  3096. W3_HANDLER * pHandler = NULL;
  3097. URL_CONTEXT * pUrlContext;
  3098. FILE_CACHE_USER fileUser;
  3099. if ( ppHandler == NULL )
  3100. {
  3101. DBG_ASSERT( FALSE );
  3102. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  3103. }
  3104. *ppHandler = NULL;
  3105. pUrlContext = QueryUrlContext();
  3106. DBG_ASSERT( pUrlContext != NULL );
  3107. QueryFileCacheUser( &fileUser );
  3108. hr = pUrlContext->OpenFile( &fileUser, &pOpenFile );
  3109. if ( FAILED( hr ) )
  3110. {
  3111. DBG_ASSERT( pOpenFile == NULL );
  3112. switch ( WIN32_FROM_HRESULT( hr ) )
  3113. {
  3114. case ERROR_FILE_NOT_FOUND:
  3115. case ERROR_PATH_NOT_FOUND:
  3116. pHandler = new W3_GENERAL_HANDLER( this,
  3117. HttpStatusNotFound );
  3118. break;
  3119. case ERROR_INVALID_PARAMETER:
  3120. pHandler = new W3_GENERAL_HANDLER( this,
  3121. HttpStatusUrlTooLong );
  3122. break;
  3123. case ERROR_ACCESS_DENIED:
  3124. case ERROR_ACCOUNT_DISABLED:
  3125. case ERROR_LOGON_FAILURE:
  3126. pHandler = new W3_GENERAL_HANDLER( this,
  3127. HttpStatusUnauthorized,
  3128. Http401Application );
  3129. break;
  3130. default:
  3131. pHandler = new W3_GENERAL_HANDLER( this,
  3132. HttpStatusServerError );
  3133. break;
  3134. }
  3135. if ( pHandler == NULL )
  3136. {
  3137. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3138. }
  3139. else
  3140. {
  3141. hr = NO_ERROR;
  3142. }
  3143. }
  3144. else
  3145. {
  3146. DBG_ASSERT( pOpenFile != NULL );
  3147. pOpenFile->DereferenceCacheEntry();
  3148. }
  3149. *ppHandler = pHandler;
  3150. return hr;
  3151. }
  3152. HRESULT
  3153. W3_CONTEXT::DetermineHandler(
  3154. BOOL fEnableWildcardMapping
  3155. )
  3156. /*++
  3157. Routine Description:
  3158. Determine the handler for a given request, and setup it.
  3159. How to determine handler is driven by the Magic Flow Chart (TM)
  3160. Arguments:
  3161. fEnableWildcardMapping - Enable wildcard mapping
  3162. Return Value:
  3163. HRESULT
  3164. --*/
  3165. {
  3166. W3_REQUEST * pRequest;
  3167. URL_CONTEXT * pUrlContext;
  3168. W3_METADATA * pMetaData;
  3169. HRESULT hr = NO_ERROR;
  3170. W3_HANDLER * pHandler = NULL;
  3171. META_SCRIPT_MAP_ENTRY * pScriptMapEntry = NULL;
  3172. STACK_STRA( strHeaderValue, 10 );
  3173. STACK_STRA( strVerb, 10 );
  3174. HTTP_VERB VerbType;
  3175. BOOL fKnownVerb = FALSE;
  3176. DWORD dwFilePerms;
  3177. GATEWAY_TYPE GatewayType;
  3178. BOOL fAccessAllowed = FALSE;
  3179. BOOL fSuspectUrl = FALSE;
  3180. CHAR * szHeaderValue = NULL;
  3181. //
  3182. // We shouldn't have a handler set for this request
  3183. //
  3184. DBG_ASSERT( _pHandler == NULL );
  3185. pRequest = QueryRequest();
  3186. DBG_ASSERT( pRequest != NULL );
  3187. pUrlContext = QueryUrlContext();
  3188. DBG_ASSERT( pUrlContext != NULL );
  3189. pMetaData = pUrlContext->QueryMetaData();
  3190. DBG_ASSERT( pMetaData != NULL );
  3191. //
  3192. // First check for a * script map. If one exists, use it if allowed
  3193. //
  3194. VerbType = pRequest->QueryVerbType();
  3195. if ( VerbType == HttpVerbTRACE ||
  3196. VerbType == HttpVerbTRACK )
  3197. {
  3198. pHandler = new W3_TRACE_HANDLER( this );
  3199. if ( pHandler == NULL )
  3200. {
  3201. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3202. }
  3203. goto Finished;
  3204. }
  3205. pScriptMapEntry = pMetaData->QueryScriptMap()->QueryStarScriptMap();
  3206. if ( pScriptMapEntry != NULL &&
  3207. fEnableWildcardMapping )
  3208. {
  3209. //
  3210. // If there is a ./ in the URL, then always check for existence. This
  3211. // prevents a trailing . metabase equivilency problem
  3212. //
  3213. fSuspectUrl = pRequest->IsSuspectUrl();
  3214. if ( pScriptMapEntry->QueryCheckPathInfoExists() ||
  3215. fSuspectUrl )
  3216. {
  3217. hr = CheckPathInfoExists( &pHandler );
  3218. if ( FAILED( hr ) )
  3219. {
  3220. goto Finished;
  3221. }
  3222. if ( pHandler != NULL )
  3223. {
  3224. //
  3225. // We already have an error handler for this request.
  3226. // That means path info didn't really exist
  3227. //
  3228. goto Finished;
  3229. }
  3230. }
  3231. //
  3232. // Create the appropriate handler for the script mapping
  3233. //
  3234. if ( pScriptMapEntry->QueryGateway() == GATEWAY_CGI )
  3235. {
  3236. pHandler = new W3_CGI_HANDLER( this, pScriptMapEntry );
  3237. }
  3238. else
  3239. {
  3240. DBG_ASSERT( pScriptMapEntry->QueryGateway() == GATEWAY_ISAPI );
  3241. pHandler = new W3_ISAPI_HANDLER( this, pScriptMapEntry );
  3242. }
  3243. if ( pHandler == NULL )
  3244. {
  3245. hr = HRESULT_FROM_WIN32( GetLastError() );
  3246. }
  3247. goto Finished;
  3248. }
  3249. //
  3250. // Check for Translate: f
  3251. //
  3252. // If there, it goes to DAVFS unless DAV is disabled
  3253. //
  3254. if (!pMetaData->QueryIsDavDisabled())
  3255. {
  3256. szHeaderValue = pRequest->GetHeader( HttpHeaderTranslate );
  3257. if ( szHeaderValue &&
  3258. toupper( szHeaderValue[ 0 ] ) == 'F' &&
  3259. szHeaderValue[ 1 ] == '\0' &&
  3260. !pMetaData->QueryIgnoreTranslate() )
  3261. {
  3262. //
  3263. // This is a DAV request
  3264. //
  3265. pHandler = new W3_DAV_HANDLER( this );
  3266. if ( pHandler == NULL )
  3267. {
  3268. hr = HRESULT_FROM_WIN32( GetLastError() );
  3269. }
  3270. goto Finished;
  3271. }
  3272. }
  3273. //
  3274. // Does this request map to an extension
  3275. //
  3276. // Map can mean one of two things
  3277. // a) A script map (as in MD_SCRIPT_MAP)
  3278. // b) An explicit .DLL/.EXE/.COM/etc.
  3279. //
  3280. pScriptMapEntry = pUrlContext->QueryUrlInfo()->QueryScriptMapEntry();
  3281. if ( pScriptMapEntry != NULL ||
  3282. pUrlContext->QueryUrlInfo()->QueryGateway() == GATEWAY_ISAPI ||
  3283. pUrlContext->QueryUrlInfo()->QueryGateway() == GATEWAY_CGI )
  3284. {
  3285. dwFilePerms = pMetaData->QueryAccessPerms();
  3286. if ( pScriptMapEntry != NULL )
  3287. {
  3288. GatewayType = pScriptMapEntry->QueryGateway();
  3289. //
  3290. // We have a script map. Check access rights
  3291. //
  3292. if ( pScriptMapEntry->QueryAllowScriptAccess() &&
  3293. IS_ACCESS_ALLOWED( pRequest, dwFilePerms, SCRIPT ) )
  3294. {
  3295. fAccessAllowed = TRUE;
  3296. }
  3297. if ( !fAccessAllowed &&
  3298. IS_ACCESS_ALLOWED( pRequest, dwFilePerms, EXECUTE ) )
  3299. {
  3300. fAccessAllowed = TRUE;
  3301. }
  3302. if ( !fAccessAllowed )
  3303. {
  3304. pHandler = new W3_GENERAL_HANDLER( this,
  3305. HttpStatusForbidden,
  3306. Http403ExecAccessDenied );
  3307. if ( pHandler == NULL )
  3308. {
  3309. hr = HRESULT_FROM_WIN32( GetLastError() );
  3310. }
  3311. goto Finished;
  3312. }
  3313. //
  3314. // If there is a ./ in the URL, then always check for existence. This
  3315. // prevents a trailing . metabase equivilency problem
  3316. //
  3317. fSuspectUrl = pRequest->IsSuspectUrl();
  3318. //
  3319. // Should we verify that path info does exist?
  3320. //
  3321. if ( pScriptMapEntry->QueryCheckPathInfoExists() ||
  3322. fSuspectUrl )
  3323. {
  3324. hr = CheckPathInfoExists( &pHandler );
  3325. if ( FAILED( hr ) )
  3326. {
  3327. goto Finished;
  3328. }
  3329. if ( pHandler != NULL )
  3330. {
  3331. //
  3332. // We already have an error handler for this request.
  3333. // That means path info didn't really exist
  3334. //
  3335. goto Finished;
  3336. }
  3337. }
  3338. //
  3339. // Does the script map support the verb?
  3340. //
  3341. hr = pRequest->GetVerbString( &strVerb );
  3342. if ( FAILED( hr ) )
  3343. {
  3344. goto Finished;
  3345. }
  3346. if ( !pScriptMapEntry->IsVerbAllowed( strVerb ) )
  3347. {
  3348. pHandler = new W3_GENERAL_HANDLER( this,
  3349. HttpStatusForbidden,
  3350. Http403ExecAccessDenied );
  3351. if ( pHandler == NULL )
  3352. {
  3353. hr = HRESULT_FROM_WIN32( GetLastError() );
  3354. }
  3355. goto Finished;
  3356. }
  3357. }
  3358. else
  3359. {
  3360. GatewayType = pUrlContext->QueryUrlInfo()->QueryGateway();
  3361. }
  3362. //
  3363. // OK. If we got to here, we can setup an executable handler
  3364. //
  3365. if ( GatewayType == GATEWAY_CGI )
  3366. {
  3367. pHandler = new W3_CGI_HANDLER( this, pScriptMapEntry );
  3368. }
  3369. else
  3370. {
  3371. DBG_ASSERT( GatewayType == GATEWAY_ISAPI );
  3372. pHandler = new W3_ISAPI_HANDLER( this, pScriptMapEntry );
  3373. }
  3374. if ( pHandler == NULL )
  3375. {
  3376. hr = HRESULT_FROM_WIN32( GetLastError() );
  3377. }
  3378. goto Finished;
  3379. }
  3380. //
  3381. // Ok. No script map applied. Is this an unknown verb?
  3382. // (i.e. not GET, HEAD, POST, TRACE, TRACK)
  3383. //
  3384. if ( VerbType == HttpVerbGET ||
  3385. VerbType == HttpVerbPOST ||
  3386. VerbType == HttpVerbHEAD )
  3387. {
  3388. fKnownVerb = TRUE;
  3389. }
  3390. else
  3391. {
  3392. fKnownVerb = FALSE;
  3393. }
  3394. //
  3395. // If this verb is unknown, then it goes to DAV
  3396. //
  3397. if ( fKnownVerb == FALSE )
  3398. {
  3399. if (!pMetaData->QueryIsDavDisabled())
  3400. {
  3401. pHandler = new W3_DAV_HANDLER( this );
  3402. }
  3403. else if ( VerbType == HttpVerbOPTIONS )
  3404. {
  3405. //
  3406. // We handle OPTIONS if DAV is disabled
  3407. //
  3408. pHandler = new W3_OPTIONS_HANDLER( this );
  3409. }
  3410. else
  3411. {
  3412. pHandler = new W3_GENERAL_HANDLER( this,
  3413. HttpStatusNotImplemented );
  3414. }
  3415. if ( pHandler == NULL )
  3416. {
  3417. hr = HRESULT_FROM_WIN32( GetLastError() );
  3418. }
  3419. goto Finished;
  3420. }
  3421. //
  3422. // If there are special DAV verbs like If: and Lock-Token:, then it also
  3423. // go to DAV unless it is disabled
  3424. //
  3425. if (!pMetaData->QueryIsDavDisabled())
  3426. {
  3427. if ( SUCCEEDED( pRequest->GetHeader( "If",
  3428. 2,
  3429. &strHeaderValue,
  3430. TRUE ) ) ||
  3431. SUCCEEDED( pRequest->GetHeader( "Lock-Token",
  3432. 10,
  3433. &strHeaderValue,
  3434. TRUE ) ) )
  3435. {
  3436. pHandler = new W3_DAV_HANDLER( this );
  3437. if ( pHandler == NULL )
  3438. {
  3439. hr = HRESULT_FROM_WIN32( GetLastError() );
  3440. }
  3441. goto Finished;
  3442. }
  3443. }
  3444. //
  3445. // OK. Exchange/DAV-providers have had their chance. Now its our turn!
  3446. //
  3447. //
  3448. // Call the static file handler
  3449. //
  3450. pHandler = new W3_STATIC_FILE_HANDLER( this );
  3451. if ( pHandler == NULL )
  3452. {
  3453. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3454. goto Finished;
  3455. }
  3456. Finished:
  3457. if ( FAILED( hr ) )
  3458. {
  3459. DBG_ASSERT( pHandler == NULL );
  3460. }
  3461. _pHandler = pHandler;
  3462. return hr;
  3463. }
  3464. HRESULT
  3465. W3_CONTEXT::SetupHttpRedirect(
  3466. STRA & strPath,
  3467. BOOL fIncludeParameters,
  3468. HTTP_STATUS & httpStatus
  3469. )
  3470. /*++
  3471. Routine Description:
  3472. Do an HTTP redirect (301 or 302)
  3473. Arguments:
  3474. strPath - New path component of destination
  3475. fIncludeParameters - Include query string in Location: header
  3476. httpStatus - Status for redirect (i.e. HttpStatusRedirect)
  3477. Return Value:
  3478. HRESULT
  3479. --*/
  3480. {
  3481. HRESULT hr;
  3482. STACK_STRA( strRedirect, MAX_PATH);
  3483. STRA *pstrRedirect;
  3484. W3_RESPONSE *pResponse = QueryResponse();
  3485. W3_REQUEST *pRequest = QueryRequest();
  3486. //
  3487. // If it an absolute path add the protocol, host name and QueryString
  3488. // if specified, otherwise just assume it is a fully qualified URL.
  3489. //
  3490. if (strPath.QueryStr()[0] == '/')
  3491. {
  3492. // build the redirect URL (with http://) into strRedirect
  3493. hr = pRequest->BuildFullUrl( strPath,
  3494. &strRedirect,
  3495. fIncludeParameters );
  3496. if ( FAILED( hr ) )
  3497. {
  3498. return hr;
  3499. }
  3500. pstrRedirect = &strRedirect;
  3501. }
  3502. else
  3503. {
  3504. pstrRedirect = &strPath;
  3505. }
  3506. //
  3507. // Setup the response, starting with status and location:
  3508. //
  3509. pResponse->SetStatus( httpStatus );
  3510. //
  3511. // Be careful to reset the response on subsequent failures so we don't
  3512. // end up with an incomplete response!
  3513. //
  3514. hr = pResponse->SetHeader( HttpHeaderLocation,
  3515. pstrRedirect->QueryStr(),
  3516. pstrRedirect->QueryCCH() );
  3517. if ( FAILED( hr ) )
  3518. {
  3519. pResponse->Clear();
  3520. return hr;
  3521. }
  3522. pResponse->SetHeaderByReference( HttpHeaderContentType,
  3523. "text/html", 9 );
  3524. //
  3525. // Now add any metabase configured "redirect" headers (lame)
  3526. //
  3527. STRA *pstrRedirectHeaders = QueryUrlContext()->QueryMetaData()->QueryRedirectHeaders();
  3528. if ( pstrRedirectHeaders != NULL )
  3529. {
  3530. hr = pResponse->AppendResponseHeaders( *pstrRedirectHeaders );
  3531. if ( FAILED( hr ) )
  3532. {
  3533. return hr;
  3534. }
  3535. }
  3536. //
  3537. // Add the canned redirect body. This means taking the format string,
  3538. // and inserting the URL into it
  3539. //
  3540. CHAR *pvRedirect;
  3541. DWORD cbRedirect = sm_cbRedirectMessage + pstrRedirect->QueryCCH();
  3542. //
  3543. // Keep a buffer around
  3544. //
  3545. hr = QueryHeaderBuffer()->AllocateSpace( cbRedirect + 1,
  3546. &pvRedirect );
  3547. if ( FAILED( hr ) )
  3548. {
  3549. return hr;
  3550. }
  3551. cbRedirect = _snprintf( pvRedirect,
  3552. cbRedirect,
  3553. sm_achRedirectMessage,
  3554. pstrRedirect->QueryStr() );
  3555. //
  3556. // Setup the response
  3557. //
  3558. hr = pResponse->AddMemoryChunkByReference( pvRedirect,
  3559. cbRedirect );
  3560. if ( FAILED( hr ) )
  3561. {
  3562. pResponse->Clear();
  3563. return hr;
  3564. }
  3565. return S_OK;
  3566. }
  3567. HRESULT
  3568. W3_CONTEXT::SetupHttpRedirect(
  3569. STRU & strPath,
  3570. BOOL fIncludeParameters,
  3571. HTTP_STATUS & httpStatus
  3572. )
  3573. /*++
  3574. Routine Description:
  3575. Do an HTTP redirect (301 or 302)
  3576. Arguments:
  3577. strPath - New path component of destination
  3578. fIncludeParameters - Include query string in Location: header
  3579. httpStatus - Status for redirect (i.e. HttpStatusRedirect)
  3580. Return Value:
  3581. HRESULT
  3582. --*/
  3583. {
  3584. //
  3585. // Convert the unicode path to ansi
  3586. //
  3587. HRESULT hr;
  3588. STACK_STRA (straPath, MAX_PATH);
  3589. if (FAILED(hr = straPath.CopyWToUTF8Unescaped(strPath)))
  3590. {
  3591. return hr;
  3592. }
  3593. return SetupHttpRedirect(straPath,
  3594. fIncludeParameters,
  3595. httpStatus);
  3596. }
  3597. BOOL W3_CONTEXT::QueryDoUlLogging()
  3598. {
  3599. if (QuerySite() == NULL ||
  3600. !QuerySite()->QueryDoUlLogging())
  3601. {
  3602. return FALSE;
  3603. }
  3604. if (QueryUrlContext() == NULL ||
  3605. QueryUrlContext()->QueryMetaData() == NULL)
  3606. {
  3607. return TRUE;
  3608. }
  3609. return !QueryUrlContext()->QueryMetaData()->QueryDontLog();
  3610. }
  3611. BOOL W3_CONTEXT::QueryDoCustomLogging()
  3612. {
  3613. if (QuerySite() == NULL ||
  3614. !QuerySite()->QueryDoCustomLogging())
  3615. {
  3616. return FALSE;
  3617. }
  3618. if (QueryUrlContext() == NULL ||
  3619. QueryUrlContext()->QueryMetaData() == NULL)
  3620. {
  3621. return TRUE;
  3622. }
  3623. return !QueryUrlContext()->QueryMetaData()->QueryDontLog();
  3624. }
  3625. VOID
  3626. W3_CONTEXT::SetSSICommandHandler(
  3627. W3_HANDLER * pHandler
  3628. )
  3629. /*++
  3630. Routine Description:
  3631. Explicitly sets the CGI_HANDLER for this request. This is the handler
  3632. which executes explicit command lines on behalf of SSI
  3633. The function is named SetSSICommandHandler (instead of just SetHandler())
  3634. to discourage others from using this function.
  3635. Arguments:
  3636. pHandler - Handler to set. Must be the CGI handler
  3637. Return Value:
  3638. None
  3639. --*/
  3640. {
  3641. DBG_ASSERT( pHandler != NULL );
  3642. DBG_ASSERT( _pHandler == NULL );
  3643. DBG_ASSERT( wcscmp( pHandler->QueryName(), L"CGIHandler" ) == 0 );
  3644. _pHandler = pHandler;
  3645. }