b396e1dbb788b221c7182e311dedcde695f7d012
[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 // AvailableStrings
213 AvailableStrings::AvailableStrings()
214 {
215 //FIXME: maybe provide a fallback?
216 if (GetStorageDirectory(szPath))
217 {
218 szAppsPath = szPath + L"\\rapps\\";
219 szCabPath = szPath + L"\\rappmgr.cab";
220 szSearchPath = szAppsPath + L"*.txt";
221 }
222 }
223 // AvailableStrings
224
225 // CAvailableApps
226 AvailableStrings CAvailableApps::m_Strings;
227
228 CAvailableApps::CAvailableApps()
229 {
230 }
231
232 VOID CAvailableApps::FreeCachedEntries()
233 {
234 POSITION InfoListPosition = m_InfoList.GetHeadPosition();
235
236 /* loop and deallocate all the cached app infos in the list */
237 while (InfoListPosition)
238 {
239 CAvailableApplicationInfo* Info = m_InfoList.GetNext(InfoListPosition);
240 delete Info;
241 }
242
243 m_InfoList.RemoveAll();
244 }
245
246 VOID CAvailableApps::DeleteCurrentAppsDB()
247 {
248 HANDLE hFind = INVALID_HANDLE_VALUE;
249 WIN32_FIND_DATAW FindFileData;
250
251 hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData);
252
253 if (hFind != INVALID_HANDLE_VALUE)
254 {
255 ATL::CStringW szTmp;
256 do
257 {
258 szTmp = m_Strings.szAppsPath + FindFileData.cFileName;
259 DeleteFileW(szTmp.GetString());
260 } while (FindNextFileW(hFind, &FindFileData) != 0);
261 FindClose(hFind);
262 }
263
264 RemoveDirectoryW(m_Strings.szAppsPath);
265 RemoveDirectoryW(m_Strings.szPath);
266 }
267
268 BOOL CAvailableApps::UpdateAppsDB()
269 {
270 HANDLE hFind = INVALID_HANDLE_VALUE;
271 WIN32_FIND_DATAW FindFileData;
272
273 if (!CreateDirectoryW(m_Strings.szPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
274 {
275 return FALSE;
276 }
277
278 //if there are some files in the db folder - we're good
279 hFind = FindFirstFileW(m_Strings.szSearchPath, &FindFileData);
280 if (hFind != INVALID_HANDLE_VALUE)
281 {
282 FindClose(hFind);
283 return TRUE;
284 }
285
286 CDownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
287
288 if (!ExtractFilesFromCab(m_Strings.szCabPath, m_Strings.szAppsPath))
289 {
290 return FALSE;
291 }
292
293 DeleteFileW(m_Strings.szCabPath);
294
295 return TRUE;
296 }
297
298 BOOL CAvailableApps::ForceUpdateAppsDB()
299 {
300 DeleteCurrentAppsDB();
301 return UpdateAppsDB();
302 }
303
304 BOOL CAvailableApps::Enum(INT EnumType, AVAILENUMPROC lpEnumProc)
305 {
306
307 HANDLE hFind = INVALID_HANDLE_VALUE;
308 WIN32_FIND_DATAW FindFileData;
309
310 hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData);
311
312 if (hFind == INVALID_HANDLE_VALUE)
313 {
314 //no db yet
315 return FALSE;
316 }
317
318 do
319 {
320 // loop for all the cached entries
321 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
322 CAvailableApplicationInfo* Info = NULL;
323
324 while (CurrentListPosition != NULL)
325 {
326 POSITION LastListPosition = CurrentListPosition;
327 Info = m_InfoList.GetNext(CurrentListPosition);
328
329 // do we already have this entry in cache?
330 if (Info->m_sFileName == FindFileData.cFileName)
331 {
332 // is it current enough, or the file has been modified since our last time here?
333 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->m_ftCacheStamp) == 1)
334 {
335 // recreate our cache, this is the slow path
336 m_InfoList.RemoveAt(LastListPosition);
337
338 delete Info;
339 Info = NULL;
340 break;
341 }
342 else
343 {
344 // speedy path, compare directly, we already have the data
345 goto skip_if_cached;
346 }
347 }
348 }
349
350 // create a new entry
351 Info = new CAvailableApplicationInfo(FindFileData.cFileName);
352
353 // set a timestamp for the next time
354 Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
355 m_InfoList.AddTail(Info);
356
357 skip_if_cached:
358 if (Info->m_Category == FALSE)
359 continue;
360
361 if (EnumType != Info->m_Category && EnumType != ENUM_ALL_AVAILABLE)
362 continue;
363
364 Info->RefreshAppInfo();
365
366 if (lpEnumProc)
367 lpEnumProc(Info, m_Strings.szAppsPath.GetString());
368
369 } while (FindNextFileW(hFind, &FindFileData) != 0);
370
371 FindClose(hFind);
372 return TRUE;
373 }
374
375 CAvailableApplicationInfo* CAvailableApps::FindInfo(const ATL::CStringW& szAppName) const
376 {
377 if (m_InfoList.IsEmpty())
378 {
379 return NULL;
380 }
381
382 // linear search
383 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
384 CAvailableApplicationInfo* info;
385 while (CurrentListPosition != NULL)
386 {
387 info = m_InfoList.GetNext(CurrentListPosition);
388 if (info->m_szName == szAppName)
389 {
390 return info;
391 }
392 }
393 return NULL;
394 }
395
396 ATL::CSimpleArray<CAvailableApplicationInfo> CAvailableApps::FindInfoList(const ATL::CSimpleArray<ATL::CStringW> &arrAppsNames) const
397 {
398 ATL::CSimpleArray<CAvailableApplicationInfo> result;
399 for (INT i = 0; i < arrAppsNames.GetSize(); ++i)
400 {
401 CAvailableApplicationInfo* Info = FindInfo(arrAppsNames[i]);
402 if (Info)
403 {
404 result.Add(*Info);
405 }
406 }
407 return result;
408 }
409
410 const ATL::CStringW& CAvailableApps::GetFolderPath() const
411 {
412 return m_Strings.szPath;
413 }
414
415 const ATL::CStringW& CAvailableApps::GetAppPath() const
416 {
417 return m_Strings.szAppsPath;
418 }
419
420 const ATL::CStringW& CAvailableApps::GetCabPath() const
421 {
422 return m_Strings.szCabPath;
423 }
424 // CAvailableApps