| /* |
| * Copyright 2012 Jacek Caban for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #define OEMRESOURCE |
| |
| #include <assert.h> |
| |
| #include "urlmon_main.h" |
| #include "resource.h" |
| |
| #include "advpub.h" |
| #include "fdi.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(urlmon); |
| |
| static const WCHAR ctxW[] = {'c','t','x',0}; |
| static const WCHAR cab_extW[] = {'.','c','a','b',0}; |
| static const WCHAR infW[] = {'i','n','f',0}; |
| static const WCHAR dllW[] = {'d','l','l',0}; |
| static const WCHAR ocxW[] = {'o','c','x',0}; |
| |
| enum install_type { |
| INSTALL_UNKNOWN, |
| INSTALL_DLL, |
| INSTALL_INF |
| }; |
| |
| typedef struct { |
| IUri *uri; |
| IBindStatusCallback *callback; |
| BOOL release_on_stop; |
| BOOL cancel; |
| WCHAR *install_file; |
| const WCHAR *cache_file; |
| const WCHAR *tmp_dir; |
| const WCHAR *file_name; |
| enum install_type install_type; |
| HWND hwnd; |
| int counter; |
| INT_PTR timer; |
| } install_ctx_t; |
| |
| static void release_install_ctx(install_ctx_t *ctx) |
| { |
| if(ctx->uri) |
| IUri_Release(ctx->uri); |
| if(ctx->callback) |
| IBindStatusCallback_Release(ctx->callback); |
| heap_free(ctx->install_file); |
| heap_free(ctx); |
| } |
| |
| static inline BOOL file_exists(const WCHAR *file_name) |
| { |
| return GetFileAttributesW(file_name) != INVALID_FILE_ATTRIBUTES; |
| } |
| |
| static HRESULT extract_cab_file(install_ctx_t *ctx) |
| { |
| size_t path_len, file_len; |
| WCHAR *ptr; |
| HRESULT hres; |
| |
| hres = ExtractFilesW(ctx->cache_file, ctx->tmp_dir, 0, NULL, NULL, 0); |
| if(FAILED(hres)) { |
| WARN("ExtractFilesW failed: %08x\n", hres); |
| return hres; |
| } |
| |
| path_len = strlenW(ctx->tmp_dir); |
| file_len = strlenW(ctx->file_name); |
| ctx->install_file = heap_alloc((path_len+file_len+2)*sizeof(WCHAR)); |
| if(!ctx->install_file) |
| return E_OUTOFMEMORY; |
| |
| memcpy(ctx->install_file, ctx->tmp_dir, path_len*sizeof(WCHAR)); |
| ctx->install_file[path_len] = '\\'; |
| memcpy(ctx->install_file+path_len+1, ctx->file_name, (file_len+1)*sizeof(WCHAR)); |
| |
| /* NOTE: Assume that file_name contains ".cab" extension */ |
| ptr = ctx->install_file+path_len+1+file_len-3; |
| |
| memcpy(ptr, infW, sizeof(infW)); |
| if(file_exists(ctx->install_file)) { |
| ctx->install_type = INSTALL_INF; |
| return S_OK; |
| } |
| |
| memcpy(ptr, dllW, sizeof(dllW)); |
| if(file_exists(ctx->install_file)) { |
| ctx->install_type = INSTALL_DLL; |
| return S_OK; |
| } |
| |
| memcpy(ptr, ocxW, sizeof(ocxW)); |
| if(file_exists(ctx->install_file)) { |
| ctx->install_type = INSTALL_DLL; |
| return S_OK; |
| } |
| |
| FIXME("No known install file\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT setup_dll(install_ctx_t *ctx) |
| { |
| HMODULE module; |
| HRESULT hres; |
| |
| HRESULT (WINAPI *reg_func)(void); |
| |
| module = LoadLibraryW(ctx->install_file); |
| if(!module) |
| return E_FAIL; |
| |
| reg_func = (void*)GetProcAddress(module, "DllRegisterServer"); |
| if(reg_func) { |
| hres = reg_func(); |
| }else { |
| WARN("no DllRegisterServer function\n"); |
| hres = E_FAIL; |
| } |
| |
| FreeLibrary(module); |
| return hres; |
| } |
| |
| static void expand_command(install_ctx_t *ctx, const WCHAR *cmd, WCHAR *buf, size_t *size) |
| { |
| const WCHAR *ptr = cmd, *prev_ptr = cmd; |
| size_t len = 0, len2; |
| |
| static const WCHAR expand_dirW[] = {'%','E','X','T','R','A','C','T','_','D','I','R','%'}; |
| |
| while((ptr = strchrW(ptr, '%'))) { |
| if(buf) |
| memcpy(buf+len, prev_ptr, ptr-prev_ptr); |
| len += ptr-prev_ptr; |
| |
| if(!strncmpiW(ptr, expand_dirW, sizeof(expand_dirW)/sizeof(WCHAR))) { |
| len2 = strlenW(ctx->tmp_dir); |
| if(buf) |
| memcpy(buf+len, ctx->tmp_dir, len2*sizeof(WCHAR)); |
| len += len2; |
| ptr += sizeof(expand_dirW)/sizeof(WCHAR); |
| }else { |
| FIXME("Can't expand %s\n", debugstr_w(ptr)); |
| if(buf) |
| buf[len] = '%'; |
| len++; |
| ptr++; |
| } |
| |
| prev_ptr = ptr; |
| } |
| |
| if(buf) |
| strcpyW(buf+len, prev_ptr); |
| *size = len + strlenW(prev_ptr) + 1; |
| } |
| |
| static HRESULT process_hook_section(install_ctx_t *ctx, const WCHAR *sect_name) |
| { |
| WCHAR buf[2048], val[2*MAX_PATH]; |
| const WCHAR *key; |
| DWORD len; |
| HRESULT hres; |
| |
| static const WCHAR runW[] = {'r','u','n',0}; |
| |
| len = GetPrivateProfileStringW(sect_name, NULL, NULL, buf, sizeof(buf)/sizeof(*buf), ctx->install_file); |
| if(!len) |
| return S_OK; |
| |
| for(key = buf; *key; key += strlenW(key)+1) { |
| if(!strcmpiW(key, runW)) { |
| WCHAR *cmd; |
| size_t size; |
| |
| len = GetPrivateProfileStringW(sect_name, runW, NULL, val, sizeof(val)/sizeof(*val), ctx->install_file); |
| |
| TRACE("Run %s\n", debugstr_w(val)); |
| |
| expand_command(ctx, val, NULL, &size); |
| |
| cmd = heap_alloc(size*sizeof(WCHAR)); |
| if(!cmd) |
| heap_free(cmd); |
| |
| expand_command(ctx, val, cmd, &size); |
| hres = RunSetupCommandW(ctx->hwnd, cmd, NULL, ctx->tmp_dir, NULL, NULL, 0, NULL); |
| heap_free(cmd); |
| if(FAILED(hres)) |
| return hres; |
| }else { |
| FIXME("Unsupported hook %s\n", debugstr_w(key)); |
| return E_NOTIMPL; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT install_inf_file(install_ctx_t *ctx) |
| { |
| WCHAR buf[2048], sect_name[128]; |
| BOOL default_install = TRUE; |
| const WCHAR *key; |
| DWORD len; |
| HRESULT hres; |
| |
| static const WCHAR setup_hooksW[] = {'S','e','t','u','p',' ','H','o','o','k','s',0}; |
| static const WCHAR add_codeW[] = {'A','d','d','.','C','o','d','e',0}; |
| |
| len = GetPrivateProfileStringW(setup_hooksW, NULL, NULL, buf, sizeof(buf)/sizeof(*buf), ctx->install_file); |
| if(len) { |
| default_install = FALSE; |
| |
| for(key = buf; *key; key += strlenW(key)+1) { |
| TRACE("[Setup Hooks] key: %s\n", debugstr_w(key)); |
| |
| len = GetPrivateProfileStringW(setup_hooksW, key, NULL, sect_name, sizeof(sect_name)/sizeof(*sect_name), |
| ctx->install_file); |
| if(!len) { |
| WARN("Could not get key value\n"); |
| return E_FAIL; |
| } |
| |
| hres = process_hook_section(ctx, sect_name); |
| if(FAILED(hres)) |
| return hres; |
| } |
| } |
| |
| len = GetPrivateProfileStringW(add_codeW, NULL, NULL, buf, sizeof(buf)/sizeof(*buf), ctx->install_file); |
| if(len) { |
| FIXME("[Add.Code] section not supported\n"); |
| |
| /* Don't throw an error if we successfully ran setup hooks; |
| installation is likely to be complete enough */ |
| if(default_install) |
| return E_NOTIMPL; |
| } |
| |
| if(default_install) { |
| hres = RunSetupCommandW(ctx->hwnd, ctx->install_file, NULL, ctx->tmp_dir, NULL, NULL, RSC_FLAG_INF, NULL); |
| if(FAILED(hres)) { |
| WARN("RunSetupCommandW failed: %08x\n", hres); |
| return hres; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT install_cab_file(install_ctx_t *ctx) |
| { |
| WCHAR tmp_path[MAX_PATH], tmp_dir[MAX_PATH]; |
| BOOL res = FALSE, leave_temp = FALSE; |
| DWORD i; |
| HRESULT hres; |
| |
| GetTempPathW(sizeof(tmp_path)/sizeof(WCHAR), tmp_path); |
| |
| for(i=0; !res && i < 100; i++) { |
| GetTempFileNameW(tmp_path, NULL, GetTickCount() + i*17037, tmp_dir); |
| res = CreateDirectoryW(tmp_dir, NULL); |
| } |
| if(!res) |
| return E_FAIL; |
| |
| ctx->tmp_dir = tmp_dir; |
| |
| TRACE("Using temporary directory %s\n", debugstr_w(tmp_dir)); |
| |
| hres = extract_cab_file(ctx); |
| if(SUCCEEDED(hres)) { |
| if(ctx->callback) |
| IBindStatusCallback_OnProgress(ctx->callback, 0, 0, BINDSTATUS_INSTALLINGCOMPONENTS, ctx->install_file); |
| |
| switch(ctx->install_type) { |
| case INSTALL_INF: |
| hres = install_inf_file(ctx); |
| break; |
| case INSTALL_DLL: |
| FIXME("Installing DLL, registering in temporary location\n"); |
| hres = setup_dll(ctx); |
| if(SUCCEEDED(hres)) |
| leave_temp = TRUE; |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| if(!leave_temp) |
| RemoveDirectoryW(ctx->tmp_dir); |
| return hres; |
| } |
| |
| static void update_counter(install_ctx_t *ctx, HWND hwnd) |
| { |
| WCHAR text[100]; |
| |
| if(--ctx->counter <= 0) { |
| HWND button_hwnd; |
| |
| KillTimer(hwnd, ctx->timer); |
| LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALL, text, sizeof(text)/sizeof(WCHAR)); |
| |
| button_hwnd = GetDlgItem(hwnd, ID_AXINSTALL_INSTALL_BTN); |
| EnableWindow(button_hwnd, TRUE); |
| }else { |
| WCHAR buf[100]; |
| LoadStringW(urlmon_instance, IDS_AXINSTALL_INSTALLN, buf, sizeof(buf)/sizeof(WCHAR)); |
| sprintfW(text, buf, ctx->counter); |
| } |
| |
| SetDlgItemTextW(hwnd, ID_AXINSTALL_INSTALL_BTN, text); |
| } |
| |
| static BOOL init_warning_dialog(HWND hwnd, install_ctx_t *ctx) |
| { |
| BSTR display_uri; |
| HRESULT hres; |
| |
| if(!SetPropW(hwnd, ctxW, ctx)) |
| return FALSE; |
| |
| hres = IUri_GetDisplayUri(ctx->uri, &display_uri); |
| if(FAILED(hres)) |
| return FALSE; |
| |
| SetDlgItemTextW(hwnd, ID_AXINSTALL_LOCATION, display_uri); |
| SysFreeString(display_uri); |
| |
| SendDlgItemMessageW(hwnd, ID_AXINSTALL_ICON, STM_SETICON, |
| (WPARAM)LoadIconW(0, (const WCHAR*)OIC_WARNING), 0); |
| |
| ctx->counter = 4; |
| update_counter(ctx, hwnd); |
| ctx->timer = SetTimer(hwnd, 1, 1000, NULL); |
| return TRUE; |
| } |
| |
| static INT_PTR WINAPI warning_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
| { |
| switch(msg) { |
| case WM_INITDIALOG: { |
| if(!init_warning_dialog(hwnd, (install_ctx_t*)lparam)) |
| EndDialog(hwnd, 0); |
| return TRUE; |
| } |
| case WM_COMMAND: |
| switch(wparam) { |
| case ID_AXINSTALL_INSTALL_BTN: { |
| install_ctx_t *ctx = GetPropW(hwnd, ctxW); |
| if(ctx) |
| ctx->cancel = FALSE; |
| EndDialog(hwnd, 0); |
| return FALSE; |
| } |
| case IDCANCEL: |
| EndDialog(hwnd, 0); |
| return FALSE; |
| } |
| case WM_TIMER: |
| update_counter(GetPropW(hwnd, ctxW), hwnd); |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static BOOL install_warning(install_ctx_t *ctx) |
| { |
| IWindowForBindingUI *window_iface; |
| HWND parent_hwnd = NULL; |
| HRESULT hres; |
| |
| if(!ctx->callback) { |
| FIXME("no callback\n"); |
| return FALSE; |
| } |
| |
| hres = IBindStatusCallback_QueryInterface(ctx->callback, &IID_IWindowForBindingUI, (void**)&window_iface); |
| if(FAILED(hres)) |
| return FALSE; |
| |
| hres = IWindowForBindingUI_GetWindow(window_iface, &IID_ICodeInstall, &ctx->hwnd); |
| IWindowForBindingUI_Release(window_iface); |
| if(FAILED(hres)) |
| return FALSE; |
| |
| ctx->cancel = TRUE; |
| DialogBoxParamW(urlmon_instance, MAKEINTRESOURCEW(ID_AXINSTALL_WARNING_DLG), parent_hwnd, warning_proc, (LPARAM)ctx); |
| return !ctx->cancel; |
| } |
| |
| static HRESULT install_file(install_ctx_t *ctx, const WCHAR *cache_file) |
| { |
| BSTR path; |
| HRESULT hres; |
| |
| TRACE("%s\n", debugstr_w(cache_file)); |
| |
| ctx->cache_file = cache_file; |
| |
| if(!install_warning(ctx)) { |
| TRACE("Installation cancelled\n"); |
| return S_OK; |
| } |
| |
| hres = IUri_GetPath(ctx->uri, &path); |
| if(SUCCEEDED(hres)) { |
| const WCHAR *ptr, *ptr2, *ext; |
| |
| ptr = strrchrW(path, '/'); |
| if(!ptr) |
| ptr = path; |
| else |
| ptr++; |
| |
| ptr2 = strrchrW(ptr, '\\'); |
| if(ptr2) |
| ptr = ptr2+1; |
| |
| ctx->file_name = ptr; |
| ext = strrchrW(ptr, '.'); |
| if(!ext) |
| ext = ptr; |
| |
| if(!strcmpiW(ext, cab_extW)) { |
| hres = install_cab_file(ctx); |
| }else { |
| FIXME("Unsupported extension %s\n", debugstr_w(ext)); |
| hres = E_NOTIMPL; |
| } |
| SysFreeString(path); |
| } |
| |
| return hres; |
| } |
| |
| static void failure_msgbox(install_ctx_t *ctx, HRESULT hres) |
| { |
| WCHAR buf[1024], fmt[1024]; |
| |
| LoadStringW(urlmon_instance, IDS_AXINSTALL_FAILURE, fmt, sizeof(fmt)/sizeof(WCHAR)); |
| sprintfW(buf, fmt, hres); |
| MessageBoxW(ctx->hwnd, buf, NULL, MB_OK); |
| } |
| |
| static HRESULT distunit_on_stop(void *ctx, const WCHAR *cache_file, HRESULT hresult, const WCHAR *error_str) |
| { |
| install_ctx_t *install_ctx = ctx; |
| |
| TRACE("(%p %s %08x %s)\n", ctx, debugstr_w(cache_file), hresult, debugstr_w(error_str)); |
| |
| if(hresult == S_OK) { |
| hresult = install_file(install_ctx, cache_file); |
| if(FAILED(hresult)) |
| failure_msgbox(ctx, hresult); |
| } |
| |
| if(install_ctx->callback) |
| IBindStatusCallback_OnStopBinding(install_ctx->callback, hresult, error_str); |
| |
| if(install_ctx->release_on_stop) |
| release_install_ctx(install_ctx); |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * AsyncInstallDistributionUnit (URLMON.@) |
| */ |
| HRESULT WINAPI AsyncInstallDistributionUnit(const WCHAR *szDistUnit, const WCHAR *szTYPE, const WCHAR *szExt, |
| DWORD dwFileVersionMS, DWORD dwFileVersionLS, const WCHAR *szURL, IBindCtx *pbc, void *pvReserved, DWORD flags) |
| { |
| install_ctx_t *ctx; |
| HRESULT hres; |
| |
| TRACE("(%s %s %s %x %x %s %p %p %x)\n", debugstr_w(szDistUnit), debugstr_w(szTYPE), debugstr_w(szExt), |
| dwFileVersionMS, dwFileVersionLS, debugstr_w(szURL), pbc, pvReserved, flags); |
| |
| if(szDistUnit || szTYPE || szExt) |
| FIXME("Unsupported arguments\n"); |
| |
| ctx = heap_alloc_zero(sizeof(*ctx)); |
| if(!ctx) |
| return E_OUTOFMEMORY; |
| |
| hres = CreateUri(szURL, 0, 0, &ctx->uri); |
| if(FAILED(hres)) { |
| heap_free(ctx); |
| return E_OUTOFMEMORY; |
| } |
| |
| ctx->callback = bsc_from_bctx(pbc); |
| |
| hres = download_to_cache(ctx->uri, distunit_on_stop, ctx, ctx->callback); |
| if(hres == MK_S_ASYNCHRONOUS) |
| ctx->release_on_stop = TRUE; |
| else |
| release_install_ctx(ctx); |
| |
| return hres; |
| } |