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.

3981 lines
97 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. odbcreq.cxx
  5. Abstract:
  6. ODBC Request class used for ODBC requests from a query file
  7. Author:
  8. John Ludeman (johnl) 22-Feb-1995
  9. Revision History:
  10. MuraliK 25-Aug-1995 Fixed a heap corruption problem
  11. Phillich 24-Jan-1996 Fixed nested Ifs problem
  12. --*/
  13. #include "precomp.hxx"
  14. //
  15. // Accumulate and output data in chunks of this size
  16. //
  17. #define OUTPUT_BUFFER_SIZE 8192
  18. //
  19. // This is the maximum value for the expires time. It's 10 years in seconds
  20. //
  21. #define MAX_EXPIRES_TIME 0x12cc0300
  22. //
  23. // The special tag names for marking the beginning and ending of the
  24. // special tag sections
  25. //
  26. #define BEGIN_DETAIL_TEXT "begindetail"
  27. #define END_DETAIL_TEXT "enddetail"
  28. #define IF_TEXT "if"
  29. #define ELSE_TEXT "else"
  30. #define END_IF_TEXT "endif"
  31. //
  32. // Does a case insensitive compare of a .idc field name
  33. //
  34. #define COMP_FIELD( pchName, pchField, cch ) ((toupper(*(pchName)) == \
  35. toupper(*(pchField))) && \
  36. !_strnicmp( (pchName), (pchField), (cch)))
  37. //
  38. // Given a pointer to a token, skips to the next white space
  39. // delimited token
  40. //
  41. #define NEXT_TOKEN( pchToken ) SkipWhite( SkipNonWhite( pchToken ) )
  42. ALLOC_CACHE_HANDLER * ODBC_REQ::sm_pachOdbcRequests;
  43. //
  44. // Globals
  45. //
  46. extern BOOL g_fIsSystemDBCS; // Is this system DBCS?
  47. //
  48. // Local Function Prototypes
  49. //
  50. HRESULT
  51. DoSynchronousReadFile(
  52. IN HANDLE hFile,
  53. IN PCHAR Buffer,
  54. IN DWORD nBuffer,
  55. OUT PDWORD nRead,
  56. IN LPOVERLAPPED Overlapped
  57. );
  58. HRESULT
  59. GetFileData(
  60. IN const CHAR * pchFile,
  61. OUT BYTE * * ppbData,
  62. OUT DWORD * pcbData,
  63. IN int nCharset,
  64. IN BOOL fUseWin32Canon
  65. );
  66. HRESULT
  67. SetOdbcOptions(
  68. ODBC_CONNECTION * pOdbcConn,
  69. STRA * pStrOptions
  70. );
  71. HRESULT
  72. BuildMultiValue(
  73. const CHAR * pchValue,
  74. STRA * pstrMulti,
  75. BOOL fQuoteElements
  76. );
  77. const CHAR *
  78. SkipNonWhite(
  79. const CHAR * pch
  80. );
  81. const CHAR *
  82. SkipTo(
  83. const CHAR * pch,
  84. CHAR ch
  85. );
  86. const CHAR *
  87. SkipWhite(
  88. const CHAR * pch
  89. );
  90. ODBC_REQ::ODBC_REQ(
  91. EXTENSION_CONTROL_BLOCK * pECB,
  92. DWORD csecConnPool,
  93. int nCharset
  94. )
  95. : _dwSignature ( ODBC_REQ_SIGNATURE ),
  96. _pECB ( pECB ),
  97. _cchMaxFieldSize ( 0 ),
  98. _cMaxRecords ( 0xffffffff ),
  99. _cCurrentRecordNum ( 0 ),
  100. _cClientParams ( 0 ),
  101. _podbcstmt ( NULL ),
  102. _podbcconnPool ( NULL ),
  103. _cbQueryFile ( 0 ),
  104. _cNestedIfs ( 0 ),
  105. _fDirect ( FALSE ),
  106. _fValid ( FALSE ),
  107. _pbufOut ( NULL ),
  108. _csecExpires ( 0 ),
  109. _csecExpiresAt ( 0 ),
  110. _pstrValues ( NULL ),
  111. _pcbValues ( NULL ),
  112. _cQueries ( 0 ),
  113. _csecConnPool ( csecConnPool ),
  114. _pSecDesc ( NULL ),
  115. _pstrCols ( NULL ),
  116. _nCharset ( nCharset )
  117. {}
  118. ODBC_REQ::~ODBC_REQ()
  119. {
  120. DBG_ASSERT( CheckSignature() );
  121. if ( _podbcstmt )
  122. {
  123. delete _podbcstmt;
  124. _podbcstmt = NULL;
  125. }
  126. Close();
  127. if ( _pbufOut )
  128. {
  129. delete _pbufOut;
  130. _pbufOut = NULL;
  131. }
  132. if ( _pSecDesc )
  133. {
  134. LocalFree( _pSecDesc );
  135. _pSecDesc = NULL;
  136. }
  137. _dwSignature = ODBC_REQ_FREE_SIGNATURE;
  138. }
  139. HRESULT
  140. ODBC_REQ::Create(
  141. CONST CHAR * pszQueryFile,
  142. CONST CHAR * pszParameters
  143. )
  144. {
  145. HRESULT hr;
  146. DBG_ASSERT( CheckSignature() );
  147. hr = _strQueryFile.Copy( pszQueryFile );
  148. if( FAILED( hr ) )
  149. {
  150. DBGPRINTF(( DBG_CONTEXT,
  151. "Error copying QueryFile name, hr = 0x%x.\n",
  152. hr ));
  153. return hr;
  154. }
  155. hr = _plParams.ParsePairs( pszParameters, FALSE, FALSE, FALSE );
  156. if( FAILED( hr ) )
  157. {
  158. DBGPRINTF(( DBG_CONTEXT,
  159. "Error parsing param pairs, hr = 0x%x.\n",
  160. hr ));
  161. return hr;
  162. }
  163. if ( _strQueryFile.IsValid() )
  164. {
  165. _fValid = TRUE;
  166. }
  167. _cClientParams = _plParams.GetCount();
  168. return hr;
  169. }
  170. HRESULT
  171. ODBC_REQ::OpenQueryFile(
  172. BOOL * pfAccessDenied
  173. )
  174. {
  175. CHAR * pchQueryFile;
  176. HRESULT hr;
  177. DBG_ASSERT( CheckSignature() );
  178. hr = GetFileData( _strQueryFile.QueryStr(),
  179. (BYTE **) &pchQueryFile,
  180. &_cbQueryFile,
  181. _nCharset,
  182. TRUE );
  183. if ( FAILED( hr ) )
  184. {
  185. STRA strError;
  186. LPCSTR apsz[1];
  187. apsz[0] = _pECB->lpszPathInfo;
  188. strError.FormatString( ODBCMSG_QUERY_FILE_NOT_FOUND,
  189. apsz,
  190. HTTP_ODBC_DLL );
  191. SetErrorText( strError.QueryStr() );
  192. DWORD dwE = GetLastError();
  193. if ( dwE == ERROR_ACCESS_DENIED || dwE == ERROR_LOGON_FAILURE )
  194. {
  195. *pfAccessDenied = TRUE;
  196. }
  197. DBGPRINTF(( DBG_CONTEXT,
  198. "Error in GetFileData(), hr = 0x%x.\n",
  199. hr ));
  200. return hr;
  201. }
  202. //
  203. // CODEWORK - It is possible to avoid this copy by not modifying the
  204. // contents of the query file. Would save a buffer copy
  205. //
  206. if( !_bufQueryFile.Resize( _cbQueryFile ) )
  207. {
  208. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  209. }
  210. memcpy( _bufQueryFile.QueryPtr(),
  211. pchQueryFile,
  212. _cbQueryFile );
  213. return S_OK;
  214. }
  215. HRESULT
  216. ODBC_REQ::ParseAndQuery(
  217. CHAR * pszLoggedOnUser
  218. )
  219. /*++
  220. Routine Description:
  221. This method parses the query file and executes the SQL statement
  222. Arguments:
  223. pchLoggedOnUser - The NT user account this user is running under
  224. Return Value:
  225. HRESULT
  226. --*/
  227. {
  228. STACK_STRA( strDatasource, 64 );
  229. STACK_STRA( strUsername, 64 );
  230. STACK_STRA( strPassword, 64 );
  231. CHAR * pch;
  232. CHAR * pchEnd;
  233. CHAR * pszField;
  234. CHAR * pszValue;
  235. BOOL fRet;
  236. VOID * pCookie = NULL;
  237. DWORD csecPoolConnection = _csecConnPool;
  238. BOOL fRetried;
  239. HRESULT hr;
  240. DBG_ASSERT( CheckSignature() );
  241. //
  242. // We don't allow some security related parameters to be
  243. // specified from the client so remove those now
  244. //
  245. _plParams.RemoveEntry( "REMOTE_USER" );
  246. _plParams.RemoveEntry( "LOGON_USER" );
  247. _plParams.RemoveEntry( "AUTH_USER" );
  248. //
  249. // Do a quick Scan for the DefaultParameters value to fill
  250. // in the blanks in the parameter list from the web browser
  251. //
  252. {
  253. pch = (CHAR *) _bufQueryFile.QueryPtr();
  254. pchEnd = pch + strlen(pch);
  255. ODBC_PARSER Parser( (CHAR *) _bufQueryFile.QueryPtr() );
  256. while ( ( pszField = Parser.QueryToken() ) < pchEnd )
  257. {
  258. if ( COMP_FIELD( "DefaultParameters:", pszField, 18 ))
  259. {
  260. Parser.SkipTo( ':' );
  261. Parser += 1;
  262. Parser.EatWhite();
  263. hr = _plParams.ParsePairs( Parser.QueryLine(), TRUE );
  264. if ( FAILED( hr ) )
  265. {
  266. DBGPRINTF(( DBG_CONTEXT,
  267. "Error parsing param pairs, hr = 0x%x.\n",
  268. hr ));
  269. return hr;
  270. }
  271. break;
  272. }
  273. Parser.NextLine();
  274. }
  275. }
  276. //
  277. // Replace any %XXX% fields with the corresponding parameter.
  278. // Note we reassign pch in case of a pointer shift during
  279. // ReplaceParams
  280. //
  281. hr = ReplaceParams( &_bufQueryFile, &_plParams );
  282. if ( FAILED( hr ) )
  283. {
  284. DBGPRINTF(( DBG_CONTEXT,
  285. "Error in ReplaceParams(), hr = 0x%x.\n",
  286. hr ));
  287. return hr;
  288. }
  289. pch = (CHAR *) _bufQueryFile.QueryPtr();
  290. pchEnd = pch + strlen(pch);
  291. //
  292. // Loop through the fields looking for values we recognize
  293. //
  294. {
  295. ODBC_PARSER Parser( pch );
  296. while ( (pszField = Parser.QueryToken()) < pchEnd )
  297. {
  298. //
  299. // Ignore blank lines and Ctrl-Zs
  300. //
  301. if ( !*pszField || *pszField == 0x1a)
  302. {
  303. Parser.NextLine();
  304. continue;
  305. }
  306. Parser.SkipTo( ':' );
  307. Parser += 1;
  308. Parser.EatWhite();
  309. //
  310. // Ignore comment fields
  311. //
  312. if ( *pszField == '#' || *pszField == ';' )
  313. {
  314. Parser.NextLine();
  315. continue;
  316. }
  317. if ( COMP_FIELD( "Datasource:", pszField, 11 ))
  318. {
  319. hr = Parser.CopyToEOL( &strDatasource );
  320. }
  321. else if ( COMP_FIELD( "Username:", pszField, 9 ))
  322. {
  323. hr = Parser.CopyToEOL( &strUsername );
  324. }
  325. else if ( COMP_FIELD( "Password:", pszField, 9 ))
  326. {
  327. hr = Parser.CopyToEOL( &strPassword );
  328. }
  329. else if ( COMP_FIELD( "Template:", pszField, 9 ))
  330. {
  331. hr = Parser.CopyToEOL( &_strTemplateFile );
  332. //
  333. // Specifying a template of "Direct" means return the
  334. // first column of data as raw data to the client
  335. //
  336. if ( !_stricmp( _strTemplateFile.QueryStr(), "Direct" ))
  337. {
  338. _fDirect = TRUE;
  339. }
  340. }
  341. else if ( COMP_FIELD( "MaxFieldSize:", pszField, 13 ))
  342. {
  343. _cchMaxFieldSize = atoi( Parser.QueryPos() );
  344. }
  345. else if ( COMP_FIELD( "MaxRecords:", pszField, 11 ))
  346. {
  347. _cMaxRecords = atoi( Parser.QueryPos() );
  348. }
  349. else if ( COMP_FIELD( "RequiredParameters:", pszField, 12 ))
  350. {
  351. hr = _plReqParams.ParseSimpleList( Parser.QueryLine() );
  352. }
  353. else if ( COMP_FIELD( "Content-Type:", pszField, 13 ))
  354. {
  355. hr = Parser.CopyToEOL( &_strContentType );
  356. }
  357. else if ( COMP_FIELD( "DefaultParameters:", pszField, 18 ))
  358. {
  359. //
  360. // Ignore, already processed
  361. //
  362. }
  363. else if ( COMP_FIELD( "Expires:", pszField, 8 ))
  364. {
  365. // _csecExpires = min( (DWORD) atoi( Parser.QueryPos() ),
  366. // MAX_EXPIRES_TIME );
  367. }
  368. else if ( COMP_FIELD( "ODBCOptions:", pszField, 12 ))
  369. {
  370. hr = Parser.CopyToEOL( &_strOdbcOptions );
  371. }
  372. else if ( COMP_FIELD( "ODBCConnection:", pszField, 15 ))
  373. {
  374. //
  375. // Is there an override to the default?
  376. //
  377. if ( !_strnicmp( Parser.QueryToken(), "Pool", 4 ))
  378. {
  379. if ( !csecPoolConnection )
  380. {
  381. // This is bogus - if somebody has turned off connection
  382. // pooling on the vroot and enabled it in the idc,
  383. // there's no defined way to set the timeout
  384. // need to add a timeout here
  385. csecPoolConnection = 30;
  386. }
  387. }
  388. else if ( !_strnicmp( Parser.QueryToken(), "NoPool", 6 ))
  389. {
  390. csecPoolConnection = 0;
  391. }
  392. }
  393. else if ( COMP_FIELD( "SQLStatement:", pszField, 13 ))
  394. {
  395. if ( _cQueries >= MAX_QUERIES )
  396. {
  397. STRA strError;
  398. strError.FormatString( ODBCMSG_TOO_MANY_SQL_STATEMENTS,
  399. NULL,
  400. HTTP_ODBC_DLL );
  401. SetErrorText( strError.QueryStr() );
  402. return E_FAIL;
  403. }
  404. while ( TRUE )
  405. {
  406. hr = _strQueries[_cQueries].Append(
  407. Parser.QueryLine() );
  408. if ( FAILED( hr ) )
  409. {
  410. DBGPRINTF(( DBG_CONTEXT,
  411. "Error appeinding value, hr = 0x%x.\n",
  412. hr ));
  413. return hr;
  414. }
  415. Parser.NextLine();
  416. //
  417. // Line continuation is signified by putting a '+' at
  418. // the beginning of the line
  419. //
  420. if ( *Parser.QueryLine() == '+' )
  421. {
  422. hr = _strQueries[_cQueries].Append( " " );
  423. if ( FAILED( hr ) )
  424. {
  425. DBGPRINTF(( DBG_CONTEXT,
  426. "Error appending space, hr = 0x%x.\n",
  427. hr ));
  428. return hr;
  429. }
  430. Parser += 1;
  431. }
  432. else
  433. {
  434. //
  435. // Ignore blank line
  436. //
  437. if ( !*Parser.QueryLine() &&
  438. Parser.QueryLine() < pchEnd )
  439. {
  440. continue;
  441. }
  442. break;
  443. }
  444. }
  445. _cQueries++;
  446. continue;
  447. }
  448. else if ( COMP_FIELD( IDC_FIELDNAME_CHARSET,
  449. pszField,
  450. sizeof(IDC_FIELDNAME_CHARSET)-1 ))
  451. {
  452. //
  453. // Ignore "Charset:" field
  454. //
  455. Parser.NextLine();
  456. continue;
  457. }
  458. else if ( COMP_FIELD( "TranslationFile:", pszField, 16 ))
  459. {
  460. hr = Parser.CopyToEOL( &_strTranslationFile );
  461. }
  462. else
  463. {
  464. //
  465. // Unrecognized field, generate an error
  466. //
  467. STRA strError;
  468. LPCSTR apsz[1];
  469. apsz[0] = pszField;
  470. strError.FormatString( ODBCMSG_UNREC_FIELD,
  471. apsz,
  472. HTTP_ODBC_DLL );
  473. SetErrorText( strError.QueryStr() );
  474. hr = E_FAIL;
  475. }
  476. if ( FAILED( hr ) )
  477. {
  478. return hr;
  479. }
  480. Parser.NextLine();
  481. }
  482. }
  483. //
  484. // Make sure the Datasource and SQLStatement fields are non-empty
  485. //
  486. if ( strDatasource.IsEmpty() ||
  487. !_cQueries ||
  488. _strQueries[0].IsEmpty() )
  489. {
  490. STRA strError;
  491. strError.FormatString( ODBCMSG_DSN_AND_SQLSTATEMENT_REQ,
  492. NULL,
  493. HTTP_ODBC_DLL );
  494. SetErrorText( strError.QueryStr() );
  495. return E_FAIL;
  496. }
  497. //
  498. // Make sure all of the required parameters have been supplied
  499. //
  500. while ( pCookie = _plReqParams.NextPair( pCookie,
  501. &pszField,
  502. &pszValue ))
  503. {
  504. if ( !_plParams.FindValue( pszField ))
  505. {
  506. STRA strError;
  507. LPCSTR apsz[1];
  508. apsz[0] = pszField;
  509. if ( FAILED( hr = strError.FormatString( ODBCMSG_MISSING_REQ_PARAM,
  510. apsz,
  511. HTTP_ODBC_DLL )))
  512. {
  513. return hr;
  514. }
  515. //
  516. // Set the error text to return the user and indicate we
  517. // couldn't continue the operation
  518. //
  519. SetErrorText( strError.QueryStr() );
  520. return E_FAIL;
  521. }
  522. }
  523. //
  524. // Don't retry the connection/query if not pooling. The reason
  525. // we do the retry is to report the error that occurred (this
  526. // requires the ODBC connection object).
  527. //
  528. fRetried = csecPoolConnection == 0;
  529. RetryConnection:
  530. //
  531. // Open the database
  532. //
  533. if ( FAILED( OpenConnection( &_odbcconn,
  534. &_podbcconnPool,
  535. csecPoolConnection,
  536. strDatasource.QueryStr(),
  537. strUsername.QueryStr(),
  538. strPassword.QueryStr(),
  539. pszLoggedOnUser ) ) ||
  540. FAILED( SetOdbcOptions( QueryOdbcConnection(),
  541. &_strOdbcOptions ) ) ||
  542. !( _podbcstmt = QueryOdbcConnection()->AllocStatement() ) ||
  543. FAILED( _podbcstmt->ExecDirect( _strQueries[0].QueryStr(),
  544. _strQueries[0].QueryCCH() ) ) )
  545. {
  546. //
  547. // Delete the pooled connection and retry the open
  548. //
  549. if ( csecPoolConnection )
  550. {
  551. CloseConnection( _podbcconnPool, TRUE );
  552. _podbcconnPool = NULL;
  553. csecPoolConnection = 0;
  554. }
  555. if ( !fRetried )
  556. {
  557. if( _podbcstmt )
  558. {
  559. delete _podbcstmt;
  560. _podbcstmt = NULL;
  561. }
  562. fRetried = TRUE;
  563. goto RetryConnection;
  564. }
  565. return E_FAIL;
  566. }
  567. CloseConnection( _podbcconnPool, TRUE );
  568. return S_OK;
  569. }
  570. HRESULT
  571. ODBC_REQ::OutputResults(
  572. ODBC_REQ_CALLBACK pfnCallback,
  573. PVOID pvContext,
  574. STRA * pstrHeaders,
  575. ODBC_REQ_HEADER pfnSendHeader,
  576. BOOL fIsAuth,
  577. BOOL * pfAccessDenied
  578. )
  579. /*++
  580. Routine Description:
  581. This method reads the template file and does the necessary
  582. result set column substitution
  583. Arguments:
  584. pfnCallback - Send callback function
  585. pvContext - Context for send callback
  586. Return Value:
  587. HRESULT
  588. --*/
  589. {
  590. DWORD cbOut;
  591. DWORD cbFile, cbHigh;
  592. DWORD BytesRead;
  593. DWORD cbToSend;
  594. BOOL fLastRow = FALSE;
  595. const CHAR * pchStartDetail;
  596. const CHAR * pchIn;
  597. const CHAR * pchEOF;
  598. const CHAR * pchBOF = NULL;
  599. CHAR * pchTag;
  600. const CHAR * pchValue;
  601. DWORD cbValue;
  602. enum TAG_TYPE TagType;
  603. DWORD err;
  604. BOOL fTriedRelative = FALSE;
  605. BOOL fExpr;
  606. STRA strError;
  607. const CHAR * CharacterMap[256];
  608. BOOL fIsSelect;
  609. BOOL fMoreResults;
  610. BOOL fHaveResultSet = FALSE;
  611. DWORD iQuery = 1;
  612. HRESULT hr;
  613. DBG_ASSERT( CheckSignature() );
  614. //
  615. // Set up the first buffer in the output chain
  616. //
  617. if ( !_pbufOut )
  618. {
  619. _pbufOut = new BUFFER_CHAIN_ITEM( OUTPUT_BUFFER_SIZE );
  620. if ( !_pbufOut || !_pbufOut->QueryPtr() )
  621. {
  622. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  623. }
  624. }
  625. if ( !_fDirect )
  626. {
  627. CHAR * pchLastSlash;
  628. STACK_STRA( str, MAX_PATH );
  629. TryAgain:
  630. //
  631. // Open and read the template file (automatically zero terminated)
  632. //
  633. hr = GetFileData( ( fTriedRelative ? str.QueryStr() :
  634. _strTemplateFile.QueryStr() ),
  635. ( BYTE ** )&pchBOF,
  636. &BytesRead,
  637. _nCharset,
  638. TRUE );
  639. if ( FAILED( hr ) )
  640. {
  641. //
  642. // If the open fails with a not found error, then make the
  643. // template file relative to the query file and try again
  644. //
  645. if ( fTriedRelative ||
  646. ( ( GetLastError() != ERROR_FILE_NOT_FOUND )
  647. && ( GetLastError() != ERROR_PATH_NOT_FOUND ) ) ||
  648. FAILED( str.Copy( _strQueryFile ) ) )
  649. {
  650. LPCSTR apsz[1];
  651. DWORD dwE = GetLastError();
  652. apsz[0] = _strTemplateFile.QueryStr();
  653. strError.FormatString( ODBCMSG_QUERY_FILE_NOT_FOUND,
  654. apsz,
  655. HTTP_ODBC_DLL );
  656. SetErrorText( strError.QueryStr() );
  657. if ( ( dwE == ERROR_ACCESS_DENIED ||
  658. dwE == ERROR_LOGON_FAILURE) )
  659. {
  660. *pfAccessDenied = TRUE;
  661. return HRESULT_FROM_WIN32( dwE );
  662. }
  663. if (!pfnSendHeader( pvContext,
  664. "500 IDC Query Error",
  665. pstrHeaders->QueryStr() ))
  666. {
  667. hr = HRESULT_FROM_WIN32(GetLastError());
  668. DBGPRINTF(( DBG_CONTEXT,
  669. "Error in SendHeader(), hr = 0x%x.\n",
  670. hr ));
  671. return hr;
  672. }
  673. goto ErrorExit;
  674. }
  675. pchLastSlash = ( PCHAR )_mbsrchr( ( PUCHAR )str.QueryStr(),
  676. '\\' );
  677. if ( !pchLastSlash )
  678. {
  679. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  680. }
  681. str.SetLen( DIFF(pchLastSlash - str.QueryStr()) + 1 );
  682. hr = str.Append( _strTemplateFile );
  683. if ( FAILED( hr ) )
  684. {
  685. DBGPRINTF(( DBG_CONTEXT,
  686. "Error appending template file name, hr = 0x%x.\n",
  687. hr ));
  688. return hr;
  689. }
  690. fTriedRelative = TRUE;
  691. goto TryAgain;
  692. }
  693. else
  694. {
  695. //
  696. // Update our template file path if it changed
  697. //
  698. if ( fTriedRelative )
  699. {
  700. hr = _strTemplateFile.Copy( str );
  701. if ( FAILED( hr ) )
  702. {
  703. DBGPRINTF(( DBG_CONTEXT,
  704. "Error updating template file path, hr = 0x%x.\n",
  705. hr ));
  706. return hr;
  707. }
  708. }
  709. }
  710. }
  711. //
  712. // Open the translation file if one was specified
  713. //
  714. if ( !_strTranslationFile.IsEmpty() )
  715. {
  716. CHAR * pchLastSlash;
  717. CHAR * pchTranslationFile;
  718. STACK_STRA( str, MAX_PATH );
  719. BOOL fRet;
  720. VOID * pvCookie = NULL;
  721. CHAR * pchField;
  722. DWORD cbRead;
  723. fTriedRelative = FALSE;
  724. TranslationTryAgain:
  725. //
  726. // Open and read the template file (automatically zero terminated)
  727. //
  728. hr = GetFileData( ( fTriedRelative ? str.QueryStr() :
  729. _strTranslationFile.QueryStr()),
  730. ( BYTE ** )&pchTranslationFile,
  731. &cbRead,
  732. _nCharset,
  733. TRUE );
  734. if ( FAILED( hr ) )
  735. {
  736. //
  737. // If the open fails with a not found error, then make the
  738. // template file relative to the query file and try again
  739. //
  740. if ( fTriedRelative ||
  741. ( GetLastError() != ERROR_FILE_NOT_FOUND
  742. && GetLastError() != ERROR_PATH_NOT_FOUND) ||
  743. FAILED( str.Copy( _strQueryFile ) ) )
  744. {
  745. LPCSTR apsz[1];
  746. DWORD dwE = GetLastError();
  747. apsz[0] = _strTranslationFile.QueryStr();
  748. strError.FormatString( ODBCMSG_QUERY_FILE_NOT_FOUND,
  749. apsz,
  750. HTTP_ODBC_DLL );
  751. SetErrorText( strError.QueryStr());
  752. if ( ( dwE == ERROR_ACCESS_DENIED ||
  753. dwE == ERROR_LOGON_FAILURE ) )
  754. {
  755. *pfAccessDenied = TRUE;
  756. return HRESULT_FROM_WIN32( dwE );
  757. }
  758. goto ErrorExit;
  759. }
  760. pchLastSlash = (PCHAR)_mbsrchr( ( PUCHAR )str.QueryStr(),
  761. '\\' );
  762. if ( !pchLastSlash )
  763. {
  764. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  765. }
  766. str.SetLen( DIFF(pchLastSlash - str.QueryStr()) + 1 );
  767. hr = str.Append( _strTranslationFile );
  768. if ( FAILED( hr ) )
  769. {
  770. DBGPRINTF(( DBG_CONTEXT,
  771. "Error appending translation file name, hr = 0x%x.\n",
  772. hr ));
  773. return hr;
  774. }
  775. fTriedRelative = TRUE;
  776. goto TranslationTryAgain;
  777. }
  778. else
  779. {
  780. //
  781. // Update our template file path if it changed
  782. //
  783. if ( fTriedRelative )
  784. {
  785. hr = _strTranslationFile.Copy( str );
  786. if ( FAILED( hr ) )
  787. {
  788. return hr;
  789. }
  790. }
  791. }
  792. hr = _plTransList.ParsePairs( pchTranslationFile,
  793. FALSE,
  794. TRUE,
  795. FALSE );
  796. if ( FAILED( hr ) )
  797. {
  798. DBGPRINTF(( DBG_CONTEXT,
  799. "Error in ParsePairs(), hr = 0x%x.\n",
  800. hr ));
  801. return hr;
  802. }
  803. //
  804. // Build the character map
  805. //
  806. memset( CharacterMap, 0, sizeof( CharacterMap ) );
  807. while ( pvCookie = _plTransList.NextPair( pvCookie,
  808. &pchField,
  809. (LPSTR *)&pchValue ))
  810. {
  811. CharacterMap[ (BYTE) *pchField ] = pchValue;
  812. }
  813. }
  814. //
  815. // We've already performed the first query at this point
  816. //
  817. NextResultSet:
  818. //
  819. // Get the list of column names in the initial result set.
  820. // The initial set must be initialized for compatibility
  821. // with previous versions of IDC (i.e., column variables
  822. // can be referenced outside the detail section ).
  823. //
  824. hr = _podbcstmt->QueryColNames( &_pstrCols,
  825. &_cCols,
  826. _cchMaxFieldSize,
  827. &fHaveResultSet );
  828. if ( FAILED( hr ) )
  829. {
  830. DBGPRINTF(( DBG_CONTEXT,
  831. "Error in QueryColNames, hr = 0x%x.\n",
  832. hr ));
  833. return hr;
  834. }
  835. if ( !fHaveResultSet )
  836. {
  837. //
  838. // Check to see if there are anymore result sets for this query
  839. //
  840. hr = _podbcstmt->MoreResults( &fMoreResults );
  841. if ( FAILED( hr ) )
  842. {
  843. DBGPRINTF(( DBG_CONTEXT,
  844. "Error in MoreResults, hr = 0x%x.\n",
  845. hr ));
  846. return hr;
  847. }
  848. if ( fMoreResults )
  849. {
  850. goto NextResultSet;
  851. }
  852. else if ( iQuery < _cQueries )
  853. {
  854. //
  855. // If there are no more result sets, see if there
  856. // are more queries. Note calling SQLMoreResults
  857. // will discard this result set
  858. //
  859. hr = _podbcstmt->ExecDirect(
  860. _strQueries[iQuery].QueryStr(),
  861. _strQueries[iQuery].QueryCCH() );
  862. if ( FAILED( hr ) )
  863. {
  864. DBGPRINTF(( DBG_CONTEXT,
  865. "Error in ExecDirect(), hr = 0x%x.\n",
  866. hr ));
  867. return hr;
  868. }
  869. iQuery++;
  870. goto NextResultSet;
  871. }
  872. }
  873. //
  874. // Get the first row of values
  875. //
  876. hr = NextRow( &fLastRow );
  877. if ( fHaveResultSet && FAILED( hr ) )
  878. {
  879. //
  880. // Some SQL statements don't generate any rows (i.e.,
  881. // insert, delete etc.). So don't bail if there's a column in
  882. // the result set
  883. //
  884. if ( !_cCols )
  885. {
  886. return hr;
  887. }
  888. }
  889. // Send reply header
  890. if (!pfnSendHeader( pvContext, "200 OK", pstrHeaders->QueryStr() ))
  891. {
  892. hr = HRESULT_FROM_WIN32(GetLastError());
  893. DBGPRINTF(( DBG_CONTEXT,
  894. "Error in SendHeader(), hr = 0x%x.\n",
  895. hr ));
  896. return hr;
  897. }
  898. //
  899. // Copy the template to the output buffer while scanning for column
  900. // fields that need to be replaced
  901. //
  902. #define SEND_DATA( pchData, cbData ) SendData( pfnCallback, \
  903. pvContext, \
  904. (pchData), \
  905. (DWORD)(cbData), \
  906. &_pbufOut, \
  907. &cbOut )
  908. #define SEND_DATA_CHECK_ESC( pchData, cbData ) \
  909. ((TagType == TAG_TYPE_VALUE_TO_ESCAPE) \
  910. ? SendEscapedData( pfnCallback, \
  911. pvContext, \
  912. pchData, \
  913. (DWORD)(cbData), \
  914. &cbOut ) \
  915. : SEND_DATA( pchData, \
  916. (DWORD)(cbData) ) )
  917. cbOut = 0;
  918. pchStartDetail = NULL;
  919. if( pchBOF == NULL )
  920. {
  921. return E_FAIL;
  922. }
  923. pchIn = pchBOF;
  924. pchEOF = pchBOF + BytesRead;
  925. while ( pchIn < pchEOF )
  926. {
  927. //
  928. // Look for the start of a "<!--%" or <%" tag
  929. //
  930. pchTag = strchr( pchIn, '<' );
  931. if ( pchTag )
  932. {
  933. //
  934. // Send any data preceding the tag
  935. //
  936. cbToSend = DIFF(pchTag - pchIn);
  937. hr = SEND_DATA( pchIn, cbToSend );
  938. if ( FAILED( hr ) )
  939. {
  940. DBGPRINTF(( DBG_CONTEXT,
  941. "Error sending data, hr = 0x%x.\n",
  942. hr ));
  943. return hr;
  944. }
  945. pchIn += cbToSend;
  946. if ( !memcmp( pchTag, "<!--%", 5 ) ||
  947. !memcmp( pchTag, "<%", 2 ) )
  948. {
  949. //
  950. // Is this a tag we care about? pchIn is advanced
  951. // except in the unknown case
  952. //
  953. LookupTag( pchTag,
  954. &pchIn,
  955. &pchValue,
  956. &cbValue,
  957. &TagType );
  958. switch( TagType )
  959. {
  960. case TAG_TYPE_VALUE:
  961. case TAG_TYPE_VALUE_TO_ESCAPE:
  962. //
  963. // Map any characters if there was a translation file
  964. //
  965. if ( _strTranslationFile.IsEmpty() )
  966. {
  967. hr = SEND_DATA_CHECK_ESC( pchValue, (DWORD) -1 );
  968. if ( FAILED( hr ) )
  969. {
  970. DBGPRINTF(( DBG_CONTEXT,
  971. "Error sending esc data, hr = 0x%x.\n",
  972. hr ));
  973. return hr;
  974. }
  975. }
  976. else
  977. {
  978. const CHAR * pchStart = pchValue;
  979. while ( *pchValue )
  980. {
  981. if ( CharacterMap[ (BYTE) *pchValue ] )
  982. {
  983. hr = SEND_DATA_CHECK_ESC( pchStart,
  984. pchValue - pchStart );
  985. if( FAILED( hr ) )
  986. {
  987. DBGPRINTF(( DBG_CONTEXT,
  988. "Error sending data, hr = 0x%x.\n",
  989. hr ));
  990. return hr;
  991. }
  992. hr = SEND_DATA_CHECK_ESC(
  993. CharacterMap[ (BYTE) *pchValue],
  994. (DWORD) -1 );
  995. if( FAILED( hr ) )
  996. {
  997. DBGPRINTF(( DBG_CONTEXT,
  998. "Error sending data, hr = 0x%x.\n",
  999. hr ));
  1000. return hr;
  1001. }
  1002. pchStart = pchValue = pchValue + 1;
  1003. }
  1004. else
  1005. {
  1006. pchValue++;
  1007. }
  1008. }
  1009. hr = SEND_DATA_CHECK_ESC( pchStart,
  1010. pchValue - pchStart );
  1011. if( FAILED( hr ) )
  1012. {
  1013. DBGPRINTF(( DBG_CONTEXT,
  1014. "Error sending data, hr = 0x%x.\n",
  1015. hr ));
  1016. return hr;
  1017. }
  1018. }
  1019. break;
  1020. case TAG_TYPE_BEGIN_DETAIL:
  1021. //
  1022. // If we don't have a result set, get one now
  1023. //
  1024. if ( !fHaveResultSet )
  1025. {
  1026. fLastRow = TRUE;
  1027. _podbcstmt->FreeColumnMemory();
  1028. _cCurrentRecordNum = 0;
  1029. _pstrCols = _pstrValues = NULL;
  1030. NextResultSet2:
  1031. hr = _podbcstmt->MoreResults( &fMoreResults );
  1032. if ( FAILED( hr ) )
  1033. {
  1034. DBGPRINTF(( DBG_CONTEXT,
  1035. "Error in MoreResults(), hr = 0x%x.\n",
  1036. hr ));
  1037. return hr;
  1038. }
  1039. if ( fMoreResults )
  1040. {
  1041. NewQuery:
  1042. hr = _podbcstmt->QueryColNames(
  1043. &_pstrCols,
  1044. &_cCols,
  1045. _cchMaxFieldSize,
  1046. &fHaveResultSet );
  1047. if ( FAILED( hr ) )
  1048. {
  1049. DBGPRINTF(( DBG_CONTEXT,
  1050. "Error in QueryColNames, hr = 0x%x.\n",
  1051. hr ));
  1052. return hr;
  1053. }
  1054. if ( !fHaveResultSet )
  1055. goto NextResultSet2;
  1056. hr = NextRow( &fLastRow );
  1057. if ( FAILED( hr ) )
  1058. {
  1059. //
  1060. // Some SQL statements don't generate
  1061. // any rows (i.e., insert, delete etc.).
  1062. // So don't bail if there's a column in
  1063. // the result set
  1064. //
  1065. if ( !_cCols )
  1066. {
  1067. DBGPRINTF(( DBG_CONTEXT,
  1068. "Error in NextRow(), hr = 0x%x.\n",
  1069. hr ));
  1070. return hr;
  1071. }
  1072. }
  1073. }
  1074. else if ( iQuery < _cQueries )
  1075. {
  1076. //
  1077. // If there are no more result sets, see if
  1078. // there are more queries. Note calling
  1079. // SQLMoreResults will discard this result
  1080. // set
  1081. //
  1082. hr = _podbcstmt->ExecDirect(
  1083. _strQueries[iQuery].QueryStr(),
  1084. _strQueries[iQuery].QueryCCH() );
  1085. if ( FAILED( hr ) )
  1086. {
  1087. DBGPRINTF(( DBG_CONTEXT,
  1088. "Error in ExecDirect(), hr = 0x%x.\n",
  1089. hr ));
  1090. return hr;
  1091. }
  1092. iQuery++;
  1093. goto NewQuery;
  1094. }
  1095. }
  1096. if ( !fLastRow )
  1097. {
  1098. pchStartDetail = pchIn;
  1099. }
  1100. else
  1101. {
  1102. //
  1103. // If no more data, then skip the detail section
  1104. //
  1105. SkipToTag( &pchIn, END_DETAIL_TEXT );
  1106. fHaveResultSet = FALSE;
  1107. }
  1108. break;
  1109. case TAG_TYPE_END_DETAIL:
  1110. hr = NextRow( &fLastRow );
  1111. if ( FAILED( hr ) )
  1112. {
  1113. DBGPRINTF(( DBG_CONTEXT,
  1114. "Error in NextRow(), hr = 0x%x.\n",
  1115. hr ));
  1116. return hr;
  1117. }
  1118. _cCurrentRecordNum++;
  1119. if ( !fLastRow && _cCurrentRecordNum < _cMaxRecords )
  1120. {
  1121. pchIn = pchStartDetail;
  1122. }
  1123. else
  1124. {
  1125. fHaveResultSet = FALSE;
  1126. }
  1127. break;
  1128. case TAG_TYPE_IF:
  1129. //
  1130. // pchIn points to the first character of the
  1131. // expression on the way in, the first character
  1132. // after the tag on the way out
  1133. //
  1134. hr = EvaluateExpression( (const CHAR **)&pchIn,
  1135. &fExpr );
  1136. if ( FAILED( hr ) )
  1137. {
  1138. DBGPRINTF(( DBG_CONTEXT,
  1139. "Error in EvaluateExpression(), hr = 0x%x.\n",
  1140. hr ));
  1141. return hr;
  1142. }
  1143. //
  1144. // If the expression is FALSE, then skip the
  1145. // intervening data till the endif tag
  1146. //
  1147. if ( !fExpr )
  1148. {
  1149. //
  1150. // Look for a closing else or endif
  1151. //
  1152. if ( SkipConditionalBlock( &pchIn,
  1153. pchEOF,
  1154. ELSE_TEXT ) )
  1155. {
  1156. _cNestedIfs++;
  1157. }
  1158. }
  1159. else
  1160. {
  1161. _cNestedIfs++;
  1162. }
  1163. break;
  1164. case TAG_TYPE_ELSE:
  1165. if ( _cNestedIfs == 0 )
  1166. {
  1167. //
  1168. // else w/o an if
  1169. //
  1170. strError.FormatString( ODBCMSG_TOO_MANY_ELSES,
  1171. NULL,
  1172. HTTP_ODBC_DLL );
  1173. SetErrorText( strError.QueryStr());
  1174. goto ErrorExit;
  1175. }
  1176. //
  1177. // We got here because we just finished processing a
  1178. // TRUE expression, so skip the else portion of the if
  1179. //
  1180. SkipConditionalBlock( &pchIn, pchEOF, NULL );
  1181. _cNestedIfs--;
  1182. break;
  1183. case TAG_TYPE_END_IF:
  1184. if ( _cNestedIfs == 0 )
  1185. {
  1186. //
  1187. // endif w/o an if
  1188. //
  1189. strError.FormatString( ODBCMSG_TOO_MANY_ENDIFS,
  1190. NULL,
  1191. HTTP_ODBC_DLL );
  1192. SetErrorText( strError.QueryStr());
  1193. goto ErrorExit;
  1194. }
  1195. _cNestedIfs--;
  1196. break;
  1197. default:
  1198. case TAG_TYPE_UNKNOWN:
  1199. goto UnknownTag;
  1200. }
  1201. }
  1202. else
  1203. {
  1204. UnknownTag:
  1205. //
  1206. // Move past the beginning of the tag so the next
  1207. // search skips this tag
  1208. //
  1209. hr = SEND_DATA( pchIn, 1 );
  1210. if ( FAILED( hr ) )
  1211. {
  1212. DBGPRINTF(( DBG_CONTEXT,
  1213. "Error sending data, hr = 0x%x.\n",
  1214. hr ));
  1215. return hr;
  1216. }
  1217. pchIn += 1;
  1218. }
  1219. }
  1220. else
  1221. {
  1222. //
  1223. // No more tags, copy the rest of the data to the output
  1224. // buffer.
  1225. //
  1226. hr = SEND_DATA( pchIn, (DWORD) -1 );
  1227. if ( FAILED( hr ) )
  1228. {
  1229. DBGPRINTF(( DBG_CONTEXT,
  1230. "Error sending data, hr = 0x%x.\n",
  1231. hr ));
  1232. return hr;
  1233. }
  1234. break;
  1235. }
  1236. }
  1237. //
  1238. // Send the last of the data and append the last buffer chain
  1239. // if we're caching
  1240. //
  1241. err = pfnCallback( pvContext,
  1242. (CHAR *)_pbufOut->QueryPtr(),
  1243. cbOut );
  1244. if ( err )
  1245. {
  1246. SetLastError( err );
  1247. goto ErrorExit;
  1248. }
  1249. return S_OK;
  1250. ErrorExit:
  1251. //
  1252. // We've already sent the HTTP headers at this point, so just
  1253. // append the error text to the end of this document.
  1254. //
  1255. {
  1256. STRA str;
  1257. if ( !GetLastErrorText( &strError ) )
  1258. {
  1259. return E_FAIL;
  1260. }
  1261. hr = str.Append( "<h1>Error performing operation</h1>");
  1262. if( FAILED( hr ) )
  1263. {
  1264. DBGPRINTF(( DBG_CONTEXT,
  1265. "Error appending error msg, hr = 0x%x.\n",
  1266. hr ));
  1267. return hr;
  1268. }
  1269. hr = str.Append( strError );
  1270. if( FAILED( hr ) )
  1271. {
  1272. DBGPRINTF(( DBG_CONTEXT,
  1273. "Error appending error msg, hr = 0x%x.\n",
  1274. hr ));
  1275. return hr;
  1276. }
  1277. hr = str.Append( "</body>" );
  1278. if( FAILED( hr ) )
  1279. {
  1280. DBGPRINTF(( DBG_CONTEXT,
  1281. "Error appending error msg, hr = 0x%x.\n",
  1282. hr ));
  1283. return hr;
  1284. }
  1285. err = pfnCallback( pvContext,
  1286. str.QueryStr(),
  1287. str.QueryCB() );
  1288. if ( err )
  1289. {
  1290. hr = HRESULT_FROM_WIN32( err );
  1291. DBGPRINTF(( DBG_CONTEXT,
  1292. "Error sending data, hr = 0x%x.\n",
  1293. hr ));
  1294. return hr;
  1295. }
  1296. }
  1297. return S_OK;
  1298. }
  1299. HRESULT
  1300. ODBC_REQ::NextRow(
  1301. BOOL * pfLast
  1302. )
  1303. /*++
  1304. Routine Description:
  1305. Advances the result set to the next row
  1306. Arguments:
  1307. pfLast - Set to TRUE if there is no more data
  1308. Return Value:
  1309. HRESULT
  1310. --*/
  1311. {
  1312. DBG_ASSERT( CheckSignature() );
  1313. return _podbcstmt->QueryValuesAsStr( &_pstrValues,
  1314. &_pcbValues,
  1315. pfLast );
  1316. }
  1317. HRESULT
  1318. ODBC_REQ::ReplaceParams(
  1319. BUFFER * pbufFile,
  1320. PARAM_LIST* pParamList
  1321. )
  1322. /*++
  1323. Routine Description:
  1324. This method looks at the query file and replaces any occurrences
  1325. of %xxx% with the specified replacement value from pszParams
  1326. Arguments:
  1327. pbufFile - Contents of file buffer
  1328. ParamList - List of parameters to replace in the query file
  1329. Return Value:
  1330. HRESULT
  1331. --*/
  1332. {
  1333. DWORD cParams = 0;
  1334. CHAR * pch;
  1335. BOOL fIsMultiValue;
  1336. CHAR * pchValue;
  1337. CHAR * pchTag;
  1338. CHAR * pchTerm;
  1339. CHAR * pchOldPointer;
  1340. CHAR * pchTmp;
  1341. STACK_STRA( strMultiValue, MAX_PATH );
  1342. DWORD cbFile;
  1343. DWORD cbTag;
  1344. DWORD cbValue;
  1345. HRESULT hr;
  1346. CHAR szSymbolValue[ 256 ];
  1347. CHAR * achSymbolValue = szSymbolValue;
  1348. DWORD dwSymbolValue = sizeof( szSymbolValue );
  1349. DBG_ASSERT( CheckSignature() );
  1350. //
  1351. // Scan the query file looking for %xxx% replacements
  1352. //
  1353. pch = pchOldPointer = (CHAR *) pbufFile->QueryPtr();
  1354. cbFile = strlen( pch ) + sizeof( CHAR );
  1355. while ( *pch )
  1356. {
  1357. if ( (pchTag = strchr( pch, '%' )) &&
  1358. (pchTerm = strchr( pchTag + 1, '%' )) )
  1359. {
  1360. *pchTerm = '\0';
  1361. //
  1362. // Was this a '%' escape (i.e., '%%')?
  1363. //
  1364. if ( (pchTag + 1) == pchTerm )
  1365. {
  1366. pchValue = "%";
  1367. goto Found;
  1368. }
  1369. //
  1370. // Look through the replacement list for a matching param
  1371. //
  1372. pchValue = pParamList->FindValue( pchTag + 1,
  1373. &fIsMultiValue );
  1374. if ( !pchValue )
  1375. {
  1376. //
  1377. // Check to see if it's something the client has
  1378. // defined
  1379. //
  1380. if( _pECB->GetServerVariable( _pECB->ConnID,
  1381. pchTag + 1,
  1382. achSymbolValue,
  1383. &dwSymbolValue ) )
  1384. {
  1385. hr = _strSymbolValue.Copy( achSymbolValue, dwSymbolValue );
  1386. if( FAILED( hr ) )
  1387. {
  1388. DBGPRINTF(( DBG_CONTEXT,
  1389. "Error copying symbol value, hr = 0x%x.\n",
  1390. hr ));
  1391. return hr;
  1392. }
  1393. pchValue = _strSymbolValue.QueryStr();
  1394. fIsMultiValue = FALSE;
  1395. goto Found;
  1396. }
  1397. if( dwSymbolValue > sizeof( szSymbolValue ) )
  1398. {
  1399. achSymbolValue = ( CHAR * )_alloca( dwSymbolValue );
  1400. if( !achSymbolValue )
  1401. {
  1402. return E_OUTOFMEMORY;
  1403. }
  1404. if( _pECB->GetServerVariable( _pECB->ConnID,
  1405. pchTag + 1,
  1406. achSymbolValue,
  1407. &dwSymbolValue ) )
  1408. {
  1409. hr = _strSymbolValue.Copy( achSymbolValue, dwSymbolValue );
  1410. if( FAILED( hr ) )
  1411. {
  1412. DBGPRINTF(( DBG_CONTEXT,
  1413. "Error copying symbol value, hr = 0x%x.\n",
  1414. hr ));
  1415. return hr;
  1416. }
  1417. pchValue = _strSymbolValue.QueryStr();
  1418. fIsMultiValue = FALSE;
  1419. goto Found;
  1420. }
  1421. }
  1422. //
  1423. // We didn't find a match, nuke the tag
  1424. //
  1425. memmove( pchTag,
  1426. pchTerm + 1,
  1427. strlen( pchTerm + 1 ) + sizeof( CHAR ) );
  1428. pch = pchTag;
  1429. continue;
  1430. }
  1431. Found:
  1432. if ( fIsMultiValue )
  1433. {
  1434. //
  1435. // Determine whether this is a quoted multi-value or
  1436. // not
  1437. //
  1438. pchTmp = pchTag;
  1439. while ( --pchTmp >= pchOldPointer &&
  1440. ISWHITE( *pchTmp ) )
  1441. {
  1442. ;
  1443. }
  1444. hr = BuildMultiValue( pchValue,
  1445. &strMultiValue,
  1446. *pchTmp == '\'' );
  1447. if ( FAILED( hr ) )
  1448. {
  1449. DBGPRINTF(( DBG_CONTEXT,
  1450. "Error in BuildMultiValue(), hr = 0x%x.\n",
  1451. hr ));
  1452. return hr;
  1453. }
  1454. pchValue = strMultiValue.QueryStr();
  1455. }
  1456. //
  1457. // We have a match, replace the tag with the value.
  1458. // Note we count the surrounding '%'s with cbTag.
  1459. //
  1460. cbTag = DIFF( pchTerm - pchTag ) + sizeof( CHAR );
  1461. cbValue = strlen( pchValue );
  1462. if ( cbValue > cbTag )
  1463. {
  1464. //
  1465. // Resize if needed but watch for pointer shift
  1466. //
  1467. if ( pbufFile->QuerySize() < (cbFile + cbValue - cbTag))
  1468. {
  1469. if ( !pbufFile->Resize( cbFile + cbValue - cbTag,
  1470. 512 ) )
  1471. {
  1472. DBGPRINTF(( DBG_CONTEXT,
  1473. "Error resizing the buffer.\n" ));
  1474. return HRESULT_FROM_WIN32(
  1475. ERROR_NOT_ENOUGH_MEMORY );
  1476. }
  1477. if ( pbufFile->QueryPtr() != pchOldPointer )
  1478. {
  1479. CHAR * pchNewPointer =
  1480. ( CHAR * ) pbufFile->QueryPtr();
  1481. DWORD cchLen = strlen( pchNewPointer);
  1482. pch = pchNewPointer +
  1483. ( pch - pchOldPointer );
  1484. pchTag = pchNewPointer +
  1485. ( pchTag - pchOldPointer );
  1486. pchTerm = pchNewPointer +
  1487. ( pchTerm - pchOldPointer );
  1488. pchOldPointer = pchNewPointer;
  1489. DBG_ASSERT( pch >= pchNewPointer &&
  1490. pch < pchNewPointer + cchLen);
  1491. DBG_ASSERT( pchTag >= pchNewPointer &&
  1492. pchTag < pchNewPointer + cchLen);
  1493. DBG_ASSERT( pchTerm >= pchNewPointer &&
  1494. pchTerm <= pchNewPointer + cchLen);
  1495. }
  1496. }
  1497. //
  1498. // Expand the space for the value
  1499. //
  1500. memmove( pchTerm + 1 + cbValue - cbTag,
  1501. pchTerm + 1,
  1502. strlen( pchTerm + 1 ) + sizeof( CHAR ) );
  1503. cbFile += cbValue - cbTag;
  1504. }
  1505. else
  1506. {
  1507. //
  1508. // Collapse the space since tag is longer then the
  1509. // value
  1510. //
  1511. memmove( pchTag + cbValue,
  1512. pchTerm + 1,
  1513. strlen( pchTerm + 1 ) + sizeof( CHAR ) );
  1514. cbFile -= cbTag - cbValue;
  1515. }
  1516. //
  1517. // Replace the tag value with the replacement value
  1518. //
  1519. memcpy( pchTag,
  1520. pchValue,
  1521. cbValue );
  1522. pch = pchTag + cbValue;
  1523. }
  1524. else
  1525. {
  1526. //
  1527. // No more tags to replace so get out
  1528. //
  1529. break;
  1530. }
  1531. }
  1532. return S_OK;
  1533. }
  1534. VOID
  1535. ODBC_REQ::LookupTag(
  1536. CHAR * pchBeginTag,
  1537. const CHAR * * ppchAfterTag,
  1538. const CHAR * * ppchValue,
  1539. DWORD * pcbValue,
  1540. enum TAG_TYPE * pTagType
  1541. )
  1542. /*++
  1543. Routine Description:
  1544. This method looks at the tag, determines the tag type and
  1545. returns the associated value. This is used only for the .htx file.
  1546. Arguments:
  1547. pchBeginTag - Points to first character of tag (i.e., '<')
  1548. ppchAfterTag - Receives the first character after the tag if
  1549. the tag
  1550. ppchValue - If the tag is a value, returns the database value
  1551. pcbValue - Receives number of bytes in the value being returned
  1552. pTagType - Returns the tag type
  1553. Return Value:
  1554. NONE
  1555. --*/
  1556. {
  1557. CHAR * pchTerm;
  1558. BOOL fLongTagMarker;
  1559. STACK_STRA( strTagName, 128);
  1560. DWORD cchToCopy;
  1561. BOOL fDoEsc = FALSE;
  1562. DBG_ASSERT( CheckSignature() );
  1563. *pTagType = TAG_TYPE_UNKNOWN;
  1564. *ppchAfterTag = pchBeginTag;
  1565. *pcbValue = (DWORD) -1;
  1566. DBG_ASSERT( !memcmp( pchBeginTag, "<!--%", 5 ) ||
  1567. !memcmp( pchBeginTag, "<%", 2 ) );
  1568. fLongTagMarker = pchBeginTag[1] == '!';
  1569. //
  1570. // Move past the tag marker, 5 for "<!--%" and 2 for "<%"
  1571. //
  1572. pchBeginTag += (fLongTagMarker ? 5 : 2);
  1573. if ( *pchBeginTag == '"' )
  1574. {
  1575. if ( !memcmp( pchBeginTag, "\"%z\",", sizeof("\"%z\",") - 1 ) )
  1576. {
  1577. fDoEsc = TRUE;
  1578. pchBeginTag += sizeof("\"%z\",") - 1;
  1579. }
  1580. else
  1581. {
  1582. return;
  1583. }
  1584. }
  1585. //
  1586. // Find the end of the tag and make a copy.
  1587. //
  1588. pchTerm = strchr( pchBeginTag, '%' );
  1589. if ( !pchTerm )
  1590. {
  1591. return;
  1592. }
  1593. cchToCopy = DIFF(pchTerm - pchBeginTag);
  1594. if ( FAILED( strTagName.Copy( pchBeginTag,
  1595. cchToCopy * sizeof( CHAR ) ) ) )
  1596. {
  1597. DBGPRINTF(( DBG_CONTEXT,
  1598. "Error copying BeginTag." ));
  1599. return;
  1600. }
  1601. LookupSymbol( strTagName.QueryStr(),
  1602. pTagType,
  1603. ppchValue,
  1604. pcbValue );
  1605. if ( fDoEsc && *pTagType == TAG_TYPE_VALUE )
  1606. {
  1607. *pTagType = TAG_TYPE_VALUE_TO_ESCAPE;
  1608. }
  1609. if ( *pTagType != TAG_TYPE_IF )
  1610. {
  1611. *ppchAfterTag = pchTerm + (fLongTagMarker ? 4 : 2);
  1612. }
  1613. else
  1614. {
  1615. //
  1616. // We leave the pointer on the expression if this was an if
  1617. //
  1618. *ppchAfterTag = NEXT_TOKEN(pchBeginTag);
  1619. }
  1620. *pchTerm = '%';
  1621. }
  1622. HRESULT
  1623. ODBC_REQ::LookupSymbol(
  1624. const CHAR * pchSymbolName,
  1625. enum TAG_TYPE * pTagType,
  1626. const CHAR * * ppchValue,
  1627. DWORD * pcbValue
  1628. )
  1629. /*++
  1630. Routine Description:
  1631. Looks to see if the specified symbol name is defined and what
  1632. the type value of the symbol is.
  1633. The "if" symbols is special cased to allow the expression to
  1634. follow it
  1635. If the symbol is a multi-value field (from command line) then
  1636. the tabs in the value in will be replaced by commas.
  1637. Arguments:
  1638. pchSymbolName - Name of zero terminated symbol to find
  1639. pTagType - Returns the tag type of the symbol
  1640. ppchValue - Returns a pointer to the string value of the symbol
  1641. if it has one
  1642. pcbValue - Returns length of value
  1643. Return Value:
  1644. HRESULT
  1645. --*/
  1646. {
  1647. BOOL fIsMultiValue;
  1648. CHAR szSymbolValue[ 256 ];
  1649. CHAR * achSymbolValue = szSymbolValue;
  1650. DWORD dwSymbolValue = sizeof( szSymbolValue );
  1651. HRESULT hr;
  1652. DBG_ASSERT( CheckSignature() );
  1653. //
  1654. // Does the symbol match one of the column names?
  1655. //
  1656. if ( _pstrCols && _pstrValues )
  1657. {
  1658. for ( DWORD i = 0; i < _cCols; i++ )
  1659. {
  1660. if ( !lstrcmpiA( _pstrCols[i].QueryStr(),
  1661. pchSymbolName ))
  1662. {
  1663. *pTagType = TAG_TYPE_VALUE;
  1664. *ppchValue = _pstrValues[i].QueryStr();
  1665. *pcbValue = _pcbValues[i];
  1666. //
  1667. // BugID 33406 - Don't return half DBCS char at end
  1668. // of data
  1669. //
  1670. if ( g_fIsSystemDBCS )
  1671. {
  1672. CHAR * pch;
  1673. for ( pch = (CHAR *)*ppchValue; *pch; pch++ )
  1674. {
  1675. if ( IsDBCSLeadByte( *pch ) )
  1676. {
  1677. if ( !*(pch+1) )
  1678. {
  1679. *pch = '\0';
  1680. (*pcbValue)--;
  1681. break;
  1682. }
  1683. pch++;
  1684. }
  1685. }
  1686. }
  1687. return S_OK;
  1688. }
  1689. }
  1690. }
  1691. //
  1692. // Does it match any of the special values?
  1693. //
  1694. if ( !_stricmp( pchSymbolName, BEGIN_DETAIL_TEXT ))
  1695. {
  1696. *pTagType = TAG_TYPE_BEGIN_DETAIL;
  1697. return S_OK;
  1698. }
  1699. else if ( !_stricmp( pchSymbolName, END_DETAIL_TEXT ))
  1700. {
  1701. *pTagType = TAG_TYPE_END_DETAIL;
  1702. return S_OK;
  1703. }
  1704. else if ( !_strnicmp( pchSymbolName, IF_TEXT, sizeof(IF_TEXT) - 1 ))
  1705. {
  1706. //
  1707. // The IF tag is treated a little bit differently cause we
  1708. // expect the expression to be included as part of the
  1709. // symbol
  1710. //
  1711. *pTagType = TAG_TYPE_IF;
  1712. return S_OK;
  1713. }
  1714. else if ( !_stricmp( pchSymbolName, END_IF_TEXT ))
  1715. {
  1716. *pTagType = TAG_TYPE_END_IF;
  1717. return S_OK;
  1718. }
  1719. else if ( !_stricmp( pchSymbolName, ELSE_TEXT ))
  1720. {
  1721. *pTagType = TAG_TYPE_ELSE;
  1722. return S_OK;
  1723. }
  1724. //
  1725. // Is it one of the parameters from the query (either one of the
  1726. // form fields or from the DefaultParameters field in the wdg
  1727. // file)? These must be prefixed by "idc.", that is
  1728. // "<%idc.Assign%>"
  1729. //
  1730. if ( !_strnicmp( pchSymbolName, "idc.", 4 ) &&
  1731. (*ppchValue = _plParams.FindValue( pchSymbolName + 4,
  1732. &fIsMultiValue,
  1733. pcbValue )) )
  1734. {
  1735. *pTagType = TAG_TYPE_VALUE;
  1736. //
  1737. // If this is a multi-value field, replace all the tabs with
  1738. // commas. This is somewhat of a hack as it breaks the use of
  1739. // this field when multiple queries are supported
  1740. //
  1741. if ( fIsMultiValue )
  1742. {
  1743. CHAR * pchtmp = (CHAR *) *ppchValue;
  1744. while ( pchtmp = strchr( pchtmp, '\t' ))
  1745. {
  1746. *pchtmp = ',';
  1747. }
  1748. }
  1749. return S_OK;
  1750. }
  1751. //
  1752. // Lastly, check to see if it's something the client has defined
  1753. //
  1754. if( !_pECB->GetServerVariable( _pECB->ConnID,
  1755. ( LPSTR )pchSymbolName,
  1756. achSymbolValue,
  1757. &dwSymbolValue ) &&
  1758. dwSymbolValue <= sizeof( szSymbolValue ) )
  1759. {
  1760. DBGPRINTF(( DBG_CONTEXT,
  1761. "Error getting server variable." ));
  1762. return E_FAIL;
  1763. }
  1764. if( dwSymbolValue > sizeof( szSymbolValue ) )
  1765. {
  1766. achSymbolValue = ( CHAR * )_alloca( dwSymbolValue );
  1767. if( !achSymbolValue )
  1768. {
  1769. return E_OUTOFMEMORY;
  1770. }
  1771. if( !_pECB->GetServerVariable( _pECB->ConnID,
  1772. ( LPSTR )pchSymbolName,
  1773. achSymbolValue,
  1774. &dwSymbolValue ) )
  1775. {
  1776. DBGPRINTF(( DBG_CONTEXT,
  1777. "Error getting server variable." ));
  1778. return E_FAIL;
  1779. }
  1780. }
  1781. hr = _strSymbolValue.Copy( achSymbolValue, dwSymbolValue );
  1782. if( FAILED( hr ) )
  1783. {
  1784. DBGPRINTF(( DBG_CONTEXT,
  1785. "Error copying symbol value, hr = 0x%x.\n",
  1786. hr ));
  1787. return hr;
  1788. }
  1789. *pTagType = TAG_TYPE_VALUE;
  1790. *ppchValue = _strSymbolValue.QueryStr();
  1791. *pcbValue = _strSymbolValue.QueryCB();
  1792. return S_OK;
  1793. }
  1794. HRESULT
  1795. ODBC_REQ::EvaluateExpression(
  1796. const CHAR * * ppchExpression,
  1797. BOOL * pfExprValue
  1798. )
  1799. /*++
  1800. Routine Description:
  1801. Performs simple expression evaluation for an 'if' tag in the
  1802. template file. Valid expressions are:
  1803. <%if <V1> <OP> <V2>%>
  1804. where V1, V2 can be one of:
  1805. Positive integer
  1806. TotalRecords - Total records contained in result set
  1807. MaxRecords - The maximum records specified in the query file
  1808. OP can be one of:
  1809. EQ - Equal
  1810. LT - Less then
  1811. GT - Greater then
  1812. Arguments:
  1813. ppchExpression - Points to V1 on entry, set to the first character
  1814. after the end tag on exit
  1815. pfExprValue - TRUE if the expression is TRUE, FALSE otherwise
  1816. Return Value:
  1817. HRESULT
  1818. --*/
  1819. {
  1820. EXPR_VALUE v1( this );
  1821. EXPR_VALUE v2( this );
  1822. TAG_TYPE OpType;
  1823. STRA strError;
  1824. CHAR * pch;
  1825. DBG_ASSERT( CheckSignature() );
  1826. if ( !v1.Evaluate( ppchExpression ) ||
  1827. FAILED(EvaluateOperator( ppchExpression, &OpType )) ||
  1828. !v2.Evaluate( ppchExpression ))
  1829. {
  1830. return E_FAIL;
  1831. }
  1832. //
  1833. // If the symbols weren't found, default them to empty
  1834. // strings
  1835. //
  1836. if ( v1.QueryType() == TAG_TYPE_UNKNOWN )
  1837. {
  1838. v1.SetType( TAG_TYPE_STRING );
  1839. }
  1840. if ( v2.QueryType() == TAG_TYPE_UNKNOWN )
  1841. {
  1842. v2.SetType( TAG_TYPE_STRING );
  1843. }
  1844. //
  1845. // The value types must match
  1846. //
  1847. if ( v1.QueryType() != v2.QueryType() )
  1848. {
  1849. BOOL fSt = FALSE;
  1850. if ( v1.QueryType() == TAG_TYPE_STRING && v2.QueryType() == TAG_TYPE_INTEGER )
  1851. fSt = v1.ConvertToInteger();
  1852. else if ( v1.QueryType() == TAG_TYPE_INTEGER && v2.QueryType() == TAG_TYPE_STRING )
  1853. fSt = v2.ConvertToInteger();
  1854. if ( !fSt )
  1855. {
  1856. strError.FormatString( ODBCMSG_MISMATCHED_VALUES,
  1857. NULL,
  1858. HTTP_ODBC_DLL );
  1859. SetErrorText( strError.QueryStr());
  1860. return E_FAIL;
  1861. }
  1862. }
  1863. //
  1864. // Move the current position to the end of this tag
  1865. //
  1866. if ( pch = strchr( *ppchExpression, '>' ))
  1867. {
  1868. *ppchExpression = pch + 1;
  1869. }
  1870. switch ( OpType )
  1871. {
  1872. case TAG_TYPE_OP_LT:
  1873. *pfExprValue = v1.LT( v2 );
  1874. break;
  1875. case TAG_TYPE_OP_GT:
  1876. *pfExprValue = v1.GT( v2 );
  1877. break;
  1878. case TAG_TYPE_OP_EQ:
  1879. *pfExprValue = v1.EQ( v2 );
  1880. break;
  1881. case TAG_TYPE_OP_CONTAINS:
  1882. //
  1883. // Contains is only valid for string values
  1884. //
  1885. if ( v1.QueryType() != TAG_TYPE_STRING )
  1886. {
  1887. strError.FormatString(
  1888. ODBCMSG_CONTAINS_ONLY_VALID_ON_STRINGS,
  1889. NULL,
  1890. HTTP_ODBC_DLL );
  1891. SetErrorText( strError.QueryStr());
  1892. return E_FAIL;
  1893. }
  1894. *pfExprValue = v1.CONTAINS( v2 );
  1895. break;
  1896. default:
  1897. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1898. }
  1899. return S_OK;
  1900. }
  1901. HRESULT
  1902. ODBC_REQ::EvaluateOperator(
  1903. const CHAR * * ppchExpression,
  1904. TAG_TYPE * pTagType
  1905. )
  1906. /*++
  1907. Routine Description:
  1908. Determines which operator is being used in this expression
  1909. Arguments:
  1910. ppchExpression - Points to the operator on entry, set to the
  1911. next token on exit
  1912. pTagType - Receives operator type
  1913. Return Value:
  1914. TRUE if successful, FALSE on error
  1915. --*/
  1916. {
  1917. DBG_ASSERT( CheckSignature() );
  1918. if ( COMP_FIELD( *ppchExpression, "GT", 2 ))
  1919. {
  1920. *pTagType = TAG_TYPE_OP_GT;
  1921. }
  1922. else if ( COMP_FIELD( *ppchExpression, "LT", 2 ))
  1923. {
  1924. *pTagType = TAG_TYPE_OP_LT;
  1925. }
  1926. else if ( COMP_FIELD( *ppchExpression, "EQ", 2 ))
  1927. {
  1928. *pTagType = TAG_TYPE_OP_EQ;
  1929. }
  1930. else if ( COMP_FIELD( *ppchExpression, "CONTAINS", 8 ))
  1931. {
  1932. *pTagType = TAG_TYPE_OP_CONTAINS;
  1933. }
  1934. else
  1935. {
  1936. //
  1937. // Unknown operator specified
  1938. //
  1939. STRA strError;
  1940. strError.FormatString( ODBCMSG_UNRECOGNIZED_OPERATOR,
  1941. NULL,
  1942. HTTP_ODBC_DLL );
  1943. SetErrorText( strError.QueryStr());
  1944. return E_FAIL;
  1945. }
  1946. *ppchExpression = NEXT_TOKEN( *ppchExpression );
  1947. return S_OK;
  1948. }
  1949. HRESULT
  1950. ODBC_REQ::SendData(
  1951. ODBC_REQ_CALLBACK pfnCallback,
  1952. PVOID pvContext,
  1953. const CHAR * pbData,
  1954. DWORD cbData,
  1955. BUFFER_CHAIN_ITEM * * ppbufOut,
  1956. DWORD * pcbOut
  1957. )
  1958. /*++
  1959. Routine Description:
  1960. This method buffers the outgoing data and sends it when the
  1961. output buffer is full
  1962. Arguments:
  1963. pfnCallback - Send callback function
  1964. pvContext - Context for send callback
  1965. pbData - Pointer to data to send
  1966. cbData - Number of bytes to send
  1967. ppbufOut - Output buffer to buffer nonsent and cached data in
  1968. pcbOut - Number of valid bytes in output buffer
  1969. Return Value:
  1970. TRUE if successful, FALSE on error
  1971. --*/
  1972. {
  1973. DWORD cbToCopy;
  1974. DWORD err;
  1975. BUFFER_CHAIN_ITEM * pbufOut = *ppbufOut;
  1976. STACK_STRA( strTemp, MAX_PATH);
  1977. HRESULT hr;
  1978. DBG_ASSERT( CheckSignature() );
  1979. //
  1980. // if cbData is -1 then assume the data is zero terminated
  1981. //
  1982. if ( cbData == -1 )
  1983. {
  1984. cbData = strlen( pbData );
  1985. }
  1986. //
  1987. // Convert the string from shift_jis to iso-2022-jp or euc-jp
  1988. //
  1989. if ( CODE_ONLY_SBCS != _nCharset )
  1990. {
  1991. int cbUNIXSize;
  1992. int nResult;
  1993. //
  1994. // Get the size after Conversion
  1995. //
  1996. cbUNIXSize = PC_to_UNIX( GetACP(),
  1997. _nCharset,
  1998. (UCHAR *)pbData,
  1999. (int)cbData,
  2000. NULL,
  2001. 0 );
  2002. hr = strTemp.Resize( cbUNIXSize + sizeof(CHAR) );
  2003. if ( FAILED( hr ) )
  2004. {
  2005. DBGPRINTF(( DBG_CONTEXT,
  2006. "Error resizing string buffer, hr = 0x%x.\n",
  2007. hr ));
  2008. return hr;
  2009. }
  2010. //
  2011. // Do conversion
  2012. //
  2013. nResult = PC_to_UNIX( GetACP(),
  2014. _nCharset,
  2015. (UCHAR *)pbData,
  2016. (int)cbData,
  2017. (UCHAR *)strTemp.QueryStr(),
  2018. cbUNIXSize );
  2019. if ( -1 == nResult || nResult != cbUNIXSize )
  2020. {
  2021. DBGPRINTF(( DBG_CONTEXT,
  2022. "Error converting data.\n" ));
  2023. return E_FAIL;
  2024. }
  2025. //
  2026. // update the string pointer and count
  2027. //
  2028. pbData = strTemp.QueryStr();
  2029. cbData = cbUNIXSize;
  2030. *(strTemp.QueryStr() + cbUNIXSize) = '\0';
  2031. }
  2032. //
  2033. // Append the new data onto the old data
  2034. //
  2035. cbToCopy = min( cbData, OUTPUT_BUFFER_SIZE - *pcbOut );
  2036. memcpy( (BYTE *) pbufOut->QueryPtr() + *pcbOut,
  2037. pbData,
  2038. cbToCopy );
  2039. *pcbOut += cbToCopy;
  2040. cbData -= cbToCopy;
  2041. pbData += cbToCopy;
  2042. //
  2043. // If we filled up the buffer, send the data
  2044. //
  2045. if ( cbData )
  2046. {
  2047. err = pfnCallback( pvContext,
  2048. (CHAR *) pbufOut->QueryPtr(),
  2049. *pcbOut );
  2050. if ( err )
  2051. {
  2052. hr = HRESULT_FROM_WIN32( err );
  2053. DBGPRINTF(( DBG_CONTEXT,
  2054. "Error sending the data, hr = 0x%x.\n",
  2055. hr ));
  2056. return hr;
  2057. }
  2058. *pcbOut = 0;
  2059. }
  2060. else
  2061. {
  2062. return S_OK;
  2063. }
  2064. //
  2065. // We know at this point the output buffer is empty
  2066. //
  2067. while ( cbData )
  2068. {
  2069. //
  2070. // If the input data will fill the output buffer, send the
  2071. // data directly from the input buffer
  2072. //
  2073. if ( cbData > OUTPUT_BUFFER_SIZE )
  2074. {
  2075. err = pfnCallback( pvContext,
  2076. pbData,
  2077. OUTPUT_BUFFER_SIZE );
  2078. if ( err )
  2079. {
  2080. hr = HRESULT_FROM_WIN32( err );
  2081. DBGPRINTF(( DBG_CONTEXT,
  2082. "Error sending the data, hr = 0x%x.\n",
  2083. hr ));
  2084. return hr;
  2085. }
  2086. cbData -= OUTPUT_BUFFER_SIZE;
  2087. pbData += OUTPUT_BUFFER_SIZE;
  2088. }
  2089. else
  2090. {
  2091. //
  2092. // We don't have enough to send so put it in the output buffer
  2093. //
  2094. memcpy( pbufOut->QueryPtr(),
  2095. pbData,
  2096. cbData );
  2097. *pcbOut = cbData;
  2098. break;
  2099. }
  2100. }
  2101. return S_OK;
  2102. }
  2103. BOOL
  2104. ODBC_REQ::SkipConditionalBlock(
  2105. const CHAR * * ppchIn,
  2106. const CHAR * pchEOF,
  2107. const CHAR * pchSearchTag
  2108. )
  2109. /*++
  2110. Routine Description:
  2111. Skip a conditional block delimited by ENDIF or specified Tag
  2112. returns TRUE if specified Tag found instead of ENDIF or end
  2113. of text
  2114. Arguments:
  2115. ppchIn - Text stream to scan for tag
  2116. pchTag - Name of tag (w/o '<%%>') to find and skip
  2117. --*/
  2118. {
  2119. const CHAR * pchIn = *ppchIn;
  2120. const CHAR * pchIf;
  2121. const CHAR * pchEndif;
  2122. const CHAR * pchTag;
  2123. int cLev = 0;
  2124. DBG_ASSERT( CheckSignature() );
  2125. for ( ; pchIn < pchEOF ; )
  2126. {
  2127. pchEndif = pchIf = pchIn;
  2128. SkipToTag( &pchIf, IF_TEXT );
  2129. SkipToTag( &pchEndif, END_IF_TEXT );
  2130. if ( pchSearchTag == NULL )
  2131. {
  2132. pchTag = pchEOF;
  2133. }
  2134. else
  2135. {
  2136. pchTag = pchIn;
  2137. SkipToTag( &pchTag, pchSearchTag );
  2138. }
  2139. if ( pchIf < pchTag && pchIf < pchEndif )
  2140. {
  2141. ++cLev;
  2142. pchIn = pchIf;
  2143. }
  2144. else if ( pchTag < pchIf && pchTag < pchEndif )
  2145. {
  2146. if ( !cLev )
  2147. {
  2148. *ppchIn = pchTag;
  2149. return TRUE;
  2150. }
  2151. pchIn = pchTag;
  2152. }
  2153. else // END_IF_TEXT or nothing found
  2154. {
  2155. if ( !cLev )
  2156. {
  2157. *ppchIn = pchEndif;
  2158. return FALSE;
  2159. }
  2160. --cLev;
  2161. pchIn = pchEndif;
  2162. }
  2163. }
  2164. // else/endif not found
  2165. *ppchIn = pchEOF;
  2166. return FALSE;
  2167. }
  2168. VOID
  2169. ODBC_REQ::SkipToTag(
  2170. const CHAR * * ppchIn,
  2171. const CHAR * pchTag
  2172. )
  2173. /*++
  2174. Routine Description:
  2175. Given the name of a tag, skips to the first character after the tag
  2176. Arguments:
  2177. ppchIn - Text stream to scan for tag
  2178. pchTag - Name of tag (w/o '<%%>') to find and skip
  2179. --*/
  2180. {
  2181. const CHAR * pchIn = *ppchIn;
  2182. DWORD cchTag;
  2183. DBG_ASSERT( CheckSignature() );
  2184. cchTag = strlen( pchTag );
  2185. while ( pchIn = strchr( pchIn, '<' ))
  2186. {
  2187. if ( (!memcmp( pchIn, "<!--%", 5 ) ||
  2188. !memcmp( pchIn, "<%", 2 )) &&
  2189. !_strnicmp( pchIn + (pchIn[1] == '!' ? 5 : 2),
  2190. pchTag,
  2191. cchTag ))
  2192. {
  2193. goto Found;
  2194. }
  2195. else
  2196. pchIn++;
  2197. }
  2198. //
  2199. // Not found, return the end of file
  2200. //
  2201. *ppchIn += strlen( *ppchIn );
  2202. return;
  2203. Found:
  2204. pchIn = strchr( pchIn + cchTag, '>' );
  2205. if ( !pchIn )
  2206. {
  2207. *ppchIn += strlen( *ppchIn );
  2208. }
  2209. else
  2210. {
  2211. *ppchIn = pchIn + 1;
  2212. }
  2213. return;
  2214. }
  2215. BOOL
  2216. ODBC_REQ::IsEqual(
  2217. ODBC_REQ * podbcreq
  2218. )
  2219. /*++
  2220. Routine Description:
  2221. Determines if the passed query's parameter would make it equivalent
  2222. to this query
  2223. A query is deemed equal if:
  2224. 1) The query has the same number of parameters passed from the
  2225. client
  2226. 2) The query's parameters match
  2227. Note parameter comparison is case insensitive for both the field
  2228. and the value
  2229. The template can be different if it's parameterized, but we'll pick
  2230. up the difference in the parameter list in this case.
  2231. Since podbcreq is a query that has already been processed, it may
  2232. contain additional values from the .wdg default list.
  2233. Arguments:
  2234. podbcreq - Query to check for equality
  2235. --*/
  2236. {
  2237. VOID * pCookie = NULL;
  2238. CHAR * pszField;
  2239. CHAR * pszValue1, * pszValue2;
  2240. DBG_ASSERT( CheckSignature() );
  2241. //
  2242. // First compare the number of parameters passed from the client
  2243. //
  2244. if ( QueryClientParamCount() != podbcreq->QueryClientParamCount() )
  2245. {
  2246. return FALSE;
  2247. }
  2248. //
  2249. // Walk the list of parameters making sure they all match
  2250. //
  2251. while ( pCookie = podbcreq->_plParams.NextPair( pCookie,
  2252. &pszField,
  2253. &pszValue1 ))
  2254. {
  2255. if ( !(pszValue2 = _plParams.FindValue( pszField )) ||
  2256. lstrcmpiA( pszValue1, pszValue2 ) )
  2257. {
  2258. //
  2259. // Either the value wasn't found or it doesn't match,
  2260. // the queries are not equal
  2261. //
  2262. return FALSE;
  2263. }
  2264. }
  2265. //
  2266. // The queries are equal
  2267. //
  2268. return TRUE;
  2269. }
  2270. HRESULT
  2271. ODBC_REQ::AppendHeaders(
  2272. STRA * pstrHeaders
  2273. )
  2274. /*++
  2275. Routine Description:
  2276. Adds any headers required for this query, this will generally be
  2277. the content type and an Expires header if this query is cached
  2278. Arguments:
  2279. pstrHeaders - String to append headers to
  2280. --*/
  2281. {
  2282. CHAR * pszTail;
  2283. HRESULT hr;
  2284. DBG_ASSERT( CheckSignature() );
  2285. //
  2286. // The length of the content type is less than 255
  2287. //
  2288. hr = pstrHeaders->Resize( 255 );
  2289. if ( FAILED( hr ) )
  2290. {
  2291. DBGPRINTF(( DBG_CONTEXT,
  2292. "Error resizing the header string buffer, hr = 0x%x.\n",
  2293. hr ));
  2294. return hr;
  2295. }
  2296. pszTail = pstrHeaders->QueryStr();
  2297. pszTail += wsprintfA( pszTail,
  2298. "Content-Type: %s\r\n\r\n",
  2299. QueryContentType() );
  2300. return S_OK;
  2301. }
  2302. BOOL
  2303. ODBC_REQ::GetLastErrorText(
  2304. STRA * pstrError
  2305. )
  2306. {
  2307. DBG_ASSERT( CheckSignature() );
  2308. //
  2309. // If we stored an error explanation return that, otherwise fall back
  2310. // to an ODBC error
  2311. //
  2312. if ( !_strErrorText.IsEmpty() )
  2313. {
  2314. if( FAILED( pstrError->Copy( _strErrorText ) ) )
  2315. {
  2316. return FALSE;
  2317. }
  2318. return TRUE;
  2319. }
  2320. else if ( _podbcstmt )
  2321. {
  2322. return _podbcstmt->GetLastErrorTextAsHtml( pstrError );
  2323. }
  2324. else
  2325. {
  2326. return QueryOdbcConnection()->GetLastErrorTextAsHtml( pstrError,
  2327. SQL_NULL_HSTMT,
  2328. QueryOdbcConnection()->QueryErrorCode() );
  2329. }
  2330. }
  2331. //static
  2332. HRESULT
  2333. ODBC_REQ::Initialize(
  2334. VOID
  2335. )
  2336. /*++
  2337. Routine Description:
  2338. Initialize ODBC_REQ lookaside
  2339. Arguments:
  2340. None
  2341. Return Value:
  2342. HRESULT
  2343. --*/
  2344. {
  2345. ALLOC_CACHE_CONFIGURATION acConfig;
  2346. acConfig.nConcurrency = 1;
  2347. acConfig.nThreshold = 100;
  2348. acConfig.cbSize = sizeof( ODBC_REQ );
  2349. DBG_ASSERT( sm_pachOdbcRequests == NULL );
  2350. sm_pachOdbcRequests = new ALLOC_CACHE_HANDLER( "ODBC_REQ",
  2351. &acConfig );
  2352. if ( sm_pachOdbcRequests == NULL )
  2353. {
  2354. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  2355. }
  2356. return NO_ERROR;
  2357. }
  2358. //static
  2359. VOID
  2360. ODBC_REQ::Terminate(
  2361. VOID
  2362. )
  2363. /*++
  2364. Routine Description:
  2365. Terminate ODBC_REQ lookaside
  2366. Arguments:
  2367. None
  2368. Return Value:
  2369. None
  2370. --*/
  2371. {
  2372. if ( sm_pachOdbcRequests != NULL )
  2373. {
  2374. delete sm_pachOdbcRequests;
  2375. sm_pachOdbcRequests = NULL;
  2376. }
  2377. }
  2378. BOOL
  2379. EXPR_VALUE::ConvertToInteger(
  2380. VOID )
  2381. {
  2382. if ( _tagType == TAG_TYPE_STRING )
  2383. {
  2384. PSTR pS =_strValue.QueryStr();
  2385. if ( *pS == '-' || isdigit(*(UCHAR *)pS) )
  2386. {
  2387. _dwValue = atoi( pS );
  2388. _tagType = TAG_TYPE_INTEGER;
  2389. return TRUE;
  2390. }
  2391. }
  2392. return FALSE;
  2393. }
  2394. BOOL
  2395. EXPR_VALUE::Evaluate(
  2396. const CHAR * * ppchIn
  2397. )
  2398. /*++
  2399. Routine Description:
  2400. Determines the type of value and retrieves the value appropriately
  2401. Arguments:
  2402. ppchIn - Pointer to first character of value on way in, next token
  2403. on they way out
  2404. --*/
  2405. {
  2406. const CHAR * pchIn = *ppchIn;
  2407. const CHAR * pchEnd;
  2408. const CHAR * pchValue;
  2409. DWORD cbValue;
  2410. STRA strError;
  2411. DWORD cchToCopy;
  2412. if ( isdigit( *(UCHAR *)pchIn ) )
  2413. {
  2414. //
  2415. // Simple number
  2416. //
  2417. _tagType = TAG_TYPE_INTEGER;
  2418. _dwValue = atoi( pchIn );
  2419. while ( isdigit( *(UCHAR *)pchIn ) )
  2420. {
  2421. pchIn++;
  2422. }
  2423. *ppchIn = SkipWhite( pchIn );
  2424. }
  2425. else if ( *pchIn == '"' )
  2426. {
  2427. //
  2428. // Simple string, find the closing quote
  2429. //
  2430. pchEnd = strchr( ++pchIn, '\"' );
  2431. if ( !pchEnd )
  2432. {
  2433. strError.FormatString( ODBCMSG_UNBALANCED_STRING,
  2434. NULL,
  2435. HTTP_ODBC_DLL );
  2436. _podbcreq->SetErrorText( strError.QueryStr());
  2437. return FALSE;
  2438. }
  2439. cchToCopy = DIFF(pchEnd - pchIn);
  2440. if ( FAILED( _strValue.Copy( pchIn,
  2441. cchToCopy * sizeof(CHAR) ) ) )
  2442. {
  2443. DBGPRINTF(( DBG_CONTEXT,
  2444. "Error copying the value." ));
  2445. return FALSE;
  2446. }
  2447. _tagType = TAG_TYPE_STRING;
  2448. *ppchIn = SkipWhite( pchEnd + 1 );
  2449. }
  2450. else
  2451. {
  2452. STACK_STRA( strSymbol, 64 );
  2453. //
  2454. // This is a keyword we need to interpret
  2455. //
  2456. //
  2457. // These fields are delimited with either white space
  2458. // or '\'' or the closing %>
  2459. //
  2460. pchEnd = pchIn;
  2461. if ( *pchEnd == '\'' )
  2462. {
  2463. ++pchIn;
  2464. ++pchEnd;
  2465. while ( *pchEnd && *pchEnd != '\'' && *pchEnd != '%' )
  2466. {
  2467. pchEnd++;
  2468. }
  2469. }
  2470. else
  2471. {
  2472. while ( *pchEnd && !ISWHITE( *pchEnd ) && *pchEnd != '%' )
  2473. {
  2474. pchEnd++;
  2475. }
  2476. }
  2477. if ( COMP_FIELD( "MaxRecords", pchIn, 10 ))
  2478. {
  2479. _tagType = TAG_TYPE_INTEGER;
  2480. _dwValue = _podbcreq->QueryMaxRecords();
  2481. }
  2482. else if ( COMP_FIELD( "CurrentRecord", pchIn, 12 ))
  2483. {
  2484. _tagType = TAG_TYPE_INTEGER;
  2485. _dwValue = _podbcreq->QueryCurrentRecordNum();
  2486. }
  2487. else
  2488. {
  2489. //
  2490. // Isolate the symbol name
  2491. //
  2492. cchToCopy = DIFF(pchEnd - pchIn);
  2493. if ( FAILED( strSymbol.Copy( pchIn,
  2494. cchToCopy * sizeof(CHAR) ) ) )
  2495. {
  2496. DBGPRINTF(( DBG_CONTEXT,
  2497. "Error copying symbol name." ));
  2498. return FALSE;
  2499. }
  2500. //
  2501. // Look up the symbol
  2502. //
  2503. if ( FAILED( _podbcreq->LookupSymbol(
  2504. strSymbol.QueryStr(),
  2505. &_tagType,
  2506. &pchValue,
  2507. &cbValue ) ) )
  2508. {
  2509. DBGPRINTF(( DBG_CONTEXT,
  2510. "Error in LookupSymbol()." ));
  2511. return FALSE;
  2512. }
  2513. if ( _tagType == TAG_TYPE_VALUE ||
  2514. _tagType == TAG_TYPE_STRING )
  2515. {
  2516. if ( FAILED( _strValue.Copy( pchValue ) ) )
  2517. {
  2518. DBGPRINTF(( DBG_CONTEXT,
  2519. "Error copying tag value." ));
  2520. return FALSE;
  2521. }
  2522. _tagType = TAG_TYPE_STRING;
  2523. }
  2524. }
  2525. if ( *pchEnd == '\'' )
  2526. {
  2527. ++pchEnd;
  2528. }
  2529. *ppchIn = SkipWhite( pchEnd );
  2530. }
  2531. return TRUE;
  2532. }
  2533. BOOL
  2534. EXPR_VALUE::GT(
  2535. EXPR_VALUE & v1
  2536. )
  2537. /*++
  2538. Routine Description:
  2539. Returns TRUE if *this is Greater Then v1
  2540. Arguments:
  2541. v1 - Value for right side of the expression
  2542. --*/
  2543. {
  2544. if ( QueryType() == TAG_TYPE_INTEGER )
  2545. {
  2546. return QueryInteger() > v1.QueryInteger();
  2547. }
  2548. else
  2549. {
  2550. return lstrcmpiA( QueryStr(), v1.QueryStr() ) > 0;
  2551. }
  2552. return FALSE;
  2553. }
  2554. BOOL
  2555. EXPR_VALUE::LT(
  2556. EXPR_VALUE & v1
  2557. )
  2558. /*++
  2559. Routine Description:
  2560. Returns TRUE if *this is Less Then v1
  2561. Arguments:
  2562. v1 - Value for right side of the expression
  2563. --*/
  2564. {
  2565. if ( QueryType() == TAG_TYPE_INTEGER )
  2566. {
  2567. return QueryInteger() < v1.QueryInteger();
  2568. }
  2569. else
  2570. {
  2571. return lstrcmpiA( QueryStr(), v1.QueryStr() ) < 0;
  2572. }
  2573. return FALSE;
  2574. }
  2575. BOOL
  2576. EXPR_VALUE::EQ(
  2577. EXPR_VALUE & v1
  2578. )
  2579. /*++
  2580. Routine Description:
  2581. Returns TRUE if *this is Equal to v1
  2582. Arguments:
  2583. v1 - Value for right side of the expression
  2584. --*/
  2585. {
  2586. if ( QueryType() == TAG_TYPE_INTEGER )
  2587. {
  2588. return QueryInteger() == v1.QueryInteger();
  2589. }
  2590. else
  2591. {
  2592. return lstrcmpiA( QueryStr(), v1.QueryStr() ) == 0;
  2593. }
  2594. return FALSE;
  2595. }
  2596. BOOL
  2597. EXPR_VALUE::CONTAINS(
  2598. EXPR_VALUE & v1
  2599. )
  2600. /*++
  2601. Routine Description:
  2602. Returns TRUE if *this contains the string in v1
  2603. Arguments:
  2604. v1 - Value for right side of the expression
  2605. --*/
  2606. {
  2607. if ( QueryType() != TAG_TYPE_STRING ||
  2608. v1.QueryType() != TAG_TYPE_STRING )
  2609. {
  2610. return FALSE;
  2611. }
  2612. //
  2613. // Upper case the strings then do a search
  2614. //
  2615. UpperCase();
  2616. v1.UpperCase();
  2617. return strstr( QueryStr(), v1.QueryStr() ) != NULL;
  2618. }
  2619. const CHAR * SkipNonWhite( const CHAR * pch )
  2620. {
  2621. while ( *pch && !ISWHITE( *pch ) && *pch != '\n' )
  2622. pch++;
  2623. return pch;
  2624. }
  2625. const CHAR * SkipTo( const CHAR * pch, CHAR ch )
  2626. {
  2627. while ( *pch && *pch != '\n' && *pch != ch )
  2628. pch++;
  2629. return pch;
  2630. }
  2631. const CHAR * SkipWhite( const CHAR * pch )
  2632. {
  2633. while ( ISWHITE( *pch ) )
  2634. {
  2635. pch++;
  2636. }
  2637. return pch;
  2638. }
  2639. struct _ODBC_OPTIONS
  2640. {
  2641. CHAR * pszOptionName;
  2642. DWORD dwOption;
  2643. BOOL fNumeric;
  2644. }
  2645. OdbcOptions[] =
  2646. {
  2647. //
  2648. // Order roughly in order of likelihood of being used
  2649. //
  2650. "SQL_OPT_TRACEFILE", SQL_OPT_TRACEFILE, FALSE,
  2651. "SQL_QUERY_TIMEOUT", SQL_QUERY_TIMEOUT, TRUE,
  2652. "SQL_MAX_ROWS", SQL_MAX_ROWS, TRUE,
  2653. "SQL_LOGIN_TIMEOUT", SQL_LOGIN_TIMEOUT, TRUE,
  2654. "SQL_PACKET_SIZE", SQL_PACKET_SIZE, TRUE,
  2655. "SQL_NOSCAN", SQL_NOSCAN, TRUE,
  2656. "SQL_MAX_LENGTH", SQL_MAX_LENGTH, TRUE,
  2657. "SQL_ASYNC_ENABLE", SQL_ASYNC_ENABLE, TRUE,
  2658. "SQL_ACCESS_MODE", SQL_ACCESS_MODE, TRUE,
  2659. "SQL_OPT_TRACE", SQL_OPT_TRACE, TRUE,
  2660. "SQL_TRANSLATE_OPTION", SQL_TRANSLATE_OPTION, TRUE,
  2661. "SQL_TXN_ISOLATION", SQL_TXN_ISOLATION, TRUE,
  2662. "SQL_TRANSLATE_DLL", SQL_TRANSLATE_DLL, FALSE,
  2663. "SQL_CURRENT_QUALIFIER", SQL_CURRENT_QUALIFIER, FALSE,
  2664. NULL, 0, 0
  2665. };
  2666. HRESULT
  2667. SetOdbcOptions(
  2668. ODBC_CONNECTION * pOdbcConn,
  2669. STRA * pStrOptions
  2670. )
  2671. /*++
  2672. Routine Description:
  2673. Sets the options specified in the OdbcOptions: keyword of the
  2674. .idc file
  2675. Arguments:
  2676. pOdbcConn - ODBC connection to set options on
  2677. pStrOptions - List of options in "v=f,y=z" format.
  2678. --*/
  2679. {
  2680. PARAM_LIST OptionList;
  2681. VOID * pvCookie = NULL;
  2682. CHAR * pszField;
  2683. CHAR * pszValue;
  2684. DWORD dwOption;
  2685. SQLULEN dwValue;
  2686. DWORD i;
  2687. HRESULT hr;
  2688. hr = OptionList.ParsePairs( pStrOptions->QueryStr(),
  2689. FALSE,
  2690. FALSE );
  2691. if ( FAILED( hr ) )
  2692. {
  2693. DBGPRINTF(( DBG_CONTEXT,
  2694. "Error in ParsePairs(), hr = 0x%x.\n",
  2695. hr ));
  2696. return hr;
  2697. }
  2698. while ( pvCookie = OptionList.NextPair( pvCookie,
  2699. &pszField,
  2700. &pszValue ))
  2701. {
  2702. //
  2703. // If the field is a digit, then this is a driver specific
  2704. // option. convert the value and field as appropriate,
  2705. // otherwise look it up in our option table
  2706. //
  2707. if ( isdigit( *(UCHAR *)pszField ))
  2708. {
  2709. dwOption = atoi( pszField );
  2710. if ( isdigit( *(UCHAR *)pszValue ))
  2711. {
  2712. dwValue = ( SQLULEN )atoi( pszValue );
  2713. }
  2714. else
  2715. {
  2716. dwValue = ( SQLULEN ) pszValue;
  2717. }
  2718. }
  2719. else
  2720. {
  2721. i = 0;
  2722. while ( OdbcOptions[i].pszOptionName )
  2723. {
  2724. if ( !_stricmp( OdbcOptions[i].pszOptionName,
  2725. pszField ))
  2726. {
  2727. goto Found;
  2728. }
  2729. i++;
  2730. }
  2731. //
  2732. // Not found, skip this value
  2733. //
  2734. continue;
  2735. Found:
  2736. dwOption = OdbcOptions[i].dwOption;
  2737. if ( OdbcOptions[i].fNumeric )
  2738. {
  2739. //
  2740. // Numeric option, convert the value
  2741. //
  2742. dwValue = ( SQLULEN )atoi( pszValue );
  2743. }
  2744. else
  2745. {
  2746. dwValue = ( SQLULEN ) pszValue;
  2747. }
  2748. }
  2749. pOdbcConn->SetConnectOption( ( UWORD ) dwOption,
  2750. dwValue );
  2751. }
  2752. return S_OK;
  2753. }
  2754. HRESULT
  2755. BuildMultiValue(
  2756. const CHAR * pchValue,
  2757. STRA * pstrMulti,
  2758. BOOL fQuoteElements
  2759. )
  2760. {
  2761. CHAR * pchtmp = (CHAR *) pchValue;
  2762. DWORD cElements = 0;
  2763. HRESULT hr;
  2764. //
  2765. // If we're going to have to expand the size of the string,
  2766. // figure out the total size we'll need now
  2767. //
  2768. if ( fQuoteElements )
  2769. {
  2770. while ( pchtmp = strchr( pchtmp, '\t' ))
  2771. {
  2772. cElements++;
  2773. pchtmp++;
  2774. }
  2775. hr = pstrMulti->Resize( strlen( pchValue ) + 1 + 2 * cElements );
  2776. if ( FAILED( hr ) )
  2777. {
  2778. return hr;
  2779. }
  2780. }
  2781. hr = pstrMulti->Copy( pchValue );
  2782. if ( FAILED( hr ) )
  2783. {
  2784. DBGPRINTF(( DBG_CONTEXT,
  2785. "Error copying value, hr = 0x%x.\n",
  2786. hr ));
  2787. return hr;
  2788. }
  2789. //
  2790. // Replace tabs with "','" if fQuoteElements is TRUE,
  2791. // otherwise just ','
  2792. //
  2793. pchtmp = pstrMulti->QueryStr();
  2794. while ( pchtmp = strchr( pchtmp, '\t' ))
  2795. {
  2796. if ( fQuoteElements )
  2797. {
  2798. memmove( pchtmp + 3,
  2799. pchtmp + 1,
  2800. strlen( pchtmp + 1 ) + sizeof(CHAR));
  2801. memcpy( pchtmp, "','", 3 );
  2802. }
  2803. else
  2804. {
  2805. *pchtmp = ',';
  2806. }
  2807. }
  2808. return S_OK;
  2809. }
  2810. //
  2811. // Converts a value between zero and fifteen to the appropriate hex digit
  2812. //
  2813. #define HEXDIGIT( nDigit ) \
  2814. (CHAR)((nDigit) > 9 ? \
  2815. (nDigit) - 10 + 'A' \
  2816. : (nDigit) + '0')
  2817. HRESULT
  2818. ODBC_REQ::SendEscapedData(
  2819. ODBC_REQ_CALLBACK pfnCallback,
  2820. PVOID pvContext,
  2821. PCSTR pch,
  2822. DWORD cbIn,
  2823. LPDWORD pcbOut
  2824. )
  2825. /*++
  2826. Routine Description:
  2827. This method escape the outgoing data and then send it to the
  2828. SendData() function
  2829. Arguments:
  2830. pfnCallback - Send callback function
  2831. pvContext - Context for send callback
  2832. pch - Pointer to data to send
  2833. cbIn - Number of bytes to send
  2834. pcbOut - Number of valid bytes in output buffer
  2835. Return Value:
  2836. HRESLUT
  2837. --*/
  2838. {
  2839. CHAR ch;
  2840. int cNonEscaped = 0;
  2841. DWORD cbOut;
  2842. HRESULT hr;
  2843. #define SEND_DATA2( pchData, cbData ) SendData( pfnCallback, \
  2844. pvContext, \
  2845. (pchData), \
  2846. (cbData), \
  2847. &_pbufOut, \
  2848. pcbOut )
  2849. if ( cbIn == (DWORD)-1 )
  2850. {
  2851. cbIn = strlen( pch );
  2852. }
  2853. while ( cbIn-- )
  2854. {
  2855. ch = *pch;
  2856. //
  2857. // Escape characters that are in the non-printable range
  2858. // but ignore CR and LF
  2859. //
  2860. if ( (((ch >= 0) && (ch <= 32)) ||
  2861. ((ch >= 128) && (ch <= 159))||
  2862. (ch == '%') || (ch == '?') || (ch == '+') || (ch == '&'))
  2863. && !(ch == TEXT('\n') || ch == TEXT('\r')) )
  2864. {
  2865. CHAR achTmp[3];
  2866. //
  2867. // Insert the escape character
  2868. //
  2869. achTmp[0] = TEXT('%');
  2870. //
  2871. // Convert the low then the high character to hex
  2872. //
  2873. UINT nDigit = (UINT)(ch % 16);
  2874. achTmp[2] = HEXDIGIT( nDigit );
  2875. ch /= 16;
  2876. nDigit = (UINT)(ch % 16);
  2877. achTmp[1] = HEXDIGIT( nDigit );
  2878. hr = SEND_DATA2( pch-cNonEscaped, cNonEscaped );
  2879. if ( cNonEscaped && FAILED( hr ) )
  2880. {
  2881. DBGPRINTF(( DBG_CONTEXT,
  2882. "Error sending data, hr = 0x%x.\n",
  2883. hr ));
  2884. return hr;
  2885. }
  2886. hr = SEND_DATA2( achTmp, sizeof(achTmp) );
  2887. if ( FAILED( hr ) )
  2888. {
  2889. DBGPRINTF(( DBG_CONTEXT,
  2890. "Error sending data, hr = 0x%x.\n",
  2891. hr ));
  2892. return hr;
  2893. }
  2894. cNonEscaped = 0;
  2895. }
  2896. else
  2897. {
  2898. ++cNonEscaped;
  2899. }
  2900. ++pch;
  2901. }
  2902. hr = SEND_DATA2( pch-cNonEscaped, cNonEscaped );
  2903. if ( cNonEscaped && FAILED( hr ) )
  2904. {
  2905. DBGPRINTF(( DBG_CONTEXT,
  2906. "Error sending data, hr = 0x%x.\n",
  2907. hr ));
  2908. return hr;
  2909. }
  2910. return S_OK;
  2911. }
  2912. HRESULT
  2913. GetFileData(
  2914. IN const CHAR * pchFile,
  2915. OUT BYTE * * ppbData,
  2916. OUT DWORD * pcbData,
  2917. IN int nCharset,
  2918. IN BOOL fUseWin32Canon
  2919. )
  2920. /*++
  2921. Description:
  2922. Attempts to retrieve the passed file from the cache. If it's
  2923. not cached, then we read the file and add it to the cache.
  2924. Arguments:
  2925. pchFile - Fully qualified file to retrieve
  2926. pcbData - Receives pointer to first byte of data, used as handle
  2927. to free data
  2928. pcbSize - Size of output buffer
  2929. pCacheFileInfo - File cache information
  2930. nCharset - Charset (if this isn't SJIS, we convert it to SJIS
  2931. before Check-In)
  2932. ppSecDesc - Returns security descriptor if not null
  2933. fUseWin32Canon - The resource has not been canonicalized and
  2934. it's ok to use the win32 canonicalization code
  2935. Returns:
  2936. HRESULT
  2937. Notes:
  2938. The file is extended by two bytes and is appended with two zero
  2939. bytes,thus callers are guaranteed of a zero terminated text file.
  2940. --*/
  2941. {
  2942. DWORD cbLow, cbHigh;
  2943. BYTE * pbData = NULL;
  2944. BYTE * pbBuff = NULL;
  2945. int cbSJISSize;
  2946. HRESULT hr;
  2947. HANDLE hFile = CreateFileA( pchFile,
  2948. GENERIC_READ,
  2949. FILE_SHARE_READ |
  2950. FILE_SHARE_WRITE |
  2951. FILE_SHARE_DELETE,
  2952. NULL,
  2953. OPEN_EXISTING,
  2954. FILE_FLAG_SEQUENTIAL_SCAN |
  2955. FILE_FLAG_OVERLAPPED |
  2956. FILE_FLAG_BACKUP_SEMANTICS,
  2957. NULL );
  2958. if ( hFile == INVALID_HANDLE_VALUE)
  2959. {
  2960. hr = HRESULT_FROM_WIN32( GetLastError() );
  2961. goto ErrorExit;
  2962. }
  2963. else
  2964. {
  2965. WIN32_FILE_ATTRIBUTE_DATA FileAttributes;
  2966. if ( !GetFileAttributesExA( pchFile,
  2967. GetFileExInfoStandard,
  2968. &FileAttributes ) )
  2969. {
  2970. hr = HRESULT_FROM_WIN32( GetLastError() );
  2971. DBGPRINTF(( DBG_CONTEXT,
  2972. "Error in GetFileAttributesExA, hr = 0x%x.\n",
  2973. hr ));
  2974. goto ErrorExit;
  2975. }
  2976. cbHigh = FileAttributes.nFileSizeHigh;
  2977. cbLow = FileAttributes.nFileSizeLow;
  2978. }
  2979. //
  2980. // Limit the file size to 128k
  2981. //
  2982. if ( cbHigh || cbLow > 131072L )
  2983. {
  2984. hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  2985. goto ErrorExit;
  2986. }
  2987. if ( CODE_ONLY_SBCS != nCharset )
  2988. {
  2989. if ( !( pbBuff = pbData = (BYTE *) LocalAlloc( LPTR, cbLow ) ) )
  2990. {
  2991. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY);
  2992. DBGPRINTF(( DBG_CONTEXT,
  2993. "Error allocating memory, hr = 0x%x.\n",
  2994. hr ));
  2995. goto ErrorExit;
  2996. }
  2997. }
  2998. else
  2999. {
  3000. if ( !(pbData = (BYTE *) LocalAlloc( LPTR,
  3001. cbLow + sizeof(WCHAR)) ) )
  3002. {
  3003. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3004. DBGPRINTF(( DBG_CONTEXT,
  3005. "Error allocating memory, hr = 0x%x.\n",
  3006. hr ));
  3007. goto ErrorExit;
  3008. }
  3009. }
  3010. //
  3011. // Read the file data
  3012. //
  3013. hr = DoSynchronousReadFile( hFile,
  3014. ( PCHAR )pbData,
  3015. cbLow,
  3016. pcbData,
  3017. NULL );
  3018. if ( FAILED( hr ) )
  3019. {
  3020. DBGPRINTF(( DBG_CONTEXT,
  3021. "Error in DoSynchronousReadFile(), hr = 0x%x.\n",
  3022. hr ));
  3023. goto ErrorExit;
  3024. }
  3025. if ( CODE_ONLY_SBCS != nCharset )
  3026. {
  3027. pbData = NULL;
  3028. //
  3029. // get the length after conversion
  3030. //
  3031. cbSJISSize = UNIX_to_PC( GetACP(),
  3032. nCharset,
  3033. pbBuff,
  3034. *pcbData,
  3035. NULL,
  3036. 0 );
  3037. DBG_ASSERT( cbSJISSize <= (int)cbLow );
  3038. if ( !(pbData = (BYTE *) LocalAlloc(
  3039. LPTR,
  3040. cbSJISSize + sizeof(WCHAR)) ) )
  3041. {
  3042. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3043. DBGPRINTF(( DBG_CONTEXT,
  3044. "Error allocating memory, hr = 0x%x.\n",
  3045. hr ));
  3046. goto ErrorExit;
  3047. }
  3048. //
  3049. // conversion
  3050. //
  3051. UNIX_to_PC( GetACP(),
  3052. nCharset,
  3053. pbBuff,
  3054. *pcbData,
  3055. pbData,
  3056. cbSJISSize );
  3057. *pcbData = cbLow = cbSJISSize;
  3058. }
  3059. DBG_ASSERT( *pcbData <= cbLow );
  3060. //
  3061. // Zero terminate the file for both ANSI and Unicode files
  3062. //
  3063. *((WCHAR UNALIGNED *)(pbData + cbLow)) = L'\0';
  3064. *pcbData += sizeof(WCHAR);
  3065. *ppbData = pbData;
  3066. DBG_REQUIRE( CloseHandle(hFile) );
  3067. if ( pbBuff )
  3068. {
  3069. LocalFree( pbBuff );
  3070. }
  3071. return S_OK;
  3072. ErrorExit:
  3073. if ( hFile != INVALID_HANDLE_VALUE )
  3074. {
  3075. DBG_REQUIRE( CloseHandle(hFile) );
  3076. }
  3077. if ( pbBuff )
  3078. {
  3079. if ( pbBuff == pbData )
  3080. {
  3081. pbData = NULL;
  3082. }
  3083. LocalFree( pbBuff );
  3084. }
  3085. if ( pbData )
  3086. {
  3087. DBG_REQUIRE( LocalFree(pbData));
  3088. }
  3089. return hr;
  3090. }
  3091. HRESULT
  3092. DoSynchronousReadFile(
  3093. IN HANDLE hFile,
  3094. IN PCHAR Buffer,
  3095. IN DWORD nBuffer,
  3096. OUT PDWORD nRead,
  3097. IN LPOVERLAPPED Overlapped
  3098. )
  3099. /*++
  3100. Description:
  3101. Does Asynchronous file reads. Assumes that NT handles are
  3102. opened for OVERLAPPED I/O, win95 handles are not.
  3103. Arguments:
  3104. hFile - Handle to use for the read
  3105. Buffer - Buffer to read with
  3106. nBuffer - size of buffer
  3107. nRead - returns the number of bytes read
  3108. Overlapped - user supplied overlapped structure
  3109. Returns:
  3110. TRUE/FALSE
  3111. --*/
  3112. {
  3113. BOOL fNewEvent = FALSE;
  3114. OVERLAPPED ov;
  3115. BOOL fRet = FALSE;
  3116. DWORD err = NO_ERROR;
  3117. if ( Overlapped == NULL )
  3118. {
  3119. Overlapped = &ov;
  3120. ov.Offset = 0;
  3121. ov.OffsetHigh = 0;
  3122. ov.hEvent = IIS_CREATE_EVENT(
  3123. "OVERLAPPED::hEvent",
  3124. &ov,
  3125. TRUE,
  3126. FALSE
  3127. );
  3128. if ( ov.hEvent == NULL )
  3129. {
  3130. err = GetLastError();
  3131. DBGPRINTF( ( DBG_CONTEXT,
  3132. "CreateEvent failed with %d\n",
  3133. err ) );
  3134. goto ErrorExit;
  3135. }
  3136. fNewEvent = TRUE;
  3137. }
  3138. if ( !ReadFile( hFile,
  3139. Buffer,
  3140. nBuffer,
  3141. nRead,
  3142. Overlapped ))
  3143. {
  3144. err = GetLastError();
  3145. if ( ( err != ERROR_IO_PENDING ) &&
  3146. ( err != ERROR_HANDLE_EOF ) )
  3147. {
  3148. DBGPRINTF( ( DBG_CONTEXT,
  3149. "Error %d in ReadFile\n",
  3150. err));
  3151. goto ErrorExit;
  3152. }
  3153. }
  3154. if ( err == ERROR_IO_PENDING )
  3155. {
  3156. if ( !GetOverlappedResult( hFile,
  3157. Overlapped,
  3158. nRead,
  3159. TRUE ))
  3160. {
  3161. err = GetLastError();
  3162. DBGPRINTF( ( DBG_CONTEXT,
  3163. "Error %d in GetOverlappedResult\n",
  3164. err ) );
  3165. if ( err != ERROR_HANDLE_EOF )
  3166. {
  3167. goto ErrorExit;
  3168. }
  3169. }
  3170. }
  3171. return S_OK;
  3172. ErrorExit:
  3173. if ( fNewEvent ) {
  3174. DBG_REQUIRE( CloseHandle( ov.hEvent ) );
  3175. }
  3176. return HRESULT_FROM_WIN32( err );
  3177. } // DoSynchronousReadFile