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