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