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