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.

2413 lines
65 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. migdb.c
  5. Abstract:
  6. This source implements old AppDb functionality
  7. Author:
  8. Calin Negreanu (calinn) 07-Jan-1998
  9. Revision History:
  10. jimschm 23-Sep-1998 Updated for new fileops code
  11. jimschm 25-Feb-1998 Added UninstallSection support
  12. calinn 19-Jan-1998 Added CANCELLED response
  13. --*/
  14. #include "pch.h"
  15. #include "migdbp.h"
  16. #include "migappp.h"
  17. // #define _OLDAPPDB
  18. #define DBG_MIGDB "MigDb"
  19. //
  20. // Globals
  21. //
  22. POOLHANDLE g_MigDbPool;
  23. PMIGDB_CONTEXT g_ContextList;
  24. HASHTABLE g_FileTable;
  25. HINF g_MigDbInf = INVALID_HANDLE_VALUE;
  26. INT g_RegKeyPresentIndex;
  27. static PINFCONTEXT g_Line;
  28. PMIGDB_HOOK_PROTOTYPE g_MigDbHook;
  29. typedef LONG (CPL_PROTOTYPE) (HWND hwndCPl, UINT uMsg, LONG lParam1, LONG lParam2);
  30. typedef CPL_PROTOTYPE * PCPL_PROTOTYPE;
  31. #define ArgFunction TEXT("ARG")
  32. #define ArgFunctionLen 3
  33. BOOL
  34. pCallAction (
  35. IN PMIGDB_CONTEXT MigDbContext
  36. );
  37. PMIGDB_HOOK_PROTOTYPE
  38. SetMigDbHook (
  39. PMIGDB_HOOK_PROTOTYPE HookFunction
  40. )
  41. {
  42. PMIGDB_HOOK_PROTOTYPE savedHook;
  43. savedHook = g_MigDbHook;
  44. g_MigDbHook = HookFunction;
  45. return savedHook;
  46. }
  47. INT
  48. pGetAttribIndex (
  49. IN PCSTR AttribName
  50. )
  51. /*++
  52. Routine Description:
  53. This routine returns the index in attribute functions array for a specified attribute.
  54. Arguments:
  55. AttribName - Attribute name
  56. Return value:
  57. -1 - no such attribute in attribute table
  58. --*/
  59. {
  60. INT attribIndex;
  61. INT rc = 0;
  62. PSTR attrEnd = NULL;
  63. CHAR savedChar = 0;
  64. attrEnd = (PSTR)SkipSpaceR (AttribName, GetEndOfStringA (AttribName));
  65. if (attrEnd != NULL) {
  66. attrEnd = _mbsinc (attrEnd);
  67. savedChar = attrEnd [0];
  68. attrEnd [0] = 0;
  69. }
  70. __try {
  71. attribIndex = MigDb_GetAttributeIdx (AttribName);
  72. if (attribIndex == -1) {
  73. LOG((LOG_ERROR, "Attribute not found: %s", AttribName));
  74. }
  75. rc = attribIndex;
  76. }
  77. __finally {
  78. if (attrEnd != NULL) {
  79. attrEnd [0] = savedChar;
  80. }
  81. }
  82. return rc;
  83. }
  84. PMIGDB_REQ_FILE
  85. pReadReqFiles (
  86. IN PCSTR SectionName
  87. )
  88. {
  89. INFCONTEXT context;
  90. CHAR fileName [MEMDB_MAX];
  91. CHAR tempField [MEMDB_MAX];
  92. PMIGDB_REQ_FILE result = NULL;
  93. PMIGDB_REQ_FILE tmpResult = NULL;
  94. MYASSERT (g_MigDbInf != INVALID_HANDLE_VALUE);
  95. if (SetupFindFirstLine (g_MigDbInf, SectionName, NULL, &context)) {
  96. do {
  97. if (!SetupGetStringField (&context, 1, fileName, MEMDB_MAX, NULL)) {
  98. LOG ((LOG_ERROR, "Error while loading file name."));
  99. break;
  100. }
  101. tmpResult = (PMIGDB_REQ_FILE) PoolMemGetMemory (g_MigDbPool, sizeof (MIGDB_REQ_FILE));
  102. ZeroMemory (tmpResult, sizeof (MIGDB_REQ_FILE));
  103. tmpResult->Next = result;
  104. result = tmpResult;
  105. result->ReqFilePath = PoolMemDuplicateString (g_MigDbPool, fileName);
  106. if (SetupGetMultiSzField (&context, 2, tempField, MEMDB_MAX, NULL)) {
  107. result->FileAttribs = LoadAttribData (tempField, g_MigDbPool);
  108. }
  109. } while (SetupFindNextLine (&context, &context));
  110. }
  111. return result;
  112. }
  113. BOOL
  114. pValidateArg (
  115. IN OUT PMIGDB_ATTRIB AttribStruct
  116. )
  117. {
  118. BOOL b;
  119. INT Index;
  120. PCSTR p;
  121. BOOL IsHkr;
  122. MYASSERT (AttribStruct);
  123. if (AttribStruct->ArgCount != MigDb_GetReqArgCount (AttribStruct->AttribIndex)) {
  124. #ifdef DEBUG
  125. if (AttribStruct->AttribIndex != -1) {
  126. CHAR Buffer[16384];
  127. SetupGetLineText (g_Line, NULL, NULL, NULL, Buffer, ARRAYSIZE(Buffer), NULL);
  128. DEBUGMSG ((
  129. DBG_WHOOPS,
  130. "Discarding attribute %s because of too few arguments.\n"
  131. " Line: %s\n",
  132. MigDb_GetAttributeName (AttribStruct->AttribIndex),
  133. Buffer
  134. ));
  135. }
  136. #endif
  137. AttribStruct->AttribIndex = -1;
  138. return FALSE;
  139. }
  140. //
  141. // HACK: If REGKEYPRESENT attrib with HKR, put in a special
  142. // global list.
  143. //
  144. if (AttribStruct->AttribIndex == g_RegKeyPresentIndex) {
  145. //
  146. // Is this HKR?
  147. //
  148. Index = GetOffsetOfRootString (AttribStruct->Arguments, NULL);
  149. p = GetRootStringFromOffset (Index);
  150. if (!p) {
  151. DEBUGMSG ((DBG_WHOOPS, "RegKeyPresent: %s is not a valid key", AttribStruct->Arguments));
  152. } else {
  153. IsHkr = StringICompare (p, "HKR") || StringICompare (p, "HKEY_ROOT");
  154. if (IsHkr) {
  155. //
  156. // Yes, add full arg to the hash table
  157. //
  158. b = FALSE;
  159. HtAddStringAndData (
  160. g_PerUserRegKeys,
  161. AttribStruct->Arguments,
  162. &b
  163. );
  164. }
  165. }
  166. }
  167. return TRUE;
  168. }
  169. #define STATE_ATTRNAME 1
  170. #define STATE_ATTRARG 2
  171. PMIGDB_ATTRIB
  172. LoadAttribData (
  173. IN PCSTR MultiSzStr,
  174. IN POOLHANDLE hPool
  175. )
  176. /*++
  177. Routine Description:
  178. This routine creates a list of MIGDB_ATTRIBs from a multisz.
  179. Arguments:
  180. MultiSzStr - multisz to be processed
  181. Return value:
  182. MIGDB_ATTRIB nodes
  183. --*/
  184. {
  185. MULTISZ_ENUM multiSzEnum;
  186. PMIGDB_ATTRIB result = NULL;
  187. PMIGDB_ATTRIB tmpAttr = NULL;
  188. INT state = STATE_ATTRNAME;
  189. PSTR currStrPtr = NULL;
  190. PSTR currArgPtr = NULL;
  191. PSTR endArgPtr = NULL;
  192. CHAR savedChar = 0;
  193. GROWBUFFER argList = GROWBUF_INIT;
  194. if (EnumFirstMultiSz (&multiSzEnum, MultiSzStr)) {
  195. do {
  196. currStrPtr = (PSTR) SkipSpace (multiSzEnum.CurrentString);
  197. if (state == STATE_ATTRNAME) {
  198. tmpAttr = (PMIGDB_ATTRIB) PoolMemGetMemory (hPool, sizeof (MIGDB_ATTRIB));
  199. ZeroMemory (tmpAttr, sizeof (MIGDB_ATTRIB));
  200. if (_mbsnextc (currStrPtr) == '!') {
  201. currStrPtr = _mbsinc (currStrPtr);
  202. currStrPtr = (PSTR) SkipSpace (currStrPtr);
  203. tmpAttr->NotOperator = TRUE;
  204. }
  205. currArgPtr = _mbschr (currStrPtr, '(');
  206. if (currArgPtr) {
  207. endArgPtr = _mbsdec (currStrPtr, currArgPtr);
  208. if (endArgPtr) {
  209. endArgPtr = (PSTR) SkipSpaceR (currStrPtr, endArgPtr);
  210. endArgPtr = _mbsinc (endArgPtr);
  211. }
  212. else {
  213. endArgPtr = currStrPtr;
  214. }
  215. savedChar = *endArgPtr;
  216. *endArgPtr = 0;
  217. tmpAttr->AttribIndex = pGetAttribIndex (currStrPtr);
  218. *endArgPtr = savedChar;
  219. currStrPtr = _mbsinc (currArgPtr);
  220. state = STATE_ATTRARG;
  221. }
  222. else {
  223. // this attribute has no arguments.
  224. tmpAttr->AttribIndex = pGetAttribIndex (currStrPtr);
  225. tmpAttr->Next = result;
  226. result = tmpAttr;
  227. pValidateArg (result);
  228. continue;
  229. }
  230. }
  231. if (state == STATE_ATTRARG) {
  232. currStrPtr = (PSTR) SkipSpace (currStrPtr);
  233. endArgPtr = _mbsrchr (currStrPtr, ')');
  234. if (endArgPtr) {
  235. endArgPtr = _mbsdec (currStrPtr, endArgPtr);
  236. if (endArgPtr) {
  237. endArgPtr = (PSTR) SkipSpaceR (currStrPtr, endArgPtr);
  238. endArgPtr = _mbsinc (endArgPtr);
  239. }
  240. else {
  241. endArgPtr = currStrPtr;
  242. }
  243. savedChar = *endArgPtr;
  244. *endArgPtr = 0;
  245. }
  246. MultiSzAppend (&argList, currStrPtr);
  247. tmpAttr->ArgCount++;
  248. if (endArgPtr) {
  249. *endArgPtr = savedChar;
  250. tmpAttr->Arguments = PoolMemDuplicateMultiSz (hPool, (PSTR)argList.Buf);
  251. FreeGrowBuffer (&argList);
  252. state = STATE_ATTRNAME;
  253. tmpAttr->Next = result;
  254. result = tmpAttr;
  255. pValidateArg (result);
  256. }
  257. }
  258. if (state == STATE_ATTRNAME) {
  259. // we successfully parsed one attribute
  260. // we have a special case here (REQFILE attribute)
  261. if (StringIMatch (MigDb_GetAttributeName (result->AttribIndex), TEXT("ReqFile"))) {
  262. // we found ReqFile attribute. For this attribute a field will point to a special structure
  263. // of PMIGDB_REQ_FILE type
  264. result->ExtraData = pReadReqFiles (result->Arguments);
  265. }
  266. }
  267. } while (EnumNextMultiSz (&multiSzEnum));
  268. }
  269. return result;
  270. }
  271. VOID
  272. FreeAttribData(
  273. IN POOLHANDLE hPool,
  274. IN PMIGDB_ATTRIB pData
  275. )
  276. {
  277. while(pData){
  278. if(pData->Arguments){
  279. PoolMemReleaseMemory(hPool, (PVOID)pData->Arguments);
  280. }
  281. PoolMemReleaseMemory(hPool, pData);
  282. pData = pData->Next;
  283. }
  284. }
  285. BOOL
  286. AddFileToMigDbLinkage (
  287. IN PCTSTR FileName,
  288. IN PINFCONTEXT Context, OPTIONAL
  289. IN DWORD FieldIndex OPTIONAL
  290. )
  291. {
  292. CHAR tempField [MEMDB_MAX];
  293. DWORD fieldIndex = FieldIndex;
  294. PMIGDB_FILE migDbFile = NULL;
  295. PMIGDB_ATTRIB migDbAttrib = NULL;
  296. HASHITEM stringId;
  297. FILE_LIST_STRUCT fileList;
  298. //creating MIGDB_FILE structure for current file
  299. migDbFile = (PMIGDB_FILE) PoolMemGetMemory (g_MigDbPool, sizeof (MIGDB_FILE));
  300. if (migDbFile != NULL) {
  301. ZeroMemory (migDbFile, sizeof (MIGDB_FILE));
  302. migDbFile->Section = g_ContextList->Sections;
  303. if (Context) {
  304. fieldIndex ++;
  305. if (SetupGetMultiSzField (Context, fieldIndex, tempField, MEMDB_MAX, NULL)) {
  306. g_Line = Context;
  307. migDbFile->Attributes = LoadAttribData (tempField, g_MigDbPool);
  308. if (g_MigDbHook != NULL) {
  309. migDbAttrib = migDbFile->Attributes;
  310. while (migDbAttrib) {
  311. g_MigDbHook (FileName, g_ContextList, g_ContextList->Sections, migDbFile, migDbAttrib);
  312. migDbAttrib = migDbAttrib->Next;
  313. }
  314. }
  315. }
  316. }
  317. //adding this file into string table and create a MIGDB_FILE node. If file
  318. //already exists in string table then just create another MIGDB_FILE node
  319. //chained with already existing ones.
  320. stringId = HtFindString (g_FileTable, FileName);
  321. if (stringId) {
  322. HtCopyStringData (g_FileTable, stringId, &fileList);
  323. fileList.Last->Next = migDbFile;
  324. fileList.Last = migDbFile;
  325. HtSetStringData (g_FileTable, stringId, &fileList);
  326. } else {
  327. fileList.First = fileList.Last = migDbFile;
  328. HtAddStringAndData (g_FileTable, FileName, &fileList);
  329. }
  330. }
  331. return TRUE;
  332. }
  333. BOOL
  334. pScanForFile (
  335. IN PINFCONTEXT Context,
  336. IN DWORD FieldIndex
  337. )
  338. /*++
  339. Routine Description:
  340. This routine updates migdb data structures loading a specified file info from inf file.
  341. Creates a migdb file node and the file is added in a string table for fast query.
  342. Arguments:
  343. SectionStr - section to process
  344. Return value:
  345. TRUE - the operation was successful
  346. FALSE - otherwise
  347. --*/
  348. {
  349. CHAR fileName [MEMDB_MAX];
  350. if (CANCELLED()) {
  351. SetLastError (ERROR_CANCELLED);
  352. return FALSE;
  353. }
  354. //scanning for file name
  355. if (!SetupGetStringField (Context, FieldIndex, fileName, MEMDB_MAX, NULL)) {
  356. LOG ((LOG_ERROR, "Error while loading file name."));
  357. return FALSE;
  358. }
  359. return AddFileToMigDbLinkage (fileName, Context, FieldIndex);
  360. }
  361. /*++
  362. Routine Description:
  363. The subsequent two routines enumerate the sections with a particular name and
  364. with .999 extension from an inf file.
  365. Arguments:
  366. SectEnum - enumeration structure
  367. Return value:
  368. TRUE - enumeration continues
  369. FALSE - enumeration ended
  370. --*/
  371. typedef struct _SECT_ENUM {
  372. HINF InfHandle;
  373. INT SectIndex;
  374. PSTR SectNameEnd;
  375. CHAR SectName [MAX_PATH];
  376. } SECT_ENUM, *PSECT_ENUM;
  377. BOOL
  378. pEnumNextSection (
  379. IN OUT PSECT_ENUM SectEnum
  380. )
  381. {
  382. INFCONTEXT context;
  383. if (SectEnum->SectIndex == -1) {
  384. return FALSE;
  385. }
  386. SectEnum->SectIndex ++;
  387. sprintf (SectEnum->SectNameEnd, ".%d", SectEnum->SectIndex);
  388. return SetupFindFirstLine (SectEnum->InfHandle, SectEnum->SectName, NULL, &context);
  389. }
  390. BOOL
  391. pEnumFirstSection (
  392. OUT PSECT_ENUM SectEnum,
  393. IN PCSTR SectionStr,
  394. IN HINF InfHandle
  395. )
  396. {
  397. INFCONTEXT context;
  398. SectEnum->SectIndex = -1;
  399. SectEnum->InfHandle = InfHandle;
  400. StringCopyA (SectEnum->SectName, SectionStr);
  401. SectEnum->SectNameEnd = GetEndOfStringA (SectEnum->SectName);
  402. if (SetupFindFirstLine (SectEnum->InfHandle, SectEnum->SectName, NULL, &context)) {
  403. //good, only one section
  404. return TRUE;
  405. }
  406. else {
  407. //more than one section
  408. SectEnum->SectIndex = 0;
  409. return pEnumNextSection (SectEnum);
  410. }
  411. }
  412. BOOL
  413. pLoadSectionData (
  414. IN PCSTR SectionStr
  415. )
  416. /*++
  417. Routine Description:
  418. This routine updates migdb data structures loading a specified section from inf file. For
  419. every line in the section there is a migdb file node created. Also the file is added in
  420. a string table for fast query.
  421. Arguments:
  422. SectionStr - section to process
  423. Return value:
  424. TRUE - the operation was successful
  425. FALSE - otherwise
  426. --*/
  427. {
  428. INFCONTEXT context;
  429. SECT_ENUM sectEnum;
  430. PMIGDB_SECTION migDbSection;
  431. BOOL result = TRUE;
  432. if (CANCELLED()) {
  433. SetLastError (ERROR_CANCELLED);
  434. return FALSE;
  435. }
  436. MYASSERT (g_MigDbInf != INVALID_HANDLE_VALUE);
  437. if (pEnumFirstSection (&sectEnum, SectionStr, g_MigDbInf)) {
  438. do {
  439. //initialize the section (this context can have multiple sections)
  440. //and parse the file info
  441. migDbSection = (PMIGDB_SECTION) PoolMemGetMemory (g_MigDbPool, sizeof (MIGDB_SECTION));
  442. if (migDbSection != NULL) {
  443. ZeroMemory (migDbSection, sizeof (MIGDB_SECTION));
  444. migDbSection->Context = g_ContextList;
  445. migDbSection->Next = g_ContextList->Sections;
  446. g_ContextList->Sections = migDbSection;
  447. if (SetupFindFirstLine (g_MigDbInf, sectEnum.SectName, NULL, &context)) {
  448. do {
  449. if (!pScanForFile (&context, 1)) {
  450. return FALSE;
  451. }
  452. }
  453. while (SetupFindNextLine (&context, &context));
  454. }
  455. }
  456. else {
  457. LOG ((LOG_ERROR, "Unable to create section for %s", SectionStr));
  458. }
  459. }
  460. while (pEnumNextSection (&sectEnum));
  461. }
  462. return result;
  463. }
  464. BOOL
  465. pLoadTypeData (
  466. IN PCSTR TypeStr
  467. )
  468. /*++
  469. Routine Description:
  470. This routine updates migdb data structures loading a specified type data from inf file. For
  471. every line in type section there is a migdb context created. Also for every migdb context
  472. the coresponding section(s) is processed.
  473. Arguments:
  474. TypeStr - file type to process
  475. Return value:
  476. TRUE - the operation was successful
  477. FALSE - otherwise
  478. --*/
  479. {
  480. CHAR section [MEMDB_MAX];
  481. CHAR locSection [MEMDB_MAX];
  482. CHAR message [MEMDB_MAX];
  483. CHAR tempField [MEMDB_MAX];
  484. PSTR tempFieldPtr;
  485. PSTR endOfArg = NULL;
  486. DWORD fieldIndex;
  487. PMIGDB_CONTEXT migDbContext = NULL;
  488. INFCONTEXT context, context1;
  489. BOOL result = TRUE;
  490. INT actionIndex;
  491. GROWBUFFER argList = GROWBUF_INIT;
  492. if (CANCELLED()) {
  493. SetLastError (ERROR_CANCELLED);
  494. return FALSE;
  495. }
  496. MYASSERT (g_MigDbInf != INVALID_HANDLE_VALUE);
  497. if (SetupFindFirstLine (g_MigDbInf, TypeStr, NULL, &context)) {
  498. //let's identify the action function index to update MIGDB_CONTEXT structure
  499. actionIndex = MigDb_GetActionIdx (TypeStr);
  500. if (actionIndex == -1) {
  501. LOG ((LOG_ERROR, "Unable to identify action index for %s", TypeStr));
  502. }
  503. do {
  504. if (!SetupGetStringField (&context, 1, section, MEMDB_MAX, NULL)) {
  505. LOG ((LOG_ERROR, "Unable to load section for %s", TypeStr));
  506. return FALSE;
  507. }
  508. if (!SetupGetStringField (&context, 2, message, MEMDB_MAX, NULL)) {
  509. message [0] = 0;
  510. }
  511. migDbContext = (PMIGDB_CONTEXT) PoolMemGetMemory (g_MigDbPool, sizeof (MIGDB_CONTEXT));
  512. if (migDbContext == NULL) {
  513. LOG ((LOG_ERROR, "Unable to create context for %s.", TypeStr));
  514. return FALSE;
  515. }
  516. ZeroMemory (migDbContext, sizeof (MIGDB_CONTEXT));
  517. migDbContext->Next = g_ContextList;
  518. g_ContextList = migDbContext;
  519. // update ActionIndex with known value
  520. migDbContext->ActionIndex = actionIndex;
  521. // update SectName field
  522. migDbContext->SectName = PoolMemDuplicateString (g_MigDbPool, section);
  523. // update SectLocalizedName field
  524. if (SetupFindFirstLine (g_MigDbInf, S_STRINGS, section, &context1)) {
  525. if (SetupGetStringField (&context1, 1, locSection, MEMDB_MAX, NULL)) {
  526. migDbContext->SectLocalizedName = PoolMemDuplicateString (g_MigDbPool, locSection);
  527. }
  528. }
  529. // set SectNameForDisplay to localized name, or sect name if no localized name
  530. if (migDbContext->SectLocalizedName) {
  531. migDbContext->SectNameForDisplay = migDbContext->SectLocalizedName;
  532. } else {
  533. migDbContext->SectNameForDisplay = migDbContext->SectName;
  534. }
  535. // update Message field
  536. if (message[0] != 0) {
  537. migDbContext->Message = PoolMemDuplicateString (g_MigDbPool, message);
  538. }
  539. // OK, now let's scan all the remaining fields
  540. fieldIndex = 3;
  541. do {
  542. if (!TickProgressBar()) {
  543. return FALSE;
  544. }
  545. tempField [0] = 0;
  546. if (SetupGetStringField (&context, fieldIndex, tempField, MEMDB_MAX, NULL)) {
  547. if (StringIMatchCharCountA (tempField, ArgFunction, ArgFunctionLen)) {
  548. //we have an additional argument for action function
  549. tempFieldPtr = _mbschr (tempField, '(');
  550. if (tempFieldPtr != NULL) {
  551. tempFieldPtr = (PSTR) SkipSpaceA (_mbsinc (tempFieldPtr));
  552. if (tempFieldPtr != NULL) {
  553. endOfArg = _mbschr (tempFieldPtr, ')');
  554. if (endOfArg != NULL) {
  555. *endOfArg = 0;
  556. endOfArg = (PSTR) SkipSpaceRA (tempFieldPtr, endOfArg);
  557. }
  558. if (endOfArg != NULL) {
  559. *_mbsinc (endOfArg) = 0;
  560. MultiSzAppend (&argList, tempFieldPtr);
  561. }
  562. ELSE_DEBUGMSG ((
  563. DBG_WHOOPS,
  564. "Improperly formatted arg: %s in %s",
  565. tempField,
  566. TypeStr
  567. ));
  568. }
  569. }
  570. }
  571. else {
  572. //we have something else, probably file name and attributes
  573. if (_tcschr (tempField, TEXT('.')) == NULL) {
  574. LOG ((LOG_ERROR, "Dot not found in %s\\%s", TypeStr, section));
  575. }
  576. //therefore we initialize the section (this context will have
  577. //only one section) and parse the file info
  578. migDbContext->Sections = (PMIGDB_SECTION) PoolMemGetMemory (
  579. g_MigDbPool,
  580. sizeof (MIGDB_SECTION)
  581. );
  582. if (migDbContext->Sections != NULL) {
  583. ZeroMemory (migDbContext->Sections, sizeof (MIGDB_SECTION));
  584. migDbContext->Sections->Context = migDbContext;
  585. migDbContext->Arguments = PoolMemDuplicateMultiSz (g_MigDbPool, (PSTR)argList.Buf);
  586. FreeGrowBuffer (&argList);
  587. if (!pScanForFile (&context, fieldIndex)) {
  588. return FALSE;
  589. }
  590. tempField [0] = 0;
  591. }
  592. else {
  593. LOG ((LOG_ERROR, "Unable to create section for %s/%s", TypeStr, section));
  594. return FALSE;
  595. }
  596. }
  597. }
  598. fieldIndex ++;
  599. } while (tempField [0] != 0);
  600. if (migDbContext->Sections == NULL) {
  601. //now let's add action function arguments in MIGDB_CONTEXT structure
  602. migDbContext->Arguments = PoolMemDuplicateMultiSz (g_MigDbPool, (PSTR)argList.Buf);
  603. FreeGrowBuffer (&argList);
  604. //let's go to the sections and scan all files
  605. if (!pLoadSectionData (section)) {
  606. return FALSE;
  607. }
  608. }
  609. }
  610. while (SetupFindNextLine (&context, &context));
  611. }
  612. return result;
  613. }
  614. #define szMigDbFile TEXT("MIGDB.INF")
  615. #define szMigDbFile2 TEXT("MIGDB2.INF")
  616. #define szExtraMigDbDir TEXT("INF\\NTUPG")
  617. BOOL
  618. InitMigDbEx (
  619. PCSTR MigDbFile
  620. )
  621. /*++
  622. Routine Description:
  623. This routine initialize memory and data structures used by MigDb.
  624. Arguments:
  625. NONE
  626. Return value:
  627. TRUE - the operation was successful
  628. FALSE - otherwise
  629. --*/
  630. {
  631. PCSTR migDbFile = NULL;
  632. PCSTR migDbFile2 = NULL;
  633. PCSTR extraMigDbDir = NULL;
  634. PCSTR pattern = NULL;
  635. PCSTR extraMigDbFile = NULL;
  636. WIN32_FIND_DATA migDbFiles;
  637. HANDLE findHandle;
  638. BOOL result = TRUE;
  639. INT i;
  640. PCSTR typeStr;
  641. TCHAR fileName [MAX_TCHAR_PATH] = "";
  642. PTSTR dontCare;
  643. if (CANCELLED()) {
  644. SetLastError (ERROR_CANCELLED);
  645. return FALSE;
  646. }
  647. MYASSERT (g_MigDbInf == INVALID_HANDLE_VALUE);
  648. __try {
  649. if (MigDbFile != NULL) {
  650. g_MigDbInf = InfOpenInfFile (MigDbFile);
  651. if (g_MigDbInf == INVALID_HANDLE_VALUE) {
  652. SearchPath (NULL, MigDbFile, NULL, MAX_TCHAR_PATH, fileName, &dontCare);
  653. g_MigDbInf = InfOpenInfFile (fileName);
  654. if (g_MigDbInf == INVALID_HANDLE_VALUE) {
  655. LOG((LOG_ERROR, "Cannot open migration database : %s", MigDbFile));
  656. result = FALSE;
  657. __leave;
  658. }
  659. }
  660. }
  661. else {
  662. migDbFile = JoinPaths (g_UpgradeSources, szMigDbFile);
  663. g_MigDbInf = InfOpenInfFile (migDbFile);
  664. if (g_MigDbInf == INVALID_HANDLE_VALUE) {
  665. LOG((LOG_ERROR, "Cannot open migration database : %s", migDbFile));
  666. result = FALSE;
  667. __leave;
  668. }
  669. TickProgressBar ();
  670. migDbFile2 = JoinPaths (g_UpgradeSources, szMigDbFile2);
  671. if (!SetupOpenAppendInfFile (migDbFile2, g_MigDbInf, NULL)) {
  672. DEBUGMSG((DBG_WARNING, "Cannot append second migration database : %s", migDbFile2));
  673. }
  674. extraMigDbDir = JoinPaths (g_WinDir, szExtraMigDbDir);
  675. pattern = JoinPaths (extraMigDbDir, TEXT("*.INF"));
  676. findHandle = FindFirstFile (pattern, &migDbFiles);
  677. FreePathString (pattern);
  678. if (findHandle != INVALID_HANDLE_VALUE) {
  679. do {
  680. extraMigDbFile = JoinPaths (extraMigDbDir, migDbFiles.cFileName);
  681. if (!SetupOpenAppendInfFile (extraMigDbFile, g_MigDbInf, NULL)) {
  682. DEBUGMSG((DBG_WARNING, "Cannot append external migration database : %s", extraMigDbFile));
  683. }
  684. FreePathString (extraMigDbFile);
  685. }
  686. while (FindNextFile (findHandle, &migDbFiles));
  687. FindClose (findHandle);
  688. }
  689. //
  690. // need to read [UseNtFiles] section to decide exclusion of some
  691. // file names replacement
  692. //
  693. InitUseNtFilesMap ();
  694. }
  695. g_MigDbPool = PoolMemInitNamedPool ("MigDb Pool");
  696. PoolMemDisableTracking (g_MigDbPool);
  697. g_FileTable = HtAllocWithData (sizeof (FILE_LIST_STRUCT));
  698. if (g_FileTable == NULL) {
  699. LOG((LOG_ERROR, "Cannot initialize memory for migdb operations"));
  700. result = FALSE;
  701. __leave;
  702. }
  703. //load known types from migdb
  704. i = 0;
  705. do {
  706. typeStr = MigDb_GetActionName (i);
  707. if (typeStr != NULL) {
  708. if (!pLoadTypeData (typeStr)) {
  709. result = FALSE;
  710. __leave;
  711. }
  712. }
  713. i++;
  714. }
  715. while (typeStr != NULL);
  716. }
  717. __finally {
  718. if (extraMigDbDir != NULL) {
  719. FreePathString (extraMigDbDir);
  720. }
  721. if (migDbFile2 != NULL) {
  722. FreePathString (migDbFile2);
  723. }
  724. if (migDbFile != NULL) {
  725. FreePathString (migDbFile);
  726. }
  727. }
  728. return result;
  729. }
  730. VOID
  731. pCheckForPerUserKeys (
  732. VOID
  733. )
  734. /*++
  735. Routine Description:
  736. pCheckForPerUserKeys scans all users for the keys or values specified in
  737. the g_PerUserRegKeys hash table. The values of the hash table are updated,
  738. so the RegKeyExists attribute is fast.
  739. Arguments:
  740. None.
  741. Return Value:
  742. None.
  743. --*/
  744. {
  745. BOOL b = FALSE;
  746. CHAR RegKey[MAX_REGISTRY_KEY];
  747. CHAR RegValue[MAX_REGISTRY_VALUE_NAME];
  748. BOOL HasValue;
  749. USERENUM ue;
  750. HASHTABLE_ENUM he;
  751. HKEY Key;
  752. PBYTE Data;
  753. HKEY OldRoot;
  754. if (!EnumFirstHashTableString (&he, g_PerUserRegKeys)) {
  755. return;
  756. }
  757. //
  758. // Enumerate each user, then enumerate the g_PerUserRegKeys hash
  759. // table and test the registry
  760. //
  761. if (EnumFirstUser (&ue, 0)) {
  762. do {
  763. //
  764. // Skip users we don't care about
  765. //
  766. if (!ue.UserRegKey || ue.CreateAccountOnly || (ue.AccountType & INVALID_ACCOUNT)) {
  767. continue;
  768. }
  769. //
  770. // Set HKR to this user
  771. //
  772. OldRoot = GetRegRoot();
  773. SetRegRoot (ue.UserRegKey);
  774. //
  775. // Process the hash table
  776. //
  777. if (EnumFirstHashTableString (&he, g_PerUserRegKeys)) {
  778. do {
  779. //
  780. // Skip this entry if we already know it exists for
  781. // one user.
  782. //
  783. if (*((PBOOL) he.ExtraData)) {
  784. continue;
  785. }
  786. //
  787. // Decode registry string using hash table entry
  788. //
  789. HasValue = DecodeRegistryString (
  790. he.String,
  791. RegKey,
  792. RegValue,
  793. NULL
  794. );
  795. //
  796. // Ping the registry. RegKey always starts with HKR.
  797. //
  798. b = FALSE;
  799. Key = OpenRegKeyStr (RegKey);
  800. if (Key) {
  801. if (HasValue) {
  802. Data = GetRegValueData (Key, RegValue);
  803. if (Data) {
  804. b = TRUE;
  805. MemFree (g_hHeap, 0, Data);
  806. }
  807. } else {
  808. b = TRUE;
  809. }
  810. CloseRegKey (Key);
  811. }
  812. //
  813. // Update hash table if the key or value exists
  814. //
  815. if (b) {
  816. HtAddStringAndData (g_PerUserRegKeys, he.String, &b);
  817. }
  818. } while (EnumNextHashTableString (&he));
  819. }
  820. //
  821. // Restore HKR
  822. //
  823. SetRegRoot (OldRoot);
  824. } while (EnumNextUser (&ue));
  825. }
  826. }
  827. DWORD
  828. InitMigDb (
  829. IN DWORD Request
  830. )
  831. {
  832. switch (Request) {
  833. case REQUEST_QUERYTICKS:
  834. return TICKS_INIT_MIGDB;
  835. case REQUEST_RUN:
  836. if (!InitMigDbEx (NULL)) {
  837. return GetLastError ();
  838. }
  839. pCheckForPerUserKeys();
  840. return ERROR_SUCCESS;
  841. default:
  842. DEBUGMSG ((DBG_ERROR, "Bad parameter in InitMigDb"));
  843. }
  844. return 0;
  845. }
  846. BOOL
  847. CleanupMigDb (
  848. VOID
  849. )
  850. /*++
  851. Routine Description:
  852. This routine cleans up all memory used by MigDb.
  853. Arguments:
  854. NONE
  855. Return value:
  856. always TRUE
  857. --*/
  858. {
  859. PMIGDB_CONTEXT migDbContext = NULL;
  860. // first, let's walk through any context and check if it's a required one
  861. migDbContext = g_ContextList;
  862. while (migDbContext) {
  863. if ((!MigDb_CallWhenTriggered (migDbContext->ActionIndex)) &&
  864. (migDbContext->TriggerCount == 0)
  865. ) {
  866. pCallAction (migDbContext);
  867. }
  868. migDbContext = migDbContext->Next;
  869. }
  870. if (g_FileTable != NULL) {
  871. HtFree (g_FileTable);
  872. g_FileTable = NULL;
  873. }
  874. if (g_MigDbPool != NULL) {
  875. PoolMemDestroyPool (g_MigDbPool);
  876. g_MigDbPool = NULL;
  877. }
  878. if (g_MigDbInf != INVALID_HANDLE_VALUE) {
  879. InfCloseInfFile (g_MigDbInf);
  880. g_MigDbInf = INVALID_HANDLE_VALUE;
  881. }
  882. g_ContextList = NULL;
  883. return TRUE;
  884. }
  885. DWORD
  886. DoneMigDb (
  887. IN DWORD Request
  888. )
  889. {
  890. switch (Request) {
  891. case REQUEST_QUERYTICKS:
  892. return TICKS_DONE_MIGDB;
  893. case REQUEST_RUN:
  894. if (!CleanupMigDb ()) {
  895. return GetLastError ();
  896. }
  897. else {
  898. return ERROR_SUCCESS;
  899. }
  900. default:
  901. DEBUGMSG ((DBG_ERROR, "Bad parameter in DoneMigDb"));
  902. }
  903. return 0;
  904. }
  905. BOOL
  906. IsKnownMigDbFile (
  907. IN PCTSTR FileName
  908. )
  909. /*++
  910. Routine Description:
  911. This routine looks if the file given as argument is in MigDb string table.
  912. Arguments:
  913. FileName - file name
  914. Return value:
  915. TRUE - the file is in MigDb string table
  916. FALSE - otherwise
  917. --*/
  918. {
  919. return (HtFindString (g_FileTable, FileName) != 0);
  920. }
  921. BOOL
  922. CallAttribute (
  923. IN PMIGDB_ATTRIB MigDbAttrib,
  924. IN PDBATTRIB_PARAMS AttribParams
  925. )
  926. /*++
  927. Routine Description:
  928. This routine calls a specified attribute function for a specified file.
  929. Arguments:
  930. MigDbAttrib - See definition.
  931. AttribParams - See definition
  932. Return value:
  933. TRUE - if attribute function succeded
  934. FALSE - otherwise
  935. --*/
  936. {
  937. PATTRIBUTE_PROTOTYPE p;
  938. BOOL b;
  939. #ifdef DEBUG
  940. TCHAR DbgBuf[32];
  941. BOOL InterestingFile;
  942. #endif
  943. if (MigDbAttrib->AttribIndex == -1) {
  944. //invalid index for attribute function
  945. return FALSE;
  946. }
  947. #ifdef DEBUG
  948. if (!g_ConfigOptions.Fast) {
  949. //
  950. // Check if this file is in [FilesToTrack] inside debug.inf
  951. //
  952. GetPrivateProfileString ("FilesToTrack", AttribParams->FileParams->FullFileSpec, "", DbgBuf, ARRAYSIZE(DbgBuf), g_DebugInfPath);
  953. if (!(*DbgBuf)) {
  954. GetPrivateProfileString ("FilesToTrack", AttribParams->FileParams->FindData->cFileName, "", DbgBuf, ARRAYSIZE(DbgBuf), g_DebugInfPath);
  955. }
  956. InterestingFile = (*DbgBuf != 0);
  957. if (InterestingFile) {
  958. DEBUGMSG ((
  959. DBG_TRACK,
  960. "Calling %s for %s",
  961. MigDb_GetAttributeName (MigDbAttrib->AttribIndex),
  962. AttribParams->FileParams->FindData->cFileName
  963. ));
  964. }
  965. }
  966. #endif
  967. p = MigDb_GetAttributeAddr (MigDbAttrib->AttribIndex);
  968. MYASSERT (p);
  969. if (MigDbAttrib->NotOperator) {
  970. b = !(p (AttribParams, MigDbAttrib->Arguments));
  971. } else {
  972. b = p (AttribParams, MigDbAttrib->Arguments);
  973. }
  974. #ifdef DEBUG
  975. if (!g_ConfigOptions.Fast && InterestingFile) {
  976. DEBUGMSG ((
  977. DBG_TRACK,
  978. "Result of %s is %s",
  979. MigDb_GetAttributeName (MigDbAttrib->AttribIndex),
  980. b ? TEXT("TRUE") : TEXT("FALSE")
  981. ));
  982. }
  983. #endif
  984. return b;
  985. }
  986. BOOL
  987. pCallAction (
  988. IN PMIGDB_CONTEXT MigDbContext
  989. )
  990. /*++
  991. Routine Description:
  992. This routine calls an appropriate action for a specified migdb context.
  993. Arguments:
  994. MigDbContext - See definition.
  995. Return value:
  996. TRUE - if action function succeded
  997. FALSE - otherwise
  998. --*/
  999. {
  1000. PACTION_PROTOTYPE p;
  1001. BOOL b;
  1002. #ifdef DEBUG
  1003. TCHAR DbgBuf[512];
  1004. BOOL InterestingFile = FALSE;
  1005. MULTISZ_ENUM e;
  1006. PCTSTR FileName;
  1007. UINT FileCount = 0;
  1008. if (!g_ConfigOptions.Fast) {
  1009. //
  1010. // Dump out action information if this file is being tracked
  1011. //
  1012. if (EnumFirstMultiSz (&e, (PCTSTR) MigDbContext->FileList.Buf)) {
  1013. do {
  1014. //
  1015. // Check if this file is in [FilesToTrack] inside debug.inf
  1016. //
  1017. FileName = GetFileNameFromPath (e.CurrentString);
  1018. *DbgBuf = 0;
  1019. if (FileName) {
  1020. GetPrivateProfileString ("FilesToTrack", FileName, "", DbgBuf, ARRAYSIZE(DbgBuf), g_DebugInfPath);
  1021. }
  1022. if (!(*DbgBuf)) {
  1023. GetPrivateProfileString ("FilesToTrack", e.CurrentString, "", DbgBuf, ARRAYSIZE(DbgBuf), g_DebugInfPath);
  1024. }
  1025. FileCount++;
  1026. InterestingFile |= (*DbgBuf != 0);
  1027. } while (EnumNextMultiSz (&e));
  1028. if (InterestingFile) {
  1029. if (FileCount == 1) {
  1030. DEBUGMSG ((
  1031. DBG_TRACK,
  1032. "Calling action %s for %s",
  1033. MigDb_GetActionName (MigDbContext->ActionIndex),
  1034. (PCTSTR) MigDbContext->FileList.Buf
  1035. ));
  1036. } else {
  1037. wsprintf (DbgBuf, "Calling %s for:", MigDb_GetActionName (MigDbContext->ActionIndex));
  1038. LOGTITLE (DBG_TRACK, DbgBuf);
  1039. if (EnumFirstMultiSz (&e, (PCTSTR) MigDbContext->FileList.Buf)) {
  1040. do {
  1041. wsprintf (DbgBuf, " %s", e.CurrentString);
  1042. LOGLINE ((DbgBuf));
  1043. } while (EnumNextMultiSz (&e));
  1044. }
  1045. }
  1046. }
  1047. } else {
  1048. DEBUGMSG ((
  1049. DBG_TRACK,
  1050. "Calling action %s",
  1051. MigDb_GetActionName (MigDbContext->ActionIndex)
  1052. ));
  1053. }
  1054. }
  1055. #endif
  1056. p = MigDb_GetActionAddr (MigDbContext->ActionIndex);
  1057. MYASSERT (p);
  1058. b = p (MigDbContext);
  1059. #ifdef DEBUG
  1060. if (!g_ConfigOptions.Fast && InterestingFile) {
  1061. DEBUGMSG ((
  1062. DBG_TRACK,
  1063. "%s returned %s",
  1064. MigDb_GetActionName (MigDbContext->ActionIndex),
  1065. b ? "TRUE" : "FALSE"
  1066. ));
  1067. }
  1068. #endif
  1069. return b;
  1070. }
  1071. BOOL
  1072. pCheckContext (
  1073. IN PMIGDB_CONTEXT MigDbContext,
  1074. IN BOOL Handled
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. This routine checkes to see if a migdb context is met, that is if all sections
  1079. have Satisfied field TRUE.
  1080. Arguments:
  1081. MigDbContext - See definition.
  1082. Return value:
  1083. always TRUE
  1084. --*/
  1085. {
  1086. PMIGDB_SECTION migDbSection;
  1087. BOOL contextSelected;
  1088. BOOL result = FALSE;
  1089. migDbSection = MigDbContext->Sections;
  1090. contextSelected = TRUE;
  1091. while (migDbSection) {
  1092. if (!migDbSection->Satisfied) {
  1093. contextSelected = FALSE;
  1094. break;
  1095. }
  1096. migDbSection = migDbSection->Next;
  1097. }
  1098. if (contextSelected) {
  1099. MigDbContext->TriggerCount ++;
  1100. if (MigDbContext->ActionIndex == -1) {
  1101. //
  1102. // invalid index for action function
  1103. //
  1104. DEBUGMSG ((DBG_ERROR, "MigDb: Invalid action index"));
  1105. return FALSE;
  1106. }
  1107. //
  1108. // if appropriate call the action
  1109. //
  1110. if (MigDb_CallWhenTriggered (MigDbContext->ActionIndex)) {
  1111. if ((!Handled) ||
  1112. (MigDb_CallAlways (MigDbContext->ActionIndex))
  1113. ) {
  1114. if ((!MigDbContext->VirtualFile) ||
  1115. (MigDb_CanHandleVirtualFiles (MigDbContext->ActionIndex))
  1116. ) {
  1117. result = pCallAction (MigDbContext);
  1118. }
  1119. }
  1120. }
  1121. //clean up the grow buffer with file list
  1122. FreeGrowBuffer (&MigDbContext->FileList);
  1123. }
  1124. return result;
  1125. }
  1126. BOOL
  1127. MigDbTestFile (
  1128. IN OUT PFILE_HELPER_PARAMS Params
  1129. )
  1130. /*++
  1131. Routine Description:
  1132. This is a callback function called for every file scanned. If the file is not handled we try
  1133. to see if we have this file in database. If so then we check for attributes, update the migdb
  1134. context and if necessarry call the appropriate action.
  1135. Arguments:
  1136. Params - See definition.
  1137. Return value:
  1138. TRUE - if operation was successful
  1139. FALSE - otherwise
  1140. --*/
  1141. {
  1142. HASHITEM stringId;
  1143. PMIGDB_FILE migDbFile;
  1144. PMIGDB_ATTRIB migDbAttrib;
  1145. DBATTRIB_PARAMS attribParams;
  1146. BOOL fileSelected;
  1147. PCTSTR fileName;
  1148. PCTSTR fileExt;
  1149. FILE_LIST_STRUCT fileList;
  1150. // we don't check the Handled field here because the code will be carefull enough not
  1151. // to call actions that are not gathering informations if the Handled field is not 0.
  1152. fileName = GetFileNameFromPath (Params->FullFileSpec);
  1153. fileExt = GetFileExtensionFromPath (fileName);
  1154. #ifdef DEBUG
  1155. {
  1156. TCHAR DbgBuf[256];
  1157. if (GetPrivateProfileString ("MigDb", fileName, "", DbgBuf, 256, g_DebugInfPath) ||
  1158. GetPrivateProfileString ("MigDb", Params->FullFileSpec, "", DbgBuf, 256, g_DebugInfPath)
  1159. ) {
  1160. DEBUGMSG ((DBG_WHOOPS, "Ready to process %s", Params->FullFileSpec));
  1161. }
  1162. }
  1163. #endif
  1164. stringId = HtFindString (g_FileTable, fileName);
  1165. if (stringId) {
  1166. //The string table has extra data (a pointer to a MigDbFile node)
  1167. HtCopyStringData (g_FileTable, stringId, &fileList);
  1168. migDbFile = fileList.First;
  1169. while (migDbFile) {
  1170. //check all attributes for this file
  1171. migDbAttrib = migDbFile->Attributes;
  1172. fileSelected = TRUE;
  1173. if (!Params->VirtualFile) {
  1174. while (migDbAttrib != NULL) {
  1175. attribParams.FileParams = Params;
  1176. attribParams.ExtraData = migDbAttrib->ExtraData;
  1177. if (!CallAttribute (migDbAttrib, &attribParams)) {
  1178. fileSelected = FALSE;
  1179. break;
  1180. }
  1181. migDbAttrib = migDbAttrib->Next;
  1182. }
  1183. }
  1184. if (fileSelected) {
  1185. MYASSERT (migDbFile->Section);
  1186. //go to section and mark it as satisfied
  1187. migDbFile->Section->Satisfied = TRUE;
  1188. //go to context and mark there if this is a virtual file or not
  1189. migDbFile->Section->Context->VirtualFile = Params->VirtualFile;
  1190. //go to context and add there the file we found in file list
  1191. MultiSzAppend (&migDbFile->Section->Context->FileList, Params->FullFileSpec);
  1192. //check if context is satisfied and if so then call the appropriate action
  1193. if (pCheckContext (migDbFile->Section->Context, Params->Handled)) {
  1194. Params->Handled = TRUE;
  1195. }
  1196. }
  1197. migDbFile = migDbFile->Next;
  1198. }
  1199. }
  1200. if ((!Params->Handled) &&
  1201. (fileExt) &&
  1202. ((StringIMatch (fileExt, TEXT("VXD"))) ||
  1203. (StringIMatch (fileExt, TEXT("DRV"))) ||
  1204. (StringIMatch (fileExt, TEXT("SPD"))) ||
  1205. (StringIMatch (fileExt, TEXT("386")))) &&
  1206. (StringIMatchCharCount (g_WinDirWack, Params->FullFileSpec, g_WinDirWackChars))
  1207. ) {
  1208. DeleteFileWithWarning (Params->FullFileSpec);
  1209. Params->Handled = TRUE;
  1210. return TRUE;
  1211. }
  1212. return TRUE;
  1213. }
  1214. BOOL
  1215. pProcessMigrationLine (
  1216. IN PCTSTR Source,
  1217. IN PCTSTR Destination,
  1218. IN PCTSTR AppDir
  1219. )
  1220. /*++
  1221. Routine Description:
  1222. pProcessMigrationLine processes one line and adds the appropriate operations for files
  1223. or registry
  1224. Arguments:
  1225. Source - Specifies the source registry key/value or file.
  1226. Destination - Specifies the destination registry key/value or file.
  1227. If destination is same as source => handled
  1228. If destination is null => delete
  1229. Otherwise add a move operation
  1230. AppDir - Specifies the application directory, which is put in front of Line
  1231. when Line does not specify the drive but points to a file.
  1232. Return Value:
  1233. TRUE on success, FALSE on failure.
  1234. --*/
  1235. {
  1236. PCTSTR LocalSource = NULL;
  1237. PCTSTR LocalDestination = NULL;
  1238. DWORD Attribs;
  1239. PTSTR PathCopy;
  1240. PTSTR p;
  1241. BOOL Excluded;
  1242. TREE_ENUM TreeEnum;
  1243. CHAR NewDest[MEMDB_MAX];
  1244. //
  1245. // Is this HKLM or HKR? If so, go directly to MemDb.
  1246. //
  1247. if (StringIMatchCharCount (Source, TEXT("HKLM"), 4) ||
  1248. StringIMatchCharCount (Source, TEXT("HKR"), 3)
  1249. ) {
  1250. if (Destination) {
  1251. DEBUGMSG ((DBG_WHOOPS, "Handling and moving registry is not implemented. Do it yourself!"));
  1252. } else {
  1253. DEBUGMSG ((DBG_MIGDB, "Will uninstall %s", Source));
  1254. Suppress95Object (Source);
  1255. }
  1256. }
  1257. //
  1258. // Else this is a file/dir spec.
  1259. //
  1260. else {
  1261. if (_tcsnextc (_tcsinc (Source)) != ':') {
  1262. LocalSource = JoinPaths (AppDir, Source);
  1263. } else {
  1264. LocalSource = Source;
  1265. }
  1266. if ((Destination) && (_tcsnextc (_tcsinc (Destination)) != ':')) {
  1267. LocalDestination = JoinPaths (AppDir, Destination);
  1268. } else {
  1269. LocalDestination = Destination;
  1270. }
  1271. //
  1272. // Is this path excluded?
  1273. //
  1274. Excluded = FALSE;
  1275. if (!Excluded && LocalSource) {
  1276. PathCopy = DuplicatePathString (LocalSource, 0);
  1277. p = GetEndOfString (PathCopy);
  1278. do {
  1279. *p = 0;
  1280. if (IsPathExcluded (g_ExclusionValue, PathCopy)) {
  1281. DEBUGMSG ((DBG_MIGDB, "%s is excluded and will not be processed", LocalSource));
  1282. Excluded = TRUE;
  1283. break;
  1284. }
  1285. p = _tcsrchr (PathCopy, TEXT('\\'));
  1286. } while (p);
  1287. FreePathString (PathCopy);
  1288. }
  1289. if (!Excluded && LocalDestination) {
  1290. PathCopy = DuplicatePathString (LocalDestination, 0);
  1291. p = GetEndOfString (PathCopy);
  1292. do {
  1293. *p = 0;
  1294. if (IsPathExcluded (g_ExclusionValue, PathCopy)) {
  1295. DEBUGMSG ((DBG_MIGDB, "%s is excluded and will not be processed", LocalDestination));
  1296. Excluded = TRUE;
  1297. break;
  1298. }
  1299. p = _tcsrchr (PathCopy, TEXT('\\'));
  1300. } while (p);
  1301. FreePathString (PathCopy);
  1302. }
  1303. if (!Excluded) {
  1304. Attribs = QuietGetFileAttributes (LocalSource);
  1305. if (Attribs != 0xffffffff) {
  1306. if (LocalDestination) {
  1307. if (StringIMatch (LocalSource, LocalDestination)) {
  1308. //
  1309. // This object is handled
  1310. //
  1311. if (Attribs & FILE_ATTRIBUTE_DIRECTORY) {
  1312. HandleObject (LocalSource, S_DIRECTORY);
  1313. } else {
  1314. HandleObject (LocalSource, S_FILE);
  1315. }
  1316. } else {
  1317. //
  1318. // This object is moved
  1319. //
  1320. if (Attribs & FILE_ATTRIBUTE_DIRECTORY) {
  1321. if (EnumFirstFileInTree (&TreeEnum, LocalSource, NULL, TRUE)) {
  1322. StringCopy (NewDest, LocalDestination);
  1323. p = AppendWack (NewDest);
  1324. do {
  1325. DontTouchThisFile (TreeEnum.FullPath);
  1326. MYASSERT (*TreeEnum.SubPath != '\\');
  1327. StringCopy (p, TreeEnum.SubPath);
  1328. MarkFileForMove (TreeEnum.FullPath, NewDest);
  1329. } while (EnumNextFileInTree (&TreeEnum));
  1330. }
  1331. } else {
  1332. DontTouchThisFile (LocalSource);
  1333. MarkFileForMove (LocalSource, LocalDestination);
  1334. }
  1335. }
  1336. } else {
  1337. if (Attribs & FILE_ATTRIBUTE_DIRECTORY) {
  1338. MemDbSetValueEx (
  1339. MEMDB_CATEGORY_CLEAN_UP_DIR,
  1340. LocalSource,
  1341. NULL,
  1342. NULL,
  1343. 0,
  1344. NULL
  1345. );
  1346. } else {
  1347. DontTouchThisFile (LocalSource);
  1348. MarkFileForDelete (LocalSource);
  1349. }
  1350. }
  1351. }
  1352. ELSE_DEBUGMSG ((DBG_MIGDB, "pProcessMigrationLine: source %s not found", LocalSource));
  1353. }
  1354. if (LocalSource != Source) {
  1355. FreePathString (LocalSource);
  1356. }
  1357. if (LocalDestination != Destination) {
  1358. FreePathString (LocalDestination);
  1359. }
  1360. }
  1361. return TRUE;
  1362. // add this
  1363. /*
  1364. //
  1365. // Add the app dir to CleanUpDirs
  1366. //
  1367. MemDbSetValueEx (
  1368. MEMDB_CATEGORY_CLEAN_UP_DIR,
  1369. p,
  1370. NULL,
  1371. NULL,
  1372. 0,
  1373. NULL
  1374. );
  1375. */
  1376. }
  1377. BOOL
  1378. pProcessMigrationSection (
  1379. IN PCTSTR SectionName,
  1380. IN PCTSTR AppDir
  1381. )
  1382. /*++
  1383. Routine Description:
  1384. pProcessMigrationSection enumerates the caller-specified section and adds
  1385. appropriate operations for files and/or registry. This routine
  1386. supports the following environment variable
  1387. replacement:
  1388. %WINDIR%
  1389. %SYSTEMDIR%
  1390. %SYSTEM32DIR%
  1391. %SYSTEMDRIVE%
  1392. %USERPROFILE%
  1393. %APPDIR%
  1394. Arguments:
  1395. SectionName - Specifies the uninstall section name in migdb.inf.
  1396. AppDir - Specifies the directory where the installed app was found
  1397. Return Value:
  1398. TRUE if successful, or FALSE if an error occurrs. GetLastError provides
  1399. failure code.
  1400. --*/
  1401. {
  1402. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  1403. PCTSTR Source = NULL;
  1404. PCTSTR Destination = NULL;
  1405. PCTSTR NewSource = NULL;
  1406. PCTSTR NewDestination = NULL;
  1407. PCTSTR UserSource = NULL;
  1408. PCTSTR UserDestination = NULL;
  1409. TCHAR Drive[3];
  1410. USERENUM e;
  1411. MYASSERT (g_MigDbInf != INVALID_HANDLE_VALUE);
  1412. Drive[0] = g_SystemDir[0];
  1413. Drive[1] = g_SystemDir[1];
  1414. Drive[2] = 0;
  1415. if (InfFindFirstLine (g_MigDbInf, SectionName, NULL, &is)) {
  1416. do {
  1417. //
  1418. // Get INF line
  1419. //
  1420. Source = InfGetStringField (&is, 1);
  1421. Destination = InfGetStringField (&is, 2);
  1422. //
  1423. // Expand system environment variables
  1424. //
  1425. if (Source) {
  1426. ReplaceOneEnvVar (&NewSource, Source, S_WINDIR_ENV, g_WinDir);
  1427. ReplaceOneEnvVar (&NewSource, Source, S_SYSTEMDIR_ENV, g_SystemDir);
  1428. ReplaceOneEnvVar (&NewSource, Source, S_SYSTEM32DIR_ENV, g_System32Dir);
  1429. ReplaceOneEnvVar (&NewSource, Source, S_SYSTEMDRIVE_ENV, Drive);
  1430. ReplaceOneEnvVar (&NewSource, Source, S_APPDIR_ENV, AppDir);
  1431. ReplaceOneEnvVar (&NewSource, Source, S_PROGRAMFILES_ENV, g_ProgramFilesDir);
  1432. ReplaceOneEnvVar (&NewSource, Source, S_COMMONPROGRAMFILES_ENV, g_ProgramFilesCommonDir);
  1433. }
  1434. if (Destination) {
  1435. ReplaceOneEnvVar (&NewDestination, Destination, S_WINDIR_ENV, g_WinDir);
  1436. ReplaceOneEnvVar (&NewDestination, Destination, S_SYSTEMDIR_ENV, g_SystemDir);
  1437. ReplaceOneEnvVar (&NewDestination, Destination, S_SYSTEM32DIR_ENV, g_System32Dir);
  1438. ReplaceOneEnvVar (&NewDestination, Destination, S_SYSTEMDRIVE_ENV, Drive);
  1439. ReplaceOneEnvVar (&NewDestination, Destination, S_APPDIR_ENV, AppDir);
  1440. ReplaceOneEnvVar (&NewDestination, Destination, S_PROGRAMFILES_ENV, g_ProgramFilesDir);
  1441. ReplaceOneEnvVar (&NewDestination, Destination, S_COMMONPROGRAMFILES_ENV, g_ProgramFilesCommonDir);
  1442. }
  1443. if (NewSource) {
  1444. Source = NewSource;
  1445. }
  1446. if (NewDestination) {
  1447. Destination = NewDestination;
  1448. }
  1449. //
  1450. // If %USERPROFILE% exists in the string, then expand for all users
  1451. //
  1452. if (((Source) && (_tcsistr (Source, S_USERPROFILE_ENV))) ||
  1453. ((Destination) && (_tcsistr (Destination, S_USERPROFILE_ENV)))
  1454. ) {
  1455. if (EnumFirstUser (&e, ENUMUSER_ENABLE_NAME_FIX|ENUMUSER_DO_NOT_MAP_HIVE)) {
  1456. do {
  1457. //
  1458. // Skip invalid users and logon account
  1459. //
  1460. if (e.AccountType & (INVALID_ACCOUNT|DEFAULT_USER)) {
  1461. continue;
  1462. }
  1463. if (Source) {
  1464. UserSource = DuplicatePathString (Source, 0);
  1465. MYASSERT (UserSource);
  1466. ReplaceOneEnvVar (
  1467. &UserSource,
  1468. UserSource,
  1469. S_USERPROFILE_ENV,
  1470. e.OrgProfilePath
  1471. );
  1472. }
  1473. if (Destination) {
  1474. UserDestination = DuplicatePathString (Destination, 0);
  1475. MYASSERT (UserDestination);
  1476. ReplaceOneEnvVar (
  1477. &UserDestination,
  1478. UserDestination,
  1479. S_USERPROFILE_ENV,
  1480. e.OrgProfilePath
  1481. );
  1482. }
  1483. //
  1484. // Add the uninstall line for the user
  1485. //
  1486. pProcessMigrationLine (UserSource, UserDestination, AppDir);
  1487. if (UserSource) {
  1488. FreePathString (UserSource);
  1489. UserSource = NULL;
  1490. }
  1491. if (UserDestination) {
  1492. FreePathString (UserDestination);
  1493. UserDestination = NULL;
  1494. }
  1495. } while (EnumNextUser (&e));
  1496. }
  1497. } else {
  1498. //
  1499. // When %USERPROFILE% is not in the string, add the uninstall line
  1500. // for the system
  1501. //
  1502. pProcessMigrationLine (Source, Destination, AppDir);
  1503. }
  1504. //
  1505. // Free the expanded string
  1506. //
  1507. if (NewSource) {
  1508. FreePathString (NewSource);
  1509. NewSource = NULL;
  1510. }
  1511. if (NewDestination) {
  1512. FreePathString (NewDestination);
  1513. NewDestination = NULL;
  1514. }
  1515. } while (InfFindNextLine (&is));
  1516. }
  1517. InfCleanUpInfStruct (&is);
  1518. return TRUE;
  1519. }
  1520. DWORD
  1521. ProcessMigrationSections (
  1522. IN DWORD Request
  1523. )
  1524. /*++
  1525. Routine Description:
  1526. ProcessMigrationSections processes all sections in the memdb category
  1527. MigrationSections, generating memdb operations for files and registry
  1528. Arguments:
  1529. Request - Specifies weather the progress bar is being computed (REQUEST_QUERYTICKS),
  1530. or if the actual operation should be performed (REQUEST_RUN).
  1531. This routine estimates 1 tick for all of its operations. (It's pretty
  1532. fast.)
  1533. Return Value:
  1534. Win32 status code.
  1535. --*/
  1536. {
  1537. MEMDB_ENUM e;
  1538. PCTSTR p;
  1539. TCHAR SectionName[MEMDB_MAX];
  1540. switch (Request) {
  1541. case REQUEST_QUERYTICKS:
  1542. return TICKS_PROCESSMIGRATIONSECTIONS;
  1543. case REQUEST_RUN:
  1544. if (MemDbGetValueEx (&e, MEMDB_CATEGORY_MIGRATION_SECTION, NULL, NULL)) {
  1545. do {
  1546. p = _tcschr (e.szName, TEXT('\\'));
  1547. MYASSERT (p);
  1548. StringCopyAB (SectionName, e.szName, p);
  1549. p = _tcsinc (p);
  1550. if (CharCount (p) < 3) {
  1551. DEBUGMSG ((
  1552. DBG_WARNING,
  1553. "Ignoring migration section %s: Application detected in the root directory",
  1554. SectionName
  1555. ));
  1556. continue;
  1557. }
  1558. if (!pProcessMigrationSection (SectionName, p)) {
  1559. return GetLastError();
  1560. }
  1561. } while (MemDbEnumNextValue (&e));
  1562. }
  1563. return ERROR_SUCCESS;
  1564. }
  1565. MYASSERT (FALSE);
  1566. return 0;
  1567. }
  1568. BOOL
  1569. IsDisplayableCPL (
  1570. IN PCTSTR FileName
  1571. )
  1572. {
  1573. PCTSTR filePtr;
  1574. HINF infHandle = INVALID_HANDLE_VALUE;
  1575. PCTSTR infName = NULL;
  1576. PCTSTR field = NULL;
  1577. INFSTRUCT context = INITINFSTRUCT_POOLHANDLE;
  1578. BOOL result;
  1579. filePtr = GetFileNameFromPath (FileName);
  1580. if (!filePtr) {
  1581. return FALSE;
  1582. }
  1583. result = TRUE;
  1584. infName = JoinPaths (g_WinDir, TEXT("CONTROL.INI"));
  1585. __try {
  1586. infHandle = InfOpenInfFile (infName);
  1587. if (infHandle == INVALID_HANDLE_VALUE) {
  1588. __leave;
  1589. }
  1590. if (InfFindFirstLine (infHandle, TEXT("don't load"), NULL, &context)) {
  1591. do {
  1592. field = InfGetStringField (&context, 0);
  1593. if ((field != NULL) &&
  1594. ((StringIMatch (field, filePtr )) ||
  1595. (StringIMatch (field, FileName))
  1596. )) {
  1597. result = FALSE;
  1598. __leave;
  1599. }
  1600. }
  1601. while (InfFindNextLine (&context));
  1602. }
  1603. }
  1604. __finally {
  1605. if (infHandle != INVALID_HANDLE_VALUE) {
  1606. InfCloseInfFile (infHandle);
  1607. }
  1608. if (infName != NULL) {
  1609. FreePathString (infName);
  1610. }
  1611. InfCleanUpInfStruct(&context);
  1612. }
  1613. if (!result) {
  1614. return FALSE;
  1615. }
  1616. result = FALSE;
  1617. infName = JoinPaths (g_WinDir, TEXT("CONTROL.INI"));
  1618. __try {
  1619. infHandle = InfOpenInfFile (infName);
  1620. if (infHandle == INVALID_HANDLE_VALUE) {
  1621. __leave;
  1622. }
  1623. if (InfFindFirstLine (infHandle, TEXT("MMCPL"), NULL, &context)) {
  1624. do {
  1625. field = InfGetStringField (&context, 1);
  1626. if ((field != NULL) &&
  1627. ((StringIMatch (field, filePtr )) ||
  1628. (StringIMatch (field, FileName))
  1629. )) {
  1630. result = TRUE;
  1631. __leave;
  1632. }
  1633. }
  1634. while (InfFindNextLine (&context));
  1635. }
  1636. }
  1637. __finally {
  1638. if (infHandle != INVALID_HANDLE_VALUE) {
  1639. InfCloseInfFile (infHandle);
  1640. }
  1641. if (infName != NULL) {
  1642. FreePathString (infName);
  1643. }
  1644. InfCleanUpInfStruct(&context);
  1645. }
  1646. if (result) {
  1647. return TRUE;
  1648. }
  1649. if (StringIMatchAB (g_SystemDirWack, FileName, filePtr)) {
  1650. return TRUE;
  1651. }
  1652. if (StringIMatchAB (g_System32DirWack, FileName, filePtr)) {
  1653. return TRUE;
  1654. }
  1655. return FALSE;
  1656. }
  1657. BOOL
  1658. pGetCPLFriendlyName (
  1659. IN PCTSTR FileName,
  1660. IN OUT PGROWBUFFER FriendlyName
  1661. )
  1662. {
  1663. HANDLE cplInstance;
  1664. PCPL_PROTOTYPE cplMain;
  1665. LONG numEntries,i;
  1666. TCHAR localName[MEMDB_MAX];
  1667. UINT oldErrorMode;
  1668. PTSTR p, q;
  1669. LPCPLINFO info;
  1670. LPNEWCPLINFO newInfo;
  1671. UINT u;
  1672. oldErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS);
  1673. cplInstance = LoadLibrary (FileName);
  1674. if (!cplInstance) {
  1675. LOG ((LOG_ERROR, "Cannot load %s. Error:%ld", FileName, GetLastError()));
  1676. SetErrorMode (oldErrorMode);
  1677. return FALSE;
  1678. }
  1679. cplMain = (PCPL_PROTOTYPE)GetProcAddress (cplInstance, TEXT("CPlApplet"));
  1680. if (!cplMain) {
  1681. LOG ((LOG_ERROR, "Cannot get main entry point for %s. Error:%ld", FileName, GetLastError()));
  1682. SetErrorMode (oldErrorMode);
  1683. return FALSE;
  1684. }
  1685. if ((*cplMain) (NULL, CPL_INIT, 0, 0) == 0) {
  1686. (*cplMain) (NULL, CPL_EXIT, 0, 0);
  1687. LOG ((LOG_ERROR, "%s failed unexpectedly. Error:%ld", FileName, GetLastError()));
  1688. FreeLibrary (cplInstance);
  1689. SetErrorMode (oldErrorMode);
  1690. return FALSE;
  1691. }
  1692. numEntries = (*cplMain) (NULL, CPL_GETCOUNT, 0, 0);
  1693. if (numEntries == 0) {
  1694. (*cplMain) (NULL, CPL_EXIT, 0, 0);
  1695. FreeLibrary (cplInstance);
  1696. SetErrorMode (oldErrorMode);
  1697. DEBUGMSG ((DBG_WARNING, "CPL: No display info available for %s.", FileName));
  1698. return FALSE;
  1699. }
  1700. info = MemAlloc (g_hHeap, HEAP_ZERO_MEMORY, sizeof (CPLINFO) * numEntries);
  1701. newInfo = MemAlloc (g_hHeap, HEAP_ZERO_MEMORY, sizeof (NEWCPLINFO) * numEntries);
  1702. for (i=0;i<numEntries;i++) {
  1703. (*cplMain) (NULL, CPL_INQUIRE, i, (LONG)&info[i]);
  1704. (*cplMain) (NULL, CPL_NEWINQUIRE, i, (LONG)&newInfo[i]);
  1705. u = FriendlyName->End;
  1706. if (newInfo[i].szName[0]) {
  1707. MultiSzAppend (FriendlyName, newInfo[i].szName);
  1708. } else if (LoadString (cplInstance, info[i].idName, localName, MEMDB_MAX)) {
  1709. MultiSzAppend (FriendlyName, localName);
  1710. }
  1711. ELSE_DEBUGMSG ((DBG_ERROR, "CPL: Can't get string id %u", info[i].idName));
  1712. //
  1713. // Remove ampersands from the name
  1714. //
  1715. if (FriendlyName->End > u) {
  1716. q = p = (PTSTR) (FriendlyName->Buf + u);
  1717. while (*p) {
  1718. if (_tcsnextc (p) != TEXT('&')) {
  1719. _copytchar (q, p);
  1720. q = _tcsinc (q);
  1721. } else {
  1722. if (_tcsnextc (p + 1) == TEXT('&')) {
  1723. p++;
  1724. _copytchar (q, p);
  1725. q = _tcsinc (q);
  1726. }
  1727. }
  1728. p = _tcsinc (p);
  1729. }
  1730. *q = 0;
  1731. }
  1732. }
  1733. for (i=0;i<numEntries;i++) {
  1734. (*cplMain) (NULL, CPL_STOP, i, info[i].lData?info[i].lData:newInfo[i].lData);
  1735. }
  1736. (*cplMain) (NULL, CPL_EXIT, 0, 0);
  1737. FreeLibrary (cplInstance);
  1738. MemFree (g_hHeap, 0, newInfo);
  1739. MemFree (g_hHeap, 0, info);
  1740. SetErrorMode (oldErrorMode);
  1741. return (FriendlyName->Buf != NULL);
  1742. }
  1743. BOOL
  1744. ReportControlPanelApplet (
  1745. IN PCTSTR FileName,
  1746. IN PMIGDB_CONTEXT Context, OPTIONAL
  1747. IN DWORD ActType
  1748. )
  1749. {
  1750. GROWBUFFER friendlyName = GROWBUF_INIT;
  1751. MULTISZ_ENUM namesEnum;
  1752. PTSTR displayName = NULL;
  1753. PCTSTR reportEntry = NULL;
  1754. PTSTR component = NULL;
  1755. BOOL reportEntryIsResource = TRUE;
  1756. BOOL padName = FALSE;
  1757. PCTSTR temp1, temp2;
  1758. if ((Context != NULL) &&
  1759. (Context->SectLocalizedName != NULL)
  1760. ) {
  1761. MultiSzAppend (&friendlyName, Context->SectLocalizedName);
  1762. }
  1763. if (friendlyName.Buf == NULL) {
  1764. if (!pGetCPLFriendlyName (FileName, &friendlyName)) {
  1765. FreeGrowBuffer (&friendlyName);
  1766. return FALSE;
  1767. }
  1768. padName = TRUE;
  1769. }
  1770. MYASSERT (friendlyName.Buf);
  1771. if (EnumFirstMultiSz (&namesEnum, friendlyName.Buf)) {
  1772. do {
  1773. if (padName) {
  1774. displayName = (PTSTR)ParseMessageID (MSG_NICE_PATH_CONTROL_PANEL, &namesEnum.CurrentString);
  1775. } else {
  1776. displayName = DuplicatePathString (namesEnum.CurrentString, 0);
  1777. }
  1778. MYASSERT (displayName);
  1779. switch (ActType) {
  1780. case ACT_MINORPROBLEMS:
  1781. reportEntry = GetStringResource (MSG_MINOR_PROBLEM_ROOT);
  1782. break;
  1783. case ACT_INCOMPATIBLE:
  1784. case ACT_INC_NOBADAPPS:
  1785. case ACT_INC_IHVUTIL:
  1786. case ACT_INC_PREINSTUTIL:
  1787. case ACT_INC_SIMILAROSFUNC:
  1788. temp1 = GetStringResource (MSG_INCOMPATIBLE_ROOT);
  1789. if (!temp1) {
  1790. break;
  1791. }
  1792. switch (ActType) {
  1793. case ACT_INC_SIMILAROSFUNC:
  1794. temp2 = GetStringResource (MSG_INCOMPATIBLE_UTIL_SIMILAR_FEATURE_SUBGROUP);
  1795. break;
  1796. case ACT_INC_PREINSTUTIL:
  1797. temp2 = GetStringResource (MSG_INCOMPATIBLE_PREINSTALLED_UTIL_SUBGROUP);
  1798. break;
  1799. case ACT_INC_IHVUTIL:
  1800. temp2 = GetStringResource (MSG_INCOMPATIBLE_HW_UTIL_SUBGROUP);
  1801. break;
  1802. default:
  1803. temp2 = GetStringResource (
  1804. Context && Context->Message ?
  1805. MSG_INCOMPATIBLE_DETAIL_SUBGROUP :
  1806. MSG_TOTALLY_INCOMPATIBLE_SUBGROUP
  1807. );
  1808. break;
  1809. }
  1810. if (!temp2) {
  1811. break;
  1812. }
  1813. reportEntry = JoinPaths (temp1, temp2);
  1814. reportEntryIsResource = FALSE;
  1815. FreeStringResource (temp1);
  1816. FreeStringResource (temp2);
  1817. break;
  1818. case ACT_INC_SAFETY:
  1819. temp1 = GetStringResource (MSG_INCOMPATIBLE_ROOT);
  1820. if (!temp1) {
  1821. break;
  1822. }
  1823. temp2 = GetStringResource (MSG_REMOVED_FOR_SAFETY_SUBGROUP);
  1824. if (!temp2) {
  1825. break;
  1826. }
  1827. reportEntry = JoinPaths (temp1, temp2);
  1828. reportEntryIsResource = FALSE;
  1829. FreeStringResource (temp1);
  1830. FreeStringResource (temp2);
  1831. break;
  1832. case ACT_REINSTALL:
  1833. temp1 = GetStringResource (MSG_REINSTALL_ROOT);
  1834. if (!temp1) {
  1835. break;
  1836. }
  1837. temp2 = GetStringResource (
  1838. Context && Context->Message ?
  1839. MSG_REINSTALL_DETAIL_SUBGROUP :
  1840. MSG_REINSTALL_LIST_SUBGROUP
  1841. );
  1842. if (!temp2) {
  1843. break;
  1844. }
  1845. reportEntry = JoinPaths (temp1, temp2);
  1846. reportEntryIsResource = FALSE;
  1847. FreeStringResource (temp1);
  1848. FreeStringResource (temp2);
  1849. break;
  1850. case ACT_REINSTALL_BLOCK:
  1851. temp1 = GetStringResource (MSG_BLOCKING_ITEMS_ROOT);
  1852. if (!temp1) {
  1853. break;
  1854. }
  1855. temp2 = GetStringResource (MSG_REINSTALL_BLOCK_ROOT);
  1856. if (!temp2) {
  1857. break;
  1858. }
  1859. reportEntry = JoinPaths (temp1, temp2);
  1860. reportEntryIsResource = FALSE;
  1861. FreeStringResource (temp1);
  1862. FreeStringResource (temp2);
  1863. break;
  1864. default:
  1865. LOG((LOG_ERROR, "Bad parameter found while processing control panel applets: %u", ActType));
  1866. return FALSE;
  1867. }
  1868. if (!reportEntry) {
  1869. LOG((LOG_ERROR, "Could not read resources while processing control panel applets: %u", ActType));
  1870. break;
  1871. } else {
  1872. component = JoinPaths (reportEntry, displayName);
  1873. MsgMgr_ObjectMsg_Add (FileName, component, Context ? Context->Message : NULL);
  1874. FreePathString (component);
  1875. if (reportEntryIsResource) {
  1876. FreeStringResource (reportEntry);
  1877. } else {
  1878. FreePathString (reportEntry);
  1879. reportEntryIsResource = TRUE;
  1880. }
  1881. reportEntry = NULL;
  1882. }
  1883. if (padName) {
  1884. FreeStringResourcePtrA (&displayName);
  1885. } else {
  1886. FreePathString (displayName);
  1887. }
  1888. } while (EnumNextMultiSz (&namesEnum));
  1889. }
  1890. FreeGrowBuffer (&friendlyName);
  1891. return TRUE;
  1892. }