[RAPPS] More code 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 //Check both registry keys in 64bit system
46 //TODO: check system type beforehand to avoid double checks?
47
48 inline BOOL GetInstalledVersionEx(PAPPLICATION_INFO Info, ATL::CStringW* szVersion, REGSAM key)
49 {
50 return (!Info->szRegName.IsEmpty()
51 && (GetInstalledVersion_WowUser(szVersion, Info->szRegName, TRUE, key)
52 || GetInstalledVersion_WowUser(szVersion, Info->szRegName, FALSE, key)))
53 || (!Info->szName.IsEmpty()
54 && (GetInstalledVersion_WowUser(szVersion, Info->szName, TRUE, key)
55 || GetInstalledVersion_WowUser(szVersion, Info->szName, FALSE, key)));
56 }
57
58 inline BOOL GetInstalledVersion(PAPPLICATION_INFO Info, ATL::CStringW* szVersion)
59 {
60 return GetInstalledVersionEx(Info, szVersion, KEY_WOW64_32KEY)
61 || GetInstalledVersionEx(Info, szVersion, KEY_WOW64_64KEY);
62 }
63
64 inline BOOL IsAppInstalled(PAPPLICATION_INFO Info)
65 {
66 return GetInstalledVersion(Info, NULL);
67 }
68
69
70 LIST_ENTRY CachedEntriesHead = {&CachedEntriesHead, &CachedEntriesHead};
71 PLIST_ENTRY pCachedEntry = &CachedEntriesHead;
72
73 BOOL
74 ShowAvailableAppInfo(INT Index)
75 {
76 PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
77 ATL::CStringW szVersion;
78 ATL::CStringW szLicense;
79 BOOL bIsInstalled = IsAppInstalled(Info),
80 bHasVersion = GetInstalledVersion(Info, &szVersion);
81
82 if (!Info) return FALSE;
83
84 NewRichEditText(Info->szName, CFE_BOLD);
85 //installed status
86 if (bIsInstalled)
87 {
88 if (bHasVersion)
89 {
90 if (Info->szVersion.Compare(szVersion) > 0)
91 InsertLoadedTextNewl_RichEdit(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
92 else
93 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
94
95 InsertTextAfterLoaded_RichEdit(IDS_AINFO_VERSION, szVersion, CFE_BOLD, 0);
96 }
97 else
98 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
99
100 }
101 else
102 InsertLoadedTextNewl_RichEdit(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
103
104 InsertTextAfterLoaded_RichEdit(IDS_AINFO_AVAILABLEVERSION, Info->szVersion, CFE_BOLD, 0);
105 //license
106 switch (Info->LicenseType)
107 {
108 case LICENSE_TYPE::OpenSource:
109 szLicense.LoadStringW(hInst, IDS_LICENSE_OPENSOURCE);
110 break;
111 case LICENSE_TYPE::Freeware:
112 szLicense.LoadStringW(hInst, IDS_LICENSE_FREEWARE);
113 break;
114 case LICENSE_TYPE::Trial:
115 szLicense.LoadStringW(hInst, IDS_LICENSE_TRIAL);
116 break;
117 default:
118 break;
119 }
120
121 if (szLicense.IsEmpty())
122 {
123 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
124 }
125 else
126 {
127 szLicense.Format(L"(%ls)", Info->szLicense);
128 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
129 }
130
131 InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
132 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
133 InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
134 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->szUrlDownload, CFE_BOLD, CFE_LINK);
135
136 return TRUE;
137 }
138
139 static BOOL
140 DeleteCurrentAppsDB(VOID)
141 {
142 HANDLE hFind = INVALID_HANDLE_VALUE;
143 WIN32_FIND_DATAW FindFileData;
144 ATL::CStringW szCabPath;
145 ATL::CStringW szSearchPath;
146 ATL::CStringW szPath;
147 BOOL result = TRUE;
148
149 if (!GetStorageDirectory(szPath))
150 return FALSE;
151
152 szCabPath = szPath + L"\\rappmgr.cab";
153 result = result && DeleteFileW(szCabPath.GetString());
154
155 szPath += L"\\rapps\\";
156 szSearchPath = szPath + L"*.txt";
157
158 hFind = FindFirstFileW(szSearchPath.GetString(), &FindFileData);
159
160 if (hFind == INVALID_HANDLE_VALUE)
161 return result;
162
163 ATL::CStringW szTmp;
164 do
165 {
166 szTmp = szPath + FindFileData.cFileName;
167 result = result && DeleteFileW(szTmp.GetString());
168 } while (FindNextFileW(hFind, &FindFileData) != 0);
169
170 FindClose(hFind);
171
172 return result;
173 }
174
175 BOOL
176 UpdateAppsDB(VOID)
177 {
178 ATL::CStringW szPath;
179 ATL::CStringW szAppsPath;
180 ATL::CStringW szCabPath;
181
182 if (!DeleteCurrentAppsDB())
183 return FALSE;
184
185 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
186
187 if (!GetStorageDirectory(szPath))
188 return FALSE;
189
190 szCabPath = szPath + L"\\rappmgr.cab";
191 szAppsPath = szPath + L"\\rapps\\";
192
193 if (!ExtractFilesFromCab(szCabPath, szAppsPath))
194 {
195 return FALSE;
196 }
197
198 return TRUE;
199 }
200
201
202 BOOL
203 EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
204 {
205 HANDLE hFind = INVALID_HANDLE_VALUE;
206 WIN32_FIND_DATAW FindFileData;
207 ATL::CStringW szPath;
208 ATL::CStringW szAppsPath;
209 ATL::CStringW szCabPath;
210 PAPPLICATION_INFO Info;
211
212 if (!GetStorageDirectory(szPath))
213 return FALSE;
214
215 szCabPath = szPath + L"\\rappmgr.cab";
216 szPath += L"\\rapps\\";
217 szAppsPath = szPath;
218
219 if (!CreateDirectoryW(szPath.GetString(), NULL) &&
220 GetLastError() != ERROR_ALREADY_EXISTS)
221 {
222 return FALSE;
223 }
224
225 szPath += L"*.txt";
226 hFind = FindFirstFileW(szPath.GetString(), &FindFileData);
227
228 if (hFind == INVALID_HANDLE_VALUE)
229 {
230 if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
231 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
232
233 ExtractFilesFromCab(szCabPath, szAppsPath);
234 hFind = FindFirstFileW(szPath, &FindFileData);
235
236 if (hFind == INVALID_HANDLE_VALUE)
237 return FALSE;
238 }
239
240 do
241 {
242 /* loop for all the cached entries */
243 POSITION CurrentListPosition = InfoList.GetHeadPosition();
244
245 while (CurrentListPosition != NULL)
246 {
247 POSITION LastListPosition = CurrentListPosition;
248 Info = InfoList.GetNext(CurrentListPosition);
249
250 /* do we already have this entry in cache? */
251 if (!Info->cFileName.Compare(FindFileData.cFileName))
252 {
253 /* is it current enough, or the file has been modified since our last time here? */
254 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
255 {
256 /* recreate our cache, this is the slow path */
257 InfoList.RemoveAt(LastListPosition);
258 delete Info;
259 break;
260 }
261 else
262 {
263 /* speedy path, compare directly, we already have the data */
264 goto skip_if_cached;
265 }
266 }
267 }
268
269 /* create a new entry */
270 Info = new APPLICATION_INFO();
271
272 if (!Info)
273 break;
274
275 Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
276 INT IntBuffer = ParserGetInt(L"LicenseType", FindFileData.cFileName);
277 if (IntBuffer < LICENSE_TYPE::Max)
278 {
279 Info->LicenseType = (LICENSE_TYPE) IntBuffer;
280 }
281
282 /* copy the cache-related fields for the next time */
283 Info->cFileName = FindFileData.cFileName;
284 RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
285
286 /* add our cached entry to the cached list */
287 InfoList.AddTail(Info);
288
289 skip_if_cached:
290 if (Info->Category == FALSE)
291 continue;
292
293 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
294 continue;
295
296 /* if our cache hit was only partial, we need to parse
297 and lazily fill the rest of fields only when needed */
298
299 if (Info->szUrlDownload.IsEmpty())
300 {
301 if (!GetString(L"Name", Info->szName, FindFileData.cFileName)
302 || !GetString(L"URLDownload", Info->szUrlDownload, FindFileData.cFileName))
303 {
304 continue;
305 }
306
307 GetString(L"RegName", Info->szRegName, FindFileData.cFileName);
308 GetString(L"Version", Info->szVersion, FindFileData.cFileName);
309 GetString(L"License", Info->szLicense, FindFileData.cFileName);
310 GetString(L"Description", Info->szDesc, FindFileData.cFileName);
311 GetString(L"Size", Info->szSize, FindFileData.cFileName);
312 GetString(L"URLSite", Info->szUrlSite, FindFileData.cFileName);
313 GetString(L"CDPath", Info->szCDPath, FindFileData.cFileName);
314 GetString(L"Language", Info->szRegName, FindFileData.cFileName);
315 GetString(L"SHA1", Info->szSHA1, FindFileData.cFileName);
316 }
317
318 if (!lpEnumProc(Info))
319 break;
320
321 } while (FindNextFileW(hFind, &FindFileData) != 0);
322
323 FindClose(hFind);
324
325 return TRUE;
326 }
327
328 VOID FreeCachedAvailableEntries(VOID)
329 {
330 PAPPLICATION_INFO Info;
331 POSITION InfoListPosition = InfoList.GetHeadPosition();
332 /* loop and deallocate all the cached app infos in the list */
333 while (InfoListPosition)
334 {
335 Info = InfoList.GetAt(InfoListPosition);
336 InfoList.RemoveHead();
337 InfoListPosition = InfoList.GetHeadPosition();
338
339 delete Info;
340 }
341 }