[APPWIZ] Enable the 'Cancel' button while downloading. Patch by Jared Smudde and...
[reactos.git] / reactos / dll / cpl / appwiz / addons.c
1 /*
2 * Copyright 2006-2010 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "appwiz.h"
20
21 #include <stdio.h>
22
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 #include <msi.h>
28
29 #define GECKO_VERSION "2.40"
30
31 #ifdef __i386__
32 #define ARCH_STRING "x86"
33 #define GECKO_SHA "8a3adedf3707973d1ed4ac3b2e791486abf814bd"
34 #else
35 #define ARCH_STRING ""
36 #define GECKO_SHA "???"
37 #endif
38
39 typedef struct {
40 const char *version;
41 const char *file_name;
42 const char *sha;
43 const char *config_key;
44 const char *dir_config_key;
45 LPCWSTR dialog_template;
46 } addon_info_t;
47
48 static const addon_info_t addons_info[] = {
49 {
50 GECKO_VERSION,
51 "wine_gecko-" GECKO_VERSION "-" ARCH_STRING ".msi",
52 GECKO_SHA,
53 "MSHTML",
54 "GeckoCabDir",
55 MAKEINTRESOURCEW(ID_DWL_GECKO_DIALOG)
56 }
57 };
58
59 static const addon_info_t *addon;
60
61 static HWND install_dialog = NULL;
62 static IBinding *download_binding;
63
64 static WCHAR GeckoUrl[] = L"https://svn.reactos.org/amine/wine_gecko-2.40-x86.msi";
65
66 /* SHA definitions are copied from advapi32. They aren't available in headers. */
67
68 typedef struct {
69 ULONG Unknown[6];
70 ULONG State[5];
71 ULONG Count[2];
72 UCHAR Buffer[64];
73 } SHA_CTX, *PSHA_CTX;
74
75 void WINAPI A_SHAInit(PSHA_CTX);
76 void WINAPI A_SHAUpdate(PSHA_CTX,const unsigned char*,UINT);
77 void WINAPI A_SHAFinal(PSHA_CTX,PULONG);
78
79 static BOOL sha_check(const WCHAR *file_name)
80 {
81 const unsigned char *file_map;
82 HANDLE file, map;
83 ULONG sha[5];
84 char buf[2*sizeof(sha)+1];
85 SHA_CTX ctx;
86 DWORD size, i;
87
88 file = CreateFileW(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
89 if(file == INVALID_HANDLE_VALUE)
90 return FALSE;
91
92 size = GetFileSize(file, NULL);
93
94 map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
95 CloseHandle(file);
96 if(!map)
97 return FALSE;
98
99 file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
100 CloseHandle(map);
101 if(!file_map)
102 return FALSE;
103
104 A_SHAInit(&ctx);
105 A_SHAUpdate(&ctx, file_map, size);
106 A_SHAFinal(&ctx, sha);
107
108 UnmapViewOfFile(file_map);
109
110 for(i=0; i < sizeof(sha); i++)
111 sprintf(buf + i*2, "%02x", *((unsigned char*)sha+i));
112
113 if(strcmp(buf, addon->sha)) {
114 WARN("Got %s, expected %s\n", buf, addon->sha);
115 return FALSE;
116 }
117
118 return TRUE;
119 }
120
121 static void set_status(DWORD id)
122 {
123 HWND status = GetDlgItem(install_dialog, ID_DWL_STATUS);
124 WCHAR buf[64];
125
126 LoadStringW(hApplet, id, buf, sizeof(buf)/sizeof(WCHAR));
127 SendMessageW(status, WM_SETTEXT, 0, (LPARAM)buf);
128 }
129
130 enum install_res {
131 INSTALL_OK = 0,
132 INSTALL_FAILED,
133 INSTALL_NEXT,
134 };
135
136 static enum install_res install_file(const WCHAR *file_name)
137 {
138 ULONG res;
139
140 res = MsiInstallProductW(file_name, NULL);
141 if(res != ERROR_SUCCESS) {
142 ERR("MsiInstallProduct failed: %u\n", res);
143 return INSTALL_FAILED;
144 }
145
146 return INSTALL_OK;
147 }
148
149 static enum install_res install_from_unix_file(const char *dir, const char *subdir, const char *file_name)
150 {
151 LPWSTR dos_file_name;
152 char *file_path;
153 int fd, len;
154 enum install_res ret;
155 UINT res;
156
157 len = strlen(dir);
158 file_path = heap_alloc(len+strlen(subdir)+strlen(file_name)+3);
159 if(!file_path)
160 return INSTALL_FAILED;
161
162 memcpy(file_path, dir, len);
163 if(len && file_path[len-1] != '/' && file_path[len-1] != '\\')
164 file_path[len++] = '/';
165 if(*subdir) {
166 strcpy(file_path+len, subdir);
167 len += strlen(subdir);
168 file_path[len++] = '/';
169 }
170 strcpy(file_path+len, file_name);
171
172 fd = _open(file_path, O_RDONLY);
173 if(fd == -1) {
174 TRACE("%s not found\n", debugstr_a(file_path));
175 heap_free(file_path);
176 return INSTALL_NEXT;
177 }
178
179 _close(fd);
180
181 WARN("Could not get wine_get_dos_file_name function, calling install_cab directly.\n");
182 res = MultiByteToWideChar( CP_ACP, 0, file_path, -1, 0, 0);
183 dos_file_name = heap_alloc (res*sizeof(WCHAR));
184 MultiByteToWideChar( CP_ACP, 0, file_path, -1, dos_file_name, res);
185
186 heap_free(file_path);
187
188 ret = install_file(dos_file_name);
189
190 heap_free(dos_file_name);
191 return ret;
192 }
193
194 static const CHAR mshtml_keyA[] =
195 {'S','o','f','t','w','a','r','e',
196 '\\','W','i','n','e',
197 '\\','M','S','H','T','M','L',0};
198
199 static enum install_res install_from_registered_dir(void)
200 {
201 char *package_dir;
202 DWORD res, type, size = MAX_PATH;
203 enum install_res ret;
204
205 package_dir = heap_alloc(size + sizeof(addon->file_name));
206
207 res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
208 if(res == ERROR_MORE_DATA) {
209 package_dir = heap_realloc(package_dir, size + sizeof(addon->file_name));
210 res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
211 }
212
213 if(res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
214 heap_free(package_dir);
215 return INSTALL_FAILED;
216 }
217
218 if (type == REG_EXPAND_SZ)
219 {
220 size = ExpandEnvironmentStringsA(package_dir, NULL, 0);
221 if (size)
222 {
223 char* buf = heap_alloc(size + sizeof(addon->file_name));
224 ExpandEnvironmentStringsA(package_dir, buf, size);
225 heap_free(package_dir);
226 package_dir = buf;
227 }
228 }
229
230 TRACE("Trying %s/%s\n", debugstr_a(package_dir), debugstr_a(addon->file_name));
231
232 ret = install_from_unix_file(package_dir, "", addon->file_name);
233
234 heap_free(package_dir);
235 return ret;
236 }
237
238 static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface,
239 REFIID riid, void **ppv)
240 {
241 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IBindStatusCallback, riid)) {
242 *ppv = iface;
243 return S_OK;
244 }
245
246 return E_INVALIDARG;
247 }
248
249 static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface)
250 {
251 return 2;
252 }
253
254 static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface)
255 {
256 return 1;
257 }
258
259 static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface,
260 DWORD dwReserved, IBinding *pib)
261 {
262 set_status(IDS_DOWNLOADING);
263 IBinding_AddRef(pib);
264 download_binding = pib;
265
266 return S_OK;
267 }
268
269 static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface,
270 LONG *pnPriority)
271 {
272 return E_NOTIMPL;
273 }
274
275 static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface,
276 DWORD dwReserved)
277 {
278 return E_NOTIMPL;
279 }
280
281 static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
282 ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
283 {
284 HWND progress = GetDlgItem(install_dialog, ID_DWL_PROGRESS);
285
286 if(ulProgressMax)
287 SendMessageW(progress, PBM_SETRANGE32, 0, ulProgressMax);
288 if(ulProgress)
289 SendMessageW(progress, PBM_SETPOS, ulProgress, 0);
290
291 return S_OK;
292 }
293
294 static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface,
295 HRESULT hresult, LPCWSTR szError)
296 {
297 if(download_binding) {
298 IBinding_Release(download_binding);
299 download_binding = NULL;
300 }
301
302 if(FAILED(hresult)) {
303 if(hresult == E_ABORT)
304 TRACE("Binding aborted\n");
305 else
306 ERR("Binding failed %08x\n", hresult);
307 return S_OK;
308 }
309
310 set_status(IDS_INSTALLING);
311 return S_OK;
312 }
313
314 static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface,
315 DWORD* grfBINDF, BINDINFO* pbindinfo)
316 {
317 /* FIXME */
318 *grfBINDF = 0;
319 return S_OK;
320 }
321
322 static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
323 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
324 {
325 ERR("\n");
326 return E_NOTIMPL;
327 }
328
329 static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface,
330 REFIID riid, IUnknown* punk)
331 {
332 ERR("\n");
333 return E_NOTIMPL;
334 }
335
336 static const IBindStatusCallbackVtbl InstallCallbackVtbl = {
337 InstallCallback_QueryInterface,
338 InstallCallback_AddRef,
339 InstallCallback_Release,
340 InstallCallback_OnStartBinding,
341 InstallCallback_GetPriority,
342 InstallCallback_OnLowResource,
343 InstallCallback_OnProgress,
344 InstallCallback_OnStopBinding,
345 InstallCallback_GetBindInfo,
346 InstallCallback_OnDataAvailable,
347 InstallCallback_OnObjectAvailable
348 };
349
350 static IBindStatusCallback InstallCallback = { &InstallCallbackVtbl };
351
352 static DWORD WINAPI download_proc(PVOID arg)
353 {
354 WCHAR tmp_dir[MAX_PATH], tmp_file[MAX_PATH];
355 HRESULT hres;
356
357 GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir);
358 GetTempFileNameW(tmp_dir, NULL, 0, tmp_file);
359
360 TRACE("using temp file %s\n", debugstr_w(tmp_file));
361
362 hres = URLDownloadToFileW(NULL, GeckoUrl, tmp_file, 0, &InstallCallback);
363 if(FAILED(hres)) {
364 ERR("URLDownloadToFile failed: %08x\n", hres);
365 } else {
366 if(sha_check(tmp_file)) {
367 install_file(tmp_file);
368 }else {
369 WCHAR message[256];
370
371 if(LoadStringW(hApplet, IDS_INVALID_SHA, message, sizeof(message)/sizeof(WCHAR))) {
372 MessageBoxW(NULL, message, NULL, MB_ICONERROR);
373 }
374 }
375 }
376
377 DeleteFileW(tmp_file);
378 EndDialog(install_dialog, 0);
379 return 0;
380 }
381
382 static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
383 {
384 switch(msg) {
385 case WM_INITDIALOG:
386 ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_HIDE);
387 install_dialog = hwnd;
388 return TRUE;
389
390 case WM_NOTIFY:
391 break;
392
393 case WM_COMMAND:
394 switch(wParam) {
395 case IDCANCEL:
396 if(download_binding) {
397 IBinding_Abort(download_binding);
398 }
399 else {
400 EndDialog(hwnd, 0);
401 }
402 return FALSE;
403
404 case ID_DWL_INSTALL:
405 ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_SHOW);
406 EnableWindow(GetDlgItem(hwnd, ID_DWL_INSTALL), 0);
407 CloseHandle( CreateThread(NULL, 0, download_proc, NULL, 0, NULL));
408 return FALSE;
409 }
410 }
411
412 return FALSE;
413 }
414
415 BOOL install_addon(addon_t addon_type, HWND hwnd_parent)
416 {
417
418 if(!*ARCH_STRING)
419 return FALSE;
420
421 addon = addons_info + addon_type;
422
423 /*
424 * Try to find addon .msi file in following order:
425 * - directory stored in $dir_config_key value of HKCU/Wine/Software/$config_key key
426 * - download the package
427 */
428 if (install_from_registered_dir() == INSTALL_NEXT)
429 DialogBoxW(hApplet, addon->dialog_template, hwnd_parent, installer_proc);
430
431 return TRUE;
432 }