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