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