xref: /reactos/dll/win32/urlmon/axinstall.c (revision 58588b76)
1 /*
2  * Copyright 2012 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #define OEMRESOURCE
20 
21 #include <assert.h>
22 
23 #include "urlmon_main.h"
24 #include "resource.h"
25 
26 #include "advpub.h"
27 #include "fdi.h"
28 
29 #include "wine/debug.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
32 
33 static const WCHAR ctxW[] = {'c','t','x',0};
34 static const WCHAR cab_extW[] = {'.','c','a','b',0};
35 static const WCHAR infW[] = {'i','n','f',0};
36 static const WCHAR dllW[] = {'d','l','l',0};
37 static const WCHAR ocxW[] = {'o','c','x',0};
38 
39 enum install_type {
40     INSTALL_UNKNOWN,
41     INSTALL_DLL,
42     INSTALL_INF
43 };
44 
45 typedef struct {
46     IUri *uri;
47     IBindStatusCallback *callback;
48     BOOL release_on_stop;
49     BOOL cancel;
50     WCHAR *install_file;
51     const WCHAR *cache_file;
52     const WCHAR *tmp_dir;
53     const WCHAR *file_name;
54     enum install_type install_type;
55     HWND hwnd;
56     int counter;
57     INT_PTR timer;
58 } install_ctx_t;
59 
60 static void release_install_ctx(install_ctx_t *ctx)
61 {
62     if(ctx->uri)
63         IUri_Release(ctx->uri);
64     if(ctx->callback)
65         IBindStatusCallback_Release(ctx->callback);
66     heap_free(ctx->install_file);
67     heap_free(ctx);
68 }
69 
70 static inline BOOL file_exists(const WCHAR *file_name)
71 {
72     return GetFileAttributesW(file_name) != INVALID_FILE_ATTRIBUTES;
73 }
74 
75 #ifdef __REACTOS__
76 
77 /* The following definitions were copied from dll/win32/advpack32/files.c */
78 
79 /* SESSION Operation */
80 #define EXTRACT_FILLFILELIST  0x00000001
81 #define EXTRACT_EXTRACTFILES  0x00000002
82 
83 struct FILELIST{
84     LPSTR FileName;
85     struct FILELIST *next;
86     BOOL DoExtract;
87 };
88 
89 typedef struct {
90     INT FileSize;
91     ERF Error;
92     struct FILELIST *FileList;
93     INT FileCount;
94     INT Operation;
95     CHAR Destination[MAX_PATH];
96     CHAR CurrentFile[MAX_PATH];
97     CHAR Reserved[MAX_PATH];
98     struct FILELIST *FilterList;
99 } SESSION;
100 
101 static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR);
102 
103 
104 /* The following functions were copied from dll/win32/advpack32/files.c
105    All unused arguments are removed */
106 
107 static void free_file_node(struct FILELIST *pNode)
108 {
109     HeapFree(GetProcessHeap(), 0, pNode->FileName);
110     HeapFree(GetProcessHeap(), 0, pNode);
111 }
112 
113 static void free_file_list(SESSION* session)
114 {
115     struct FILELIST *next, *curr = session->FileList;
116 
117     while (curr)
118     {
119         next = curr->next;
120         free_file_node(curr);
121         curr = next;
122     }
123 }
124 
125 HRESULT WINAPI Modified_ExtractFilesA(LPCSTR CabName, LPCSTR ExpandDir)
126 {
127     SESSION session;
128     HMODULE hCabinet;
129     HRESULT res = S_OK;
130     LPSTR szConvertedList = NULL;
131 
132     TRACE("(%s, %s)\n", debugstr_a(CabName), debugstr_a(ExpandDir));
133 
134     if (!CabName || !ExpandDir)
135         return E_INVALIDARG;
136 
137     if (GetFileAttributesA(ExpandDir) == INVALID_FILE_ATTRIBUTES)
138         return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
139 
140     hCabinet = LoadLibraryA("cabinet.dll");
141     if (!hCabinet)
142         return E_FAIL;
143 
144     ZeroMemory(&session, sizeof(SESSION));
145 
146     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
147     if (!pExtract)
148     {
149         res = E_FAIL;
150         goto done;
151     }
152 
153     lstrcpyA(session.Destination, ExpandDir);
154 
155     session.Operation |= (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES);
156     res = pExtract(&session, CabName);
157 
158 done:
159     free_file_list(&session);
160     FreeLibrary(hCabinet);
161     HeapFree(GetProcessHeap(), 0, szConvertedList);
162 
163     return res;
164 }
165 
166 
167 
168 HRESULT WINAPI Modified_ExtractFilesW(LPCWSTR CabName, LPCWSTR ExpandDir)
169 {
170     char *cab_name = NULL, *expand_dir = NULL;
171     HRESULT hres = S_OK;
172 
173     TRACE("(%s, %s, %d)\n", debugstr_w(CabName), debugstr_w(ExpandDir));
174 
175     if(CabName) {
176         cab_name = heap_strdupWtoA(CabName);
177         if(!cab_name)
178             return E_OUTOFMEMORY;
179     }
180 
181     if(ExpandDir) {
182         expand_dir = heap_strdupWtoA(ExpandDir);
183         if(!expand_dir)
184             hres = E_OUTOFMEMORY;
185     }
186 
187     /* cabinet.dll, which does the real job of extracting files, doesn't have UNICODE API,
188     so we need W->A conversion at some point anyway. */
189     if(SUCCEEDED(hres))
190         hres = Modified_ExtractFilesA(cab_name, expand_dir);
191 
192     heap_free(cab_name);
193     heap_free(expand_dir);
194     return hres;
195 }
196 
197 #endif
198 
199 
200 static HRESULT extract_cab_file(install_ctx_t *ctx)
201 {
202     size_t path_len, file_len;
203     WCHAR *ptr;
204     HRESULT hres;
205 
206 #ifdef __REACTOS__
207     hres = Modified_ExtractFilesW(ctx->cache_file, ctx->tmp_dir);
208 #else
209     hres = ExtractFilesW(ctx->cache_file, ctx->tmp_dir, 0, NULL, NULL, 0);
210 #endif
211     if(FAILED(hres)) {
212         WARN("ExtractFilesW failed: %08x\n", hres);
213         return hres;
214     }
215 
216     path_len = strlenW(ctx->tmp_dir);
217     file_len = strlenW(ctx->file_name);
218     ctx->install_file = heap_alloc((path_len+file_len+2)*sizeof(WCHAR));
219     if(!ctx->install_file)
220         return E_OUTOFMEMORY;
221 
222     memcpy(ctx->install_file, ctx->tmp_dir, path_len*sizeof(WCHAR));
223     ctx->install_file[path_len] = '\\';
224     memcpy(ctx->install_file+path_len+1, ctx->file_name, (file_len+1)*sizeof(WCHAR));
225 
226     /* NOTE: Assume that file_name contains ".cab" extension */
227     ptr = ctx->install_file+path_len+1+file_len-3;
228 
229     memcpy(ptr, infW, sizeof(infW));
230     if(file_exists(ctx->install_file)) {
231         ctx->install_type = INSTALL_INF;
232         return S_OK;
233     }
234 
235     memcpy(ptr, dllW, sizeof(dllW));
236     if(file_exists(ctx->install_file)) {
237         ctx->install_type = INSTALL_DLL;
238         return S_OK;
239     }
240 
241     memcpy(ptr, ocxW, sizeof(ocxW));
242     if(file_exists(ctx->install_file)) {
243         ctx->install_type = INSTALL_DLL;
244         return S_OK;
245     }
246 
247     FIXME("No known install file\n");
248     return E_NOTIMPL;
249 }
250 
251 static HRESULT setup_dll(install_ctx_t *ctx)
252 {
253     HMODULE module;
254     HRESULT hres;
255 
256     HRESULT (WINAPI *reg_func)(void);
257 
258     module = LoadLibraryW(ctx->install_file);
259     if(!module)
260         return E_FAIL;
261 
262     reg_func = (void*)GetProcAddress(module, "DllRegisterServer");
263     if(reg_func) {
264         hres = reg_func();
265     }else {
266         WARN("no DllRegisterServer function\n");
267         hres = E_FAIL;
268     }
269 
270     FreeLibrary(module);
271     return hres;
272 }
273 
274 static void expand_command(install_ctx_t *ctx, const WCHAR *cmd, WCHAR *buf, size_t *size)
275 {
276     const WCHAR *ptr = cmd, *prev_ptr = cmd;
277     size_t len = 0, len2;
278 
279     static const WCHAR expand_dirW[] = {'%','E','X','T','R','A','C','T','_','D','I','R','%'};
280 
281     while((ptr = strchrW(ptr, '%'))) {
282         if(buf)
283             memcpy(buf+len, prev_ptr, ptr-prev_ptr);
284         len += ptr-prev_ptr;
285 
286         if(!strncmpiW(ptr, expand_dirW, ARRAY_SIZE(expand_dirW))) {
287             len2 = strlenW(ctx->tmp_dir);
288             if(buf)
289                 memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR));
290             len += len2;
291             ptr += ARRAY_SIZE(expand_dirW);
292         }else {
293             FIXME("Can't expand %s\n", debugstr_w(ptr));
294             if(buf)
295                 buf[len] = '%';
296             len++;
297             ptr++;
298         }
299 
300         prev_ptr = ptr;
301     }
302 
303     if(buf)
304         strcpyW(buf+len, prev_ptr);
305     *size = len + strlenW(prev_ptr) + 1;
306 }
307 
308 static HRESULT process_hook_section(install_ctx_t *ctx, const WCHAR *sect_name)
309 {
310     WCHAR buf[2048], val[2*MAX_PATH];
311     const WCHAR *key;
312     DWORD len;
313     HRESULT hres;
314 
315     static const WCHAR runW[] = {'r','u','n',0};
316 
317     len = GetPrivateProfileStringW(sect_name, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
318     if(!len)
319         return S_OK;
320 
321     for(key = buf; *key; key += strlenW(key)+1) {
322         if(!strcmpiW(key, runW)) {
323             WCHAR *cmd;
324             size_t size;
325 
326             len = GetPrivateProfileStringW(sect_name, runW, NULL, val, ARRAY_SIZE(val), ctx->install_file);
327 
328             TRACE("Run %s\n", debugstr_w(val));
329 
330             expand_command(ctx, val, NULL, &size);
331 
332             cmd = heap_alloc(size*sizeof(WCHAR));
333             if(!cmd)
334                 heap_free(cmd);
335 
336             expand_command(ctx, val, cmd, &size);
337             hres = RunSetupCommandW(ctx->hwnd, cmd, NULL, ctx->tmp_dir, NULL, NULL, 0, NULL);
338             heap_free(cmd);
339             if(FAILED(hres))
340                 return hres;
341         }else {
342             FIXME("Unsupported hook %s\n", debugstr_w(key));
343             return E_NOTIMPL;
344         }
345     }
346 
347     return S_OK;
348 }
349 
350 static HRESULT install_inf_file(install_ctx_t *ctx)
351 {
352     WCHAR buf[2048], sect_name[128];
353     BOOL default_install = TRUE;
354     const WCHAR *key;
355     DWORD len;
356     HRESULT hres;
357 
358     static const WCHAR setup_hooksW[] = {'S','e','t','u','p',' ','H','o','o','k','s',0};
359     static const WCHAR add_codeW[] = {'A','d','d','.','C','o','d','e',0};
360 
361     len = GetPrivateProfileStringW(setup_hooksW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
362     if(len) {
363         default_install = FALSE;
364 
365         for(key = buf; *key; key += strlenW(key)+1) {
366             TRACE("[Setup Hooks] key: %s\n", debugstr_w(key));
367 
368             len = GetPrivateProfileStringW(setup_hooksW, key, NULL, sect_name, ARRAY_SIZE(sect_name),
369                     ctx->install_file);
370             if(!len) {
371                 WARN("Could not get key value\n");
372                 return E_FAIL;
373             }
374 
375             hres = process_hook_section(ctx, sect_name);
376             if(FAILED(hres))
377                 return hres;
378         }
379     }
380 
381     len = GetPrivateProfileStringW(add_codeW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
382     if(len) {
383         default_install = FALSE;
384 
385         for(key = buf; *key; key += strlenW(key)+1) {
386             TRACE("[Add.Code] key: %s\n", debugstr_w(key));
387 
388             len = GetPrivateProfileStringW(add_codeW, key, NULL, sect_name, ARRAY_SIZE(sect_name),
389                     ctx->install_file);
390             if(!len) {
391                 WARN("Could not get key value\n");
392                 return E_FAIL;
393             }
394 
395             hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, sect_name,
396                     ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL);
397             if(FAILED(hres)) {
398                 WARN("RunSetupCommandW failed: %08x\n", hres);
399                 return hres;
400             }
401         }
402     }
403 
404     if(default_install) {
405         hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL);
406         if(FAILED(hres)) {
407             WARN("RunSetupCommandW failed: %08x\n", hres);
408             return hres;
409         }
410     }
411 
412     return S_OK;
413 }
414 
415 static HRESULT install_cab_file(install_ctx_t *ctx)
416 {
417     WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH];
418     BOOL res = FALSE, leave_temp = FALSE;
419     DWORD i;
420     HRESULT hres;
421 
422     GetTempPathW(ARRAY_SIZE(tmp_path), tmp_path);
423 
424     for(i=0; !res && i < 100; i++) {
425         GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir);
426         res = CreateDirectoryW(tmp_dir, NULL);
427     }
428     if(!res)
429         return E_FAIL;
430 
431     ctx->tmp_dir = tmp_dir;
432 
433     TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir));
434 
435     hres = extract_cab_file(ctx);
436     if(SUCCEEDED(hres)) {
437         if(ctx->callback)
438             IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file);
439 
440         switch(ctx->install_type) {
441         case INSTALL_INF:
442             hres = install_inf_file(ctx);
443             break;
444         case INSTALL_DLL:
445             FIXME("Installing DLL, registering in temporary location\n");
446             hres = setup_dll(ctx);
447             if(SUCCEEDED(hres))
448                 leave_temp = TRUE;
449             break;
450         default:
451             assert(0);
452         }
453     }
454 
455     if(!leave_temp)
456         RemoveDirectoryW(ctx->tmp_dir);
457     return hres;
458 }
459 
460 static void update_counter(install_ctx_t *ctx, HWND hwnd)
461 {
462     WCHAR text[100];
463 
464     if(--ctx->counter <= 0) {
465         HWND button_hwnd;
466 
467         KillTimer(hwnd, ctx->timer);
468         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, ARRAY_SIZE(text));
469 
470         button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN);
471         EnableWindow(button_hwnd, TRUE);
472     }else {
473         WCHAR buf[100];
474         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, ARRAY_SIZE(buf));
475         sprintfW(text, buf, ctx->counter);
476     }
477 
478     SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text);
479 }
480 
481 static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx)
482 {
483     BSTR display_uri;
484     HRESULT hres;
485 
486     if(!SetPropW(hwnd, ctxW, ctx))
487         return FALSE;
488 
489     hres = IUri_GetDisplayUri(ctx->uri, &display_uri);
490     if(FAILED(hres))
491         return FALSE;
492 
493     SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri);
494     SysFreeString(display_uri);
495 
496     SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON,
497             (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0);
498 
499     ctx->counter = 4;
500     update_counter(ctx, hwnd);
501     ctx->timer = SetTimer(hwnd, 1, 1000, NULL);
502     return TRUE;
503 }
504 
505 static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
506 {
507     switch(msg) {
508     case WM_INITDIALOG: {
509         if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam))
510             EndDialog(hwnd, 0);
511         return TRUE;
512     }
513     case WM_COMMAND:
514         switch(wparam) {
515         case ID_AXINSTALL_INSTALL_BTN: {
516             install_ctx_t *ctx = GetPropW(hwnd, ctxW);
517             if(ctx)
518                 ctx->cancel = FALSE;
519             EndDialog(hwnd, 0);
520             return FALSE;
521         }
522         case IDCANCEL:
523             EndDialog(hwnd, 0);
524             return FALSE;
525         }
526     case WM_TIMER:
527         update_counter(GetPropW(hwnd, ctxW), hwnd);
528         return TRUE;
529     }
530 
531     return FALSE;
532 }
533 
534 static BOOL install_warning(install_ctx_t *ctx)
535 {
536     IWindowForBindingUI *window_iface;
537     HWND parent_hwnd = NULL;
538     HRESULT hres;
539 
540     if(!ctx->callback) {
541         FIXME("no callback\n");
542         return FALSE;
543     }
544 
545     hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface);
546     if(FAILED(hres))
547         return FALSE;
548 
549     hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd);
550     IWindowForBindingUI_Release(window_iface);
551     if(FAILED(hres))
552         return FALSE;
553 
554     ctx->cancel = TRUE;
555     DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx);
556     return !ctx->cancel;
557 }
558 
559 static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file)
560 {
561     BSTR path;
562     HRESULT hres;
563 
564     TRACE("%s\n", debugstr_w(cache_file));
565 
566     ctx->cache_file = cache_file;
567 
568     if(!install_warning(ctx)) {
569         TRACE("Installation cancelled\n");
570         return S_OK;
571     }
572 
573     hres = IUri_GetPath(ctx->uri, &path);
574     if(SUCCEEDED(hres)) {
575         const WCHAR *ptr, *ptr2, *ext;
576 
577         ptr = strrchrW(path, '/');
578         if(!ptr)
579             ptr = path;
580         else
581             ptr++;
582 
583         ptr2 = strrchrW(ptr, '\\');
584         if(ptr2)
585             ptr = ptr2+1;
586 
587         ctx->file_name = ptr;
588         ext = strrchrW(ptr, '.');
589         if(!ext)
590             ext = ptr;
591 
592         if(!strcmpiW(ext, cab_extW)) {
593             hres = install_cab_file(ctx);
594         }else {
595             FIXME("Unsupported extension %s\n", debugstr_w(ext));
596             hres = E_NOTIMPL;
597         }
598         SysFreeString(path);
599     }
600 
601     return hres;
602 }
603 
604 static void failure_msgbox(install_ctx_t *ctx, HRESULT hres)
605 {
606     WCHAR buf[1024], fmt[1024];
607 
608     LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, ARRAY_SIZE(fmt));
609     sprintfW(buf, fmt, hres);
610     MessageBoxW(ctx->hwnd, buf, NULL, MB_OK);
611 }
612 
613 static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str)
614 {
615     install_ctx_t *install_ctx = ctx;
616 
617     TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str));
618 
619     if(hresult == S_OK) {
620         hresult = install_file(install_ctx, cache_file);
621         if(FAILED(hresult))
622             failure_msgbox(ctx, hresult);
623     }
624 
625     if(install_ctx->callback)
626         IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str);
627 
628     if(install_ctx->release_on_stop)
629         release_install_ctx(install_ctx);
630     return S_OK;
631 }
632 
633 /***********************************************************************
634  *           AsyncInstallDistributionUnit (URLMON.@)
635  */
636 HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt,
637         DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags)
638 {
639     install_ctx_t *ctx;
640     HRESULT hres;
641 
642     TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt),
643           dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags);
644 
645     if(szDistUnit || szTYPE || szExt)
646         FIXME("Unsupported arguments\n");
647 
648     ctx = heap_alloc_zero(sizeof(*ctx));
649     if(!ctx)
650         return E_OUTOFMEMORY;
651 
652     hres = CreateUri(szURL, 0, 0, &ctx->uri);
653     if(FAILED(hres)) {
654         heap_free(ctx);
655         return E_OUTOFMEMORY;
656     }
657 
658     ctx->callback = bsc_from_bctx(pbc);
659 
660     hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback);
661     if(hres == MK_S_ASYNCHRONOUS)
662         ctx->release_on_stop = TRUE;
663     else
664         release_install_ctx(ctx);
665 
666     return hres;
667 }
668