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 #ifdef __REACTOS__ 76 77 /* The following definitions were copied from dll/win32/advpack32/files.c */ 78 79 /* SESSION Operation */ 80 #define EXTRACT_FILLFILELIST 0x00000001 81 #define EXTRACT_EXTRACTFILES 0x00000002 82 83 struct FILELIST{ 84 LPSTR FileName; 85 struct FILELIST *next; 86 BOOL DoExtract; 87 }; 88 89 typedef struct { 90 INT FileSize; 91 ERF Error; 92 struct FILELIST *FileList; 93 INT FileCount; 94 INT Operation; 95 CHAR Destination[MAX_PATH]; 96 CHAR CurrentFile[MAX_PATH]; 97 CHAR Reserved[MAX_PATH]; 98 struct FILELIST *FilterList; 99 } SESSION; 100 101 static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR); 102 103 104 /* The following functions were copied from dll/win32/advpack32/files.c 105 All unused arguments are removed */ 106 107 static void free_file_node(struct FILELIST *pNode) 108 { 109 HeapFree(GetProcessHeap(), 0, pNode->FileName); 110 HeapFree(GetProcessHeap(), 0, pNode); 111 } 112 113 static void free_file_list(SESSION* session) 114 { 115 struct FILELIST *next, *curr = session->FileList; 116 117 while (curr) 118 { 119 next = curr->next; 120 free_file_node(curr); 121 curr = next; 122 } 123 } 124 125 HRESULT WINAPI Modified_ExtractFilesA(LPCSTR CabName, LPCSTR ExpandDir) 126 { 127 SESSION session; 128 HMODULE hCabinet; 129 HRESULT res = S_OK; 130 LPSTR szConvertedList = NULL; 131 132 TRACE("(%s, %s)\n", debugstr_a(CabName), debugstr_a(ExpandDir)); 133 134 if (!CabName || !ExpandDir) 135 return E_INVALIDARG; 136 137 if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES) 138 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); 139 140 hCabinet = LoadLibraryA("cabinet.dll"); 141 if (!hCabinet) 142 return E_FAIL; 143 144 ZeroMemory(&session, sizeof(SESSION)); 145 146 pExtract = (void *)GetProcAddress(hCabinet, "Extract"); 147 if (!pExtract) 148 { 149 res = E_FAIL; 150 goto done; 151 } 152 153 lstrcpyA(session.Destination, ExpandDir); 154 155 session.Operation |= (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES); 156 res = pExtract(&session, CabName); 157 158 done: 159 free_file_list(&session); 160 FreeLibrary(hCabinet); 161 HeapFree(GetProcessHeap(), 0, szConvertedList); 162 163 return res; 164 } 165 166 167 168 HRESULT WINAPI Modified_ExtractFilesW(LPCWSTR CabName, LPCWSTR ExpandDir) 169 { 170 char *cab_name = NULL, *expand_dir = NULL; 171 HRESULT hres = S_OK; 172 173 TRACE("(%s, %s, %d)\n", debugstr_w(CabName), debugstr_w(ExpandDir)); 174 175 if(CabName) { 176 cab_name = heap_strdupWtoA(CabName); 177 if(!cab_name) 178 return E_OUTOFMEMORY; 179 } 180 181 if(ExpandDir) { 182 expand_dir = heap_strdupWtoA(ExpandDir); 183 if(!expand_dir) 184 hres = E_OUTOFMEMORY; 185 } 186 187 /* cabinet.dll, which does the real job of extracting files, doesn't have UNICODE API, 188 so we need W->A conversion at some point anyway. */ 189 if(SUCCEEDED(hres)) 190 hres = Modified_ExtractFilesA(cab_name, expand_dir); 191 192 heap_free(cab_name); 193 heap_free(expand_dir); 194 return hres; 195 } 196 197 #endif 198 199 200 static HRESULT extract_cab_file(install_ctx_t *ctx) 201 { 202 size_t path_len, file_len; 203 WCHAR *ptr; 204 HRESULT hres; 205 206 #ifdef __REACTOS__ 207 hres = Modified_ExtractFilesW(ctx->cache_file, ctx->tmp_dir); 208 #else 209 hres = ExtractFilesW(ctx->cache_file, ctx->tmp_dir, 0, NULL, NULL, 0); 210 #endif 211 if(FAILED(hres)) { 212 WARN("ExtractFilesW failed: %08x\n", hres); 213 return hres; 214 } 215 216 path_len = strlenW(ctx->tmp_dir); 217 file_len = strlenW(ctx->file_name); 218 ctx->install_file = heap_alloc((path_len+file_len+2)*sizeof(WCHAR)); 219 if(!ctx->install_file) 220 return E_OUTOFMEMORY; 221 222 memcpy(ctx->install_file, ctx->tmp_dir, path_len*sizeof(WCHAR)); 223 ctx->install_file[path_len] = '\\'; 224 memcpy(ctx->install_file+path_len+1, ctx->file_name, (file_len+1)*sizeof(WCHAR)); 225 226 /* NOTE: Assume that file_name contains ".cab" extension */ 227 ptr = ctx->install_file+path_len+1+file_len-3; 228 229 memcpy(ptr, infW, sizeof(infW)); 230 if(file_exists(ctx->install_file)) { 231 ctx->install_type = INSTALL_INF; 232 return S_OK; 233 } 234 235 memcpy(ptr, dllW, sizeof(dllW)); 236 if(file_exists(ctx->install_file)) { 237 ctx->install_type = INSTALL_DLL; 238 return S_OK; 239 } 240 241 memcpy(ptr, ocxW, sizeof(ocxW)); 242 if(file_exists(ctx->install_file)) { 243 ctx->install_type = INSTALL_DLL; 244 return S_OK; 245 } 246 247 FIXME("No known install file\n"); 248 return E_NOTIMPL; 249 } 250 251 static HRESULT setup_dll(install_ctx_t *ctx) 252 { 253 HMODULE module; 254 HRESULT hres; 255 256 HRESULT (WINAPI *reg_func)(void); 257 258 module = LoadLibraryW(ctx->install_file); 259 if(!module) 260 return E_FAIL; 261 262 reg_func = (void*)GetProcAddress(module, "DllRegisterServer"); 263 if(reg_func) { 264 hres = reg_func(); 265 }else { 266 WARN("no DllRegisterServer function\n"); 267 hres = E_FAIL; 268 } 269 270 FreeLibrary(module); 271 return hres; 272 } 273 274 static void expand_command(install_ctx_t *ctx, const WCHAR *cmd, WCHAR *buf, size_t *size) 275 { 276 const WCHAR *ptr = cmd, *prev_ptr = cmd; 277 size_t len = 0, len2; 278 279 static const WCHAR expand_dirW[] = {'%','E','X','T','R','A','C','T','_','D','I','R','%'}; 280 281 while((ptr = strchrW(ptr, '%'))) { 282 if(buf) 283 memcpy(buf+len, prev_ptr, ptr-prev_ptr); 284 len += ptr-prev_ptr; 285 286 if(!strncmpiW(ptr, expand_dirW, ARRAY_SIZE(expand_dirW))) { 287 len2 = strlenW(ctx->tmp_dir); 288 if(buf) 289 memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR)); 290 len += len2; 291 ptr += ARRAY_SIZE(expand_dirW); 292 }else { 293 FIXME("Can't expand %s\n", debugstr_w(ptr)); 294 if(buf) 295 buf[len] = '%'; 296 len++; 297 ptr++; 298 } 299 300 prev_ptr = ptr; 301 } 302 303 if(buf) 304 strcpyW(buf+len, prev_ptr); 305 *size = len + strlenW(prev_ptr) + 1; 306 } 307 308 static HRESULT process_hook_section(install_ctx_t *ctx, const WCHAR *sect_name) 309 { 310 WCHAR buf[2048], val[2*MAX_PATH]; 311 const WCHAR *key; 312 DWORD len; 313 HRESULT hres; 314 315 static const WCHAR runW[] = {'r','u','n',0}; 316 317 len = GetPrivateProfileStringW(sect_name, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 318 if(!len) 319 return S_OK; 320 321 for(key = buf; *key; key += strlenW(key)+1) { 322 if(!strcmpiW(key, runW)) { 323 WCHAR *cmd; 324 size_t size; 325 326 len = GetPrivateProfileStringW(sect_name, runW, NULL, val, ARRAY_SIZE(val), ctx->install_file); 327 328 TRACE("Run %s\n", debugstr_w(val)); 329 330 expand_command(ctx, val, NULL, &size); 331 332 cmd = heap_alloc(size*sizeof(WCHAR)); 333 if(!cmd) 334 heap_free(cmd); 335 336 expand_command(ctx, val, cmd, &size); 337 hres = RunSetupCommandW(ctx->hwnd, cmd, NULL, ctx->tmp_dir, NULL, NULL, 0, NULL); 338 heap_free(cmd); 339 if(FAILED(hres)) 340 return hres; 341 }else { 342 FIXME("Unsupported hook %s\n", debugstr_w(key)); 343 return E_NOTIMPL; 344 } 345 } 346 347 return S_OK; 348 } 349 350 static HRESULT install_inf_file(install_ctx_t *ctx) 351 { 352 WCHAR buf[2048], sect_name[128]; 353 BOOL default_install = TRUE; 354 const WCHAR *key; 355 DWORD len; 356 HRESULT hres; 357 358 static const WCHAR setup_hooksW[] = {'S','e','t','u','p',' ','H','o','o','k','s',0}; 359 static const WCHAR add_codeW[] = {'A','d','d','.','C','o','d','e',0}; 360 361 len = GetPrivateProfileStringW(setup_hooksW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 362 if(len) { 363 default_install = FALSE; 364 365 for(key = buf; *key; key += strlenW(key)+1) { 366 TRACE("[Setup Hooks] key: %s\n", debugstr_w(key)); 367 368 len = GetPrivateProfileStringW(setup_hooksW, key, NULL, sect_name, ARRAY_SIZE(sect_name), 369 ctx->install_file); 370 if(!len) { 371 WARN("Could not get key value\n"); 372 return E_FAIL; 373 } 374 375 hres = process_hook_section(ctx, sect_name); 376 if(FAILED(hres)) 377 return hres; 378 } 379 } 380 381 len = GetPrivateProfileStringW(add_codeW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file); 382 if(len) { 383 default_install = FALSE; 384 385 for(key = buf; *key; key += strlenW(key)+1) { 386 TRACE("[Add.Code] key: %s\n", debugstr_w(key)); 387 388 len = GetPrivateProfileStringW(add_codeW, key, NULL, sect_name, ARRAY_SIZE(sect_name), 389 ctx->install_file); 390 if(!len) { 391 WARN("Could not get key value\n"); 392 return E_FAIL; 393 } 394 395 hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, sect_name, 396 ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL); 397 if(FAILED(hres)) { 398 WARN("RunSetupCommandW failed: %08x\n", hres); 399 return hres; 400 } 401 } 402 } 403 404 if(default_install) { 405 hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL); 406 if(FAILED(hres)) { 407 WARN("RunSetupCommandW failed: %08x\n", hres); 408 return hres; 409 } 410 } 411 412 return S_OK; 413 } 414 415 static HRESULT install_cab_file(install_ctx_t *ctx) 416 { 417 WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH]; 418 BOOL res = FALSE, leave_temp = FALSE; 419 DWORD i; 420 HRESULT hres; 421 422 GetTempPathW(ARRAY_SIZE(tmp_path), tmp_path); 423 424 for(i=0; !res && i < 100; i++) { 425 GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir); 426 res = CreateDirectoryW(tmp_dir, NULL); 427 } 428 if(!res) 429 return E_FAIL; 430 431 ctx->tmp_dir = tmp_dir; 432 433 TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir)); 434 435 hres = extract_cab_file(ctx); 436 if(SUCCEEDED(hres)) { 437 if(ctx->callback) 438 IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file); 439 440 switch(ctx->install_type) { 441 case INSTALL_INF: 442 hres = install_inf_file(ctx); 443 break; 444 case INSTALL_DLL: 445 FIXME("Installing DLL, registering in temporary location\n"); 446 hres = setup_dll(ctx); 447 if(SUCCEEDED(hres)) 448 leave_temp = TRUE; 449 break; 450 default: 451 assert(0); 452 } 453 } 454 455 if(!leave_temp) 456 RemoveDirectoryW(ctx->tmp_dir); 457 return hres; 458 } 459 460 static void update_counter(install_ctx_t *ctx, HWND hwnd) 461 { 462 WCHAR text[100]; 463 464 if(--ctx->counter <= 0) { 465 HWND button_hwnd; 466 467 KillTimer(hwnd, ctx->timer); 468 LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, ARRAY_SIZE(text)); 469 470 button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN); 471 EnableWindow(button_hwnd, TRUE); 472 }else { 473 WCHAR buf[100]; 474 LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, ARRAY_SIZE(buf)); 475 sprintfW(text, buf, ctx->counter); 476 } 477 478 SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text); 479 } 480 481 static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx) 482 { 483 BSTR display_uri; 484 HRESULT hres; 485 486 if(!SetPropW(hwnd, ctxW, ctx)) 487 return FALSE; 488 489 hres = IUri_GetDisplayUri(ctx->uri, &display_uri); 490 if(FAILED(hres)) 491 return FALSE; 492 493 SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri); 494 SysFreeString(display_uri); 495 496 SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON, 497 (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0); 498 499 ctx->counter = 4; 500 update_counter(ctx, hwnd); 501 ctx->timer = SetTimer(hwnd, 1, 1000, NULL); 502 return TRUE; 503 } 504 505 static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 506 { 507 switch(msg) { 508 case WM_INITDIALOG: { 509 if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam)) 510 EndDialog(hwnd, 0); 511 return TRUE; 512 } 513 case WM_COMMAND: 514 switch(wparam) { 515 case ID_AXINSTALL_INSTALL_BTN: { 516 install_ctx_t *ctx = GetPropW(hwnd, ctxW); 517 if(ctx) 518 ctx->cancel = FALSE; 519 EndDialog(hwnd, 0); 520 return FALSE; 521 } 522 case IDCANCEL: 523 EndDialog(hwnd, 0); 524 return FALSE; 525 } 526 case WM_TIMER: 527 update_counter(GetPropW(hwnd, ctxW), hwnd); 528 return TRUE; 529 } 530 531 return FALSE; 532 } 533 534 static BOOL install_warning(install_ctx_t *ctx) 535 { 536 IWindowForBindingUI *window_iface; 537 HWND parent_hwnd = NULL; 538 HRESULT hres; 539 540 if(!ctx->callback) { 541 FIXME("no callback\n"); 542 return FALSE; 543 } 544 545 hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface); 546 if(FAILED(hres)) 547 return FALSE; 548 549 hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd); 550 IWindowForBindingUI_Release(window_iface); 551 if(FAILED(hres)) 552 return FALSE; 553 554 ctx->cancel = TRUE; 555 DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx); 556 return !ctx->cancel; 557 } 558 559 static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file) 560 { 561 BSTR path; 562 HRESULT hres; 563 564 TRACE("%s\n", debugstr_w(cache_file)); 565 566 ctx->cache_file = cache_file; 567 568 if(!install_warning(ctx)) { 569 TRACE("Installation cancelled\n"); 570 return S_OK; 571 } 572 573 hres = IUri_GetPath(ctx->uri, &path); 574 if(SUCCEEDED(hres)) { 575 const WCHAR *ptr, *ptr2, *ext; 576 577 ptr = strrchrW(path, '/'); 578 if(!ptr) 579 ptr = path; 580 else 581 ptr++; 582 583 ptr2 = strrchrW(ptr, '\\'); 584 if(ptr2) 585 ptr = ptr2+1; 586 587 ctx->file_name = ptr; 588 ext = strrchrW(ptr, '.'); 589 if(!ext) 590 ext = ptr; 591 592 if(!strcmpiW(ext, cab_extW)) { 593 hres = install_cab_file(ctx); 594 }else { 595 FIXME("Unsupported extension %s\n", debugstr_w(ext)); 596 hres = E_NOTIMPL; 597 } 598 SysFreeString(path); 599 } 600 601 return hres; 602 } 603 604 static void failure_msgbox(install_ctx_t *ctx, HRESULT hres) 605 { 606 WCHAR buf[1024], fmt[1024]; 607 608 LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, ARRAY_SIZE(fmt)); 609 sprintfW(buf, fmt, hres); 610 MessageBoxW(ctx->hwnd, buf, NULL, MB_OK); 611 } 612 613 static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str) 614 { 615 install_ctx_t *install_ctx = ctx; 616 617 TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str)); 618 619 if(hresult == S_OK) { 620 hresult = install_file(install_ctx, cache_file); 621 if(FAILED(hresult)) 622 failure_msgbox(ctx, hresult); 623 } 624 625 if(install_ctx->callback) 626 IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str); 627 628 if(install_ctx->release_on_stop) 629 release_install_ctx(install_ctx); 630 return S_OK; 631 } 632 633 /*********************************************************************** 634 * AsyncInstallDistributionUnit (URLMON.@) 635 */ 636 HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt, 637 DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags) 638 { 639 install_ctx_t *ctx; 640 HRESULT hres; 641 642 TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt), 643 dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags); 644 645 if(szDistUnit || szTYPE || szExt) 646 FIXME("Unsupported arguments\n"); 647 648 ctx = heap_alloc_zero(sizeof(*ctx)); 649 if(!ctx) 650 return E_OUTOFMEMORY; 651 652 hres = CreateUri(szURL, 0, 0, &ctx->uri); 653 if(FAILED(hres)) { 654 heap_free(ctx); 655 return E_OUTOFMEMORY; 656 } 657 658 ctx->callback = bsc_from_bctx(pbc); 659 660 hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback); 661 if(hres == MK_S_ASYNCHRONOUS) 662 ctx->release_on_stop = TRUE; 663 else 664 release_install_ctx(ctx); 665 666 return hres; 667 } 668