[NTOSKRNL] Drop the useless Timestamp field
[reactos.git] / dll / win32 / inseng / inseng_main.c
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 }