1 /* 2 * INSENG Implementation 3 * 4 * Copyright 2006 Mike McCormack 5 * Copyright 2016 Michael M�ller 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #define COBJMACROS 23 24 #include "config.h" 25 26 #include <stdarg.h> 27 28 #include "windef.h" 29 #include "winbase.h" 30 #include "winuser.h" 31 #include "ole2.h" 32 #include "rpcproxy.h" 33 #include "urlmon.h" 34 #ifdef __REACTOS__ 35 #include <winreg.h> 36 #endif 37 #include "shlwapi.h" 38 #include "initguid.h" 39 #include "inseng.h" 40 41 #include "inseng_private.h" 42 43 #include "wine/debug.h" 44 45 WINE_DEFAULT_DEBUG_CHANNEL(inseng); 46 47 static HINSTANCE instance; 48 49 enum thread_operation 50 { 51 OP_DOWNLOAD, 52 OP_INSTALL 53 }; 54 55 struct thread_info 56 { 57 DWORD operation; 58 DWORD jobflags; 59 IEnumCifComponents *enum_comp; 60 61 DWORD download_size; 62 DWORD install_size; 63 64 DWORD downloaded_kb; 65 ULONGLONG download_start; 66 }; 67 68 struct InstallEngine { 69 IInstallEngine2 IInstallEngine2_iface; 70 IInstallEngineTiming IInstallEngineTiming_iface; 71 LONG ref; 72 73 IInstallEngineCallback *callback; 74 char *baseurl; 75 char *downloaddir; 76 ICifFile *icif; 77 DWORD status; 78 79 /* used for the installation thread */ 80 struct thread_info thread; 81 }; 82 83 struct downloadcb 84 { 85 IBindStatusCallback IBindStatusCallback_iface; 86 LONG ref; 87 88 WCHAR *file_name; 89 WCHAR *cache_file; 90 91 char *id; 92 char *display; 93 94 DWORD dl_size; 95 DWORD dl_previous_kb; 96 97 InstallEngine *engine; 98 HANDLE event_done; 99 HRESULT hr; 100 }; 101 102 static inline InstallEngine *impl_from_IInstallEngine2(IInstallEngine2 *iface) 103 { 104 return CONTAINING_RECORD(iface, InstallEngine, IInstallEngine2_iface); 105 } 106 107 static inline struct downloadcb *impl_from_IBindStatusCallback(IBindStatusCallback *iface) 108 { 109 return CONTAINING_RECORD(iface, struct downloadcb, IBindStatusCallback_iface); 110 } 111 112 static inline InstallEngine *impl_from_IInstallEngineTiming(IInstallEngineTiming *iface) 113 { 114 return CONTAINING_RECORD(iface, InstallEngine, IInstallEngineTiming_iface); 115 } 116 117 static HRESULT WINAPI downloadcb_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv) 118 { 119 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 120 121 if (IsEqualGUID(&IID_IUnknown, riid)) 122 { 123 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 124 *ppv = &This->IBindStatusCallback_iface; 125 } 126 else if (IsEqualGUID(&IID_IBindStatusCallback, riid)) 127 { 128 TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv); 129 *ppv = &This->IBindStatusCallback_iface; 130 } 131 else 132 { 133 FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); 134 *ppv = NULL; 135 return E_NOINTERFACE; 136 } 137 138 IUnknown_AddRef((IUnknown *)*ppv); 139 return S_OK; 140 } 141 142 static ULONG WINAPI downloadcb_AddRef(IBindStatusCallback *iface) 143 { 144 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 145 LONG ref = InterlockedIncrement(&This->ref); 146 147 TRACE("(%p) ref = %d\n", This, ref); 148 149 return ref; 150 } 151 152 static ULONG WINAPI downloadcb_Release(IBindStatusCallback *iface) 153 { 154 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 155 LONG ref = InterlockedDecrement(&This->ref); 156 157 TRACE("(%p) ref = %d\n", This, ref); 158 159 if (!ref) 160 { 161 heap_free(This->file_name); 162 heap_free(This->cache_file); 163 164 IInstallEngine2_Release(&This->engine->IInstallEngine2_iface); 165 heap_free(This); 166 } 167 168 return ref; 169 } 170 171 static HRESULT WINAPI downloadcb_OnStartBinding(IBindStatusCallback *iface, DWORD reserved, IBinding *pbind) 172 { 173 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 174 175 TRACE("(%p)->(%u %p)\n", This, reserved, pbind); 176 177 return S_OK; 178 } 179 180 static HRESULT WINAPI downloadcb_GetPriority(IBindStatusCallback *iface, LONG *priority) 181 { 182 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 183 184 FIXME("(%p)->(%p): stub\n", This, priority); 185 186 return E_NOTIMPL; 187 } 188 189 static HRESULT WINAPI downloadcb_OnLowResource(IBindStatusCallback *iface, DWORD reserved) 190 { 191 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 192 193 FIXME("(%p)->(%u): stub\n", This, reserved); 194 195 return E_NOTIMPL; 196 } 197 198 static HRESULT WINAPI downloadcb_OnProgress(IBindStatusCallback *iface, ULONG progress, 199 ULONG progress_max, ULONG status, const WCHAR *status_text) 200 { 201 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 202 HRESULT hr = S_OK; 203 204 TRACE("%p)->(%u %u %u %s)\n", This, progress, progress_max, status, debugstr_w(status_text)); 205 206 switch(status) 207 { 208 case BINDSTATUS_BEGINDOWNLOADDATA: 209 if (!This->engine->thread.download_start) 210 This->engine->thread.download_start = GetTickCount64(); 211 /* fall-through */ 212 case BINDSTATUS_DOWNLOADINGDATA: 213 case BINDSTATUS_ENDDOWNLOADDATA: 214 This->engine->thread.downloaded_kb = This->dl_previous_kb + progress / 1024; 215 if (This->engine->callback) 216 { 217 hr = IInstallEngineCallback_OnComponentProgress(This->engine->callback, 218 This->id, INSTALLSTATUS_DOWNLOADING, This->display, NULL, progress / 1024, This->dl_size); 219 } 220 break; 221 222 case BINDSTATUS_CACHEFILENAMEAVAILABLE: 223 This->cache_file = strdupW(status_text); 224 if (!This->cache_file) 225 { 226 ERR("Failed to allocate memory for cache file\n"); 227 hr = E_OUTOFMEMORY; 228 } 229 break; 230 231 case BINDSTATUS_CONNECTING: 232 case BINDSTATUS_SENDINGREQUEST: 233 case BINDSTATUS_MIMETYPEAVAILABLE: 234 case BINDSTATUS_FINDINGRESOURCE: 235 break; 236 237 default: 238 FIXME("Unsupported status %u\n", status); 239 } 240 241 return hr; 242 } 243 244 static HRESULT WINAPI downloadcb_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError) 245 { 246 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 247 248 TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError)); 249 250 if (FAILED(hresult)) 251 { 252 This->hr = hresult; 253 goto done; 254 } 255 256 if (!This->cache_file) 257 { 258 This->hr = E_FAIL; 259 goto done; 260 } 261 262 if (CopyFileW(This->cache_file, This->file_name, FALSE)) 263 This->hr = S_OK; 264 else 265 { 266 ERR("CopyFile failed: %u\n", GetLastError()); 267 This->hr = E_FAIL; 268 } 269 270 done: 271 SetEvent(This->event_done); 272 return S_OK; 273 } 274 275 static HRESULT WINAPI downloadcb_GetBindInfo(IBindStatusCallback *iface, 276 DWORD *grfBINDF, BINDINFO *pbindinfo) 277 { 278 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 279 280 TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo); 281 282 *grfBINDF = BINDF_PULLDATA | BINDF_NEEDFILE; 283 return S_OK; 284 } 285 286 static HRESULT WINAPI downloadcb_OnDataAvailable(IBindStatusCallback *iface, 287 DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) 288 { 289 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 290 291 TRACE("(%p)->(%08x %u %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); 292 293 return S_OK; 294 } 295 296 static HRESULT WINAPI downloadcb_OnObjectAvailable(IBindStatusCallback *iface, 297 REFIID riid, IUnknown *punk) 298 { 299 struct downloadcb *This = impl_from_IBindStatusCallback(iface); 300 301 FIXME("(%p)->(%s %p): stub\n", This, debugstr_guid(riid), punk); 302 303 return E_NOTIMPL; 304 } 305 306 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = 307 { 308 downloadcb_QueryInterface, 309 downloadcb_AddRef, 310 downloadcb_Release, 311 downloadcb_OnStartBinding, 312 downloadcb_GetPriority, 313 downloadcb_OnLowResource, 314 downloadcb_OnProgress, 315 downloadcb_OnStopBinding, 316 downloadcb_GetBindInfo, 317 downloadcb_OnDataAvailable, 318 downloadcb_OnObjectAvailable 319 }; 320 321 static HRESULT downloadcb_create(InstallEngine *engine, HANDLE event, char *file_name, char *id, 322 char *display, DWORD dl_size, struct downloadcb **callback) 323 { 324 struct downloadcb *cb; 325 326 cb = heap_alloc_zero(sizeof(*cb)); 327 if (!cb) return E_OUTOFMEMORY; 328 329 cb->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl; 330 cb->ref = 1; 331 cb->hr = E_FAIL; 332 cb->id = id; 333 cb->display = display; 334 cb->engine = engine; 335 cb->dl_size = dl_size; 336 cb->dl_previous_kb = engine->thread.downloaded_kb; 337 cb->event_done = event; 338 cb->file_name = strAtoW(file_name); 339 if (!cb->file_name) 340 { 341 heap_free(cb); 342 return E_OUTOFMEMORY; 343 } 344 345 IInstallEngine2_AddRef(&engine->IInstallEngine2_iface); 346 347 *callback = cb; 348 return S_OK; 349 } 350 351 static HRESULT WINAPI InstallEngine_QueryInterface(IInstallEngine2 *iface, REFIID riid, void **ppv) 352 { 353 InstallEngine *This = impl_from_IInstallEngine2(iface); 354 355 if(IsEqualGUID(&IID_IUnknown, riid)) { 356 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 357 *ppv = &This->IInstallEngine2_iface; 358 }else if(IsEqualGUID(&IID_IInstallEngine, riid)) { 359 TRACE("(%p)->(IID_IInstallEngine %p)\n", This, ppv); 360 *ppv = &This->IInstallEngine2_iface; 361 }else if(IsEqualGUID(&IID_IInstallEngine2, riid)) { 362 TRACE("(%p)->(IID_IInstallEngine2 %p)\n", This, ppv); 363 *ppv = &This->IInstallEngine2_iface; 364 }else if(IsEqualGUID(&IID_IInstallEngineTiming, riid)) { 365 TRACE("(%p)->(IID_IInstallEngineTiming %p)\n", This, ppv); 366 *ppv = &This->IInstallEngineTiming_iface; 367 }else { 368 FIXME("(%p)->(%s %p) not found\n", This, debugstr_guid(riid), ppv); 369 *ppv = NULL; 370 return E_NOINTERFACE; 371 } 372 373 IUnknown_AddRef((IUnknown *)*ppv); 374 return S_OK; 375 } 376 377 static ULONG WINAPI InstallEngine_AddRef(IInstallEngine2 *iface) 378 { 379 InstallEngine *This = impl_from_IInstallEngine2(iface); 380 LONG ref = InterlockedIncrement(&This->ref); 381 382 TRACE("(%p) ref=%d\n", This, ref); 383 384 return ref; 385 } 386 387 static ULONG WINAPI InstallEngine_Release(IInstallEngine2 *iface) 388 { 389 InstallEngine *This = impl_from_IInstallEngine2(iface); 390 LONG ref = InterlockedDecrement(&This->ref); 391 392 TRACE("(%p) ref=%d\n", This, ref); 393 394 if (!ref) 395 { 396 if (This->icif) 397 ICifFile_Release(This->icif); 398 399 heap_free(This->baseurl); 400 heap_free(This->downloaddir); 401 heap_free(This); 402 } 403 404 return ref; 405 } 406 407 static void set_status(InstallEngine *This, DWORD status) 408 { 409 This->status = status; 410 411 if (This->callback) 412 IInstallEngineCallback_OnEngineStatusChange(This->callback, status, 0); 413 } 414 415 static HRESULT calc_sizes(IEnumCifComponents *enum_comp, DWORD operation, DWORD *size_download, DWORD *size_install) 416 { 417 ICifComponent *comp; 418 DWORD download = 0; 419 DWORD install = 0; 420 HRESULT hr; 421 422 /* FIXME: what about inactive dependencies and how does 423 * INSTALLOPTIONS_FORCEDEPENDENCIES play into this ?*/ 424 425 hr = IEnumCifComponents_Reset(enum_comp); 426 if (FAILED(hr)) return hr; 427 428 while (SUCCEEDED(IEnumCifComponents_Next(enum_comp, &comp))) 429 { 430 if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) 431 continue; 432 433 /* FIXME: handle install options and find out the default options*/ 434 if (operation == OP_DOWNLOAD && ICifComponent_IsComponentDownloaded(comp) == S_FALSE) 435 download = ICifComponent_GetDownloadSize(comp); 436 /* 437 if (operation == OP_INSTALL && ICifComponent_IsComponentInstalled(comp) == S_FALSE) 438 install = ICifComponent_GetInstalledSize(comp); 439 */ 440 } 441 442 *size_download = download; 443 *size_install = install; 444 445 return S_OK; 446 } 447 448 static HRESULT get_next_component(IEnumCifComponents *enum_comp, DWORD operation, ICifComponent **ret_comp) 449 { 450 ICifComponent *comp; 451 HRESULT hr; 452 453 hr = IEnumCifComponents_Reset(enum_comp); 454 if (FAILED(hr)) return hr; 455 456 while (SUCCEEDED(IEnumCifComponents_Next(enum_comp, &comp))) 457 { 458 if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) 459 continue; 460 461 /* FIXME: handle install options and find out the default options*/ 462 if (operation == OP_DOWNLOAD && ICifComponent_IsComponentDownloaded(comp) != S_FALSE) 463 continue; 464 if (operation == OP_INSTALL && ICifComponent_IsComponentInstalled(comp) != S_FALSE) 465 continue; 466 467 *ret_comp = comp; 468 return S_OK; 469 } 470 471 return S_FALSE; 472 } 473 474 static HRESULT get_url(ICifComponent *comp, int index, char **url, DWORD *flags) 475 { 476 char *url_temp = NULL; 477 int size = MAX_PATH / 2; 478 HRESULT hr; 479 480 /* FIXME: should we add an internal get function to prevent this ugly code ? */ 481 482 /* check if there is an url with such an index */ 483 hr = ICifComponent_GetUrl(comp, index, NULL, 0, flags); 484 if (FAILED(hr)) 485 { 486 *url = NULL; 487 *flags = 0; 488 return S_OK; 489 } 490 491 do 492 { 493 size *= 2; 494 heap_free(url_temp); 495 url_temp = heap_alloc(size); 496 if (!url_temp) return E_OUTOFMEMORY; 497 498 hr = ICifComponent_GetUrl(comp, index, url_temp, size, flags); 499 if (FAILED(hr)) 500 { 501 heap_free(url_temp); 502 return hr; 503 } 504 } 505 while (strlen(url_temp) == size-1); 506 507 *url = url_temp; 508 return S_OK; 509 } 510 511 static char *combine_url(char *baseurl, char *url) 512 { 513 int len_base = strlen(baseurl); 514 int len_url = strlen(url); 515 char *combined; 516 517 combined = heap_alloc(len_base + len_url + 2); 518 if (!combined) return NULL; 519 520 strcpy(combined, baseurl); 521 if (len_base && combined[len_base-1] != '/') 522 strcat(combined, "/"); 523 strcat(combined, url); 524 525 return combined; 526 } 527 528 static HRESULT generate_moniker(char *baseurl, char *url, DWORD flags, IMoniker **moniker) 529 { 530 WCHAR *urlW; 531 HRESULT hr; 532 533 if (flags & URLF_RELATIVEURL) 534 { 535 char *combined; 536 if (!baseurl) 537 return E_FAIL; 538 539 combined = combine_url(baseurl, url); 540 if (!combined) return E_OUTOFMEMORY; 541 542 urlW = strAtoW(combined); 543 heap_free(combined); 544 if (!urlW) return E_OUTOFMEMORY; 545 } 546 else 547 { 548 urlW = strAtoW(url); 549 if (!urlW) return E_OUTOFMEMORY; 550 } 551 552 hr = CreateURLMoniker(NULL, urlW, moniker); 553 heap_free(urlW); 554 return hr; 555 } 556 557 static char *merge_path(char *path1, char *path2) 558 { 559 int len = strlen(path1) + strlen(path2) + 2; 560 char *combined = heap_alloc(len); 561 562 if (!combined) return NULL; 563 strcpy(combined, path1); 564 strcat(combined, "\\"); 565 strcat(combined, path2); 566 567 return combined; 568 } 569 570 static HRESULT download_url(InstallEngine *This, char *id, char *display, char *url, DWORD flags, DWORD dl_size) 571 { 572 struct downloadcb *callback = NULL; 573 char *filename = NULL; 574 IUnknown *unk = NULL; 575 IMoniker *mon = NULL; 576 IBindCtx *bindctx = NULL; 577 HANDLE event = NULL; 578 HRESULT hr; 579 580 if (!This->downloaddir) 581 { 582 WARN("No download directory set\n"); 583 return E_FAIL; 584 } 585 586 hr = generate_moniker(This->baseurl, url, flags, &mon); 587 if (FAILED(hr)) 588 { 589 FIXME("Failed to create moniker\n"); 590 return hr; 591 } 592 593 event = CreateEventW(NULL, TRUE, FALSE, NULL); 594 if (!event) 595 { 596 IMoniker_Release(mon); 597 return E_FAIL; 598 } 599 600 filename = strrchr(url, '/'); 601 if (!filename) filename = url; 602 603 filename = merge_path(This->downloaddir, filename); 604 if (!filename) 605 { 606 hr = E_OUTOFMEMORY; 607 goto error; 608 } 609 610 hr = downloadcb_create(This, event, filename, id, display, dl_size, &callback); 611 if (FAILED(hr)) goto error; 612 613 hr = CreateAsyncBindCtx(0, &callback->IBindStatusCallback_iface, NULL, &bindctx); 614 if(FAILED(hr)) goto error; 615 616 hr = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); 617 if (FAILED(hr)) goto error; 618 if (unk) IUnknown_Release(unk); 619 620 heap_free(filename); 621 IMoniker_Release(mon); 622 IBindCtx_Release(bindctx); 623 624 WaitForSingleObject(event, INFINITE); 625 hr = callback->hr; 626 627 CloseHandle(event); 628 IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); 629 return hr; 630 631 error: 632 if (mon) IMoniker_Release(mon); 633 if (event) CloseHandle(event); 634 if (callback) IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); 635 if (bindctx) IBindCtx_Release(bindctx); 636 if (filename) heap_free(filename); 637 return hr; 638 } 639 640 static HRESULT process_component_dependencies(InstallEngine *This, ICifComponent *comp) 641 { 642 char id[MAX_ID_LENGTH+1], type; 643 DWORD ver, build; 644 HRESULT hr; 645 int i; 646 647 for (i = 0;; i++) 648 { 649 hr = ICifComponent_GetDependency(comp, i, id, sizeof(id), &type, &ver, &build); 650 if (SUCCEEDED(hr)) 651 FIXME("Can't handle dependencies yet: %s\n", debugstr_a(id)); 652 else 653 break; 654 } 655 656 return S_OK; 657 } 658 659 static HRESULT process_component(InstallEngine *This, ICifComponent *comp) 660 { 661 DWORD size_dl, size_install, phase; 662 char display[MAX_DISPLAYNAME_LENGTH+1]; 663 char id[MAX_ID_LENGTH+1]; 664 HRESULT hr; 665 int i; 666 667 hr = ICifComponent_GetID(comp, id, sizeof(id)); 668 if (FAILED(hr)) return hr; 669 670 TRACE("processing component %s\n", debugstr_a(id)); 671 672 hr = ICifComponent_GetDescription(comp, display, sizeof(display)); 673 if (FAILED(hr)) return hr; 674 675 size_dl = (This->thread.operation == OP_DOWNLOAD) ? ICifComponent_GetDownloadSize(comp) : 0; 676 size_install = 0; /* (This->thread.operation == OP_INSTALL) ? ICifComponent_GetInstalledSize(comp) : 0; */ 677 678 if (This->callback) 679 { 680 IInstallEngineCallback_OnStartComponent(This->callback, id, size_dl, size_install, display); 681 IInstallEngineCallback_OnComponentProgress(This->callback, id, INSTALLSTATUS_INITIALIZING, display, NULL, 0, 0); 682 phase = INSTALLSTATUS_INITIALIZING; 683 } 684 685 hr = process_component_dependencies(This, comp); 686 if (FAILED(hr)) return hr; 687 688 if (This->thread.operation == OP_DOWNLOAD) 689 { 690 for (i = 0;; i++) 691 { 692 DWORD flags; 693 char *url; 694 695 phase = INSTALLSTATUS_DOWNLOADING; 696 697 hr = get_url(comp, i, &url, &flags); 698 if (FAILED(hr)) goto done; 699 if (!url) break; 700 701 TRACE("processing url %s\n", debugstr_a(url)); 702 703 hr = download_url(This, id, display, url, flags, size_dl); 704 heap_free(url); 705 if (FAILED(hr)) 706 { 707 DWORD retry = 0; 708 709 if (This->callback) 710 IInstallEngineCallback_OnEngineProblem(This->callback, ENGINEPROBLEM_DOWNLOADFAIL, &retry); 711 if (!retry) goto done; 712 713 i--; 714 continue; 715 } 716 717 phase = INSTALLSTATUS_CHECKINGTRUST; 718 /* FIXME: check trust */ 719 IInstallEngineCallback_OnComponentProgress(This->callback, id, INSTALLSTATUS_CHECKINGTRUST, display, NULL, 0, 0); 720 } 721 722 component_set_downloaded(comp, TRUE); 723 phase = INSTALLSTATUS_DOWNLOADFINISHED; 724 } 725 else 726 FIXME("Installation not yet implemented\n"); 727 728 done: 729 IInstallEngineCallback_OnStopComponent(This->callback, id, hr, phase, display, 0); 730 return hr; 731 } 732 733 DWORD WINAPI thread_installation(LPVOID param) 734 { 735 InstallEngine *This = param; 736 ICifComponent *comp; 737 HRESULT hr; 738 739 if (This->callback) 740 IInstallEngineCallback_OnStartInstall(This->callback, This->thread.download_size, This->thread.install_size); 741 742 for (;;) 743 { 744 hr = get_next_component(This->thread.enum_comp, This->thread.operation, &comp); 745 if (FAILED(hr)) break; 746 if (hr == S_FALSE) 747 { 748 hr = S_OK; 749 break; 750 } 751 752 hr = process_component(This, comp); 753 if (FAILED(hr)) break; 754 } 755 756 if (This->callback) 757 IInstallEngineCallback_OnStopInstall(This->callback, hr, NULL, 0); 758 759 IEnumCifComponents_Release(This->thread.enum_comp); 760 IInstallEngine2_Release(&This->IInstallEngine2_iface); 761 762 set_status(This, ENGINESTATUS_READY); 763 return 0; 764 } 765 766 static HRESULT start_installation(InstallEngine *This, DWORD operation, DWORD jobflags) 767 { 768 HANDLE thread; 769 HRESULT hr; 770 771 This->thread.operation = operation; 772 This->thread.jobflags = jobflags; 773 This->thread.downloaded_kb = 0; 774 This->thread.download_start = 0; 775 776 /* Windows sends the OnStartInstall event from a different thread, 777 * but OnStartInstall already contains the required download and install size. 778 * The only way to signal an error from the thread is to send an OnStopComponent / 779 * OnStopInstall signal which can only occur after OnStartInstall. We need to 780 * precompute the sizes here to be able inform the application about errors while 781 * calculating the required sizes. */ 782 783 hr = ICifFile_EnumComponents(This->icif, &This->thread.enum_comp, 0, NULL); 784 if (FAILED(hr)) return hr; 785 786 hr = calc_sizes(This->thread.enum_comp, operation, &This->thread.download_size, &This->thread.install_size); 787 if (FAILED(hr)) goto error; 788 789 IInstallEngine2_AddRef(&This->IInstallEngine2_iface); 790 791 thread = CreateThread(NULL, 0, thread_installation, This, 0, NULL); 792 if (!thread) 793 { 794 IInstallEngine2_Release(&This->IInstallEngine2_iface); 795 hr = E_FAIL; 796 goto error; 797 } 798 799 CloseHandle(thread); 800 return S_OK; 801 802 error: 803 IEnumCifComponents_Release(This->thread.enum_comp); 804 return hr; 805 } 806 807 static HRESULT WINAPI InstallEngine_GetEngineStatus(IInstallEngine2 *iface, DWORD *status) 808 { 809 InstallEngine *This = impl_from_IInstallEngine2(iface); 810 811 TRACE("(%p)->(%p)\n", This, status); 812 813 if (!status) 814 return E_FAIL; 815 816 *status = This->status; 817 return S_OK; 818 } 819 820 static HRESULT WINAPI InstallEngine_SetCifFile(IInstallEngine2 *iface, const char *cab_name, const char *cif_name) 821 { 822 InstallEngine *This = impl_from_IInstallEngine2(iface); 823 824 FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(cab_name), debugstr_a(cif_name)); 825 826 return E_NOTIMPL; 827 } 828 829 static HRESULT WINAPI InstallEngine_DownloadComponents(IInstallEngine2 *iface, DWORD flags) 830 { 831 InstallEngine *This = impl_from_IInstallEngine2(iface); 832 833 TRACE("(%p)->(%x)\n", This, flags); 834 835 /* The interface is not really threadsafe on windows, but we can at least prevent multiple installations */ 836 if (InterlockedCompareExchange((LONG *)&This->status, ENGINESTATUS_INSTALLING, ENGINESTATUS_READY) != ENGINESTATUS_READY) 837 return E_FAIL; 838 839 if (This->callback) 840 IInstallEngineCallback_OnEngineStatusChange(This->callback, ENGINESTATUS_INSTALLING, 0); 841 842 return start_installation(This, OP_DOWNLOAD, flags); 843 } 844 845 static HRESULT WINAPI InstallEngine_InstallComponents(IInstallEngine2 *iface, DWORD flags) 846 { 847 InstallEngine *This = impl_from_IInstallEngine2(iface); 848 849 FIXME("(%p)->(%x): stub\n", This, flags); 850 851 return E_NOTIMPL; 852 } 853 854 static HRESULT WINAPI InstallEngine_EnumInstallIDs(IInstallEngine2 *iface, UINT index, char **id) 855 { 856 InstallEngine *This = impl_from_IInstallEngine2(iface); 857 858 FIXME("(%p)->(%u %p): stub\n", This, index, id); 859 860 return E_NOTIMPL; 861 } 862 863 static HRESULT WINAPI InstallEngine_EnumDownloadIDs(IInstallEngine2 *iface, UINT index, char **id) 864 { 865 InstallEngine *This = impl_from_IInstallEngine2(iface); 866 IEnumCifComponents *enum_components; 867 ICifComponent *comp; 868 HRESULT hr; 869 870 TRACE("(%p)->(%u %p)\n", This, index, id); 871 872 if (!This->icif || !id) 873 return E_FAIL; 874 875 hr = ICifFile_EnumComponents(This->icif, &enum_components, 0, NULL); 876 if (FAILED(hr)) return hr; 877 878 for (;;) 879 { 880 hr = IEnumCifComponents_Next(enum_components, &comp); 881 if (FAILED(hr)) goto done; 882 883 if (ICifComponent_GetInstallQueueState(comp) != ActionInstall) 884 continue; 885 886 if (ICifComponent_IsComponentDownloaded(comp) != S_FALSE) 887 continue; 888 889 if (index == 0) 890 { 891 char *id_src = component_get_id(comp); 892 *id = CoTaskMemAlloc(strlen(id_src) + 1); 893 894 if (*id) 895 strcpy(*id, id_src); 896 else 897 hr = E_OUTOFMEMORY; 898 goto done; 899 } 900 901 index--; 902 } 903 904 done: 905 IEnumCifComponents_Release(enum_components); 906 return hr; 907 } 908 909 static HRESULT WINAPI InstallEngine_IsComponentInstalled(IInstallEngine2 *iface, const char *id, DWORD *status) 910 { 911 InstallEngine *This = impl_from_IInstallEngine2(iface); 912 913 FIXME("(%p)->(%s %p): stub\n", This, debugstr_a(id), status); 914 915 return E_NOTIMPL; 916 } 917 918 static HRESULT WINAPI InstallEngine_RegisterInstallEngineCallback(IInstallEngine2 *iface, IInstallEngineCallback *callback) 919 { 920 InstallEngine *This = impl_from_IInstallEngine2(iface); 921 922 TRACE("(%p)->(%p)\n", This, callback); 923 924 This->callback = callback; 925 return S_OK; 926 } 927 928 static HRESULT WINAPI InstallEngine_UnregisterInstallEngineCallback(IInstallEngine2 *iface) 929 { 930 InstallEngine *This = impl_from_IInstallEngine2(iface); 931 932 TRACE("(%p)\n", This); 933 934 This->callback = NULL; 935 return S_OK; 936 } 937 938 static HRESULT WINAPI InstallEngine_SetAction(IInstallEngine2 *iface, const char *id, DWORD action, DWORD priority) 939 { 940 InstallEngine *This = impl_from_IInstallEngine2(iface); 941 ICifComponent *comp; 942 HRESULT hr; 943 944 TRACE("(%p)->(%s %u %u)\n", This, debugstr_a(id), action, priority); 945 946 if (!This->icif) 947 return E_FAIL; /* FIXME: check error code */ 948 949 hr = ICifFile_FindComponent(This->icif, id, &comp); 950 if (FAILED(hr)) return hr; 951 952 hr = ICifComponent_SetInstallQueueState(comp, action); 953 if (FAILED(hr)) return hr; 954 955 hr = ICifComponent_SetCurrentPriority(comp, priority); 956 return hr; 957 } 958 959 static HRESULT WINAPI InstallEngine_GetSizes(IInstallEngine2 *iface, const char *id, COMPONENT_SIZES *sizes) 960 { 961 InstallEngine *This = impl_from_IInstallEngine2(iface); 962 963 FIXME("(%p)->(%s %p): stub\n", This, debugstr_a(id), sizes); 964 965 return E_NOTIMPL; 966 } 967 968 static HRESULT WINAPI InstallEngine_LaunchExtraCommand(IInstallEngine2 *iface, const char *inf_name, const char *section) 969 { 970 InstallEngine *This = impl_from_IInstallEngine2(iface); 971 972 FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(inf_name), debugstr_a(section)); 973 974 return E_NOTIMPL; 975 } 976 977 static HRESULT WINAPI InstallEngine_GetDisplayName(IInstallEngine2 *iface, const char *id, const char *name) 978 { 979 InstallEngine *This = impl_from_IInstallEngine2(iface); 980 981 FIXME("(%p)->(%s %s): stub\n", This, debugstr_a(id), debugstr_a(name)); 982 983 return E_NOTIMPL; 984 } 985 986 static HRESULT WINAPI InstallEngine_SetBaseUrl(IInstallEngine2 *iface, const char *base_name) 987 { 988 InstallEngine *This = impl_from_IInstallEngine2(iface); 989 990 TRACE("(%p)->(%s)\n", This, debugstr_a(base_name)); 991 992 if (This->baseurl) 993 heap_free(This->baseurl); 994 995 This->baseurl = strdupA(base_name); 996 return This->baseurl ? S_OK : E_OUTOFMEMORY; 997 } 998 999 static HRESULT WINAPI InstallEngine_SetDownloadDir(IInstallEngine2 *iface, const char *download_dir) 1000 { 1001 InstallEngine *This = impl_from_IInstallEngine2(iface); 1002 1003 TRACE("(%p)->(%s)\n", This, debugstr_a(download_dir)); 1004 1005 if (This->downloaddir) 1006 heap_free(This->downloaddir); 1007 1008 This->downloaddir = strdupA(download_dir); 1009 return This->downloaddir ? S_OK : E_OUTOFMEMORY; 1010 } 1011 1012 static HRESULT WINAPI InstallEngine_SetInstallDrive(IInstallEngine2 *iface, char drive) 1013 { 1014 InstallEngine *This = impl_from_IInstallEngine2(iface); 1015 1016 FIXME("(%p)->(%c): stub\n", This, drive); 1017 1018 return E_NOTIMPL; 1019 } 1020 1021 static HRESULT WINAPI InstallEngine_SetInstallOptions(IInstallEngine2 *iface, DWORD flags) 1022 { 1023 InstallEngine *This = impl_from_IInstallEngine2(iface); 1024 1025 FIXME("(%p)->(%x): stub\n", This, flags); 1026 1027 return E_NOTIMPL; 1028 } 1029 1030 static HRESULT WINAPI InstallEngine_SetHWND(IInstallEngine2 *iface, HWND hwnd) 1031 { 1032 InstallEngine *This = impl_from_IInstallEngine2(iface); 1033 1034 FIXME("(%p)->(%p): stub\n", This, hwnd); 1035 1036 return E_NOTIMPL; 1037 } 1038 1039 static HRESULT WINAPI InstallEngine_SetIStream(IInstallEngine2 *iface, IStream *stream) 1040 { 1041 InstallEngine *This = impl_from_IInstallEngine2(iface); 1042 1043 FIXME("(%p)->(%p): stub\n", This, stream); 1044 1045 return E_NOTIMPL; 1046 } 1047 1048 static HRESULT WINAPI InstallEngine_Abort(IInstallEngine2 *iface, DWORD flags) 1049 { 1050 InstallEngine *This = impl_from_IInstallEngine2(iface); 1051 1052 FIXME("(%p)->(%x): stub\n", This, flags); 1053 1054 return E_NOTIMPL; 1055 } 1056 1057 static HRESULT WINAPI InstallEngine_Suspend(IInstallEngine2 *iface) 1058 { 1059 InstallEngine *This = impl_from_IInstallEngine2(iface); 1060 1061 FIXME("(%p): stub\n", This); 1062 1063 return E_NOTIMPL; 1064 } 1065 1066 static HRESULT WINAPI InstallEngine_Resume(IInstallEngine2 *iface) 1067 { 1068 InstallEngine *This = impl_from_IInstallEngine2(iface); 1069 1070 FIXME("(%p): stub\n", This); 1071 1072 return E_NOTIMPL; 1073 } 1074 1075 static HRESULT WINAPI InstallEngine2_SetLocalCif(IInstallEngine2 *iface, const char *cif) 1076 { 1077 InstallEngine *This = impl_from_IInstallEngine2(iface); 1078 HRESULT hr; 1079 1080 TRACE("(%p)->(%s)\n", This, debugstr_a(cif)); 1081 1082 if (This->icif) 1083 ICifFile_Release(This->icif); 1084 1085 set_status(This, ENGINESTATUS_LOADING); 1086 1087 hr = GetICifFileFromFile(&This->icif, cif); 1088 if (SUCCEEDED(hr)) 1089 set_status(This, ENGINESTATUS_READY); 1090 else 1091 { 1092 This->icif = NULL; 1093 set_status(This, ENGINESTATUS_NOTREADY); 1094 } 1095 return hr; 1096 } 1097 1098 static HRESULT WINAPI InstallEngine2_GetICifFile(IInstallEngine2 *iface, ICifFile **cif_file) 1099 { 1100 InstallEngine *This = impl_from_IInstallEngine2(iface); 1101 1102 TRACE("(%p)->(%p)\n", This, cif_file); 1103 1104 if (!This->icif || !cif_file) 1105 return E_FAIL; 1106 1107 ICifFile_AddRef(This->icif); 1108 *cif_file = This->icif; 1109 return S_OK; 1110 } 1111 1112 static const IInstallEngine2Vtbl InstallEngine2Vtbl = 1113 { 1114 InstallEngine_QueryInterface, 1115 InstallEngine_AddRef, 1116 InstallEngine_Release, 1117 InstallEngine_GetEngineStatus, 1118 InstallEngine_SetCifFile, 1119 InstallEngine_DownloadComponents, 1120 InstallEngine_InstallComponents, 1121 InstallEngine_EnumInstallIDs, 1122 InstallEngine_EnumDownloadIDs, 1123 InstallEngine_IsComponentInstalled, 1124 InstallEngine_RegisterInstallEngineCallback, 1125 InstallEngine_UnregisterInstallEngineCallback, 1126 InstallEngine_SetAction, 1127 InstallEngine_GetSizes, 1128 InstallEngine_LaunchExtraCommand, 1129 InstallEngine_GetDisplayName, 1130 InstallEngine_SetBaseUrl, 1131 InstallEngine_SetDownloadDir, 1132 InstallEngine_SetInstallDrive, 1133 InstallEngine_SetInstallOptions, 1134 InstallEngine_SetHWND, 1135 InstallEngine_SetIStream, 1136 InstallEngine_Abort, 1137 InstallEngine_Suspend, 1138 InstallEngine_Resume, 1139 InstallEngine2_SetLocalCif, 1140 InstallEngine2_GetICifFile 1141 }; 1142 1143 static HRESULT WINAPI InstallEngineTiming_QueryInterface(IInstallEngineTiming *iface, REFIID riid, void **ppv) 1144 { 1145 InstallEngine *This = impl_from_IInstallEngineTiming(iface); 1146 return IInstallEngine2_QueryInterface(&This->IInstallEngine2_iface, riid, ppv); 1147 } 1148 1149 static ULONG WINAPI InstallEngineTiming_AddRef(IInstallEngineTiming *iface) 1150 { 1151 InstallEngine *This = impl_from_IInstallEngineTiming(iface); 1152 return IInstallEngine2_AddRef(&This->IInstallEngine2_iface); 1153 } 1154 1155 static ULONG WINAPI InstallEngineTiming_Release(IInstallEngineTiming *iface) 1156 { 1157 InstallEngine *This = impl_from_IInstallEngineTiming(iface); 1158 return IInstallEngine2_Release(&This->IInstallEngine2_iface); 1159 } 1160 1161 static HRESULT WINAPI InstallEngineTiming_GetRates(IInstallEngineTiming *iface, DWORD *download, DWORD *install) 1162 { 1163 InstallEngine *This = impl_from_IInstallEngineTiming(iface); 1164 1165 FIXME("(%p)->(%p, %p): stub\n", This, download, install); 1166 1167 *download = 0; 1168 *install = 0; 1169 1170 return S_OK; 1171 } 1172 1173 static HRESULT WINAPI InstallEngineTiming_GetInstallProgress(IInstallEngineTiming *iface, INSTALLPROGRESS *progress) 1174 { 1175 InstallEngine *This = impl_from_IInstallEngineTiming(iface); 1176 ULONGLONG elapsed; 1177 static int once; 1178 1179 if (!once) 1180 FIXME("(%p)->(%p): semi-stub\n", This, progress); 1181 else 1182 TRACE("(%p)->(%p): semi-stub\n", This, progress); 1183 1184 progress->dwDownloadKBRemaining = max(This->thread.download_size, This->thread.downloaded_kb) - This->thread.downloaded_kb; 1185 1186 elapsed = GetTickCount64() - This->thread.download_start; 1187 if (This->thread.download_start && This->thread.downloaded_kb && elapsed > 100) 1188 progress->dwDownloadSecsRemaining = (progress->dwDownloadKBRemaining * elapsed) / (This->thread.downloaded_kb * 1000); 1189 else 1190 progress->dwDownloadSecsRemaining = -1; 1191 1192 progress->dwInstallKBRemaining = 0; 1193 progress->dwInstallSecsRemaining = -1; 1194 1195 return S_OK; 1196 } 1197 1198 static const IInstallEngineTimingVtbl InstallEngineTimingVtbl = 1199 { 1200 InstallEngineTiming_QueryInterface, 1201 InstallEngineTiming_AddRef, 1202 InstallEngineTiming_Release, 1203 InstallEngineTiming_GetRates, 1204 InstallEngineTiming_GetInstallProgress, 1205 }; 1206 1207 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) 1208 { 1209 *ppv = NULL; 1210 1211 if(IsEqualGUID(&IID_IUnknown, riid)) { 1212 TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv); 1213 *ppv = iface; 1214 }else if(IsEqualGUID(&IID_IClassFactory, riid)) { 1215 TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv); 1216 *ppv = iface; 1217 } 1218 1219 if(*ppv) { 1220 IUnknown_AddRef((IUnknown*)*ppv); 1221 return S_OK; 1222 } 1223 1224 FIXME("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppv); 1225 return E_NOINTERFACE; 1226 } 1227 1228 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) 1229 { 1230 return 2; 1231 } 1232 1233 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) 1234 { 1235 return 1; 1236 } 1237 1238 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock) 1239 { 1240 return S_OK; 1241 } 1242 1243 static HRESULT WINAPI InstallEngineCF_CreateInstance(IClassFactory *iface, IUnknown *outer, 1244 REFIID riid, void **ppv) 1245 { 1246 InstallEngine *engine; 1247 HRESULT hres; 1248 1249 TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv); 1250 1251 engine = heap_alloc_zero(sizeof(*engine)); 1252 if(!engine) 1253 return E_OUTOFMEMORY; 1254 1255 engine->IInstallEngine2_iface.lpVtbl = &InstallEngine2Vtbl; 1256 engine->IInstallEngineTiming_iface.lpVtbl = &InstallEngineTimingVtbl; 1257 engine->ref = 1; 1258 engine->status = ENGINESTATUS_NOTREADY; 1259 1260 hres = IInstallEngine2_QueryInterface(&engine->IInstallEngine2_iface, riid, ppv); 1261 IInstallEngine2_Release(&engine->IInstallEngine2_iface); 1262 return hres; 1263 } 1264 1265 static const IClassFactoryVtbl InstallEngineCFVtbl = { 1266 ClassFactory_QueryInterface, 1267 ClassFactory_AddRef, 1268 ClassFactory_Release, 1269 InstallEngineCF_CreateInstance, 1270 ClassFactory_LockServer 1271 }; 1272 1273 static IClassFactory InstallEngineCF = { &InstallEngineCFVtbl }; 1274 1275 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) 1276 { 1277 switch(fdwReason) 1278 { 1279 case DLL_WINE_PREATTACH: 1280 return FALSE; /* prefer native version */ 1281 case DLL_PROCESS_ATTACH: 1282 instance = hInstDLL; 1283 DisableThreadLibraryCalls(hInstDLL); 1284 break; 1285 } 1286 return TRUE; 1287 } 1288 1289 /*********************************************************************** 1290 * DllGetClassObject (INSENG.@) 1291 */ 1292 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv) 1293 { 1294 if(IsEqualGUID(rclsid, &CLSID_InstallEngine)) { 1295 TRACE("(CLSID_InstallEngine %s %p)\n", debugstr_guid(iid), ppv); 1296 return IClassFactory_QueryInterface(&InstallEngineCF, iid, ppv); 1297 } 1298 1299 FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(iid), ppv); 1300 return CLASS_E_CLASSNOTAVAILABLE; 1301 } 1302 1303 /*********************************************************************** 1304 * DllCanUnloadNow (INSENG.@) 1305 */ 1306 HRESULT WINAPI DllCanUnloadNow(void) 1307 { 1308 return S_FALSE; 1309 } 1310 1311 /*********************************************************************** 1312 * DllRegisterServer (INSENG.@) 1313 */ 1314 HRESULT WINAPI DllRegisterServer(void) 1315 { 1316 return __wine_register_resources( instance ); 1317 } 1318 1319 /*********************************************************************** 1320 * DllUnregisterServer (INSENG.@) 1321 */ 1322 HRESULT WINAPI DllUnregisterServer(void) 1323 { 1324 return __wine_unregister_resources( instance ); 1325 } 1326 1327 BOOL WINAPI CheckTrustEx( LPVOID a, LPVOID b, LPVOID c, LPVOID d, LPVOID e ) 1328 { 1329 FIXME("%p %p %p %p %p\n", a, b, c, d, e ); 1330 return TRUE; 1331 } 1332 1333 /*********************************************************************** 1334 * DllInstall (INSENG.@) 1335 */ 1336 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline) 1337 { 1338 FIXME("(%s, %s): stub\n", bInstall ? "TRUE" : "FALSE", debugstr_w(cmdline)); 1339 return S_OK; 1340 } 1341