[RAPPS] Fixed multiple redraws of the apps list
[reactos.git] / reactos / base / applications / rapps / available.cpp
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/rapps/available.cpp
5 * PURPOSE: Classes for working with available applications
6 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
7 * Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 * Alexander Shaposhnikov (chaez.san@gmail.com)
9 */
10
11 #include "rapps.h"
12
13 // CAvailableApplicationInfo
14
15 CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam)
16 : m_Parser(sFileNameParam)
17 {
18 LicenseType = LICENSE_TYPE::None;
19 sFileName = sFileNameParam;
20
21 RetrieveGeneralInfo();
22 }
23
24 VOID CAvailableApplicationInfo::RefreshAppInfo()
25 {
26 if (szUrlDownload.IsEmpty())
27 {
28 RetrieveGeneralInfo();
29 }
30 }
31
32 VOID CAvailableApplicationInfo::RetrieveGeneralInfo()
33 {
34 Category = m_Parser.GetInt(L"Category");
35
36 if (!GetString(L"Name", szName)
37 || !GetString(L"URLDownload", szUrlDownload))
38 {
39 return;
40 }
41
42 GetString(L"RegName", szRegName);
43 GetString(L"Version", szVersion);
44 GetString(L"License", szLicense);
45 GetString(L"Description", szDesc);
46 GetString(L"Size", szSize);
47 GetString(L"URLSite", szUrlSite);
48 GetString(L"CDPath", szCDPath);
49 GetString(L"Language", szRegName);
50 GetString(L"SHA1", szSHA1);
51
52 RetrieveLicenseType();
53 RetrieveLanguages();
54 RetrieveInstalledStatus();
55 if (m_IsInstalled)
56 {
57 RetrieveInstalledVersion();
58 }
59 }
60
61 VOID CAvailableApplicationInfo::RetrieveInstalledStatus()
62 {
63 m_IsInstalled = ::GetInstalledVersion(NULL, szRegName)
64 || ::GetInstalledVersion(NULL, szName);
65 }
66
67 VOID CAvailableApplicationInfo::RetrieveInstalledVersion()
68 {
69 m_HasInstalledVersion = ::GetInstalledVersion(&szInstalledVersion, szRegName)
70 || ::GetInstalledVersion(&szInstalledVersion, szName);
71 }
72
73 VOID CAvailableApplicationInfo::RetrieveLanguages()
74 {
75 const WCHAR cDelimiter = L'|';
76 ATL::CStringW szBuffer;
77
78 // TODO: Get multiline parameter
79 if (!m_Parser.GetString(L"Languages", szBuffer))
80 {
81 m_HasLanguageInfo = FALSE;
82 return;
83 }
84
85 // Parse parameter string
86 ATL::CStringW m_szLocale;
87 for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
88 {
89 if (szBuffer[i] != cDelimiter)
90 {
91 m_szLocale += szBuffer[i];
92 }
93 else
94 {
95 Languages.Add(m_szLocale);
96 m_szLocale.Empty();
97 }
98 }
99
100 // For the text after delimiter
101 if (!m_szLocale.IsEmpty())
102 {
103 Languages.Add(m_szLocale);
104 }
105
106 m_HasLanguageInfo = TRUE;
107 }
108
109 VOID CAvailableApplicationInfo::RetrieveLicenseType()
110 {
111 INT IntBuffer = m_Parser.GetInt(L"LicenseType");
112
113 if (IntBuffer < 0 || IntBuffer > LICENSE_TYPE::Max)
114 {
115 LicenseType = LICENSE_TYPE::None;
116 }
117 else
118 {
119 LicenseType = (LICENSE_TYPE) IntBuffer;
120 }
121 }
122
123 BOOL CAvailableApplicationInfo::HasLanguageInfo() const
124 {
125 return m_HasLanguageInfo;
126 }
127
128 BOOL CAvailableApplicationInfo::HasNativeLanguage() const
129 {
130 if (!m_HasLanguageInfo)
131 {
132 return FALSE;
133 }
134
135 //Find locale code in the list
136 const INT nLanguagesSize = Languages.GetSize();
137 for (INT i = 0; i < nLanguagesSize; ++i)
138 {
139 if (Languages[i] == CConfigParser::GetLocale())
140 {
141 return TRUE;
142 }
143 }
144
145 return FALSE;
146 }
147
148 BOOL CAvailableApplicationInfo::HasEnglishLanguage() const
149 {
150 static ATL::CStringW szEnglishLocaleID;
151 const INT cchLocaleSize = m_Parser.GetLocaleSize();
152 if (!m_HasLanguageInfo)
153 {
154 return FALSE;
155 }
156
157 //Get English locale code
158 if (szEnglishLocaleID.IsEmpty())
159 {
160 INT result = GetLocaleInfoW(MAKELCID(LANG_ENGLISH, SORT_DEFAULT), LOCALE_ILANGUAGE,
161 szEnglishLocaleID.GetBuffer(cchLocaleSize), cchLocaleSize);
162 szEnglishLocaleID.ReleaseBuffer();
163 if (result != ERROR_SUCCESS)
164 {
165 return FALSE;
166 }
167
168 }
169
170 //Find locale code in the list
171 const INT nLanguagesSize = Languages.GetSize();
172 for (INT i = 0; i < nLanguagesSize; ++i)
173 {
174 if (Languages[i] == szEnglishLocaleID)
175 {
176 return TRUE;
177 }
178 }
179
180 return FALSE;
181 }
182
183 BOOL CAvailableApplicationInfo::IsInstalled() const
184 {
185 return m_IsInstalled;
186 }
187
188 BOOL CAvailableApplicationInfo::HasInstalledVersion() const
189 {
190 return m_HasInstalledVersion;
191 }
192
193 BOOL CAvailableApplicationInfo::HasUpdate() const
194 {
195 return (szInstalledVersion.Compare(szVersion) < 0) ? TRUE : FALSE;
196 }
197
198 VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime)
199 {
200 RtlCopyMemory(&ftCacheStamp, ftTime, sizeof(FILETIME));
201 }
202
203 inline BOOL CAvailableApplicationInfo::GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString)
204 {
205 if (!m_Parser.GetString(lpKeyName, ReturnedString))
206 {
207 ReturnedString.Empty();
208 return FALSE;
209 }
210 return TRUE;
211 }
212 // CAvailableApplicationInfo
213
214 // CAvailableApps
215 CAvailableApps::CAvailableApps()
216 {
217 //set all paths
218 if (GetStorageDirectory(m_szPath))
219 {
220 m_szAppsPath = m_szPath + L"\\rapps\\";
221 m_szCabPath = m_szPath + L"\\rappmgr.cab";
222 m_szSearchPath = m_szAppsPath + L"*.txt";
223 }
224 }
225
226 VOID CAvailableApps::FreeCachedEntries()
227 {
228 POSITION InfoListPosition = m_InfoList.GetHeadPosition();
229
230 /* loop and deallocate all the cached app infos in the list */
231 while (InfoListPosition)
232 {
233 CAvailableApplicationInfo* Info = m_InfoList.GetAt(InfoListPosition);
234 m_InfoList.RemoveHead();
235 delete Info;
236
237 InfoListPosition = m_InfoList.GetHeadPosition();
238 }
239 }
240
241 BOOL CAvailableApps::DeleteCurrentAppsDB()
242 {
243 HANDLE hFind = INVALID_HANDLE_VALUE;
244 WIN32_FIND_DATAW FindFileData;
245 BOOL result = TRUE;
246
247 if (m_szPath.IsEmpty())
248 return FALSE;
249
250 result = result && DeleteFileW(m_szCabPath.GetString());
251 hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
252
253 if (hFind == INVALID_HANDLE_VALUE)
254 return result;
255
256 ATL::CStringW szTmp;
257 do
258 {
259 szTmp = m_szPath + FindFileData.cFileName;
260 result = result && DeleteFileW(szTmp.GetString());
261 } while (FindNextFileW(hFind, &FindFileData) != 0);
262
263 FindClose(hFind);
264
265 return result;
266 }
267
268 BOOL CAvailableApps::UpdateAppsDB()
269 {
270 if (!DeleteCurrentAppsDB())
271 return FALSE;
272
273 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
274
275 if (m_szPath.IsEmpty())
276 return FALSE;
277
278 if (!ExtractFilesFromCab(m_szCabPath, m_szAppsPath))
279 {
280 return FALSE;
281 }
282
283 return TRUE;
284 }
285
286 BOOL CAvailableApps::EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
287 {
288 HANDLE hFind = INVALID_HANDLE_VALUE;
289 WIN32_FIND_DATAW FindFileData;
290
291 if (!CreateDirectoryW(m_szPath.GetString(), NULL) &&
292 GetLastError() != ERROR_ALREADY_EXISTS)
293 {
294 return FALSE;
295 }
296
297 hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
298
299 if (hFind == INVALID_HANDLE_VALUE)
300 {
301 if (GetFileAttributesW(m_szCabPath) == INVALID_FILE_ATTRIBUTES)
302 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
303
304 ExtractFilesFromCab(m_szCabPath, m_szAppsPath);
305 hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
306
307 if (hFind == INVALID_HANDLE_VALUE)
308 return FALSE;
309 }
310
311 do
312 {
313 // loop for all the cached entries
314 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
315 CAvailableApplicationInfo* Info = NULL;
316
317 while (CurrentListPosition != NULL)
318 {
319 POSITION LastListPosition = CurrentListPosition;
320 Info = m_InfoList.GetNext(CurrentListPosition);
321
322 // do we already have this entry in cache?
323 if (Info->sFileName == FindFileData.cFileName)
324 {
325 // is it current enough, or the file has been modified since our last time here?
326 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
327 {
328 // recreate our cache, this is the slow path
329 m_InfoList.RemoveAt(LastListPosition);
330
331 delete Info;
332 Info = nullptr;
333 break;
334 }
335 else
336 {
337 // speedy path, compare directly, we already have the data
338 goto skip_if_cached;
339 }
340 }
341 }
342
343 // create a new entry
344 Info = new CAvailableApplicationInfo(FindFileData.cFileName);
345
346 // set a timestamp for the next time
347 Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
348 m_InfoList.AddTail(Info);
349
350 skip_if_cached:
351 if (Info->Category == FALSE)
352 continue;
353
354 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
355 continue;
356
357 Info->RefreshAppInfo();
358
359 if (!lpEnumProc(static_cast<PAPPLICATION_INFO>(Info), m_szAppsPath.GetString()))
360 break;
361
362 } while (FindNextFileW(hFind, &FindFileData) != 0);
363
364 FindClose(hFind);
365 return TRUE;
366 }
367
368 const ATL::CStringW & CAvailableApps::GetFolderPath()
369 {
370 return m_szPath;
371 }
372
373 const ATL::CStringW & CAvailableApps::GetAppPath()
374 {
375 return m_szAppsPath;
376 }
377
378 const ATL::CStringW & CAvailableApps::GetCabPath()
379 {
380 return m_szCabPath;
381 }
382
383 const LPCWSTR CAvailableApps::GetFolderPathString()
384 {
385 return m_szPath.GetString();
386 }
387
388 const LPCWSTR CAvailableApps::GetAppPathString()
389 {
390 return m_szPath.GetString();
391 }
392
393 const LPCWSTR CAvailableApps::GetCabPathString()
394 {
395 return m_szPath.GetString();
396 }
397 // CAvailableApps
398
399 // CConfigParser
400 ATL::CStringW CConfigParser::m_szLocale;
401 ATL::CStringW CConfigParser::m_szCachedINISectionLocale;
402 ATL::CStringW CConfigParser::m_szCachedINISectionLocaleNeutral;
403
404 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName))
405 {
406 // we don't have cached section strings for the current system language, create them, lazy
407 CacheINILocaleLazy();
408 }
409
410 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName)
411 {
412 ATL::CStringW szDir;
413 static ATL::CStringW szBuffer;
414
415 GetStorageDirectory(szDir);
416 szBuffer.Format(L"%ls\\rapps\\%ls", szDir, FileName);
417
418 return szBuffer;
419 }
420
421 VOID CConfigParser::CacheINILocaleLazy()
422 {
423 if (m_szLocale.IsEmpty())
424 {
425 // TODO: Set default locale if call fails
426 // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale
427 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE,
428 m_szLocale.GetBuffer(m_cchLocaleSize), m_cchLocaleSize);
429 m_szLocale.ReleaseBuffer();
430 m_szCachedINISectionLocale = L"Section." + m_szLocale;
431
432 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
433 m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale + m_szLocale.Right(2);
434 }
435 }
436
437 const ATL::CStringW& CConfigParser::GetLocale()
438 {
439 CacheINILocaleLazy();
440 return m_szLocale;
441 }
442
443 INT CConfigParser::GetLocaleSize()
444 {
445 return m_cchLocaleSize;
446 }
447
448 UINT CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
449 {
450 DWORD dwResult;
451
452 LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH);
453 // 1st - find localized strings (e.g. "Section.0c0a")
454 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocale.GetString(),
455 KeyName.GetString(),
456 NULL,
457 ResultStringBuffer,
458 MAX_PATH,
459 szConfigPath.GetString());
460
461 if (!dwResult)
462 {
463 // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a")
464 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocaleNeutral.GetString(),
465 KeyName.GetString(),
466 NULL,
467 ResultStringBuffer,
468 MAX_PATH,
469 szConfigPath.GetString());
470 if (!dwResult)
471 {
472 // 3rd - if they weren't present fallback to standard english strings (just "Section")
473 dwResult = GetPrivateProfileStringW(L"Section",
474 KeyName.GetString(),
475 NULL,
476 ResultStringBuffer,
477 MAX_PATH,
478 szConfigPath.GetString());
479 }
480 }
481
482 ResultString.ReleaseBuffer();
483 return (dwResult != 0 ? TRUE : FALSE);
484 }
485
486 UINT CConfigParser::GetInt(const ATL::CStringW& KeyName)
487 {
488 ATL::CStringW Buffer;
489 UNICODE_STRING BufferW;
490 ULONG Result;
491
492 // grab the text version of our entry
493 if (!GetString(KeyName, Buffer))
494 return FALSE;
495
496 if (Buffer.IsEmpty())
497 return FALSE;
498
499 // convert it to an actual integer
500 RtlInitUnicodeString(&BufferW, Buffer.GetString());
501 RtlUnicodeStringToInteger(&BufferW, 0, &Result);
502
503 return (UINT) Result;
504 }
505 // CConfigParser