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.

2911 lines
97 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 2000.
  5. //
  6. // File: wqcache.cxx
  7. //
  8. // Contents: WEB Query cache class
  9. //
  10. // History: 96/Jan/3 DwightKr Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. #include <perfobj.hxx>
  16. #include <params.hxx>
  17. DECLARE_INFOLEVEL(ciGib);
  18. //+---------------------------------------------------------------------------
  19. //
  20. // Function: GetSecurityToken
  21. //
  22. // Synopsis: Gets the security token handle for the current thread
  23. //
  24. // History: 96-Jan-18 DwightKr Created
  25. //
  26. //---------------------------------------------------------------------------
  27. HANDLE GetSecurityToken(TOKEN_STATISTICS & TokenInformation)
  28. {
  29. HANDLE hToken;
  30. NTSTATUS status = NtOpenThreadToken( GetCurrentThread(),
  31. TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
  32. TRUE, // OpenAsSelf
  33. &hToken );
  34. if ( !NT_SUCCESS( status ) )
  35. return INVALID_HANDLE_VALUE;
  36. DWORD ReturnLength;
  37. status = NtQueryInformationToken( hToken,
  38. TokenStatistics,
  39. (LPVOID)&TokenInformation,
  40. sizeof TokenInformation,
  41. &ReturnLength );
  42. if ( !NT_SUCCESS( status ) )
  43. {
  44. NtClose( hToken );
  45. ciGibDebugOut(( DEB_ERROR,
  46. "NtQueryInformationToken failed, 0x%x\n",
  47. status ));
  48. THROW( CException( status ));
  49. }
  50. Win4Assert( TokenInformation.TokenType == TokenImpersonation );
  51. return hToken;
  52. }
  53. //+---------------------------------------------------------------------------
  54. //
  55. // Member: CWQueryBookmark::CWQueryBookmark - public constructor
  56. //
  57. // Synopsis: Reads the values from a bookmark into the member variables
  58. //
  59. // History: 96-Jan-18 DwightKr Created
  60. //
  61. //----------------------------------------------------------------------------
  62. CWQueryBookmark::CWQueryBookmark( WCHAR const * wcsBookmark )
  63. {
  64. Win4Assert( 0 != wcsBookmark );
  65. //
  66. // Bookmarks have the following format:
  67. //
  68. // {S|N}-ptr_to_record-sequence_number-row_number
  69. //
  70. // Eg:
  71. //
  72. // S-1b234a-250-100
  73. // 0123456789 123456789 1234567
  74. //
  75. // S = sequential cursor
  76. // 001b234a = address of CWQueryItem containing query results, in hex
  77. // 00000250 = sequence number, in hex
  78. // 00000010 = next row # to display, in hex
  79. //
  80. //
  81. // N-1b277a-a50-2a000
  82. // 0123456789 123456789 1234567
  83. //
  84. // N = non-sequential cursor
  85. // 001b277a = address of CWQueryItem containing query results, in hex
  86. // 00000a50 = sequence number, in hex
  87. // 0002a000 = next row # to display, in hex
  88. //
  89. // Bookmarks are a maximum of 34 characters long, but they may be shorter.
  90. // On 32 bit platforms, the maximum length is 28 characters.
  91. WCHAR * wcsPtr = (WCHAR *)wcsBookmark;
  92. //
  93. // Verify that the bookmark is well formed. There should be 3 hyphens
  94. // and the length must be at least 24 characters; there may be trailing
  95. // spaces.
  96. //
  97. if ( (*(wcsBookmark+1) != L'-') )
  98. {
  99. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  100. }
  101. if ( *wcsBookmark == L'S' )
  102. {
  103. _fSequential = TRUE;
  104. }
  105. else if ( *wcsBookmark == L'N' )
  106. {
  107. _fSequential = FALSE;
  108. }
  109. else
  110. {
  111. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  112. }
  113. WCHAR *pwcStart = wcsPtr + 2;
  114. #if defined(_WIN64)
  115. _pItem = (CWQueryItem *) _wcstoui64( pwcStart, &wcsPtr, 16 );
  116. #else
  117. _pItem = (CWQueryItem *) wcstoul( pwcStart, &wcsPtr, 16 );
  118. #endif
  119. if ( ( pwcStart == wcsPtr ) ||
  120. ( *wcsPtr++ != L'-' ) )
  121. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  122. pwcStart = wcsPtr;
  123. _ulSequenceNumber = wcstoul( wcsPtr, &wcsPtr, 16 );
  124. if ( ( pwcStart == wcsPtr ) ||
  125. ( *wcsPtr++ != L'-' ) )
  126. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  127. pwcStart = wcsPtr;
  128. _lRecordNumber = wcstol( wcsPtr, &wcsPtr, 16 );
  129. if ( ( pwcStart == wcsPtr ) ||
  130. ( 0 != *wcsPtr ) )
  131. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  132. unsigned cbBmk = (unsigned)(wcsPtr - wcsBookmark + 1) * sizeof (WCHAR);
  133. if ( cbBmk >= sizeof( _wcsBookmark ) )
  134. THROW( CException( DB_E_ERRORSINCOMMAND ) );
  135. RtlCopyMemory( _wcsBookmark,
  136. wcsBookmark,
  137. cbBmk );
  138. //Win4Assert( 0 == _wcsBookmark[ cbBmk / ( sizeof WCHAR ) ] );
  139. }
  140. //+---------------------------------------------------------------------------
  141. //
  142. // Functon: AppendHex64Number, inline
  143. //
  144. // Synopsis: Append a 64 bit hex value to a wide string.
  145. //
  146. // Arguments: [pwc] - string to append number to
  147. // [x] - value to add to string
  148. //
  149. // Returns: Pointer to next character in string to be filled in
  150. //
  151. // History: 1998 Nov 06 AlanW Created header; return pwc
  152. //
  153. //----------------------------------------------------------------------------
  154. inline WCHAR * AppendHex64Number( WCHAR * pwc, ULONG_PTR x )
  155. {
  156. #if defined(_WIN64)
  157. _i64tow( x, pwc, 16 );
  158. #else
  159. _itow( x, pwc, 16 );
  160. #endif
  161. pwc += wcslen( pwc );
  162. return pwc;
  163. }
  164. //+---------------------------------------------------------------------------
  165. //
  166. // Functon: AppendHexNumber, inline
  167. //
  168. // Synopsis: Append a hex value to a wide string.
  169. //
  170. // Arguments: [pwc] - string to append number to
  171. // [x] - value to add to string
  172. //
  173. // Returns: Pointer to next character in string to be filled in
  174. //
  175. // History: 96 Apr 09 AlanW Created header; return pwc
  176. //
  177. //----------------------------------------------------------------------------
  178. inline WCHAR * AppendHexNumber( WCHAR * pwc, ULONG x )
  179. {
  180. _itow( x, pwc, 16 );
  181. pwc += wcslen( pwc );
  182. return pwc;
  183. }
  184. //+---------------------------------------------------------------------------
  185. //
  186. // Method: CWQueryBookmark::CWQueryBookmark, public
  187. //
  188. // Synopsis: Constructs a query bookmark
  189. //
  190. // Arguments: [fSequential] - TRUE if a sequential query
  191. // [pItem] - the query
  192. // [ulSequenceNumber] - # of queries executed so far
  193. // [lRecordNumber] - starting record number of the bookmark
  194. //
  195. // History: 96 DwightKr Created
  196. // 97 Apr 20 dlee Created header
  197. //
  198. //----------------------------------------------------------------------------
  199. CWQueryBookmark::CWQueryBookmark( BOOL fSequential,
  200. CWQueryItem * pItem,
  201. ULONG ulSequenceNumber,
  202. LONG lRecordNumber ) :
  203. _fSequential( fSequential ),
  204. _pItem( pItem ),
  205. _ulSequenceNumber( ulSequenceNumber ),
  206. _lRecordNumber( lRecordNumber )
  207. {
  208. WCHAR *pwc = _wcsBookmark;
  209. *pwc++ = _fSequential ? L'S' : L'N';
  210. *pwc++ = L'-';
  211. pwc = AppendHex64Number( pwc, (ULONG_PTR) _pItem );
  212. *pwc++ = L'-';
  213. pwc = AppendHexNumber( pwc, _ulSequenceNumber );
  214. *pwc++ = L'-';
  215. pwc = AppendHexNumber( pwc, _lRecordNumber );
  216. *pwc = 0;
  217. }
  218. //+---------------------------------------------------------------------------
  219. //
  220. // Member: CWQueryCache::CWQueryCache - public constructor
  221. //
  222. // Synopsis: Initializes the linked list of query items, and initializes
  223. // the query sequence counter.
  224. //
  225. // History: 96-Jan-18 DwightKr Created
  226. //
  227. //----------------------------------------------------------------------------
  228. CWQueryCache::CWQueryCache() :
  229. _ulSignature( LONGSIG( 'q', 'c', 'a', 'c' ) ),
  230. _cRequestsRejected( 0 ),
  231. _ulSequenceNumber( 0 ),
  232. _cActiveRequests( 0 ),
  233. _pendingQueue( 512 ), // large enough so realloc never happens
  234. #pragma warning( disable : 4355 ) // this used in base initialization
  235. _threadWatchDog( WatchDogThread, this, TRUE )
  236. #pragma warning( default : 4355 )
  237. {
  238. //
  239. // Create a security descriptor for the shared memory. The security
  240. // descriptor gives full access to the shared memory for the creator
  241. // and read acccess for everyone else. By default, only the creator
  242. // can access the shared memory. But we want that anyone will be able
  243. // to read the performance data. So we must give read access to
  244. // everyone.
  245. //
  246. SECURITY_DESCRIPTOR sd;
  247. BOOL f = InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
  248. if ( !f )
  249. THROW( CException() );
  250. HANDLE hToken;
  251. f = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken );
  252. if ( !f )
  253. THROW( CException() );
  254. SWin32Handle xHandle( hToken );
  255. DWORD cbTokenInfo;
  256. f = GetTokenInformation( hToken, TokenOwner, 0, 0, &cbTokenInfo );
  257. if ( ( !f ) && ( ERROR_INSUFFICIENT_BUFFER != GetLastError() ) )
  258. THROW( CException() );
  259. XArray<BYTE> xTo( cbTokenInfo );
  260. TOKEN_OWNER *pTO = (TOKEN_OWNER*)(char*) xTo.Get();
  261. f = GetTokenInformation( hToken, TokenOwner, pTO, cbTokenInfo, &cbTokenInfo );
  262. if ( !f )
  263. THROW( CException() );
  264. SID_IDENTIFIER_AUTHORITY WorldSidAuth = SECURITY_WORLD_SID_AUTHORITY;
  265. CSid sidWorld( WorldSidAuth, SECURITY_WORLD_RID );
  266. DWORD cbAcl = sizeof(ACL) +
  267. 2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) +
  268. GetLengthSid( sidWorld.Get() ) + GetLengthSid( pTO->Owner );
  269. XArray<BYTE> xDacl( cbAcl );
  270. PACL pDacl = (PACL)(char*) xDacl.Get();
  271. f = InitializeAcl( pDacl, cbAcl, ACL_REVISION );
  272. if ( !f )
  273. THROW( CException() );
  274. f = AddAccessAllowedAce( pDacl,
  275. ACL_REVISION,
  276. FILE_MAP_READ,
  277. sidWorld.Get() );
  278. if ( !f )
  279. THROW( CException() );
  280. f = AddAccessAllowedAce( pDacl,
  281. ACL_REVISION,
  282. FILE_MAP_ALL_ACCESS,
  283. pTO->Owner );
  284. if ( !f )
  285. THROW( CException() );
  286. f = SetSecurityDescriptorDacl( &sd, TRUE, pDacl, TRUE );
  287. if ( !f )
  288. THROW( CException() );
  289. SECURITY_ATTRIBUTES sa;
  290. sa.nLength = sizeof(sa);
  291. sa.lpSecurityDescriptor = &sd;
  292. sa.bInheritHandle = FALSE;
  293. _smPerf.CreateForWriteFromSA( CI_ISAPI_PERF_SHARED_MEM,
  294. CI_ISAPI_SIZE_OF_COUNTER_BLOCK,
  295. sa );
  296. // CreateForWrite throws on failure, so it's OK by this point
  297. Win4Assert( _smPerf.Ok() );
  298. //
  299. // Always write to the "spare" entry, them CopyMemory to the actual
  300. // perfcounters in the watchdog thread from time to time.
  301. //
  302. CI_ISAPI_COUNTERS * pc = &_smSpare;
  303. _pcCacheItems = &pc->_cCacheItems;
  304. _pcCacheHits = &pc->_cCacheHits;
  305. _pcCacheMisses = &pc->_cCacheMisses;
  306. _pcRunningQueries = &pc->_cRunningQueries;
  307. _pcCacheHitsAndMisses = &pc->_cCacheHitsAndMisses;
  308. _pcTotalQueries = &pc->_cTotalQueries;
  309. _pcRequestsQueued = &pc->_cRequestsQueued;
  310. _pcRequestsRejected = (ULONG *) &pc->_cRequestsRejected;
  311. _pcQueriesPerMinute = &pc->_cQueriesPerMinute;
  312. *_pcCacheItems = 0;
  313. *_pcCacheHits = 0;
  314. *_pcCacheMisses = 0;
  315. *_pcRunningQueries = 0;
  316. *_pcCacheHitsAndMisses = 0;
  317. *_pcTotalQueries = 0;
  318. *_pcRequestsQueued = 0;
  319. *_pcRequestsRejected = 0;
  320. *_pcQueriesPerMinute = 0;
  321. CopyMemory( _smPerf.GetPointer(), &_smSpare, sizeof _smSpare );
  322. ULONG ulOffset = 0;
  323. for (unsigned i=0; i<MAX_QUERY_COLUMNS; i++)
  324. {
  325. g_aDbBinding[i].iOrdinal = i+1; // Column #
  326. g_aDbBinding[i].obValue = ulOffset; // Offset of data
  327. g_aDbBinding[i].obLength = 0; // Offset where length data is stored
  328. g_aDbBinding[i].obStatus = 0; // Status info for column written
  329. g_aDbBinding[i].pTypeInfo = 0; // Reserved
  330. g_aDbBinding[i].pObject = 0; // DBOBJECT structure
  331. g_aDbBinding[i].pBindExt = 0; // Ignored
  332. g_aDbBinding[i].dwPart = DBPART_VALUE; // Return data
  333. g_aDbBinding[i].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Memory owner
  334. g_aDbBinding[i].eParamIO = 0; // eParamIo
  335. g_aDbBinding[i].cbMaxLen = sizeof(PROPVARIANT *); // Size of data to return
  336. g_aDbBinding[i].dwFlags = 0; // Reserved
  337. g_aDbBinding[i].wType = DBTYPE_VARIANT | DBTYPE_BYREF; // Type of return data
  338. g_aDbBinding[i].bPrecision = 0; // Precision to use
  339. g_aDbBinding[i].bScale = 0; // Scale to use
  340. ulOffset += sizeof(COutputColumn);
  341. }
  342. _threadWatchDog.Resume();
  343. }
  344. //+---------------------------------------------------------------------------
  345. //
  346. // Member: CWQueryCache::~CWQueryCache - public destructor
  347. //
  348. // Synopsis: Deletes all query items in the linked list.
  349. //
  350. // History: 96-Jan-18 DwightKr Created
  351. //
  352. //----------------------------------------------------------------------------
  353. CWQueryCache::~CWQueryCache()
  354. {
  355. Win4Assert( _pendingQueue.Count() == 0 );
  356. Win4Assert( IsEmpty() );
  357. }
  358. //+---------------------------------------------------------------------------
  359. //
  360. // Member: CWQueryCache::ThreadWatchDog - private
  361. //
  362. // Synopsis: The watchdog thread; it periodically:
  363. // moves queries from the pending queue to the active cache
  364. // moves queries from the active cache to the done cache
  365. // flushes old queries from the done cache
  366. // updates values from the registry.
  367. //
  368. // History: 96-Feb-26 DwightKr Created
  369. //
  370. //----------------------------------------------------------------------------
  371. DWORD WINAPI CWQueryCache::WatchDogThread( void *self )
  372. {
  373. CWQueryCache &me = * (CWQueryCache *) self;
  374. while ( !fTheActiveXSearchShutdown )
  375. {
  376. TRY
  377. {
  378. me.ProcessCacheEvents();
  379. }
  380. CATCH ( CException, e )
  381. {
  382. ciGibDebugOut(( DEB_IWARN,
  383. "Watchdog thread in CWQueryCache caught exception 0x%x\n",
  384. e.GetErrorCode() ));
  385. }
  386. END_CATCH
  387. }
  388. ciGibDebugOut(( DEB_WARN, "Watchdog thread in CWQueryCache is terminating\n" ));
  389. //
  390. // This is only necessary if thread is terminated from DLL_PROCESS_DETACH.
  391. //
  392. //TerminateThread( me._threadWatchDog.GetHandle(), 0 );
  393. return 0;
  394. }
  395. //+---------------------------------------------------------------------------
  396. //
  397. // Member: CWQueryCache::ProcessCacheEvents - private
  398. //
  399. // Synopsis: Process events such a cache flushes & completed asynchronous
  400. // queries.
  401. //
  402. // History: 96-Feb-26 DwightKr Created
  403. // 96-Mar-06 DwightKr Added asynchronous query completion
  404. // 86-May-16 DwightKr Add registry change event
  405. //
  406. //----------------------------------------------------------------------------
  407. void CWQueryCache::ProcessCacheEvents()
  408. {
  409. //
  410. // Setup for Queries / Minute
  411. //
  412. ULONG cStartTotal = Total();
  413. time_t ttStartTotal = time(0);
  414. time_t ttLastCachePurge = ttStartTotal;
  415. ULONG cTicks = GetTickCount();
  416. CRegChangeEvent regChangeEvent(wcsRegAdminTree);
  417. HANDLE waitHandles[2];
  418. waitHandles[0] = _eventCacheWork.GetHandle();
  419. waitHandles[1] = regChangeEvent.GetEventHandle();
  420. BOOL fBusy = FALSE;
  421. while ( !fTheActiveXSearchShutdown )
  422. {
  423. ULONG ulWaitTime;
  424. //
  425. // Set the wait period depending on the existence of any
  426. // asynchronous queries.
  427. //
  428. if ( fBusy )
  429. {
  430. // sleep very little for queries to complete
  431. ulWaitTime = 10;
  432. }
  433. else
  434. {
  435. if ( ( _pendingQueue.Count() > 0 ) ||
  436. ( TheWebPendingRequestQueue.Any() ) )
  437. {
  438. ulWaitTime = 100;
  439. }
  440. else
  441. {
  442. // If we're only processing sequential queries, we need
  443. // to wake up often to update perfcounters.
  444. ulWaitTime = 1000;
  445. }
  446. }
  447. ULONG res = WaitForMultipleObjects( 2,
  448. waitHandles,
  449. FALSE,
  450. ulWaitTime );
  451. if ( 1 == res )
  452. {
  453. TheIDQRegParams.Refresh();
  454. regChangeEvent.Reset();
  455. }
  456. else
  457. {
  458. // time() is expensive, GetTickCount() is cheap
  459. if ( ( GetTickCount() - cTicks ) > 30000 )
  460. {
  461. cTicks = GetTickCount();
  462. time_t ttCurrent = time(0);
  463. //
  464. // Compute Queries / Minute
  465. //
  466. time_t deltaTime = ttCurrent - ttStartTotal;
  467. if ( deltaTime >= 30 )
  468. {
  469. ULONG deltaTotal = Total() - cStartTotal;
  470. *_pcQueriesPerMinute = (ULONG)((deltaTotal * 60) / deltaTime);
  471. //
  472. // Start over every 15 minutes
  473. //
  474. if ( deltaTime > 15 * 60 )
  475. {
  476. cStartTotal = Total();
  477. ttStartTotal = time(0);
  478. }
  479. }
  480. //
  481. // Calculate the elapsed time since we deleted old queries.
  482. // If sufficient time has elapsed, flush unused old queries
  483. // from the cache.
  484. //
  485. if ( (ttCurrent - ttLastCachePurge) >=
  486. ((time_t) TheIDQRegParams.GetISCachePurgeInterval() * 60) )
  487. {
  488. ciGibDebugOut(( DEB_ITRACE, "Waking up query-cache purge thread\n" ));
  489. DeleteOldQueries();
  490. _idqFileList.DeleteZombies();
  491. _htxFileList.DeleteZombies();
  492. TheICommandCache.Purge();
  493. TheFormattingCache.Purge();
  494. time(&ttLastCachePurge);
  495. }
  496. }
  497. // Check for any completed asynchronous queries
  498. // Re-check for any completed asynchronous queries
  499. fBusy = CheckForCompletedQueries();
  500. fBusy |= CheckForPendingRequests();
  501. fBusy |= CheckForCompletedQueries();
  502. _eventCacheWork.Reset();
  503. }
  504. // Update perfcounters
  505. CopyMemory( _smPerf.GetPointer(), &_smSpare, sizeof _smSpare );
  506. }
  507. } //ProcessCacheEvents
  508. //+---------------------------------------------------------------------------
  509. //
  510. // Member: CWQueryCache::UpdatePendingRequestCount - public
  511. //
  512. // Synopsis: Updates the pending request statistic
  513. //
  514. // History: 96-May-14 Alanw Created
  515. //
  516. //----------------------------------------------------------------------------
  517. void CWQueryCache::UpdatePendingRequestCount()
  518. {
  519. *_pcRequestsQueued = (ULONG) TheWebPendingRequestQueue.Count();
  520. }
  521. //+---------------------------------------------------------------------------
  522. //
  523. // Member: CWQueryCache::CheckForPendingRequests - private
  524. //
  525. // Synopsis: Check pending for requests and process them
  526. //
  527. // Returns: TRUE if any pending requests were processed
  528. //
  529. // History: 96-Apr-12 dlee Created
  530. //
  531. //----------------------------------------------------------------------------
  532. BOOL CWQueryCache::CheckForPendingRequests()
  533. {
  534. // While the pending query queue is getting small in relation to the
  535. // number of threads processing the queries, move a pending request to
  536. // the pending query queue (or process it outright if it's synchronous).
  537. // Only process up to 8 queries -- the thread has other work to do too!
  538. ULONG cTwiceMaxThreads = 2 * TheIDQRegParams.GetMaxActiveQueryThreads();
  539. ULONG cProcessed = 0;
  540. while ( ( cProcessed < 8 ) &&
  541. ( TheWebPendingRequestQueue.Any() ) &&
  542. ( cTwiceMaxThreads >= _pendingQueue.Count() ) &&
  543. ( !fTheActiveXSearchShutdown ) )
  544. {
  545. ciGibDebugOut(( DEB_GIB_REQUEST, "Processing a pending request\n" ));
  546. CWebPendingItem item;
  547. // any items in the pending request queue?
  548. if ( !TheWebPendingRequestQueue.AcquireTop( item ) )
  549. return ( 0 != cProcessed );
  550. cProcessed++;
  551. CWebServer webServer( item.GetEcb() );
  552. Win4Assert( webServer.GetHttpStatus() == HTTP_STATUS_ACCEPTED );
  553. UpdatePendingRequestCount();
  554. // Process the request and complete the session if the query
  555. // was synchronous or if the parsing failed. If the status is
  556. // pending, the request is owned by the pending query queue.
  557. // Impersonate the user specified by the client's browser
  558. // Note: the constructor of CImpersonateClient cannot throw, or
  559. // the ecb will be leaked.
  560. CImpersonateClient impersonate( item.GetSecurityToken() );
  561. DWORD hseStatus = ProcessWebRequest( webServer );
  562. if ( HSE_STATUS_PENDING != hseStatus )
  563. {
  564. DecrementActiveRequests();
  565. ciGibDebugOut(( DEB_GIB_REQUEST,
  566. "Released session in CheckForPendingRequests, active: %d\n",
  567. _cActiveRequests ));
  568. ciGibDebugOut(( DEB_ITRACE, "releasing session hse %d http %d\n",
  569. hseStatus, HTTP_STATUS_OK ));
  570. webServer.SetHttpStatus( HTTP_STATUS_OK );
  571. webServer.ReleaseSession( hseStatus );
  572. }
  573. //
  574. // The destructor of CImpersonateClient will call RevertToSelf()
  575. // thus restoring our privledge level, and the destructor of
  576. // CWebPendingItem will close the security token handle.
  577. //
  578. }
  579. return ( 0 != cProcessed );
  580. } //CheckForPendingRequests
  581. //+---------------------------------------------------------------------------
  582. //
  583. // Member: CWQueryCache::CheckForCompletedQueries - private
  584. //
  585. // Synopsis: Check for completed asynchronous queries
  586. //
  587. // Returns: TRUE if any completed queries were processed, FALSE otherwise
  588. //
  589. // History: 96-Mar-04 DwightKr Created
  590. //
  591. //----------------------------------------------------------------------------
  592. BOOL CWQueryCache::CheckForCompletedQueries()
  593. {
  594. // shortcut for all-sequential query case
  595. if ( 0 == _pendingQueue.Count() )
  596. return FALSE;
  597. BOOL fDidWork = FALSE;
  598. XPtr<CWPendingQueryItem> pendingQuery;
  599. do
  600. {
  601. pendingQuery.Free();
  602. // Try to find a completed asynchronous query.
  603. // The snapshot code is necessary so IsQueryDone() isn't called
  604. // while holding the lock, as it can take a long time.
  605. {
  606. CLock shutdownLock( _mutexShutdown );
  607. // snapshot the pending query list
  608. XArray<CWPendingQueryItem *> xItems;
  609. {
  610. CLock lock( _mutex );
  611. xItems.Init( _pendingQueue.Count() );
  612. for ( unsigned i = 0; i < xItems.Count(); i++ )
  613. xItems[ i ] = _pendingQueue[ i ];
  614. }
  615. unsigned iPos;
  616. // look for a completed query
  617. for ( unsigned i = 0;
  618. pendingQuery.IsNull() && i < xItems.Count();
  619. i++ )
  620. {
  621. TRY
  622. {
  623. if ( xItems[i]->IsQueryDone() )
  624. {
  625. iPos = i;
  626. pendingQuery.Set( xItems[i] );
  627. }
  628. }
  629. CATCH( CException, e )
  630. {
  631. // the query is in an error state. pass it on below
  632. // so that its state will be reported.
  633. iPos = i;
  634. pendingQuery.Set( xItems[i] );
  635. }
  636. END_CATCH
  637. }
  638. // Remove the query (if found) from the pending query queue.
  639. if ( !pendingQuery.IsNull() )
  640. {
  641. CLock lock( _mutex );
  642. // iPos must be accurate since new pending queries are
  643. // appended to the array, and this is the only place
  644. // items are removed.
  645. Win4Assert( _pendingQueue[ iPos ] == pendingQuery.GetPointer() );
  646. _pendingQueue.Remove( iPos );
  647. }
  648. }
  649. //
  650. // If we found a completed query, output its results
  651. //
  652. if ( !pendingQuery.IsNull() )
  653. {
  654. CWQueryItem * pQueryItem = 0;
  655. TRY
  656. {
  657. ciGibDebugOut(( DEB_ITRACE, "An asynchronous web query has completed\n" ));
  658. fDidWork = TRUE;
  659. SCODE status = S_OK;
  660. CVariableSet & variableSet = pendingQuery->GetVariableSet();
  661. COutputFormat & outputFormat = pendingQuery->GetOutputFormat();
  662. Win4Assert( HTTP_STATUS_ACCEPTED == outputFormat.GetHttpStatus() );
  663. BOOL fCanonic = FALSE;
  664. CVirtualString vString( 16384 );
  665. TRY
  666. {
  667. // Note: this acquire doesn't acquire the ecb
  668. pQueryItem = pendingQuery->GetPendingQueryItem();
  669. AddToCache( pQueryItem );
  670. pendingQuery->AcquirePendingQueryItem();
  671. Win4Assert( !pQueryItem->IsCanonicalOutput() );
  672. pQueryItem->OutputQueryResults( variableSet,
  673. outputFormat,
  674. vString );
  675. }
  676. CATCH( CException, e )
  677. {
  678. status = e.GetErrorCode();
  679. }
  680. END_CATCH
  681. if ( S_OK != status )
  682. {
  683. vString.Empty();
  684. GetErrorPageNoThrow( eDefaultISAPIError,
  685. status,
  686. 0,
  687. pQueryItem->GetIDQFileName(),
  688. & variableSet,
  689. & outputFormat,
  690. outputFormat.GetLCID(),
  691. outputFormat,
  692. vString );
  693. }
  694. if ( !fCanonic )
  695. outputFormat.WriteClient( vString );
  696. }
  697. CATCH( CException, e )
  698. {
  699. // Ignore failures writing to the web server, likely
  700. // out of memory converting output to narrow string.
  701. }
  702. END_CATCH
  703. Release( pQueryItem );
  704. // note: the ecb will be released when pendingQuery is freed
  705. // above in the loop or when we leave scope
  706. }
  707. else
  708. {
  709. break;
  710. }
  711. } while ( !fTheActiveXSearchShutdown );
  712. return fDidWork;
  713. } //CheckForCompletedQueries
  714. //+---------------------------------------------------------------------------
  715. //
  716. // Member: CWQueryCache::CreateOrFindQuery - public
  717. //
  718. // Synopsis: Locates the query item specified by the variables
  719. //
  720. // Arguments: [wcsIDQFile] - name of the IDQ file containing the query
  721. // [variableSet] - parameters describing the query to find
  722. // [outputFormat] - format of #'s & dates
  723. // [securityIdentity] - User session for this query
  724. // [fAsynchronous] - was the query asynchronous
  725. //
  726. // Returns: query item matching; 0 otherwise
  727. //
  728. // History: 96-Jan-18 DwightKr Created
  729. //
  730. //----------------------------------------------------------------------------
  731. CWQueryItem * CWQueryCache::CreateOrFindQuery( WCHAR const * wcsIDQFile,
  732. XPtr<CVariableSet> & variableSet,
  733. XPtr<COutputFormat> & outputFormat,
  734. CSecurityIdentity securityIdentity,
  735. BOOL & fAsynchronous )
  736. {
  737. XInterface<CWQueryItem> xQueryItem;
  738. CVariable * pVarBookmark = variableSet->Find( ISAPI_CI_BOOKMARK );
  739. if ( 0 != pVarBookmark )
  740. {
  741. //
  742. // Get the bookmark & skipcount if specified
  743. //
  744. ULONG cwcValue;
  745. CWQueryBookmark bookMark( pVarBookmark->GetStringValueRAW() );
  746. LONG lSkipCount = 0;
  747. CVariable * pVarSkipCount = variableSet->Find( ISAPI_CI_BOOKMARK_SKIP_COUNT );
  748. if ( 0 != pVarSkipCount )
  749. {
  750. lSkipCount = IDQ_wtol( pVarSkipCount->GetStringValueRAW() );
  751. }
  752. xQueryItem.Set( FindItem( bookMark,
  753. lSkipCount,
  754. securityIdentity ) );
  755. }
  756. if ( !xQueryItem.IsNull() )
  757. {
  758. XArray<WCHAR> wcsLocale;
  759. LCID lcid = GetQueryLocale( xQueryItem->GetIDQFile().GetLocale(),
  760. variableSet.GetReference(),
  761. outputFormat.GetReference(),
  762. wcsLocale );
  763. Win4Assert( lcid == xQueryItem->GetLocale() );
  764. //
  765. // Now that we have the appropriate locale information, we can generate
  766. // the output format information.
  767. //
  768. outputFormat->LoadNumberFormatInfo( lcid );
  769. Win4Assert( 0 != wcsLocale.GetPointer() );
  770. variableSet->AcquireStringValue( ISAPI_CI_LOCALE, wcsLocale.GetPointer(), 0 );
  771. wcsLocale.Acquire();
  772. }
  773. else
  774. {
  775. //
  776. // Attempt to find a matching query with the same restriction,
  777. // scope, sort order, etc.
  778. xQueryItem.Set( FindItem( variableSet,
  779. outputFormat,
  780. wcsIDQFile,
  781. securityIdentity ) );
  782. if ( !xQueryItem.IsNull() )
  783. (*_pcTotalQueries)++;
  784. }
  785. // make sure the query isn't in bad shape (e.g. the pipe went away)
  786. if ( !xQueryItem.IsNull() )
  787. {
  788. TRY
  789. {
  790. // this will force a GetStatus(), and will throw on problems
  791. xQueryItem->IsQueryDone();
  792. }
  793. CATCH( CException, e )
  794. {
  795. // zombify the query before releasing it, so it isn't pulled
  796. // out of the cache and used again.
  797. xQueryItem->Zombify();
  798. Release( xQueryItem.Acquire(), FALSE );
  799. }
  800. END_CATCH;
  801. }
  802. if ( xQueryItem.IsNull() )
  803. {
  804. //
  805. // We still haven't found a matching query item. Build a new one
  806. //
  807. xQueryItem.Set( CreateNewQuery( wcsIDQFile,
  808. variableSet,
  809. outputFormat,
  810. securityIdentity,
  811. fAsynchronous ) );
  812. ciGibDebugOut(( DEB_ITRACE, "Item NOT found in cache\n" ));
  813. (*_pcTotalQueries)++;
  814. (*_pcCacheMisses)++;
  815. (*_pcCacheHitsAndMisses)++;
  816. }
  817. else
  818. {
  819. (*_pcCacheHits)++;
  820. (*_pcCacheHitsAndMisses)++;
  821. fAsynchronous = FALSE;
  822. ciGibDebugOut(( DEB_ITRACE, "Item found in cache\n" ));
  823. SetupDefaultCiVariables( variableSet.GetReference() );
  824. SetupDefaultISAPIVariables( variableSet.GetReference() );
  825. }
  826. Win4Assert( ( fAsynchronous && xQueryItem.IsNull() ) ||
  827. ( !fAsynchronous && !xQueryItem.IsNull() ) );
  828. // If it's asynchronous, CreateNewQuery will have already bumped the
  829. // count of running queries under lock, so we won't have the problem
  830. // where the query will be completed, output, and released before this
  831. // increment is called.
  832. if ( !fAsynchronous )
  833. IncrementRunningQueries();
  834. return xQueryItem.Acquire();
  835. } //CreateOrFindQuery
  836. //+---------------------------------------------------------------------------
  837. //
  838. // Member: CWQueryCache::FindItem - private
  839. //
  840. // Synopsis: Locates the query item specified by Bookmark. The
  841. // bookmark is used to generate the address of the CWQueryItem
  842. // and the lSkipCount is used when the query is using a sequential
  843. // cursor to determine if the rowset is positioned at the
  844. // appropriate row.
  845. //
  846. // Arguments: [bookmark] - bookmark of the item to find
  847. // [lSkipCount] - number of records to skip past bookmark
  848. // [securityIdentity] - security LUID of the browser
  849. //
  850. // Returns: query item matching [bookmark]; 0 otherwise
  851. //
  852. // History: 96-Jan-18 DwightKr Created
  853. //
  854. //----------------------------------------------------------------------------
  855. CWQueryItem * CWQueryCache::FindItem( CWQueryBookmark & bookmark,
  856. LONG lSkipCount,
  857. CSecurityIdentity securityIdentity )
  858. {
  859. if ( 0 == *_pcCacheItems )
  860. return 0;
  861. CWQueryItem * pQueryItem = 0;
  862. // ==========================================
  863. CLock lock( _mutex );
  864. TRY
  865. {
  866. CWQueryItem * pItem = (CWQueryItem *) bookmark.GetQueryItem();
  867. //
  868. // Iterate through the cache looking for this item.
  869. //
  870. for ( CWQueryCacheForwardIter iter(*this);
  871. !AtEnd(iter);
  872. Advance(iter) )
  873. {
  874. if ( iter.Get() == pItem )
  875. {
  876. //
  877. // Check to verify that all of the following match:
  878. //
  879. // 1 If sequential, its not in use
  880. // 2. The sequence number matches
  881. // 3. The memory block addressed is a CWQueryItem; check signature
  882. // 4. We have the same security context
  883. // 5. The cached data is still valid
  884. // 6. next records # (for sequential queries only) matches
  885. // 7. the item isn't a zombie
  886. //
  887. if ( (pItem->GetSequenceNumber() == bookmark.GetSequenceNumber()) &&
  888. ( pItem->GetSignature() == CWQueryItemSignature ) &&
  889. ( !pItem->IsSequential() ||
  890. ( (bookmark.GetRecordNumber() + lSkipCount == pItem->GetNextRecordNumber() ) &&
  891. (pItem->LokGetRefCount() == 0 )
  892. )
  893. ) &&
  894. ( pItem->LokIsCachedDataValid() ) &&
  895. ( !pItem->IsZombie() ) &&
  896. ( securityIdentity.IsEqual(pItem->GetSecurityIdentity()) )
  897. )
  898. {
  899. pItem->AddRef();
  900. LokMoveToFront( pItem );
  901. pQueryItem = pItem;
  902. break;
  903. }
  904. }
  905. }
  906. }
  907. CATCH (CException, e)
  908. {
  909. }
  910. END_CATCH
  911. return pQueryItem;
  912. // ==========================================
  913. }
  914. //+---------------------------------------------------------------------------
  915. //
  916. // Member: CWQueryCache::FindItem - private
  917. //
  918. // Synopsis: Locates the query item specified in the variables
  919. //
  920. // Arguments: [variableSet] - parameters describing the query to find
  921. // [outputFormat] - format of #'s & dates
  922. // [wcsIDQFile] - name of the IDQ file containing the query
  923. // [securityIdentity] - security LUID of the browser
  924. //
  925. // Returns: query item matching; 0 otherwise
  926. //
  927. // History: 96-Jan-18 DwightKr Created
  928. // 96-Mar-13 DwightKr Allow output columns to be replaceable
  929. // 97-Jun-11 KyleP Use web server from output format
  930. //
  931. //----------------------------------------------------------------------------
  932. CWQueryItem * CWQueryCache::FindItem( XPtr<CVariableSet> & variableSet,
  933. XPtr<COutputFormat> & outputFormat,
  934. WCHAR const * wcsIDQFile,
  935. CSecurityIdentity securityIdentity )
  936. {
  937. // The idq search and parameter replacement need to be done
  938. // regardless of whether a cached query will be used.
  939. CIDQFile * pIdqFile = _idqFileList.Find( wcsIDQFile,
  940. outputFormat->CodePage(),
  941. securityIdentity );
  942. XInterface<CIDQFile> xIDQFile( pIdqFile );
  943. //
  944. // Get string values for all parameters that define the query
  945. //
  946. ULONG cwc;
  947. XPtrST<WCHAR> wcsRestriction( ReplaceParameters( pIdqFile->GetRestriction(),
  948. variableSet.GetReference(),
  949. outputFormat.GetReference(),
  950. cwc ) );
  951. XPtrST<WCHAR> wcsScope( ReplaceParameters( pIdqFile->GetScope(),
  952. variableSet.GetReference(),
  953. outputFormat.GetReference(),
  954. cwc ) );
  955. // ConvertSlashToBackSlash( wcsScope.GetPointer() );
  956. XPtrST<WCHAR> wcsSort( ReplaceParameters( pIdqFile->GetSort(),
  957. variableSet.GetReference(),
  958. outputFormat.GetReference(),
  959. cwc ) );
  960. XPtrST<WCHAR> wcsTemplate( ReplaceParameters( pIdqFile->GetHTXFileName(),
  961. variableSet.GetReference(),
  962. outputFormat.GetReference(),
  963. cwc ) );
  964. XPtrST<WCHAR> wcsCatalog( ReplaceParameters( pIdqFile->GetCatalog(),
  965. variableSet.GetReference(),
  966. outputFormat.GetReference(),
  967. cwc ) );
  968. XPtrST<WCHAR> wcsColumns( ReplaceParameters( pIdqFile->GetColumns(),
  969. variableSet.GetReference(),
  970. outputFormat.GetReference(),
  971. cwc ) );
  972. XPtrST<WCHAR> wcsCiFlags( ReplaceParameters( pIdqFile->GetCiFlags(),
  973. variableSet.GetReference(),
  974. outputFormat.GetReference(),
  975. cwc ) );
  976. XPtrST<WCHAR> wcsForceUseCI( ReplaceParameters( pIdqFile->GetForceUseCI(),
  977. variableSet.GetReference(),
  978. outputFormat.GetReference(),
  979. cwc ) );
  980. XPtrST<WCHAR> wcsDeferTrimming( ReplaceParameters( pIdqFile->GetDeferTrimming(),
  981. variableSet.GetReference(),
  982. outputFormat.GetReference(),
  983. cwc ) );
  984. LONG lMaxRecordsInResultSet =
  985. ReplaceNumericParameter( pIdqFile->GetMaxRecordsInResultSet(),
  986. variableSet.GetReference(),
  987. outputFormat.GetReference(),
  988. TheIDQRegParams.GetMaxISRowsInResultSet(),
  989. IS_MAX_ROWS_IN_RESULT_MIN,
  990. IS_MAX_ROWS_IN_RESULT_MAX );
  991. LONG lFirstRowsInResultSet =
  992. ReplaceNumericParameter( pIdqFile->GetFirstRowsInResultSet(),
  993. variableSet.GetReference(),
  994. outputFormat.GetReference(),
  995. TheIDQRegParams.GetISFirstRowsInResultSet(),
  996. IS_FIRST_ROWS_IN_RESULT_MIN,
  997. IS_FIRST_ROWS_IN_RESULT_MAX );
  998. XArray<WCHAR> wcsLocale;
  999. LCID lcid = GetQueryLocale( pIdqFile->GetLocale(),
  1000. variableSet.GetReference(),
  1001. outputFormat.GetReference(),
  1002. wcsLocale );
  1003. //
  1004. // Now that we have the appropriate locale information, we can generate
  1005. // the output format information.
  1006. //
  1007. outputFormat->LoadNumberFormatInfo( lcid );
  1008. Win4Assert( 0 != wcsLocale.GetPointer() );
  1009. variableSet->AcquireStringValue( ISAPI_CI_LOCALE, wcsLocale.GetPointer(), 0 );
  1010. wcsLocale.Acquire();
  1011. XInterface<CWQueryItem> xMatchItem;
  1012. // ======================================
  1013. if ( 0 != *_pcCacheItems )
  1014. {
  1015. CLock lock( _mutex );
  1016. for ( CWQueryCacheForwardIter iter(*this);
  1017. !AtEnd(iter);
  1018. Advance(iter) )
  1019. {
  1020. CWQueryItem * pItem = iter.Get();
  1021. Win4Assert( pItem != 0 );
  1022. //
  1023. // Two queries are identical if all of the following match:
  1024. //
  1025. // 1. idq file names & its not a zombie
  1026. // 2. restriction
  1027. // 3. scope
  1028. // 4. sort set
  1029. // 5. template file
  1030. // 6. output columns
  1031. // 7. security context
  1032. // 8. CiFlags
  1033. // 9. ForceUseCi
  1034. // 10. CiDeferNonIndexedTrimming
  1035. // 11. MaxRecordsInResultSet matches
  1036. // 12. FirstRowsInResultSet matches
  1037. // 13. CiLocale
  1038. // 14. next records # (for sequential queries only)
  1039. // 15. cached data is still valid
  1040. //
  1041. //
  1042. // Verify condition #1.
  1043. //
  1044. if ( ( _wcsicmp( wcsIDQFile, pItem->GetIDQFileName() ) != 0 ) &&
  1045. ( !pItem->IsZombie() )
  1046. )
  1047. {
  1048. continue;
  1049. }
  1050. ciGibDebugOut(( DEB_ITRACE, "Checking cache item: %x\n", pItem ));
  1051. LONG lFirstSequentialRecord = 0; // Assume no first record #
  1052. ULONG cMatchedItems = 0; // # of Matched items
  1053. //
  1054. // Iterate through the list of parameters the browser passed
  1055. // to verify conditions #2 - #5 mentioned above. Stop testing
  1056. // paramaters as soon as a mismatch is found. Also, save the
  1057. // BOOKMARK & SKIPCOUNT so that condition #7 can be verified later.
  1058. //
  1059. if ( (wcsRestriction.GetPointer() != 0) &&
  1060. (_wcsicmp( wcsRestriction.GetPointer(), pItem->GetRestriction() ) != 0 )
  1061. )
  1062. {
  1063. ciGibDebugOut(( DEB_ITRACE,
  1064. "Searching cache: restrictions DONT match: %ws != %ws\n",
  1065. wcsRestriction.GetPointer(),
  1066. pItem->GetRestriction() ));
  1067. continue;
  1068. }
  1069. cMatchedItems++;
  1070. ciGibDebugOut(( DEB_ITRACE, "Searching cache: restrictions match\n" ));
  1071. #if DBG == 1
  1072. #if 0 // NTBUG #114206 - we don't do this anymore
  1073. //
  1074. // If we have a scope, verify that there are no slashes
  1075. //
  1076. WCHAR const * wcsSlashTest = wcsScope.GetPointer();
  1077. if ( 0 != wcsSlashTest )
  1078. {
  1079. while ( 0 != *wcsSlashTest )
  1080. {
  1081. Win4Assert( L'/' != *wcsSlashTest );
  1082. wcsSlashTest++;
  1083. }
  1084. }
  1085. #endif // 0
  1086. #if 0 // bogus assert due to the slash-flipping browser bug
  1087. wcsSlashTest = pItem->GetScope();
  1088. if ( 0 != wcsSlashTest )
  1089. {
  1090. while ( 0 != *wcsSlashTest )
  1091. {
  1092. Win4Assert( L'/' != *wcsSlashTest );
  1093. wcsSlashTest++;
  1094. }
  1095. }
  1096. #endif // 0
  1097. #endif // DBG == 1
  1098. if ( (wcsScope.GetPointer() != 0) &&
  1099. (_wcsicmp( wcsScope.GetPointer(), pItem->GetScope() ) != 0 )
  1100. )
  1101. {
  1102. ciGibDebugOut(( DEB_ITRACE,
  1103. "Searching cache: scopes DONT match: %ws != %ws\n",
  1104. wcsScope.GetPointer(),
  1105. pItem->GetScope() ));
  1106. continue;
  1107. }
  1108. cMatchedItems++;
  1109. ciGibDebugOut(( DEB_ITRACE, "Searching cache: scopes match\n" ));
  1110. if ( (wcsSort.GetPointer() != 0) &&
  1111. (pItem->GetSort() != 0 ) &&
  1112. (_wcsicmp( wcsSort.GetPointer(), pItem->GetSort() ) != 0 )
  1113. )
  1114. {
  1115. ciGibDebugOut(( DEB_ITRACE,
  1116. "Searching cache: sorts DONT match: %ws != %ws\n",
  1117. wcsSort.GetPointer(),
  1118. pItem->GetSort() ));
  1119. continue;
  1120. }
  1121. cMatchedItems++;
  1122. ciGibDebugOut(( DEB_ITRACE, "Searching cache: sorts match\n" ));
  1123. if ( (wcsTemplate.GetPointer() != 0) &&
  1124. (_wcsicmp( wcsTemplate.GetPointer(), pItem->GetTemplate() ) != 0 )
  1125. )
  1126. {
  1127. ciGibDebugOut(( DEB_ITRACE,
  1128. "Searching cache: templates DONT match: %ws != %ws\n",
  1129. wcsTemplate.GetPointer(),
  1130. pItem->GetTemplate() ));
  1131. continue;
  1132. }
  1133. cMatchedItems++;
  1134. ciGibDebugOut(( DEB_ITRACE, "Searching cache: templates match\n" ));
  1135. if ( (wcsCatalog.GetPointer() != 0) &&
  1136. (_wcsicmp( wcsCatalog.GetPointer(), pItem->GetCatalog() ) != 0 )
  1137. )
  1138. {
  1139. ciGibDebugOut(( DEB_ITRACE,
  1140. "Searching cache: catalogs DONT match: %ws != %ws\n",
  1141. wcsCatalog.GetPointer(),
  1142. pItem->GetCatalog() ));
  1143. continue;
  1144. }
  1145. cMatchedItems++;
  1146. ciGibDebugOut(( DEB_ITRACE, "Searching cache: catalogs match\n" ));
  1147. if ( (wcsColumns.GetPointer() != 0) &&
  1148. (_wcsicmp( wcsColumns.GetPointer(), pItem->GetColumns() ) != 0 )
  1149. )
  1150. {
  1151. ciGibDebugOut(( DEB_ITRACE,
  1152. "Searching cache: catalogs DONT match: %ws != %ws\n",
  1153. wcsColumns.GetPointer(),
  1154. pItem->GetColumns() ));
  1155. continue;
  1156. }
  1157. cMatchedItems++;
  1158. ciGibDebugOut(( DEB_ITRACE, "Searching cache: output columns match\n" ));
  1159. if ( (wcsCiFlags.GetPointer() != 0) &&
  1160. (pItem->GetCiFlags() != 0) &&
  1161. (_wcsicmp( wcsCiFlags.GetPointer(), pItem->GetCiFlags() ) != 0 )
  1162. )
  1163. {
  1164. ciGibDebugOut(( DEB_ITRACE,
  1165. "Searching cache: CIFlags DONT match: %ws != %ws\n",
  1166. wcsCiFlags.GetPointer(),
  1167. pItem->GetCiFlags() ));
  1168. continue;
  1169. }
  1170. cMatchedItems++;
  1171. ciGibDebugOut(( DEB_ITRACE, "Searching cache: CiFlags columns match\n" ));
  1172. if ( (wcsForceUseCI.GetPointer() != 0) &&
  1173. (pItem->GetForceUseCI() != 0) &&
  1174. (_wcsicmp( wcsForceUseCI.GetPointer(), pItem->GetForceUseCI() ) != 0 )
  1175. )
  1176. {
  1177. ciGibDebugOut(( DEB_ITRACE,
  1178. "Searching cache: ForceUseCI DONT match: %ws != %ws\n",
  1179. wcsForceUseCI.GetPointer(),
  1180. pItem->GetForceUseCI() ));
  1181. continue;
  1182. }
  1183. cMatchedItems++;
  1184. ciGibDebugOut(( DEB_ITRACE, "Searching cache: ForceUseCI match\n" ));
  1185. if ( (wcsDeferTrimming.GetPointer() != 0) &&
  1186. (pItem->GetDeferTrimming() != 0) &&
  1187. (_wcsicmp( wcsDeferTrimming.GetPointer(), pItem->GetDeferTrimming() ) != 0 )
  1188. )
  1189. {
  1190. ciGibDebugOut(( DEB_ITRACE,
  1191. "Searching cache: CiDeferNonIndexedTrimming DONT match: %ws != %ws\n",
  1192. wcsDeferTrimming.GetPointer(),
  1193. pItem->GetDeferTrimming() ));
  1194. continue;
  1195. }
  1196. cMatchedItems++;
  1197. ciGibDebugOut(( DEB_ITRACE, "Searching cache: CiDeferNonIndexedTrimming match\n" ));
  1198. if ( lMaxRecordsInResultSet != pItem->GetMaxRecordsInResultSet() )
  1199. {
  1200. ciGibDebugOut(( DEB_ITRACE,
  1201. "Searching cache: MaxRecordsInResultSet DONT match: %d != %d\n",
  1202. lMaxRecordsInResultSet,
  1203. pItem->GetMaxRecordsInResultSet() ));
  1204. continue;
  1205. }
  1206. cMatchedItems++;
  1207. ciGibDebugOut(( DEB_ITRACE, "Searching cache: MaxRecordsInResultSet match\n" ));
  1208. if ( lFirstRowsInResultSet != pItem->GetFirstRowsInResultSet() )
  1209. {
  1210. ciGibDebugOut(( DEB_ITRACE,
  1211. "Searching cache: FirstRowsInResultSet DONT match: %d != %d\n",
  1212. lFirstRowsInResultSet,
  1213. pItem->GetFirstRowsInResultSet() ));
  1214. continue;
  1215. }
  1216. cMatchedItems++;
  1217. ciGibDebugOut(( DEB_ITRACE, "Searching cache: FirstRowsInResultSet match\n" ));
  1218. if ( lcid != pItem->GetLocale() )
  1219. {
  1220. ciGibDebugOut(( DEB_ITRACE,
  1221. "Searching cache: lcid DONT match: 0x%x != 0x%x\n",
  1222. lcid,
  1223. pItem->GetLocale() ));
  1224. continue;
  1225. }
  1226. cMatchedItems++;
  1227. ciGibDebugOut(( DEB_ITRACE, "Searching cache: lcid match\n" ));
  1228. if ( pItem->IsSequential() )
  1229. {
  1230. if ( pItem->LokGetRefCount() != 0 )
  1231. {
  1232. ciGibDebugOut(( DEB_ITRACE,
  1233. "Searching cache: Sequential query in use\n" ));
  1234. continue;
  1235. }
  1236. ULONG cwcValue;
  1237. ULONG ulHash = ISAPIVariableNameHash( ISAPI_CI_BOOKMARK );
  1238. WCHAR const * wcsBookmark = variableSet->GetStringValueRAW( ISAPI_CI_BOOKMARK,
  1239. ulHash,
  1240. outputFormat.GetReference(),
  1241. cwcValue );
  1242. if ( 0 != wcsBookmark )
  1243. {
  1244. CWQueryBookmark bookMark( wcsBookmark );
  1245. lFirstSequentialRecord += bookMark.GetRecordNumber();
  1246. }
  1247. ulHash = ISAPIVariableNameHash( ISAPI_CI_BOOKMARK_SKIP_COUNT );
  1248. WCHAR const * wcsBookmarkSkipCount = variableSet->GetStringValueRAW( ISAPI_CI_BOOKMARK_SKIP_COUNT,
  1249. ulHash,
  1250. outputFormat.GetReference(),
  1251. cwcValue );
  1252. if ( 0 != wcsBookmarkSkipCount )
  1253. {
  1254. lFirstSequentialRecord += IDQ_wtol( wcsBookmarkSkipCount );
  1255. }
  1256. }
  1257. //
  1258. // We've found a match after examining all of the parameters
  1259. // passed from the browser. Now verify conditions #6 - #8 above.
  1260. //
  1261. ciGibDebugOut(( DEB_ITRACE, "Matched %d out of %d parameters\n",
  1262. cMatchedItems,
  1263. pItem->GetReplaceableParameterCount() ));
  1264. if ( pItem->GetReplaceableParameterCount() > cMatchedItems )
  1265. {
  1266. continue;
  1267. }
  1268. if ( securityIdentity.IsEqual( pItem->GetSecurityIdentity() ) )
  1269. {
  1270. if ( pItem->IsSequential() &&
  1271. ( lFirstSequentialRecord != pItem->GetNextRecordNumber() )
  1272. )
  1273. {
  1274. continue;
  1275. }
  1276. if ( !pItem->LokIsCachedDataValid() )
  1277. {
  1278. continue;
  1279. }
  1280. //
  1281. // We've found a match. Move it to the front of the list and
  1282. // increment its refcount. We assume that a query once referenced
  1283. // will be referenced again, hence the move to the front of the
  1284. // list.
  1285. //
  1286. pItem->AddRef();
  1287. LokMoveToFront( pItem );
  1288. xMatchItem.Set( pItem );
  1289. break;
  1290. }
  1291. }
  1292. }
  1293. // ======================================
  1294. //
  1295. // If we got a match, then save away the parameters we've expanded. They
  1296. // will be used later as output parameters.
  1297. //
  1298. // Setting ISAPI_CI_MAX_RECORDS_IN_RESULTSET can fail, so we'd
  1299. // leak an addref on the query item without the smart pointer.
  1300. if ( !xMatchItem.IsNull() )
  1301. {
  1302. if ( 0 != wcsRestriction.GetPointer() )
  1303. {
  1304. variableSet->AcquireStringValue( ISAPI_CI_RESTRICTION, wcsRestriction.GetPointer(), 0 );
  1305. wcsRestriction.Acquire();
  1306. }
  1307. if ( 0 != wcsScope.GetPointer() )
  1308. {
  1309. variableSet->AcquireStringValue( ISAPI_CI_SCOPE, wcsScope.GetPointer(), 0 );
  1310. wcsScope.Acquire();
  1311. }
  1312. if ( 0 != wcsSort.GetPointer() )
  1313. {
  1314. variableSet->AcquireStringValue( ISAPI_CI_SORT, wcsSort.GetPointer(), 0 );
  1315. wcsSort.Acquire();
  1316. }
  1317. if ( 0 != wcsTemplate.GetPointer() )
  1318. {
  1319. variableSet->AcquireStringValue( ISAPI_CI_TEMPLATE, wcsTemplate.GetPointer(), 0 );
  1320. wcsTemplate.Acquire();
  1321. }
  1322. if ( 0 != wcsCatalog.GetPointer() )
  1323. {
  1324. variableSet->AcquireStringValue( ISAPI_CI_CATALOG, wcsCatalog.GetPointer(), 0 );
  1325. wcsCatalog.Acquire();
  1326. }
  1327. if ( 0 != wcsColumns.GetPointer() )
  1328. {
  1329. variableSet->AcquireStringValue( ISAPI_CI_COLUMNS, wcsColumns.GetPointer(), 0 );
  1330. wcsColumns.Acquire();
  1331. }
  1332. if ( 0 != wcsCiFlags.GetPointer() )
  1333. {
  1334. variableSet->AcquireStringValue( ISAPI_CI_FLAGS, wcsCiFlags.GetPointer(), 0 );
  1335. wcsCiFlags.Acquire();
  1336. }
  1337. if ( 0 != wcsForceUseCI.GetPointer() )
  1338. {
  1339. variableSet->AcquireStringValue( ISAPI_CI_FORCE_USE_CI, wcsForceUseCI.GetPointer(), 0 );
  1340. wcsForceUseCI.Acquire();
  1341. }
  1342. if ( 0 != wcsDeferTrimming.GetPointer() )
  1343. {
  1344. variableSet->AcquireStringValue( ISAPI_CI_DEFER_NONINDEXED_TRIMMING, wcsDeferTrimming.GetPointer(), 0 );
  1345. wcsDeferTrimming.Acquire();
  1346. }
  1347. PROPVARIANT propVariant;
  1348. propVariant.vt = VT_I4;
  1349. propVariant.lVal = lMaxRecordsInResultSet;
  1350. variableSet->SetVariable( ISAPI_CI_MAX_RECORDS_IN_RESULTSET, &propVariant, 0 );
  1351. PROPVARIANT propVar;
  1352. propVar.vt = VT_I4;
  1353. propVar.lVal = lFirstRowsInResultSet;
  1354. variableSet->SetVariable( ISAPI_CI_FIRST_ROWS_IN_RESULTSET, &propVar, 0 );
  1355. }
  1356. return xMatchItem.Acquire();
  1357. } //FindItem
  1358. //+---------------------------------------------------------------------------
  1359. //
  1360. // Member: CWQueryCache::DeleteOldQueries - public
  1361. //
  1362. // Synopsis: If we haven't checked the query list for at least the maximum
  1363. // time an unused query is allowed to remain in the list, walk
  1364. // the query item list and delete all items whose last access
  1365. // time is greater than the purge time.
  1366. //
  1367. // History: 96-Jan-18 DwightKr Created
  1368. //
  1369. //----------------------------------------------------------------------------
  1370. void CWQueryCache::DeleteOldQueries()
  1371. {
  1372. time_t ttNow = time(0);
  1373. time_t oldestAllowableTime = ttNow - (60 * TheIDQRegParams.GetISCachePurgeInterval());
  1374. //
  1375. // Don't free the queries under lock. It can take a long long time.
  1376. // We don't need an allocation here; delete at most 40 queries.
  1377. //
  1378. //
  1379. // NOTE: the code below can't throw or we'll leak queries!
  1380. //
  1381. const int cAtMost = 40;
  1382. CWQueryItem * aItems[ cAtMost ];
  1383. int iQueries = 0;
  1384. {
  1385. CLock lock( _mutex );
  1386. CWQueryCacheForwardIter iter( *this );
  1387. while ( !AtEnd( iter ) && iQueries < cAtMost )
  1388. {
  1389. //
  1390. // If no one is using this query, and it is old, delete it now.
  1391. //
  1392. CWQueryItem * pItem = iter.Get();
  1393. if ( pItem->LokGetRefCount() == 0 &&
  1394. ( pItem->LokGetLastAccessTime() < oldestAllowableTime ||
  1395. pItem->IsZombie() ) )
  1396. {
  1397. Advance( iter );
  1398. pItem->Unlink();
  1399. aItems[ iQueries++ ] = pItem;
  1400. Win4Assert( *_pcCacheItems > 0 );
  1401. (*_pcCacheItems)--;
  1402. ciGibDebugOut(( DEB_ITRACE,
  1403. "Removing an expired item from the cache, %d queries cached\n",
  1404. *_pcCacheItems ));
  1405. }
  1406. else
  1407. {
  1408. Advance( iter );
  1409. }
  1410. }
  1411. }
  1412. for ( int i = 0; i < iQueries; i++ )
  1413. Remove( aItems[ i ] );
  1414. } //DeleteOldQueries
  1415. //+---------------------------------------------------------------------------
  1416. //
  1417. // Member: CWQueryCache::LokMoveToFront - public
  1418. //
  1419. // Arguments: [pItem] - the CWQueryItem to move to the front the of list
  1420. //
  1421. // Synopsis: Moves a query item to the front of the list. This routine
  1422. // is called whenever a query object is accessed.
  1423. //
  1424. // History: 96-Jan-18 DwightKr Created
  1425. // 96-Feb-20 DwightKr Remove time reset
  1426. //
  1427. //----------------------------------------------------------------------------
  1428. void CWQueryCache::LokMoveToFront( CWQueryItem * pItem )
  1429. {
  1430. Win4Assert( pItem != 0 );
  1431. pItem->Unlink();
  1432. Push(pItem);
  1433. }
  1434. //+---------------------------------------------------------------------------
  1435. //
  1436. // Member: CWQueryCache::CreateNewQuery - private
  1437. //
  1438. // Synopsis: Creates a new query and adds it to the linked list of items.
  1439. //
  1440. // Arguments: [wcsIDQFile] - name of the IDQ file referenced by this query
  1441. // [variableSet] - variables used to create this new query
  1442. // [outputFormat] - format of numbers & dates
  1443. // [securityIdentity] - security context of this query
  1444. //
  1445. // Returns: a new CWQueryItem, fully constructed with the query results
  1446. // already cached & the item added to the linked list of
  1447. // cached queries ONLY if this is a non-sequential query.
  1448. //
  1449. // History: 18-Jan-96 DwightKr Created
  1450. // 11-Jun-97 KyleP Use web server from output format
  1451. //
  1452. //----------------------------------------------------------------------------
  1453. CWQueryItem * CWQueryCache::CreateNewQuery( WCHAR const * wcsIDQFile,
  1454. XPtr<CVariableSet> & variableSet,
  1455. XPtr<COutputFormat> & outputFormat,
  1456. CSecurityIdentity securityIdentity,
  1457. BOOL & fAsynchronous )
  1458. {
  1459. LONG lFirstRecordNumber = 1;
  1460. CVariable *pVariable = variableSet->Find( ISAPI_CI_FIRST_RECORD_NUMBER );
  1461. if ( 0 != pVariable )
  1462. {
  1463. lFirstRecordNumber = IDQ_wtol( pVariable->GetStringValueRAW() );
  1464. }
  1465. //
  1466. // Attempt to find a parsed version of the IDQ file in the IDQ file
  1467. // list.
  1468. //
  1469. CIDQFile * pIdqFile = _idqFileList.Find( wcsIDQFile,
  1470. outputFormat->CodePage(),
  1471. securityIdentity );
  1472. XInterface<CIDQFile> xIDQFile( pIdqFile );
  1473. //
  1474. // Did we parse the IDQ file with the correct local & code page? Check
  1475. // to see if we used the wrong one. We attempted to open it with the locale
  1476. // and code page specified by the browser. Determine if the IDQ file
  1477. // overrides this value.
  1478. //
  1479. XArray<WCHAR> wcsLocale;
  1480. LCID locale = GetQueryLocale( pIdqFile->GetLocale(),
  1481. variableSet.GetReference(),
  1482. outputFormat.GetReference(),
  1483. wcsLocale );
  1484. Win4Assert( !pIdqFile->IsCanonicalOutput() );
  1485. if ( outputFormat->GetLCID() != locale )
  1486. {
  1487. ciGibDebugOut(( DEB_ITRACE,
  1488. "Wrong codePage used for loading IDQ file, used 0x%x retrying with 0x%x\n",
  1489. outputFormat->CodePage(),
  1490. LocaleToCodepage(locale) ));
  1491. //
  1492. // We've parsed the IDQ file with the wrong locale.
  1493. //
  1494. _idqFileList.Release( *(xIDQFile.Acquire()) );
  1495. outputFormat->LoadNumberFormatInfo( locale );
  1496. pIdqFile = _idqFileList.Find( wcsIDQFile,
  1497. outputFormat->CodePage(),
  1498. securityIdentity );
  1499. xIDQFile.Set( pIdqFile );
  1500. }
  1501. SetupDefaultCiVariables( variableSet.GetReference() );
  1502. SetupDefaultISAPIVariables( variableSet.GetReference() );
  1503. //
  1504. // Determine which output columns this IDQ file uses
  1505. //
  1506. ULONG cwcOut;
  1507. XPtrST<WCHAR> wcsColumns( ReplaceParameters( pIdqFile->GetColumns(),
  1508. variableSet.GetReference(),
  1509. outputFormat.GetReference(),
  1510. cwcOut ) );
  1511. CDynArray<WCHAR> awcsColumns;
  1512. XPtr<CDbColumns> dbColumns( pIdqFile->ParseColumns( wcsColumns.GetPointer(),
  1513. variableSet.GetReference(),
  1514. awcsColumns ) );
  1515. //
  1516. // Attempt to find a parsed version of the HTX file in the HTX file
  1517. // list.
  1518. //
  1519. Win4Assert( !pIdqFile->IsCanonicalOutput() );
  1520. CHTXFile & htxFile = _htxFileList.Find( pIdqFile->GetHTXFileName(),
  1521. variableSet.GetReference(),
  1522. outputFormat.GetReference(),
  1523. securityIdentity,
  1524. outputFormat->GetServerInstance() );
  1525. XInterface<CHTXFile> xHTXFile( &htxFile );
  1526. CWQueryItem *pNewItem = new CWQueryItem( *pIdqFile,
  1527. htxFile,
  1528. wcsColumns,
  1529. dbColumns,
  1530. awcsColumns,
  1531. GetNextSequenceNumber(),
  1532. lFirstRecordNumber,
  1533. securityIdentity );
  1534. XPtr<CWQueryItem> xNewItem( pNewItem);
  1535. xIDQFile.Acquire();
  1536. xHTXFile.Acquire();
  1537. pNewItem->ExecuteQuery( variableSet.GetReference(),
  1538. outputFormat.GetReference() );
  1539. if ( xNewItem->IsSequential() || xNewItem->IsQueryDone() )
  1540. {
  1541. AddToCache( xNewItem.GetPointer() );
  1542. fAsynchronous = FALSE;
  1543. ciGibDebugOut(( DEB_ITRACE, "Creating a synchronous web query\n" ));
  1544. }
  1545. else
  1546. {
  1547. {
  1548. // ==========================================
  1549. CLock lock( _mutex );
  1550. if ( fTheActiveXSearchShutdown )
  1551. THROW( CException(STATUS_TOO_LATE) );
  1552. // xNewItem is acquired in CWPendingQueryItem's constructor
  1553. CWPendingQueryItem * pItem = new CWPendingQueryItem( xNewItem,
  1554. outputFormat,
  1555. variableSet );
  1556. // _pendingQueue's array has been pre-allocated to a large size,
  1557. // so it can't fail. Even if it could fail we don't want to
  1558. // put CWPendingQueryItem in an xptr since the webServer may
  1559. // be released twice on failure.
  1560. _pendingQueue.Add( pItem, _pendingQueue.Count() );
  1561. IncrementRunningQueries();
  1562. // ==========================================
  1563. }
  1564. fAsynchronous = TRUE;
  1565. ciGibDebugOut(( DEB_ITRACE, "Creating an asynchronous web query\n" ));
  1566. Wakeup(); // wake up thread to check if the query is completed
  1567. }
  1568. Win4Assert( ( fAsynchronous && xNewItem.IsNull() ) ||
  1569. ( !fAsynchronous && !xNewItem.IsNull() ) );
  1570. return xNewItem.Acquire();
  1571. } //CreateNewQuery
  1572. //+---------------------------------------------------------------------------
  1573. //
  1574. // Member: CWQueryCache::AddToCache - public
  1575. //
  1576. // Arguments: [pNewItem] - Item to add to cache
  1577. //
  1578. // History: 96-Mar-04 DwightKr Created
  1579. //
  1580. //----------------------------------------------------------------------------
  1581. void CWQueryCache::AddToCache( CWQueryItem * pNewItem )
  1582. {
  1583. // This assert can hit if the user has just lowered
  1584. // IsapiMaxEntriesInQueryCache and the cache was full and a new query
  1585. // was just issued and the cache has not yet been reduced.
  1586. //Win4Assert ( *_pcCacheItems <= TheIDQRegParams.GetMaxISQueryCache() );
  1587. //
  1588. // If we already have too many query items in the cache, try to
  1589. // delete some.
  1590. //
  1591. CWQueryItem * pItemToDelete = 0;
  1592. // ==========================================
  1593. if ( pNewItem->CanCache() )
  1594. {
  1595. CLock lock( _mutex );
  1596. CWQueryCacheBackwardIter iter(*this);
  1597. while ( *_pcCacheItems >= TheIDQRegParams.GetMaxISQueryCache() &&
  1598. !AtEnd(iter) )
  1599. {
  1600. ciGibDebugOut(( DEB_ITRACE, "Too many items in cache, attempting to delete one\n" ));
  1601. CWQueryItem * pItem = iter.Get();
  1602. if ( pItem->LokGetRefCount() == 0 )
  1603. {
  1604. pItemToDelete = pItem;
  1605. pItemToDelete->Unlink();
  1606. Win4Assert( *_pcCacheItems > 0 );
  1607. (*_pcCacheItems)--;
  1608. break;
  1609. }
  1610. else
  1611. {
  1612. BackUp(iter);
  1613. }
  1614. }
  1615. }
  1616. if ( 0 != pItemToDelete )
  1617. {
  1618. Remove( pItemToDelete );
  1619. }
  1620. // If we STILL have too many queries in the cache, we couldn't delete
  1621. // any because they were all in use.
  1622. //
  1623. pNewItem->AddRef();
  1624. if ( 0 != TheIDQRegParams.GetMaxISQueryCache() )
  1625. {
  1626. // ==========================================
  1627. CLock lock( _mutex );
  1628. //
  1629. // If we're shutting down, don't attempt to put anything in the cache.
  1630. //
  1631. if ( *_pcCacheItems < TheIDQRegParams.GetMaxISQueryCache() &&
  1632. pNewItem->CanCache() &&
  1633. !fTheActiveXSearchShutdown )
  1634. {
  1635. Push( pNewItem );
  1636. pNewItem->InCache();
  1637. (*_pcCacheItems)++;
  1638. }
  1639. else
  1640. {
  1641. ciGibDebugOut(( DEB_ITRACE, "Still too many items in cache, creating non-cached query\n" ));
  1642. }
  1643. // ==========================================
  1644. }
  1645. }
  1646. //+---------------------------------------------------------------------------
  1647. //
  1648. // Member: CWQueryCache::Remove - public
  1649. //
  1650. // Arguments: [pItem] - Item to remove from cache
  1651. //
  1652. // Synopsis: Removes from cache and releases IDQ & HTX files.
  1653. //
  1654. // History: 96-Mar-28 DwightKr Created
  1655. //
  1656. //----------------------------------------------------------------------------
  1657. void CWQueryCache::Remove( CWQueryItem * pItem )
  1658. {
  1659. Win4Assert( 0 != pItem );
  1660. delete pItem;
  1661. } //Remove
  1662. //+---------------------------------------------------------------------------
  1663. //
  1664. // Member: CWQueryCache::Release - public
  1665. //
  1666. // Arguments: [pItem] -- Item to release - return to cache
  1667. // [fDecRunningQueries] -- If true, the count of running
  1668. // queries should be decremented.
  1669. //
  1670. // Synopsis: Decrements the refcount, and attempts to add it to the
  1671. // cache if it's not already there.
  1672. //
  1673. // History: 96-Jan-18 DwightKr Created
  1674. //
  1675. //----------------------------------------------------------------------------
  1676. void CWQueryCache::Release( CWQueryItem * pItem, BOOL fDecRunningQueries )
  1677. {
  1678. if ( 0 != pItem )
  1679. {
  1680. pItem->Release();
  1681. if ( fDecRunningQueries )
  1682. DecrementRunningQueries();
  1683. //
  1684. // The item may not be in the cache, because the cache was full
  1685. // at the time the query was created.
  1686. //
  1687. if ( ! pItem->IsInCache() )
  1688. {
  1689. //
  1690. // Don't attempt to add the query item to the cache here. If
  1691. // the add operation throws, we won't release the refcount
  1692. // on the idq & htx files.
  1693. //
  1694. Remove( pItem );
  1695. }
  1696. }
  1697. } //Release
  1698. //+---------------------------------------------------------------------------
  1699. //
  1700. // Member: CWQueryCache::FlushCache - public
  1701. //
  1702. // Synopsis: Flushes the cache .. waits until the cache is empty
  1703. //
  1704. // History: 96-Jan-18 DwightKr Created
  1705. //
  1706. //----------------------------------------------------------------------------
  1707. void CWQueryCache::FlushCache()
  1708. {
  1709. //
  1710. // Delete each of the pending asynchronous queries. Take the lock so
  1711. // that the worker thread can't wake up and start processing one of
  1712. // these items while we're deleting it.
  1713. //
  1714. {
  1715. // ==========================================
  1716. CLock shutdownLock( _mutexShutdown );
  1717. CLock lock( _mutex );
  1718. for ( unsigned i=0; i<_pendingQueue.Count(); i++ )
  1719. {
  1720. delete _pendingQueue.Get(i);
  1721. }
  1722. _pendingQueue.Clear();
  1723. Win4Assert( _pendingQueue.Count() == 0 );
  1724. // ==========================================
  1725. }
  1726. //
  1727. // Wait for each of the cached queries to be deleted. We many have to
  1728. // sleep for a bit to allow a thread to write the results of an
  1729. // active query.
  1730. //
  1731. while ( *_pcCacheItems > 0 )
  1732. {
  1733. ciGibDebugOut(( DEB_ITRACE, "Flushing the cache\n" ));
  1734. {
  1735. // ==========================================
  1736. CLock lock( _mutex );
  1737. CWQueryCacheForwardIter iter(*this);
  1738. while ( !AtEnd(iter) )
  1739. {
  1740. CWQueryItem * pItem = iter.Get();
  1741. if ( pItem->LokGetRefCount() == 0 )
  1742. {
  1743. Advance(iter);
  1744. pItem->Unlink();
  1745. Remove( pItem );
  1746. Win4Assert( *_pcCacheItems > 0 );
  1747. (*_pcCacheItems)--;
  1748. }
  1749. else
  1750. {
  1751. Advance(iter);
  1752. }
  1753. }
  1754. // ==========================================
  1755. }
  1756. //
  1757. // If there are more items to delete, release the lock, wait a
  1758. // bit then try again.
  1759. //
  1760. if ( *_pcCacheItems > 0 )
  1761. {
  1762. ciGibDebugOut(( DEB_ITRACE, "CWQueryCache::FlushCache waiting for queries to complete\n" ));
  1763. Sleep(1000);
  1764. }
  1765. }
  1766. }
  1767. //+---------------------------------------------------------------------------
  1768. //----------------------------------------------------------------------------
  1769. void SetupDefaultCiVariables( CVariableSet & variableSet )
  1770. {
  1771. //
  1772. // Setup the default Ci variables
  1773. //
  1774. for ( unsigned i=0; i<cCiGlobalVars; i++)
  1775. {
  1776. if ( variableSet.Find( aCiGlobalVars[i].wcsVariableName ) == 0 )
  1777. {
  1778. PROPVARIANT Variant;
  1779. Variant.vt = aCiGlobalVars[i].vt;
  1780. Variant.uhVal.QuadPart = aCiGlobalVars[i].i64DefaultValue;
  1781. variableSet.SetVariable( aCiGlobalVars[i].wcsVariableName,
  1782. &Variant,
  1783. aCiGlobalVars[i].flags);
  1784. }
  1785. }
  1786. }
  1787. //+---------------------------------------------------------------------------
  1788. //----------------------------------------------------------------------------
  1789. void SetupDefaultISAPIVariables( CVariableSet & variableSet )
  1790. {
  1791. //
  1792. // Setup the default ISAPI variables
  1793. //
  1794. for ( unsigned i=0; i<cISAPIGlobalVars; i++)
  1795. {
  1796. if ( variableSet.Find( aISAPIGlobalVars[i].wcsVariableName ) == 0 )
  1797. {
  1798. PROPVARIANT Variant;
  1799. Variant.vt = aISAPIGlobalVars[i].vt;
  1800. Variant.uhVal.QuadPart = aISAPIGlobalVars[i].i64DefaultValue;
  1801. variableSet.SetVariable( aISAPIGlobalVars[i].wcsVariableName,
  1802. &Variant,
  1803. aISAPIGlobalVars[i].flags);
  1804. }
  1805. }
  1806. }
  1807. #if (DBG == 1)
  1808. //+---------------------------------------------------------------------------
  1809. //
  1810. // Member: CWQueryCache::Dump - public
  1811. //
  1812. // Arguments: [string] - buffer to send results to
  1813. // [variableSet] - replaceable parameters
  1814. // [outputFormat] - format of numbers & dates
  1815. //
  1816. // Synopsis: Dumps the state of all queries
  1817. //
  1818. // Notes: The variableSet and outputFormat are for the
  1819. // current request, i.e., the !dump command, not
  1820. // for the individual queries in the cache.
  1821. //
  1822. // History: 96-Jan-18 DwightKr Created
  1823. //
  1824. //----------------------------------------------------------------------------
  1825. WCHAR wcsDumpBuffer[500];
  1826. void CWQueryCache::Dump( CVirtualString & string,
  1827. CVariableSet & variableSet,
  1828. COutputFormat & outputFormat )
  1829. {
  1830. // ==========================================
  1831. CLock lock( _mutex );
  1832. string.StrCat( L"<H1>Dump of query cache</H1><P>"
  1833. // L"<A HREF=\"#stats\">Cache statistics</A><BR>"
  1834. // L"<A HREF=\"#pending\">Pending queries</A><BR>"
  1835. // L"<A HREF=\"#cache\">Cached queries</A><BR>"
  1836. L"<P><H2><A NAME=stats>Cache statistics</H2><P>\n" );
  1837. ULONG cwcDumpBuffer = swprintf( wcsDumpBuffer,
  1838. L"Unique queries since service startup: %d<BR>"
  1839. L"Number of items in cache: %d<BR>"
  1840. L"Number of cache hits: %d<BR>"
  1841. L"Number of cache misses: %d<BR>"
  1842. L"Number of cache hits and misses: %d<BR>"
  1843. L"Number of running queries: %d<BR>"
  1844. L"Number of queries run thus far: %d<BR>\n",
  1845. _ulSequenceNumber,
  1846. *_pcCacheItems,
  1847. *_pcCacheHits,
  1848. *_pcCacheMisses,
  1849. *_pcCacheHitsAndMisses,
  1850. *_pcRunningQueries,
  1851. *_pcTotalQueries );
  1852. string.StrCat( wcsDumpBuffer, cwcDumpBuffer );
  1853. string.StrCat( L"<H2><A NAME=pending>Pending Queries</H2><P>" );
  1854. //
  1855. // Dump the pending query queue
  1856. //
  1857. for (unsigned i=0;
  1858. i<_pendingQueue.Count();
  1859. i++ )
  1860. {
  1861. if ( _pendingQueue[i] )
  1862. {
  1863. cwcDumpBuffer = swprintf( wcsDumpBuffer,
  1864. L"<P>\n<H3>Pending query # %d contents:</H3><P>\n",
  1865. i+1 );
  1866. string.StrCat( wcsDumpBuffer, cwcDumpBuffer );
  1867. _pendingQueue[i]->LokDump( string /*, variableSet, outputFormat */);
  1868. }
  1869. }
  1870. string.StrCat( L"<H2><HREF NAME=cache>Cached Queries</H2><P>" );
  1871. i = 1;
  1872. for ( CWQueryCacheForwardIter iter(*this);
  1873. !AtEnd(iter);
  1874. Advance(iter), i++ )
  1875. {
  1876. cwcDumpBuffer = swprintf( wcsDumpBuffer,
  1877. L"<P>\n<H3>Cached query # %d contents:</H3><P>\n",
  1878. i );
  1879. string.StrCat( wcsDumpBuffer, cwcDumpBuffer );
  1880. iter.Get()->LokDump( string /*, variableSet, outputFormat */);
  1881. }
  1882. // ==========================================
  1883. }
  1884. //+---------------------------------------------------------------------------
  1885. //
  1886. // Member: CWQueryCache::Internal - public
  1887. //
  1888. // Arguments: [variableSet] - variable containing command to execute
  1889. // [outputFormat] - format of numbers & dates
  1890. // [string] - buffer to send results to
  1891. //
  1892. // Synopsis: Executes one of a number of internal commands to the
  1893. // query cache.
  1894. //
  1895. // History: 96-Jan-18 DwightKr Created
  1896. // 96-Fen-21 DwightKr Added help
  1897. //
  1898. //----------------------------------------------------------------------------
  1899. BOOL CWQueryCache::Internal( CVariableSet & variableSet,
  1900. COutputFormat & outputFormat,
  1901. CVirtualString & string )
  1902. {
  1903. CVariable * pVarRestriction = variableSet.Find(ISAPI_CI_RESTRICTION );
  1904. if ( (0 != pVarRestriction) && (0 != pVarRestriction->GetStringValueRAW()) )
  1905. {
  1906. if (_wcsicmp( pVarRestriction->GetStringValueRAW(), L"!dump") == 0 )
  1907. {
  1908. string.StrCat( L"<HEAD><TITLE>Dump of query cache</TITLE></HEADER>" );
  1909. Dump( string, variableSet, outputFormat );
  1910. return TRUE;
  1911. }
  1912. else if (_wcsicmp( pVarRestriction->GetStringValueRAW(), L"!flush") == 0 )
  1913. {
  1914. string.StrCat( L"<HEAD><TITLE>Cache flush</TITLE></HEADER>Flushing cache...<BR>\n" );
  1915. FlushCache();
  1916. string.StrCat( L"Flush complete<BR>\n" );
  1917. return TRUE;
  1918. }
  1919. else if (_wcsicmp( pVarRestriction->GetStringValueRAW(), L"!?") == 0 )
  1920. {
  1921. string.StrCat( L"<HEAD><TITLE>Help</TITLE></HEADER>" );
  1922. string.StrCat( L"Avaiable commands:<BR>\n");
  1923. string.StrCat( L"!dump - dumps contents of the query cache<BR>\n" );
  1924. string.StrCat( L"!flush - empties the query cache<BR>\n" );
  1925. string.StrCat( L"!? - help (this page)<BR>\n" );
  1926. return TRUE;
  1927. }
  1928. }
  1929. return FALSE;
  1930. }
  1931. #endif // DBG == 1
  1932. //+---------------------------------------------------------------------------
  1933. //
  1934. // Member: CWQueryCache::AddToPendingRequestQueue - public
  1935. //
  1936. // Synopsis: Adds the ECB to the pending queue, if we're not shutting down
  1937. //
  1938. // History: 96-May-22 DwightKr Created
  1939. //
  1940. //----------------------------------------------------------------------------
  1941. BOOL CWQueryCache::AddToPendingRequestQueue( EXTENSION_CONTROL_BLOCK *pEcb )
  1942. {
  1943. // Don't take the query cache lock here -- there is no reason to since
  1944. // reads are atomic and we don't want IIS to make a zillion threads.
  1945. if ( fTheActiveXSearchShutdown ||
  1946. TheWebPendingRequestQueue.IsFull( ) )
  1947. {
  1948. return FALSE;
  1949. }
  1950. TOKEN_STATISTICS TokenInformation;
  1951. HANDLE hToken = GetSecurityToken(TokenInformation);
  1952. //
  1953. // It must be an impersonation token, hence we must have a valid handle.
  1954. // Build a pending request using the ECB and the security token,
  1955. //
  1956. Win4Assert( INVALID_HANDLE_VALUE != hToken );
  1957. Win4Assert( TokenInformation.TokenType == TokenImpersonation );
  1958. CWebPendingItem item( pEcb, hToken );
  1959. //
  1960. // Add the request to the pending queue.
  1961. //
  1962. return TheWebPendingRequestQueue.Add( item );
  1963. }
  1964. //+---------------------------------------------------------------------------
  1965. //
  1966. // Member: CWQueryCache::Shutdown - public
  1967. //
  1968. // Synopsis: Stops and empties the query cache
  1969. //
  1970. // History: 96-May-22 DwightKr Created
  1971. //
  1972. //----------------------------------------------------------------------------
  1973. void CWQueryCache::Shutdown()
  1974. {
  1975. //
  1976. // First set the shutdown flag so that no queues will be added to after
  1977. // this point.
  1978. //
  1979. {
  1980. CLock lock( _mutex );
  1981. fTheActiveXSearchShutdown = TRUE;
  1982. }
  1983. FlushCache();
  1984. Wakeup(); // wake up thread & wait for death
  1985. WaitForSingleObject( _threadWatchDog.GetHandle(), INFINITE );
  1986. Win4Assert( IsEmpty() && "Query cache must be empty after flush" );
  1987. }
  1988. //+---------------------------------------------------------------------------
  1989. //
  1990. // Member: CICommandCache::CICommandCache, public
  1991. //
  1992. // Synopsis: Constructor for the ICommand cache
  1993. //
  1994. // History: 97-Feb-23 dlee Created
  1995. //
  1996. //----------------------------------------------------------------------------
  1997. CICommandCache::CICommandCache() : _ulSig( LONGSIG( 'c', 'i', 'c', 'c' ) )
  1998. {
  1999. //
  2000. // These registry params are taken at startup and ignored
  2001. // thereafter. This isn't an issue on big machines where the
  2002. // query cache is turned off, but we might want to fix it.
  2003. // Maybe someday, but IDQ is pretty much a dead technology.
  2004. //
  2005. unsigned cItems = TheIDQRegParams.GetMaxISQueryCache();
  2006. ULONG factor = TheIDQRegParams.GetISRequestThresholdFactor();
  2007. SYSTEM_INFO si;
  2008. GetSystemInfo( &si );
  2009. // # of threads allowed in idq + # pending queries + # queries in cache
  2010. cItems += ( 2 * si.dwNumberOfProcessors * factor );
  2011. _aItems.Init( cItems );
  2012. RtlZeroMemory( _aItems.GetPointer(), _aItems.SizeOf() );
  2013. const CLSID clsidCommandCreator = CLSID_CISimpleCommandCreator;
  2014. HRESULT hr = CoCreateInstance( clsidCommandCreator,
  2015. NULL,
  2016. CLSCTX_INPROC_SERVER,
  2017. IID_ISimpleCommandCreator,
  2018. xCmdCreator.GetQIPointer() );
  2019. if ( FAILED( hr ) )
  2020. THROW( CException() );
  2021. } //CICommandCache
  2022. //+---------------------------------------------------------------------------
  2023. //
  2024. // Member: CICommandCache::Make, public
  2025. //
  2026. // Synopsis: Returns an ICommand, either from the cache or by making one
  2027. //
  2028. // Arguments: [ppCommand] -- Where the ICommand is returned
  2029. // [depth] -- deep / shallow, etc.
  2030. // [pwcMachine] -- The machine
  2031. // [pwcCatalog] -- The catalog
  2032. // [pwcScope] -- The comma separated list of scopes
  2033. //
  2034. // History: 97-Feb-23 dlee Created
  2035. //
  2036. //----------------------------------------------------------------------------
  2037. SCODE CICommandCache::Make(
  2038. ICommand ** ppCommand,
  2039. DWORD depth,
  2040. WCHAR const * pwcMachine,
  2041. WCHAR const * pwcCatalog,
  2042. WCHAR const * pwcScope )
  2043. {
  2044. *ppCommand = 0;
  2045. // first look for an available item in the cache
  2046. {
  2047. CLock lock( _mutex );
  2048. for ( unsigned x = 0; x < _aItems.Count(); x++ )
  2049. {
  2050. CICommandItem & item = _aItems[ x ];
  2051. if ( ( !item.xCommand.IsNull() ) &&
  2052. ( !item.fInUse ) &&
  2053. ( depth == item.depth ) &&
  2054. ( !wcscmp( pwcMachine, item.aMachine.Get() ) ) &&
  2055. ( !wcscmp( pwcCatalog, item.aCatalog.Get() ) ) &&
  2056. ( !wcscmp( pwcScope, item.aScope.Get() ) ) )
  2057. {
  2058. ciGibDebugOut(( DEB_ITRACE, "reusing icommand from cache\n" ));
  2059. item.fInUse = TRUE;
  2060. *ppCommand = item.xCommand.GetPointer();
  2061. Win4Assert( 0 != *ppCommand );
  2062. return S_OK;
  2063. }
  2064. }
  2065. }
  2066. // not found in the cache -- make the item
  2067. ciGibDebugOut(( DEB_ITRACE, "creating icommand\n" ));
  2068. XInterface<ICommand> xCommand;
  2069. SCODE sc = ParseAndMake( xCommand.GetPPointer(),
  2070. depth,
  2071. pwcMachine,
  2072. pwcCatalog,
  2073. pwcScope );
  2074. if ( FAILED( sc ) )
  2075. return sc;
  2076. // can we put the item in the cache?
  2077. {
  2078. CLock lock( _mutex );
  2079. for ( unsigned x = 0; x < _aItems.Count(); x++ )
  2080. {
  2081. CICommandItem & item = _aItems[ x ];
  2082. if ( item.xCommand.IsNull() )
  2083. {
  2084. // First see if we can add it.
  2085. item.aMachine.ReSize( wcslen( pwcMachine ) + 1 );
  2086. wcscpy( item.aMachine.Get(), pwcMachine );
  2087. item.aCatalog.ReSize( wcslen( pwcCatalog ) + 1 );
  2088. wcscpy( item.aCatalog.Get(), pwcCatalog );
  2089. item.aScope.ReSize( wcslen( pwcScope ) + 1 );
  2090. wcscpy( item.aScope.Get(), pwcScope );
  2091. // Now mark it as owned
  2092. Win4Assert( !item.fInUse );
  2093. item.fInUse = TRUE;
  2094. item.xCommand.Set( xCommand.GetPointer() );
  2095. Win4Assert( 0 != item.xCommand.GetPointer() );
  2096. item.depth = depth;
  2097. break;
  2098. }
  2099. }
  2100. }
  2101. *ppCommand = xCommand.Acquire();
  2102. Win4Assert( 0 != *ppCommand );
  2103. return S_OK;
  2104. } //Make
  2105. //+---------------------------------------------------------------------------
  2106. //
  2107. // Member: CICommandCache::Release, public
  2108. //
  2109. // Synopsis: Releases the ICommand to the cache or to be freed
  2110. //
  2111. // Arguments: [pCommand] -- The ICommand to release.
  2112. //
  2113. // History: 97-Feb-23 dlee Created
  2114. //
  2115. //----------------------------------------------------------------------------
  2116. void CICommandCache::Release(
  2117. ICommand * pCommand )
  2118. {
  2119. {
  2120. CLock lock( _mutex );
  2121. // first see if it can be returned to the cache
  2122. for ( unsigned x = 0; x < _aItems.Count(); x++ )
  2123. {
  2124. CICommandItem & item = _aItems[ x ];
  2125. if ( item.xCommand.GetPointer() == pCommand )
  2126. {
  2127. Win4Assert( item.fInUse );
  2128. ciGibDebugOut(( DEB_ITRACE, "returning icommand to cache\n" ));
  2129. item.fInUse = FALSE;
  2130. return;
  2131. }
  2132. }
  2133. }
  2134. ciGibDebugOut(( DEB_ITRACE, "icommand not in cache, releasing\n" ));
  2135. // not in the cache -- just release it
  2136. pCommand->Release();
  2137. } //Release
  2138. //+---------------------------------------------------------------------------
  2139. //
  2140. // Member: CICommandCache::Remove, public
  2141. //
  2142. // Synopsis: Removes the item from the cache, likely because the ICommand
  2143. // is stale because cisvc went down.
  2144. //
  2145. // Arguments: [pCommand] -- The ICommand to release.
  2146. //
  2147. // History: 97-Feb-23 dlee Created
  2148. //
  2149. //----------------------------------------------------------------------------
  2150. void CICommandCache::Remove(
  2151. ICommand * pCommand )
  2152. {
  2153. {
  2154. CLock lock( _mutex );
  2155. // first see if it is in the cache
  2156. for ( unsigned x = 0; x < _aItems.Count(); x++ )
  2157. {
  2158. CICommandItem & item = _aItems[ x ];
  2159. if ( item.xCommand.GetPointer() == pCommand )
  2160. {
  2161. Win4Assert( item.fInUse );
  2162. item.xCommand.Acquire();
  2163. item.fInUse = FALSE;
  2164. break;
  2165. }
  2166. }
  2167. }
  2168. // not in the cache -- just release it
  2169. pCommand->Release();
  2170. } //Remove
  2171. //+---------------------------------------------------------------------------
  2172. //
  2173. // Member: CICommandCache::Purge, public
  2174. //
  2175. // Synopsis: Releases all ICommands not currently in use
  2176. //
  2177. // Arguments: [pCommand] -- The ICommand to release.
  2178. //
  2179. // History: 97-Feb-23 dlee Created
  2180. //
  2181. //----------------------------------------------------------------------------
  2182. void CICommandCache::Purge()
  2183. {
  2184. CLock lock( _mutex );
  2185. // Remove all non-used items from the cache.
  2186. for ( unsigned x = 0; x < _aItems.Count(); x++ )
  2187. {
  2188. CICommandItem & item = _aItems[ x ];
  2189. if ( ( !item.fInUse ) &&
  2190. ( !item.xCommand.IsNull() ) )
  2191. {
  2192. item.xCommand.Free();
  2193. }
  2194. }
  2195. } //Purge
  2196. //+---------------------------------------------------------------------------
  2197. //
  2198. // Function: IsAVirtualPath
  2199. //
  2200. // Synopsis: Determines if the path passed is a virtual or physical path.
  2201. // If it's a virtual path, then / are changed to \.
  2202. //
  2203. // History: 96-Feb-14 DwightKr Created
  2204. //
  2205. //----------------------------------------------------------------------------
  2206. BOOL IsAVirtualPath( WCHAR * wcsPath )
  2207. {
  2208. Win4Assert ( 0 != wcsPath );
  2209. if ( 0 == wcsPath[0] )
  2210. return TRUE;
  2211. if ( ( L':' == wcsPath[1] ) || ( L'\\' == wcsPath[0] ) )
  2212. {
  2213. return FALSE;
  2214. }
  2215. else
  2216. {
  2217. //
  2218. // Flip slashes to backslashes
  2219. //
  2220. for ( WCHAR *wcsLetter = wcsPath; 0 != *wcsLetter; wcsLetter++ )
  2221. {
  2222. if ( L'/' == *wcsLetter )
  2223. *wcsLetter = L'\\';
  2224. }
  2225. }
  2226. return TRUE;
  2227. } //IsAVirtualPath
  2228. //+---------------------------------------------------------------------------
  2229. //
  2230. // Function: ParseScopes
  2231. //
  2232. // Synopsis: Translates a string like:
  2233. // " /foo ,c:\bar , "/a b , /c " , j:\dog "
  2234. // into a multisz string like:
  2235. // "/foo0c:\bar0/a b , /c 0j:\dog00"
  2236. //
  2237. // Leading and trailing white space is removed unless the
  2238. // path is quoted, in which case you get exactly what you
  2239. // asked for even though it may be incorrect.
  2240. //
  2241. // Arguments: [pwcIn] -- the source string
  2242. // [pwcOut] -- the multisz result string, guaranteed to be
  2243. // no more than 1 WCHAR larger than pwcIn.
  2244. //
  2245. // History: 97-Jun-17 dlee Created
  2246. //
  2247. //----------------------------------------------------------------------------
  2248. ULONG ParseScopes(
  2249. WCHAR const * pwcIn,
  2250. WCHAR * pwcOut )
  2251. {
  2252. ULONG cScopes = 0;
  2253. while ( 0 != *pwcIn )
  2254. {
  2255. // eat space and commas
  2256. while ( L' ' == *pwcIn || L',' == *pwcIn )
  2257. pwcIn++;
  2258. if ( 0 == *pwcIn )
  2259. break;
  2260. // is this a quoted path?
  2261. if ( L'"' == *pwcIn )
  2262. {
  2263. pwcIn++;
  2264. while ( 0 != *pwcIn && L'"' != *pwcIn )
  2265. *pwcOut++ = *pwcIn++;
  2266. if ( L'"' != *pwcIn )
  2267. THROW( CIDQException( MSG_CI_IDQ_BAD_SCOPE_OR_CATALOG, 0 ) );
  2268. pwcIn++;
  2269. *pwcOut++ = 0;
  2270. }
  2271. else
  2272. {
  2273. while ( 0 != *pwcIn && L',' != *pwcIn )
  2274. *pwcOut++ = *pwcIn++;
  2275. // back up over trailing spaces
  2276. while ( L' ' == * (pwcOut - 1) )
  2277. pwcOut--;
  2278. *pwcOut++ = 0;
  2279. }
  2280. cScopes++;
  2281. }
  2282. if ( 0 == cScopes )
  2283. THROW( CIDQException( MSG_CI_IDQ_BAD_SCOPE_OR_CATALOG, 0 ) );
  2284. // end the string with a second null
  2285. *pwcOut = 0;
  2286. return cScopes;
  2287. } //ParseScopes
  2288. //+---------------------------------------------------------------------------
  2289. //
  2290. // Member: CICommandCache::ParseAndMake, private
  2291. //
  2292. // Synopsis: Parses parameters for an ICommand and creates one
  2293. //
  2294. // Arguments: [ppCommand] -- Where the ICommand is returned
  2295. // [depth] -- deep / shallow, etc.
  2296. // [pwcMachine] -- The machine
  2297. // [pwcCatalog] -- The catalog
  2298. // [pwcScope] -- The comma separated list of scopes
  2299. //
  2300. // History: 97-Feb-23 dlee Created
  2301. //
  2302. //----------------------------------------------------------------------------
  2303. SCODE CICommandCache::ParseAndMake(
  2304. ICommand ** ppCommand,
  2305. DWORD depth,
  2306. WCHAR const * pwcMachine,
  2307. WCHAR const * pwcCatalog,
  2308. WCHAR const * pwcScope )
  2309. {
  2310. Win4Assert(pwcMachine && pwcCatalog);
  2311. #if 0 // This is actually a bogus check. We don't care how long it is.
  2312. if ( wcslen( pwcMachine ) > MAX_COMPUTERNAME_LENGTH )
  2313. THROW( CIDQException( MSG_CI_IDQ_BAD_SCOPE_OR_CATALOG, 0 ) );
  2314. #endif
  2315. if ( wcslen( pwcCatalog ) > MAX_PATH )
  2316. THROW( CIDQException( MSG_CI_IDQ_BAD_SCOPE_OR_CATALOG, 0 ) );
  2317. IUnknown * pIUnknown;
  2318. XInterface<ICommand> xCmd;
  2319. if (0 == xCmdCreator.GetPointer())
  2320. return REGDB_E_CLASSNOTREG;
  2321. SCODE sc = xCmdCreator->CreateICommand(&pIUnknown, 0);
  2322. XInterface<IUnknown> xUnk( pIUnknown );
  2323. if ( SUCCEEDED (sc) )
  2324. {
  2325. sc = pIUnknown->QueryInterface(IID_ICommand, xCmd.GetQIPointer());
  2326. }
  2327. if (FAILED(sc))
  2328. return sc;
  2329. TRY
  2330. {
  2331. CDynArrayInPlace<DWORD> aDepths(2);
  2332. CDynArrayInPlace<WCHAR const *> aScopes(2);
  2333. CDynArrayInPlace<WCHAR const *> aMachines(2);
  2334. CDynArrayInPlace<WCHAR const *> aCatalogs(2);
  2335. // allocate +2 for two trailing nulls in the multisz string
  2336. ULONG cwcScope = 2 + wcslen( pwcScope );
  2337. XGrowable<WCHAR> aScope( cwcScope );
  2338. ULONG cScopes = ParseScopes( pwcScope, aScope.Get() );
  2339. Win4Assert( 0 != cScopes );
  2340. if ( cScopes > 1 )
  2341. {
  2342. // Add support for multiple catalogs, and/or machines,
  2343. // and/or depths. For now, all scopes share a single
  2344. // catalog/machine/depth. (though you can mix virtual and
  2345. // physical). Maybe someday, but IDQ is dead moving forward.
  2346. WCHAR *pwc = aScope.Get();
  2347. for ( ULONG iScope = 0; iScope < cScopes; iScope++ )
  2348. {
  2349. if ( wcslen( pwc ) >= MAX_PATH )
  2350. THROW( CIDQException( MSG_CI_IDQ_BAD_SCOPE_OR_CATALOG, 0 ) );
  2351. aDepths[iScope] = depth;
  2352. if ( IsAVirtualPath( pwc ) )
  2353. aDepths[iScope] |= QUERY_VIRTUAL_PATH;
  2354. aScopes[iScope] = pwc;
  2355. ciGibDebugOut(( DEB_ITRACE, "scope %d: flags 0x%x '%ws'\n",
  2356. iScope, aDepths[iScope], pwc ));
  2357. // pwc is a multi-sz string. Skip to the next scope
  2358. pwc += ( 1 + wcslen( pwc ) );
  2359. aMachines[iScope] = pwcMachine;
  2360. aCatalogs[iScope] = pwcCatalog;
  2361. }
  2362. }
  2363. else
  2364. {
  2365. aMachines[0] = pwcMachine;
  2366. aCatalogs[0] = pwcCatalog;
  2367. aDepths[0] = depth;
  2368. WCHAR *pwc = aScope.Get();
  2369. if ( IsAVirtualPath( pwc ) )
  2370. aDepths[0] |= QUERY_VIRTUAL_PATH;
  2371. aScopes[0] = pwc;
  2372. }
  2373. SetScopeProperties( xCmd.GetPointer(),
  2374. cScopes,
  2375. aScopes.GetPointer(),
  2376. aDepths.GetPointer(),
  2377. aCatalogs.GetPointer(),
  2378. aMachines.GetPointer() );
  2379. *ppCommand = xCmd.Acquire();
  2380. Win4Assert( 0 != *ppCommand );
  2381. }
  2382. CATCH ( CException, e )
  2383. {
  2384. sc = GetOleError(e);
  2385. }
  2386. END_CATCH
  2387. return sc;
  2388. } //ParseAndMake