mirror of https://github.com/OpenVPN/openvpn-gui
Group configs based on the directory structure to support a nested view
- Group all configs in a subdirectory with directory name as the label. - If any connection is active, newly found configs are added to the root group to keep the logic simple. - Directory hierarchy is scanned up to a depth of 4: i.e., config_dir and global_config_dir and its subdirectories up to 3 levels down. Only support for scanning configs and attaching group labels is added here. Rendering the nested menu is the subject of a later commit. Signed-off-by: Selva Nair <selva.nair@gmail.com>pull/298/head
parent
0702acf70c
commit
398a771840
17
main.c
17
main.c
|
@ -879,3 +879,20 @@ MsgToEventLog(WORD type, wchar_t *format, ...)
|
|||
msg[1] = buf;
|
||||
ReportEventW(o.event_log, type, 0, 0, NULL, 2, 0, msg, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
ErrorExit(int exit_code, const wchar_t *msg)
|
||||
{
|
||||
if (msg)
|
||||
MessageBoxExW(NULL, msg, TEXT(PACKAGE_NAME),
|
||||
MB_OK | MB_SETFOREGROUND|MB_ICONERROR, GetGUILanguage());
|
||||
if (o.hWnd)
|
||||
{
|
||||
StopAllOpenVPN();
|
||||
PostQuitMessage(exit_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
|
2
main.h
2
main.h
|
@ -133,4 +133,6 @@ DWORD GetDllVersion(LPCTSTR lpszDllName);
|
|||
#define DPI_SCALE(x) MulDiv(x, o.dpi_scale, 100)
|
||||
void MsgToEventLog(WORD type, wchar_t *format, ...);
|
||||
|
||||
void ErrorExit(int exit_code, const wchar_t *msg);
|
||||
|
||||
#endif
|
||||
|
|
109
openvpn_config.c
109
openvpn_config.c
|
@ -135,9 +135,81 @@ AddConfigFileToList(int config, const TCHAR *filename, const TCHAR *config_dir)
|
|||
}
|
||||
}
|
||||
|
||||
#define FLAG_WARN_DUPLICATES (0x1)
|
||||
#define FLAG_WARN_MAX_CONFIGS (0x2)
|
||||
#define FLAG_ADD_CONFIG_GROUPS (0x4)
|
||||
|
||||
/*
|
||||
* Create a new group with the given name as a child of the
|
||||
* specified parent group and return pointer to the new group.
|
||||
* If FLAG_ADD_CONFIG_GROUPS is not enabled, returns the
|
||||
* parent itself.
|
||||
*/
|
||||
static config_group_t *
|
||||
NewConfigGroup(const wchar_t *name, config_group_t *parent, int flags)
|
||||
{
|
||||
if (!(flags & FLAG_ADD_CONFIG_GROUPS))
|
||||
return parent;
|
||||
|
||||
if (!o.groups || o.num_groups == o.max_groups)
|
||||
{
|
||||
o.max_groups += 10;
|
||||
void *tmp = realloc(o.groups, sizeof(*o.groups)*o.max_groups);
|
||||
if (!tmp)
|
||||
{
|
||||
o.max_groups -= 10;
|
||||
ErrorExit(1, L"Out of memory while grouping configs");
|
||||
}
|
||||
o.groups = tmp;
|
||||
}
|
||||
|
||||
config_group_t *cg = &o.groups[o.num_groups];
|
||||
memset(cg, 0, sizeof(*cg));
|
||||
|
||||
_sntprintf_0(cg->name, L"%s", name);
|
||||
cg->id = o.num_groups++;
|
||||
cg->parent = parent;
|
||||
cg->active = false; /* activated later if not empty */
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
/*
|
||||
* All groups that link at least one config to the root are
|
||||
* enabled. Dangling entries with no terminal configs will stay
|
||||
* disabled and are not displayed in the menu tree.
|
||||
*/
|
||||
static void
|
||||
BuildFileList0(const TCHAR *config_dir, int recurse_depth, bool warn_duplicates)
|
||||
ActivateConfigGroups(void)
|
||||
{
|
||||
/* the root group is always active */
|
||||
o.groups[0].active = true;
|
||||
|
||||
/* activate all groups that connect a config to the root */
|
||||
for (int i = 0; i < o.num_configs; i++)
|
||||
{
|
||||
config_group_t *cg = o.conn[i].group;
|
||||
|
||||
while (cg)
|
||||
{
|
||||
cg->active = true;
|
||||
cg = cg->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan for configs in config_dir recursing down up to recurse_depth.
|
||||
* Input: config_dir -- root of the directory to scan from
|
||||
* group -- the group into which add the configs to
|
||||
* flags -- enable warnings, use directory based
|
||||
* grouping of configs etc.
|
||||
* Currently configs in a directory are grouped together and group is
|
||||
* a pointer to the current parent group in the global group array |o.groups|
|
||||
* This may be recursively called until depth becomes 1 and each time
|
||||
* the group is changed to that of the directory being recursed into.
|
||||
*/
|
||||
static void
|
||||
BuildFileList0(const TCHAR *config_dir, int recurse_depth, config_group_t *group, int flags)
|
||||
{
|
||||
WIN32_FIND_DATA find_obj;
|
||||
HANDLE find_handle;
|
||||
|
@ -163,13 +235,16 @@ BuildFileList0(const TCHAR *config_dir, int recurse_depth, bool warn_duplicates)
|
|||
{
|
||||
if (ConfigAlreadyExists(find_obj.cFileName))
|
||||
{
|
||||
if (warn_duplicates)
|
||||
if (flags & FLAG_WARN_DUPLICATES)
|
||||
ShowLocalizedMsg(IDS_ERR_CONFIG_EXIST, find_obj.cFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckReadAccess (config_dir, find_obj.cFileName))
|
||||
AddConfigFileToList(o.num_configs++, find_obj.cFileName, config_dir);
|
||||
{
|
||||
AddConfigFileToList(o.num_configs, find_obj.cFileName, config_dir);
|
||||
o.conn[o.num_configs++].group = group;
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(find_handle, &find_obj));
|
||||
|
||||
|
@ -193,7 +268,9 @@ BuildFileList0(const TCHAR *config_dir, int recurse_depth, bool warn_duplicates)
|
|||
{
|
||||
/* recurse into subdirectory */
|
||||
_sntprintf_0(subdir_name, _T("%s\\%s"), config_dir, find_obj.cFileName);
|
||||
BuildFileList0(subdir_name, recurse_depth - 1, warn_duplicates);
|
||||
config_group_t *sub_group = NewConfigGroup(find_obj.cFileName, group, flags);
|
||||
|
||||
BuildFileList0(subdir_name, recurse_depth - 1, sub_group, flags);
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(find_handle, &find_obj));
|
||||
|
@ -205,7 +282,9 @@ void
|
|||
BuildFileList()
|
||||
{
|
||||
static bool issue_warnings = true;
|
||||
int recurse_depth = 2; /* read config_dir and sub-directories */
|
||||
int recurse_depth = 4; /* read config_dir and 3 levels of sub-directories */
|
||||
int flags = 0;
|
||||
config_group_t *root = NULL;
|
||||
|
||||
if (o.silent_connection)
|
||||
issue_warnings = false;
|
||||
|
@ -216,15 +295,31 @@ BuildFileList()
|
|||
* rescan to add any new one's found
|
||||
*/
|
||||
if (CountConnState(disconnected) == o.num_configs)
|
||||
{
|
||||
o.num_configs = 0;
|
||||
o.num_groups = 0;
|
||||
flags |= FLAG_ADD_CONFIG_GROUPS;
|
||||
root = NewConfigGroup(L"User Profiles", NULL, flags);
|
||||
}
|
||||
else
|
||||
root = &o.groups[0];
|
||||
|
||||
BuildFileList0 (o.config_dir, recurse_depth, issue_warnings);
|
||||
if (issue_warnings)
|
||||
{
|
||||
flags |= FLAG_WARN_DUPLICATES | FLAG_WARN_MAX_CONFIGS;
|
||||
}
|
||||
|
||||
BuildFileList0 (o.config_dir, recurse_depth, root, flags);
|
||||
|
||||
root = NewConfigGroup(L"System Profiles", root, flags);
|
||||
|
||||
if (_tcscmp (o.global_config_dir, o.config_dir))
|
||||
BuildFileList0 (o.global_config_dir, recurse_depth, issue_warnings);
|
||||
BuildFileList0 (o.global_config_dir, recurse_depth, root, flags);
|
||||
|
||||
if (o.num_configs == 0 && issue_warnings)
|
||||
ShowLocalizedMsg(IDS_NFO_NO_CONFIGS, o.config_dir, o.global_config_dir);
|
||||
|
||||
ActivateConfigGroups();
|
||||
|
||||
issue_warnings = false;
|
||||
}
|
||||
|
|
26
options.h
26
options.h
|
@ -87,10 +87,30 @@ typedef struct {
|
|||
#define FLAG_SAVE_AUTH_PASS (1<<5)
|
||||
#define FLAG_DISABLE_SAVE_PASS (1<<6)
|
||||
|
||||
#define CONFIG_VIEW_AUTO (0)
|
||||
#define CONFIG_VIEW_FLAT (1)
|
||||
#define CONFIG_VIEW_NESTED (2)
|
||||
|
||||
typedef struct {
|
||||
unsigned short major, minor, build, revision;
|
||||
} version_t;
|
||||
|
||||
/* A node of config groups tree that can be navigated from the end
|
||||
* node (where config file is attached) to the root. The nodes are stored
|
||||
* as array (o.groups[]) with each node linked to its parent.
|
||||
* Not a complete tree: only navigation from child to parent is supported
|
||||
* which is enough for our purposes.
|
||||
*/
|
||||
typedef struct config_group {
|
||||
int id; /* A unique id for the group */
|
||||
wchar_t name[40]; /* Name of the group -- possibly truncated */
|
||||
struct config_group *parent; /* Pointer to parent group */
|
||||
BOOL active; /* Displayed in the menu if true -- used to prune empty groups */
|
||||
int children; /* Number of children groups and configs */
|
||||
int pos; /* Index within the parent group -- used for rendering */
|
||||
HMENU menu; /* Handle to menu entry for this group */
|
||||
} config_group_t;
|
||||
|
||||
/* Connections parameters */
|
||||
struct connection {
|
||||
TCHAR config_file[MAX_PATH]; /* Name of the config file */
|
||||
|
@ -105,6 +125,8 @@ struct connection {
|
|||
int failed_auth_attempts; /* # of failed user-auth attempts */
|
||||
time_t connected_since; /* Time when the connection was established */
|
||||
proxy_t proxy_type; /* Set during querying proxy credentials */
|
||||
config_group_t *group; /* Pointer to the group this config belongs to */
|
||||
int pos; /* Index of the config within its group */
|
||||
|
||||
struct {
|
||||
SOCKET sk;
|
||||
|
@ -137,7 +159,10 @@ typedef struct {
|
|||
|
||||
/* Connection parameters */
|
||||
connection_t conn[MAX_CONFIGS]; /* Connection structure */
|
||||
config_group_t *groups; /* Array of nodes defining the config groups tree */
|
||||
int num_configs; /* Number of configs */
|
||||
int num_groups; /* Number of config groups */
|
||||
int max_groups; /* Current capacity of groups array */
|
||||
|
||||
service_state_t service_state; /* State of the OpenVPN Service */
|
||||
|
||||
|
@ -169,6 +194,7 @@ typedef struct {
|
|||
DWORD connectscript_timeout; /* Connect Script execution timeout (sec) */
|
||||
DWORD disconnectscript_timeout; /* Disconnect Script execution timeout (sec) */
|
||||
DWORD preconnectscript_timeout; /* Preconnect Script execution timeout (sec) */
|
||||
DWORD config_menu_view; /* 0 for auto, 1 for original flat menu, 2 for hierarchical */
|
||||
|
||||
#ifdef DEBUG
|
||||
FILE *debug_fp;
|
||||
|
|
Loading…
Reference in New Issue