[RAPPS]
[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
296 ExtractFilesFromCab(m_szCabPath, m_szAppsPath);
297 hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
298
299 if (hFind == INVALID_HANDLE_VALUE)
300 return FALSE;
301 }
302
303 do
304 {
305 // loop for all the cached entries
306 POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
307 CAvailableApplicationInfo* Info = NULL;
308
309 while (CurrentListPosition != NULL)
310 {
311 POSITION LastListPosition = CurrentListPosition;
312 Info = m_InfoList.GetNext(CurrentListPosition);
313
314 // do we already have this entry in cache?
315 if (Info->sFileName == FindFileData.cFileName)
316 {
317 // is it current enough, or the file has been modified since our last time here?
318 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
319 {
320 // recreate our cache, this is the slow path
321 m_InfoList.RemoveAt(LastListPosition);
322
323 delete Info;
324 Info = NULL;
325 break;
326 }
327 else
328 {
329 // speedy path, compare directly, we already have the data
330 goto skip_if_cached;
331 }
332 }
333 }
334
335 // create a new entry
336 Info = new CAvailableApplicationInfo(FindFileData.cFileName);
337
338 // set a timestamp for the next time
339 Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
340 m_InfoList.AddTail(Info);
341
342 skip_if_cached:
343 if (Info->Category == FALSE)
344 continue;
345
346 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
347 continue;
348
349 Info->RefreshAppInfo();
350
351 if (!lpEnumProc(static_cast<PAPPLICATION_INFO>(Info), m_szAppsPath.GetString()))
352 break;
353
354 } while (FindNextFileW(hFind, &FindFileData) != 0);
355
356 FindClose(hFind);
357 return TRUE;
358 }
359
360 const ATL::CStringW & CAvailableApps::GetFolderPath()
361 {
362 return m_szPath;
363 }
364
365 const ATL::CStringW & CAvailableApps::GetAppPath()
366 {
367 return m_szAppsPath;
368 }
369
370 const ATL::CStringW & CAvailableApps::GetCabPath()
371 {
372 return m_szCabPath;
373 }
374
375 const LPCWSTR CAvailableApps::GetFolderPathString()
376 {
377 return m_szPath.GetString();
378 }
379
380 const LPCWSTR CAvailableApps::GetAppPathString()
381 {
382 return m_szPath.GetString();
383 }
384
385 const LPCWSTR CAvailableApps::GetCabPathString()
386 {
387 return m_szPath.GetString();
388 }
389 // CAvailableApps
390
391 // CConfigParser
392 ATL::CStringW CConfigParser::m_szLocaleID;
393 ATL::CStringW CConfigParser::m_szCachedINISectionLocale;
394 ATL::CStringW CConfigParser::m_szCachedINISectionLocaleNeutral;
395
396 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName))
397 {
398 // we don't have cached section strings for the current system language, create them, lazy
399 CacheINILocaleLazy();
400 }
401
402 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName)
403 {
404 ATL::CStringW szDir;
405 ATL::CStringW szBuffer;
406
407 GetStorageDirectory(szDir);
408 szBuffer.Format(L"%ls\\rapps\\%ls", szDir, FileName);
409
410 return szBuffer;
411 }
412
413 VOID CConfigParser::CacheINILocaleLazy()
414 {
415 if (m_szLocaleID.IsEmpty())
416 {
417 // TODO: Set default locale if call fails
418 // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale
419 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
420 m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize);
421
422 m_szLocaleID.ReleaseBuffer();
423 m_szCachedINISectionLocale = L"Section." + m_szLocaleID;
424
425 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
426 m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale + m_szLocaleID.Right(2);
427 }
428 }
429
430 const ATL::CStringW& CConfigParser::GetLocale()
431 {
432 CacheINILocaleLazy();
433 return m_szLocaleID;
434 }
435
436 INT CConfigParser::GetLocaleSize()
437 {
438 return m_cchLocaleSize;
439 }
440
441 UINT CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
442 {
443 DWORD dwResult;
444
445 LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH);
446 // 1st - find localized strings (e.g. "Section.0c0a")
447 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocale.GetString(),
448 KeyName.GetString(),
449 NULL,
450 ResultStringBuffer,
451 MAX_PATH,
452 szConfigPath.GetString());
453
454 if (!dwResult)
455 {
456 // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a")
457 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocaleNeutral.GetString(),
458 KeyName.GetString(),
459 NULL,
460 ResultStringBuffer,
461 MAX_PATH,
462 szConfigPath.GetString());
463 if (!dwResult)
464 {
465 // 3rd - if they weren't present fallback to standard english strings (just "Section")
466 dwResult = GetPrivateProfileStringW(L"Section",
467 KeyName.GetString(),
468 NULL,
469 ResultStringBuffer,
470 MAX_PATH,
471 szConfigPath.GetString());
472 }
473 }
474
475 ResultString.ReleaseBuffer();
476 return (dwResult != 0 ? TRUE : FALSE);
477 }
478
479 UINT CConfigParser::GetInt(const ATL::CStringW& KeyName)
480 {
481 ATL::CStringW Buffer;
482
483 // grab the text version of our entry
484 if (!GetString(KeyName, Buffer))
485 return FALSE;
486
487 if (Buffer.IsEmpty())
488 return FALSE;
489
490 // convert it to an actual integer
491 int result = StrToIntW(Buffer.GetString());
492
493 return (UINT) (result <= 0) ? 0 : result;
494 }
495 // CConfigParser