1 /* 2 * Copyright 2012 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 #define OEMRESOURCE 20 21 #include <assert.h> 22 23 #include "urlmon_main.h" 24 #include "resource.h" 25 26 #include "advpub.h" 27 #include "fdi.h" 28 29 #include "wine/debug.h" 30 31 WINE_DEFAULT_DEBUG_CHANNEL(urlmon); 32 33 static const WCHAR ctxW[] = {'c','t','x',0}; 34 static const WCHAR cab_extW[] = {'.','c','a','b',0}; 35 static const WCHAR infW[] = {'i','n','f',0}; 36 static const WCHAR dllW[] = {'d','l','l',0}; 37 static const WCHAR ocxW[] = {'o','c','x',0}; 38 39 enum install_type { 40 INSTALL_UNKNOWN, 41 INSTALL_DLL, 42 INSTALL_INF 43 }; 44 45 typedef struct { 46 IUri *uri; 47 IBindStatusCallback *callback; 48 BOOL release_on_stop; 49 BOOL cancel; 50 WCHAR *install_file; 51 const WCHAR *cache_file; 52 const WCHAR *tmp_dir; 53 const WCHAR *file_name; 54 enum install_type install_type; 55 HWND hwnd; 56 int counter; 57 INT_PTR timer; 58 } install_ctx_t; 59 60 static void release_install_ctx(install_ctx_t *ctx) 61 { 62 if(ctx->uri) 63 IUri_Release(ctx->uri); 64 if(ctx->callback) 65 IBindStatusCallback_Release(ctx->callback); 66 heap_free(ctx->install_file); 67 heap_free(ctx); 68 } 69 70 static inline BOOL file_exists(const WCHAR *file_name) 71 { 72 return GetFileAttributesW(file_name) != INVALID_FILE_ATTRIBUTES; 73 } 74 75 static HRESULT extract_cab_file(install_ctx_t *ctx) 76 { 77 size_t path_len, file_len; 78 WCHAR *ptr; 79 HRESULT hres; 80 81 hres = ExtractFilesW(ctx->cache_file, ctx->tmp_dir, 0, NULL, NULL, 0); 82 if(FAILED(hres)) { 83 WARN("ExtractFilesW failed: %08x\n", hres); 84 return hres; 85 } 86 87 path_len = strlenW(ctx->tmp_dir); 88 file_len = strlenW(ctx->file_name); 89 ctx->install_file = heap_alloc((path_len+file_len+2)*sizeof(WCHAR)); 90 if(!ctx->install_file) 91 return E_OUTOFMEMORY; 92 93 memcpy(ctx->install_file, ctx->tmp_dir, path_len*sizeof(WCHAR)); 94 ctx->install_file[path_len] = '\\'; 95 memcpy(ctx->install_file+path_len+1, ctx->file_name, (file_len+1)*sizeof(WCHAR)); 96 97 /* NOTE: Assume that file_name contains ".cab" extension */ 98 ptr = ctx->install_file+path_len+1+file_len-3; 99 100 memcpy(ptr, infW, sizeof(infW)); 101 if(file_exists(ctx->install_file)) { 102 ctx->install_type = INSTALL_INF; 103 return S_OK; 104 } 105 106 memcpy(ptr, dllW, sizeof(dllW)); 107 if(file_exists(ctx->install_file)) { 108 ctx->install_type = INSTALL_DLL; 109 return S_OK; 110 } 111 112 memcpy(ptr, ocxW, sizeof(ocxW)); 113 if(file_exists(ctx->install_file)) { 114 ctx->install_type = INSTALL_DLL; 115 return S_OK; 116 } 117 118 FIXME("No known install file\n"); 119 return E_NOTIMPL; 120 } 121 122 static HRESULT setup_dll(install_ctx_t *ctx) 123 { 124 HMODULE module; 125 HRESULT hres; 126 127 HRESULT (WINAPI *reg_func)(void); 128 129 module = LoadLibraryW(ctx->install_file); 130 if(!module) 131 return E_FAIL; 132 133 reg_func = (void*)GetProcAddress(module, "DllRegisterServer"); 134 if(reg_func) { 135 hres = reg_func(); 136 }else { 137 WARN("no DllRegisterServer function\n"); 138 hres = E_FAIL; 139 } 140 141 FreeLibrary(module); 142 return hres; 143 } 144 145 static void expand_command(install_ctx_t *ctx, const WCHAR *cmd, WCHAR *buf, size_t *size) 146 { 147 const WCHAR *ptr = cmd, *prev_ptr = cmd; 148 size_t len = 0, len2; 149 150 static const WCHAR expand_dirW[] = {'%','E','X','T','R','A','C','T','_','D','I','R','%'}; 151 152 while((ptr = strchrW(ptr, '%'))) { 153 if(buf) 154 memcpy(buf+len, prev_ptr, ptr-prev_ptr); 155 len += ptr-prev_ptr; 156 157 if(!strncmpiW(ptr, expand_dirW, ARRAY_SIZE(expand_dirW))) { 158 len2 = strlenW(ctx->tmp_dir); 159 if(buf) 160 memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR)); 161 len += len2; 162 ptr += ARRAY_SIZE(expand_dirW); 163 }else { 164 FIXME("Can't expand %s\n", debugstr_w(ptr)); 165 if(buf) 166 buf[len] = '%'; 167 len++; 168 ptr++; 169 } 170 171 prev_ptr = ptr; 172 } 173 174 if(buf) 175 strcpyW(buf+len, prev_ptr); 176 *size = len + strlenW(prev_ptr) + 1; 177 } 178 179 static HRESULT process_hook_section(install_ctx_t *ctx, const WCHAR *sect_name) 180 { 181 WCHAR buf[2048], val[2*MAX_PATH]; 182 const WCHAR *key; 183 DWORD len; 184 HRESULT hres; 185 186 static const WCHAR runW[] = {'r','u','n',0}; 187 188 len = GetPrivateProfileStringW(sect_name, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 189 if(!len) 190 return S_OK; 191 192 for(key = buf; *key; key += strlenW(key)+1) { 193 if(!strcmpiW(key, runW)) { 194 WCHAR *cmd; 195 size_t size; 196 197 len = GetPrivateProfileStringW(sect_name, runW, NULL, val, ARRAY_SIZE(val), ctx->install_file); 198 199 TRACE("Run %s\n", debugstr_w(val)); 200 201 expand_command(ctx, val, NULL, &size); 202 203 cmd = heap_alloc(size*sizeof(WCHAR)); 204 if(!cmd) 205 heap_free(cmd); 206 207 expand_command(ctx, val, cmd, &size); 208 hres = RunSetupCommandW(ctx->hwnd, cmd, NULL, ctx->tmp_dir, NULL, NULL, 0, NULL); 209 heap_free(cmd); 210 if(FAILED(hres)) 211 return hres; 212 }else { 213 FIXME("Unsupported hook %s\n", debugstr_w(key)); 214 return E_NOTIMPL; 215 } 216 } 217 218 return S_OK; 219 } 220 221 static HRESULT install_inf_file(install_ctx_t *ctx) 222 { 223 WCHAR buf[2048], sect_name[128]; 224 BOOL default_install = TRUE; 225 const WCHAR *key; 226 DWORD len; 227 HRESULT hres; 228 229 static const WCHAR setup_hooksW[] = {'S','e','t','u','p',' ','H','o','o','k','s',0}; 230 static const WCHAR add_codeW[] = {'A','d','d','.','C','o','d','e',0}; 231 232 len = GetPrivateProfileStringW(setup_hooksW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 233 if(len) { 234 default_install = FALSE; 235 236 for(key = buf; *key; key += strlenW(key)+1) { 237 TRACE("[Setup Hooks] key: %s\n", debugstr_w(key)); 238 239 len = GetPrivateProfileStringW(setup_hooksW, key, NULL, sect_name, ARRAY_SIZE(sect_name), 240 ctx->install_file); 241 if(!len) { 242 WARN("Could not get key value\n"); 243 return E_FAIL; 244 } 245 246 hres = process_hook_section(ctx, sect_name); 247 if(FAILED(hres)) 248 return hres; 249 } 250 } 251 252 len = GetPrivateProfileStringW(add_codeW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 253 if(len) { 254 default_install = FALSE; 255 256 for(key = buf; *key; key += strlenW(key)+1) { 257 TRACE("[Add.Code] key: %s\n", debugstr_w(key)); 258 259 len = GetPrivateProfileStringW(add_codeW, key, NULL, sect_name, ARRAY_SIZE(sect_name), 260 ctx->install_file); 261 if(!len) { 262 WARN("Could not get key value\n"); 263 return E_FAIL; 264 } 265 266 hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, sect_name, 267 ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL); 268 if(FAILED(hres)) { 269 WARN("RunSetupCommandW failed: %08x\n", hres); 270 return hres; 271 } 272 } 273 } 274 275 if(default_install) { 276 hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL); 277 if(FAILED(hres)) { 278 WARN("RunSetupCommandW failed: %08x\n", hres); 279 return hres; 280 } 281 } 282 283 return S_OK; 284 } 285 286 static HRESULT install_cab_file(install_ctx_t *ctx) 287 { 288 WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH]; 289 BOOL res = FALSE, leave_temp = FALSE; 290 DWORD i; 291 HRESULT hres; 292 293 GetTempPathW(ARRAY_SIZE(tmp_path), tmp_path); 294 295 for(i=0; !res && i < 100; i++) { 296 GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir); 297 res = CreateDirectoryW(tmp_dir, NULL); 298 } 299 if(!res) 300 return E_FAIL; 301 302 ctx->tmp_dir = tmp_dir; 303 304 TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir)); 305 306 hres = extract_cab_file(ctx); 307 if(SUCCEEDED(hres)) { 308 if(ctx->callback) 309 IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file); 310 311 switch(ctx->install_type) { 312 case INSTALL_INF: 313 hres = install_inf_file(ctx); 314 break; 315 case INSTALL_DLL: 316 FIXME("Installing DLL, registering in temporary location\n"); 317 hres = setup_dll(ctx); 318 if(SUCCEEDED(hres)) 319 leave_temp = TRUE; 320 break; 321 default: 322 assert(0); 323 } 324 } 325 326 if(!leave_temp) 327 RemoveDirectoryW(ctx->tmp_dir); 328 return hres; 329 } 330 331 static void update_counter(install_ctx_t *ctx, HWND hwnd) 332 { 333 WCHAR text[100]; 334 335 if(--ctx->counter <= 0) { 336 HWND button_hwnd; 337 338 KillTimer(hwnd, ctx->timer); 339 LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, ARRAY_SIZE(text)); 340 341 button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN); 342 EnableWindow(button_hwnd, TRUE); 343 }else { 344 WCHAR buf[100]; 345 LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, ARRAY_SIZE(buf)); 346 sprintfW(text, buf, ctx->counter); 347 } 348 349 SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text); 350 } 351 352 static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx) 353 { 354 BSTR display_uri; 355 HRESULT hres; 356 357 if(!SetPropW(hwnd, ctxW, ctx)) 358 return FALSE; 359 360 hres = IUri_GetDisplayUri(ctx->uri, &display_uri); 361 if(FAILED(hres)) 362 return FALSE; 363 364 SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri); 365 SysFreeString(display_uri); 366 367 SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON, 368 (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0); 369 370 ctx->counter = 4; 371 update_counter(ctx, hwnd); 372 ctx->timer = SetTimer(hwnd, 1, 1000, NULL); 373 return TRUE; 374 } 375 376 static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 377 { 378 switch(msg) { 379 case WM_INITDIALOG: { 380 if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam)) 381 EndDialog(hwnd, 0); 382 return TRUE; 383 } 384 case WM_COMMAND: 385 switch(wparam) { 386 case ID_AXINSTALL_INSTALL_BTN: { 387 install_ctx_t *ctx = GetPropW(hwnd, ctxW); 388 if(ctx) 389 ctx->cancel = FALSE; 390 EndDialog(hwnd, 0); 391 return FALSE; 392 } 393 case IDCANCEL: 394 EndDialog(hwnd, 0); 395 return FALSE; 396 } 397 case WM_TIMER: 398 update_counter(GetPropW(hwnd, ctxW), hwnd); 399 return TRUE; 400 } 401 402 return FALSE; 403 } 404 405 static BOOL install_warning(install_ctx_t *ctx) 406 { 407 IWindowForBindingUI *window_iface; 408 HWND parent_hwnd = NULL; 409 HRESULT hres; 410 411 if(!ctx->callback) { 412 FIXME("no callback\n"); 413 return FALSE; 414 } 415 416 hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface); 417 if(FAILED(hres)) 418 return FALSE; 419 420 hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd); 421 IWindowForBindingUI_Release(window_iface); 422 if(FAILED(hres)) 423 return FALSE; 424 425 ctx->cancel = TRUE; 426 DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx); 427 return !ctx->cancel; 428 } 429 430 static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file) 431 { 432 BSTR path; 433 HRESULT hres; 434 435 TRACE("%s\n", debugstr_w(cache_file)); 436 437 ctx->cache_file = cache_file; 438 439 if(!install_warning(ctx)) { 440 TRACE("Installation cancelled\n"); 441 return S_OK; 442 } 443 444 hres = IUri_GetPath(ctx->uri, &path); 445 if(SUCCEEDED(hres)) { 446 const WCHAR *ptr, *ptr2, *ext; 447 448 ptr = strrchrW(path, '/'); 449 if(!ptr) 450 ptr = path; 451 else 452 ptr++; 453 454 ptr2 = strrchrW(ptr, '\\'); 455 if(ptr2) 456 ptr = ptr2+1; 457 458 ctx->file_name = ptr; 459 ext = strrchrW(ptr, '.'); 460 if(!ext) 461 ext = ptr; 462 463 if(!strcmpiW(ext, cab_extW)) { 464 hres = install_cab_file(ctx); 465 }else { 466 FIXME("Unsupported extension %s\n", debugstr_w(ext)); 467 hres = E_NOTIMPL; 468 } 469 SysFreeString(path); 470 } 471 472 return hres; 473 } 474 475 static void failure_msgbox(install_ctx_t *ctx, HRESULT hres) 476 { 477 WCHAR buf[1024], fmt[1024]; 478 479 LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, ARRAY_SIZE(fmt)); 480 sprintfW(buf, fmt, hres); 481 MessageBoxW(ctx->hwnd, buf, NULL, MB_OK); 482 } 483 484 static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str) 485 { 486 install_ctx_t *install_ctx = ctx; 487 488 TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str)); 489 490 if(hresult == S_OK) { 491 hresult = install_file(install_ctx, cache_file); 492 if(FAILED(hresult)) 493 failure_msgbox(ctx, hresult); 494 } 495 496 if(install_ctx->callback) 497 IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str); 498 499 if(install_ctx->release_on_stop) 500 release_install_ctx(install_ctx); 501 return S_OK; 502 } 503 504 /*********************************************************************** 505 * AsyncInstallDistributionUnit (URLMON.@) 506 */ 507 HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt, 508 DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags) 509 { 510 install_ctx_t *ctx; 511 HRESULT hres; 512 513 TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt), 514 dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags); 515 516 if(szDistUnit || szTYPE || szExt) 517 FIXME("Unsupported arguments\n"); 518 519 ctx = heap_alloc_zero(sizeof(*ctx)); 520 if(!ctx) 521 return E_OUTOFMEMORY; 522 523 hres = CreateUri(szURL, 0, 0, &ctx->uri); 524 if(FAILED(hres)) { 525 heap_free(ctx); 526 return E_OUTOFMEMORY; 527 } 528 529 ctx->callback = bsc_from_bctx(pbc); 530 531 hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback); 532 if(hres == MK_S_ASYNCHRONOUS) 533 ctx->release_on_stop = TRUE; 534 else 535 release_install_ctx(ctx); 536 537 return hres; 538 } 539