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 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 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 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 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 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 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 250 static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface) 251 { 252 return 2; 253 } 254 255 static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface) 256 { 257 return 1; 258 } 259 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 274 static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface, 275 LONG *pnPriority) 276 { 277 return E_NOTIMPL; 278 } 279 280 static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface, 281 DWORD dwReserved) 282 { 283 return E_NOTIMPL; 284 } 285 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 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 321 static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface, 322 DWORD* grfBINDF, BINDINFO* pbindinfo) 323 { 324 return E_NOTIMPL; 325 } 326 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 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 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 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 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