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