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 CRITICAL_SECTION csLock;
63 static IBinding *download_binding = NULL;
64
65 static WCHAR GeckoUrl[] = L"https://svn.reactos.org/amine/wine_gecko-2.40-x86.msi";
66
67 /* SHA definitions are copied from advapi32. They aren't available in headers. */
68
69 typedef struct {
70 ULONG Unknown[6];
71 ULONG State[5];
72 ULONG Count[2];
73 UCHAR Buffer[64];
74 } SHA_CTX, *PSHA_CTX;
75
76 void WINAPI A_SHAInit(PSHA_CTX);
77 void WINAPI A_SHAUpdate(PSHA_CTX,const unsigned char*,UINT);
78 void WINAPI A_SHAFinal(PSHA_CTX,PULONG);
79
sha_check(const WCHAR * file_name)80 static BOOL sha_check(const WCHAR *file_name)
81 {
82 const unsigned char *file_map;
83 HANDLE file, map;
84 ULONG sha[5];
85 char buf[2*sizeof(sha)+1];
86 SHA_CTX ctx;
87 DWORD size, i;
88
89 file = CreateFileW(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
90 if(file == INVALID_HANDLE_VALUE)
91 return FALSE;
92
93 size = GetFileSize(file, NULL);
94
95 map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
96 CloseHandle(file);
97 if(!map)
98 return FALSE;
99
100 file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
101 CloseHandle(map);
102 if(!file_map)
103 return FALSE;
104
105 A_SHAInit(&ctx);
106 A_SHAUpdate(&ctx, file_map, size);
107 A_SHAFinal(&ctx, sha);
108
109 UnmapViewOfFile(file_map);
110
111 for(i=0; i < sizeof(sha); i++)
112 sprintf(buf + i*2, "%02x", *((unsigned char*)sha+i));
113
114 if(strcmp(buf, addon->sha)) {
115 WARN("Got %s, expected %s\n", buf, addon->sha);
116 return FALSE;
117 }
118
119 return TRUE;
120 }
121
set_status(DWORD id)122 static void set_status(DWORD id)
123 {
124 HWND status = GetDlgItem(install_dialog, ID_DWL_STATUS);
125 WCHAR buf[64];
126
127 LoadStringW(hApplet, id, buf, sizeof(buf)/sizeof(WCHAR));
128 SendMessageW(status, WM_SETTEXT, 0, (LPARAM)buf);
129 }
130
131 enum install_res {
132 INSTALL_OK = 0,
133 INSTALL_FAILED,
134 INSTALL_NEXT,
135 };
136
install_file(const WCHAR * file_name)137 static enum install_res install_file(const WCHAR *file_name)
138 {
139 ULONG res;
140
141 res = MsiInstallProductW(file_name, NULL);
142 if(res != ERROR_SUCCESS) {
143 ERR("MsiInstallProduct failed: %u\n", res);
144 return INSTALL_FAILED;
145 }
146
147 return INSTALL_OK;
148 }
149
install_from_unix_file(const char * dir,const char * subdir,const char * file_name)150 static enum install_res install_from_unix_file(const char *dir, const char *subdir, const char *file_name)
151 {
152 LPWSTR dos_file_name;
153 char *file_path;
154 int fd, len;
155 enum install_res ret;
156 UINT res;
157
158 len = strlen(dir);
159 file_path = heap_alloc(len+strlen(subdir)+strlen(file_name)+3);
160 if(!file_path)
161 return INSTALL_FAILED;
162
163 memcpy(file_path, dir, len);
164 if(len && file_path[len-1] != '/' && file_path[len-1] != '\\')
165 file_path[len++] = '/';
166 if(*subdir) {
167 strcpy(file_path+len, subdir);
168 len += strlen(subdir);
169 file_path[len++] = '/';
170 }
171 strcpy(file_path+len, file_name);
172
173 fd = _open(file_path, O_RDONLY);
174 if(fd == -1) {
175 TRACE("%s not found\n", debugstr_a(file_path));
176 heap_free(file_path);
177 return INSTALL_NEXT;
178 }
179
180 _close(fd);
181
182 WARN("Could not get wine_get_dos_file_name function, calling install_cab directly.\n");
183 res = MultiByteToWideChar( CP_ACP, 0, file_path, -1, 0, 0);
184 dos_file_name = heap_alloc (res*sizeof(WCHAR));
185 MultiByteToWideChar( CP_ACP, 0, file_path, -1, dos_file_name, res);
186
187 heap_free(file_path);
188
189 ret = install_file(dos_file_name);
190
191 heap_free(dos_file_name);
192 return ret;
193 }
194
195 static const CHAR mshtml_keyA[] =
196 {'S','o','f','t','w','a','r','e',
197 '\\','W','i','n','e',
198 '\\','M','S','H','T','M','L',0};
199
install_from_registered_dir(void)200 static enum install_res install_from_registered_dir(void)
201 {
202 char *package_dir;
203 DWORD res, type, size = MAX_PATH;
204 enum install_res ret;
205
206 package_dir = heap_alloc(size + sizeof(addon->file_name));
207
208 res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
209 if(res == ERROR_MORE_DATA) {
210 package_dir = heap_realloc(package_dir, size + sizeof(addon->file_name));
211 res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
212 }
213
214 if(res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
215 heap_free(package_dir);
216 return INSTALL_FAILED;
217 }
218
219 if (type == REG_EXPAND_SZ)
220 {
221 size = ExpandEnvironmentStringsA(package_dir, NULL, 0);
222 if (size)
223 {
224 char* buf = heap_alloc(size + sizeof(addon->file_name));
225 ExpandEnvironmentStringsA(package_dir, buf, size);
226 heap_free(package_dir);
227 package_dir = buf;
228 }
229 }
230
231 TRACE("Trying %s/%s\n", debugstr_a(package_dir), debugstr_a(addon->file_name));
232
233 ret = install_from_unix_file(package_dir, "", addon->file_name);
234
235 heap_free(package_dir);
236 return ret;
237 }
238
InstallCallback_QueryInterface(IBindStatusCallback * iface,REFIID riid,void ** ppv)239 static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface,
240 REFIID riid, void **ppv)
241 {
242 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IBindStatusCallback, riid)) {
243 *ppv = iface;
244 return S_OK;
245 }
246
247 return E_INVALIDARG;
248 }
249
InstallCallback_AddRef(IBindStatusCallback * iface)250 static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface)
251 {
252 return 2;
253 }
254
InstallCallback_Release(IBindStatusCallback * iface)255 static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface)
256 {
257 return 1;
258 }
259
InstallCallback_OnStartBinding(IBindStatusCallback * iface,DWORD dwReserved,IBinding * pib)260 static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface,
261 DWORD dwReserved, IBinding *pib)
262 {
263 set_status(IDS_DOWNLOADING);
264
265 IBinding_AddRef(pib);
266
267 EnterCriticalSection(&csLock);
268 download_binding = pib;
269 LeaveCriticalSection(&csLock);
270
271 return S_OK;
272 }
273
InstallCallback_GetPriority(IBindStatusCallback * iface,LONG * pnPriority)274 static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface,
275 LONG *pnPriority)
276 {
277 return E_NOTIMPL;
278 }
279
InstallCallback_OnLowResource(IBindStatusCallback * iface,DWORD dwReserved)280 static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface,
281 DWORD dwReserved)
282 {
283 return E_NOTIMPL;
284 }
285
InstallCallback_OnProgress(IBindStatusCallback * iface,ULONG ulProgress,ULONG ulProgressMax,ULONG ulStatusCode,LPCWSTR szStatusText)286 static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
287 ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
288 {
289 HWND progress = GetDlgItem(install_dialog, ID_DWL_PROGRESS);
290
291 if(ulProgressMax)
292 SendMessageW(progress, PBM_SETRANGE32, 0, ulProgressMax);
293 if(ulProgress)
294 SendMessageW(progress, PBM_SETPOS, ulProgress, 0);
295
296 return S_OK;
297 }
298
InstallCallback_OnStopBinding(IBindStatusCallback * iface,HRESULT hresult,LPCWSTR szError)299 static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface,
300 HRESULT hresult, LPCWSTR szError)
301 {
302 EnterCriticalSection(&csLock);
303 if(download_binding) {
304 IBinding_Release(download_binding);
305 download_binding = NULL;
306 }
307 LeaveCriticalSection(&csLock);
308
309 if(FAILED(hresult)) {
310 if(hresult == E_ABORT)
311 TRACE("Binding aborted\n");
312 else
313 ERR("Binding failed %08x\n", hresult);
314 return S_OK;
315 }
316
317 set_status(IDS_INSTALLING);
318 return S_OK;
319 }
320
InstallCallback_GetBindInfo(IBindStatusCallback * iface,DWORD * grfBINDF,BINDINFO * pbindinfo)321 static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface,
322 DWORD* grfBINDF, BINDINFO* pbindinfo)
323 {
324 return E_NOTIMPL;
325 }
326
InstallCallback_OnDataAvailable(IBindStatusCallback * iface,DWORD grfBSCF,DWORD dwSize,FORMATETC * pformatetc,STGMEDIUM * pstgmed)327 static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
328 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
329 {
330 ERR("\n");
331 return E_NOTIMPL;
332 }
333
InstallCallback_OnObjectAvailable(IBindStatusCallback * iface,REFIID riid,IUnknown * punk)334 static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface,
335 REFIID riid, IUnknown* punk)
336 {
337 ERR("\n");
338 return E_NOTIMPL;
339 }
340
341 static const IBindStatusCallbackVtbl InstallCallbackVtbl = {
342 InstallCallback_QueryInterface,
343 InstallCallback_AddRef,
344 InstallCallback_Release,
345 InstallCallback_OnStartBinding,
346 InstallCallback_GetPriority,
347 InstallCallback_OnLowResource,
348 InstallCallback_OnProgress,
349 InstallCallback_OnStopBinding,
350 InstallCallback_GetBindInfo,
351 InstallCallback_OnDataAvailable,
352 InstallCallback_OnObjectAvailable
353 };
354
355 static IBindStatusCallback InstallCallback = { &InstallCallbackVtbl };
356
download_proc(PVOID arg)357 static DWORD WINAPI download_proc(PVOID arg)
358 {
359 WCHAR message[256];
360 WCHAR tmp_dir[MAX_PATH], tmp_file[MAX_PATH];
361 HRESULT hres, hrCoInit;
362
363 hrCoInit = CoInitializeEx(NULL, COINIT_MULTITHREADED);
364
365 GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir);
366 GetTempFileNameW(tmp_dir, NULL, 0, tmp_file);
367
368 TRACE("using temp file %s\n", debugstr_w(tmp_file));
369
370 hres = URLDownloadToFileW(NULL, GeckoUrl, tmp_file, 0, &InstallCallback);
371 if(FAILED(hres)) {
372 if (LoadStringW(hApplet, IDS_DWL_FAILED, message, sizeof(message) / sizeof(WCHAR))) {
373 /* If the user aborted the download, DO NOT display the message box */
374 if (hres == E_ABORT) {
375 TRACE("Downloading of Gecko package aborted!\n");
376 } else {
377 MessageBoxW(NULL, message, NULL, MB_ICONERROR);
378 }
379 }
380 ERR("URLDownloadToFile failed: %08x\n", hres);
381 } else {
382 if(sha_check(tmp_file)) {
383 install_file(tmp_file);
384 }else {
385 if(LoadStringW(hApplet, IDS_INVALID_SHA, message, sizeof(message)/sizeof(WCHAR))) {
386 MessageBoxW(NULL, message, NULL, MB_ICONERROR);
387 }
388 }
389 }
390
391 DeleteFileW(tmp_file);
392 PostMessageW(install_dialog, WM_COMMAND, IDCANCEL, 0);
393
394 if (SUCCEEDED(hrCoInit))
395 CoUninitialize();
396
397 return 0;
398 }
399
installer_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)400 static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
401 {
402 HWND hwndProgress, hwndInstallButton;
403 switch(msg) {
404 case WM_INITDIALOG:
405 hwndProgress = GetDlgItem(hwnd, ID_DWL_PROGRESS);
406
407 /* CORE-5737: Move focus before SW_HIDE */
408 if (hwndProgress == GetFocus())
409 SendMessageW(hwnd, WM_NEXTDLGCTL, 0, FALSE);
410
411 ShowWindow(hwndProgress, SW_HIDE);
412 install_dialog = hwnd;
413 return TRUE;
414
415 case WM_NOTIFY:
416 break;
417
418 case WM_COMMAND:
419 switch(wParam) {
420 case IDCANCEL:
421 EnterCriticalSection(&csLock);
422 if(download_binding) {
423 IBinding_Abort(download_binding);
424 }
425 else {
426 EndDialog(hwnd, 0);
427 }
428 LeaveCriticalSection(&csLock);
429 return FALSE;
430
431 case ID_DWL_INSTALL:
432 ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_SHOW);
433
434 /* CORE-17550: Never leave focus on a disabled control (Old/New/Thing p.228) */
435 hwndInstallButton = GetDlgItem(hwnd, ID_DWL_INSTALL);
436 if (hwndInstallButton == GetFocus())
437 {
438 SendMessageW(hwnd, WM_NEXTDLGCTL, 0, FALSE);
439 }
440 EnableWindow(hwndInstallButton, FALSE);
441
442 CloseHandle( CreateThread(NULL, 0, download_proc, NULL, 0, NULL));
443 return FALSE;
444 }
445 }
446
447 return FALSE;
448 }
449
install_addon(addon_t addon_type,HWND hwnd_parent)450 BOOL install_addon(addon_t addon_type, HWND hwnd_parent)
451 {
452
453 if(!*ARCH_STRING)
454 return FALSE;
455
456 addon = addons_info + addon_type;
457
458 InitializeCriticalSection(&csLock);
459
460 /*
461 * Try to find addon .msi file in following order:
462 * - directory stored in $dir_config_key value of HKCU/Wine/Software/$config_key key
463 * - download the package
464 */
465 if (install_from_registered_dir() == INSTALL_NEXT)
466 DialogBoxW(hApplet, addon->dialog_template, hwnd_parent, installer_proc);
467
468 DeleteCriticalSection(&csLock);
469
470 return TRUE;
471 }
472