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