/*++ Copyright (c) 1996 Microsoft Corporation Module Name: shell.c Abstract: Contains code that implements shell folder migration. Shell folders are moved into new NT locations whenever possible. Also, a set of filters alter the content of the shell folders. Author: Jim Schmidt (jimschm) 24-Aug-1998 Revision History: Calin Negreanu (calinn) 09-Sep-1998 Obsolete links, fixes and other changes --*/ #include "pch.h" #include #include "migmainp.h" #define DBG_SHELL "Shell" #define SHELL_FOLDER_FILTERS_9X_NT \ DEFMAC(pObsoleteLinksFilter) \ DEFMAC(pStartupDisableFilter) \ DEFMAC(pFontNameFilter) \ DEFMAC(pCollisionDetection9xNt) \ #define SHELL_FOLDER_FILTERS_NT_9X \ DEFMAC(pDetectOtherShellFolder) \ DEFMAC(pCollisionDetectionNt9x) \ typedef enum { INITIALIZE, PROCESS_PATH, TERMINATE } CALL_CONTEXT; #define SHELLFILTER_OK 0 #define SHELLFILTER_SKIP_FILE 1 #define SHELLFILTER_SKIP_DIRECTORY 2 #define SHELLFILTER_ERROR 3 #define SHELLFILTER_FORCE_CHANGE 4 typedef struct { IN PCWSTR Win9xUser; OPTIONAL IN PCWSTR FixedUserName; OPTIONAL IN HKEY UserHiveRoot; // HKLM or the Default User hive IN PCWSTR ShellFolderIdentifier; // i.e., Fonts, Programs, etc... IN OUT WCHAR TempSourcePath[MEMDB_MAX]; // full path, a child of SrcRootPath IN OUT WCHAR DestinationPath[MEMDB_MAX]; IN PCWSTR DefaultShellFolder; OPTIONAL IN PCWSTR UserDefaultLocation; IN PCWSTR SrcRootPath; // the temp root dir IN PCWSTR DestRootPath; // the dest root dir IN PCWSTR OrigRootPath; // the Win9x root dir IN OUT DWORD Attributes; IN DWORD UserFlags; IN OUT DWORD State; IN PMIGRATE_USER_ENUM EnumPtr; IN CALL_CONTEXT Context; } PROFILE_MERGE_DATA, *PPROFILE_MERGE_DATA; typedef DWORD(PROFILEMERGEFILTER_PROTOTYPE)(IN OUT PPROFILE_MERGE_DATA Data); typedef PROFILEMERGEFILTER_PROTOTYPE * PROFILEMERGEFILTER; typedef struct { PROFILEMERGEFILTER Fn; PCSTR Name; DWORD State; } SHELL_FOLDER_FILTER, *PSHELL_FOLDER_FILTER; #define DEFMAC(fn) PROFILEMERGEFILTER_PROTOTYPE fn; SHELL_FOLDER_FILTERS_9X_NT SHELL_FOLDER_FILTERS_NT_9X #undef DEFMAC #define DEFMAC(fn) {fn, #fn}, static SHELL_FOLDER_FILTER g_Filters_9xNt[] = { SHELL_FOLDER_FILTERS_9X_NT /* , */ {NULL} }; static SHELL_FOLDER_FILTER g_Filters_Nt9x[] = { SHELL_FOLDER_FILTERS_NT_9X /* , */ {NULL} }; #undef DEFMAC GROWLIST g_SfQueueSrc; GROWLIST g_SfQueueDest; PVOID g_SystemSfList; PVOID g_UserSfList; typedef struct { PCTSTR sfName; PCTSTR sfPath; HKEY SfKey; REGVALUE_ENUM SfKeyEnum; BOOL UserSf; } SF_ENUM, *PSF_ENUM; #define MAX_SHELL_TAG 64 typedef struct { INT CsidlValue; PCTSTR Tag; } CSIDLMAP, *PCSIDLMAP; CSIDLMAP g_CsidlMap[] = { CSIDL_ADMINTOOLS, TEXT("Administrative Tools"), CSIDL_ALTSTARTUP, TEXT("AltStartup"), CSIDL_APPDATA, TEXT("AppData"), CSIDL_BITBUCKET, TEXT("RecycleBinFolder"), CSIDL_CONNECTIONS, TEXT("ConnectionsFolder"), CSIDL_CONTROLS, TEXT("ControlPanelFolder"), CSIDL_COOKIES, TEXT("Cookies"), CSIDL_DESKTOP, TEXT("Desktop"), CSIDL_DRIVES, TEXT("DriveFolder"), CSIDL_FAVORITES, TEXT("Favorites"), CSIDL_FONTS, TEXT("Fonts"), CSIDL_HISTORY, TEXT("History"), CSIDL_INTERNET, TEXT("InternetFolder"), CSIDL_INTERNET_CACHE, TEXT("Cache"), CSIDL_LOCAL_APPDATA, TEXT("Local AppData"), CSIDL_MYDOCUMENTS, TEXT("My Documents"), CSIDL_MYMUSIC, TEXT("My Music"), CSIDL_MYPICTURES, TEXT("My Pictures"), CSIDL_MYVIDEO, TEXT("My Video"), CSIDL_NETHOOD, TEXT("NetHood"), CSIDL_NETWORK, TEXT("NetworkFolder"), CSIDL_PERSONAL, TEXT("Personal"), CSIDL_PROGRAMS, TEXT("Programs"), CSIDL_RECENT, TEXT("Recent"), CSIDL_SENDTO, TEXT("SendTo"), CSIDL_STARTMENU, TEXT("Start Menu"), CSIDL_STARTUP, TEXT("Startup"), CSIDL_TEMPLATES, TEXT("Templates"), CSIDL_COMMON_ADMINTOOLS, TEXT("Common Administrative Tools"), CSIDL_COMMON_ALTSTARTUP, TEXT("Common AltStartup"), CSIDL_COMMON_APPDATA, TEXT("Common AppData"), CSIDL_COMMON_DESKTOPDIRECTORY, TEXT("Common Desktop"), CSIDL_COMMON_DOCUMENTS, TEXT("Common Documents"), CSIDL_COMMON_FAVORITES, TEXT("Common Favorites"), CSIDL_COMMON_PROGRAMS, TEXT("Common Programs"), CSIDL_COMMON_STARTMENU, TEXT("Common Start Menu"), CSIDL_COMMON_STARTUP, TEXT("Common Startup"), CSIDL_COMMON_TEMPLATES, TEXT("Common Templates"), CSIDL_COMMON_DOCUMENTS, TEXT("Common Personal"), CSIDL_COMMON_MUSIC, TEXT("CommonMusic"), CSIDL_COMMON_PICTURES, TEXT("CommonPictures"), CSIDL_COMMON_VIDEO, TEXT("CommonVideo"), 0, NULL }; VOID pConvertCommonSfToPerUser ( IN PCTSTR CommonSf, OUT PTSTR PerUserSf // must hold MAX_SHELL_TAG chars ); BOOL pIsCommonSf ( IN PCTSTR ShellFolderTag ); VOID pConvertPerUserSfToCommon ( IN PCTSTR PerUserSf, OUT PTSTR CommonSf // must hold MAX_SHELL_TAG chars ); /*++ Routine Description: EnumFirstRegShellFolder and EnumNextRegShellFolder are enumeration routines that enumerate all shell folders per system or for a particular user. Arguments: e - enumeration structure EnumPtr - user enumeration structure Return Value: Both routines return TRUE if a new shell folder could be found, FALSE otherwise --*/ BOOL EnumFirstRegShellFolder ( IN OUT PSF_ENUM e, IN BOOL UserSf ) { HKEY UsfKey; e->UserSf = UserSf; e->sfPath = NULL; if (UserSf) { e->SfKey = OpenRegKey (HKEY_CURRENT_USER, S_SHELL_FOLDERS_KEY_USER); } else { e->SfKey = OpenRegKeyStr (S_SHELL_FOLDERS_KEY_SYSTEM); } if (!e->SfKey) { return FALSE; } if (EnumFirstRegValue (&e->SfKeyEnum, e->SfKey)) { e->sfName = e->SfKeyEnum.ValueName; e->sfPath = NULL; if (UserSf) { UsfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER); } else { UsfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM); } if (UsfKey) { e->sfPath = GetRegValueString (UsfKey, e->SfKeyEnum.ValueName); CloseRegKey (UsfKey); } if (e->sfPath == NULL) { e->sfPath = GetRegValueString (e->SfKey, e->SfKeyEnum.ValueName); } return TRUE; } CloseRegKey (e->SfKey); return FALSE; } BOOL EnumNextRegShellFolder ( IN OUT PSF_ENUM e ) { HKEY UsfKey; if (e->sfPath) { MemFree (g_hHeap, 0, e->sfPath); e->sfPath = NULL; } if (EnumNextRegValue (&e->SfKeyEnum)) { e->sfName = e->SfKeyEnum.ValueName; e->sfPath = NULL; if (e->UserSf) { UsfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER); } else { UsfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM); } if (UsfKey) { e->sfPath = GetRegValueString (UsfKey, e->SfKeyEnum.ValueName); CloseRegKey (UsfKey); } if (e->sfPath == NULL) { e->sfPath = GetRegValueString (e->SfKey, e->SfKeyEnum.ValueName); } return TRUE; } CloseRegKey (e->SfKey); return FALSE; } VOID AbortEnumRegShellFolder ( IN OUT PSF_ENUM e ) { if (e->sfPath) { MemFree (g_hHeap, 0, e->sfPath); e->sfPath = NULL; } } VOID pPrepareSfRestartability( VOID ) { PTSTR userProfilePath = NULL; DWORD Size; MIGRATE_USER_ENUM e; if (EnumFirstUserToMigrate (&e, ENUM_NO_FLAGS)) { do { if (!e.CreateOnly && (e.AccountType != DEFAULT_USER_ACCOUNT) && (e.AccountType != LOGON_USER_SETTINGS) ) { if (GetUserProfilePath (e.FixedUserName, &userProfilePath)) { RenameOnRestartOfGuiMode (userProfilePath, NULL); FreePathString (userProfilePath); } } } while (EnumNextUserToMigrate (&e)); } } VOID pFlushSfQueue ( VOID ) { UINT u; UINT count; PCTSTR source; PCTSTR dest; // // For files that need to be copied, do that now before writing to the journal // count = GrowListGetSize (&g_SfQueueSrc); if (!count) { return; } for (u = 0 ; u < count ; u++) { dest = GrowListGetString (&g_SfQueueDest, u); if (!dest) { continue; } if (DoesFileExist (dest)) { source = GrowListGetString (&g_SfQueueSrc, u); MYASSERT (source); if (!OurCopyFileW (source, dest)) { LOG ((LOG_WARNING, (PCSTR)MSG_COULD_NOT_MOVE_FILE_LOG, dest, GetLastError ())); g_BlowAwayTempShellFolders = FALSE; } // // Make the string pointers NULL for this item // GrowListResetItem (&g_SfQueueSrc, u); GrowListResetItem (&g_SfQueueDest, u); } } // // Now record the remaining items in the journal (before the move // happens). Ignore journal failures. Since we are undoing the move, // source and dest must be flipped. // RenameListOnRestartOfGuiMode (&g_SfQueueDest, &g_SfQueueSrc); // // Do the move // for (u = 0 ; u < count ; u++) { source = GrowListGetString (&g_SfQueueSrc, u); dest = GrowListGetString (&g_SfQueueDest, u); if (!source || !dest) { continue; } if (!OurMoveFileEx (source, dest, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) { if (GetLastError() == ERROR_ALREADY_EXISTS) { DEBUGMSG ((DBG_WARNING, "%s already exists", dest)); } else { LOG ((LOG_WARNING, (PCSTR)MSG_COULD_NOT_MOVE_FILE_LOG, dest, GetLastError ())); g_BlowAwayTempShellFolders = FALSE; } } } // // Clean up -- grow lists are ready for reuse after FreeGrowList // FreeGrowList (&g_SfQueueSrc); FreeGrowList (&g_SfQueueDest); } VOID pQueueSfMove ( IN PCTSTR Source, IN PCTSTR Destination ) { UINT count; MYASSERT (Source && Destination); count = GrowListGetSize (&g_SfQueueSrc); if (count == 1000) { // // Do 1,000 moves at once // pFlushSfQueue(); } GrowListAppendString (&g_SfQueueSrc, Source); GrowListAppendString (&g_SfQueueDest, Destination); } PVOID pCreateSystemSfList ( ) { PCTSTR expandedPath; PVOID Table; SF_ENUM e; Table = pSetupStringTableInitialize(); if (!Table) { return NULL; } // // Load all the System shell folders into this table // if (EnumFirstRegShellFolder (&e, FALSE)) { do { expandedPath = ExpandEnvironmentText (e.sfPath); pSetupStringTableAddString (Table, (PVOID) expandedPath, STRTAB_CASE_INSENSITIVE); FreeText (expandedPath); } while (EnumNextRegShellFolder (&e)); } return Table; } PVOID pCreateUserSfList ( IN PPROFILE_MERGE_DATA Data ) { PTSTR CurrentUserProfilePath = NULL; TCHAR DefaultUserProfilePath[MAX_TCHAR_PATH]; DWORD Size; PCTSTR expandedPath; PCTSTR tempExpand; PVOID Table; SF_ENUM e; if (Data && Data->FixedUserName) { if (!GetUserProfilePath (Data->FixedUserName, &CurrentUserProfilePath)) { return NULL; } } else { Size = sizeof (DefaultUserProfilePath); if (!GetDefaultUserProfileDirectory (DefaultUserProfilePath, &Size)) { return NULL; } } Table = pSetupStringTableInitialize(); if (!Table) { return NULL; } // // Load all the System shell folders into this table // if (EnumFirstRegShellFolder (&e, TRUE)) { do { tempExpand = StringSearchAndReplace ( e.sfPath, S_USERPROFILE_ENV, CurrentUserProfilePath?CurrentUserProfilePath:DefaultUserProfilePath ); if (!tempExpand) { tempExpand = DuplicatePathString (e.sfPath, 0); } expandedPath = ExpandEnvironmentText (tempExpand); FreePathString (tempExpand); pSetupStringTableAddString (Table, (PVOID) expandedPath, STRTAB_CASE_INSENSITIVE); FreeText (expandedPath); } while (EnumNextRegShellFolder (&e)); } if (CurrentUserProfilePath) { FreePathString (CurrentUserProfilePath); CurrentUserProfilePath = NULL; } return Table; } VOID pDestroySfList ( IN PVOID Table ) { if (Table) { pSetupStringTableDestroy (Table); } } PVOID g_LinkDataPool = NULL; typedef struct _LINK_DATA { PCTSTR Target; PCTSTR Arguments; PCTSTR ShellFolderName; struct _LINK_DATA *Next; } LINK_DATA, *PLINK_DATA; PVOID g_FoldersTable; PVOID g_Merged9xFolders; typedef struct _LINK_RENAME_DATA { PCTSTR OldTarget; PCTSTR NewTarget; PCTSTR OldArguments; PCTSTR NewArguments; PCTSTR ShellFolderName; struct _LINK_RENAME_DATA *Next; } LINK_RENAME_DATA, *PLINK_RENAME_DATA; PLINK_RENAME_DATA g_LinkRenameData; VOID pAddAllLinksToList ( PTSTR AllocBuffer, // MEMDB_MAX * 4, caller-owned for less allocs PCTSTR ShellFolderName, PCTSTR RootPath, IShellLink *ShellLink, IPersistFile *PersistFile ) { TREE_ENUM e; PTSTR ShortcutTarget; PTSTR ShortcutArgs; PTSTR ShortcutWorkDir; PTSTR ShortcutIconPath; INT ShortcutIcon; WORD ShortcutHotKey; BOOL dosApp; BOOL msDosMode; PLINK_DATA linkData; LONG stringId; ShortcutTarget = AllocBuffer + MEMDB_MAX; ShortcutArgs = ShortcutTarget + MEMDB_MAX; ShortcutWorkDir = ShortcutArgs + MEMDB_MAX; ShortcutIconPath = ShortcutWorkDir + MEMDB_MAX; if (EnumFirstFileInTree (&e, RootPath, NULL, FALSE)) { do { if (e.Directory) { if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) || ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) ) { AbortEnumCurrentDir (&e); } continue; } DEBUGMSG ((DBG_SHELL, "Extracting shortcut info for enumerated file %s", e.FullPath)); if (ExtractShortcutInfo ( ShortcutTarget, ShortcutArgs, ShortcutWorkDir, ShortcutIconPath, &ShortcutIcon, &ShortcutHotKey, &dosApp, &msDosMode, NULL, NULL, e.FullPath, ShellLink, PersistFile )) { linkData = (PLINK_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_DATA))); ZeroMemory (linkData, sizeof (LINK_DATA)); linkData->Target = PoolMemDuplicateString (g_LinkDataPool, ShortcutTarget); linkData->Arguments = PoolMemDuplicateString (g_LinkDataPool, ShortcutArgs); linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, ShellFolderName); linkData->Next = NULL; DEBUGMSG ((DBG_SHELL, "Recording NT default shortcut: %s in %s", e.FullPath, ShellFolderName)); stringId = pSetupStringTableLookUpString (g_FoldersTable, (PTSTR)ShellFolderName, 0); if (stringId != -1) { pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData->Next, sizeof (PLINK_DATA)); pSetupStringTableSetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA)); } else { pSetupStringTableAddStringEx ( g_FoldersTable, (PTSTR)ShellFolderName, STRTAB_CASE_INSENSITIVE, &linkData, sizeof (PLINK_DATA) ); } } } while (EnumNextFileInTree (&e)); } } VOID pAddKnownLinks ( VOID ) { INFCONTEXT context; TCHAR field[MEMDB_MAX]; BOOL result = FALSE; PLINK_DATA linkData; PCTSTR pathExp; LONG stringId; PCTSTR ArgList [4] = {TEXT("ProgramFiles"), g_ProgramFiles, NULL, NULL}; MYASSERT (g_WkstaMigInf); if (SetupFindFirstLine (g_WkstaMigInf, S_KNOWN_NT_LINKS, NULL, &context)) { do { linkData = (PLINK_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_DATA))); ZeroMemory (linkData, sizeof (LINK_DATA)); result = FALSE; __try { if (!SetupGetStringField (&context, 1, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->Target = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 2, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->Arguments = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 3, field, MEMDB_MAX, NULL)) { __leave; } linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, field); linkData->Next = NULL; result = TRUE; } __finally { if (result) { DEBUGMSG ((DBG_SHELL, "Recording known link: %s in %s", linkData->Target, linkData->ShellFolderName)); stringId = pSetupStringTableLookUpString (g_FoldersTable, (PTSTR)linkData->ShellFolderName, 0); if (stringId != -1) { pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData->Next, sizeof (PLINK_DATA)); pSetupStringTableSetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA)); } else { pSetupStringTableAddStringEx ( g_FoldersTable, (PTSTR)linkData->ShellFolderName, STRTAB_CASE_INSENSITIVE, &linkData, sizeof (PLINK_DATA) ); } } else { if (linkData->Target) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->Target); } if (linkData->Arguments) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->Arguments); } if (linkData->ShellFolderName) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->ShellFolderName); } PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData); linkData = NULL; } } } while (SetupFindNextLine (&context, &context)); } } VOID pCreateLinksList ( VOID ) { SF_ENUM e; PCTSTR expandedPath; UINT commonLen; DWORD Size; PCTSTR tempExpand; PTSTR DefaultUserProfilePath; IShellLink *shellLink; IPersistFile *persistFile; PTSTR perUserName; PTSTR bigBuf = NULL; __try { bigBuf = (PTSTR) MemAllocUninit ((MEMDB_MAX * 4 + MAX_TCHAR_PATH + MAX_SHELL_TAG) * sizeof (TCHAR)); if (!bigBuf) { __leave; } DefaultUserProfilePath = bigBuf + MEMDB_MAX * 4; perUserName = DefaultUserProfilePath + MAX_TCHAR_PATH; g_LinkDataPool = PoolMemInitNamedPool ("LinkData Pool"); g_FoldersTable = pSetupStringTableInitializeEx (sizeof (PLINK_DATA), 0); if (!g_FoldersTable) { DEBUGMSG((DBG_ERROR, "Cannot initialize Shell Folders table.")); __leave; } // // First thing: Load links from the INF files. These are links that we know NT is going to install // pAddKnownLinks (); if (InitCOMLink (&shellLink, &persistFile)) { // // Go through all system shell folders and list the links // if (EnumFirstRegShellFolder (&e, FALSE)) { do { if (*e.sfPath) { expandedPath = ExpandEnvironmentText (e.sfPath); pConvertCommonSfToPerUser (e.sfName, perUserName); pAddAllLinksToList (bigBuf, perUserName, expandedPath, shellLink, persistFile); FreeText (expandedPath); } ELSE_DEBUGMSG ((DBG_WARNING, "Shell Folder <%s> data is empty!", e.sfName)); } while (EnumNextRegShellFolder (&e)); } Size = MAX_TCHAR_PATH; if (!GetDefaultUserProfileDirectory (DefaultUserProfilePath, &Size)) { __leave; } // // Go through all user shell folders and list the links from the default user dirs // if (EnumFirstRegShellFolder (&e, TRUE)) { do { if (*e.sfPath) { tempExpand = StringSearchAndReplace ( e.sfPath, S_USERPROFILE_ENV, DefaultUserProfilePath ); if (!tempExpand) { tempExpand = DuplicatePathString (e.sfPath, 0); } expandedPath = ExpandEnvironmentText (tempExpand); FreePathString (tempExpand); pAddAllLinksToList (bigBuf, e.sfName, expandedPath, shellLink, persistFile); FreeText (expandedPath); } ELSE_DEBUGMSG ((DBG_WARNING, "Shell Folder <%s> data is empty!", e.sfName)); } while (EnumNextRegShellFolder (&e)); } FreeCOMLink (&shellLink, &persistFile); } else { DEBUGMSG((DBG_ERROR, "Cannot initialize COM. Obsolete links filter will not work.")); } } __finally { if (bigBuf) { FreeMem (bigBuf); } } } VOID pCreateLinksRenameList ( VOID ) { INFCONTEXT context; TCHAR field[MEMDB_MAX]; BOOL result = FALSE; PLINK_RENAME_DATA linkData; PCTSTR pathExp; PCTSTR ArgList [4] = {TEXT("ProgramFiles"), g_ProgramFiles, NULL, NULL}; MYASSERT (g_WkstaMigInf); if (SetupFindFirstLine (g_WkstaMigInf, S_OBSOLETE_LINKS, NULL, &context)) { do { linkData = (PLINK_RENAME_DATA) (PoolMemGetMemory (g_LinkDataPool, sizeof (LINK_RENAME_DATA))); ZeroMemory (linkData, sizeof (LINK_RENAME_DATA)); result = FALSE; __try { if (!SetupGetStringField (&context, 1, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->OldTarget = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 2, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->OldArguments = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 3, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->NewTarget = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 4, field, MEMDB_MAX, NULL)) { __leave; } pathExp = ExpandEnvironmentTextEx (field, ArgList); linkData->NewArguments = PoolMemDuplicateString (g_LinkDataPool, pathExp); FreeText (pathExp); if (!SetupGetStringField (&context, 5, field, MEMDB_MAX, NULL)) { __leave; } linkData->ShellFolderName = PoolMemDuplicateString (g_LinkDataPool, field); result = TRUE; } __finally { if (result) { linkData->Next = g_LinkRenameData; g_LinkRenameData = linkData; } else { if (linkData->OldTarget) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->OldTarget); } if (linkData->NewTarget) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->NewTarget); } if (linkData->OldArguments) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->OldArguments); } if (linkData->NewArguments) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->NewArguments); } if (linkData->ShellFolderName) { PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData->ShellFolderName); } PoolMemReleaseMemory (g_LinkDataPool, (PVOID)linkData); linkData = NULL; } } } while (SetupFindNextLine (&context, &context)); } } VOID pDestroyLinksData ( VOID ) { if (g_LinkDataPool != NULL) { PoolMemDestroyPool (g_LinkDataPool); g_LinkDataPool = NULL; } if (g_FoldersTable != NULL) { pSetupStringTableDestroy (g_FoldersTable); } g_LinkRenameData = NULL; } BOOL pMigrateShellFolder ( IN PCTSTR Win9xUser, OPTIONAL IN PCTSTR FixedUserName, OPTIONAL IN BOOL SystemShellFolder, IN PCTSTR ShellFolderIdentifier, IN PCTSTR SourcePath, IN PCTSTR DestinationPath, IN PCTSTR OrigSourcePath, IN DWORD UserFlags, IN PMIGRATE_USER_ENUM EnumPtr ); TCHAR g_DefaultHivePath[MAX_TCHAR_PATH]; HKEY g_DefaultHiveRoot; INT g_DefaultHiveMapped; VOID pMigrateSystemShellFolders ( VOID ) { FILEOP_ENUM eOp; FILEOP_PROP_ENUM eOpProp; PTSTR NewDest; PTSTR OrigSrc; if (EnumFirstPathInOperation (&eOp, OPERATION_SHELL_FOLDER)) { do { if (IsPatternMatch (S_DOT_ALLUSERS TEXT("\\*"), eOp.Path)) { NewDest = NULL; OrigSrc = NULL; if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_SHELL_FOLDER)) { do { if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_DEST)) { NewDest = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_ORIGINAL_SRC)) { OrigSrc = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_SRC)) { MYASSERT (NewDest); MYASSERT (OrigSrc); DEBUGMSG ((DBG_NAUSEA, "System SourcePath: %s", eOpProp.Property)); pMigrateShellFolder ( NULL, NULL, TRUE, _tcsinc(_tcschr (eOp.Path, '\\')), eOpProp.Property, NewDest, OrigSrc, 0, NULL ); } } while (EnumNextFileOpProperty (&eOpProp)); } if (NewDest) { FreePathString (NewDest); NewDest = NULL; } if (OrigSrc) { FreePathString (OrigSrc); OrigSrc = NULL; } } } while (EnumNextPathInOperation (&eOp)); } } VOID pWriteMyDocsHelpFile ( IN PCTSTR SubDir ) /*++ Routine Description: pWriteMyDocsHelpFile outputs a text file to the given path. This assists the user in locating their documents, when the My Documents shell folder goes to Shared Documents. Arguments: SubDir - Specifies the path to the subdir where the file should be written Return Value: None. --*/ { HANDLE file; PCTSTR fileName; PCTSTR msg; DWORD bytesWritten; PCTSTR path; fileName = GetStringResource (MSG_EMPTY_MYDOCS_TITLE); msg = GetStringResource (MSG_EMPTY_MYDOCS_TEXT); path = JoinPaths (SubDir, fileName); if (fileName && msg && path) { // // For uninstall, mark the file as create. Because of a bug, we have // to treat this file as an OS file. What we really want to do is // call: // // MarkFileForCreation (path); // // but this does not work. So we call MarkFileAsOsFile. // MarkFileAsOsFile (path); // allows uninstall to work properly file = CreateFile ( path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (file != INVALID_HANDLE_VALUE) { #ifdef UNICODE WriteFile (file, "\xff\xfe", 2, &bytesWritten, NULL); #endif WriteFile (file, msg, SizeOfString (msg), &bytesWritten, NULL); CloseHandle (file); } } FreeStringResource (msg); FreeStringResource (fileName); FreePathString (path); } VOID pMigrateUserShellFolders ( IN PMIGRATE_USER_ENUM EnumPtr ) { FILEOP_ENUM eOp; FILEOP_PROP_ENUM eOpProp; PTSTR NewDest; PTSTR OrigSrc; TCHAR node[MEMDB_MAX]; MEMDB_ENUM e; if (EnumFirstPathInOperation (&eOp, OPERATION_SHELL_FOLDER)) { do { MemDbBuildKey (node, EnumPtr->FixedUserName, TEXT("*"), NULL, NULL); if (IsPatternMatch (node, eOp.Path)) { NewDest = NULL; OrigSrc = NULL; if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_SHELL_FOLDER)) { do { if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_DEST)) { NewDest = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_ORIGINAL_SRC)) { OrigSrc = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_SHELLFOLDERS_SRC)) { MYASSERT (NewDest); MYASSERT (OrigSrc); DEBUGMSG ((DBG_NAUSEA, "Per-User SourcePath: %s", eOpProp.Property)); pMigrateShellFolder ( EnumPtr->Win9xUserName, EnumPtr->FixedUserName, FALSE, _tcsinc(_tcschr (eOp.Path, '\\')), eOpProp.Property, NewDest, OrigSrc, 0, NULL ); } } while (EnumNextFileOpProperty (&eOpProp)); } if (NewDest) { FreePathString (NewDest); NewDest = NULL; } if (OrigSrc) { FreePathString (OrigSrc); OrigSrc = NULL; } } } while (EnumNextPathInOperation (&eOp)); } if (EnumPtr->FixedUserName) { MemDbBuildKey ( node, MEMDB_CATEGORY_MYDOCS_WARNING, EnumPtr->FixedUserName, TEXT("*"), NULL ); if (MemDbEnumFirstValue (&e, node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { DEBUGMSG ((DBG_SHELL, "Creating mydocs help file %s", e.szName)); pWriteMyDocsHelpFile (e.szName); } while (MemDbEnumNextValue (&e)); } } } BOOL pCleanupDir ( IN PCTSTR Path, IN BOOL CleanUpRoot ) { TREE_ENUM e; DWORD oldAttributes; if (EnumFirstFileInTreeEx (&e, Path, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) { do { if (e.Directory) { // // This is a dir. Let's see if we enter another shell folder // if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) || ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) e.FullPath, STRTAB_CASE_INSENSITIVE) != -1)) || (IsDirectoryMarkedAsEmpty (e.FullPath)) ) { // // we are just getting into another shell folder. Let's skip it // AbortEnumCurrentDir (&e); } else { SetLongPathAttributes (e.FullPath, FILE_ATTRIBUTE_NORMAL); if (!RemoveLongDirectoryPath (e.FullPath)) { SetLongPathAttributes (e.FullPath, e.FindData->dwFileAttributes); } } } } while (EnumNextFileInTree (&e)); } AbortEnumFileInTree (&e); if (CleanUpRoot) { oldAttributes = GetLongPathAttributes (Path); SetLongPathAttributes (Path, FILE_ATTRIBUTE_NORMAL); if (!RemoveLongDirectoryPath (Path)) { SetLongPathAttributes (Path, oldAttributes); } } return TRUE; } INT pGetCsidlFromTag ( IN PCTSTR ShellFolderIdentifier ) { PCSIDLMAP map; for (map = g_CsidlMap ; map->Tag ; map++) { if (StringIMatch (map->Tag, ShellFolderIdentifier)) { return map->CsidlValue; } } return -1; } INT CALLBACK pSfCopyCallback ( PCTSTR FullFileSpec, PCTSTR DestSpec, WIN32_FIND_DATA *FindData, DWORD EnumTreeID, PVOID Param, PDWORD CurrentDirData ) { // // Put this file in the cleanout category, so that it gets removed unless // it has been backed up. // MemDbSetValueEx ( MEMDB_CATEGORY_CLEAN_OUT, DestSpec, NULL, NULL, BACKUP_FILE, NULL ); return CALLBACK_CONTINUE; } BOOL pCreateSfWithApi ( IN PCTSTR ShellFolderIdentifier, IN PCTSTR FolderToCreate ) { HRESULT hr; INT csidl; TCHAR folderPath[MAX_PATH]; BOOL destroy = FALSE; BOOL result = TRUE; DWORD attribs; // // Convert the tag to a CSIDL constant // csidl = pGetCsidlFromTag (ShellFolderIdentifier); if (csidl < 0) { DEBUGMSG ((DBG_VERBOSE, "CSIDL ID for %s not known", ShellFolderIdentifier)); return FALSE; } // // Query the shell for an existing shell folder // hr = SHGetFolderPath (NULL, csidl, NULL, SHGFP_TYPE_CURRENT, folderPath); if (hr != S_OK && hr != S_FALSE) { DEBUGMSG ((DBG_WARNING, "Can't get shell folder path for ID %s", ShellFolderIdentifier)); return FALSE; } // // Get the attributes of the existing shell folder // if (hr == S_OK) { DEBUGMSG ((DBG_VERBOSE, "Shell folder %s already exists at %s", ShellFolderIdentifier, folderPath)); attribs = GetLongPathAttributes (folderPath); } else { attribs = INVALID_ATTRIBUTES; } // // If existing shell folder is not present, create it temporarily // if (attribs == INVALID_ATTRIBUTES) { DEBUGMSG ((DBG_VERBOSE, "Shell folder %s needs to be created", ShellFolderIdentifier)); destroy = TRUE; hr = SHGetFolderPath ( NULL, csidl | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, folderPath ); if (hr != S_OK) { LOG ((LOG_ERROR, "Can't create shell folder path for ID %s", ShellFolderIdentifier)); return FALSE; } attribs = GetLongPathAttributes (folderPath); if (attribs == INVALID_ATTRIBUTES) { LOG ((LOG_ERROR, "Can't get attributes of %s for ID %s", folderPath, ShellFolderIdentifier)); result = FALSE; } } // // On success (either existing sf or we created it), make a copy of the whole folder // if (result) { MakeSurePathExists (FolderToCreate, TRUE); attribs = GetLongPathAttributes (folderPath); if (attribs != INVALID_ATTRIBUTES) { SetLongPathAttributes (FolderToCreate, attribs); } CopyTree ( folderPath, FolderToCreate, 0, // no EnumTree ID COPYTREE_DOCOPY | COPYTREE_NOOVERWRITE, ENUM_ALL_LEVELS, FILTER_ALL, NULL, // no exclude.inf struct pSfCopyCallback, NULL // no error callback ); } // // If we created the sf, we must destroy it to return the system back // to its original state. We punt the case where power goes out and // GUI mode restarts. // if (destroy) { RemoveCompleteDirectory (folderPath); } return result; } BOOL pMigrateShellFolder ( IN PCTSTR Win9xUser, OPTIONAL IN PCTSTR FixedUserName, OPTIONAL IN BOOL SystemShellFolder, IN PCTSTR ShellFolderIdentifier, IN PCTSTR SourcePath, IN PCTSTR OrgDestinationPath, IN PCTSTR OrigSourcePath, IN DWORD UserFlags, IN PMIGRATE_USER_ENUM EnumPtr ) { TREE_ENUM e; PSHELL_FOLDER_FILTER Filter; TCHAR DefaultShellFolder[MAX_TCHAR_PATH]; PCTSTR DestPath = NULL; PROFILE_MERGE_DATA Data; BOOL Result = FALSE; TCHAR UserRoot[MAX_TCHAR_PATH]; PCTSTR NtDefaultLocation = NULL; PCTSTR DefaultUserLocation = NULL; PCTSTR tempExpand = NULL; PCTSTR nextExpand; TCHAR ShellFolderPath[MAX_TCHAR_PATH]; DWORD Offset; DWORD Size; HKEY Key; DWORD Attributes; PCTSTR ValData = NULL; PTSTR p; DWORD d; HKEY UserHiveRoot; LONG rc; PCTSTR EncodedKey; PCTSTR NewDestPath; BOOL AlreadyMoved; PCTSTR OrigFullPath; BOOL regFolder; PCTSTR freeMe; TCHAR driveLetter[] = TEXT("?:"); BOOL allUsers; BOOL keep; PCWSTR OrigRootPath, DestRootPath; PBYTE bufferRoot; PTSTR destPathBuffer; DWORD fileStatus; __try { bufferRoot = MemAllocUninit (MEMDB_MAX * sizeof (TCHAR)); if (!bufferRoot) { __leave; } destPathBuffer = (PTSTR) bufferRoot; DEBUGMSG ((DBG_SHELL, "Entering shell folder %s", ShellFolderIdentifier)); regFolder = TRUE; if (StringIMatch (ShellFolderIdentifier, S_SF_PROFILES)) { regFolder = FALSE; } if (StringIMatch (ShellFolderIdentifier, S_SF_COMMON_PROFILES)) { regFolder = FALSE; } // // Get root default folder // Size = sizeof (DefaultShellFolder); if (!GetDefaultUserProfileDirectory (DefaultShellFolder, &Size)) { MYASSERT (FALSE); __leave; } if (regFolder) { // // Get ShellFolderPath (with environment variables in it) // if (SystemShellFolder) { UserHiveRoot = HKEY_LOCAL_MACHINE; } else { UserHiveRoot = g_DefaultHiveRoot; } Key = OpenRegKey (UserHiveRoot, S_USER_SHELL_FOLDERS_KEY); if (Key) { ValData = GetRegValueString (Key, ShellFolderIdentifier); DEBUGMSG_IF ((!ValData, DBG_WARNING, "Can't get NT default for %s from registry", ShellFolderIdentifier)); CloseRegKey (Key); } ELSE_DEBUGMSG ((DBG_ERROR, "Can't open %s", S_USER_SHELL_FOLDERS_KEY)); if (ValData) { StringCopy (ShellFolderPath, ValData); MemFree (g_hHeap, 0, ValData); ValData = NULL; } else { wsprintf (ShellFolderPath, TEXT("%s\\%s"), S_USERPROFILE_ENV, ShellFolderIdentifier); } } // // Get the user's profile root // if (FixedUserName) { if (!GetUserProfilePath (FixedUserName, &p)) { MYASSERT (FALSE); __leave; } StringCopy (UserRoot, p); allUsers = FALSE; FreePathString (p); } else { Size = sizeof (UserRoot); if (regFolder) { if (!GetAllUsersProfileDirectory (UserRoot, &Size)) { MYASSERT (FALSE); __leave; } allUsers = TRUE; } else { if (!GetProfilesDirectory (UserRoot, &Size)) { MYASSERT (FALSE); __leave; } allUsers = FALSE; } } if (regFolder) { // // Compute the default NT location and the Default User location // tempExpand = StringSearchAndReplace ( ShellFolderPath, S_USERPROFILE_ENV, UserRoot ); if (!tempExpand) { tempExpand = DuplicatePathString (ShellFolderPath, 0); } } else { tempExpand = DuplicatePathString (UserRoot, 0); } NtDefaultLocation = ExpandEnvironmentText (tempExpand); FreePathString (tempExpand); if (regFolder) { tempExpand = StringSearchAndReplace ( ShellFolderPath, S_USERPROFILE_ENV, DefaultShellFolder ); if (!tempExpand) { tempExpand = DuplicatePathString (ShellFolderPath, 0); } } else { tempExpand = StringSearchAndReplace ( UserRoot, S_USERPROFILE_ENV, DefaultShellFolder ); if (!tempExpand) { tempExpand = DuplicatePathString (UserRoot, 0); } } DefaultUserLocation = ExpandEnvironmentText (tempExpand); FreePathString (tempExpand); // // Init the filter data struct // ZeroMemory (&Data, sizeof (Data)); Data.Win9xUser = Win9xUser; Data.FixedUserName = FixedUserName; Data.UserHiveRoot = UserHiveRoot; Data.ShellFolderIdentifier = ShellFolderIdentifier; Data.DefaultShellFolder = DefaultUserLocation; Data.UserDefaultLocation = NtDefaultLocation; Data.UserFlags = UserFlags; Data.Context = INITIALIZE; StringCopyByteCount (Data.TempSourcePath, SourcePath, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath)); Data.SrcRootPath = SourcePath; Data.DestRootPath = OrgDestinationPath; Data.OrigRootPath = OrigSourcePath; Data.EnumPtr = EnumPtr; Data.Attributes = GetLongPathAttributes (OrgDestinationPath); // // Establish the shell folder using the shell APIs // if (pCreateSfWithApi ( ShellFolderIdentifier, OrgDestinationPath )) { DEBUGMSG (( DBG_VERBOSE, "Using API defaults for shell folder %s", ShellFolderIdentifier )); Data.Attributes = GetLongPathAttributes (OrgDestinationPath); } if (Data.Attributes == INVALID_ATTRIBUTES) { // // We don't care about this shell folder's desktop.ini or // attributes -- use the NT default attributes, or the // Win9x attributes if there is no default. // Data.Attributes = GetLongPathAttributes (NtDefaultLocation); if (Data.Attributes == INVALID_ATTRIBUTES) { Data.Attributes = GetLongPathAttributes (Data.TempSourcePath); } if (Data.Attributes == INVALID_ATTRIBUTES) { // // This happens for shell folders like My Music & My Video // which don't exist on Win9x // Data.Attributes = FILE_ATTRIBUTE_READONLY; } MakeSureLongPathExists (OrgDestinationPath, TRUE); SetLongPathAttributes (OrgDestinationPath, Data.Attributes); DEBUGMSG (( DBG_VERBOSE, "Using previous OS desktop.ini for shell folder %s, attribs=%08X", ShellFolderIdentifier, Data.Attributes )); } // // Now add string mappings for this shell folder. The reason for doing // this is that we want to catch the case of paths to non-existent files // within shell stored in the registry. // OrigRootPath = JoinPaths (Data.OrigRootPath, TEXT("")); DestRootPath = JoinPaths (Data.DestRootPath, TEXT("")); AddStringMappingPair (g_SubStringMap, OrigRootPath, DestRootPath); FreePathString (DestRootPath); FreePathString (OrigRootPath); // // PHASE ONE - move the files from 9x shell folder to their NT locations // // // Call filters for init // for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "9X->NT: INIT: %s (enter)", Filter->Name)); Data.State = 0; Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "9X->NT: INIT: %s (done)", Filter->Name)); } // // Enumerate the shell folder and move it to the destination // DEBUGMSG ((DBG_SHELL, "9X->NT: Enumerating %s", SourcePath)); if (EnumFirstFileInTree (&e, SourcePath, NULL, FALSE)) { do { // // Update the filter data struct // OrigFullPath = JoinPaths (OrigSourcePath, e.SubPath); fileStatus = GetFileInfoOnNt (OrigFullPath, destPathBuffer, MEMDB_MAX); DestPath = destPathBuffer; if (fileStatus == FILESTATUS_UNCHANGED) { // // No reason not to move this file too // MYASSERT (StringIMatch (destPathBuffer, OrigFullPath)); DestPath = JoinPaths (Data.DestRootPath, e.SubPath); if (!StringIMatch (OrigFullPath, DestPath)) { MarkFileForMoveExternal (OrigFullPath, DestPath); } } Data.Attributes = e.FindData->dwFileAttributes; StringCopyByteCount (Data.TempSourcePath, e.FullPath, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, DestPath, sizeof (Data.DestinationPath)); Data.Context = PROCESS_PATH; DEBUGMSG ((DBG_SHELL, "9X->NT: Original temp source path: %s", Data.TempSourcePath)); // // Allow filters to change source or dest, or to skip copy // keep = TRUE; for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "9X->NT: FILTER: %s (enter)", Filter->Name)); Data.State = Filter->State; d = Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "9X->NT: FILTER: %s (result=%u)", Filter->Name, d)); // ignore SHELLFILTER_ERROR & try to complete processing if (d == SHELLFILTER_FORCE_CHANGE) { DEBUGMSG ((DBG_SHELL, "9X->NT: Skipping additional filters because shell folder filter %hs said so", Filter->Name)); break; } if (d == SHELLFILTER_SKIP_FILE) { DEBUGMSG ((DBG_SHELL, "9X->NT:Skipping %s because shell folder filter %hs said so", DestPath, Filter->Name)); keep = FALSE; break; } if (d == SHELLFILTER_SKIP_DIRECTORY) { AbortEnumCurrentDir (&e); keep = FALSE; break; } } if (keep && !(Data.Attributes & FILE_ATTRIBUTE_DIRECTORY)) { // // Is source different from the dest? // if (!StringIMatch (Data.TempSourcePath, Data.DestinationPath)) { // // Make sure dest exists // MakeSureLongPathExists (Data.DestinationPath, FALSE); // FALSE == not path only // // Move or copy the file. // pQueueSfMove (Data.TempSourcePath, Data.DestinationPath); } } else if (keep) { MakeSureLongPathExists (Data.DestinationPath, TRUE); // TRUE == path only SetLongPathAttributes (Data.DestinationPath, Data.Attributes); } else if (d == SHELLFILTER_SKIP_FILE) { // // Mark this file for deletion if it won't be moved from temp to dest // if (!StringIMatch (Data.TempSourcePath, Data.DestinationPath)) { DEBUGMSG ((DBG_SHELL, "Deleting shell folder file %s", e.FullPath)); ForceOperationOnPath (e.FullPath, OPERATION_CLEANUP); } } if (DestPath && DestPath != destPathBuffer) { FreePathString (DestPath); } DestPath = NULL; FreePathString (OrigFullPath); OrigFullPath = NULL; } while (EnumNextFileInTree (&e)); } pFlushSfQueue(); // // Call filters one last time // Data.Attributes = 0; Data.Context = TERMINATE; StringCopyByteCount (Data.TempSourcePath, SourcePath, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath)); for (Filter = g_Filters_9xNt ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "9X->NT: TERMINATE: %s (enter)", Filter->Name)); Data.State = Filter->State; Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "9X->NT: TERMINATE: %s (done)", Filter->Name)); } // // Now cleanup this directory for all empty dirs (excluding the root) // Do not cleanup non reg folders!! // if (regFolder) { DEBUGMSG ((DBG_NAUSEA, "Cleaning up %s", Data.DestinationPath)); pCleanupDir (Data.DestinationPath, FALSE); } // // PHASE TWO - if necessary, merge files from NT default shell folder // to the new location and update the registry // if (regFolder) { // // Encode string with %USERPROFILE%/%ALLUSERSPROFILE%, %SYSTEMROOT% // or %SYSTEMDRIVE% if possible // // %USERPROFILE% or %ALLUSERSPROFILE% tempExpand = OrgDestinationPath; if (allUsers) { nextExpand = StringSearchAndReplace ( tempExpand, UserRoot, S_ALLUSERSPROFILE_ENV ); } else { nextExpand = StringSearchAndReplace ( tempExpand, UserRoot, S_USERPROFILE_ENV ); } if (nextExpand) { tempExpand = nextExpand; } // %SYSTEMROOT% nextExpand = StringSearchAndReplace ( tempExpand, g_WinDir, S_SYSTEMROOT_ENV ); if (nextExpand) { if (tempExpand != OrgDestinationPath) { FreePathString (tempExpand); } tempExpand = nextExpand; } // %SYSTEMDRIVE% driveLetter[0] = g_WinDir[0]; nextExpand = StringSearchAndReplace ( tempExpand, driveLetter, S_SYSTEMDRIVE_ENV ); if (nextExpand) { if (tempExpand != OrgDestinationPath) { FreePathString (tempExpand); } tempExpand = nextExpand; } // tempExpand points to OrgDestinationPath or a expanded path from the path pool MYASSERT (tempExpand); // // Now store it. If HKLM, put it in the registry. Otherwise, put it // in memdb, which will later be put in the user's hive. // if (Data.UserHiveRoot == HKEY_LOCAL_MACHINE) { // // Update the registry, User Shell Folder must point to original // location // Key = OpenRegKey (Data.UserHiveRoot, S_USER_SHELL_FOLDERS_KEY); if (Key) { rc = RegSetValueEx ( Key, Data.ShellFolderIdentifier, 0, REG_EXPAND_SZ, (PBYTE) tempExpand, SizeOfString (tempExpand) ); DEBUGMSG_IF (( rc != ERROR_SUCCESS, DBG_ERROR, "Can't save %s for %s", tempExpand, Data.ShellFolderIdentifier )); DEBUGMSG_IF (( rc == ERROR_SUCCESS, DBG_SHELL, "Win9x shell location preserved: %s (%s)", tempExpand, Data.ShellFolderIdentifier )); CloseRegKey (Key); } ELSE_DEBUGMSG ((DBG_ERROR, "Can't open %s", S_USER_SHELL_FOLDERS_KEY)); } else { EncodedKey = CreateEncodedRegistryStringEx ( S_USER_SHELL_FOLDERS_KEY, Data.ShellFolderIdentifier, FALSE ); MemDbSetValueEx ( MEMDB_CATEGORY_USER_REGISTRY_VALUE, tempExpand, NULL, NULL, REG_EXPAND_SZ, &Offset ); MemDbSetValueEx ( MEMDB_CATEGORY_SET_USER_REGISTRY, Data.FixedUserName, EncodedKey, NULL, Offset, NULL ); FreeEncodedRegistryString (EncodedKey); } if (tempExpand != OrgDestinationPath) { FreePathString (tempExpand); } } if (!StringIMatch (OrgDestinationPath, NtDefaultLocation)) { // // Now move from the NT default location into the preserved location // // // Fix the Data structure // Data.UserFlags = UserFlags; Data.Context = INITIALIZE; StringCopyByteCount (Data.TempSourcePath, NtDefaultLocation, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath)); Data.SrcRootPath = NtDefaultLocation; Data.DestRootPath = OrgDestinationPath; Data.OrigRootPath = OrigSourcePath; // // Now check to see if we already moved something into the preserved directory. // If we did, we will not make the move (we will only delete the default files). // if (g_Merged9xFolders && (pSetupStringTableLookUpString (g_Merged9xFolders, (PTSTR)Data.DestRootPath, 0) != -1)) { AlreadyMoved = TRUE; } else { AlreadyMoved = FALSE; pSetupStringTableAddString (g_Merged9xFolders, (PVOID) Data.DestRootPath, STRTAB_CASE_INSENSITIVE); } // // Call filters for init // for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "NT->9X: INIT: %s (enter)", Filter->Name)); Data.State = 0; Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "NT->9X: INIT: %s (done)", Filter->Name)); } DEBUGMSG ((DBG_SHELL, "NT->9X: Enumerating %s", Data.TempSourcePath)); MYASSERT (Data.TempSourcePath && *Data.TempSourcePath); if (EnumFirstFileInTree (&e, Data.TempSourcePath, NULL, FALSE)) { do { // // This is only needed for user shell folders but does not hurt. // if (StringIMatch (TEXT("ntuser.dat"), e.Name)) { continue; } // // start with the assumption that the dest file is under the original // destination path // NewDestPath = JoinPaths (OrgDestinationPath, e.SubPath); // // If this is desktop.ini, merge it with the existing one // if (StringIMatch (TEXT("desktop.ini"), e.Name)) { DEBUGMSG (( DBG_VERBOSE, "Merging clean install %s with the one in Default User", e.FullPath )); MergeIniFile (NewDestPath, e.FullPath, FALSE); continue; } // // Not the root shell folder desktop.ini -- continue processing // Data.Attributes = e.FindData->dwFileAttributes; StringCopyByteCount (Data.TempSourcePath, e.FullPath, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, NewDestPath, sizeof (Data.DestinationPath)); Data.Context = PROCESS_PATH; DEBUGMSG ((DBG_SHELL, "NT->9X: Original temp source path: %s", Data.TempSourcePath)); // // if we only need to delete the default files, skip the filters // if (AlreadyMoved) { SetLongPathAttributes (Data.TempSourcePath, FILE_ATTRIBUTE_NORMAL); if (!DeleteLongPath (Data.TempSourcePath)) { SetLongPathAttributes (Data.TempSourcePath, Data.Attributes); DEBUGMSG ((DBG_WARNING, "%s could not be removed.", Data.TempSourcePath)); } } else { // // Allow filters to change source or dest, or to skip copy // keep = TRUE; for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "NT->9X: FILTER: %s (enter)", Filter->Name)); Data.State = Filter->State; d = Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "NT->9X: FILTER: %s (result=%u)", Filter->Name, d)); if (d == SHELLFILTER_FORCE_CHANGE) { break; } if (d == SHELLFILTER_SKIP_FILE) { keep = FALSE; break; } if (d == SHELLFILTER_SKIP_DIRECTORY) { AbortEnumCurrentDir (&e); keep = FALSE; break; } } if (keep) { if (!(e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { pQueueSfMove (Data.TempSourcePath, Data.DestinationPath); } else { MakeSureLongPathExists (Data.DestinationPath, TRUE); // TRUE == path only SetLongPathAttributes (Data.DestinationPath, Data.Attributes); } } } FreePathString (NewDestPath); } while (EnumNextFileInTree (&e)); } pFlushSfQueue(); // // Call filters one last time // Data.Attributes = 0; Data.Context = TERMINATE; StringCopyByteCount (Data.TempSourcePath, NtDefaultLocation, sizeof (Data.TempSourcePath)); StringCopyByteCount (Data.DestinationPath, OrgDestinationPath, sizeof (Data.DestinationPath)); for (Filter = g_Filters_Nt9x ; Filter->Fn ; Filter++) { //DEBUGMSGA ((DBG_SHELL, "NT->9X: TERMINATE: %s (enter)", Filter->Name)); Data.State = Filter->State; Filter->Fn (&Data); Filter->State = Data.State; //DEBUGMSGA ((DBG_SHELL, "NT->9X: TERMINATE: %s (done)", Filter->Name)); } // // Now cleanup this directory for all empty dirs (including the root) // Do not cleanup non reg folders!! // if (regFolder) { DEBUGMSG ((DBG_NAUSEA, "Cleaning up %s (including root)", Data.TempSourcePath)); pCleanupDir (Data.TempSourcePath, TRUE); } } // // Loop through the whole tree and add desktop.ini to cleanup // if (EnumFirstFileInTree (&e, OrgDestinationPath, NULL, FALSE)) { do { if (!e.Directory) { continue; } MemDbSetValueEx ( MEMDB_CATEGORY_CLEAN_OUT, e.FullPath, TEXT("desktop.ini"), NULL, BACKUP_FILE, NULL ); } while (EnumNextFileInTree (&e)); } Result = TRUE; } __finally { PushError(); AbortEnumFileInTree (&e); FreeText (NtDefaultLocation); FreeText (DefaultUserLocation); if (bufferRoot) { FreeMem (bufferRoot); } PopError(); } DEBUGMSG (( DBG_SHELL, "Leaving shell folder %s with result %s", ShellFolderIdentifier, Result ? TEXT("TRUE") : TEXT("FALSE") )); return Result; } HKEY pLoadDefaultUserHive ( VOID ) { DWORD Size; BOOL b; LONG rc; if (!g_DefaultHiveMapped) { if (!g_DefaultHivePath[0]) { Size = sizeof (g_DefaultHivePath); b = GetDefaultUserProfileDirectory (g_DefaultHivePath, &Size); MYASSERT (b); if (!b) { wsprintf (g_DefaultHivePath, TEXT("%s\\profiles\\default user"), g_WinDir); } StringCopy (AppendWack (g_DefaultHivePath), TEXT("ntuser.dat")); } rc = RegLoadKey (HKEY_USERS, S_DEFAULT_USER, g_DefaultHivePath); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't load default user hive from %s", g_DefaultHivePath)); g_DefaultHiveRoot = NULL; return NULL; } g_DefaultHiveRoot = OpenRegKey (HKEY_USERS, S_DEFAULT_USER); if (!g_DefaultHiveRoot) { DEBUGMSG ((DBG_WHOOPS, "Loaded hive %s but could not open it", g_DefaultHivePath)); } } g_DefaultHiveMapped++; return g_DefaultHiveRoot; } VOID pUnloadDefaultUserHive ( VOID ) { if (!g_DefaultHiveMapped) { return; } g_DefaultHiveMapped--; if (!g_DefaultHiveMapped) { CloseRegKey (g_DefaultHiveRoot); RegUnLoadKey (HKEY_USERS, S_DEFAULT_USER); } } VOID pLoadIgnoredCollisions ( VOID ) { INFCONTEXT context; TCHAR sfId[MEMDB_MAX]; TCHAR file[MEMDB_MAX]; INT value; MYASSERT (g_WkstaMigInf); if (SetupFindFirstLine (g_WkstaMigInf, S_IGNORED_COLLISIONS, NULL, &context)) { do { if (SetupGetStringField (&context, 1, sfId, MEMDB_MAX, NULL) && SetupGetStringField (&context, 2, file, MEMDB_MAX, NULL) && SetupGetIntField (&context, 3, &value) ) { MemDbSetValueEx (MEMDB_CATEGORY_IGNORED_COLLISIONS, sfId, file, NULL, value, NULL); } } while (SetupFindNextLine (&context, &context)); } } DWORD MigrateShellFolders ( IN DWORD Request ) { MIGRATE_USER_ENUM e; if (Request == REQUEST_QUERYTICKS) { return TICKS_SYSTEM_SHELL_MIGRATION; } else if (Request != REQUEST_RUN) { return ERROR_SUCCESS; } pPrepareSfRestartability(); pLoadIgnoredCollisions (); g_SystemSfList = pCreateSystemSfList (); g_UserSfList = pCreateUserSfList (NULL); pCreateLinksList (); pCreateLinksRenameList (); pDestroySfList (g_UserSfList); pLoadDefaultUserHive(); g_Merged9xFolders = pSetupStringTableInitialize(); pMigrateSystemShellFolders(); if (EnumFirstUserToMigrate (&e, ENUM_NO_FLAGS)) { do { if (!e.CreateOnly && e.AccountType != DEFAULT_USER_ACCOUNT) { pMigrateUserShellFolders (&e); } } while (EnumNextUserToMigrate (&e)); } if (g_Merged9xFolders) { pSetupStringTableDestroy (g_Merged9xFolders); } pFlushSfQueue(); pUnloadDefaultUserHive(); pDestroyLinksData (); pDestroySfList (g_SystemSfList); return ERROR_SUCCESS; } PCTSTR GenerateNewFileName ( IN PCTSTR OldName, IN WORD Sequencer, IN BOOL CheckExistence ) { PCTSTR extPtr; PTSTR newName; PTSTR result; extPtr = GetFileExtensionFromPath (OldName); if (!extPtr) { extPtr = GetEndOfString (OldName); } else { extPtr = _tcsdec (OldName, extPtr); } newName = DuplicatePathString (OldName, 0); result = DuplicatePathString (OldName, 10); StringCopyAB (newName, OldName, extPtr); do { Sequencer ++; wsprintf (result, TEXT("%s (%u)%s"), newName, Sequencer, extPtr); } while ((CheckExistence) && (DoesFileExist (result))); FreePathString (newName); return result; } BOOL pIgnoredCollisions ( IN PPROFILE_MERGE_DATA Data ) { TCHAR key[MEMDB_MAX]; DWORD value; MemDbBuildKey ( key, MEMDB_CATEGORY_IGNORED_COLLISIONS, Data->ShellFolderIdentifier, GetFileNameFromPath (Data->DestinationPath), NULL); if (MemDbGetPatternValue (key, &value)) { return value; } else { return 0; } } // // Filters 9X -> NT // DWORD pCollisionDetection9xNt ( IN OUT PPROFILE_MERGE_DATA Data ) { // // this filter will detect name collision while copying win9x shell folders files. // If we have a name collision, it means that NT already installed a file with the // same name. In this case, we want the new file to be survive even with a different // name. We will build a new file name starting with filename.ext. The new file will // look something like filename001.ext. In all cases we want to keep the extension, // since there might be some shell extensions active for this file. // Important: we do not care about directory collisions. // PCTSTR newName; PCTSTR OriginalSource; PCTSTR extPtr; DWORD value; switch (Data->Context) { case INITIALIZE: break; case PROCESS_PATH: if ((!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) && (!StringIMatch (Data->SrcRootPath, Data->DestRootPath)) && (DoesFileExist (Data->DestinationPath)) ) { value = pIgnoredCollisions (Data); if (value) { if (value == 1) { // we should keep the NT file // By returning SHELLFILTER_SKIP_FILE we are instructing the copy routine // not to copy this file. As a result the already installed NT file will // survive return SHELLFILTER_SKIP_FILE; } else { // we should keep the 9x file // We want to delete the NT file installed here to make room for the 9x // file that should be copied when we return from this filter SetLongPathAttributes (Data->DestinationPath, FILE_ATTRIBUTE_NORMAL); DeleteLongPath (Data->DestinationPath); } } else { newName = GenerateNewFileName (Data->DestinationPath, 0, TRUE); //TRUE - check unique StringCopyByteCount (Data->DestinationPath, newName, sizeof (Data->DestinationPath)); FreePathString (newName); // // now if this was a link we need to fix the destination of the move external operation // We have two reasons to do this. One is that the LinkEdit code needs the actual destination // to be able to edit the link, and secondly we need this new target for the uninstall programs // to work properly. If this file is not a LNK or a PIF, we don't care, we want everybody to // use the NT installed file. BTW, there is a collision here only because NT installed a file // with the same name in this location. // extPtr = GetFileExtensionFromPath (Data->DestinationPath); if ((extPtr) && ((StringIMatch (extPtr, TEXT("LNK"))) || (StringIMatch (extPtr, TEXT("PIF"))) ) ) { // // Get the original source for this file // OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath); MYASSERT (OriginalSource); if (IsFileMarkedForOperation (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) { RemoveOperationsFromPath (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER); MarkFileForShellFolderMove (OriginalSource, Data->DestinationPath); } FreePathString (OriginalSource); } } } break; case TERMINATE: break; } return SHELLFILTER_OK; } DWORD pFontNameFilter ( IN OUT PPROFILE_MERGE_DATA Data ) { static HASHTABLE HashTable; HKEY FontKey; REGVALUE_ENUM e; PCTSTR Font; switch (Data->Context) { case INITIALIZE: // // Preload a hash table with all the font names // HashTable = HtAlloc(); FontKey = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")); if (FontKey) { if (EnumFirstRegValue (&e, FontKey)) { do { Font = GetRegValueString (FontKey, e.ValueName); if (Font) { HtAddString (HashTable, Font); MemFree (g_hHeap, 0, Font); } ELSE_DEBUGMSG ((DBG_ERROR, "Can't get value data for %s in fonts key", e.ValueName)); } while (EnumNextRegValue (&e)); } CloseRegKey (FontKey); } ELSE_LOG ((LOG_ERROR, "Can't open Fonts registry key. There may be duplicate font files.")); break; case PROCESS_PATH: // // If the shell folder is Fonts, and the font is already // registered, skip the Win9x copy. // if (StringIMatch (Data->ShellFolderIdentifier, TEXT("Fonts"))) { if (!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) { if (DoesFileExist (Data->DestinationPath)) { // // NT already installed this file. We won't overwrite this // with the 9x copy. // DEBUGMSG (( DBG_SHELL, "Skipping copy of already existent font file: %s", Data->DestinationPath )); return SHELLFILTER_SKIP_FILE; } if (HtFindString (HashTable, GetFileNameFromPath (Data->DestinationPath))) { DEBUGMSG (( DBG_SHELL, "Skipping copy of already registered font file: %s", Data->DestinationPath )); return SHELLFILTER_SKIP_FILE; } } } break; case TERMINATE: HtFree (HashTable); HashTable = NULL; break; } return SHELLFILTER_OK; } BOOL pIsCommonSf ( IN PCTSTR ShellFolderTag ) { TCHAR memdbKey[MAX_SHELL_TAG + 32]; if (StringIPrefix (ShellFolderTag, TEXT("Common"))) { return TRUE; } MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_COMMON, ShellFolderTag, NULL, NULL); return MemDbGetValue (memdbKey, NULL); } VOID pConvertPerUserSfToCommon ( IN PCTSTR PerUserSf, OUT PTSTR CommonSf // must hold MAX_SHELL_TAG chars ) { TCHAR memdbKey[MAX_SHELL_TAG + 32]; DWORD offset; BOOL useDefault = TRUE; MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_PERUSER, PerUserSf, NULL, NULL); if (MemDbGetValue (memdbKey, &offset)) { if (MemDbBuildKeyFromOffset (offset, CommonSf, 1, NULL)) { useDefault = FALSE; } } if (useDefault) { wsprintf (CommonSf, TEXT("Common %s"), PerUserSf); } } VOID pConvertCommonSfToPerUser ( IN PCTSTR CommonSf, OUT PTSTR PerUserSf // must hold MAX_SHELL_TAG chars ) { TCHAR memdbKey[MAX_SHELL_TAG + 32]; DWORD offset; BOOL useDefault = TRUE; MemDbBuildKey (memdbKey, MEMDB_CATEGORY_SF_COMMON, CommonSf, NULL, NULL); if (MemDbGetValue (memdbKey, &offset)) { if (MemDbBuildKeyFromOffset (offset, PerUserSf, 1, NULL)) { useDefault = FALSE; } } if (useDefault) { if (StringIPrefix (CommonSf, TEXT("Common"))) { CommonSf += 6; if (_tcsnextc (CommonSf) == TEXT(' ')) { CommonSf++; } } StringCopy (PerUserSf, CommonSf); } } BOOL pIsObsoleteLink ( IN PCTSTR ShortcutName, IN PCTSTR ShortcutTarget, IN PCTSTR ShortcutArgs, IN PCTSTR CurrentShellFolder, IN PCTSTR CurrentShellFolderPath ) { PLINK_DATA linkData = NULL; PLINK_RENAME_DATA linkRenameData = NULL; LONG stringId; TCHAR perUserName[MAX_SHELL_TAG]; DEBUGMSG (( DBG_SHELL, "pIsObsoleteLink: Checking %s\n" " Input Target: %s\n" " Input Args: %s\n" " Current Shell Folder: %s\n" " Current Shell Folder Path: %s", ShortcutName, ShortcutTarget, ShortcutArgs, CurrentShellFolder, CurrentShellFolderPath )); pConvertCommonSfToPerUser (CurrentShellFolder, perUserName); stringId = pSetupStringTableLookUpString (g_FoldersTable, perUserName, 0); if (stringId != -1) { pSetupStringTableGetExtraData (g_FoldersTable, stringId, &linkData, sizeof (PLINK_DATA)); while (linkData) { #if 0 DEBUGMSG (( DBG_SHELL, "Checking NT-installed LNK:\n" " Target: %s\n" " Args: %s", linkData->Target, linkData->Arguments )); #endif if ((IsPatternMatch (linkData->Target, ShortcutTarget)) && (IsPatternMatch (linkData->Arguments, ShortcutArgs)) ) { DEBUGMSG (( DBG_SHELL, "Obsolete link:\n" " \"%s\" matched \"%s\"\n" " \"%s\" matched \"%s\"", linkData->Target, ShortcutTarget, linkData->Arguments, ShortcutArgs )); return TRUE; } linkRenameData = g_LinkRenameData; while (linkRenameData) { #if 0 DEBUGMSG (( DBG_SHELL, "Checking NT rename data:\n" " Old Target: %s\n" " New Target: %s\n" " Old Args: %s\n" " New Args: %s", linkRenameData->OldTarget, linkRenameData->NewTarget, linkRenameData->OldArguments, linkRenameData->NewArguments )); #endif if (StringIMatch (linkRenameData->ShellFolderName, perUserName)) { if ((IsPatternMatch (linkRenameData->OldTarget, ShortcutTarget)) && (IsPatternMatch (linkRenameData->NewTarget, linkData->Target)) && (IsPatternMatch (linkRenameData->OldArguments, ShortcutArgs)) && (IsPatternMatch (linkRenameData->NewArguments, linkData->Arguments)) ) { DEBUGMSG (( DBG_SHELL, "Obsolete link:\n" " \"%s\" matched \"%s\"\n" " \"%s\" matched \"%s\"\n" " \"%s\" matched \"%s\"\n" " \"%s\" matched \"%s\"\n", linkRenameData->OldTarget, ShortcutTarget, linkRenameData->NewTarget, linkData->Target, linkRenameData->OldArguments, ShortcutArgs, linkRenameData->NewArguments, linkData->Arguments )); return TRUE; } } linkRenameData = linkRenameData->Next; } linkData = linkData->Next; } } ELSE_DEBUGMSG ((DBG_SHELL, "Nothing in shell folder %s is obsolete", perUserName)); return FALSE; } DWORD pStartupDisableFilter ( IN OUT PPROFILE_MERGE_DATA Data ) { DWORD status; PCTSTR originalSource; PCTSTR newSource; PCTSTR path; TCHAR disablePath[MAX_TCHAR_PATH]; PTSTR dontCare; DWORD result = SHELLFILTER_OK; switch (Data->Context) { case INITIALIZE: break; case PROCESS_PATH: DEBUGMSG (( DBG_SHELL, __FUNCTION__ ": Processing %s in %s", Data->TempSourcePath, Data->ShellFolderIdentifier )); if (!StringIMatch (Data->ShellFolderIdentifier, TEXT("startup")) && !StringIMatch (Data->ShellFolderIdentifier, TEXT("common startup")) ) { DEBUGMSG (( DBG_SHELL, "Shell folder ID %s does not match startup or common startup", Data->ShellFolderIdentifier )); break; } if (Data->DestRootPath[0] == 0 || Data->DestRootPath[1] == 0 || Data->DestRootPath[2] == 0 || Data->DestRootPath[3] == 0 ) { DEBUGMSG (( DBG_SHELL, "Skipping disable of startup item %s because its dest is a root directory", Data->DestinationPath )); break; } originalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath); MYASSERT (originalSource); DEBUGMSG ((DBG_SHELL, "Checking if %s is disabled", originalSource)); if (IsFileDisabled (originalSource)) { // // Redirect disabled startup items to ..\Disabled Startup // path = JoinPaths (Data->DestRootPath, TEXT("..\\Disabled Startup")); MakeSureLongPathExists (path, TRUE); // TRUE == path only GetFullPathName (path, ARRAYSIZE(disablePath), disablePath, &dontCare); FreePathString (path); DEBUGMSG ((DBG_SHELL, "Disabled startup dest is %s", disablePath)); SetLongPathAttributes (disablePath, FILE_ATTRIBUTE_HIDDEN); newSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, disablePath); StringCopy (Data->DestinationPath, newSource); FreePathString (newSource); DEBUGMSG ((DBG_SHELL, "Startup item moved to %s", Data->DestinationPath)); RemoveOperationsFromPath (originalSource, OPERATION_FILE_DISABLED); if (IsFileMarkedForOperation (originalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) { RemoveOperationsFromPath (originalSource, OPERATION_FILE_MOVE_SHELL_FOLDER); MarkFileForShellFolderMove (originalSource, Data->DestinationPath); } // // By returning SHELLFILTER_FORCE_CHANGE, we are instructing the // shell folder algorithm to use our destination and not call anyone // else. // result = SHELLFILTER_FORCE_CHANGE; } FreePathString (originalSource); break; case TERMINATE: break; } return result; } DWORD pObsoleteLinksFilter ( IN OUT PPROFILE_MERGE_DATA Data ) { static IShellLink *shellLink = NULL; static IPersistFile *persistFile = NULL; static PTSTR bigBuf; static PTSTR ShortcutTarget; static PTSTR ShortcutArgs; static PTSTR ShortcutWorkDir; static PTSTR ShortcutIconPath; INT ShortcutIcon; WORD ShortcutHotKey; BOOL result = FALSE; BOOL dosApp; BOOL msDosMode; PCTSTR extPtr; FILEOP_PROP_ENUM eOpProp; PTSTR NewTarget; PCTSTR OriginalSource; switch (Data->Context) { case INITIALIZE: if (!InitCOMLink (&shellLink, &persistFile)) { DEBUGMSG ((DBG_ERROR, "Cannot initialize COM. Obsolete links filter will not work.")); return SHELLFILTER_ERROR; } bigBuf = (PTSTR) MemAllocUninit ((MEMDB_MAX * 4) * sizeof (TCHAR)); if (!bigBuf) { return SHELLFILTER_ERROR; } ShortcutTarget = bigBuf; ShortcutArgs = ShortcutTarget + MEMDB_MAX; ShortcutWorkDir = ShortcutArgs + MEMDB_MAX; ShortcutIconPath = ShortcutWorkDir + MEMDB_MAX; break; case PROCESS_PATH: extPtr = GetFileExtensionFromPath (Data->DestinationPath); if (!extPtr) { return SHELLFILTER_OK; } if ((!StringIMatch (extPtr, TEXT("LNK"))) && (!StringIMatch (extPtr, TEXT("PIF"))) ) { return SHELLFILTER_OK; } DEBUGMSG ((DBG_SHELL, "Extracting shortcut info for temp file %s", Data->TempSourcePath)); if ((shellLink) && (persistFile) && (ExtractShortcutInfo ( ShortcutTarget, ShortcutArgs, ShortcutWorkDir, ShortcutIconPath, &ShortcutIcon, &ShortcutHotKey, &dosApp, &msDosMode, NULL, NULL, Data->TempSourcePath, shellLink, persistFile ))) { // get the new destination if this shortcut is to be edited NewTarget = NULL; // // Get the original source for this file // OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath); MYASSERT (OriginalSource); DEBUGMSG ((DBG_SHELL, "OriginalSource for shortcut is %s", OriginalSource)); if (IsFileMarkedForOperation (OriginalSource, OPERATION_LINK_EDIT)) { DEBUGMSG ((DBG_SHELL, "OriginalSource is marked for file edit")); if (EnumFirstFileOpProperty (&eOpProp, GetSequencerFromPath (OriginalSource), OPERATION_LINK_EDIT)) { do { if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_TARGET)) { NewTarget = DuplicatePathString (eOpProp.Property, 0); break; } } while (EnumNextFileOpProperty (&eOpProp)); } } FreePathString (OriginalSource); if (!NewTarget) { NewTarget = DuplicatePathString (ShortcutTarget, 0); } result = pIsObsoleteLink (Data->DestinationPath, NewTarget, ShortcutArgs, Data->ShellFolderIdentifier, Data->DestRootPath); DEBUGMSG_IF ((result, DBG_SHELL, "%s is obsolete", Data->DestinationPath)); DEBUGMSG_IF ((!result, DBG_SHELL, "%s is not obsolete", Data->DestinationPath)); FreePathString (NewTarget); } if (result) { // // If this link is to be edited by the LinkEdit code we should remove this // operation because the file will not be available. // DEBUGMSG ((DBG_SHELL, "File %s will not be available for LinkEdit", Data->TempSourcePath)); // // Get the original source for this file // OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath); MYASSERT (OriginalSource); if (IsFileMarkedForOperation (OriginalSource, OPERATION_LINK_EDIT)) { RemoveOperationsFromPath (OriginalSource, OPERATION_LINK_EDIT); } FreePathString (OriginalSource); // // Now remove the source file. We cannot keep this file to be restored by the UNDO code. // The reason for this is that we might have some other // shell folder pointing to the same source and destination. In this case, obsolete links // filter will not work since we just removed the file from OPERATION_LINK_EDIT. // MYASSERT ((Data->Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0); SetLongPathAttributes (Data->TempSourcePath, FILE_ATTRIBUTE_NORMAL); if (!DeleteLongPath (Data->TempSourcePath)) { DEBUGMSG ((DBG_ERROR, "Cannot remove file %s", Data->TempSourcePath)); SetLongPathAttributes (Data->TempSourcePath, Data->Attributes); } return SHELLFILTER_SKIP_FILE; } return SHELLFILTER_OK; case TERMINATE: if (bigBuf) { FreeMem (bigBuf); } FreeCOMLink (&shellLink, &persistFile); break; } return SHELLFILTER_OK; } // // Filters NT -> 9X // DWORD pCollisionDetectionNt9x ( IN OUT PPROFILE_MERGE_DATA Data ) { // // this filter will detect name collision while copying files from NT shell folders // or default user to migrated 9x shell folder. // If we have a name collision, we want to keep the NT file original name and to rename // the migrated Win9x file. We will build a new file name starting with filename.ext. // The new file will look something like filename001.ext. In all cases we want to keep // the extension, since there might be some shell extensions active for this file. // Important: we do not care about directory collisions. // PCTSTR newName; PCTSTR extPtr; PCTSTR OriginalSource; DWORD value; switch (Data->Context) { case INITIALIZE: break; case PROCESS_PATH: if ((!(Data->Attributes & FILE_ATTRIBUTE_DIRECTORY)) && (!StringIMatch (Data->SrcRootPath, Data->DestRootPath)) && (DoesFileExist (Data->DestinationPath)) ) { value = pIgnoredCollisions (Data); if (value) { if (value == 1) { // we should keep the 9x file // By returning SHELLFILTER_SKIP_FILE we are instructing the copy routine // not to copy this file. As a result the already installed 9x file will // survive return SHELLFILTER_SKIP_FILE; } else { // we should keep the NT file // We want to delete the 9x file installed here to make room for the NT // file that should be copied when we return from this filter SetLongPathAttributes (Data->DestinationPath, FILE_ATTRIBUTE_NORMAL); DeleteLongPath (Data->DestinationPath); } } else { newName = GenerateNewFileName (Data->DestinationPath, 0, TRUE); //TRUE - check unique DEBUGMSG (( DBG_SHELL, "9x file collides with NT file -- renaming 9x file from %s to %s", Data->DestinationPath, newName )); pQueueSfMove (Data->DestinationPath, newName); // // now if this was a link we need to fix the destination of the move external operation // We have two reasons to do this. One is that the LinkEdit code needs the actual destination // to be able to edit the link, and secondly we need this new target for the uninstall programs // to work properly. If this file is not a LNK or a PIF, we don't care, we want everybody to // use the NT installed file. BTW, there is a collision here only because NT installed a file // with the same name in this location. // extPtr = GetFileExtensionFromPath (Data->DestinationPath); if ((extPtr) && ((StringIMatch (extPtr, TEXT("LNK"))) || (StringIMatch (extPtr, TEXT("PIF"))) ) ) { // // Get the original source for this file // OriginalSource = StringSearchAndReplace (Data->TempSourcePath, Data->SrcRootPath, Data->OrigRootPath); MYASSERT (OriginalSource); if (IsFileMarkedForOperation (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER)) { DEBUGMSG (( DBG_SHELL, "Removing shell move op from %s", OriginalSource )); RemoveOperationsFromPath (OriginalSource, OPERATION_FILE_MOVE_SHELL_FOLDER); MarkFileForShellFolderMove (OriginalSource, newName); } FreePathString (OriginalSource); } FreePathString (newName); } } break; case TERMINATE: break; } return SHELLFILTER_OK; } DWORD pDetectOtherShellFolder ( IN OUT PPROFILE_MERGE_DATA Data ) { switch (Data->Context) { case INITIALIZE: g_UserSfList = pCreateUserSfList (Data); break; case PROCESS_PATH: if (Data->Attributes & FILE_ATTRIBUTE_DIRECTORY) { // // This is a dir. Let's see if we enter another shell folder // if (((g_SystemSfList) && (pSetupStringTableLookUpString (g_SystemSfList, (PVOID) Data->TempSourcePath, STRTAB_CASE_INSENSITIVE) != -1)) || ((g_UserSfList) && (pSetupStringTableLookUpString (g_UserSfList, (PVOID) Data->TempSourcePath, STRTAB_CASE_INSENSITIVE) != -1)) ) { // // we are just getting into another shell folder. Let's skip it // return SHELLFILTER_SKIP_DIRECTORY; } } break; case TERMINATE: pDestroySfList (g_UserSfList); break; } return SHELLFILTER_OK; }