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