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