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.

1322 lines
40 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 2002.
  5. //
  6. // File: htx.cxx
  7. //
  8. // Contents: Parser for a HTX file
  9. //
  10. // History: 96/Jan/3 DwightKr Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. //+---------------------------------------------------------------------------
  16. //
  17. // Member: CHTXScanner::CHTXScanner - public constructor
  18. //
  19. // Synopsis: Builds a scanner for a section within a HTX file
  20. //
  21. // Arguments: [variableSet] - list of replaceable parameters
  22. // [wcsPrefix] - prefix delimiter for replacable parameters
  23. // [wcsSuffix] - suffix delimiter for replacable parameters
  24. //
  25. // Notes: The wcsPrefix and wcsSuffix are expected to be the same
  26. // length and either one or two characters.
  27. //
  28. // History: 96/Jan/03 DwightKr created
  29. //
  30. //----------------------------------------------------------------------------
  31. CHTXScanner::CHTXScanner( CVariableSet & variableSet,
  32. WCHAR const * wcsPrefix,
  33. WCHAR const * wcsSuffix ) :
  34. _wcsPrefix(wcsPrefix),
  35. _wcsSuffix(wcsSuffix),
  36. _variableSet(variableSet),
  37. _type(eNone),
  38. _nextType(eNone),
  39. _wcsString(0),
  40. _wcsPrefixToken(0),
  41. _wcsSuffixToken(0)
  42. {
  43. Win4Assert( wcslen( _wcsPrefix ) == wcslen( _wcsSuffix ) &&
  44. wcslen( _wcsPrefix ) <= 2 );
  45. if ( _wcsPrefix[1] == L'\0' )
  46. _cchPrefix = _cchSuffix = 1;
  47. else
  48. _cchPrefix = _cchSuffix = 2;
  49. }
  50. //+---------------------------------------------------------------------------
  51. //
  52. // Member: CHTXScanner::Init - public
  53. //
  54. // Synopsis: Saves a pointer to the string to be parsed.
  55. //
  56. // Arguments: [wcsString] - the string to be parsed
  57. //
  58. // History: 96/Jan/03 DwightKr created
  59. //
  60. // NOTES: THIS STRING WILL BE MODIFIED BY SUBSEQUENT CALLS TO MEMBER
  61. // FUNCTIONS OF THIS CLASS.
  62. //
  63. //----------------------------------------------------------------------------
  64. void CHTXScanner::Init( WCHAR * wcsString )
  65. {
  66. _wcsString = wcsString;
  67. }
  68. //+---------------------------------------------------------------------------
  69. //
  70. // Member: CHTXScanner::IsToken - private
  71. //
  72. // Synopsis: Determines if a string is a special token.
  73. //
  74. // Arguments: [wcs] - start of string to be tested.
  75. //
  76. // Notes: If the string is a token, the members _type, _wcsPrefixToken
  77. // and _wcsSuffixToken are set appropriately.
  78. //
  79. // History: 96/Apr/02 AlanW Created
  80. // 96/May/17 DwightKr Treat all <%..%> as variables
  81. //
  82. //----------------------------------------------------------------------------
  83. BOOL CHTXScanner::IsToken(WCHAR * wcs)
  84. {
  85. if ( wcsncmp( _wcsPrefix, wcs, _cchPrefix ) != 0 )
  86. {
  87. ciGibDebugOut(( DEB_USER1, "CHTXScanner::IsToken end of string\n" ));
  88. return FALSE;
  89. }
  90. wcs += _cchPrefix;
  91. WCHAR * wcsSuffixTok = wcs2chr( wcs, _wcsSuffix );
  92. if ( 0 == wcsSuffixTok )
  93. {
  94. ciGibDebugOut(( DEB_USER1, "CHTXScanner::IsToken no suffix token\n" ));
  95. return FALSE;
  96. }
  97. *wcsSuffixTok = L'\0';
  98. _wcsPrefixToken = wcs - _cchPrefix;
  99. _wcsupr( wcs );
  100. //
  101. // Strip leading spaces before token
  102. //
  103. while ( iswspace(*wcs) && (wcs < wcsSuffixTok) )
  104. {
  105. wcs++;
  106. }
  107. //
  108. // Strip trailing spaces after token
  109. //
  110. WCHAR * wcsSuffix = wcsSuffixTok - 1;
  111. while ( iswspace(*wcsSuffix) && (wcsSuffix > wcs) )
  112. {
  113. *wcsSuffix = 0;
  114. wcsSuffix--;
  115. }
  116. ciGibDebugOut(( DEB_USER1, "CHTXScanner::IsToken wcs=%ws\n", wcs ));
  117. if ( wcsncmp( wcs, L"IF ", 3 ) == 0 )
  118. {
  119. _type = eIf;
  120. }
  121. else if ( wcscmp( wcs, L"ELSE" ) == 0 )
  122. {
  123. _type = eElse;
  124. }
  125. else if ( wcscmp( wcs, L"ENDIF" ) == 0 )
  126. {
  127. _type = eEndIf;
  128. }
  129. else if ( wcsncmp( wcs, L"ESCAPEHTML ", 11 ) == 0 )
  130. {
  131. _type = eEscapeHTML;
  132. }
  133. else if ( wcsncmp( wcs, L"ESCAPEURL ", 10 ) == 0 )
  134. {
  135. _type = eEscapeURL;
  136. }
  137. else if ( wcsncmp( wcs, L"ESCAPERAW ", 10 ) == 0 )
  138. {
  139. _type = eEscapeRAW;
  140. }
  141. else
  142. {
  143. //
  144. // Find this name in the list of replaceable parameters. Note that
  145. // if we can't find this variable in the list of replaceable
  146. // parameters, we've converted some output text to uppercase. This
  147. // is probably OK since the user used <% ... %> to delimit their
  148. // output; <% & %> are reserved tokens hence this would be an error.
  149. //
  150. CVariable *pVariable = _variableSet.Find( wcs );
  151. if ( 0 != pVariable )
  152. {
  153. //
  154. // We have a match, this is a replaceable parameter. Compiler
  155. // bug. _type needs to be assigned in both places.
  156. //
  157. _type = eParameter | pVariable->GetFlags();
  158. }
  159. else
  160. {
  161. ciGibDebugOut(( DEB_IWARN,
  162. "Warning: CHTXScanner::IsToken found a unknown variable: '%ws'\n",
  163. wcs ));
  164. _type = eParameter;
  165. }
  166. }
  167. *_wcsPrefixToken = L'\0';
  168. _wcsSuffixToken = wcsSuffixTok;
  169. _wcsNextToken = wcsSuffixTok + _cchSuffix;
  170. return TRUE;
  171. }
  172. //+---------------------------------------------------------------------------
  173. //
  174. // Member: CHTXScanner::FindNextToken - public
  175. //
  176. // Synopsis: Locates the next token in the string.
  177. //
  178. // History: 96/Jan/03 DwightKr created
  179. //
  180. //----------------------------------------------------------------------------
  181. BOOL CHTXScanner::FindNextToken()
  182. {
  183. if (_nextType != eNone)
  184. {
  185. //
  186. // Found a token on the previous call. Just return it.
  187. //
  188. Win4Assert ( _wcsPrefixToken && _wcsSuffixToken > _wcsPrefixToken );
  189. _type = _nextType;
  190. _nextType = eNone;
  191. _wcsString = _wcsNextToken = _wcsSuffixToken + _cchSuffix;
  192. return TRUE;
  193. }
  194. if ( (0 == _wcsString) || (0 == *_wcsString) )
  195. {
  196. _type = eNone;
  197. _wcsNextToken = 0;
  198. return FALSE;
  199. }
  200. if ( *_wcsString == *_wcsPrefix &&
  201. IsToken( _wcsString ) )
  202. {
  203. _nextType = eNone;
  204. return TRUE;
  205. }
  206. //
  207. // The string doesn't start with one of our special keywords.
  208. // Treat it as an ordinary string, and look ahead to the next
  209. // valid token.
  210. //
  211. _wcsPrefixToken = wcs2chr( _wcsString+1, _wcsPrefix );
  212. while ( _wcsPrefixToken )
  213. {
  214. if ( IsToken( _wcsPrefixToken ) )
  215. {
  216. _nextType = _type;
  217. _wcsNextToken = _wcsPrefixToken;
  218. _type = eString;
  219. return TRUE;
  220. }
  221. _wcsPrefixToken = wcs2chr( _wcsPrefixToken+_cchPrefix, _wcsPrefix );
  222. }
  223. _nextType = eNone;
  224. _type = eString;
  225. _wcsNextToken = _wcsString + wcslen( _wcsString );
  226. return TRUE;
  227. }
  228. //+---------------------------------------------------------------------------
  229. //
  230. // Member: CHTXScanner::GetToken - public
  231. //
  232. // Synopsis: Returns a pointer to the replaceable parameter token found.
  233. // Prepares the scanner to return the next token.
  234. //
  235. // History: 96/Jan/03 DwightKr created
  236. // 96/Mar/13 DwightKr add support for eEscapeURL &
  237. // eEscapeHTML
  238. //
  239. //----------------------------------------------------------------------------
  240. WCHAR * CHTXScanner::GetToken()
  241. {
  242. if ( eString == _type )
  243. {
  244. if ( 0 != _wcsString )
  245. {
  246. WCHAR * wcsString = _wcsString;
  247. _wcsString = _wcsNextToken;
  248. return wcsString;
  249. }
  250. }
  251. else if ( eEscapeHTML == _type )
  252. {
  253. WCHAR * wcsString = _wcsPrefixToken + _cchPrefix;
  254. wcsString += 10; // Skip 'EscapeHTML'
  255. *_wcsSuffixToken = 0; // Null terminate
  256. while ( (0 != *wcsString) && iswspace(*wcsString) )
  257. {
  258. wcsString++;
  259. }
  260. _wcsString = _wcsNextToken;
  261. return wcsString;
  262. }
  263. else if ( eEscapeURL == _type ||
  264. eEscapeRAW == _type )
  265. {
  266. WCHAR * wcsString = _wcsPrefixToken + _cchPrefix;
  267. wcsString += 9; // Skip 'EscapeURL'
  268. *_wcsSuffixToken = 0; // Null terminate
  269. while ( (0 != *wcsString) && iswspace(*wcsString) )
  270. {
  271. wcsString++;
  272. }
  273. _wcsString = _wcsNextToken;
  274. return wcsString;
  275. }
  276. else
  277. {
  278. if ( 0 != _wcsPrefixToken )
  279. {
  280. Win4Assert( 0 != _wcsSuffixToken &&
  281. _wcsPrefixToken < _wcsSuffixToken &&
  282. _wcsSuffixToken < _wcsNextToken );
  283. *_wcsPrefixToken = 0;
  284. *_wcsSuffixToken = 0;
  285. _wcsString = _wcsNextToken;
  286. WCHAR * wcsString = _wcsPrefixToken + _cchPrefix;
  287. while ( (0 != *wcsString) && iswspace(*wcsString) )
  288. {
  289. wcsString++;
  290. }
  291. return wcsString;
  292. }
  293. }
  294. return 0;
  295. }
  296. //+---------------------------------------------------------------------------
  297. //
  298. // Member: CHTXFile::CHTXFile - public constructor
  299. //
  300. // Synopsis: Builds a CHTXFile object and initializes values.
  301. //
  302. // History: 96/Jan/03 DwightKr created
  303. //
  304. //----------------------------------------------------------------------------
  305. CHTXFile::CHTXFile( XPtrST<WCHAR> & wcsTemplate,
  306. UINT codePage,
  307. CSecurityIdentity const & securityIdentity,
  308. ULONG ulServerInstance )
  309. : _wcsVirtualName( wcsTemplate.Acquire() ),
  310. _pVarHeader(0),
  311. _pVarRowDetails(0),
  312. _pVarFooter(0),
  313. _wcsFileBuffer(0),
  314. _fSequential(TRUE),
  315. _cIncludeFiles(0),
  316. _refCount(0),
  317. _codePage(codePage),
  318. _securityIdentity( securityIdentity ),
  319. _ulServerInstance( ulServerInstance )
  320. {
  321. _wcsPhysicalName[0] = 0;
  322. }
  323. //+---------------------------------------------------------------------------
  324. //
  325. // Member: CHTXFile::~CHTXFile - public destructor
  326. //
  327. // History: 96/Jan/03 DwightKr created
  328. //
  329. //----------------------------------------------------------------------------
  330. CHTXFile::~CHTXFile()
  331. {
  332. Win4Assert( _refCount == 0 );
  333. delete _wcsVirtualName;
  334. delete _pVarHeader;
  335. delete _pVarRowDetails;
  336. delete _pVarFooter;
  337. delete _wcsFileBuffer;
  338. for (unsigned i=0; i<_cIncludeFiles; i++)
  339. {
  340. delete _awcsIncludeFileName[i];
  341. }
  342. }
  343. //+---------------------------------------------------------------------------
  344. //
  345. // Member: CHTXFile::ParseFile - public
  346. //
  347. // Synopsis: Parses the HTX file and breaks it up into its sections.
  348. //
  349. // History: 96/Jan/03 DwightKr created
  350. //
  351. //----------------------------------------------------------------------------
  352. void CHTXFile::ParseFile( WCHAR const * wcsFileName,
  353. CVariableSet & variableSet,
  354. CWebServer & webServer )
  355. {
  356. Win4Assert( wcsFileName != 0 );
  357. if ( wcslen( wcsFileName ) >= MAX_PATH )
  358. THROW( CException( E_INVALIDARG ) );
  359. wcscpy( _wcsPhysicalName, wcsFileName );
  360. //
  361. // Read the entire file into a buffer
  362. //
  363. CVirtualString wcsBuffer;
  364. ExpandFile( _wcsPhysicalName, webServer, wcsBuffer, _ftHTXLastWriteTime );
  365. Win4Assert( wcsBuffer.GetPointer() != 0 );
  366. //
  367. // Break the buffer into the sections; the header, the detail section,
  368. // and the footer. Verify that if there is a <%BeginDetail%>
  369. // section, then there MUST be a <%EndDetail%> section AFTER it, not
  370. // before. Neither <%EndDetail%> nor <%BeginDetail%> can appear on
  371. // their own.
  372. //
  373. //
  374. // Find the <%BeginDetail%> and <%EndDetail%> sections
  375. //
  376. _wcsFileBuffer = wcsBuffer.StrDup(); // Save buffer
  377. WCHAR * wcsHeader = _wcsFileBuffer; // Assume a header
  378. WCHAR * wcsRowDetails = wcsipattern(wcsHeader, L"<%BEGINDETAIL%>" );
  379. WCHAR * wcsFooter = wcsipattern(wcsHeader, L"<%ENDDETAIL%>" );
  380. if ( wcsHeader == wcsRowDetails )
  381. {
  382. //
  383. // No header found in this file; it begins with the detail section.
  384. //
  385. wcsHeader = 0;
  386. }
  387. const int cwcBeginDetail = 15;
  388. const int cwcEndDetail = 13;
  389. Win4Assert( cwcBeginDetail == wcslen( L"<%BEGINDETAIL%>" ) );
  390. Win4Assert( cwcEndDetail == wcslen( L"<%ENDDETAIL%>" ) );
  391. if ( 0 != wcsRowDetails )
  392. {
  393. //
  394. // A <%BeginDetail%> section was found. We better also have an
  395. // <%EndDetail%> section AFTER the <%BeginDetail%> section.
  396. //
  397. *wcsRowDetails = 0; // Null terminate the header string
  398. wcsRowDetails += cwcBeginDetail;
  399. if ( 0 != wcsFooter )
  400. {
  401. if ( wcsFooter < wcsRowDetails )
  402. {
  403. //
  404. // The <%EndDetail%> was found before the <%BeginDetail%>
  405. //
  406. WCHAR * wcsHTXFileName;
  407. LONG lLineNumber;
  408. GetFileNameAndLineNumber( (int)(wcsFooter - _wcsFileBuffer),
  409. wcsHTXFileName,
  410. lLineNumber );
  411. THROW( CHTXException(MSG_CI_HTX_ENDDETAIL_BEFORE_BEGINDETAIL,
  412. wcsHTXFileName,
  413. lLineNumber) );
  414. }
  415. *wcsFooter = 0; // Null terminate the BeginDetail section
  416. wcsFooter += cwcEndDetail;
  417. }
  418. else
  419. {
  420. //
  421. // Report an error: <%BeginDetail%> without an <%EndDetail%>
  422. //
  423. WCHAR * wcsHTXFileName;
  424. LONG lLineNumber;
  425. GetFileNameAndLineNumber( (int)(wcsRowDetails - _wcsFileBuffer),
  426. wcsHTXFileName,
  427. lLineNumber );
  428. THROW( CHTXException(MSG_CI_HTX_NO_ENDDETAIL_SECTION,
  429. wcsHTXFileName,
  430. lLineNumber) );
  431. }
  432. }
  433. else if ( 0 != wcsFooter )
  434. {
  435. //
  436. // A <%BeginDetail%> section could be found. There should
  437. // be no <%EndDetail%> section either.
  438. //
  439. WCHAR * wcsHTXFileName;
  440. LONG lLineNumber;
  441. GetFileNameAndLineNumber( (int)(wcsFooter - _wcsFileBuffer),
  442. wcsHTXFileName,
  443. lLineNumber );
  444. THROW( CHTXException(MSG_CI_HTX_NO_BEGINDETAIL_SECTION,
  445. wcsHTXFileName,
  446. lLineNumber) );
  447. }
  448. if ( 0 != wcsHeader )
  449. {
  450. _pVarHeader = new CParameterReplacer ( wcsHeader,
  451. L"<%",
  452. L"%>" );
  453. _pVarHeader->ParseString( variableSet );
  454. }
  455. if ( 0 != wcsRowDetails )
  456. {
  457. _pVarRowDetails = new CParameterReplacer ( wcsRowDetails,
  458. L"<%",
  459. L"%>" );
  460. _pVarRowDetails->ParseString( variableSet );
  461. }
  462. if ( 0 != wcsFooter )
  463. {
  464. _pVarFooter = new CParameterReplacer ( wcsFooter,
  465. L"<%",
  466. L"%>" );
  467. _pVarFooter->ParseString( variableSet );
  468. }
  469. _fSequential = CheckForSequentialAccess();
  470. }
  471. //+---------------------------------------------------------------------------
  472. //
  473. // Member: CHTXFile::ReadFile - public
  474. //
  475. // Synopsis: Read the HTX file into a buffer
  476. //
  477. // Arguments: [wcsFileName] - full physical path name of file
  478. // [ftLastWrite] - File's last write time is stored here
  479. //
  480. // History: 96/Jan/03 DwightKr created
  481. // 96/Apr/06 DwightKr add support for unicode files
  482. //
  483. //----------------------------------------------------------------------------
  484. WCHAR * CHTXFile::ReadFile( WCHAR const * wcsFileName,
  485. FILETIME & ftLastWrite )
  486. {
  487. Win4Assert ( 0 != wcsFileName );
  488. // We don't support impersonation for scripts.
  489. // It involves getting the server ip address, vpath and then using
  490. // that for impersonation
  491. if ( IsNetPath(wcsFileName) )
  492. {
  493. ciGibDebugOut(( DEB_ERROR, "The htx file (%ws) is on remote UNC\n",
  494. wcsFileName ));
  495. THROW( CHTXException( MSG_CI_SCRIPTS_ON_REMOTE_UNC, wcsFileName, 0 ));
  496. }
  497. //
  498. // Verify the HTX file exists, and is a file, not a directory.
  499. //
  500. WIN32_FILE_ATTRIBUTE_DATA ffData;
  501. if ( !GetFileAttributesEx( wcsFileName, GetFileExInfoStandard, &ffData ) )
  502. {
  503. ULONG error = GetLastError();
  504. ciGibDebugOut(( DEB_IERROR,
  505. "Unable to GetFileAttributesEx(%ws) GetLastError=0x%x\n",
  506. wcsFileName,
  507. error ));
  508. THROW( CIDQException(MSG_CI_IDQ_NO_SUCH_TEMPLATE, 0) );
  509. }
  510. if ( (ffData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 )
  511. {
  512. THROW( CIDQException(MSG_CI_IDQ_NO_SUCH_TEMPLATE, 0) );
  513. }
  514. //
  515. // Save the last write time of this file.
  516. //
  517. ftLastWrite = ffData.ftLastWriteTime;
  518. //
  519. // Open the file and map its contents
  520. //
  521. CFileMapView mapView( wcsFileName );
  522. mapView.Init();
  523. int cbBuffer = mapView.GetBufferSize() + 1;
  524. XArray<WCHAR> pwBuffer(cbBuffer);
  525. //
  526. // If the first two BYTES of the file are 0xFF 0xFE, then this is a
  527. // unicode file, and we don't need to convert it.
  528. //
  529. if ( mapView.IsUnicode() )
  530. {
  531. RtlCopyMemory( pwBuffer.Get(), mapView.GetBuffer()+2, cbBuffer-2 );
  532. pwBuffer[ ( cbBuffer - 2 ) / sizeof WCHAR ] = 0;
  533. return pwBuffer.Acquire();
  534. }
  535. //
  536. // Copy & convert the ASCII buffer to a WCHAR buffer.
  537. //
  538. int cwBuffer = mapView.GetBufferSize() + 1;
  539. int cwConvert;
  540. do
  541. {
  542. cwConvert = MultiByteToWideChar(_codePage,
  543. 0,
  544. (const char *) mapView.GetBuffer(), // Ptr to input buf
  545. mapView.GetBufferSize(),// Size of input buf
  546. pwBuffer.Get(), // Ptr to output buf
  547. cwBuffer - 1 ); // Size of output buf
  548. if ( 0 == cwConvert )
  549. {
  550. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  551. {
  552. cwBuffer += (cwBuffer/2);
  553. delete pwBuffer.Acquire();
  554. pwBuffer.Init(cwBuffer);
  555. }
  556. else
  557. {
  558. THROW( CException() );
  559. }
  560. }
  561. else
  562. {
  563. pwBuffer[cwConvert] = 0; // Null terminate the buffer
  564. }
  565. Win4Assert( cwConvert < cwBuffer );
  566. } while ( 0 == cwConvert );
  567. return pwBuffer.Acquire();
  568. }
  569. //+---------------------------------------------------------------------------
  570. //
  571. // Member: CHTXFile::ExpandFile, public
  572. //
  573. // Synopsis: Expands the contents of an HTX file into memory, processing
  574. // included files.
  575. //
  576. // Arguments: [wcsFileName] - file path name of the file to be expanded
  577. // [webServer] - CWebServer for virtual path translation
  578. // [vString] - String to which file contents are appended
  579. // [ftLastWrite] - File's last write time is stored here
  580. //
  581. //----------------------------------------------------------------------------
  582. void CHTXFile::ExpandFile( WCHAR const * wcsFileName,
  583. CWebServer & webServer,
  584. CVirtualString & vString,
  585. FILETIME & ftLastWrite )
  586. {
  587. Win4Assert ( 0 != wcsFileName );
  588. //
  589. // Read the existing file into a WCHAR buffer.
  590. //
  591. XPtrST<WCHAR> wcsBuffer( ReadFile(wcsFileName, ftLastWrite) );
  592. WCHAR * wcsString = wcsBuffer.GetPointer();
  593. ULONG cwcString = wcslen( wcsString );
  594. WCHAR * wcsEnd = wcsString + cwcString;
  595. //
  596. // Search the WCHAR buffer for <%include ... %>
  597. //
  598. WCHAR * wcsPattern = L"<%INCLUDE ";
  599. ULONG cwcPattern = 10;
  600. Win4Assert( cwcPattern == wcslen(wcsPattern) );
  601. WCHAR * wcsToken = wcsipattern( wcsString, wcsPattern );
  602. while ( 0 != wcsToken )
  603. {
  604. if ( _cIncludeFiles >= MAX_HTX_INCLUDE_FILES )
  605. {
  606. LONG cLines = CountLines( wcsBuffer.GetPointer(), wcsToken );
  607. THROW( CHTXException(MSG_CI_HTX_TOO_MANY_INCLUDES,
  608. wcsFileName,
  609. cLines) );
  610. }
  611. //
  612. // Concatentate everything before the <%include .. %> into the
  613. // virtual string.
  614. //
  615. ULONG cwcCat = (ULONG)(wcsToken - wcsString);
  616. Win4Assert( cwcCat <= cwcString );
  617. *wcsToken = 0; // Null terminate the string
  618. vString.StrCat( wcsString, cwcCat );
  619. //
  620. // Find the end of the <%include ... %>
  621. //
  622. wcsToken += cwcPattern; // Skip the <%include
  623. WCHAR * wcsIncludeFileName = wcsToken; // Point to the include filename
  624. wcsString = wcs2chr( wcsToken, L"%>" ); // Point to the end of the filename
  625. //
  626. // wcsString should be pointing to the %> at the end of the include
  627. //
  628. if ( 0 == wcsString )
  629. {
  630. //
  631. // Missing %>
  632. //
  633. LONG cLines = CountLines( vString.GetPointer(), wcsToken );
  634. THROW( CHTXException(MSG_CI_HTX_ILL_FORMED_INCLUDE,
  635. wcsFileName,
  636. cLines) );
  637. }
  638. //
  639. // Process the <%include ... %>
  640. //
  641. *wcsString = 0;
  642. if ( (wcsString - wcsIncludeFileName) >= MAX_PATH )
  643. {
  644. LONG cLines = CountLines( wcsBuffer.GetPointer(), wcsToken );
  645. THROW( CHTXException(MSG_CI_HTX_INVALID_INCLUDE_FILENAME,
  646. wcsFileName,
  647. cLines ) );
  648. }
  649. WCHAR awcPhysicalPath[MAX_PATH];
  650. webServer.GetPhysicalPath( wcsIncludeFileName,
  651. awcPhysicalPath,
  652. MAX_PATH );
  653. //
  654. // Save the include filename away
  655. //
  656. ULONG cwcPhysicalPath = wcslen(awcPhysicalPath) + 1;
  657. _awcsIncludeFileName[_cIncludeFiles] = new WCHAR[ cwcPhysicalPath ];
  658. _aulIncludeFileOffset[_cIncludeFiles] = vString.StrLen();
  659. RtlCopyMemory( _awcsIncludeFileName[_cIncludeFiles],
  660. awcPhysicalPath,
  661. cwcPhysicalPath * sizeof(WCHAR) );
  662. FILETIME & ftLastWrite = _aftIncludeLastWriteTime[ _cIncludeFiles ];
  663. _cIncludeFiles++;
  664. ExpandFile( awcPhysicalPath, webServer, vString, ftLastWrite );
  665. wcsString += 2; // Skip the %>
  666. cwcString = (ULONG)(wcsEnd - wcsString);
  667. wcsToken = wcsipattern( wcsString, wcsPattern );
  668. }
  669. vString.StrCat( wcsString, cwcString );
  670. }
  671. //+---------------------------------------------------------------------------
  672. //
  673. // Member: CHTXFile::GetFileNameAndLineNumber
  674. //
  675. // Synopsis: Determines the filename & line number amoung a group of
  676. // nested includes for a particular offset into the buffer.
  677. //
  678. // Arguments: [offset] - offset of the error in the overall buffer
  679. // [wcsFileName] - resulting name of file containing error
  680. // [lineNumber] - line # containing the error
  681. //
  682. // History: 96/Jun/25 DwightKr created
  683. //
  684. //----------------------------------------------------------------------------
  685. void CHTXFile::GetFileNameAndLineNumber( int offset,
  686. WCHAR *& wcsFileName,
  687. LONG & lineNumber )
  688. {
  689. //
  690. // Search the array of offsets for the one containing our offset
  691. //
  692. for (ULONG i = 0;
  693. (i < _cIncludeFiles) && ((ULONG) offset > _aulIncludeFileOffset[i]);
  694. i++ )
  695. {
  696. }
  697. //
  698. // Save a pointer to the name of the file containing the error
  699. //
  700. WCHAR const * pCurrent = _wcsFileBuffer;
  701. if ( 0 == i )
  702. {
  703. //
  704. // We have a problem in the outer-most container file; not
  705. // an include file.
  706. //
  707. wcsFileName = _wcsVirtualName;
  708. }
  709. else
  710. {
  711. wcsFileName = _awcsIncludeFileName[i-1];
  712. pCurrent += _aulIncludeFileOffset[i-1];
  713. }
  714. //
  715. // Count the number of lines in this sub-file
  716. //
  717. Win4Assert( 0 != _wcsFileBuffer );
  718. WCHAR const * pEnd = _wcsFileBuffer + offset;
  719. lineNumber = CountLines( pCurrent, pEnd );
  720. }
  721. //+---------------------------------------------------------------------------
  722. //
  723. // Member: CHTXFile::CountLines - private
  724. //
  725. // Synopsis: Deterines the number of lines (CR's) between the start
  726. // of the buffer and the end.
  727. //
  728. // Arguments: [wcsStart] - start location of search
  729. // [wcsEnd] - end of search
  730. //
  731. // History: 96/Jun/25 DwightKr created
  732. //
  733. //----------------------------------------------------------------------------
  734. LONG CHTXFile::CountLines( WCHAR const * wcsStart,
  735. WCHAR const * wcsEnd ) const
  736. {
  737. Win4Assert( 0 != wcsStart );
  738. Win4Assert( 0 != wcsEnd );
  739. LONG cLines = 1;
  740. while ( wcsStart <= wcsEnd )
  741. {
  742. if ( L'\n' == *wcsStart )
  743. {
  744. cLines++;
  745. }
  746. wcsStart++;
  747. }
  748. return cLines;
  749. }
  750. //+---------------------------------------------------------------------------
  751. //
  752. // Member: CHTXFile::IsCachedDataValid - public
  753. //
  754. // Synopsis: Determines if the cached & parsed data from the HTX file
  755. // is still valid. The HTX file itself may have changed
  756. // since we read and parsed it.
  757. //
  758. // History: 96/Jan/03 DwightKr created
  759. // 96/Mar/14 DwightKr check <%include%> file times
  760. //
  761. //----------------------------------------------------------------------------
  762. BOOL CHTXFile::IsCachedDataValid()
  763. {
  764. FILETIME ft;
  765. SCODE sc = GetLastWriteTime( _wcsPhysicalName, ft );
  766. if ( FAILED( sc ) )
  767. return FALSE;
  768. if ( (_ftHTXLastWriteTime.dwLowDateTime != ft.dwLowDateTime) ||
  769. (_ftHTXLastWriteTime.dwHighDateTime != ft.dwHighDateTime) )
  770. {
  771. return FALSE;
  772. }
  773. for ( unsigned i=0; i<_cIncludeFiles; i++ )
  774. {
  775. sc = GetLastWriteTime(_awcsIncludeFileName[i], ft );
  776. if ( FAILED( sc ) )
  777. return FALSE;
  778. if ( (_aftIncludeLastWriteTime[i].dwLowDateTime != ft.dwLowDateTime) ||
  779. (_aftIncludeLastWriteTime[i].dwHighDateTime != ft.dwHighDateTime) )
  780. {
  781. return FALSE;
  782. }
  783. }
  784. return TRUE;
  785. }
  786. //+---------------------------------------------------------------------------
  787. //
  788. // Member: CHTXFile::GetHeader - public
  789. //
  790. // Synopsis: Appends to a CVirtualString the data in the HTX file BEFORE
  791. // the <%begindetail%> section. This may require replacing
  792. // parameters.
  793. //
  794. // Arguments: [string] - the CVirtualString to append data to
  795. // [variableSet] - a list of replaceable parameters
  796. // [outputFormat] - format for numbers & dates
  797. //
  798. // History: 96/Jan/03 DwightKr created
  799. //
  800. //----------------------------------------------------------------------------
  801. void CHTXFile::GetHeader( CVirtualString & string,
  802. CVariableSet & variableSet,
  803. COutputFormat & outputFormat )
  804. {
  805. if ( 0 != _pVarHeader )
  806. {
  807. _pVarHeader->ReplaceParams( string, variableSet, outputFormat );
  808. }
  809. }
  810. //+---------------------------------------------------------------------------
  811. //
  812. // Member: CHTXFile::GetFooter - public
  813. //
  814. // Synopsis: Appends to a CVirtualString the data in the HTX file AFTER
  815. // the <%enddetail%> section. This may require replacing
  816. // parameters.
  817. //
  818. // Arguments: [string] - the CVirtualString to append data to
  819. // [variableSet] - a list of replaceable parameters
  820. // [outputFormat] - format for numbers & dates
  821. //
  822. // History: 96/Jan/03 DwightKr created
  823. //
  824. //----------------------------------------------------------------------------
  825. void CHTXFile::GetFooter( CVirtualString & string,
  826. CVariableSet & variableSet,
  827. COutputFormat & outputFormat )
  828. {
  829. if ( 0 != _pVarFooter )
  830. {
  831. _pVarFooter->ReplaceParams( string, variableSet, outputFormat );
  832. }
  833. }
  834. //+---------------------------------------------------------------------------
  835. //
  836. // Member: CHTXFile::CheckForSequentialAccess - public
  837. //
  838. // Synopsis: Determines if a sequential query cursor can be used to
  839. // extract query results. This is possible if the HTX file
  840. // does not use any replaceable parameters which require
  841. // data from an IRowsetScroll.
  842. //
  843. // History: 96/Jan/03 DwightKr created
  844. //
  845. //----------------------------------------------------------------------------
  846. BOOL CHTXFile::CheckForSequentialAccess()
  847. {
  848. //
  849. // If an HTX file contains any of the following variables, it must
  850. // use a non-sequential access, since we need one or more interfaces
  851. // from IRowsetScroll.
  852. //
  853. // CiMatchedRecordCount
  854. // CiRecordsNextPage
  855. // CiTotalNumberPages
  856. //
  857. if ( (0 != _pVarHeader) && (_pVarHeader->GetFlags() & eParamRequiresNonSequentialCursor) )
  858. {
  859. return FALSE;
  860. }
  861. if ( (0 != _pVarRowDetails) && (_pVarRowDetails->GetFlags() & eParamRequiresNonSequentialCursor) )
  862. {
  863. return FALSE;
  864. }
  865. if ( (0 != _pVarFooter) && (_pVarFooter->GetFlags() & eParamRequiresNonSequentialCursor) )
  866. {
  867. return FALSE;
  868. }
  869. return TRUE;
  870. }
  871. //+---------------------------------------------------------------------------
  872. //
  873. // Member: CHTXFileList::Find - public
  874. //
  875. // Synopsis: Finds a matching parsed HTX file in list, or builds a new
  876. // one if a match can not be found.
  877. //
  878. // Arguments: [wcsFileName] -- full path to HTX file
  879. // [variableSet] -- list of replaceable parameters
  880. // [outputFormat] -- format for numbers and dates
  881. // [securityIdentity] -- Logon for this query
  882. // [ulServerInstance] -- Virtual Server Instance Number
  883. //
  884. // History: 96/Mar/27 DwightKr Created.
  885. //
  886. //----------------------------------------------------------------------------
  887. CHTXFile & CHTXFileList::Find( WCHAR const * wcsFileName,
  888. CVariableSet & variableSet,
  889. COutputFormat & outputFormat,
  890. CSecurityIdentity const & securityIdentity,
  891. ULONG ulServerInstance )
  892. {
  893. Win4Assert( 0 != wcsFileName );
  894. //
  895. // Determine the name of the HTX/template file. It may have a
  896. // replaceable value from the client.
  897. //
  898. ULONG cwcOut;
  899. XPtrST<WCHAR> wcsVirtualName( ReplaceParameters( wcsFileName,
  900. variableSet,
  901. outputFormat,
  902. cwcOut ) );
  903. if ( 0 == *(wcsVirtualName.GetPointer()) )
  904. {
  905. THROW( CIDQException( MSG_CI_IDQ_MISSING_TEMPLATEFILE, 0 ) );
  906. }
  907. //
  908. // Refcount everything in the list so that we can examine the list
  909. // outside of the lock.
  910. //
  911. ULONG cItems;
  912. XArray<CHTXFile *> aHTXFile;
  913. // ==========================================
  914. {
  915. CLock lock( _mutex );
  916. cItems = _aHTXFile.Count(); // Save count of items to examine
  917. aHTXFile.Init( cItems );
  918. for (unsigned i=0; i<cItems; i++)
  919. {
  920. aHTXFile[i] = _aHTXFile[i];
  921. aHTXFile[i]->LokAddRef();
  922. }
  923. }
  924. // ==========================================
  925. //
  926. // Now walk though the list looking for a match; outside of the lock.
  927. //
  928. XInterface<CHTXFile> xHTXFile;
  929. SCODE sc = S_OK;
  930. TRY
  931. {
  932. for (unsigned i=0; i<cItems; i++)
  933. {
  934. if ( (_wcsicmp(aHTXFile[i]->GetVirtualName(), wcsVirtualName.GetPointer() ) == 0) &&
  935. (aHTXFile[i]->GetCodePage() == outputFormat.CodePage()) &&
  936. (aHTXFile[i]->GetServerInstance() == ulServerInstance) &&
  937. (aHTXFile[i]->IsCachedDataValid() )
  938. )
  939. {
  940. xHTXFile.Set( aHTXFile[i] );
  941. ciGibDebugOut(( DEB_ITRACE,
  942. "A cached version of HTX file %ws was found\n",
  943. wcsVirtualName.GetPointer() ));
  944. break;
  945. }
  946. }
  947. }
  948. CATCH( CException, e )
  949. {
  950. sc = e.GetErrorCode();
  951. }
  952. END_CATCH
  953. //
  954. // If xHTXFile is non-0, we've found a match. Decrement the ref-count
  955. // for all items which did not match.
  956. //
  957. for (unsigned i=0; i<cItems; i++)
  958. {
  959. if ( aHTXFile[i] != xHTXFile.GetPointer() )
  960. {
  961. aHTXFile[i]->Release();
  962. }
  963. }
  964. if ( S_OK != sc )
  965. {
  966. Win4Assert( xHTXFile.IsNull() );
  967. THROW( CException( sc ) );
  968. }
  969. //
  970. // We may have matched, but still not have access to this file. First, make
  971. // a quick check for an exact match on security token, and then try harder
  972. // by opening the file.
  973. //
  974. if ( !xHTXFile.IsNull() )
  975. {
  976. if ( !xHTXFile->CheckSecurity( securityIdentity ) )
  977. {
  978. HANDLE h = CreateFile( xHTXFile->GetPhysicalName(),
  979. GENERIC_READ,
  980. FILE_SHARE_READ,
  981. 0,
  982. OPEN_EXISTING,
  983. 0,
  984. 0 );
  985. //
  986. // Don't try to determine here if security caused the problem.
  987. // Just let the standard exception handling below in file parsing
  988. // deal with the error.
  989. //
  990. if ( INVALID_HANDLE_VALUE == h )
  991. {
  992. xHTXFile.Free();
  993. }
  994. else
  995. {
  996. CloseHandle( h );
  997. //
  998. // Update the security token of the cached Htx file,
  999. // to optimize away the CreateFile check in two cases:
  1000. // 1. When the file is first parsed with admin
  1001. // privileges, and all subsequent queries are with
  1002. // anonymous privileges.
  1003. // 2. When the security token changes over time
  1004. //
  1005. xHTXFile->SetSecurityToken( securityIdentity );
  1006. }
  1007. }
  1008. }
  1009. //
  1010. // If we didn't find a match, then open and parse a new HTX file, and
  1011. // add it to the list of parsed HTX files
  1012. //
  1013. if ( xHTXFile.IsNull() )
  1014. {
  1015. ciGibDebugOut(( DEB_ITRACE,
  1016. "Adding HTX file %ws to cache\n",
  1017. wcsVirtualName.GetPointer() ));
  1018. WCHAR wcsPhysicalName[MAX_PATH];
  1019. if ( outputFormat.IsValid() )
  1020. {
  1021. outputFormat.GetPhysicalPath( wcsVirtualName.GetPointer(),
  1022. wcsPhysicalName,
  1023. MAX_PATH );
  1024. }
  1025. else
  1026. {
  1027. if ( !GetFullPathName( wcsVirtualName.GetPointer(),
  1028. MAX_PATH,
  1029. wcsPhysicalName,
  1030. 0 ) )
  1031. {
  1032. THROW( CException() );
  1033. }
  1034. }
  1035. XPtr<CHTXFile> xHTXFilePtr( new CHTXFile( wcsVirtualName,
  1036. outputFormat.CodePage(),
  1037. securityIdentity,
  1038. ulServerInstance ) );
  1039. xHTXFilePtr->ParseFile( wcsPhysicalName,
  1040. variableSet,
  1041. outputFormat );
  1042. {
  1043. // ==========================================
  1044. CLock lock( _mutex );
  1045. _aHTXFile.Add( xHTXFilePtr.GetPointer(), _aHTXFile.Count() );
  1046. xHTXFilePtr->LokAddRef();
  1047. // ==========================================
  1048. }
  1049. xHTXFile.Set( xHTXFilePtr.Acquire() );
  1050. }
  1051. // CopyStringValue can fail.
  1052. variableSet.CopyStringValue( ISAPI_CI_TEMPLATE,
  1053. xHTXFile->GetVirtualName(),
  1054. 0 );
  1055. return *xHTXFile.Acquire();
  1056. }
  1057. //+---------------------------------------------------------------------------
  1058. //
  1059. // Member: CHTXFileList::~CHTXFileList - public destructor
  1060. //
  1061. // History: 96/Mar/27 DwightKr Created.
  1062. //
  1063. //----------------------------------------------------------------------------
  1064. CHTXFileList::~CHTXFileList()
  1065. {
  1066. for (unsigned i=0; i<_aHTXFile.Count(); i++)
  1067. {
  1068. ciGibDebugOut(( DEB_ITRACE,
  1069. "Deleting HTX cache entry %ws\n",
  1070. _aHTXFile[i]->GetVirtualName() ));
  1071. delete _aHTXFile[i];
  1072. }
  1073. delete _pCanonicHTX;
  1074. }
  1075. //+---------------------------------------------------------------------------
  1076. //
  1077. // Member: CHTXFileList::Release - public
  1078. //
  1079. // Synopsis: Releases the HTX file by decrementing its refcount.
  1080. //
  1081. // Arguments: [htxFile] -- pointer to the HTX file object
  1082. //
  1083. // History: 96/Mar/27 DwightKr Created.
  1084. //
  1085. //----------------------------------------------------------------------------
  1086. void CHTXFileList::Release( CHTXFile & htxFile )
  1087. {
  1088. htxFile.Release();
  1089. }
  1090. //+---------------------------------------------------------------------------
  1091. //
  1092. // Member: CHTXFileList::DeleteZombies - public
  1093. //
  1094. // Synopsis: Removes HTX files that are zombies; i.e. out of date
  1095. //
  1096. // History: 96/Mar/28 DwightKr Created.
  1097. //
  1098. //----------------------------------------------------------------------------
  1099. void CHTXFileList::DeleteZombies()
  1100. {
  1101. // ==========================================
  1102. CLock lock( _mutex );
  1103. unsigned i=0;
  1104. while ( i<_aHTXFile.Count() )
  1105. {
  1106. if ( _aHTXFile[i]->LokGetRefCount() == 0 &&
  1107. !_aHTXFile[i]->IsCachedDataValid() )
  1108. {
  1109. CHTXFile * pHTXFile = _aHTXFile[i];
  1110. _aHTXFile.Remove(i);
  1111. ciGibDebugOut(( DEB_ITRACE,
  1112. "Deleting zombie HTX cache entry %ws, %d entries cached\n",
  1113. pHTXFile->GetVirtualName(),
  1114. _aHTXFile.Count() ));
  1115. delete pHTXFile;
  1116. }
  1117. else
  1118. {
  1119. ciGibDebugOut(( DEB_ITRACE,
  1120. "HTX cache entry %ws was not deleted, refCount=%d\n",
  1121. _aHTXFile[i]->GetVirtualName(),
  1122. _aHTXFile[i]->LokGetRefCount() ));
  1123. i++;
  1124. }
  1125. }
  1126. // ==========================================
  1127. }