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