windows-nt/Source/XPSP1/NT/windows/winstate/v1/common/special.hxx
2020-09-26 16:20:57 +08:00

804 lines
24 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1998 - 1998.
//
// File: special.hxx
//
// Contents: Special directories class and headers
//
// Classes:
//
// Functions:
//
// History: 23-Sep-99 PhilipLa Created
//
//----------------------------------------------------------------------------
#ifndef __SPECIAL_HXX__
#define __SPECIAL_HXX__
#include <filelist.hxx>
#include <bothchar.hxx>
const DWORD SPECIALDIR_MACHINE = 2;
const DWORD SPECIALDIR_USER = 1;
#define SDM SPECIALDIR_MACHINE
#define SDU SPECIALDIR_USER
struct sSpecialDir
{
TCHAR *ptsSpecialName;
int iSpecialDir;
DWORD dwFlags;
TCHAR *ptsKeyName;
};
const sSpecialDir ssdSpecial[] = {
{TEXT("%csidl_admintools%"), CSIDL_ADMINTOOLS,
SDU, TEXT("Administrative Tools")},
{TEXT("%csidl_altstartup%"), CSIDL_ALTSTARTUP,
SDU, TEXT("AltStartup")},
{TEXT("%csidl_appdata%"), CSIDL_APPDATA,
SDU, TEXT("AppData")},
{TEXT("%csidl_bitbucket%"), CSIDL_BITBUCKET,
SDM, TEXT("RecycleBinFolder")},
{TEXT("%csidl_common_admintools%"), CSIDL_COMMON_ADMINTOOLS,
SDM, TEXT("Common Administrative Tools")},
{TEXT("%csidl_common_altstartup%"), CSIDL_COMMON_ALTSTARTUP,
SDU, TEXT("Common AltStartup")},
{TEXT("%csidl_common_appdata%"), CSIDL_COMMON_APPDATA,
SDM, TEXT("Common AppData")},
{TEXT("%csidl_common_desktopdirectory%"), CSIDL_COMMON_DESKTOPDIRECTORY,
SDM, TEXT("Common Desktop")},
{TEXT("%csidl_common_documents%"), CSIDL_COMMON_DOCUMENTS,
SDM, TEXT("Common Documents")},
{TEXT("%csidl_common_favorites%"), CSIDL_COMMON_FAVORITES,
SDM, TEXT("Common Favorites")},
{TEXT("%csidl_common_programs%"), CSIDL_COMMON_PROGRAMS,
SDM, TEXT("Common Programs")},
{TEXT("%csidl_common_startmenu%"), CSIDL_COMMON_STARTMENU,
SDM, TEXT("Common Start Menu")},
{TEXT("%csidl_common_startup%"), CSIDL_COMMON_STARTUP,
SDM, TEXT("Common Startup")},
{TEXT("%csidl_common_templates%"), CSIDL_COMMON_TEMPLATES,
SDM, TEXT("Common Templates")},
{TEXT("%csidl_controls%"), CSIDL_CONTROLS,
SDM, TEXT("ControlPanelFolder")},
{TEXT("%csidl_cookies%"), CSIDL_COOKIES,
SDU, TEXT("Cookies")},
{TEXT("%csidl_desktop%"), CSIDL_DESKTOP,
SDU, TEXT("Desktop")}, //The key shfolder.dll uses is "DesktopFolder"
{TEXT("%csidl_desktopdirectory%"), CSIDL_DESKTOPDIRECTORY,
SDU, TEXT("Desktop")},
{TEXT("%csidl_drives%"), CSIDL_DRIVES,
SDM, TEXT("DriveFolder")},
{TEXT("%csidl_favorites%"), CSIDL_FAVORITES,
SDU, TEXT("Favorites")},
{TEXT("%csidl_fonts%"), CSIDL_FONTS,
SDU, TEXT("Fonts")},
{TEXT("%csidl_history%"), CSIDL_HISTORY,
SDU, TEXT("History")},
{TEXT("%csidl_internet%"), CSIDL_INTERNET,
SDM, TEXT("InternetFolder")},
{TEXT("%csidl_internet_cache%"), CSIDL_INTERNET_CACHE,
SDU, TEXT("Cache")},
{TEXT("%csidl_local_appdata%"), CSIDL_LOCAL_APPDATA,
SDU, TEXT("Local AppData")},
{TEXT("%csidl_mypictures%"), CSIDL_MYPICTURES,
SDU, TEXT("My Pictures")},
{TEXT("%csidl_nethood%"), CSIDL_NETHOOD,
SDU, TEXT("Nethood")},
{TEXT("%csidl_network%"), CSIDL_NETWORK,
SDM, TEXT("NetworkFolder")},
{TEXT("%csidl_personal%"), CSIDL_PERSONAL,
SDU, TEXT("Personal")},
{TEXT("%csidl_printers%"), CSIDL_PRINTERS,
SDM, TEXT("PrintersFolder")},
{TEXT("%csidl_printhood%"), CSIDL_PRINTHOOD,
SDU, TEXT("PrintHood")},
{TEXT("%csidl_profile%"), CSIDL_PROFILE,
SDU, TEXT("Profile")},
{TEXT("%csidl_program_files%"), CSIDL_PROGRAM_FILES,
SDM, TEXT("ProgramFiles")},
{TEXT("%csidl_program_files_common%"), CSIDL_PROGRAM_FILES_COMMON,
SDM, TEXT("CommonProgramFiles")},
{TEXT("%csidl_program_filesx86%"), CSIDL_PROGRAM_FILESX86,
SDM, TEXT("ProgramFilesX86")},
{TEXT("%csidl_programs%"), CSIDL_PROGRAMS,
SDU, TEXT("Programs")},
{TEXT("%csidl_recent%"), CSIDL_RECENT,
SDU, TEXT("Recent")},
{TEXT("%csidl_sendto%"), CSIDL_SENDTO,
SDU, TEXT("SendTo")},
{TEXT("%csidl_startmenu%"), CSIDL_STARTMENU,
SDU, TEXT("Start Menu")},
{TEXT("%csidl_startup%"), CSIDL_STARTUP,
SDU, TEXT("Startup")},
{TEXT("%csidl_system%"), CSIDL_SYSTEM,
SDM, TEXT("System")},
{TEXT("%csidl_systemx86%"), CSIDL_SYSTEMX86,
SDM, TEXT("SystemX86")},
{TEXT("%csidl_templates%"), CSIDL_TEMPLATES,
SDU, TEXT("Templates")},
{TEXT("%csidl_windows%"), CSIDL_WINDOWS,
SDM, TEXT("Windows")}};
const DWORD cSpecialDirs = sizeof(ssdSpecial) / sizeof(sSpecialDir);
class CSpecialDirectory
{
public:
CSpecialDirectory();
~CSpecialDirectory();
inline const TCHAR * GetDirectoryName(ULONG i) const;
inline const TCHAR * GetDirectoryPath(ULONG i) const;
inline DWORD GetDirectoryCount(void) const;
inline DWORD Init(void);
inline DWORD InitForUser(HKEY hKey);
inline DWORD InitFromInf(TCHAR *ptsName, TCHAR *ptsPath);
inline DWORD AddSpecialDir(TCHAR *ptsName, TCHAR *ptsPath, LONG *i);
inline DWORD ExpandMacro(TCHAR *pbuf,
TCHAR *pbufTmp,
TCHAR **pptsFinal,
BOOL fUpdate);
private:
CFileList *_pfl;
BOOL _fUser;
};
CSpecialDirectory::CSpecialDirectory()
{
_pfl = NULL;
_fUser = FALSE;
}
CSpecialDirectory::~CSpecialDirectory()
{
FreeFileList(_pfl);
}
inline const TCHAR * CSpecialDirectory::GetDirectoryName(ULONG i) const
{
return _pfl->GetFullName(i);
}
inline const TCHAR * CSpecialDirectory::GetDirectoryPath(ULONG i) const
{
return _pfl->GetDestination(i);
}
inline DWORD CSpecialDirectory::GetDirectoryCount(void) const
{
return _pfl->GetNameCount();
}
inline DWORD CSpecialDirectory::AddSpecialDir(TCHAR *ptsName,
TCHAR *ptsPath,
LONG *piName)
{
DWORD dwErr = ERROR_SUCCESS;
if (_pfl == NULL)
{
_pfl = new CFileList;
if (_pfl == NULL)
{
Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
return ERROR_OUTOFMEMORY;
}
}
DWORD cDirs = _pfl->GetNameCount();
for (ULONG i = 0; i < cDirs; i++)
{
if (_tcsicmp(_pfl->GetFullName(i), ptsName) == 0)
{
Win32PrintfResource(LogFile,
IDS_FAILED);
return ERROR_INVALID_PARAMETER;
}
}
//Not found, so go ahead and try to add it.
if (ptsPath == NULL)
{
//Look it up
HRESULT hr;
int iFolder = 0;
//It's a new one, see if it matches anything in the special
//dir list
for (i = 0; i < cSpecialDirs; i++)
{
if (_tcsicmp(ptsName, ssdSpecial[i].ptsSpecialName) == 0)
{
//Bingo
iFolder = ssdSpecial[i].iSpecialDir;
break;
}
}
if (iFolder != 0)
{
TCHAR tsDirName[MAX_PATH + 1];
hr = SHGetFolderPath(NULL,
iFolder,
NULL,
SHGFP_TYPE_CURRENT,
tsDirName);
//Only handle success cases, since some special paths aren't
//available on all machines.
if (SUCCEEDED(hr) && (tsDirName[0] != 0))
{
dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
if (dwErr)
return dwErr;
dwErr = _pfl->SetDestination(tsDirName, (ULONG)*piName);
return dwErr;
}
}
//It isn't a special directory, see if it's an environment
//variable.
TCHAR tsVarName[MAX_PATH + 1];
TCHAR tsVarValue[MAX_PATH + 1];
if (_tcslen(ptsName) > MAX_PATH)
{
if (DebugOutput)
Win32Printf(LogFile, "Error: ptsName too long %s\r\n", ptsName);
return ERROR_FILENAME_EXCED_RANGE;
}
_tcscpy(tsVarName, ptsName + 1);
tsVarName[_tcslen(tsVarName) - 1] = 0;
dwErr = ERROR_INVALID_PARAMETER;
if (_fUser)
{
//Check for userprofile, which is a special case
if (_tcsicmp(tsVarName, TEXT("USERPROFILE")) == 0)
{
//We have the user's profile directory stored
//in UserPath
if (_tcslen(UserPath) > MAX_PATH)
{
if (DebugOutput)
Win32Printf(LogFile, "Error: UserPath too long %s\r\n", UserPath);
return ERROR_FILENAME_EXCED_RANGE;
}
_tcscpy(tsVarValue, UserPath);
dwErr = ERROR_SUCCESS;
}
else if (CurrentUser != NULL)
{
//Check in the registry
HKEY hKeyEnv = NULL;
dwErr = RegOpenKey(CurrentUser,
TEXT("Environment"),
&hKeyEnv);
if (dwErr)
{
//Do nothing, try again below
}
else
{
DWORD dwValueType;
DWORD dwLen = MAX_PATH + 1;
dwErr = RegQueryValueEx(hKeyEnv,
tsVarName,
NULL,
&dwValueType,
(BYTE *)tsVarValue,
&dwLen);
if ((dwErr == ERROR_SUCCESS) &&
(dwValueType != REG_SZ))
{
//Flag the error, try again below
dwErr = ERROR_INVALID_PARAMETER;
}
RegCloseKey(hKeyEnv);
}
}
}
if (dwErr != ERROR_SUCCESS)
{
dwErr = GetEnvironmentVariable(tsVarName,
tsVarValue,
MAX_PATH + 1);
//GetEnvironmentVariable returns a length, not an error code.
//A value of 0 is an error here.
if (dwErr == 0)
{
// if no mapping found, it might not actually be a macro.
// just continue without setting this as a special dir
if (Verbose)
{
Win32Printf(LogFile,
"Warning: Couldn't find macro mapping for [%s]. "
"Assuming not a macro\r\n",
ptsName);
}
*piName = -1;
return ERROR_SUCCESS;
}
}
dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
if (dwErr)
return dwErr;
dwErr = _pfl->SetDestination(tsVarValue, (ULONG)*piName);
}
else
{
dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
if (dwErr)
return dwErr;
dwErr = _pfl->SetDestination(ptsPath, (ULONG)*piName);
}
return dwErr;
}
inline DWORD CSpecialDirectory::InitFromInf(TCHAR *ptsName, TCHAR *ptsPath)
{
LONG i;
return AddSpecialDir(ptsName, ptsPath, &i);
}
inline DWORD CSpecialDirectory::Init(void)
{
_pfl = new CFileList;
if (_pfl == NULL)
{
Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
return ERROR_OUTOFMEMORY;
}
for (ULONG i = 0; i < cSpecialDirs; i++)
{
HRESULT hr;
ULONG iName;
TCHAR tsDirName[MAX_PATH + 1];
DWORD dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName,
&iName,
FALSE);
if (dwErr)
return dwErr;
hr = SHGetFolderPath(NULL,
ssdSpecial[i].iSpecialDir,
NULL,
SHGFP_TYPE_CURRENT,
tsDirName);
//Only handle success cases, since some special paths aren't
//available on all machines.
if (SUCCEEDED(hr))
{
if (tsDirName[0] == 0)
{
//WTF happened? Why didn't we get an error?
//Continue anyway, we can ignore this case
}
else
{
dwErr = _pfl->SetDestination(tsDirName, iName);
if (dwErr)
return dwErr;
}
}
}
return ERROR_SUCCESS;
}
inline DWORD CSpecialDirectory::InitForUser(HKEY hKey)
{
DWORD dwErr = ERROR_SUCCESS;
TCHAR *ptsValData = NULL;
_fUser = TRUE;
_pfl = new CFileList;
if (_pfl == NULL)
{
Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
return ERROR_OUTOFMEMORY;
}
HKEY hKeyUser = NULL;
HKEY hKeyEnv = NULL;
//Pick up all the environment strings from the user's registry block
dwErr = RegOpenKey(CurrentUser,
TEXT("Environment"),
&hKeyEnv);
if (dwErr)
{
Win32PrintfResource(LogFile,
ResultToRC(dwErr));
goto Cleanup;
}
DWORD dwIndex;
dwIndex = 0;
do
{
TCHAR tsValName[MAX_PATH + 1];
DWORD dwValNameLength = (MAX_PATH + 1);
DWORD dwValDataLength = 0;
DWORD dwType;
// Read the value name
dwErr = RegEnumValue(hKeyEnv,
dwIndex,
tsValName,
&dwValNameLength,
NULL, //reserved
NULL, // dont care about type
NULL, // dont care about data
NULL); // dont care about datalength
if (dwErr == ERROR_NO_MORE_ITEMS)
{
dwErr = ERROR_SUCCESS;
break;
}
if (dwErr)
{
Win32PrintfResource(LogFile,
ResultToRC(dwErr));
goto Cleanup;
}
// Read the size required for the data
dwErr = RegQueryValueEx(hKeyEnv,
tsValName,
NULL, // reserved
NULL, // dont care about type
NULL, // dont care about data
&dwValDataLength);
if (dwErr)
{
Win32PrintfResource(LogFile,
ResultToRC(dwErr));
goto Cleanup;
}
ptsValData = (TCHAR *)malloc(dwValDataLength + 1);
if (NULL == ptsValData)
{
Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
return ERROR_OUTOFMEMORY;
}
// Read the actual data
dwErr = RegQueryValueEx(hKeyEnv,
tsValName,
NULL, // reserved
&dwType,
(BYTE *)ptsValData,
&dwValDataLength);
if (dwErr)
{
Win32PrintfResource(LogFile,
ResultToRC(dwErr));
goto Cleanup;
}
if ((dwType == REG_SZ) ||
(dwType == REG_EXPAND_SZ))
{
DWORD i;
//Add %s
TCHAR tsFinal[MAX_PATH+1];
if (dwValNameLength > MAX_PATH-1)
{
dwErr = ERROR_FILENAME_EXCED_RANGE;
goto Cleanup;
}
tsFinal[0] = TEXT('%');
_tcscpy(tsFinal+1, tsValName);
tsFinal[dwValNameLength + 1] = TEXT('%');
tsFinal[dwValNameLength + 2] = 0;
dwErr = _pfl->SetName(tsFinal, &i, FALSE);
if (dwErr)
goto Cleanup;
dwErr = _pfl->SetDestination(ptsValData, i);
if (dwErr)
goto Cleanup;
}
dwIndex++;
free (ptsValData); ptsValData = NULL;
} while (1);
//Also pick up USERPROFILE so we don't need to get it later.
dwErr = _pfl->SetName(TEXT("%USERPROFILE%"), &dwIndex, FALSE);
if (dwErr)
goto Cleanup;
dwErr = _pfl->SetDestination(UserPath, dwIndex);
if (dwErr)
goto Cleanup;
dwErr = RegOpenKey(hKey,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"),
&hKeyUser);
if (dwErr)
{
Win32PrintfResource(LogFile,
ResultToRC(dwErr));
goto Cleanup;
}
ULONG i;
for (i = 0; i < cSpecialDirs; i++)
{
HRESULT hr;
ULONG iName;
TCHAR tsDirName[MAX_PATH + 1];
int iSpecialDir = ssdSpecial[i].iSpecialDir;
dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName,
&iName,
FALSE);
if (dwErr)
{
goto Cleanup;
}
if (ssdSpecial[i].dwFlags & SDU)
{
//User specific directory, get from appropriate place in
//hKey
TCHAR *ptsFinal;
TCHAR tsMacro[MAX_PATH + 1]; // ptsFinal points here after ExpandMacro call
if (iSpecialDir == CSIDL_DESKTOP)
{
//Another special case. This is the user profile directory
//plus a localized desktop directory name, but we don't
//have that localization info, so we're in trouble.
//
//Pretend we're doing CSIDL_DESKTOPDIRECTORY instead
iSpecialDir = CSIDL_DESKTOPDIRECTORY;
}
if (iSpecialDir == CSIDL_PROFILE)
{
//Special case, not stored in registry
if (_tcslen(UserPath) > MAX_PATH)
{
if (DebugOutput)
Win32Printf(LogFile, "Error: UserPath Too Long %s\r\n", UserPath);
dwErr = ERROR_FILENAME_EXCED_RANGE;
goto Cleanup;
}
_tcscpy(tsDirName, UserPath);
dwErr = ERROR_SUCCESS;
ptsFinal = tsDirName;
}
else
{
DWORD dwValueType;
DWORD dwLen = MAX_PATH + 1;
dwErr = RegQueryValueEx(hKeyUser,
ssdSpecial[i].ptsKeyName,
NULL,
&dwValueType,
(BYTE *)tsDirName,
&dwLen);
if (dwErr == ERROR_SUCCESS)
{
if (dwValueType == REG_EXPAND_SZ)
{
dwErr = ExpandMacro(tsDirName, tsMacro, &ptsFinal, TRUE);
if (dwErr)
goto Cleanup;
}
else
{
ptsFinal = tsDirName;
}
}
}
if (dwErr == ERROR_SUCCESS)
{
dwErr = _pfl->SetDestination(ptsFinal, iName);
if (dwErr)
goto Cleanup;
continue;
}
}
dwErr = ERROR_SUCCESS;
//Machine global directory, get through normal mechanism
hr = SHGetFolderPath(NULL,
iSpecialDir,
NULL,
SHGFP_TYPE_CURRENT,
tsDirName);
//Only handle success cases, since some special paths aren't
//available on all machines.
if (SUCCEEDED(hr))
{
if (tsDirName[0] == 0)
{
//WTF happened? Why didn't we get an error?
//Continue anyway, we can ignore this case
}
else
{
dwErr = _pfl->SetDestination(tsDirName, iName);
if (dwErr)
{
goto Cleanup;
}
}
}
}
Cleanup:
if (hKeyUser != NULL)
RegCloseKey(hKeyUser);
if (hKeyEnv != NULL)
RegCloseKey(hKeyEnv);
if (ptsValData != NULL)
free(ptsValData);
return dwErr;
}
// Notes (chrisab 3/1/00): this function expands nested macros, but
// does not expand multiple macros in the same source string. Could
// also be changed to use cleaner temp buffers.
DWORD CSpecialDirectory::ExpandMacro(TCHAR *pbuf,
TCHAR *pbufTmp,
TCHAR **pptsFinal,
BOOL fUpdate)
{
TCHAR *ptsFinalName = pbuf;
TCHAR *ptsMacro;
// Point the final at the original, in case we
// return early without making any substitutions
*pptsFinal = pbuf;
while ((ptsMacro = _tcschr(ptsFinalName, TEXT('%'))) != NULL)
{
//% found, find the closing %
TCHAR tsMacro[MAX_PATH + 1];
TCHAR *ptsMacroEnd;
ptsMacroEnd = _tcschr(ptsMacro + 1, TEXT('%'));
if (ptsMacroEnd == NULL)
{
// '%' is a valid character in files, too.
// Assume if no match, it's not an error
return ERROR_SUCCESS;
}
if (ptsMacroEnd - ptsMacro + 1 > MAX_PATH)
{
if (DebugOutput)
Win32Printf(LogFile,
"Error: ptsMacro too long %*s\r\n",
ptsMacroEnd - ptsMacro + 1,
ptsMacro);
return ERROR_FILENAME_EXCED_RANGE;
}
_tcsncpy(tsMacro, ptsMacro, ptsMacroEnd - ptsMacro + 1);
tsMacro[ptsMacroEnd - ptsMacro + 1] = 0;
if (DebugOutput)
{
Win32Printf(LogFile, "Found macro %s\r\n", tsMacro);
}
const TCHAR *ptsDirMapping = NULL;
for (ULONG i = 0; i < GetDirectoryCount(); i++)
{
if (_tcsicmp(tsMacro, GetDirectoryName(i)) == 0)
{
//Winnah
ptsDirMapping = GetDirectoryPath(i);
// Set fUpdate to FALSE so we don't try to add this again
// for when the Name is a macro we can't expand
fUpdate = FALSE;
break;
}
}
if ((ptsDirMapping == NULL) && fUpdate)
{
//It's a new one, try to add it.
LONG iName;
DWORD dwErr = AddSpecialDir(tsMacro, NULL, &iName);
if (dwErr)
return dwErr;
if (iName >= 0)
{
ptsDirMapping = GetDirectoryPath(iName);
}
}
if (ptsDirMapping == NULL)
{
// If no mapping found, it might not actually be a macro.
// Just leave it as it is
return ERROR_SUCCESS;
}
if (DebugOutput)
Win32Printf(LogFile,
"Mapping for %s == %s\r\n",
tsMacro,
ptsDirMapping);
//Do substitution
TCHAR *ptsWorkBuffer = (ptsFinalName == pbuf) ? pbufTmp : pbuf;
if ((ptsMacro - ptsFinalName) + _tcslen(ptsDirMapping) + _tcslen(ptsMacroEnd+1) > MAX_PATH)
{
if (Verbose)
{
Win32Printf(LogFile, "Error too long filename: %*s\\%s\\%s\r\n",
ptsMacro-ptsFinalName, // int: length of ptsFinalName to print
ptsFinalName, ptsDirMapping, ptsMacroEnd+1);
}
return ERROR_FILENAME_EXCED_RANGE;
}
_tcsncpy(ptsWorkBuffer, ptsFinalName, ptsMacro - ptsFinalName);
_tcscpy(ptsWorkBuffer + (ptsMacro - ptsFinalName),
ptsDirMapping);
_tcscpy(ptsWorkBuffer +
(ptsMacro - ptsFinalName) +
_tcslen(ptsDirMapping),
ptsMacroEnd + 1);
if (DebugOutput)
Win32Printf(LogFile, "New string: %s\r\n", ptsWorkBuffer);
ptsFinalName = ptsWorkBuffer;
}
*pptsFinal = ptsFinalName;
return ERROR_SUCCESS;
}
#endif // #ifndef __SPECIAL_HXX__