[RAPPS] Extensive conversion to ATL and general improvements
[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 InsertTextAfterLoaded_RichEdit(UINT uStringID, const ATL::CStringW& szText, DWORD StringFlags, DWORD TextFlags)
16 {
17 ATL::CStringW szLoadedText;
18 if (!szText.IsEmpty() && szLoadedText.LoadStringW(hInst, uStringID))
19 {
20 InsertRichEditText(szLoadedText, StringFlags);
21 InsertRichEditText(szText, TextFlags);
22 }
23 }
24
25 inline void InsertLoadedTextNewl_RichEdit(UINT uStringID, DWORD StringFlags)
26 {
27 ATL::CStringW szLoadedText;
28 if (szLoadedText.LoadStringW(hInst, uStringID))
29 {
30 InsertRichEditText(L"\n", 0);
31 InsertRichEditText(szLoadedText, StringFlags);
32 InsertRichEditText(L"\n", 0);
33 }
34 }
35
36 inline BOOL GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString, const ATL::CStringW& cFileName)
37 {
38 if (!ParserGetString(lpKeyName, cFileName, ReturnedString))
39 {
40 ReturnedString.Empty();
41 return FALSE;
42 }
43 return TRUE;
44 }
45
46 //App is "installed" if the RegName or Name is in the registry
47 inline BOOL IsAppInstalledKey(PAPPLICATION_INFO Info, REGSAM key)
48 {
49 return (!Info->szRegName.IsEmpty()
50 && (IsInstalledApplication(Info->szRegName, TRUE, key)
51 || IsInstalledApplication(Info->szRegName, FALSE, key)))
52 || (!Info->szName.IsEmpty()
53 && (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 IsAppInstalled(PAPPLICATION_INFO Info)
61 {
62 return IsAppInstalledKey(Info, KEY_WOW64_32KEY)
63 || IsAppInstalledKey(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, ATL::CStringW& szVersion, REGSAM key)
68 {
69 return (!Info->szRegName.IsEmpty()
70 && (InstalledVersion(szVersion, Info->szRegName, TRUE, key)
71 || InstalledVersion(szVersion, Info->szRegName, FALSE, key)))
72 || (!Info->szName.IsEmpty()
73 && (InstalledVersion(szVersion, Info->szName, TRUE, key)
74 || InstalledVersion(szVersion, Info->szName, FALSE, key)));
75 }
76
77 inline BOOL GetInstalledVersion(PAPPLICATION_INFO Info, ATL::CStringW& szVersion)
78 {
79 return GetInstalledVersionWithKey(Info, szVersion, KEY_WOW64_32KEY)
80 || GetInstalledVersionWithKey(Info, szVersion, 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 ATL::CStringW szVersion;
91 ATL::CStringW szLicense;
92 BOOL bIsInstalled = IsAppInstalled(Info),
93 bHasVersion = GetInstalledVersion(Info, 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 (Info->szVersion.Compare(szVersion) > 0)
104 InsertLoadedTextNewl_RichEdit(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
105 else
106 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
107
108 InsertTextAfterLoaded_RichEdit(IDS_AINFO_VERSION, szVersion, CFE_BOLD, 0);
109 }
110 else
111 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
112
113 }
114 else
115 InsertLoadedTextNewl_RichEdit(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
116
117 InsertTextAfterLoaded_RichEdit(IDS_AINFO_AVAILABLEVERSION, Info->szVersion, CFE_BOLD, 0);
118 //license
119 switch (Info->LicenseType)
120 {
121 case LICENSE_TYPE::OpenSource:
122 szLicense.LoadStringW(hInst, IDS_LICENSE_OPENSOURCE);
123 break;
124 case LICENSE_TYPE::Freeware:
125 szLicense.LoadStringW(hInst, IDS_LICENSE_FREEWARE);
126 break;
127 case LICENSE_TYPE::Trial:
128 szLicense.LoadStringW(hInst, IDS_LICENSE_TRIAL);
129 break;
130 default:
131 break;
132 }
133
134 if (szLicense.IsEmpty())
135 {
136 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
137 }
138 else
139 {
140 szLicense.Format(L"(%ls)", Info->szLicense);
141 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
142 }
143
144 InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
145 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
146 InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
147 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->szUrlDownload, CFE_BOLD, CFE_LINK);
148
149 return TRUE;
150 }
151
152 static BOOL
153 DeleteCurrentAppsDB(VOID)
154 {
155 HANDLE hFind = INVALID_HANDLE_VALUE;
156 WIN32_FIND_DATAW FindFileData;
157 ATL::CStringW szCabPath;
158 ATL::CStringW szSearchPath;
159 ATL::CStringW szPath;
160 BOOL result = TRUE;
161
162 if (!GetStorageDirectory(szPath))
163 return FALSE;
164
165 szCabPath = szPath + L"\\rappmgr.cab";
166 result = result && DeleteFileW(szCabPath.GetString());
167
168 szPath += L"\\rapps\\";
169 szSearchPath = szPath + L"*.txt";
170
171 hFind = FindFirstFileW(szSearchPath.GetString(), &FindFileData);
172
173 if (hFind == INVALID_HANDLE_VALUE)
174 return result;
175
176 ATL::CStringW szTmp;
177 do
178 {
179 szTmp = szPath + FindFileData.cFileName;
180 result = result && DeleteFileW(szTmp.GetString());
181 } while (FindNextFileW(hFind, &FindFileData) != 0);
182
183 FindClose(hFind);
184
185 return result;
186 }
187
188 BOOL
189 UpdateAppsDB(VOID)
190 {
191 ATL::CStringW szPath;
192 ATL::CStringW szAppsPath;
193 ATL::CStringW szCabPath;
194
195 if (!DeleteCurrentAppsDB())
196 return FALSE;
197
198 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
199
200 if (!GetStorageDirectory(szPath))
201 return FALSE;
202
203 szCabPath = szPath + L"\\rappmgr.cab";
204 szAppsPath = szPath + L"\\rapps\\";
205
206 if (!ExtractFilesFromCab(szCabPath, szAppsPath))
207 {
208 return FALSE;
209 }
210
211 return TRUE;
212 }
213
214
215 BOOL
216 EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
217 {
218 HANDLE hFind = INVALID_HANDLE_VALUE;
219 WIN32_FIND_DATAW FindFileData;
220 ATL::CStringW szPath;
221 ATL::CStringW szAppsPath;
222 ATL::CStringW szCabPath;
223 PAPPLICATION_INFO Info;
224
225
226 if (!GetStorageDirectory(szPath))
227 return FALSE;
228
229 szCabPath = szPath + L"\\rappmgr.cab";
230 szPath += L"\\rapps\\";
231 szAppsPath = szPath;
232
233 if (!CreateDirectoryW(szPath.GetString(), NULL) &&
234 GetLastError() != ERROR_ALREADY_EXISTS)
235 {
236 return FALSE;
237 }
238
239 szPath += L"*.txt";
240 hFind = FindFirstFileW(szPath.GetString(), &FindFileData);
241
242 if (hFind == INVALID_HANDLE_VALUE)
243 {
244 if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
245 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
246
247 ExtractFilesFromCab(szCabPath, szAppsPath);
248 hFind = FindFirstFileW(szPath, &FindFileData);
249
250 if (hFind == INVALID_HANDLE_VALUE)
251 return FALSE;
252 }
253
254 do
255 {
256 /* loop for all the cached entries */
257 POSITION CurrentListPosition = InfoList.GetHeadPosition();
258
259 while (CurrentListPosition != NULL)
260 {
261 POSITION LastListPosition = CurrentListPosition;
262 Info = InfoList.GetNext(CurrentListPosition);
263
264 /* do we already have this entry in cache? */
265 if (!Info->cFileName.Compare(FindFileData.cFileName))
266 {
267 /* is it current enough, or the file has been modified since our last time here? */
268 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
269 {
270 /* recreate our cache, this is the slow path */
271 InfoList.RemoveAt(LastListPosition);
272 delete Info;
273 break;
274 }
275 else
276 {
277 /* speedy path, compare directly, we already have the data */
278 goto skip_if_cached;
279 }
280 }
281 }
282
283 /* create a new entry */
284 Info = new APPLICATION_INFO();
285
286 if (!Info)
287 break;
288
289 Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
290 INT IntBuffer = ParserGetInt(L"LicenseType", FindFileData.cFileName);
291 if (IntBuffer < LICENSE_TYPE::Max)
292 {
293 Info->LicenseType = (LICENSE_TYPE) IntBuffer;
294 }
295
296 /* copy the cache-related fields for the next time */
297 Info->cFileName = FindFileData.cFileName;
298 RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
299
300 /* add our cached entry to the cached list */
301 InfoList.AddTail(Info);
302
303 skip_if_cached:
304
305 if (Info->Category == FALSE)
306 continue;
307
308 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
309 continue;
310
311 /* if our cache hit was only partial, we need to parse
312 and lazily fill the rest of fields only when needed */
313
314 if (Info->szUrlDownload.IsEmpty())
315 {
316 if (!GetString(L"Name", Info->szName, FindFileData.cFileName)
317 || !GetString(L"URLDownload", Info->szUrlDownload, FindFileData.cFileName))
318 {
319 continue;
320 }
321
322 GetString(L"RegName", Info->szRegName, FindFileData.cFileName);
323 GetString(L"Version", Info->szVersion, FindFileData.cFileName);
324 GetString(L"License", Info->szLicense, FindFileData.cFileName);
325 GetString(L"Description", Info->szDesc, FindFileData.cFileName);
326 GetString(L"Size", Info->szSize, FindFileData.cFileName);
327 GetString(L"URLSite", Info->szUrlSite, FindFileData.cFileName);
328 GetString(L"CDPath", Info->szCDPath, FindFileData.cFileName);
329 GetString(L"Language", Info->szRegName, FindFileData.cFileName);
330 GetString(L"SHA1", Info->szSHA1, FindFileData.cFileName);
331 }
332
333 if (!lpEnumProc(Info))
334 break;
335
336 } while (FindNextFileW(hFind, &FindFileData) != 0);
337
338 FindClose(hFind);
339
340 return TRUE;
341 }
342
343 VOID FreeCachedAvailableEntries(VOID)
344 {
345 PAPPLICATION_INFO Info;
346 POSITION InfoListPosition = InfoList.GetHeadPosition();
347 /* loop and deallocate all the cached app infos in the list */
348 while (InfoListPosition)
349 {
350 Info = InfoList.GetAt(InfoListPosition);
351 InfoList.RemoveHead();
352
353 /* flush them down the toilet :D */
354 delete Info;
355
356 InfoListPosition = InfoList.GetHeadPosition();
357 }
358 }