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] = {0, 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 CAvailableApplicationInfo * 38 CAppDB::FindAvailableByPackageName(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 static_cast<CAvailableApplicationInfo *>(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(DB_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 static inline HKEY 145 GetRootKeyInfo(UINT Index, REGSAM &RegSam) 146 { 147 C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum)); 148 if (Index < _countof(g_RootKeyEnum)) 149 { 150 RegSam = g_RegSamEnum[Index]; 151 return g_RootKeyEnum[Index]; 152 } 153 return NULL; 154 } 155 156 HKEY 157 CAppDB::EnumInstalledRootKey(UINT Index, REGSAM &RegSam) 158 { 159 // Loop for through all combinations. 160 // Note that HKEY_CURRENT_USER\Software does not have a redirect 161 // https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64 162 if (Index < (IsSystem64Bit() ? 3 : 2)) 163 return GetRootKeyInfo(Index, RegSam); 164 else 165 return NULL; 166 } 167 168 CInstalledApplicationInfo * 169 CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex) 170 { 171 CRegKey hSubKey; 172 if (hSubKey.Open(hKeyParent, KeyName, KEY_READ) != ERROR_SUCCESS) 173 return NULL; 174 DWORD value, size; 175 176 size = sizeof(DWORD); 177 if (!RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&value, &size) && value == 1) 178 { 179 // Ignore system components 180 return NULL; 181 } 182 183 size = 0; 184 BOOL bIsUpdate = !RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &size); 185 186 AppsCategories cat = bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS; 187 CInstalledApplicationInfo *pInfo; 188 pInfo = new CInstalledApplicationInfo(hSubKey.Detach(), KeyName, cat, KeyIndex); 189 if (pInfo && pInfo->Valid()) 190 { 191 return pInfo; 192 } 193 delete pInfo; 194 return NULL; 195 } 196 197 CInstalledApplicationInfo * 198 CAppDB::EnumerateRegistry(CAtlList<CAppInfo *> *List, LPCWSTR SearchOnly) 199 { 200 ATLASSERT(List || SearchOnly); 201 REGSAM wowsam; 202 HKEY hRootKey; 203 for (UINT rki = 0; (hRootKey = EnumInstalledRootKey(rki, wowsam)); ++rki) 204 { 205 CRegKey hKey; 206 if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | wowsam) != ERROR_SUCCESS) 207 { 208 continue; 209 } 210 for (DWORD Index = 0;; ++Index) 211 { 212 WCHAR szKeyName[MAX_PATH]; 213 DWORD dwSize = _countof(szKeyName); 214 if (hKey.EnumKey(Index, szKeyName, &dwSize) != ERROR_SUCCESS) 215 { 216 break; 217 } 218 if (List || !StrCmpIW(SearchOnly, szKeyName)) 219 { 220 CInstalledApplicationInfo *Info; 221 Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, rki); 222 if (Info) 223 { 224 if (List) 225 List->AddTail(Info); 226 else 227 return Info; 228 } 229 } 230 } 231 } 232 return NULL; 233 } 234 235 VOID 236 CAppDB::UpdateInstalled() 237 { 238 // Remove all old entries 239 ClearList(m_Installed); 240 241 EnumerateRegistry(&m_Installed, NULL); 242 } 243 244 CInstalledApplicationInfo * 245 CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR Name) 246 { 247 return EnumerateRegistry(NULL, Name); 248 } 249 250 CInstalledApplicationInfo * 251 CAppDB::CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam) 252 { 253 HKEY hRootKey = User ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 254 UINT KeyIndex = User ? (0) : ((WowSam & KEY_WOW64_64KEY) ? 2 : 1); 255 CRegKey hKey; 256 if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | WowSam) == ERROR_SUCCESS) 257 { 258 return CreateInstalledAppByRegistryKey(KeyName, hKey, KeyIndex); 259 } 260 return NULL; 261 } 262 263 static void 264 DeleteWithWildcard(const CPathW &Dir, const CStringW &Filter) 265 { 266 HANDLE hFind = INVALID_HANDLE_VALUE; 267 WIN32_FIND_DATAW FindFileData; 268 269 CPathW DirWithFilter = Dir; 270 DirWithFilter += Filter; 271 272 hFind = FindFirstFileW(DirWithFilter, &FindFileData); 273 274 if (hFind == INVALID_HANDLE_VALUE) 275 return; 276 277 do 278 { 279 CPathW szTmp = Dir; 280 szTmp += FindFileData.cFileName; 281 282 if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 283 { 284 DeleteFileW(szTmp); 285 } 286 } while (FindNextFileW(hFind, &FindFileData) != 0); 287 FindClose(hFind); 288 } 289 290 VOID 291 CAppDB::RemoveCached() 292 { 293 // Delete icons 294 CPathW AppsPath = m_BasePath; 295 AppsPath += RAPPS_DATABASE_SUBDIR; 296 CPathW IconPath = AppsPath; 297 IconPath += L"icons"; 298 DeleteWithWildcard(IconPath, L"*.ico"); 299 300 // Delete leftover screenshots 301 CPathW ScrnshotFolder = AppsPath; 302 ScrnshotFolder += L"screenshots"; 303 DeleteWithWildcard(ScrnshotFolder, L"*.tmp"); 304 305 // Delete data base files (*.txt) 306 DeleteWithWildcard(AppsPath, L"*.txt"); 307 308 RemoveDirectoryW(IconPath); 309 RemoveDirectoryW(ScrnshotFolder); 310 RemoveDirectoryW(AppsPath); 311 RemoveDirectoryW(m_BasePath); 312 } 313 314 DWORD 315 CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info) 316 { 317 // Validate that this is actually an installed app / update 318 ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES); 319 if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES) 320 return ERROR_INVALID_PARAMETER; 321 322 const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info); 323 324 CStringW Name = InstalledInfo->szIdentifier; 325 REGSAM wowsam; 326 HKEY hRoot = GetRootKeyInfo(InstalledInfo->m_KeyInfo, wowsam); 327 ATLASSERT(hRoot); 328 if (!hRoot) 329 return ERROR_OPEN_FAILED; 330 331 CRegKey Uninstall; 332 LSTATUS err = Uninstall.Open(hRoot, UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | wowsam); 333 if (err == ERROR_SUCCESS) 334 { 335 err = Uninstall.RecurseDeleteKey(Name); 336 } 337 return err; 338 } 339