windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/migmain/shell.c
2020-09-26 16:20:57 +08:00

3257 lines
95 KiB
C

/*++
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 <linkpif.h>
#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;
}