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.

6555 lines
164 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. httpreq.cxx
  7. This module contains the http request class implementation
  8. FILE HISTORY:
  9. Johnl 24-Aug-1994 Created
  10. MuraliK 16-May-1995 Modified LogInformation structure
  11. after adding additional fields.
  12. MuraliK 22-Jan-1996 Cache & use UNC impersonation.
  13. */
  14. #include "w3p.hxx"
  15. #include <inetinfo.h>
  16. #include <ole2.h>
  17. #include <issperr.h>
  18. #include <imd.h>
  19. #include <mb.hxx>
  20. #include <mbstring.h>
  21. #include "wamexec.hxx"
  22. extern "C"
  23. {
  24. #include "md5.h"
  25. }
  26. #pragma warning( disable:4355 ) // 'this' used in base member initialization
  27. extern LPSYSTEMTIME
  28. MinSystemTime(
  29. LPSYSTEMTIME Now,
  30. LPSYSTEMTIME Other
  31. );
  32. //
  33. // Hash defines
  34. //
  35. //
  36. // Size of read during cert renegotiation phase
  37. //
  38. #define CERT_RENEGO_READ_SIZE (1024*4)
  39. //
  40. // CGI Header Prefix
  41. //
  42. #define CGI_HEADER_PREFIX_SZ "HTTP_"
  43. #define CGI_HEADER_PREFIX_CCH (sizeof(CGI_HEADER_PREFIX_SZ) - 1)
  44. //
  45. // Private globals.
  46. //
  47. CHAR Slash[] = "/";
  48. BOOL HTTP_REQUEST::_fGlobalInit = FALSE;
  49. HTTP_REQUEST::PFN_GET_INFO HTTP_REQUEST::sm_GetInfoFuncs[26];
  50. //
  51. // This table contains the verbs we recognize
  52. //
  53. struct _HTTP_VERBS
  54. {
  55. TCHAR * pchVerb; // Verb name
  56. UINT cchVerb; // Count of characters in verb
  57. HTTP_VERB httpVerb;
  58. HTTP_REQUEST::PMFN_DOVERB pmfnVerb; // Pointer to member function
  59. }
  60. DoVerb[] =
  61. {
  62. "GET", 3, HTV_GET, &HTTP_REQUEST::DoGet,
  63. "HEAD", 4, HTV_HEAD, &HTTP_REQUEST::DoGet,
  64. "TRACE", 5, HTV_TRACE, &HTTP_REQUEST::DoTrace,
  65. "PUT", 3, HTV_PUT, &HTTP_REQUEST::DoUnknown,
  66. "DELETE", 6, HTV_DELETE, &HTTP_REQUEST::DoUnknown,
  67. "TRACK", 5, HTV_TRACECK, &HTTP_REQUEST::DoTraceCk,
  68. "POST", 4, HTV_POST, &HTTP_REQUEST::DoUnknown,
  69. "OPTIONS", 7, HTV_OPTIONS, &HTTP_REQUEST::DoOptions,
  70. NULL, 0, HTV_UNKNOWN, NULL
  71. };
  72. //
  73. // Private Prototypes.
  74. //
  75. BOOL
  76. BuildCGIHeaderListInSTR( STR * pstr,
  77. HTTP_HEADERS * pHeaderList
  78. );
  79. /*******************************************************************
  80. Maco support for ref/deref of CLIENT_CONN
  81. HISTORY:
  82. DaveK 22-Sep-1997 Added ref logging to HTTP_REQUEST
  83. NOTE callers to HR_LOG_REF_COUNT will not change ref count
  84. directly because HTTP_REQUEST can only change ref count by
  85. calling CLIENT_CONN::Ref/Deref. We use negative ref count here
  86. to indicate no change to ref count.
  87. ********************************************************************/
  88. #if DBG
  89. extern PTRACE_LOG g_pDbgCCRefTraceLog;
  90. #endif
  91. #define HR_LOG_REF_COUNT() \
  92. \
  93. SHARED_LOG_REF_COUNT( \
  94. - (int) QueryRefCount() \
  95. , _pClientConn \
  96. , this \
  97. , _pWamRequest \
  98. , _htrState \
  99. ); \
  100. //
  101. // Functions.
  102. //
  103. /*******************************************************************
  104. NAME: HTTP_REQUEST::HTTP_REQUEST
  105. SYNOPSIS: Http request object constructor
  106. ENTRY: pClientConn - Client connection the request is being made on
  107. NOTES: Constructor can't fail
  108. HISTORY:
  109. Johnl 24-Aug-1994 Created
  110. ********************************************************************/
  111. HTTP_REQUEST::HTTP_REQUEST(
  112. CLIENT_CONN * pClientConn,
  113. PVOID pvInitialBuff,
  114. DWORD cbInitialBuff
  115. )
  116. : HTTP_REQ_BASE( pClientConn,
  117. pvInitialBuff,
  118. cbInitialBuff ),
  119. _pGetFile ( NULL ),
  120. _pWamRequest ( NULL )
  121. {
  122. }
  123. HTTP_REQUEST::~HTTP_REQUEST( VOID )
  124. {
  125. DBG_ASSERT( _pGetFile == NULL );
  126. DBG_ASSERT( _pURIInfo == NULL );
  127. }
  128. /*******************************************************************
  129. NAME: W3MetaDataFree
  130. SYNOPSIS: Frees a formatted meta data object when it's not in use.
  131. ENTRY: pObject - Pointer to the meta data object.
  132. RETURNS:
  133. NOTES:
  134. ********************************************************************/
  135. VOID
  136. W3MetaDataFree(
  137. PVOID pObject
  138. )
  139. {
  140. PW3_METADATA pMD;
  141. pMD = (PW3_METADATA)pObject;
  142. delete pMD;
  143. }
  144. /*******************************************************************
  145. NAME: ValidateURL
  146. SYNOPSIS: WinSE 14424, added a function to check and make sure
  147. the URL is not being used to bypass the metabase settings
  148. ENTRY: mb - Metabase Object
  149. szPath - The full metabase path
  150. RETURNS: TRUE - URL is good
  151. FALSE - URL contains characters sequence which is meant
  152. to confuse the metabase. ie. "/foo./bar.asp"
  153. NOTES:
  154. ********************************************************************/
  155. BOOL
  156. HTTP_REQUEST::ValidateURL( MB & mb, LPSTR szPath )
  157. {
  158. UCHAR ch;
  159. FILETIME ft;
  160. LPSTR szPeriod = szPath;
  161. LPSTR szFirstPeriod;
  162. while ( ( szPeriod ) && ( *szPeriod != '\0' ) )
  163. {
  164. szPeriod = (PCHAR) _mbschr( (PUCHAR) szPeriod, '.' );
  165. while ( ( szPeriod ) &&
  166. ( *(szPeriod+1) != '/' ) &&
  167. ( *(szPeriod+1) != '\\' ) &&
  168. ( *(szPeriod+1) != '\0' )
  169. )
  170. {
  171. szPeriod = (PCHAR) _mbschr( (PUCHAR) szPeriod+1, '.' );
  172. }
  173. // This string does not contain a './' or '.\'
  174. if (!szPeriod)
  175. {
  176. return TRUE;
  177. }
  178. // find first period in this row of dots, /test.../test.htm
  179. szFirstPeriod = szPeriod;
  180. while (*CharPrev(szPath,szFirstPeriod)=='.')
  181. {
  182. szFirstPeriod--;
  183. }
  184. // Terminate Mini-String
  185. ch = *(szPeriod + 1);
  186. *(szPeriod + 1) = '\0';
  187. // Search for item in Metabase
  188. if (FAILED(mb.QueryPMDCOM()->ComMDGetLastChangeTime(mb.QueryHandle(),(PUCHAR) szPath,&ft)))
  189. {
  190. // Item was not found, lets see if we can find it without the '.'
  191. *szFirstPeriod = '\0';
  192. if (SUCCEEDED(mb.QueryPMDCOM()->ComMDGetLastChangeTime(mb.QueryHandle(),(PUCHAR) szPath,&ft)))
  193. {
  194. // They are trying to bypass the name in the metabase, by using the '.'
  195. *(szPeriod + 1) = ch;
  196. *szFirstPeriod = '.';
  197. SetLastError( ERROR_FILE_NOT_FOUND );
  198. return FALSE;
  199. }
  200. } // if (FAILED(mb.QueryPMDCOM()->ComMDGetLastChangeTime(mb.QueryHandle(),(PUCHAR) pszURL,&ft)))
  201. *(szPeriod + 1) = ch;
  202. *szFirstPeriod = '.';
  203. szPeriod++;
  204. } // while ( ( szPeriod ) && ( *szPeriod != '\0' ) )
  205. return TRUE;
  206. } // ValidateURL
  207. /*******************************************************************
  208. NAME: HTTP_REQUEST::ReadMetaData
  209. SYNOPSIS: Reads metadata for a URI, and formats it appropriately.
  210. ENTRY: pszURL - URL to find metadata for
  211. pstrPhysicalPath - Physical path of strURL
  212. ppMetadata - Receives pointer to metadata object
  213. RETURNS: TRUE if successful, FALSE if an error occurred (call
  214. GetLastError())
  215. NOTES:
  216. ********************************************************************/
  217. #define RMD_ASSERT(x) if (!(x)) {DBG_ASSERT(FALSE); delete pMD; return FALSE; }
  218. BOOL HTTP_REQUEST::ReadMetaData(
  219. LPSTR pszURL,
  220. STR * pstrPhysicalPath,
  221. PW3_METADATA * ppMetaData
  222. ){
  223. PW3_METADATA pMD;
  224. DWORD cbPath;
  225. DWORD dwDataSetNumber;
  226. DWORD dwDataSetSize;
  227. PVOID pCacheInfo;
  228. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  229. LPSTR pszVrPath = NULL;
  230. INT ch;
  231. LPSTR pszInVr;
  232. LPSTR pszMinInVr;
  233. DWORD dwNeed;
  234. BUFFER bufTemp1;
  235. DWORD dwL;
  236. STACK_STR( strFullPath, MAX_PATH );
  237. METADATA_ERROR_INFO MDErrorInfo;
  238. DBG_ASSERT( ppMetaData != NULL );
  239. *ppMetaData = NULL;
  240. // First read the data set number, and see if we already have it
  241. // cached. We don't do a full open in this case.
  242. if (*pszURL == '*')
  243. {
  244. pszURL = Slash;
  245. }
  246. if ( !strFullPath.Copy( QueryW3Instance()->QueryMDVRPath() ) ||
  247. !strFullPath.Append( ( *pszURL == '/' ) ? pszURL + 1 : pszURL ) )
  248. {
  249. return FALSE;
  250. }
  251. if ( !ValidateURL( mb, strFullPath.QueryStr() ) )
  252. {
  253. return FALSE;
  254. }
  255. if (!mb.GetDataSetNumber( strFullPath.QueryStr(),
  256. &dwDataSetNumber ))
  257. {
  258. return FALSE;
  259. }
  260. // See if we can find a matching data set already formatted.
  261. pMD = (PW3_METADATA)TsFindMetaData(dwDataSetNumber, METACACHE_W3_SERVER_ID);
  262. if (pMD == NULL)
  263. {
  264. pMD = new W3_METADATA;
  265. if (pMD == NULL)
  266. {
  267. return FALSE;
  268. }
  269. if ( !pMD->ReadMetaData( QueryW3Instance(),
  270. &mb,
  271. pszURL,
  272. &MDErrorInfo) )
  273. {
  274. delete pMD;
  275. mb.Close();
  276. SendMetaDataError(&MDErrorInfo);
  277. return FALSE;
  278. }
  279. // We were succesfull, so try and add this metadata. There is a race
  280. // condition where someone else could have added it while we were
  281. // formatting. This is OK - we'll have two cached, but they should be
  282. // consistent, and one of them will eventually time out. We could have
  283. // AddMetaData check for this, and free the new one while returning a
  284. // pointer to the old one if it finds one, but that isn't worthwhile
  285. // now.
  286. pCacheInfo = TsAddMetaData(pMD, W3MetaDataFree,
  287. dwDataSetNumber, METACACHE_W3_SERVER_ID
  288. );
  289. }
  290. *ppMetaData = pMD;
  291. //
  292. // Build physical path from VR_PATH & portion of URI not used to define VR_PATH
  293. //
  294. return pMD->BuildPhysicalPath( pszURL, pstrPhysicalPath );
  295. }
  296. BOOL
  297. W3_METADATA::BuildProviderList(
  298. CHAR *pszProviders
  299. )
  300. /*++
  301. Routine Description:
  302. Builds an array of SSPI Authentication providers
  303. Arguments:
  304. pszProviders - Comma separated list of providers
  305. Returns:
  306. TRUE if we succeed, FALSE if we don't.
  307. --*/
  308. {
  309. BOOL fRet = TRUE;
  310. INET_PARSER Parser( pszProviders );
  311. DWORD cProv = 0;
  312. while ( m_apszNTProviders[cProv] )
  313. {
  314. TCP_FREE( m_apszNTProviders[cProv] );
  315. m_apszNTProviders[cProv++] = NULL;
  316. }
  317. Parser.SetListMode( TRUE );
  318. cProv = 0;
  319. while ( cProv < (MAX_SSPI_PROVIDERS-1) && *Parser.QueryToken() )
  320. {
  321. m_apszNTProviders[cProv] = (CHAR *) LocalAlloc( LMEM_FIXED,
  322. strlen( Parser.QueryToken() ) + sizeof(CHAR));
  323. if ( !m_apszNTProviders[cProv] )
  324. {
  325. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  326. fRet = FALSE;
  327. break;
  328. }
  329. strcpy( m_apszNTProviders[cProv++], Parser.QueryToken() );
  330. Parser.NextItem();
  331. }
  332. Parser.RestoreBuffer();
  333. return fRet;
  334. }
  335. BOOL
  336. W3_METADATA::CheckSSPPackage(
  337. IN LPCSTR pszAuthString
  338. )
  339. {
  340. DWORD i = 0;
  341. while ( m_apszNTProviders[i] )
  342. {
  343. if ( !_stricmp( pszAuthString, m_apszNTProviders[i++] ))
  344. {
  345. return TRUE;
  346. }
  347. }
  348. return FALSE;
  349. } // W3_SERVER_INSTANCE::CheckSSPPackage
  350. /*******************************************************************
  351. NAME: HTTP_REQUEST::Reset
  352. SYNOPSIS: Resets the request object getting it ready for the next
  353. client request. Called at the beginning of a request.
  354. RETURNS: TRUE if successful, FALSE if an error occurred (call
  355. GetLastError())
  356. HISTORY:
  357. Johnl 04-Sep-1994 Created
  358. ********************************************************************/
  359. BOOL HTTP_REQUEST::Reset( BOOL fResetPipelineInfo )
  360. {
  361. //
  362. // Must reset the base object first
  363. //
  364. TCP_REQUIRE( HTTP_REQ_BASE::Reset(fResetPipelineInfo) );
  365. _fAcceptsAll = FALSE;
  366. _fAnyParams = FALSE;
  367. _fSendToDav = FALSE;
  368. _fDisableScriptmap = FALSE;
  369. _GatewayType = GATEWAY_UNKNOWN;
  370. _dwScriptMapFlags = 0;
  371. _dwFileSystemType = FS_FAT;
  372. _hTempFileHandle = INVALID_HANDLE_VALUE;
  373. _strGatewayImage.Reset();
  374. _strContentType.Reset();
  375. _strReturnMimeType.Reset();
  376. _strTempFileName.Reset();
  377. _pFileNameLock = NULL;
  378. CloseGetFile();
  379. QueryClientConn()->UnbindAccessCheckList();
  380. // Free the URI and/or metadata information if we have any. We know that
  381. // if we have URI info, then it points at meta data and will free the
  382. // metadata info when the URI info is free. Otherwise, we need to free
  383. // the metadata information here.
  384. ReleaseCacheInfo();
  385. // reset execution descriptor block
  386. _Exec.Reset();
  387. _dwCallLevel = 0;
  388. // DLC reset
  389. _strDLCString.Reset();
  390. // Default execute document
  391. _fPossibleDefaultExecute = FALSE;
  392. return TRUE;
  393. }
  394. VOID
  395. HTTP_REQUEST::ReleaseCacheInfo(
  396. VOID
  397. )
  398. /*++
  399. Routine Description:
  400. Checks in any cache info we have out at the end of a
  401. request
  402. This is a virtual method from HTTP_REQ_BASE.
  403. Arguments:
  404. None.
  405. --*/
  406. {
  407. //
  408. // Let's release the base object first
  409. //
  410. HTTP_REQ_BASE::ReleaseCacheInfo();
  411. _Exec.ReleaseCacheInfo();
  412. }
  413. /*******************************************************************
  414. NAME: HTTP_REQUEST::DoWork
  415. SYNOPSIS: Calls the appropriate work item based on our state
  416. ENTRY: pfFinished - Gets set to TRUE if the client request has
  417. been completed
  418. RETURNS: TRUE if successful, FALSE if an error occurred (call
  419. GetLastError())
  420. NOTES: If a failure occurs because of bad info from the client (bad
  421. URL, syntax error etc) then the code that found the problem
  422. should call SendErrorResponse( error ), set the state to
  423. HTR_DONE and return TRUE (when send completes, HTR_DONE will
  424. cleanup).
  425. If an error occurs in the server (out of memory etc) then
  426. LastError should be set and FALSE should be returned. A server
  427. error will be sent then the client will be disconnected.
  428. HISTORY:
  429. Johnl 26-Aug-1994 Created
  430. ********************************************************************/
  431. BOOL HTTP_REQUEST::DoWork(
  432. BOOL * pfFinished
  433. )
  434. {
  435. BOOL fRet = TRUE;
  436. BOOL fContinueProcessingRequest;
  437. BOOL fHandled;
  438. BOOL fDone = FALSE;
  439. BOOL fCompleteRequest;
  440. DWORD dwOffset;
  441. DWORD dwSizeToSend;
  442. BOOL fEntireFile;
  443. BOOL fIsNxRange;
  444. BOOL fIsLastRange;
  445. IF_DEBUG( CONNECTION )
  446. {
  447. DBGPRINTF(( DBG_CONTEXT,
  448. "[http_request::DoWork] Object %lx. State = %d, Bytes Written = %d\n",
  449. QueryClientConn(),
  450. QueryState(),
  451. QueryBytesWritten() ));
  452. }
  453. DBG_CODE( HR_LOG_REF_COUNT(); );
  454. switch ( QueryState() )
  455. {
  456. case HTR_GATEWAY_ASYNC_IO:
  457. fRet = ProcessAsyncGatewayIO();
  458. break;
  459. case HTR_READING_CLIENT_REQUEST:
  460. _cbBytesReceived += QueryBytesWritten();
  461. fRet = OnFillClientReq( &fCompleteRequest,
  462. pfFinished,
  463. &fContinueProcessingRequest);
  464. if ( !fRet )
  465. break;
  466. if ( *pfFinished || !fContinueProcessingRequest)
  467. break;
  468. if ( fCompleteRequest )
  469. {
  470. goto ProcessClientRequest;
  471. }
  472. break;
  473. case HTR_READING_GATEWAY_DATA:
  474. _cbBytesReceived += QueryBytesWritten();
  475. fRet = ReadEntityBody( &fDone );
  476. if ( !fRet || !fDone )
  477. {
  478. break;
  479. }
  480. // Otherwise we're done reading the first piece of the entity body.
  481. // PUT (and possibly other verbs in the future) come through here to
  482. // read their intial entity body. We need to set the state to DOVERB
  483. // here in order to make them work, so that subsequent reads they might
  484. // issue don't come back through this path. Then fall through to the
  485. // DOVERB handlesr.
  486. SetState(HTR_DOVERB);
  487. case HTR_DOVERB:
  488. ProcessClientRequest:
  489. DBG_ASSERT(QueryState() == HTR_DOVERB);
  490. // WinNT 379450
  491. if ( _GatewayType == GATEWAY_MALFORMED )
  492. {
  493. SetState( HTR_DONE, HT_NOT_FOUND, ERROR_INVALID_PARAMETER );
  494. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  495. fRet = TRUE;
  496. break;
  497. }
  498. //
  499. // Check to see if encryption is required before we do any processing
  500. //
  501. if ( (GetFilePerms() & VROOT_MASK_SSL) && !IsSecurePort() )
  502. {
  503. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  504. Disconnect( HT_FORBIDDEN, IDS_SSL_REQUIRED, FALSE, pfFinished );
  505. fRet = TRUE;
  506. DBG_CODE( HR_LOG_REF_COUNT(); );
  507. break;
  508. }
  509. //
  510. // Check for short name as they break metabase equivalency
  511. //
  512. if ( memchr( _strPhysicalPath.QueryStr(), '~', _strPhysicalPath.QueryCB() ))
  513. {
  514. BOOL fShort;
  515. DWORD err;
  516. if ( err = CheckIfShortFileName( (UCHAR *) _strPhysicalPath.QueryStr(),
  517. QueryImpersonationHandle(),
  518. &fShort ))
  519. {
  520. if ( err != ERROR_FILE_NOT_FOUND &&
  521. err != ERROR_PATH_NOT_FOUND )
  522. {
  523. fRet = FALSE;
  524. break;
  525. }
  526. goto PathNotFound;
  527. }
  528. if ( fShort )
  529. {
  530. PathNotFound:
  531. SetState( HTR_DONE, HT_NOT_FOUND, ERROR_FILE_NOT_FOUND );
  532. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  533. fRet = TRUE;
  534. break;
  535. }
  536. }
  537. if ( IsProbablyGatewayRequest() )
  538. {
  539. //
  540. // Optionally check whether the PATH_INFO exists and is openable
  541. //
  542. if ( *(_Exec._pdwScriptMapFlags) & MD_SCRIPTMAPFLAG_CHECK_PATH_INFO )
  543. {
  544. STACK_STR( strTemp, MAX_PATH );
  545. TS_OPEN_FILE_INFO * pGetFile = NULL;
  546. DWORD err;
  547. if ( !LookupVirtualRoot( &strTemp,
  548. _Exec._pstrPathInfo->QueryStr(),
  549. _Exec._pstrPathInfo->QueryCCH() ) )
  550. {
  551. fRet = FALSE;
  552. break;
  553. }
  554. if ( !ImpersonateUser() )
  555. {
  556. fRet = FALSE;
  557. break;
  558. }
  559. pGetFile = TsCreateFile( QueryW3Instance()->GetTsvcCache(),
  560. strTemp.QueryStr(),
  561. QueryImpersonationHandle(),
  562. (_fClearTextPass || _fAnonymous) ?
  563. TS_CACHING_DESIRED : 0 );
  564. err = GetLastError();
  565. RevertUser();
  566. if ( pGetFile == NULL )
  567. {
  568. fRet = TRUE;
  569. if ( err == ERROR_FILE_NOT_FOUND ||
  570. err == ERROR_PATH_NOT_FOUND )
  571. {
  572. SetState( HTR_DONE, HT_NOT_FOUND, err );
  573. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  574. break;
  575. }
  576. else if (err == ERROR_INSUFFICIENT_BUFFER)
  577. {
  578. // Really means the file name was too long.
  579. SetState(HTR_DONE, HT_URL_TOO_LONG, err);
  580. Disconnect(HT_URL_TOO_LONG, IDS_URL_TOO_LONG, FALSE, pfFinished );
  581. break;
  582. }
  583. else if ( err == ERROR_INVALID_NAME )
  584. {
  585. SetState( HTR_DONE, HT_BAD_REQUEST, err );
  586. Disconnect( HT_BAD_REQUEST, NO_ERROR, FALSE, pfFinished );
  587. break;
  588. }
  589. else if ( err == ERROR_ACCESS_DENIED )
  590. {
  591. fRet = FALSE;
  592. SetDeniedFlags( SF_DENIED_RESOURCE );
  593. break;
  594. }
  595. else
  596. {
  597. fRet = FALSE;
  598. break;
  599. }
  600. }
  601. else
  602. {
  603. DBG_REQUIRE( TsCloseHandle( QueryW3Instance()->GetTsvcCache(),
  604. pGetFile ) );
  605. }
  606. }
  607. fHandled = FALSE;
  608. if ( _GatewayType == GATEWAY_BGI )
  609. {
  610. DBG_CODE( HR_LOG_REF_COUNT(); );
  611. fRet = ProcessBGI( &_Exec,
  612. &fHandled,
  613. pfFinished,
  614. _dwScriptMapFlags & MD_SCRIPTMAPFLAG_SCRIPT,
  615. _dwScriptMapFlags & MD_SCRIPTMAPFLAG_WILDCARD);
  616. DBG_CODE( HR_LOG_REF_COUNT(); );
  617. }
  618. else
  619. {
  620. fRet = ProcessGateway( &_Exec,
  621. &fHandled,
  622. pfFinished,
  623. _dwScriptMapFlags
  624. & MD_SCRIPTMAPFLAG_SCRIPT );
  625. }
  626. if ( !fRet) {
  627. break;
  628. }
  629. if ( fHandled || *pfFinished )
  630. break;
  631. //
  632. // Either an error ocurred or the gateways should indicate they
  633. // handled this
  634. //
  635. DBGPRINTF((
  636. DBG_CONTEXT
  637. , "HTTP_REQUEST(%08x)::DoWork - "
  638. "Un-finished, un-handled, succeeded request. "
  639. "_pWamRequest(%08x) "
  640. "fRet(%d) "
  641. "fHandled(%d) "
  642. "*pfFinished(%d) "
  643. "\n"
  644. , this
  645. , _pWamRequest
  646. , fRet
  647. , fHandled
  648. , *pfFinished
  649. ));
  650. DBG_ASSERT( FALSE );
  651. }
  652. // SteveBr: Removing MD_SCRIPTMAPFLAG_NOTRANSMIT_ON_READ_DIR
  653. else if ( _dwScriptMapFlags )
  654. {
  655. //
  656. // Disallow GET on requests with script mappings. This ensures that the only
  657. // way to get source code access is via DAV (translate:f header)
  658. //
  659. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  660. Disconnect( HT_FORBIDDEN, IDS_EXECUTE_ACCESS_DENIED, FALSE, pfFinished );
  661. break;
  662. }
  663. fRet = (this->*_pmfnVerb)( pfFinished );
  664. break;
  665. case HTR_CGI:
  666. fRet = TRUE;
  667. break;
  668. case HTR_RANGE:
  669. dwOffset = _dwRgNxOffset;
  670. dwSizeToSend = _dwRgNxSizeToSend;
  671. DBG_REQUIRE(ScanRange( &_dwRgNxOffset, &_dwRgNxSizeToSend, &fEntireFile, &fIsLastRange));
  672. fRet = SendRange( 0, dwOffset, dwSizeToSend, fIsLastRange );
  673. break;
  674. case HTR_CERT_RENEGOTIATE:
  675. _cbBytesReceived += QueryBytesWritten();
  676. fRet = HandleCertRenegotiation( pfFinished,
  677. &fContinueProcessingRequest,
  678. QueryBytesWritten() );
  679. if ( !fRet || !fContinueProcessingRequest || *pfFinished )
  680. {
  681. break;
  682. }
  683. goto ProcessClientRequest;
  684. case HTR_RESTART_REQUEST:
  685. DBG_CODE( HR_LOG_REF_COUNT(); );
  686. fRet = OnRestartRequest( (char *)_bufClientRequest.QueryPtr(),
  687. _cbBytesWritten = _cbRestartBytesWritten,
  688. pfFinished,
  689. &fContinueProcessingRequest);
  690. DBG_CODE( HR_LOG_REF_COUNT(); );
  691. if ( !fRet || !fContinueProcessingRequest || *pfFinished )
  692. {
  693. break;
  694. }
  695. goto ProcessClientRequest;
  696. case HTR_REDIRECT:
  697. DBG_CODE( HR_LOG_REF_COUNT(); );
  698. _cbBytesReceived += QueryBytesWritten();
  699. fRet = ReadEntityBody( &fDone, FALSE, QueryClientContentLength() );
  700. if ( !fRet || !fDone )
  701. {
  702. break;
  703. }
  704. //
  705. // Now we can do the redirect
  706. //
  707. fRet = DoRedirect( pfFinished );
  708. break;
  709. case HTR_ACCESS_DENIED:
  710. DBG_CODE( HR_LOG_REF_COUNT(); );
  711. _cbBytesReceived += QueryBytesWritten();
  712. fRet = ReadEntityBody( &fDone, FALSE, QueryClientContentLength() );
  713. if ( !fRet || !fDone )
  714. {
  715. break;
  716. }
  717. fRet = FALSE;
  718. SetLastError( ERROR_ACCESS_DENIED );
  719. break;
  720. case HTR_DONE:
  721. fRet = TRUE;
  722. *pfFinished = TRUE;
  723. break;
  724. default:
  725. DBG_ASSERT( FALSE );
  726. fRet = FALSE;
  727. SetLastError( ERROR_INVALID_PARAMETER );
  728. break;
  729. }
  730. IF_DEBUG( CONNECTION )
  731. {
  732. DBGPRINTF(( DBG_CONTEXT,
  733. "[http_request::DoWork] Leaving, Object %lx. State = %d\n",
  734. QueryClientConn(),
  735. QueryState() ));
  736. }
  737. DBG_CODE( HR_LOG_REF_COUNT(); );
  738. return fRet;
  739. }
  740. BOOL
  741. HTTP_REQUEST::FindHost(
  742. VOID
  743. )
  744. /*++
  745. Routine Description:
  746. Find the appropriate host from a request. This involves looking at
  747. the host header field and/or the URL itself, and selecting the
  748. appropriate value. For down level clients we may invoke the munging
  749. code.
  750. Arguments:
  751. None.
  752. Return value:
  753. TRUE if no error, otherwise FALSE
  754. --*/
  755. {
  756. CHAR *pszURL;
  757. CHAR *pszHost;
  758. BOOL bFoundColon;
  759. DWORD dwHostNameLength;
  760. BOOL fHTTPS = FALSE;
  761. BOOL fStartsWithHTTP = FALSE;
  762. pszURL = (CHAR *)_HeaderList.FastMapQueryValue(HM_URL);
  763. DBG_ASSERT(pszURL != NULL);
  764. if ( *pszURL != '/' )
  765. {
  766. //
  767. // We probably have an absolute URL. Verify that, then save
  768. // the host name part of the URL, and update the URL to point
  769. // beyond the host name.
  770. if ( _strnicmp("http://", pszURL, sizeof("http://") - 1) == 0)
  771. {
  772. fHTTPS = FALSE;
  773. fStartsWithHTTP = TRUE;
  774. }
  775. else if ( _strnicmp("https://", pszURL, sizeof("https://") - 1) == 0)
  776. {
  777. fHTTPS = TRUE;
  778. fStartsWithHTTP = TRUE;
  779. }
  780. if ( fStartsWithHTTP )
  781. {
  782. pszHost = pszURL + ( fHTTPS ? sizeof("https://") : sizeof("http://") ) - 1;
  783. // pszHost points to the host name. Walk forward from there.
  784. // If we find a colon we'll want to remember the length at
  785. // that point, and we'll keep searching for a termination slash.
  786. pszURL = pszHost;
  787. bFoundColon = FALSE;
  788. while (*pszURL != '\0')
  789. {
  790. if (*pszURL == ':')
  791. {
  792. // Found a colon. If we haven't already seen one,
  793. // calculate the length of the host name now.
  794. if (!bFoundColon)
  795. {
  796. dwHostNameLength = DIFF(pszURL - pszHost);
  797. }
  798. // Convert the colon to a termianting NULL, remember we
  799. // saw it, and keep going.
  800. bFoundColon = TRUE;
  801. pszURL++;
  802. }
  803. else
  804. {
  805. // If we find a slash, we're done.
  806. if (*pszURL == '/')
  807. {
  808. break;
  809. }
  810. pszURL++;
  811. }
  812. }
  813. if (*pszURL == '\0')
  814. {
  815. // A URL like http://www.microsoft.com is invalid, so
  816. // fail it here.
  817. SetLastError(ERROR_INVALID_PARAMETER);
  818. return FALSE;
  819. }
  820. if (!bFoundColon)
  821. {
  822. // Haven't computed host name length yet, do it now.
  823. dwHostNameLength = DIFF(pszURL - pszHost);
  824. }
  825. if (!_strHostAddr.Copy(pszHost, dwHostNameLength))
  826. {
  827. return FALSE;
  828. }
  829. return TRUE;
  830. }
  831. else
  832. {
  833. // This was not a valid HTTP scheme, nor is it an absolute path.
  834. // Fail the request, choosing the correct error depending on
  835. // whether or not this looks like a scheme.
  836. if (*pszURL != '*' || pszURL[1] != '\0')
  837. {
  838. if (strchr(pszURL, ':') != NULL)
  839. {
  840. // unknown scheme ( e.g FTP )
  841. // This is not an error as this may be handled by a filter
  842. // such as the proxy filter
  843. return TRUE;
  844. }
  845. else
  846. {
  847. SetLastError(ERROR_INVALID_PARAMETER);
  848. }
  849. return FALSE;
  850. }
  851. }
  852. }
  853. //
  854. // If we get here we know we didn't have an absolute URI. Check
  855. // for a Host: header, and if we have one save that as the host name.
  856. //
  857. pszHost = (CHAR *)_HeaderList.FastMapQueryValue(HM_HST);
  858. if (pszHost != NULL)
  859. {
  860. CHAR *pszTemp = pszHost;
  861. // Have a host value. Scan it for a colon, and calculate the
  862. // length based on the colon or the terminating null.
  863. while (*pszTemp != '\0' && *pszTemp != ':')
  864. {
  865. pszTemp++;
  866. }
  867. if (!_strHostAddr.Copy(pszHost, DIFF(pszTemp - pszHost) ) )
  868. {
  869. return FALSE;
  870. }
  871. return TRUE;
  872. }
  873. //
  874. // Right now we have neither an absolute URI or a Host: header. Invoke
  875. // the down level client support if we're supposed to.
  876. //
  877. if ( g_fDLCSupport )
  878. {
  879. if ( !DLCMungeSimple() )
  880. {
  881. return FALSE;
  882. }
  883. }
  884. return TRUE;
  885. }
  886. // Used to extract fast-map parameter, and parse it using sub-function.
  887. # define CheckAndParseField( fld, pstr, func) \
  888. if ( ((pstr = (LPSTR ) _HeaderList.FastMapQueryValue( fld)) != NULL) && \
  889. !(this->func( pstr))) { \
  890. return ( FALSE); \
  891. }
  892. /*******************************************************************
  893. NAME: HTTP_REQUEST::Parse
  894. SYNOPSIS: Gathers all of the interesting information in the client
  895. request
  896. ENTRY: pchRequest - raw Latin-1 request received from the
  897. client, zero terminated
  898. pfFinished - Set to TRUE if no further processing is needed
  899. pfHandled - Set to TRUE if request has been handled
  900. RETURNS: APIERR if an error occurred parsing the header
  901. HISTORY:
  902. Johnl 24-Aug-1994 Created
  903. MuraliK 19-Nov-1996 Created new HTTP HEADERS object and
  904. modified parsing
  905. ********************************************************************/
  906. BOOL
  907. HTTP_REQUEST::Parse(
  908. const CHAR* pchRequest,
  909. DWORD cbData,
  910. DWORD * pcbExtraData,
  911. BOOL * pfHandled,
  912. BOOL * pfFinished
  913. )
  914. {
  915. PMFN_ONGRAMMAR pmfn;
  916. BOOL fRet;
  917. BOOL fIsTrace = FALSE;
  918. BOOL fSecondTry = FALSE;
  919. PW3_SERVER_INSTANCE pInstance;
  920. BOOL fMaxConnExceeded;
  921. LPSTR pszV;
  922. //
  923. // 1. Eliminate all the leading spaces
  924. // Also eliminate \r\n since some clients are sending
  925. // extra \r\n\r\n at the end of the POST
  926. // sending extra characters is incorrect but RFC recommends
  927. // to handle it
  928. while ( cbData &&
  929. ( isspace( (UCHAR)(*(PCHAR)pchRequest) ) ||
  930. (*pchRequest == '\r') ||
  931. (*pchRequest == '\n')
  932. )
  933. )
  934. {
  935. --cbData;
  936. ++pchRequest;
  937. }
  938. //
  939. // 2. Find if Trace operation was requested.
  940. //
  941. if (*pchRequest == 'T' ) {
  942. if ( (cbData > 6) && !memcmp( pchRequest, "TRAC", sizeof("TRAC")-1) )
  943. {
  944. if ( ( pchRequest[sizeof("TRAC")-1] == 'E' &&
  945. _HTTP_IS_LINEAR_SPACE( ((PBYTE)pchRequest)[sizeof("TRACE")-1] ) ) ||
  946. ( pchRequest[sizeof("TRAC")-1] == 'K' &&
  947. _HTTP_IS_LINEAR_SPACE( ((PBYTE)pchRequest)[sizeof("TRACK")-1] ) ) )
  948. {
  949. if ( !QueryRespBuf()->Resize( cbData + sizeof( CHAR ) ) )
  950. {
  951. return FALSE;
  952. }
  953. CopyMemory( QueryRespBufPtr(), pchRequest, cbData );
  954. QueryRespBufPtr()[ cbData ] = '\0';
  955. fIsTrace = TRUE;
  956. }
  957. }
  958. }
  959. if ( !_HeaderList.ParseInput( pchRequest, cbData, pcbExtraData)) {
  960. return ( FALSE);
  961. }
  962. // WinSE 26482 - reject if request header has the character nul in it
  963. //
  964. DWORD cchHeader = cbData - *pcbExtraData;
  965. if ( memchr(pchRequest, 0, cchHeader)!=NULL )
  966. {
  967. SetLastError( ERROR_INVALID_PARAMETER );
  968. return FALSE;
  969. }
  970. //
  971. // Parse out the version number now. We do this so that we can send
  972. // the appropriate Connection: header in the error response if
  973. // FindHost() fails.
  974. //
  975. CheckAndParseField( HM_VER, pszV, HTTP_REQUEST::OnVersion);
  976. //
  977. // If this client didn't send a host header, and down level client support
  978. // is required, then munge the URL if necessary.
  979. //
  980. if (!FindHost())
  981. {
  982. return FALSE;
  983. }
  984. RetryQueryInstance:
  985. fMaxConnExceeded = FALSE;
  986. //
  987. // Get the server instance
  988. //
  989. pInstance = QueryW3Instance();
  990. if ( pInstance == NULL )
  991. {
  992. pInstance = (PW3_SERVER_INSTANCE)
  993. QueryClientConn()->QueryW3Endpoint()->FindAndReferenceInstance(
  994. _strHostAddr.QueryStr(),
  995. QueryClientConn()->QueryLocalIPAddress(),
  996. &fMaxConnExceeded
  997. );
  998. #if 0
  999. if( pInstance != NULL && fMaxConnExceeded ) {
  1000. pInstance->DecrementCurrentConnections();
  1001. pInstance->Dereference();
  1002. pInstance = NULL;
  1003. SetLastError( ERROR_TOO_MANY_SESS );
  1004. return FALSE;
  1005. }
  1006. #endif
  1007. //
  1008. // set the instance. We know that fMaxConnExceeded can't be true
  1009. // unless we found an instance.
  1010. //
  1011. DBG_ASSERT(!fMaxConnExceeded || pInstance != NULL);
  1012. if ( pInstance == NULL)
  1013. {
  1014. DWORD err;
  1015. if ( g_fDLCSupport )
  1016. {
  1017. if ( !fSecondTry &&
  1018. _strHostAddr.IsEmpty() &&
  1019. DLCHandleRequest( pfFinished ) )
  1020. {
  1021. if ( *pfFinished )
  1022. {
  1023. return TRUE;
  1024. }
  1025. fSecondTry = TRUE;
  1026. goto RetryQueryInstance;
  1027. }
  1028. }
  1029. err = GetLastError();
  1030. IF_DEBUG(ERROR) {
  1031. DBGPRINTF((DBG_CONTEXT,"FindInstance failed with %d\n",err));
  1032. }
  1033. //
  1034. // map access denied to something else so we don't request
  1035. // for authentication
  1036. //
  1037. if ( err == ERROR_ACCESS_DENIED ) {
  1038. SetLastError(ERROR_LOGIN_WKSTA_RESTRICTION);
  1039. }
  1040. return FALSE;
  1041. }
  1042. DBG_ASSERT(pInstance != NULL);
  1043. //
  1044. // Set the timeout for future IOs on this context
  1045. //
  1046. AtqContextSetInfo( QueryClientConn()->QueryAtqContext(),
  1047. ATQ_INFO_TIMEOUT,
  1048. (ULONG_PTR) pInstance->QueryConnectionTimeout() );
  1049. //
  1050. // Setup bandwidth throttling for this context
  1051. //
  1052. if ( pInstance->QueryBandwidthInfo() )
  1053. {
  1054. AtqContextSetInfo( QueryClientConn()->QueryAtqContext(),
  1055. ATQ_INFO_BANDWIDTH_INFO,
  1056. (ULONG_PTR) pInstance->QueryBandwidthInfo() );
  1057. }
  1058. //
  1059. // FindInstance sets a reference to pInstance which
  1060. // we use here.
  1061. //
  1062. QueryClientConn()->SetW3Instance(pInstance);
  1063. SetW3Instance(pInstance);
  1064. //
  1065. // Set statistics object to point to pInstance's statistics object
  1066. //
  1067. QueryClientConn()->SetW3StatsObj(pInstance->QueryStatsObj());
  1068. SetW3StatsObj(pInstance->QueryStatsObj());
  1069. //
  1070. // Set and reference the filter list for this instance and then copy
  1071. // any global filter context pointers to the per-instance filter list
  1072. //
  1073. pInstance->LockThisForRead();
  1074. if ( !_Filter.SetFilterList( pInstance->QueryFilterList() ) )
  1075. {
  1076. pInstance->UnlockThis();
  1077. return FALSE;
  1078. }
  1079. pInstance->UnlockThis();
  1080. _Filter.CopyContextPointers();
  1081. }
  1082. //
  1083. // Keep track of what URL was before we passed it to any filters that might modify
  1084. // it [other than READ_RAW filters]
  1085. //
  1086. _strOriginalURL.Copy( _HeaderList.FastMapQueryValue( HM_URL ) );
  1087. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_PREPROC_HEADERS,
  1088. IsSecurePort() ))
  1089. {
  1090. //
  1091. // Notify any filters interested in the request and headers before
  1092. // we do any processing
  1093. //
  1094. if ( !_Filter.NotifyPreProcHeaderFilters( pfFinished ))
  1095. {
  1096. if ( GetLastError() == ERROR_ACCESS_DENIED )
  1097. {
  1098. //
  1099. // we need to read metadata so that WWW authentication
  1100. // headers can be properly generated.
  1101. //
  1102. OnURL( (LPSTR)_HeaderList.FastMapQueryValue( HM_URL ) );
  1103. //
  1104. // restore error ( may have been modified by OnUrl )
  1105. // we disregard any error from OnUrl as we are already
  1106. // doing error processing.
  1107. //
  1108. SetLastError( ERROR_ACCESS_DENIED );
  1109. SetDeniedFlags( SF_DENIED_FILTER );
  1110. }
  1111. return FALSE;
  1112. }
  1113. if ( *pfFinished )
  1114. {
  1115. return TRUE;
  1116. }
  1117. }
  1118. //
  1119. // Now scan for any RFC822 field names that we recognize
  1120. //
  1121. //
  1122. // Check individual items of interest and execute the parse function
  1123. // for that item
  1124. // NYI: It will be great if we can defer the execution and do it on
  1125. // demand basis. We need to modify entire use of the values from
  1126. // HTTP_REQUEST object to make this happen.
  1127. //
  1128. //WinSE 24317: URL and Verb are required
  1129. {
  1130. LPSTR pszVerb;
  1131. LPSTR pszURL;
  1132. pszVerb = (LPSTR)_HeaderList.FastMapQueryValue(HM_MET);
  1133. pszURL = (LPSTR)_HeaderList.FastMapQueryValue(HM_URL);
  1134. if ( pszVerb == NULL || pszURL == NULL )
  1135. {
  1136. SetState( HTR_DONE, HT_SERVER_ERROR, NO_ERROR );
  1137. Disconnect( HT_SERVER_ERROR, 0, FALSE, pfFinished );
  1138. *pfHandled = TRUE;
  1139. return TRUE;
  1140. }
  1141. if (!(this->HTTP_REQUEST::OnVerb( pszVerb)))
  1142. return ( FALSE);
  1143. if (!(this->HTTP_REQUEST::OnURL( pszURL)))
  1144. return ( FALSE);
  1145. }
  1146. // Now that we've accompished a minimum amount of header processing,
  1147. // check to see if we've exceeded the maximum connection count.
  1148. if (fMaxConnExceeded)
  1149. {
  1150. // We've exceeded the max, so we're done now.
  1151. SetLastError( ERROR_TOO_MANY_SESS );
  1152. return FALSE;
  1153. }
  1154. //
  1155. // See if the site is stopped
  1156. //
  1157. DBG_ASSERT( pInstance != NULL);
  1158. if (pInstance->IsSiteCPUPaused())
  1159. {
  1160. SetState( HTR_DONE, HT_SVC_UNAVAILABLE, ERROR_NOT_ENOUGH_QUOTA );
  1161. Disconnect( HT_SVC_UNAVAILABLE, IDS_SITE_RESOURCE_BLOCKED, FALSE, pfFinished );
  1162. *pfHandled = TRUE;
  1163. return TRUE;
  1164. }
  1165. CheckAndParseField( HM_ACC, pszV, HTTP_REQUEST::OnAccept);
  1166. CheckAndParseField( HM_CON, pszV, HTTP_REQUEST::OnConnection);
  1167. // CheckAndParseField( HM_HST, pszV, HTTP_REQ_BASE::OnHost);
  1168. // HM_AUT is deferred
  1169. // CheckAndParseField( HM_AUT, pszV, HTTP_REQ_BASE::OnAuthorization);
  1170. CheckAndParseField( HM_IMS, pszV, HTTP_REQ_BASE::OnIfModifiedSince);
  1171. CheckAndParseField( HM_IFM, pszV, HTTP_REQ_BASE::OnIfMatch);
  1172. CheckAndParseField( HM_INM, pszV, HTTP_REQ_BASE::OnIfNoneMatch);
  1173. CheckAndParseField( HM_IFR, pszV, HTTP_REQ_BASE::OnIfRange);
  1174. CheckAndParseField( HM_UMS, pszV, HTTP_REQ_BASE::OnUnlessModifiedSince);
  1175. CheckAndParseField( HM_IUM, pszV, HTTP_REQ_BASE::OnIfUnmodifiedSince);
  1176. CheckAndParseField( HM_CLE, pszV, HTTP_REQ_BASE::OnContentLength);
  1177. CheckAndParseField( HM_CTY, pszV, HTTP_REQUEST::OnContentType);
  1178. CheckAndParseField( HM_PRA, pszV, HTTP_REQUEST::OnProxyAuthorization);
  1179. CheckAndParseField( HM_RNG, pszV, HTTP_REQ_BASE::OnRange);
  1180. CheckAndParseField( HM_TEC, pszV, HTTP_REQUEST::OnTransferEncoding);
  1181. CheckAndParseField( HM_LCK, pszV, HTTP_REQUEST::OnLockToken);
  1182. //
  1183. // Optionally ignore the Translate header
  1184. //
  1185. if ( !_pMetaData || !_pMetaData->QueryIgnoreTranslate() )
  1186. {
  1187. CheckAndParseField( HM_TRN, pszV, HTTP_REQUEST::OnTranslate);
  1188. }
  1189. CheckAndParseField( HM_ISM, pszV, HTTP_REQUEST::OnIf);
  1190. // Make sure we're see a host header if this is a 1.1 request.
  1191. if (IsOneOne() && _HeaderList.FastMapQueryValue(HM_HST) == NULL)
  1192. {
  1193. _HeaderList.FastMapCancel( HM_AUT );
  1194. SetState( HTR_DONE, HT_BAD_REQUEST, ERROR_INVALID_PARAMETER );
  1195. Disconnect( HT_BAD_REQUEST, IDS_HOST_REQUIRED, FALSE, pfFinished );
  1196. *pfHandled = TRUE;
  1197. return TRUE;
  1198. }
  1199. //
  1200. // OK. Now we have determined whether or not to keep the connection
  1201. // alive. Now we can check when the VROOT allows it.
  1202. //
  1203. if ( !_pMetaData->QueryKeepAlives() )
  1204. {
  1205. SetKeepConn( FALSE );
  1206. }
  1207. //
  1208. // If we picked up some gateway data in the headers, adjust for that
  1209. // now
  1210. //
  1211. _cbClientRequest -= *pcbExtraData;
  1212. //
  1213. // store available input byte count for OnRestartRequest
  1214. // necessary because _cbBytesWritten will be overwritten
  1215. // by IO completion
  1216. //
  1217. if ( _fHaveContentLength )
  1218. {
  1219. _cbBytesWritten = _cbRestartBytesWritten = *pcbExtraData;
  1220. }
  1221. else
  1222. {
  1223. _cbRestartBytesWritten = _cbBytesWritten;
  1224. }
  1225. SetState( HTR_DOVERB );
  1226. return (fIsTrace ? TRUE : ProcessURL( pfFinished, pfHandled ));
  1227. } // HTTP_REQUEST::Parse()
  1228. /*******************************************************************
  1229. NAME: HTTP_REQUEST::OnVerb
  1230. SYNOPSIS: Parses the verb from an HTTP request
  1231. ENTRY: pszValue - Pointer to zero terminated string
  1232. RETURNS: TRUE if successful, FALSE if an error occurred
  1233. HISTORY:
  1234. Johnl 24-Aug-1994 Created
  1235. ********************************************************************/
  1236. BOOL HTTP_REQUEST::OnVerb( CHAR * pszValue )
  1237. {
  1238. UINT i = 0;
  1239. UINT cchMethod;
  1240. if ( !_strMethod.Copy( pszValue ) )
  1241. return FALSE;
  1242. //
  1243. // Look for the verbs we recognize
  1244. //
  1245. cchMethod = _strMethod.QueryCCH();
  1246. while ( DoVerb[i].pchVerb )
  1247. {
  1248. if ( (cchMethod == DoVerb[i].cchVerb) &&
  1249. (!memcmp( pszValue,
  1250. DoVerb[i].pchVerb,
  1251. DoVerb[i].cchVerb )))
  1252. {
  1253. _verb = DoVerb[i].httpVerb;
  1254. switch ( _verb )
  1255. {
  1256. case HTV_GET:
  1257. QueryW3StatsObj()->IncrTotalGets();
  1258. break;
  1259. case HTV_HEAD:
  1260. QueryW3StatsObj()->IncrTotalHeads();
  1261. break;
  1262. case HTV_TRACE:
  1263. QueryW3StatsObj()->IncrTotalTraces();
  1264. break;
  1265. case HTV_TRACECK:
  1266. break;
  1267. case HTV_PUT:
  1268. QueryW3StatsObj()->IncrTotalPuts();
  1269. _putstate = PSTATE_START;
  1270. _fSendToDav = TRUE; // Verb is handled by DAV.
  1271. #if 0 // Not used anywhere /SAB
  1272. _fIsWrite = TRUE;
  1273. #endif
  1274. break;
  1275. case HTV_DELETE:
  1276. QueryW3StatsObj()->IncrTotalDeletes();
  1277. _fSendToDav = TRUE; // Verb is handled by DAV.
  1278. break;
  1279. case HTV_POST:
  1280. QueryW3StatsObj()->IncrTotalPosts();
  1281. break;
  1282. case HTV_OPTIONS:
  1283. QueryW3StatsObj()->IncrTotalOthers();
  1284. _fSendToDav = TRUE; // Verb is handled by DAV.
  1285. break;
  1286. default:
  1287. DBG_ASSERT( FALSE );
  1288. break;
  1289. }
  1290. _pmfnVerb = DoVerb[i].pmfnVerb;
  1291. return TRUE;
  1292. }
  1293. i++;
  1294. }
  1295. //
  1296. // The verb may be a verb a gateway knows how to deal with so
  1297. // all hope isn't lost
  1298. //
  1299. QueryW3StatsObj()->IncrTotalOthers();
  1300. _pmfnVerb = &HTTP_REQUEST::DoUnknown;
  1301. _verb = HTV_UNKNOWN;
  1302. _fSendToDav = TRUE; // Send unknown verbs to DAV.
  1303. return TRUE;
  1304. }
  1305. BOOL
  1306. HTTP_REQUEST::NormalizeUrl(
  1307. LPSTR pszStart
  1308. )
  1309. /*++
  1310. Routine Description:
  1311. Normalize URL
  1312. Arguments:
  1313. strUrl - URL to be updated to a canonical URI
  1314. Return value:
  1315. TRUE if no error, otherwise FALSE
  1316. --*/
  1317. {
  1318. TCHAR * pchParams;
  1319. TCHAR * pch;
  1320. TCHAR * pszACU;
  1321. TCHAR chParams;
  1322. LPSTR pszSlash;
  1323. LPSTR pszURL;
  1324. LPSTR pszValue;
  1325. BOOL fSt;
  1326. STACK_STR( strChgUrl, MAX_PATH );
  1327. if ( *pszStart != '/' )
  1328. {
  1329. //
  1330. // assume HTTP URL, skip protocol & host name by
  1331. // searching for 1st '/' following "//"
  1332. //
  1333. // We handle this information as a "Host:" header.
  1334. // It will be overwritten by the real header if it is
  1335. // present.
  1336. //
  1337. // We do not check for a match in this case.
  1338. //
  1339. if ( (pszSlash = strchr( pszStart, '/' )) && pszSlash[1] == '/' )
  1340. {
  1341. pszSlash += 2;
  1342. if ( pszURL = strchr( pszSlash, '/' ) )
  1343. {
  1344. //
  1345. // update pointer to URL to point to the 1st slash
  1346. // following host name
  1347. //
  1348. pszValue = pszURL;
  1349. }
  1350. else
  1351. {
  1352. //
  1353. // if no single slash following host name
  1354. // consider the URL to be empty.
  1355. //
  1356. pszValue = pszSlash + strlen( pszSlash );
  1357. }
  1358. memmove( pszStart, pszValue, strlen(pszValue)+1 );
  1359. }
  1360. //
  1361. // if no double slash, this is not a fully qualified URL
  1362. // and we leave it alone.
  1363. //
  1364. }
  1365. //
  1366. // references to /_[W3_AUTH_CHANGE_URL] will be redirected
  1367. // to the configured URL. This URL will be accessed from
  1368. // the system context ( i.e with system access rights )
  1369. //
  1370. if ( pszStart[0] == '/' && pszStart[1] == '_'
  1371. && !memcmp( pszStart+2, W3_AUTH_CHANGE_URL, sizeof(W3_AUTH_CHANGE_URL)-1 ) )
  1372. {
  1373. QueryW3Instance()->LockThisForRead();
  1374. fSt = strChgUrl.Copy( pszACU = (TCHAR*)QueryW3Instance()->QueryAuthChangeUrl() );
  1375. QueryW3Instance()->UnlockThis();
  1376. if ( pszACU )
  1377. {
  1378. if ( fSt )
  1379. {
  1380. strcpy( pszStart, strChgUrl.QueryStr() );
  1381. }
  1382. else
  1383. {
  1384. return FALSE;
  1385. }
  1386. }
  1387. }
  1388. //
  1389. // Check for a question mark which indicates this URL contains some
  1390. // parameters and break the two apart if found
  1391. //
  1392. if ( (pchParams = ::_tcschr( pszStart, TEXT('?') )) )
  1393. {
  1394. *pchParams = TEXT('\0');
  1395. }
  1396. //
  1397. // Unescape wants a STR ( sigh )
  1398. //
  1399. strChgUrl.Copy( (TCHAR*)pszStart );
  1400. strChgUrl.Unescape();
  1401. strcpy( pszStart, strChgUrl.QueryStr() );
  1402. //
  1403. // Canonicalize the URL
  1404. //
  1405. CanonURL( pszStart, g_pInetSvc->IsSystemDBCS() );
  1406. return TRUE;
  1407. }
  1408. /*******************************************************************
  1409. NAME: HTTP_REQUEST::OnURL
  1410. SYNOPSIS: Parses the URL from an HTTP request
  1411. ENTRY: pszValue - URL on http request line
  1412. RETURNS: TRUE if successful, FALSE if an error occurred
  1413. NOTES: The URL and Path info are unescaped in this method.
  1414. Parameters coming after the "?" are *not* unescaped as they
  1415. contain encoded '&' or other meaningful items.
  1416. HISTORY:
  1417. Johnl 24-Aug-1994 Created
  1418. ********************************************************************/
  1419. BOOL HTTP_REQUEST::OnURL( CHAR * pszValue )
  1420. {
  1421. TCHAR * pchParams;
  1422. TCHAR * pchStart;
  1423. TCHAR * pch;
  1424. TCHAR * pszACU;
  1425. TCHAR chParams;
  1426. LPSTR pszSlash;
  1427. BOOL fSt;
  1428. LPSTR pszArg = NULL;
  1429. STACK_STR( strChgUrl, MAX_PATH );
  1430. PW3_URI_INFO pURIBlob;
  1431. BOOL fHTTPS = FALSE;
  1432. BOOL fHTTP = FALSE;
  1433. if ( *pszValue != '/' )
  1434. {
  1435. //
  1436. // skip protocol & host name by
  1437. // searching for 1st '/' following "http://" or "https://"
  1438. //
  1439. if ( _strnicmp("http://", pszValue, sizeof("http://") - 1) == 0)
  1440. {
  1441. fHTTP = TRUE;
  1442. }
  1443. else if ( _strnicmp("https://", pszValue, sizeof("https://") - 1) == 0)
  1444. {
  1445. fHTTPS = TRUE;
  1446. }
  1447. if ( fHTTP || fHTTPS )
  1448. {
  1449. pszSlash = pszValue + ( fHTTP ? sizeof("http://") - 1 :
  1450. sizeof("https://") - 1 );
  1451. if ( !(pszValue = strchr( pszSlash, '/' )) )
  1452. {
  1453. //
  1454. // if no single slash following host name
  1455. // then URL is empty and this is an invalid request
  1456. //
  1457. SetLastError( ERROR_INVALID_PARAMETER );
  1458. return FALSE;
  1459. }
  1460. }
  1461. else
  1462. {
  1463. if ( (*pszValue != '*' || pszValue[1] != '\0') &&
  1464. strchr(pszValue, ':') != NULL )
  1465. {
  1466. // unknown scheme ( e.g FTP )
  1467. SetLastError( ERROR_NOT_SUPPORTED );
  1468. return FALSE;
  1469. }
  1470. }
  1471. }
  1472. //
  1473. // references to /_[W3_AUTH_CHANGE_URL] will be redirected
  1474. // to the configured URL. This URL will be accessed from
  1475. // the system context ( i.e with system access rights )
  1476. //
  1477. if ( pszValue[0] == '/' && pszValue[1] == '_'
  1478. && !strncmp( pszValue+2, W3_AUTH_CHANGE_URL, sizeof(W3_AUTH_CHANGE_URL)-1 ) )
  1479. {
  1480. QueryW3Instance()->LockThisForRead();
  1481. fSt = strChgUrl.Copy( pszACU = (TCHAR*)QueryW3Instance()->QueryAuthChangeUrl() );
  1482. QueryW3Instance()->UnlockThis();
  1483. // store ptr to arg to append it to URL
  1484. pszArg = strchr( pszValue, '?' );
  1485. if ( pszACU )
  1486. {
  1487. if ( fSt && LogonAsSystem() )
  1488. {
  1489. pszValue = strChgUrl.QueryStr();
  1490. _strUnmappedUserName.Copy( _strUserName );
  1491. }
  1492. else
  1493. {
  1494. return FALSE;
  1495. }
  1496. }
  1497. }
  1498. // Check for the asterisk URL. If it's an asterisk with trailing stuff
  1499. // it's illegal.
  1500. if (*pszValue == '*')
  1501. {
  1502. if (pszValue[1] != '\0')
  1503. {
  1504. SetLastError( ERROR_INVALID_PARAMETER );
  1505. return FALSE;
  1506. }
  1507. }
  1508. if ( !_strRawURL.Copy( pszValue ) )
  1509. {
  1510. return FALSE;
  1511. }
  1512. if ( pszArg != NULL && !_strRawURL.Append( pszArg ) )
  1513. {
  1514. return FALSE;
  1515. }
  1516. pchStart = pszValue;
  1517. //
  1518. // Check for a question mark which indicates this URL contains some
  1519. // parameters and break the two apart if found
  1520. //
  1521. if ( (pchParams = pszArg) || (pchParams = ::_tcschr( pchStart, TEXT('?') )) )
  1522. {
  1523. if ( !pszArg )
  1524. {
  1525. chParams = *pchParams;
  1526. *pchParams = TEXT('\0');
  1527. }
  1528. _fAnyParams = TRUE;
  1529. if ( !_strURL.Copy( pchStart ) ||
  1530. !_strURL.Unescape() ||
  1531. !_strURLParams.Copy( pchParams + 1 ))
  1532. {
  1533. return FALSE;
  1534. }
  1535. }
  1536. else
  1537. {
  1538. _fAnyParams = FALSE;
  1539. if ( !_strURL.Copy( _strRawURL ) ||
  1540. !_strURL.Unescape() )
  1541. {
  1542. return FALSE;
  1543. }
  1544. _strURLParams.Reset();
  1545. }
  1546. //
  1547. // Canonicalize the URL and make sure it's valid
  1548. //
  1549. INT cchURL = CanonURL( _strURL.QueryStr(), g_pInetSvc->IsSystemDBCS() );
  1550. _strURL.SetLen( cchURL );
  1551. if( !_strURLPathInfo.Copy( _strURL ) )
  1552. {
  1553. return FALSE;
  1554. }
  1555. if ( pchParams && !pszArg )
  1556. {
  1557. *pchParams = chParams;
  1558. }
  1559. //
  1560. // Now that we have the canonicalized URL we need to get metadata
  1561. // information about it. Call the cache to get this. If it's not
  1562. // in the cache, we'll call the metadata API to get it and add
  1563. // it to the cache.
  1564. //
  1565. if ( !TsCheckOutCachedBlob( QueryW3Instance()->GetTsvcCache(),
  1566. _strURL.QueryStr(),
  1567. _strURL.QueryCCH(),
  1568. RESERVED_DEMUX_URI_INFO,
  1569. (VOID **) &pURIBlob,
  1570. NULL ))
  1571. {
  1572. // We don't have URI info available for this yet. We need to read
  1573. // the metadata for this URI and format it into a usable form, and
  1574. // save that with the request. If this request turns out to be
  1575. // valid later on we'll add it to the cache.
  1576. //
  1577. if ( !ReadMetaData( _strURL.QueryStr(),
  1578. &_strPhysicalPath,
  1579. &_pMetaData ) ) {
  1580. return FALSE;
  1581. }
  1582. } else
  1583. {
  1584. // We have a cached URI blob. Save it in the HTTP request.
  1585. _pURIInfo = pURIBlob;
  1586. if ( _pURIInfo->pszUnmappedName )
  1587. {
  1588. _strPhysicalPath.Copy( _pURIInfo->pszUnmappedName );
  1589. }
  1590. else
  1591. {
  1592. _strPhysicalPath.Copy( _pURIInfo->pszName, _pURIInfo->cchName );
  1593. }
  1594. _pMetaData = _pURIInfo->pMetaData;
  1595. }
  1596. if ( _pMetaData->QueryVrError() )
  1597. {
  1598. SetLastError( _pMetaData->QueryVrError() );
  1599. return FALSE;
  1600. }
  1601. if ( _pMetaData->QueryIpDnsAccessCheckPtr() )
  1602. {
  1603. QueryClientConn()->BindAccessCheckList( (LPBYTE)_pMetaData->QueryIpDnsAccessCheckPtr(),
  1604. _pMetaData->QueryIpDnsAccessCheckSize() );
  1605. }
  1606. //
  1607. // If we're already logged on as an anonymouse user, make sure that
  1608. // the anonymous user for this URL is compatible.
  1609. //
  1610. if (_fLoggedOn && _fAnonymous)
  1611. {
  1612. if (_cbLastAnonAcctDesc != _pMetaData->QueryAuthentInfo()->cbAnonAcctDesc ||
  1613. memcmp(_bufLastAnonAcctDesc.QueryPtr(),
  1614. _pMetaData->QueryAuthentInfo()->bAnonAcctDesc.QueryPtr(),
  1615. _cbLastAnonAcctDesc))
  1616. {
  1617. QueryW3StatsObj()->DecrCurrentAnonymousUsers();
  1618. ResetAuth(FALSE);
  1619. }
  1620. }
  1621. return TRUE;
  1622. }
  1623. BOOL
  1624. HTTP_REQUEST::ExecuteChildCGIBGI(
  1625. IN CHAR * pszURL,
  1626. IN DWORD dwChildExecFlags,
  1627. IN CHAR * pszVerb
  1628. )
  1629. /*++
  1630. Routine Description:
  1631. Provides ISAPI apps with ability to synchronously execute
  1632. CGI scripts.
  1633. Arguments:
  1634. pszURL - URL of request to be executed (must be executable)
  1635. dwChildExecFlags - HSE_EXEC_???? flags
  1636. pszVerb - Verb of request
  1637. Return value:
  1638. TRUE if no error, otherwise FALSE
  1639. --*/
  1640. {
  1641. EXEC_DESCRIPTOR Exec;
  1642. STACK_STR( strChildURL, MAX_PATH + 1 );
  1643. STACK_STR( strChildPhysicalPath, MAX_PATH + 1 );
  1644. STACK_STR( strChildGatewayImage, MAX_PATH + 1 );
  1645. STACK_STR( strChildPathInfo, MAX_PATH + 1 );
  1646. STACK_STR( strChildURLParams, MAX_PATH + 1 );
  1647. STACK_STR( strChildUnmappedPhysicalPath, MAX_PATH + 1 );
  1648. DWORD dwChildScriptMapFlags = 0;
  1649. GATEWAY_TYPE ChildGatewayType = GATEWAY_UNKNOWN;
  1650. BOOL fHandled;
  1651. BOOL fFinished;
  1652. CHAR * pchParams = NULL;
  1653. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  1654. BOOL fRet;
  1655. PW3_URI_INFO pURIInfo = NULL;
  1656. BOOL fMustCache = FALSE;
  1657. BOOL fVerbExcluded;
  1658. enum HTTP_VERB RequestVerb;
  1659. enum HTTP_VERB OldVerb;
  1660. CHAR * pszRequestVerb;
  1661. STACK_STR( strOldVerb, 16 );
  1662. if ( _dwCallLevel > EXEC_MAX_NESTED_LEVELS )
  1663. {
  1664. SetLastError( ERROR_NOT_SUPPORTED );
  1665. return FALSE;
  1666. }
  1667. //
  1668. // Populate the execution descriptor block with our own "allocated"
  1669. // members (instead of those from HTTP_REQUEST)
  1670. //
  1671. Exec._pstrURL = &strChildURL;
  1672. Exec._pstrPhysicalPath = &strChildPhysicalPath;
  1673. Exec._pstrUnmappedPhysicalPath = &strChildUnmappedPhysicalPath;
  1674. Exec._pstrGatewayImage = &strChildGatewayImage;
  1675. Exec._pstrPathInfo = &strChildPathInfo;
  1676. Exec._pstrURLParams = &strChildURLParams;
  1677. Exec._pdwScriptMapFlags = &dwChildScriptMapFlags;
  1678. Exec._pGatewayType = &ChildGatewayType;
  1679. Exec._pRequest = this;
  1680. Exec._pPathInfoMetaData = NULL;
  1681. Exec._pPathInfoURIBlob = NULL;
  1682. Exec._pAppPathURIBlob = NULL;
  1683. Exec._dwExecFlags = ( EXEC_FLAG_CHILD |
  1684. ( dwChildExecFlags &
  1685. ( HSE_EXEC_NO_HEADERS |
  1686. HSE_EXEC_REDIRECT_ONLY |
  1687. HSE_EXEC_NO_ISA_WILDCARDS |
  1688. HSE_EXEC_CUSTOM_ERROR ) ) );
  1689. if ( !strChildURL.Copy( pszURL ) )
  1690. {
  1691. return FALSE;
  1692. }
  1693. //
  1694. // Tiny bit of "duplicate" code to parse out a query string from the
  1695. // original URL request.
  1696. //
  1697. pchParams = ::_tcschr( strChildURL.QueryStr(), TEXT('?') );
  1698. if ( pchParams != NULL )
  1699. {
  1700. if ( !strChildURL.SetLen( DIFF(pchParams - strChildURL.QueryStr()) ) ||
  1701. !strChildURLParams.Copy( pchParams + 1 ) )
  1702. {
  1703. return FALSE;
  1704. }
  1705. }
  1706. if ( !strChildURL.Unescape() )
  1707. {
  1708. return FALSE;
  1709. }
  1710. //
  1711. // Canonicalize the URL and make sure it's valid
  1712. //
  1713. CanonURL( strChildURL.QueryStr(), g_pInetSvc->IsSystemDBCS() );
  1714. strChildURL.SetLen( strlen( strChildURL.QueryStr() ));
  1715. if ( !LookupVirtualRoot( &strChildPhysicalPath,
  1716. strChildURL.QueryStr(),
  1717. strChildURL.QueryCCH(),
  1718. NULL,
  1719. NULL,
  1720. NULL,
  1721. NULL,
  1722. FALSE,
  1723. &(Exec._pMetaData),
  1724. & pURIInfo) )
  1725. {
  1726. fRet = FALSE;
  1727. goto Exit;
  1728. }
  1729. //
  1730. // Change the verb if requested
  1731. //
  1732. if ( pszVerb )
  1733. {
  1734. DWORD i = 0;
  1735. DWORD cchVerb = strlen( pszVerb );
  1736. while ( DoVerb[i].pchVerb )
  1737. {
  1738. if ( ( cchVerb == DoVerb[ i ].cchVerb ) &&
  1739. ( !memcmp( pszVerb,
  1740. DoVerb[ i ].pchVerb,
  1741. DoVerb[ i ].cchVerb ) ) )
  1742. {
  1743. break;
  1744. }
  1745. i++;
  1746. }
  1747. if ( !DoVerb[i].pchVerb )
  1748. {
  1749. fRet = FALSE;
  1750. SetLastError( ERROR_INVALID_PARAMETER );
  1751. goto Exit;
  1752. }
  1753. RequestVerb = DoVerb[ i ].httpVerb;
  1754. pszRequestVerb = pszVerb;
  1755. }
  1756. else
  1757. {
  1758. RequestVerb = QueryVerb();
  1759. pszRequestVerb = _strMethod.QueryStr();
  1760. }
  1761. //
  1762. // Fill in members of execution descriptor block
  1763. //
  1764. if ( !ParseExecute( &Exec,
  1765. TRUE,
  1766. &fVerbExcluded,
  1767. pURIInfo,
  1768. RequestVerb,
  1769. pszRequestVerb
  1770. ) )
  1771. {
  1772. fRet = FALSE;
  1773. goto Exit;
  1774. }
  1775. DBG_ASSERT(!(dwChildScriptMapFlags & MD_SCRIPTMAPFLAG_WILDCARD));
  1776. if (ChildGatewayType != GATEWAY_UNKNOWN && ChildGatewayType != GATEWAY_NONE &&
  1777. fVerbExcluded)
  1778. {
  1779. ChildGatewayType = GATEWAY_NONE;
  1780. }
  1781. _dwCallLevel++;
  1782. //
  1783. // Remember the original verb to be restored after handling execute
  1784. //
  1785. if ( pszVerb )
  1786. {
  1787. OldVerb = QueryVerb();
  1788. if ( !strOldVerb.Copy( _strMethod ) )
  1789. {
  1790. fRet = FALSE;
  1791. goto Exit;
  1792. }
  1793. if ( !_strMethod.Copy( pszVerb ) )
  1794. {
  1795. fRet = FALSE;
  1796. goto Exit;
  1797. }
  1798. _verb = RequestVerb;
  1799. }
  1800. if ( ChildGatewayType == GATEWAY_CGI )
  1801. {
  1802. fRet = ProcessGateway( &Exec,
  1803. &fHandled,
  1804. &fFinished,
  1805. dwChildScriptMapFlags &
  1806. MD_SCRIPTMAPFLAG_SCRIPT );
  1807. }
  1808. else if ( ChildGatewayType == GATEWAY_BGI )
  1809. {
  1810. if ( fRet = Exec.CreateChildEvent() ) {
  1811. Exec._pParentWamRequest = _pWamRequest;
  1812. // process ISAPI request under System
  1813. RevertUser();
  1814. fRet = ProcessBGI( &Exec,
  1815. &fHandled,
  1816. &fFinished,
  1817. dwChildScriptMapFlags &
  1818. MD_SCRIPTMAPFLAG_SCRIPT );
  1819. // restore impersonation
  1820. DBG_REQUIRE( ImpersonateUser() );
  1821. Exec.WaitForChildEvent();
  1822. } else {
  1823. DBGPRINTF((
  1824. DBG_CONTEXT
  1825. , "HTTP_REQUEST(%08x)::ExecuteChildCGIBGI() "
  1826. "CreateChildEvent() failed. "
  1827. "\n"
  1828. , this
  1829. ));
  1830. //
  1831. // fRet is failure, so just fall through
  1832. //
  1833. }
  1834. }
  1835. else
  1836. {
  1837. SetLastError( ERROR_INVALID_PARAMETER );
  1838. fRet = FALSE;
  1839. }
  1840. _dwCallLevel--;
  1841. //
  1842. // Restore the verb if necessary
  1843. //
  1844. if ( pszVerb )
  1845. {
  1846. _verb = OldVerb;
  1847. DBG_REQUIRE( _strMethod.Copy( strOldVerb ) );
  1848. }
  1849. Exit:
  1850. if ( pURIInfo != NULL)
  1851. {
  1852. if (pURIInfo->bIsCached)
  1853. {
  1854. TsCheckInCachedBlob( pURIInfo );
  1855. }
  1856. else
  1857. {
  1858. TsFree(QueryW3Instance()->GetTsvcCache(), pURIInfo );
  1859. }
  1860. pURIInfo = NULL;
  1861. }
  1862. else
  1863. {
  1864. if ( Exec._pMetaData != NULL)
  1865. {
  1866. TsFreeMetaData( Exec._pMetaData->QueryCacheInfo() );
  1867. }
  1868. }
  1869. Exec.Reset();
  1870. return fRet;
  1871. }
  1872. BOOL
  1873. HTTP_REQUEST::ExecuteChildCommand(
  1874. IN CHAR * pszCommand,
  1875. IN DWORD dwChildExecFlags
  1876. )
  1877. /*++
  1878. Routine Description:
  1879. Provides ISAPI apps with ability to synchronously execute a shell command
  1880. (useful for SSI functionality #exec cmd=)
  1881. Arguments:
  1882. pszCommand - Command to be executed
  1883. dwChildExecFlags - HSE_EXEC_???? flags
  1884. Return value:
  1885. TRUE if no error, otherwise FALSE
  1886. --*/
  1887. {
  1888. STACK_STR( strCommand, MAX_PATH );
  1889. BOOL fHandled;
  1890. DWORD dwOldFlags = _Exec._dwExecFlags;
  1891. BOOL fRet;
  1892. if ( !strCommand.Copy( pszCommand ) )
  1893. {
  1894. return FALSE;
  1895. }
  1896. //
  1897. // Since there is no URL associated with a shell command, there is no
  1898. // metadata. Just use the HTTP_REQUEST Exec block. But first set its
  1899. // execFlags correctly
  1900. //
  1901. _Exec._dwExecFlags |= ( EXEC_FLAG_CHILD |
  1902. ( dwChildExecFlags &
  1903. ( HSE_EXEC_NO_HEADERS |
  1904. HSE_EXEC_REDIRECT_ONLY ) ) );
  1905. fRet = ProcessCGI( &_Exec,
  1906. NULL,
  1907. NULL,
  1908. &fHandled,
  1909. NULL,
  1910. &strCommand );
  1911. //
  1912. // Restore original _Exec flags
  1913. //
  1914. _Exec._dwExecFlags = dwOldFlags;
  1915. return fRet;
  1916. }
  1917. BOOL
  1918. HTTP_REQUEST::ParseExecute(
  1919. IN OUT EXEC_DESCRIPTOR * pExec,
  1920. IN BOOL fExecChildCGIBGI,
  1921. OUT BOOL * pfVerbExcluded,
  1922. IN PW3_URI_INFO pURIInfo,
  1923. IN enum HTTP_VERB Verb,
  1924. IN CHAR * pszVerb
  1925. )
  1926. /*++
  1927. Routine Description:
  1928. Parse the request now that we know it is an execute.
  1929. Arguments:
  1930. pExec - Execution Descriptor Block
  1931. fExecChildCGIBGI - TRUE if this is a #exec request for a
  1932. CGI or ISAPI from inside an .stm page
  1933. or other isapi app. FALSE in the normal
  1934. case of a top level request to execute
  1935. a gateway of some sort.
  1936. pfVerbExcluded - Is the verb excluded, regardless of extension match
  1937. pURIInfo - URI Blob for this 'request'
  1938. Verb - Verb (enum form) of request
  1939. pszVerb - Verb (string form)
  1940. Returns:
  1941. TRUE on success, FALSE on failure
  1942. --*/
  1943. {
  1944. TCHAR * pchStart = pExec->_pstrURL->QueryStr();
  1945. TCHAR * pchtmp = pchStart;
  1946. TCHAR * pchSlash = NULL;
  1947. INT cchToEnd = 0;
  1948. DWORD cchExt = 0;
  1949. GATEWAY_TYPE GatewayType = GATEWAY_UNKNOWN;
  1950. BOOL fImageInURL = FALSE;
  1951. TCHAR * pch = NULL;
  1952. PVOID pvExtMapInfo;
  1953. BOOL fUseURIInfo = pURIInfo && pURIInfo->pvExtMapInfo;
  1954. DWORD cDots = 0;
  1955. *pfVerbExcluded = FALSE;
  1956. BOOL ExitedHere = FALSE;
  1957. while ( *pchtmp )
  1958. {
  1959. pvExtMapInfo = NULL;
  1960. pchtmp = strchr( pchtmp + 1, '.' );
  1961. //
  1962. // Is this file extension mapped to a script? _GatewayType is
  1963. // set to unknown if a mapping isn't found
  1964. //
  1965. if ( !pExec->_pMetaData->LookupExtMap( pchtmp,
  1966. pExec->NoIsaWildcards(),
  1967. pExec->_pstrGatewayImage,
  1968. pExec->_pGatewayType,
  1969. &cchExt,
  1970. &fImageInURL,
  1971. pfVerbExcluded,
  1972. pExec->_pdwScriptMapFlags,
  1973. pszVerb,
  1974. Verb,
  1975. fUseURIInfo ?
  1976. &(pURIInfo->pvExtMapInfo) :
  1977. &pvExtMapInfo
  1978. ))
  1979. {
  1980. return FALSE;
  1981. }
  1982. GatewayType = *(pExec->_pGatewayType);
  1983. if ( pchtmp == NULL )
  1984. {
  1985. break;
  1986. }
  1987. cDots++;
  1988. if ( GatewayType != GATEWAY_UNKNOWN )
  1989. {
  1990. if ( GatewayType == GATEWAY_NONE )
  1991. {
  1992. _fAnyParams = FALSE;
  1993. return TRUE;
  1994. }
  1995. //
  1996. // If this is a regular CGI script, check for an "nph-"
  1997. //
  1998. if ( GatewayType == GATEWAY_CGI )
  1999. {
  2000. //
  2001. // Walk backwards till we find the '/' that begins
  2002. // this segment
  2003. //
  2004. pch = pchtmp;
  2005. while ( pch >= pchStart && *pch != '/' )
  2006. {
  2007. pch--;
  2008. }
  2009. if ( !_strnicmp( (*pch == '/' ? pch+1 : pch),
  2010. "nph-",
  2011. 4 ))
  2012. {
  2013. pExec->_dwExecFlags |= EXEC_FLAG_CGI_NPH;
  2014. }
  2015. }
  2016. cchToEnd = DIFF(pchtmp - pchStart) + cchExt;
  2017. break;
  2018. }
  2019. else if ( fUseURIInfo )
  2020. {
  2021. ExitedHere = TRUE;
  2022. break;
  2023. }
  2024. }
  2025. // WinNT 379450
  2026. if (!ExitedHere) // with URIInfo the extension map is got from cache
  2027. {
  2028. if (pchtmp)
  2029. {
  2030. TCHAR *pNextDot = strchr(pchtmp+1,'.');
  2031. if (pNextDot && (pchtmp != pNextDot)) // two dot, bad we have to check
  2032. {
  2033. TCHAR * pchStart2 = pExec->_pstrURL->QueryStr();
  2034. DWORD dwLenURL = lstrlen((TCHAR *)pchStart2);
  2035. TCHAR * pCopyUrl = (TCHAR *)LocalAlloc(LPTR,dwLenURL+1);
  2036. if (!pCopyUrl)
  2037. {
  2038. return FALSE;
  2039. };
  2040. lstrcpy(pCopyUrl,pchStart2);
  2041. // terminate string
  2042. pCopyUrl[DIFF(pchtmp - pchStart2)+cchExt] = 0;
  2043. STACK_STR( StrPhys , MAX_PATH );
  2044. // we are not going to enlarge string
  2045. if ( !pExec->_pMetaData->BuildPhysicalPath( pCopyUrl, //shortened via '/' -> '\0'
  2046. &StrPhys) )
  2047. {
  2048. // free memory and fail
  2049. LocalFree(pCopyUrl);
  2050. *(pExec->_pGatewayType) = GATEWAY_MALFORMED;
  2051. return TRUE;
  2052. };
  2053. DWORD dwAttributes = 0xffffffff;
  2054. TSVC_CACHE bogus;
  2055. TS_OPEN_FILE_INFO* pOpenFile = TsCreateFile(bogus,
  2056. StrPhys.QueryStr(),
  2057. 0,
  2058. TS_CACHING_DESIRED );
  2059. if (pOpenFile != NULL)
  2060. {
  2061. dwAttributes = pOpenFile->QueryAttributes();
  2062. TsCloseHandle(bogus, pOpenFile);
  2063. };
  2064. if ( dwAttributes != 0xffffffff )
  2065. {
  2066. if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY )
  2067. {
  2068. LocalFree(pCopyUrl);
  2069. *(pExec->_pGatewayType) = GATEWAY_MALFORMED ;
  2070. return TRUE;
  2071. }
  2072. };
  2073. LocalFree(pCopyUrl);
  2074. }
  2075. }
  2076. }
  2077. //-----------------------------------------------
  2078. //
  2079. // We should update the URI blob with the matched EXT_MAP_ITEM if
  2080. //
  2081. // a) We have a URIInfo to update
  2082. // b) LookupExtMap() found a matching EXT_MAP_ITEM (or EXTMAP_UNKNOWN_PTR)
  2083. // c) We didn't previously have a value for pvExtMapInfo
  2084. // d) If the match occurred on the first dot encountered OR if this
  2085. // is a static file (and thus an EXTMAP_UNKNOWN_PTR)
  2086. //
  2087. if ( !fUseURIInfo &&
  2088. pURIInfo &&
  2089. pvExtMapInfo &&
  2090. ( ( cDots == 1 ) || ( pvExtMapInfo == EXTMAP_UNKNOWN_PTR ) ) )
  2091. {
  2092. InterlockedExchangePointer( &(pURIInfo->pvExtMapInfo), pvExtMapInfo );
  2093. }
  2094. if ( GatewayType & GT_CGI_BGI )
  2095. {
  2096. //
  2097. // If this is a top level request for an executable (not
  2098. // #exec-ing from inside of an .stm or other isapi),
  2099. // and the image was found in the URL, but the vroot
  2100. // doesn't have execute permission set, then this isn't
  2101. // really a gateway and the client is trying to download
  2102. // the file, so let him.
  2103. //
  2104. if (!fExecChildCGIBGI && fImageInURL && !(GetFilePerms() & VROOT_MASK_EXECUTE))
  2105. {
  2106. *(pExec->_pGatewayType) = GATEWAY_NONE;
  2107. _fAnyParams = FALSE;
  2108. return TRUE;
  2109. }
  2110. if (*(pExec->_pdwScriptMapFlags) & MD_SCRIPTMAPFLAG_WILDCARD)
  2111. {
  2112. cchToEnd = pExec->_pstrURL->QueryCB();
  2113. }
  2114. //
  2115. // Save the path info and remove it from the URL. If this is a
  2116. // script by association and there isn't any path info, then
  2117. // copy the base URL as the path info (reflects what the URL
  2118. // would like without an association (i.e., "/foo/bar.idc?a=b"
  2119. // is really "/scripts/httpodbc.dll/foo/bar.idc?a=b"))
  2120. //
  2121. // If the binary image is actually in the URL, then always copy
  2122. // the path info.
  2123. //
  2124. if ( !pExec->_pstrPathInfo->Copy( ( fImageInURL ||
  2125. (*(pchStart + cchToEnd) &&
  2126. QueryW3Instance()->QueryAllowPathInfoForScriptMappings() ))
  2127. ?
  2128. pchStart + cchToEnd :
  2129. pchStart ) )
  2130. {
  2131. return FALSE;
  2132. }
  2133. if ( pExec->_pstrURL->QueryCCH() != (UINT)cchToEnd )
  2134. {
  2135. pExec->_pstrURL->SetLen( cchToEnd );
  2136. if ( !pExec->_pMetaData->BuildPhysicalPath( pExec->_pstrURL->QueryStr(),
  2137. pExec->_pstrPhysicalPath ) )
  2138. {
  2139. return FALSE;
  2140. }
  2141. pExec->_pstrUnmappedPhysicalPath->Reset();
  2142. }
  2143. IF_DEBUG( PARSING )
  2144. {
  2145. DBGPRINTF((DBG_CONTEXT,
  2146. "[OnURL] Possible script \"%s\" with path info \"%s\", parms \"%s\"\n",
  2147. pExec->_pstrURL->QueryStr(),
  2148. pExec->_pstrPathInfo->QueryStr(),
  2149. pExec->_pstrURLParams->QueryStr()));
  2150. }
  2151. }
  2152. else if ( ( QueryDirBrowseFlags() & DIRBROW_LOADDEFAULT ) &&
  2153. ( GetFilePerms() & VROOT_MASK_EXECUTE ) &&
  2154. ( ( QueryVerb() == HTV_GET ) || ( QueryVerb() == HTV_HEAD ) ) )
  2155. {
  2156. //
  2157. // The vroot is EXECUTE but it isn't a CGI or BGI.
  2158. // This could be a directory for which we will later
  2159. // load a default document
  2160. //
  2161. #if 0
  2162. DWORD dwAttributes = GetFileAttributes( _strPhysicalPath.QueryStr() );
  2163. #else
  2164. DWORD dwAttributes = 0xffffffff;
  2165. TSVC_CACHE bogus;
  2166. TS_OPEN_FILE_INFO* pOpenFile =
  2167. TsCreateFile(bogus, _strPhysicalPath.QueryStr(),
  2168. 0, TS_CACHING_DESIRED | TS_NO_ACCESS_CHECK );
  2169. if (pOpenFile != NULL)
  2170. {
  2171. dwAttributes = pOpenFile->QueryAttributes();
  2172. TsCloseHandle(bogus, pOpenFile);
  2173. }
  2174. #endif
  2175. if ( ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
  2176. ( dwAttributes != 0xffffffff ) )
  2177. {
  2178. _fPossibleDefaultExecute = TRUE;
  2179. }
  2180. }
  2181. return TRUE;
  2182. }
  2183. VOID
  2184. HTTP_REQUEST::SetupDAVExecute(
  2185. EXEC_DESCRIPTOR *pExec,
  2186. CHAR *szDavDll
  2187. )
  2188. /*++
  2189. Routine Description:
  2190. Setups up the exec descriptor to call DAV to process the request. Assumes
  2191. it has already been setup for a call to ProcessExecute().
  2192. Arguments:
  2193. pExec - Pointer to the exec descriptor
  2194. Returns
  2195. TRUE on success, FALSE on failure
  2196. --*/
  2197. {
  2198. if (pExec->_pstrGatewayImage->Copy(szDavDll) &&
  2199. pExec->_pstrPathInfo->Copy(pExec->_pstrURL->QueryStr()))
  2200. {
  2201. *pExec->_pGatewayType = GATEWAY_BGI;
  2202. pExec->_dwExecFlags |= EXEC_FLAG_RUNNING_DAV;
  2203. }
  2204. }
  2205. BOOL
  2206. HTTP_REQUEST::ProcessURL(
  2207. BOOL * pfFinished,
  2208. BOOL * pfHandled
  2209. )
  2210. /*++
  2211. Routine Description:
  2212. Finally converts the URL to a physical path, checking for extension mappings
  2213. Arguments:
  2214. pfFinished - Set to TRUE if no further processing is needed and no IOs
  2215. are pending
  2216. pfHandled - If !NULL, set to TRUE if request is handled
  2217. Returns:
  2218. TRUE on success, FALSE on failure
  2219. --*/
  2220. {
  2221. TCHAR chParams;
  2222. BOOL fVerbExcluded;
  2223. BOOL fRet;
  2224. //
  2225. // First check if a redirect is in order. If so, just do it.
  2226. //
  2227. if ( _pMetaData->QueryRedirectionBlob() != NULL )
  2228. {
  2229. BOOL fDone = FALSE;
  2230. HTR_STATE OldState = QueryState();
  2231. SetState( HTR_REDIRECT );
  2232. if ( !ReadEntityBody( &fDone, TRUE, QueryClientContentLength() ) )
  2233. {
  2234. return FALSE;
  2235. }
  2236. if ( fDone )
  2237. {
  2238. SetState( OldState );
  2239. if ( DoRedirect( pfFinished ) )
  2240. {
  2241. if ( pfHandled )
  2242. {
  2243. *pfHandled = TRUE;
  2244. }
  2245. return TRUE;
  2246. }
  2247. }
  2248. else
  2249. {
  2250. if ( pfHandled )
  2251. {
  2252. *pfHandled = TRUE;
  2253. }
  2254. return TRUE;
  2255. }
  2256. }
  2257. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_URL_MAP,
  2258. IsSecurePort() ))
  2259. {
  2260. DWORD dwDenied = SF_DENIED_RESOURCE;
  2261. BOOL fTmp = FALSE;
  2262. _strUnmappedPhysicalPath.Copy( _strPhysicalPath );
  2263. if ( !_strPhysicalPath.Resize( MAX_PATH+sizeof(TCHAR) ))
  2264. {
  2265. return FALSE;
  2266. }
  2267. //
  2268. // If the caller is going to ignore the Finished request flag, supply
  2269. // a value ourselves
  2270. //
  2271. if ( !pfFinished )
  2272. {
  2273. pfFinished = &fTmp;
  2274. }
  2275. fRet = _Filter.NotifyUrlMap( _strURL.QueryStr(),
  2276. _strPhysicalPath.QueryStr(),
  2277. _strPhysicalPath.QuerySize(),
  2278. pfFinished );
  2279. if ( !fRet )
  2280. {
  2281. dwDenied |= SF_DENIED_FILTER;
  2282. if ( GetLastError() == ERROR_ACCESS_DENIED )
  2283. {
  2284. SetDeniedFlags( dwDenied );
  2285. }
  2286. }
  2287. _strPhysicalPath.SetLen( strlen(_strPhysicalPath.QueryStr()) );
  2288. //
  2289. // If the filters didn't change the physical path, zero out the unmapped
  2290. // path we saved before the filter call - this will save us work later
  2291. //
  2292. if ( _strPhysicalPath.QueryCCH() == _strUnmappedPhysicalPath.QueryCCH() &&
  2293. !memcmp( _strPhysicalPath.QueryStr(),
  2294. _strUnmappedPhysicalPath.QueryStr(),
  2295. _strPhysicalPath.QueryCCH() ))
  2296. {
  2297. _strUnmappedPhysicalPath.Reset();
  2298. }
  2299. //
  2300. // If the new mapped physical path does not match what we have in the URI
  2301. // cache entry we need to delete this entry.
  2302. //
  2303. if ( _pURIInfo && strcmp( _pURIInfo->pszName, _strPhysicalPath.QueryStr() ) )
  2304. {
  2305. //
  2306. // We keep a reference to metadata in the HTTP_REQUEST object, so
  2307. // we increment the reference count to metadata, as freeing the URI
  2308. // cache entry will decrement the reference count.
  2309. //
  2310. DBG_ASSERT( _pURIInfo->pMetaData != NULL );
  2311. TsAddRefMetaData( _pURIInfo->pMetaData->QueryCacheInfo() );
  2312. if ( _pURIInfo->bIsCached )
  2313. {
  2314. //
  2315. // This will dereference the URI cache entry and
  2316. // kick it out of the cache.
  2317. //
  2318. TsDeCacheCachedBlob( (PVOID)_pURIInfo );
  2319. // TsCheckInCachedBlob( _pURIInfo );
  2320. }
  2321. else
  2322. {
  2323. TsFree(QueryW3Instance()->GetTsvcCache(), _pURIInfo );
  2324. }
  2325. _pURIInfo = NULL;
  2326. }
  2327. }
  2328. if ( *pfFinished )
  2329. {
  2330. return TRUE;
  2331. }
  2332. #if 0 // This block does nothing in effect. If execute permissions are
  2333. // set, then the first 'if' is false. If they are not set, then the
  2334. // script map code below it will not execute, resulting in a return
  2335. // of TRUE anyway. /SAB
  2336. //
  2337. // If the read bit is set, the execute bit is not set, we recognize the
  2338. // verb, there are no parameters and no ext is allowed on read dir,
  2339. // then don't bother looking at the execute mask
  2340. //
  2341. if ( (_verb != HTV_UNKNOWN) &&
  2342. !(GetFilePerms() & VROOT_MASK_EXECUTE ) &&
  2343. !_fAnyParams &&
  2344. !fCheckExt )
  2345. {
  2346. if (_verb == HTV_GET && (GetFilePerms() & VROOT_MASK_READ) ) {
  2347. return TRUE;
  2348. }
  2349. if (IS_WRITE_VERB(_verb) && (GetFilePerms() & VROOT_MASK_WRITE) ) {
  2350. return TRUE;
  2351. }
  2352. }
  2353. #endif // 0
  2354. //
  2355. // If this is on a virtual root with execute permissions ||
  2356. // this might be an ismap request || should be handled by DAV
  2357. // {
  2358. // Check for a possible .exe, .com, or .dll gateway (CGI or BGI)
  2359. // If none, send to DAV if required.
  2360. // }
  2361. //
  2362. DBG_ASSERT(_GatewayType == GATEWAY_UNKNOWN);
  2363. // Populate an EXECUTION descriptor block and check the scriptmap.
  2364. _Exec._pstrURL = &_strURL;
  2365. _Exec._pstrPhysicalPath = &_strPhysicalPath;
  2366. _Exec._pstrUnmappedPhysicalPath = &_strUnmappedPhysicalPath;
  2367. _Exec._pstrGatewayImage = &_strGatewayImage;
  2368. _Exec._pstrPathInfo = &_strPathInfo;
  2369. _Exec._pstrURLParams = &_strURLParams;
  2370. _Exec._pdwScriptMapFlags = &_dwScriptMapFlags;
  2371. _Exec._pGatewayType = &_GatewayType;
  2372. _Exec._pMetaData = _pMetaData;
  2373. _Exec._pRequest = this;
  2374. _Exec._pPathInfoMetaData = NULL;
  2375. _Exec._pPathInfoURIBlob = NULL;
  2376. _Exec._pAppPathURIBlob = NULL;
  2377. if ( _bProcessingCustomError )
  2378. {
  2379. _Exec._dwExecFlags |= EXEC_FLAG_CUSTOM_ERROR;
  2380. }
  2381. //
  2382. // If we know this is a static file, then we don't have to go thru
  2383. // ParseExecute()
  2384. //
  2385. /*
  2386. // This optimization causes a regression for directories that use
  2387. // asp/isapi as the default page when only execute/script access
  2388. // is set. It should be fairly simple to reimplement, but given the
  2389. // lateness in the release cycle I'm removing it as a safer fix.
  2390. // Bug 379306 - taylorw
  2391. if ( !_pURIInfo ||
  2392. _pURIInfo->pvExtMapInfo != EXTMAP_UNKNOWN_PTR )
  2393. {
  2394. */
  2395. if (!ParseExecute(&_Exec,
  2396. FALSE,
  2397. &fVerbExcluded,
  2398. _pURIInfo,
  2399. QueryVerb(),
  2400. _strMethod.QueryStr()
  2401. ))
  2402. {
  2403. return FALSE;
  2404. }
  2405. /*
  2406. }
  2407. */
  2408. // Did we find a scriptmap entry at all?
  2409. if (_GatewayType != GATEWAY_UNKNOWN &&
  2410. _GatewayType != GATEWAY_NONE &&
  2411. _GatewayType != GATEWAY_MAP)
  2412. {
  2413. // Is it a wildcard?
  2414. if (_dwScriptMapFlags & MD_SCRIPTMAPFLAG_WILDCARD ||
  2415. // or is it a regular scriptmap entry and we allow these?
  2416. !_fDisableScriptmap &&
  2417. !fVerbExcluded &&
  2418. (
  2419. (_dwScriptMapFlags & MD_SCRIPTMAPFLAG_SCRIPT) && IS_ACCESS_ALLOWED(SCRIPT) ||
  2420. IS_ACCESS_ALLOWED(EXECUTE)
  2421. )
  2422. )
  2423. {
  2424. // Call this scriptmap.
  2425. DBG_ASSERT(!fVerbExcluded); // Should not return this.
  2426. return TRUE;
  2427. }
  2428. //
  2429. // If DAV isn't going to handle it, this script doesn't have execute permissions
  2430. //
  2431. if ( !_fSendToDav )
  2432. {
  2433. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  2434. Disconnect( HT_FORBIDDEN, IDS_EXECUTE_ACCESS_DENIED, FALSE, pfFinished );
  2435. if ( pfHandled )
  2436. {
  2437. *pfHandled = TRUE;
  2438. }
  2439. return TRUE;
  2440. }
  2441. else
  2442. {
  2443. _GatewayType = GATEWAY_NONE;
  2444. }
  2445. }
  2446. // If no script map is assigned, and if this is not a verb that IIS should
  2447. // handle, setup for execution by the DAV .dll.
  2448. if (_fSendToDav)
  2449. {
  2450. W3_IIS_SERVICE * pservice = (W3_IIS_SERVICE *) QueryW3Instance()->m_Service;
  2451. if (pservice->FDavDll())
  2452. SetupDAVExecute(&_Exec, pservice->SzDavDllGet());
  2453. }
  2454. return TRUE;
  2455. }
  2456. /*******************************************************************
  2457. NAME: HTTP_REQUEST::OnVersion
  2458. SYNOPSIS: Parses the version from an HTTP request
  2459. ENTRY: pszValue - Pointer to zero terminated string
  2460. RETURNS: TRUE if successful, FALSE if an error occurred
  2461. HISTORY:
  2462. Johnl 24-Aug-1994 Created
  2463. MuraliK 29-Jan-1996 Optimized for 1.1
  2464. ********************************************************************/
  2465. BOOL HTTP_REQUEST::OnVersion( CHAR * pszValue )
  2466. {
  2467. //
  2468. // Did the client specify a version string? If not, assume 0.9
  2469. //
  2470. if ( strncmp( "HTTP/", pszValue, 5 ) == 0 )
  2471. {
  2472. //
  2473. // Move past "HTTP/"
  2474. //
  2475. // Optimize for Version 1.x in this release - 1/15/97 - MuraliK
  2476. if ((pszValue[5] == '1') && (pszValue[6] == '.')) {
  2477. _VersionMajor = 1;
  2478. pszValue += 6;
  2479. } else {
  2480. _VersionMajor = (BYTE ) atol( pszValue + 5);
  2481. pszValue = strchr( pszValue + 5, '.' );
  2482. }
  2483. if ( pszValue != NULL )
  2484. {
  2485. DBG_ASSERT( pszValue[0] == '.');
  2486. if ((pszValue[1] == '1') && (pszValue[2] == '\0')) {
  2487. if ( g_ReplyWith11 ) {
  2488. _VersionMinor = 1;
  2489. }
  2490. else {
  2491. _VersionMinor = 0;
  2492. }
  2493. } else if ((pszValue[1] == '0') && (pszValue[2] == '\0') ) {
  2494. _VersionMinor = 0;
  2495. } else {
  2496. _VersionMinor = (BYTE ) atol( pszValue + 1);
  2497. }
  2498. }
  2499. //
  2500. // If this is an HTTP 1.1 request, make KeepConn the default.
  2501. //
  2502. if ( (_VersionMinor == 1) && (_VersionMajor == 1) ) {
  2503. SetKeepConn( TRUE );
  2504. }
  2505. }
  2506. else
  2507. {
  2508. // Make sure this really is .9, and not garbage.
  2509. if (*pszValue == '\0')
  2510. {
  2511. _VersionMajor = 0;
  2512. _VersionMinor = 9;
  2513. }
  2514. else
  2515. {
  2516. SetLastError( ERROR_INVALID_PARAMETER );
  2517. return FALSE;
  2518. }
  2519. }
  2520. return TRUE;
  2521. } // HTTP_REQUEST::OnVersion()
  2522. /*******************************************************************
  2523. NAME: HTTP_REQUEST::OnAccept
  2524. SYNOPSIS: Adds the MIME type to our accept list
  2525. ENTRY: CHAR * pszValue
  2526. RETURNS: TRUE if successful, FALSE otherwise
  2527. NOTES: Accept fields can look like:
  2528. Accept: text/html
  2529. Accept: image/gif; audio/wav
  2530. Accept: image/jpeg, q=.8, mxb=10000, mxt=5.0; image/gif
  2531. q - Quality (between zero and one)
  2532. mxb - Maximum bytes acceptable
  2533. mxs - Maximum seconds acceptable
  2534. We currently ignore the parameters
  2535. HISTORY:
  2536. Johnl 21-Sep-1994 Created
  2537. ********************************************************************/
  2538. BOOL HTTP_REQUEST::OnAccept( CHAR * pszValue )
  2539. {
  2540. //
  2541. // Keep an eye out for "*/*". If it's sent then we
  2542. // don't have to search the list for acceptable client
  2543. // types later on. Note it won't catch the case if the "*" occurs
  2544. // after the first item in the list.
  2545. //
  2546. if ( *pszValue == '*'
  2547. || strstr( pszValue, TEXT("*/*") ) )
  2548. {
  2549. _fAcceptsAll = TRUE;
  2550. }
  2551. return TRUE;
  2552. }
  2553. /*******************************************************************
  2554. NAME: HTTP_REQUEST::DoesClientAccept
  2555. SYNOPSIS: Searches the client accept list for the specified
  2556. MIME type
  2557. ENTRY: str - MIME type to search for
  2558. RETURNS: TRUE if found, FALSE if not found
  2559. HISTORY:
  2560. Johnl 22-Sep-1994 Created
  2561. ********************************************************************/
  2562. BOOL HTTP_REQUEST::DoesClientAccept( PCSTR pstr )
  2563. {
  2564. TCHAR * pchSlash;
  2565. TCHAR * pchType;
  2566. INT cchToSlash;
  2567. //
  2568. // If the client indicated "*/*" in their accept list, then
  2569. // we don't need to check
  2570. //
  2571. if ( IsAcceptAllSet() )
  2572. return TRUE;
  2573. LPSTR pszAcc = (LPSTR ) _HeaderList.FastMapQueryStrValue(HM_ACC);
  2574. //
  2575. // If no accept headers were passed, then assume client
  2576. // accepts "text/plain" and "text/html"
  2577. //
  2578. if ( *pszAcc == '\0' )
  2579. {
  2580. //
  2581. // Accept everything if no header was sent.
  2582. //
  2583. return TRUE;
  2584. }
  2585. //
  2586. // Find out where the slash is so we can do a prefix compare
  2587. //
  2588. pchSlash = _tcschr( pstr, TEXT('/') );
  2589. if ( !pchSlash )
  2590. {
  2591. DBGPRINTF((DBG_CONTEXT,
  2592. "[DoesClientAccept] Bad accept type - \"%s\"",
  2593. pstr ));
  2594. return FALSE;
  2595. }
  2596. cchToSlash = DIFF(pchSlash - pstr);
  2597. //
  2598. // Scan through the list for entries that match up to the slash
  2599. //
  2600. INET_PARSER Parser( pszAcc );
  2601. Parser.SetListMode( TRUE );
  2602. pchType = Parser.QueryToken();
  2603. while ( *pchType )
  2604. {
  2605. if ( !::_tcscmp( TEXT("*/*"), pchType ) ||
  2606. !::_tcscmp( TEXT("*"), pchType ))
  2607. {
  2608. return TRUE;
  2609. }
  2610. if ( !_tcsnicmp( pstr,
  2611. pchType,
  2612. cchToSlash ))
  2613. {
  2614. //
  2615. // We matched to the slash. Is the second part a '*'
  2616. // or a real match?
  2617. //
  2618. if ( *(pchType + cchToSlash + 1) == TEXT('*') ||
  2619. !_tcsicmp( pstr + cchToSlash + 1,
  2620. pchType + cchToSlash + 1 ))
  2621. {
  2622. return TRUE;
  2623. }
  2624. }
  2625. pchType = Parser.NextItem();
  2626. }
  2627. IF_DEBUG( PARSING )
  2628. {
  2629. DBGPRINTF((DBG_CONTEXT,
  2630. "[DoesClientAccept] Client doesn't accept %s\n",
  2631. pstr ));
  2632. }
  2633. return FALSE;
  2634. }
  2635. /*******************************************************************
  2636. NAME: HTTP_REQUEST::OnContentType
  2637. SYNOPSIS: Saves the content type
  2638. ENTRY: pszValue - Pointer to zero terminated string
  2639. RETURNS: TRUE if successful, FALSE on error
  2640. NOTES: Client's will generally specify this only for gateway data
  2641. HISTORY:
  2642. Johnl 10-Oct-1994 Created
  2643. ********************************************************************/
  2644. BOOL HTTP_REQUEST::OnContentType( CHAR * pszValue )
  2645. {
  2646. return _strContentType.Copy( pszValue );
  2647. }
  2648. BOOL
  2649. HTTP_REQUEST::OnConnection(
  2650. CHAR * pszValue
  2651. )
  2652. /*++
  2653. Routine Description:
  2654. Looks to see if this connection is a keep-alive connection
  2655. Arguments:
  2656. pszValue - Pointer to zero terminated string
  2657. --*/
  2658. {
  2659. //
  2660. // Length should be greater than 9
  2661. //
  2662. if ( (*pszValue == 'K') || (*pszValue == 'k') ) {
  2663. if ( _stricmp( pszValue+1, "eep-Alive") == 0 ) {
  2664. SetKeepConn( TRUE );
  2665. return(TRUE);
  2666. }
  2667. }
  2668. else if ( (*pszValue == 'C') || (*pszValue == 'c') ) {
  2669. if ( _stricmp( pszValue+1, "lose") == 0 ) {
  2670. SetKeepConn( FALSE );
  2671. return TRUE;
  2672. }
  2673. }
  2674. //
  2675. // Do it the long way
  2676. //
  2677. {
  2678. INET_PARSER Parser( pszValue );
  2679. Parser.SetListMode( TRUE );
  2680. while ( *Parser.QueryToken() )
  2681. {
  2682. if ( !_stricmp( "Keep-Alive", Parser.QueryToken() ))
  2683. {
  2684. SetKeepConn( TRUE );
  2685. }
  2686. else
  2687. {
  2688. if ( !_stricmp( "Close", Parser.QueryToken() ))
  2689. {
  2690. SetKeepConn( FALSE );
  2691. }
  2692. }
  2693. Parser.NextItem();
  2694. }
  2695. }
  2696. return TRUE;
  2697. }
  2698. BOOL
  2699. HTTP_REQUEST::OnTransferEncoding(
  2700. CHAR * pszValue
  2701. )
  2702. /*++
  2703. Routine Description:
  2704. Handles the Transfer-Encoding header.
  2705. Arguments:
  2706. pszValue - Pointer to zero terminated string
  2707. --*/
  2708. {
  2709. INET_PARSER Parser( pszValue );
  2710. Parser.SetListMode( TRUE );
  2711. while ( *Parser.QueryToken() )
  2712. {
  2713. if ( !_stricmp( "Chunked", Parser.QueryToken() ))
  2714. {
  2715. SetChunked( );
  2716. _ChunkState = READ_CHUNK_SIZE;
  2717. _dwChunkSize = -1;
  2718. _cbChunkHeader = 0;
  2719. _cbChunkBytesRead = 0;
  2720. _cbContentLength = 0xffffffff;
  2721. _fHaveContentLength = TRUE;
  2722. return TRUE;
  2723. }
  2724. SetLastError(ERROR_NOT_SUPPORTED);
  2725. return FALSE;
  2726. }
  2727. return TRUE;
  2728. }
  2729. BOOL
  2730. HTTP_REQUEST::OnLockToken(
  2731. CHAR * pszValue
  2732. )
  2733. /*++
  2734. Routine Description:
  2735. Handles the LockToken header.
  2736. Arguments:
  2737. pszValue - Pointer to zero terminated string
  2738. --*/
  2739. {
  2740. _fSendToDav = TRUE; // Send all requests with lock tokens to DAV
  2741. return TRUE;
  2742. }
  2743. BOOL
  2744. HTTP_REQUEST::OnTranslate(
  2745. CHAR * pszValue
  2746. )
  2747. /*++
  2748. Routine Description:
  2749. Handles the Translate header.
  2750. Arguments:
  2751. pszValue - Pointer to zero terminated string
  2752. --*/
  2753. {
  2754. if (*pszValue == 'F' || *pszValue == 'f')
  2755. {
  2756. _fSendToDav = TRUE;
  2757. _fDisableScriptmap = TRUE;
  2758. }
  2759. return TRUE;
  2760. }
  2761. BOOL
  2762. HTTP_REQUEST::OnIf(
  2763. CHAR * pszValue
  2764. )
  2765. /*++
  2766. Routine Description:
  2767. Handles the LockToken header.
  2768. Arguments:
  2769. pszValue - Pointer to zero terminated string
  2770. --*/
  2771. {
  2772. _fSendToDav = TRUE; // Send all requests with lock tokens to DAV
  2773. return TRUE;
  2774. }
  2775. BOOL
  2776. HTTP_REQUEST::CacheUri(
  2777. PW3_SERVER_INSTANCE pInstance,
  2778. PW3_URI_INFO* ppURIInfo,
  2779. PW3_METADATA pMetaData,
  2780. LPCSTR pszURL,
  2781. ULONG cchURL,
  2782. STR* pstrPhysicalPath,
  2783. STR* pstrUnmappedPhysicalPath
  2784. )
  2785. /*++
  2786. Routine Description:
  2787. Cache a URI info structure
  2788. Arguments:
  2789. pInstance - instance for this request
  2790. ppURIInfo - updated with ptr to URI info if success
  2791. pMetaData - metadata to associate with URI info
  2792. pszURL - URL for which to cache URI info
  2793. pstrPhysicalPath - physical path associated with pszURL
  2794. pstrUnmappedPhysicalPath - unmapped physical path ( before calling filters )
  2795. associated with pszURL. Can be empty if filter not called.
  2796. --*/
  2797. {
  2798. DWORD dwStringSize;
  2799. PW3_URI_INFO pURIInfo;
  2800. // The URI information is NULL. Create a new URI blob, and try to open
  2801. // the file.
  2802. if (!TsAllocateEx(pInstance->GetTsvcCache(),
  2803. sizeof(W3_URI_INFO),
  2804. (PVOID *)&pURIInfo,
  2805. DisposeOpenURIFileInfo))
  2806. {
  2807. // Not enough memory to create the needed strucure, so fail the
  2808. // request.
  2809. return FALSE;
  2810. }
  2811. #if 0
  2812. pURIInfo->hFileEvent = IIS_CREATE_EVENT(
  2813. "W3_URI_INFO::hFileEvent",
  2814. pURIInfo,
  2815. TRUE,
  2816. FALSE
  2817. );
  2818. if ( pURIInfo->hFileEvent == NULL ) {
  2819. TsFree( pInstance->GetTsvcCache(), pURIInfo );
  2820. return FALSE;
  2821. }
  2822. #endif //!oplock
  2823. // pURIInfo->bFileInfoValid = FALSE;
  2824. pURIInfo->dwFileOpenError = ERROR_FILE_NOT_FOUND;
  2825. pURIInfo->pOpenFileInfo = NULL;
  2826. pURIInfo->bIsCached = TRUE;
  2827. pURIInfo->pMetaData = pMetaData;
  2828. pURIInfo->bInProcOnly = FALSE;
  2829. pURIInfo->bUseAppPathChecked = FALSE;
  2830. // Copy the name of the physical path, so we have it for change
  2831. // notifies.
  2832. dwStringSize = pstrPhysicalPath->QueryCCH() + 1;
  2833. pURIInfo->cchName = dwStringSize - 1;
  2834. pURIInfo->pszName = (PCHAR)TCP_ALLOC(dwStringSize * sizeof(CHAR));
  2835. if (pURIInfo->pszName == NULL)
  2836. {
  2837. // Not enough memory, fail.
  2838. TsFree(pInstance->GetTsvcCache(), pURIInfo);
  2839. return FALSE;
  2840. }
  2841. memcpy( pURIInfo->pszName, pstrPhysicalPath->QueryStr(), dwStringSize);
  2842. //
  2843. // Copy unmapped physical path ( before filter notification )
  2844. // if different from physical path
  2845. //
  2846. if ( !pstrUnmappedPhysicalPath->IsEmpty() &&
  2847. strcmp( pstrPhysicalPath->QueryStr(), pstrUnmappedPhysicalPath->QueryStr() ) )
  2848. {
  2849. dwStringSize = pstrUnmappedPhysicalPath->QueryCCH() + 1;
  2850. pURIInfo->pszUnmappedName = (PCHAR)TCP_ALLOC(dwStringSize * sizeof(CHAR));
  2851. if ( pURIInfo->pszUnmappedName == NULL)
  2852. {
  2853. // Not enough memory, fail.
  2854. TsFree(pInstance->GetTsvcCache(), pURIInfo);
  2855. return FALSE;
  2856. }
  2857. memcpy( pURIInfo->pszUnmappedName, pstrUnmappedPhysicalPath->QueryStr(), dwStringSize);
  2858. }
  2859. else
  2860. {
  2861. pURIInfo->pszUnmappedName = NULL;
  2862. }
  2863. //
  2864. // Cache the extension map info for this URI
  2865. //
  2866. pURIInfo->pvExtMapInfo = NULL;
  2867. // It's set up, so add it to the cache.
  2868. if ( !TsCacheDirectoryBlob( pInstance->GetTsvcCache(),
  2869. pszURL,
  2870. cchURL,
  2871. RESERVED_DEMUX_URI_INFO,
  2872. pURIInfo,
  2873. TRUE))
  2874. {
  2875. pURIInfo->bIsCached = FALSE;
  2876. }
  2877. *ppURIInfo = pURIInfo;
  2878. return TRUE;
  2879. }
  2880. //
  2881. // Verb worker methods
  2882. //
  2883. BOOL
  2884. HTTP_REQUEST::DoUnknown(
  2885. BOOL * pfFinished
  2886. )
  2887. {
  2888. DBGPRINTF((DBG_CONTEXT,
  2889. "OnDoUnknown - Unknown method - %s\n",
  2890. _strMethod.QueryStr()));
  2891. if (!_stricmp("POST", _strMethod.QueryStr()))
  2892. {
  2893. SetState( HTR_DONE, HT_METHOD_NOT_ALLOWED, ERROR_INVALID_FUNCTION );
  2894. Disconnect( HT_METHOD_NOT_ALLOWED, NO_ERROR, FALSE, pfFinished );
  2895. }
  2896. else
  2897. {
  2898. SetState( HTR_DONE, HT_NOT_SUPPORTED, ERROR_NOT_SUPPORTED );
  2899. Disconnect( HT_NOT_SUPPORTED, IDS_METHOD_NOT_SUPPORTED, FALSE, pfFinished );
  2900. }
  2901. return TRUE;
  2902. }
  2903. BOOL
  2904. HTTP_REQUEST::LookupVirtualRoot(
  2905. OUT STR * pstrPath,
  2906. IN const CHAR * pszURL,
  2907. IN ULONG cchURL,
  2908. OUT DWORD * pcchDirRoot,
  2909. OUT DWORD * pcchVRoot,
  2910. OUT DWORD * pdwMask,
  2911. OUT BOOL * pfFinished,
  2912. IN BOOL fGetAcl,
  2913. OUT PW3_METADATA* ppMetaData,
  2914. OUT PW3_URI_INFO* ppURIBlob
  2915. )
  2916. /*++
  2917. Routine Description:
  2918. Looks up the virtual root to find the physical drive mapping. If an
  2919. Accept-Language header was sent by the client, we look for a virtual
  2920. root prefixed by the language tag
  2921. Arguments:
  2922. pstrPath - Receives physical drive path
  2923. pszURL - URL to look for
  2924. pcchDirRoot - Number of characters in the found physical path
  2925. pcchVRoot - Number of characters in the found virtual root
  2926. pdwMask - Access mask for the specified URL
  2927. pfFinished - Set to TRUE if a filter indicated the request should end
  2928. fGetAcl - TRUE to retrieve ACL for this virtual root
  2929. ppMetaData - Pointer to metadata object for URL. If this parameter is
  2930. set (!= NULL), then MetaData/URIBlob is not freed/checked in
  2931. ppURIBlob - Pointer to URIBlob for URL. If this parameter is set
  2932. (!= NULL), then MetaData/URIBlob is not freed/checked in
  2933. --*/
  2934. {
  2935. DWORD cbPath;
  2936. BOOL fRet = TRUE;
  2937. DWORD dwDenied = SF_DENIED_RESOURCE;
  2938. BOOL fAnyFilters = FALSE;
  2939. PW3_URI_INFO pURIBlob = NULL;
  2940. PW3_METADATA pMetaData = NULL;
  2941. BOOL fMustCache = FALSE;
  2942. STR strUnmappedPhysicalPath;
  2943. if ( !TsCheckOutCachedBlob( QueryW3Instance()->GetTsvcCache(),
  2944. pszURL,
  2945. cchURL,
  2946. RESERVED_DEMUX_URI_INFO,
  2947. (VOID **) &pURIBlob,
  2948. NULL ))
  2949. {
  2950. // We don't have URI info available for this yet. We need to read
  2951. // the metadata for this URI and format it into a usable form, and
  2952. // add it to the cache
  2953. if ( !ReadMetaData( (LPSTR)pszURL,
  2954. pstrPath,
  2955. &pMetaData ) )
  2956. {
  2957. fRet = FALSE;
  2958. goto Exit;
  2959. }
  2960. fMustCache = TRUE;
  2961. }
  2962. else
  2963. {
  2964. if ( pURIBlob->pszUnmappedName )
  2965. {
  2966. fRet = pstrPath->Copy( pURIBlob->pszUnmappedName );
  2967. }
  2968. else
  2969. {
  2970. fRet = pstrPath->Copy( pURIBlob->pszName, pURIBlob->cchName );
  2971. }
  2972. pMetaData = pURIBlob->pMetaData;
  2973. }
  2974. if ( pMetaData->QueryVrError() )
  2975. {
  2976. SetLastError( pMetaData->QueryVrError() );
  2977. fRet = FALSE;
  2978. goto Exit;
  2979. }
  2980. if ( pcchVRoot )
  2981. {
  2982. *pcchVRoot = pMetaData->QueryVrLen();
  2983. }
  2984. if ( pdwMask )
  2985. {
  2986. *pdwMask = pMetaData->QueryAccessPerms();
  2987. }
  2988. if ( fRet && _Filter.IsNotificationNeeded( SF_NOTIFY_URL_MAP,
  2989. IsSecurePort() ))
  2990. {
  2991. BOOL fTmp;
  2992. fAnyFilters = TRUE;
  2993. if ( !strUnmappedPhysicalPath.Copy( *pstrPath ) ||
  2994. !pstrPath->Resize( MAX_PATH + sizeof(TCHAR) ) )
  2995. {
  2996. fRet = FALSE;
  2997. goto Exit;
  2998. }
  2999. //
  3000. // If the caller is going to ignore the Finished request flag, supply
  3001. // a value ourselves
  3002. //
  3003. if ( !pfFinished )
  3004. {
  3005. pfFinished = &fTmp;
  3006. }
  3007. fRet = _Filter.NotifyUrlMap( pszURL,
  3008. pstrPath->QueryStr(),
  3009. pstrPath->QuerySize(),
  3010. pfFinished );
  3011. if ( !fRet )
  3012. {
  3013. dwDenied |= SF_DENIED_FILTER;
  3014. }
  3015. //
  3016. // Reset length because filter may have resized the string
  3017. //
  3018. pstrPath->SetLen( strlen( pstrPath->QueryStr() ) );
  3019. }
  3020. //
  3021. // Check for short name as they break metabase equivalency
  3022. //
  3023. if ( fRet &&
  3024. memchr( pstrPath->QueryStr(), '~', pstrPath->QueryCB() ))
  3025. {
  3026. BOOL fShort;
  3027. DWORD err;
  3028. if ( err = CheckIfShortFileName( (UCHAR *) pstrPath->QueryStr(),
  3029. pMetaData->QueryVrAccessToken(),
  3030. &fShort ))
  3031. {
  3032. fRet = FALSE;
  3033. goto Exit;
  3034. }
  3035. if ( fShort )
  3036. {
  3037. fRet = FALSE;
  3038. SetLastError( ERROR_FILE_NOT_FOUND );
  3039. goto Exit;
  3040. }
  3041. }
  3042. if ( !fRet && fAnyFilters && GetLastError() == ERROR_ACCESS_DENIED )
  3043. {
  3044. SetDeniedFlags( dwDenied );
  3045. }
  3046. if ( pcchDirRoot )
  3047. {
  3048. *pcchDirRoot = pMetaData->QueryVrPath()->QueryCB();
  3049. }
  3050. Exit:
  3051. if ( fRet && fMustCache && !CacheUri( QueryW3Instance(),
  3052. &pURIBlob,
  3053. pMetaData,
  3054. pszURL,
  3055. cchURL,
  3056. pstrPath,
  3057. &strUnmappedPhysicalPath ) )
  3058. {
  3059. fRet = FALSE;
  3060. }
  3061. //
  3062. // A caller will set ppURIBlob and ppMetaData when it wants to use the
  3063. // URI cache for the URL, but takes the reponsibility of freeing the
  3064. // URIBlob/Metadata after it is done using the URL's metadata object.
  3065. //
  3066. if ( ppURIBlob )
  3067. {
  3068. *ppURIBlob = pURIBlob;
  3069. }
  3070. if ( ppMetaData )
  3071. {
  3072. *ppMetaData = pMetaData;
  3073. }
  3074. if ( ppMetaData || ppURIBlob )
  3075. {
  3076. return fRet;
  3077. }
  3078. if ( pURIBlob != NULL )
  3079. {
  3080. if (pURIBlob->bIsCached)
  3081. {
  3082. TsCheckInCachedBlob( pURIBlob );
  3083. }
  3084. else
  3085. {
  3086. TsFree(QueryW3Instance()->GetTsvcCache(), pURIBlob );
  3087. }
  3088. }
  3089. else
  3090. {
  3091. if ( pMetaData != NULL)
  3092. {
  3093. TsFreeMetaData( pMetaData->QueryCacheInfo() );
  3094. }
  3095. }
  3096. return fRet;
  3097. }
  3098. /*******************************************************************
  3099. NAME: HTTP_REQUEST::ReprocessURL
  3100. SYNOPSIS: Called when a map file or gateway has redirected us
  3101. to a different URL. An async completion will be posted
  3102. if TRUE is returned.
  3103. ENTRY: pchURL - URL we've been redirected to
  3104. htverb - New verb to use (or unknown to leave as is)
  3105. RETURNS: TRUE if successful, FALSE otherwise
  3106. HISTORY:
  3107. Johnl 04-Oct-1994 Created
  3108. ********************************************************************/
  3109. BOOL HTTP_REQUEST::ReprocessURL( TCHAR * pchURL,
  3110. enum HTTP_VERB htverb )
  3111. {
  3112. BOOL fFinished = FALSE;
  3113. BOOL fAcceptReneg = FALSE;
  3114. BOOL fHandled = FALSE;
  3115. //
  3116. // Reset the gateway type
  3117. //
  3118. _GatewayType = GATEWAY_UNKNOWN;
  3119. _strGatewayImage.Reset();
  3120. _dwScriptMapFlags = 0;
  3121. _fPossibleDefaultExecute = FALSE;
  3122. // Need to send a Content-Location header, unless the caller
  3123. // doesn't want us to.
  3124. ToggleSendCL();
  3125. switch ( htverb )
  3126. {
  3127. case HTV_GET:
  3128. _verb = HTV_GET;
  3129. _pmfnVerb = DoGet;
  3130. break;
  3131. case HTV_HEAD:
  3132. _verb = HTV_HEAD;
  3133. _pmfnVerb = DoGet;
  3134. break;
  3135. case HTV_TRACE:
  3136. _verb = HTV_TRACE;
  3137. _pmfnVerb = DoTrace;
  3138. break;
  3139. case HTV_TRACECK:
  3140. _verb = HTV_TRACECK;
  3141. _pmfnVerb = DoTraceCk;
  3142. break;
  3143. case HTV_POST:
  3144. case HTV_UNKNOWN:
  3145. break;
  3146. default:
  3147. DBG_ASSERT( !"[ReprocessURL] Unknown verb type" );
  3148. SetLastError( ERROR_INVALID_PARAMETER );
  3149. return FALSE;
  3150. }
  3151. ReleaseCacheInfo();
  3152. SetState( HTR_DOVERB );
  3153. _acIpAccess = AC_NOT_CHECKED;
  3154. if ( !OnURL( pchURL ) ||
  3155. !ProcessURL( &fFinished, &fHandled ) )
  3156. {
  3157. return FALSE;
  3158. }
  3159. //
  3160. // If ProcessUrl() handled the request, but we are not finished yet, then
  3161. // ProcessUrl() must have asynchronously send a response
  3162. //
  3163. if ( !fFinished && fHandled )
  3164. {
  3165. return TRUE;
  3166. }
  3167. //
  3168. // <Begin explanantion of client cert renegotiation voodoo>
  3169. // Check if we need to request a client cert. RequestRenegotiate() will call down
  3170. // to sspifilt to send the necessary SSPI/SSL blob to start the renegotiation
  3171. //
  3172. if ( QueryState() != HTR_CERT_RENEGOTIATE )
  3173. {
  3174. if ( !RequestRenegotiate( &fAcceptReneg ) )
  3175. {
  3176. if ( GetLastError() == SEC_E_INCOMPLETE_MESSAGE )
  3177. {
  3178. fAcceptReneg = FALSE;
  3179. }
  3180. else
  3181. {
  3182. return FALSE;
  3183. }
  3184. }
  3185. }
  3186. //
  3187. // If renegotiation was requested/accepted, begin reading data. We issue an async read
  3188. // for the blobs from the client [triggered by the call to RequestRenegotiate above] and
  3189. // back out all the way [to CLIENT_CONN::DoWork] to wait for the async completion to come
  3190. // in with the blob from the client. From there, HandleCertRenegotiation() will take
  3191. // care of the rest of the renegotiation.
  3192. // </End explanation of client cert renegotiation voodoo>
  3193. //
  3194. if ( fAcceptReneg )
  3195. {
  3196. //
  3197. // _cbOldData is the number of bytes of the client request that have already
  3198. // been seen by the READ_RAW filter(s)
  3199. //
  3200. _cbOldData = _cbClientRequest + _cbEntityBody;
  3201. DWORD cbNextRead = CERT_RENEGO_READ_SIZE;
  3202. if ( !_bufClientRequest.Resize( _cbOldData + cbNextRead ))
  3203. {
  3204. return FALSE;
  3205. }
  3206. if ( !ReadFile( (BYTE *) _bufClientRequest.QueryPtr() + _cbOldData,
  3207. cbNextRead,
  3208. NULL,
  3209. IO_FLAG_ASYNC|IO_FLAG_NO_FILTER ))
  3210. {
  3211. return FALSE;
  3212. }
  3213. return TRUE;
  3214. }
  3215. //
  3216. // If need to check IP access, do so now
  3217. //
  3218. if ( !fFinished )
  3219. {
  3220. //
  3221. // Check to see if encryption is required before we do any processing
  3222. //
  3223. if ( ( GetFilePerms() & VROOT_MASK_SSL )
  3224. && !IsSecurePort() )
  3225. {
  3226. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  3227. Disconnect( HT_FORBIDDEN, IDS_SSL_REQUIRED, FALSE, &fFinished );
  3228. goto Exit;
  3229. }
  3230. //
  3231. // Check if encryption key size should be at least 128 bits
  3232. //
  3233. if ( ( GetFilePerms() & VROOT_MASK_SSL128 ) )
  3234. {
  3235. DWORD dwKeySize;
  3236. BOOL fNoCert;
  3237. if ( !_tcpauth.QueryEncryptionKeySize(&dwKeySize, &fNoCert) || (dwKeySize < 128) )
  3238. {
  3239. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  3240. Disconnect( HT_FORBIDDEN, IDS_SSL128_REQUIRED, FALSE, &fFinished );
  3241. goto Exit;
  3242. }
  3243. }
  3244. if ( !IsIpDnsAccessCheckPresent() )
  3245. {
  3246. _acIpAccess = AC_IN_GRANT_LIST;
  3247. }
  3248. else if ( _acIpAccess == AC_NOT_CHECKED )
  3249. {
  3250. _acIpAccess = QueryClientConn()->CheckIpAccess( &_fNeedDnsCheck );
  3251. if ( (_acIpAccess == AC_IN_DENY_LIST) ||
  3252. ((_acIpAccess == AC_NOT_IN_GRANT_LIST) && !_fNeedDnsCheck) )
  3253. {
  3254. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  3255. Disconnect( HT_FORBIDDEN, IDS_ADDR_REJECT, FALSE, &fFinished );
  3256. goto Exit;
  3257. }
  3258. if ( _fNeedDnsCheck && !QueryClientConn()->IsDnsResolved() )
  3259. {
  3260. BOOL fSync;
  3261. LPSTR pDns;
  3262. AC_RESULT acDnsAccess;
  3263. if ( !QueryClientConn()->QueryDnsName( &fSync,
  3264. (ADDRCHECKFUNCEX)NULL,
  3265. (ADDRCHECKARG)QueryClientConn(),
  3266. &pDns ) )
  3267. {
  3268. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  3269. Disconnect( HT_FORBIDDEN, IDS_ADDR_REJECT, FALSE, &fFinished );
  3270. goto Exit;
  3271. }
  3272. acDnsAccess = QueryClientConn()->CheckDnsAccess();
  3273. _fNeedDnsCheck = FALSE;
  3274. if ( acDnsAccess == AC_IN_DENY_LIST ||
  3275. acDnsAccess == AC_NOT_IN_GRANT_LIST ||
  3276. (_acIpAccess == AC_NOT_IN_GRANT_LIST && acDnsAccess != AC_IN_GRANT_LIST) )
  3277. {
  3278. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  3279. Disconnect( HT_FORBIDDEN, IDS_ADDR_REJECT, FALSE, &fFinished );
  3280. goto Exit;
  3281. }
  3282. }
  3283. }
  3284. }
  3285. CheckValidAuth();
  3286. if ( !IsLoggedOn() && !LogonUser( &fFinished ) )
  3287. {
  3288. SetLastError(ERROR_ACCESS_DENIED);
  3289. return FALSE;
  3290. }
  3291. if ( !fFinished )
  3292. {
  3293. if ( !DoWork( &fFinished ))
  3294. {
  3295. return FALSE;
  3296. }
  3297. }
  3298. Exit:
  3299. //
  3300. // If no further processing is needed, set our state to done and post
  3301. // an async completion. We do this as the caller expects an async
  3302. // completion to clean things up
  3303. //
  3304. if ( fFinished )
  3305. {
  3306. DBG_ASSERT( QueryLogHttpResponse() != HT_DONT_LOG );
  3307. SetState( HTR_DONE, QueryLogHttpResponse(), QueryLogWinError() );
  3308. return PostCompletionStatus( 0 );
  3309. }
  3310. return TRUE;
  3311. }
  3312. /*******************************************************************
  3313. NAME: HTTP_REQUEST::GetInfo
  3314. SYNOPSIS: Pulls out various bits of information from this request.
  3315. ENTRY: pszValName - Value to retrieve
  3316. pstr - Receives information in a string format
  3317. pfFound - Option, Set to TRUE if a value was found, FALSE
  3318. otherwise
  3319. NOTES:
  3320. HISTORY:
  3321. Johnl 25-Sep-1994 Created
  3322. MuraliK 3-July-1996 Rewrote for efficiency - used switch-case
  3323. MuraliK 21-Nov-1996 Rewrote again for efficiency
  3324. - use sub-function pointers
  3325. ********************************************************************/
  3326. BOOL
  3327. HTTP_REQUEST::GetInfo(
  3328. const TCHAR * pszValName,
  3329. STR * pstr,
  3330. BOOL * pfFound
  3331. )
  3332. {
  3333. if ( !pszValName )
  3334. {
  3335. SetLastError( ERROR_INVALID_PARAMETER );
  3336. return FALSE;
  3337. }
  3338. if ( pfFound )
  3339. {
  3340. *pfFound = TRUE;
  3341. }
  3342. //
  3343. // terminate the string
  3344. //
  3345. pstr->Reset();
  3346. if ( !strcmp( "ALL_HTTP", pszValName ))
  3347. return BuildCGIHeaderListInSTR( pstr, &_HeaderList );
  3348. //
  3349. // Use the GetInfoForName() function to generate the required value
  3350. // All callers of GetInfo() should be directly calling GetInfoForName()
  3351. // for efficiency sake
  3352. //
  3353. BOOL fRet;
  3354. DWORD cb = pstr->QuerySize();
  3355. fRet = GetInfoForName( pszValName, pstr->QueryStr(), &cb);
  3356. if ( !fRet) {
  3357. switch ( GetLastError()) {
  3358. case ERROR_INSUFFICIENT_BUFFER:
  3359. DBG_ASSERT( cb > pstr->QuerySize());
  3360. if ( !pstr->Resize( cb + 1)) {
  3361. return ( FALSE);
  3362. }
  3363. // Try to get the value again.
  3364. fRet = GetInfoForName( pszValName, pstr->QueryStr(), &cb);
  3365. if ( fRet) {
  3366. pstr->SetLen( strlen( pstr->QueryStr()));
  3367. } else {
  3368. DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  3369. }
  3370. break;
  3371. case ERROR_INVALID_PARAMETER:
  3372. case ERROR_INVALID_INDEX:
  3373. if ( pfFound ) {
  3374. *pfFound = FALSE;
  3375. }
  3376. break;
  3377. default:
  3378. // there is a failure. Return the error to caller
  3379. break;
  3380. } // switch()
  3381. } else {
  3382. // Set the appropriate length
  3383. pstr->SetLen( strlen( pstr->QueryStr()));
  3384. }
  3385. return (fRet);
  3386. } // HTTP_REQUEST::GetInfo()
  3387. BOOL
  3388. BuildCGIHeaderListInSTR( STR * pstr,
  3389. HTTP_HEADERS * pHeaderList
  3390. )
  3391. /*++
  3392. Routine Description:
  3393. Builds a list of all client passed headers in the form of
  3394. //
  3395. // Builds a list of all client HTTP headers in the form of:
  3396. //
  3397. // HTTP_<up-case header>: <field>\n
  3398. // HTTP_<up-case header>: <field>\n
  3399. // ...
  3400. //
  3401. Arguments:
  3402. pstr - Receives full list
  3403. pHeaderList - List of headers
  3404. --*/
  3405. {
  3406. CHAR ach[MAX_HEADER_LENGTH + CGI_HEADER_PREFIX_CCH + 1];
  3407. CHAR * pch;
  3408. DWORD i;
  3409. HH_ITERATOR hhi;
  3410. NAME_VALUE_PAIR * pnp = NULL;
  3411. CopyMemory( ach, CGI_HEADER_PREFIX_SZ, CGI_HEADER_PREFIX_CCH );
  3412. pHeaderList->InitIterator( &hhi);
  3413. while (pHeaderList->NextPair( &hhi, &pnp)) {
  3414. if ( pnp->cchName + CGI_HEADER_PREFIX_CCH > sizeof( ach)) {
  3415. continue;
  3416. }
  3417. //
  3418. // Ignore some headers like "method", "url" and "version" -
  3419. // ones without the ':' at the end
  3420. //
  3421. if ( pnp->pchName[pnp->cchName - 1] != ':' )
  3422. {
  3423. continue;
  3424. }
  3425. //
  3426. // Convert the destination to upper and replace all '-' with '_'
  3427. //
  3428. pch = ach + CGI_HEADER_PREFIX_CCH;
  3429. for ( i = 0; i < pnp->cchName; i++) {
  3430. pch[i] = (( pnp->pchName[i] == '-') ? '_' :
  3431. toupper( pnp->pchName[i])
  3432. );
  3433. } // for
  3434. pch[i] = '\0';
  3435. if ( !pstr->Append( ach ) ||
  3436. !pstr->Append( pnp->pchValue, pnp->cchValue ) ||
  3437. !pstr->Append( "\n", sizeof("\n")-1 ))
  3438. {
  3439. return FALSE;
  3440. }
  3441. } // for iterator over headers
  3442. return TRUE;
  3443. } // BuildCGIHeaderListInSTR()
  3444. DWORD
  3445. HTTP_REQUEST::Initialize(
  3446. VOID
  3447. )
  3448. {
  3449. DBG_REQUIRE( HTTP_HEADERS::Initialize());
  3450. _fGlobalInit = TRUE;
  3451. //
  3452. // Initialize the GetInfo function table
  3453. //
  3454. //
  3455. // 1. init the values to standard function GetInfoMisc() to start with
  3456. //
  3457. for( CHAR ch = 'A'; ch <= 'Z'; ch++) {
  3458. sm_GetInfoFuncs[IndexOfChar(ch)] = (PFN_GET_INFO ) &GetInfoMisc;
  3459. }
  3460. //
  3461. // 2. Initialize all known functions as appropriate
  3462. //
  3463. sm_GetInfoFuncs[IndexOfChar('A')] = (PFN_GET_INFO ) &GetInfoA;
  3464. sm_GetInfoFuncs[IndexOfChar('C')] = (PFN_GET_INFO ) &GetInfoC;
  3465. sm_GetInfoFuncs[IndexOfChar('H')] = (PFN_GET_INFO ) &GetInfoH;
  3466. sm_GetInfoFuncs[IndexOfChar('I')] = (PFN_GET_INFO ) &GetInfoI;
  3467. sm_GetInfoFuncs[IndexOfChar('L')] = (PFN_GET_INFO ) &GetInfoL;
  3468. sm_GetInfoFuncs[IndexOfChar('P')] = (PFN_GET_INFO ) &GetInfoP;
  3469. sm_GetInfoFuncs[IndexOfChar('R')] = (PFN_GET_INFO ) &GetInfoR;
  3470. sm_GetInfoFuncs[IndexOfChar('S')] = (PFN_GET_INFO ) &GetInfoS;
  3471. sm_GetInfoFuncs[IndexOfChar('U')] = (PFN_GET_INFO ) &GetInfoU;
  3472. return (NO_ERROR);
  3473. } // HTTP_REQUEST::Initialize()
  3474. VOID
  3475. HTTP_REQUEST::Terminate(
  3476. VOID
  3477. )
  3478. {
  3479. if ( !_fGlobalInit )
  3480. return;
  3481. HTTP_HEADERS::Cleanup();
  3482. }
  3483. BOOL
  3484. HTTP_REQUEST::RequestRenegotiate(
  3485. LPBOOL pfAccepted
  3486. )
  3487. /*++
  3488. Routine Description:
  3489. This method is invoked to request a SSL cert renegotiation
  3490. Arguments:
  3491. pfAccepted - updated with TRUE if renegotiation accepted
  3492. Returns:
  3493. TRUE if no error, otherwise FALSE
  3494. --*/
  3495. {
  3496. if ( !IsSecurePort() ||
  3497. !(GetFilePerms() & VROOT_MASK_NEGO_CERT) )
  3498. {
  3499. *pfAccepted = FALSE;
  3500. return TRUE;
  3501. }
  3502. if ( _dwRenegotiated &&
  3503. (GetFilePerms() & VROOT_MASK_MAP_CERT) ==
  3504. (UINT)((_dwSslNegoFlags & SSLNEGO_MAP) ? VROOT_MASK_MAP_CERT : 0) )
  3505. {
  3506. *pfAccepted = FALSE;
  3507. return TRUE;
  3508. }
  3509. _dwRenegotiated = 0;
  3510. //
  3511. // Ask the filter to handle renegotiation
  3512. //
  3513. if ( !_Filter.NotifyRequestRenegotiate( &_Filter,
  3514. pfAccepted,
  3515. GetFilePerms() & VROOT_MASK_MAP_CERT )
  3516. )
  3517. {
  3518. return FALSE;
  3519. }
  3520. if ( *pfAccepted )
  3521. {
  3522. if ( GetFilePerms() & VROOT_MASK_MAP_CERT )
  3523. {
  3524. _dwSslNegoFlags |= SSLNEGO_MAP;
  3525. }
  3526. else
  3527. {
  3528. _dwSslNegoFlags &= ~SSLNEGO_MAP;
  3529. }
  3530. SetState( HTR_CERT_RENEGOTIATE );
  3531. }
  3532. return TRUE;
  3533. }
  3534. BOOL
  3535. HTTP_REQUEST::DoneRenegotiate(
  3536. BOOL fSuccess
  3537. )
  3538. /*++
  3539. Routine Description:
  3540. This method is invoked on SSL cert renegotiation completion
  3541. Arguments:
  3542. fSuccess - TRUE if renegotiation successfully retrieve a certificate
  3543. Returns:
  3544. TRUE if no error, otherwise FALSE
  3545. --*/
  3546. {
  3547. _dwRenegotiated = fSuccess ? CERT_NEGO_SUCCESS : CERT_NEGO_FAILURE;
  3548. return TRUE;
  3549. }
  3550. VOID
  3551. HTTP_REQUEST::EndOfRequest(
  3552. VOID
  3553. )
  3554. /*++
  3555. Routine Description:
  3556. This method does the necessary cleanup for the end of this request - note
  3557. on error conditions this method may not be called, so there is some
  3558. duplication with SessionTerminated which is guaranteed to be called
  3559. Arguments:
  3560. None
  3561. Returns:
  3562. None
  3563. --*/
  3564. {
  3565. //
  3566. // Notify filters this is the end of the request
  3567. //
  3568. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_END_OF_REQUEST,
  3569. IsSecurePort() ))
  3570. {
  3571. _Filter.NotifyEndOfRequest();
  3572. }
  3573. CloseGetFile();
  3574. //
  3575. // Reset the authentication if
  3576. // - not in authentication phase of connection
  3577. // - SSL connection; connection can't be reused [eg by proxy] because it's encrypted
  3578. // - flag single request per auth is set
  3579. // - flag single proxy request per auth is set and request coming from a proxy
  3580. // ( as determined by checking for 'Via:' header ). Note that this method
  3581. // cannot guarantee proxy detection, but this is the best we can do.
  3582. // There are 2 submodes : always reset auth for proxies, or reset auth only
  3583. // if not in proxy mode ( as set by ISAPI app, default )
  3584. //
  3585. if ( _pMetaData &&
  3586. !_fAuthenticating &&
  3587. !_fAnonymous &&
  3588. !IsSecurePort() &&
  3589. ( (_pMetaData->QueryAuthenticationPersistence() & MD_AUTH_SINGLEREQUEST) ||
  3590. ( (_pMetaData->QueryAuthenticationPersistence() & MD_AUTH_SINGLEREQUESTALWAYSIFPROXY) &&
  3591. IsClientProxy() ) ||
  3592. ( (_pMetaData->QueryAuthenticationPersistence() & MD_AUTH_SINGLEREQUESTIFPROXY) &&
  3593. IsClientProxy() &&
  3594. !IsProxyRequest() )
  3595. )
  3596. )
  3597. {
  3598. _fSingleRequestAuth = TRUE;
  3599. }
  3600. // Free the URI and/or metadata information if we have any. We know that
  3601. // if we have URI info, then it points at meta data and will free the
  3602. // metadata info when the URI info is free. Otherwise, we need to free
  3603. // the metadata information here.
  3604. CleanupWriteState();
  3605. ReleaseCacheInfo();
  3606. return;
  3607. } // HTTP_REQUEST::EndOfRequest()
  3608. VOID
  3609. HTTP_REQUEST::SessionTerminated(
  3610. VOID
  3611. )
  3612. /*++
  3613. Routine Description:
  3614. This method does the necessary cleanup for the connected session
  3615. for this request object.
  3616. Arguments:
  3617. None
  3618. Returns:
  3619. None
  3620. --*/
  3621. {
  3622. switch ( QueryState()) {
  3623. case HTR_GATEWAY_ASYNC_IO:
  3624. // does the necessary actions to cleanup outstanding IO operation
  3625. (VOID ) ProcessAsyncGatewayIO();
  3626. break;
  3627. default:
  3628. break;
  3629. } // switch()
  3630. CloseGetFile();
  3631. // Free the URI and/or metadata information if we have any. We know that
  3632. // if we have URI info, then it points at meta data and will free the
  3633. // metadata info when the URI info is free. Otherwise, we need to free
  3634. // the metadata information here.
  3635. ReleaseCacheInfo();
  3636. //
  3637. // do the cleanup for base object
  3638. //
  3639. HTTP_REQ_BASE::SessionTerminated();
  3640. return;
  3641. } // HTTP_REQUEST::SessionTerminated()
  3642. #define EXTRA_ETAG_PRECOND_SIZE (sizeof("ETag: W/\r\n") - 1 + \
  3643. MAX_ETAG_BUFFER_LENGTH + \
  3644. sizeof("Date: \r\n") - 1+ \
  3645. sizeof("Mon, 00 Jan 1997: 00:00:00 GMT") - 1 +\
  3646. sizeof("Last-Modified: \r\n") - 1+ \
  3647. sizeof("Mon, 00 Jan 1997 00:00:00 GMT") - 1)
  3648. #define EXTRA_PRECOND_SIZE (sizeof("Connection: keep-alive\r\n") - 1 +\
  3649. sizeof("Content-Length: 4294967295\r\n\r\n") - 1)
  3650. BOOL
  3651. HTTP_REQUEST::SendPreconditionResponse(
  3652. DWORD HT_Response,
  3653. DWORD dwIOFlags,
  3654. BOOL *pfFinished
  3655. )
  3656. /*++
  3657. Routine Description:
  3658. Utility routine to send the appropriate header for a precondition
  3659. failure.
  3660. Arguments:
  3661. HT_Response - The response to be sent.
  3662. pfFinished - Boolean indicating whether or not we're finished.
  3663. Returns:
  3664. TRUE if we sent a response to the request, FALSE if we didn't.
  3665. --*/
  3666. {
  3667. CHAR *pszTail;
  3668. STACK_STR(strTemp, 80);
  3669. DWORD dwCurrentSize;
  3670. CHAR ach[64];
  3671. //
  3672. // Build the response with support for keep-alives
  3673. //
  3674. if ( !BuildStatusLine( &strTemp,
  3675. HT_Response,
  3676. NO_ERROR ))
  3677. {
  3678. return FALSE;
  3679. }
  3680. //
  3681. // If this is a 304 response we need to send an ETag, and Expires.
  3682. //
  3683. if (HT_Response == HT_NOT_MODIFIED)
  3684. {
  3685. if (!BuildBaseResponseHeader(QueryRespBuf(),
  3686. pfFinished,
  3687. &strTemp,
  3688. HTTPH_SEND_GLOBAL_EXPIRE))
  3689. {
  3690. return FALSE;
  3691. }
  3692. dwCurrentSize = strlen(QueryRespBufPtr());
  3693. if (!QueryRespBuf()->Resize(dwCurrentSize + EXTRA_ETAG_PRECOND_SIZE +
  3694. EXTRA_PRECOND_SIZE))
  3695. {
  3696. return FALSE;
  3697. }
  3698. pszTail = QueryRespBufPtr() + dwCurrentSize;
  3699. DBG_ASSERT(_pGetFile != NULL);
  3700. if ( !QueryNoCache() )
  3701. {
  3702. //
  3703. // Don't send a Last-Modified time, per the 1.1 spec.
  3704. //
  3705. //
  3706. // ETag: <Etag>
  3707. //
  3708. if (_pGetFile->WeakETag())
  3709. {
  3710. APPEND_PSZ_HEADER(pszTail, "ETag: W/", _pGetFile->QueryETag(),
  3711. "\r\n");
  3712. } else
  3713. {
  3714. APPEND_PSZ_HEADER(pszTail, "ETag: ", _pGetFile->QueryETag(),
  3715. "\r\n");
  3716. }
  3717. }
  3718. }
  3719. else
  3720. {
  3721. if (!BuildBaseResponseHeader(QueryRespBuf(),
  3722. pfFinished,
  3723. &strTemp,
  3724. HTTPH_NO_CUSTOM))
  3725. {
  3726. return FALSE;
  3727. }
  3728. pszTail = QueryRespBufPtr();
  3729. dwCurrentSize = strlen(pszTail);
  3730. if (!QueryRespBuf()->Resize(dwCurrentSize + EXTRA_PRECOND_SIZE))
  3731. {
  3732. return FALSE;
  3733. }
  3734. pszTail = QueryRespBufPtr() + dwCurrentSize;
  3735. }
  3736. //
  3737. // Don't need to add Connection: header, that's done by
  3738. // BuildBaseResponseHeader.
  3739. if ( IsKeepConnSet() )
  3740. {
  3741. if (!_fHaveContentLength &&
  3742. (_cbBytesReceived == _cbClientRequest))
  3743. {
  3744. dwIOFlags |= IO_FLAG_AND_RECV;
  3745. }
  3746. }
  3747. if (HT_Response != HT_NOT_MODIFIED)
  3748. {
  3749. BYTE cMsg[128];
  3750. BUFFER bufMsg(cMsg, sizeof(cMsg));
  3751. DWORD dwMsgSize;
  3752. if (CheckCustomError(&bufMsg, HT_Response, 0, pfFinished, &dwMsgSize, FALSE))
  3753. {
  3754. //
  3755. // Now that we know the content length, add a content length
  3756. // header, and copy the custom error message to the buffer.
  3757. //
  3758. APPEND_NUMERIC_HEADER( pszTail, "Content-Length: ", dwMsgSize, "\r\n" );
  3759. dwCurrentSize = DIFF(pszTail - QueryRespBufPtr());
  3760. if (!QueryRespBuf()->Resize(dwCurrentSize + bufMsg.QuerySize()))
  3761. {
  3762. return FALSE;
  3763. }
  3764. pszTail = QueryRespBufPtr() + dwCurrentSize;
  3765. strcat(pszTail, (CHAR *)bufMsg.QueryPtr());
  3766. }
  3767. else
  3768. {
  3769. APPEND_STRING(pszTail, "Content-Length: 0\r\n\r\n");
  3770. }
  3771. }
  3772. else
  3773. {
  3774. //
  3775. // Need to send a content length of 0 because of HTTP/1.0 clients
  3776. //
  3777. APPEND_STRING(pszTail, "Content-Length: 0\r\n\r\n");
  3778. }
  3779. SetState( HTR_DONE, HT_Response, NO_ERROR );
  3780. return SendHeader( QueryRespBufPtr(),
  3781. QueryRespBufCB(),
  3782. dwIOFlags,
  3783. pfFinished );
  3784. }
  3785. BOOL
  3786. HTTP_REQUEST::FindInETagList(
  3787. PCHAR pLocalETag,
  3788. PCHAR pETagList,
  3789. BOOL bWeakCompare
  3790. )
  3791. /*++
  3792. Routine Description:
  3793. Search and input list of ETag for one that matches our local ETag.
  3794. Arguments:
  3795. pLocalETag - The local ETag we're using.
  3796. pETagList - The ETag list we've received from the client.
  3797. bWeakCompare - TRUE if we're doing a weak compare.
  3798. Returns:
  3799. TRUE if we found a matching ETag, FALSE otherwise.
  3800. --*/
  3801. {
  3802. UINT QuoteCount;
  3803. PCHAR pFileETag;
  3804. BOOL Matched;
  3805. // Otherwise, we'll loop through the ETag string, looking for ETag to
  3806. // compare, as long as we have an ETag to look at.
  3807. do {
  3808. while (isspace((UCHAR)(*pETagList)))
  3809. {
  3810. pETagList++;
  3811. }
  3812. if (!*pETagList)
  3813. {
  3814. // Ran out of ETag.
  3815. return FALSE;
  3816. }
  3817. // If this ETag is '*', it's a match.
  3818. if (*pETagList == '*')
  3819. {
  3820. return TRUE;
  3821. }
  3822. // See if this ETag is weak.
  3823. if (*pETagList == 'W' && *(pETagList+1) == '/')
  3824. {
  3825. // This is a weak validator. If we're not doing the weak comparison,
  3826. // fail.
  3827. if (!bWeakCompare)
  3828. {
  3829. return FALSE;
  3830. }
  3831. // Skip over the 'W/', and any intervening whitespace.
  3832. pETagList += 2;
  3833. while (isspace((UCHAR)(*pETagList)))
  3834. {
  3835. pETagList++;
  3836. }
  3837. if (!*pETagList)
  3838. {
  3839. // Ran out of ETag.
  3840. return FALSE;
  3841. }
  3842. }
  3843. if (*pETagList != '"')
  3844. {
  3845. // This isn't a quoted string, so fail.
  3846. return FALSE;
  3847. }
  3848. // OK, right now we should be at the start of a quoted string that
  3849. // we can compare against our current ETag.
  3850. QuoteCount = 0;
  3851. Matched = TRUE;
  3852. pFileETag = pLocalETag;
  3853. // Do the actual compare. We do this by scanning the current ETag,
  3854. // which is a quoted string. We look for two quotation marks, the
  3855. // the delimiters if the quoted string. If after we find two quotes
  3856. // in the ETag everything has matched, then we've matched this ETag.
  3857. // Otherwise we'll try the next one.
  3858. do
  3859. {
  3860. CHAR Temp;
  3861. Temp = *pETagList;
  3862. if (Temp == '"')
  3863. {
  3864. QuoteCount++;
  3865. }
  3866. if (*pFileETag != Temp)
  3867. {
  3868. Matched = FALSE;
  3869. }
  3870. if (!Temp)
  3871. {
  3872. return FALSE;
  3873. }
  3874. pETagList++;
  3875. if (*pFileETag == '\0')
  3876. {
  3877. break;
  3878. }
  3879. pFileETag++;
  3880. } while (QuoteCount != 2);
  3881. if (Matched)
  3882. {
  3883. return TRUE;
  3884. }
  3885. // Otherwise, at this point we need to look at the next ETag.
  3886. while (QuoteCount != 2)
  3887. {
  3888. if (*pETagList == '"')
  3889. {
  3890. QuoteCount++;
  3891. }
  3892. else
  3893. {
  3894. if (*pETagList == '\0')
  3895. {
  3896. return FALSE;
  3897. }
  3898. }
  3899. pETagList++;
  3900. }
  3901. while (isspace((UCHAR)(*pETagList)))
  3902. {
  3903. pETagList++;
  3904. }
  3905. if (*pETagList == ',')
  3906. {
  3907. pETagList++;
  3908. }
  3909. else
  3910. {
  3911. return FALSE;
  3912. }
  3913. } while ( *pETagList );
  3914. return FALSE;
  3915. }
  3916. BOOL
  3917. HTTP_REQUEST::CancelPreconditions(
  3918. )
  3919. /*++
  3920. Routine Description:
  3921. Cancel preconditions for this request
  3922. Arguments:
  3923. None
  3924. Returns:
  3925. TRUE if success, otherwise FALSE
  3926. --*/
  3927. {
  3928. _strRange.Reset();
  3929. _HeaderList.FastMapCancel(HM_IFM);
  3930. _liUnmodifiedSince.QuadPart = 0;
  3931. _HeaderList.FastMapCancel(HM_INM);
  3932. _liModifiedSince.QuadPart = 0;
  3933. _dwModifiedSinceLength = 0;
  3934. _HeaderList.FastMapCancel(HM_IFR);
  3935. return TRUE;
  3936. }
  3937. BOOL
  3938. HTTP_REQUEST::CheckPreconditions(
  3939. LPTS_OPEN_FILE_INFO pFile,
  3940. BOOL *pfFinished,
  3941. BOOL *bReturn
  3942. )
  3943. /*++
  3944. Routine Description:
  3945. Handle all of the If Modifiers on a request, and do the right thing
  3946. with them. Note that this version implicitly assumes that the caller
  3947. is performing a GET or HEAD or something compatible.
  3948. Arguments:
  3949. pFile - Pointer to file information for file we're checking.
  3950. pfFinished - pass through BOOLEAN.
  3951. bReturn - Where to store the response value for this routine.
  3952. Returns:
  3953. TRUE if we sent a response to the request, FALSE if we didn't.
  3954. --*/
  3955. {
  3956. LPSTR pszETagList;
  3957. BOOL bWeakCompare;
  3958. //
  3959. // There are currently 5 possible If-* modifiers: If-Match,
  3960. // If-Unmodified-Since, If-Non-Match, If-Modified-Since, and
  3961. // If-Range. We handle them in that order if all are present,
  3962. // and as soon as one condition fails we stop processing and return
  3963. // the appropriate header.
  3964. //
  3965. // If-Range is an exception. It only modifies the behavior of an incoming
  3966. // Range: request, and it only applies if the response would otherwise
  3967. // be 2xx. If it succeeds nothing is done, but if it fails we (essentially)
  3968. // delete the Range header and force sending of the whole header.
  3969. //
  3970. //
  3971. // First see if we can use the weak comparison function. We can use
  3972. // the weak comparison function iff this is for a conditional GET with
  3973. // no range request. Note that If-Match always requires the strong
  3974. // comparison function.
  3975. bWeakCompare = _strRange.IsEmpty();
  3976. // Now handle the If-Match header, if we have one.
  3977. pszETagList = (LPSTR ) _HeaderList.FastMapQueryValue(HM_IFM);
  3978. if (pszETagList != NULL)
  3979. {
  3980. // Have an If-Match header. If we can match the ETag we have in
  3981. // the list we'll continue, otherwise send back
  3982. // 412 Precondition Failed. If-Match requires the strong
  3983. // comparison function, so it fails if the local ETag is weak or
  3984. // the incoming ETags are weak.
  3985. if ( pFile->WeakETag() ||
  3986. !FindInETagList(pFile->QueryETag(), pszETagList, FALSE))
  3987. {
  3988. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  3989. IO_FLAG_ASYNC,
  3990. pfFinished);
  3991. return TRUE;
  3992. }
  3993. }
  3994. // Made it through that, handle If-Unmodified-Since if we have that.
  3995. if ( _liUnmodifiedSince.QuadPart)
  3996. {
  3997. FILETIME tm;
  3998. TCP_REQUIRE( pFile->QueryLastWriteTime( &tm ));
  3999. // If our last write time is greater than their UnmodifiedSince
  4000. // time, the precondition fails.
  4001. if (*(LONGLONG*)&tm > _liUnmodifiedSince.QuadPart )
  4002. {
  4003. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4004. IO_FLAG_ASYNC,
  4005. pfFinished);
  4006. return TRUE;
  4007. }
  4008. }
  4009. //
  4010. // Now see if we have an If-None-Match, and if so handle that.
  4011. //
  4012. pszETagList = (LPSTR ) _HeaderList.FastMapQueryValue(HM_INM);
  4013. if (pszETagList != NULL)
  4014. {
  4015. // Have an If-None-Match header. We send 304 if the If-None-Match
  4016. // condition is false. The If-None-Match condition is false if
  4017. // it's not true that we're doing a strong compare against a weak
  4018. // local ETag and we can find a match in the ETag list.
  4019. if (!(!bWeakCompare && pFile->WeakETag()) &&
  4020. FindInETagList(pFile->QueryETag(), pszETagList,
  4021. bWeakCompare))
  4022. {
  4023. *bReturn = SendPreconditionResponse(HT_NOT_MODIFIED,
  4024. IO_FLAG_ASYNC,
  4025. pfFinished);
  4026. return TRUE;
  4027. }
  4028. }
  4029. // And check for last modified since.
  4030. if ( _liModifiedSince.QuadPart)
  4031. {
  4032. FILETIME tm;
  4033. TCP_REQUIRE( pFile->QueryLastWriteTime( &tm ));
  4034. if ( *(LONGLONG*)&tm <= _liModifiedSince.QuadPart )
  4035. {
  4036. // Need to check and see if the Modified-Since time is greater than
  4037. // our current time. If it is, we ignore it.
  4038. ::GetSystemTimeAsFileTime(&tm);
  4039. if (*(LONGLONG *)&tm >= _liModifiedSince.QuadPart)
  4040. {
  4041. LARGE_INTEGER liFileSize;
  4042. TCP_REQUIRE( pFile->QuerySize( liFileSize ));
  4043. if (_dwModifiedSinceLength == 0 ||
  4044. (liFileSize.HighPart == 0 &&
  4045. (liFileSize.LowPart == _dwModifiedSinceLength)))
  4046. {
  4047. *bReturn = SendPreconditionResponse(HT_NOT_MODIFIED,
  4048. IO_FLAG_ASYNC,
  4049. pfFinished);
  4050. return TRUE;
  4051. }
  4052. }
  4053. }
  4054. }
  4055. // Finally, we can handle If-Range: if it exists. If we've gotten this
  4056. // far presumably we're going to send a 2xx response. We'll ignore the
  4057. // If-Range if this is not a range request. If it is a range request and
  4058. // the If-Range matches we don't do anything. If the If-Range doesn't
  4059. // match then we force retrieval of the whole file.
  4060. pszETagList = (LPSTR ) _HeaderList.FastMapQueryValue(HM_IFR);
  4061. if (pszETagList != NULL)
  4062. {
  4063. if (!_strRange.IsEmpty())
  4064. {
  4065. // Need to determine if what we have is a date or an ETag.
  4066. // What we have can be either an ETag or a date string. An ETag
  4067. // may start with a W/ or a quote. A date may start with a W
  4068. // but will never have the second character be a /.
  4069. if ( *pszETagList == '"' ||
  4070. (*pszETagList == 'W' && pszETagList[1] == '/'))
  4071. {
  4072. // This is an ETag.
  4073. if (pFile->WeakETag() ||
  4074. !FindInETagList(pFile->QueryETag(), pszETagList,
  4075. FALSE))
  4076. {
  4077. // The If-Range failed, so we can't send a range. Force
  4078. // sending the whole thing.
  4079. _strRange.SetLen(0);
  4080. }
  4081. } else
  4082. {
  4083. LARGE_INTEGER liRangeTime;
  4084. // This must be a date. Convert it to a time, and see if it's
  4085. // less than or equal to our last write time. If it is, the
  4086. // file's changed, and we can't perform the range.
  4087. if ( !StringTimeToFileTime( pszETagList, &liRangeTime))
  4088. {
  4089. // Couldn't convert it, so don't send the range.
  4090. _strRange.SetLen(0);
  4091. } else
  4092. {
  4093. FILETIME tm;
  4094. TCP_REQUIRE( pFile->QueryLastWriteTime( &tm ));
  4095. if (*(LONGLONG*)&tm > liRangeTime.QuadPart )
  4096. {
  4097. _strRange.SetLen(0);
  4098. }
  4099. }
  4100. }
  4101. }
  4102. }
  4103. return FALSE;
  4104. }
  4105. BOOL
  4106. HTTP_REQUEST::CheckPreconditions(
  4107. HANDLE hFile,
  4108. BOOL bExisted,
  4109. BOOL *pfFinished,
  4110. BOOL *bReturn
  4111. )
  4112. /*++
  4113. Routine Description:
  4114. Handle the If Modifiers on a request, and do the right thing
  4115. with them. This routine is very similar to the other CheckPreconditions
  4116. routine. The difference is that the other one is intended to be called
  4117. by someone doing a GET or HEAD, and this one is intended for other
  4118. methods. This version of the routine takes as input a file handle
  4119. instead of a pointer to a TS_OPEN_FILE_INFO class. Also, this version
  4120. always uses the strong comparison function for ETags, doesn't check
  4121. If-Modified-Since or If-Range headers, and sends 412 instead of 304
  4122. for If-None-Match failures. We don't handle the If-Match and If-Range
  4123. headers because the spec. calls them out as being for use with a GET
  4124. and this version of the routine isn't called for GETs.
  4125. Arguments:
  4126. hFile - Handle for file we're checking.
  4127. bExists - TRUE if the file we're checking existed before this call.
  4128. pfFinished - pass through BOOLEAN.
  4129. bReturn - Where to store the response value for this routine.
  4130. Returns:
  4131. TRUE if we sent a response to the request, FALSE if we didn't.
  4132. --*/
  4133. {
  4134. LPSTR pszETagList;
  4135. CHAR ETag[MAX_ETAG_BUFFER_LENGTH];
  4136. BOOL bETagIsValid = FALSE;
  4137. BOOL bWeakETag;
  4138. FILETIME tm;
  4139. //
  4140. // There are currently 3 possible If-* modifiers we handle here: If-Match,
  4141. // If-Unmodified-Since, and If-Non-Match. We handle them in that order if
  4142. // all are present, and as soon as one condition fails we stop processing
  4143. // and return the appropriate header.
  4144. //
  4145. // Now handle the If-Match header, if we have one.
  4146. pszETagList = (LPSTR ) _HeaderList.FastMapQueryValue(HM_IFM);
  4147. if (pszETagList != NULL)
  4148. {
  4149. // Have an If-Match header. If we can match the ETag we have in
  4150. // the list we'll continue, otherwise send back
  4151. // 412 Precondition Failed. If-Match requires the strong comparison
  4152. // function. The first thing we have to do is call the tsunami cache to
  4153. // create an ETag for us. If we can't do that, we assume the
  4154. // precondition failed, and return the appropriate error.
  4155. // Check for the special case of '*'.
  4156. if (*pszETagList == '*')
  4157. {
  4158. if (!bExisted)
  4159. {
  4160. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4161. IO_FLAG_SYNC,
  4162. pfFinished);
  4163. return TRUE;
  4164. }
  4165. } else
  4166. {
  4167. bETagIsValid = TsCreateETagFromHandle(hFile, ETag, &bWeakETag);
  4168. if (!bETagIsValid ||
  4169. bWeakETag ||
  4170. !FindInETagList(ETag, pszETagList, FALSE))
  4171. {
  4172. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4173. IO_FLAG_SYNC,
  4174. pfFinished);
  4175. return TRUE;
  4176. }
  4177. }
  4178. }
  4179. // Made it through that, handle If-Unmodified-Since if we have that.
  4180. if ( _liUnmodifiedSince.QuadPart)
  4181. {
  4182. // If our last write time is greater than their UnmodifiedSince
  4183. // time, the precondition fails. We'll need to call tsunami to
  4184. // get the file time, if that fails we can't do the compare, so
  4185. // we err on the safe side and return the error.
  4186. if (!TsLastWriteTimeFromHandle(hFile, &tm) ||
  4187. *(LONGLONG*)&tm > _liUnmodifiedSince.QuadPart )
  4188. {
  4189. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4190. IO_FLAG_SYNC,
  4191. pfFinished);
  4192. return TRUE;
  4193. }
  4194. }
  4195. //
  4196. // Now see if we have an If-None-Match, and if so handle that.
  4197. //
  4198. pszETagList = (LPSTR ) _HeaderList.FastMapQueryValue(HM_INM);
  4199. if (pszETagList != NULL)
  4200. {
  4201. // Have an If-None-Match header. We send 412 if the If-None-Match
  4202. // condition is false. The If-None-Match condition is false if
  4203. // it's not true that we're doing a strong compare against a weak
  4204. // local ETag and we can find a match in the ETag list. We may
  4205. // still need to create an ETag. In this case we need the
  4206. // ETag to be valid, if we can't create one we won't go with
  4207. // the 412 response.
  4208. // Check for the special case of '*'.
  4209. if (*pszETagList == '*')
  4210. {
  4211. if (bExisted)
  4212. {
  4213. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4214. IO_FLAG_SYNC,
  4215. pfFinished);
  4216. return TRUE;
  4217. }
  4218. } else
  4219. {
  4220. if (!bETagIsValid)
  4221. {
  4222. bETagIsValid = TsCreateETagFromHandle(hFile, ETag, &bWeakETag);
  4223. }
  4224. if (bETagIsValid && !bWeakETag &&
  4225. FindInETagList(ETag, pszETagList, FALSE))
  4226. {
  4227. *bReturn = SendPreconditionResponse(HT_PRECOND_FAILED,
  4228. IO_FLAG_SYNC,
  4229. pfFinished);
  4230. return TRUE;
  4231. }
  4232. }
  4233. }
  4234. return FALSE;
  4235. }
  4236. BOOL
  4237. HTTP_REQUEST::DLCMungeSimple(
  4238. VOID
  4239. )
  4240. /*++
  4241. Routine Description:
  4242. Handle the URL before (optionally) looking up instance. This means
  4243. removing any "/HostMenu" and embeeded hosts.
  4244. Arguments:
  4245. None
  4246. Returns:
  4247. TRUE if successful, else FALSE
  4248. --*/
  4249. {
  4250. const CHAR * pchURL = _HeaderList.FastMapQueryValue( HM_URL );
  4251. if ( !_strnicmp( pchURL,
  4252. g_pszDLCMenu,
  4253. g_cbDLCMenu ) )
  4254. {
  4255. if ( !_strHostAddr.Copy( g_pszDLCHostName, g_cbDLCHostName ) )
  4256. {
  4257. return FALSE;
  4258. }
  4259. if ( *( pchURL + g_cbDLCMenu ) == '\0' ||
  4260. *( pchURL + g_cbDLCMenu ) == '?' )
  4261. {
  4262. STACK_STR( strHostCookie, MAX_PATH );
  4263. STACK_STR( strMenuURL, MAX_PATH );
  4264. const CHAR * pchCookie = _HeaderList.FastMapQueryValue( HM_COK );
  4265. if ( pchCookie != NULL && DLCGetCookie( (CHAR*) pchCookie,
  4266. g_pszDLCCookieName,
  4267. g_cbDLCCookieName,
  4268. &strHostCookie ) )
  4269. {
  4270. if ( !strMenuURL.Copy( g_pszDLCCookieMenuDocument,
  4271. g_cbDLCCookieMenuDocument ) )
  4272. {
  4273. return FALSE;
  4274. }
  4275. }
  4276. else
  4277. {
  4278. if ( !strMenuURL.Copy( g_pszDLCMungeMenuDocument,
  4279. g_cbDLCMungeMenuDocument ) )
  4280. {
  4281. return FALSE;
  4282. }
  4283. }
  4284. if ( !strMenuURL.Append( pchURL + g_cbDLCMenu ) )
  4285. {
  4286. return FALSE;
  4287. }
  4288. _HeaderList.FastMapStore( HM_URL,
  4289. NULL );
  4290. return _HeaderList.FastMapStoreWithConcat( HM_URL,
  4291. strMenuURL.QueryStr(),
  4292. strMenuURL.QueryCB() );
  4293. }
  4294. else
  4295. {
  4296. _HeaderList.FastMapStore( HM_URL,
  4297. pchURL + g_cbDLCMenu );
  4298. return TRUE;
  4299. }
  4300. }
  4301. else
  4302. {
  4303. if ( *pchURL == '/' )
  4304. {
  4305. pchURL++;
  4306. }
  4307. if ( *pchURL == '*' )
  4308. {
  4309. CHAR * pchNextSlash = strchr( pchURL, '/' );
  4310. if ( pchNextSlash != NULL )
  4311. {
  4312. if ( !_strDLCString.Copy( pchURL + 1,
  4313. DIFF(pchNextSlash - pchURL) - 1 ) )
  4314. {
  4315. return FALSE;
  4316. }
  4317. _HeaderList.FastMapStore( HM_URL,
  4318. pchNextSlash );
  4319. }
  4320. }
  4321. return TRUE;
  4322. }
  4323. }
  4324. BOOL
  4325. HTTP_REQUEST::DLCHandleRequest(
  4326. IN OUT BOOL * pfFinished
  4327. )
  4328. /*++
  4329. Routine Description:
  4330. Check for whether a down level client has sent a cookie for use as a
  4331. host header. If so, set the host header. If no cookie is sent, redirect
  4332. the client to the register configured host menu.
  4333. Arguments:
  4334. pfFinished - Set to TRUE if request finished.
  4335. Returns:
  4336. TRUE if successful, else FALSE
  4337. --*/
  4338. {
  4339. const CHAR * pch = NULL;
  4340. const CHAR * pchURL = NULL;
  4341. STACK_STR( strHostCookie, MAX_PATH );
  4342. //
  4343. // Check for a pseudoheader cookie
  4344. //
  4345. pch = _HeaderList.FastMapQueryValue( HM_COK );
  4346. pchURL = _HeaderList.FastMapQueryValue( HM_URL );
  4347. if ( pch != NULL && DLCGetCookie( (CHAR*) _HeaderList.FastMapQueryValue( HM_COK ),
  4348. g_pszDLCCookieName,
  4349. g_cbDLCCookieName,
  4350. &strHostCookie ) )
  4351. {
  4352. return _strHostAddr.Copy( strHostCookie );
  4353. }
  4354. else if ( !_strDLCString.IsEmpty() )
  4355. {
  4356. return _strHostAddr.Copy( _strDLCString );
  4357. }
  4358. else
  4359. {
  4360. STACK_STR( strRedirect, MAX_PATH );
  4361. //
  4362. // No cookie, no munged string. Do a redirect!
  4363. //
  4364. //
  4365. // Send a cookie with the redirect so we can tell how capable the
  4366. // user's browser really is.
  4367. //
  4368. if ( !strRedirect.Copy( "Set-Cookie: path=/; domain=" ) ||
  4369. !strRedirect.Append( g_pszDLCHostName ) ||
  4370. !strRedirect.Append( "; " ) ||
  4371. !strRedirect.Append( g_pszDLCCookieName ) ||
  4372. !strRedirect.Append( "=" ) ||
  4373. !strRedirect.Append( g_pszDLCHostName ) ||
  4374. !strRedirect.Append( "\r\n" ) ||
  4375. !strRedirect.Append( "Location: http://" ) ||
  4376. !strRedirect.Append( g_pszDLCHostName ) ||
  4377. !strRedirect.Append( g_pszDLCMenu ) ||
  4378. !strRedirect.Append( "?" ) ||
  4379. !strRedirect.Append( _HeaderList.FastMapQueryValue( HM_URL ) ) ||
  4380. !strRedirect.Append( "\r\n\r\n" ) )
  4381. {
  4382. return FALSE;
  4383. }
  4384. if ( !SendHeader( "302 Temporary Redirect",
  4385. strRedirect.QueryStr(),
  4386. IO_FLAG_SYNC,
  4387. pfFinished ) )
  4388. {
  4389. return FALSE;
  4390. }
  4391. *pfFinished = TRUE;
  4392. return TRUE;
  4393. }
  4394. }
  4395. BOOL
  4396. HTTP_REQUEST::DLCGetCookie(
  4397. IN CHAR * pszCookieString,
  4398. IN CHAR * pszCookieName,
  4399. IN DWORD cbCookieName,
  4400. OUT STR * pstrCookieValue
  4401. )
  4402. {
  4403. CHAR * pchPos = NULL;
  4404. DBG_ASSERT( pszCookieString != NULL );
  4405. DBG_ASSERT( pszCookieName != NULL );
  4406. DBG_ASSERT( pstrCookieValue != NULL );
  4407. pchPos = strstr( pszCookieString, pszCookieName );
  4408. if ( pchPos != NULL )
  4409. {
  4410. CHAR * pchEnd = NULL;
  4411. BOOL fCopyOK;
  4412. pchPos += cbCookieName + 1;
  4413. // Handle case where multiple cookies are sent
  4414. pchEnd = strchr( pchPos, ';' );
  4415. if ( pchEnd == NULL )
  4416. {
  4417. fCopyOK = pstrCookieValue->Copy( pchPos );
  4418. }
  4419. else
  4420. {
  4421. fCopyOK = pstrCookieValue->Copy( pchPos,
  4422. DIFF(pchEnd - pchPos) );
  4423. }
  4424. //
  4425. // Unescape the value if we got it
  4426. //
  4427. if ( fCopyOK ) {
  4428. return pstrCookieValue->Unescape();
  4429. } else {
  4430. return FALSE;
  4431. }
  4432. }
  4433. return FALSE;
  4434. }
  4435. BOOL
  4436. HTTP_REQUEST::RequestAbortiveClose(
  4437. )
  4438. /*++
  4439. Routine Description:
  4440. Request for abortive close on disconnect
  4441. Arguments:
  4442. None
  4443. Returns:
  4444. TRUE if successful, else FALSE
  4445. --*/
  4446. {
  4447. SetKeepConn( FALSE );
  4448. return QueryClientConn()->RequestAbortiveClose();
  4449. }
  4450. BOOL
  4451. HTTP_REQUEST::CloseConnection(
  4452. )
  4453. /*++
  4454. Routine Description:
  4455. Closes the socket connection
  4456. Arguments:
  4457. None
  4458. Returns:
  4459. TRUE if successful, else FALSE
  4460. --*/
  4461. {
  4462. CLIENT_CONN *pConn;
  4463. BOOL fSuccess = FALSE;
  4464. pConn = QueryClientConn();
  4465. DBG_ASSERT( pConn );
  4466. //
  4467. // If client is not already disconnected itself,
  4468. // clear _fKeepConn flag and close connection
  4469. //
  4470. if( pConn->QueryState() != CCS_DISCONNECTING ) {
  4471. //
  4472. // RACE ALERT: it is still possible that client disconnects
  4473. // while we are getting down to ATQ.
  4474. SetKeepConn( FALSE );
  4475. fSuccess = pConn->CloseConnection();
  4476. }
  4477. else
  4478. {
  4479. //
  4480. // at this point we know that client did disconnect itself
  4481. //
  4482. fSuccess = FALSE;
  4483. }
  4484. return fSuccess;
  4485. }
  4486. APIERR
  4487. WriteConfiguration( VOID )
  4488. /*++
  4489. Routine Description:
  4490. Determination certain configuration items of server and write them out
  4491. to the metabase so that admin can access
  4492. Arguments:
  4493. None
  4494. Returns:
  4495. 0 if successful, else Win32 Error Code
  4496. --*/
  4497. {
  4498. CHAR achFilename[ MAX_PATH ];
  4499. CHAR * pchFilePart;
  4500. DWORD dwRet;
  4501. DWORD dwHandle;
  4502. UINT dwLen;
  4503. HKEY hKey = NULL;
  4504. DWORD dwServerConfiguration = 0;
  4505. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  4506. //
  4507. // With the availability of Server gated crypto, all servers
  4508. // can use 128 bits encryption
  4509. //
  4510. #if !defined(CHECK_SCHANNEL_FOR_128_BITS)
  4511. dwServerConfiguration |= MD_SERVER_CONFIG_SSL_128;
  4512. #else
  4513. //
  4514. // Determine what type of SCHANNEL.DLL is installed
  4515. //
  4516. dwRet = SearchPath( NULL,
  4517. "schannel.dll",
  4518. NULL,
  4519. MAX_PATH,
  4520. achFilename,
  4521. &pchFilePart );
  4522. if ( dwRet != 0 )
  4523. {
  4524. VOID * pBuf;
  4525. VOID * pValue;
  4526. WORD * pVerTransInfo;
  4527. CHAR achBlockName[ MAX_PATH ];
  4528. dwRet = GetFileVersionInfoSize( achFilename,
  4529. &dwHandle );
  4530. if ( dwRet == 0 )
  4531. {
  4532. goto Continue;
  4533. }
  4534. pBuf = LocalAlloc( LPTR,
  4535. dwRet );
  4536. if ( pBuf == NULL )
  4537. {
  4538. return GetLastError();
  4539. }
  4540. if ( !GetFileVersionInfo( achFilename,
  4541. dwHandle,
  4542. dwRet,
  4543. pBuf ) )
  4544. {
  4545. LocalFree( pBuf );
  4546. goto Continue;
  4547. }
  4548. if ( !VerQueryValue( pBuf,
  4549. "\\VarFileInfo\\Translation",
  4550. &pValue,
  4551. &dwLen ) )
  4552. {
  4553. LocalFree( pBuf );
  4554. goto Continue;
  4555. }
  4556. DBG_ASSERT( dwLen == sizeof( WORD * ) );
  4557. pVerTransInfo = (WORD*) pValue;
  4558. wsprintf( achBlockName,
  4559. "\\StringFileInfo\\%04hx%04hx\\FileDescription",
  4560. pVerTransInfo[ 0 ],
  4561. pVerTransInfo[ 1 ] );
  4562. if ( !VerQueryValue( pBuf,
  4563. achBlockName,
  4564. &pValue,
  4565. &dwLen ) )
  4566. {
  4567. LocalFree( pBuf );
  4568. goto Continue;
  4569. }
  4570. if ( strstr( (CHAR*) pValue, "Not for Export" ) )
  4571. {
  4572. dwServerConfiguration |= MD_SERVER_CONFIG_SSL_128;
  4573. }
  4574. else
  4575. {
  4576. dwServerConfiguration |= MD_SERVER_CONFIG_SSL_40;
  4577. }
  4578. LocalFree( pBuf );
  4579. }
  4580. Continue:
  4581. #endif
  4582. //
  4583. // Is encryption disabled due to locality?
  4584. //
  4585. if ( IsEncryptionPermitted() )
  4586. {
  4587. dwServerConfiguration |= MD_SERVER_CONFIG_ALLOW_ENCRYPT;
  4588. }
  4589. //
  4590. // Now determine whether the sub-authenticator is installed
  4591. //
  4592. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  4593. W3_AUTHENTICATOR_KEY,
  4594. 0,
  4595. KEY_READ,
  4596. &hKey ) == NO_ERROR )
  4597. {
  4598. DWORD lRet;
  4599. CHAR * pchNext;
  4600. HKEY hSubKey = NULL;
  4601. DWORD dwType;
  4602. BUFFER buf( MAX_PATH );
  4603. DWORD dwBufLen;
  4604. BOOL fSubAuthenticate = FALSE;
  4605. Retry:
  4606. dwBufLen = buf.QuerySize();
  4607. lRet = RegQueryValueEx( hKey,
  4608. "Authentication Packages",
  4609. NULL,
  4610. &dwType,
  4611. (BYTE*) buf.QueryPtr(),
  4612. &dwBufLen );
  4613. if ( lRet == ERROR_MORE_DATA )
  4614. {
  4615. if ( !buf.Resize( dwBufLen ) )
  4616. {
  4617. RegCloseKey( hKey );
  4618. return GetLastError();
  4619. }
  4620. goto Retry;
  4621. }
  4622. else if ( lRet == ERROR_SUCCESS )
  4623. {
  4624. DBG_ASSERT( dwType == REG_MULTI_SZ );
  4625. pchNext = (CHAR*) buf.QueryPtr();
  4626. while ( *pchNext != '\0' )
  4627. {
  4628. if ( RegOpenKeyEx( hKey,
  4629. pchNext,
  4630. 0,
  4631. KEY_READ,
  4632. &hSubKey ) == NO_ERROR )
  4633. {
  4634. BYTE achBuffer[ MAX_PATH ];
  4635. DWORD dwBufSize = MAX_PATH;
  4636. DWORD dwType;
  4637. if ( RegQueryValueEx( hSubKey,
  4638. "Auth132",
  4639. NULL,
  4640. &dwType,
  4641. achBuffer,
  4642. &dwBufSize ) == ERROR_SUCCESS)
  4643. {
  4644. DBG_ASSERT( dwType == REG_SZ );
  4645. if ( strstr( (CHAR*) achBuffer, "iissuba" ) != NULL )
  4646. {
  4647. fSubAuthenticate = TRUE;
  4648. }
  4649. }
  4650. RegCloseKey( hSubKey );
  4651. if ( fSubAuthenticate )
  4652. {
  4653. dwServerConfiguration |= MD_SERVER_CONFIG_AUTO_PW_SYNC;
  4654. break;
  4655. }
  4656. }
  4657. else
  4658. {
  4659. break;
  4660. }
  4661. pchNext += strlen( pchNext );
  4662. }
  4663. }
  4664. RegCloseKey( hKey );
  4665. }
  4666. if ( !mb.Open( "/LM/W3SVC/",
  4667. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) )
  4668. {
  4669. return GetLastError();
  4670. }
  4671. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  4672. MD_SERVER_CONFIGURATION_INFO,
  4673. IIS_MD_UT_SERVER,
  4674. dwServerConfiguration,
  4675. 0 ) )
  4676. {
  4677. DBG_REQUIRE( mb.Close() );
  4678. return GetLastError();
  4679. }
  4680. DBG_REQUIRE( mb.Close() );
  4681. return 0;
  4682. }
  4683. VOID
  4684. HTTP_REQUEST::CloseGetFile(
  4685. VOID
  4686. )
  4687. /*++
  4688. Routine Description:
  4689. Closes our tsunami file handle structure if we have one.
  4690. Arguments:
  4691. None
  4692. Returns:
  4693. Nothing
  4694. --*/
  4695. {
  4696. TS_OPEN_FILE_INFO * pGetFile;
  4697. //
  4698. // This function is sometimes called after an async I/O
  4699. // has been initiated, so to prevent a race we the
  4700. // completion routine we have to do this interlocked
  4701. // operation.
  4702. //
  4703. pGetFile = (TS_OPEN_FILE_INFO *) InterlockedExchangePointer(
  4704. (PVOID *)& _pGetFile,
  4705. NULL
  4706. );
  4707. if ( pGetFile ) {
  4708. DBG_REQUIRE( TsCloseURIFile(pGetFile) );
  4709. }
  4710. }
  4711. VOID
  4712. EXEC_DESCRIPTOR::ReleaseCacheInfo(
  4713. VOID
  4714. )
  4715. /*++
  4716. Routine Description:
  4717. Frees just the items we have checked out in the cache
  4718. Arguments:
  4719. None
  4720. Returns:
  4721. Nothing
  4722. --*/
  4723. {
  4724. //
  4725. // Always release AppPathURIBlob info.
  4726. //
  4727. if ( _pAppPathURIBlob != NULL)
  4728. {
  4729. if (_pAppPathURIBlob->bIsCached)
  4730. {
  4731. TsCheckInCachedBlob( _pAppPathURIBlob );
  4732. }
  4733. else
  4734. {
  4735. TsFree(_pRequest->QueryW3Instance()->GetTsvcCache(), _pAppPathURIBlob );
  4736. }
  4737. _pAppPathURIBlob = NULL;
  4738. }
  4739. if ( _pPathInfoURIBlob != NULL)
  4740. {
  4741. if (_pPathInfoURIBlob->bIsCached)
  4742. {
  4743. TsCheckInCachedBlob( _pPathInfoURIBlob );
  4744. } else
  4745. {
  4746. TsFree(_pRequest->QueryW3Instance()->GetTsvcCache(), _pPathInfoURIBlob );
  4747. }
  4748. _pPathInfoMetaData = NULL;
  4749. _pPathInfoURIBlob = NULL;
  4750. }
  4751. else
  4752. {
  4753. if (_pPathInfoMetaData != NULL)
  4754. {
  4755. TsFreeMetaData(_pPathInfoMetaData->QueryCacheInfo() );
  4756. _pPathInfoMetaData = NULL;
  4757. }
  4758. }
  4759. }
  4760. VOID
  4761. EXEC_DESCRIPTOR::Reset(
  4762. VOID
  4763. )
  4764. /*++
  4765. Routine Description:
  4766. Reset structure after usage
  4767. Arguments:
  4768. None
  4769. Returns:
  4770. Nothing
  4771. --*/
  4772. {
  4773. ReleaseCacheInfo();
  4774. _pstrURL = NULL;
  4775. _pstrPhysicalPath = NULL;
  4776. _pstrUnmappedPhysicalPath = NULL;
  4777. _pstrGatewayImage = NULL;
  4778. _pstrPathInfo = NULL;
  4779. _pstrURLParams = NULL;
  4780. _pdwScriptMapFlags = NULL;
  4781. _pGatewayType = NULL;
  4782. _pRequest = NULL;
  4783. _pParentWamRequest = NULL;
  4784. _pMetaData = NULL;
  4785. //
  4786. // if this is a child request, preserve its childishness
  4787. // (which is specified in flags member), since we need
  4788. // to set child event after this reset has occurred.
  4789. //
  4790. if ( !IsChild() ) {
  4791. _dwExecFlags = 0;
  4792. }
  4793. }
  4794. HANDLE
  4795. EXEC_DESCRIPTOR::QueryImpersonationHandle(
  4796. )
  4797. /*++
  4798. Routine Description:
  4799. returns impersonation access token for this request
  4800. Arguments:
  4801. None
  4802. Returns:
  4803. HANDLE value
  4804. --*/
  4805. {
  4806. if ( _pPathInfoMetaData &&
  4807. _pPathInfoMetaData->QueryVrAccessToken() &&
  4808. ( !_pPathInfoMetaData->QueryVrPassThrough()
  4809. || _pRequest->QueryAuthenticationObj()->IsForwardable()) )
  4810. {
  4811. return _pPathInfoMetaData->QueryVrAccessToken();
  4812. }
  4813. else
  4814. {
  4815. return _pRequest->QueryImpersonationHandle();
  4816. }
  4817. }
  4818. HANDLE
  4819. EXEC_DESCRIPTOR::QueryPrimaryHandle(
  4820. HANDLE* phDel
  4821. )
  4822. /*++
  4823. Routine Description:
  4824. returns primary access token for this request
  4825. Arguments:
  4826. phDel - updated with token to delete when usage of returned token is complete.
  4827. can be NULL if no token to delete.
  4828. Returns:
  4829. HANDLE value
  4830. --*/
  4831. {
  4832. HANDLE hDel;
  4833. HANDLE hRet;
  4834. if ( _pPathInfoMetaData &&
  4835. _pPathInfoMetaData->QueryVrAccessToken() &&
  4836. ( !_pPathInfoMetaData->QueryVrPassThrough()
  4837. || _pRequest->QueryAuthenticationObj()->IsForwardable()) )
  4838. {
  4839. hRet = _pPathInfoMetaData->QueryVrPrimaryAccessToken();
  4840. *phDel = NULL;
  4841. }
  4842. else
  4843. {
  4844. hRet = _pRequest->QueryPrimaryToken( phDel );
  4845. }
  4846. return hRet;
  4847. }
  4848. BOOL
  4849. EXEC_DESCRIPTOR::CreateChildEvent(
  4850. )
  4851. /*++
  4852. Routine Description:
  4853. Creates the child event, which will be used to signal
  4854. that a child request has completed.
  4855. Arguments:
  4856. None
  4857. Returns:
  4858. BOOL
  4859. --*/
  4860. {
  4861. DBG_ASSERT( IsChild() );
  4862. DBG_ASSERT( _hChildEvent == NULL );
  4863. _hChildEvent =
  4864. CreateEvent(
  4865. NULL, // LPSECURITY_ATTRIBUTES ptr to security attributes
  4866. TRUE, // BOOL bManualReset - flag for manual-reset event
  4867. FALSE,// BOOL bInitialState - flag for initial state
  4868. NULL // LPCTSTR lpName - ptr to event-object name
  4869. );
  4870. // Only wait for this event if marked so (after WamRequest.Bind())
  4871. _fMustWaitForChildEvent = FALSE;
  4872. return ( _hChildEvent != NULL );
  4873. }
  4874. VOID
  4875. EXEC_DESCRIPTOR::WaitForChildEvent(
  4876. )
  4877. /*++
  4878. Routine Description:
  4879. Waits for a child request to complete.
  4880. Arguments:
  4881. None
  4882. Returns:
  4883. Nothing
  4884. --*/
  4885. {
  4886. DBG_ASSERT( IsChild() );
  4887. DBG_ASSERT( _hChildEvent != NULL );
  4888. if ( _hChildEvent ) {
  4889. if ( _fMustWaitForChildEvent ) {
  4890. WaitForSingleObject( _hChildEvent, INFINITE );
  4891. }
  4892. //
  4893. // After we emerge from wait, close and null the event.
  4894. //
  4895. // Because we force a child request to execute
  4896. // synchronously to completion, we have no
  4897. // further need for the event once it becomes
  4898. // signaled.
  4899. //
  4900. CloseHandle( _hChildEvent );
  4901. _hChildEvent = NULL;
  4902. }
  4903. }
  4904. VOID
  4905. EXEC_DESCRIPTOR::SetChildEvent(
  4906. )
  4907. /*++
  4908. Routine Description:
  4909. Sets the child event, which signals that a child request
  4910. has completed.
  4911. Arguments:
  4912. None
  4913. Returns:
  4914. Nothing
  4915. --*/
  4916. {
  4917. DBG_ASSERT( IsChild() );
  4918. DBG_ASSERT( _hChildEvent != NULL );
  4919. if ( _hChildEvent ) {
  4920. DBG_ASSERT( _fMustWaitForChildEvent ); // someone better be waiting
  4921. SetEvent( _hChildEvent );
  4922. }
  4923. }
  4924. VOID
  4925. IncrErrorCount(
  4926. IMDCOM* pCom,
  4927. DWORD dwProp,
  4928. LPCSTR pszPath,
  4929. LPBOOL pbOverTheLimit
  4930. )
  4931. /*++
  4932. Routine Description:
  4933. Increment DWORD counter in metabase
  4934. Arguments:
  4935. pCom - ptr to metabase I/F
  4936. dwProp - property ID to increment, must be MD_UT_SERVER
  4937. pszPath - path in metabase,
  4938. pbOverTheLimit - updated with TRUE if counter over limit, otherwise FALSE
  4939. Returns:
  4940. Nothing
  4941. --*/
  4942. {
  4943. MB mb( pCom );
  4944. DWORD dwCnt;
  4945. *pbOverTheLimit = FALSE;
  4946. if ( mb.Open( pszPath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) )
  4947. {
  4948. if ( !mb.GetDword(
  4949. "",
  4950. dwProp,
  4951. IIS_MD_UT_SERVER,
  4952. &dwCnt
  4953. ) )
  4954. {
  4955. dwCnt = 0;
  4956. }
  4957. mb.SetDword(
  4958. "",
  4959. dwProp,
  4960. IIS_MD_UT_SERVER,
  4961. dwCnt + 1
  4962. );
  4963. mb.Close();
  4964. }
  4965. }
  4966. VOID
  4967. HTTP_REQUEST::CheckValidAuth(
  4968. )
  4969. /*++
  4970. Routine Description:
  4971. Check that current authentication method valid for the current URL
  4972. Arguments:
  4973. None
  4974. --*/
  4975. {
  4976. BOOL fEnab;
  4977. //
  4978. // Check valid auth type for this URI
  4979. //
  4980. if ( IsLoggedOn() )
  4981. {
  4982. if ( _fAnonymous )
  4983. {
  4984. fEnab = MD_AUTH_ANONYMOUS & QueryAuthentication();
  4985. }
  4986. else if ( _fClearTextPass )
  4987. {
  4988. fEnab = MD_AUTH_BASIC & QueryAuthentication();
  4989. }
  4990. else if ( _fAuthTypeDigest )
  4991. {
  4992. fEnab = MD_AUTH_MD5 & QueryAuthentication();
  4993. }
  4994. else if ( _fAuthSystem )
  4995. {
  4996. fEnab = TRUE;
  4997. }
  4998. else if ( _fAuthCert )
  4999. {
  5000. fEnab = (((HTTP_REQUEST*)this)->GetFilePerms()) & MD_ACCESS_MAP_CERT;
  5001. }
  5002. else if ( _fMappedAcct )
  5003. {
  5004. fEnab = MD_AUTH_MAPBASIC & QueryAuthentication();
  5005. }
  5006. else
  5007. {
  5008. if ( fEnab = MD_AUTH_NT & QueryAuthentication() )
  5009. {
  5010. // check SSPI package is valid
  5011. fEnab = FALSE;
  5012. const LPSTR* apszNTProviders = _pMetaData->QueryNTProviders();
  5013. DWORD i;
  5014. for ( i = 0 ; apszNTProviders[i] ; ++i )
  5015. {
  5016. if ( !strcmp( _strAuthType.QueryStr(), apszNTProviders[i] ) )
  5017. {
  5018. fEnab = TRUE;
  5019. break;
  5020. }
  5021. }
  5022. }
  5023. }
  5024. if ( !fEnab )
  5025. {
  5026. if ( _fAnonymous )
  5027. {
  5028. QueryW3StatsObj()->DecrCurrentAnonymousUsers();
  5029. }
  5030. else
  5031. {
  5032. QueryW3StatsObj()->DecrCurrentNonAnonymousUsers();
  5033. }
  5034. ResetAuth( FALSE );
  5035. }
  5036. }
  5037. }
  5038. /*++
  5039. Routine Description:
  5040. returns ApplicationPath for this EXEC. For those ISAPI.dll that
  5041. are only runnable in process, _pAppPathURIBlob->bInProcOnly will be true.
  5042. Therefore, the default application path is returned.
  5043. Example: .STM file.
  5044. Arguments:
  5045. None
  5046. Returns:
  5047. a pointer to STR that contains application path.
  5048. --*/
  5049. STR * EXEC_DESCRIPTOR::QueryAppPath
  5050. (
  5051. void
  5052. )
  5053. {
  5054. STR * pstr = NULL;
  5055. DBG_REQUIRE(_pAppPathURIBlob != NULL);
  5056. DBG_REQUIRE(_pMetaData != NULL);
  5057. //
  5058. // InProcOnly ISAPI gets the default application path.
  5059. // Otherwise, use the application path come from MetaData.
  5060. //
  5061. if (_pAppPathURIBlob->bInProcOnly)
  5062. {
  5063. pstr = g_pWamDictator->QueryDefaultAppPath();
  5064. }
  5065. else
  5066. {
  5067. pstr = _pMetaData->QueryAppPath();
  5068. }
  5069. DBG_ASSERT(pstr != NULL);
  5070. return pstr;
  5071. }