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