xref: /reactos/base/applications/rapps/appdb.cpp (revision 85377ee3)
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