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