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