[RAPPS] Language loading
[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: Functions 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 ATL::CAtlList<PAPPLICATION_INFO> InfoList;
14
15 inline VOID
16 InsertTextAfterLoaded_RichEdit(UINT uStringID, const ATL::CStringW& szText, DWORD StringFlags, DWORD TextFlags)
17 {
18 ATL::CStringW szLoadedText;
19 if (!szText.IsEmpty() && szLoadedText.LoadStringW(hInst, uStringID))
20 {
21 InsertRichEditText(szLoadedText, StringFlags);
22 InsertRichEditText(szText, TextFlags);
23 }
24 }
25
26 inline VOID
27 InsertLoadedTextNewl_RichEdit(UINT uStringID, DWORD StringFlags)
28 {
29 ATL::CStringW szLoadedText;
30 if (szLoadedText.LoadStringW(hInst, uStringID))
31 {
32 InsertRichEditText(L"\n", 0);
33 InsertRichEditText(szLoadedText, StringFlags);
34 InsertRichEditText(L"\n", 0);
35 }
36 }
37
38 inline BOOL
39 GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString, const ATL::CStringW& cFileName)
40 {
41 if (!ParserGetString(lpKeyName, cFileName, ReturnedString))
42 {
43 ReturnedString.Empty();
44 return FALSE;
45 }
46 return TRUE;
47 }
48
49 //Check both registry keys in 64bit system
50 //TODO: check system type beforehand to avoid double checks?
51 inline BOOL
52 GetInstalledVersionEx(PAPPLICATION_INFO Info, ATL::CStringW* szVersion, REGSAM key)
53 {
54 return (!Info->szRegName.IsEmpty()
55 && (GetInstalledVersion_WowUser(szVersion, Info->szRegName, TRUE, key)
56 || GetInstalledVersion_WowUser(szVersion, Info->szRegName, FALSE, key)))
57 || (!Info->szName.IsEmpty()
58 && (GetInstalledVersion_WowUser(szVersion, Info->szName, TRUE, key)
59 || GetInstalledVersion_WowUser(szVersion, Info->szName, FALSE, key)));
60 }
61
62 inline BOOL
63 GetInstalledVersion(PAPPLICATION_INFO Info, ATL::CStringW* szVersion)
64 {
65 return GetInstalledVersionEx(Info, szVersion, KEY_WOW64_32KEY)
66 || GetInstalledVersionEx(Info, szVersion, KEY_WOW64_64KEY);
67 }
68
69 inline BOOL
70 IsAppInstalled(PAPPLICATION_INFO Info)
71 {
72 return GetInstalledVersion(Info, NULL);
73 }
74
75 ATL::CSimpleArray<ATL::CStringW>
76 ParserGetAppLanguages(const ATL::CStringW& szFileName)
77 {
78 const WCHAR cDelimiter = L'|';
79 ATL::CStringW szBuffer;
80
81 if (!ParserGetString(L"Languages", szFileName, szBuffer))
82 return CSimpleArray<ATL::CStringW>();
83
84 ATL::CStringW szLocale;
85 ATL::CSimpleArray<ATL::CStringW> arrLocalesResult;
86 for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
87 {
88 if (szBuffer[i] != cDelimiter)
89 {
90 szLocale += szBuffer[i];
91 } else {
92 arrLocalesResult.Add(szLocale);
93 szLocale.Empty();
94 }
95 }
96 // For the text after delimiter
97 if (!szLocale.IsEmpty())
98 {
99 arrLocalesResult.Add(szLocale);
100 }
101 return arrLocalesResult;
102 }
103
104 BOOL
105 HasNativeLanguage(PAPPLICATION_INFO Info)
106 {
107 if (!Info)
108 {
109 return FALSE;
110 }
111 //TODO: make the actual check
112 return TRUE;
113 }
114
115 LICENSE_TYPE
116 ParserGetLicenseType(const ATL::CStringW& szFileName)
117 {
118 INT IntBuffer = ParserGetInt(L"LicenseType", szFileName);
119 if (IntBuffer < 0 || IntBuffer > LICENSE_TYPE::Max)
120 {
121 return LICENSE_TYPE::None;
122 }
123 return (LICENSE_TYPE) IntBuffer;
124 }
125
126 LIST_ENTRY CachedEntriesHead = {&CachedEntriesHead, &CachedEntriesHead};
127 PLIST_ENTRY pCachedEntry = &CachedEntriesHead;
128
129 VOID
130 InsertVersionInfo_RichEdit(PAPPLICATION_INFO Info)
131 {
132 ATL::CStringW szVersion;
133 BOOL bIsInstalled = IsAppInstalled(Info),
134 bHasVersion = GetInstalledVersion(Info, &szVersion);
135
136 if (bIsInstalled)
137 {
138 if (bHasVersion)
139 {
140 if (Info->szVersion.Compare(szVersion) > 0)
141 InsertLoadedTextNewl_RichEdit(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
142 else
143 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
144
145 InsertTextAfterLoaded_RichEdit(IDS_AINFO_VERSION, szVersion, CFE_BOLD, 0);
146 }
147 else
148 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
149
150 }
151 else
152 InsertLoadedTextNewl_RichEdit(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
153
154 InsertTextAfterLoaded_RichEdit(IDS_AINFO_AVAILABLEVERSION, Info->szVersion, CFE_BOLD, 0);
155 }
156
157 VOID
158 InsertLicenseInfo_RichEdit(PAPPLICATION_INFO Info)
159 {
160 ATL::CStringW szLicense;
161 switch (Info->LicenseType)
162 {
163 case LICENSE_TYPE::OpenSource:
164 szLicense.LoadStringW(hInst, IDS_LICENSE_OPENSOURCE);
165 break;
166 case LICENSE_TYPE::Freeware:
167 szLicense.LoadStringW(hInst, IDS_LICENSE_FREEWARE);
168 break;
169 case LICENSE_TYPE::Trial:
170 szLicense.LoadStringW(hInst, IDS_LICENSE_TRIAL);
171 break;
172 default:
173 break;
174 }
175
176 if (szLicense.IsEmpty())
177 {
178 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
179 }
180 else
181 {
182 szLicense.Format(L"(%ls)", Info->szLicense);
183 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
184 }
185
186 }
187
188 BOOL
189 ShowAvailableAppInfo(INT Index)
190 {
191 PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
192 if (!Info) return FALSE;
193
194 NewRichEditText(Info->szName, CFE_BOLD);
195 InsertVersionInfo_RichEdit(Info);
196 InsertLicenseInfo_RichEdit(Info);
197
198 InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
199 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
200 InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
201 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->szUrlDownload, CFE_BOLD, CFE_LINK);
202
203 return TRUE;
204 }
205
206 static BOOL
207 DeleteCurrentAppsDB(VOID)
208 {
209 HANDLE hFind = INVALID_HANDLE_VALUE;
210 WIN32_FIND_DATAW FindFileData;
211 ATL::CStringW szCabPath;
212 ATL::CStringW szSearchPath;
213 ATL::CStringW szPath;
214 BOOL result = TRUE;
215
216 if (!GetStorageDirectory(szPath))
217 return FALSE;
218
219 szCabPath = szPath + L"\\rappmgr.cab";
220 result = result && DeleteFileW(szCabPath.GetString());
221
222 szPath += L"\\rapps\\";
223 szSearchPath = szPath + L"*.txt";
224
225 hFind = FindFirstFileW(szSearchPath.GetString(), &FindFileData);
226
227 if (hFind == INVALID_HANDLE_VALUE)
228 return result;
229
230 ATL::CStringW szTmp;
231 do
232 {
233 szTmp = szPath + FindFileData.cFileName;
234 result = result && DeleteFileW(szTmp.GetString());
235 } while (FindNextFileW(hFind, &FindFileData) != 0);
236
237 FindClose(hFind);
238
239 return result;
240 }
241
242 BOOL
243 UpdateAppsDB(VOID)
244 {
245 ATL::CStringW szPath;
246 ATL::CStringW szAppsPath;
247 ATL::CStringW szCabPath;
248
249 if (!DeleteCurrentAppsDB())
250 return FALSE;
251
252 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
253
254 if (!GetStorageDirectory(szPath))
255 return FALSE;
256
257 szCabPath = szPath + L"\\rappmgr.cab";
258 szAppsPath = szPath + L"\\rapps\\";
259
260 if (!ExtractFilesFromCab(szCabPath, szAppsPath))
261 {
262 return FALSE;
263 }
264
265 return TRUE;
266 }
267
268
269 BOOL
270 EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
271 {
272 HANDLE hFind = INVALID_HANDLE_VALUE;
273 WIN32_FIND_DATAW FindFileData;
274 ATL::CStringW szPath;
275 ATL::CStringW szAppsPath;
276 ATL::CStringW szCabPath;
277 PAPPLICATION_INFO Info;
278
279 if (!GetStorageDirectory(szPath))
280 return FALSE;
281
282 szCabPath = szPath + L"\\rappmgr.cab";
283 szPath += L"\\rapps\\";
284 szAppsPath = szPath;
285
286 if (!CreateDirectoryW(szPath.GetString(), NULL) &&
287 GetLastError() != ERROR_ALREADY_EXISTS)
288 {
289 return FALSE;
290 }
291
292 szPath += L"*.txt";
293 hFind = FindFirstFileW(szPath.GetString(), &FindFileData);
294
295 if (hFind == INVALID_HANDLE_VALUE)
296 {
297 if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
298 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
299
300 ExtractFilesFromCab(szCabPath, szAppsPath);
301 hFind = FindFirstFileW(szPath, &FindFileData);
302
303 if (hFind == INVALID_HANDLE_VALUE)
304 return FALSE;
305 }
306
307 do
308 {
309 /* loop for all the cached entries */
310 POSITION CurrentListPosition = InfoList.GetHeadPosition();
311
312 while (CurrentListPosition != NULL)
313 {
314 POSITION LastListPosition = CurrentListPosition;
315 Info = InfoList.GetNext(CurrentListPosition);
316
317 /* do we already have this entry in cache? */
318 if (!Info->cFileName.Compare(FindFileData.cFileName))
319 {
320 /* is it current enough, or the file has been modified since our last time here? */
321 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
322 {
323 /* recreate our cache, this is the slow path */
324 InfoList.RemoveAt(LastListPosition);
325 delete Info;
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 APPLICATION_INFO();
338
339 if (!Info)
340 break;
341
342 Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
343 Info->LicenseType = ParserGetLicenseType(FindFileData.cFileName);
344 Info->Languages = ParserGetAppLanguages(FindFileData.cFileName);
345
346 /* copy the cache-related fields for the next time */
347 Info->cFileName = FindFileData.cFileName;
348 RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
349
350 /* add our cached entry to the cached list */
351 InfoList.AddTail(Info);
352
353 skip_if_cached:
354 if (Info->Category == FALSE)
355 continue;
356
357 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
358 continue;
359
360 /* if our cache hit was only partial, we need to parse
361 and lazily fill the rest of fields only when needed */
362
363 if (Info->szUrlDownload.IsEmpty())
364 {
365 if (!GetString(L"Name", Info->szName, FindFileData.cFileName)
366 || !GetString(L"URLDownload", Info->szUrlDownload, FindFileData.cFileName))
367 {
368 continue;
369 }
370
371 GetString(L"RegName", Info->szRegName, FindFileData.cFileName);
372 GetString(L"Version", Info->szVersion, FindFileData.cFileName);
373 GetString(L"License", Info->szLicense, FindFileData.cFileName);
374 GetString(L"Description", Info->szDesc, FindFileData.cFileName);
375 GetString(L"Size", Info->szSize, FindFileData.cFileName);
376 GetString(L"URLSite", Info->szUrlSite, FindFileData.cFileName);
377 GetString(L"CDPath", Info->szCDPath, FindFileData.cFileName);
378 GetString(L"Language", Info->szRegName, FindFileData.cFileName);
379 GetString(L"SHA1", Info->szSHA1, FindFileData.cFileName);
380 }
381
382 if (!lpEnumProc(Info))
383 break;
384
385 } while (FindNextFileW(hFind, &FindFileData) != 0);
386
387 FindClose(hFind);
388
389 return TRUE;
390 }
391
392 VOID FreeCachedAvailableEntries(VOID)
393 {
394 PAPPLICATION_INFO Info;
395 POSITION InfoListPosition = InfoList.GetHeadPosition();
396
397 /* loop and deallocate all the cached app infos in the list */
398 while (InfoListPosition)
399 {
400 Info = InfoList.GetAt(InfoListPosition);
401 InfoList.RemoveHead();
402 InfoListPosition = InfoList.GetHeadPosition();
403
404 delete Info;
405 }
406 }