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