/* * OpenVPN-GUI -- A Windows GUI for OpenVPN. * * Copyright (C) 2004 Mathias Sundman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #if !defined (UNICODE) #error UNICODE and _UNICODE must be defined. This version only supports unicode builds. #endif #include #include #include #include #include #include #include "tray.h" #include "openvpn.h" #include "openvpn_config.h" #include "viewlog.h" #include "service.h" #include "main.h" #include "options.h" #include "proxy.h" #include "registry.h" #include "openvpn-gui-res.h" #include "localization.h" #include "manage.h" #include "misc.h" #include "save_pass.h" #include "echo.h" #include "as.h" #define OVPN_EXITCODE_ERROR 1 #define OVPN_EXITCODE_TIMEOUT 2 #define OVPN_EXITCODE_NOTREADY 3 /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM); static void ShowSettingsDialog(); void CloseApplication(HWND hwnd, BOOL ask_user); void ImportConfigFileFromDisk(); void ImportConfigFromAS(); static void SaveAutoRestartList(); static void LoadAutoRestartList(); /* Class name and window title */ TCHAR szClassName[] = _T("OpenVPN-GUI"); TCHAR szTitleText[] = _T("OpenVPN"); /* Options structure */ options_t o; /* Workaround for ASLR on Windows */ __declspec(dllexport) char aslr_workaround; /* globals */ static HWND settings_window; /* Handle of Settings window */ static int VerifyAutoConnections() { int i; for (i = 0; i < o.num_auto_connect; i++) { if (GetConnByName(o.auto_connect[i]) == NULL) { /* autostart config not found */ ShowLocalizedMsg(IDS_ERR_AUTOSTART_CONF, o.auto_connect[i]); return FALSE; } } return TRUE; } /* * Send a copydata message corresponding to any --command action option specified * to the running instance and return success or error. */ static int NotifyRunningInstance() { /* Check if a previous instance has a window initialized * Even if we are not the first instance this may return null * if the previous instance has not fully started up */ HANDLE hwnd_master = FindWindow(szClassName, NULL); int exit_code = 0; if (hwnd_master) { /* GUI up and running -- send a message if any action is pecified, * else show the balloon */ COPYDATASTRUCT config_data = {0}; int timeout = 30*1000; /* 30 seconds */ if (!o.action) { o.action = WM_OVPN_NOTIFY; o.action_arg = LoadLocalizedString(IDS_NFO_CLICK_HERE_TO_START); } config_data.dwData = o.action; if (o.action_arg) { config_data.cbData = (wcslen(o.action_arg)+1)*sizeof(o.action_arg[0]); config_data.lpData = (void *) o.action_arg; } PrintDebug(L"Instance 2: called with action %d : %ls", o.action, o.action_arg); if (!SendMessageTimeout(hwnd_master, WM_COPYDATA, 0, (LPARAM) &config_data, 0, timeout, NULL)) { DWORD error = GetLastError(); if (error == ERROR_TIMEOUT) { exit_code = OVPN_EXITCODE_TIMEOUT; MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Sending command to running instance timed out."); } else { exit_code = OVPN_EXITCODE_ERROR; MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Sending command to running instance failed (error = %lu).", error); } } } else { /* An instance is already running but its main window not yet initialized */ exit_code = OVPN_EXITCODE_NOTREADY; MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Previous instance not yet ready to accept commands. " "Try again later."); } return exit_code; } int WINAPI _tWinMain(HINSTANCE hThisInstance, UNUSED HINSTANCE hPrevInstance, UNUSED LPTSTR lpszArgument, UNUSED int nCmdShow) { MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ DWORD shell32_version; BOOL first_instance = TRUE; /* a session local semaphore to detect second instance */ HANDLE session_semaphore = InitSemaphore(L"Local\\"PACKAGE_NAME); srand(time(NULL)); /* try to lock the semaphore, else we are not the first instance */ if (session_semaphore && WaitForSingleObject(session_semaphore, 200) != WAIT_OBJECT_0) { first_instance = FALSE; } /* Initialize handlers for manangement interface notifications */ mgmt_rtmsg_handler handler[] = { { ready_, OnReady }, { hold_, OnHold }, { log_, OnLogLine }, { state_, OnStateChange }, { password_, OnPassword }, { proxy_, OnProxy }, { stop_, OnStop }, { needok_, OnNeedOk }, { needstr_, OnNeedStr }, { echo_, OnEcho }, { bytecount_, OnByteCount }, { infomsg_, OnInfoMsg }, { timeout_, OnTimeout }, { 0, NULL } }; InitManagement(handler); /* initialize options to default state */ InitOptions(&o); if (first_instance) { o.session_semaphore = session_semaphore; } #ifdef DEBUG /* Open debug file for output */ if (!(o.debug_fp = _wfopen(DEBUG_FILE, L"a+,ccs=UTF-8"))) { /* can't open debug file */ ShowLocalizedMsg(IDS_ERR_OPEN_DEBUG_FILE, DEBUG_FILE); exit(1); } PrintDebug(_T("Starting OpenVPN GUI v%hs"), PACKAGE_VERSION); #endif o.hInstance = hThisInstance; if (!GetModuleHandle(_T("RICHED20.DLL"))) { LoadLibrary(_T("RICHED20.DLL")); } else { /* can't load riched20.dll */ ShowLocalizedMsg(IDS_ERR_LOAD_RICHED20); exit(1); } /* Check version of shell32.dll */ shell32_version = GetDllVersion(_T("shell32.dll")); if (shell32_version < PACKVERSION(5, 0)) { /* shell32.dll version to low */ ShowLocalizedMsg(IDS_ERR_SHELL_DLL_VERSION, shell32_version); exit(1); } #ifdef DEBUG PrintDebug(_T("Shell32.dll version: 0x%lx"), shell32_version); #endif if (first_instance) { UpdateRegistry(); /* Checks version change and update keys/values */ } GetRegistryKeys(); /* Parse command-line options */ ProcessCommandLine(&o, GetCommandLine()); EnsureDirExists(o.config_dir); if (!first_instance) { int res = NotifyRunningInstance(); exit(res); } else if (o.action == WM_OVPN_START) { PrintDebug(L"Instance 1: Called with --command connect xxx. Treating it as --connect xxx"); } else if (o.action == WM_OVPN_IMPORT) { /* pass -- import is handled after Window initialization */ } else if (o.action) { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Called with --command when no previous instance available"); exit(OVPN_EXITCODE_ERROR); } if (!CheckVersion()) { exit(1); } if (!EnsureDirExists(o.log_dir)) { ShowLocalizedMsg(IDS_ERR_CREATE_PATH, _T("log_dir"), o.log_dir); exit(1); } BOOL use_iservice = (o.iservice_admin && IsWindows7OrGreater()) || !IsUserAdmin(); if (use_iservice && strtod(o.ovpn_version, NULL) > 2.3 && !o.silent_connection) { CheckIServiceStatus(TRUE); } CheckServiceStatus(); /* Check if automatic service is running or not */ BuildFileList(); if (!VerifyAutoConnections()) { exit(1); } GetProxyRegistrySettings(); /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof(WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadLocalizedIcon(ID_ICO_APP); wincl.hIconSm = LoadLocalizedIcon(ID_ICO_APP); wincl.hCursor = LoadCursor(NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default color as the background of the window */ wincl.hbrBackground = (HBRUSH) COLOR_3DSHADOW; /*COLOR_BACKGROUND; */ /* Register the window class, and if it fails quit the program */ if (!RegisterClassEx(&wincl)) { return 1; } /* The class is registered, let's create the program*/ CreateWindowEx( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ szTitleText, /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ (int)CW_USEDEFAULT, /* Windows decides the position */ (int)CW_USEDEFAULT, /* where the window ends up on the screen */ 230, /* The programs width */ 200, /* and height in pixels */ HWND_DESKTOP, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage(&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } CloseSemaphore(o.session_semaphore); o.session_semaphore = NULL; /* though we're going to die.. */ if (o.event_log) { DeregisterEventSource(o.event_log); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; } static void StopAllOpenVPN(bool exiting) { int i; if (exiting) { RemoveTrayIcon(); } /* Stop all connections started by us -- we leave persistent ones * at their current state. Use the disconnect menu to put them into * hold state before exit, if desired. */ for (connection_t *c = o.chead; c; c = c->next) { if (c->state != disconnected && c->state != detached) { if (c->flags & FLAG_DAEMON_PERSISTENT) { DetachOpenVPN(c); } else { StopOpenVPN(c); } } } /* Wait for all connections to terminate (Max 20 rounds of 250 msec = 5 sec) */ for (i = 0; i < 20; i++) { if (CountConnState(disconnected) + CountConnState(detached) == o.num_configs || !OVPNMsgWait(250, NULL)) /* Quit received */ { break; } } } static int AutoStartConnections() { for (connection_t *c = o.chead; c; c = c->next) { if (c->auto_connect && !(c->flags & FLAG_DAEMON_PERSISTENT)) { StartOpenVPN(c); } } return TRUE; } static void ResumeConnections() { for (connection_t *c = o.chead; c; c = c->next) { /* Restart suspend connections */ if (c->state == suspended) { StartOpenVPN(c); } /* If some connection never reached SUSPENDED state */ if (c->state == suspending) { StopOpenVPN(c); } } } static int HandleCopyDataMessage(const COPYDATASTRUCT *copy_data) { WCHAR *str = NULL; connection_t *c = NULL; PrintDebug(L"WM_COPYDATA message received. (dwData: %lu, cbData: %lu, lpData: %ls)", copy_data->dwData, copy_data->cbData, copy_data->lpData); if (copy_data->cbData >= sizeof(WCHAR) && copy_data->lpData) { str = (WCHAR *) copy_data->lpData; str[copy_data->cbData/sizeof(WCHAR)-1] = L'\0'; /* in case not nul-terminated */ c = GetConnByName(str); } if (copy_data->dwData == WM_OVPN_START && c) { if (!o.silent_connection) { ForceForegroundWindow(o.hWnd); } StartOpenVPN(c); } else if (copy_data->dwData == WM_OVPN_STOP && c) { StopOpenVPN(c); } else if (copy_data->dwData == WM_OVPN_RESTART && c) { if (!o.silent_connection) { ForceForegroundWindow(o.hWnd); } RestartOpenVPN(c); } else if (copy_data->dwData == WM_OVPN_SHOWSTATUS && c && c->hwndStatus) { ForceForegroundWindow(o.hWnd); ShowWindow(c->hwndStatus, SW_SHOW); } else if (copy_data->dwData == WM_OVPN_STOPALL) { StopAllOpenVPN(false); } else if (copy_data->dwData == WM_OVPN_SILENT && str) { if (_wtoi(str) == 0) { o.silent_connection = 0; } else { o.silent_connection = 1; } } else if (copy_data->dwData == WM_OVPN_EXIT) { CloseApplication(o.hWnd, true); } else if (copy_data->dwData == WM_OVPN_IMPORT && str) { ImportConfigFile(str, true); /* prompt user */ } else if (copy_data->dwData == WM_OVPN_NOTIFY) { ShowTrayBalloon(L"", copy_data->lpData); } else if (copy_data->dwData == WM_OVPN_RESCAN) { OnNotifyTray(WM_OVPN_RESCAN); } else { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Unknown WM_COPYDATA message ignored. (dwData: %lu, cbData: %lu, lpData: %ls)", copy_data->dwData, copy_data->cbData, copy_data->lpData); return FALSE; } return TRUE; /* indicate we handled the message */ } /* If automatic service is running, check whether we are * attached to the management i/f of persistent daemons * and re-attach if necessary. The timer is reset to * call this routine again after a delay. Periodic check * is required as we get detached if the daemon gets restarted * by the service or we do a disconnect. */ static void CALLBACK ManagePersistent(HWND hwnd, UINT UNUSED msg, UINT_PTR id, DWORD UNUSED now) { CheckServiceStatus(); if (o.service_state == service_connected) { for (connection_t *c = o.chead; c; c = c->next) { if (c->flags & FLAG_DAEMON_PERSISTENT && c->auto_connect && (c->state == disconnected || c->state == detached)) { /* disable auto-connect to avoid repeated re-connect * after unrecoverable errors. Re-enabled on successful * connect. */ c->auto_connect = false; c->state = detached; /* this is required to retain management-hold on re-attach */ StartOpenVPN(c); /* attach to the management i/f */ } } } /* schedule to call again after 10 sec */ SetTimer(hwnd, id, 10000, ManagePersistent); } /* Detach from the mgmt i/f of all atatched persistent * connections. Called on session lock so that another * user can attach to these. */ static void HandleSessionLock(void) { for (connection_t *c = o.chead; c; c = c->next) { if (c->flags & FLAG_DAEMON_PERSISTENT && (c->state != disconnected && c->state != detached)) { c->auto_connect = false; DetachOpenVPN(c); c->flags |= FLAG_WAIT_UNLOCK; } } } /* Undo any actions done at session lock/disconnect. */ void HandleSessionUnlock(void) { for (connection_t *c = o.chead; c; c = c->next) { if (c->flags & FLAG_WAIT_UNLOCK) { c->auto_connect = true; /* so that ManagePersistent will trigger attach */ c->flags &= ~FLAG_WAIT_UNLOCK; } } } /* This function is called by the Windows function DispatchMessage() */ LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static UINT s_uTaskbarRestart; MENUINFO minfo = {.cbSize = sizeof(MENUINFO)}; connection_t *c = NULL; switch (message) { case WM_CREATE: /* Save Window Handle */ o.hWnd = hwnd; dpi_initialize(&o); s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION); /* Load application icon */ HICON hIcon = LoadLocalizedIcon(ID_ICO_APP); if (hIcon) { SendMessage(hwnd, WM_SETICON, (WPARAM) (ICON_SMALL), (LPARAM) (hIcon)); SendMessage(hwnd, WM_SETICON, (WPARAM) (ICON_BIG), (LPARAM) (hIcon)); } /* Enable next line to accept WM_COPYDATA messages from lower level processes */ #if 0 ChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, MSGFLT_ALLOW, NULL); #endif echo_msg_init(); CreatePopupMenus(); /* Create popup menus */ ShowTrayIcon(); /* if '--import' was specified, do it now */ if (o.action == WM_OVPN_IMPORT && o.action_arg) { ImportConfigFile(o.action_arg, true); /* prompt user */ } if (o.enable_auto_restart) { LoadAutoRestartList(); } if (!AutoStartConnections()) { SendMessage(hwnd, WM_CLOSE, 0, 0); break; } /* A timer to periodically tend to persistent connections */ SetTimer(hwnd, 1, 100, ManagePersistent); break; case WM_NOTIFYICONTRAY: OnNotifyTray(lParam); /* Manages message from tray */ break; case WM_COPYDATA: /* custom messages with data from other processes */ HandleCopyDataMessage((COPYDATASTRUCT *) lParam); return TRUE; /* lets the sender free copy_data */ case WM_MENUCOMMAND: /* Get the menu item id and save it in wParam for use below */ wParam = GetMenuItemID((HMENU) lParam, wParam); /* we first check global menu items which do not require a connnection index */ if (LOWORD(wParam) == IDM_IMPORT_FILE) { ImportConfigFileFromDisk(); } else if (LOWORD(wParam) == IDM_IMPORT_AS) { ImportConfigFromAS(); } else if (LOWORD(wParam) == IDM_IMPORT_URL) { ImportConfigFromURL(); } else if (LOWORD(wParam) == IDM_SETTINGS) { ShowSettingsDialog(); } else if (LOWORD(wParam) == IDM_CLOSE) { CloseApplication(hwnd, true); } /* rest of the handlers require a connection id */ else { minfo.fMask = MIM_MENUDATA; GetMenuInfo((HMENU) lParam, &minfo); c = (connection_t *) minfo.dwMenuData; if (!c) { break; /* ignore invalid connection */ } } /* reach here only if the command did not match any global items and a valid connection id is available */ if (LOWORD(wParam) == IDM_CONNECTMENU) { StartOpenVPN(c); } else if (LOWORD(wParam) == IDM_DISCONNECTMENU) { StopOpenVPN(c); } else if (LOWORD(wParam) == IDM_RECONNECTMENU) { RestartOpenVPN(c); } else if (LOWORD(wParam) == IDM_STATUSMENU) { ShowWindow(c->hwndStatus, SW_SHOW); } else if (LOWORD(wParam) == IDM_VIEWLOGMENU) { ViewLog(c); } else if (LOWORD(wParam) == IDM_EDITMENU) { EditConfig(c); } else if (LOWORD(wParam) == IDM_CLEARPASSMENU) { ResetSavePasswords(c); } break; case WM_CLOSE: CloseApplication(hwnd, false); /* do not wait for user confirmation */ break; case WM_DESTROY: WTSUnRegisterSessionNotification(hwnd); StopAllOpenVPN(true); OnDestroyTray(); /* Remove Tray Icon and destroy menus */ PostQuitMessage(0); /* Send a WM_QUIT to the message queue */ break; case WM_QUERYENDSESSION: return(TRUE); case WM_ENDSESSION: SaveAutoRestartList(); StopAllOpenVPN(true); OnDestroyTray(); break; case WM_WTSSESSION_CHANGE: switch (wParam) { case WTS_SESSION_LOCK: PrintDebug(L"Session lock triggered"); o.session_locked = TRUE; /* Detach persistent connections so that other users can connect to it */ HandleSessionLock(); KillTimer(hwnd, 1); /* This ensure ManagePersistent is not called when session is locked */ break; case WTS_SESSION_UNLOCK: PrintDebug(L"Session unlock triggered"); o.session_locked = FALSE; HandleSessionUnlock(); SetTimer(hwnd, 1, 100, ManagePersistent); if (CountConnState(suspended) != 0) { ResumeConnections(); } break; default: PrintDebug(L"Session change with wParam = %lu", wParam); break; } break; default: /* for messages that we don't deal with */ if (message == s_uTaskbarRestart) { /* Explorer has restarted, re-register the tray icon. */ ShowTrayIcon(); CheckAndSetTrayIcon(); break; } return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } static INT_PTR CALLBACK AboutDialogFunc(UNUSED HWND hDlg, UINT msg, UNUSED WPARAM wParam, LPARAM lParam) { LPPSHNOTIFY psn; wchar_t tmp1[300], tmp2[300]; switch (msg) { case WM_INITDIALOG: if (GetDlgItemText(hDlg, ID_TXT_VERSION, tmp1, _countof(tmp1))) { _sntprintf_0(tmp2, tmp1, TEXT(PACKAGE_VERSION_RESOURCE_STR)); SetDlgItemText(hDlg, ID_TXT_VERSION, tmp2); } /* Modify the ABOUT3 line that reads as "OpenVPN ... " by * including the version, like "OpenVPN v2.5.8 ... ". * The logic used depends on this text starting with * "OpenVPN", which is the case in all languages. */ const wchar_t *prefix = L"OpenVPN "; if (GetDlgItemText(hDlg, ID_LTEXT_ABOUT3, tmp1, _countof(tmp1)) && wcsbegins(tmp1, prefix)) { _sntprintf_0(tmp2, L"%lsv%hs %ls", prefix, o.ovpn_version, tmp1 + wcslen(prefix)); SetDlgItemText(hDlg, ID_LTEXT_ABOUT3, tmp2); } break; case WM_NOTIFY: psn = (LPPSHNOTIFY) lParam; if (psn->hdr.code == (UINT) PSN_APPLY) { return TRUE; } } return FALSE; } static int CALLBACK SettingsPsCallback(HWND hwnd, UINT msg, UNUSED LPARAM lParam) { switch (msg) { case PSCB_INITIALIZED: settings_window = hwnd; break; } return 0; } static void ShowSettingsDialog() { PROPSHEETPAGE psp[4]; int page_number = 0; if (settings_window && IsWindow(settings_window)) { SetForegroundWindow(settings_window); ShowWindow(settings_window, SW_SHOW); return; } /* General tab */ psp[page_number].dwSize = sizeof(PROPSHEETPAGE); psp[page_number].dwFlags = PSP_DLGINDIRECT; psp[page_number].hInstance = o.hInstance; psp[page_number].pResource = LocalizedDialogResource(ID_DLG_GENERAL); psp[page_number].pfnDlgProc = GeneralSettingsDlgProc; psp[page_number].lParam = 0; psp[page_number].pfnCallback = NULL; ++page_number; /* Proxy tab */ psp[page_number].dwSize = sizeof(PROPSHEETPAGE); psp[page_number].dwFlags = PSP_DLGINDIRECT; psp[page_number].hInstance = o.hInstance; psp[page_number].pResource = LocalizedDialogResource(ID_DLG_PROXY); psp[page_number].pfnDlgProc = ProxySettingsDialogFunc; psp[page_number].lParam = 0; psp[page_number].pfnCallback = NULL; ++page_number; /* Advanced tab */ psp[page_number].dwSize = sizeof(PROPSHEETPAGE); psp[page_number].dwFlags = PSP_DLGINDIRECT; psp[page_number].hInstance = o.hInstance; psp[page_number].pResource = LocalizedDialogResource(ID_DLG_ADVANCED); psp[page_number].pfnDlgProc = AdvancedSettingsDlgProc; psp[page_number].lParam = 0; psp[page_number].pfnCallback = NULL; ++page_number; /* About tab */ psp[page_number].dwSize = sizeof(PROPSHEETPAGE); psp[page_number].dwFlags = PSP_DLGINDIRECT; psp[page_number].hInstance = o.hInstance; psp[page_number].pResource = LocalizedDialogResource(ID_DLG_ABOUT); psp[page_number].pfnDlgProc = AboutDialogFunc; psp[page_number].lParam = 0; psp[page_number].pfnCallback = NULL; ++page_number; PROPSHEETHEADER psh; psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_USEHICON | PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USECALLBACK; psh.hwndParent = o.hWnd; psh.hInstance = o.hInstance; psh.hIcon = LoadLocalizedIcon(ID_ICO_APP); psh.pszCaption = LoadLocalizedString(IDS_SETTINGS_CAPTION); psh.nPages = page_number; psh.nStartPage = 0; psh.ppsp = (LPCPROPSHEETPAGE) &psp; psh.pfnCallback = SettingsPsCallback; PropertySheet(&psh); } void CloseApplication(HWND hwnd, BOOL ask_user) { /* Do not let user access main menu through tray icon */ RemoveTrayIcon(); /* Show a message if any non-persistent connections are active */ for (connection_t *c = o.chead; c && ask_user; c = c->next) { if (c->state == disconnected || c->flags & FLAG_DAEMON_PERSISTENT) { continue; } /* Ask for confirmation if still connected */ if (ShowLocalizedMsgEx(MB_YESNO|MB_TOPMOST, o.hWnd, _T("Exit OpenVPN"), IDS_NFO_ACTIVE_CONN_EXIT) == IDNO) { /* recreate the tray icon */ ShowTrayIcon(); CheckAndSetTrayIcon(); return; } break; /* show the above message box only once */ } SaveAutoRestartList(); /* active connection names saved in registry */ DestroyWindow(hwnd); } void ImportConfigFileFromDisk() { TCHAR filter[2*_countof(o.ext_string)+5]; _sntprintf_0(filter, _T("*.%ls%lc*.%ls%lc"), o.ext_string, _T('\0'), o.ext_string, _T('\0')); OPENFILENAME fn; TCHAR source[MAX_PATH] = _T(""); fn.lStructSize = sizeof(OPENFILENAME); fn.hwndOwner = NULL; fn.lpstrFilter = filter; fn.lpstrCustomFilter = NULL; fn.nFilterIndex = 1; fn.lpstrFile = source; fn.nMaxFile = MAX_PATH; fn.lpstrFileTitle = NULL; fn.lpstrInitialDir = NULL; fn.lpstrTitle = NULL; fn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST; fn.lpstrDefExt = NULL; if (GetOpenFileName(&fn)) { ImportConfigFile(source, false); /* do not prompt user */ } } #ifdef DEBUG void PrintDebugMsg(TCHAR *msg) { time_t log_time; struct tm *time_struct; TCHAR date[30]; log_time = time(NULL); time_struct = localtime(&log_time); _sntprintf(date, _countof(date), _T("%d-%.2d-%.2d %.2d:%.2d:%.2d"), time_struct->tm_year + 1900, time_struct->tm_mon + 1, time_struct->tm_mday, time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec); _ftprintf(o.debug_fp, _T("%ls %ls\n"), date, msg); fflush(o.debug_fp); } #endif /* ifdef DEBUG */ #define PACKVERSION(major, minor) MAKELONG(minor, major) DWORD GetDllVersion(LPCTSTR lpszDllName) { HINSTANCE hinstDll; DWORD dwVersion = 0; /* For security purposes, LoadLibrary should be provided with a * fully-qualified path to the DLL. The lpszDllName variable should be * tested to ensure that it is a fully qualified path before it is used. */ hinstDll = LoadLibrary(lpszDllName); if (hinstDll) { DLLGETVERSIONPROC pDllGetVersion; pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll, "DllGetVersion"); /* Because some DLLs might not implement this function, you * must test for it explicitly. Depending on the particular * DLL, the lack of a DllGetVersion function can be a useful * indicator of the version. */ if (pDllGetVersion) { DLLVERSIONINFO dvi; HRESULT hr; ZeroMemory(&dvi, sizeof(dvi)); dvi.cbSize = sizeof(dvi); hr = (*pDllGetVersion)(&dvi); if (SUCCEEDED(hr)) { dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion); } } FreeLibrary(hinstDll); } return dwVersion; } void ErrorExit(int exit_code, const wchar_t *msg) { if (msg) { MessageBoxExW(NULL, msg, TEXT(PACKAGE_NAME), MB_OK | MB_SETFOREGROUND | MB_ICONERROR | MBOX_RTL_FLAGS, GetGUILanguage()); } if (o.hWnd) { StopAllOpenVPN(true); PostQuitMessage(exit_code); } else { exit(exit_code); } } /** * Save a list of active connections in registry. If enable_auto_restart * is false, any previously saved list is deleted and no new list is saved. * Do not show any messages here as this may be called on receiving * WM_ENDSESSION (user logout). */ static void SaveAutoRestartList() { size_t len = 0; int nactive = 0; int max_active = o.num_configs - CountConnState(disconnected); if (!o.enable_auto_restart || max_active <= 0) { /* delete the list -- on load this gets treated as an empty list */ RegDeleteKeyValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list"); return; } connection_t **active_conns = malloc((size_t) max_active*sizeof(connection_t *)); if (!active_conns) { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while persisting state in registry"); return; } for (connection_t *c = o.chead; c && nactive < max_active; c = c->next) { if (c->state == disconnected || c->flags & FLAG_DAEMON_PERSISTENT) { continue; } /* accumulate space needed for list of active connections */ len += wcslen(c->config_name) + 1; active_conns[nactive++] = c; } len++; /* for double nul termination */ if (len == 1) { len++; /* two nuls for empty string */ } /* Make a double nul terminated list of active connections */ wchar_t *list = calloc(len, sizeof(wchar_t)); if (!list) { free(active_conns); MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while persisting state in registry"); return; } wchar_t *p = list; for (int i = 0; i < nactive; i++) { connection_t *c = active_conns[i]; wcscpy(p, c->config_name); /* wcscpy is safe here */ p += wcslen(c->config_name) + 1; } /* Save the list in registry for auto-connect on restart */ LSTATUS status = RegSetKeyValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", REG_MULTI_SZ, list, (DWORD) len*sizeof(wchar_t)); if (status != ERROR_SUCCESS) { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"RegSetKeyValue returned error: status = %lu", status); } free(active_conns); free(list); } /* * Read list of active connections in last session and set them to auto-start */ static void LoadAutoRestartList() { wchar_t *list; DWORD len = 0; LSTATUS status = RegGetValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", RRF_RT_REG_MULTI_SZ, NULL, NULL, &len); if (status != ERROR_SUCCESS || len == 0) { return; } list = malloc(len); if (!list) { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory while reading state from registry"); return; } status = RegGetValueW(HKEY_CURRENT_USER, GUI_REGKEY_HKCU, L"auto_restart_list", RRF_RT_REG_MULTI_SZ, NULL, list, &len); if (status != ERROR_SUCCESS) { MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Error reading state from registry"); free(list); return; } for (wchar_t *p = list; *p; p += wcslen(p) + 1) { connection_t *c = GetConnByName(p); if (!c || c->flags & FLAG_DAEMON_PERSISTENT) { continue; } c->auto_connect = true; } }