d2cf7e6d85f3e46ae25468d6a39a27b448500bba
[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 #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, sizeof(expand_dirW)/sizeof(WCHAR))) {
158 len2 = strlenW(ctx->tmp_dir);
159 if(buf)
160 memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR));
161 len += len2;
162 ptr += sizeof(expand_dirW)/sizeof(WCHAR);
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, sizeof(buf)/sizeof(*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, sizeof(val)/sizeof(*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, sizeof(buf)/sizeof(*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, sizeof(sect_name)/sizeof(*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, sizeof(buf)/sizeof(*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, sizeof(sect_name)/sizeof(*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(sizeof(tmp_path)/sizeof(WCHAR), 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, sizeof(text)/sizeof(WCHAR));
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, sizeof(buf)/sizeof(WCHAR));
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, sizeof(fmt)/sizeof(WCHAR));
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 }