[RAPPS]
[reactos.git] / reactos / base / applications / rapps / available.c
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/rapps/available.c
5 * PURPOSE: Functions for working with availabled applications
6 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
7 * Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 */
9
10 #include <ndk/rtlfuncs.h>
11 #include "rapps.h"
12
13 #define ADD_TEXT(a, b, c, d) \
14 if (b[0] != '\0') \
15 { \
16 LoadStringW(hInst, a, szText, _countof(szText)); \
17 InsertRichEditText(szText, c); \
18 InsertRichEditText(b, d); \
19 } \
20
21 #define GET_STRING1(a, b) \
22 if (!ParserGetString(a, b, MAX_PATH, FindFileData.cFileName)) \
23 continue;
24
25 #define GET_STRING2(a, b) \
26 if (!ParserGetString(a, b, MAX_PATH, FindFileData.cFileName)) \
27 b[0] = '\0';
28
29 LIST_ENTRY CachedEntriesHead = {0};
30 PLIST_ENTRY pCachedEntry = NULL;
31
32 BOOL
33 ShowAvailableAppInfo(INT Index)
34 {
35 PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
36 WCHAR szText[MAX_STR_LEN];
37
38 if (!Info) return FALSE;
39
40 NewRichEditText(Info->szName, CFE_BOLD);
41
42 InsertRichEditText(L"\n", 0);
43
44 ADD_TEXT(IDS_AINFO_VERSION, Info->szVersion, CFE_BOLD, 0);
45 ADD_TEXT(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
46 ADD_TEXT(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
47 ADD_TEXT(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
48 ADD_TEXT(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
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 /* initialize the cached list if hasn't been yet */
156 if (pCachedEntry == NULL)
157 {
158 InitializeListHead(&CachedEntriesHead);
159 pCachedEntry = &CachedEntriesHead;
160 }
161
162 if (!GetStorageDirectory(szPath, _countof(szPath)))
163 return FALSE;
164
165 hr = StringCbPrintfW(szCabPath, sizeof(szCabPath),
166 L"%ls\\rappmgr.cab",
167 szPath);
168 if (FAILED(hr))
169 return FALSE;
170
171 hr = StringCbCatW(szPath, sizeof(szPath), L"\\rapps\\");
172
173 if (FAILED(hr))
174 return FALSE;
175
176 hr = StringCbCopyW(szAppsPath, sizeof(szAppsPath), szPath);
177
178 if (FAILED(hr))
179 return FALSE;
180
181 if (!CreateDirectory(szPath, NULL) &&
182 GetLastError() != ERROR_ALREADY_EXISTS)
183 {
184 return FALSE;
185 }
186
187 hr = StringCbCatW(szPath, sizeof(szPath), L"*.txt");
188
189 if (FAILED(hr))
190 return FALSE;
191
192 hFind = FindFirstFileW(szPath, &FindFileData);
193
194 if (hFind == INVALID_HANDLE_VALUE)
195 {
196 if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
197 DownloadApplicationsDB(APPLICATION_DATABASE_URL);
198
199 ExtractFilesFromCab(szCabPath, szAppsPath);
200 hFind = FindFirstFileW(szPath, &FindFileData);
201
202 if (hFind == INVALID_HANDLE_VALUE)
203 return FALSE;
204 }
205
206 do
207 {
208 /* loop for all the cached entries */
209 for (pCachedEntry = CachedEntriesHead.Flink; pCachedEntry != &CachedEntriesHead; pCachedEntry = pCachedEntry->Flink)
210 {
211 Info = CONTAINING_RECORD(pCachedEntry, APPLICATION_INFO, List);
212
213 /* do we already have this entry in cache? */
214 if(_wcsicmp(FindFileData.cFileName, Info->cFileName) == 0)
215 {
216 /* is it current enough, or the file has been modified since our last time here? */
217 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
218 {
219 /* recreate our cache, this is the slow path */
220 RemoveEntryList(&Info->List);
221 HeapFree(GetProcessHeap(), 0, Info);
222 }
223 else
224 {
225 /* speedy path, compare directly, we already have the data */
226 goto skip_if_cached;
227 }
228
229 break;
230 }
231 }
232
233 /* create a new entry */
234 Info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APPLICATION_INFO));
235
236 if(!Info)
237 break;
238
239 Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
240
241 /* copy the cache-related fields for the next time */
242 RtlCopyMemory(&Info->cFileName, &FindFileData.cFileName, MAX_PATH);
243 RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
244
245 /* add our cached entry to the cached list */
246 InsertTailList(&CachedEntriesHead, &Info->List);
247
248 skip_if_cached:
249
250 if (Info->Category == -1)
251 continue;
252
253 if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
254 continue;
255
256 /* if our cache hit was only partial, we need to parse
257 and lazily fill the rest of fields only when needed */
258
259 if (Info->szUrlDownload[0] == 0)
260 {
261 GET_STRING1(L"Name", Info->szName);
262 GET_STRING1(L"URLDownload", Info->szUrlDownload);
263
264 GET_STRING2(L"RegName", Info->szRegName);
265 GET_STRING2(L"Version", Info->szVersion);
266 GET_STRING2(L"License", Info->szLicense);
267 GET_STRING2(L"Description", Info->szDesc);
268 GET_STRING2(L"Size", Info->szSize);
269 GET_STRING2(L"URLSite", Info->szUrlSite);
270 GET_STRING2(L"CDPath", Info->szCDPath);
271 }
272
273 if (!lpEnumProc(Info))
274 break;
275
276 } while (FindNextFileW(hFind, &FindFileData) != 0);
277
278 FindClose(hFind);
279
280 return TRUE;
281 }