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.

1346 lines
34 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. staticfile.cxx
  5. Abstract:
  6. Handle static file request
  7. Author:
  8. Bilal Alam (balam) 7-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "staticfile.hxx"
  16. ALLOC_CACHE_HANDLER * W3_STATIC_FILE_HANDLER::sm_pachStaticFileHandlers;
  17. HRESULT
  18. W3_STATIC_FILE_HANDLER::HandleDefaultLoad(
  19. W3_CONTEXT * pW3Context,
  20. BOOL * pfHandled,
  21. BOOL * pfAsyncPending
  22. )
  23. /*++
  24. Routine Description:
  25. Attempts to find a default load file applicable for this request. If it
  26. does, it will switch the URL of the request and back track.
  27. Arguments:
  28. pW3Context - Context
  29. pfHandled - Set to TRUE if this function has set a response or switched URL
  30. (in other words, no more processing is required)
  31. pfAsyncPending - Set to TRUE if async is pending so bail
  32. Return Value:
  33. HRESULT - If not NO_ERROR, then *pfHandled is irrelevent
  34. --*/
  35. {
  36. URL_CONTEXT * pUrlContext;
  37. W3_METADATA * pMetaData;
  38. STACK_STRU( strDefaultFiles, MAX_PATH );
  39. HRESULT hr = NO_ERROR;
  40. W3_REQUEST * pRequest = pW3Context->QueryRequest();
  41. STRU * pstrPhysical;
  42. STACK_STRU( strNextFile, MAX_PATH );
  43. WCHAR * pszNextFile;
  44. WCHAR * pszEndFile;
  45. W3_FILE_INFO * pOpenFile = NULL;
  46. BOOL fFound = FALSE;
  47. STACK_STRU( strNewUrl, MAX_PATH );
  48. WCHAR * pszQuery;
  49. CONTEXT_STATUS status;
  50. FILE_CACHE_USER FileUser;
  51. DBG_ASSERT( pW3Context != NULL );
  52. DBG_ASSERT( pRequest != NULL );
  53. DBG_ASSERT( pfHandled != NULL );
  54. DBG_ASSERT( pfAsyncPending != NULL );
  55. *pfHandled = FALSE;
  56. *pfAsyncPending = FALSE;
  57. //
  58. // Get the configuration info
  59. //
  60. pUrlContext = pW3Context->QueryUrlContext();
  61. DBG_ASSERT( pUrlContext != NULL );
  62. pMetaData = pUrlContext->QueryMetaData();
  63. DBG_ASSERT( pMetaData != NULL );
  64. pstrPhysical = pUrlContext->QueryPhysicalPath();
  65. DBG_ASSERT( pstrPhysical != NULL );
  66. //
  67. // First ensure the path is / suffixed. Otherwise, redirect to such
  68. //
  69. if (pstrPhysical->QueryStr()[pstrPhysical->QueryCCH() - 1] != L'\\')
  70. {
  71. //
  72. // Before redirecting, first make sure it is a GET or a HEAD
  73. //
  74. HTTP_VERB VerbType = pRequest->QueryVerbType();
  75. if ( VerbType != HttpVerbGET &&
  76. VerbType != HttpVerbHEAD )
  77. {
  78. pW3Context->QueryResponse()->SetStatus( HttpStatusMethodNotAllowed );
  79. hr = pW3Context->SetupAllowHeader();
  80. if ( FAILED( hr ) )
  81. {
  82. return hr;
  83. }
  84. return S_OK;
  85. }
  86. STACK_STRU (strRedirect, MAX_PATH );
  87. //
  88. // Append the suffix '/'
  89. //
  90. if (FAILED(hr = pRequest->GetUrl(&strRedirect)) ||
  91. FAILED(hr = strRedirect.Append(L"/")))
  92. {
  93. return hr;
  94. }
  95. //
  96. // Do the HTTP redirect
  97. //
  98. if (FAILED(hr = pW3Context->SetupHttpRedirect(strRedirect,
  99. TRUE,
  100. HttpStatusMovedPermanently)))
  101. {
  102. return hr;
  103. }
  104. //
  105. // Tell callers we are finished
  106. //
  107. *pfHandled = TRUE;
  108. return S_OK;
  109. }
  110. //
  111. // Look for default load files
  112. //
  113. hr = strDefaultFiles.Copy( *pMetaData->QueryDefaultLoadFiles() );
  114. if ( FAILED( hr ) )
  115. {
  116. return hr;
  117. }
  118. pszNextFile = strDefaultFiles.QueryStr();
  119. while ( pszNextFile != NULL &&
  120. *pszNextFile != L'\0' )
  121. {
  122. pszEndFile = wcschr( pszNextFile, L',' );
  123. if ( pszEndFile != NULL )
  124. {
  125. *pszEndFile = L'\0';
  126. }
  127. //
  128. // Append portion to directory to create a filename to check for
  129. //
  130. hr = strNextFile.Copy( *pstrPhysical );
  131. if ( FAILED( hr ) )
  132. {
  133. return hr;
  134. }
  135. //
  136. // Remove any query string
  137. //
  138. pszQuery = wcschr( pszNextFile, L'?' );
  139. if ( pszQuery != NULL )
  140. {
  141. hr = strNextFile.Append( pszNextFile,
  142. DIFF( pszQuery - pszNextFile ) );
  143. }
  144. else
  145. {
  146. hr = strNextFile.Append( pszNextFile );
  147. }
  148. if ( FAILED( hr ) )
  149. {
  150. return hr;
  151. }
  152. //
  153. // Make a FS path
  154. //
  155. FlipSlashes( strNextFile.QueryStr() );
  156. //
  157. // Open the file
  158. //
  159. pW3Context->QueryFileCacheUser( &FileUser );
  160. DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL );
  161. hr = g_pW3Server->QueryFileCache()->GetFileInfo(
  162. strNextFile,
  163. pMetaData->QueryDirmonConfig(),
  164. &FileUser,
  165. !( pMetaData->QueryNoCache() ),
  166. &pOpenFile );
  167. if ( FAILED( hr ) )
  168. {
  169. DWORD dwError = WIN32_FROM_HRESULT( hr );
  170. DBG_ASSERT( pOpenFile == NULL );
  171. //
  172. // If not found, or name invalid, that's ok -> proceed to next file
  173. //
  174. if ( dwError != ERROR_FILE_NOT_FOUND &&
  175. dwError != ERROR_PATH_NOT_FOUND &&
  176. dwError != ERROR_INVALID_NAME )
  177. {
  178. return hr;
  179. }
  180. hr = NO_ERROR;
  181. }
  182. else
  183. {
  184. DWORD dwAttributes;
  185. //
  186. // Great, we can open the file. We only need it for attributes.
  187. //
  188. DBG_ASSERT( pOpenFile != NULL );
  189. dwAttributes = pOpenFile->QueryAttributes();
  190. pOpenFile->DereferenceCacheEntry();
  191. if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY )
  192. {
  193. //
  194. // For legacy, if we see a directory, our default load file
  195. // search is over, and we act like we never found one
  196. //
  197. return NO_ERROR;
  198. }
  199. fFound = TRUE;
  200. break;
  201. }
  202. //
  203. // Goto next file
  204. //
  205. pszNextFile = pszEndFile ? pszEndFile + 1 : NULL;
  206. }
  207. //
  208. // Change the url and retrack
  209. //
  210. if ( fFound )
  211. {
  212. //
  213. // Ok. We can change the URL and retrack. Do so.
  214. //
  215. hr = pW3Context->QueryRequest()->GetUrl( &strNewUrl );
  216. if ( FAILED( hr ) )
  217. {
  218. return hr;
  219. }
  220. hr = strNewUrl.Append( pszNextFile );
  221. if ( FAILED( hr ) )
  222. {
  223. return hr;
  224. }
  225. //
  226. // Change the URL
  227. //
  228. hr = pW3Context->QueryRequest()->SetUrl( strNewUrl, FALSE );
  229. if ( FAILED( hr ) )
  230. {
  231. return hr;
  232. }
  233. hr = pW3Context->ExecuteChildRequest( pW3Context->QueryRequest(),
  234. FALSE,
  235. W3_FLAG_ASYNC );
  236. if ( FAILED( hr ) )
  237. {
  238. return hr;
  239. }
  240. else
  241. {
  242. *pfHandled = TRUE;
  243. *pfAsyncPending = TRUE;
  244. return NO_ERROR;
  245. }
  246. }
  247. else
  248. {
  249. //
  250. // If not found, the caller will continue since *pfHandled == FALSE
  251. // if we're here
  252. //
  253. }
  254. return NO_ERROR;
  255. }
  256. HRESULT
  257. W3_STATIC_FILE_HANDLER::DirectoryDoWork(
  258. W3_CONTEXT * pW3Context,
  259. BOOL * pfAsyncPending
  260. )
  261. /*++
  262. Routine Description:
  263. Handle directories. This means default loads and directory listings
  264. Arguments:
  265. pW3Context - Context
  266. pfAsyncPending - Set to TRUE if async pending
  267. Return Value:
  268. HRESULT
  269. --*/
  270. {
  271. DWORD dwDirBrowseFlags;
  272. URL_CONTEXT * pUrlContext;
  273. HRESULT hr;
  274. BOOL fHandled = FALSE;
  275. FILE_CACHE_USER fileUser;
  276. BOOL fImpersonated = FALSE;
  277. DBG_ASSERT( pW3Context != NULL );
  278. DBG_ASSERT( pfAsyncPending != NULL );
  279. *pfAsyncPending = FALSE;
  280. W3_REQUEST *pRequest = pW3Context->QueryRequest();
  281. DBG_ASSERT(pRequest != NULL);
  282. W3_RESPONSE *pResponse = pW3Context->QueryResponse();
  283. DBG_ASSERT(pResponse != NULL);
  284. pUrlContext = pW3Context->QueryUrlContext();
  285. DBG_ASSERT( pUrlContext != NULL );
  286. STRU *pstrPhysical = pUrlContext->QueryPhysicalPath();
  287. DBG_ASSERT( pstrPhysical != NULL );
  288. //
  289. // Get the directory browsing flags for this directory
  290. //
  291. dwDirBrowseFlags = pUrlContext->QueryMetaData()->QueryDirBrowseFlags();
  292. //
  293. // First check for a default load (by first checking whether we are
  294. // allowed to serve default load)
  295. //
  296. if ( dwDirBrowseFlags & MD_DIRBROW_LOADDEFAULT )
  297. {
  298. //
  299. // OK. Look for a default load
  300. //
  301. hr = HandleDefaultLoad( pW3Context,
  302. &fHandled,
  303. pfAsyncPending );
  304. if ( FAILED( hr ) || fHandled || *pfAsyncPending )
  305. {
  306. return hr;
  307. }
  308. }
  309. //
  310. // If doing directory listing, first make sure it is a GET or a HEAD
  311. //
  312. HTTP_VERB VerbType = pRequest->QueryVerbType();
  313. if ( VerbType != HttpVerbGET &&
  314. VerbType != HttpVerbHEAD )
  315. {
  316. pW3Context->QueryResponse()->SetStatus( HttpStatusMethodNotAllowed );
  317. hr = pW3Context->SetupAllowHeader();
  318. if ( FAILED( hr ) )
  319. {
  320. return hr;
  321. }
  322. return S_OK;
  323. }
  324. //
  325. // OK. Check for whether directory listings are enabled
  326. //
  327. if ( dwDirBrowseFlags & MD_DIRBROW_ENABLED )
  328. {
  329. //
  330. // We may need to impersonate some other user to open the file
  331. //
  332. pW3Context->QueryFileCacheUser( &fileUser );
  333. if ( fileUser._hToken != NULL )
  334. {
  335. if ( !SetThreadToken( NULL, fileUser._hToken ) )
  336. {
  337. return HRESULT_FROM_WIN32( GetLastError() );
  338. }
  339. fImpersonated = TRUE;
  340. }
  341. hr = HandleDirectoryListing( pW3Context,
  342. &fHandled );
  343. if( fImpersonated )
  344. {
  345. RevertToSelf();
  346. fImpersonated = FALSE;
  347. }
  348. if ( FAILED( hr ) || fHandled )
  349. {
  350. return hr;
  351. }
  352. }
  353. //
  354. // If we are here, then neither browsing nor default loads are enabled.
  355. // There is nothing we can do but return a 403.
  356. //
  357. pW3Context->QueryResponse()->SetStatus( HttpStatusForbidden,
  358. Http403DirBrowsingDenied );
  359. return NO_ERROR;
  360. }
  361. HRESULT
  362. GetTypeAndSubType(
  363. CHAR * pszType,
  364. STRA * pstrMainType,
  365. STRA * pstrSubType,
  366. BOOL * pfTypeOk
  367. )
  368. /*++
  369. Routine Description:
  370. Given a mimetype of "foobar/barfoo", return "foobar" as the main type
  371. and "barfoo" as the subtype.
  372. Arguments:
  373. pszType - Whole mime type
  374. pstrMainType - Filled with main type
  375. pstrSubType - Filled with sub type
  376. pfTypeOk - Is this mime type ok?
  377. Return Value:
  378. HRESULT
  379. --*/
  380. {
  381. HRESULT hr;
  382. CHAR * pszSlash = strchr( pszType, '/' );
  383. if (pszSlash == NULL)
  384. {
  385. *pfTypeOk = FALSE;
  386. return S_OK;
  387. }
  388. hr = pstrMainType->Copy( pszType,
  389. DIFF( pszSlash - pszType ) );
  390. hr = pstrSubType->Copy( pszSlash + 1 );
  391. *pfTypeOk = TRUE;
  392. return hr;
  393. }
  394. HRESULT
  395. IsAcceptable(
  396. CHAR * pszContentType,
  397. CHAR * pszAcceptHeader,
  398. BOOL * pfIsAcceptAble
  399. )
  400. /*++
  401. Routine Description:
  402. Return whether given content type is acceptable for the given
  403. Accept: header
  404. Arguments:
  405. pszContentType - Content type
  406. pszAcceptHeader - Accept header to check
  407. pfIsAcceptAble - Filled with bool indicating whether type is acceptable
  408. Return Value:
  409. HRESULT
  410. --*/
  411. {
  412. HRESULT hr;
  413. BOOL fTypeOk;
  414. //
  415. // Quickly handle the */* case
  416. //
  417. if ( pszAcceptHeader[ 0 ] == '*' &&
  418. pszAcceptHeader[ 1 ] == '/' &&
  419. pszAcceptHeader[ 2 ] == '*' &&
  420. pszAcceptHeader[ 3 ] == '\0' )
  421. {
  422. *pfIsAcceptAble = TRUE;
  423. return S_OK;
  424. }
  425. //
  426. // Break the Content-Type into the main- and sub-content-type
  427. //
  428. STACK_STRA ( strMainContentType, 32);
  429. STACK_STRA ( strSubContentType, 32);
  430. if ( FAILED( hr = GetTypeAndSubType( pszContentType,
  431. &strMainContentType,
  432. &strSubContentType,
  433. &fTypeOk ) ) )
  434. {
  435. return hr;
  436. }
  437. if ( !fTypeOk )
  438. {
  439. *pfIsAcceptAble = FALSE;
  440. return S_OK;
  441. }
  442. //
  443. // Skip over any spaces
  444. //
  445. while ( *pszAcceptHeader == ' ' )
  446. {
  447. pszAcceptHeader++;
  448. }
  449. STACK_STRA (strAcceptType, 64);
  450. STACK_STRA (strMainAcceptType, 32);
  451. STACK_STRA (strSubAcceptType, 32);
  452. while (TRUE)
  453. {
  454. //
  455. // Multiple Acceptable Types are ',' separated, get the next one
  456. //
  457. CHAR * pszComma = strchr( pszAcceptHeader, L',' );
  458. if ( pszComma == NULL )
  459. {
  460. if ( FAILED( hr = strAcceptType.Copy( pszAcceptHeader ) ) )
  461. {
  462. return hr;
  463. }
  464. }
  465. else
  466. {
  467. if ( FAILED( hr = strAcceptType.Copy( pszAcceptHeader,
  468. DIFF( pszComma - pszAcceptHeader ) ) ) )
  469. {
  470. return hr;
  471. }
  472. }
  473. //
  474. // Trim out any quality specifier specified after a ';'
  475. //
  476. CHAR * pszQuality = strchr( strAcceptType.QueryStr(), ';' );
  477. if ( pszQuality != NULL )
  478. {
  479. strAcceptType.SetLen(DIFF(pszQuality - strAcceptType.QueryStr()));
  480. }
  481. //
  482. // Trim any spaces at the end
  483. //
  484. INT iSpace = strAcceptType.QueryCCH() - 1;
  485. while ( iSpace >= 0 &&
  486. strAcceptType.QueryStr()[iSpace] == ' ' )
  487. {
  488. iSpace--;
  489. }
  490. strAcceptType.SetLen( iSpace + 1 );
  491. //
  492. // Just check if this Type is */*
  493. //
  494. if ( !strcmp( strAcceptType.QueryStr(), "*/*" ) )
  495. {
  496. *pfIsAcceptAble = TRUE;
  497. return S_OK;
  498. }
  499. //
  500. // Get the main- and sub-Accept types for this type
  501. //
  502. if ( FAILED(hr = GetTypeAndSubType( strAcceptType.QueryStr(),
  503. &strMainAcceptType,
  504. &strSubAcceptType,
  505. &fTypeOk ) ) )
  506. {
  507. return hr;
  508. }
  509. if ( !fTypeOk )
  510. {
  511. *pfIsAcceptAble = TRUE;
  512. return S_OK;
  513. }
  514. //
  515. // Now actually find out if this type is acceptable
  516. //
  517. if ( !_stricmp( strMainAcceptType.QueryStr(),
  518. strMainContentType.QueryStr() ) )
  519. {
  520. if ( !strcmp( strSubAcceptType.QueryStr(), "*" ) ||
  521. !_stricmp( strSubAcceptType.QueryStr(),
  522. strSubContentType.QueryStr() ) )
  523. {
  524. *pfIsAcceptAble = TRUE;
  525. return S_OK;
  526. }
  527. }
  528. //
  529. // Set AcceptHeader to the start of the next type
  530. //
  531. if (pszComma == NULL)
  532. {
  533. *pfIsAcceptAble = FALSE;
  534. return S_OK;
  535. }
  536. pszAcceptHeader = pszComma + 1;
  537. while ( *pszAcceptHeader == ' ' )
  538. {
  539. pszAcceptHeader++;
  540. }
  541. }
  542. }
  543. HRESULT
  544. W3_STATIC_FILE_HANDLER::FileDoWork(
  545. W3_CONTEXT * pW3Context,
  546. W3_FILE_INFO * pOpenFile
  547. )
  548. /*++
  549. Routine Description:
  550. Handle files (non-directories).
  551. Arguments:
  552. pW3Context - Context
  553. pOpenFile - W3_FILE_INFO with the file to send
  554. Return Value:
  555. HRESULT
  556. --*/
  557. {
  558. LARGE_INTEGER liFileSize;
  559. W3_RESPONSE * pResponse;
  560. W3_REQUEST * pRequest;
  561. W3_URL_INFO * pUrlInfo;
  562. W3_METADATA * pMetaData;
  563. BOOL fRet;
  564. HRESULT hr;
  565. STACK_STRU ( strUrl, MAX_PATH );
  566. CHAR * pszRange;
  567. BOOL fHandled = FALSE;
  568. FILE_CACHE_USER fileUser;
  569. DBG_ASSERT( pW3Context != NULL );
  570. DBG_ASSERT( pOpenFile != NULL );
  571. pResponse = pW3Context->QueryResponse();
  572. DBG_ASSERT( pResponse != NULL );
  573. pRequest = pW3Context->QueryRequest();
  574. DBG_ASSERT( pRequest != NULL );
  575. pUrlInfo = pW3Context->QueryUrlContext()->QueryUrlInfo();
  576. DBG_ASSERT( pUrlInfo != NULL );
  577. pMetaData = pW3Context->QueryUrlContext()->QueryMetaData();
  578. DBG_ASSERT( pMetaData != NULL );
  579. //
  580. // First make sure it a GET or a HEAD
  581. //
  582. HTTP_VERB VerbType = pRequest->QueryVerbType();
  583. if ( VerbType != HttpVerbGET &&
  584. VerbType != HttpVerbHEAD )
  585. {
  586. pW3Context->QueryResponse()->SetStatus( HttpStatusMethodNotAllowed );
  587. hr = pW3Context->SetupAllowHeader();
  588. if ( FAILED( hr ) )
  589. {
  590. return hr;
  591. }
  592. return S_OK;
  593. }
  594. //
  595. // If this an image-map file, do the image-map stuff
  596. //
  597. if (pUrlInfo->QueryGateway() == GATEWAY_MAP)
  598. {
  599. fHandled = FALSE;
  600. hr = MapFileDoWork(pW3Context, pOpenFile, &fHandled);
  601. if (FAILED(hr) ||
  602. fHandled)
  603. {
  604. return hr;
  605. }
  606. //
  607. // fHandled was false, so this is a .map file which wasn't really
  608. // an image-map file, handle it as any other static file
  609. //
  610. }
  611. //
  612. // Do compression, if so configured
  613. //
  614. if (pMetaData->QueryDoStaticCompression() &&
  615. !pW3Context->QueryDoneWithCompression())
  616. {
  617. //
  618. // If this file is compressible, don't let UL store the uncompressed
  619. // version in its cache
  620. //
  621. pW3Context->DisableUlCache();
  622. if (FAILED(hr = HTTP_COMPRESSION::DoStaticFileCompression(
  623. pW3Context, &pOpenFile)))
  624. {
  625. return hr;
  626. }
  627. m_pOpenFile = pOpenFile;
  628. }
  629. //
  630. // First see if the Content-Type is acceptable to the client
  631. //
  632. STRA *pstrContentType = pUrlInfo->QueryContentType();
  633. CHAR * pszAccept = pRequest->GetHeader( HttpHeaderAccept );
  634. if ( pszAccept != NULL && *pszAccept != L'\0' )
  635. {
  636. BOOL fIsAcceptAble;
  637. if ( FAILED( hr = IsAcceptable( pstrContentType->QueryStr(),
  638. pszAccept,
  639. &fIsAcceptAble ) ) )
  640. {
  641. return hr;
  642. }
  643. if ( !fIsAcceptAble )
  644. {
  645. pResponse->ClearHeaders();
  646. pResponse->SetStatus( HttpStatusNotAcceptable );
  647. return S_OK;
  648. }
  649. }
  650. //
  651. // Setup the response headers. First ETag
  652. //
  653. hr = pResponse->SetHeaderByReference( HttpHeaderEtag,
  654. pOpenFile->QueryETag(),
  655. pOpenFile->QueryETagSize() );
  656. if ( FAILED( hr ) )
  657. {
  658. goto Failure;
  659. }
  660. //
  661. // Next is Last-Modified
  662. //
  663. hr = pResponse->SetHeaderByReference( HttpHeaderLastModified,
  664. pOpenFile->QueryLastModifiedString(),
  665. GMT_STRING_SIZE - 1 );
  666. if ( FAILED( hr ) )
  667. {
  668. goto Failure;
  669. }
  670. //
  671. // Next is Content-Location. We only need to send this header if
  672. // we have internally changed the URL of the request. In other words,
  673. // if this is a child execute
  674. //
  675. if ( pW3Context->QuerySendLocation() )
  676. {
  677. STACK_STRA (strContentLocation, MAX_PATH);
  678. STACK_STRA (strRawUrl, MAX_PATH);
  679. if (FAILED(hr = pRequest->GetRawUrl(&strRawUrl)) ||
  680. FAILED(hr = pRequest->BuildFullUrl(strRawUrl,
  681. &strContentLocation,
  682. FALSE)) ||
  683. FAILED(hr = pResponse->SetHeader(HttpHeaderContentLocation,
  684. strContentLocation.QueryStr(),
  685. strContentLocation.QueryCCH())))
  686. {
  687. return hr;
  688. }
  689. }
  690. //
  691. // Next is Accept-Ranges
  692. //
  693. if ( FAILED( hr = pResponse->SetHeaderByReference( HttpHeaderAcceptRanges,
  694. "bytes", 5 ) ) )
  695. {
  696. goto Failure;
  697. }
  698. //
  699. // Handle the If-* (except If-Range) headers if present
  700. //
  701. fHandled = FALSE;
  702. if ( FAILED( hr = CacheValidationDoWork( pW3Context,
  703. pOpenFile,
  704. &fHandled ) ) )
  705. {
  706. goto Failure;
  707. }
  708. if ( fHandled )
  709. {
  710. return hr;
  711. }
  712. //
  713. // Now handle If-Range and Range headers
  714. //
  715. pszRange = pRequest->GetHeader( HttpHeaderRange );
  716. if ( ( pszRange != NULL ) &&
  717. ( !_strnicmp ( pszRange, "bytes", 5 ) ) )
  718. {
  719. //
  720. // Handle range request
  721. //
  722. fHandled = FALSE;
  723. if ( FAILED( hr = RangeDoWork( pW3Context, pOpenFile, &fHandled ) ) )
  724. {
  725. goto Failure;
  726. }
  727. if ( fHandled )
  728. {
  729. return hr;
  730. }
  731. }
  732. //
  733. // If we fell thru, then we are sending out the entire file
  734. //
  735. //
  736. // Setup Content-Type
  737. //
  738. if ( FAILED( hr = pResponse->SetHeaderByReference(
  739. HttpHeaderContentType,
  740. pstrContentType->QueryStr(),
  741. pstrContentType->QueryCCH() ) ) )
  742. {
  743. goto Failure;
  744. }
  745. //
  746. // Setup the response chunks
  747. //
  748. pOpenFile->QuerySize( &liFileSize );
  749. if (liFileSize.QuadPart > 0)
  750. {
  751. if ( pOpenFile->QueryFileBuffer() != NULL &&
  752. liFileSize.HighPart == 0 )
  753. {
  754. hr = pResponse->AddMemoryChunkByReference(
  755. pOpenFile->QueryFileBuffer(),
  756. liFileSize.LowPart );
  757. }
  758. else
  759. {
  760. hr = pResponse->AddFileHandleChunk( pOpenFile->QueryFileHandle(),
  761. 0,
  762. liFileSize.QuadPart );
  763. }
  764. if ( FAILED( hr ) )
  765. {
  766. goto Failure;
  767. }
  768. }
  769. // perf ctr
  770. pW3Context->QuerySite()->IncFilesSent();
  771. // Setup the document footer
  772. if (pMetaData->QueryIsFooterEnabled())
  773. {
  774. if (!pMetaData->QueryFooterString()->IsEmpty() )
  775. {
  776. STRA *pFooterString = pMetaData->QueryFooterString();
  777. if (pFooterString->QueryCCH())
  778. {
  779. if (FAILED(hr = pResponse->AddMemoryChunkByReference(
  780. pFooterString->QueryStr(),
  781. pFooterString->QueryCCH())))
  782. {
  783. goto Failure;
  784. }
  785. }
  786. }
  787. else if (!pMetaData->QueryFooterDocument()->IsEmpty() )
  788. {
  789. DBG_ASSERT( m_pFooterDocument == NULL );
  790. DBG_ASSERT( g_pW3Server->QueryFileCache() );
  791. hr = g_pW3Server->QueryFileCache()->GetFileInfo(
  792. *(pMetaData->QueryFooterDocument()),
  793. NULL,
  794. &fileUser,
  795. TRUE,
  796. &m_pFooterDocument );
  797. if ( SUCCEEDED( hr ) )
  798. {
  799. DBG_ASSERT( m_pFooterDocument != NULL );
  800. m_pFooterDocument->QuerySize( &liFileSize );
  801. if (liFileSize.QuadPart > 0)
  802. {
  803. if ( m_pFooterDocument->QueryFileBuffer() != NULL &&
  804. liFileSize.HighPart == 0 )
  805. {
  806. hr = pResponse->AddMemoryChunkByReference(
  807. m_pFooterDocument->QueryFileBuffer(),
  808. liFileSize.LowPart );
  809. }
  810. else
  811. {
  812. hr = pResponse->AddFileHandleChunk(
  813. m_pFooterDocument->QueryFileHandle(),
  814. 0,
  815. liFileSize.QuadPart );
  816. }
  817. if ( FAILED( hr ) )
  818. {
  819. goto Failure;
  820. }
  821. }
  822. }
  823. else
  824. {
  825. //
  826. // Could not open the footer document. Sub in a error string
  827. //
  828. CHAR achErrorString[ 512 ];
  829. DWORD cbErrorString = sizeof( achErrorString );
  830. hr = g_pW3Server->LoadString( IDS_ERROR_FOOTER,
  831. achErrorString,
  832. &cbErrorString );
  833. if ( FAILED( hr ) )
  834. {
  835. goto Failure;
  836. }
  837. hr = m_strFooterString.Copy( achErrorString, cbErrorString );
  838. if ( FAILED( hr ) )
  839. {
  840. goto Failure;
  841. }
  842. hr = pResponse->AddMemoryChunkByReference(
  843. m_strFooterString.QueryStr(),
  844. m_strFooterString.QueryCCH() );
  845. if ( FAILED( hr ) )
  846. {
  847. goto Failure;
  848. }
  849. }
  850. }
  851. }
  852. return S_OK;
  853. Failure:
  854. //
  855. // It is our responsibility to ensure that there is no incomplete response
  856. //
  857. pResponse->Clear();
  858. return hr;
  859. }
  860. CONTEXT_STATUS
  861. W3_STATIC_FILE_HANDLER::DoWork(
  862. VOID
  863. )
  864. /*++
  865. Routine Description:
  866. Execute the static file handler
  867. Return Value:
  868. CONTEXT_STATUS_PENDING or CONTEXT_STATUS_CONTINUE
  869. --*/
  870. {
  871. W3_CONTEXT *pW3Context = QueryW3Context();
  872. DBG_ASSERT( pW3Context != NULL );
  873. HRESULT hr = NO_ERROR;
  874. W3_RESPONSE * pResponse = pW3Context->QueryResponse();
  875. W3_REQUEST * pRequest = pW3Context->QueryRequest();
  876. W3_METADATA * pMetaData;
  877. URL_CONTEXT * pUrlContext;
  878. W3_FILE_INFO * pOpenFile = NULL;
  879. BOOL fRet;
  880. DWORD dwFilePerms;
  881. BOOL fAccess;
  882. BOOL fAsyncPending = FALSE;
  883. FILE_CACHE_USER fileUser;
  884. //
  885. // Get the metadata, in particular the cached W3_URL_INFO off which we
  886. // we attempt to open the file
  887. //
  888. pUrlContext = pW3Context->QueryUrlContext();
  889. DBG_ASSERT( pUrlContext != NULL );
  890. pMetaData = pUrlContext->QueryMetaData();
  891. DBG_ASSERT( pMetaData != NULL );
  892. //
  893. // Check web permissions.
  894. // Will fail, if no VROOT_MASK_READ, or if we forbid remote access and
  895. // the request is remote
  896. //
  897. dwFilePerms = pMetaData->QueryAccessPerms();
  898. if ( !IS_ACCESS_ALLOWED(pRequest, dwFilePerms, READ) )
  899. {
  900. pResponse->SetStatus( HttpStatusForbidden,
  901. Http403ReadAccessDenied );
  902. goto Failure;
  903. }
  904. //
  905. // Now try to open the file
  906. //
  907. pW3Context->QueryFileCacheUser( &fileUser );
  908. hr = pUrlContext->OpenFile( &fileUser, &pOpenFile );
  909. if (FAILED(hr))
  910. {
  911. DWORD dwError;
  912. IF_DEBUG( STATICFILE )
  913. {
  914. DBGPRINTF(( DBG_CONTEXT,
  915. "Error opening file %ws. hr = %x\n",
  916. pUrlContext->QueryPhysicalPath()->QueryStr(),
  917. hr ));
  918. }
  919. dwError = WIN32_FROM_HRESULT( hr );
  920. switch( dwError )
  921. {
  922. case ERROR_FILE_NOT_FOUND:
  923. case ERROR_PATH_NOT_FOUND:
  924. case ERROR_INVALID_NAME:
  925. hr = NO_ERROR;
  926. pResponse->SetStatus( HttpStatusNotFound );
  927. break;
  928. case ERROR_LOGON_FAILURE:
  929. case ERROR_ACCOUNT_DISABLED:
  930. case ERROR_ACCESS_DENIED:
  931. hr = NO_ERROR;
  932. pResponse->SetStatus( HttpStatusUnauthorized,
  933. Http401Resource );
  934. break;
  935. case ERROR_INSUFFICIENT_BUFFER:
  936. hr = NO_ERROR;
  937. pResponse->SetStatus( HttpStatusUrlTooLong );
  938. break;
  939. }
  940. goto Failure;
  941. }
  942. DBG_ASSERT( pOpenFile != NULL );
  943. //
  944. // Is the file hidden? If so, don't serve it out for legacy reasons
  945. //
  946. if ( pOpenFile->QueryAttributes() & FILE_ATTRIBUTE_HIDDEN )
  947. {
  948. pOpenFile->DereferenceCacheEntry();
  949. pResponse->SetStatus( HttpStatusNotFound );
  950. goto Failure;
  951. }
  952. //
  953. // Is this a file or directory?
  954. //
  955. if ( pOpenFile->QueryAttributes() & FILE_ATTRIBUTE_DIRECTORY )
  956. {
  957. //
  958. // At this point, we will do one of the following:
  959. // a) Send a directory listing
  960. // b) Send a default load file
  961. // c) Send a 302 (to redirect to a slash suffixed URL)
  962. // d) Send a 403 (forbidden)
  963. //
  964. pOpenFile->DereferenceCacheEntry();
  965. pOpenFile = NULL;
  966. hr = DirectoryDoWork( pW3Context,
  967. &fAsyncPending );
  968. if ( fAsyncPending )
  969. {
  970. return CONTEXT_STATUS_PENDING;
  971. }
  972. //
  973. // If access denied, then send the response now
  974. //
  975. if ( WIN32_FROM_HRESULT( hr ) == ERROR_ACCESS_DENIED )
  976. {
  977. pW3Context->SetErrorStatus( hr );
  978. pW3Context->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  979. Http401Resource );
  980. hr = NO_ERROR;
  981. }
  982. }
  983. else
  984. {
  985. //
  986. // This is just a regular file. Serve it out
  987. //
  988. //
  989. // Save away the file now. We will clean it up at the end of the
  990. // request when this current context is cleaned up
  991. //
  992. m_pOpenFile = pOpenFile;
  993. hr = FileDoWork( pW3Context,
  994. pOpenFile );
  995. }
  996. //
  997. // If there was an error here, then generate a 500. If successful, it
  998. // is assumed that the response status is already set
  999. //
  1000. Failure:
  1001. if ( FAILED( hr ) )
  1002. {
  1003. pResponse->Clear();
  1004. pW3Context->SetErrorStatus( hr );
  1005. pResponse->SetStatus( HttpStatusServerError );
  1006. }
  1007. hr = pW3Context->SendResponse( W3_FLAG_ASYNC );
  1008. if ( FAILED( hr ) )
  1009. {
  1010. pW3Context->SetErrorStatus( hr );
  1011. return CONTEXT_STATUS_CONTINUE;
  1012. }
  1013. return CONTEXT_STATUS_PENDING;
  1014. }
  1015. HRESULT
  1016. W3_STATIC_FILE_HANDLER::SetupUlCachedResponse(
  1017. W3_CONTEXT * pW3Context
  1018. )
  1019. /*++
  1020. Routine Description:
  1021. Setup a response to be cached by UL. In this case we will muck with
  1022. the cached file object to
  1023. a) Remove its TTL
  1024. b) Associate the current request's URL with the file object so that when
  1025. the file object goes away, we will be called with enough info to
  1026. flush the appropriate UL cache entry
  1027. Arguments:
  1028. pW3Context - Context
  1029. Return Value:
  1030. HRESULT
  1031. --*/
  1032. {
  1033. STACK_STRU( strFlushUrl, MAX_PATH );
  1034. STACK_STRU( strPhysicalPath, MAX_PATH );
  1035. TOKEN_CACHE_ENTRY * pToken;
  1036. HRESULT hr;
  1037. FILE_CACHE_USER fileUser;
  1038. W3_METADATA * pMetaData;
  1039. if ( pW3Context == NULL )
  1040. {
  1041. DBG_ASSERT( FALSE );
  1042. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1043. }
  1044. if ( m_pOpenFile == NULL )
  1045. {
  1046. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1047. }
  1048. //
  1049. // If the file wasn't cached, then don't use UL cache
  1050. //
  1051. if ( m_pOpenFile->QueryCached() == FALSE )
  1052. {
  1053. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  1054. }
  1055. //
  1056. // If this file was not accessed anonymously, then we need to do access
  1057. // check anonymously before putting into cache
  1058. //
  1059. if ( pW3Context->QueryUserContext()->QueryAuthType() != MD_AUTH_ANONYMOUS )
  1060. {
  1061. pMetaData = pW3Context->QueryUrlContext()->QueryMetaData();
  1062. DBG_ASSERT( pMetaData != NULL );
  1063. pToken = pMetaData->QueryVrAccessToken();
  1064. if ( pToken == NULL )
  1065. {
  1066. pToken = pMetaData->QueryAnonymousToken();
  1067. }
  1068. if ( pToken == NULL )
  1069. {
  1070. return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  1071. }
  1072. fileUser._hToken = pToken->QueryImpersonationToken();
  1073. fileUser._pSid = pToken->QuerySid();
  1074. hr = m_pOpenFile->DoAccessCheck( &fileUser );
  1075. if ( FAILED( hr ) )
  1076. {
  1077. return hr;
  1078. }
  1079. }
  1080. //
  1081. // Get the exact URL used to flush UL cache
  1082. //
  1083. hr = pW3Context->QueryMainContext()->QueryRequest()->GetOriginalFullUrl(
  1084. &strFlushUrl );
  1085. if ( FAILED( hr ) )
  1086. {
  1087. return hr;
  1088. }
  1089. //
  1090. // Get the physical path
  1091. //
  1092. hr = strPhysicalPath.Copy( m_pOpenFile->QueryPhysicalPath() );
  1093. if ( FAILED( hr ) )
  1094. {
  1095. return hr;
  1096. }
  1097. //
  1098. // Setup UL cache response token
  1099. //
  1100. DBG_ASSERT( g_pW3Server->QueryUlCache() != NULL );
  1101. hr = g_pW3Server->QueryUlCache()->SetupUlCachedResponse(
  1102. pW3Context,
  1103. strFlushUrl,
  1104. strPhysicalPath );
  1105. return hr;
  1106. }