/* * Copyright 2018 Piotr 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 * */ #include <stdarg.h> #include <windef.h> #include <winbase.h> #include <winnls.h> #include <wininet.h> #include <winuser.h> #include <winreg.h> #include "inetcpl.h" #include "wine/debug.h" #include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(inetcpl); static const WCHAR internet_settings[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; static BOOL initdialog_done; #define CONNECTION_SETTINGS_VERSION 0x46 #define CONNECTION_SETTINGS_MANUAL_PROXY 0x2 #define CONNECTION_SETTINGS_PAC_SCRIPT 0x4 #define CONNECTION_SETTINGS_WPAD 0x8 typedef struct { DWORD version; DWORD id; DWORD flags; BYTE data[1]; /* DWORD proxy_server_len; */ /* UTF8 proxy_server[proxy_server_len]; */ /* DWORD bypass_list_len; */ /* UTF8 bypass_list[bypass_list_len]; */ /* DWORD configuration_script_len; */ /* UTF8 configuration_script[configuration_script_len]; */ /* DWORD unk[8]; set to 0 */ } connection_settings; static DWORD create_connection_settings(BOOL manual_proxy, const WCHAR *proxy_server, BOOL use_wpad, BOOL use_pac_script, const WCHAR *pac_url, connection_settings **ret) { DWORD size = FIELD_OFFSET(connection_settings, data), pos; DWORD proxy_server_len; DWORD pac_url_len; size += sizeof(DWORD); if(L"ProxyServer") { proxy_server_len = WideCharToMultiByte(CP_UTF8, 0, proxy_server, -1, NULL, 0, NULL, NULL); if(!proxy_server_len) return 0; proxy_server_len--; } else proxy_server_len = 0; size += proxy_server_len; if(pac_url) { pac_url_len = WideCharToMultiByte(CP_UTF8, 0, pac_url, -1, NULL, 0, NULL, NULL); if(!pac_url_len) return 0; pac_url_len--; } else pac_url_len = 0; size += sizeof(DWORD)*10; *ret = heap_alloc_zero(size); if(!*ret) return 0; (*ret)->version = CONNECTION_SETTINGS_VERSION; (*ret)->flags = 1; if(manual_proxy) (*ret)->flags |= CONNECTION_SETTINGS_MANUAL_PROXY; if(use_pac_script) (*ret)->flags |= CONNECTION_SETTINGS_PAC_SCRIPT; if(use_wpad) (*ret)->flags |= CONNECTION_SETTINGS_WPAD; ((DWORD*)(*ret)->data)[0] = proxy_server_len; pos = sizeof(DWORD); if(proxy_server_len) { WideCharToMultiByte(CP_UTF8, 0, proxy_server, -1, (char*)(*ret)->data+pos, proxy_server_len, NULL, NULL); pos += proxy_server_len; } pos += sizeof(DWORD); /* skip proxy bypass list */ ((DWORD*)((*ret)->data+pos))[0] = pac_url_len; pos += sizeof(DWORD); if(pac_url_len) { WideCharToMultiByte(CP_UTF8, 0, pac_url, -1, (char*)(*ret)->data+pos, pac_url_len, NULL, NULL); pos += pac_url_len; } return size; } static void connections_on_initdialog(HWND hwnd) { DWORD type, size, enabled; WCHAR address[INTERNET_MAX_URL_LENGTH], *port; WCHAR pac_url[INTERNET_MAX_URL_LENGTH]; HKEY hkey, con; LONG res; SendMessageW(GetDlgItem(hwnd, IDC_EDIT_PAC_SCRIPT), EM_LIMITTEXT, INTERNET_MAX_URL_LENGTH, 0); SendMessageW(GetDlgItem(hwnd, IDC_EDIT_PROXY_SERVER), EM_LIMITTEXT, INTERNET_MAX_URL_LENGTH-10, 0); SendMessageW(GetDlgItem(hwnd, IDC_EDIT_PROXY_PORT), EM_LIMITTEXT, 8, 0); res = RegOpenKeyW(HKEY_CURRENT_USER, internet_settings, &hkey); if(res) return; size = sizeof(enabled); res = RegQueryValueExW(hkey, L"ProxyEnable", NULL, &type, (BYTE*)&enabled, &size); if(res || type != REG_DWORD) enabled = 0; size = sizeof(address); res = RegQueryValueExW(hkey, L"ProxyServer", NULL, &type, (BYTE*)address, &size); if(res || type != REG_SZ) address[0] = 0; size = sizeof(pac_url); res = RegQueryValueExW(hkey, L"AutoConfigURL", NULL, &type, (BYTE*)pac_url, &size); if(res || type != REG_SZ) pac_url[0] = 0; res = RegOpenKeyW(hkey, L"Connections", &con); RegCloseKey(hkey); if(!res) { connection_settings *settings = NULL; size = 0; while((res = RegQueryValueExW(con, L"DefaultConnectionSettings", NULL, &type, (BYTE*)settings, &size)) == ERROR_MORE_DATA || !settings) { connection_settings *new_settings = heap_realloc(settings, size); if(!new_settings) { RegCloseKey(con); heap_free(settings); return; } settings = new_settings; } RegCloseKey(con); if(!res && type == REG_BINARY) { if(settings->version != CONNECTION_SETTINGS_VERSION) FIXME("unexpected structure version (%x)\n", settings->version); else if(settings->flags & CONNECTION_SETTINGS_WPAD) CheckDlgButton(hwnd, IDC_USE_WPAD, BST_CHECKED); } heap_free(settings); } TRACE("ProxyEnable = %x\n", enabled); TRACE("ProxyServer = %s\n", wine_dbgstr_w(address)); TRACE("AutoConfigURL = %s\n", wine_dbgstr_w(pac_url)); if(enabled) { CheckDlgButton(hwnd, IDC_USE_PROXY_SERVER, BST_CHECKED); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PROXY_SERVER), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PROXY_PORT), TRUE); } port = wcschr(address, ':'); if(port) { *port = 0; port++; } SetDlgItemTextW(hwnd, IDC_EDIT_PROXY_SERVER, address); if(port) SetDlgItemTextW(hwnd, IDC_EDIT_PROXY_PORT, port); if(pac_url[0]) { CheckDlgButton(hwnd, IDC_USE_PAC_SCRIPT, BST_CHECKED); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PAC_SCRIPT), TRUE); SetDlgItemTextW(hwnd, IDC_EDIT_PAC_SCRIPT, pac_url); } return; } static INT_PTR connections_on_command(HWND hwnd, WPARAM wparam) { BOOL checked; switch (wparam) { case IDC_USE_PAC_SCRIPT: checked = IsDlgButtonChecked(hwnd, IDC_USE_PAC_SCRIPT); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PAC_SCRIPT), checked); break; case IDC_USE_PROXY_SERVER: checked = IsDlgButtonChecked(hwnd, IDC_USE_PROXY_SERVER); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PROXY_SERVER), checked); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_PROXY_PORT), checked); } switch (wparam) { case IDC_USE_WPAD: case IDC_USE_PAC_SCRIPT: case IDC_USE_PROXY_SERVER: case MAKEWPARAM(IDC_EDIT_PAC_SCRIPT, EN_CHANGE): case MAKEWPARAM(IDC_EDIT_PROXY_SERVER, EN_CHANGE): case MAKEWPARAM(IDC_EDIT_PROXY_PORT, EN_CHANGE): if(initdialog_done) SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); return TRUE; } return FALSE; } static INT_PTR connections_on_notify(HWND hwnd, WPARAM wparam, LPARAM lparam) { connection_settings *default_connection; WCHAR proxy[INTERNET_MAX_URL_LENGTH]; WCHAR pac_script[INTERNET_MAX_URL_LENGTH]; PSHNOTIFY *psn = (PSHNOTIFY*)lparam; DWORD proxy_len, port_len, pac_script_len; DWORD use_proxy, use_pac_script, use_wpad, size; LRESULT res; HKEY hkey, con; if(psn->hdr.code != PSN_APPLY) return FALSE; res = RegOpenKeyW(HKEY_CURRENT_USER, internet_settings, &hkey); if(res) return FALSE; use_proxy = IsDlgButtonChecked(hwnd, IDC_USE_PROXY_SERVER); res = RegSetValueExW(hkey, L"ProxyEnable", 0, REG_DWORD, (BYTE*)&use_proxy, sizeof(use_proxy)); if(res) { RegCloseKey(hkey); return FALSE; } TRACE("ProxyEnable set to %x\n", use_proxy); proxy_len = GetDlgItemTextW(hwnd, IDC_EDIT_PROXY_SERVER, proxy, ARRAY_SIZE(proxy)); if(proxy_len) { proxy[proxy_len++] = ':'; port_len = GetDlgItemTextW(hwnd, IDC_EDIT_PROXY_PORT, proxy+proxy_len, ARRAY_SIZE(proxy)-proxy_len); if(!port_len) { proxy[proxy_len++] = '8'; proxy[proxy_len++] = '0'; proxy[proxy_len] = 0; } res = RegSetValueExW(hkey, L"ProxyServer", 0, REG_SZ, (BYTE*)proxy, (proxy_len+port_len)*sizeof(WCHAR)); } else { res = RegDeleteValueW(hkey, L"ProxyServer"); if(res == ERROR_FILE_NOT_FOUND) res = ERROR_SUCCESS; } if(res) { RegCloseKey(hkey); return FALSE; } TRACE("ProxtServer set to %s\n", wine_dbgstr_w(proxy)); use_pac_script = IsDlgButtonChecked(hwnd, IDC_USE_PAC_SCRIPT); pac_script_len = GetDlgItemTextW(hwnd, IDC_EDIT_PAC_SCRIPT, pac_script, ARRAY_SIZE(pac_script)); if(!pac_script_len) use_pac_script = FALSE; if(use_pac_script) { res = RegSetValueExW(hkey, L"AutoConfigURL", 0, REG_SZ, (BYTE*)pac_script, pac_script_len*sizeof(WCHAR)); } else { res = RegDeleteValueW(hkey, L"AutoConfigURL"); if(res == ERROR_FILE_NOT_FOUND) res = ERROR_SUCCESS; } if(res) { RegCloseKey(hkey); return FALSE; } TRACE("AutoConfigURL set to %s\n", wine_dbgstr_w(use_pac_script ? pac_script : NULL)); use_wpad = IsDlgButtonChecked(hwnd, IDC_USE_WPAD); res = RegCreateKeyExW(hkey, L"Connections", 0, NULL, 0, KEY_WRITE, NULL, &con, NULL); RegCloseKey(hkey); if(res) return FALSE; size = create_connection_settings(use_proxy, proxy, use_wpad, use_pac_script, pac_script, &default_connection); if(!size) { RegCloseKey(con); return FALSE; } res = RegSetValueExW(con, L"DefaultConnectionSettings", 0, REG_BINARY, (BYTE*)default_connection, size); heap_free(default_connection); RegCloseKey(con); return !res; } INT_PTR CALLBACK connections_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_INITDIALOG: connections_on_initdialog(hwnd); initdialog_done = TRUE; break; case WM_COMMAND: return connections_on_command(hwnd, wparam); case WM_NOTIFY: return connections_on_notify(hwnd, wparam, lparam); } return FALSE; }