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