Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1833 lines
52 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ssi_include_file.cxx
  5. Abstract:
  6. This module contains the server side include processing code. We
  7. aim for support as specified by iis\spec\ssi.doc. The code is based
  8. on existing SSI support done in iis\svcs\w3\gateways\ssinc\ssinc.cxx.
  9. A STM file may include other files. Each of those files is represented
  10. by SSI_INCLUDE_FILE class instance
  11. Most of the output generation is happening in this file
  12. Author:
  13. Ming Lu (MingLu) 5-Apr-2000
  14. Revision history
  15. Jaroslad Dec-2000
  16. - modified to execute asynchronously
  17. Jaroslad Apr-2001
  18. - added VectorSend support, keepalive, split to multiple source files
  19. --*/
  20. #include "precomp.hxx"
  21. //
  22. // This is a list of #ECHO variables not supported by ISAPI
  23. //
  24. struct _SSI_VAR_MAP
  25. {
  26. CHAR * pszMap;
  27. DWORD cchMap;
  28. SSI_VARS ssiMap;
  29. }
  30. SSIVarMap[] =
  31. {
  32. "DOCUMENT_NAME", 13, SSI_VAR_DOCUMENT_NAME,
  33. "DOCUMENT_URI", 12, SSI_VAR_DOCUMENT_URI,
  34. "QUERY_STRING_UNESCAPED", 22, SSI_VAR_QUERY_STRING_UNESCAPED,
  35. "DATE_LOCAL", 10, SSI_VAR_DATE_LOCAL,
  36. "DATE_GMT", 8, SSI_VAR_DATE_GMT,
  37. "LAST_MODIFIED", 13, SSI_VAR_LAST_MODIFIED,
  38. NULL, 0, SSI_VAR_UNKNOWN
  39. };
  40. //
  41. // SSI_INCLUDE_FILE methods implementation
  42. //
  43. //static
  44. ALLOC_CACHE_HANDLER * SSI_INCLUDE_FILE::sm_pachSsiIncludeFiles = NULL;
  45. SSI_INCLUDE_FILE::~SSI_INCLUDE_FILE( VOID )
  46. /*++
  47. Routine Description:
  48. Destructor
  49. --*/
  50. {
  51. if ( _pSsiFile != NULL )
  52. {
  53. _pSsiFile->DereferenceSsiFile();
  54. _pSsiFile = NULL;
  55. }
  56. }
  57. // static
  58. HRESULT
  59. SSI_INCLUDE_FILE::CreateInstance(
  60. IN STRU & strFilename,
  61. IN STRU & strURL,
  62. IN SSI_REQUEST * pRequest,
  63. IN SSI_INCLUDE_FILE * pParent,
  64. OUT SSI_INCLUDE_FILE ** ppSsiIncludeFile
  65. )
  66. /*++
  67. Routine Description:
  68. Instantiate SSI_INCLUDE_FILE
  69. Arguments:
  70. Return Value:
  71. HRESULT
  72. --*/
  73. {
  74. HRESULT hr = E_FAIL;
  75. SSI_INCLUDE_FILE * pSsiIncludeFile = NULL;
  76. DBG_ASSERT( pRequest != NULL );
  77. pSsiIncludeFile = new SSI_INCLUDE_FILE( pRequest,
  78. pParent );
  79. if ( pSsiIncludeFile == NULL )
  80. {
  81. return HRESULT( ERROR_OUTOFMEMORY );
  82. }
  83. hr = pSsiIncludeFile->Initialize( strFilename,
  84. strURL );
  85. if ( FAILED( hr ) )
  86. {
  87. goto failed;
  88. }
  89. //
  90. // pass created SSI_INCLUDE_FILE through OUT parameter
  91. //
  92. *ppSsiIncludeFile = pSsiIncludeFile;
  93. return S_OK;
  94. failed:
  95. DBG_ASSERT( FAILED( hr ) );
  96. if ( pSsiIncludeFile != NULL )
  97. {
  98. delete pSsiIncludeFile;
  99. pSsiIncludeFile = NULL;
  100. }
  101. *ppSsiIncludeFile = NULL;
  102. return hr;
  103. }
  104. HRESULT
  105. SSI_INCLUDE_FILE::Initialize(
  106. IN STRU & strFilename,
  107. IN STRU & strURL
  108. )
  109. /*++
  110. Routine Description:
  111. Private initialization rouinte for CreateInstance
  112. Arguments:
  113. Return Value:
  114. HRESULT
  115. --*/
  116. {
  117. HRESULT hr = E_FAIL;
  118. if ( FAILED( hr = _strFilename.Copy( strFilename ) ) )
  119. {
  120. goto failed;
  121. }
  122. if ( FAILED( hr =_strURL.Copy( strURL ) ) )
  123. {
  124. goto failed;
  125. }
  126. if ( FAILED( hr =_strTimeFmt.Copy( SSI_DEF_TIMEFMT ) ) )
  127. {
  128. goto failed;
  129. }
  130. //
  131. // get the SSI_FILE (it enables acces to file data and
  132. // also contains SSI_ELEMENT_LIST
  133. //
  134. hr = SSI_FILE::GetReferencedInstance(
  135. _strFilename,
  136. _pRequest->GetUserToken(),
  137. &_pSsiFile );
  138. if ( FAILED( hr ) )
  139. {
  140. goto failed;
  141. }
  142. SetState( SIF_STATE_READY );
  143. return S_OK;
  144. failed:
  145. DBG_ASSERT( FAILED( hr ) );
  146. //
  147. // In the case of error set state to Processed
  148. // Note: Don't set state to SIF_STATE_COMPLETED because that would cause
  149. // buffered data from the response not be sent
  150. // (including the last error message)
  151. //
  152. SetState( SIF_STATE_PROCESSED );
  153. //
  154. // leftover data structures will be cleaned up in the destructor
  155. //
  156. return hr;
  157. }
  158. HRESULT
  159. SSI_INCLUDE_FILE::DoWork(
  160. IN HRESULT hrLastOp,
  161. OUT BOOL *pfAsyncPending
  162. )
  163. /*++
  164. Routine Description:
  165. This method walks the element list sending the appropriate chunks of
  166. data
  167. Arguments:
  168. hrLastOp - error of the last asynchronous operation
  169. pfAsyncPending - TRUE if there is pending async operation
  170. Return Value:
  171. HRESULT
  172. --*/
  173. {
  174. HSE_EXEC_URL_STATUS ExecUrlStatus;
  175. SSI_ELEMENT_ITEM * pSEI;
  176. DWORD dwID = 0;
  177. LPSTR apszParms[ 2 ] = { NULL, NULL };
  178. CHAR achNumberBuffer[ SSI_MAX_NUMBER_STRING ];
  179. HRESULT hr = E_FAIL;
  180. DBG_ASSERT( _pRequest != NULL );
  181. DBG_ASSERT( pfAsyncPending != NULL );
  182. *pfAsyncPending = FALSE;
  183. if ( FAILED( hrLastOp ) )
  184. {
  185. //
  186. // Last asynchronous operation failed
  187. // only in the state: SIF_STATE_EXEC_URL_PENDING we will proceed if error occured
  188. //
  189. if ( _State != SIF_STATE_EXEC_URL_PENDING )
  190. {
  191. //
  192. // Completion error means that we are forced to finish processing
  193. //
  194. SetState( SIF_STATE_COMPLETED );
  195. hr = hrLastOp;
  196. }
  197. }
  198. while( _State != SIF_STATE_COMPLETED )
  199. {
  200. switch( _State )
  201. {
  202. case SIF_STATE_READY:
  203. //
  204. // All the necessary data structures are ready
  205. //
  206. // Set the Response Headers and switch to PROCESSING state
  207. //
  208. //
  209. // Set response headers if we are in the top level INCLUDE file
  210. //
  211. SetState( SIF_STATE_PROCESSING );
  212. if ( IsBaseFile() )
  213. {
  214. if ( _pSsiFile->QueryHasDirectives() )
  215. {
  216. //
  217. // there are SSI directives to be processesed
  218. // send only SSI_HEADER
  219. //
  220. return _pRequest->GetVectorBuffer().AddVectorHeaders(
  221. SSI_HEADER );
  222. }
  223. else
  224. {
  225. //
  226. // There are no SSI directives, we will be able to send
  227. // the whole response at once
  228. //
  229. CHAR * pszResponseHeaders = NULL;
  230. BOOL fIncludesContentLength = FALSE;
  231. DWORD cbFileSize = 0;
  232. hr = _pSsiFile->GetResponseHeaders( &pszResponseHeaders,
  233. &fIncludesContentLength );
  234. if( FAILED( hr ) )
  235. {
  236. return hr;
  237. }
  238. DBG_ASSERT( pszResponseHeaders != NULL );
  239. hr = _pRequest->GetVectorBuffer().AddVectorHeaders(
  240. pszResponseHeaders,
  241. fIncludesContentLength );
  242. if( FAILED( hr ) )
  243. {
  244. return hr;
  245. }
  246. // Optimization:
  247. // If there are no directives in the file then AddToVector
  248. // and set state to complete.
  249. //
  250. hr = _pSsiFile->SSIGetFileSize( &cbFileSize );
  251. if( FAILED( hr ) )
  252. {
  253. return hr;
  254. }
  255. if ( _pSsiFile->SSIGetFileData() != NULL )
  256. {
  257. //
  258. // file cached in memory
  259. //
  260. hr = _pRequest->GetVectorBuffer().AddToVector(
  261. (PCHAR)_pSsiFile->SSIGetFileData(),
  262. cbFileSize );
  263. }
  264. else
  265. {
  266. hr = _pRequest->GetVectorBuffer().AddFileChunkToVector(
  267. 0,
  268. cbFileSize,
  269. _pSsiFile->SSIGetFileHandle() );
  270. }
  271. if ( FAILED( hr ) )
  272. {
  273. return( hr );
  274. }
  275. SetState( SIF_STATE_PROCESSED );
  276. }
  277. }
  278. break;
  279. case SIF_STATE_PROCESSING:
  280. //
  281. // There are few cases when ProcessElements() will return
  282. // a) request completed
  283. // b) pending operation
  284. // c) child include file to be processed
  285. //
  286. // in any case return back to caller
  287. //
  288. hr = ProcessSsiElements( pfAsyncPending );
  289. if ( SUCCEEDED( hr ) &&
  290. !*pfAsyncPending )
  291. {
  292. if (_State == SIF_STATE_INCLUDE_CHILD_PENDING)
  293. {
  294. return hr;
  295. }
  296. break;
  297. }
  298. else
  299. {
  300. return hr;
  301. }
  302. case SIF_STATE_INCLUDE_CHILD_PENDING:
  303. //
  304. // Child include completed. Restore processing of current include file
  305. //
  306. SetState( SIF_STATE_PROCESSING );
  307. break;
  308. case SIF_STATE_EXEC_URL_PENDING:
  309. //
  310. // We were able to spawn child request. Get the status
  311. //
  312. pSEI = CONTAINING_RECORD( _pCurrentEntry, SSI_ELEMENT_ITEM, _ListEntry );
  313. if( _pRequest->GetECB()->ServerSupportFunction(
  314. _pRequest->GetECB()->ConnID,
  315. HSE_REQ_GET_EXEC_URL_STATUS,
  316. &ExecUrlStatus,
  317. NULL,
  318. NULL
  319. ) )
  320. {
  321. if ( ExecUrlStatus.uHttpStatusCode >= 400 )
  322. {
  323. apszParms[ 0 ] = ( CHAR* )pSEI->QueryTagValue()->QueryStr();
  324. if ( ExecUrlStatus.uHttpStatusCode == 403 )
  325. {
  326. dwID = SSINCMSG_NO_EXECUTE_PERMISSION;
  327. }
  328. else if ( ExecUrlStatus.dwWin32Error != 0 )
  329. {
  330. //
  331. // If there was a Win32 error return that rather than
  332. // HTTP error
  333. //
  334. switch( pSEI->QueryTag() )
  335. {
  336. case SSI_TAG_CMD:
  337. dwID = SSINCMSG_CANT_EXEC_CMD;
  338. break;
  339. case SSI_TAG_CGI:
  340. dwID = SSINCMSG_CANT_EXEC_CGI;
  341. break;
  342. case SSI_TAG_ISA:
  343. dwID = SSINCMSG_CANT_EXEC_ISA;
  344. break;
  345. default:
  346. DBG_ASSERT( FALSE );
  347. break;
  348. }
  349. _itoa( ExecUrlStatus.dwWin32Error, achNumberBuffer, 10 );
  350. apszParms[ 1 ] = achNumberBuffer;
  351. }
  352. else
  353. {
  354. dwID = SSINCMSG_CANT_EXEC_CGI_REPORT_HTTP_STATUS;
  355. _itoa( ExecUrlStatus.uHttpStatusCode, achNumberBuffer, 10 );
  356. apszParms[ 1 ] = achNumberBuffer;
  357. }
  358. }
  359. else
  360. {
  361. dwID = 0;
  362. }
  363. }
  364. else
  365. {
  366. _ultoa( GetLastError(), achNumberBuffer, 10 );
  367. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->QueryStr();
  368. apszParms[ 1 ] = achNumberBuffer;
  369. dwID = SSINCMSG_CANT_EXEC_CGI;
  370. }
  371. //
  372. // EXEC_URL completed. Adjust State back to PROCESSING
  373. //
  374. SetState( SIF_STATE_PROCESSING );
  375. if ( dwID != 0 )
  376. {
  377. hr = _pRequest->SSISendError( dwID, apszParms );
  378. if ( FAILED (hr) )
  379. {
  380. return hr;
  381. }
  382. }
  383. break;
  384. case SIF_STATE_VECTOR_SEND_PENDING:
  385. //
  386. // Vector was sent. It's time to reset Vector data and continue processing
  387. //
  388. if ( FAILED( hr = _pRequest->GetVectorBuffer().Reset() ) )
  389. {
  390. return hr;
  391. }
  392. SetState( SIF_STATE_PROCESSING );
  393. break;
  394. case SIF_STATE_PROCESSED:
  395. //
  396. // All the data was processed
  397. // Make the VectorSend only for the Base file
  398. //
  399. //
  400. if ( IsBaseFile() )
  401. {
  402. SetState( SIF_STATE_COMPLETE_PENDING );
  403. hr = _pRequest->GetVectorBuffer().VectorSend(
  404. pfAsyncPending,
  405. TRUE /*FinalSend*/ );
  406. }
  407. else
  408. {
  409. //
  410. // not a base file
  411. // SSI_INCLUDE_FILE processing is completed
  412. // however sending the response will happen later
  413. // (either at the very end for the Base file
  414. // of whenever ExecuteUrl is called)
  415. //
  416. SetState( SIF_STATE_COMPLETED );
  417. break;
  418. }
  419. if( FAILED(hr) || !*pfAsyncPending )
  420. {
  421. SetState( SIF_STATE_COMPLETED );
  422. _pRequest->GetVectorBuffer().Reset();
  423. }
  424. else
  425. {
  426. // wait for the completion of VectorSend() or bail out on error
  427. return hr;
  428. }
  429. break;
  430. case SIF_STATE_COMPLETE_PENDING:
  431. //
  432. // last VectorSend for SSI_INCLUDE_FILE of Sending Custom Error
  433. // completed
  434. //
  435. if ( FAILED( hr = _pRequest->GetVectorBuffer().Reset() ) )
  436. {
  437. return hr;
  438. }
  439. SetState( SIF_STATE_COMPLETED );
  440. hr = S_OK;
  441. break;
  442. default:
  443. //
  444. // Unexpected State
  445. //
  446. DBG_ASSERT( _State > SIF_STATE_UNINITIALIZED && _State < SIF_STATE_UNKNOWN );
  447. return E_FAIL;
  448. } // switch( _State )
  449. }
  450. return hr;
  451. }
  452. HRESULT
  453. SSI_INCLUDE_FILE::ProcessSsiElements(
  454. OUT BOOL * pfAsyncPending
  455. )
  456. /*++
  457. Routine Description:
  458. This method walks the element list sending the appropriate chunks of
  459. data
  460. Arguments:
  461. Return Value:
  462. HRESULT
  463. --*/
  464. {
  465. STACK_STRU( strPath, SSI_DEFAULT_PATH_SIZE + 1 );
  466. SSI_ELEMENT_ITEM * pSEI;
  467. DWORD dwID;
  468. LPSTR apszParms[ 2 ];
  469. CHAR achNumberBuffer[ SSI_MAX_NUMBER_STRING ];
  470. HRESULT hr = E_FAIL;
  471. SSI_ELEMENT_LIST * pSEL = NULL;
  472. SSI_EXEC_TYPE ssiExecType;
  473. DBG_ASSERT( _pRequest != NULL );
  474. DBG_ASSERT( _pSsiFile != NULL );
  475. pSEL = _pSsiFile->GetSsiElementList();
  476. if ( pSEL == NULL )
  477. {
  478. //
  479. // empty list means empty file
  480. //
  481. SetState( SIF_STATE_PROCESSED );
  482. return NO_ERROR;
  483. }
  484. DBG_ASSERT( pSEL != NULL );
  485. if( _pCurrentEntry == NULL )
  486. {
  487. _pCurrentEntry = pSEL->QueryListHead();
  488. }
  489. //
  490. // Move CurrentEntry pointer to next element
  491. //
  492. _pCurrentEntry = _pCurrentEntry->Flink;
  493. //
  494. // Loop through each element and take the appropriate action
  495. //
  496. while( _pCurrentEntry != pSEL->QueryListHead() )
  497. {
  498. DBG_ASSERT( _State == SIF_STATE_PROCESSING );
  499. pSEI = CONTAINING_RECORD( _pCurrentEntry,
  500. SSI_ELEMENT_ITEM,
  501. _ListEntry );
  502. DBG_ASSERT( pSEI->CheckSignature() );
  503. //
  504. // Initialize error structures
  505. //
  506. dwID = 0;
  507. apszParms[ 0 ] = NULL;
  508. apszParms[ 1 ] = NULL;
  509. switch ( pSEI->QueryCommand() )
  510. {
  511. case SSI_CMD_BYTERANGE:
  512. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_BYTERANGE element, SSI_FILE %S, offset %d\n", _strFilename.QueryStr(), pSEI->QueryBegin()));
  513. //
  514. // SSIGetFileData() may be NULL if file is too large to be cached
  515. // in memory by w3core FILE CACHE
  516. //
  517. if ( _pSsiFile->SSIGetFileData() != NULL )
  518. {
  519. if ( FAILED( hr = _pRequest->GetVectorBuffer().AddToVector(
  520. (PCHAR)_pSsiFile->SSIGetFileData() +
  521. pSEI->QueryBegin(),
  522. pSEI->QueryLength() ) ) )
  523. {
  524. return hr;
  525. }
  526. }
  527. else
  528. {
  529. //
  530. // if file is not cached in memory then file handle must be available
  531. //
  532. DBG_ASSERT( _pSsiFile->SSIGetFileHandle() != NULL );
  533. if ( FAILED( hr = _pRequest->GetVectorBuffer().AddFileChunkToVector(
  534. pSEI->QueryBegin(),
  535. pSEI->QueryLength(),
  536. _pSsiFile->SSIGetFileHandle() ) ) )
  537. {
  538. return hr;
  539. }
  540. }
  541. break;
  542. case SSI_CMD_INCLUDE:
  543. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_INCLUDE element, SSI_FILE %S\n", _strFilename.QueryStr()));
  544. switch ( pSEI->QueryTag() )
  545. {
  546. case SSI_TAG_FILE:
  547. case SSI_TAG_VIRTUAL:
  548. {
  549. STACK_STRU( strFullURL, SSI_DEFAULT_URL_SIZE + 1 );
  550. if ( FAILED ( hr = GetFullPath( pSEI,
  551. &strPath,
  552. HSE_URL_FLAGS_READ,
  553. &_strURL,
  554. &strFullURL ) ) )
  555. {
  556. _ultoa( WIN32_FROM_HRESULT( hr ),
  557. achNumberBuffer,
  558. 10 );
  559. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  560. QueryStr();
  561. apszParms[ 1 ] = achNumberBuffer;
  562. dwID = SSINCMSG_ERROR_HANDLING_FILE;
  563. break;
  564. }
  565. if ( IsRecursiveInclude( strPath ) )
  566. {
  567. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  568. QueryStr();
  569. apszParms[ 1 ] = NULL;
  570. dwID = SSINCMSG_ERROR_RECURSIVE_INCLUDE;
  571. break;
  572. }
  573. //
  574. // Nested STM include
  575. //
  576. SSI_INCLUDE_FILE * pChild = NULL;
  577. hr = SSI_INCLUDE_FILE::CreateInstance(
  578. strPath,
  579. strFullURL,
  580. _pRequest,
  581. this, /*Parent*/
  582. &pChild );
  583. if ( FAILED( hr ) )
  584. {
  585. _ultoa( WIN32_FROM_HRESULT( hr ), achNumberBuffer, 10 );
  586. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  587. QueryStr();
  588. apszParms[ 1 ] = achNumberBuffer;
  589. dwID = SSINCMSG_ERROR_HANDLING_FILE;
  590. break;
  591. }
  592. //
  593. // _pRequest is taking over the ownership of the pChild
  594. // it will control it's lifetime
  595. //
  596. _pRequest->SetCurrentIncludeFile( pChild );
  597. SetState( SIF_STATE_INCLUDE_CHILD_PENDING );
  598. //
  599. // Return back to SSI_REQUEST (_pRequest)
  600. // SSI_REQUEST will start executing it just added SSI_INCLUDE_FILE (pChild)
  601. //
  602. // This way recursive function calls that were used in the previous
  603. // implementation can be avoided (to makes possible to implement
  604. // asynchronous processing)
  605. //
  606. return NO_ERROR;
  607. }
  608. default:
  609. dwID = SSINCMSG_INVALID_TAG;
  610. }
  611. break;
  612. case SSI_CMD_FLASTMOD:
  613. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_FLASTMOD element, SSI_FILE %S\n", _strFilename.QueryStr()));
  614. switch( pSEI->QueryTag() )
  615. {
  616. case SSI_TAG_FILE:
  617. case SSI_TAG_VIRTUAL:
  618. if ( FAILED( hr = GetFullPath( pSEI,
  619. &strPath,
  620. 0,
  621. &_strURL ) ) ||
  622. FAILED( hr = DoFLastMod( &strPath ) ) )
  623. {
  624. _ultoa( WIN32_FROM_HRESULT( hr ),
  625. achNumberBuffer,
  626. 10 );
  627. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  628. QueryStr();
  629. apszParms[ 1 ] = achNumberBuffer;
  630. dwID = SSINCMSG_CANT_DO_FLASTMOD;
  631. }
  632. break;
  633. default:
  634. dwID = SSINCMSG_INVALID_TAG;
  635. }
  636. break;
  637. case SSI_CMD_CONFIG:
  638. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_CONFIG element, SSI_FILE %S\n", _strFilename.QueryStr()));
  639. switch( pSEI->QueryTag() )
  640. {
  641. case SSI_TAG_ERRMSG:
  642. if ( !_pRequest->SetUserErrorMessage(
  643. pSEI->QueryTagValue() ) )
  644. {
  645. dwID = SSINCMSG_INVALID_TAG;
  646. }
  647. break;
  648. case SSI_TAG_TIMEFMT:
  649. if ( pSEI->QueryTagValue()->IsEmpty() ||
  650. FAILED( _strTimeFmt.Resize( pSEI->QueryTagValue()->QueryCCH() ) ) )
  651. {
  652. dwID = SSINCMSG_INVALID_TAG;
  653. }
  654. if ( FAILED( hr = _strTimeFmt.Copy( pSEI->QueryTagValue()->QueryStr() ) ) )
  655. {
  656. return hr;
  657. }
  658. break;
  659. case SSI_TAG_SIZEFMT:
  660. if ( _strnicmp( SSI_DEF_BYTES,
  661. ( CHAR * )pSEI->QueryTagValue()->QueryStr(),
  662. SSI_DEF_BYTES_LEN ) == 0 )
  663. {
  664. _fSizeFmtBytes = TRUE;
  665. }
  666. else if ( _strnicmp( SSI_DEF_ABBREV,
  667. ( CHAR * )pSEI->QueryTagValue()->QueryStr(),
  668. SSI_DEF_ABBREV_LEN ) == 0 )
  669. {
  670. _fSizeFmtBytes = FALSE;
  671. }
  672. else
  673. {
  674. dwID = SSINCMSG_INVALID_TAG;
  675. }
  676. break;
  677. default:
  678. dwID = SSINCMSG_INVALID_TAG;
  679. }
  680. break;
  681. case SSI_CMD_FSIZE:
  682. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_FSIZE element, SSI_FILE %S,\n", _strFilename.QueryStr()));
  683. switch( pSEI->QueryTag() )
  684. {
  685. case SSI_TAG_FILE:
  686. case SSI_TAG_VIRTUAL:
  687. if ( FAILED ( hr = GetFullPath( pSEI,
  688. &strPath,
  689. 0,
  690. &_strURL ) ) ||
  691. FAILED ( hr = DoFSize( &strPath ) ) )
  692. {
  693. _ultoa( WIN32_FROM_HRESULT( hr ),
  694. achNumberBuffer,
  695. 10 );
  696. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  697. QueryStr();
  698. apszParms[ 1 ] = achNumberBuffer;
  699. dwID = SSINCMSG_CANT_DO_FSIZE;
  700. }
  701. break;
  702. default:
  703. dwID = SSINCMSG_INVALID_TAG;
  704. }
  705. break;
  706. case SSI_CMD_ECHO:
  707. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_ECHO element, SSI_FILE %S\n", _strFilename.QueryStr()));
  708. if ( pSEI->QueryTag() == SSI_TAG_VAR )
  709. {
  710. // First let ISAPI try to evaluate variable.
  711. hr = DoEchoISAPIVariable( pSEI->QueryTagValue() );
  712. if ( SUCCEEDED (hr ) )
  713. {
  714. break;
  715. }
  716. else
  717. {
  718. DWORD dwVar;
  719. HRESULT hrEcho = E_FAIL;
  720. // if ISAPI couldn't resolve var, try internal list
  721. if ( !FindInternalVariable( pSEI->QueryTagValue(),
  722. &dwVar ) )
  723. {
  724. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->
  725. QueryStr();
  726. apszParms[ 1 ] = NULL;
  727. dwID = SSINCMSG_CANT_FIND_VARIABLE;
  728. }
  729. else
  730. {
  731. switch( dwVar )
  732. {
  733. case SSI_VAR_DOCUMENT_NAME:
  734. hrEcho = DoEchoDocumentName();
  735. break;
  736. case SSI_VAR_DOCUMENT_URI:
  737. hrEcho = DoEchoDocumentURI();
  738. break;
  739. case SSI_VAR_QUERY_STRING_UNESCAPED:
  740. hrEcho = DoEchoQueryStringUnescaped();
  741. break;
  742. case SSI_VAR_DATE_LOCAL:
  743. hrEcho = DoEchoDateLocal();
  744. break;
  745. case SSI_VAR_DATE_GMT:
  746. hrEcho = DoEchoDateGMT();
  747. break;
  748. case SSI_VAR_LAST_MODIFIED:
  749. hrEcho = DoEchoLastModified();
  750. break;
  751. default:
  752. apszParms[ 0 ] = ( CHAR * )pSEI->
  753. QueryTagValue()->QueryStr();
  754. apszParms[ 1 ] = NULL;
  755. dwID = SSINCMSG_CANT_FIND_VARIABLE;
  756. }
  757. if ( FAILED ( hrEcho ) )
  758. {
  759. apszParms[ 0 ] = ( CHAR * )pSEI->
  760. QueryTagValue()->QueryStr();
  761. apszParms[ 1 ] = NULL;
  762. dwID = SSINCMSG_CANT_EVALUATE_VARIABLE;
  763. }
  764. }
  765. }
  766. }
  767. else
  768. {
  769. dwID = SSINCMSG_INVALID_TAG;
  770. }
  771. break;
  772. case SSI_CMD_EXEC:
  773. // DBGPRINTF((DBG_CONTEXT, "SSI_CMD_EXEC element, SSI_FILE %S\n", _strFilename.QueryStr()));
  774. ssiExecType = SSI_EXEC_UNKNOWN;
  775. if ( _pRequest->IsExecDisabled() )
  776. {
  777. dwID = SSINCMSG_EXEC_DISABLED;
  778. }
  779. else if ( pSEI->QueryTag() == SSI_TAG_CMD )
  780. {
  781. if ( !_pRequest->IsCmdDirectiveEnabled() )
  782. {
  783. dwID = SSINCMSG_CMD_NOT_ENABLED;
  784. }
  785. else
  786. {
  787. ssiExecType = SSI_EXEC_CMD;
  788. }
  789. }
  790. else if ( pSEI->QueryTag() == SSI_TAG_CGI )
  791. {
  792. ssiExecType = SSI_EXEC_CGI;
  793. }
  794. else if ( pSEI->QueryTag() == SSI_TAG_ISA )
  795. {
  796. ssiExecType = SSI_EXEC_ISA;
  797. }
  798. else
  799. {
  800. dwID = SSINCMSG_INVALID_TAG;
  801. }
  802. if ( ssiExecType != SSI_EXEC_UNKNOWN )
  803. {
  804. BOOL fOk = FALSE;
  805. //
  806. // We have to send all of the currently buffered Vector data
  807. // before we start with Child Execute
  808. //
  809. // If there is data to be sent then VectorSend will execute
  810. // asynchronously and we can proceed with EXEC_URL
  811. // only after it is completed. That's why we have to move
  812. // _pCurrentEntry back by one
  813. // After VectorSend completes, then the next call to VectorSend
  814. // will not have to send anything and it completes right away
  815. // and we can continue with EXEC_URL
  816. //
  817. // _pCurrentEntry must be changed before VectorSend is called
  818. // because VectorSend could complete on different thread
  819. // before this thread completed with the request
  820. //
  821. SetState( SIF_STATE_VECTOR_SEND_PENDING );
  822. _pCurrentEntry = _pCurrentEntry->Blink;
  823. hr = _pRequest->GetVectorBuffer().VectorSend( pfAsyncPending );
  824. if( SUCCEEDED( hr ) && !*pfAsyncPending )
  825. {
  826. SetState( SIF_STATE_PROCESSING );
  827. _pRequest->GetVectorBuffer().Reset();
  828. //
  829. // Restore _pCurrentEntry
  830. //
  831. _pCurrentEntry = _pCurrentEntry->Flink;
  832. }
  833. else
  834. {
  835. //
  836. // Wait for the completion of VectorSend()
  837. // or bail out on error
  838. //
  839. return hr;
  840. }
  841. ZeroMemory( &_ExecUrlInfo,
  842. sizeof( _ExecUrlInfo ) );
  843. //
  844. // Make asynchronous Child Execute
  845. //
  846. _ExecUrlInfo.dwExecUrlFlags = HSE_EXEC_URL_NO_HEADERS |
  847. HSE_EXEC_URL_DISABLE_CUSTOM_ERROR |
  848. HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE;
  849. if ( ssiExecType == SSI_EXEC_CMD )
  850. {
  851. _ExecUrlInfo.dwExecUrlFlags |= HSE_EXEC_URL_SSI_CMD;
  852. }
  853. _ExecUrlInfo.pszUrl = (LPSTR) pSEI->QueryTagValue()->QueryStr();
  854. DBG_ASSERT( _ExecUrlInfo.pszUrl != NULL );
  855. //
  856. // Avoid execution of empty URL
  857. //
  858. if ( _ExecUrlInfo.pszUrl[0] == '\0' )
  859. {
  860. _ultoa( ERROR_INVALID_NAME, achNumberBuffer, 10 );
  861. apszParms[ 0 ] = _ExecUrlInfo.pszUrl;
  862. apszParms[ 1 ] = achNumberBuffer;
  863. switch( pSEI->QueryTag() )
  864. {
  865. case SSI_TAG_CMD:
  866. dwID = SSINCMSG_CANT_EXEC_CMD;
  867. break;
  868. case SSI_TAG_CGI:
  869. dwID = SSINCMSG_CANT_EXEC_CGI;
  870. break;
  871. case SSI_TAG_ISA:
  872. dwID = SSINCMSG_CANT_EXEC_ISA;
  873. break;
  874. default:
  875. DBG_ASSERT( FALSE );
  876. break;
  877. }
  878. break;
  879. }
  880. SetState( SIF_STATE_EXEC_URL_PENDING );
  881. fOk = _pRequest->GetECB()->ServerSupportFunction(
  882. _pRequest->GetECB()->ConnID,
  883. HSE_REQ_EXEC_URL,
  884. &_ExecUrlInfo,
  885. NULL,
  886. NULL
  887. );
  888. if ( !fOk )
  889. {
  890. SetState( SIF_STATE_PROCESSING );
  891. _ultoa( GetLastError(), achNumberBuffer, 10 );
  892. apszParms[ 0 ] = ( CHAR * )pSEI->QueryTagValue()->QueryStr();
  893. apszParms[ 1 ] = achNumberBuffer;
  894. dwID = SSINCMSG_CANT_EXEC_CGI;
  895. }
  896. else
  897. {
  898. hr = S_OK;
  899. *pfAsyncPending = TRUE;
  900. return hr;
  901. }
  902. }
  903. break;
  904. default:
  905. // DBGPRINTF((DBG_CONTEXT, "unknown element, SSI_FILE %S\n", _strFilename.QueryStr()));
  906. dwID = SSINCMSG_NOT_SUPPORTED;
  907. break;
  908. }
  909. if ( dwID )
  910. {
  911. hr = _pRequest->SSISendError( dwID, apszParms );
  912. if ( FAILED (hr) )
  913. {
  914. return hr;
  915. }
  916. }
  917. //
  918. // Before moving to next element of SSI_ELEMENT_LIST
  919. // check if there is a need to flush currently buffered VectorSend
  920. //
  921. if ( _pRequest->GetVectorBuffer().QueryCurrentNumberOfElements() > SSI_DEFAULT_NUM_VECTOR_ELEMENTS )
  922. {
  923. SetState( SIF_STATE_VECTOR_SEND_PENDING );
  924. hr = _pRequest->GetVectorBuffer().VectorSend( pfAsyncPending );
  925. if( SUCCEEDED( hr ) && !*pfAsyncPending )
  926. {
  927. SetState( SIF_STATE_PROCESSING );
  928. _pRequest->GetVectorBuffer().Reset();
  929. }
  930. else
  931. {
  932. //
  933. // Wait for the completion of VectorSend()
  934. // or bail out on error
  935. //
  936. return hr;
  937. }
  938. }
  939. //
  940. // Move to next element of SSI_ELEMENT_LIST
  941. //
  942. _pCurrentEntry = _pCurrentEntry->Flink;
  943. }
  944. //
  945. // End of the list has been reached
  946. // It means that processing of the current SSI_INCLUDE_FILE has completed
  947. //
  948. SetState( SIF_STATE_PROCESSED );
  949. return NO_ERROR;
  950. }
  951. HRESULT
  952. SSI_INCLUDE_FILE::GetFullPath(
  953. IN SSI_ELEMENT_ITEM * pSEI,
  954. OUT STRU * pstrPath,
  955. IN DWORD dwPermission,
  956. IN STRU * pstrCurrentURL,
  957. OUT STRU * pstrURL
  958. )
  959. /*++
  960. Routine Description:
  961. Used to resolve FILE= and VIRTUAL= references. Fills in the physical
  962. path of such file references and optionally checks the permissions
  963. of the virtual directory.
  964. Arguments:
  965. pSEI - Element item ( either FILE or VIRTUAL )
  966. pstrPath - Filled in with physical path of file
  967. dwPermission - Contains permissions that the virtual
  968. path must satisfy. For example HSE_URL_FLAGS_READ.
  969. If 0, then no permissions are checked
  970. pstrCurrentURL - Current .STM URL being parsed
  971. pstrURL - Full URL filled in here (may be NULL if only pstrPath is to be retrieved)
  972. Return Value:
  973. HRESULT
  974. --*/
  975. {
  976. WCHAR * pszValue;
  977. STACK_STRU( strValue, SSI_DEFAULT_TAG_SIZE + 1 );
  978. STACK_STRA( straValue, SSI_DEFAULT_TAG_SIZE + 1 );
  979. STACK_STRU( strPath, SSI_MAX_PATH + 1 );
  980. HRESULT hr = E_FAIL;
  981. //
  982. // We recalc the virtual root each time in case the root
  983. // to directory mapping has changed
  984. //
  985. if ( FAILED( hr = strValue.CopyA( pSEI->QueryTagValue()->QueryStr() ) ) )
  986. {
  987. return hr;
  988. }
  989. pszValue = strValue.QueryStr();
  990. if ( *pszValue == L'/' )
  991. {
  992. if ( FAILED( hr = strPath.Copy( pszValue ) ) )
  993. {
  994. return hr;
  995. }
  996. }
  997. else if ( ( int )pSEI->QueryTag() == ( int )SSI_TAG_FILE )
  998. {
  999. if ( FAILED( hr = strPath.Copy( pstrCurrentURL->QueryStr() ) ) )
  1000. {
  1001. return hr;
  1002. }
  1003. LPWSTR pL = strPath.QueryStr() + strPath.QueryCCH();
  1004. while ( pL > strPath.QueryStr() && pL[ -1 ] != L'/' )
  1005. {
  1006. --pL;
  1007. }
  1008. if ( pL == strPath.QueryStr() )
  1009. {
  1010. if ( FAILED( hr = strPath.Copy( L"/" ) ) )
  1011. {
  1012. return hr;
  1013. }
  1014. }
  1015. else
  1016. {
  1017. //
  1018. // truncate the path off the filename
  1019. // (SetLen takes number of characters)
  1020. //
  1021. strPath.SetLen( (DWORD)DIFF( pL - strPath.QueryStr() ) );
  1022. }
  1023. if ( FAILED( hr = strPath.Append( pszValue ) ) )
  1024. {
  1025. return hr;
  1026. }
  1027. }
  1028. else
  1029. {
  1030. if ( FAILED( hr = strPath.Copy( L"/" ) ) )
  1031. {
  1032. return hr;
  1033. }
  1034. if ( FAILED( hr = strPath.Append( pszValue ) ) )
  1035. {
  1036. return hr;
  1037. }
  1038. }
  1039. //
  1040. // Convert the URL into MBCS in the server's default codepage
  1041. // for normalization.
  1042. //
  1043. // CODEWORK: This is a bit extraneous, since the URL begins
  1044. // in the SSI_ELEMENT_ITEM structure as ANSI. The only reason
  1045. // that it even needs to be handled as UNICODE above is that
  1046. // pstrCurrentUrl comes in as UNICODE...
  1047. //
  1048. hr = straValue.CopyW( strPath.QueryStr() );
  1049. if ( FAILED( hr ) )
  1050. {
  1051. return hr;
  1052. }
  1053. if( !_pRequest->GetECB()->ServerSupportFunction(
  1054. _pRequest->GetECB()->ConnID,
  1055. HSE_REQ_NORMALIZE_URL,
  1056. straValue.QueryStr(),
  1057. NULL,
  1058. NULL
  1059. ) )
  1060. {
  1061. return HRESULT_FROM_WIN32( GetLastError() );
  1062. }
  1063. hr = strPath.CopyA( straValue.QueryStr() );
  1064. if ( FAILED( hr ) )
  1065. {
  1066. return hr;
  1067. }
  1068. //
  1069. // Map to a physical directory
  1070. //
  1071. if ( FAILED( hr =_pRequest->LookupVirtualRoot(
  1072. strPath.QueryStr(),
  1073. pstrPath,
  1074. dwPermission ) ) )
  1075. {
  1076. return hr;
  1077. }
  1078. if( pstrURL == NULL )
  1079. {
  1080. return NO_ERROR;
  1081. }
  1082. if( FAILED( hr = pstrURL->Copy( strPath ) ) )
  1083. {
  1084. return hr;
  1085. }
  1086. return NO_ERROR;
  1087. }
  1088. //static
  1089. BOOL
  1090. SSI_INCLUDE_FILE::FindInternalVariable(
  1091. IN STRA * pstrVariable,
  1092. OUT PDWORD pdwID
  1093. )
  1094. /*++
  1095. Routine Description:
  1096. Lookup internal list of SSI variables that aren't supported by ISAPI.
  1097. These include "DOCUMENT_NAME", "DATE_LOCAL", etc.
  1098. Arguments:
  1099. pstrVariable - Variable to check
  1100. pdwID - Variable ID (or SSI_VAR_UNKNOWN if not found)
  1101. Return Value:
  1102. TRUE on success, FALSE on failure
  1103. --*/
  1104. {
  1105. DWORD dwCounter = 0;
  1106. while ( ( SSIVarMap[ dwCounter ].pszMap != NULL ) &&
  1107. _strnicmp( SSIVarMap[ dwCounter ].pszMap,
  1108. ( CHAR * )pstrVariable->QueryStr(),
  1109. SSIVarMap[ dwCounter ].cchMap ) )
  1110. {
  1111. dwCounter++;
  1112. }
  1113. if ( SSIVarMap[ dwCounter ].pszMap != NULL )
  1114. {
  1115. *pdwID = SSIVarMap[ dwCounter ].ssiMap;
  1116. return TRUE;
  1117. }
  1118. else
  1119. {
  1120. *pdwID = SSI_VAR_UNKNOWN;
  1121. return FALSE;
  1122. }
  1123. }
  1124. HRESULT
  1125. SSI_INCLUDE_FILE::DoEchoISAPIVariable(
  1126. IN STRA * pstrVariable
  1127. )
  1128. /*++
  1129. Routine Description:
  1130. Get ISAPI variable and if successful, send it to HTML stream
  1131. Arguments:
  1132. pstrVariable - Variable
  1133. Return Value:
  1134. HRESULT
  1135. --*/
  1136. {
  1137. HRESULT hr = E_FAIL;
  1138. DWORD dwBufLen = 0;
  1139. PCHAR pszVectorBufferSpace = NULL;
  1140. BOOL fRet;
  1141. fRet = _pRequest->GetECB()->GetServerVariable(
  1142. _pRequest->GetECB()->ConnID,
  1143. pstrVariable->QueryStr(),
  1144. NULL,
  1145. &dwBufLen );
  1146. if ( !fRet )
  1147. {
  1148. DWORD dwError = GetLastError();
  1149. if ( dwError == ERROR_INSUFFICIENT_BUFFER )
  1150. {
  1151. if ( FAILED( hr = _pRequest->GetVectorBuffer().AllocateSpace(
  1152. dwBufLen,
  1153. &pszVectorBufferSpace ) ) )
  1154. {
  1155. goto failed;
  1156. }
  1157. }
  1158. else
  1159. {
  1160. hr = HRESULT_FROM_WIN32( dwError );
  1161. goto failed;
  1162. }
  1163. fRet = _pRequest->GetECB()->GetServerVariable(
  1164. _pRequest->GetECB()->ConnID,
  1165. pstrVariable->QueryStr(),
  1166. pszVectorBufferSpace,
  1167. &dwBufLen );
  1168. if ( !fRet )
  1169. {
  1170. hr = HRESULT_FROM_WIN32( GetLastError() );
  1171. goto failed;
  1172. }
  1173. }
  1174. //
  1175. // dwBufLen includes terminating 0
  1176. //
  1177. if ( dwBufLen > 1 )
  1178. {
  1179. return _pRequest->GetVectorBuffer().AddToVector(
  1180. pszVectorBufferSpace,
  1181. dwBufLen - sizeof('\0') );
  1182. }
  1183. else
  1184. {
  1185. //
  1186. // Don't bother sending anything for empty variable
  1187. //
  1188. return S_OK;
  1189. }
  1190. failed:
  1191. DBG_ASSERT( FAILED( hr ) );
  1192. return hr;
  1193. }
  1194. HRESULT
  1195. SSI_INCLUDE_FILE::DoEchoDateLocal(
  1196. VOID
  1197. )
  1198. /*++
  1199. Routine Description:
  1200. Sends local time (#ECHO VAR="DATE_LOCAL")
  1201. uses _strFileFmt - Format of time (follows strftime() convention)
  1202. Arguments:
  1203. Return Value:
  1204. HRESULT
  1205. --*/
  1206. {
  1207. SYSTEMTIME sysTime;
  1208. ::GetLocalTime( &sysTime );
  1209. return _pRequest->SendDate( &sysTime,
  1210. &_strTimeFmt );
  1211. }
  1212. HRESULT
  1213. SSI_INCLUDE_FILE::DoEchoDateGMT(
  1214. VOID
  1215. )
  1216. /*++
  1217. Routine Description:
  1218. Sends GMT time (#ECHO VAR="DATE_GMT")
  1219. Arguments:
  1220. pstrTimefmt - Format of time (follows strftime() convention)
  1221. Return Value:
  1222. HRESULT
  1223. --*/
  1224. {
  1225. SYSTEMTIME sysTime;
  1226. STACK_STRA( straGmtTimeFmt, SSI_DEFAULT_TIME_FMT + 1 );
  1227. CHAR * pszTimeFmt = _strTimeFmt.QueryStr();
  1228. HRESULT hr = E_FAIL;
  1229. CHAR szGMT[] = "GMT";
  1230. //
  1231. // for GMT time the time zone as displayes by strftime() will be incorrect
  1232. // lets replace all the occurences with time zone string (%z,%Z, %#z, %#Z)
  1233. // with hardcoded GMT value
  1234. //
  1235. while ( *pszTimeFmt != '\0' )
  1236. {
  1237. if ( *pszTimeFmt == '%' )
  1238. {
  1239. if ( *( pszTimeFmt + 1 ) == 'z' || *( pszTimeFmt + 1 ) == 'Z' )
  1240. {
  1241. pszTimeFmt += 2;
  1242. //
  1243. // replace %z or %Z with GMT
  1244. //
  1245. if ( FAILED( hr = straGmtTimeFmt.Append( szGMT ) ) )
  1246. {
  1247. return hr;
  1248. }
  1249. continue;
  1250. }
  1251. else if ( *( pszTimeFmt + 1 ) == '#' )
  1252. {
  1253. if ( *( pszTimeFmt + 2 ) == 'z' || *( pszTimeFmt + 2 ) == 'Z' )
  1254. {
  1255. pszTimeFmt +=3 ;
  1256. //
  1257. // replace %z or %Z with GMT
  1258. //
  1259. if ( FAILED( hr = straGmtTimeFmt.Append( szGMT ) ) )
  1260. {
  1261. return hr;
  1262. }
  1263. continue;
  1264. }
  1265. }
  1266. }
  1267. if ( FAILED( hr = straGmtTimeFmt.Append( pszTimeFmt++, 1 ) ) )
  1268. {
  1269. return hr;
  1270. }
  1271. }
  1272. ::GetSystemTime( &sysTime );
  1273. return _pRequest->SendDate( &sysTime,
  1274. &straGmtTimeFmt );
  1275. }
  1276. HRESULT
  1277. SSI_INCLUDE_FILE::DoEchoDocumentName(
  1278. VOID
  1279. )
  1280. /*++
  1281. Routine Description:
  1282. Sends filename of current SSI document (#ECHO VAR="DOCUMENT_NAME")
  1283. Arguments:
  1284. pstrFilename - filename to print
  1285. Return Value:
  1286. HRESULT
  1287. --*/
  1288. {
  1289. return _pRequest->GetVectorBuffer().CopyToVector( _strFilename );
  1290. }
  1291. HRESULT
  1292. SSI_INCLUDE_FILE::DoEchoDocumentURI(
  1293. VOID
  1294. )
  1295. /*++
  1296. Routine Description:
  1297. Sends URL of current SSI document (#ECHO VAR="DOCUMENT_URI")
  1298. Arguments:
  1299. pstrURL - URL to print
  1300. Return Value:
  1301. HRESULT
  1302. --*/
  1303. {
  1304. return _pRequest->GetVectorBuffer().CopyToVector( _strURL );
  1305. }
  1306. HRESULT
  1307. SSI_INCLUDE_FILE::DoEchoQueryStringUnescaped(
  1308. VOID
  1309. )
  1310. /*++
  1311. Routine Description:
  1312. Sends unescaped querystring to HTML stream (#ECHO VAR="QUERY_STRING_UNESCAPED")
  1313. Arguments:
  1314. none
  1315. Return Value:
  1316. HRESULT
  1317. --*/
  1318. {
  1319. HRESULT hr = E_FAIL;
  1320. STACK_STRA( straQueryString, SSI_DEFAULT_URL_SIZE + 1 );
  1321. if ( FAILED( hr = straQueryString.Copy( _pRequest->GetECB()->lpszQueryString ) ) )
  1322. {
  1323. return hr;
  1324. }
  1325. if ( FAILED( hr = straQueryString.Unescape()) )
  1326. {
  1327. return hr;
  1328. }
  1329. return _pRequest->GetVectorBuffer().CopyToVector( straQueryString );
  1330. }
  1331. HRESULT
  1332. SSI_INCLUDE_FILE::DoEchoLastModified(
  1333. VOID
  1334. )
  1335. /*++
  1336. Routine Description:
  1337. Sends LastModTime of current document (#ECHO VAR="LAST_MODIFIED")
  1338. Arguments:
  1339. pstrFilename - Filename of current SSI document
  1340. pstrTimeFmt - Time format (follows strftime() convention)
  1341. Return Value:
  1342. HRESULT
  1343. --*/
  1344. {
  1345. return DoFLastMod( &_strFilename );
  1346. }
  1347. HRESULT
  1348. SSI_INCLUDE_FILE::DoFSize(
  1349. IN STRU * pstrFilename
  1350. )
  1351. /*++
  1352. Routine Description:
  1353. Sends file size of file to HTML stream
  1354. Arguments:
  1355. pstrfilename - Filename
  1356. bSizeFmtBytes - TRUE if count is in Bytes, FALSE if in KBytes
  1357. Return Value:
  1358. HRESULT
  1359. --*/
  1360. {
  1361. DWORD cbFileSize;
  1362. CHAR achInputNumber[ SSI_MAX_NUMBER_STRING + 1 ];
  1363. NUMBERFMTA nfNumberFormat;
  1364. int iOutputSize;
  1365. HRESULT hr = E_FAIL;
  1366. CHAR * pszVectorBufferSpace = NULL;
  1367. if ( wcscmp( pstrFilename->QueryStr(),
  1368. _strFilename.QueryStr() ) )
  1369. {
  1370. //
  1371. // FSize requested for the file different from this on
  1372. //
  1373. SSI_FILE *pSsiFile = NULL;
  1374. hr = SSI_FILE::GetReferencedInstance(
  1375. *pstrFilename,
  1376. _pRequest->GetUserToken(),
  1377. &pSsiFile );
  1378. if (FAILED( hr ))
  1379. {
  1380. return hr;
  1381. }
  1382. hr = pSsiFile->SSIGetFileSize( &cbFileSize );
  1383. pSsiFile->DereferenceSsiFile();
  1384. if (FAILED(hr))
  1385. {
  1386. return hr;
  1387. }
  1388. }
  1389. else
  1390. {
  1391. //
  1392. // FSize requested for the current file
  1393. //
  1394. if ( FAILED( hr = _pSsiFile->SSIGetFileSize( &cbFileSize ) ) )
  1395. {
  1396. return hr;
  1397. }
  1398. }
  1399. if ( !_fSizeFmtBytes )
  1400. {
  1401. //
  1402. // express in terms of KB
  1403. // most applications round up the size in KB (eg. 1B is shown as 1KB)
  1404. // Let's do the same
  1405. //
  1406. cbFileSize = cbFileSize / 1024 + (( cbFileSize % 1024 != 0 )?1:0);
  1407. }
  1408. nfNumberFormat.NumDigits = 0;
  1409. nfNumberFormat.LeadingZero = 0;
  1410. nfNumberFormat.Grouping = 3;
  1411. nfNumberFormat.lpThousandSep = ",";
  1412. nfNumberFormat.lpDecimalSep = ".";
  1413. nfNumberFormat.NegativeOrder = 2;
  1414. _snprintf( achInputNumber,
  1415. SSI_MAX_NUMBER_STRING,
  1416. "%ld",
  1417. cbFileSize );
  1418. achInputNumber[ SSI_MAX_NUMBER_STRING ] = '\0';
  1419. if ( FAILED( hr = _pRequest->GetVectorBuffer().AllocateSpace(
  1420. SSI_MAX_NUMBER_STRING + 1,
  1421. &pszVectorBufferSpace ) ) )
  1422. {
  1423. return hr;
  1424. }
  1425. iOutputSize = GetNumberFormatA( LOCALE_SYSTEM_DEFAULT,
  1426. 0,
  1427. achInputNumber,
  1428. &nfNumberFormat,
  1429. pszVectorBufferSpace,
  1430. SSI_MAX_NUMBER_STRING + 1 );
  1431. if ( !iOutputSize )
  1432. {
  1433. return HRESULT_FROM_WIN32( GetLastError() );
  1434. }
  1435. //
  1436. // Do not count trailing '\0'
  1437. //
  1438. iOutputSize--;
  1439. return _pRequest->GetVectorBuffer().AddToVector(
  1440. pszVectorBufferSpace,
  1441. iOutputSize );
  1442. }
  1443. HRESULT
  1444. SSI_INCLUDE_FILE::DoFLastMod(
  1445. IN STRU * pstrFilename
  1446. )
  1447. /*++
  1448. Routine Description:
  1449. Send the LastModTime of file to HTML stream
  1450. Arguments:
  1451. pstrFilename - Filename
  1452. pstrTimeFmt - Format of time -> follows strftime() convention
  1453. Return Value:
  1454. HRESULT
  1455. --*/
  1456. {
  1457. FILETIME ftTime;
  1458. FILETIME ftLocalTime;
  1459. SYSTEMTIME sysLocal;
  1460. HRESULT hr = E_FAIL;
  1461. if ( wcscmp( pstrFilename->QueryStr(),
  1462. _strFilename.QueryStr() ) )
  1463. {
  1464. //
  1465. // FLastMod requested for file different from the current one
  1466. //
  1467. SSI_FILE *pSsiFile = NULL;
  1468. hr = SSI_FILE::GetReferencedInstance(
  1469. *pstrFilename,
  1470. _pRequest->GetUserToken(),
  1471. &pSsiFile );
  1472. if (FAILED( hr ))
  1473. {
  1474. return hr;
  1475. }
  1476. hr = pSsiFile->SSIGetLastModTime( &ftTime );
  1477. pSsiFile->DereferenceSsiFile();
  1478. if (FAILED(hr))
  1479. {
  1480. return hr;
  1481. }
  1482. }
  1483. else
  1484. {
  1485. //
  1486. // FLastMod requested for the current file
  1487. //
  1488. hr = _pSsiFile->SSIGetLastModTime( &ftTime );
  1489. if ( FAILED( hr ) )
  1490. {
  1491. return hr;
  1492. }
  1493. }
  1494. if ( ( !FileTimeToLocalFileTime( &ftTime, &ftLocalTime ) ) ||
  1495. ( !FileTimeToSystemTime( &ftLocalTime, &sysLocal ) ) )
  1496. {
  1497. return HRESULT_FROM_WIN32( GetLastError() );
  1498. }
  1499. return _pRequest->SendDate( &sysLocal,
  1500. &_strTimeFmt );
  1501. }