xref: /reactos/dll/win32/inseng/inseng_main.c (revision 84ccccab)
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