[RAPPS] Make selection global
[reactos.git] / base / applications / rapps / available.cpp
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * FILE: base/applications/rapps/available.cpp
5 * PURPOSE: Classes for working with available applications
6 * COPYRIGHT: Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org)
7 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
9 */
10 #include "rapps.h"
11
12 #include "available.h"
13 #include "misc.h"
14 #include "dialogs.h"
15
16 #include <atlcoll.h>
17 #include <atlsimpcoll.h>
18 #include <atlstr.h>
19
20 // CAvailableApplicationInfo
21 CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam)
22 : m_IsSelected(FALSE), m_LicenseType(LICENSE_NONE), m_sFileName(sFileNameParam),
23 m_IsInstalled(FALSE), m_HasLanguageInfo(FALSE), m_HasInstalledVersion(FALSE)
24 {
25 RetrieveGeneralInfo();
26 }
27
28 VOID CAvailableApplicationInfo::RefreshAppInfo()
29 {
30 if (m_szUrlDownload.IsEmpty())
31 {
32 RetrieveGeneralInfo();
33 }
34 }
35
36 // Lazily load general info from the file
37 VOID CAvailableApplicationInfo::RetrieveGeneralInfo()
38 {
39 m_Parser = new CConfigParser(m_sFileName);
40
41 m_Category = m_Parser->GetInt(L"Category");
42
43 if (!GetString(L"Name", m_szName)
44 || !GetString(L"URLDownload", m_szUrlDownload))
45 {
46 delete m_Parser;
47 return;
48 }
49
50 GetString(L"RegName", m_szRegName);
51 GetString(L"Version", m_szVersion);
52 GetString(L"License", m_szLicense);
53 GetString(L"Description", m_szDesc);
54 GetString(L"Size", m_szSize);
55 GetString(L"URLSite", m_szUrlSite);
56 GetString(L"CDPath", m_szCDPath);
57 GetString(L"Language", m_szRegName);
58 GetString(L"SHA1", m_szSHA1);
59
60 RetrieveLicenseType();
61 RetrieveLanguages();
62 RetrieveInstalledStatus();
63 if (m_IsInstalled)
64 {
65 RetrieveInstalledVersion();
66 }
67
68 delete m_Parser;
69 }
70
71 VOID CAvailableApplicationInfo::RetrieveInstalledStatus()
72 {
73 m_IsInstalled = ::GetInstalledVersion(NULL, m_szRegName)
74 || ::GetInstalledVersion(NULL, m_szName);
75 }
76
77 VOID CAvailableApplicationInfo::RetrieveInstalledVersion()
78 {
79 ATL::CStringW szNameVersion;
80 szNameVersion = m_szName + L" " + m_szVersion;
81 m_HasInstalledVersion = ::GetInstalledVersion(&m_szInstalledVersion, m_szRegName)
82 || ::GetInstalledVersion(&m_szInstalledVersion, m_szName)
83 || ::GetInstalledVersion(&m_szInstalledVersion, szNameVersion);
84 }
85
86 VOID CAvailableApplicationInfo::RetrieveLanguages()
87 {
88 const WCHAR cDelimiter = L'|';
89 ATL::CStringW szBuffer;
90
91 // TODO: Get multiline parameter
92 if (!m_Parser->GetString(L"Languages", szBuffer))
93 {
94 m_HasLanguageInfo = FALSE;
95 return;
96 }
97
98 // Parse parameter string
99 ATL::CStringW m_szLocale;
100 INT iLCID;
101 for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
102 {
103 if (szBuffer[i] != cDelimiter && szBuffer[i] != L'\n')
104 {
105 m_szLocale += szBuffer[i];
106 }
107 else
108 {
109 if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
110 {
111 m_LanguageLCIDs.Add(static_cast<LCID>(iLCID));
112 m_szLocale.Empty();
113 }
114 }
115 }
116
117 // For the text after delimiter
118 if (!m_szLocale.IsEmpty())
119 {
120 if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
121 {
122 m_LanguageLCIDs.Add(static_cast<LCID>(iLCID));
123 }
124 }
125
126 m_HasLanguageInfo = TRUE;
127 }
128
129 VOID CAvailableApplicationInfo::RetrieveLicenseType()
130 {
131 INT IntBuffer = m_Parser->GetInt(L"LicenseType");
132
133 if (IsLicenseType(IntBuffer))
134 {
135 m_LicenseType = static_cast<LicenseType>(IntBuffer);
136 }
137 else
138 {
139 m_LicenseType = LICENSE_NONE;
140 }
141 }
142
143 BOOL CAvailableApplicationInfo::FindInLanguages(LCID what) const
144 {
145 if (!m_HasLanguageInfo)
146 {
147 return FALSE;
148 }
149
150 //Find locale code in the list
151 const INT nLanguagesSize = m_LanguageLCIDs.GetSize();
152 for (INT i = 0; i < nLanguagesSize; ++i)
153 {
154 if (m_LanguageLCIDs[i] == what)
155 {
156 return TRUE;
157 }
158 }
159
160 return FALSE;
161 }
162
163 BOOL CAvailableApplicationInfo::HasLanguageInfo() const
164 {
165 return m_HasLanguageInfo;
166 }
167
168 BOOL CAvailableApplicationInfo::HasNativeLanguage() const
169 {
170 return FindInLanguages(GetUserDefaultLCID());
171 }
172
173 BOOL CAvailableApplicationInfo::HasEnglishLanguage() const
174 {
175 return FindInLanguages(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT));
176 }
177
178 BOOL CAvailableApplicationInfo::IsInstalled() const
179 {
180 return m_IsInstalled;
181 }
182
183 BOOL CAvailableApplicationInfo::HasInstalledVersion() const
184 {
185 return m_HasInstalledVersion;
186 }
187
188 BOOL CAvailableApplicationInfo::HasUpdate() const
189 {
190 return (m_szInstalledVersion.Compare(m_szVersion) < 0) ? TRUE : FALSE;
191 }
192
193 VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime)
194 {
195 RtlCopyMemory(&m_ftCacheStamp, ftTime, sizeof(FILETIME));
196 }
197
198 inline BOOL CAvailableApplicationInfo::GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString)
199 {
200 if (!m_Parser->GetString(lpKeyName, ReturnedString))
201 {
202 ReturnedString.Empty();
203 return FALSE;
204 }
205 return TRUE;
206 }
207 // CAvailableApplicationInfo
208
209 // AvailableStrings
210 AvailableStrings::AvailableStrings()
211 {
212 //FIXME: maybe provide a fallback?
213 if (GetStorageDirectory(szPath))
214 {
215 szAppsPath = szPath + L"\\rapps\\";
216 szCabPath = szPath + L"\\rappmgr.cab";
217 szSearchPath = szAppsPath + L"*.txt";
218 }
219 }
220 // AvailableStrings
221
222 // CAvailableApps
223 AvailableStrings CAvailableApps::m_Strings;
224
225 CAvailableApps::CAvailableApps()
226 {
227 }
228
229 VOID CAvailableApps::FreeCachedEntries()
230 {
231 POSITION InfoListPosition = m_InfoList.GetHeadPosition();
232
233 /* loop and deallocate all the cached app infos in the list */
234 while (InfoListPosition)
235 {
236 CAvailableApplicationInfo* Info = m_InfoList.GetNext(InfoListPosition);
237 delete Info;
238 }
239
240 m_InfoList.RemoveAll();
241 }
242
243 VOID CAvailableApps::DeleteCurrentAppsDB()
244 {
245 HANDLE hFind = INVALID_HANDLE_VALUE;
246 WIN32_FIND_DATAW FindFileData;
247
248 hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData);
249
250 if (hFind != INVALID_HANDLE_VALUE)
251 {
252 ATL::CStringW szTmp;
253 do
254 {
255 szTmp = m_Strings.szAppsPath + FindFileData.cFileName;
256 DeleteFileW(szTmp.GetString());
257 } while (FindNextFileW(hFind, &FindFileData) != 0);
258 FindClose(hFind);
259 }
260
261 RemoveDirectoryW(m_Strings.szAppsPath);
262 RemoveDirectoryW(m_Strings.szPath);
263 }
264
265 BOOL CAvailableApps::UpdateAppsDB()
266 {
267 HANDLE hFind = INVALID_HANDLE_VALUE;
268 WIN32_FIND_DATAW FindFileData;
269
270 if (!CreateDirectoryW(m_Strings.szPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
271 {
272 return FALSE;
273 }
274
275 //if there are some files in the db folder - we're good
276 hFind = FindFirstFileW(m_Strings.szSearchPath, &FindFileData);
277 if (hFind != INVALID_HANDLE_VALUE)
278 {
279 FindClose(hFind);
280 return TRUE;
281 }
282
283 CDownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
284
285 if (!ExtractFilesFromCab(m_Strings.szCabPath, m_Strings.szAppsPath))
286 {
287 return FALSE;
288 }
289
290 DeleteFileW(m_Strings.szCabPath);
291
292 return TRUE;
293 }
294
295 BOOL CAvailableApps::ForceUpdateAppsDB()
296 {
297 DeleteCurrentAppsDB();
298 return UpdateAppsDB();
299 }
300
301 BOOL CAvailableApps::Enum(INT EnumType, AVAILENUMPROC lpEnumProc)
302 {
303
304 HANDLE hFind = INVALID_HANDLE_VALUE;
305 WIN32_FIND_DATAW FindFileData;
306
307 hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData);
308
309 if (hFind == INVALID_HANDLE_VALUE)
310 {
311 //no db yet
312 return FALSE;
313 }
314
315 do
316 {
317 // loop for all the cached entries
318 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
319 CAvailableApplicationInfo* Info = NULL;
320
321 while (CurrentListPosition != NULL)
322 {
323 POSITION LastListPosition = CurrentListPosition;
324 Info = m_InfoList.GetNext(CurrentListPosition);
325
326 // do we already have this entry in cache?
327 if (Info->m_sFileName == FindFileData.cFileName)
328 {
329 // is it current enough, or the file has been modified since our last time here?
330 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->m_ftCacheStamp) == 1)
331 {
332 // recreate our cache, this is the slow path
333 m_InfoList.RemoveAt(LastListPosition);
334
335 delete Info;
336 Info = NULL;
337 break;
338 }
339 else
340 {
341 // speedy path, compare directly, we already have the data
342 goto skip_if_cached;
343 }
344 }
345 }
346
347 // create a new entry
348 Info = new CAvailableApplicationInfo(FindFileData.cFileName);
349
350 // set a timestamp for the next time
351 Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
352 m_InfoList.AddTail(Info);
353
354 skip_if_cached:
355 if (EnumType == Info->m_Category
356 || EnumType == ENUM_ALL_AVAILABLE
357 || (EnumType == ENUM_CAT_SELECTED && Info->m_IsSelected))
358 {
359 Info->RefreshAppInfo();
360
361 if (lpEnumProc)
362 lpEnumProc(Info, m_Strings.szAppsPath.GetString());
363 }
364 } while (FindNextFileW(hFind, &FindFileData) != 0);
365
366 FindClose(hFind);
367 return TRUE;
368 }
369
370 CAvailableApplicationInfo* CAvailableApps::FindInfo(const ATL::CStringW& szAppName) const
371 {
372 if (m_InfoList.IsEmpty())
373 {
374 return NULL;
375 }
376
377 // linear search
378 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
379 CAvailableApplicationInfo* info;
380 while (CurrentListPosition != NULL)
381 {
382 info = m_InfoList.GetNext(CurrentListPosition);
383 if (info->m_szName == szAppName)
384 {
385 return info;
386 }
387 }
388 return NULL;
389 }
390
391 ATL::CSimpleArray<CAvailableApplicationInfo> CAvailableApps::FindInfoList(const ATL::CSimpleArray<ATL::CStringW> &arrAppsNames) const
392 {
393 ATL::CSimpleArray<CAvailableApplicationInfo> result;
394 for (INT i = 0; i < arrAppsNames.GetSize(); ++i)
395 {
396 CAvailableApplicationInfo* Info = FindInfo(arrAppsNames[i]);
397 if (Info)
398 {
399 result.Add(*Info);
400 }
401 }
402 return result;
403 }
404
405 ATL::CSimpleArray<CAvailableApplicationInfo> CAvailableApps::GetSelected() const
406 {
407 ATL::CSimpleArray<CAvailableApplicationInfo> result;
408 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
409 CAvailableApplicationInfo* Info;
410
411 while (CurrentListPosition != NULL)
412 {
413 Info = m_InfoList.GetNext(CurrentListPosition);
414 if (Info->m_IsSelected)
415 {
416 result.Add(*Info);
417 }
418 }
419 return result;
420 }
421
422 const ATL::CStringW& CAvailableApps::GetFolderPath() const
423 {
424 return m_Strings.szPath;
425 }
426
427 const ATL::CStringW& CAvailableApps::GetAppPath() const
428 {
429 return m_Strings.szAppsPath;
430 }
431
432 const ATL::CStringW& CAvailableApps::GetCabPath() const
433 {
434 return m_Strings.szCabPath;
435 }
436 // CAvailableApps