xref: /reactos/base/applications/rapps/appdb.cpp (revision 57b775ef)
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
ClearList(CAtlList<CAppInfo * > & list)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 
CAppDB(const CStringW & path)32 CAppDB::CAppDB(const CStringW &path) : m_BasePath(path)
33 {
34     m_BasePath.Canonicalize();
35 }
36 
37 CAvailableApplicationInfo *
FindAvailableByPackageName(const CStringW & name)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
GetApps(CAtlList<CAppInfo * > & List,AppsCategories Type) const53 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
EnumerateFiles()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
UpdateAvailable()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
GetRootKeyInfo(UINT Index,REGSAM & RegSam)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
EnumInstalledRootKey(UINT Index,REGSAM & RegSam)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 *
CreateInstalledAppByRegistryKey(LPCWSTR KeyName,HKEY hKeyParent,UINT KeyIndex)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 *
EnumerateRegistry(CAtlList<CAppInfo * > * List,LPCWSTR SearchOnly)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
UpdateInstalled()236 CAppDB::UpdateInstalled()
237 {
238     // Remove all old entries
239     ClearList(m_Installed);
240 
241     EnumerateRegistry(&m_Installed, NULL);
242 }
243 
244 CInstalledApplicationInfo *
CreateInstalledAppByRegistryKey(LPCWSTR Name)245 CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR Name)
246 {
247     return EnumerateRegistry(NULL, Name);
248 }
249 
250 CInstalledApplicationInfo *
CreateInstalledAppInstance(LPCWSTR KeyName,BOOL User,REGSAM WowSam)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
DeleteWithWildcard(const CPathW & Dir,const CStringW & Filter)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
RemoveCached()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
RemoveInstalledAppFromRegistry(const CAppInfo * Info)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