Use a list instead of array for connections list

Currently we use an array of connection pointers which needs
to be reallocated when space runs out. But, that happens from
the main thread while the status thread may be referring to those
pointers. Its very hard to fence against possible invalid memory
access. Instead, use a list so that connection pointer never
changes once created.

The connection list is no longer recreated from scratch even when
no connections are active. This means configs added while GUI is
running will always appear at the bottom of the root group listing
until the GUI is restarted.

TODO: This behaviour could be improved by scanning through the groups to
graft new configs at the right branch in the config-group tree.

v2: removed unused references to SetMenuStatusById()

Signed-off-by: Selva Nair <selva.nair@gmail.com>
pull/589/head
Selva Nair 2023-01-14 10:34:11 -05:00
parent 8a4fec9d13
commit 9417991168
11 changed files with 165 additions and 191 deletions

109
main.c
View File

@ -358,17 +358,17 @@ StopAllOpenVPN()
* at their current state. Use the disconnect menu to put them into * at their current state. Use the disconnect menu to put them into
* hold state before exit, if desired. * hold state before exit, if desired.
*/ */
for (i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].state != disconnected && o.conn[i].state != detached) if (c->state != disconnected && c->state != detached)
{ {
if (o.conn[i].flags & FLAG_DAEMON_PERSISTENT) if (c->flags & FLAG_DAEMON_PERSISTENT)
{ {
DetachOpenVPN(&o.conn[i]); DetachOpenVPN(c);
} }
else else
{ {
StopOpenVPN(&o.conn[i]); StopOpenVPN(c);
} }
} }
} }
@ -385,12 +385,10 @@ StopAllOpenVPN()
static int static int
AutoStartConnections() AutoStartConnections()
{ {
int i; for (connection_t *c = o.chead; c; c = c->next)
for (i = 0; i < o.num_configs; i++)
{ {
if (o.conn[i].auto_connect && !(o.conn[i].flags & FLAG_DAEMON_PERSISTENT)) if (c->auto_connect && !(c->flags & FLAG_DAEMON_PERSISTENT))
StartOpenVPN(&o.conn[i]); StartOpenVPN(c);
} }
return TRUE; return TRUE;
@ -400,15 +398,15 @@ AutoStartConnections()
static void static void
ResumeConnections() ResumeConnections()
{ {
int i; for (connection_t *c = o.chead; c; c = c->next)
for (i = 0; i < o.num_configs; i++) { {
/* Restart suspend connections */ /* Restart suspend connections */
if (o.conn[i].state == suspended) if (c->state == suspended)
StartOpenVPN(&o.conn[i]); StartOpenVPN(c);
/* If some connection never reached SUSPENDED state */ /* If some connection never reached SUSPENDED state */
if (o.conn[i].state == suspending) if (c->state == suspending)
StopOpenVPN(&o.conn[i]); StopOpenVPN(c);
} }
} }
@ -492,19 +490,19 @@ ManagePersistent(HWND hwnd, UINT UNUSED msg, UINT_PTR id, DWORD UNUSED now)
CheckServiceStatus(); CheckServiceStatus();
if (o.service_state == service_connected) if (o.service_state == service_connected)
{ {
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].flags & FLAG_DAEMON_PERSISTENT if (c->flags & FLAG_DAEMON_PERSISTENT
&& o.conn[i].auto_connect && c->auto_connect
&& (o.conn[i].state == disconnected || o.conn[i].state == detached)) && (c->state == disconnected || c->state == detached))
{ {
/* disable auto-connect to avoid repeated re-connect /* disable auto-connect to avoid repeated re-connect
* after unrecoverable errors. Re-enabled on successful * after unrecoverable errors. Re-enabled on successful
* connect. * connect.
*/ */
o.conn[i].auto_connect = false; c->auto_connect = false;
o.conn[i].state = detached; /* this is required to retain management-hold on re-attach */ c->state = detached; /* this is required to retain management-hold on re-attach */
StartOpenVPN(&o.conn[i]); /* attach to the management i/f */ StartOpenVPN(c); /* attach to the management i/f */
} }
} }
} }
@ -519,14 +517,14 @@ ManagePersistent(HWND hwnd, UINT UNUSED msg, UINT_PTR id, DWORD UNUSED now)
static void static void
HandleSessionLock(void) HandleSessionLock(void)
{ {
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].flags & FLAG_DAEMON_PERSISTENT if (c->flags & FLAG_DAEMON_PERSISTENT
&& (o.conn[i].state != disconnected && o.conn[i].state != detached)) && (c->state != disconnected && c->state != detached))
{ {
o.conn[i].auto_connect = false; c->auto_connect = false;
DetachOpenVPN(&o.conn[i]); DetachOpenVPN(c);
o.conn[i].flags |= FLAG_WAIT_UNLOCK; c->flags |= FLAG_WAIT_UNLOCK;
} }
} }
} }
@ -536,12 +534,12 @@ HandleSessionLock(void)
void void
HandleSessionUnlock(void) HandleSessionUnlock(void)
{ {
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].flags & FLAG_WAIT_UNLOCK) if (c->flags & FLAG_WAIT_UNLOCK)
{ {
o.conn[i].auto_connect = true; /* so that ManagePersistent will trigger attach */ c->auto_connect = true; /* so that ManagePersistent will trigger attach */
o.conn[i].flags &= ~ FLAG_WAIT_UNLOCK; c->flags &= ~ FLAG_WAIT_UNLOCK;
} }
} }
} }
@ -550,8 +548,8 @@ HandleSessionUnlock(void)
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
static UINT s_uTaskbarRestart; static UINT s_uTaskbarRestart;
int conn_id = 0;
MENUINFO minfo = {.cbSize = sizeof(MENUINFO)}; MENUINFO minfo = {.cbSize = sizeof(MENUINFO)};
connection_t *c = NULL;
switch (message) { switch (message) {
case WM_CREATE: case WM_CREATE:
@ -633,36 +631,37 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
else { else {
minfo.fMask = MIM_MENUDATA; minfo.fMask = MIM_MENUDATA;
GetMenuInfo((HMENU) lParam, &minfo); GetMenuInfo((HMENU) lParam, &minfo);
conn_id = (INT) minfo.dwMenuData; c = (connection_t *) minfo.dwMenuData;
if (conn_id < 0 || conn_id >= o.num_configs) break; /* ignore invalid connection id */ 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 */ /* reach here only if the command did not match any global items and a valid connection id is available */
if (LOWORD(wParam) == IDM_CONNECTMENU) { if (LOWORD(wParam) == IDM_CONNECTMENU) {
StartOpenVPN(&o.conn[conn_id]); StartOpenVPN(c);
} }
else if (LOWORD(wParam) == IDM_DISCONNECTMENU) { else if (LOWORD(wParam) == IDM_DISCONNECTMENU) {
StopOpenVPN(&o.conn[conn_id]); StopOpenVPN(c);
} }
else if (LOWORD(wParam) == IDM_RECONNECTMENU) { else if (LOWORD(wParam) == IDM_RECONNECTMENU) {
RestartOpenVPN(&o.conn[conn_id]); RestartOpenVPN(c);
} }
else if (LOWORD(wParam) == IDM_STATUSMENU) { else if (LOWORD(wParam) == IDM_STATUSMENU) {
ShowWindow(o.conn[conn_id].hwndStatus, SW_SHOW); ShowWindow(c->hwndStatus, SW_SHOW);
} }
else if (LOWORD(wParam) == IDM_VIEWLOGMENU) { else if (LOWORD(wParam) == IDM_VIEWLOGMENU) {
ViewLog(conn_id); ViewLog(c);
} }
else if (LOWORD(wParam) == IDM_EDITMENU) { else if (LOWORD(wParam) == IDM_EDITMENU) {
EditConfig(conn_id); EditConfig(c);
} }
else if (LOWORD(wParam) == IDM_CLEARPASSMENU) { else if (LOWORD(wParam) == IDM_CLEARPASSMENU) {
ResetSavePasswords(&o.conn[conn_id]); ResetSavePasswords(c);
} }
#ifndef DISABLE_CHANGE_PASSWORD #ifndef DISABLE_CHANGE_PASSWORD
else if (LOWORD(wParam) == IDM_PASSPHRASEMENU) { else if (LOWORD(wParam) == IDM_PASSPHRASEMENU) {
ShowChangePassphraseDialog(&o.conn[conn_id]); ShowChangePassphraseDialog(c);
} }
#endif #endif
break; break;
@ -825,13 +824,11 @@ ShowSettingsDialog()
void void
CloseApplication(HWND hwnd) CloseApplication(HWND hwnd)
{ {
int i;
/* Show a message if any non-persistent connections are active */ /* Show a message if any non-persistent connections are active */
for (i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].state == disconnected if (c->state == disconnected
|| o.conn[i].flags & FLAG_DAEMON_PERSISTENT) || c->flags & FLAG_DAEMON_PERSISTENT)
{ {
continue; continue;
} }
@ -980,7 +977,7 @@ SaveAutoRestartList()
return; return;
} }
int *active_conns = malloc((size_t) max_active*sizeof(int)); connection_t **active_conns = malloc((size_t) max_active*sizeof(connection_t *));
if (!active_conns) if (!active_conns)
{ {
@ -988,16 +985,16 @@ SaveAutoRestartList()
return; return;
} }
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c && nactive < max_active; c = c->next)
{ {
if (o.conn[i].state == disconnected if (c->state == disconnected
|| o.conn[i].flags & FLAG_DAEMON_PERSISTENT) || c->flags & FLAG_DAEMON_PERSISTENT)
{ {
continue; continue;
} }
/* accumulate space needed for list of active connections */ /* accumulate space needed for list of active connections */
len += wcslen(o.conn[i].config_name) + 1; len += wcslen(c->config_name) + 1;
active_conns[nactive++] = i; active_conns[nactive++] = c;
} }
len++; /* for double nul termination */ len++; /* for double nul termination */
@ -1015,7 +1012,7 @@ SaveAutoRestartList()
wchar_t *p = list; wchar_t *p = list;
for (int i = 0; i < nactive; i++) for (int i = 0; i < nactive; i++)
{ {
connection_t *c = &o.conn[active_conns[i]]; connection_t *c = active_conns[i];
wcscpy(p, c->config_name); /* wcscpy is safe here */ wcscpy(p, c->config_name); /* wcscpy is safe here */
p += wcslen(c->config_name) + 1; p += wcslen(c->config_name) + 1;
} }

View File

@ -2681,12 +2681,6 @@ TerminateOpenVPN (connection_t *c)
return retval; return retval;
} }
void
SuspendOpenVPN(int config)
{
PostMessage(o.conn[config].hwndStatus, WM_OVPN_SUSPEND, 0, 0);
}
void void
RestartOpenVPN(connection_t *c) RestartOpenVPN(connection_t *c)
{ {

View File

@ -79,22 +79,36 @@ CheckReadAccess (const TCHAR *dir, const TCHAR *file)
static int static int
ConfigAlreadyExists(TCHAR *newconfig) ConfigAlreadyExists(TCHAR *newconfig)
{ {
int i; for (connection_t *c = o.chead; c; c = c->next)
for (i = 0; i < o.num_configs; ++i)
{ {
if (_tcsicmp(o.conn[i].config_file, newconfig) == 0) if (_tcsicmp(c->config_file, newconfig) == 0)
return true; return true;
} }
return false; return false;
} }
static void static void
AddConfigFileToList(int config, const TCHAR *filename, const TCHAR *config_dir) AddConfigFileToList(int group, const TCHAR *filename, const TCHAR *config_dir)
{ {
connection_t *c = &o.conn[config]; connection_t *c = calloc(1, sizeof(connection_t));
int i;
memset(c, 0, sizeof(*c)); if (!c)
{
ErrorExit(1, L"Out of memory in AddConfigFileToList");
}
if (o.ctail)
{
o.ctail->next = c;
o.ctail = c;
}
else
{
o.chead = o.ctail = c;
}
c->id = o.num_configs++;
c->group = group;
_tcsncpy(c->config_file, filename, _countof(c->config_file) - 1); _tcsncpy(c->config_file, filename, _countof(c->config_file) - 1);
_tcsncpy(c->config_dir, config_dir, _countof(c->config_dir) - 1); _tcsncpy(c->config_dir, config_dir, _countof(c->config_dir) - 1);
@ -105,7 +119,7 @@ AddConfigFileToList(int config, const TCHAR *filename, const TCHAR *config_dir)
c->manage.sk = INVALID_SOCKET; c->manage.sk = INVALID_SOCKET;
c->manage.skaddr.sin_family = AF_INET; c->manage.skaddr.sin_family = AF_INET;
c->manage.skaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); c->manage.skaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
c->manage.skaddr.sin_port = htons(o.mgmt_port_offset + config); c->manage.skaddr.sin_port = htons(o.mgmt_port_offset + c->id);
#ifndef DISABLE_CHANGE_PASSWORD #ifndef DISABLE_CHANGE_PASSWORD
if (CheckKeyFileWriteAccess (c)) if (CheckKeyFileWriteAccess (c))
@ -123,7 +137,7 @@ AddConfigFileToList(int config, const TCHAR *filename, const TCHAR *config_dir)
} }
/* Check if connection should be autostarted */ /* Check if connection should be autostarted */
for (i = 0; i < o.num_auto_connect; ++i) for (int i = 0; i < o.num_auto_connect; ++i)
{ {
if (_tcsicmp(c->config_file, o.auto_connect[i]) == 0 if (_tcsicmp(c->config_file, o.auto_connect[i]) == 0
|| _tcsicmp(c->config_name, o.auto_connect[i]) == 0) || _tcsicmp(c->config_name, o.auto_connect[i]) == 0)
@ -211,9 +225,9 @@ ActivateConfigGroups(void)
/* count children of each group -- this includes groups /* count children of each group -- this includes groups
* and configs which have it as parent * and configs which have it as parent
*/ */
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
CONFIG_GROUP(&o.conn[i])->children++; CONFIG_GROUP(c)->children++;
} }
for (int i = 1; i < o.num_groups; i++) for (int i = 1; i < o.num_groups; i++)
@ -235,23 +249,23 @@ ActivateConfigGroups(void)
* script etc.) in a separate directory, without making the menu structure * script etc.) in a separate directory, without making the menu structure
* too deeply nested. * too deeply nested.
*/ */
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
config_group_t *cg = CONFIG_GROUP(&o.conn[i]); config_group_t *cg = CONFIG_GROUP(c);
/* if not root and has only this config as child -- squash it */ /* if not root and has only this config as child -- squash it */
if (PARENT_GROUP(cg) && cg->children == 1 if (PARENT_GROUP(cg) && cg->children == 1
&& !wcscmp(cg->name, o.conn[i].config_name)) && !wcscmp(cg->name, c->config_name))
{ {
cg->children--; cg->children--;
o.conn[i].group = cg->parent; c->group = cg->parent;
} }
} }
/* activate all groups that connect a config to the root */ /* activate all groups that connect a config to the root */
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
config_group_t *cg = CONFIG_GROUP(&o.conn[i]); config_group_t *cg = CONFIG_GROUP(c);
while (cg) while (cg)
{ {
@ -287,19 +301,6 @@ BuildFileList0(const TCHAR *config_dir, int recurse_depth, int group, int flags)
/* Loop over each config file in config dir */ /* Loop over each config file in config dir */
do do
{ {
if (!o.conn || o.num_configs == o.max_configs)
{
o.max_configs += 50;
void *tmp = realloc(o.conn, sizeof(*o.conn)*o.max_configs);
if (!tmp)
{
o.max_configs -= 50;
FindClose(find_handle);
ErrorExit(1, L"Out of memory while scanning configs");
}
o.conn = tmp;
}
match_t match_type = match(&find_obj, o.ext_string); match_t match_type = match(&find_obj, o.ext_string);
if (match_type == match_file) if (match_type == match_file)
{ {
@ -312,8 +313,7 @@ BuildFileList0(const TCHAR *config_dir, int recurse_depth, int group, int flags)
if (CheckReadAccess (config_dir, find_obj.cFileName)) if (CheckReadAccess (config_dir, find_obj.cFileName))
{ {
AddConfigFileToList(o.num_configs, find_obj.cFileName, config_dir); AddConfigFileToList(group, find_obj.cFileName, config_dir);
o.conn[o.num_configs++].group = group;
} }
} }
} while (FindNextFile(find_handle, &find_obj)); } while (FindNextFile(find_handle, &find_obj));
@ -419,17 +419,12 @@ BuildFileList()
issue_warnings = false; issue_warnings = false;
/* /*
* If no connections are active reset num_configs and rescan * If first time or no entries in the connection list reset groups and rescan
* to make a new list. Else we keep all current configs and * to make a new list. Else we keep all current configs and
* rescan to add any new one's found. * rescan to add any new one's found.
* Do the same when persistent connections are in auto-attach mode,
* to avoid over-writing their status info such as auto_connect=false
* after manual detach.
*/ */
if (!o.num_groups if (!o.num_configs)
|| (CountConnState(disconnected) == o.num_configs && o.enable_persistent != 2))
{ {
o.num_configs = 0;
o.num_groups = 0; o.num_groups = 0;
flags |= FLAG_ADD_CONFIG_GROUPS; flags |= FLAG_ADD_CONFIG_GROUPS;
root_gp = NewConfigGroup(L"ROOT", -1, flags); /* -1 indicates no parent */ root_gp = NewConfigGroup(L"ROOT", -1, flags); /* -1 indicates no parent */

View File

@ -453,12 +453,11 @@ ProcessCommandLine(options_t *options, TCHAR *command_line)
int int
CountConnState(conn_state_t check) CountConnState(conn_state_t check)
{ {
int i;
int count = 0; int count = 0;
for (i = 0; i < o.num_configs; ++i) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].state == check) if (c->state == check)
++count; ++count;
} }
@ -468,11 +467,10 @@ CountConnState(conn_state_t check)
connection_t* connection_t*
GetConnByManagement(SOCKET sk) GetConnByManagement(SOCKET sk)
{ {
int i; for (connection_t *c = o.chead; c; c = c->next)
for (i = 0; i < o.num_configs; ++i)
{ {
if (o.conn[i].manage.sk == sk) if (c->manage.sk == sk)
return &o.conn[i]; return c;
} }
return NULL; return NULL;
} }
@ -480,11 +478,11 @@ GetConnByManagement(SOCKET sk)
connection_t* connection_t*
GetConnByName(const WCHAR *name) GetConnByName(const WCHAR *name)
{ {
for (int i = 0; i < o.num_configs; ++i) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (wcsicmp (o.conn[i].config_file, name) == 0 if (wcsicmp (c->config_file, name) == 0
|| wcsicmp(o.conn[i].config_name, name) == 0) || wcsicmp(c->config_name, name) == 0)
return &o.conn[i]; return c;
} }
return NULL; return NULL;
} }

View File

@ -167,6 +167,8 @@ struct connection {
struct echo_msg echo_msg; /* Message echo-ed from server or client config and related data */ struct echo_msg echo_msg; /* Message echo-ed from server or client config and related data */
struct pkcs11_list pkcs11_list; struct pkcs11_list pkcs11_list;
char daemon_state[20]; /* state of openvpn.ex: WAIT, AUTH, GET_CONFIG etc.. */ char daemon_state[20]; /* state of openvpn.ex: WAIT, AUTH, GET_CONFIG etc.. */
int id; /* index of config -- treat as immutable once assigned */
connection_t *next;
}; };
/* All options used within OpenVPN GUI */ /* All options used within OpenVPN GUI */
@ -175,7 +177,8 @@ typedef struct {
const TCHAR **auto_connect; const TCHAR **auto_connect;
/* Connection parameters */ /* Connection parameters */
connection_t *conn; /* Array of connection structure */ connection_t *chead; /* Head of connection list */
connection_t *ctail; /* Tail of connection list */
config_group_t *groups; /* Array of nodes defining the config groups tree */ config_group_t *groups; /* Array of nodes defining the config groups tree */
int num_configs; /* Number of configs */ int num_configs; /* Number of configs */
int num_auto_connect; /* Number of auto-connect configs */ int num_auto_connect; /* Number of auto-connect configs */

View File

@ -76,10 +76,6 @@ void SetMenuStatus(UNUSED connection_t *c, UNUSED conn_state_t state)
{ {
return; return;
} }
void SetMenuStatusById(UNUSED int id, UNUSED conn_state_t state)
{
return;
}
void SetServiceMenuStatus(void) void SetServiceMenuStatus(void)
{ {
return; return;

View File

@ -265,8 +265,9 @@ InitializeUI(HINSTANCE hinstance)
CheckServiceStatus(); CheckServiceStatus();
int num_persistent = 0; int num_persistent = 0;
for (int i = 0; i < o.num_configs; i++) { for (connection_t *c = o.chead; c; c = c->next)
if (o.conn[i].flags & FLAG_DAEMON_PERSISTENT) num_persistent++; {
if (c->flags & FLAG_DAEMON_PERSISTENT) num_persistent++;
} }
if (o.service_state == service_disconnected && num_persistent > 0) { if (o.service_state == service_disconnected && num_persistent > 0) {
@ -281,15 +282,14 @@ InitializeUI(HINSTANCE hinstance)
/* Returns number of PLAP enabled configs -- for now /* Returns number of PLAP enabled configs -- for now
* same as autostarted (persistent) connections. * same as autostarted (persistent) connections.
* The corresponding connection pointers are set in * The corresponding connection pointers are set in
* the conn[] array. * the conn[] array
*/ */
DWORD DWORD
FindPLAPConnections(connection_t *conn[], size_t max_count) FindPLAPConnections(connection_t *conn[], size_t max_count)
{ {
DWORD count = 0; DWORD count = 0;
for (int i = 0; i < o.num_configs && count < max_count; i++) for (connection_t *c = o.chead; c && count < max_count; c = c->next)
{ {
connection_t *c = &o.conn[i];
if (!(c->flags & FLAG_DAEMON_PERSISTENT) if (!(c->flags & FLAG_DAEMON_PERSISTENT)
|| !ParseManagementAddress(c)) || !ParseManagementAddress(c))
{ {
@ -390,11 +390,11 @@ DetachAllOpenVPN()
int i; int i;
/* Detach from the mgmt i/f of all connections */ /* Detach from the mgmt i/f of all connections */
for (i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].state != disconnected) if (c->state != disconnected)
{ {
DetachOpenVPN(&o.conn[i]); DetachOpenVPN(c);
} }
} }
@ -408,12 +408,12 @@ DetachAllOpenVPN()
Sleep(100); Sleep(100);
} }
for (i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
if (o.conn[i].hwndStatus) if (c->hwndStatus)
{ {
/* Status thread still running? kill it */ /* Status thread still running? kill it */
WaitOnThread(&o.conn[i], 0); WaitOnThread(c, 0);
} }
} }
} }

90
tray.c
View File

@ -161,13 +161,13 @@ CreatePopupMenus()
CreateMenuBitmaps(); CreateMenuBitmaps();
MENUINFO minfo = {.cbSize = sizeof(MENUINFO)}; MENUINFO minfo = {.cbSize = sizeof(MENUINFO)};
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
hMenuConn[i] = CreatePopupMenu(); hMenuConn[c->id] = CreatePopupMenu();
/* Save the connection index in the menu.*/ /* Save the connection index in the menu.*/
minfo.fMask = MIM_MENUDATA; minfo.fMask = MIM_MENUDATA;
minfo.dwMenuData = i; minfo.dwMenuData = (UINT_PTR) c;
SetMenuInfo(hMenuConn[i], &minfo); SetMenuInfo(hMenuConn[c->id], &minfo);
} }
for (int i = 0; i < o.num_groups; i++) for (int i = 0; i < o.num_groups; i++)
{ {
@ -185,7 +185,7 @@ CreatePopupMenus()
minfo.dwStyle |= MNS_NOTIFYBYPOS; minfo.dwStyle |= MNS_NOTIFYBYPOS;
SetMenuInfo(hMenu, &minfo); SetMenuInfo(hMenu, &minfo);
if (o.num_configs == 1) { if (o.num_configs == 1 && o.chead) {
/* Create Main menu with actions */ /* Create Main menu with actions */
AppendMenu(hMenu, MF_STRING, IDM_CONNECTMENU, LoadLocalizedString(IDS_MENU_CONNECT)); AppendMenu(hMenu, MF_STRING, IDM_CONNECTMENU, LoadLocalizedString(IDS_MENU_CONNECT));
AppendMenu(hMenu, MF_STRING, IDM_DISCONNECTMENU, LoadLocalizedString(IDS_MENU_DISCONNECT)); AppendMenu(hMenu, MF_STRING, IDM_DISCONNECTMENU, LoadLocalizedString(IDS_MENU_DISCONNECT));
@ -199,7 +199,7 @@ CreatePopupMenus()
AppendMenu(hMenu, MF_STRING, IDM_CLEARPASSMENU, LoadLocalizedString(IDS_MENU_CLEARPASS)); AppendMenu(hMenu, MF_STRING, IDM_CLEARPASSMENU, LoadLocalizedString(IDS_MENU_CLEARPASS));
#ifndef DISABLE_CHANGE_PASSWORD #ifndef DISABLE_CHANGE_PASSWORD
if (o.conn[0].flags & FLAG_ALLOW_CHANGE_PASSPHRASE) if (o.chead->flags & FLAG_ALLOW_CHANGE_PASSPHRASE)
AppendMenu(hMenu, MF_STRING, IDM_PASSPHRASEMENU, LoadLocalizedString(IDS_MENU_PASSPHRASE)); AppendMenu(hMenu, MF_STRING, IDM_PASSPHRASEMENU, LoadLocalizedString(IDS_MENU_PASSPHRASE));
#endif #endif
@ -214,7 +214,7 @@ CreatePopupMenus()
AppendMenu(hMenu, MF_STRING ,IDM_SETTINGS, LoadLocalizedString(IDS_MENU_SETTINGS)); AppendMenu(hMenu, MF_STRING ,IDM_SETTINGS, LoadLocalizedString(IDS_MENU_SETTINGS));
AppendMenu(hMenu, MF_STRING ,IDM_CLOSE, LoadLocalizedString(IDS_MENU_CLOSE)); AppendMenu(hMenu, MF_STRING ,IDM_CLOSE, LoadLocalizedString(IDS_MENU_CLOSE));
SetMenuStatusById(0, o.conn[0].state); SetMenuStatus(o.chead, o.chead->state);
} }
else { else {
/* construct the submenu tree first */ /* construct the submenu tree first */
@ -240,9 +240,8 @@ CreatePopupMenus()
} }
/* add config file (connection) entries */ /* add config file (connection) entries */
for (int i = 0; i < o.num_configs; i++) for (connection_t *c = o.chead; c; c = c->next)
{ {
connection_t *c = &o.conn[i];
config_group_t *parent = &o.groups[0]; /* by default config is added to the root */ config_group_t *parent = &o.groups[0]; /* by default config is added to the root */
if (USE_NESTED_CONFIG_MENU) if (USE_NESTED_CONFIG_MENU)
@ -257,11 +256,11 @@ CreatePopupMenus()
assert(parent); assert(parent);
/* Add config to the current sub menu */ /* Add config to the current sub menu */
AppendMenu(parent->menu, MF_POPUP, (UINT_PTR) hMenuConn[i], c->config_name); AppendMenu(parent->menu, MF_POPUP, (UINT_PTR) hMenuConn[c->id], c->config_name);
c->pos = parent->children++; c->pos = parent->children++;
PrintDebug(L"Config %d named %ls added to submenu %ls with position %d", PrintDebug(L"Config %d named %ls added to submenu %ls with position %d",
i, c->config_name, parent->name, c->pos); c->id, c->config_name, parent->name, c->pos);
} }
if (o.num_configs > 0) if (o.num_configs > 0)
@ -277,7 +276,9 @@ CreatePopupMenus()
AppendMenu(hMenu, MF_STRING, IDM_CLOSE, LoadLocalizedString(IDS_MENU_CLOSE)); AppendMenu(hMenu, MF_STRING, IDM_CLOSE, LoadLocalizedString(IDS_MENU_CLOSE));
/* Create popup menus for every connection */ /* Create popup menus for every connection */
for (int i = 0; i < o.num_configs; i++) { for (connection_t *c = o.chead; c; c = c->next)
{
int i = c->id;
AppendMenu(hMenuConn[i], MF_STRING, IDM_CONNECTMENU, LoadLocalizedString(IDS_MENU_CONNECT)); AppendMenu(hMenuConn[i], MF_STRING, IDM_CONNECTMENU, LoadLocalizedString(IDS_MENU_CONNECT));
AppendMenu(hMenuConn[i], MF_STRING, IDM_DISCONNECTMENU, LoadLocalizedString(IDS_MENU_DISCONNECT)); AppendMenu(hMenuConn[i], MF_STRING, IDM_DISCONNECTMENU, LoadLocalizedString(IDS_MENU_DISCONNECT));
AppendMenu(hMenuConn[i], MF_STRING, IDM_RECONNECTMENU, LoadLocalizedString(IDS_MENU_RECONNECT)); AppendMenu(hMenuConn[i], MF_STRING, IDM_RECONNECTMENU, LoadLocalizedString(IDS_MENU_RECONNECT));
@ -290,11 +291,11 @@ CreatePopupMenus()
AppendMenu(hMenuConn[i], MF_STRING, IDM_CLEARPASSMENU, LoadLocalizedString(IDS_MENU_CLEARPASS)); AppendMenu(hMenuConn[i], MF_STRING, IDM_CLEARPASSMENU, LoadLocalizedString(IDS_MENU_CLEARPASS));
#ifndef DISABLE_CHANGE_PASSWORD #ifndef DISABLE_CHANGE_PASSWORD
if (o.conn[i].flags & FLAG_ALLOW_CHANGE_PASSPHRASE) if (c->flags & FLAG_ALLOW_CHANGE_PASSPHRASE)
AppendMenu(hMenuConn[i], MF_STRING, IDM_PASSPHRASEMENU, LoadLocalizedString(IDS_MENU_PASSPHRASE)); AppendMenu(hMenuConn[i], MF_STRING, IDM_PASSPHRASEMENU, LoadLocalizedString(IDS_MENU_PASSPHRASE));
#endif #endif
SetMenuStatusById(i, o.conn[i].state); SetMenuStatus(c, c->state);
} }
} }
} }
@ -304,9 +305,10 @@ CreatePopupMenus()
static void static void
DestroyPopupMenus() DestroyPopupMenus()
{ {
int i; for (connection_t *c = o.chead; c; c = c->next)
for (i = 0; i < o.num_configs; i++) {
DestroyMenu(hMenuConn[i]); DestroyMenu(hMenuConn[c->id]);
}
DestroyMenu(hMenuImport); DestroyMenu(hMenuImport);
DestroyMenu(hMenu); DestroyMenu(hMenu);
@ -349,16 +351,16 @@ OnNotifyTray(LPARAM lParam)
RecreatePopupMenus(); RecreatePopupMenus();
/* Start connection if only one config exist */ /* Start connection if only one config exist */
if (o.num_configs == 1 && o.conn[0].state == disconnected) if (o.num_configs == 1 && o.chead->state == disconnected)
StartOpenVPN(&o.conn[0]); StartOpenVPN(o.chead);
/* show the status window of all connected/connecting profiles upto a max of 10 */ /* show the status window of all connected/connecting profiles upto a max of 10 */
else if (disconnected_conns < o.num_configs) { else if (disconnected_conns < o.num_configs) {
int i;
int num_shown = 0; int num_shown = 0;
for (i = 0; i < o.num_configs; i++) { for (connection_t *c = o.chead; c; c = c->next)
if (o.conn[i].state != disconnected) { {
ShowWindow(o.conn[i].hwndStatus, SW_SHOW); if (c->state != disconnected && c->hwndStatus) {
SetForegroundWindow(o.conn[i].hwndStatus); ShowWindow(c->hwndStatus, SW_SHOW);
SetForegroundWindow(c->hwndStatus);
if (++num_shown >= 10) break; if (++num_shown >= 10) break;
} }
} }
@ -402,47 +404,48 @@ SetTrayIcon(conn_state_t state)
TCHAR msg[500]; TCHAR msg[500];
TCHAR msg_connected[100]; TCHAR msg_connected[100];
TCHAR msg_connecting[100]; TCHAR msg_connecting[100];
int i, config = 0;
BOOL first_conn; BOOL first_conn;
UINT icon_id; UINT icon_id;
connection_t *cc = NULL; /* a connected config */
_tcsncpy(msg, LoadLocalizedString(IDS_TIP_DEFAULT), _countof(ni.szTip)); _tcsncpy(msg, LoadLocalizedString(IDS_TIP_DEFAULT), _countof(ni.szTip));
_tcsncpy(msg_connected, LoadLocalizedString(IDS_TIP_CONNECTED), _countof(msg_connected)); _tcsncpy(msg_connected, LoadLocalizedString(IDS_TIP_CONNECTED), _countof(msg_connected));
_tcsncpy(msg_connecting, LoadLocalizedString(IDS_TIP_CONNECTING), _countof(msg_connecting)); _tcsncpy(msg_connecting, LoadLocalizedString(IDS_TIP_CONNECTING), _countof(msg_connecting));
first_conn = TRUE; first_conn = TRUE;
for (i = 0; i < o.num_configs; i++) { for (connection_t *c = o.chead; c; c = c->next)
if (o.conn[i].state == connected) { {
if (c->state == connected) {
/* Append connection name to Icon Tip Msg */ /* Append connection name to Icon Tip Msg */
_tcsncat(msg, (first_conn ? msg_connected : _T(", ")), _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, (first_conn ? msg_connected : _T(", ")), _countof(msg) - _tcslen(msg) - 1);
_tcsncat(msg, o.conn[i].config_name, _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, c->config_name, _countof(msg) - _tcslen(msg) - 1);
first_conn = FALSE; first_conn = FALSE;
config = i; cc = c;
} }
} }
first_conn = TRUE; first_conn = TRUE;
for (i = 0; i < o.num_configs; i++) { for (connection_t *c = o.chead; c; c = c->next)
if (o.conn[i].state == connecting || o.conn[i].state == resuming || o.conn[i].state == reconnecting) { {
if (c->state == connecting || c->state == resuming || c->state == reconnecting) {
/* Append connection name to Icon Tip Msg */ /* Append connection name to Icon Tip Msg */
_tcsncat(msg, (first_conn ? msg_connecting : _T(", ")), _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, (first_conn ? msg_connecting : _T(", ")), _countof(msg) - _tcslen(msg) - 1);
_tcsncat(msg, o.conn[i].config_name, _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, c->config_name, _countof(msg) - _tcslen(msg) - 1);
first_conn = FALSE; first_conn = FALSE;
} }
} }
if (CountConnState(connected) == 1) { if (CountConnState(connected) == 1 && cc) {
/* Append "Connected since and assigned IP" to message */ /* Append "Connected since and assigned IP" to message */
const connection_t *c = &o.conn[config];
TCHAR time[50]; TCHAR time[50];
LocalizedTime(o.conn[config].connected_since, time, _countof(time)); LocalizedTime(cc->connected_since, time, _countof(time));
_tcsncat(msg, LoadLocalizedString(IDS_TIP_CONNECTED_SINCE), _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, LoadLocalizedString(IDS_TIP_CONNECTED_SINCE), _countof(msg) - _tcslen(msg) - 1);
_tcsncat(msg, time, _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, time, _countof(msg) - _tcslen(msg) - 1);
/* concatenate ipv4 and ipv6 addresses into one string */ /* concatenate ipv4 and ipv6 addresses into one string */
WCHAR ip[64]; WCHAR ip[64];
wcs_concat2(ip, _countof(ip), c->ip, c->ipv6, L", "); wcs_concat2(ip, _countof(ip), cc->ip, cc->ipv6, L", ");
WCHAR *assigned_ip = LoadLocalizedString(IDS_TIP_ASSIGNED_IP, ip); WCHAR *assigned_ip = LoadLocalizedString(IDS_TIP_ASSIGNED_IP, ip);
_tcsncat(msg, assigned_ip, _countof(msg) - _tcslen(msg) - 1); _tcsncat(msg, assigned_ip, _countof(msg) - _tcslen(msg) - 1);
} }
@ -502,21 +505,8 @@ ShowTrayBalloon(TCHAR *infotitle_msg, TCHAR *info_msg)
void void
SetMenuStatus(connection_t *c, conn_state_t state) SetMenuStatus(connection_t *c, conn_state_t state)
{ {
int i;
for (i = 0; i < o.num_configs; ++i)
{
if (c == &o.conn[i])
break;
}
SetMenuStatusById(i, state);
}
void
SetMenuStatusById(int i, conn_state_t state)
{
connection_t *c = &o.conn[i];
int checked = 0; int checked = 0;
int i = c->id;
if (state == connected || state == disconnecting) checked = 1; if (state == connected || state == disconnecting) checked = 1;
else if (state != disconnected && state != detached && state != onhold) checked = 2; else if (state != disconnected && state != detached && state != onhold) checked = 2;

1
tray.h
View File

@ -52,7 +52,6 @@ void OnDestroyTray(void);
void ShowTrayIcon(); void ShowTrayIcon();
void SetTrayIcon(conn_state_t); void SetTrayIcon(conn_state_t);
void SetMenuStatus(connection_t *, conn_state_t); void SetMenuStatus(connection_t *, conn_state_t);
void SetMenuStatusById(int, conn_state_t);
void SetServiceMenuStatus(); void SetServiceMenuStatus();
void ShowTrayBalloon(TCHAR *, TCHAR *); void ShowTrayBalloon(TCHAR *, TCHAR *);
void CheckAndSetTrayIcon(); void CheckAndSetTrayIcon();

View File

@ -37,7 +37,7 @@
extern options_t o; extern options_t o;
void ViewLog(int config) void ViewLog(connection_t *c)
{ {
TCHAR filename[2*MAX_PATH]; TCHAR filename[2*MAX_PATH];
@ -54,15 +54,15 @@ void ViewLog(int config)
/* Try first using file association */ /* Try first using file association */
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); /* Safe to init COM multiple times */ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); /* Safe to init COM multiple times */
status = ShellExecuteW (o.hWnd, L"open", o.conn[config].log_path, NULL, o.log_dir, SW_SHOWNORMAL); status = ShellExecuteW (o.hWnd, L"open", c->log_path, NULL, o.log_dir, SW_SHOWNORMAL);
if (status > (HINSTANCE) 32) /* Success */ if (status > (HINSTANCE) 32) /* Success */
return; return;
else else
PrintDebug (L"Opening log file using ShellExecute with verb = open failed" PrintDebug (L"Opening log file using ShellExecute with verb = open failed"
" for config '%ls' (status = %lu)", o.conn[config].config_name, status); " for config '%ls' (status = %lu)", c->config_name, status);
_sntprintf_0(filename, _T("%ls \"%ls\""), o.log_viewer, o.conn[config].log_path); _sntprintf_0(filename, _T("%ls \"%ls\""), o.log_viewer, c->log_path);
/* fill in STARTUPINFO struct */ /* fill in STARTUPINFO struct */
GetStartupInfo(&start_info); GetStartupInfo(&start_info);
@ -92,7 +92,7 @@ void ViewLog(int config)
} }
void EditConfig(int config) void EditConfig(connection_t *c)
{ {
TCHAR filename[2*MAX_PATH]; TCHAR filename[2*MAX_PATH];
@ -108,17 +108,17 @@ void EditConfig(int config)
CLEAR (sd); CLEAR (sd);
/* Try first using file association */ /* Try first using file association */
_sntprintf_0(filename, L"%ls\\%ls", o.conn[config].config_dir, o.conn[config].config_file); _sntprintf_0(filename, L"%ls\\%ls", c->config_dir, c->config_file);
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); /* Safe to init COM multiple times */ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); /* Safe to init COM multiple times */
status = ShellExecuteW (o.hWnd, L"open", filename, NULL, o.conn[config].config_dir, SW_SHOWNORMAL); status = ShellExecuteW (o.hWnd, L"open", filename, NULL, c->config_dir, SW_SHOWNORMAL);
if (status > (HINSTANCE) 32) if (status > (HINSTANCE) 32)
return; return;
else else
PrintDebug (L"Opening config file using ShellExecute with verb = open failed" PrintDebug (L"Opening config file using ShellExecute with verb = open failed"
" for config '%ls' (status = %lu)", o.conn[config].config_name, status); " for config '%ls' (status = %lu)", c->config_name, status);
_sntprintf_0(filename, _T("%ls \"%ls\\%ls\""), o.editor, o.conn[config].config_dir, o.conn[config].config_file); _sntprintf_0(filename, _T("%ls \"%ls\\%ls\""), o.editor, c->config_dir, c->config_file);
/* fill in STARTUPINFO struct */ /* fill in STARTUPINFO struct */
GetStartupInfo(&start_info); GetStartupInfo(&start_info);
@ -135,7 +135,7 @@ void EditConfig(int config)
TRUE, TRUE,
CREATE_NEW_CONSOLE, CREATE_NEW_CONSOLE,
NULL, NULL,
o.conn[config].config_dir, //start-up dir c->config_dir, //start-up dir
&start_info, &start_info,
&proc_info)) &proc_info))
{ {

View File

@ -19,5 +19,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
void ViewLog(int config); struct connection;
void EditConfig(int config);
void ViewLog(struct connection *c);
void EditConfig(struct connection *c);