mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
57b775ef6e
With a single database line added to applications distributed as zip/cab allows rapps.exe to act as an installer that automatically extracts the files and creates a startmenu shortcut. It can also uninstall the extracted files (and optionally other files and registry entries created by the application).
338 lines
9.3 KiB
C++
338 lines
9.3 KiB
C++
/*
|
|
* PROJECT: ReactOS Applications Manager
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Classes for working with available applications
|
|
* COPYRIGHT: Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
|
|
* Copyright 2020 He Yang (1160386205@qq.com)
|
|
* Copyright 2021-2023 Mark Jansen <mark.jansen@reactos.org>
|
|
*/
|
|
|
|
#include "rapps.h"
|
|
#include "appdb.h"
|
|
#include "configparser.h"
|
|
#include "settings.h"
|
|
|
|
|
|
static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
|
|
static REGSAM g_RegSamEnum[3] = {0, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
|
|
#define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
|
|
|
static VOID
|
|
ClearList(CAtlList<CAppInfo *> &list)
|
|
{
|
|
POSITION InfoListPosition = list.GetHeadPosition();
|
|
while (InfoListPosition)
|
|
{
|
|
CAppInfo *Info = list.GetNext(InfoListPosition);
|
|
delete Info;
|
|
}
|
|
list.RemoveAll();
|
|
}
|
|
|
|
CAppDB::CAppDB(const CStringW &path) : m_BasePath(path)
|
|
{
|
|
m_BasePath.Canonicalize();
|
|
}
|
|
|
|
CAvailableApplicationInfo *
|
|
CAppDB::FindAvailableByPackageName(const CStringW &name)
|
|
{
|
|
POSITION CurrentListPosition = m_Available.GetHeadPosition();
|
|
while (CurrentListPosition)
|
|
{
|
|
CAppInfo *Info = m_Available.GetNext(CurrentListPosition);
|
|
if (Info->szIdentifier == name)
|
|
{
|
|
return static_cast<CAvailableApplicationInfo *>(Info);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
CAppDB::GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const
|
|
{
|
|
const BOOL UseInstalled = IsInstalledEnum(Type);
|
|
const CAtlList<CAppInfo *> &list = UseInstalled ? m_Installed : m_Available;
|
|
const BOOL IncludeAll = UseInstalled ? (Type == ENUM_ALL_INSTALLED) : (Type == ENUM_ALL_AVAILABLE);
|
|
|
|
POSITION CurrentListPosition = list.GetHeadPosition();
|
|
while (CurrentListPosition)
|
|
{
|
|
CAppInfo *Info = list.GetNext(CurrentListPosition);
|
|
|
|
if (IncludeAll || Type == Info->iCategory)
|
|
{
|
|
List.AddTail(Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CAppDB::EnumerateFiles()
|
|
{
|
|
ClearList(m_Available);
|
|
|
|
CPathW AppsPath = m_BasePath;
|
|
AppsPath += RAPPS_DATABASE_SUBDIR;
|
|
CPathW WildcardPath = AppsPath;
|
|
WildcardPath += L"*.txt";
|
|
|
|
WIN32_FIND_DATAW FindFileData;
|
|
HANDLE hFind = FindFirstFileW(WildcardPath, &FindFileData);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
do
|
|
{
|
|
CStringW szPkgName = FindFileData.cFileName;
|
|
PathRemoveExtensionW(szPkgName.GetBuffer(MAX_PATH));
|
|
szPkgName.ReleaseBuffer();
|
|
|
|
CAppInfo *Info = FindByPackageName(szPkgName);
|
|
ATLASSERT(Info == NULL);
|
|
if (!Info)
|
|
{
|
|
CConfigParser *Parser = new CConfigParser(CPathW(AppsPath) += FindFileData.cFileName);
|
|
int Cat;
|
|
if (!Parser->GetInt(DB_CATEGORY, Cat))
|
|
Cat = ENUM_INVALID;
|
|
|
|
Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
|
|
if (Info->Valid())
|
|
{
|
|
m_Available.AddTail(Info);
|
|
}
|
|
else
|
|
{
|
|
delete Info;
|
|
}
|
|
}
|
|
|
|
} while (FindNextFileW(hFind, &FindFileData));
|
|
|
|
FindClose(hFind);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
CAppDB::UpdateAvailable()
|
|
{
|
|
if (!CreateDirectoryW(m_BasePath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return;
|
|
|
|
if (EnumerateFiles())
|
|
return;
|
|
|
|
DownloadApplicationsDB(
|
|
SettingsInfo.bUseSource ? SettingsInfo.szSourceURL : APPLICATION_DATABASE_URL, !SettingsInfo.bUseSource);
|
|
|
|
CPathW AppsPath = m_BasePath;
|
|
AppsPath += RAPPS_DATABASE_SUBDIR;
|
|
if (!ExtractFilesFromCab(APPLICATION_DATABASE_NAME, m_BasePath, AppsPath))
|
|
return;
|
|
|
|
CPathW CabFile = m_BasePath;
|
|
CabFile += APPLICATION_DATABASE_NAME;
|
|
DeleteFileW(CabFile);
|
|
|
|
EnumerateFiles();
|
|
}
|
|
|
|
static inline HKEY
|
|
GetRootKeyInfo(UINT Index, REGSAM &RegSam)
|
|
{
|
|
C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum));
|
|
if (Index < _countof(g_RootKeyEnum))
|
|
{
|
|
RegSam = g_RegSamEnum[Index];
|
|
return g_RootKeyEnum[Index];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
HKEY
|
|
CAppDB::EnumInstalledRootKey(UINT Index, REGSAM &RegSam)
|
|
{
|
|
// Loop for through all combinations.
|
|
// Note that HKEY_CURRENT_USER\Software does not have a redirect
|
|
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64
|
|
if (Index < (IsSystem64Bit() ? 3 : 2))
|
|
return GetRootKeyInfo(Index, RegSam);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
CInstalledApplicationInfo *
|
|
CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex)
|
|
{
|
|
CRegKey hSubKey;
|
|
if (hSubKey.Open(hKeyParent, KeyName, KEY_READ) != ERROR_SUCCESS)
|
|
return NULL;
|
|
DWORD value, size;
|
|
|
|
size = sizeof(DWORD);
|
|
if (!RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&value, &size) && value == 1)
|
|
{
|
|
// Ignore system components
|
|
return NULL;
|
|
}
|
|
|
|
size = 0;
|
|
BOOL bIsUpdate = !RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &size);
|
|
|
|
AppsCategories cat = bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS;
|
|
CInstalledApplicationInfo *pInfo;
|
|
pInfo = new CInstalledApplicationInfo(hSubKey.Detach(), KeyName, cat, KeyIndex);
|
|
if (pInfo && pInfo->Valid())
|
|
{
|
|
return pInfo;
|
|
}
|
|
delete pInfo;
|
|
return NULL;
|
|
}
|
|
|
|
CInstalledApplicationInfo *
|
|
CAppDB::EnumerateRegistry(CAtlList<CAppInfo *> *List, LPCWSTR SearchOnly)
|
|
{
|
|
ATLASSERT(List || SearchOnly);
|
|
REGSAM wowsam;
|
|
HKEY hRootKey;
|
|
for (UINT rki = 0; (hRootKey = EnumInstalledRootKey(rki, wowsam)); ++rki)
|
|
{
|
|
CRegKey hKey;
|
|
if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | wowsam) != ERROR_SUCCESS)
|
|
{
|
|
continue;
|
|
}
|
|
for (DWORD Index = 0;; ++Index)
|
|
{
|
|
WCHAR szKeyName[MAX_PATH];
|
|
DWORD dwSize = _countof(szKeyName);
|
|
if (hKey.EnumKey(Index, szKeyName, &dwSize) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
if (List || !StrCmpIW(SearchOnly, szKeyName))
|
|
{
|
|
CInstalledApplicationInfo *Info;
|
|
Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, rki);
|
|
if (Info)
|
|
{
|
|
if (List)
|
|
List->AddTail(Info);
|
|
else
|
|
return Info;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
CAppDB::UpdateInstalled()
|
|
{
|
|
// Remove all old entries
|
|
ClearList(m_Installed);
|
|
|
|
EnumerateRegistry(&m_Installed, NULL);
|
|
}
|
|
|
|
CInstalledApplicationInfo *
|
|
CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR Name)
|
|
{
|
|
return EnumerateRegistry(NULL, Name);
|
|
}
|
|
|
|
CInstalledApplicationInfo *
|
|
CAppDB::CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam)
|
|
{
|
|
HKEY hRootKey = User ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
UINT KeyIndex = User ? (0) : ((WowSam & KEY_WOW64_64KEY) ? 2 : 1);
|
|
CRegKey hKey;
|
|
if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | WowSam) == ERROR_SUCCESS)
|
|
{
|
|
return CreateInstalledAppByRegistryKey(KeyName, hKey, KeyIndex);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
DeleteWithWildcard(const CPathW &Dir, const CStringW &Filter)
|
|
{
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATAW FindFileData;
|
|
|
|
CPathW DirWithFilter = Dir;
|
|
DirWithFilter += Filter;
|
|
|
|
hFind = FindFirstFileW(DirWithFilter, &FindFileData);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
do
|
|
{
|
|
CPathW szTmp = Dir;
|
|
szTmp += FindFileData.cFileName;
|
|
|
|
if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
DeleteFileW(szTmp);
|
|
}
|
|
} while (FindNextFileW(hFind, &FindFileData) != 0);
|
|
FindClose(hFind);
|
|
}
|
|
|
|
VOID
|
|
CAppDB::RemoveCached()
|
|
{
|
|
// Delete icons
|
|
CPathW AppsPath = m_BasePath;
|
|
AppsPath += RAPPS_DATABASE_SUBDIR;
|
|
CPathW IconPath = AppsPath;
|
|
IconPath += L"icons";
|
|
DeleteWithWildcard(IconPath, L"*.ico");
|
|
|
|
// Delete leftover screenshots
|
|
CPathW ScrnshotFolder = AppsPath;
|
|
ScrnshotFolder += L"screenshots";
|
|
DeleteWithWildcard(ScrnshotFolder, L"*.tmp");
|
|
|
|
// Delete data base files (*.txt)
|
|
DeleteWithWildcard(AppsPath, L"*.txt");
|
|
|
|
RemoveDirectoryW(IconPath);
|
|
RemoveDirectoryW(ScrnshotFolder);
|
|
RemoveDirectoryW(AppsPath);
|
|
RemoveDirectoryW(m_BasePath);
|
|
}
|
|
|
|
DWORD
|
|
CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info)
|
|
{
|
|
// Validate that this is actually an installed app / update
|
|
ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES);
|
|
if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info);
|
|
|
|
CStringW Name = InstalledInfo->szIdentifier;
|
|
REGSAM wowsam;
|
|
HKEY hRoot = GetRootKeyInfo(InstalledInfo->m_KeyInfo, wowsam);
|
|
ATLASSERT(hRoot);
|
|
if (!hRoot)
|
|
return ERROR_OPEN_FAILED;
|
|
|
|
CRegKey Uninstall;
|
|
LSTATUS err = Uninstall.Open(hRoot, UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | wowsam);
|
|
if (err == ERROR_SUCCESS)
|
|
{
|
|
err = Uninstall.RecurseDeleteKey(Name);
|
|
}
|
|
return err;
|
|
}
|