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 message[256]; 355 WCHAR tmp_dir[MAX_PATH], tmp_file[MAX_PATH]; 356 HRESULT hres; 357 358 GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir); 359 GetTempFileNameW(tmp_dir, NULL, 0, tmp_file); 360 361 TRACE("using temp file %s\n", debugstr_w(tmp_file)); 362 363 hres = URLDownloadToFileW(NULL, GeckoUrl, tmp_file, 0, &InstallCallback); 364 if(FAILED(hres)) { 365 if (LoadStringW(hApplet, IDS_DWL_FAILED, message, sizeof(message) / sizeof(WCHAR))) { 366 /* If the user aborted the download, DO NOT display the message box */ 367 if (hres == E_ABORT) { 368 TRACE("Downloading of Gecko package aborted!\n"); 369 } else { 370 MessageBoxW(NULL, message, NULL, MB_ICONERROR); 371 } 372 } 373 ERR("URLDownloadToFile failed: %08x\n", hres); 374 } else { 375 if(sha_check(tmp_file)) { 376 install_file(tmp_file); 377 }else { 378 if(LoadStringW(hApplet, IDS_INVALID_SHA, message, sizeof(message)/sizeof(WCHAR))) { 379 MessageBoxW(NULL, message, NULL, MB_ICONERROR); 380 } 381 } 382 } 383 384 DeleteFileW(tmp_file); 385 EndDialog(install_dialog, 0); 386 return 0; 387 } 388 389 static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 390 { 391 switch(msg) { 392 case WM_INITDIALOG: 393 ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_HIDE); 394 install_dialog = hwnd; 395 return TRUE; 396 397 case WM_NOTIFY: 398 break; 399 400 case WM_COMMAND: 401 switch(wParam) { 402 case IDCANCEL: 403 if(download_binding) { 404 IBinding_Abort(download_binding); 405 } 406 else { 407 EndDialog(hwnd, 0); 408 } 409 return FALSE; 410 411 case ID_DWL_INSTALL: 412 ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_SHOW); 413 EnableWindow(GetDlgItem(hwnd, ID_DWL_INSTALL), 0); 414 CloseHandle( CreateThread(NULL, 0, download_proc, NULL, 0, NULL)); 415 return FALSE; 416 } 417 } 418 419 return FALSE; 420 } 421 422 BOOL install_addon(addon_t addon_type, HWND hwnd_parent) 423 { 424 425 if(!*ARCH_STRING) 426 return FALSE; 427 428 addon = addons_info + addon_type; 429 430 /* 431 * Try to find addon .msi file in following order: 432 * - directory stored in $dir_config_key value of HKCU/Wine/Software/$config_key key 433 * - download the package 434 */ 435 if (install_from_registered_dir() == INSTALL_NEXT) 436 DialogBoxW(hApplet, addon->dialog_template, hwnd_parent, installer_proc); 437 438 return TRUE; 439 } 440