1*a9b1e059SJoachim Henze /* 2*a9b1e059SJoachim Henze * PROJECT: ReactOS Applications Manager 3*a9b1e059SJoachim Henze * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4*a9b1e059SJoachim Henze * PURPOSE: Classes for working with available applications 5*a9b1e059SJoachim Henze * COPYRIGHT: Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 6*a9b1e059SJoachim Henze * Copyright 2020 He Yang (1160386205@qq.com) 7*a9b1e059SJoachim Henze * Copyright 2021-2023 Mark Jansen <mark.jansen@reactos.org> 8*a9b1e059SJoachim Henze */ 9*a9b1e059SJoachim Henze 10*a9b1e059SJoachim Henze #include "rapps.h" 11*a9b1e059SJoachim Henze #include "appdb.h" 12*a9b1e059SJoachim Henze #include "configparser.h" 13*a9b1e059SJoachim Henze #include "settings.h" 14*a9b1e059SJoachim Henze 15*a9b1e059SJoachim Henze 16*a9b1e059SJoachim Henze static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE}; 17*a9b1e059SJoachim Henze static REGSAM g_RegSamEnum[3] = {KEY_WOW64_32KEY, KEY_WOW64_32KEY, KEY_WOW64_64KEY}; 18*a9b1e059SJoachim Henze #define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" 19*a9b1e059SJoachim Henze 20*a9b1e059SJoachim Henze static VOID 21*a9b1e059SJoachim Henze ClearList(CAtlList<CAppInfo *> &list) 22*a9b1e059SJoachim Henze { 23*a9b1e059SJoachim Henze POSITION InfoListPosition = list.GetHeadPosition(); 24*a9b1e059SJoachim Henze while (InfoListPosition) 25*a9b1e059SJoachim Henze { 26*a9b1e059SJoachim Henze CAppInfo *Info = list.GetNext(InfoListPosition); 27*a9b1e059SJoachim Henze delete Info; 28*a9b1e059SJoachim Henze } 29*a9b1e059SJoachim Henze list.RemoveAll(); 30*a9b1e059SJoachim Henze } 31*a9b1e059SJoachim Henze 32*a9b1e059SJoachim Henze CAppDB::CAppDB(const CStringW &path) : m_BasePath(path) 33*a9b1e059SJoachim Henze { 34*a9b1e059SJoachim Henze m_BasePath.Canonicalize(); 35*a9b1e059SJoachim Henze } 36*a9b1e059SJoachim Henze 37*a9b1e059SJoachim Henze CAppInfo * 38*a9b1e059SJoachim Henze CAppDB::FindByPackageName(const CStringW &name) 39*a9b1e059SJoachim Henze { 40*a9b1e059SJoachim Henze POSITION CurrentListPosition = m_Available.GetHeadPosition(); 41*a9b1e059SJoachim Henze while (CurrentListPosition) 42*a9b1e059SJoachim Henze { 43*a9b1e059SJoachim Henze CAppInfo *Info = m_Available.GetNext(CurrentListPosition); 44*a9b1e059SJoachim Henze if (Info->szIdentifier == name) 45*a9b1e059SJoachim Henze { 46*a9b1e059SJoachim Henze return Info; 47*a9b1e059SJoachim Henze } 48*a9b1e059SJoachim Henze } 49*a9b1e059SJoachim Henze return NULL; 50*a9b1e059SJoachim Henze } 51*a9b1e059SJoachim Henze 52*a9b1e059SJoachim Henze void 53*a9b1e059SJoachim Henze CAppDB::GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const 54*a9b1e059SJoachim Henze { 55*a9b1e059SJoachim Henze const BOOL UseInstalled = IsInstalledEnum(Type); 56*a9b1e059SJoachim Henze const CAtlList<CAppInfo *> &list = UseInstalled ? m_Installed : m_Available; 57*a9b1e059SJoachim Henze const BOOL IncludeAll = UseInstalled ? (Type == ENUM_ALL_INSTALLED) : (Type == ENUM_ALL_AVAILABLE); 58*a9b1e059SJoachim Henze 59*a9b1e059SJoachim Henze POSITION CurrentListPosition = list.GetHeadPosition(); 60*a9b1e059SJoachim Henze while (CurrentListPosition) 61*a9b1e059SJoachim Henze { 62*a9b1e059SJoachim Henze CAppInfo *Info = list.GetNext(CurrentListPosition); 63*a9b1e059SJoachim Henze 64*a9b1e059SJoachim Henze if (IncludeAll || Type == Info->iCategory) 65*a9b1e059SJoachim Henze { 66*a9b1e059SJoachim Henze List.AddTail(Info); 67*a9b1e059SJoachim Henze } 68*a9b1e059SJoachim Henze } 69*a9b1e059SJoachim Henze } 70*a9b1e059SJoachim Henze 71*a9b1e059SJoachim Henze BOOL 72*a9b1e059SJoachim Henze CAppDB::EnumerateFiles() 73*a9b1e059SJoachim Henze { 74*a9b1e059SJoachim Henze ClearList(m_Available); 75*a9b1e059SJoachim Henze 76*a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath; 77*a9b1e059SJoachim Henze AppsPath += L"rapps"; 78*a9b1e059SJoachim Henze CPathW WildcardPath = AppsPath; 79*a9b1e059SJoachim Henze WildcardPath += L"*.txt"; 80*a9b1e059SJoachim Henze 81*a9b1e059SJoachim Henze WIN32_FIND_DATAW FindFileData; 82*a9b1e059SJoachim Henze HANDLE hFind = FindFirstFileW(WildcardPath, &FindFileData); 83*a9b1e059SJoachim Henze if (hFind == INVALID_HANDLE_VALUE) 84*a9b1e059SJoachim Henze { 85*a9b1e059SJoachim Henze return FALSE; 86*a9b1e059SJoachim Henze } 87*a9b1e059SJoachim Henze 88*a9b1e059SJoachim Henze do 89*a9b1e059SJoachim Henze { 90*a9b1e059SJoachim Henze CStringW szPkgName = FindFileData.cFileName; 91*a9b1e059SJoachim Henze PathRemoveExtensionW(szPkgName.GetBuffer(MAX_PATH)); 92*a9b1e059SJoachim Henze szPkgName.ReleaseBuffer(); 93*a9b1e059SJoachim Henze 94*a9b1e059SJoachim Henze CAppInfo *Info = FindByPackageName(szPkgName); 95*a9b1e059SJoachim Henze ATLASSERT(Info == NULL); 96*a9b1e059SJoachim Henze if (!Info) 97*a9b1e059SJoachim Henze { 98*a9b1e059SJoachim Henze CConfigParser *Parser = new CConfigParser(FindFileData.cFileName); 99*a9b1e059SJoachim Henze int Cat; 100*a9b1e059SJoachim Henze if (!Parser->GetInt(L"Category", Cat)) 101*a9b1e059SJoachim Henze Cat = ENUM_INVALID; 102*a9b1e059SJoachim Henze 103*a9b1e059SJoachim Henze Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath); 104*a9b1e059SJoachim Henze if (Info->Valid()) 105*a9b1e059SJoachim Henze { 106*a9b1e059SJoachim Henze m_Available.AddTail(Info); 107*a9b1e059SJoachim Henze } 108*a9b1e059SJoachim Henze else 109*a9b1e059SJoachim Henze { 110*a9b1e059SJoachim Henze delete Info; 111*a9b1e059SJoachim Henze } 112*a9b1e059SJoachim Henze } 113*a9b1e059SJoachim Henze 114*a9b1e059SJoachim Henze } while (FindNextFileW(hFind, &FindFileData)); 115*a9b1e059SJoachim Henze 116*a9b1e059SJoachim Henze FindClose(hFind); 117*a9b1e059SJoachim Henze return TRUE; 118*a9b1e059SJoachim Henze } 119*a9b1e059SJoachim Henze 120*a9b1e059SJoachim Henze VOID 121*a9b1e059SJoachim Henze CAppDB::UpdateAvailable() 122*a9b1e059SJoachim Henze { 123*a9b1e059SJoachim Henze if (!CreateDirectoryW(m_BasePath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) 124*a9b1e059SJoachim Henze return; 125*a9b1e059SJoachim Henze 126*a9b1e059SJoachim Henze if (EnumerateFiles()) 127*a9b1e059SJoachim Henze return; 128*a9b1e059SJoachim Henze 129*a9b1e059SJoachim Henze DownloadApplicationsDB( 130*a9b1e059SJoachim Henze SettingsInfo.bUseSource ? SettingsInfo.szSourceURL : APPLICATION_DATABASE_URL, !SettingsInfo.bUseSource); 131*a9b1e059SJoachim Henze 132*a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath; 133*a9b1e059SJoachim Henze AppsPath += L"rapps"; 134*a9b1e059SJoachim Henze if (!ExtractFilesFromCab(APPLICATION_DATABASE_NAME, m_BasePath, AppsPath)) 135*a9b1e059SJoachim Henze return; 136*a9b1e059SJoachim Henze 137*a9b1e059SJoachim Henze CPathW CabFile = m_BasePath; 138*a9b1e059SJoachim Henze CabFile += APPLICATION_DATABASE_NAME; 139*a9b1e059SJoachim Henze DeleteFileW(CabFile); 140*a9b1e059SJoachim Henze 141*a9b1e059SJoachim Henze EnumerateFiles(); 142*a9b1e059SJoachim Henze } 143*a9b1e059SJoachim Henze 144*a9b1e059SJoachim Henze VOID 145*a9b1e059SJoachim Henze CAppDB::UpdateInstalled() 146*a9b1e059SJoachim Henze { 147*a9b1e059SJoachim Henze // Remove all old entries 148*a9b1e059SJoachim Henze ClearList(m_Installed); 149*a9b1e059SJoachim Henze 150*a9b1e059SJoachim Henze int LoopKeys = 2; 151*a9b1e059SJoachim Henze 152*a9b1e059SJoachim Henze if (IsSystem64Bit()) 153*a9b1e059SJoachim Henze { 154*a9b1e059SJoachim Henze // loop for all 3 combination. 155*a9b1e059SJoachim Henze // note that HKEY_CURRENT_USER\Software don't have a redirect 156*a9b1e059SJoachim Henze // https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64 157*a9b1e059SJoachim Henze LoopKeys = 3; 158*a9b1e059SJoachim Henze } 159*a9b1e059SJoachim Henze 160*a9b1e059SJoachim Henze for (int keyIndex = 0; keyIndex < LoopKeys; keyIndex++) 161*a9b1e059SJoachim Henze { 162*a9b1e059SJoachim Henze LONG ItemIndex = 0; 163*a9b1e059SJoachim Henze WCHAR szKeyName[MAX_PATH]; 164*a9b1e059SJoachim Henze 165*a9b1e059SJoachim Henze CRegKey hKey; 166*a9b1e059SJoachim Henze if (hKey.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | g_RegSamEnum[keyIndex]) != ERROR_SUCCESS) 167*a9b1e059SJoachim Henze { 168*a9b1e059SJoachim Henze continue; 169*a9b1e059SJoachim Henze } 170*a9b1e059SJoachim Henze 171*a9b1e059SJoachim Henze while (1) 172*a9b1e059SJoachim Henze { 173*a9b1e059SJoachim Henze DWORD dwSize = _countof(szKeyName); 174*a9b1e059SJoachim Henze if (hKey.EnumKey(ItemIndex, szKeyName, &dwSize) != ERROR_SUCCESS) 175*a9b1e059SJoachim Henze { 176*a9b1e059SJoachim Henze break; 177*a9b1e059SJoachim Henze } 178*a9b1e059SJoachim Henze 179*a9b1e059SJoachim Henze ItemIndex++; 180*a9b1e059SJoachim Henze 181*a9b1e059SJoachim Henze CRegKey hSubKey; 182*a9b1e059SJoachim Henze if (hSubKey.Open(hKey, szKeyName, KEY_READ) == ERROR_SUCCESS) 183*a9b1e059SJoachim Henze { 184*a9b1e059SJoachim Henze DWORD dwValue = 0; 185*a9b1e059SJoachim Henze 186*a9b1e059SJoachim Henze dwSize = sizeof(DWORD); 187*a9b1e059SJoachim Henze if (RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&dwValue, &dwSize) == 188*a9b1e059SJoachim Henze ERROR_SUCCESS && 189*a9b1e059SJoachim Henze dwValue == 1) 190*a9b1e059SJoachim Henze { 191*a9b1e059SJoachim Henze // Ignore system components 192*a9b1e059SJoachim Henze continue; 193*a9b1e059SJoachim Henze } 194*a9b1e059SJoachim Henze 195*a9b1e059SJoachim Henze BOOL bIsUpdate = 196*a9b1e059SJoachim Henze (RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &dwSize) == ERROR_SUCCESS); 197*a9b1e059SJoachim Henze 198*a9b1e059SJoachim Henze CInstalledApplicationInfo *Info = new CInstalledApplicationInfo( 199*a9b1e059SJoachim Henze hSubKey.Detach(), szKeyName, bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS, keyIndex); 200*a9b1e059SJoachim Henze 201*a9b1e059SJoachim Henze if (Info->Valid()) 202*a9b1e059SJoachim Henze { 203*a9b1e059SJoachim Henze m_Installed.AddTail(Info); 204*a9b1e059SJoachim Henze } 205*a9b1e059SJoachim Henze else 206*a9b1e059SJoachim Henze { 207*a9b1e059SJoachim Henze delete Info; 208*a9b1e059SJoachim Henze } 209*a9b1e059SJoachim Henze } 210*a9b1e059SJoachim Henze } 211*a9b1e059SJoachim Henze } 212*a9b1e059SJoachim Henze } 213*a9b1e059SJoachim Henze 214*a9b1e059SJoachim Henze static void 215*a9b1e059SJoachim Henze DeleteWithWildcard(const CPathW &Dir, const CStringW &Filter) 216*a9b1e059SJoachim Henze { 217*a9b1e059SJoachim Henze HANDLE hFind = INVALID_HANDLE_VALUE; 218*a9b1e059SJoachim Henze WIN32_FIND_DATAW FindFileData; 219*a9b1e059SJoachim Henze 220*a9b1e059SJoachim Henze CPathW DirWithFilter = Dir; 221*a9b1e059SJoachim Henze DirWithFilter += Filter; 222*a9b1e059SJoachim Henze 223*a9b1e059SJoachim Henze hFind = FindFirstFileW(DirWithFilter, &FindFileData); 224*a9b1e059SJoachim Henze 225*a9b1e059SJoachim Henze if (hFind == INVALID_HANDLE_VALUE) 226*a9b1e059SJoachim Henze return; 227*a9b1e059SJoachim Henze 228*a9b1e059SJoachim Henze do 229*a9b1e059SJoachim Henze { 230*a9b1e059SJoachim Henze CPathW szTmp = Dir; 231*a9b1e059SJoachim Henze szTmp += FindFileData.cFileName; 232*a9b1e059SJoachim Henze 233*a9b1e059SJoachim Henze if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 234*a9b1e059SJoachim Henze { 235*a9b1e059SJoachim Henze DeleteFileW(szTmp); 236*a9b1e059SJoachim Henze } 237*a9b1e059SJoachim Henze } while (FindNextFileW(hFind, &FindFileData) != 0); 238*a9b1e059SJoachim Henze FindClose(hFind); 239*a9b1e059SJoachim Henze } 240*a9b1e059SJoachim Henze 241*a9b1e059SJoachim Henze VOID 242*a9b1e059SJoachim Henze CAppDB::RemoveCached() 243*a9b1e059SJoachim Henze { 244*a9b1e059SJoachim Henze // Delete icons 245*a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath; 246*a9b1e059SJoachim Henze AppsPath += L"rapps"; 247*a9b1e059SJoachim Henze CPathW IconPath = AppsPath; 248*a9b1e059SJoachim Henze IconPath += L"icons"; 249*a9b1e059SJoachim Henze DeleteWithWildcard(IconPath, L"*.ico"); 250*a9b1e059SJoachim Henze 251*a9b1e059SJoachim Henze // Delete leftover screenshots 252*a9b1e059SJoachim Henze CPathW ScrnshotFolder = AppsPath; 253*a9b1e059SJoachim Henze ScrnshotFolder += L"screenshots"; 254*a9b1e059SJoachim Henze DeleteWithWildcard(ScrnshotFolder, L"*.tmp"); 255*a9b1e059SJoachim Henze 256*a9b1e059SJoachim Henze // Delete data base files (*.txt) 257*a9b1e059SJoachim Henze DeleteWithWildcard(AppsPath, L"*.txt"); 258*a9b1e059SJoachim Henze 259*a9b1e059SJoachim Henze RemoveDirectoryW(IconPath); 260*a9b1e059SJoachim Henze RemoveDirectoryW(ScrnshotFolder); 261*a9b1e059SJoachim Henze RemoveDirectoryW(AppsPath); 262*a9b1e059SJoachim Henze RemoveDirectoryW(m_BasePath); 263*a9b1e059SJoachim Henze } 264*a9b1e059SJoachim Henze 265*a9b1e059SJoachim Henze BOOL 266*a9b1e059SJoachim Henze CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info) 267*a9b1e059SJoachim Henze { 268*a9b1e059SJoachim Henze // Validate that this is actually an installed app / update 269*a9b1e059SJoachim Henze ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES); 270*a9b1e059SJoachim Henze if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES) 271*a9b1e059SJoachim Henze return FALSE; 272*a9b1e059SJoachim Henze 273*a9b1e059SJoachim Henze // Grab the index in the registry keys 274*a9b1e059SJoachim Henze const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info); 275*a9b1e059SJoachim Henze ATLASSERT(InstalledInfo->iKeyIndex >= 0 && InstalledInfo->iKeyIndex < (int)_countof(g_RootKeyEnum)); 276*a9b1e059SJoachim Henze if (InstalledInfo->iKeyIndex < 0 && InstalledInfo->iKeyIndex >= (int)_countof(g_RootKeyEnum)) 277*a9b1e059SJoachim Henze return FALSE; 278*a9b1e059SJoachim Henze 279*a9b1e059SJoachim Henze int keyIndex = InstalledInfo->iKeyIndex; 280*a9b1e059SJoachim Henze 281*a9b1e059SJoachim Henze // Grab the registry key name 282*a9b1e059SJoachim Henze CStringW Name = InstalledInfo->szIdentifier; 283*a9b1e059SJoachim Henze 284*a9b1e059SJoachim Henze // Recursively delete this key 285*a9b1e059SJoachim Henze CRegKey Uninstall; 286*a9b1e059SJoachim Henze if (Uninstall.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | g_RegSamEnum[keyIndex]) != 287*a9b1e059SJoachim Henze ERROR_SUCCESS) 288*a9b1e059SJoachim Henze return FALSE; 289*a9b1e059SJoachim Henze 290*a9b1e059SJoachim Henze return Uninstall.RecurseDeleteKey(Name) == ERROR_SUCCESS; 291*a9b1e059SJoachim Henze } 292