/*++ Copyright (c) 2000 Microsoft Corporation Module Name: components.c Abstract: Implements a set of APIs for the purposes of allowing the application layer to select module functionality. Author: Jim Schmidt (jimschm) 07-Aug-2000 Revision History: --*/ // // Includes // #include "pch.h" #include "ism.h" #include "ismp.h" #define DBG_COMP "Comp" // // Strings // #define S_COMPONENT_ROOT TEXT("Components") #define S_USER_SUPPLIED TEXT("User") #define S_MODULE_SUPPLIED TEXT("Module") // // Constants // #define MAX_COMPONENT_SPEC 127 #define MAX_COMPONENT_SPEC_PLUS_NUL (MAX_COMPONENT_SPEC+1) #define MAX_CONTROLLED_NODE_SIZE (MAX_COMPONENT_SPEC_PLUS_NUL + \ ARRAYSIZE(S_COMPONENT_ROOT) + \ ARRAYSIZE(S_MODULE_SUPPLIED) + 16) #define MEMDB_FLAG_PREFERRED 1 #define MEMDB_FLAG_SELECTED 1 // // Macros // // none // // Types // typedef enum { CES_DONE = 0, CES_FIRST_COMPONENT, CES_NEXT_COMPONENT, CES_FIRST_ALIAS, CES_NEXT_ALIAS, CES_CHECK_ALIAS_FLAGS } COMPONENTENUMSTATE; typedef struct { MEMDB_ENUM ComponentEnumStruct; MEMDB_ENUM AliasEnumStruct; BOOL EnumAliases; UINT GroupIdFilter; BOOL EnumEnabled; BOOL EnumDisabled; BOOL EnumPreferredOnly; BOOL EnumNonPreferredOnly; COMPONENTENUMSTATE State; } COMPONENTENUM_HANDLE, *PCOMPONENTENUM_HANDLE; // // Globals // // none // // Macro expansion list // // none // // Private function prototypes // // none // // Macro expansion definition // // none // // Code // BOOL pCheckCompChar ( IN CHARTYPE Char, IN BOOL CheckDecoration ) { // // Process decoration chars // if (Char == TEXT('$') || Char == TEXT('@') || Char == TEXT('~') || Char == TEXT('#')) { return CheckDecoration; } if (CheckDecoration) { return FALSE; } // // Block illegal chars // if (Char == TEXT('\"') || Char == TEXT('*') || Char == TEXT('?') || Char== TEXT('\\') || Char == TEXT('%') || Char == TEXT(';') ) { return FALSE; } // // Make sure char is printable // if (Char < 33 || Char > 126) { return FALSE; } return TRUE; } BOOL pCheckComponentName ( IN PCTSTR ComponentString ) { BOOL result = FALSE; PCTSTR end; PCTSTR begin; // // Check for a non-empty spec // if (ComponentString && ComponentString[0]) { // // Allow for decoration // end = ComponentString; while (pCheckCompChar ((CHARTYPE) _tcsnextc (end), TRUE)) { end = _tcsinc (end); } // // Now enforce the name character set: non-decorated characters and no // more than MAX_COMPONENT_SPEC characters. Allow spaces in the middle. // begin = end; while (*end) { if (!pCheckCompChar ((CHARTYPE) _tcsnextc (end), FALSE)) { if (_tcsnextc (end) == TEXT(' ')) { if (!end[1] || end == begin) { break; } } else { break; } } end = _tcsinc (end); } if (!(*end) && *begin) { if (end - ComponentString <= MAX_COMPONENT_SPEC) { result = TRUE; } } } if (!result) { SetLastError (ERROR_INVALID_PARAMETER); DEBUGMSG ((DBG_ERROR, "%s is not a valid component name", ComponentString)); } return result; } BOOL pFindComponent ( IN PCTSTR LocalizedAlias, IN UINT ComponentGroupId, OUT PCTSTR *ComponentKey, OPTIONAL OUT PCTSTR *AliasKey OPTIONAL ) { MEMDB_ENUM component; PCTSTR memdbNode = NULL; PCTSTR encodedUserAlias; PCTSTR encodedModuleAlias; TCHAR number[32]; BOOL result = FALSE; // // Find the component based on the localized alias // wsprintf (number, TEXT("\\%s\\%04u"), S_USER_SUPPLIED, ComponentGroupId); encodedUserAlias = JoinPaths (number, LocalizedAlias); wsprintf (number, TEXT("\\%s\\%04u"), S_MODULE_SUPPLIED, ComponentGroupId); encodedModuleAlias = JoinPaths (number, LocalizedAlias); if (MemDbEnumFirst ( &component, S_COMPONENT_ROOT TEXT("\\*"), ENUMFLAG_ALL, 1, 1 )) { do { memdbNode = JoinText (component.FullKeyName, encodedModuleAlias); if (MemDbTestKey (memdbNode)) { break; } FreeText (memdbNode); memdbNode = NULL; memdbNode = JoinText (component.FullKeyName, encodedUserAlias); if (MemDbTestKey (memdbNode)) { break; } FreeText (memdbNode); memdbNode = NULL; } while (MemDbEnumNext (&component)); } if (memdbNode) { if (ComponentKey) { *ComponentKey = DuplicateText (component.FullKeyName); } if (AliasKey) { *AliasKey = memdbNode; memdbNode = NULL; } MemDbAbortEnum (&component); result = TRUE; } FreeText (memdbNode); INVALID_POINTER (memdbNode); FreePathString (encodedUserAlias); INVALID_POINTER (encodedUserAlias); FreePathString (encodedModuleAlias); INVALID_POINTER (encodedModuleAlias); return result; } BOOL WINAPI IsmSelectPreferredAlias ( IN PCTSTR ComponentString, IN PCTSTR LocalizedAlias, OPTIONAL IN UINT ComponentGroupId OPTIONAL ) /*++ Routine Description: IsmSelectPreferredAlias marks a specific alias as the "preferred" one, so that the UI knows what to display. If LocalizedAlias is not specified, none of the aliases are preferred. A component can have only one preferred localized alias. If another alias is selected as preferred, it will be deselected automatically. Arguments: ComponentString - Specifies the non-displayed component identifier LocalizedAlias - Specifies the displayable string to mark as "preferred," or NULL to remove the preferred flag from the component. ComponentGroupId - Specifies the group ID for LocalizedAlias. Required if LocalizedAlias is not NULL. Return Value: TRUE if selection (or deselection) succeeded, FALSE if LocalizedAlias does not exist. --*/ { MEMDB_ENUM e; TCHAR number[32]; PCTSTR memdbNode = NULL; PCTSTR baseOfPattern; PCTSTR enumPattern; PCTSTR groupedAlias; BOOL result; if (!ComponentString || (LocalizedAlias && !ComponentGroupId)) { MYASSERT (FALSE); SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } result = (LocalizedAlias == NULL); // // Build enumeration string Components\\* // baseOfPattern = JoinPaths (S_COMPONENT_ROOT, ComponentString); enumPattern = JoinPaths (baseOfPattern, TEXT("*")); FreePathString (baseOfPattern); INVALID_POINTER (baseOfPattern); if (LocalizedAlias) { wsprintf (number, TEXT("%04u"), ComponentGroupId); groupedAlias = JoinPaths (number, LocalizedAlias); } else { groupedAlias = NULL; } if (MemDbEnumFirst ( &e, enumPattern, ENUMFLAG_NORMAL, 3, ENUMLEVEL_ALLLEVELS )) { do { if (groupedAlias && StringIMatch (e.KeyName, groupedAlias)) { MemDbSetFlags (e.FullKeyName, MEMDB_FLAG_PREFERRED, MEMDB_FLAG_PREFERRED); result = TRUE; } else { MemDbSetFlags (e.FullKeyName, 0, MEMDB_FLAG_PREFERRED); } } while (MemDbEnumNext (&e)); } FreePathString (enumPattern); INVALID_POINTER (enumPattern); FreePathString (groupedAlias); INVALID_POINTER (groupedAlias); return result; } BOOL WINAPI IsmAddComponentAlias ( IN PCTSTR ComponentString, OPTIONAL IN UINT MasterGroup, IN PCTSTR LocalizedAlias, IN UINT ComponentGroupId, IN BOOL UserSupplied ) /*++ Routine Description: IsmAddComponentAlias associates a display string (LocalizedAlias) with a logical component tag (ComponentString). Arguments: ComponentString - Specifies the identifier of the component. This identifier is not used for display purposes. MasterGroup - Specifies a MASTERGROUP_xxx constant, which organizes the components into major groups such as system settings and app settings (to simplify selection). LocalizedAliais - The displayable text. It is a localized component name, a path, a file, etc. ComponentGroupId - An arbitrary numeric ID defined outside of the ISM. This ID is used to implement requirements specific to the app layer. It allows for arbitrary idenfication and grouping. UserSupplied - Specifies TRUE if the end-user supplied this info, FALSE if it is built into the migration package. Return Value: A flag indicating success or failure. --*/ { PCTSTR memdbNode; TCHAR workNode[MAX_CONTROLLED_NODE_SIZE]; static UINT sequencer = 0; TCHAR madeUpComponent[MAX_COMPONENT_SPEC_PLUS_NUL]; BOOL b; BOOL newComponent = FALSE; // // Components are kept in memdb in the form of // // Component\\\ = // // is stored as a 4 digit number (such as 0001) // // Component\ = , // // // Validate arguments // if (ComponentGroupId > 9999) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } if (!ComponentString || !ComponentString[0]) { wsprintf (madeUpComponent, TEXT("Component %u"), ++sequencer); ComponentString = madeUpComponent; } else if (!pCheckComponentName (ComponentString)) { return FALSE; } if (!MasterGroup || MasterGroup >= MASTERGROUP_ALL) { SetLastError (ERROR_INVALID_PARAMETER); DEBUGMSG ((DBG_ERROR, "MasterGroup is invalid")); return FALSE; } // // See if the component already exists // // This is safe since ComponentString was already checked for length by pCheckComponentName wsprintf (workNode, TEXT("%s\\%s"), S_COMPONENT_ROOT, ComponentString); if (pFindComponent (LocalizedAlias, ComponentGroupId, &memdbNode, NULL)) { if (StringIMatch (workNode, memdbNode)) { DEBUGMSG ((DBG_VERBOSE, "Alias %s already exists; not adding it again", LocalizedAlias)); } else { DEBUGMSG (( DBG_WARNING, "Alias %s is in use by component %s; not adding it again", LocalizedAlias, _tcschr (memdbNode, TEXT('\\')) + 1 )); } FreeText (memdbNode); return FALSE; } // // Create the component if it doesn't exist, and then add the alias // if (!MemDbTestKey (workNode)) { if (!MemDbSetValueAndFlags (workNode, MasterGroup, MEMDB_FLAG_SELECTED, MEMDB_FLAG_SELECTED)) { EngineError (); return FALSE; } newComponent = TRUE; } // This is safe since ComponentString was already checked for length by pCheckComponentName wsprintf ( workNode, TEXT("%s\\%s\\%s\\%04u"), S_COMPONENT_ROOT, ComponentString, UserSupplied ? S_USER_SUPPLIED : S_MODULE_SUPPLIED, ComponentGroupId ); memdbNode = JoinPaths (workNode, LocalizedAlias); if (newComponent) { b = MemDbSetFlags (memdbNode, MEMDB_FLAG_PREFERRED, MEMDB_FLAG_PREFERRED); } else { b = MemDbSetKey (memdbNode); } FreePathString (memdbNode); if (!b) { EngineError (); return FALSE; } return TRUE; } BOOL WINAPI IsmSelectComponent ( IN PCTSTR ComponentOrAlias, IN UINT ComponentGroupId, OPTIONAL IN BOOL Enable ) { PCTSTR memdbNode = NULL; UINT flags; BOOL b; if (ComponentGroupId > 9999) { SetLastError (ERROR_INVALID_PARAMETER); DEBUGMSG ((DBG_ERROR, "Invalid component group")); return FALSE; } if (!ComponentOrAlias || !ComponentOrAlias[0]) { SetLastError (ERROR_INVALID_PARAMETER); DEBUGMSG ((DBG_ERROR, "Invalid localized alias")); return FALSE; } if (!ComponentGroupId) { if (!pCheckComponentName (ComponentOrAlias)) { return FALSE; } memdbNode = JoinText (S_COMPONENT_ROOT TEXT("\\"), ComponentOrAlias); if (!MemDbTestKey (memdbNode)) { FreeText (memdbNode); return FALSE; } } else { if (!pFindComponent (ComponentOrAlias, ComponentGroupId, &memdbNode, NULL)) { SetLastError (ERROR_NO_SUCH_ALIAS); return FALSE; } } flags = Enable ? MEMDB_FLAG_SELECTED : 0; b = MemDbSetFlags (memdbNode, flags, MEMDB_FLAG_SELECTED); FreeText (memdbNode); if (!b) { EngineError (); return FALSE; } return TRUE; } BOOL pEnumWorker ( IN OUT PMIG_COMPONENT_ENUM EnumPtr, IN OUT PCOMPONENTENUM_HANDLE Handle ) { BOOL result = FALSE; PCTSTR pattern; PCTSTR p; BOOL enabled; while (Handle->State != CES_DONE) { switch (Handle->State) { case CES_FIRST_COMPONENT: if (!MemDbEnumFirst ( &Handle->ComponentEnumStruct, S_COMPONENT_ROOT TEXT("\\*"), ENUMFLAG_ALL, 1, 1 )) { Handle->State = CES_DONE; } else { if (Handle->EnumAliases) { Handle->State = CES_FIRST_ALIAS; } else { Handle->State = CES_NEXT_COMPONENT; result = TRUE; } } break; case CES_FIRST_ALIAS: enabled = ((Handle->ComponentEnumStruct.Flags & MEMDB_FLAG_SELECTED) != 0); if ((!Handle->EnumEnabled && enabled) || (!Handle->EnumDisabled && !enabled) ) { Handle->State = CES_NEXT_COMPONENT; continue; } EnumPtr->Instance = 0; pattern = JoinPaths (Handle->ComponentEnumStruct.FullKeyName, TEXT("*")); if (!MemDbEnumFirst ( &Handle->AliasEnumStruct, pattern, ENUMFLAG_NORMAL, 4, ENUMLEVEL_ALLLEVELS )) { Handle->State = CES_NEXT_COMPONENT; } else { Handle->State = CES_CHECK_ALIAS_FLAGS; } FreePathString (pattern); break; case CES_NEXT_ALIAS: if (EnumPtr->SkipToNextComponent) { MemDbAbortEnum (&Handle->AliasEnumStruct); Handle->State = CES_NEXT_COMPONENT; EnumPtr->SkipToNextComponent = FALSE; break; } if (!MemDbEnumNext (&Handle->AliasEnumStruct)) { Handle->State = CES_NEXT_COMPONENT; } else { Handle->State = CES_CHECK_ALIAS_FLAGS; } break; case CES_CHECK_ALIAS_FLAGS: EnumPtr->Preferred = ((Handle->AliasEnumStruct.Flags & MEMDB_FLAG_PREFERRED) != 0); Handle->State = CES_NEXT_ALIAS; if (Handle->EnumPreferredOnly) { result = EnumPtr->Preferred; } else if (Handle->EnumNonPreferredOnly) { result = !EnumPtr->Preferred; } else { result = TRUE; } break; case CES_NEXT_COMPONENT: if (!MemDbEnumNext (&Handle->ComponentEnumStruct)) { Handle->State = CES_DONE; } else { if (Handle->EnumAliases) { Handle->State = CES_FIRST_ALIAS; } else { Handle->State = CES_NEXT_COMPONENT; result = TRUE; } } break; default: Handle->State = CES_DONE; break; } if (result) { // // Fill in all of the caller enum struct fields // EnumPtr->SkipToNextComponent = FALSE; EnumPtr->ComponentString = Handle->ComponentEnumStruct.KeyName; EnumPtr->Enabled = ((Handle->ComponentEnumStruct.Flags & MEMDB_FLAG_SELECTED) != 0); EnumPtr->MasterGroup = Handle->ComponentEnumStruct.Value; if (Handle->EnumAliases) { p = _tcschr (Handle->AliasEnumStruct.FullKeyName, TEXT('\\')); MYASSERT (p); if (p) { p = _tcschr (p + 1, TEXT('\\')); MYASSERT (p); } if (p) { p++; if (_totlower (p[0]) == TEXT('u')) { EnumPtr->UserSupplied = TRUE; } else { EnumPtr->UserSupplied = FALSE; } p = _tcschr (p, TEXT('\\')); MYASSERT (p); } if (p) { p++; EnumPtr->GroupId = _tcstoul (p, (PTSTR *) (&p), 10); MYASSERT (p && p[0] == TEXT('\\')); } if (p) { EnumPtr->LocalizedAlias = p + 1; } // // If group ID filter was specified, loop until a match is found // if (Handle->GroupIdFilter && Handle->GroupIdFilter != EnumPtr->GroupId) { result = FALSE; continue; } } else { EnumPtr->Preferred = FALSE; EnumPtr->UserSupplied = FALSE; EnumPtr->GroupId = 0; EnumPtr->LocalizedAlias = NULL; } EnumPtr->Instance++; break; } } if (!result) { IsmAbortComponentEnum (EnumPtr); } return result; } BOOL WINAPI IsmEnumFirstComponent ( OUT PMIG_COMPONENT_ENUM EnumPtr, IN DWORD Flags, IN UINT GroupIdFilter OPTIONAL ) { PCOMPONENTENUM_HANDLE handle; ZeroMemory (EnumPtr, sizeof (MIG_COMPONENT_ENUM)); if (Flags & (COMPONENTENUM_PREFERRED_ONLY|COMPONENTENUM_NON_PREFERRED_ONLY)) { if (!(Flags & COMPONENTENUM_ALIASES)) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } if ((Flags & (COMPONENTENUM_PREFERRED_ONLY|COMPONENTENUM_NON_PREFERRED_ONLY)) == (COMPONENTENUM_PREFERRED_ONLY|COMPONENTENUM_NON_PREFERRED_ONLY) ) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } } EnumPtr->Handle = MemAllocUninit (sizeof (COMPONENTENUM_HANDLE)); handle = (PCOMPONENTENUM_HANDLE) EnumPtr->Handle; handle->EnumAliases = Flags & COMPONENTENUM_ALIASES ? TRUE : FALSE; handle->EnumEnabled = Flags & COMPONENTENUM_ENABLED ? TRUE : FALSE; handle->EnumDisabled = Flags & COMPONENTENUM_DISABLED ? TRUE : FALSE; handle->GroupIdFilter = GroupIdFilter; handle->EnumPreferredOnly = Flags & COMPONENTENUM_PREFERRED_ONLY; handle->EnumNonPreferredOnly = Flags & COMPONENTENUM_NON_PREFERRED_ONLY; handle->State = CES_FIRST_COMPONENT; return pEnumWorker (EnumPtr, handle); } BOOL WINAPI IsmEnumNextComponent ( IN OUT PMIG_COMPONENT_ENUM EnumPtr ) { PCOMPONENTENUM_HANDLE handle; handle = (PCOMPONENTENUM_HANDLE) EnumPtr->Handle; return pEnumWorker (EnumPtr, handle); } VOID WINAPI IsmAbortComponentEnum ( IN PMIG_COMPONENT_ENUM EnumPtr ZEROED ) { PCOMPONENTENUM_HANDLE handle; handle = (PCOMPONENTENUM_HANDLE) EnumPtr->Handle; if (handle) { if (handle->State == CES_NEXT_COMPONENT) { MemDbAbortEnum (&handle->ComponentEnumStruct); } if (handle->EnumAliases) { if (handle->State == CES_NEXT_ALIAS) { MemDbAbortEnum (&handle->AliasEnumStruct); } } FreeAlloc (handle); } ZeroMemory (EnumPtr, sizeof (MIG_COMPONENT_ENUM)); } VOID WINAPI IsmRemoveAllUserSuppliedComponents ( VOID ) { MEMDB_ENUM e; MULTISZ_ENUM listEnum; GROWBUFFER list = INIT_GROWBUFFER; // // Collect all the components that have user-supplied aliases. Then after // enum completes, delete them. We don't delete during the enum because it // is never a good idea to delete the item just enumerated, and then try // to continue enumerating. // if (MemDbEnumFirst ( &e, S_COMPONENT_ROOT TEXT("\\*\\") S_USER_SUPPLIED TEXT("\\*"), ENUMFLAG_ALL, 2, 2 )) { do { GbMultiSzAppend (&list, e.FullKeyName); } while (MemDbEnumNext (&e)); } if (EnumFirstMultiSz (&listEnum, (PCTSTR) list.Buf)) { do { MemDbDeleteTree (listEnum.CurrentString); } while (EnumNextMultiSz (&listEnum)); } GbFree (&list); } BOOL WINAPI IsmSelectMasterGroup ( IN UINT MasterGroup, IN BOOL Enable ) { MEMDB_ENUM e; UINT flags; if (MasterGroup > MASTERGROUP_ALL) { SetLastError (ERROR_INVALID_PARAMETER); DEBUGMSG ((DBG_ERROR, "Can't select invalid MasterGroup")); return FALSE; } // // Enumerate all components and mark them enabled or disabled // depending on the master group // if (MemDbEnumFirst ( &e, S_COMPONENT_ROOT TEXT("\\*"), ENUMFLAG_NORMAL, 1, 1 )) { do { if (MasterGroup == MASTERGROUP_ALL || MasterGroup == e.Value ) { flags = Enable ? MEMDB_FLAG_SELECTED : 0; } else { flags = 0; } if (!MemDbSetFlags (e.FullKeyName, flags, MEMDB_FLAG_SELECTED)) { EngineError (); MemDbAbortEnum (&e); return FALSE; } } while (MemDbEnumNext (&e)); } return TRUE; } BOOL WINAPI IsmIsComponentSelected ( IN PCTSTR ComponentOrAlias, IN UINT ComponentGroupId OPTIONAL ) { UINT flags = 0; TCHAR memdbNode[MAX_CONTROLLED_NODE_SIZE]; PCTSTR componentNode; if (!ComponentGroupId) { if (!pCheckComponentName (ComponentOrAlias)) { return FALSE; } // This is safe since ComponentOrAlias was already checked for length by pCheckComponentName wsprintf (memdbNode, TEXT("%s\\%s"), S_COMPONENT_ROOT, ComponentOrAlias); MemDbGetFlags (memdbNode, &flags); } else { if (pFindComponent (ComponentOrAlias, ComponentGroupId, &componentNode, NULL)) { MemDbGetFlags (componentNode, &flags); FreeText (componentNode); } } return (flags & MEMDB_FLAG_SELECTED) != 0; }