xref: /reactos/dll/win32/urlmon/axinstall.c (revision e5873161)
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 static HRESULT extract_cab_file(install_ctx_t *ctx)
76 {
77     size_t path_len, file_len;
78     WCHAR *ptr;
79     HRESULT hres;
80 
81     hres = ExtractFilesW(ctx->cache_file, ctx->tmp_dir, 0, NULL, NULL, 0);
82     if(FAILED(hres)) {
83         WARN("ExtractFilesW failed: %08x\n", hres);
84         return hres;
85     }
86 
87     path_len = strlenW(ctx->tmp_dir);
88     file_len = strlenW(ctx->file_name);
89     ctx->install_file = heap_alloc((path_len+file_len+2)*sizeof(WCHAR));
90     if(!ctx->install_file)
91         return E_OUTOFMEMORY;
92 
93     memcpy(ctx->install_file, ctx->tmp_dir, path_len*sizeof(WCHAR));
94     ctx->install_file[path_len] = '\\';
95     memcpy(ctx->install_file+path_len+1, ctx->file_name, (file_len+1)*sizeof(WCHAR));
96 
97     /* NOTE: Assume that file_name contains ".cab" extension */
98     ptr = ctx->install_file+path_len+1+file_len-3;
99 
100     memcpy(ptr, infW, sizeof(infW));
101     if(file_exists(ctx->install_file)) {
102         ctx->install_type = INSTALL_INF;
103         return S_OK;
104     }
105 
106     memcpy(ptr, dllW, sizeof(dllW));
107     if(file_exists(ctx->install_file)) {
108         ctx->install_type = INSTALL_DLL;
109         return S_OK;
110     }
111 
112     memcpy(ptr, ocxW, sizeof(ocxW));
113     if(file_exists(ctx->install_file)) {
114         ctx->install_type = INSTALL_DLL;
115         return S_OK;
116     }
117 
118     FIXME("No known install file\n");
119     return E_NOTIMPL;
120 }
121 
122 static HRESULT setup_dll(install_ctx_t *ctx)
123 {
124     HMODULE module;
125     HRESULT hres;
126 
127     HRESULT (WINAPI *reg_func)(void);
128 
129     module = LoadLibraryW(ctx->install_file);
130     if(!module)
131         return E_FAIL;
132 
133     reg_func = (void*)GetProcAddress(module, "DllRegisterServer");
134     if(reg_func) {
135         hres = reg_func();
136     }else {
137         WARN("no DllRegisterServer function\n");
138         hres = E_FAIL;
139     }
140 
141     FreeLibrary(module);
142     return hres;
143 }
144 
145 static void expand_command(install_ctx_t *ctx, const WCHAR *cmd, WCHAR *buf, size_t *size)
146 {
147     const WCHAR *ptr = cmd, *prev_ptr = cmd;
148     size_t len = 0, len2;
149 
150     static const WCHAR expand_dirW[] = {'%','E','X','T','R','A','C','T','_','D','I','R','%'};
151 
152     while((ptr = strchrW(ptr, '%'))) {
153         if(buf)
154             memcpy(buf+len, prev_ptr, ptr-prev_ptr);
155         len += ptr-prev_ptr;
156 
157         if(!strncmpiW(ptr, expand_dirW, ARRAY_SIZE(expand_dirW))) {
158             len2 = strlenW(ctx->tmp_dir);
159             if(buf)
160                 memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR));
161             len += len2;
162             ptr += ARRAY_SIZE(expand_dirW);
163         }else {
164             FIXME("Can't expand %s\n", debugstr_w(ptr));
165             if(buf)
166                 buf[len] = '%';
167             len++;
168             ptr++;
169         }
170 
171         prev_ptr = ptr;
172     }
173 
174     if(buf)
175         strcpyW(buf+len, prev_ptr);
176     *size = len + strlenW(prev_ptr) + 1;
177 }
178 
179 static HRESULT process_hook_section(install_ctx_t *ctx, const WCHAR *sect_name)
180 {
181     WCHAR buf[2048], val[2*MAX_PATH];
182     const WCHAR *key;
183     DWORD len;
184     HRESULT hres;
185 
186     static const WCHAR runW[] = {'r','u','n',0};
187 
188     len = GetPrivateProfileStringW(sect_name, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
189     if(!len)
190         return S_OK;
191 
192     for(key = buf; *key; key += strlenW(key)+1) {
193         if(!strcmpiW(key, runW)) {
194             WCHAR *cmd;
195             size_t size;
196 
197             len = GetPrivateProfileStringW(sect_name, runW, NULL, val, ARRAY_SIZE(val), ctx->install_file);
198 
199             TRACE("Run %s\n", debugstr_w(val));
200 
201             expand_command(ctx, val, NULL, &size);
202 
203             cmd = heap_alloc(size*sizeof(WCHAR));
204             if(!cmd)
205                 heap_free(cmd);
206 
207             expand_command(ctx, val, cmd, &size);
208             hres = RunSetupCommandW(ctx->hwnd, cmd, NULL, ctx->tmp_dir, NULL, NULL, 0, NULL);
209             heap_free(cmd);
210             if(FAILED(hres))
211                 return hres;
212         }else {
213             FIXME("Unsupported hook %s\n", debugstr_w(key));
214             return E_NOTIMPL;
215         }
216     }
217 
218     return S_OK;
219 }
220 
221 static HRESULT install_inf_file(install_ctx_t *ctx)
222 {
223     WCHAR buf[2048], sect_name[128];
224     BOOL default_install = TRUE;
225     const WCHAR *key;
226     DWORD len;
227     HRESULT hres;
228 
229     static const WCHAR setup_hooksW[] = {'S','e','t','u','p',' ','H','o','o','k','s',0};
230     static const WCHAR add_codeW[] = {'A','d','d','.','C','o','d','e',0};
231 
232     len = GetPrivateProfileStringW(setup_hooksW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
233     if(len) {
234         default_install = FALSE;
235 
236         for(key = buf; *key; key += strlenW(key)+1) {
237             TRACE("[Setup Hooks] key: %s\n", debugstr_w(key));
238 
239             len = GetPrivateProfileStringW(setup_hooksW, key, NULL, sect_name, ARRAY_SIZE(sect_name),
240                     ctx->install_file);
241             if(!len) {
242                 WARN("Could not get key value\n");
243                 return E_FAIL;
244             }
245 
246             hres = process_hook_section(ctx, sect_name);
247             if(FAILED(hres))
248                 return hres;
249         }
250     }
251 
252     len = GetPrivateProfileStringW(add_codeW, NULL, NULL, buf, ARRAY_SIZE(buf), ctx->install_file);
253     if(len) {
254         default_install = FALSE;
255 
256         for(key = buf; *key; key += strlenW(key)+1) {
257             TRACE("[Add.Code] key: %s\n", debugstr_w(key));
258 
259             len = GetPrivateProfileStringW(add_codeW, key, NULL, sect_name, ARRAY_SIZE(sect_name),
260                     ctx->install_file);
261             if(!len) {
262                 WARN("Could not get key value\n");
263                 return E_FAIL;
264             }
265 
266             hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, sect_name,
267                     ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL);
268             if(FAILED(hres)) {
269                 WARN("RunSetupCommandW failed: %08x\n", hres);
270                 return hres;
271             }
272         }
273     }
274 
275     if(default_install) {
276         hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL);
277         if(FAILED(hres)) {
278             WARN("RunSetupCommandW failed: %08x\n", hres);
279             return hres;
280         }
281     }
282 
283     return S_OK;
284 }
285 
286 static HRESULT install_cab_file(install_ctx_t *ctx)
287 {
288     WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH];
289     BOOL res = FALSE, leave_temp = FALSE;
290     DWORD i;
291     HRESULT hres;
292 
293     GetTempPathW(ARRAY_SIZE(tmp_path), tmp_path);
294 
295     for(i=0; !res && i < 100; i++) {
296         GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir);
297         res = CreateDirectoryW(tmp_dir, NULL);
298     }
299     if(!res)
300         return E_FAIL;
301 
302     ctx->tmp_dir = tmp_dir;
303 
304     TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir));
305 
306     hres = extract_cab_file(ctx);
307     if(SUCCEEDED(hres)) {
308         if(ctx->callback)
309             IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file);
310 
311         switch(ctx->install_type) {
312         case INSTALL_INF:
313             hres = install_inf_file(ctx);
314             break;
315         case INSTALL_DLL:
316             FIXME("Installing DLL, registering in temporary location\n");
317             hres = setup_dll(ctx);
318             if(SUCCEEDED(hres))
319                 leave_temp = TRUE;
320             break;
321         default:
322             assert(0);
323         }
324     }
325 
326     if(!leave_temp)
327         RemoveDirectoryW(ctx->tmp_dir);
328     return hres;
329 }
330 
331 static void update_counter(install_ctx_t *ctx, HWND hwnd)
332 {
333     WCHAR text[100];
334 
335     if(--ctx->counter <= 0) {
336         HWND button_hwnd;
337 
338         KillTimer(hwnd, ctx->timer);
339         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, ARRAY_SIZE(text));
340 
341         button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN);
342         EnableWindow(button_hwnd, TRUE);
343     }else {
344         WCHAR buf[100];
345         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, ARRAY_SIZE(buf));
346         sprintfW(text, buf, ctx->counter);
347     }
348 
349     SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text);
350 }
351 
352 static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx)
353 {
354     BSTR display_uri;
355     HRESULT hres;
356 
357     if(!SetPropW(hwnd, ctxW, ctx))
358         return FALSE;
359 
360     hres = IUri_GetDisplayUri(ctx->uri, &display_uri);
361     if(FAILED(hres))
362         return FALSE;
363 
364     SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri);
365     SysFreeString(display_uri);
366 
367     SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON,
368             (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0);
369 
370     ctx->counter = 4;
371     update_counter(ctx, hwnd);
372     ctx->timer = SetTimer(hwnd, 1, 1000, NULL);
373     return TRUE;
374 }
375 
376 static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
377 {
378     switch(msg) {
379     case WM_INITDIALOG: {
380         if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam))
381             EndDialog(hwnd, 0);
382         return TRUE;
383     }
384     case WM_COMMAND:
385         switch(wparam) {
386         case ID_AXINSTALL_INSTALL_BTN: {
387             install_ctx_t *ctx = GetPropW(hwnd, ctxW);
388             if(ctx)
389                 ctx->cancel = FALSE;
390             EndDialog(hwnd, 0);
391             return FALSE;
392         }
393         case IDCANCEL:
394             EndDialog(hwnd, 0);
395             return FALSE;
396         }
397     case WM_TIMER:
398         update_counter(GetPropW(hwnd, ctxW), hwnd);
399         return TRUE;
400     }
401 
402     return FALSE;
403 }
404 
405 static BOOL install_warning(install_ctx_t *ctx)
406 {
407     IWindowForBindingUI *window_iface;
408     HWND parent_hwnd = NULL;
409     HRESULT hres;
410 
411     if(!ctx->callback) {
412         FIXME("no callback\n");
413         return FALSE;
414     }
415 
416     hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface);
417     if(FAILED(hres))
418         return FALSE;
419 
420     hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd);
421     IWindowForBindingUI_Release(window_iface);
422     if(FAILED(hres))
423         return FALSE;
424 
425     ctx->cancel = TRUE;
426     DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx);
427     return !ctx->cancel;
428 }
429 
430 static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file)
431 {
432     BSTR path;
433     HRESULT hres;
434 
435     TRACE("%s\n", debugstr_w(cache_file));
436 
437     ctx->cache_file = cache_file;
438 
439     if(!install_warning(ctx)) {
440         TRACE("Installation cancelled\n");
441         return S_OK;
442     }
443 
444     hres = IUri_GetPath(ctx->uri, &path);
445     if(SUCCEEDED(hres)) {
446         const WCHAR *ptr, *ptr2, *ext;
447 
448         ptr = strrchrW(path, '/');
449         if(!ptr)
450             ptr = path;
451         else
452             ptr++;
453 
454         ptr2 = strrchrW(ptr, '\\');
455         if(ptr2)
456             ptr = ptr2+1;
457 
458         ctx->file_name = ptr;
459         ext = strrchrW(ptr, '.');
460         if(!ext)
461             ext = ptr;
462 
463         if(!strcmpiW(ext, cab_extW)) {
464             hres = install_cab_file(ctx);
465         }else {
466             FIXME("Unsupported extension %s\n", debugstr_w(ext));
467             hres = E_NOTIMPL;
468         }
469         SysFreeString(path);
470     }
471 
472     return hres;
473 }
474 
475 static void failure_msgbox(install_ctx_t *ctx, HRESULT hres)
476 {
477     WCHAR buf[1024], fmt[1024];
478 
479     LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, ARRAY_SIZE(fmt));
480     sprintfW(buf, fmt, hres);
481     MessageBoxW(ctx->hwnd, buf, NULL, MB_OK);
482 }
483 
484 static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str)
485 {
486     install_ctx_t *install_ctx = ctx;
487 
488     TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str));
489 
490     if(hresult == S_OK) {
491         hresult = install_file(install_ctx, cache_file);
492         if(FAILED(hresult))
493             failure_msgbox(ctx, hresult);
494     }
495 
496     if(install_ctx->callback)
497         IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str);
498 
499     if(install_ctx->release_on_stop)
500         release_install_ctx(install_ctx);
501     return S_OK;
502 }
503 
504 /***********************************************************************
505  *           AsyncInstallDistributionUnit (URLMON.@)
506  */
507 HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt,
508         DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags)
509 {
510     install_ctx_t *ctx;
511     HRESULT hres;
512 
513     TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt),
514           dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags);
515 
516     if(szDistUnit || szTYPE || szExt)
517         FIXME("Unsupported arguments\n");
518 
519     ctx = heap_alloc_zero(sizeof(*ctx));
520     if(!ctx)
521         return E_OUTOFMEMORY;
522 
523     hres = CreateUri(szURL, 0, 0, &ctx->uri);
524     if(FAILED(hres)) {
525         heap_free(ctx);
526         return E_OUTOFMEMORY;
527     }
528 
529     ctx->callback = bsc_from_bctx(pbc);
530 
531     hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback);
532     if(hres == MK_S_ASYNCHRONOUS)
533         ctx->release_on_stop = TRUE;
534     else
535         release_install_ctx(ctx);
536 
537     return hres;
538 }
539