[PATCH 08/11] vpn-config: Provision VPN by using .config file

Jukka Rissanen jukka.rissanen at linux.intel.com
Wed Nov 21 06:31:04 PST 2012


---
 Makefile.am        |   3 +-
 src/connman.h      |   1 +
 src/storage.c      |  16 ++
 vpn/main.c         |   2 +
 vpn/vpn-config.c   | 703 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 vpn/vpn-provider.c | 155 ++++++++++--
 vpn/vpn.h          |   8 +
 7 files changed, 869 insertions(+), 19 deletions(-)
 create mode 100644 vpn/vpn-config.c

diff --git a/Makefile.am b/Makefile.am
index 07dce6d..5cbbcf0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -121,7 +121,8 @@ vpn_connman_vpnd_SOURCES = $(gdbus_sources) $(builtin_vpn_sources) \
 			vpn/vpn-manager.c vpn/vpn-provider.c \
 			vpn/vpn-provider.h vpn/vpn-rtnl.h \
 			vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
-			src/dbus.c src/storage.c src/ipaddress.c src/agent.c
+			src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
+			vpn/vpn-config.c
 
 vpn_connman_vpnd_LDADD = $(builtin_vpn_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
 				@GNUTLS_LIBS@ -lresolv -ldl
diff --git a/src/connman.h b/src/connman.h
index f925899..45b101a 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -207,6 +207,7 @@ int __connman_storage_save_global(GKeyFile *keyfile);
 void __connman_storage_delete_global(void);
 
 GKeyFile *__connman_storage_load_config(const char *ident);
+GKeyFile *__connman_storage_load_provider_config(const char *ident);
 
 GKeyFile *__connman_storage_open_service(const char *ident);
 int __connman_storage_save_service(GKeyFile *keyfile, const char *ident);
diff --git a/src/storage.c b/src/storage.c
index 3c3ce41..77d0c95 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -147,6 +147,22 @@ GKeyFile *__connman_storage_load_config(const char *ident)
 	return keyfile;
 }
 
+GKeyFile *__connman_storage_load_provider_config(const char *ident)
+{
+	gchar *pathname;
+	GKeyFile *keyfile = NULL;
+
+	pathname = g_strdup_printf("%s/vpn/%s.config", STORAGEDIR, ident);
+	if (pathname == NULL)
+		return NULL;
+
+	keyfile = storage_load(pathname);
+
+	g_free(pathname);
+
+	return keyfile;
+}
+
 GKeyFile *__connman_storage_open_service(const char *service_id)
 {
 	gchar *pathname;
diff --git a/vpn/main.c b/vpn/main.c
index 161df98..98e087f 100644
--- a/vpn/main.c
+++ b/vpn/main.c
@@ -315,6 +315,7 @@ int main(int argc, char *argv[])
 	__vpn_rtnl_init();
 	__connman_task_init();
 	__connman_plugin_init(option_plugin, option_noplugin);
+	__vpn_config_init();
 
 	__vpn_rtnl_start();
 
@@ -325,6 +326,7 @@ int main(int argc, char *argv[])
 
 	g_source_remove(signal);
 
+	__vpn_config_cleanup();
 	__connman_task_cleanup();
 	__vpn_rtnl_cleanup();
 	__vpn_ipconfig_cleanup();
diff --git a/vpn/vpn-config.c b/vpn/vpn-config.c
new file mode 100644
index 0000000..89e3aca
--- /dev/null
+++ b/vpn/vpn-config.c
@@ -0,0 +1,703 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/vfs.h>
+#include <sys/inotify.h>
+#include <glib.h>
+
+#include <connman/log.h>
+#include "../src/connman.h"
+
+#include "vpn.h"
+
+enum what {
+	REMOVE = 1,
+	ADD = 2,
+};
+
+struct vpn_config_provider {
+	char *provider_identifier;
+	char *ident;
+	char *name;
+	char *type;
+	char *host;
+	char *domain;
+	char *networks;
+	GHashTable *setting_strings;
+
+	char *config_ident; /* file prefix */
+	char *config_entry; /* entry name */
+};
+
+struct vpn_config {
+	char *ident;
+	char *name;
+	char *description;
+	connman_bool_t protected;
+	GHashTable *provider_table;
+};
+
+static GHashTable *config_table = NULL;
+static GSList *protected_providers = NULL;
+
+static int inotify_wd = -1;
+
+static GIOChannel *inotify_channel = NULL;
+static unsigned int inotify_watch = 0;
+static connman_bool_t cleanup = FALSE;
+
+/* Definition of possible strings in the .config files */
+#define CONFIG_KEY_NAME                "Name"
+#define CONFIG_KEY_DESC                "Description"
+#define CONFIG_KEY_PROT                "Protected"
+
+static const char *config_possible_keys[] = {
+	CONFIG_KEY_NAME,
+	CONFIG_KEY_DESC,
+	CONFIG_KEY_PROT,
+	NULL,
+};
+
+static void unregister_config(gpointer data)
+{
+	struct vpn_config *config = data;
+
+	connman_info("Removing configuration %s", config->ident);
+
+	g_hash_table_destroy(config->provider_table);
+
+	g_free(config->description);
+	g_free(config->name);
+	g_free(config->ident);
+	g_free(config);
+}
+
+static void unregister_provider(gpointer data)
+{
+	struct vpn_config_provider *config_provider = data;
+	struct vpn_provider *provider;
+	char *provider_id;
+
+	if (cleanup == TRUE)
+		goto free_only;
+
+	provider_id = config_provider->provider_identifier;
+
+	connman_info("Removing provider configuration %s provider %s",
+				config_provider->ident, provider_id);
+
+	protected_providers = g_slist_remove(protected_providers,
+						config_provider);
+
+	provider = __vpn_provider_lookup(provider_id);
+	if (provider != NULL)
+		__vpn_provider_delete(provider);
+	else {
+		if (__connman_storage_remove_provider(provider_id) == FALSE)
+			DBG("Could not remove all files for provider %s",
+								provider_id);
+	}
+
+free_only:
+	g_free(config_provider->ident);
+	g_free(config_provider->type);
+	g_free(config_provider->name);
+	g_free(config_provider->host);
+	g_free(config_provider->domain);
+	g_free(config_provider->networks);
+	g_hash_table_destroy(config_provider->setting_strings);
+	g_free(config_provider->provider_identifier);
+	g_free(config_provider->config_ident);
+	g_free(config_provider->config_entry);
+	g_free(config_provider);
+}
+
+static connman_bool_t check_type(const char *type)
+{
+	if (g_strcmp0(type, "OpenConnect") == 0)
+		return TRUE;
+	if (g_strcmp0(type, "OpenVPN") == 0)
+		return TRUE;
+	if (g_strcmp0(type, "VPNC") == 0)
+		return TRUE;
+	if (g_strcmp0(type, "L2TP") == 0)
+		return TRUE;
+	if (g_strcmp0(type, "PPTP") == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static connman_bool_t
+is_protected_provider(struct vpn_config_provider *config_provider)
+{
+	GSList *list;
+
+	DBG("ident %s", config_provider->ident);
+
+	for (list = protected_providers; list; list = list->next) {
+		struct vpn_config_provider *p = list->data;
+
+		if (g_strcmp0(p->type, config_provider->type) != 0)
+			continue;
+
+		if (check_type(config_provider->type) == TRUE)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static int set_string(struct vpn_config_provider *config_provider,
+					const char *key, const char *value)
+{
+	DBG("provider %p key %s value %s", config_provider, key, value);
+
+	if (g_str_equal(key, "Type") == TRUE) {
+		g_free(config_provider->type);
+		config_provider->type = g_strdup(value);
+	} else if (g_str_equal(key, "Name") == TRUE) {
+		g_free(config_provider->name);
+		config_provider->name = g_strdup(value);
+	} else if (g_str_equal(key, "Host") == TRUE) {
+		g_free(config_provider->host);
+		config_provider->host = g_strdup(value);
+	} else if (g_str_equal(key, "Domain") == TRUE) {
+		g_free(config_provider->domain);
+		config_provider->domain = g_strdup(value);
+	} else if (g_str_equal(key, "Networks") == TRUE) {
+		g_free(config_provider->networks);
+		config_provider->networks = g_strdup(value);
+	}
+
+	g_hash_table_replace(config_provider->setting_strings,
+					g_strdup(key), g_strdup(value));
+	return 0;
+}
+
+static const char *get_string(struct vpn_config_provider *config_provider,
+							const char *key)
+{
+	DBG("provider %p key %s", config_provider, key);
+
+	if (g_str_equal(key, "Type") == TRUE)
+		return config_provider->type;
+	else if (g_str_equal(key, "Name") == TRUE)
+		return config_provider->name;
+	else if (g_str_equal(key, "Host") == TRUE)
+		return config_provider->host;
+	else if (g_str_equal(key, "Domain") == TRUE)
+		return config_provider->domain;
+	else if (g_str_equal(key, "Networks") == TRUE)
+		return config_provider->networks;
+
+	return g_hash_table_lookup(config_provider->setting_strings, key);
+}
+
+static void add_keys(struct vpn_config_provider *config_provider,
+			GKeyFile *keyfile, const char *group)
+{
+	char **avail_keys;
+	gsize nb_avail_keys, i;
+
+	avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
+	if (avail_keys == NULL)
+		return;
+
+	for (i = 0 ; i < nb_avail_keys; i++) {
+		char *value = g_key_file_get_value(keyfile, group,
+						avail_keys[i], NULL);
+		if (value == NULL) {
+			connman_warn("Cannot find value for %s",
+							avail_keys[i]);
+			continue;
+		}
+
+		set_string(config_provider, avail_keys[i], value);
+		g_free(value);
+	}
+
+	g_strfreev(avail_keys);
+}
+
+static int load_provider(GKeyFile *keyfile, const char *group,
+				struct vpn_config *config, enum what action)
+{
+	struct vpn_config_provider *config_provider;
+	const char *ident, *host, *domain;
+	int err;
+
+	/* Strip off "provider_" prefix */
+	ident = group + 9;
+
+	if (strlen(ident) < 1)
+		return -EINVAL;
+
+	config_provider = g_hash_table_lookup(config->provider_table, ident);
+	if (config_provider != NULL)
+		return -EALREADY;
+
+	config_provider = g_try_new0(struct vpn_config_provider, 1);
+	if (config_provider == NULL)
+		return -ENOMEM;
+
+	config_provider->ident = g_strdup(ident);
+
+	config_provider->setting_strings = g_hash_table_new_full(g_str_hash,
+						g_str_equal, g_free, g_free);
+
+	add_keys(config_provider, keyfile, group);
+
+	host = get_string(config_provider, "Host");
+	domain = get_string(config_provider, "Domain");
+	if (host != NULL && domain != NULL) {
+		char *id = __vpn_provider_create_identifier(host, domain);
+
+		struct vpn_provider *provider;
+		provider = __vpn_provider_lookup(id);
+		if (provider != NULL) {
+			if (action == REMOVE)
+				__vpn_provider_delete(provider);
+
+			g_free(id);
+			err = -EALREADY;
+			goto err;
+		}
+
+		config_provider->provider_identifier = id;
+
+		DBG("provider identifier %s", id);
+	} else {
+		DBG("invalid values host %s domain %s", host, domain);
+		err = -EINVAL;
+		goto err;
+	}
+
+	if (is_protected_provider(config_provider) == TRUE) {
+		connman_error("Trying to provision a protected service");
+		err = -EACCES;
+		goto err;
+	}
+
+	config_provider->config_ident = g_strdup(config->ident);
+	config_provider->config_entry = g_strdup_printf("provider_%s",
+						config_provider->ident);
+
+	g_hash_table_insert(config->provider_table,
+				config_provider->ident,	config_provider);
+
+	if (config->protected == TRUE)
+		protected_providers =
+			g_slist_prepend(protected_providers, config_provider);
+
+	err = __vpn_provider_create_from_config(
+					config_provider->setting_strings,
+					config_provider->config_ident,
+					config_provider->config_entry);
+	if (err != 0) {
+		DBG("Cannot create provider from config file (%d/%s)",
+			-err, strerror(-err));
+		goto err;
+	}
+
+	connman_info("Added provider configuration %s",
+						config_provider->ident);
+	return 0;
+
+err:
+	g_free(config_provider->ident);
+	g_free(config_provider->type);
+	g_free(config_provider->name);
+	g_free(config_provider->host);
+	g_free(config_provider->domain);
+	g_free(config_provider->networks);
+	g_hash_table_destroy(config_provider->setting_strings);
+	g_free(config_provider);
+
+	return err;
+}
+
+static void check_keys(GKeyFile *keyfile, const char *group,
+			const char **possible_keys)
+{
+	char **avail_keys;
+	gsize nb_avail_keys, i, j;
+
+	avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
+	if (avail_keys == NULL)
+		return;
+
+	for (i = 0 ; i < nb_avail_keys; i++) {
+		for (j = 0; possible_keys[j] ; j++)
+			if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0)
+				break;
+
+		if (possible_keys[j] == NULL)
+			connman_warn("Unknown configuration key %s in [%s]",
+					avail_keys[i], group);
+	}
+
+	g_strfreev(avail_keys);
+}
+
+static int load_config(struct vpn_config *config, char *path, enum what action)
+{
+	GKeyFile *keyfile;
+	GError *error = NULL;
+	gsize length;
+	char **groups;
+	char *str;
+	gboolean protected, found = FALSE;
+	int i;
+
+	DBG("config %p", config);
+
+	keyfile = __connman_storage_load_provider_config(config->ident);
+	if (keyfile == NULL)
+		return -EIO;
+
+	/* Verify keys validity of the global section */
+	check_keys(keyfile, "global", config_possible_keys);
+
+	str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL);
+	if (str != NULL) {
+		g_free(config->name);
+		config->name = str;
+	}
+
+	str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL);
+	if (str != NULL) {
+		g_free(config->description);
+		config->description = str;
+	}
+
+	protected = g_key_file_get_boolean(keyfile, "global",
+					CONFIG_KEY_PROT, &error);
+	if (error == NULL)
+		config->protected = protected;
+	else
+		config->protected = TRUE;
+	g_clear_error(&error);
+
+	groups = g_key_file_get_groups(keyfile, &length);
+
+	for (i = 0; groups[i] != NULL; i++) {
+		if (g_str_has_prefix(groups[i], "provider_") == TRUE) {
+			int ret = load_provider(keyfile, groups[i], config,
+						action);
+			if (ret == 0 || ret == -EALREADY)
+				found = TRUE;
+		}
+	}
+
+	if (found == FALSE)
+		connman_warn("Config file %s/%s.config does not contain any "
+			"configuration that can be provisioned!",
+			path, config->ident);
+
+	g_strfreev(groups);
+
+	g_key_file_free(keyfile);
+
+	return 0;
+}
+
+static struct vpn_config *create_config(const char *ident)
+{
+	struct vpn_config *config;
+
+	DBG("ident %s", ident);
+
+	if (g_hash_table_lookup(config_table, ident) != NULL)
+		return NULL;
+
+	config = g_try_new0(struct vpn_config, 1);
+	if (config == NULL)
+		return NULL;
+
+	config->ident = g_strdup(ident);
+
+	config->provider_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+						NULL, unregister_provider);
+
+	g_hash_table_insert(config_table, config->ident, config);
+
+	connman_info("Adding configuration %s", config->ident);
+
+	return config;
+}
+
+static connman_bool_t validate_ident(const char *ident)
+{
+	unsigned int i;
+
+	if (ident == NULL)
+		return FALSE;
+
+	for (i = 0; i < strlen(ident); i++)
+		if (g_ascii_isprint(ident[i]) == FALSE)
+			return FALSE;
+
+	return TRUE;
+}
+
+static char *get_dir()
+{
+	return g_strdup_printf("%s/vpn", STORAGEDIR);
+}
+
+static int read_configs(void)
+{
+	GDir *dir;
+	char *path = get_dir();
+
+	DBG("path %s", path);
+
+	dir = g_dir_open(path, 0, NULL);
+	if (dir != NULL) {
+		const gchar *file;
+
+		while ((file = g_dir_read_name(dir)) != NULL) {
+			GString *str;
+			gchar *ident;
+
+			if (g_str_has_suffix(file, ".config") == FALSE)
+				continue;
+
+			ident = g_strrstr(file, ".config");
+			if (ident == NULL)
+				continue;
+
+			str = g_string_new_len(file, ident - file);
+			if (str == NULL)
+				continue;
+
+			ident = g_string_free(str, FALSE);
+
+			if (validate_ident(ident) == TRUE) {
+				struct vpn_config *config;
+
+				config = create_config(ident);
+				if (config != NULL)
+					load_config(config, path, ADD);
+			} else {
+				connman_error("Invalid config ident %s", ident);
+			}
+			g_free(ident);
+		}
+
+		g_dir_close(dir);
+	}
+
+	g_free(path);
+
+	return 0;
+}
+
+static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	char buffer[256];
+	char *next_event;
+	gsize bytes_read;
+	GIOStatus status;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		inotify_watch = 0;
+		return FALSE;
+	}
+
+	status = g_io_channel_read_chars(channel, buffer,
+					sizeof(buffer) - 1, &bytes_read, NULL);
+
+	switch (status) {
+	case G_IO_STATUS_NORMAL:
+		break;
+	case G_IO_STATUS_AGAIN:
+		return TRUE;
+	default:
+		connman_error("Reading from inotify channel failed");
+		inotify_watch = 0;
+		return FALSE;
+	}
+
+	next_event = buffer;
+
+	while (bytes_read > 0) {
+		struct inotify_event *event;
+		gchar *ext;
+		gchar *ident;
+		gsize len;
+
+		event = (struct inotify_event *) next_event;
+		if (event->len)
+			ident = next_event + sizeof(struct inotify_event);
+		else
+			ident = NULL;
+
+		len = sizeof(struct inotify_event) + event->len;
+
+		/* check if inotify_event block fit */
+		if (len > bytes_read)
+			break;
+
+		next_event += len;
+		bytes_read -= len;
+
+		if (ident == NULL)
+			continue;
+
+		if (g_str_has_suffix(ident, ".config") == FALSE)
+			continue;
+
+		ext = g_strrstr(ident, ".config");
+		if (ext == NULL)
+			continue;
+
+		*ext = '\0';
+
+		if (validate_ident(ident) == FALSE) {
+			connman_error("Invalid config ident %s", ident);
+			continue;
+		}
+
+		if (event->mask & IN_CREATE)
+			create_config(ident);
+
+		if (event->mask & IN_MODIFY) {
+			struct vpn_config *config;
+
+			config = g_hash_table_lookup(config_table, ident);
+			if (config != NULL) {
+				char *path = get_dir();
+
+				g_hash_table_remove_all(config->provider_table);
+				load_config(config, path, REMOVE);
+
+				/* Re-scan the config file for any changes */
+				g_hash_table_remove_all(config->provider_table);
+				load_config(config, path, ADD);
+
+				g_free(path);
+			}
+		}
+
+		if (event->mask & IN_DELETE)
+			g_hash_table_remove(config_table, ident);
+	}
+
+	return TRUE;
+}
+
+static int create_watch(void)
+{
+	int fd;
+	char *dir;
+
+	fd = inotify_init();
+	if (fd < 0)
+		return -EIO;
+
+	dir = get_dir();
+	inotify_wd = inotify_add_watch(fd, dir,
+					IN_MODIFY | IN_CREATE | IN_DELETE);
+	g_free(dir);
+	if (inotify_wd < 0) {
+		connman_error("Creation of %s watch failed", dir);
+		close(fd);
+		return -EIO;
+	}
+
+	inotify_channel = g_io_channel_unix_new(fd);
+	if (inotify_channel == NULL) {
+		connman_error("Creation of inotify channel failed");
+		inotify_rm_watch(fd, inotify_wd);
+		inotify_wd = 0;
+
+		close(fd);
+		return -EIO;
+	}
+
+	g_io_channel_set_close_on_unref(inotify_channel, TRUE);
+	g_io_channel_set_encoding(inotify_channel, NULL, NULL);
+	g_io_channel_set_buffered(inotify_channel, FALSE);
+
+	inotify_watch = g_io_add_watch(inotify_channel,
+				G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+				inotify_data, NULL);
+
+	return 0;
+}
+
+static void remove_watch(void)
+{
+	int fd;
+
+	if (inotify_channel == NULL)
+		return;
+
+	if (inotify_watch > 0) {
+		g_source_remove(inotify_watch);
+		inotify_watch = 0;
+	}
+
+	fd = g_io_channel_unix_get_fd(inotify_channel);
+
+	if (inotify_wd >= 0) {
+		inotify_rm_watch(fd, inotify_wd);
+		inotify_wd = 0;
+	}
+
+	g_io_channel_unref(inotify_channel);
+}
+
+int __vpn_config_init(void)
+{
+	DBG("");
+
+	config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+						NULL, unregister_config);
+	create_watch();
+
+	return read_configs();
+}
+
+void __vpn_config_cleanup(void)
+{
+	DBG("");
+
+	cleanup = TRUE;
+
+	remove_watch();
+
+	g_hash_table_destroy(config_table);
+	config_table = NULL;
+
+	cleanup = FALSE;
+}
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index acbed85..fccad84 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -817,7 +817,7 @@ static int vpn_provider_save(struct vpn_provider *provider)
 	return 0;
 }
 
-static struct vpn_provider *vpn_provider_lookup(const char *identifier)
+struct vpn_provider *__vpn_provider_lookup(const char *identifier)
 {
 	struct vpn_provider *provider = NULL;
 
@@ -839,7 +839,8 @@ static int provider_probe(struct vpn_provider *provider)
 {
 	GSList *list;
 
-	DBG("provider %p name %s", provider, provider->name);
+	DBG("provider %p driver %p name %s", provider, provider->driver,
+						provider->name);
 
 	if (provider->driver != NULL)
 		return -EALREADY;
@@ -1029,20 +1030,26 @@ int __vpn_provider_remove(const char *path)
 
 	ident = get_ident(path);
 
-	provider = vpn_provider_lookup(ident);
-	if (provider != NULL) {
-		DBG("Removing VPN %s", provider->identifier);
+	provider = __vpn_provider_lookup(ident);
+	if (provider != NULL)
+		return __vpn_provider_delete(provider);
 
-		connection_removed_signal(provider);
+	return -ENXIO;
+}
 
-		provider_unregister(provider);
-		g_hash_table_remove(provider_hash, provider->identifier);
+int __vpn_provider_delete(struct vpn_provider *provider)
+{
+	DBG("Deleting VPN %s", provider->identifier);
 
-		__connman_storage_remove_provider(ident);
-		return 0;
-	}
+	connection_removed_signal(provider);
 
-	return -ENXIO;
+	provider_unregister(provider);
+
+	__connman_storage_remove_provider(provider->identifier);
+
+	g_hash_table_remove(provider_hash, provider->identifier);
+
+	return 0;
 }
 
 static void append_ipv4(DBusMessageIter *iter, void *user_data)
@@ -1535,7 +1542,7 @@ static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile,
 	if (keyfile == NULL || ident == NULL)
 		return NULL;
 
-	provider = vpn_provider_lookup(ident);
+	provider = __vpn_provider_lookup(ident);
 	if (provider == NULL) {
 		provider = vpn_provider_get(ident);
 		if (provider == NULL) {
@@ -1599,6 +1606,19 @@ static void provider_create_all_from_type(const char *provider_type)
 	g_strfreev(providers);
 }
 
+char *__vpn_provider_create_identifier(const char *host, const char *domain)
+{
+	char *ident;
+
+	ident = g_strdup_printf("%s_%s", host, domain);
+	if (ident == NULL)
+		return NULL;
+
+	provider_dbus_ident(ident);
+
+	return ident;
+}
+
 int __vpn_provider_create(DBusMessage *msg)
 {
 	struct vpn_provider *provider;
@@ -1650,12 +1670,10 @@ int __vpn_provider_create(DBusMessage *msg)
 	if (type == NULL || name == NULL)
 		return -EOPNOTSUPP;
 
-	ident = g_strdup_printf("%s_%s", host, domain);
-	provider_dbus_ident(ident);
-
+	ident = __vpn_provider_create_identifier(host, domain);
 	DBG("ident %s", ident);
 
-	provider = vpn_provider_lookup(ident);
+	provider = __vpn_provider_lookup(ident);
 	if (provider == NULL) {
 		provider = vpn_provider_get(ident);
 		if (provider == NULL) {
@@ -1726,6 +1744,107 @@ int __vpn_provider_create(DBusMessage *msg)
 	return 0;
 }
 
+static const char *get_string(GHashTable *settings, const char *key)
+{
+	DBG("settings %p key %s", settings, key);
+
+	return g_hash_table_lookup(settings, key);
+}
+
+static GSList *parse_user_networks(const char *network_str)
+{
+	// XXX
+	return NULL;
+}
+
+int __vpn_provider_create_from_config(GHashTable *settings,
+				const char *config_ident,
+				const char *config_entry)
+{
+	struct vpn_provider *provider;
+	const char *type, *name, *host, *domain, *networks_str;
+	GSList *networks;
+	char *ident = NULL;
+	GHashTableIter hash;
+	gpointer value, key;
+	int err;
+
+	type = get_string(settings, "Type");
+	name = get_string(settings, "Name");
+	host = get_string(settings, "Host");
+	domain = get_string(settings, "Domain");
+	networks_str = get_string(settings, "Networks");
+	networks = parse_user_networks(networks_str);
+
+	if (host == NULL || domain == NULL) {
+		err = -EINVAL;
+		goto fail;
+	}
+
+	DBG("type %s name %s networks %s", type, name, networks_str);
+
+	if (type == NULL || name == NULL) {
+		err = -EOPNOTSUPP;
+		goto fail;
+	}
+
+	ident = __vpn_provider_create_identifier(host, domain);
+	DBG("ident %s", ident);
+
+	provider = __vpn_provider_lookup(ident);
+	if (provider == NULL) {
+		provider = vpn_provider_get(ident);
+		if (provider == NULL) {
+			DBG("can not create provider");
+			err = -EOPNOTSUPP;
+			goto fail;
+		}
+
+		provider->host = g_strdup(host);
+		provider->domain = g_strdup(domain);
+		provider->name = g_strdup(name);
+		provider->type = g_ascii_strdown(type, -1);
+
+		if (provider_register(provider) == 0)
+			vpn_provider_load(provider);
+
+		provider_resolv_host_addr(provider);
+	}
+
+	if (networks != NULL) {
+		g_slist_free_full(provider->user_networks, free_route);
+		provider->user_networks = networks;
+		set_user_networks(provider, provider->user_networks);
+	}
+
+	g_hash_table_iter_init(&hash, settings);
+
+	while (g_hash_table_iter_next(&hash, &key, &value) == TRUE)
+		vpn_provider_set_string(provider, key, value);
+
+	vpn_provider_save(provider);
+
+	err = provider_register(provider);
+	if (err != 0 && err != -EALREADY)
+		goto fail;
+
+	connection_register(provider);
+
+	DBG("provider %p index %d path %s", provider, provider->index,
+							provider->path);
+
+	connection_added_signal(provider);
+
+	err = 0;
+
+fail:
+	g_free(ident);
+	if (networks != NULL)
+		g_slist_free_full(networks, free_route);
+
+	return err;
+}
+
 static void append_connection_structs(DBusMessageIter *iter, void *user_data)
 {
 	DBusMessageIter entry;
@@ -1782,7 +1901,7 @@ int vpn_provider_set_string(struct vpn_provider *provider,
 
 	if (g_str_equal(key, "Type") == TRUE) {
 		g_free(provider->type);
-		provider->type = g_strdup(value);
+		provider->type = g_ascii_strdown(value, -1);
 	} else if (g_str_equal(key, "Name") == TRUE) {
 		g_free(provider->name);
 		provider->name = g_strdup(value);
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 2b36b30..3cdba2e 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -71,14 +71,18 @@ void __vpn_ipconfig_cleanup(void);
 
 #include "vpn-provider.h"
 
+char *__vpn_provider_create_identifier(const char *host, const char *domain);
 connman_bool_t __vpn_provider_check_routes(struct vpn_provider *provider);
 int __vpn_provider_append_user_route(struct vpn_provider *provider,
 			int family, const char *network, const char *netmask);
 void __vpn_provider_append_properties(struct vpn_provider *provider, DBusMessageIter *iter);
 void __vpn_provider_list(DBusMessageIter *iter, void *user_data);
 int __vpn_provider_create(DBusMessage *msg);
+int __vpn_provider_create_from_config(GHashTable *settings,
+			const char *config_ident, const char *config_entry);
 DBusMessage *__vpn_provider_get_connections(DBusMessage *msg);
 const char * __vpn_provider_get_ident(struct vpn_provider *provider);
+struct vpn_provider *__vpn_provider_lookup(const char *identifier);
 int __vpn_provider_indicate_state(struct vpn_provider *provider,
 					enum vpn_provider_state state);
 int __vpn_provider_indicate_error(struct vpn_provider *provider,
@@ -87,6 +91,7 @@ int __vpn_provider_connect(struct vpn_provider *provider);
 int __vpn_provider_connect_path(const char *path);
 int __vpn_provider_disconnect(struct vpn_provider *provider);
 int __vpn_provider_remove(const char *path);
+int __vpn_provider_delete(struct vpn_provider *provider);
 void __vpn_provider_check_connections(void);
 void __vpn_provider_cleanup(void);
 int __vpn_provider_init(gboolean handle_routes);
@@ -101,3 +106,6 @@ unsigned int __vpn_rtnl_update_interval_add(unsigned int interval);
 unsigned int __vpn_rtnl_update_interval_remove(unsigned int interval);
 int __vpn_rtnl_request_update(void);
 int __vpn_rtnl_send(const void *buf, size_t len);
+
+int __vpn_config_init(void);
+void __vpn_config_cleanup(void);
-- 
1.7.11.4




More information about the connman mailing list