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

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