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