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.

1378 lines
41 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 2000.
  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. wcscpy( _wcsPhysicalName, wcsFileName );
  358. //
  359. // Read the entire file into a buffer
  360. //
  361. CVirtualString wcsBuffer;
  362. ExpandFile( _wcsPhysicalName, webServer, wcsBuffer, _ftHTXLastWriteTime );
  363. Win4Assert( wcsBuffer.GetPointer() != 0 );
  364. //
  365. // Break the buffer into the sections; the header, the detail section,
  366. // and the footer. Verify that if there is a <%BeginDetail%>
  367. // section, then there MUST be a <%EndDetail%> section AFTER it, not
  368. // before. Neither <%EndDetail%> nor <%BeginDetail%> can appear on
  369. // their own.
  370. //
  371. //
  372. // Find the <%BeginDetail%> and <%EndDetail%> sections
  373. //
  374. _wcsFileBuffer = wcsBuffer.StrDup(); // Save buffer
  375. WCHAR * wcsHeader = _wcsFileBuffer; // Assume a header
  376. WCHAR * wcsRowDetails = wcsipattern(wcsHeader, L"<%BEGINDETAIL%>" );
  377. WCHAR * wcsFooter = wcsipattern(wcsHeader, L"<%ENDDETAIL%>" );
  378. if ( wcsHeader == wcsRowDetails )
  379. {
  380. //
  381. // No header found in this file; it begins with the detail section.
  382. //
  383. wcsHeader = 0;
  384. }
  385. const int cwcBeginDetail = 15;
  386. const int cwcEndDetail = 13;
  387. Win4Assert( cwcBeginDetail == wcslen( L"<%BEGINDETAIL%>" ) );
  388. Win4Assert( cwcEndDetail == wcslen( L"<%ENDDETAIL%>" ) );
  389. if ( 0 != wcsRowDetails )
  390. {
  391. //
  392. // A <%BeginDetail%> section was found. We better also have an
  393. // <%EndDetail%> section AFTER the <%BeginDetail%> section.
  394. //
  395. *wcsRowDetails = 0; // Null terminate the header string
  396. wcsRowDetails += cwcBeginDetail;
  397. if ( 0 != wcsFooter )
  398. {
  399. if ( wcsFooter < wcsRowDetails )
  400. {
  401. //
  402. // The <%EndDetail%> was found before the <%BeginDetail%>
  403. //
  404. WCHAR * wcsHTXFileName;
  405. LONG lLineNumber;
  406. GetFileNameAndLineNumber( (int)(wcsFooter - _wcsFileBuffer),
  407. wcsHTXFileName,
  408. lLineNumber );
  409. THROW( CHTXException(MSG_CI_HTX_ENDDETAIL_BEFORE_BEGINDETAIL,
  410. wcsHTXFileName,
  411. lLineNumber) );
  412. }
  413. *wcsFooter = 0; // Null terminate the BeginDetail section
  414. wcsFooter += cwcEndDetail;
  415. }
  416. else
  417. {
  418. //
  419. // Report an error: <%BeginDetail%> without an <%EndDetail%>
  420. //
  421. WCHAR * wcsHTXFileName;
  422. LONG lLineNumber;
  423. GetFileNameAndLineNumber( (int)(wcsRowDetails - _wcsFileBuffer),
  424. wcsHTXFileName,
  425. lLineNumber );
  426. THROW( CHTXException(MSG_CI_HTX_NO_ENDDETAIL_SECTION,
  427. wcsHTXFileName,
  428. lLineNumber) );
  429. }
  430. }
  431. else if ( 0 != wcsFooter )
  432. {
  433. //
  434. // A <%BeginDetail%> section could be found. There should
  435. // be no <%EndDetail%> section either.
  436. //
  437. WCHAR * wcsHTXFileName;
  438. LONG lLineNumber;
  439. GetFileNameAndLineNumber( (int)(wcsFooter - _wcsFileBuffer),
  440. wcsHTXFileName,
  441. lLineNumber );
  442. THROW( CHTXException(MSG_CI_HTX_NO_BEGINDETAIL_SECTION,
  443. wcsHTXFileName,
  444. lLineNumber) );
  445. }
  446. if ( 0 != wcsHeader )
  447. {
  448. _pVarHeader = new CParameterReplacer ( wcsHeader,
  449. L"<%",
  450. L"%>" );
  451. _pVarHeader->ParseString( variableSet );
  452. }
  453. if ( 0 != wcsRowDetails )
  454. {
  455. _pVarRowDetails = new CParameterReplacer ( wcsRowDetails,
  456. L"<%",
  457. L"%>" );
  458. _pVarRowDetails->ParseString( variableSet );
  459. }
  460. if ( 0 != wcsFooter )
  461. {
  462. _pVarFooter = new CParameterReplacer ( wcsFooter,
  463. L"<%",
  464. L"%>" );
  465. _pVarFooter->ParseString( variableSet );
  466. }
  467. _fSequential = CheckForSequentialAccess();
  468. }
  469. //+---------------------------------------------------------------------------
  470. //
  471. // Member: CHTXFile::ReadFile - public
  472. //
  473. // Synopsis: Read the HTX file into a buffer
  474. //
  475. // Arguments: [wcsFileName] - full physical path name of file
  476. // [ftLastWrite] - File's last write time is stored here
  477. //
  478. // History: 96/Jan/03 DwightKr created
  479. // 96/Apr/06 DwightKr add support for unicode files
  480. //
  481. //----------------------------------------------------------------------------
  482. WCHAR * CHTXFile::ReadFile( WCHAR const * wcsFileName,
  483. FILETIME & ftLastWrite )
  484. {
  485. Win4Assert ( 0 != wcsFileName );
  486. // We don't support impersonation for scripts.
  487. // It involves getting the server ip address, vpath and then using
  488. // that for impersonation
  489. if ( IsNetPath(wcsFileName) )
  490. {
  491. ciGibDebugOut(( DEB_ERROR, "The htx file (%ws) is on remote UNC\n",
  492. wcsFileName ));
  493. THROW( CHTXException( MSG_CI_SCRIPTS_ON_REMOTE_UNC, wcsFileName, 0 ));
  494. }
  495. //
  496. // Verify the HTX file exists, and is a file, not a directory.
  497. //
  498. WIN32_FIND_DATA ffData;
  499. if ( !GetFileAttributesEx( wcsFileName, GetFileExInfoStandard, &ffData ) )
  500. {
  501. ULONG error = GetLastError();
  502. ciGibDebugOut(( DEB_IERROR,
  503. "Unable to GetFileAttributesEx(%ws) GetLastError=0x%x\n",
  504. wcsFileName,
  505. error ));
  506. THROW( CIDQException(MSG_CI_IDQ_NO_SUCH_TEMPLATE, 0) );
  507. }
  508. if ( (ffData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 )
  509. {
  510. THROW( CIDQException(MSG_CI_IDQ_NO_SUCH_TEMPLATE, 0) );
  511. }
  512. //
  513. // Save the last write time of this file.
  514. //
  515. ftLastWrite = ffData.ftLastWriteTime;
  516. //
  517. // Open the file and map its contents
  518. //
  519. CFileMapView mapView( wcsFileName );
  520. mapView.Init();
  521. int cbBuffer = mapView.GetBufferSize() + 1;
  522. XArray<WCHAR> pwBuffer(cbBuffer);
  523. //
  524. // If the first two BYTES of the file are 0xFF 0xFE, then this is a
  525. // unicode file, and we don't need to convert it.
  526. //
  527. if ( mapView.IsUnicode() )
  528. {
  529. RtlCopyMemory( pwBuffer.Get(), mapView.GetBuffer()+2, cbBuffer-2 );
  530. pwBuffer[ ( cbBuffer - 2 ) / sizeof WCHAR ] = 0;
  531. return pwBuffer.Acquire();
  532. }
  533. //
  534. // Copy & convert the ASCII buffer to a WCHAR buffer.
  535. //
  536. int cwBuffer = mapView.GetBufferSize() + 1;
  537. int cwConvert;
  538. do
  539. {
  540. cwConvert = MultiByteToWideChar(_codePage,
  541. 0,
  542. (const char *) mapView.GetBuffer(), // Ptr to input buf
  543. mapView.GetBufferSize(),// Size of input buf
  544. pwBuffer.Get(), // Ptr to output buf
  545. cwBuffer - 1 ); // Size of output buf
  546. if ( 0 == cwConvert )
  547. {
  548. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  549. {
  550. cwBuffer += (cwBuffer/2);
  551. delete pwBuffer.Acquire();
  552. pwBuffer.Init(cwBuffer);
  553. }
  554. else
  555. {
  556. THROW( CException() );
  557. }
  558. }
  559. else
  560. {
  561. pwBuffer[cwConvert] = 0; // Null terminate the buffer
  562. }
  563. Win4Assert( cwConvert < cwBuffer );
  564. } while ( 0 == cwConvert );
  565. return pwBuffer.Acquire();
  566. }
  567. //+---------------------------------------------------------------------------
  568. //
  569. // Member: CHTXFile::ExpandFile, public
  570. //
  571. // Synopsis: Expands the contents of an HTX file into memory, processing
  572. // included files.
  573. //
  574. // Arguments: [wcsFileName] - file path name of the file to be expanded
  575. // [webServer] - CWebServer for virtual path translation
  576. // [vString] - String to which file contents are appended
  577. // [ftLastWrite] - File's last write time is stored here
  578. //
  579. //----------------------------------------------------------------------------
  580. void CHTXFile::ExpandFile( WCHAR const * wcsFileName,
  581. CWebServer & webServer,
  582. CVirtualString & vString,
  583. FILETIME & ftLastWrite )
  584. {
  585. Win4Assert ( 0 != wcsFileName );
  586. //
  587. // Read the existing file into a WCHAR buffer.
  588. //
  589. XPtrST<WCHAR> wcsBuffer( ReadFile(wcsFileName, ftLastWrite) );
  590. WCHAR * wcsString = wcsBuffer.GetPointer();
  591. ULONG cwcString = wcslen( wcsString );
  592. WCHAR * wcsEnd = wcsString + cwcString;
  593. //
  594. // Search the WCHAR buffer for <%include ... %>
  595. //
  596. WCHAR * wcsPattern = L"<%INCLUDE ";
  597. ULONG cwcPattern = 10;
  598. Win4Assert( cwcPattern == wcslen(wcsPattern) );
  599. WCHAR * wcsToken = wcsipattern( wcsString, wcsPattern );
  600. while ( 0 != wcsToken )
  601. {
  602. if ( _cIncludeFiles >= MAX_HTX_INCLUDE_FILES )
  603. {
  604. LONG cLines = CountLines( wcsBuffer.GetPointer(), wcsToken );
  605. THROW( CHTXException(MSG_CI_HTX_TOO_MANY_INCLUDES,
  606. wcsFileName,
  607. cLines) );
  608. }
  609. //
  610. // Concatentate everything before the <%include .. %> into the
  611. // virtual string.
  612. //
  613. ULONG cwcCat = (ULONG)(wcsToken - wcsString);
  614. Win4Assert( cwcCat <= cwcString );
  615. *wcsToken = 0; // Null terminate the string
  616. vString.StrCat( wcsString, cwcCat );
  617. //
  618. // Find the end of the <%include ... %>
  619. //
  620. wcsToken += cwcPattern; // Skip the <%include
  621. WCHAR * wcsIncludeFileName = wcsToken; // Point to the include filename
  622. wcsString = wcs2chr( wcsToken, L"%>" ); // Point to the end of the filename
  623. //
  624. // wcsString should be pointing to the %> at the end of the include
  625. //
  626. if ( 0 == wcsString )
  627. {
  628. //
  629. // Missing %>
  630. //
  631. LONG cLines = CountLines( vString.GetPointer(), wcsToken );
  632. THROW( CHTXException(MSG_CI_HTX_ILL_FORMED_INCLUDE,
  633. wcsFileName,
  634. cLines) );
  635. }
  636. //
  637. // Process the <%include ... %>
  638. //
  639. *wcsString = 0;
  640. if ( (wcsString - wcsIncludeFileName) >= MAX_PATH )
  641. {
  642. LONG cLines = CountLines( wcsBuffer.GetPointer(), wcsToken );
  643. THROW( CHTXException(MSG_CI_HTX_INVALID_INCLUDE_FILENAME,
  644. wcsFileName,
  645. cLines ) );
  646. }
  647. WCHAR awcPhysicalPath[MAX_PATH];
  648. webServer.GetPhysicalPath( wcsIncludeFileName,
  649. awcPhysicalPath,
  650. MAX_PATH );
  651. //
  652. // Save the include filename away
  653. //
  654. ULONG cwcPhysicalPath = wcslen(awcPhysicalPath) + 1;
  655. _awcsIncludeFileName[_cIncludeFiles] = new WCHAR[ cwcPhysicalPath ];
  656. _aulIncludeFileOffset[_cIncludeFiles] = vString.StrLen();
  657. RtlCopyMemory( _awcsIncludeFileName[_cIncludeFiles],
  658. awcPhysicalPath,
  659. cwcPhysicalPath * sizeof(WCHAR) );
  660. FILETIME & ftLastWrite = _aftIncludeLastWriteTime[ _cIncludeFiles ];
  661. _cIncludeFiles++;
  662. ExpandFile( awcPhysicalPath, webServer, vString, ftLastWrite );
  663. wcsString += 2; // Skip the %>
  664. cwcString = (ULONG)(wcsEnd - wcsString);
  665. wcsToken = wcsipattern( wcsString, wcsPattern );
  666. }
  667. vString.StrCat( wcsString, cwcString );
  668. }
  669. //+---------------------------------------------------------------------------
  670. //
  671. // Member: CHTXFile::GetFileNameAndLineNumber
  672. //
  673. // Synopsis: Determines the filename & line number amoung a group of
  674. // nested includes for a particular offset into the buffer.
  675. //
  676. // Arguments: [offset] - offset of the error in the overall buffer
  677. // [wcsFileName] - resulting name of file containing error
  678. // [lineNumber] - line # containing the error
  679. //
  680. // History: 96/Jun/25 DwightKr created
  681. //
  682. //----------------------------------------------------------------------------
  683. void CHTXFile::GetFileNameAndLineNumber( int offset,
  684. WCHAR *& wcsFileName,
  685. LONG & lineNumber )
  686. {
  687. //
  688. // Search the array of offsets for the one containing our offset
  689. //
  690. for (ULONG i = 0;
  691. (i < _cIncludeFiles) && ((ULONG) offset > _aulIncludeFileOffset[i]);
  692. i++ )
  693. {
  694. }
  695. //
  696. // Save a pointer to the name of the file containing the error
  697. //
  698. WCHAR const * pCurrent = _wcsFileBuffer;
  699. if ( 0 == i )
  700. {
  701. //
  702. // We have a problem in the outer-most container file; not
  703. // an include file.
  704. //
  705. wcsFileName = _wcsVirtualName;
  706. }
  707. else
  708. {
  709. wcsFileName = _awcsIncludeFileName[i-1];
  710. pCurrent += _aulIncludeFileOffset[i-1];
  711. }
  712. //
  713. // Count the number of lines in this sub-file
  714. //
  715. Win4Assert( 0 != _wcsFileBuffer );
  716. WCHAR const * pEnd = _wcsFileBuffer + offset;
  717. lineNumber = CountLines( pCurrent, pEnd );
  718. }
  719. //+---------------------------------------------------------------------------
  720. //
  721. // Member: CHTXFile::CountLines - private
  722. //
  723. // Synopsis: Deterines the number of lines (CR's) between the start
  724. // of the buffer and the end.
  725. //
  726. // Arguments: [wcsStart] - start location of search
  727. // [wcsEnd] - end of search
  728. //
  729. // History: 96/Jun/25 DwightKr created
  730. //
  731. //----------------------------------------------------------------------------
  732. LONG CHTXFile::CountLines( WCHAR const * wcsStart,
  733. WCHAR const * wcsEnd ) const
  734. {
  735. Win4Assert( 0 != wcsStart );
  736. Win4Assert( 0 != wcsEnd );
  737. LONG cLines = 1;
  738. while ( wcsStart <= wcsEnd )
  739. {
  740. if ( L'\n' == *wcsStart )
  741. {
  742. cLines++;
  743. }
  744. wcsStart++;
  745. }
  746. return cLines;
  747. }
  748. //+---------------------------------------------------------------------------
  749. //
  750. // Member: CHTXFile::IsCachedDataValid - public
  751. //
  752. // Synopsis: Determines if the cached & parsed data from the HTX file
  753. // is still valid. The HTX file itself may have changed
  754. // since we read and parsed it.
  755. //
  756. // History: 96/Jan/03 DwightKr created
  757. // 96/Mar/14 DwightKr check <%include%> file times
  758. //
  759. //----------------------------------------------------------------------------
  760. BOOL CHTXFile::IsCachedDataValid()
  761. {
  762. FILETIME ft;
  763. SCODE sc = GetLastWriteTime( _wcsPhysicalName, ft );
  764. if ( FAILED( sc ) )
  765. return FALSE;
  766. if ( (_ftHTXLastWriteTime.dwLowDateTime != ft.dwLowDateTime) ||
  767. (_ftHTXLastWriteTime.dwHighDateTime != ft.dwHighDateTime) )
  768. {
  769. return FALSE;
  770. }
  771. for ( unsigned i=0; i<_cIncludeFiles; i++ )
  772. {
  773. sc = GetLastWriteTime(_awcsIncludeFileName[i], ft );
  774. if ( FAILED( sc ) )
  775. return FALSE;
  776. if ( (_aftIncludeLastWriteTime[i].dwLowDateTime != ft.dwLowDateTime) ||
  777. (_aftIncludeLastWriteTime[i].dwHighDateTime != ft.dwHighDateTime) )
  778. {
  779. return FALSE;
  780. }
  781. }
  782. return TRUE;
  783. }
  784. //+---------------------------------------------------------------------------
  785. //
  786. // Member: CHTXFile::GetHeader - public
  787. //
  788. // Synopsis: Appends to a CVirtualString the data in the HTX file BEFORE
  789. // the <%begindetail%> section. This may require replacing
  790. // parameters.
  791. //
  792. // Arguments: [string] - the CVirtualString to append data to
  793. // [variableSet] - a list of replaceable parameters
  794. // [outputFormat] - format for numbers & dates
  795. //
  796. // History: 96/Jan/03 DwightKr created
  797. //
  798. //----------------------------------------------------------------------------
  799. void CHTXFile::GetHeader( CVirtualString & string,
  800. CVariableSet & variableSet,
  801. COutputFormat & outputFormat )
  802. {
  803. if ( 0 != _pVarHeader )
  804. {
  805. _pVarHeader->ReplaceParams( string, variableSet, outputFormat );
  806. }
  807. }
  808. //+---------------------------------------------------------------------------
  809. //
  810. // Member: CHTXFile::GetFooter - public
  811. //
  812. // Synopsis: Appends to a CVirtualString the data in the HTX file AFTER
  813. // the <%enddetail%> section. This may require replacing
  814. // parameters.
  815. //
  816. // Arguments: [string] - the CVirtualString to append data to
  817. // [variableSet] - a list of replaceable parameters
  818. // [outputFormat] - format for numbers & dates
  819. //
  820. // History: 96/Jan/03 DwightKr created
  821. //
  822. //----------------------------------------------------------------------------
  823. void CHTXFile::GetFooter( CVirtualString & string,
  824. CVariableSet & variableSet,
  825. COutputFormat & outputFormat )
  826. {
  827. if ( 0 != _pVarFooter )
  828. {
  829. _pVarFooter->ReplaceParams( string, variableSet, outputFormat );
  830. }
  831. }
  832. //+---------------------------------------------------------------------------
  833. //
  834. // Member: CHTXFile::CheckForSequentialAccess - public
  835. //
  836. // Synopsis: Determines if a sequential query cursor can be used to
  837. // extract query results. This is possible if the HTX file
  838. // does not use any replaceable parameters which require
  839. // data from an IRowsetScroll.
  840. //
  841. // History: 96/Jan/03 DwightKr created
  842. //
  843. //----------------------------------------------------------------------------
  844. BOOL CHTXFile::CheckForSequentialAccess()
  845. {
  846. //
  847. // If an HTX file contains any of the following variables, it must
  848. // use a non-sequential access, since we need one or more interfaces
  849. // from IRowsetScroll.
  850. //
  851. // CiMatchedRecordCount
  852. // CiRecordsNextPage
  853. // CiTotalNumberPages
  854. //
  855. if ( (0 != _pVarHeader) && (_pVarHeader->GetFlags() & eParamRequiresNonSequentialCursor) )
  856. {
  857. return FALSE;
  858. }
  859. if ( (0 != _pVarRowDetails) && (_pVarRowDetails->GetFlags() & eParamRequiresNonSequentialCursor) )
  860. {
  861. return FALSE;
  862. }
  863. if ( (0 != _pVarFooter) && (_pVarFooter->GetFlags() & eParamRequiresNonSequentialCursor) )
  864. {
  865. return FALSE;
  866. }
  867. return TRUE;
  868. }
  869. //+---------------------------------------------------------------------------
  870. //
  871. // Member: CHTXFileList::Find - public
  872. //
  873. // Synopsis: Finds a matching parsed HTX file in list, or builds a new
  874. // one if a match can not be found.
  875. //
  876. // Arguments: [wcsFileName] -- full path to HTX file
  877. // [variableSet] -- list of replaceable parameters
  878. // [outputFormat] -- format for numbers and dates
  879. // [securityIdentity] -- Logon for this query
  880. // [ulServerInstance] -- Virtual Server Instance Number
  881. //
  882. // History: 96/Mar/27 DwightKr Created.
  883. //
  884. //----------------------------------------------------------------------------
  885. CHTXFile & CHTXFileList::Find( WCHAR const * wcsFileName,
  886. CVariableSet & variableSet,
  887. COutputFormat & outputFormat,
  888. CSecurityIdentity const & securityIdentity,
  889. ULONG ulServerInstance )
  890. {
  891. Win4Assert( 0 != wcsFileName );
  892. //
  893. // Determine the name of the HTX/template file. It may have a
  894. // replaceable value from the client.
  895. //
  896. ULONG cwcOut;
  897. XPtrST<WCHAR> wcsVirtualName( ReplaceParameters( wcsFileName,
  898. variableSet,
  899. outputFormat,
  900. cwcOut ) );
  901. if ( 0 == *(wcsVirtualName.GetPointer()) )
  902. {
  903. THROW( CIDQException( MSG_CI_IDQ_MISSING_TEMPLATEFILE, 0 ) );
  904. }
  905. //
  906. // Refcount everything in the list so that we can examine the list
  907. // outside of the lock.
  908. //
  909. ULONG cItems;
  910. XArray<CHTXFile *> aHTXFile;
  911. // ==========================================
  912. {
  913. CLock lock( _mutex );
  914. cItems = _aHTXFile.Count(); // Save count of items to examine
  915. aHTXFile.Init( cItems );
  916. for (unsigned i=0; i<cItems; i++)
  917. {
  918. aHTXFile[i] = _aHTXFile[i];
  919. aHTXFile[i]->LokAddRef();
  920. }
  921. }
  922. // ==========================================
  923. //
  924. // Now walk though the list looking for a match; outside of the lock.
  925. //
  926. XInterface<CHTXFile> xHTXFile;
  927. SCODE sc = S_OK;
  928. TRY
  929. {
  930. for (unsigned i=0; i<cItems; i++)
  931. {
  932. if ( (_wcsicmp(aHTXFile[i]->GetVirtualName(), wcsVirtualName.GetPointer() ) == 0) &&
  933. (aHTXFile[i]->GetCodePage() == outputFormat.CodePage()) &&
  934. (aHTXFile[i]->GetServerInstance() == ulServerInstance) &&
  935. (aHTXFile[i]->IsCachedDataValid() )
  936. )
  937. {
  938. xHTXFile.Set( aHTXFile[i] );
  939. ciGibDebugOut(( DEB_ITRACE,
  940. "A cached version of HTX file %ws was found\n",
  941. wcsVirtualName.GetPointer() ));
  942. break;
  943. }
  944. }
  945. }
  946. CATCH( CException, e )
  947. {
  948. sc = e.GetErrorCode();
  949. }
  950. END_CATCH
  951. //
  952. // If xHTXFile is non-0, we've found a match. Decrement the ref-count
  953. // for all items which did not match.
  954. //
  955. for (unsigned i=0; i<cItems; i++)
  956. {
  957. if ( aHTXFile[i] != xHTXFile.GetPointer() )
  958. {
  959. aHTXFile[i]->Release();
  960. }
  961. }
  962. if ( S_OK != sc )
  963. {
  964. Win4Assert( xHTXFile.IsNull() );
  965. THROW( CException( sc ) );
  966. }
  967. //
  968. // We may have matched, but still not have access to this file. First, make
  969. // a quick check for an exact match on security token, and then try harder
  970. // by opening the file.
  971. //
  972. if ( !xHTXFile.IsNull() )
  973. {
  974. if ( !xHTXFile->CheckSecurity( securityIdentity ) )
  975. {
  976. HANDLE h = CreateFile( xHTXFile->GetPhysicalName(),
  977. GENERIC_READ,
  978. FILE_SHARE_READ,
  979. 0,
  980. OPEN_EXISTING,
  981. 0,
  982. 0 );
  983. //
  984. // Don't try to determine here if security caused the problem.
  985. // Just let the standard exception handling below in file parsing
  986. // deal with the error.
  987. //
  988. if ( INVALID_HANDLE_VALUE == h )
  989. {
  990. xHTXFile.Free();
  991. }
  992. else
  993. {
  994. CloseHandle( h );
  995. //
  996. // Update the security token of the cached Htx file,
  997. // to optimize away the CreateFile check in two cases:
  998. // 1. When the file is first parsed with admin
  999. // privileges, and all subsequent queries are with
  1000. // anonymous privileges.
  1001. // 2. When the security token changes over time
  1002. //
  1003. xHTXFile->SetSecurityToken( securityIdentity );
  1004. }
  1005. }
  1006. }
  1007. //
  1008. // If we didn't find a match, then open and parse a new HTX file, and
  1009. // add it to the list of parsed HTX files
  1010. //
  1011. if ( xHTXFile.IsNull() )
  1012. {
  1013. ciGibDebugOut(( DEB_ITRACE,
  1014. "Adding HTX file %ws to cache\n",
  1015. wcsVirtualName.GetPointer() ));
  1016. WCHAR wcsPhysicalName[MAX_PATH];
  1017. if ( outputFormat.IsValid() )
  1018. {
  1019. outputFormat.GetPhysicalPath( wcsVirtualName.GetPointer(),
  1020. wcsPhysicalName,
  1021. MAX_PATH );
  1022. }
  1023. else
  1024. {
  1025. if ( !GetFullPathName( wcsVirtualName.GetPointer(),
  1026. MAX_PATH,
  1027. wcsPhysicalName,
  1028. 0 ) )
  1029. {
  1030. THROW( CException() );
  1031. }
  1032. }
  1033. XPtr<CHTXFile> xHTXFilePtr( new CHTXFile( wcsVirtualName,
  1034. outputFormat.CodePage(),
  1035. securityIdentity,
  1036. ulServerInstance ) );
  1037. xHTXFilePtr->ParseFile( wcsPhysicalName,
  1038. variableSet,
  1039. outputFormat );
  1040. {
  1041. // ==========================================
  1042. CLock lock( _mutex );
  1043. _aHTXFile.Add( xHTXFilePtr.GetPointer(), _aHTXFile.Count() );
  1044. xHTXFilePtr->LokAddRef();
  1045. // ==========================================
  1046. }
  1047. xHTXFile.Set( xHTXFilePtr.Acquire() );
  1048. }
  1049. // CopyStringValue can fail.
  1050. variableSet.CopyStringValue( ISAPI_CI_TEMPLATE,
  1051. xHTXFile->GetVirtualName(),
  1052. 0 );
  1053. return *xHTXFile.Acquire();
  1054. }
  1055. //+---------------------------------------------------------------------------
  1056. //
  1057. // Member: CHTXFileList::FindCanonicHTX
  1058. //
  1059. // Synopsis: Finds the standard HTX file for canonical output
  1060. //
  1061. // Arguments: [variableSet] - list of replaceable parameters
  1062. // [codePage] - code page to open HTX with
  1063. // [securityIdentity] - Identity of the htx file needed
  1064. // [ulServerInstance] - Virtual Server metabase instance #
  1065. //
  1066. // History: 7-03-96 srikants Created
  1067. //
  1068. // Notes: There is nothing to be displayed for the canonical output.
  1069. //
  1070. //----------------------------------------------------------------------------
  1071. CHTXFile & CHTXFileList::FindCanonicHTX( CVariableSet & variableSet,
  1072. UINT codePage,
  1073. CSecurityIdentity const & securityIdentity,
  1074. ULONG ulServerInstance )
  1075. {
  1076. CHTXFile * pHTXFile = 0;
  1077. CLock lock(_mutex);
  1078. if ( 0 == _pCanonicHTX )
  1079. {
  1080. ciGibDebugOut(( DEB_ITRACE,
  1081. "Adding Canonic HTX file cache\n" ));
  1082. unsigned len = wcslen( CANONIC_HTX_FILE );
  1083. XPtrST<WCHAR> wcsVirtualName( new WCHAR [len+1] );
  1084. RtlCopyMemory( wcsVirtualName.GetPointer(), CANONIC_HTX_FILE,
  1085. (len+1)*sizeof(WCHAR) );
  1086. pHTXFile = new CHTXFile( wcsVirtualName,
  1087. codePage,
  1088. securityIdentity,
  1089. ulServerInstance );
  1090. _pCanonicHTX = pHTXFile;
  1091. pHTXFile->LokAddRef();
  1092. }
  1093. else
  1094. {
  1095. pHTXFile = _pCanonicHTX;
  1096. pHTXFile->LokAddRef();
  1097. }
  1098. Win4Assert( 0 != variableSet.Find( ISAPI_CI_BOOKMARK ) );
  1099. variableSet.CopyStringValue( ISAPI_CI_TEMPLATE,
  1100. pHTXFile->GetVirtualName(),
  1101. 0 );
  1102. return *pHTXFile;
  1103. }
  1104. //+---------------------------------------------------------------------------
  1105. //
  1106. // Member: CHTXFileList::~CHTXFileList - public destructor
  1107. //
  1108. // History: 96/Mar/27 DwightKr Created.
  1109. //
  1110. //----------------------------------------------------------------------------
  1111. CHTXFileList::~CHTXFileList()
  1112. {
  1113. for (unsigned i=0; i<_aHTXFile.Count(); i++)
  1114. {
  1115. ciGibDebugOut(( DEB_ITRACE,
  1116. "Deleting HTX cache entry %ws\n",
  1117. _aHTXFile[i]->GetVirtualName() ));
  1118. delete _aHTXFile[i];
  1119. }
  1120. delete _pCanonicHTX;
  1121. }
  1122. //+---------------------------------------------------------------------------
  1123. //
  1124. // Member: CHTXFileList::Release - public
  1125. //
  1126. // Synopsis: Releases the HTX file by decrementing its refcount.
  1127. //
  1128. // Arguments: [htxFile] -- pointer to the HTX file object
  1129. //
  1130. // History: 96/Mar/27 DwightKr Created.
  1131. //
  1132. //----------------------------------------------------------------------------
  1133. void CHTXFileList::Release( CHTXFile & htxFile )
  1134. {
  1135. htxFile.Release();
  1136. }
  1137. //+---------------------------------------------------------------------------
  1138. //
  1139. // Member: CHTXFileList::DeleteZombies - public
  1140. //
  1141. // Synopsis: Removes HTX files that are zombies; i.e. out of date
  1142. //
  1143. // History: 96/Mar/28 DwightKr Created.
  1144. //
  1145. //----------------------------------------------------------------------------
  1146. void CHTXFileList::DeleteZombies()
  1147. {
  1148. // ==========================================
  1149. CLock lock( _mutex );
  1150. unsigned i=0;
  1151. while ( i<_aHTXFile.Count() )
  1152. {
  1153. if ( _aHTXFile[i]->LokGetRefCount() == 0 &&
  1154. !_aHTXFile[i]->IsCachedDataValid() )
  1155. {
  1156. CHTXFile * pHTXFile = _aHTXFile[i];
  1157. _aHTXFile.Remove(i);
  1158. ciGibDebugOut(( DEB_ITRACE,
  1159. "Deleting zombie HTX cache entry %ws, %d entries cached\n",
  1160. pHTXFile->GetVirtualName(),
  1161. _aHTXFile.Count() ));
  1162. delete pHTXFile;
  1163. }
  1164. else
  1165. {
  1166. ciGibDebugOut(( DEB_ITRACE,
  1167. "HTX cache entry %ws was not deleted, refCount=%d\n",
  1168. _aHTXFile[i]->GetVirtualName(),
  1169. _aHTXFile[i]->LokGetRefCount() ));
  1170. i++;
  1171. }
  1172. }
  1173. // ==========================================
  1174. }