Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

846 lines
22 KiB

  1. //
  2. // Author: DebiM
  3. // Date: January 97
  4. //
  5. //
  6. // Class Access Implementation
  7. //
  8. // This source file contains implementations for IClassAccess
  9. // interface on CAppContainer object.
  10. //
  11. // It uses ADs interfaces (over LDAP) to talk to an LDAP
  12. // provider such as NTDS.
  13. //
  14. //---------------------------------------------------------------------
  15. //
  16. #include "cstore.hxx"
  17. void
  18. GetDefaultPlatform(CSPLATFORM *pPlatform);
  19. BOOL
  20. CheckMatching (QUERYCONTEXT *pQryContext,
  21. INSTALLINFO *pInstallInfo,
  22. PLATFORMINFO *pPlatformInfo
  23. );
  24. extern LPOLESTR szInstallInfoColumns;
  25. extern LPOLESTR szPackageInfoColumns;
  26. void FreeInstallInfo(INSTALLINFO *pInstallInfo)
  27. {
  28. CoTaskMemFree(pInstallInfo->pszScriptPath);
  29. CoTaskMemFree(pInstallInfo->pszSetupCommand);
  30. CoTaskMemFree(pInstallInfo->pszUrl);
  31. CoTaskMemFree(pInstallInfo->pClsid);
  32. memset(pInstallInfo, 0, sizeof (INSTALLINFO));
  33. }
  34. void FreePlatformInfo(PLATFORMINFO *pPlatformInfo)
  35. {
  36. CoTaskMemFree(pPlatformInfo->prgLocale);
  37. CoTaskMemFree(pPlatformInfo->prgPlatform);
  38. }
  39. //
  40. // CAppContainer implementation
  41. //
  42. CAppContainer::CAppContainer()
  43. {
  44. m_fOpen = FALSE;
  45. m_ADsContainer = NULL;
  46. m_pADsClassStore = NULL;
  47. m_ADsClassContainer = NULL;
  48. m_ADsPackageContainer = NULL;
  49. m_uRefs = 1;
  50. StartQuery(&m_pIDBCreateCommand);
  51. }
  52. //
  53. // CAppContainer implementation
  54. //
  55. CAppContainer::CAppContainer(LPOLESTR szStoreName,
  56. HRESULT *phr)
  57. {
  58. IADs *pADs = NULL;
  59. LPOLESTR pszName = NULL;
  60. DWORD dwStoreVersion = 0;
  61. *phr = S_OK;
  62. #ifdef DBG
  63. WCHAR Name[32];
  64. DWORD NameSize = 32;
  65. if ( ! GetUserName( Name, &NameSize ) )
  66. CSDbgPrint(("CAppContainer::CAppContainer GetUserName failed 0x%x\n", GetLastError()));
  67. else
  68. CSDbgPrint(("CAppContainer::CAppContainer as %S\n", Name));
  69. #endif
  70. m_fOpen = FALSE;
  71. m_ADsContainer = NULL;
  72. m_pADsClassStore = NULL;
  73. m_ADsClassContainer = NULL;
  74. m_ADsPackageContainer = NULL;
  75. //
  76. // For every interface pointer, we create a separate session
  77. //
  78. StartQuery(&m_pIDBCreateCommand);
  79. // Bind to a Class Store Container Object
  80. // Cache the interface pointer
  81. //
  82. wcscpy (m_szContainerName, szStoreName);
  83. *phr = ADsGetObject(
  84. szStoreName,
  85. IID_IADsContainer,
  86. (void **)&m_ADsContainer
  87. );
  88. if (!SUCCEEDED(*phr))
  89. return;
  90. //
  91. // Check the Schema Version of this container
  92. //
  93. *phr = m_ADsContainer->QueryInterface (IID_IADs, (void **)&m_pADsClassStore);
  94. if (!SUCCEEDED(*phr))
  95. return;
  96. *phr = GetPropertyDW (m_pADsClassStore, STOREVERSION, &dwStoreVersion);
  97. if ((!SUCCEEDED(*phr)) ||
  98. (dwStoreVersion != SCHEMA_VERSION_NUMBER))
  99. {
  100. CSDbgPrint(("CS: .. Wrong Version of Class Container:%ws\n",
  101. m_szContainerName));
  102. *phr = CS_E_INVALID_VERSION;
  103. return;
  104. }
  105. CSDbgPrint(("CS: .. Connected to Class Container:%ws\n",
  106. m_szContainerName));
  107. //
  108. // Bind to the class container Object
  109. // Cache the interface pointer
  110. //
  111. m_ADsClassContainer = NULL;
  112. *phr = m_ADsContainer->GetObject(
  113. NULL,
  114. CLASSCONTAINERNAME,
  115. (IDispatch **)&pADs);
  116. if (!SUCCEEDED(*phr))
  117. return;
  118. pADs->QueryInterface(IID_IADsContainer,
  119. (void **)&m_ADsClassContainer
  120. );
  121. *phr = pADs->get_ADsPath(&pszName);
  122. wcscpy (m_szClassName, pszName);
  123. SysFreeString(pszName);
  124. pADs->Release();
  125. pADs = NULL;
  126. if (!SUCCEEDED(*phr))
  127. return;
  128. //
  129. // Bind to the Package container Object
  130. // Cache the interface pointer
  131. //
  132. m_ADsPackageContainer = NULL;
  133. *phr = m_ADsContainer->GetObject(
  134. NULL,
  135. PACKAGECONTAINERNAME,
  136. (IDispatch **)&pADs);
  137. if (!SUCCEEDED(*phr))
  138. return;
  139. pADs->QueryInterface(IID_IADsContainer,
  140. (void **)&m_ADsPackageContainer
  141. );
  142. *phr = pADs->get_ADsPath(&pszName);
  143. wcscpy (m_szPackageName, pszName);
  144. SysFreeString(pszName);
  145. pADs->Release();
  146. pADs = NULL;
  147. if (!SUCCEEDED(*phr))
  148. return;
  149. m_fOpen = TRUE;
  150. m_uRefs = 1;
  151. }
  152. CAppContainer::~CAppContainer(void)
  153. {
  154. UINT i;
  155. EndQuery(m_pIDBCreateCommand);
  156. m_pIDBCreateCommand = NULL;
  157. if (m_fOpen)
  158. {
  159. //UnlistDB (this);
  160. m_fOpen = FALSE;
  161. }
  162. if (m_ADsClassContainer)
  163. {
  164. m_ADsClassContainer->Release();
  165. m_ADsClassContainer = NULL;
  166. }
  167. if (m_ADsPackageContainer)
  168. {
  169. m_ADsPackageContainer->Release();
  170. m_ADsPackageContainer = NULL;
  171. }
  172. if (m_ADsContainer)
  173. {
  174. m_ADsContainer->Release();
  175. m_ADsContainer = NULL;
  176. }
  177. if (m_pADsClassStore)
  178. {
  179. m_pADsClassStore->Release();
  180. m_pADsClassStore = NULL;
  181. }
  182. }
  183. HRESULT CAppContainer::GetPackageDetails (
  184. LPOLESTR pszPackageName,
  185. PACKAGEDETAIL *pPackageDetail)
  186. {
  187. HRESULT hr = S_OK;
  188. IADs *pPackageADs = NULL;
  189. WCHAR szRdn [_MAX_PATH];
  190. wcscpy (szRdn, L"CN=");
  191. wcscat (szRdn, pszPackageName);
  192. hr = m_ADsPackageContainer->GetObject(NULL, szRdn, (IDispatch **)&pPackageADs);
  193. if (!SUCCEEDED(hr))
  194. return hr;
  195. hr = GetPackageDetail (pPackageADs, pPackageDetail);
  196. return hr;
  197. }
  198. //
  199. // ::EnumPackages
  200. //----------------
  201. //
  202. // Obtains an enumerator for packages in the app container.
  203. // Takes the following optional filter specifications:
  204. //
  205. // [in] pszPackageName : Substring match for a package name
  206. // [in] pCategory : Category id of interest
  207. // [in] dwAppFlags : following bits set to select specific ones
  208. // Published Only APPINFO_PUBLISHED
  209. // Assigned Only APPINFO_ASSIGNED
  210. // Msi Only APPINFO_MSI
  211. // Visible APPINFO_VISIBLE
  212. // Auto-Install APPINFO_AUTOINSTALL
  213. //
  214. // All Locale APPINFO_ALLLOCALE
  215. // All Platform APPINFO_ALLPLATFORM
  216. //
  217. //
  218. HRESULT CAppContainer::EnumPackages (
  219. LPOLESTR pszPackageName,
  220. GUID *pCategory,
  221. ULONGLONG *pLastUsn,
  222. DWORD dwAppFlags,
  223. IEnumPackage **ppIEnumPackage
  224. )
  225. {
  226. HRESULT hr;
  227. CEnumPackage *pEnum;
  228. WCHAR szLdapFilter [2000];
  229. UINT len;
  230. UINT fFilters = 0;
  231. LCID dwLocale, *pdwLocale;
  232. CSPLATFORM *pPlatform, Platform;
  233. if (!IsValidPtrOut(ppIEnumPackage, sizeof(IEnumPackage *)))
  234. return E_INVALIDARG;
  235. *ppIEnumPackage = NULL;
  236. pEnum = new CEnumPackage;
  237. if(NULL == pEnum)
  238. return E_OUTOFMEMORY;
  239. //
  240. // Create a LDAP Search Filter based on input params
  241. //
  242. // Count Filters
  243. if (pszPackageName && (*pszPackageName))
  244. fFilters++;
  245. if (pLastUsn)
  246. fFilters++;
  247. if (pCategory)
  248. fFilters++;
  249. if (dwAppFlags & APPINFO_ASSIGNED)
  250. fFilters++;
  251. if (fFilters == 0)
  252. {
  253. // No Conditionals
  254. wsprintf (szLdapFilter,
  255. L"<%s>;(objectClass=packageRegistration)",
  256. m_szPackageName);
  257. len = wcslen (szLdapFilter);
  258. }
  259. else
  260. {
  261. if (fFilters == 1)
  262. {
  263. wsprintf (szLdapFilter,
  264. L"<%s>;",
  265. m_szPackageName);
  266. }
  267. else
  268. {
  269. wsprintf (szLdapFilter,
  270. L"<%s>;(&",
  271. m_szPackageName);
  272. }
  273. len = wcslen (szLdapFilter);
  274. if (pszPackageName)
  275. {
  276. //
  277. // Validate
  278. //
  279. if (IsBadStringPtr(pszPackageName, _MAX_PATH))
  280. return E_INVALIDARG;
  281. if (*pszPackageName)
  282. {
  283. wsprintf (&szLdapFilter[len],
  284. L"(%s=*%s*)",
  285. PACKAGENAME,
  286. pszPackageName);
  287. len = wcslen (szLdapFilter);
  288. }
  289. }
  290. if (pLastUsn)
  291. {
  292. //
  293. // Validate
  294. //
  295. if (!IsValidReadPtrIn(pLastUsn, sizeof(ULONGLONG)))
  296. return E_INVALIDARG;
  297. wsprintf (&szLdapFilter[len],
  298. L"(%s>=%ul)",
  299. PKGUSN,
  300. *pLastUsn);
  301. len = wcslen (szLdapFilter);
  302. }
  303. if (pCategory)
  304. {
  305. //
  306. // Validate
  307. //
  308. STRINGGUID szCat;
  309. if (!IsValidReadPtrIn(pCategory, sizeof(GUID)))
  310. return E_INVALIDARG;
  311. StringFromGUID (*pCategory, szCat);
  312. wsprintf (&szLdapFilter[len],
  313. L"(%s=%s)",
  314. PKGCATEGORYLIST,
  315. szCat);
  316. len = wcslen (szLdapFilter);
  317. }
  318. if (dwAppFlags & APPINFO_ASSIGNED)
  319. // if only Assigned Packages are in demand
  320. {
  321. wsprintf (&szLdapFilter[len],
  322. L"(%s>=%d)",
  323. PACKAGEFLAGS,
  324. (ACTFLG_Assigned));
  325. len = wcslen (szLdapFilter);
  326. }
  327. if (fFilters > 1)
  328. {
  329. szLdapFilter[len] = L')';
  330. szLdapFilter[++len] = NULL;
  331. }
  332. }
  333. //
  334. // Now append the attribute list to the search string
  335. //
  336. wsprintf (&szLdapFilter[len], L";%s", szPackageInfoColumns);
  337. //
  338. // Check all local/platform flags
  339. //
  340. if (dwAppFlags & APPINFO_ALLLOCALE)
  341. {
  342. pdwLocale = NULL;
  343. }
  344. else
  345. {
  346. dwLocale = GetThreadLocale();
  347. pdwLocale = &dwLocale;
  348. }
  349. if (dwAppFlags & APPINFO_ALLPLATFORM)
  350. {
  351. pPlatform = NULL;
  352. }
  353. else
  354. {
  355. pPlatform = &Platform;
  356. GetDefaultPlatform(pPlatform);
  357. }
  358. hr = pEnum->Initialize(szLdapFilter, dwAppFlags, pdwLocale, pPlatform);
  359. if (FAILED(hr))
  360. {
  361. delete pEnum;
  362. return hr;
  363. }
  364. hr = pEnum->QueryInterface(IID_IEnumPackage,(void**) ppIEnumPackage);
  365. if (FAILED(hr))
  366. {
  367. delete pEnum;
  368. return hr;
  369. }
  370. return S_OK;
  371. }
  372. //
  373. // CAppContainer::GetAppInfo
  374. // -----------------------------
  375. //
  376. //
  377. //
  378. // Synopsis: This is the most common access point to the Class Store.
  379. // It receives a CLSID and a QUERYCONTEXT.
  380. // It looks up the Class Store container for a matching set
  381. // of packages with in the context of the QUERYCONTEXT.
  382. //
  383. // QUERYCONTEXT includes
  384. // Execution Context
  385. // Locale Id
  386. // Platform/OS
  387. //
  388. // If i finds an app that matches the requirements, it returns
  389. // an INSTALLINFO structure containing installation details.
  390. //
  391. // Arguments: [in] clsid
  392. // [in] pQryContext
  393. // [out] pInstallInfo
  394. //
  395. // Returns: CS_E_PACKAGE_NOTFOUND
  396. // S_OK
  397. //
  398. //
  399. //
  400. HRESULT STDMETHODCALLTYPE
  401. CAppContainer::GetAppInfo(
  402. uCLSSPEC * pclsspec, // Class Spec (GUID/Ext/MIME)
  403. QUERYCONTEXT * pQryContext, // Query Attributes
  404. INSTALLINFO * pInstallInfo
  405. )
  406. //
  407. // This is the most common method to access the Class Store.
  408. // It queries the class store for implementations for a specific
  409. // Class Id, or File Ext, or ProgID.
  410. //
  411. // If a matching implementation is available (for the object type,
  412. // client architecture, locale and class context) then the installation
  413. // parameters of the package is returned.
  414. {
  415. GUID clsid;
  416. WCHAR pszCommandText[1000];
  417. STRINGGUID szClsid;
  418. UINT i;
  419. ULONG cRead;
  420. HRESULT hr;
  421. ULONG cSize = _MAX_PATH;
  422. BOOL fFound = FALSE;
  423. PLATFORMINFO PlatformInfo;
  424. LPOLESTR pFileExt = NULL;
  425. memset(pInstallInfo, 0, sizeof(INSTALLINFO));
  426. if (!m_fOpen)
  427. return E_FAIL;
  428. //
  429. // Check if the TypeSpec is MIMEType
  430. // then map it to a CLSID
  431. //
  432. if (pclsspec->tyspec == TYSPEC_MIMETYPE)
  433. {
  434. //
  435. // BUGBUG.
  436. // Considering removal of MimeType support from Class Store
  437. // Till it is decided the code is OUT.
  438. return E_NOTIMPL;
  439. /*
  440. if (IsBadStringPtr(pclsspec->tagged_union.pMimeType, _MAX_PATH))
  441. return E_INVALIDARG;
  442. if ((pclsspec->tagged_union.pMimeType == NULL) ||
  443. (*(pclsspec->tagged_union.pMimeType) == NULL))
  444. return E_INVALIDARG;
  445. wsprintf (pszCommandText,
  446. L"<%s>;(%s=%s);name",
  447. m_szClassName, MIMETYPES, pclsspec->tagged_union.pMimeType);
  448. */
  449. }
  450. switch (pclsspec->tyspec)
  451. {
  452. case TYSPEC_TYPELIB:
  453. if (IsNullGuid(pclsspec->tagged_union.typelibID))
  454. return E_INVALIDARG;
  455. StringFromGUID (pclsspec->tagged_union.typelibID, szClsid);
  456. wsprintf (pszCommandText,
  457. L"<%s>;(%s=%s);",
  458. m_szPackageName, PKGTLBIDLIST, szClsid);
  459. break;
  460. case TYSPEC_IID:
  461. if (IsNullGuid(pclsspec->tagged_union.iid))
  462. return E_INVALIDARG;
  463. StringFromGUID (pclsspec->tagged_union.iid, szClsid);
  464. wsprintf (pszCommandText,
  465. L"<%s>;(%s=%s);",
  466. m_szPackageName, PKGIIDLIST, szClsid);
  467. break;
  468. case TYSPEC_CLSID:
  469. //
  470. // Check TreatAs
  471. /* BUGBUG
  472. Considering removal of support for TreatAs in Class Store.
  473. Till this is closed the code is out.
  474. if ((hr = GetTreatAs (clsid, &MapClsid, &(pPackageInfo->pszClassIconPath)))
  475. == S_OK)
  476. {
  477. //
  478. // Put the result in the TreatAs field of PackageInfo
  479. //
  480. pPackageInfo->pTreatAsClsid = (GUID *)CoTaskMemAlloc(sizeof (CLSID));
  481. memcpy (pPackageInfo->pTreatAsClsid, &MapClsid, sizeof (CLSID));
  482. //
  483. // BUGBUG. Must cache presence/absence of TreatAs info as it is
  484. // a very common lookup into DS.
  485. //
  486. }
  487. */
  488. if (IsNullGuid(pclsspec->tagged_union.clsid))
  489. return E_INVALIDARG;
  490. StringFromGUID (pclsspec->tagged_union.clsid, szClsid);
  491. wsprintf (pszCommandText,
  492. L"<%s>;(%s=%s);",
  493. m_szPackageName, PKGCLSIDLIST, szClsid);
  494. break;
  495. case TYSPEC_FILEEXT:
  496. if (IsBadStringPtr(pclsspec->tagged_union.pFileExt, _MAX_PATH))
  497. return E_INVALIDARG;
  498. if ((pclsspec->tagged_union.pFileExt == NULL) ||
  499. (*(pclsspec->tagged_union.pFileExt) == NULL))
  500. return E_INVALIDARG;
  501. /*
  502. //
  503. // BUGBUG. Because FileExt is stored with priority
  504. // make sure that wildcard at end is used.
  505. //
  506. wsprintf (pszCommandText,
  507. L"<%s>;(%s=%s*);",
  508. m_szPackageName, PKGFILEEXTNLIST, pclsspec->tagged_union.pFileExt);
  509. */
  510. //
  511. // BUGBUG. Workaround for IDS bug.
  512. // Change the below to the code above when this is fixed in NTDEV
  513. //
  514. wsprintf (pszCommandText,
  515. L"<%s>;(%s=%s);",
  516. m_szPackageName, QRYFILEEXT, pclsspec->tagged_union.pFileExt);
  517. pFileExt = pclsspec->tagged_union.pFileExt;
  518. break;
  519. case TYSPEC_PROGID:
  520. if (IsBadStringPtr(pclsspec->tagged_union.pProgId, _MAX_PATH))
  521. return E_INVALIDARG;
  522. if ((pclsspec->tagged_union.pProgId == NULL) ||
  523. (*(pclsspec->tagged_union.pProgId) == NULL))
  524. return E_INVALIDARG;
  525. wsprintf (pszCommandText,
  526. L"<%s>;(%s=%s);",
  527. m_szPackageName, PKGPROGIDLIST, pclsspec->tagged_union.pProgId);
  528. break;
  529. case TYSPEC_PACKAGENAME:
  530. //
  531. // Validate package name
  532. //
  533. if (IsBadStringPtr(pclsspec->tagged_union.pPackageName, _MAX_PATH))
  534. return E_INVALIDARG;
  535. if ((pclsspec->tagged_union.pPackageName == NULL) ||
  536. (*(pclsspec->tagged_union.pPackageName) == NULL))
  537. return E_INVALIDARG;
  538. //
  539. PACKAGEDETAIL PackageDetail;
  540. hr = GetPackageDetails (pclsspec->tagged_union.pPackageName,
  541. &PackageDetail);
  542. if (SUCCEEDED(hr))
  543. {
  544. memcpy (pInstallInfo, PackageDetail.pInstallInfo, sizeof(INSTALLINFO));
  545. if (PackageDetail.pActInfo)
  546. {
  547. CoTaskMemFree(PackageDetail.pActInfo->prgShellFileExt);
  548. CoTaskMemFree(PackageDetail.pActInfo->prgPriority);
  549. CoTaskMemFree(PackageDetail.pActInfo->prgInterfaceId);
  550. CoTaskMemFree(PackageDetail.pActInfo->prgTlbId);
  551. CoTaskMemFree(PackageDetail.pActInfo);
  552. }
  553. if (PackageDetail.pPlatformInfo)
  554. {
  555. CoTaskMemFree(PackageDetail.pPlatformInfo->prgPlatform);
  556. CoTaskMemFree(PackageDetail.pPlatformInfo->prgLocale);
  557. CoTaskMemFree(PackageDetail.pPlatformInfo);
  558. }
  559. CoTaskMemFree(PackageDetail.pszSourceList);
  560. CoTaskMemFree(PackageDetail.rpCategory);
  561. CoTaskMemFree(PackageDetail.pInstallInfo);
  562. }
  563. return hr;
  564. default:
  565. return E_NOTIMPL;
  566. }
  567. //
  568. //
  569. HACCESSOR hAccessor = NULL;
  570. IAccessor * pIAccessor = NULL;
  571. IRowset * pIRowset = NULL;
  572. ULONG cRowsFetched;
  573. INSTALLINFO InstallInfo[10];
  574. UINT rgPriority [10];
  575. wcscat (pszCommandText,
  576. szInstallInfoColumns);
  577. hr = ExecuteQuery (m_pIDBCreateCommand,
  578. pszCommandText,
  579. PACKAGEQUERY_COLUMN_COUNT,
  580. NULL,
  581. &hAccessor,
  582. &pIAccessor,
  583. &pIRowset
  584. );
  585. if (!SUCCEEDED(hr))
  586. goto done;
  587. //
  588. // BUGBUG. Currently limited to 10.
  589. // Must put a loop to retry more in future.
  590. //
  591. hr = FetchInstallData(pIRowset,
  592. hAccessor,
  593. pQryContext,
  594. pFileExt,
  595. 10,
  596. &cRowsFetched,
  597. &InstallInfo[0],
  598. &rgPriority[0]
  599. );
  600. if ((hr != S_OK) || (cRowsFetched == 0))
  601. {
  602. hr = CS_E_PACKAGE_NOTFOUND;
  603. }
  604. else
  605. {
  606. //
  607. // Selected one is j
  608. //
  609. UINT j = 0;
  610. //
  611. // process file-ext priority.
  612. //
  613. if ((pFileExt) && (cRowsFetched > 1))
  614. {
  615. UINT Pri = rgPriority[0];
  616. for (i=1; i < cRowsFetched; ++i)
  617. {
  618. if (rgPriority[i] > Pri)
  619. {
  620. Pri = rgPriority[i];
  621. j = i;
  622. }
  623. }
  624. }
  625. //
  626. // return the jth one
  627. //
  628. memcpy (pInstallInfo, &InstallInfo[j], sizeof(INSTALLINFO));
  629. memset (&InstallInfo[j], NULL, sizeof(INSTALLINFO));
  630. // Clean up all fetched except for jth one
  631. for (i=0; i < cRowsFetched; i++)
  632. {
  633. if (i != j)
  634. FreeInstallInfo(&InstallInfo[i]);
  635. }
  636. }
  637. CloseQuery(pIAccessor,
  638. hAccessor,
  639. pIRowset);
  640. done:
  641. return hr;
  642. }
  643. BOOL
  644. CheckMatching (QUERYCONTEXT *pQryContext,
  645. INSTALLINFO *pInstallInfo,
  646. PLATFORMINFO *pPlatformInfo
  647. )
  648. {
  649. HRESULT hr;
  650. DWORD dwCtx, dwLocale;
  651. ULONG i;
  652. BOOL fMatch;
  653. CSPLATFORM *pPlatform;
  654. //
  655. // Get all the specifics of this package
  656. //
  657. dwCtx = pInstallInfo->dwComClassContext;
  658. //
  659. // if the implementation is a remote server,
  660. // then skip matching platform
  661. //
  662. fMatch = FALSE;
  663. for (i=0; i < pPlatformInfo->cPlatforms; i++)
  664. {
  665. if (MatchPlatform (&(pQryContext->Platform), pPlatformInfo->prgPlatform+i))
  666. {
  667. // matches
  668. fMatch = TRUE;
  669. break;
  670. }
  671. }
  672. //
  673. // either the locale seen is LANG_NEUTRAL (means does not matter)
  674. // or the locale matches as specified
  675. // then treat this as found.
  676. // BUGBUG. In future we should be going thru the
  677. // entire list to pick the best match
  678. if (fMatch)
  679. {
  680. fMatch = FALSE;
  681. for (i=0; i < pPlatformInfo->cLocales; i++)
  682. {
  683. if (MatchLocale (pQryContext->Locale, pPlatformInfo->prgLocale[i]))
  684. {
  685. // Does not match the Locale requested
  686. fMatch = TRUE;
  687. break;
  688. }
  689. }
  690. }
  691. return fMatch;
  692. }