mshtml: Pass DispatchEx pointer instead of outer IUnknown to DispatchEx's vtbl functions.
[wine] / dlls / appwiz.cpl / addons.c
1 /*
2  * Copyright 2006-2010 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 "config.h"
20
21 #include <stdarg.h>
22 #include <fcntl.h>
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 #define COBJMACROS
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winuser.h"
34 #include "cpl.h"
35 #include "winreg.h"
36 #include "ole2.h"
37 #include "commctrl.h"
38 #include "advpub.h"
39 #include "wininet.h"
40 #include "shellapi.h"
41 #include "urlmon.h"
42
43 #include "appwiz.h"
44 #include "res.h"
45
46 #include "wine/debug.h"
47 #include "wine/unicode.h"
48 #include "wine/library.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
51
52 #define GECKO_VERSION "1.1.0"
53
54 #ifdef __i386__
55 #define ARCH_STRING "x86"
56 #elif defined(__x86_64__)
57 #define ARCH_STRING "x86_64"
58 #else
59 #define ARCH_STRING ""
60 #endif
61
62 #define GECKO_FILE_NAME "wine_gecko-" GECKO_VERSION "-" ARCH_STRING ".cab"
63
64 static const WCHAR mshtml_keyW[] =
65     {'S','o','f','t','w','a','r','e',
66      '\\','W','i','n','e',
67      '\\','M','S','H','T','M','L',0};
68
69 static HWND install_dialog = NULL;
70 static LPWSTR url = NULL;
71
72 static inline char *heap_strdupWtoA(LPCWSTR str)
73 {
74     char *ret = NULL;
75
76     if(str) {
77         DWORD size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
78         ret = heap_alloc(size);
79         WideCharToMultiByte(CP_ACP, 0, str, -1, ret, size, NULL, NULL);
80     }
81
82     return ret;
83 }
84
85 static void set_status(DWORD id)
86 {
87     HWND status = GetDlgItem(install_dialog, ID_DWL_STATUS);
88     WCHAR buf[64];
89
90     LoadStringW(hInst, id, buf, sizeof(buf)/sizeof(WCHAR));
91     SendMessageW(status, WM_SETTEXT, 0, (LPARAM)buf);
92 }
93
94 static void set_registry(const WCHAR *install_dir)
95 {
96     WCHAR mshtml_key[100];
97     LPWSTR gecko_path;
98     HKEY hkey;
99     DWORD res, len;
100
101     static const WCHAR wszGeckoPath[] = {'G','e','c','k','o','P','a','t','h',0};
102     static const WCHAR wszWineGecko[] = {'w','i','n','e','_','g','e','c','k','o',0};
103
104     memcpy(mshtml_key, mshtml_keyW, sizeof(mshtml_keyW));
105     mshtml_key[sizeof(mshtml_keyW)/sizeof(WCHAR)-1] = '\\';
106     MultiByteToWideChar(CP_ACP, 0, GECKO_VERSION, sizeof(GECKO_VERSION),
107             mshtml_key+sizeof(mshtml_keyW)/sizeof(WCHAR),
108             (sizeof(mshtml_key)-sizeof(mshtml_keyW))/sizeof(WCHAR));
109
110     /* @@ Wine registry key: HKCU\Software\Wine\MSHTML\<version> */
111     res = RegCreateKeyW(HKEY_CURRENT_USER, mshtml_key, &hkey);
112     if(res != ERROR_SUCCESS) {
113         ERR("Faild to create MSHTML key: %d\n", res);
114         return;
115     }
116
117     len = strlenW(install_dir);
118     gecko_path = heap_alloc((len+1)*sizeof(WCHAR)+sizeof(wszWineGecko));
119     memcpy(gecko_path, install_dir, len*sizeof(WCHAR));
120
121     if (len && gecko_path[len-1] != '\\')
122         gecko_path[len++] = '\\';
123
124     memcpy(gecko_path+len, wszWineGecko, sizeof(wszWineGecko));
125
126     res = RegSetValueExW(hkey, wszGeckoPath, 0, REG_SZ, (LPVOID)gecko_path,
127                        len*sizeof(WCHAR)+sizeof(wszWineGecko));
128     heap_free(gecko_path);
129     RegCloseKey(hkey);
130     if(res != ERROR_SUCCESS)
131         ERR("Failed to set GeckoPath value: %08x\n", res);
132 }
133
134 static BOOL install_cab(LPCWSTR file_name)
135 {
136     char *install_dir_a, *file_name_a;
137     WCHAR install_dir[MAX_PATH];
138     DWORD res, len;
139     HRESULT hres;
140
141     static const WCHAR gecko_subdirW[] = {'\\','g','e','c','k','o','\\',0};
142
143     TRACE("(%s)\n", debugstr_w(file_name));
144
145     GetSystemDirectoryW(install_dir, sizeof(install_dir)/sizeof(WCHAR));
146     strcatW(install_dir, gecko_subdirW);
147     res = CreateDirectoryW(install_dir, NULL);
148     if(!res && GetLastError() != ERROR_ALREADY_EXISTS) {
149         ERR("Could not create directory: %08u\n", GetLastError());
150         return FALSE;
151     }
152
153     len = strlenW(install_dir);
154     MultiByteToWideChar(CP_ACP, 0, GECKO_VERSION, -1, install_dir+len, sizeof(install_dir)/sizeof(WCHAR)-len);
155     res = CreateDirectoryW(install_dir, NULL);
156     if(!res && GetLastError() != ERROR_ALREADY_EXISTS) {
157         ERR("Could not create directory: %08u\n", GetLastError());
158         return FALSE;
159     }
160
161
162     /* FIXME: Use ExtractFilesW once it's implemented */
163     file_name_a = heap_strdupWtoA(file_name);
164     install_dir_a = heap_strdupWtoA(install_dir);
165     if(file_name_a && install_dir_a)
166         hres = ExtractFilesA(file_name_a, install_dir_a, 0, NULL, NULL, 0);
167     else
168         hres = E_OUTOFMEMORY;
169     heap_free(file_name_a);
170     heap_free(install_dir_a);
171     if(FAILED(hres)) {
172         ERR("Could not extract package: %08x\n", hres);
173         return FALSE;
174     }
175
176     set_registry(install_dir);
177     return TRUE;
178 }
179
180 static BOOL install_from_unix_file(const char *file_name)
181 {
182     LPWSTR dos_file_name;
183     int fd;
184     BOOL ret;
185
186     static WCHAR * (CDECL *wine_get_dos_file_name)(const char*);
187     static const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0};
188
189     fd = open(file_name, O_RDONLY);
190     if(fd == -1) {
191         TRACE("%s not found\n", debugstr_a(file_name));
192         return FALSE;
193     }
194
195     close(fd);
196
197     if(!wine_get_dos_file_name)
198         wine_get_dos_file_name = (void*)GetProcAddress(GetModuleHandleW(kernel32W), "wine_get_dos_file_name");
199
200     if(wine_get_dos_file_name) { /* Wine UNIX mode */
201         dos_file_name = wine_get_dos_file_name(file_name);
202         if(!dos_file_name) {
203             ERR("Could not get dos file name of %s\n", debugstr_a(file_name));
204             return FALSE;
205         }
206     } else { /* Windows mode */
207         UINT res;
208         WARN("Could not get wine_get_dos_file_name function, calling install_cab directly.\n");
209         res = MultiByteToWideChar( CP_ACP, 0, file_name, -1, 0, 0);
210         dos_file_name = heap_alloc (res*sizeof(WCHAR));
211         MultiByteToWideChar( CP_ACP, 0, file_name, -1, dos_file_name, res);
212     }
213
214     ret = install_cab(dos_file_name);
215
216     heap_free(dos_file_name);
217     return ret;
218 }
219
220 static BOOL install_from_registered_dir(void)
221 {
222     char *file_name;
223     HKEY hkey;
224     DWORD res, type, size = MAX_PATH;
225     BOOL ret;
226
227     /* @@ Wine registry key: HKCU\Software\Wine\MSHTML */
228     res = RegOpenKeyW(HKEY_CURRENT_USER, mshtml_keyW, &hkey);
229     if(res != ERROR_SUCCESS)
230         return FALSE;
231
232     file_name = heap_alloc(size+sizeof(GECKO_FILE_NAME));
233     res = RegGetValueA(hkey, NULL, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)file_name, &size);
234     if(res == ERROR_MORE_DATA) {
235         file_name = heap_realloc(file_name, size+sizeof(GECKO_FILE_NAME));
236         res = RegGetValueA(hkey, NULL, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)file_name, &size);
237     }
238     RegCloseKey(hkey);
239     if(res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
240         heap_free(file_name);
241         return FALSE;
242     }
243
244     strcat(file_name, GECKO_FILE_NAME);
245
246     TRACE("Trying %s\n", debugstr_a(file_name));
247
248     ret = install_from_unix_file(file_name);
249
250     heap_free(file_name);
251     return ret;
252 }
253
254 static BOOL install_from_default_dir(void)
255 {
256     const char *data_dir, *subdir;
257     char *file_name;
258     int len, len2;
259     BOOL ret;
260
261     if((data_dir = wine_get_data_dir()))
262         subdir = "/gecko/";
263     else if((data_dir = wine_get_build_dir()))
264         subdir = "/../gecko/";
265     else
266         return FALSE;
267
268     len = strlen(data_dir);
269     len2 = strlen(subdir);
270
271     file_name = heap_alloc(len+len2+sizeof(GECKO_FILE_NAME));
272     memcpy(file_name, data_dir, len);
273     memcpy(file_name+len, subdir, len2);
274     memcpy(file_name+len+len2, GECKO_FILE_NAME, sizeof(GECKO_FILE_NAME));
275
276     ret = install_from_unix_file(file_name);
277
278     heap_free(file_name);
279
280     if (!ret)
281         ret = install_from_unix_file(INSTALL_DATADIR "/wine/gecko/" GECKO_FILE_NAME);
282     if (!ret && strcmp(INSTALL_DATADIR, "/usr/share"))
283         ret = install_from_unix_file("/usr/share/wine/gecko/" GECKO_FILE_NAME);
284     return ret;
285 }
286
287 static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface,
288         REFIID riid, void **ppv)
289 {
290     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IBindStatusCallback, riid)) {
291         *ppv = iface;
292         return S_OK;
293     }
294
295     return E_INVALIDARG;
296 }
297
298 static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface)
299 {
300     return 2;
301 }
302
303 static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface)
304 {
305     return 1;
306 }
307
308 static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface,
309         DWORD dwReserved, IBinding *pib)
310 {
311     set_status(IDS_DOWNLOADING);
312     return S_OK;
313 }
314
315 static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface,
316         LONG *pnPriority)
317 {
318     return E_NOTIMPL;
319 }
320
321 static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface,
322        DWORD dwReserved)
323 {
324     return E_NOTIMPL;
325 }
326
327 static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
328         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
329 {
330     HWND progress = GetDlgItem(install_dialog, ID_DWL_PROGRESS);
331
332     if(ulProgressMax)
333         SendMessageW(progress, PBM_SETRANGE32, 0, ulProgressMax);
334     if(ulProgress)
335         SendMessageW(progress, PBM_SETPOS, ulProgress, 0);
336
337     return S_OK;
338 }
339
340 static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface,
341         HRESULT hresult, LPCWSTR szError)
342 {
343     if(FAILED(hresult)) {
344         ERR("Binding failed %08x\n", hresult);
345         return S_OK;
346     }
347
348     set_status(IDS_INSTALLING);
349     return S_OK;
350 }
351
352 static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface,
353         DWORD* grfBINDF, BINDINFO* pbindinfo)
354 {
355     /* FIXME */
356     *grfBINDF = 0;
357     return S_OK;
358 }
359
360 static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
361         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
362 {
363     ERR("\n");
364     return E_NOTIMPL;
365 }
366
367 static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface,
368         REFIID riid, IUnknown* punk)
369 {
370     ERR("\n");
371     return E_NOTIMPL;
372 }
373
374 static const IBindStatusCallbackVtbl InstallCallbackVtbl = {
375     InstallCallback_QueryInterface,
376     InstallCallback_AddRef,
377     InstallCallback_Release,
378     InstallCallback_OnStartBinding,
379     InstallCallback_GetPriority,
380     InstallCallback_OnLowResource,
381     InstallCallback_OnProgress,
382     InstallCallback_OnStopBinding,
383     InstallCallback_GetBindInfo,
384     InstallCallback_OnDataAvailable,
385     InstallCallback_OnObjectAvailable
386 };
387
388 static IBindStatusCallback InstallCallback = { &InstallCallbackVtbl };
389
390 static LPWSTR get_url(void)
391 {
392     HKEY hkey;
393     DWORD res, type;
394     DWORD size = INTERNET_MAX_URL_LENGTH*sizeof(WCHAR);
395     DWORD returned_size;
396     LPWSTR url;
397
398     static const WCHAR wszGeckoUrl[] = {'G','e','c','k','o','U','r','l',0};
399     static const WCHAR httpW[] = {'h','t','t','p'};
400     static const WCHAR arch_formatW[] = {'?','a','r','c','h','='};
401     static const WCHAR v_formatW[] = {'&','v','='};
402
403     /* @@ Wine registry key: HKCU\Software\Wine\MSHTML */
404     res = RegOpenKeyW(HKEY_CURRENT_USER, mshtml_keyW, &hkey);
405     if(res != ERROR_SUCCESS)
406         return NULL;
407
408     url = heap_alloc(size);
409     returned_size = size;
410
411     res = RegQueryValueExW(hkey, wszGeckoUrl, NULL, &type, (LPBYTE)url, &returned_size);
412     RegCloseKey(hkey);
413     if(res != ERROR_SUCCESS || type != REG_SZ) {
414         heap_free(url);
415         return NULL;
416     }
417
418     if(returned_size > sizeof(httpW) && !memcmp(url, httpW, sizeof(httpW))) {
419         DWORD len;
420
421         len = strlenW(url);
422         memcpy(url+len, arch_formatW, sizeof(arch_formatW));
423         len += sizeof(arch_formatW)/sizeof(WCHAR);
424         len += MultiByteToWideChar(CP_ACP, 0, ARCH_STRING, sizeof(ARCH_STRING), url+len, size/sizeof(WCHAR)-len)-1;
425         memcpy(url+len, v_formatW, sizeof(v_formatW));
426         len += sizeof(v_formatW)/sizeof(WCHAR);
427         MultiByteToWideChar(CP_ACP, 0, GECKO_VERSION, -1, url+len, size/sizeof(WCHAR)-len);
428     }
429
430     TRACE("Got URL %s\n", debugstr_w(url));
431     return url;
432 }
433
434 static DWORD WINAPI download_proc(PVOID arg)
435 {
436     WCHAR tmp_dir[MAX_PATH], tmp_file[MAX_PATH];
437     HRESULT hres;
438
439     GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir);
440     GetTempFileNameW(tmp_dir, NULL, 0, tmp_file);
441
442     TRACE("using temp file %s\n", debugstr_w(tmp_file));
443
444     hres = URLDownloadToFileW(NULL, url, tmp_file, 0, &InstallCallback);
445     if(FAILED(hres)) {
446         ERR("URLDownloadToFile failed: %08x\n", hres);
447         return 0;
448     }
449
450     install_cab(tmp_file);
451     DeleteFileW(tmp_file);
452     EndDialog(install_dialog, 0);
453     return 0;
454 }
455
456 static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
457 {
458     switch(msg) {
459     case WM_INITDIALOG:
460         ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_HIDE);
461         install_dialog = hwnd;
462         return TRUE;
463
464     case WM_COMMAND:
465         switch(wParam) {
466         case IDCANCEL:
467             EndDialog(hwnd, 0);
468             return FALSE;
469
470         case ID_DWL_INSTALL:
471             ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_SHOW);
472             EnableWindow(GetDlgItem(hwnd, ID_DWL_INSTALL), 0);
473             EnableWindow(GetDlgItem(hwnd, IDCANCEL), 0); /* FIXME */
474             CreateThread(NULL, 0, download_proc, NULL, 0, NULL);
475             return FALSE;
476         }
477     }
478
479     return FALSE;
480 }
481
482 BOOL install_wine_gecko(void)
483 {
484     if(!*ARCH_STRING)
485         return FALSE;
486
487     /*
488      * Try to find Gecko .cab file in following order:
489      * - directory stored in GeckoCabDir value of HKCU/Wine/Software/MSHTML key
490      * - $datadir/gecko/
491      * - $INSTALL_DATADIR/wine/gecko/
492      * - /usr/share/wine/gecko/
493      * - download from URL stored in GeckoUrl value of HKCU/Wine/Software/MSHTML key
494      */
495     if(!install_from_registered_dir()
496        && !install_from_default_dir()
497        && (url = get_url()))
498         DialogBoxW(hInst, MAKEINTRESOURCEW(ID_DWL_DIALOG), 0, installer_proc);
499
500     heap_free(url);
501     url = NULL;
502     return TRUE;
503 }