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