summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-07-29 23:26:52 +0200
committerThomas Haller <thaller@redhat.com>2015-07-29 23:33:31 +0200
commit3e39e5b4f04aa1ad16b226ae687413d02aa5b8eb (patch)
tree345be85fe931b3f65489e36e47da1e38b170ba25
parent5f7f38a5867338d910fd10a49957d2796dce9435 (diff)
parent6be8a1f5495a6fb73f591cb47dd53a00cfda778b (diff)
libnm: merge branch 'th/libnm-vpn-plugin-bgo749877'
- add new NMVpnPluginInfo class with new API to load VPN name files. - move NMVpnEditorPlugin to libnm-core, including code to load the client plugin from the shared library. - deprecate NMVpnPluginOld and add NMVpnServicePlugin. The latter is identical to NMVpnPluginOld but renamed and introduced as new API for 1.2. https://bugzilla.gnome.org/show_bug.cgi?id=749877
-rw-r--r--clients/tui/vpn-helpers.c151
-rw-r--r--clients/tui/vpn-helpers.h9
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec2
-rw-r--r--docs/libnm/Makefile.am1
-rw-r--r--libnm-core/Makefile.am2
-rw-r--r--libnm-core/Makefile.libnm-core8
-rw-r--r--libnm-core/nm-core-internal.h43
-rw-r--r--libnm-core/nm-utils.c254
-rw-r--r--libnm-core/nm-utils.h4
-rw-r--r--libnm-core/nm-vpn-editor-plugin.c284
-rw-r--r--libnm-core/nm-vpn-editor-plugin.h (renamed from libnm/nm-vpn-editor-plugin.h)62
-rw-r--r--libnm-core/nm-vpn-plugin-info.c1016
-rw-r--r--libnm-core/nm-vpn-plugin-info.h114
-rw-r--r--libnm-core/tests/test-general.c49
-rw-r--r--libnm/Makefile.am8
-rw-r--r--libnm/NetworkManager.h2
-rw-r--r--libnm/libnm.ver42
-rw-r--r--libnm/nm-vpn-editor-plugin.c181
-rw-r--r--libnm/nm-vpn-editor.c69
-rw-r--r--libnm/nm-vpn-editor.h86
-rw-r--r--libnm/nm-vpn-plugin-old.c69
-rw-r--r--libnm/nm-vpn-plugin-old.h34
-rw-r--r--libnm/nm-vpn-service-plugin.c1208
-rw-r--r--libnm/nm-vpn-service-plugin.h164
-rw-r--r--po/POTFILES.in3
-rw-r--r--src/vpn-manager/nm-vpn-manager.c227
-rw-r--r--src/vpn-manager/nm-vpn-service.c99
-rw-r--r--src/vpn-manager/nm-vpn-service.h6
28 files changed, 3614 insertions, 583 deletions
diff --git a/clients/tui/vpn-helpers.c b/clients/tui/vpn-helpers.c
index be8f538463..9cf8ce84c0 100644
--- a/clients/tui/vpn-helpers.c
+++ b/clients/tui/vpn-helpers.c
@@ -34,142 +34,41 @@
#include <gmodule.h>
#include <glib/gi18n.h>
-#include <nm-connection.h>
-#include <nm-setting-connection.h>
-#include <nm-setting-vpn.h>
-
#include "nm-glib.h"
#include "vpn-helpers.h"
-#define NM_VPN_API_SUBJECT_TO_CHANGE
-#include "nm-vpn-plugin-ui-interface.h"
-
-#define VPN_NAME_FILES_DIR SYSCONFDIR"/NetworkManager/VPN"
-
-static GHashTable *plugins = NULL;
+#include "nm-macros-internal.h"
-G_DEFINE_QUARK (NMA_ERROR, nma_error)
-#define NMA_ERROR nma_error_quark ()
-#define NMA_ERROR_GENERIC 0
+static gboolean plugins_loaded;
+static GSList *plugins = NULL;
-NMVpnPluginUiInterface *
+NMVpnEditorPlugin *
vpn_get_plugin_by_service (const char *service)
{
+ NMVpnEditorPlugin *plugin = NULL;
+ NMVpnPluginInfo *plugin_info;
+
g_return_val_if_fail (service != NULL, NULL);
- return g_hash_table_lookup (plugins, service);
+ if (G_UNLIKELY (!plugins_loaded))
+ vpn_get_plugins ();
+
+ plugin_info = nm_vpn_plugin_info_list_find_by_service (plugins, service);
+ if (plugin_info) {
+ plugin = nm_vpn_plugin_info_get_editor_plugin (plugin_info);
+ if (!plugin)
+ plugin = nm_vpn_plugin_info_load_editor_plugin (plugin_info, NULL);
+ }
+ return plugin;
}
-GHashTable *
-vpn_get_plugins (GError **error)
+GSList *
+vpn_get_plugins ()
{
- GDir *dir;
- const char *f;
-
- if (error)
- g_return_val_if_fail (*error == NULL, NULL);
-
- if (plugins)
+ if (G_LIKELY (plugins_loaded))
return plugins;
-
- dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
- if (!dir) {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Couldn't read VPN .name files directory " VPN_NAME_FILES_DIR ".");
- return NULL;
- }
-
- plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
-
- while ((f = g_dir_read_name (dir))) {
- char *path = NULL, *service = NULL;
- char *so_path = NULL, *so_name = NULL;
- GKeyFile *keyfile = NULL;
- GModule *module;
- NMVpnPluginUiFactory factory = NULL;
-
- if (!g_str_has_suffix (f, ".name"))
- continue;
-
- path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f);
-
- keyfile = g_key_file_new ();
- if (!g_key_file_load_from_file (keyfile, path, 0, NULL))
- goto next;
-
- service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL);
- if (!service)
- goto next;
-
- so_path = g_key_file_get_string (keyfile, "GNOME", "properties", NULL);
- if (!so_path)
- goto next;
-
- /* Remove any path and extension components, then reconstruct path
- * to the SO in LIBDIR
- */
- so_name = g_path_get_basename (so_path);
- g_free (so_path);
- so_path = g_strdup_printf ("%s/NetworkManager/%s", LIBDIR, so_name);
- g_free (so_name);
-
- module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
- if (!module) {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Cannot load the VPN plugin which provides the "
- "service '%s'.", service);
- goto next;
- }
-
- if (g_module_symbol (module, "nm_vpn_plugin_ui_factory", (gpointer) &factory)) {
- NMVpnPluginUiInterface *plugin;
- GError *factory_error = NULL;
- gboolean success = FALSE;
-
- plugin = factory (&factory_error);
- if (plugin) {
- char *plug_name = NULL, *plug_service = NULL;
-
- /* Validate plugin properties */
- g_object_get (G_OBJECT (plugin),
- NM_VPN_PLUGIN_UI_INTERFACE_NAME, &plug_name,
- NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, &plug_service,
- NULL);
- if (!plug_name || !strlen (plug_name)) {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': missing plugin name",
- g_module_name (module));
- } else if (!plug_service || strcmp (plug_service, service)) {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': invalid service name",
- g_module_name (module));
- } else {
- /* Success! */
- g_object_set_data_full (G_OBJECT (plugin), "gmodule", module,
- (GDestroyNotify) g_module_close);
- g_hash_table_insert (plugins, g_strdup (service), plugin);
- success = TRUE;
- }
- g_free (plug_name);
- g_free (plug_service);
- } else {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': %s",
- g_module_name (module), g_module_error ());
- }
-
- if (!success)
- g_module_close (module);
- } else {
- g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot locate nm_vpn_plugin_ui_factory() in '%s': %s",
- g_module_name (module), g_module_error ());
- g_module_close (module);
- }
-
- next:
- g_free (so_path);
- g_free (service);
- g_key_file_free (keyfile);
- g_free (path);
- }
- g_dir_close (dir);
-
+ plugins_loaded = TRUE;
+ plugins = nm_vpn_plugin_info_list_load ();
return plugins;
}
@@ -409,7 +308,7 @@ vpn_supports_ipv6 (NMConnection *connection)
{
NMSettingVpn *s_vpn;
const char *service_type;
- NMVpnPluginUiInterface *plugin;
+ NMVpnEditorPlugin *plugin;
guint32 capabilities;
s_vpn = nm_connection_get_setting_vpn (connection);
@@ -421,6 +320,6 @@ vpn_supports_ipv6 (NMConnection *connection)
plugin = vpn_get_plugin_by_service (service_type);
g_return_val_if_fail (plugin != NULL, FALSE);
- capabilities = nm_vpn_plugin_ui_interface_get_capabilities (plugin);
- return (capabilities & NM_VPN_PLUGIN_UI_CAPABILITY_IPV6) != 0;
+ capabilities = nm_vpn_editor_plugin_get_capabilities (plugin);
+ return NM_FLAGS_HAS (capabilities, NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6);
}
diff --git a/clients/tui/vpn-helpers.h b/clients/tui/vpn-helpers.h
index 3bb26c6d76..4503b8178f 100644
--- a/clients/tui/vpn-helpers.h
+++ b/clients/tui/vpn-helpers.h
@@ -19,16 +19,13 @@
#ifndef _VPN_HELPERS_H_
#define _VPN_HELPERS_H_
-#include <nm-connection.h>
-
-#define NM_VPN_API_SUBJECT_TO_CHANGE
-#include <nm-vpn-plugin-ui-interface.h>
+#include <NetworkManager.h>
#include "nm-glib.h"
-GHashTable *vpn_get_plugins (GError **error);
+GSList *vpn_get_plugins (void);
-NMVpnPluginUiInterface *vpn_get_plugin_by_service (const char *service);
+NMVpnEditorPlugin *vpn_get_plugin_by_service (const char *service);
typedef void (*VpnImportSuccessCallback) (NMConnection *connection, gpointer user_data);
void vpn_import (VpnImportSuccessCallback callback, gpointer user_data);
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index c3769a2249..048cedae1f 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -440,6 +440,7 @@ make install DESTDIR=$RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/conf.d
mkdir -p $RPM_BUILD_ROOT%{nmlibdir}/conf.d
+mkdir -p $RPM_BUILD_ROOT%{nmlibdir}/VPN
%{__cp} %{SOURCE2} $RPM_BUILD_ROOT%{nmlibdir}/conf.d/
%{__cp} %{SOURCE3} $RPM_BUILD_ROOT%{nmlibdir}/conf.d/
@@ -541,6 +542,7 @@ fi
%dir %{_sysconfdir}/%{name}/conf.d
%dir %{nmlibdir}
%dir %{nmlibdir}/conf.d
+%dir %{nmlibdir}/VPN
%{_mandir}/man1/*
%{_mandir}/man5/*
%{_mandir}/man8/*
diff --git a/docs/libnm/Makefile.am b/docs/libnm/Makefile.am
index 2525856d5f..23631ddc24 100644
--- a/docs/libnm/Makefile.am
+++ b/docs/libnm/Makefile.am
@@ -54,6 +54,7 @@ IGNORE_HFILES= \
nm-types.h \
nm-utils-private.h \
nm-vpn-plugin-old.h \
+ nm-vpn-service-plugin.h \
nm-core-tests-enum-types.h
# Images to copy into HTML directory.
diff --git a/libnm-core/Makefile.am b/libnm-core/Makefile.am
index 73c5eaccf2..08f2afc86a 100644
--- a/libnm-core/Makefile.am
+++ b/libnm-core/Makefile.am
@@ -6,6 +6,8 @@ AM_CPPFLAGS = \
-I${top_srcdir}/include \
-DG_LOG_DOMAIN=\""libnm"\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
+ -DNMCONFDIR=\"$(nmconfdir)\" \
+ -DNMLIBDIR=\"$(nmlibdir)\" \
-DNETWORKMANAGER_COMPILATION \
-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
$(GLIB_CFLAGS)
diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core
index bbd504503a..a3763f4e56 100644
--- a/libnm-core/Makefile.libnm-core
+++ b/libnm-core/Makefile.libnm-core
@@ -42,7 +42,9 @@ libnm_core_headers = \
$(core)/nm-setting.h \
$(core)/nm-simple-connection.h \
$(core)/nm-utils.h \
- $(core)/nm-vpn-dbus-interface.h
+ $(core)/nm-vpn-dbus-interface.h \
+ $(core)/nm-vpn-editor-plugin.h \
+ $(core)/nm-vpn-plugin-info.h
libnm_core_private_headers = \
$(core)/crypto.h \
@@ -93,5 +95,7 @@ libnm_core_sources = \
$(core)/nm-setting-wireless.c \
$(core)/nm-setting.c \
$(core)/nm-simple-connection.c \
- $(core)/nm-utils.c
+ $(core)/nm-utils.c \
+ $(core)/nm-vpn-editor-plugin.c \
+ $(core)/nm-vpn-plugin-info.c
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 2ff9e5348e..ea095a6510 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -136,6 +136,19 @@ char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy);
GPtrArray * _nm_utils_strv_to_ptrarray (char **strv);
char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray);
+gboolean _nm_utils_check_file (const char *filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ struct stat *out_st,
+ GError **error);
+
+char *_nm_utils_check_module_file (const char *name,
+ int check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error);
+
#define NM_UTILS_UUID_TYPE_LEGACY 0
#define NM_UTILS_UUID_TYPE_VARIANT3 1
@@ -187,6 +200,26 @@ gboolean _nm_dbus_error_has_name (GError *error,
/***********************************************************/
+gboolean _nm_vpn_plugin_info_check_file (const char *filename,
+ gboolean check_absolute,
+ gboolean do_validate_filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error);
+
+const char *_nm_vpn_plugin_info_get_default_dir_etc (void);
+const char *_nm_vpn_plugin_info_get_default_dir_lib (void);
+const char *_nm_vpn_plugin_info_get_default_dir_user (void);
+
+GSList *_nm_vpn_plugin_info_list_load_dir (const char *dirname,
+ gboolean do_validate_filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data);
+
+/***********************************************************/
+
typedef struct {
const char *name;
gboolean numeric;
@@ -202,4 +235,14 @@ int _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option)
/***********************************************************/
+typedef struct _NMUtilsStrStrDictKey NMUtilsStrStrDictKey;
+guint _nm_utils_strstrdictkey_hash (gconstpointer a);
+gboolean _nm_utils_strstrdictkey_equal (gconstpointer a, gconstpointer b);
+NMUtilsStrStrDictKey *_nm_utils_strstrdictkey_create (const char *v1, const char *v2);
+
+#define _nm_utils_strstrdictkey_static(v1, v2) \
+ ( (NMUtilsStrStrDictKey *) ("\03" v1 "\0" v2 "") )
+
+/***********************************************************/
+
#endif
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index f6a9e5a05a..2cc6ed3955 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -30,6 +30,7 @@
#include <libintl.h>
#include <gmodule.h>
#include <glib/gi18n-lib.h>
+#include <sys/stat.h>
#include "nm-glib.h"
#include "nm-utils.h"
@@ -2409,6 +2410,160 @@ nm_utils_file_is_pkcs12 (const char *filename)
/**********************************************************************************************/
+gboolean
+_nm_utils_check_file (const char *filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ struct stat *out_st,
+ GError **error)
+{
+ struct stat st_backup;
+
+ if (!out_st)
+ out_st = &st_backup;
+
+ if (stat (filename, out_st) != 0) {
+ int errsv = errno;
+
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("failed stat file %s: %s"), filename, strerror (errsv));
+ return FALSE;
+ }
+
+ /* ignore non-files. */
+ if (!S_ISREG (out_st->st_mode)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("not a file (%s)"), filename);
+ return FALSE;
+ }
+
+ /* with check_owner enabled, check that the file belongs to the
+ * owner or root. */
+ if ( check_owner >= 0
+ && (out_st->st_uid != 0 && (gint64) out_st->st_uid != check_owner)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("invalid file owner %d for %s"), out_st->st_uid, filename);
+ return FALSE;
+ }
+
+ /* with check_owner enabled, check that the file cannot be modified
+ * by other users (except root). */
+ if ( check_owner >= 0
+ && NM_FLAGS_ANY (out_st->st_mode, S_IWGRP | S_IWOTH | S_ISUID)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("file permissions for %s"), filename);
+ return FALSE;
+ }
+
+ if ( check_file
+ && !check_file (filename, out_st, user_data, error)) {
+ if (error && !*error) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("reject %s"), filename);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static char *
+_resolve_module_file_name (const char *file_name)
+{
+ char *name = NULL;
+
+ /* g_module_open() is searching for the exact file to load,
+ * but it doesn't give us a hook to check file permissions
+ * and ownership. Reimplement the file name resolution.
+ *
+ * Copied from g_module_open(). */
+
+ /* check whether we have a readable file right away */
+ if (g_file_test (file_name, G_FILE_TEST_IS_REGULAR))
+ name = g_strdup (file_name);
+
+ /* try completing file name with standard library suffix */
+ if ( !name
+ && !g_str_has_suffix (file_name, "." G_MODULE_SUFFIX)) {
+ name = g_strconcat (file_name, "." G_MODULE_SUFFIX, NULL);
+ if (!g_file_test (name, G_FILE_TEST_IS_REGULAR)) {
+ g_free (name);
+ name = NULL;
+ }
+ }
+
+ /* g_module_open() would also try appending ".la". We don't do that
+ * because we require the user to specify a shared library (directly). */
+
+ return name;
+}
+
+char *
+_nm_utils_check_module_file (const char *name,
+ int check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error)
+{
+ gs_free char *name_resolved = NULL;
+ char *s;
+
+ if (!g_path_is_absolute (name)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("path is not absolute (%s)"), name);
+ return NULL;
+ }
+
+ name_resolved = _resolve_module_file_name (name);
+
+ if (!name_resolved) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("could not resolve plugin path (%s)"), name);
+ return NULL;
+ }
+
+ if (g_str_has_suffix (name_resolved, ".la")) {
+ /* g_module_open() treats files that end with .la special.
+ * We don't want to parse the libtool archive. Just error out. */
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("libtool archives are not supported (%s)"), name_resolved);
+ return NULL;
+ }
+
+ if (!_nm_utils_check_file (name_resolved,
+ check_owner,
+ check_file,
+ user_data,
+ NULL,
+ error)) {
+ return NULL;
+ }
+
+ s = name_resolved;
+ name_resolved = NULL;
+ return s;
+}
+
+/**********************************************************************************************/
+
/**
* nm_utils_file_search_in_paths:
* @progname: the helper program name, like "iptables"
@@ -3488,6 +3643,105 @@ nm_utils_bond_mode_string_to_int (const char *mode)
/**********************************************************************************************/
+#define STRSTRDICTKEY_V1_SET 0x01
+#define STRSTRDICTKEY_V2_SET 0x02
+#define STRSTRDICTKEY_ALL_SET 0x03
+
+struct _NMUtilsStrStrDictKey {
+ char type;
+ char data[1];
+};
+
+guint
+_nm_utils_strstrdictkey_hash (gconstpointer a)
+{
+ const NMUtilsStrStrDictKey *k = a;
+ const signed char *p;
+ guint32 h = 5381;
+
+ if (k) {
+ if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET)
+ g_return_val_if_reached (0);
+
+ h = (h << 5) + h + k->type;
+ if (k->type & STRSTRDICTKEY_ALL_SET) {
+ p = (void *) k->data;
+ for (; *p != '\0'; p++)
+ h = (h << 5) + h + *p;
+ if (k->type == STRSTRDICTKEY_ALL_SET) {
+ /* the key contains two strings. Continue... */
+ h = (h << 5) + h + '\0';
+ for (p++; *p != '\0'; p++)
+ h = (h << 5) + h + *p;
+ }
+ }
+ }
+
+ return h;
+}
+
+gboolean
+_nm_utils_strstrdictkey_equal (gconstpointer a, gconstpointer b)
+{
+ const NMUtilsStrStrDictKey *k1 = a;
+ const NMUtilsStrStrDictKey *k2 = b;
+
+ if (k1 == k2)
+ return TRUE;
+ if (!k1 || !k2)
+ return FALSE;
+
+ if (k1->type != k2->type)
+ return FALSE;
+
+ if (k1->type & STRSTRDICTKEY_ALL_SET) {
+ if (strcmp (k1->data, k2->data) != 0)
+ return FALSE;
+
+ if (k1->type == STRSTRDICTKEY_ALL_SET) {
+ gsize l = strlen (k1->data) + 1;
+
+ return strcmp (&k1->data[l], &k2->data[l]) == 0;
+ }
+ }
+
+ return TRUE;
+}
+
+NMUtilsStrStrDictKey *
+_nm_utils_strstrdictkey_create (const char *v1, const char *v2)
+{
+ char type = 0;
+ gsize l1 = 0, l2 = 0;
+ NMUtilsStrStrDictKey *k;
+
+ if (!v1 && !v2)
+ return g_malloc0 (1);
+
+ /* we need to distinguish between ("",NULL) and (NULL,"").
+ * Thus, in @type we encode which strings we have present
+ * as not-NULL. */
+ if (v1) {
+ type |= STRSTRDICTKEY_V1_SET;
+ l1 = strlen (v1) + 1;
+ }
+ if (v2) {
+ type |= STRSTRDICTKEY_V2_SET;
+ l2 = strlen (v2) + 1;
+ }
+
+ k = g_malloc (G_STRUCT_OFFSET (NMUtilsStrStrDictKey, data) + l1 + l2);
+ k->type = type;
+ if (v1)
+ memcpy (&k->data[0], v1, l1);
+ if (v2)
+ memcpy (&k->data[l1], v2, l2);
+
+ return k;
+}
+
+/**********************************************************************************************/
+
/* _nm_utils_ascii_str_to_int64:
*
* A wrapper for g_ascii_strtoll, that checks whether the whole string
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index c9a01d6e6e..64e57e875a 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -128,6 +128,10 @@ gboolean nm_utils_file_is_pkcs12 (const char *filename);
typedef gboolean (*NMUtilsFileSearchInPathsPredicate) (const char *filename, gpointer user_data);
+struct stat;
+
+typedef gboolean (*NMUtilsCheckFilePredicate) (const char *filename, const struct stat *stat, gpointer user_data, GError **error);
+
const char *nm_utils_file_search_in_paths (const char *progname,
const char *try_first,
const char *const *paths,
diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c
new file mode 100644
index 0000000000..d5fa6d819b
--- /dev/null
+++ b/libnm-core/nm-vpn-editor-plugin.c
@@ -0,0 +1,284 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2008 - 2010 Red Hat, Inc.
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "nm-vpn-editor-plugin.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include "nm-macros-internal.h"
+#include "gsystem-local-alloc.h"
+#include "nm-core-internal.h"
+
+static void nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface);
+
+G_DEFINE_INTERFACE (NMVpnEditorPlugin, nm_vpn_editor_plugin, G_TYPE_OBJECT)
+
+static void
+nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface)
+{
+ /* Properties */
+
+ /**
+ * NMVpnEditorPlugin:name:
+ *
+ * Short display name of the VPN plugin.
+ */
+ g_object_interface_install_property (iface,
+ g_param_spec_string (NM_VPN_EDITOR_PLUGIN_NAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVpnEditorPlugin:description:
+ *
+ * Longer description of the VPN plugin.
+ */
+ g_object_interface_install_property (iface,
+ g_param_spec_string (NM_VPN_EDITOR_PLUGIN_DESCRIPTION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVpnEditorPlugin:service:
+ *
+ * D-Bus service name of the plugin's VPN service.
+ */
+ g_object_interface_install_property (iface,
+ g_param_spec_string (NM_VPN_EDITOR_PLUGIN_SERVICE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_editor_plugin_load_from_file:
+ * @plugin_filename: The path to the share library to load.
+ * Apply some common heuristics to find the library, such as
+ * appending "so" file ending.
+ * If the path is not an absolute path or no matching module
+ * can be found, lookup inside a directory defined at compile time.
+ * Due to this, @check_file might be called for two different paths.
+ * @check_name: if not-null, check that the loaded plugin has
+ * the given name.
+ * @check_service: if not-null, check that the loaded plugin advertises
+ * the given service.
+ * @check_owner: if non-negative, check whether the file is owned
+ * by UID @check_owner or by root. In this case also check that
+ * the file is not writable by anybody else.
+ * @check_file: optional callback to validate the file prior to
+ * loading the shared library.
+ * @user_data: user data for @check_file
+ * @error: on failure the error reason.
+ *
+ * Load the shared libary @plugin_filename and create a new
+ * #NMVpnEditorPlugin instace via the #NMVpnEditorPluginFactory
+ * function.
+ *
+ * Returns: (transfer-full): a new plugin instance or %NULL on error.
+ *
+ * Since: 1.2
+ */
+NMVpnEditorPlugin *
+nm_vpn_editor_plugin_load_from_file (const char *plugin_filename,
+ const char *check_name,
+ const char *check_service,
+ int check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error)
+{
+ GModule *module = NULL;
+ gs_free_error GError *local = NULL;
+ NMVpnEditorPluginFactory factory = NULL;
+ NMVpnEditorPlugin *editor_plugin = NULL;
+
+ g_return_val_if_fail (plugin_filename && *plugin_filename, NULL);
+
+ if (g_path_is_absolute (plugin_filename)) {
+ gs_free char *module_filename = NULL;
+
+ module_filename = _nm_utils_check_module_file (plugin_filename,
+ check_owner,
+ check_file,
+ user_data,
+ &local);
+ if (module_filename)
+ module = g_module_open (module_filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+ }
+
+ if (!module) {
+ if (local) {
+ g_propagate_error (error, local);
+ local = NULL;
+ } else {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("cannot load plugin %s"), plugin_filename);
+ }
+ return NULL;
+ }
+ g_clear_error (&local);
+
+ if (g_module_symbol (module, "nm_vpn_editor_plugin_factory", (gpointer) &factory)) {
+ gs_free_error GError *factory_error = NULL;
+ gboolean success = FALSE;
+
+ editor_plugin = factory (&factory_error);
+
+ g_assert (!editor_plugin || G_IS_OBJECT (editor_plugin));
+
+ if (editor_plugin) {
+ gs_free char *plug_name = NULL, *plug_service = NULL;
+
+ /* Validate plugin properties */
+
+ g_object_get (G_OBJECT (editor_plugin),
+ NM_VPN_EDITOR_PLUGIN_NAME, &plug_name,
+ NM_VPN_EDITOR_PLUGIN_SERVICE, &plug_service,
+ NULL);
+
+ if (check_name && g_strcmp0 (plug_name, check_name) != 0) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("cannot load VPN plugin in '%s': invalid plugin name"),
+ g_module_name (module));
+ } else if ( check_service
+ && g_strcmp0 (plug_service, check_service) != 0) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("cannot load VPN plugin in '%s': invalid service name"),
+ g_module_name (module));
+ } else {
+ /* Success! */
+ g_object_set_data_full (G_OBJECT (editor_plugin), "gmodule", module,
+ (GDestroyNotify) g_module_close);
+ success = TRUE;
+ }
+ } else {
+ if (factory_error) {
+ g_propagate_error (error, factory_error);
+ factory_error = NULL;
+ } else {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("unknown error initializing plugin %s"), plugin_filename);
+ }
+ }
+
+ if (!success) {
+ g_module_close (module);
+ editor_plugin = NULL;
+ }
+ } else {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("failed to load nm_vpn_editor_plugin_factory() from %s (%s)"),
+ g_module_name (module), g_module_error ());
+ g_module_close (module);
+ editor_plugin = NULL;
+ }
+
+ return editor_plugin;
+}
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_editor_plugin_get_editor:
+ *
+ * Returns: (transfer full):
+ */
+NMVpnEditor *
+nm_vpn_editor_plugin_get_editor (NMVpnEditorPlugin *plugin,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
+
+ return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_editor (plugin, connection, error);
+}
+
+NMVpnEditorPluginCapability
+nm_vpn_editor_plugin_get_capabilities (NMVpnEditorPlugin *plugin)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), 0);
+
+ return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_capabilities (plugin);
+}
+
+/**
+ * nm_vpn_editor_plugin_import:
+ *
+ * Returns: (transfer full):
+ */
+NMConnection *
+nm_vpn_editor_plugin_import (NMVpnEditorPlugin *plugin,
+ const char *path,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
+
+ if (nm_vpn_editor_plugin_get_capabilities (plugin) & NM_VPN_EDITOR_PLUGIN_CAPABILITY_IMPORT) {
+ g_return_val_if_fail (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->import_from_file != NULL, NULL);
+ return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->import_from_file (plugin, path, error);
+ }
+ return NULL;
+}
+
+gboolean
+nm_vpn_editor_plugin_export (NMVpnEditorPlugin *plugin,
+ const char *path,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), FALSE);
+
+ if (nm_vpn_editor_plugin_get_capabilities (plugin) & NM_VPN_EDITOR_PLUGIN_CAPABILITY_EXPORT) {
+ g_return_val_if_fail (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->export_to_file != NULL, FALSE);
+ return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->export_to_file (plugin, path, connection, error);
+ }
+ return FALSE;
+}
+
+char *
+nm_vpn_editor_plugin_get_suggested_filename (NMVpnEditorPlugin *plugin,
+ NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
+
+ if (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_suggested_filename)
+ return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_suggested_filename (plugin, connection);
+ return NULL;
+}
+
diff --git a/libnm/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h
index 5910aa3653..83d765d097 100644
--- a/libnm/nm-vpn-editor-plugin.h
+++ b/libnm-core/nm-vpn-editor-plugin.h
@@ -15,8 +15,8 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright 2008 - 2014 Red Hat, Inc.
* Copyright 2008 Novell, Inc.
+ * Copyright 2008 - 2015 Red Hat, Inc.
*/
#ifndef __NM_VPN_EDITOR_PLUGIN_H__
@@ -28,7 +28,9 @@
#include <glib.h>
#include <glib-object.h>
-#include <nm-types.h>
+
+#include "nm-connection.h"
+#include "nm-utils.h"
G_BEGIN_DECLS
@@ -137,53 +139,15 @@ gboolean nm_vpn_editor_plugin_export (NMVpnEditorPlugin *pl
char *nm_vpn_editor_plugin_get_suggested_filename (NMVpnEditorPlugin *plugin,
NMConnection *connection);
-/**************************************************/
-/* Editor interface */
-/**************************************************/
-
-#define NM_TYPE_VPN_EDITOR (nm_vpn_editor_get_type ())
-#define NM_VPN_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_EDITOR, NMVpnEditor))
-#define NM_IS_VPN_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_EDITOR))
-#define NM_VPN_EDITOR_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_EDITOR, NMVpnEditorInterface))
-
-/**
- * NMVpnEditorInterface:
- * @g_iface: the parent interface
- * @get_widget: return the #GtkWidget for the VPN editor's UI
- * @placeholder: not currently used
- * @update_connection: called to save the user-entered options to the connection
- * object. Should return %FALSE and set @error if the current options are
- * invalid. @error should contain enough information for the plugin to
- * determine which UI widget is invalid at a later point in time. For
- * example, creating unique error codes for what error occurred and populating
- * the message field of @error with the name of the invalid property.
- * @changed: emitted when the value of a UI widget changes. May trigger a
- * validity check via @update_connection to write values to the connection.
- *
- * Interface for editing a specific #NMConnection
- */
-typedef struct {
- GTypeInterface g_iface;
-
- GObject * (*get_widget) (NMVpnEditor *editor);
-
- void (*placeholder) (void);
-
- gboolean (*update_connection) (NMVpnEditor *editor,
- NMConnection *connection,
- GError **error);
-
- void (*changed) (NMVpnEditor *editor);
-} NMVpnEditorInterface;
-
-GType nm_vpn_editor_get_type (void);
-
-GObject * nm_vpn_editor_get_widget (NMVpnEditor *editor);
-
-gboolean nm_vpn_editor_update_connection (NMVpnEditor *editor,
- NMConnection *connection,
- GError **error);
+NM_AVAILABLE_IN_1_2
+NMVpnEditorPlugin *nm_vpn_editor_plugin_load_from_file (const char *plugin_filename,
+ const char *check_name,
+ const char *check_service,
+ int check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error);
G_END_DECLS
-#endif /* NM_VPN_EDITOR_PLUGIN_H */
+#endif /* __NM_VPN_EDITOR_PLUGIN_H__ */
diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c
new file mode 100644
index 0000000000..762064a5b7
--- /dev/null
+++ b/libnm-core/nm-vpn-plugin-info.c
@@ -0,0 +1,1016 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "nm-vpn-plugin-info.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "gsystem-local-alloc.h"
+#include "nm-errors.h"
+#include "nm-macros-internal.h"
+#include "nm-core-internal.h"
+
+#define DEFAULT_DIR_ETC NMCONFDIR"/VPN"
+#define DEFAULT_DIR_LIB NMLIBDIR"/VPN"
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_FILENAME,
+ PROP_KEYFILE,
+
+ LAST_PROP,
+};
+
+typedef struct {
+ char *filename;
+ char *name;
+ char *service;
+ GKeyFile *keyfile;
+
+ /* It is convenient for nm_vpn_plugin_info_lookup_property() to return a const char *,
+ * contrary to what g_key_file_get_string() does. Hence we must cache the returned
+ * value somewhere... let's put it in an internal hash table.
+ * This contains a clone of all the strings in keyfile. */
+ GHashTable *keys;
+
+ gboolean editor_plugin_loaded;
+ NMVpnEditorPlugin *editor_plugin;
+} NMVpnPluginInfoPrivate;
+
+static void nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NMVpnPluginInfo, nm_vpn_plugin_info, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_plugin_info_initable_iface_init);
+ )
+
+#define NM_VPN_PLUGIN_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfoPrivate))
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_plugin_info_validate_filename:
+ * @filename: the filename to check
+ *
+ * Regular name files have a certain pattern. That basically means
+ * they have the file extension "name". Check if @filename
+ * is valid according to that pattern.
+ *
+ * Since: 1.2
+ */
+gboolean
+nm_vpn_plugin_info_validate_filename (const char *filename)
+{
+ if (!filename || !g_str_has_suffix (filename, ".name"))
+ return FALSE;
+
+ /* originally, we didn't do further checks... but here we go. */
+ if (filename[0] == '.') {
+ /* this also rejects name ".name" alone. */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+nm_vpn_plugin_info_check_file_full (const char *filename,
+ gboolean check_absolute,
+ gboolean do_validate_filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ struct stat *out_st,
+ GError **error)
+{
+ if (!filename || !*filename) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("missing filename"));
+ return FALSE;
+ }
+
+ if (check_absolute && !g_path_is_absolute (filename)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("filename must be an absolute path (%s)"), filename);
+ return FALSE;
+ }
+
+ if ( do_validate_filename
+ && !nm_vpn_plugin_info_validate_filename (filename)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("filename has invalid format (%s)"), filename);
+ return FALSE;
+ }
+
+ return _nm_utils_check_file (filename,
+ check_owner,
+ check_file,
+ user_data,
+ out_st,
+ error);
+}
+
+/**
+ * _nm_vpn_plugin_info_check_file:
+ * @filename:
+ * @check_absolute: if %TRUE, only allow absolute path names.
+ * @do_validate_filename: if %TRUE, only accept the filename if
+ * nm_vpn_plugin_info_validate_filename() succeeds.
+ * @check_owner: if non-negative, only accept the file if the
+ * owner UID is equal to @check_owner or if the owner is 0.
+ * In this case, also check that the file is not writable by
+ * other users.
+ * @check_file: pass a callback to do your own validation.
+ * @user_data: user data for @check_file.
+ * @error: (allow-none): (out): the error reason if the check fails.
+ *
+ * Check whether the file exists and is a valid name file (in keyfile format).
+ * Additionally, also check for file permissions.
+ *
+ * Returns: %TRUE if a file @filename exists and has valid permissions.
+ *
+ * Since: 1.2
+ */
+gboolean
+_nm_vpn_plugin_info_check_file (const char *filename,
+ gboolean check_absolute,
+ gboolean do_validate_filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data,
+ GError **error)
+{
+ return nm_vpn_plugin_info_check_file_full (filename, check_absolute, do_validate_filename, check_owner, check_file, user_data, NULL, error);
+}
+
+typedef struct {
+ NMVpnPluginInfo *plugin_info;
+ struct stat stat;
+} LoadDirInfo;
+
+static int
+_sort_files (LoadDirInfo *a, LoadDirInfo *b)
+{
+ time_t ta, tb;
+
+ ta = MAX (a->stat.st_mtime, a->stat.st_ctime);
+ tb = MAX (b->stat.st_mtime, b->stat.st_ctime);
+ if (ta < tb)
+ return 1;
+ if (ta > tb)
+ return -1;
+ return g_strcmp0 (nm_vpn_plugin_info_get_filename (a->plugin_info),
+ nm_vpn_plugin_info_get_filename (b->plugin_info));
+}
+
+/**
+ * _nm_vpn_plugin_info_get_default_dir_etc:
+ *
+ * Returns: (transfer-none): compile time constant of the default
+ * VPN plugin directory.
+ */
+const char *
+_nm_vpn_plugin_info_get_default_dir_etc ()
+{
+ return DEFAULT_DIR_ETC;
+}
+
+/**
+ * _nm_vpn_plugin_info_get_default_dir_lib:
+ *
+ * Returns: (transfer-none): compile time constant of the default
+ * VPN plugin directory.
+ */
+const char *
+_nm_vpn_plugin_info_get_default_dir_lib ()
+{
+ return DEFAULT_DIR_LIB;
+}
+
+/**
+ * _nm_vpn_plugin_info_get_default_dir_user:
+ *
+ * Returns: The user can specify a different directory for VPN plugins
+ * by setting NM_VPN_PLUGIN_DIR environment variable. Return
+ * that directory.
+ */
+const char *
+_nm_vpn_plugin_info_get_default_dir_user ()
+{
+ return g_getenv ("NM_VPN_PLUGIN_DIR");
+}
+
+/**
+ * _nm_vpn_plugin_info_list_load_dir:
+ * @dirname: the name of the directory to load.
+ * @do_validate_filename: only consider filenames that have a certain
+ * pattern (i.e. end with ".name").
+ * @check_owner: if set to a non-negative number, check that the file
+ * owner is either the same uid or 0. In that case, also check
+ * that the file is not writable by group or other.
+ * @check_file: (allow-none): callback to check whether the file is valid.
+ * @user_data: data for @check_file
+ *
+ * Iterate over the content of @dirname and load name files.
+ *
+ * Returns: (transfer-full): list of loaded plugin infos.
+ */
+GSList *
+_nm_vpn_plugin_info_list_load_dir (const char *dirname,
+ gboolean do_validate_filename,
+ gint64 check_owner,
+ NMUtilsCheckFilePredicate check_file,
+ gpointer user_data)
+{
+ GDir *dir;
+ const char *fn;
+ GArray *array;
+ GSList *res = NULL;
+ guint i;
+
+ g_return_val_if_fail (dirname && dirname[0], NULL);
+
+ dir = g_dir_open (dirname, 0, NULL);
+ if (!dir)
+ return NULL;
+
+ array = g_array_new (FALSE, FALSE, sizeof (LoadDirInfo));
+
+ while ((fn = g_dir_read_name (dir))) {
+ gs_free char *filename = NULL;
+ LoadDirInfo info = { 0 };
+
+ filename = g_build_filename (dirname, fn, NULL);
+ if (nm_vpn_plugin_info_check_file_full (filename,
+ FALSE,
+ do_validate_filename,
+ check_owner,
+ check_file,
+ user_data,
+ &info.stat,
+ NULL)) {
+ info.plugin_info = nm_vpn_plugin_info_new_from_file (filename, NULL);
+ if (info.plugin_info) {
+ g_array_append_val (array, info);
+ continue;
+ }
+ }
+ }
+ g_dir_close (dir);
+
+ /* sort the files so that we have a stable behavior. The directory might contain
+ * duplicate VPNs, so while nm_vpn_plugin_info_list_load() would load them all, the
+ * caller probably wants to reject duplicates. Having a stable order means we always
+ * reject the same files in face of duplicates. */
+ g_array_sort (array, (GCompareFunc) _sort_files);
+
+ for (i = 0; i < array->len; i++)
+ res = g_slist_prepend (res, g_array_index (array, LoadDirInfo, i).plugin_info);
+
+ g_array_unref (array);
+
+ return g_slist_reverse (res);
+}
+
+/**
+ * nm_vpn_plugin_info_list_load:
+ *
+ * Returns: (tranfer-full): list of plugins loaded from the default
+ * directories rejecting duplicates.
+ *
+ * Since: 1.2
+ */
+GSList *
+nm_vpn_plugin_info_list_load ()
+{
+ int i;
+ gint64 uid;
+ GSList *list = NULL;
+ GSList *infos, *info;
+ const char *dir[] = {
+ /* We load plugins from NM_VPN_PLUGIN_DIR *and* DEFAULT_DIR*, with
+ * preference to the former.
+ *
+ * load user directory with highest priority. */
+ _nm_vpn_plugin_info_get_default_dir_user (),
+
+ /* lib directory has higher priority then etc. The reason is that
+ * etc is deprecated and used by old plugins. We expect newer plugins
+ * to install their file in lib, where they have higher priority.
+ *
+ * Optimally, there are no duplicates anyway, so it doesn't really matter. */
+ _nm_vpn_plugin_info_get_default_dir_lib (),
+ _nm_vpn_plugin_info_get_default_dir_etc (),
+ };
+
+ uid = getuid ();
+
+ for (i = 0; i < G_N_ELEMENTS (dir); i++) {
+ if ( !dir[i]
+ || _nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0)
+ continue;
+
+ infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL);
+
+ for (info = infos; info; info = info->next)
+ nm_vpn_plugin_info_list_add (&list, info->data, NULL);
+
+ g_slist_free_full (infos, g_object_unref);
+ }
+ return list;
+}
+
+/*********************************************************************/
+
+static gboolean
+_check_no_conflict (NMVpnPluginInfo *i1, NMVpnPluginInfo *i2, GError **error)
+{
+ NMVpnPluginInfoPrivate *priv1, *priv2;
+ uint i;
+ struct {
+ const char *group;
+ const char *key;
+ } check_list[] = {
+ { NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service" },
+ { NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin" },
+ { NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties" },
+ };
+
+ priv1 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i1);
+ priv2 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i2);
+
+ for (i = 0; i < G_N_ELEMENTS (check_list); i++) {
+ gs_free NMUtilsStrStrDictKey *k = NULL;
+ const char *s1, *s2;
+
+ k = _nm_utils_strstrdictkey_create (check_list[i].group, check_list[i].key);
+ s1 = g_hash_table_lookup (priv1->keys, k);
+ if (!s1)
+ continue;
+ s2 = g_hash_table_lookup (priv2->keys, k);
+ if (!s2)
+ continue;
+
+ if (strcmp (s1, s2) == 0) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("there exists a conflicting plugin (%s) that has the same %s.%s value"),
+ priv2->name,
+ check_list[i].group, check_list[i].key);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * nm_vpn_plugin_info_list_add:
+ * @list: list of plugins
+ * @plugin_info: instance to add
+ * @error: failure reason
+ *
+ * Returns: %TRUE if the plugin was added to @list. This will fail
+ * to add duplicate plugins.
+ *
+ * Since: 1.2
+ */
+gboolean
+nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError **error)
+{
+ GSList *iter;
+ const char *name;
+
+ g_return_val_if_fail (list, FALSE);
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
+
+ name = nm_vpn_plugin_info_get_name (plugin_info);
+ for (iter = *list; iter; iter = iter->next) {
+ if (iter->data == plugin_info)
+ return TRUE;
+
+ if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("there exists a conflicting plugin with the same name (%s)"),
+ name);
+ return FALSE;
+ }
+
+ /* the plugin must have unique values for certain properties. E.g. two different
+ * plugins cannot share the same D-Bus service name. */
+ if (!_check_no_conflict (plugin_info, iter->data, error))
+ return FALSE;
+ }
+
+ *list = g_slist_append (*list, g_object_ref (plugin_info));
+ return TRUE;
+}
+
+/**
+ * nm_vpn_plugin_info_list_remove:
+ * @list: list of plugins
+ * @plugin_info: instance
+ *
+ * Remove @plugin_info from @list.
+ *
+ * Returns: %TRUE if @plugin_info was in @list and successfully removed.
+ *
+ * Since: 1.2
+ */
+gboolean
+nm_vpn_plugin_info_list_remove (GSList **list, NMVpnPluginInfo *plugin_info)
+{
+ if (!plugin_info)
+ return FALSE;
+
+ g_return_val_if_fail (list, FALSE);
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
+
+ if (!g_slist_find (*list, plugin_info))
+ return FALSE;
+
+ *list = g_slist_remove (*list, plugin_info);
+ g_object_unref (plugin_info);
+ return TRUE;
+}
+
+/**
+ * nm_vpn_plugin_info_list_find_by_name:
+ * @list: list of plugins
+ * @name: name to search
+ *
+ * Returns: the first plugin with a matching @name (or %NULL).
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_list_find_by_name (GSList *list, const char *name)
+{
+ GSList *iter;
+
+ if (!name)
+ g_return_val_if_reached (NULL);
+
+ for (iter = list; iter; iter = iter->next) {
+ if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0)
+ return iter->data;
+ }
+ return NULL;
+}
+
+/**
+ * nm_vpn_plugin_info_list_find_by_filename:
+ * @list: list of plugins
+ * @filename: filename to search
+ *
+ * Returns: the first plugin with a matching @filename (or %NULL).
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename)
+{
+ GSList *iter;
+
+ if (!filename)
+ g_return_val_if_reached (NULL);
+
+ for (iter = list; iter; iter = iter->next) {
+ if (g_strcmp0 (nm_vpn_plugin_info_get_filename (iter->data), filename) == 0)
+ return iter->data;
+ }
+ return NULL;
+}
+
+/**
+ * nm_vpn_plugin_info_list_find_by_service:
+ * @list: list of plugins
+ * @service: service to search
+ *
+ * Returns: the first plugin with a matching @service (or %NULL).
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service)
+{
+ GSList *iter;
+
+ if (!service)
+ g_return_val_if_reached (NULL);
+
+ for (iter = list; iter; iter = iter->next) {
+ if (strcmp (nm_vpn_plugin_info_get_service (iter->data), service) == 0)
+ return iter->data;
+ }
+ return NULL;
+}
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_plugin_info_get_filename:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer-none): the filename. Can be %NULL.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_get_filename (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->filename;
+}
+
+/**
+ * nm_vpn_plugin_info_get_name:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer-none): the name. Cannot be %NULL.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->name;
+}
+
+/**
+ * nm_vpn_plugin_info_get_service:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer-none): the service. Cannot be %NULL.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_get_service (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->service;
+}
+
+/**
+ * nm_vpn_plugin_info_get_plugin:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer-none): the plugin. Can be %NULL.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
+ _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin"));
+}
+
+/**
+ * nm_vpn_plugin_info_get_program:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer-none): the program. Can be %NULL.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
+ _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "program"));
+}
+
+/**
+ * nm_vpn_plugin_info_lookup_property:
+ * @self: plugin info instance
+ * @group: group name
+ * @key: name of the property
+ *
+ * Returns: (transfer-none): #NMVpnPluginInfo is internally a #GKeyFile. Returns the matching
+ * property.
+ *
+ * Since: 1.2
+ */
+const char *
+nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key)
+{
+ NMVpnPluginInfoPrivate *priv;
+ gs_free NMUtilsStrStrDictKey *k = NULL;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+ g_return_val_if_fail (group, NULL);
+ g_return_val_if_fail (key, NULL);
+
+ priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ k = _nm_utils_strstrdictkey_create (group, key);
+ return g_hash_table_lookup (priv->keys, k);
+}
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_plugin_info_get_editor_plugin:
+ * @self: plugin info instance
+ *
+ * Returns: the cached #NMVpnEditorPlugin instance.
+ *
+ * Since: 1.2
+ */
+NMVpnEditorPlugin *
+nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->editor_plugin;
+}
+
+/**
+ * nm_vpn_plugin_info_set_editor_plugin:
+ * @self: plugin info instance
+ * @plugin: (allow-none): plugin instance
+ *
+ * Set the internal plugin instance. If %NULL, only clear the previous instance.
+ *
+ * Since: 1.2
+ */
+void
+nm_vpn_plugin_info_set_editor_plugin (NMVpnPluginInfo *self, NMVpnEditorPlugin *plugin)
+{
+ NMVpnPluginInfoPrivate *priv;
+ NMVpnEditorPlugin *old;
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (self));
+ g_return_if_fail (!plugin || G_IS_OBJECT (plugin));
+
+ priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ if (!plugin) {
+ priv->editor_plugin_loaded = FALSE;
+ g_clear_object (&priv->editor_plugin);
+ } else {
+ old = priv->editor_plugin;
+ priv->editor_plugin = g_object_ref (plugin);
+ priv->editor_plugin_loaded = TRUE;
+ if (old)
+ g_object_unref (old);
+ }
+}
+
+/**
+ * nm_vpn_plugin_info_load_editor_plugin:
+ * @self: plugin info instance
+ * @error: error reason on failure
+ *
+ * Returns: loads the plugin and returns the newly created instance.
+ * The plugin is owned by @self and can be later retrieved again
+ * via nm_vpn_plugin_info_get_editor_plugin(). You can load the
+ * plugin only once, unless you reset the state via
+ * nm_vpn_plugin_info_set_editor_plugin().
+ *
+ * Since: 1.2
+ */
+NMVpnEditorPlugin *
+nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error)
+{
+ NMVpnPluginInfoPrivate *priv;
+ const char *plugin_filename;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ if (priv->editor_plugin)
+ return priv->editor_plugin;
+
+ plugin_filename = nm_vpn_plugin_info_get_plugin (self);
+ if (!plugin_filename || !*plugin_filename) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("missing \"plugin\" setting"));
+ return NULL;
+ }
+
+ /* We only try once to load the plugin. If we previously tried and it was
+ * unsuccessful, error out immediately. */
+ if (priv->editor_plugin_loaded) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ _("%s: don't retry loading plugin which already failed previously"), priv->name);
+ return NULL;
+ }
+
+ priv->editor_plugin_loaded = TRUE;
+ priv->editor_plugin = nm_vpn_editor_plugin_load_from_file (plugin_filename,
+ priv->name,
+ nm_vpn_plugin_info_get_service (self),
+ getuid (),
+ NULL,
+ NULL,
+ error);
+ return priv->editor_plugin;
+}
+
+/*********************************************************************/
+
+/**
+ * nm_vpn_plugin_info_new_from_file:
+ * @filename: filename to read.
+ * @error: on failure, the error reason.
+ *
+ * Read the plugin info from file @filename. Does not do
+ * any further verification on the file. You might want to check
+ * file permissions and ownership of the file.
+ *
+ * Returns: %NULL if there is any error or a newly created
+ * #NMVpnPluginInfo instance.
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_new_from_file (const char *filename,
+ GError **error)
+{
+ g_return_val_if_fail (filename, NULL);
+
+ return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
+ NULL,
+ error,
+ NM_VPN_PLUGIN_INFO_FILENAME, filename,
+ NULL));
+}
+
+/**
+ * nm_vpn_plugin_info_new_with_data:
+ * @filename: optional filename.
+ * @keyfile: inject data for the plugin info instance.
+ * @error: construction may fail if the keyfile lacks mandatory fields.
+ * In this case, return the error reason.
+ *
+ * This constructor does not read any data from file but
+ * takes instead a @keyfile argument.
+ *
+ * Returns: new plugin info instance.
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_new_with_data (const char *filename,
+ GKeyFile *keyfile,
+ GError **error)
+{
+ g_return_val_if_fail (keyfile, NULL);
+
+ return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
+ NULL,
+ error,
+ NM_VPN_PLUGIN_INFO_FILENAME, filename,
+ NM_VPN_PLUGIN_INFO_KEYFILE, keyfile,
+ NULL));
+}
+
+/*********************************************************************/
+
+static void
+nm_vpn_plugin_info_init (NMVpnPluginInfo *plugin)
+{
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (initable);
+ NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+ gs_strfreev char **groups = NULL;
+ guint i, j;
+
+ if (!priv->keyfile) {
+ if (!priv->filename) {
+ g_set_error_literal (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("missing filename to load VPN plugin info"));
+ return FALSE;
+ }
+ priv->keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (priv->keyfile, priv->filename, G_KEY_FILE_NONE, error))
+ return FALSE;
+ }
+
+ /* we reqire at least a "name" */
+ priv->name = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "name", NULL);
+ if (!priv->name || !priv->name[0]) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("missing name for VPN plugin info"));
+ return FALSE;
+ }
+
+ /* we also require "service", because that how we associate NMSettingVpn:service-type with the
+ * NMVpnPluginInfo. */
+ priv->service = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service", NULL);
+ if (!priv->service || !*priv->service) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("missing service for VPN plugin info"));
+ return FALSE;
+ }
+
+ priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash,
+ _nm_utils_strstrdictkey_equal,
+ g_free, g_free);
+ groups = g_key_file_get_groups (priv->keyfile, NULL);
+ for (i = 0; groups && groups[i]; i++) {
+ gs_strfreev char **keys = NULL;
+
+ keys = g_key_file_get_keys (priv->keyfile, groups[i], NULL, NULL);
+ for (j = 0; keys && keys[j]; j++) {
+ char *s;
+
+ /* Lookup the value via get_string(). We want that behavior.
+ * You could still lookup the original values via g_key_file_get_value()
+ * based on priv->keyfile. */
+ s = g_key_file_get_string (priv->keyfile, groups[i], keys[j], NULL);
+ if (s)
+ g_hash_table_insert (priv->keys, _nm_utils_strstrdictkey_create (groups[i], keys[j]), s);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ priv->filename = g_value_dup_string (value);
+ break;
+ case PROP_KEYFILE:
+ priv->keyfile = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, priv->filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
+ NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ g_clear_object (&priv->editor_plugin);
+
+ G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
+ NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ g_free (priv->name);
+ g_free (priv->service);
+ g_free (priv->filename);
+ g_key_file_unref (priv->keyfile);
+ g_hash_table_unref (priv->keys);
+
+ G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->finalize (object);
+}
+
+static void
+nm_vpn_plugin_info_class_init (NMVpnPluginInfoClass *plugin_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
+
+ g_type_class_add_private (object_class, sizeof (NMVpnPluginInfoPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMVpnPluginInfo:name:
+ *
+ * The name of the VPN plugin.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property
+ (object_class, PROP_NAME,
+ g_param_spec_string (NM_VPN_PLUGIN_INFO_NAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVpnPluginInfo:filename:
+ *
+ * The filename from which the info was loaded.
+ * Can be %NULL if the instance was not loaded from
+ * a file (i.e. the keyfile instance was passed to the
+ * constructor).
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property
+ (object_class, PROP_FILENAME,
+ g_param_spec_string (NM_VPN_PLUGIN_INFO_FILENAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVpnPluginInfo:keyfile:
+ *
+ * Initalize the instance with a different keyfile instance.
+ * When passing a keyfile instance, the constructor will not
+ * try to read from filename.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property
+ (object_class, PROP_KEYFILE,
+ g_param_spec_boxed (NM_VPN_PLUGIN_INFO_KEYFILE, "", "",
+ G_TYPE_KEY_FILE,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = init_sync;
+}
+
diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h
new file mode 100644
index 0000000000..9a7246f840
--- /dev/null
+++ b/libnm-core/nm-vpn-plugin-info.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_VPN_PLUGIN_INFO_H__
+#define __NM_VPN_PLUGIN_INFO_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-utils.h"
+#include "nm-vpn-editor-plugin.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_VPN_PLUGIN_INFO (nm_vpn_plugin_info_get_type ())
+#define NM_VPN_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfo))
+#define NM_VPN_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfoClass))
+#define NM_IS_VPN_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN_INFO))
+#define NM_IS_VPN_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_PLUGIN_INFO))
+#define NM_VPN_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfoClass))
+
+#define NM_VPN_PLUGIN_INFO_NAME "name"
+#define NM_VPN_PLUGIN_INFO_FILENAME "filename"
+#define NM_VPN_PLUGIN_INFO_KEYFILE "keyfile"
+
+#define NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION "VPN Connection"
+#define NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM "libnm"
+#define NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME "GNOME"
+
+typedef struct {
+ NM_AVAILABLE_IN_1_2
+ GObject parent;
+} NMVpnPluginInfo NM_AVAILABLE_IN_1_2;
+
+typedef struct {
+ NM_AVAILABLE_IN_1_2
+ GObjectClass parent;
+
+ /*< private >*/
+ NM_AVAILABLE_IN_1_2
+ gpointer padding[8];
+} NMVpnPluginInfoClass NM_AVAILABLE_IN_1_2;
+
+NM_AVAILABLE_IN_1_2
+GType nm_vpn_plugin_info_get_type (void);
+
+NM_AVAILABLE_IN_1_2
+NMVpnPluginInfo *nm_vpn_plugin_info_new_from_file (const char *filename,
+ GError **error);
+
+NM_AVAILABLE_IN_1_2
+NMVpnPluginInfo *nm_vpn_plugin_info_new_with_data (const char *filename,
+ GKeyFile *keyfile,
+ GError **error);
+
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_get_filename (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_get_service (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2
+const char *nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key);
+
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_plugin_info_validate_filename (const char *filename);
+
+NM_AVAILABLE_IN_1_2
+GSList *nm_vpn_plugin_info_list_load (void);
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError **error);
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_plugin_info_list_remove (GSList **list, NMVpnPluginInfo *plugin_info);
+NM_AVAILABLE_IN_1_2
+NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_name (GSList *list, const char *name);
+NM_AVAILABLE_IN_1_2
+NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename);
+NM_AVAILABLE_IN_1_2
+NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service);
+
+
+NM_AVAILABLE_IN_1_2
+NMVpnEditorPlugin *nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *plugin_info);
+NM_AVAILABLE_IN_1_2
+void nm_vpn_plugin_info_set_editor_plugin (NMVpnPluginInfo *self,
+ NMVpnEditorPlugin *plugin);
+NM_AVAILABLE_IN_1_2
+NMVpnEditorPlugin *nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *plugin_info,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __NM_VPN_PLUGIN_INFO_H__ */
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index e8e96bb756..dfe591298e 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -4282,6 +4282,54 @@ test_nm_utils_ascii_str_to_int64 (void)
/******************************************************************************/
static void
+test_nm_utils_strstrdictkey ()
+{
+#define _VALUES_STATIC(_v1, _v2) { .v1 = _v1, .v2 = _v2, .v_static = _nm_utils_strstrdictkey_static (_v1, _v2), }
+ const struct {
+ const char *v1;
+ const char *v2;
+ NMUtilsStrStrDictKey *v_static;
+ } *val1, *val2, values[] = {
+ { NULL, NULL },
+ { "", NULL },
+ { NULL, "" },
+ { "a", NULL },
+ { NULL, "a" },
+ _VALUES_STATIC ("", ""),
+ _VALUES_STATIC ("a", ""),
+ _VALUES_STATIC ("", "a"),
+ _VALUES_STATIC ("a", "b"),
+ };
+ guint i, j;
+
+ for (i = 0; i < G_N_ELEMENTS (values); i++) {
+ gs_free NMUtilsStrStrDictKey *key1 = NULL;
+
+ val1 = &values[i];
+
+ key1 = _nm_utils_strstrdictkey_create (val1->v1, val1->v2);
+ if (val1->v_static) {
+ g_assert (_nm_utils_strstrdictkey_equal (key1, val1->v_static));
+ g_assert (_nm_utils_strstrdictkey_equal (val1->v_static, key1));
+ g_assert_cmpint (_nm_utils_strstrdictkey_hash (key1), ==, _nm_utils_strstrdictkey_hash (val1->v_static));
+ }
+
+ for (j = 0; j < G_N_ELEMENTS (values); j++) {
+ gs_free NMUtilsStrStrDictKey *key2 = NULL;
+
+ val2 = &values[j];
+ key2 = _nm_utils_strstrdictkey_create (val2->v1, val2->v2);
+ if (i != j) {
+ g_assert (!_nm_utils_strstrdictkey_equal (key1, key2));
+ g_assert (!_nm_utils_strstrdictkey_equal (key2, key1));
+ }
+ }
+ }
+}
+
+/******************************************************************************/
+
+static void
test_nm_utils_dns_option_validate_do (char *option, gboolean ipv6, const NMUtilsDNSOptionDesc *descs,
gboolean exp_result, char *exp_name, gboolean exp_value)
{
@@ -4764,6 +4812,7 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert);
g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
+ g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate);
g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);
diff --git a/libnm/Makefile.am b/libnm/Makefile.am
index 2c9d94ef54..0a41ab5752 100644
--- a/libnm/Makefile.am
+++ b/libnm/Makefile.am
@@ -50,12 +50,13 @@ libnminclude_hfiles = \
nm-remote-connection.h \
nm-types.h \
nm-vpn-connection.h \
- nm-vpn-editor-plugin.h \
+ nm-vpn-editor.h \
nm-wimax-nsp.h
libnminclude_nointrospect_hfiles = \
nm-secret-agent-old.h \
- nm-vpn-plugin-old.h
+ nm-vpn-plugin-old.h \
+ nm-vpn-service-plugin.h
libnminclude_HEADERS = \
$(libnminclude_hfiles) \
@@ -108,7 +109,8 @@ libnm_la_csources = \
nm-secret-agent-old.c \
nm-vpn-connection.c \
nm-vpn-plugin-old.c \
- nm-vpn-editor-plugin.c \
+ nm-vpn-editor.c \
+ nm-vpn-service-plugin.c \
nm-wimax-nsp.c
libnm_la_SOURCES = \
diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h
index 251a042216..085437e6f5 100644
--- a/libnm/NetworkManager.h
+++ b/libnm/NetworkManager.h
@@ -81,7 +81,9 @@
#include <nm-version.h>
#include <nm-vpn-connection.h>
#include <nm-vpn-dbus-interface.h>
+#include <nm-vpn-editor.h>
#include <nm-vpn-editor-plugin.h>
+#include <nm-vpn-plugin-info.h>
#include <nm-wimax-nsp.h>
#undef __NETWORKMANAGER_H_INSIDE__
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index c3a23cb574..6e1eda21b9 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -821,17 +821,6 @@ global:
nm_vpn_plugin_error_get_type;
nm_vpn_plugin_error_quark;
nm_vpn_plugin_failure_get_type;
- nm_vpn_plugin_old_disconnect;
- nm_vpn_plugin_old_failure;
- nm_vpn_plugin_old_get_connection;
- nm_vpn_plugin_old_get_secret_flags;
- nm_vpn_plugin_old_get_state;
- nm_vpn_plugin_old_get_type;
- nm_vpn_plugin_old_read_vpn_details;
- nm_vpn_plugin_old_secrets_required;
- nm_vpn_plugin_old_set_ip4_config;
- nm_vpn_plugin_old_set_login_banner;
- nm_vpn_plugin_old_set_state;
nm_vpn_service_state_get_type;
nm_wep_key_type_get_type;
nm_wimax_nsp_connection_valid;
@@ -882,4 +871,35 @@ global:
nm_utils_bond_mode_string_to_int;
nm_utils_enum_from_str;
nm_utils_enum_to_str;
+ nm_vpn_editor_plugin_load_from_file;
+ nm_vpn_plugin_info_get_filename;
+ nm_vpn_plugin_info_get_editor_plugin;
+ nm_vpn_plugin_info_get_name;
+ nm_vpn_plugin_info_get_plugin;
+ nm_vpn_plugin_info_get_program;
+ nm_vpn_plugin_info_get_service;
+ nm_vpn_plugin_info_get_type;
+ nm_vpn_plugin_info_load_editor_plugin;
+ nm_vpn_plugin_info_lookup_property;
+ nm_vpn_plugin_info_new_from_file;
+ nm_vpn_plugin_info_new_with_data;
+ nm_vpn_plugin_info_set_editor_plugin;
+ nm_vpn_plugin_info_validate_filename;
+ nm_vpn_plugin_info_list_add;
+ nm_vpn_plugin_info_list_find_by_filename;
+ nm_vpn_plugin_info_list_find_by_name;
+ nm_vpn_plugin_info_list_find_by_service;
+ nm_vpn_plugin_info_list_load;
+ nm_vpn_plugin_info_list_remove;
+ nm_vpn_service_plugin_disconnect;
+ nm_vpn_service_plugin_failure;
+ nm_vpn_service_plugin_get_connection;
+ nm_vpn_service_plugin_get_secret_flags;
+ nm_vpn_service_plugin_get_state;
+ nm_vpn_service_plugin_get_type;
+ nm_vpn_service_plugin_read_vpn_details;
+ nm_vpn_service_plugin_secrets_required;
+ nm_vpn_service_plugin_set_ip4_config;
+ nm_vpn_service_plugin_set_login_banner;
+ nm_vpn_service_plugin_set_state;
} libnm_1_0_0;
diff --git a/libnm/nm-vpn-editor-plugin.c b/libnm/nm-vpn-editor-plugin.c
deleted file mode 100644
index 904a6be483..0000000000
--- a/libnm/nm-vpn-editor-plugin.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2008 - 2010 Red Hat, Inc.
- * Copyright 2008 Novell, Inc.
- */
-
-#include "config.h"
-
-#include "nm-vpn-editor-plugin.h"
-
-static void nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface);
-
-G_DEFINE_INTERFACE (NMVpnEditorPlugin, nm_vpn_editor_plugin, G_TYPE_OBJECT)
-
-static void
-nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface)
-{
- /* Properties */
-
- /**
- * NMVpnEditorPlugin:name:
- *
- * Short display name of the VPN plugin.
- */
- g_object_interface_install_property (iface,
- g_param_spec_string (NM_VPN_EDITOR_PLUGIN_NAME, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * NMVpnEditorPlugin:description:
- *
- * Longer description of the VPN plugin.
- */
- g_object_interface_install_property (iface,
- g_param_spec_string (NM_VPN_EDITOR_PLUGIN_DESCRIPTION, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * NMVpnEditorPlugin:service:
- *
- * D-Bus service name of the plugin's VPN service.
- */
- g_object_interface_install_property (iface,
- g_param_spec_string (NM_VPN_EDITOR_PLUGIN_SERVICE, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS));
-}
-
-/**
- * nm_vpn_editor_plugin_get_editor:
- *
- * Returns: (transfer full):
- */
-NMVpnEditor *
-nm_vpn_editor_plugin_get_editor (NMVpnEditorPlugin *plugin,
- NMConnection *connection,
- GError **error)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
-
- return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_editor (plugin, connection, error);
-}
-
-NMVpnEditorPluginCapability
-nm_vpn_editor_plugin_get_capabilities (NMVpnEditorPlugin *plugin)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), 0);
-
- return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_capabilities (plugin);
-}
-
-/**
- * nm_vpn_editor_plugin_import:
- *
- * Returns: (transfer full):
- */
-NMConnection *
-nm_vpn_editor_plugin_import (NMVpnEditorPlugin *plugin,
- const char *path,
- GError **error)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
-
- if (nm_vpn_editor_plugin_get_capabilities (plugin) & NM_VPN_EDITOR_PLUGIN_CAPABILITY_IMPORT) {
- g_return_val_if_fail (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->import_from_file != NULL, NULL);
- return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->import_from_file (plugin, path, error);
- }
- return NULL;
-}
-
-gboolean
-nm_vpn_editor_plugin_export (NMVpnEditorPlugin *plugin,
- const char *path,
- NMConnection *connection,
- GError **error)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), FALSE);
-
- if (nm_vpn_editor_plugin_get_capabilities (plugin) & NM_VPN_EDITOR_PLUGIN_CAPABILITY_EXPORT) {
- g_return_val_if_fail (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->export_to_file != NULL, FALSE);
- return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->export_to_file (plugin, path, connection, error);
- }
- return FALSE;
-}
-
-char *
-nm_vpn_editor_plugin_get_suggested_filename (NMVpnEditorPlugin *plugin,
- NMConnection *connection)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL);
-
- if (NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_suggested_filename)
- return NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin)->get_suggested_filename (plugin, connection);
- return NULL;
-}
-
-
-static void nm_vpn_editor_default_init (NMVpnEditorInterface *iface);
-
-G_DEFINE_INTERFACE (NMVpnEditor, nm_vpn_editor, G_TYPE_OBJECT)
-
-static void
-nm_vpn_editor_default_init (NMVpnEditorInterface *iface)
-{
- GType iface_type = G_TYPE_FROM_INTERFACE (iface);
-
- /* Signals */
- g_signal_new ("changed",
- iface_type,
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMVpnEditorInterface, changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-}
-
-/**
- * nm_vpn_editor_get_widget:
- *
- * Returns: (transfer none):
- */
-GObject *
-nm_vpn_editor_get_widget (NMVpnEditor *editor)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR (editor), NULL);
-
- return NM_VPN_EDITOR_GET_INTERFACE (editor)->get_widget (editor);
-}
-
-gboolean
-nm_vpn_editor_update_connection (NMVpnEditor *editor,
- NMConnection *connection,
- GError **error)
-{
- g_return_val_if_fail (NM_IS_VPN_EDITOR (editor), FALSE);
-
- if (error)
- g_return_val_if_fail (*error == NULL, FALSE);
-
- return NM_VPN_EDITOR_GET_INTERFACE (editor)->update_connection (editor, connection, error);
-}
diff --git a/libnm/nm-vpn-editor.c b/libnm/nm-vpn-editor.c
new file mode 100644
index 0000000000..ba6bd5e03c
--- /dev/null
+++ b/libnm/nm-vpn-editor.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2010 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include "config.h"
+
+#include "nm-vpn-editor.h"
+
+static void nm_vpn_editor_default_init (NMVpnEditorInterface *iface);
+
+G_DEFINE_INTERFACE (NMVpnEditor, nm_vpn_editor, G_TYPE_OBJECT)
+
+static void
+nm_vpn_editor_default_init (NMVpnEditorInterface *iface)
+{
+ GType iface_type = G_TYPE_FROM_INTERFACE (iface);
+
+ /* Signals */
+ g_signal_new ("changed",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnEditorInterface, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/**
+ * nm_vpn_editor_get_widget:
+ *
+ * Returns: (transfer none):
+ */
+GObject *
+nm_vpn_editor_get_widget (NMVpnEditor *editor)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR (editor), NULL);
+
+ return NM_VPN_EDITOR_GET_INTERFACE (editor)->get_widget (editor);
+}
+
+gboolean
+nm_vpn_editor_update_connection (NMVpnEditor *editor,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_EDITOR (editor), FALSE);
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ return NM_VPN_EDITOR_GET_INTERFACE (editor)->update_connection (editor, connection, error);
+}
diff --git a/libnm/nm-vpn-editor.h b/libnm/nm-vpn-editor.h
new file mode 100644
index 0000000000..a11e9fd16d
--- /dev/null
+++ b/libnm/nm-vpn-editor.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2008 - 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_VPN_EDITOR_H__
+#define __NM_VPN_EDITOR_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <nm-types.h>
+
+#include "nm-vpn-editor-plugin.h"
+
+G_BEGIN_DECLS
+
+/**************************************************/
+/* Editor interface */
+/**************************************************/
+
+#define NM_TYPE_VPN_EDITOR (nm_vpn_editor_get_type ())
+#define NM_VPN_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_EDITOR, NMVpnEditor))
+#define NM_IS_VPN_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_EDITOR))
+#define NM_VPN_EDITOR_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_EDITOR, NMVpnEditorInterface))
+
+/**
+ * NMVpnEditorInterface:
+ * @g_iface: the parent interface
+ * @get_widget: return the #GtkWidget for the VPN editor's UI
+ * @placeholder: not currently used
+ * @update_connection: called to save the user-entered options to the connection
+ * object. Should return %FALSE and set @error if the current options are
+ * invalid. @error should contain enough information for the plugin to
+ * determine which UI widget is invalid at a later point in time. For
+ * example, creating unique error codes for what error occurred and populating
+ * the message field of @error with the name of the invalid property.
+ * @changed: emitted when the value of a UI widget changes. May trigger a
+ * validity check via @update_connection to write values to the connection.
+ *
+ * Interface for editing a specific #NMConnection
+ */
+typedef struct {
+ GTypeInterface g_iface;
+
+ GObject * (*get_widget) (NMVpnEditor *editor);
+
+ void (*placeholder) (void);
+
+ gboolean (*update_connection) (NMVpnEditor *editor,
+ NMConnection *connection,
+ GError **error);
+
+ void (*changed) (NMVpnEditor *editor);
+} NMVpnEditorInterface;
+
+GType nm_vpn_editor_get_type (void);
+
+GObject * nm_vpn_editor_get_widget (NMVpnEditor *editor);
+
+gboolean nm_vpn_editor_update_connection (NMVpnEditor *editor,
+ NMConnection *connection,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __NM_VPN_EDITOR_H__ */
diff --git a/libnm/nm-vpn-plugin-old.c b/libnm/nm-vpn-plugin-old.c
index 5748060ff0..ff7ce834a8 100644
--- a/libnm/nm-vpn-plugin-old.c
+++ b/libnm/nm-vpn-plugin-old.c
@@ -19,10 +19,6 @@
* Copyright 2007 - 2008 Red Hat, Inc.
*/
-/* This interface is expected to be deprecated in NM 1.2, at which point there
- * will be a new "NMVpnPlugin" class to replace it.
- */
-
#include "config.h"
#include <errno.h>
@@ -70,7 +66,7 @@ typedef struct {
gboolean has_ip6, got_ip6;
/* Config stuff copied from config to ip4config */
- char *banner, *tundev, *gateway, *mtu;
+ GVariant *banner, *tundev, *gateway, *mtu;
} NMVpnPluginOldPrivate;
#define NM_VPN_PLUGIN_OLD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN_OLD, NMVpnPluginOldPrivate))
@@ -117,6 +113,8 @@ nm_vpn_plugin_old_set_connection (NMVpnPluginOld *plugin,
* nm_vpn_plugin_old_get_connection:
*
* Returns: (transfer full):
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
*/
GDBusConnection *
nm_vpn_plugin_old_get_connection (NMVpnPluginOld *plugin)
@@ -300,14 +298,22 @@ nm_vpn_plugin_old_set_config (NMVpnPluginOld *plugin,
/* Record the items that need to also be inserted into the
* ip4config, for compatibility with older daemons.
*/
- g_clear_pointer (&priv->banner, g_free);
- (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER, "&s", &priv->banner);
- g_clear_pointer (&priv->tundev, g_free);
- (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV, "&s", &priv->tundev);
- g_clear_pointer (&priv->gateway, g_free);
- (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "&s", &priv->gateway);
- g_clear_pointer (&priv->mtu, g_free);
- (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU, "&s", &priv->mtu);
+ if (priv->banner)
+ g_variant_unref (priv->banner);
+ priv->banner = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_BANNER,
+ G_VARIANT_TYPE ("s"));
+ if (priv->tundev)
+ g_variant_unref (priv->tundev);
+ priv->tundev = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_TUNDEV,
+ G_VARIANT_TYPE ("s"));
+ if (priv->gateway)
+ g_variant_unref (priv->gateway);
+ priv->gateway = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
+ G_VARIANT_TYPE ("u"));
+ if (priv->mtu)
+ g_variant_unref (priv->mtu);
+ priv->mtu = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_MTU,
+ G_VARIANT_TYPE ("u"));
g_signal_emit (plugin, signals[CONFIG], 0, config);
}
@@ -320,7 +326,8 @@ nm_vpn_plugin_old_set_ip4_config (NMVpnPluginOld *plugin,
GVariant *combined_config;
GVariantBuilder builder;
GVariantIter iter;
- const char *key, *value;
+ const char *key;
+ GVariant *value;
g_return_if_fail (NM_IS_VPN_PLUGIN_OLD (plugin));
g_return_if_fail (ip4_config != NULL);
@@ -340,19 +347,21 @@ nm_vpn_plugin_old_set_ip4_config (NMVpnPluginOld *plugin,
* being emitted. So just copy all of that data into the ip4
* config too.
*/
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_iter_init (&iter, ip4_config);
- while (g_variant_iter_next (&iter, "{&s&s}", &key, &value))
- g_variant_builder_add (&builder, "{ss}", key, value);
+ while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) {
+ g_variant_builder_add (&builder, "{sv}", key, value);
+ g_variant_unref (value);
+ }
if (priv->banner)
- g_variant_builder_add (&builder, "{ss}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
if (priv->tundev)
- g_variant_builder_add (&builder, "{ss}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
if (priv->gateway)
- g_variant_builder_add (&builder, "{ss}", NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
if (priv->mtu)
- g_variant_builder_add (&builder, "{ss}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
combined_config = g_variant_builder_end (&builder);
g_variant_ref_sink (combined_config);
@@ -595,6 +604,8 @@ impl_vpn_plugin_old_new_secrets (NMVpnPluginOld *plugin,
* request new secrets when the secrets originally provided by NetworkManager
* are insufficient, or the VPN process indicates that it needs additional
* information to complete the request.
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
*/
void
nm_vpn_plugin_old_secrets_required (NMVpnPluginOld *plugin,
@@ -647,6 +658,8 @@ free_secret (gpointer data)
* an applet when the applet calls the authentication dialog of the VPN plugin.
*
* Returns: %TRUE if reading values was successful, %FALSE if not
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
**/
gboolean
nm_vpn_plugin_old_read_vpn_details (int fd,
@@ -748,6 +761,8 @@ nm_vpn_plugin_old_read_vpn_details (int fd,
*
* Returns: %TRUE if the flag data item was found and successfully converted
* to flags, %FALSE if not
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
**/
gboolean
nm_vpn_plugin_old_get_secret_flags (GHashTable *data,
@@ -1026,10 +1041,10 @@ finalize (GObject *object)
nm_vpn_plugin_old_set_connection (plugin, NULL);
g_free (priv->dbus_service_name);
- g_clear_pointer (&priv->banner, g_free);
- g_clear_pointer (&priv->tundev, g_free);
- g_clear_pointer (&priv->gateway, g_free);
- g_clear_pointer (&priv->mtu, g_free);
+ g_clear_pointer (&priv->banner, g_variant_unref);
+ g_clear_pointer (&priv->tundev, g_variant_unref);
+ g_clear_pointer (&priv->gateway, g_variant_unref);
+ g_clear_pointer (&priv->mtu, g_variant_unref);
G_OBJECT_CLASS (nm_vpn_plugin_old_parent_class)->finalize (object);
}
@@ -1077,6 +1092,8 @@ nm_vpn_plugin_old_class_init (NMVpnPluginOldClass *plugin_class)
* NMVpnPluginOld:service-name:
*
* The D-Bus service name of this plugin.
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
*/
g_object_class_install_property
(object_class, PROP_DBUS_SERVICE_NAME,
@@ -1090,6 +1107,8 @@ nm_vpn_plugin_old_class_init (NMVpnPluginOldClass *plugin_class)
* NMVpnPluginOld:state:
*
* The state of the plugin.
+ *
+ * Deprecated: 1.2: replaced by NMVpnServicePlugin
*/
g_object_class_install_property
(object_class, PROP_STATE,
diff --git a/libnm/nm-vpn-plugin-old.h b/libnm/nm-vpn-plugin-old.h
index af8f4ff61b..59cf4600c5 100644
--- a/libnm/nm-vpn-plugin-old.h
+++ b/libnm/nm-vpn-plugin-old.h
@@ -16,7 +16,7 @@
* Boston, MA 02110-1301 USA.
*
* Copyright 2007 - 2008 Novell, Inc.
- * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2015 Red Hat, Inc.
*/
#ifndef __NM_VPN_PLUGIN_OLD_H__
@@ -39,94 +39,122 @@ G_BEGIN_DECLS
#define NM_VPN_PLUGIN_OLD_STATE "state"
typedef struct {
+ NM_DEPRECATED_IN_1_2
GObject parent;
-} NMVpnPluginOld;
+} NMVpnPluginOld NM_DEPRECATED_IN_1_2;
typedef struct {
+ NM_DEPRECATED_IN_1_2
GObjectClass parent;
/* Signals */
+ NM_DEPRECATED_IN_1_2
void (*state_changed) (NMVpnPluginOld *plugin,
NMVpnServiceState state);
+ NM_DEPRECATED_IN_1_2
void (*ip4_config) (NMVpnPluginOld *plugin,
GVariant *ip4_config);
+ NM_DEPRECATED_IN_1_2
void (*login_banner) (NMVpnPluginOld *plugin,
const char *banner);
+ NM_DEPRECATED_IN_1_2
void (*failure) (NMVpnPluginOld *plugin,
NMVpnPluginFailure reason);
+ NM_DEPRECATED_IN_1_2
void (*quit) (NMVpnPluginOld *plugin);
+ NM_DEPRECATED_IN_1_2
void (*config) (NMVpnPluginOld *plugin,
GVariant *config);
+ NM_DEPRECATED_IN_1_2
void (*ip6_config) (NMVpnPluginOld *plugin,
GVariant *config);
/* virtual methods */
+ NM_DEPRECATED_IN_1_2
gboolean (*connect) (NMVpnPluginOld *plugin,
NMConnection *connection,
GError **err);
+ NM_DEPRECATED_IN_1_2
gboolean (*need_secrets) (NMVpnPluginOld *plugin,
NMConnection *connection,
const char **setting_name,
GError **error);
+ NM_DEPRECATED_IN_1_2
gboolean (*disconnect) (NMVpnPluginOld *plugin,
GError **err);
+ NM_DEPRECATED_IN_1_2
gboolean (*new_secrets) (NMVpnPluginOld *plugin,
NMConnection *connection,
GError **error);
+ NM_DEPRECATED_IN_1_2
gboolean (*connect_interactive) (NMVpnPluginOld *plugin,
NMConnection *connection,
GVariant *details,
GError **error);
/*< private >*/
+ NM_DEPRECATED_IN_1_2
gpointer padding[8];
-} NMVpnPluginOldClass;
+} NMVpnPluginOldClass NM_DEPRECATED_IN_1_2;
+NM_DEPRECATED_IN_1_2
GType nm_vpn_plugin_old_get_type (void);
+NM_DEPRECATED_IN_1_2
GDBusConnection *nm_vpn_plugin_old_get_connection (NMVpnPluginOld *plugin);
+NM_DEPRECATED_IN_1_2
NMVpnServiceState nm_vpn_plugin_old_get_state (NMVpnPluginOld *plugin);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_set_state (NMVpnPluginOld *plugin,
NMVpnServiceState state);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_secrets_required (NMVpnPluginOld *plugin,
const char *message,
const char **hints);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_set_login_banner (NMVpnPluginOld *plugin,
const char *banner);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_failure (NMVpnPluginOld *plugin,
NMVpnPluginFailure reason);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_set_config (NMVpnPluginOld *plugin,
GVariant *config);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_set_ip4_config (NMVpnPluginOld *plugin,
GVariant *ip4_config);
+NM_DEPRECATED_IN_1_2
void nm_vpn_plugin_old_set_ip6_config (NMVpnPluginOld *plugin,
GVariant *ip6_config);
+NM_DEPRECATED_IN_1_2
gboolean nm_vpn_plugin_old_disconnect (NMVpnPluginOld *plugin,
GError **err);
/* Utility functions */
+NM_DEPRECATED_IN_1_2
gboolean nm_vpn_plugin_old_read_vpn_details (int fd,
GHashTable **out_data,
GHashTable **out_secrets);
+NM_DEPRECATED_IN_1_2
gboolean nm_vpn_plugin_old_get_secret_flags (GHashTable *data,
const char *secret_name,
NMSettingSecretFlags *out_flags);
diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c
new file mode 100644
index 0000000000..1f25be6e82
--- /dev/null
+++ b/libnm/nm-vpn-service-plugin.c
@@ -0,0 +1,1208 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "nm-vpn-service-plugin.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nm-glib.h"
+#include "nm-enum-types.h"
+#include "nm-utils.h"
+#include "nm-connection.h"
+#include "nm-dbus-helpers.h"
+#include "nm-core-internal.h"
+#include "nm-simple-connection.h"
+#include "nm-macros-internal.h"
+
+#include "nmdbus-vpn-plugin.h"
+
+#define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER 20
+
+static void nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMVpnServicePlugin, nm_vpn_service_plugin, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_service_plugin_initable_iface_init);
+ )
+
+typedef struct {
+ NMVpnServiceState state;
+
+ /* DBUS-y stuff */
+ GDBusConnection *connection;
+ NMDBusVpnPlugin *dbus_vpn_service_plugin;
+ char *dbus_service_name;
+
+ /* Temporary stuff */
+ guint connect_timer;
+ guint quit_timer;
+ guint fail_stop_id;
+ gboolean interactive;
+
+ gboolean got_config;
+ gboolean has_ip4, got_ip4;
+ gboolean has_ip6, got_ip6;
+
+ /* Config stuff copied from config to ip4config */
+ GVariant *banner, *tundev, *gateway, *mtu;
+} NMVpnServicePluginPrivate;
+
+#define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate))
+
+enum {
+ STATE_CHANGED,
+ CONFIG,
+ IP4_CONFIG,
+ IP6_CONFIG,
+ LOGIN_BANNER,
+ FAILURE,
+ QUIT,
+ SECRETS_REQUIRED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+ PROP_DBUS_SERVICE_NAME,
+ PROP_STATE,
+
+ LAST_PROP
+};
+
+static GSList *active_plugins = NULL;
+
+
+static void
+nm_vpn_service_plugin_set_connection (NMVpnServicePlugin *plugin,
+ GDBusConnection *connection)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ g_clear_object (&priv->connection);
+
+ if (connection)
+ priv->connection = g_object_ref (connection);
+}
+
+/**
+ * nm_vpn_service_plugin_get_connection:
+ *
+ * Returns: (transfer full):
+ *
+ * Since: 1.2
+ */
+GDBusConnection *
+nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin)
+{
+ GDBusConnection *connection;
+
+ g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NULL);
+
+ connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connection;
+
+ if (connection)
+ g_object_ref (connection);
+
+ return connection;
+}
+
+NMVpnServiceState
+nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin)
+{
+ g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN);
+
+ return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->state;
+}
+
+void
+nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin,
+ NMVpnServiceState state)
+{
+ NMVpnServicePluginPrivate *priv;
+
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+
+ priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ if (priv->state != state) {
+ priv->state = state;
+ g_signal_emit (plugin, signals[STATE_CHANGED], 0, state);
+ }
+}
+
+void
+nm_vpn_service_plugin_set_login_banner (NMVpnServicePlugin *plugin,
+ const char *banner)
+{
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+ g_return_if_fail (banner != NULL);
+
+ g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner);
+}
+
+void
+nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin,
+ NMVpnPluginFailure reason)
+{
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+
+ g_signal_emit (plugin, signals[FAILURE], 0, reason);
+}
+
+gboolean
+nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err)
+{
+ gboolean ret = FALSE;
+ NMVpnServiceState state;
+
+ g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), FALSE);
+
+ state = nm_vpn_service_plugin_get_state (plugin);
+ switch (state) {
+ case NM_VPN_SERVICE_STATE_STOPPING:
+ g_set_error (err,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
+ "%s",
+ "Could not process the request because the VPN connection is already being stopped.");
+ break;
+ case NM_VPN_SERVICE_STATE_STOPPED:
+ g_set_error (err,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED,
+ "%s",
+ "Could not process the request because no VPN connection was active.");
+ break;
+ case NM_VPN_SERVICE_STATE_STARTING:
+ case NM_VPN_SERVICE_STATE_STARTED:
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING);
+ ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err);
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
+ break;
+ case NM_VPN_SERVICE_STATE_INIT:
+ ret = TRUE;
+ break;
+
+ default:
+ g_warning ("Unhandled VPN service state %d", state);
+ g_assert_not_reached ();
+ break;
+ }
+
+ return ret;
+}
+
+static void
+nm_vpn_service_plugin_emit_quit (NMVpnServicePlugin *plugin)
+{
+ g_signal_emit (plugin, signals[QUIT], 0);
+}
+
+static gboolean
+connect_timer_expired (gpointer data)
+{
+ NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (data);
+ GError *err = NULL;
+
+ NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connect_timer = 0;
+ g_message ("Connect timer expired, disconnecting.");
+ nm_vpn_service_plugin_disconnect (plugin, &err);
+ if (err) {
+ g_warning ("Disconnect failed: %s", err->message);
+ g_error_free (err);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+quit_timer_expired (gpointer data)
+{
+ NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
+
+ NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->quit_timer = 0;
+ nm_vpn_service_plugin_emit_quit (self);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+schedule_quit_timer (NMVpnServicePlugin *self)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self);
+
+ nm_clear_g_source (&priv->quit_timer);
+ priv->quit_timer = g_timeout_add_seconds (NM_VPN_SERVICE_PLUGIN_QUIT_TIMER,
+ quit_timer_expired,
+ self);
+}
+
+static gboolean
+fail_stop (gpointer data)
+{
+ NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
+
+ NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->fail_stop_id = 0;
+ nm_vpn_service_plugin_set_state (self, NM_VPN_SERVICE_STATE_STOPPED);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+schedule_fail_stop (NMVpnServicePlugin *plugin)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ nm_clear_g_source (&priv->fail_stop_id);
+ priv->fail_stop_id = g_idle_add (fail_stop, plugin);
+}
+
+void
+nm_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
+ GVariant *config)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+ g_return_if_fail (config != NULL);
+
+ priv->got_config = TRUE;
+
+ (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4);
+ (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6);
+
+ g_warn_if_fail (priv->has_ip4 || priv->has_ip6);
+
+ /* Record the items that need to also be inserted into the
+ * ip4config, for compatibility with older daemons.
+ */
+ if (priv->banner)
+ g_variant_unref (priv->banner);
+ priv->banner = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_BANNER,
+ G_VARIANT_TYPE ("s"));
+ if (priv->tundev)
+ g_variant_unref (priv->tundev);
+ priv->tundev = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_TUNDEV,
+ G_VARIANT_TYPE ("s"));
+ if (priv->gateway)
+ g_variant_unref (priv->gateway);
+ priv->gateway = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
+ G_VARIANT_TYPE ("u"));
+ if (priv->mtu)
+ g_variant_unref (priv->mtu);
+ priv->mtu = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_MTU,
+ G_VARIANT_TYPE ("u"));
+
+ g_signal_emit (plugin, signals[CONFIG], 0, config);
+}
+
+void
+nm_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
+ GVariant *ip4_config)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ GVariant *combined_config;
+ GVariantBuilder builder;
+ GVariantIter iter;
+ const char *key;
+ GVariant *value;
+
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+ g_return_if_fail (ip4_config != NULL);
+
+ priv->got_ip4 = TRUE;
+
+ /* Old plugins won't send the "config" signal and thus can't send
+ * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6,
+ * we can safely assume that, if we don't receive a "config" signal but do
+ * receive an "ip4-config" signal, the old plugin supports IPv4.
+ */
+ if (!priv->got_config)
+ priv->has_ip4 = TRUE;
+
+ /* Older NetworkManager daemons expect all config info to be in
+ * the ip4 config, so they won't even notice the "config" signal
+ * being emitted. So just copy all of that data into the ip4
+ * config too.
+ */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_iter_init (&iter, ip4_config);
+ while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) {
+ g_variant_builder_add (&builder, "{sv}", key, value);
+ g_variant_unref (value);
+ }
+
+ if (priv->banner)
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
+ if (priv->tundev)
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
+ if (priv->gateway)
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
+ if (priv->mtu)
+ g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
+
+ combined_config = g_variant_builder_end (&builder);
+ g_variant_ref_sink (combined_config);
+ g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
+ g_variant_unref (combined_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+}
+
+void
+nm_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
+ GVariant *ip6_config)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
+ g_return_if_fail (ip6_config != NULL);
+
+ priv->got_ip6 = TRUE;
+ g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+}
+
+static void
+connect_timer_start (NMVpnServicePlugin *plugin)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ priv->connect_timer = g_timeout_add_seconds (60, connect_timer_expired, plugin);
+}
+
+static void
+_connect_generic (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *properties,
+ GVariant *details)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ NMVpnServicePluginClass *vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin);
+ NMConnection *connection;
+ gboolean success = FALSE;
+ GError *error = NULL;
+
+ if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
+ priv->state != NM_VPN_SERVICE_STATE_INIT) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_WRONG_STATE,
+ "Could not start connection: wrong plugin state %d",
+ priv->state);
+ return;
+ }
+
+ connection = nm_simple_connection_new_from_dbus (properties, &error);
+ if (!connection) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "Invalid connection: (%d) %s",
+ error->code, error->message);
+ g_clear_error (&error);
+ }
+
+ priv->interactive = FALSE;
+ if (details && !vpn_class->connect_interactive) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
+ "Plugin does not implement ConnectInteractive()");
+ return;
+ }
+
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+
+ if (details) {
+ priv->interactive = TRUE;
+ success = vpn_class->connect_interactive (plugin, connection, details, &error);
+ } else
+ success = vpn_class->connect (plugin, connection, &error);
+
+ if (success) {
+ g_dbus_method_invocation_return_value (context, NULL);
+
+ /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
+ connect_timer_start (plugin);
+ } else {
+ g_dbus_method_invocation_take_error (context, error);
+
+ /* Stop the plugin from an idle handler so that the Connect
+ * method return gets sent before the STOP StateChanged signal.
+ */
+ schedule_fail_stop (plugin);
+ }
+
+ g_object_unref (connection);
+}
+
+static void
+impl_vpn_service_plugin_connect (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *connection,
+ gpointer user_data)
+{
+ _connect_generic (plugin, context, connection, NULL);
+}
+
+static void
+impl_vpn_service_plugin_connect_interactive (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *connection,
+ GVariant *details,
+ gpointer user_data)
+{
+ _connect_generic (plugin, context, connection, details);
+}
+
+/***************************************************************/
+
+static void
+impl_vpn_service_plugin_need_secrets (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *properties,
+ gpointer user_data)
+{
+ NMConnection *connection;
+ const char *setting_name;
+ gboolean needed;
+ GError *error = NULL;
+
+ connection = nm_simple_connection_new_from_dbus (properties, &error);
+ if (!connection) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION,
+ "The connection was invalid: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets) {
+ g_dbus_method_invocation_return_value (context,
+ g_variant_new ("(s)", ""));
+ return;
+ }
+
+ needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &setting_name, &error);
+ if (error) {
+ g_dbus_method_invocation_take_error (context, error);
+ return;
+ }
+
+ if (needed) {
+ /* Push back the quit timer so the VPN plugin doesn't quit in the
+ * middle of asking the user for secrets.
+ */
+ schedule_quit_timer (plugin);
+
+ g_assert (setting_name);
+ g_dbus_method_invocation_return_value (context,
+ g_variant_new ("(s)", setting_name));
+ } else {
+ /* No secrets required */
+ g_dbus_method_invocation_return_value (context,
+ g_variant_new ("(s)", ""));
+ }
+}
+
+static void
+impl_vpn_service_plugin_new_secrets (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *properties,
+ gpointer user_data)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ NMConnection *connection;
+ GError *error = NULL;
+ gboolean success;
+
+ if (priv->state != NM_VPN_SERVICE_STATE_STARTING) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_WRONG_STATE,
+ "Could not accept new secrets: wrong plugin state %d",
+ priv->state);
+ return;
+ }
+
+ connection = nm_simple_connection_new_from_dbus (properties, &error);
+ if (!connection) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "Invalid connection: (%d) %s",
+ error->code, error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets) {
+ g_dbus_method_invocation_return_error (context,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
+ "Could not accept new secrets: plugin cannot process interactive secrets");
+ g_object_unref (connection);
+ return;
+ }
+
+ success = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, &error);
+ if (success) {
+ g_dbus_method_invocation_return_value (context, NULL);
+
+ /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
+ connect_timer_start (plugin);
+ } else {
+ g_dbus_method_invocation_take_error (context, error);
+
+ /* Stop the plugin from and idle handler so that the NewSecrets
+ * method return gets sent before the STOP StateChanged signal.
+ */
+ schedule_fail_stop (plugin);
+ }
+
+ g_object_unref (connection);
+}
+
+/**
+ * nm_vpn_service_plugin_secrets_required:
+ * @plugin: the #NMVpnServicePlugin
+ * @message: an information message about why secrets are required, if any
+ * @hints: VPN specific secret names for required new secrets
+ *
+ * Called by VPN plugin implementations to signal to NetworkManager that secrets
+ * are required during the connection process. This signal may be used to
+ * request new secrets when the secrets originally provided by NetworkManager
+ * are insufficient, or the VPN process indicates that it needs additional
+ * information to complete the request.
+ *
+ * Since: 1.2
+ */
+void
+nm_vpn_service_plugin_secrets_required (NMVpnServicePlugin *plugin,
+ const char *message,
+ const char **hints)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ /* Plugin must be able to accept the new secrets if it calls this method */
+ g_return_if_fail (NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets);
+
+ /* Plugin cannot call this method if NetworkManager didn't originally call
+ * ConnectInteractive().
+ */
+ g_return_if_fail (priv->interactive == TRUE);
+
+ /* Cancel the connect timer since secrets might take a while. It'll
+ * get restarted when the secrets come back via NewSecrets().
+ */
+ nm_clear_g_source (&priv->connect_timer);
+
+ g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints);
+}
+
+/***************************************************************/
+
+#define DATA_KEY_TAG "DATA_KEY="
+#define DATA_VAL_TAG "DATA_VAL="
+#define SECRET_KEY_TAG "SECRET_KEY="
+#define SECRET_VAL_TAG "SECRET_VAL="
+
+static void
+free_secret (gpointer data)
+{
+ char *secret = data;
+
+ memset (secret, 0, strlen (secret));
+ g_free (secret);
+}
+
+/**
+ * nm_vpn_service_plugin_read_vpn_details:
+ * @fd: file descriptor to read from, usually stdin (0)
+ * @out_data: (out) (transfer full): on successful return, a hash table
+ * (mapping char*:char*) containing the key/value pairs of VPN data items
+ * @out_secrets: (out) (transfer full): on successful return, a hash table
+ * (mapping char*:char*) containing the key/value pairsof VPN secrets
+ *
+ * Parses key/value pairs from a file descriptor (normally stdin) passed by
+ * an applet when the applet calls the authentication dialog of the VPN plugin.
+ *
+ * Returns: %TRUE if reading values was successful, %FALSE if not
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_vpn_service_plugin_read_vpn_details (int fd,
+ GHashTable **out_data,
+ GHashTable **out_secrets)
+{
+ GHashTable *data, *secrets;
+ gboolean success = FALSE;
+ char *key = NULL, *val = NULL;
+ GString *line;
+ gchar c;
+
+ if (out_data)
+ g_return_val_if_fail (*out_data == NULL, FALSE);
+ if (out_secrets)
+ g_return_val_if_fail (*out_secrets == NULL, FALSE);
+
+ data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_secret);
+
+ line = g_string_new (NULL);
+
+ /* Read stdin for data and secret items until we get a DONE */
+ while (1) {
+ ssize_t nr;
+ GHashTable *hash = NULL;
+
+ errno = 0;
+ nr = read (fd, &c, 1);
+ if (nr == -1) {
+ if (errno == EAGAIN) {
+ g_usleep (100);
+ continue;
+ }
+ break;
+ }
+
+ if (c != '\n') {
+ g_string_append_c (line, c);
+ continue;
+ }
+
+ /* Check for the finish marker */
+ if (strcmp (line->str, "DONE") == 0)
+ break;
+
+ /* Otherwise it's a data/secret item */
+ if (strncmp (line->str, DATA_KEY_TAG, strlen (DATA_KEY_TAG)) == 0) {
+ hash = data;
+ key = g_strdup (line->str + strlen (DATA_KEY_TAG));
+ } else if (strncmp (line->str, DATA_VAL_TAG, strlen (DATA_VAL_TAG)) == 0) {
+ hash = data;
+ val = g_strdup (line->str + strlen (DATA_VAL_TAG));
+ } else if (strncmp (line->str, SECRET_KEY_TAG, strlen (SECRET_KEY_TAG)) == 0) {
+ hash = secrets;
+ key = g_strdup (line->str + strlen (SECRET_KEY_TAG));
+ } else if (strncmp (line->str, SECRET_VAL_TAG, strlen (SECRET_VAL_TAG)) == 0) {
+ hash = secrets;
+ val = g_strdup (line->str + strlen (SECRET_VAL_TAG));
+ }
+ g_string_truncate (line, 0);
+
+ if (key && val && hash) {
+ g_hash_table_insert (hash, key, val);
+ key = NULL;
+ val = NULL;
+ success = TRUE; /* Got at least one value */
+ }
+ }
+
+ if (success) {
+ if (out_data)
+ *out_data = data;
+ else
+ g_hash_table_destroy (data);
+
+ if (out_secrets)
+ *out_secrets = secrets;
+ else
+ g_hash_table_destroy (secrets);
+ } else {
+ g_hash_table_destroy (data);
+ g_hash_table_destroy (secrets);
+ }
+
+ g_string_free (line, TRUE);
+ return success;
+}
+
+/**
+ * nm_vpn_service_plugin_get_secret_flags:
+ * @data: hash table containing VPN key/value pair data items
+ * @secret_name: VPN secret key name for which to retrieve flags for
+ * @out_flags: (out): on success, the flags associated with @secret_name
+ *
+ * Given a VPN secret key name, attempts to find the corresponding flags data
+ * item in @data. If found, converts the flags data item to
+ * #NMSettingSecretFlags and returns it.
+ *
+ * Returns: %TRUE if the flag data item was found and successfully converted
+ * to flags, %FALSE if not
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_vpn_service_plugin_get_secret_flags (GHashTable *data,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags)
+{
+ char *flag_name;
+ const char *val;
+ unsigned long tmp;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (secret_name != NULL, FALSE);
+ g_return_val_if_fail (out_flags != NULL, FALSE);
+ g_return_val_if_fail (*out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE);
+
+ flag_name = g_strdup_printf ("%s-flags", secret_name);
+
+ /* Try new flags value first */
+ val = g_hash_table_lookup (data, flag_name);
+ if (val) {
+ errno = 0;
+ tmp = strtoul (val, NULL, 10);
+ if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) {
+ *out_flags = (NMSettingSecretFlags) tmp;
+ success = TRUE;
+ }
+ }
+
+ g_free (flag_name);
+ return success;
+}
+
+/***************************************************************/
+
+static void
+impl_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (nm_vpn_service_plugin_disconnect (plugin, &error))
+ g_dbus_method_invocation_return_value (context, NULL);
+ else
+ g_dbus_method_invocation_take_error (context, error);
+}
+
+static void
+impl_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *config,
+ gpointer user_data)
+{
+ nm_vpn_service_plugin_set_config (plugin, config);
+ g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *config,
+ gpointer user_data)
+{
+ nm_vpn_service_plugin_set_ip4_config (plugin, config);
+ g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ GVariant *config,
+ gpointer user_data)
+{
+ nm_vpn_service_plugin_set_ip6_config (plugin, config);
+ g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_vpn_service_plugin_set_failure (NMVpnServicePlugin *plugin,
+ GDBusMethodInvocation *context,
+ char *reason,
+ gpointer user_data)
+{
+ nm_vpn_service_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG);
+ g_dbus_method_invocation_return_value (context, NULL);
+}
+
+/*********************************************************************/
+
+static void
+sigterm_handler (int signum)
+{
+ g_slist_foreach (active_plugins, (GFunc) nm_vpn_service_plugin_emit_quit, NULL);
+}
+
+static void
+setup_unix_signal_handler (void)
+{
+ struct sigaction action;
+ sigset_t block_mask;
+
+ action.sa_handler = sigterm_handler;
+ sigemptyset (&block_mask);
+ action.sa_mask = block_mask;
+ action.sa_flags = 0;
+ sigaction (SIGINT, &action, NULL);
+ sigaction (SIGTERM, &action, NULL);
+}
+
+/*********************************************************************/
+
+static void
+one_plugin_destroyed (gpointer data,
+ GObject *object)
+{
+ active_plugins = g_slist_remove (active_plugins, object);
+}
+
+static void
+nm_vpn_service_plugin_init (NMVpnServicePlugin *plugin)
+{
+ active_plugins = g_slist_append (active_plugins, plugin);
+ g_object_weak_ref (G_OBJECT (plugin),
+ one_plugin_destroyed,
+ NULL);
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (initable);
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ GDBusConnection *connection = NULL;
+ GDBusProxy *proxy;
+ GVariant *ret;
+ gboolean success = FALSE;
+
+ if (!priv->dbus_service_name) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ _("No service name specified"));
+ return FALSE;
+ }
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (!connection)
+ return FALSE;
+
+ proxy = g_dbus_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ NULL,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ cancellable, error);
+ if (!proxy)
+ goto out;
+
+ ret = g_dbus_proxy_call_sync (proxy,
+ "RequestName",
+ g_variant_new ("(su)", priv->dbus_service_name, 0),
+ G_DBUS_CALL_FLAGS_NONE, -1,
+ cancellable, error);
+ g_object_unref (proxy);
+ if (!ret) {
+ if (error && *error)
+ g_dbus_error_strip_remote_error (*error);
+ goto out;
+ }
+ g_variant_unref (ret);
+
+ priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new ();
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_vpn_service_plugin),
+ connection,
+ NM_VPN_DBUS_PLUGIN_PATH,
+ error))
+ goto out;
+
+ _nm_dbus_bind_properties (plugin, priv->dbus_vpn_service_plugin);
+ _nm_dbus_bind_methods (plugin, priv->dbus_vpn_service_plugin,
+ "Connect", impl_vpn_service_plugin_connect,
+ "ConnectInteractive", impl_vpn_service_plugin_connect_interactive,
+ "NeedSecrets", impl_vpn_service_plugin_need_secrets,
+ "NewSecrets", impl_vpn_service_plugin_new_secrets,
+ "Disconnect", impl_vpn_service_plugin_disconnect,
+ "SetConfig", impl_vpn_service_plugin_set_config,
+ "SetIp4Config", impl_vpn_service_plugin_set_ip4_config,
+ "SetIp6Config", impl_vpn_service_plugin_set_ip6_config,
+ "SetFailure", impl_vpn_service_plugin_set_failure,
+ NULL);
+
+ nm_vpn_service_plugin_set_connection (plugin, connection);
+ nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT);
+
+ success = TRUE;
+
+ out:
+ g_clear_object (&connection);
+
+ return success;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_SERVICE_NAME:
+ /* Construct-only */
+ priv->dbus_service_name = g_value_dup_string (value);
+ break;
+ case PROP_STATE:
+ nm_vpn_service_plugin_set_state (NM_VPN_SERVICE_PLUGIN (object),
+ (NMVpnServiceState) g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_SERVICE_NAME:
+ g_value_set_string (value, priv->dbus_service_name);
+ break;
+ case PROP_STATE:
+ g_value_set_enum (value, nm_vpn_service_plugin_get_state (NM_VPN_SERVICE_PLUGIN (object)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+ NMVpnServiceState state;
+ GError *err = NULL;
+
+ nm_clear_g_source (&priv->fail_stop_id);
+ nm_clear_g_source (&priv->quit_timer);
+ nm_clear_g_source (&priv->connect_timer);
+
+ state = nm_vpn_service_plugin_get_state (plugin);
+
+ if (state == NM_VPN_SERVICE_STATE_STARTED ||
+ state == NM_VPN_SERVICE_STATE_STARTING)
+ nm_vpn_service_plugin_disconnect (plugin, &err);
+
+ if (err) {
+ g_warning ("Error disconnecting VPN connection: %s", err->message);
+ g_error_free (err);
+ }
+
+ G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ nm_vpn_service_plugin_set_connection (plugin, NULL);
+ g_free (priv->dbus_service_name);
+
+ g_clear_pointer (&priv->banner, g_variant_unref);
+ g_clear_pointer (&priv->tundev, g_variant_unref);
+ g_clear_pointer (&priv->gateway, g_variant_unref);
+ g_clear_pointer (&priv->mtu, g_variant_unref);
+
+ G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->finalize (object);
+}
+
+static void
+state_changed (NMVpnServicePlugin *plugin, NMVpnServiceState state)
+{
+ NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
+
+ switch (state) {
+ case NM_VPN_SERVICE_STATE_STARTING:
+ nm_clear_g_source (&priv->quit_timer);
+ nm_clear_g_source (&priv->fail_stop_id);
+ break;
+ case NM_VPN_SERVICE_STATE_STOPPED:
+ schedule_quit_timer (plugin);
+ break;
+ default:
+ /* Clean up all timers we might have set up. */
+ nm_clear_g_source (&priv->connect_timer);
+ nm_clear_g_source (&priv->quit_timer);
+ nm_clear_g_source (&priv->fail_stop_id);
+ break;
+ }
+}
+
+static void
+nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
+
+ g_type_class_add_private (object_class, sizeof (NMVpnServicePluginPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ plugin_class->state_changed = state_changed;
+
+ /* properties */
+
+ /**
+ * NMVpnServicePlugin:service-name:
+ *
+ * The D-Bus service name of this plugin.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property
+ (object_class, PROP_DBUS_SERVICE_NAME,
+ g_param_spec_string (NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVpnServicePlugin:state:
+ *
+ * The state of the plugin.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_enum (NM_VPN_SERVICE_PLUGIN_STATE, "", "",
+ NM_TYPE_VPN_SERVICE_STATE,
+ NM_VPN_SERVICE_STATE_INIT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+ signals[STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, state_changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[SECRETS_REQUIRED] =
+ g_signal_new ("secrets-required",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV);
+
+ signals[CONFIG] =
+ g_signal_new ("config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, config),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+
+ signals[IP4_CONFIG] =
+ g_signal_new ("ip4-config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, ip4_config),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+
+ signals[IP6_CONFIG] =
+ g_signal_new ("ip6-config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, ip6_config),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+
+ signals[LOGIN_BANNER] =
+ g_signal_new ("login-banner",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, login_banner),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ signals[FAILURE] =
+ g_signal_new ("failure",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, failure),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[QUIT] =
+ g_signal_new ("quit",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnServicePluginClass, quit),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+
+ setup_unix_signal_handler ();
+}
+
+static void
+nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = init_sync;
+}
diff --git a/libnm/nm-vpn-service-plugin.h b/libnm/nm-vpn-service-plugin.h
new file mode 100644
index 0000000000..81ba40457f
--- /dev/null
+++ b/libnm/nm-vpn-service-plugin.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_VPN_SERVICE_PLUGIN_H__
+#define __NM_VPN_SERVICE_PLUGIN_H__
+
+#include <gio/gio.h>
+#include <nm-vpn-dbus-interface.h>
+#include <nm-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_VPN_SERVICE_PLUGIN (nm_vpn_service_plugin_get_type ())
+#define NM_VPN_SERVICE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePlugin))
+#define NM_VPN_SERVICE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginClass))
+#define NM_IS_VPN_SERVICE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE_PLUGIN))
+#define NM_IS_VPN_SERVICE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE_PLUGIN))
+#define NM_VPN_SERVICE_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginClass))
+
+#define NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME "service-name"
+#define NM_VPN_SERVICE_PLUGIN_STATE "state"
+
+typedef struct {
+ NM_AVAILABLE_IN_1_2
+ GObject parent;
+} NMVpnServicePlugin NM_AVAILABLE_IN_1_2;
+
+typedef struct {
+ NM_AVAILABLE_IN_1_2
+ GObjectClass parent;
+
+ /* Signals */
+ NM_AVAILABLE_IN_1_2
+ void (*state_changed) (NMVpnServicePlugin *plugin,
+ NMVpnServiceState state);
+
+ NM_AVAILABLE_IN_1_2
+ void (*ip4_config) (NMVpnServicePlugin *plugin,
+ GVariant *ip4_config);
+
+ NM_AVAILABLE_IN_1_2
+ void (*login_banner) (NMVpnServicePlugin *plugin,
+ const char *banner);
+
+ NM_AVAILABLE_IN_1_2
+ void (*failure) (NMVpnServicePlugin *plugin,
+ NMVpnPluginFailure reason);
+
+ NM_AVAILABLE_IN_1_2
+ void (*quit) (NMVpnServicePlugin *plugin);
+
+ NM_AVAILABLE_IN_1_2
+ void (*config) (NMVpnServicePlugin *plugin,
+ GVariant *config);
+
+ NM_AVAILABLE_IN_1_2
+ void (*ip6_config) (NMVpnServicePlugin *plugin,
+ GVariant *config);
+
+ /* virtual methods */
+ NM_AVAILABLE_IN_1_2
+ gboolean (*connect) (NMVpnServicePlugin *plugin,
+ NMConnection *connection,
+ GError **err);
+
+ NM_AVAILABLE_IN_1_2
+ gboolean (*need_secrets) (NMVpnServicePlugin *plugin,
+ NMConnection *connection,
+ const char **setting_name,
+ GError **error);
+
+ NM_AVAILABLE_IN_1_2
+ gboolean (*disconnect) (NMVpnServicePlugin *plugin,
+ GError **err);
+
+ NM_AVAILABLE_IN_1_2
+ gboolean (*new_secrets) (NMVpnServicePlugin *plugin,
+ NMConnection *connection,
+ GError **error);
+
+ NM_AVAILABLE_IN_1_2
+ gboolean (*connect_interactive) (NMVpnServicePlugin *plugin,
+ NMConnection *connection,
+ GVariant *details,
+ GError **error);
+
+ /*< private >*/
+ NM_AVAILABLE_IN_1_2
+ gpointer padding[8];
+} NMVpnServicePluginClass NM_AVAILABLE_IN_1_2;
+
+NM_AVAILABLE_IN_1_2
+GType nm_vpn_service_plugin_get_type (void);
+
+NM_AVAILABLE_IN_1_2
+GDBusConnection *nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin);
+NM_AVAILABLE_IN_1_2
+NMVpnServiceState nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin);
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin,
+ NMVpnServiceState state);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_secrets_required (NMVpnServicePlugin *plugin,
+ const char *message,
+ const char **hints);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_set_login_banner (NMVpnServicePlugin *plugin,
+ const char *banner);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin,
+ NMVpnPluginFailure reason);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
+ GVariant *config);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
+ GVariant *ip4_config);
+
+NM_AVAILABLE_IN_1_2
+void nm_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
+ GVariant *ip6_config);
+
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin,
+ GError **err);
+
+/* Utility functions */
+
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_service_plugin_read_vpn_details (int fd,
+ GHashTable **out_data,
+ GHashTable **out_secrets);
+
+NM_AVAILABLE_IN_1_2
+gboolean nm_vpn_service_plugin_get_secret_flags (GHashTable *data,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags);
+
+G_END_DECLS
+
+#endif /* __NM_VPN_SERVICE_PLUGIN_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6ca4a33ff6..8bffd707a9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -77,6 +77,8 @@ libnm-core/nm-setting-wireless-security.c
libnm-core/nm-setting-wireless.c
libnm-core/nm-setting.c
libnm-core/nm-utils.c
+libnm-core/nm-vpn-editor-plugin.c
+libnm-core/nm-vpn-plugin-info.c
libnm-glib/nm-device.c
libnm-glib/nm-remote-connection.c
libnm-util/crypto.c
@@ -127,6 +129,7 @@ libnm/nm-object.c
libnm/nm-remote-connection.c
libnm/nm-remote-settings.c
libnm/nm-vpn-plugin-old.c
+libnm/nm-vpn-service-plugin.c
policy/org.freedesktop.NetworkManager.policy.in.in
src/NetworkManagerUtils.c
src/main.c
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 2a6f48b79c..8dc46c41bf 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -29,44 +29,38 @@
#include "nm-vpn-connection.h"
#include "nm-setting-vpn.h"
#include "nm-vpn-dbus-interface.h"
+#include "nm-core-internal.h"
#include "nm-enum-types.h"
#include "nm-logging.h"
-
-#define VPN_NAME_FILES_DIR NMCONFDIR "/VPN"
+#include "gsystem-local-alloc.h"
G_DEFINE_TYPE (NMVpnManager, nm_vpn_manager, G_TYPE_OBJECT)
#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVpnManagerPrivate))
typedef struct {
- GHashTable *services;
- GFileMonitor *monitor;
- guint monitor_id;
+ GSList *services;
+ GFileMonitor *monitor_etc;
+ GFileMonitor *monitor_lib;
+ guint monitor_id_etc;
+ guint monitor_id_lib;
} NMVpnManagerPrivate;
-
static NMVpnService *
-get_service_by_namefile (NMVpnManager *self, const char *namefile)
+_plugin_info_get_service (NMVpnPluginInfo *plugin_info)
{
- NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
- GHashTableIter iter;
- gpointer data;
-
- g_return_val_if_fail (namefile, NULL);
- g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
-
- g_hash_table_iter_init (&iter, priv->services);
- while (g_hash_table_iter_next (&iter, NULL, &data)) {
- NMVpnService *candidate = NM_VPN_SERVICE (data);
- const char *service_namefile;
-
- service_namefile = nm_vpn_service_get_name_file (candidate);
- if (!strcmp (namefile, service_namefile))
- return candidate;
- }
+ if (plugin_info)
+ return NM_VPN_SERVICE (g_object_get_data (G_OBJECT (plugin_info), "service-instance"));
return NULL;
}
+static void
+_plugin_info_set_service (NMVpnPluginInfo *plugin_info, NMVpnService *service)
+{
+ g_object_set_data_full (G_OBJECT (plugin_info), "service-instance", service,
+ (GDestroyNotify) g_object_unref);
+}
+
gboolean
nm_vpn_manager_activate_connection (NMVpnManager *manager,
NMVpnConnection *vpn,
@@ -75,6 +69,7 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager,
NMConnection *connection;
NMSettingVpn *s_vpn;
NMVpnService *service;
+ NMVpnPluginInfo *plugin_info;
const char *service_name;
NMDevice *device;
@@ -98,8 +93,8 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager,
g_assert (s_vpn);
service_name = nm_setting_vpn_get_service_type (s_vpn);
- g_assert (service_name);
- service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
+ plugin_info = nm_vpn_plugin_info_list_find_by_service (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
+ service = _plugin_info_get_service (plugin_info);
if (!service) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
"The VPN service '%s' was not installed.",
@@ -119,33 +114,31 @@ nm_vpn_manager_deactivate_connection (NMVpnManager *self,
}
static void
-try_add_service (NMVpnManager *self, const char *namefile)
+try_add_service (NMVpnManager *self, NMVpnPluginInfo *plugin_info)
{
NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
NMVpnService *service = NULL;
- GHashTableIter iter;
GError *error = NULL;
- const char *service_name;
-
- g_return_if_fail (g_path_is_absolute (namefile));
- /* Make sure we don't add dupes */
- g_hash_table_iter_init (&iter, priv->services);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
- if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0)
- return;
- }
+ /* Make sure we don't add dupes.
+ * We don't really allow reload of the same file. What we do allow is however to
+ * delete a file and re-add it. */
+ if (nm_vpn_plugin_info_list_find_by_filename (priv->services,
+ nm_vpn_plugin_info_get_filename (plugin_info)))
+ return;
+ if (!nm_vpn_plugin_info_list_add (&priv->services, plugin_info, NULL))
+ return;
/* New service */
- service = nm_vpn_service_new (namefile, &error);
+ service = nm_vpn_service_new (plugin_info, &error);
if (service) {
- service_name = nm_vpn_service_get_dbus_service (service);
- g_hash_table_insert (priv->services, (char *) service_name, service);
- nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
+ _plugin_info_set_service (plugin_info, service);
+ nm_log_info (LOGD_VPN, "VPN: loaded %s - %s",
+ nm_vpn_plugin_info_get_name (plugin_info),
+ nm_vpn_service_get_dbus_service (service));
} else {
- nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
- namefile,
- error ? error->code : -1,
+ nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: %s",
+ nm_vpn_plugin_info_get_filename (plugin_info),
error && error->message ? error->message : "(unknown)");
g_clear_error (&error);
}
@@ -160,41 +153,65 @@ vpn_dir_changed (GFileMonitor *monitor,
{
NMVpnManager *self = NM_VPN_MANAGER (user_data);
NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ NMVpnPluginInfo *plugin_info;
NMVpnService *service;
- char *path;
+ gs_free char *path = NULL;
+ GError *error = NULL;
path = g_file_get_path (file);
- if (!g_str_has_suffix (path, ".name")) {
- g_free (path);
+ if (!nm_vpn_plugin_info_validate_filename (path))
return;
- }
switch (event_type) {
case G_FILE_MONITOR_EVENT_DELETED:
- nm_log_dbg (LOGD_VPN, "service file %s deleted", path);
+ plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->services, path);
+ if (!plugin_info)
+ break;
- service = get_service_by_namefile (self, path);
+ nm_log_dbg (LOGD_VPN, "vpn: service file %s deleted", path);
+ service = _plugin_info_get_service (plugin_info);
if (service) {
- const char *service_name = nm_vpn_service_get_dbus_service (service);
-
/* Stop active VPN connections and destroy the service */
nm_vpn_service_stop_connections (service, FALSE,
NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
- nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name);
- g_hash_table_remove (priv->services, service_name);
+ nm_log_info (LOGD_VPN, "VPN: unloaded %s", nm_vpn_service_get_dbus_service (service));
+
+ _plugin_info_set_service (plugin_info, NULL);
}
+ nm_vpn_plugin_info_list_remove (&priv->services, plugin_info);
break;
case G_FILE_MONITOR_EVENT_CREATED:
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
- nm_log_dbg (LOGD_VPN, "service file %s created or modified", path);
- try_add_service (self, path);
+ plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->services, path);
+ if (plugin_info) {
+ /* we don't support reloading an existing plugin. You can only remove the file
+ * and re-add it. By reloading we want to support the use case of installing
+ * a VPN plugin after NM started. No need to burden ourself with a complete
+ * reload. */
+ break;
+ }
+
+ if (!_nm_vpn_plugin_info_check_file (path, TRUE, TRUE, 0,
+ NULL, NULL, &error)) {
+ nm_log_dbg (LOGD_VPN, "vpn: ignore changed service file %s (%s)", path, error->message);
+ g_clear_error (&error);
+ break;
+ }
+ plugin_info = nm_vpn_plugin_info_new_from_file (path, &error);
+ if (!plugin_info) {
+ nm_log_dbg (LOGD_VPN, "vpn: ignore changed service file %s due to invalid content (%s)", path, error->message);
+ g_clear_error (&error);
+ break;
+ }
+
+ nm_log_dbg (LOGD_VPN, "vpn: service file %s created or modified", path);
+ try_add_service (self, plugin_info);
+ g_object_unref (plugin_info);
break;
default:
- nm_log_dbg (LOGD_VPN, "service file %s change event %d", path, event_type);
+ nm_log_dbg (LOGD_VPN, "vpn: service file %s change event %d", path, event_type);
break;
}
-
- g_free (path);
}
/******************************************************************************/
@@ -206,50 +223,40 @@ nm_vpn_manager_init (NMVpnManager *self)
{
NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
GFile *file;
- GDir *dir;
- const char *fn;
- char *path;
-
- priv->services = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL, g_object_unref);
+ GSList *infos, *info;
+ const char *conf_dir_etc = _nm_vpn_plugin_info_get_default_dir_etc ();
+ const char *conf_dir_lib = _nm_vpn_plugin_info_get_default_dir_lib ();
/* Watch the VPN directory for changes */
- file = g_file_new_for_path (VPN_NAME_FILES_DIR "/");
- priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ file = g_file_new_for_path (conf_dir_lib);
+ priv->monitor_lib = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
g_object_unref (file);
- if (priv->monitor) {
- priv->monitor_id = g_signal_connect (priv->monitor, "changed",
- G_CALLBACK (vpn_dir_changed), self);
+ if (priv->monitor_lib) {
+ priv->monitor_id_lib = g_signal_connect (priv->monitor_lib, "changed",
+ G_CALLBACK (vpn_dir_changed), self);
}
- /* Load VPN service files */
- dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
- if (dir) {
- while ((fn = g_dir_read_name (dir))) {
- /* only parse filenames that end with .name */
- if (g_str_has_suffix (fn, ".name")) {
- path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
- try_add_service (self, path);
- g_free (path);
- }
- }
- g_dir_close (dir);
+ file = g_file_new_for_path (conf_dir_etc);
+ priv->monitor_etc = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_object_unref (file);
+ if (priv->monitor_etc) {
+ priv->monitor_id_etc = g_signal_connect (priv->monitor_etc, "changed",
+ G_CALLBACK (vpn_dir_changed), self);
}
-}
-
-static void
-stop_all_services (NMVpnManager *self)
-{
- NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
- GHashTableIter iter;
- NMVpnService *service;
- g_hash_table_iter_init (&iter, priv->services);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
- nm_vpn_service_stop_connections (service,
- TRUE,
- NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
- }
+ /* first read conf_dir_lib. The name files are not really user configuration, but
+ * plugin configuration. Hence we expect ~newer~ plugins to install their files
+ * in /usr/lib/NetworkManager. We want to prefer those files.
+ * In case of no-conflict, the order doesn't matter. */
+ infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_lib, TRUE, 0, NULL, NULL);
+ for (info = infos; info; info = info->next)
+ try_add_service (self, info->data);
+ g_slist_free_full (infos, g_object_unref);
+
+ infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_etc, TRUE, 0, NULL, NULL);
+ for (info = infos; info; info = info->next)
+ try_add_service (self, info->data);
+ g_slist_free_full (infos, g_object_unref);
}
static void
@@ -257,17 +264,29 @@ dispose (GObject *object)
{
NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
- if (priv->monitor) {
- if (priv->monitor_id)
- g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
- g_file_monitor_cancel (priv->monitor);
- g_clear_object (&priv->monitor);
+ if (priv->monitor_etc) {
+ if (priv->monitor_id_etc)
+ g_signal_handler_disconnect (priv->monitor_etc, priv->monitor_id_etc);
+ g_file_monitor_cancel (priv->monitor_etc);
+ g_clear_object (&priv->monitor_etc);
}
- if (priv->services) {
- stop_all_services (NM_VPN_MANAGER (object));
- g_hash_table_destroy (priv->services);
- priv->services = NULL;
+ if (priv->monitor_lib) {
+ if (priv->monitor_id_lib)
+ g_signal_handler_disconnect (priv->monitor_lib, priv->monitor_id_lib);
+ g_file_monitor_cancel (priv->monitor_lib);
+ g_clear_object (&priv->monitor_lib);
+ }
+
+ while (priv->services) {
+ NMVpnPluginInfo *plugin_info = priv->services->data;
+ NMVpnService *service = _plugin_info_get_service (plugin_info);
+
+ if (service) {
+ nm_vpn_service_stop_connections (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+ _plugin_info_set_service (plugin_info, NULL);
+ }
+ nm_vpn_plugin_info_list_remove (&priv->services, plugin_info);
}
G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
index 56cb6abe74..4661ab4223 100644
--- a/src/vpn-manager/nm-vpn-service.c
+++ b/src/vpn-manager/nm-vpn-service.c
@@ -35,10 +35,7 @@
G_DEFINE_TYPE (NMVpnService, nm_vpn_service, G_TYPE_OBJECT)
typedef struct {
- char *name;
- char *dbus_service;
- char *program;
- char *namefile;
+ NMVpnPluginInfo *plugin_info;
NMVpnConnection *active;
GSList *pending;
@@ -50,67 +47,50 @@ typedef struct {
#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVpnServicePrivate))
-#define VPN_CONNECTION_GROUP "VPN Connection"
-
static gboolean start_pending_vpn (NMVpnService *self, GError **error);
static void _name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
NMVpnService *
-nm_vpn_service_new (const char *namefile, GError **error)
+nm_vpn_service_new (NMVpnPluginInfo *plugin_info, GError **error)
{
NMVpnService *self;
NMVpnServicePrivate *priv;
- GKeyFile *kf;
- g_return_val_if_fail (namefile != NULL, NULL);
- g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), NULL);
+ g_return_val_if_fail (nm_vpn_plugin_info_get_filename (plugin_info), NULL);
- kf = g_key_file_new ();
- if (!g_key_file_load_from_file (kf, namefile, G_KEY_FILE_NONE, error)) {
- g_key_file_free (kf);
+ if (!nm_vpn_plugin_info_get_program (plugin_info)) {
+ g_set_error (error,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_FAILED,
+ "missing \"program\" entry");
return NULL;
}
self = (NMVpnService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- priv->namefile = g_strdup (namefile);
-
- priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error);
- if (!priv->dbus_service)
- goto error;
-
- priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error);
- if (!priv->program)
- goto error;
-
- priv->name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", error);
- if (!priv->name)
- goto error;
+ priv->plugin_info = g_object_ref (plugin_info);
priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
- priv->dbus_service,
+ nm_vpn_plugin_info_get_service (plugin_info),
NM_VPN_DBUS_PLUGIN_PATH,
NM_VPN_DBUS_PLUGIN_INTERFACE,
NULL, error);
- if (!priv->proxy)
- goto error;
+ if (!priv->proxy) {
+ g_object_unref (self);
+ return NULL;
+ }
g_signal_connect (priv->proxy, "notify::g-name-owner",
G_CALLBACK (_name_owner_changed), self);
_name_owner_changed (G_OBJECT (priv->proxy), NULL, self);
- g_key_file_free (kf);
return self;
-
-error:
- g_object_unref (self);
- g_key_file_free (kf);
- return NULL;
}
const char *
@@ -118,15 +98,7 @@ nm_vpn_service_get_dbus_service (NMVpnService *service)
{
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
- return NM_VPN_SERVICE_GET_PRIVATE (service)->dbus_service;
-}
-
-const char *
-nm_vpn_service_get_name_file (NMVpnService *service)
-{
- g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-
- return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile;
+ return nm_vpn_plugin_info_get_service (NM_VPN_SERVICE_GET_PRIVATE (service)->plugin_info);
}
static void
@@ -187,7 +159,7 @@ _daemon_exec_timeout (gpointer data)
NMVpnService *self = NM_VPN_SERVICE (data);
NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name);
+ nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", nm_vpn_plugin_info_get_name (priv->plugin_info));
priv->start_timeout = 0;
nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
return G_SOURCE_REMOVE;
@@ -204,17 +176,21 @@ nm_vpn_service_daemon_exec (NMVpnService *service, GError **error)
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE);
- vpn_argv[0] = priv->program;
+ vpn_argv[0] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info);
vpn_argv[1] = NULL;
+ g_assert (vpn_argv[0]);
+
success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error);
if (success) {
nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %ld",
- priv->name, priv->dbus_service, (long int) pid);
+ nm_vpn_plugin_info_get_name (priv->plugin_info),
+ nm_vpn_service_get_dbus_service (service),
+ (long int) pid);
priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service);
} else {
nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
- priv->name,
+ nm_vpn_plugin_info_get_name (priv->plugin_info),
spawn_error ? spawn_error->code : -1,
spawn_error && spawn_error->message ? spawn_error->message : "(unknown)");
@@ -244,7 +220,7 @@ start_active_vpn (NMVpnService *self, GError **error)
return TRUE;
} else if (priv->start_timeout == 0) {
/* VPN service not running, start it */
- nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
+ nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", nm_vpn_plugin_info_get_name (priv->plugin_info));
return nm_vpn_service_daemon_exec (self, error);
}
@@ -324,14 +300,14 @@ _name_owner_changed (GObject *object,
if (owner && !priv->service_running) {
/* service appeared */
priv->service_running = TRUE;
- nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
+ nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", nm_vpn_plugin_info_get_name (priv->plugin_info));
/* Expect success because the VPN service has already appeared */
success = start_active_vpn (service, NULL);
g_warn_if_fail (success);
} else if (!owner && priv->service_running) {
/* service went away */
priv->service_running = FALSE;
- nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name);
+ nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", nm_vpn_plugin_info_get_name (priv->plugin_info));
nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
}
@@ -351,10 +327,9 @@ dispose (GObject *object)
NMVpnService *self = NM_VPN_SERVICE (object);
NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- if (priv->start_timeout) {
- g_source_remove (priv->start_timeout);
- priv->start_timeout = 0;
- }
+ nm_clear_g_source (&priv->start_timeout);
+
+ g_clear_object (&priv->plugin_info);
/* VPNService owner is required to stop connections before releasing */
g_assert (priv->active == NULL);
@@ -371,19 +346,6 @@ dispose (GObject *object)
}
static void
-finalize (GObject *object)
-{
- NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
-
- g_free (priv->name);
- g_free (priv->dbus_service);
- g_free (priv->program);
- g_free (priv->namefile);
-
- G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
-}
-
-static void
nm_vpn_service_class_init (NMVpnServiceClass *service_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (service_class);
@@ -392,5 +354,4 @@ nm_vpn_service_class_init (NMVpnServiceClass *service_class)
/* virtual methods */
object_class->dispose = dispose;
- object_class->finalize = finalize;
}
diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h
index 6e935950e2..a748d32f39 100644
--- a/src/vpn-manager/nm-vpn-service.h
+++ b/src/vpn-manager/nm-vpn-service.h
@@ -25,6 +25,7 @@
#include "nm-glib.h"
#include "nm-device.h"
#include "nm-vpn-connection.h"
+#include "nm-vpn-plugin-info.h"
#define NM_TYPE_VPN_SERVICE (nm_vpn_service_get_type ())
#define NM_VPN_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVpnService))
@@ -43,14 +44,11 @@ typedef struct {
GType nm_vpn_service_get_type (void);
-NMVpnService * nm_vpn_service_new (const char *namefile, GError **error);
+NMVpnService * nm_vpn_service_new (NMVpnPluginInfo *plugin_info, GError **error);
/* Returns the VPN service's D-Bus service name */
const char *nm_vpn_service_get_dbus_service (NMVpnService *service);
-/* Returns the path of the VPN service's .name file */
-const char *nm_vpn_service_get_name_file (NMVpnService *service);
-
gboolean nm_vpn_service_activate (NMVpnService *service,
NMVpnConnection *vpn,
GError **error);