xref: /reactos/dll/cpl/appwiz/addons.c (revision 98e8827a)
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 "appwiz.h"
20 
21 #include <stdio.h>
22 
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26 
27 #include <msi.h>
28 
29 #define GECKO_VERSION "2.40"
30 
31 #ifdef __i386__
32 #define ARCH_STRING "x86"
33 #define GECKO_SHA "8a3adedf3707973d1ed4ac3b2e791486abf814bd"
34 #else
35 #define ARCH_STRING ""
36 #define GECKO_SHA "???"
37 #endif
38 
39 typedef struct {
40     const char *version;
41     const char *file_name;
42     const char *sha;
43     const char *config_key;
44     const char *dir_config_key;
45     LPCWSTR dialog_template;
46 } addon_info_t;
47 
48 static const addon_info_t addons_info[] = {
49     {
50         GECKO_VERSION,
51         "wine_gecko-" GECKO_VERSION "-" ARCH_STRING ".msi",
52         GECKO_SHA,
53         "MSHTML",
54         "GeckoCabDir",
55         MAKEINTRESOURCEW(ID_DWL_GECKO_DIALOG)
56     }
57 };
58 
59 static const addon_info_t *addon;
60 
61 static HWND install_dialog = NULL;
62 static IBinding *download_binding = NULL;
63 
64 static WCHAR GeckoUrl[] = L"https://svn.reactos.org/amine/wine_gecko-2.40-x86.msi";
65 
66 /* SHA definitions are copied from advapi32. They aren't available in headers. */
67 
68 typedef struct {
69    ULONG Unknown[6];
70    ULONG State[5];
71    ULONG Count[2];
72    UCHAR Buffer[64];
73 } SHA_CTX, *PSHA_CTX;
74 
75 void WINAPI A_SHAInit(PSHA_CTX);
76 void WINAPI A_SHAUpdate(PSHA_CTX,const unsigned char*,UINT);
77 void WINAPI A_SHAFinal(PSHA_CTX,PULONG);
78 
79 static BOOL sha_check(const WCHAR *file_name)
80 {
81     const unsigned char *file_map;
82     HANDLE file, map;
83     ULONG sha[5];
84     char buf[2*sizeof(sha)+1];
85     SHA_CTX ctx;
86     DWORD size, i;
87 
88     file = CreateFileW(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
89     if(file == INVALID_HANDLE_VALUE)
90         return FALSE;
91 
92     size = GetFileSize(file, NULL);
93 
94     map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
95     CloseHandle(file);
96     if(!map)
97         return FALSE;
98 
99     file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
100     CloseHandle(map);
101     if(!file_map)
102         return FALSE;
103 
104     A_SHAInit(&ctx);
105     A_SHAUpdate(&ctx, file_map, size);
106     A_SHAFinal(&ctx, sha);
107 
108     UnmapViewOfFile(file_map);
109 
110     for(i=0; i < sizeof(sha); i++)
111         sprintf(buf + i*2, "%02x", *((unsigned char*)sha+i));
112 
113     if(strcmp(buf, addon->sha)) {
114         WARN("Got %s, expected %s\n", buf, addon->sha);
115         return FALSE;
116     }
117 
118     return TRUE;
119 }
120 
121 static void set_status(DWORD id)
122 {
123     HWND status = GetDlgItem(install_dialog, ID_DWL_STATUS);
124     WCHAR buf[64];
125 
126     LoadStringW(hApplet, id, buf, sizeof(buf)/sizeof(WCHAR));
127     SendMessageW(status, WM_SETTEXT, 0, (LPARAM)buf);
128 }
129 
130 enum install_res {
131     INSTALL_OK = 0,
132     INSTALL_FAILED,
133     INSTALL_NEXT,
134 };
135 
136 static enum install_res install_file(const WCHAR *file_name)
137 {
138     ULONG res;
139 
140     res = MsiInstallProductW(file_name, NULL);
141     if(res != ERROR_SUCCESS) {
142         ERR("MsiInstallProduct failed: %u\n", res);
143         return INSTALL_FAILED;
144     }
145 
146     return INSTALL_OK;
147 }
148 
149 static enum install_res install_from_unix_file(const char *dir, const char *subdir, const char *file_name)
150 {
151     LPWSTR dos_file_name;
152     char *file_path;
153     int fd, len;
154     enum install_res ret;
155     UINT res;
156 
157     len = strlen(dir);
158     file_path = heap_alloc(len+strlen(subdir)+strlen(file_name)+3);
159     if(!file_path)
160         return INSTALL_FAILED;
161 
162     memcpy(file_path, dir, len);
163     if(len && file_path[len-1] != '/' && file_path[len-1] != '\\')
164         file_path[len++] = '/';
165     if(*subdir) {
166         strcpy(file_path+len, subdir);
167         len += strlen(subdir);
168         file_path[len++] = '/';
169     }
170     strcpy(file_path+len, file_name);
171 
172     fd = _open(file_path, O_RDONLY);
173     if(fd == -1) {
174         TRACE("%s not found\n", debugstr_a(file_path));
175         heap_free(file_path);
176         return INSTALL_NEXT;
177     }
178 
179     _close(fd);
180 
181     WARN("Could not get wine_get_dos_file_name function, calling install_cab directly.\n");
182     res = MultiByteToWideChar( CP_ACP, 0, file_path, -1, 0, 0);
183     dos_file_name = heap_alloc (res*sizeof(WCHAR));
184     MultiByteToWideChar( CP_ACP, 0, file_path, -1, dos_file_name, res);
185 
186     heap_free(file_path);
187 
188     ret = install_file(dos_file_name);
189 
190     heap_free(dos_file_name);
191     return ret;
192 }
193 
194 static const CHAR mshtml_keyA[] =
195     {'S','o','f','t','w','a','r','e',
196     '\\','W','i','n','e',
197     '\\','M','S','H','T','M','L',0};
198 
199 static enum install_res install_from_registered_dir(void)
200 {
201     char *package_dir;
202     DWORD res, type, size = MAX_PATH;
203     enum install_res ret;
204 
205     package_dir = heap_alloc(size + sizeof(addon->file_name));
206 
207     res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
208     if(res == ERROR_MORE_DATA) {
209         package_dir = heap_realloc(package_dir, size + sizeof(addon->file_name));
210         res = RegGetValueA(HKEY_CURRENT_USER, mshtml_keyA, "GeckoCabDir", RRF_RT_ANY, &type, (PBYTE)package_dir, &size);
211     }
212 
213     if(res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
214         heap_free(package_dir);
215         return INSTALL_FAILED;
216     }
217 
218     if (type == REG_EXPAND_SZ)
219     {
220         size = ExpandEnvironmentStringsA(package_dir, NULL, 0);
221         if (size)
222         {
223             char* buf = heap_alloc(size + sizeof(addon->file_name));
224             ExpandEnvironmentStringsA(package_dir, buf, size);
225             heap_free(package_dir);
226             package_dir = buf;
227         }
228     }
229 
230     TRACE("Trying %s/%s\n", debugstr_a(package_dir), debugstr_a(addon->file_name));
231 
232     ret = install_from_unix_file(package_dir, "", addon->file_name);
233 
234     heap_free(package_dir);
235     return ret;
236 }
237 
238 static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface,
239         REFIID riid, void **ppv)
240 {
241     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IBindStatusCallback, riid)) {
242         *ppv = iface;
243         return S_OK;
244     }
245 
246     return E_INVALIDARG;
247 }
248 
249 static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface)
250 {
251     return 2;
252 }
253 
254 static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface)
255 {
256     return 1;
257 }
258 
259 static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface,
260         DWORD dwReserved, IBinding *pib)
261 {
262     set_status(IDS_DOWNLOADING);
263     IBinding_AddRef(pib);
264     download_binding = pib;
265 
266     return S_OK;
267 }
268 
269 static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface,
270         LONG *pnPriority)
271 {
272     return E_NOTIMPL;
273 }
274 
275 static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface,
276        DWORD dwReserved)
277 {
278     return E_NOTIMPL;
279 }
280 
281 static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
282         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
283 {
284     HWND progress = GetDlgItem(install_dialog, ID_DWL_PROGRESS);
285 
286     if(ulProgressMax)
287         SendMessageW(progress, PBM_SETRANGE32, 0, ulProgressMax);
288     if(ulProgress)
289         SendMessageW(progress, PBM_SETPOS, ulProgress, 0);
290 
291     return S_OK;
292 }
293 
294 static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface,
295         HRESULT hresult, LPCWSTR szError)
296 {
297     if(download_binding) {
298         IBinding_Release(download_binding);
299         download_binding = NULL;
300     }
301 
302     if(FAILED(hresult)) {
303         if(hresult == E_ABORT)
304             TRACE("Binding aborted\n");
305         else
306             ERR("Binding failed %08x\n", hresult);
307         return S_OK;
308     }
309 
310     set_status(IDS_INSTALLING);
311     return S_OK;
312 }
313 
314 static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface,
315         DWORD* grfBINDF, BINDINFO* pbindinfo)
316 {
317     /* FIXME */
318     *grfBINDF = 0;
319     return S_OK;
320 }
321 
322 static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
323         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
324 {
325     ERR("\n");
326     return E_NOTIMPL;
327 }
328 
329 static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface,
330         REFIID riid, IUnknown* punk)
331 {
332     ERR("\n");
333     return E_NOTIMPL;
334 }
335 
336 static const IBindStatusCallbackVtbl InstallCallbackVtbl = {
337     InstallCallback_QueryInterface,
338     InstallCallback_AddRef,
339     InstallCallback_Release,
340     InstallCallback_OnStartBinding,
341     InstallCallback_GetPriority,
342     InstallCallback_OnLowResource,
343     InstallCallback_OnProgress,
344     InstallCallback_OnStopBinding,
345     InstallCallback_GetBindInfo,
346     InstallCallback_OnDataAvailable,
347     InstallCallback_OnObjectAvailable
348 };
349 
350 static IBindStatusCallback InstallCallback = { &InstallCallbackVtbl };
351 
352 static DWORD WINAPI download_proc(PVOID arg)
353 {
354     WCHAR message[256];
355     WCHAR tmp_dir[MAX_PATH], tmp_file[MAX_PATH];
356     HRESULT hres;
357 
358     GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir);
359     GetTempFileNameW(tmp_dir, NULL, 0, tmp_file);
360 
361     TRACE("using temp file %s\n", debugstr_w(tmp_file));
362 
363     hres = URLDownloadToFileW(NULL, GeckoUrl, tmp_file, 0, &InstallCallback);
364     if(FAILED(hres)) {
365         if (LoadStringW(hApplet, IDS_DWL_FAILED, message, sizeof(message) / sizeof(WCHAR))) {
366             /* If the user aborted the download, DO NOT display the message box */
367             if (hres == E_ABORT) {
368                 TRACE("Downloading of Gecko package aborted!\n");
369             } else {
370                 MessageBoxW(NULL, message, NULL, MB_ICONERROR);
371             }
372         }
373         ERR("URLDownloadToFile failed: %08x\n", hres);
374     } else {
375         if(sha_check(tmp_file)) {
376             install_file(tmp_file);
377         }else {
378             if(LoadStringW(hApplet, IDS_INVALID_SHA, message, sizeof(message)/sizeof(WCHAR))) {
379                 MessageBoxW(NULL, message, NULL, MB_ICONERROR);
380             }
381         }
382     }
383 
384     DeleteFileW(tmp_file);
385     PostMessageW(install_dialog, WM_COMMAND, IDCANCEL, 0);
386     return 0;
387 }
388 
389 static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
390 {
391     HWND hwndInstallButton;
392     switch(msg) {
393     case WM_INITDIALOG:
394         ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_HIDE);
395         install_dialog = hwnd;
396         return TRUE;
397 
398     case WM_NOTIFY:
399         break;
400 
401     case WM_COMMAND:
402         switch(wParam) {
403         case IDCANCEL:
404             if(download_binding) {
405                 IBinding_Abort(download_binding);
406             }
407             else {
408                 EndDialog(hwnd, 0);
409             }
410             return FALSE;
411 
412         case ID_DWL_INSTALL:
413             ShowWindow(GetDlgItem(hwnd, ID_DWL_PROGRESS), SW_SHOW);
414 
415             /* CORE-17550: Never leave focus on a disabled control (Old/New/Thing p.228) */
416             hwndInstallButton = GetDlgItem(hwnd, ID_DWL_INSTALL);
417             if (hwndInstallButton == GetFocus())
418             {
419                 SendMessageW(hwnd, WM_NEXTDLGCTL, 0, FALSE);
420             }
421             EnableWindow(hwndInstallButton, FALSE);
422 
423             CloseHandle( CreateThread(NULL, 0, download_proc, NULL, 0, NULL));
424             return FALSE;
425         }
426     }
427 
428     return FALSE;
429 }
430 
431 BOOL install_addon(addon_t addon_type, HWND hwnd_parent)
432 {
433 
434     if(!*ARCH_STRING)
435         return FALSE;
436 
437     addon = addons_info + addon_type;
438 
439     /*
440      * Try to find addon .msi file in following order:
441      * - directory stored in $dir_config_key value of HKCU/Wine/Software/$config_key key
442      * - download the package
443      */
444     if (install_from_registered_dir() == INSTALL_NEXT)
445         DialogBoxW(hApplet, addon->dialog_template, hwnd_parent, installer_proc);
446 
447     return TRUE;
448 }
449