winhttp/tests: Make sure proxy settings are restored.
[wine] / dlls / 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 HRESULT install_cab_file(install_ctx_t *ctx)
146 {
147     WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH];
148     BOOL res = FALSE, leave_temp = FALSE;
149     DWORD i;
150     HRESULT hres;
151
152     GetTempPathW(sizeof(tmp_path)/sizeof(WCHAR), tmp_path);
153
154     for(i=0; !res && i < 100; i++) {
155         GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir);
156         res = CreateDirectoryW(tmp_dir, NULL);
157     }
158     if(!res)
159         return E_FAIL;
160
161     ctx->tmp_dir = tmp_dir;
162
163     TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir));
164
165     hres = extract_cab_file(ctx);
166     if(SUCCEEDED(hres)) {
167         if(ctx->callback)
168             IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file);
169
170         switch(ctx->install_type) {
171         case INSTALL_INF:
172             hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL);
173             if(FAILED(hres))
174                 WARN("RunSetupCommandW failed: %08x\n", hres);
175             break;
176         case INSTALL_DLL:
177             FIXME("Installing DLL, registering in temporary location\n");
178             hres = setup_dll(ctx);
179             if(SUCCEEDED(hres))
180                 leave_temp = TRUE;
181             break;
182         default:
183             assert(0);
184         }
185     }
186
187     if(!leave_temp)
188         RemoveDirectoryW(ctx->tmp_dir);
189     return hres;
190 }
191
192 static void update_counter(install_ctx_t *ctx, HWND hwnd)
193 {
194     WCHAR text[100];
195
196     if(--ctx->counter <= 0) {
197         HWND button_hwnd;
198
199         KillTimer(hwnd, ctx->timer);
200         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, sizeof(text)/sizeof(WCHAR));
201
202         button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN);
203         EnableWindow(button_hwnd, TRUE);
204     }else {
205         WCHAR buf[100];
206         LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, sizeof(buf)/sizeof(WCHAR));
207         sprintfW(text, buf, ctx->counter);
208     }
209
210     SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text);
211 }
212
213 static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx)
214 {
215     BSTR display_uri;
216     HRESULT hres;
217
218     if(!SetPropW(hwnd, ctxW, ctx))
219         return FALSE;
220
221     hres = IUri_GetDisplayUri(ctx->uri, &display_uri);
222     if(FAILED(hres))
223         return FALSE;
224
225     SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri);
226     SysFreeString(display_uri);
227
228     SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON,
229             (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0);
230
231     ctx->counter = 4;
232     update_counter(ctx, hwnd);
233     ctx->timer = SetTimer(hwnd, 1, 1000, NULL);
234     return TRUE;
235 }
236
237 static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
238 {
239     switch(msg) {
240     case WM_INITDIALOG: {
241         if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam))
242             EndDialog(hwnd, 0);
243         return TRUE;
244     }
245     case WM_COMMAND:
246         switch(wparam) {
247         case ID_AXINSTALL_INSTALL_BTN: {
248             install_ctx_t *ctx = GetPropW(hwnd, ctxW);
249             if(ctx)
250                 ctx->cancel = FALSE;
251             EndDialog(hwnd, 0);
252             return FALSE;
253         }
254         case IDCANCEL:
255             EndDialog(hwnd, 0);
256             return FALSE;
257         }
258     case WM_TIMER:
259         update_counter(GetPropW(hwnd, ctxW), hwnd);
260         return TRUE;
261     }
262
263     return FALSE;
264 }
265
266 static BOOL install_warning(install_ctx_t *ctx)
267 {
268     IWindowForBindingUI *window_iface;
269     HWND parent_hwnd = NULL;
270     HRESULT hres;
271
272     if(!ctx->callback) {
273         FIXME("no callback\n");
274         return FALSE;
275     }
276
277     hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface);
278     if(FAILED(hres))
279         return FALSE;
280
281     hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd);
282     IWindowForBindingUI_Release(window_iface);
283     if(FAILED(hres))
284         return FALSE;
285
286     ctx->cancel = TRUE;
287     DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx);
288     return !ctx->cancel;
289 }
290
291 static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file)
292 {
293     BSTR path;
294     HRESULT hres;
295
296     TRACE("%s\n", debugstr_w(cache_file));
297
298     ctx->cache_file = cache_file;
299
300     if(!install_warning(ctx)) {
301         TRACE("Installation cancelled\n");
302         return S_OK;
303     }
304
305     hres = IUri_GetPath(ctx->uri, &path);
306     if(SUCCEEDED(hres)) {
307         const WCHAR *ptr, *ptr2, *ext;
308
309         ptr = strrchrW(path, '/');
310         if(!ptr)
311             ptr = path;
312         else
313             ptr++;
314
315         ptr2 = strrchrW(ptr, '\\');
316         if(ptr2)
317             ptr = ptr2+1;
318
319         ctx->file_name = ptr;
320         ext = strrchrW(ptr, '.');
321         if(!ext)
322             ext = ptr;
323
324         if(!strcmpW(ext, cab_extW)) {
325             hres = install_cab_file(ctx);
326         }else {
327             FIXME("Unsupported extension %s\n", debugstr_w(ext));
328             hres = E_NOTIMPL;
329         }
330         SysFreeString(path);
331     }
332
333     return hres;
334 }
335
336 static void failure_msgbox(install_ctx_t *ctx, HRESULT hres)
337 {
338     WCHAR buf[1024], fmt[1024];
339
340     LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, sizeof(fmt)/sizeof(WCHAR));
341     sprintfW(buf, fmt, hres);
342     MessageBoxW(ctx->hwnd, buf, NULL, MB_OK);
343 }
344
345 static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str)
346 {
347     install_ctx_t *install_ctx = ctx;
348
349     TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str));
350
351     if(hresult == S_OK) {
352         hresult = install_file(install_ctx, cache_file);
353         if(FAILED(hresult))
354             failure_msgbox(ctx, hresult);
355     }
356
357     if(install_ctx->callback)
358         IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str);
359
360     if(install_ctx->release_on_stop)
361         release_install_ctx(install_ctx);
362     return S_OK;
363 }
364
365 /***********************************************************************
366  *           AsyncInstallDistributionUnit (URLMON.@)
367  */
368 HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt,
369         DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags)
370 {
371     install_ctx_t *ctx;
372     HRESULT hres;
373
374     TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt),
375           dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags);
376
377     if(szDistUnit || szTYPE || szExt)
378         FIXME("Unsupported arguments\n");
379
380     ctx = heap_alloc_zero(sizeof(*ctx));
381     if(!ctx)
382         return E_OUTOFMEMORY;
383
384     hres = CreateUri(szURL, 0, 0, &ctx->uri);
385     if(FAILED(hres)) {
386         heap_free(ctx);
387         return E_OUTOFMEMORY;
388     }
389
390     ctx->callback = bsc_from_bctx(pbc);
391
392     hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback);
393     if(hres == MK_S_ASYNCHRONOUS)
394         ctx->release_on_stop = TRUE;
395     else
396         release_install_ctx(ctx);
397
398     return hres;
399 }