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