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