/*++ Copyright (c) 1997 Microsoft Corporation Module Name: namefix.c Abstract: This module generates memdb entries for names that are to be used on Windows NT. All names are validated, and incompatible names are placed in the NewNames memdb category. Names are organized into groups; within a group, each name must be unique, but across groups, duplicate names are allowed. For example, the name group "UserNames" holds all user names, and each user name must be unique. The user name can be the same as the computer name, because the computer name is stored in the "ComputerName" group. The code here is extendable for other types of name collisions. To add support for other types of names, simply add the group name to the NAME_GROUP_LIST macro expansion below, then implement three functions: pEnumGroupName pValidateGroupName pRecommendGroupName You'll replace GroupName in the function names above with the name of your actual group. The code in this module should be the only place where names are validated on the Win9x side. Author: Jim Schmidt (jimschm) 24-Dec-1997 Revision History: jimschm 21-Jan-1998 Commented macro expansion list, added g_DisableDomainChecks capability --*/ #include "pch.h" #include "cmn9xp.h" #include // nt\private\inc #define S_ILLEGAL_CHARS ILLEGAL_NAME_CHARS_STR TEXT("*") #define DBG_NAMEFIX "NameFix" #define MIN_UNLEN 20 // // TEST_ALL_INCOMPATIBLE will force all names to be considered incompatible // TEST_MANGLE_NAMES will force names to be invalid // //#define TEST_ALL_INCOMPATIBLE //#define TEST_MANGLE_NAMES /*++ Macro Expansion List Description: NAME_GROUP_LIST lists each name category, such as computer name, domain name, user name and so on. The macro expansion list automatically generates three function prototypes for each name category. Also, the message ID is used as the category identifier by external callers. Line Syntax: DEFMAC(GroupName, Id) Arguments: GroupName - Specifies the type of name. Must be a valid C function name. The macro expansion list will generate prototypes for: pEnum pValidate pRecommend where, of course, is replaced by the text in the macro declaration line. All three functions must be implemented in this source file. Id - Specifies the message ID giving the display name of the name group. The name group is displayed in the list box that the user sees when they are alerted there are some incompatible names on their machine. Id is also used to uniquely identify a name group in some of the routines below. Variables Generated From List: g_NameGroupRoutines --*/ #define NAME_GROUP_LIST \ DEFMAC(ComputerDomain, MSG_COMPUTERDOMAIN_CATEGORY) \ DEFMAC(Workgroup, MSG_WORKGROUP_CATEGORY) \ DEFMAC(UserName, MSG_USERNAME_CATEGORY) \ DEFMAC(ComputerName, MSG_COMPUTERNAME_CATEGORY) \ // // Macro expansion declarations // #define MAX_NAME 2048 typedef struct { // // The NAME_ENUM structure is passed uninitialized to pEnumGroupName // function. The same structure is passed unchanged to subsequent // calls to pEnumGroupName. Each enum function declares its // params in this struct. // union { struct { // // pEnumUser // USERENUM UserEnum; }; }; // // All enumeration routines must fill in the following if they // return TRUE: // TCHAR Name[MAX_NAME]; } NAME_ENUM, *PNAME_ENUM; typedef struct { PCTSTR GroupName; TCHAR AuthenticatingAgent[MAX_COMPUTER_NAME]; BOOL FromUserInterface; UINT FailureMsg; BOOL DomainLogonEnabled; } NAME_GROUP_CONTEXT, *PNAME_GROUP_CONTEXT; typedef BOOL (ENUM_NAME_PROTOTYPE)(PNAME_GROUP_CONTEXT Context, PNAME_ENUM EnumPtr, BOOL First); typedef ENUM_NAME_PROTOTYPE * ENUM_NAME_FN; typedef BOOL (VALIDATE_NAME_PROTOTYPE)(PNAME_GROUP_CONTEXT Context, PCTSTR NameCandidate); typedef VALIDATE_NAME_PROTOTYPE * VALIDATE_NAME_FN; typedef VOID (RECOMMEND_NAME_PROTOTYPE)(PNAME_GROUP_CONTEXT Context, PCTSTR InvalidName, PTSTR RecommendedNameBuf); typedef RECOMMEND_NAME_PROTOTYPE * RECOMMEND_NAME_FN; typedef struct { UINT NameId; PCTSTR GroupName; ENUM_NAME_FN Enum; VALIDATE_NAME_FN Validate; RECOMMEND_NAME_FN Recommend; NAME_GROUP_CONTEXT Context; } NAME_GROUP_ROUTINES, *PNAME_GROUP_ROUTINES; // // Automatic arrays and prototypes // // The prototoypes #define DEFMAC(x,id) ENUM_NAME_PROTOTYPE pEnum##x; NAME_GROUP_LIST #undef DEFMAC #define DEFMAC(x,id) VALIDATE_NAME_PROTOTYPE pValidate##x; NAME_GROUP_LIST #undef DEFMAC #define DEFMAC(x,id) RECOMMEND_NAME_PROTOTYPE pRecommend##x; NAME_GROUP_LIST #undef DEFMAC // The array of functions #define DEFMAC(x,id) {id, TEXT(#x), pEnum##x, pValidate##x, pRecommend##x}, NAME_GROUP_ROUTINES g_NameGroupRoutines[] = { NAME_GROUP_LIST /* , */ {0, NULL, NULL, NULL, NULL, NULL} }; // // Local prototypes // BOOL pDoesNameExistInMemDb ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR UserName ); // // Implementation // PNAME_GROUP_ROUTINES pGetNameGroupById ( IN UINT MessageId ) /*++ Routine Description: pGetNameGroupById finds a group by searching the list for a message ID. The message ID is the unique identifier for the group. Arguments: MessageId - Specifies the unique ID of the group to find Return Value: A pointer to the group struct, or NULL if one was not found. --*/ { INT i; for (i = 0 ; g_NameGroupRoutines[i].GroupName ; i++) { if (g_NameGroupRoutines[i].NameId == MessageId) { return &g_NameGroupRoutines[i]; } } return NULL; } PNAME_GROUP_CONTEXT pGetNameGroupContextById ( IN UINT MessageId ) /*++ Routine Description: pGetNameGroupById finds a group by searching the list for a message ID. The message ID is the unique identifier for the group. The return value is the context structure used by the group. Arguments: MessageId - Specifies the unique ID of the group to find Return Value: A pointer to the group's context struct, or NULL if one was not found. --*/ { INT i; for (i = 0 ; g_NameGroupRoutines[i].GroupName ; i++) { if (g_NameGroupRoutines[i].NameId == MessageId) { return &g_NameGroupRoutines[i].Context; } } return NULL; } BOOL pEnumComputerName ( IN PNAME_GROUP_CONTEXT Context, IN OUT PNAME_ENUM EnumPtr, IN BOOL First ) /*++ Routine Description: pEnumComputerName obtains the computer name and returns it in the EnumPtr struct. If no name is assigned to the computer, an empty string is returned. Arguments: Context - Unused (holds context about name group) EnumPtr - Receives the computer name First - Specifies TRUE on the first call to pEnumComputerName, or FALSE on subseqeuent calls to pEnumComputerName. Return Value: If First is TRUE, returns TRUE if a name is enumerated, or FALSE if the name is not valid. If First is FALSE, always returns FALSE. --*/ { DWORD Size; if (!First) { return FALSE; } // // Get the computer name // Size = sizeof (EnumPtr->Name) / sizeof (EnumPtr->Name[0]); if (!GetComputerName (EnumPtr->Name, &Size)) { EnumPtr->Name[0] = 0; } return TRUE; } BOOL pEnumUserName ( IN PNAME_GROUP_CONTEXT Context, IN OUT PNAME_ENUM EnumPtr, IN BOOL First ) /*++ Routine Description: pEnumUserName enumerates all users on the machine via the EnumFirstUser/ EnumNextUser APIs. It does not enumerate the fixed names. Like the other enumeration routines, this routine is called until it returns FALSE, so that all resources are cleaned up correctly. Arguments: Context - Unused (holds context about name group) EnumPtr - Specifies the current enumeration state. Receives the enumerated user name. First - Specifies TRUE on the first call to pEnumUserName, or FALSE on subseqeuent calls to pEnumUserName. Return Value: TRUE if a user was enumerated, or FALSE if all users were enumerated. --*/ { // // Enumerate the next user // if (First) { if (!EnumFirstUser (&EnumPtr->UserEnum, ENUMUSER_DO_NOT_MAP_HIVE)) { LOG ((LOG_ERROR, "No users to enumerate")); return FALSE; } } else { if (!EnumNextUser (&EnumPtr->UserEnum)) { return FALSE; } } // // Special case -- ignore default user // while (*EnumPtr->UserEnum.UserName == 0) { if (!EnumNextUser (&EnumPtr->UserEnum)) { return FALSE; } } // // Copy user name to name buffer // StringCopy (EnumPtr->Name, EnumPtr->UserEnum.UserName); return TRUE; } BOOL pEnumWorkgroup ( IN PNAME_GROUP_CONTEXT Context, IN OUT PNAME_ENUM EnumPtr, IN BOOL First ) /*++ Routine Description: pEnumWorkgroup obtains the workgroup name and returns it in the EnumPtr struct. If the VNETSUP support is not installed, or the workgroup name is empty, this routine returns FALSE. Arguments: Context - Receives AuthenticatingAgent value EnumPtr - Receives the computer domain name First - Specifies TRUE on the first call to pEnumWorkgroup, or FALSE on subseqeuent calls to pEnumWorkgroup. Return Value: If First is TRUE, returns TRUE if a name is enumerated, or FALSE if the name is not valid. If First is FALSE, always returns FALSE. --*/ { HKEY VnetsupKey; PCTSTR StrData; if (!First) { return FALSE; } EnumPtr->Name[0] = 0; // // Obtain the workgroup name into EnumPtr->Name // VnetsupKey = OpenRegKeyStr (S_VNETSUP); if (VnetsupKey) { StrData = GetRegValueString (VnetsupKey, S_WORKGROUP); if (StrData) { _tcssafecpy (EnumPtr->Name, StrData, MAX_COMPUTER_NAME); MemFree (g_hHeap, 0, StrData); } ELSE_DEBUGMSG ((DBG_WARNING, "pEnumWorkgroup: Workgroup value does not exist")); CloseRegKey (VnetsupKey); } ELSE_DEBUGMSG ((DBG_WARNING, "pEnumWorkgroup: VNETSUP key does not exist")); return EnumPtr->Name[0] != 0; } BOOL pEnumComputerDomain ( IN PNAME_GROUP_CONTEXT Context, IN OUT PNAME_ENUM EnumPtr, IN BOOL First ) /*++ Routine Description: pEnumComputerDomain obtains the workgroup name and returns it in the EnumPtr struct. If the MS Networking client is not installed, this routine returns FALSE. This routine also obtains the AuthenticatingAgent value, and stores it in the Context structure for use by pRecommendComputerDomain. Arguments: Context - Receives AuthenticatingAgent value EnumPtr - Receives the computer domain name First - Specifies TRUE on the first call to pEnumComputerDomain, or FALSE on subseqeuent calls to pEnumComputerDomain. Return Value: If First is TRUE, returns TRUE if a name is enumerated, or FALSE if the name is not valid. If First is FALSE, always returns FALSE. --*/ { HKEY Key; HKEY NetLogonKey; HKEY VnetsupKey; PBYTE Data; PCTSTR StrData; BOOL b = TRUE; if (!First) { return FALSE; } EnumPtr->Name[0] = 0; Context->DomainLogonEnabled = FALSE; // // Is the MS Networking client installed? // Key = OpenRegKeyStr (S_MSNP32); if (!Key) { // // MS Networking client is not installed. Return FALSE // because any Win9x workgroup name will work with NT. // DEBUGMSG ((DBG_NAMEFIX, "pEnumComputerDomain: MS Networking client is not installed.")); return FALSE; } __try { // // Determine if the domain logon is enabled // NetLogonKey = OpenRegKeyStr (S_LOGON_KEY); if (NetLogonKey) { Data = (PBYTE) GetRegValueBinary (NetLogonKey, S_LM_LOGON); if (Data) { if (*Data) { Context->DomainLogonEnabled = TRUE; } MemFree (g_hHeap, 0, Data); } CloseRegKey (NetLogonKey); } // // If no domain logon is enabled, return FALSE, because // any Win9x workgroup name will work with NT. // if (!Context->DomainLogonEnabled) { DEBUGMSG ((DBG_NAMEFIX, "pEnumComputerDomain: Domain logon is not enabled.")); b = FALSE; __leave; } // // Obtain the workgroup name into EnumPtr->Name; we will try // to use this as the NT computer domain. // VnetsupKey = OpenRegKeyStr (S_VNETSUP); if (VnetsupKey) { StrData = GetRegValueString (VnetsupKey, S_WORKGROUP); if (StrData) { _tcssafecpy (EnumPtr->Name, StrData, MAX_COMPUTER_NAME); MemFree (g_hHeap, 0, StrData); } ELSE_DEBUGMSG ((DBG_WARNING, "pEnumComputerDomain: Workgroup value does not exist")); CloseRegKey (VnetsupKey); } ELSE_DEBUGMSG ((DBG_WARNING, "pEnumComputerDomain: VNETSUP key does not exist")); // // Obtain the AuthenticatingAgent value from Key and stick it in // Context->AuthenticatingAgent // StrData = GetRegValueString (Key, S_AUTHENTICATING_AGENT); if (StrData) { // // Copy AuthenticatingAgent to enum struct // _tcssafecpy (Context->AuthenticatingAgent, StrData, MAX_COMPUTER_NAME); MemFree (g_hHeap, 0, StrData); } else { Context->AuthenticatingAgent[0] = 0; LOG ((LOG_ERROR,"Domain Logon enabled, but AuthenticatingAgent value is missing")); } } __finally { CloseRegKey (Key); } return b; } BOOL pValidateNetName ( OUT PNAME_GROUP_CONTEXT Context, OPTIONAL IN PCTSTR NameCandidate, IN BOOL SpacesAllowed, IN BOOL DotSpaceCheck, IN UINT MaxLength, OUT PCSTR *OffendingChar OPTIONAL ) /*++ Routine Description: pValidateNetName performs a check to see if the specified name is valid on NT 5. Arguments: Context - Receives the error message ID, if any error occurred. NameCandidate - Specifies the name to validate. SpacesAllowed - Specifies TRUE if spaces are allowed in the name, or FALSE if not. MaxLength - Specifies the max characters that can be in the name. DotSpaceCheck - Specifies TRUE if the name cannot consist only of a dot and a space, or FALSE if it can. OffendingChar - Receives the pointer to the character that caused the problem, or NULL if no error or the error was caused by something other than a character set mismatch or length test. Return Value: TRUE if the name is valid, or FALSE if it is not. --*/ { PCTSTR p; PCTSTR LastNonSpaceChar; CHARTYPE ch; BOOL allDigits; if (OffendingChar) { *OffendingChar = NULL; } // // Minimum length test // if (!NameCandidate[0]) { if (Context) { Context->FailureMsg = MSG_INVALID_EMPTY_NAME_POPUP; } return FALSE; } // // Maximum length test // if (CharCount (NameCandidate) > MaxLength) { if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_LENGTH_POPUP; } if (OffendingChar) { *OffendingChar = TcharCountToPointer (NameCandidate, MaxLength); } return FALSE; } // // No leading spaces allowed // if (_tcsnextc (NameCandidate) == TEXT(' ')) { if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_CHAR_POPUP; } if (OffendingChar) { *OffendingChar = NameCandidate; } return FALSE; } // // No invalid characters // ch = ' '; LastNonSpaceChar = NULL; allDigits = TRUE; for (p = NameCandidate ; *p ; p = _tcsinc (p)) { ch = _tcsnextc (p); if (_tcschr (S_ILLEGAL_CHARS, ch) != NULL || (ch == TEXT(' ') && !SpacesAllowed) ) { if (OffendingChar) { *OffendingChar = p; } if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_CHAR_POPUP; } return FALSE; } if (ch != TEXT('.') && ch != TEXT(' ')) { DotSpaceCheck = FALSE; } if (ch != TEXT(' ')) { LastNonSpaceChar = p; } if (allDigits) { if (ch < TEXT('0') || ch > TEXT('9')) { allDigits = FALSE; } } } if (allDigits) { if (OffendingChar) { *OffendingChar = NameCandidate; } if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_CHAR_POPUP; } return FALSE; } // // No trailing dot // if (ch == TEXT('.')) { MYASSERT (LastNonSpaceChar); if (OffendingChar) { *OffendingChar = LastNonSpaceChar; } if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_CHAR_POPUP; } return FALSE; } // // No trailing space // if (ch == TEXT(' ')) { MYASSERT (LastNonSpaceChar); if (OffendingChar) { *OffendingChar = _tcsinc (LastNonSpaceChar); } if (Context) { Context->FailureMsg = MSG_INVALID_COMPUTERNAME_CHAR_POPUP; } return FALSE; } // // Dot-space only check // if (DotSpaceCheck) { if (OffendingChar) { *OffendingChar = NameCandidate; } if (Context) { Context->FailureMsg = MSG_INVALID_USERNAME_SPACEDOT_POPUP; } return FALSE; } return TRUE; } BOOL ValidateDomainNameChars ( IN PCTSTR NameCandidate ) { return pValidateNetName ( NULL, NameCandidate, FALSE, FALSE, min (MAX_SERVER_NAME, DNLEN), NULL ); } BOOL ValidateUserNameChars ( IN PCTSTR NameCandidate ) { return pValidateNetName ( NULL, NameCandidate, TRUE, TRUE, min (MAX_USER_NAME, MIN_UNLEN), NULL ); } BOOL pValidateComputerName ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR NameCandidate ) /*++ Routine Description: pValidateComputerName makes sure the specified name is not empty, is not longer than 15 characters, and consists only of characters legal for a computer name. Also, if the name collides with a user name, then the computer name is invalid. Arguments: Context - Unused (holds context about name group) NameCandidate - Specifies name to validate Return Value: TRUE if the name is valid, FALSE otherwise. --*/ { BOOL b; //PNAME_GROUP_CONTEXT UserContext; //UserContext = pGetNameGroupContextById (MSG_USERNAME_CATEGORY); //if (pDoesNameExistInMemDb (UserContext, NameCandidate)) { // return FALSE; //} b = pValidateNetName ( Context, NameCandidate, FALSE, FALSE, MAX_COMPUTER_NAME, NULL ); return b; } BOOL pValidateWorkgroup ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR NameCandidate ) /*++ Routine Description: pValidateWorkgroup makes sure the specified name is not empty, is not longer than 15 characters, and consists only of characters legal for a computer name. If domain logon is enabled, this routine always returns TRUE. Arguments: Context - Unused (holds context about name group) NameCandidate - Specifies name to validate Return Value: TRUE if the name is valid, FALSE otherwise. --*/ { PNAME_GROUP_CONTEXT DomainContext; // // Return TRUE if domain is enabled // DomainContext = pGetNameGroupContextById (MSG_COMPUTERDOMAIN_CATEGORY); if (DomainContext && DomainContext->DomainLogonEnabled) { return TRUE; } // // A workgroup name is a domain name, but spaces are allowed. // return pValidateNetName (Context, NameCandidate, TRUE, FALSE, DNLEN, NULL); } BOOL pValidateUserName ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR NameCandidate ) /*++ Routine Description: pValidateUserName makes sure the specified name is not empty, is not longer than 20 characters, consists only of characters legal for a user name, and does not exist in memdb's NewName or InUseName categories. Arguments: Context - Specifies context info for the UserName name group, used for memdb operations. NameCandidate - Specifies name to validate Return Value: TRUE if the name is valid, FALSE otherwise. --*/ { BOOL b; // // Validate name // b = pValidateNetName (Context, NameCandidate, TRUE, TRUE, min (MAX_USER_NAME, MIN_UNLEN), NULL); if (!b && Context->FailureMsg == MSG_INVALID_COMPUTERNAME_LENGTH_POPUP) { Context->FailureMsg = MSG_INVALID_USERNAME_LENGTH_POPUP; } if (!b) { return FALSE; } // // Check for existence in memdb // if (pDoesNameExistInMemDb (Context, NameCandidate)) { Context->FailureMsg = MSG_INVALID_USERNAME_DUPLICATE_POPUP; return FALSE; } return TRUE; } BOOL pValidateComputerDomain ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR NameCandidate ) /*++ Routine Description: pValidateComputerDomain performs the same validatation that pValidateComputerName performs. Therefore, it simply calls pValidateComputerName. If the name comes from the registry, and not the user interface, then we check to see if the workgroup name actually refers to a domain controller. If it does, the name is returned as valid; otherwise, the name is returned as invalid, even though it may consist of valid characters. If the name comes from the user interface, we assume the UI code will do the validation to see if the name is an actual server. This allows the UI to override the API, because the API may not work properly on all networks. Arguments: Context - Specifies context of the ComputerDomain name group. In particular, the FromUserInterface member tells us to ignore validation of the domain name via the NetServerGetInfo API. NameCandidate - Specifies domain name to validate Return Value: TRUE if the domain name is legal, or FALSE if it is not. --*/ { TCHAR NewComputerName[MAX_COMPUTER_NAME]; if (!pValidateNetName (Context, NameCandidate, FALSE, FALSE, DNLEN, NULL)) { return FALSE; } if (!Context->FromUserInterface) { if (GetUpgradeComputerName (NewComputerName)) { // 1 == account was found, 0 == account does not exist, -1 == no response if (1 != DoesComputerAccountExistOnDomain ( NameCandidate, NewComputerName, TRUE )) { return FALSE; } } } return TRUE; } BOOL pCleanUpNetName ( IN PNAME_GROUP_CONTEXT Context, IN OUT PTSTR Name, IN UINT NameType ) /*++ Routine Description: pCleanUpNetName eliminates all characters that are invalid from the specified name. NOTE: We could add some smarts here, such as translation of spaces to dashes, and so on. Arguments: Context - Unused; passed along to pValidateComputerName. Name - Specifies the name containing potentially invalid characters. Receives the name with all invalid characters removed. NameType - Specifies the type of name to clean up Return Value: TRUE if the resulting name is valid, or FALSE if the resulting name is still not valid. --*/ { TCHAR TempBuf[MAX_COMPUTER_NAME]; PTSTR p; PTSTR q; UINT Len; BOOL b; // // Delete all the invalid characters // _tcssafecpy (TempBuf, Name, MAX_COMPUTER_NAME); for (;;) { p = NULL; b = FALSE; switch (NameType) { case MSG_COMPUTERNAME_CATEGORY: b = pValidateNetName (Context, TempBuf, TRUE, FALSE, MAX_COMPUTER_NAME, &p); break; case MSG_WORKGROUP_CATEGORY: b = pValidateNetName (Context, TempBuf, TRUE, FALSE, DNLEN, &p); break; case MSG_COMPUTERDOMAIN_CATEGORY: b = pValidateNetName (Context, TempBuf, FALSE, FALSE, DNLEN, &p); break; case MSG_USERNAME_CATEGORY: b = pValidateNetName (Context, TempBuf, TRUE, TRUE, MIN_UNLEN, &p); break; } if (b || !p) { break; } q = _tcsinc (p); Len = ByteCount (q) + sizeof (TCHAR); MoveMemory (p, q, Len); } if (b) { // // Do not allow names that contain a lot of invalid characters // if (CharCount (Name) - 3 > CharCount (TempBuf)) { b = FALSE; } } if (!b) { // Empty out recommended name *Name = 0; } if (b) { StringCopy (Name, TempBuf); switch (NameType) { case MSG_COMPUTERNAME_CATEGORY: b = pValidateComputerName (Context, Name); break; case MSG_WORKGROUP_CATEGORY: b = pValidateWorkgroup (Context, Name); break; case MSG_COMPUTERDOMAIN_CATEGORY: b = pValidateComputerDomain (Context, Name); break; case MSG_USERNAME_CATEGORY: b = pValidateUserName (Context, Name); break; } } return b; } VOID pRecommendComputerName ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR InvalidName, OUT PTSTR RecommendedName ) /*++ Routine Description: pRecommendComputerName obtains the current user's name and returns it for use as a computer name. If the user's name has characters that cannot be used in a computer name, the invalid characters are removed. If the name is still invalid, then a static string is returned. Arguments: Context - Unused (holds context about name group) InvalidName - Specifies the current invalid name, or an empty string if no name exists. RecommendedName - Receives the recommended name. Return Value: none --*/ { DWORD Size; PCTSTR p; PCTSTR ArgArray[1]; // // Try to clean up the invalid name // if (*InvalidName) { _tcssafecpy (RecommendedName, InvalidName, MAX_COMPUTER_NAME); if (pCleanUpNetName (Context, RecommendedName, MSG_COMPUTERNAME_CATEGORY)) { return; } } // // Generate a suggestion from the user name // Size = MAX_COMPUTER_NAME; if (!GetUserName (RecommendedName, &Size)) { *RecommendedName = 0; } else { CharUpper (RecommendedName); ArgArray[0] = RecommendedName; p = ParseMessageID (MSG_COMPUTER_REPLACEMENT_NAME, ArgArray); MYASSERT (p); if (p) { _tcssafecpy (RecommendedName, p, MAX_COMPUTER_NAME); FreeStringResource (p); } ELSE_DEBUGMSG ((DBG_ERROR, "Failed to parse message resource for MSG_COMPUTER_REPLACEMENT_NAME. Check localization.")); } // // Try to clean up invalid computer name chars in user name // if (pCleanUpNetName (Context, RecommendedName, MSG_COMPUTERNAME_CATEGORY)) { return; } // // All else has failed; obtain static computer name string // p = GetStringResource (MSG_RECOMMENDED_COMPUTER_NAME); MYASSERT (p); if (p) { StringCopy (RecommendedName, p); FreeStringResource (p); } ELSE_DEBUGMSG ((DBG_ERROR, "Failed to parse message resource for MSG_RECOMMENDED_COMPUTER_NAME. Check localization.")); } VOID pRecommendWorkgroup ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR InvalidName, OUT PTSTR RecommendedName ) /*++ Routine Description: pRecommendWorkgroupName tries to clean up the invalid workgroup name, and only if necessary recommends the name Workgroup. Arguments: Context - Unused (holds context about name group) InvalidName - Specifies the current invalid name, or an empty string if no name exists. RecommendedName - Receives the recommended name. Return Value: none --*/ { PCTSTR p; // // Try to clean up the invalid name // if (*InvalidName) { _tcssafecpy (RecommendedName, InvalidName, MAX_COMPUTER_NAME); if (pCleanUpNetName (Context, RecommendedName, MSG_WORKGROUP_CATEGORY)) { return; } } // // All else has failed; obtain static workgroup string // p = GetStringResource (MSG_RECOMMENDED_WORKGROUP_NAME); MYASSERT (p); StringCopy (RecommendedName, p); FreeStringResource (p); } VOID pRecommendUserName ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR InvalidName, OUT PTSTR RecommendedName ) /*++ Routine Description: pRecommendUserName tries to clean up the specified invalid user name. If that fails, this routine generates a generic user name (such as Windows User). If the generic name is not valid, numbers are appended until a unique, valid name is found. Arguments: Context - Specifies settings for the UserName name group context, including the group name itself. This context is used in memdb operations to validate the name. InvalidName - Specifies the current invalid name, or an empty string if no name exists. RecommendedName - Receives the recommended name. Return Value: none --*/ { PCTSTR p; UINT Sequencer; // // Attempt to clean out invalid characters from the user // name. // _tcssafecpy (RecommendedName, InvalidName, MAX_USER_NAME); if (pCleanUpNetName (Context, RecommendedName, MSG_USERNAME_CATEGORY)) { return; } // // If there are some characters left, and there is room for // a sequencer, just add the sequencer. // if (*RecommendedName) { p = DuplicateText (RecommendedName); MYASSERT (p); for (Sequencer = 1 ; Sequencer < 10 ; Sequencer++) { wsprintf (RecommendedName, TEXT("%s-%u"), p, Sequencer); if (pValidateUserName (Context, RecommendedName)) { break; } } FreeText (p); if (Sequencer < 10) { return; } } // // Obtain a generic name // p = GetStringResource (MSG_RECOMMENDED_USER_NAME); MYASSERT (p); if (p) { __try { if (pValidateUserName (Context, p)) { StringCopy (RecommendedName, p); } else { for (Sequencer = 2 ; Sequencer < 100000 ; Sequencer++) { wsprintf (RecommendedName, TEXT("%s %u"), p, Sequencer); if (pValidateUserName (Context, RecommendedName)) { break; } } if (Sequencer == 100000) { LOG ((LOG_ERROR, "Sequencer hit %u", Sequencer)); } } } __finally { FreeStringResource (p); } } ELSE_DEBUGMSG ((DBG_ERROR, "Could not retrieve string resource MSG_RECOMMENDED_USER_NAME. Check localization.")); } VOID pRecommendComputerDomain ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR InvalidName, OUT PTSTR RecommendedName ) /*++ Routine Description: pRecommendComputerDomain returns the value of AuthenticatingAgent stored in the Context structure by pEnumComputerDomain. Arguments: Context - Specifies the name group context structure, which holds the computer domain found by pEnumComputerDomain. InvalidName - Specifies the current invalid name, or an empty string if no name exists. RecommendedName - Receives the recommended name. Return Value: none --*/ { StringCopy (RecommendedName, Context->AuthenticatingAgent); } BOOL ValidateName ( IN HWND ParentWnd, OPTIONAL IN PCTSTR NameGroup, IN PCTSTR NameCandidate ) /*++ Routine Description: ValidateName is called by the UI to perform validation on the specified name. Arguments: ParentWnd - Specifies the handle used for popups that tell the user what is wrong with the name they entered. If NULL, no UI is generated. NameGroup - Specifies the name group, a static string that defines what characters are legal for the name. NameCandidate - Specifies the name to validate Return Value: TRUE if the name is valid, or FALSE if it is not valid. --*/ { INT i; BOOL b; // // Scan the g_NameGroupRoutines array for the name grup // for (i = 0 ; g_NameGroupRoutines[i].GroupName ; i++) { if (StringIMatch (g_NameGroupRoutines[i].GroupName, NameGroup)) { break; } } if (!g_NameGroupRoutines[i].GroupName) { DEBUGMSG ((DBG_WHOOPS, "ValidateName: Don't know how to validate %s names", NameGroup)); LOG ((LOG_ERROR, "Don't know how to validate %s names", NameGroup)); return TRUE; } g_NameGroupRoutines[i].Context.FromUserInterface = TRUE; b = g_NameGroupRoutines[i].Validate (&g_NameGroupRoutines[i].Context, NameCandidate); if (!b && ParentWnd) { OkBox (ParentWnd, g_NameGroupRoutines[i].Context.FailureMsg); } g_NameGroupRoutines[i].Context.FromUserInterface = FALSE; return b; } BOOL pDoesNameExistInMemDb ( IN PNAME_GROUP_CONTEXT Context, IN PCTSTR Name ) /*++ Routine Description: pDoesUserExistInMemDb looks in memdb to see if the specified name is listed in either the NewNames category (incompatible names that are going to be changed), or the InUseNames category (compatible names that cannot be used more than once). This routine compares only names in the same name group. Arguments: Context - Specifies the group context Name - Specifies the name to query Return Value: TRUE if the name is in use, or FALSE if the name is not in use. --*/ { TCHAR Node[MEMDB_MAX]; DEBUGMSG ((DBG_NAMEFIX, "%s: [%s] is compatible", Context->GroupName, Name)); MemDbBuildKey ( Node, MEMDB_CATEGORY_NEWNAMES, Context->GroupName, MEMDB_FIELD_NEW, Name ); if (MemDbGetValue (Node, NULL)) { return TRUE; } MemDbBuildKey ( Node, MEMDB_CATEGORY_INUSENAMES, Context->GroupName, NULL, Name ); return MemDbGetValue (Node, NULL); } VOID pMemDbSetIncompatibleName ( IN PCTSTR NameGroup, IN PCTSTR OrgName, IN PCTSTR NewName ) /*++ Routine Description: pMemDbSetIncompatibleName adds the correct entries to memdb to have a name appear in the name collision wizard page. Arguments: NameGroup - Specifies the name group such as ComputerName, UserName, etc... OrgName - Specifies the original name that is invalid NewName - Specifies the recommended new name Return Value: none --*/ { DWORD NewNameOffset; DEBUGMSG ((DBG_NAMEFIX, "%s: [%s]->[%s]", NameGroup, OrgName, NewName)); MemDbSetValueEx ( MEMDB_CATEGORY_NEWNAMES, NameGroup, NULL, NULL, 0, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_NEWNAMES, NameGroup, MEMDB_FIELD_NEW, NewName, 0, &NewNameOffset ); MemDbSetValueEx ( MEMDB_CATEGORY_NEWNAMES, NameGroup, MEMDB_FIELD_OLD, OrgName, NewNameOffset, NULL ); } VOID pMemDbSetCompatibleName ( IN PCTSTR NameGroup, IN PCTSTR Name ) /*++ Routine Description: pMemDbSetCompatibleName creates the memdb entries necessary to store names that are in use and are compatible. Arguments: NameGroup - Specifies the name group such as ComputerName, UserName, etc... Name - Specifies the compatible name Return Value: none --*/ { MemDbSetValueEx ( MEMDB_CATEGORY_INUSENAMES, NameGroup, NULL, Name, 0, NULL ); } VOID CreateNameTables ( VOID ) /*++ Routine Description: CreateNameTables finds all names on the computer and puts valid names into the InUseNames memdb category, and invalid names into the NewNames memdb category (including both the invalid name and recommended name). A wizard page appears if invalid names are found on the system. Arguments: none Return Value: none --*/ { INT i; NAME_ENUM e; PNAME_GROUP_ROUTINES Group; TCHAR RecommendedName[MAX_NAME]; PTSTR p; PTSTR DupList; static BOOL AlreadyDone = FALSE; if (AlreadyDone) { return; } AlreadyDone = TRUE; TurnOnWaitCursor(); // // Special case: Add NT group names to InUse list // p = (PTSTR) GetStringResource ( *g_ProductFlavor == PERSONAL_PRODUCTTYPE ? MSG_NAME_COLLISION_LIST_PER : MSG_NAME_COLLISION_LIST ); MYASSERT (p); if (p) { DupList = DuplicateText (p); MYASSERT (DupList); FreeStringResource (p); p = _tcschr (DupList, TEXT('|')); while (p) { *p = 0; p = _tcschr (_tcsinc (p), TEXT('|')); } Group = pGetNameGroupById (MSG_USERNAME_CATEGORY); MYASSERT (Group); if (Group) { p = DupList; while (*p) { pMemDbSetCompatibleName ( Group->GroupName, p ); p = GetEndOfString (p) + 1; } } FreeText (DupList); } // // General case: Enumerate all names, call Validate and add them to memdb // for (i = 0 ; g_NameGroupRoutines[i].GroupName ; i++) { Group = &g_NameGroupRoutines[i]; // // Initialize the context structure // ZeroMemory (&Group->Context, sizeof (NAME_GROUP_CONTEXT)); Group->Context.GroupName = Group->GroupName; // // Call the enum entry point // ZeroMemory (&e, sizeof (e)); if (Group->Enum (&Group->Context, &e, TRUE)) { do { // // Determine if this name is valid. If it is valid, add it to the // InUseNames memdb category. If it is not valid, get a recommended // replacement name, and store the incompatible and recommended // names in the NewNames memdb category. // #ifdef TEST_MANGLE_NAMES StringCat (e.Name, TEXT("\"%foo")); #endif #ifdef TEST_ALL_INCOMPATIBLE if (0) { #else if (Group->Validate (&Group->Context, e.Name)) { #endif pMemDbSetCompatibleName ( Group->GroupName, e.Name ); } else { Group->Recommend (&Group->Context, e.Name, RecommendedName); pMemDbSetIncompatibleName ( Group->GroupName, e.Name, RecommendedName ); } } while (Group->Enum (&Group->Context, &e, FALSE)); } } TurnOffWaitCursor(); } BOOL IsIncompatibleNamesTableEmpty ( VOID ) /*++ Routine Description: IsIncompatibleNamesTableEmpty looks at memdb to see if there are any names in the NewNames category. This function is used to determine if the name collision wizard page should appear. Arguments: none Return Value: TRUE if at least one name is invalid, or FALSE if all names are valid. --*/ { INVALID_NAME_ENUM e; return !EnumFirstInvalidName (&e); } BOOL pEnumInvalidNameWorker ( IN OUT PINVALID_NAME_ENUM EnumPtr ) /*++ Routine Description: pEnumInvalidNameWorker implements a state machine for invalid name enumeration. It returns the group name, original name and new name to the caller. Arguments: EnumPtr - Specifies the enumeration in progress; receives the updated fields Return Value: TRUE if an item was enumerated, or FALSE if no more items exist. --*/ { PCTSTR p; INT i; while (EnumPtr->State != ENUM_STATE_DONE) { switch (EnumPtr->State) { case ENUM_STATE_INIT: if (!MemDbEnumItems (&EnumPtr->NameGroup, MEMDB_CATEGORY_NEWNAMES)) { EnumPtr->State = ENUM_STATE_DONE; } else { EnumPtr->State = ENUM_STATE_ENUM_FIRST_GROUP_ITEM; } break; case ENUM_STATE_ENUM_FIRST_GROUP_ITEM: if (!MemDbGetValueEx ( &EnumPtr->Name, MEMDB_CATEGORY_NEWNAMES, EnumPtr->NameGroup.szName, MEMDB_FIELD_OLD )) { EnumPtr->State = ENUM_STATE_ENUM_NEXT_GROUP; } else { EnumPtr->State = ENUM_STATE_RETURN_GROUP_ITEM; } break; case ENUM_STATE_RETURN_GROUP_ITEM: // // Get the group name // EnumPtr->GroupName = EnumPtr->NameGroup.szName; // // Get the display group name from the message resources // for (i = 0 ; g_NameGroupRoutines[i].GroupName ; i++) { if (StringMatch (g_NameGroupRoutines[i].GroupName, EnumPtr->GroupName)) { break; } } MYASSERT (g_NameGroupRoutines[i].GroupName); if (g_NameGroupRoutines[i].NameId == MSG_COMPUTERDOMAIN_CATEGORY) { EnumPtr->State = ENUM_STATE_ENUM_NEXT_GROUP_ITEM; break; } p = GetStringResource (g_NameGroupRoutines[i].NameId); MYASSERT (p); if (p) { _tcssafecpy (EnumPtr->DisplayGroupName, p, (sizeof (EnumPtr->DisplayGroupName) / 2) / sizeof (TCHAR)); FreeStringResource (p); } ELSE_DEBUGMSG ((DBG_ERROR, "Unable to get string resource. Check localization.")); // // Get EnumPtr->NewName and EnumPtr->Identifier // EnumPtr->OriginalName = EnumPtr->Name.szName; MemDbBuildKeyFromOffset ( EnumPtr->Name.dwValue, EnumPtr->NewName, 3, NULL ); MemDbGetOffsetEx ( MEMDB_CATEGORY_NEWNAMES, EnumPtr->GroupName, MEMDB_FIELD_OLD, EnumPtr->OriginalName, &EnumPtr->Identifier ); EnumPtr->State = ENUM_STATE_ENUM_NEXT_GROUP_ITEM; return TRUE; case ENUM_STATE_ENUM_NEXT_GROUP_ITEM: if (!MemDbEnumNextValue (&EnumPtr->Name)) { EnumPtr->State = ENUM_STATE_ENUM_NEXT_GROUP; } else { EnumPtr->State = ENUM_STATE_RETURN_GROUP_ITEM; } break; case ENUM_STATE_ENUM_NEXT_GROUP: if (!MemDbEnumNextValue (&EnumPtr->NameGroup)) { EnumPtr->State = ENUM_STATE_DONE; } else { EnumPtr->State = ENUM_STATE_ENUM_FIRST_GROUP_ITEM; } break; } } return FALSE; } BOOL EnumFirstInvalidName ( OUT PINVALID_NAME_ENUM EnumPtr ) /*++ Routine Description: EnumFirstInvalidName enumerates the first entry in the memdb NewNames category. The caller receives the name group, the old name and the new name. Arguments: none Return Value: TRUE if at least one name is invalid, or FALSE if all names are valid. --*/ { EnumPtr->State = ENUM_STATE_INIT; return pEnumInvalidNameWorker (EnumPtr); } BOOL EnumNextInvalidName ( IN OUT PINVALID_NAME_ENUM EnumPtr ) /*++ Routine Description: EnumNextInvalidName enumerates the first entry in the memdb NewNames category. The caller receives the name group, the old name and the new name. Arguments: none Return Value: TRUE if another invalid name is available, or FALSE if no more names can be enumerated. --*/ { return pEnumInvalidNameWorker (EnumPtr); } VOID GetNamesFromIdentifier ( IN DWORD Identifier, IN PTSTR NameGroup, OPTIONAL IN PTSTR OriginalName, OPTIONAL IN PTSTR NewName OPTIONAL ) /*++ Routine Description: GetNamesFromIdentifier copies names to caller-specified buffers, given a unique identifier (a memdb offset). The unique identifer is provided by enumeration functions. Arguments: Identifier - Specifies the identifier of the name. NameGroup - Receives the text for the name group. OriginalName - Receivies the original name. NewName - Receives the fixed name that is compatible with NT. Return Value: none --*/ { BOOL b; PTSTR p; TCHAR NameGroupTemp[MEMDB_MAX]; TCHAR OrgNameTemp[MEMDB_MAX]; DWORD NewNameOffset; if (NameGroup) { *NameGroup = 0; } if (OriginalName) { *OriginalName = 0; } if (NewName) { *NewName = 0; } // // Get NameGroup // if (!MemDbBuildKeyFromOffset (Identifier, NameGroupTemp, 1, NULL)) { return; } p = _tcschr (NameGroupTemp, TEXT('\\')); MYASSERT (p); *p = 0; if (NameGroup) { StringCopy (NameGroup, NameGroupTemp); } // // Get OrgName and NewNameOffset. // b = MemDbBuildKeyFromOffset (Identifier, OrgNameTemp, 3, &NewNameOffset); if (OriginalName) { StringCopy (OriginalName, OrgNameTemp); } // // Get NewName // if (NewName) { b &= MemDbBuildKeyFromOffset (NewNameOffset, NewName, 3, NULL); } MYASSERT (b); } VOID ChangeName ( IN DWORD Identifier, IN PCTSTR NewName ) /*++ Routine Description: ChangeName puts a new name value in memdb for the name indicated by Identifier. The Identifier comes from enum functions. Arguments: Identifier - Specifies the name identifier (a memdb offset), and cannot be zero. NewName - Specifies the NT-compatible replacement name. Return Value: none --*/ { TCHAR Node[MEMDB_MAX]; TCHAR NameGroup[MEMDB_MAX]; TCHAR OrgName[MEMDB_MAX]; DWORD NewNameOffset; PTSTR p, q; BOOL b; MYASSERT (Identifier); if (!Identifier) { return; } // // - Obtain the original name // - Get the offset to the current new name // - Build the full key to the current new name // - Delete the current new name // if (!MemDbBuildKeyFromOffset (Identifier, OrgName, 3, &NewNameOffset)) { DEBUGMSG ((DBG_WHOOPS, "Can't obtain original name using offset %u", Identifier)); LOG ((LOG_ERROR, "Can't obtain original name using offset %u", Identifier)); return; } if (!MemDbBuildKeyFromOffset (NewNameOffset, Node, 0, NULL)) { DEBUGMSG ((DBG_WHOOPS, "Can't obtain new name key using offset %u", NewNameOffset)); LOG ((LOG_ERROR, "Can't obtain new name key using offset %u", NewNameOffset)); return; } MemDbDeleteValue (Node); // // Obtain the name group from the key string. It's the second // field (separated by backslashes). // p = _tcschr (Node, TEXT('\\')); MYASSERT (p); p = _tcsinc (p); q = _tcschr (p, TEXT('\\')); MYASSERT (q); StringCopyAB (NameGroup, p, q); // // Now set the updated new name, and link the original name // to the new name. // b = MemDbSetValueEx ( MEMDB_CATEGORY_NEWNAMES, NameGroup, MEMDB_FIELD_NEW, NewName, 0, &NewNameOffset ); b &= MemDbSetValueEx ( MEMDB_CATEGORY_NEWNAMES, NameGroup, MEMDB_FIELD_OLD, OrgName, NewNameOffset, NULL ); if (!b) { LOG ((LOG_ERROR, "Failure while attempting to change %s name to %s.",OrgName,NewName)); } } BOOL GetUpgradeComputerName ( OUT PTSTR NewName ) /*++ Routine Description: GetUpgradeComputerName obtains the computer name that will be used for upgrade. Arguments: NewName - Receives the name of the computer, as it will be set when NT is installed. Must hold at least MAX_COMPUTER_NAME characters. Return Value: TRUE if the name exists, or FALSE if it does not yet exit. --*/ { PNAME_GROUP_ROUTINES Group; NAME_ENUM e; Group = pGetNameGroupById (MSG_COMPUTERNAME_CATEGORY); MYASSERT (Group); if (!Group) return FALSE; // // Look in MemDb for a replacement name // if (MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_NEW, NewName )) { return TRUE; } // // No replacement name; obtain the current name // ZeroMemory (&e, sizeof (e)); if (Group->Enum (&Group->Context, &e, TRUE)) { StringCopy (NewName, e.Name); while (Group->Enum (&Group->Context, &e, FALSE)) { // empty } return TRUE; } return FALSE; } DWORD GetDomainIdentifier ( VOID ) /*++ Routine Description: GetDomainIdentifier returns the identifier for the domain name. The identifier is a memdb offset. Arguments: None. Return Value: A non-zero identifier which can be used with other routines in this file. --*/ { PNAME_GROUP_ROUTINES Group; MEMDB_ENUM e; DWORD Identifier = 0; Group = pGetNameGroupById (MSG_COMPUTERDOMAIN_CATEGORY); MYASSERT (Group); if (Group && MemDbGetValueEx ( &e, MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_OLD )) { MemDbGetOffsetEx ( MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_OLD, e.szName, &Identifier ); } return Identifier; } BOOL pGetUpgradeName ( IN UINT CategoryId, OUT PTSTR NewName ) /*++ Routine Description: pGetUpgradeName returns the NT-compatible name for a given name group. If a name group has multiple names, this routine should not be used. Arguments: CategoryId - Specifies the MSG_* constant for the group (see macro expansion list at top of file). NewName - Receives the text for the NT-compatible replacement name for the group. Return Value: TRUE if a name is being returned, or FALSE if no replacement name exists. --*/ { PNAME_GROUP_ROUTINES Group; NAME_ENUM e; Group = pGetNameGroupById (CategoryId); MYASSERT (Group); if (!Group) return FALSE; // // Look in MemDb for a replacement name // if (MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_NEW, NewName )) { return TRUE; } // // No replacement name; obtain the current name // ZeroMemory (&e, sizeof (e)); if (Group->Enum (&Group->Context, &e, TRUE)) { StringCopy (NewName, e.Name); while (Group->Enum (&Group->Context, &e, FALSE)) { // empty } return TRUE; } return FALSE; } BOOL GetUpgradeDomainName ( OUT PTSTR NewName ) /*++ Routine Description: GetUpgradeDomainName returns the new domain name, if one exists. Arguments: NewName - Receiveis the new domain name. Return Value: TRUE if a new name is being returned, or FALSE if no replacement name exists. --*/ { return pGetUpgradeName ( MSG_COMPUTERDOMAIN_CATEGORY, NewName ); } BOOL GetUpgradeWorkgroupName ( OUT PTSTR NewName ) /*++ Routine Description: GetUpgradeWorkgroupName returns the new workgroup name, if one exists. Arguments: None. Return Value: TRUE if a new name is being returned, or FALSE if no replacement name exists. --*/ { return pGetUpgradeName ( MSG_WORKGROUP_CATEGORY, NewName ); } BOOL GetUpgradeUserName ( IN PCTSTR User, OUT PTSTR NewUserName ) /*++ Routine Description: GetUpgradeUserName returns the fixed user name for the user specified. If no fixed name exists, this routine returns the original name. Arguments: User - Specifies the user to look up. The user name must exist on the Win9x configuration. NewUserName - Receives the NT-compatible user name, which may or may not be the same as User. Return Value: Always TRUE. --*/ { PNAME_GROUP_ROUTINES Group; TCHAR Node[MEMDB_MAX]; DWORD NewOffset; Group = pGetNameGroupById (MSG_USERNAME_CATEGORY); MYASSERT (Group); // // Look in MemDb for a replacement name // if (Group) { MemDbBuildKey ( Node, MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_OLD, User ); if (MemDbGetValue (Node, &NewOffset)) { if (MemDbBuildKeyFromOffset (NewOffset, NewUserName, 3, NULL)) { return TRUE; } } } // // No replacement name; use the current name // StringCopy (NewUserName, User); return TRUE; } BOOL WarnAboutBadNames ( IN HWND PopupParent ) /*++ Routine Description: WarnAboutBadNames adds an incompatibility message whenever domain logon is enabled but there is no account set up for the machine. A popup is generated if PopupParent is non-NULL. Other incompatibility messages are added for each name that will change. Arguments: Popup - Specifies TRUE if the message should appear in a message box, or FALSE if it should be added to the incompatibility report. Return Value: TRUE if the user wants to continue, or FALSE if the user wants to change the domain name. --*/ { PCTSTR RootGroup; PCTSTR NameSubGroup; PCTSTR FullGroupName; PCTSTR BaseGroup; PNAME_GROUP_CONTEXT Context; PCTSTR ArgArray[3]; INVALID_NAME_ENUM e; BOOL b = TRUE; TCHAR EncodedName[MEMDB_MAX]; PCTSTR Blank; if (PopupParent) { // // PopupParent is non-NULL only when the incompatible names wizard page // is being deactivated. Here we have a chance to make sure the names // specified all work together before proceeding. // // This functionality has been disabled because domain validation has // been moved to a new page. We might re-enable this when another // invalid name group comes along. // return TRUE; } // // Enumerate all bad names // if (EnumFirstInvalidName (&e)) { Blank = GetStringResource (MSG_BLANK_NAME); MYASSERT (Blank); do { // // Prepare message // ArgArray[0] = e.DisplayGroupName; ArgArray[1] = e.OriginalName; ArgArray[2] = e.NewName; if (ArgArray[1][0] == 0) { ArgArray[1] = Blank; } if (ArgArray[2][0] == 0) { ArgArray[2] = Blank; } RootGroup = GetStringResource (MSG_INSTALL_NOTES_ROOT); MYASSERT (RootGroup); NameSubGroup = ParseMessageID (MSG_NAMECHANGE_WARNING_GROUP, ArgArray); MYASSERT (NameSubGroup); BaseGroup = JoinPaths (RootGroup, NameSubGroup); MYASSERT (BaseGroup); FreeStringResource (RootGroup); FreeStringResource (NameSubGroup); NameSubGroup = ParseMessageID (MSG_NAMECHANGE_WARNING_SUBCOMPONENT, ArgArray); MYASSERT (NameSubGroup); FullGroupName = JoinPaths (BaseGroup, NameSubGroup); MYASSERT (FullGroupName); FreePathString (BaseGroup); FreeStringResource (NameSubGroup); EncodedName[0] = TEXT('|'); StringCopy (EncodedName + 1, e.OriginalName); MsgMgr_ObjectMsg_Add( EncodedName, // Object name, prefixed with a pipe symbol FullGroupName, // Message title S_EMPTY // Message text ); FreePathString (FullGroupName); } while (EnumNextInvalidName (&e)); FreeStringResource (Blank); // // Save changed user names to FixedUserNames // Context = pGetNameGroupContextById (MSG_USERNAME_CATEGORY); MYASSERT (Context); if (EnumFirstInvalidName (&e)) { do { if (StringMatch (Context->GroupName, e.GroupName)) { _tcssafecpy (EncodedName, e.OriginalName, MAX_USER_NAME); MemDbMakeNonPrintableKey ( EncodedName, MEMDB_CONVERT_DOUBLEWACKS_TO_ASCII_1| MEMDB_CONVERT_WILD_STAR_TO_ASCII_2| MEMDB_CONVERT_WILD_QMARK_TO_ASCII_3 ); MemDbSetValueEx ( MEMDB_CATEGORY_FIXEDUSERNAMES, EncodedName, NULL, e.NewName, 0, NULL ); } } while (EnumNextInvalidName (&e)); } } return b; } DWORD BadNamesWarning ( DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_BAD_NAMES_WARNING; case REQUEST_RUN: if (!WarnAboutBadNames (NULL)) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in BadNamesWarning")); } return 0; } // // The following flag is no longer in use. It used to be used // to disable domain checking (to bypass the block requiring // a valid domain). Currently there is no way to get beyond // the wizard page that resolved the domain name, except by // providing a valid domain or credentials to create a computer // account on a domain. // BOOL g_DisableDomainChecks = FALSE; VOID DisableDomainChecks ( VOID ) { g_DisableDomainChecks = TRUE; } VOID EnableDomainChecks ( VOID ) { g_DisableDomainChecks = FALSE; } BOOL IsOriginalDomainNameValid ( VOID ) /*++ Routine Description: IsOriginalDomainNameValid checks to see if there is a replacement domain name. If there is, the current domain name must be invalid. Arguments: None. Return Value: TRUE - the Win9x domain name is valid, no replacement name exists FALSE - the Win9x domain name is invalid --*/ { PNAME_GROUP_ROUTINES Group; TCHAR NewName[MEMDB_MAX]; Group = pGetNameGroupById (MSG_COMPUTERDOMAIN_CATEGORY); MYASSERT (Group); // // Look in MemDb for a replacement name // if (Group && MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NEWNAMES, Group->GroupName, MEMDB_FIELD_NEW, NewName )) { return FALSE; } return TRUE; }