diff options
author | Thomas Haller <thaller@redhat.com> | 2016-06-15 15:02:45 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-06-15 15:02:45 +0200 |
commit | b6b84d0442809d1b127c4bb36f09445ece2ac262 (patch) | |
tree | 2e8e624d595485643f2d4484cfe9c35fa2f910cd | |
parent | ccc1be34ee39731ee37f869fac8280971c69a17f (diff) | |
parent | cf34211c90d2d45cb325f5558304f64297033bc6 (diff) | |
download | NetworkManager-b6b84d0442809d1b127c4bb36f09445ece2ac262.tar.gz |
libnm/vpn,cli: merge branch 'th/vpn-service-info-bgo767197'
https://bugzilla.gnome.org/show_bug.cgi?id=767197
-rw-r--r-- | clients/cli/connections.c | 38 | ||||
-rw-r--r-- | clients/common/nm-vpn-helpers.c | 98 | ||||
-rw-r--r-- | clients/common/nm-vpn-helpers.h | 9 | ||||
-rw-r--r-- | clients/tui/nm-editor-utils.c | 2 | ||||
-rw-r--r-- | libnm-core/nm-keyfile-writer.c | 8 | ||||
-rw-r--r-- | libnm-core/nm-vpn-editor-plugin.c | 153 | ||||
-rw-r--r-- | libnm-core/nm-vpn-editor-plugin.h | 24 | ||||
-rw-r--r-- | libnm-core/nm-vpn-plugin-info.c | 223 | ||||
-rw-r--r-- | libnm-core/nm-vpn-plugin-info.h | 12 | ||||
-rw-r--r-- | libnm/libnm.ver | 6 | ||||
-rw-r--r-- | libnm/nm-vpn-service-plugin.c | 8 | ||||
-rw-r--r-- | po/POTFILES.skip | 1 | ||||
-rw-r--r-- | shared/Makefile.am | 3 | ||||
-rw-r--r-- | shared/nm-macros-internal.h | 30 | ||||
-rw-r--r-- | shared/nm-vpn-editor-plugin-call.h | 189 | ||||
-rw-r--r-- | src/nm-config.c | 11 |
16 files changed, 668 insertions, 147 deletions
diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 0c61667d92..506bfe0fd0 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -5886,16 +5886,13 @@ cleanup_bridge_slave: /* Build up the settings required for 'vpn' */ gboolean success = FALSE; const char *vpn_type = NULL; - char *vpn_type_ask = NULL; + gs_free char *vpn_type_ask = NULL; const char *user_c = NULL; char *user = NULL; - const char *st; gs_free char *service_type_free = NULL; - const char *service_type = NULL; nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, {"user", TRUE, &user_c, FALSE}, {NULL} }; - gs_free const char **plugin_names = NULL; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; @@ -5910,15 +5907,11 @@ cleanup_bridge_slave: if (vpn_type_ask) vpn_type = g_strstrip (vpn_type_ask); - plugin_names = nm_vpn_get_plugin_names (FALSE); - if (!(st = nmc_string_is_valid (vpn_type, plugin_names, NULL))) { + service_type_free = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), vpn_type); + if (!service_type_free) g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type); - st = vpn_type; - } - - service_type = nm_vpn_get_service_for_name (st); - if (!service_type) - service_type = service_type_free = nm_vpn_get_service_for_name_default (st); + else + vpn_type = service_type_free; /* Also ask for all optional arguments if '--ask' is specified. */ user = g_strdup (user_c); @@ -5929,12 +5922,11 @@ cleanup_bridge_slave: s_vpn = (NMSettingVpn *) nm_setting_vpn_new (); nm_connection_add_setting (connection, NM_SETTING (s_vpn)); - g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); + g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, vpn_type, NULL); g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); success = TRUE; cleanup_vpn: - g_free (vpn_type_ask); g_free (user); if (!success) return FALSE; @@ -6702,10 +6694,10 @@ update_connection (gboolean persistent, static char * gen_func_vpn_types (const char *text, int state) { - gs_free const char **plugin_names = NULL; + gs_strfreev char **plugin_names = NULL; - plugin_names = nm_vpn_get_plugin_names (FALSE); - return nmc_rl_gen_func_basic (text, state, plugin_names); + plugin_names = nm_vpn_plugin_info_list_get_service_types (nm_vpn_get_plugin_infos (), FALSE, TRUE); + return nmc_rl_gen_func_basic (text, state, (const char **) plugin_names); } static char * @@ -10625,6 +10617,7 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) AddConnectionInfo *info; NMConnection *connection = NULL; NMVpnEditorPlugin *plugin; + gs_free char *service_type = NULL; if (argc == 0) { if (nmc->ask) { @@ -10682,8 +10675,15 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) goto finish; } + service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type); + if (!service_type) { + g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + /* Import VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_get_editor_plugin (service_type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); @@ -10790,7 +10790,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv) type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); /* Export VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_get_editor_plugin (type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index 43ac4b2e61..f2e6f4a662 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -19,8 +19,6 @@ /** * SECTION:nm-vpn-helpers * @short_description: VPN-related utilities - * - * Some functions should probably eventually move into libnm. */ #include "nm-default.h" @@ -31,30 +29,23 @@ #include "nm-utils.h" -static gboolean plugins_loaded; -static GSList *plugins = NULL; +/*****************************************************************************/ NMVpnEditorPlugin * -nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) +nm_vpn_get_editor_plugin (const char *service_type, GError **error) { NMVpnEditorPlugin *plugin = NULL; NMVpnPluginInfo *plugin_info; gs_free_error GError *local = NULL; - g_return_val_if_fail (!service ^ !name, NULL); + g_return_val_if_fail (service_type, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (G_UNLIKELY (!plugins_loaded)) - nm_vpn_get_plugins (); - - if (service) - plugin_info = nm_vpn_plugin_info_list_find_by_service (plugins, service); - else - plugin_info = nm_vpn_plugin_info_list_find_by_name (plugins, name); + plugin_info = nm_vpn_plugin_info_list_find_by_service (nm_vpn_get_plugin_infos (), service_type); if (!plugin_info) { g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED, - _("unknown VPN plugin \"%s\""), service ?: name); + _("unknown VPN plugin \"%s\""), service_type); return NULL; } plugin = nm_vpn_plugin_info_get_editor_plugin (plugin_info); @@ -86,8 +77,11 @@ nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) } GSList * -nm_vpn_get_plugins (void) +nm_vpn_get_plugin_infos (void) { + static bool plugins_loaded; + static GSList *plugins = NULL; + if (G_LIKELY (plugins_loaded)) return plugins; plugins_loaded = TRUE; @@ -95,78 +89,6 @@ nm_vpn_get_plugins (void) return plugins; } -static int -_strcmp_data (gconstpointer a, gconstpointer b, gpointer unused) -{ - return strcmp (a, b); -} - -const char ** -nm_vpn_get_plugin_names (gboolean only_available_plugins) -{ - GSList *p; - const char **list; - const char *known_names[] = { - "openvpn", - "vpnc", - "pptp", - "openconnect", - "openswan", - "libreswan", - "strongswan", - "ssh", - "l2tp", - "iodine", - "fortisslvpn", - }; - guint i, j, k; - - p = nm_vpn_get_plugins (); - list = g_new0 (const char *, g_slist_length (p) + G_N_ELEMENTS (known_names) + 1); - - i = 0; - for (i = 0; p; p = p->next) - list[i++] = nm_vpn_plugin_info_get_name (p->data); - if (!only_available_plugins) { - for (j = 0; j < G_N_ELEMENTS (known_names); j++) - list[i++] = known_names[j]; - } - - g_qsort_with_data (list, i, sizeof (gpointer), _strcmp_data, NULL); - - /* remove duplicates */ - for (k = 0, j = 1; j < i; j++) { - if (nm_streq (list[k], list[j])) - continue; - list[k++] = list[j]; - } - list[k++] = NULL; - - return list; -} - -const char * -nm_vpn_get_service_for_name (const char *name) -{ - NMVpnPluginInfo *plugin_info; - - g_return_val_if_fail (name, NULL); - - plugin_info = nm_vpn_plugin_info_list_find_by_name (nm_vpn_get_plugins (), name); - if (plugin_info) { - /* this only means we have a .name file (NMVpnPluginInfo). Possibly the - * NMVpnEditorPlugin is not loadable. */ - return nm_vpn_plugin_info_get_service (plugin_info); - } - return NULL; -} - -char * -nm_vpn_get_service_for_name_default (const char *name) -{ - return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); -} - gboolean nm_vpn_supports_ipv6 (NMConnection *connection) { @@ -182,7 +104,7 @@ nm_vpn_supports_ipv6 (NMConnection *connection) if (!service_type) return FALSE; - plugin = nm_vpn_lookup_plugin (NULL, service_type, NULL); + plugin = nm_vpn_get_editor_plugin (service_type, NULL); if (!plugin) return FALSE; diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 79fc94e957..e69e5b4e64 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -28,14 +28,9 @@ struct { const char *ui_name; } typedef VpnPasswordName; -GSList *nm_vpn_get_plugins (void); +GSList *nm_vpn_get_plugin_infos (void); -const char **nm_vpn_get_plugin_names (gboolean only_available_plugins); - -const char *nm_vpn_get_service_for_name (const char *name); -char * nm_vpn_get_service_for_name_default (const char *name); - -NMVpnEditorPlugin *nm_vpn_lookup_plugin (const char *name, const char *service, GError **error); +NMVpnEditorPlugin *nm_vpn_get_editor_plugin (const char *service_type, GError **error); gboolean nm_vpn_supports_ipv6 (NMConnection *connection); diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c index 25a64c2eed..9e6263e633 100644 --- a/clients/tui/nm-editor-utils.c +++ b/clients/tui/nm-editor-utils.c @@ -223,7 +223,7 @@ nm_editor_utils_get_connection_type_list (void) #if 0 /* Add "VPN" only if there are plugins */ - vpn_plugins_hash = nm_vpn_get_plugins (); + vpn_plugins_hash = nm_vpn_get_plugin_infos (); have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); if (have_vpn_plugins) { GHashTableIter iter; diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index f62e97c3e6..e0e6ba4bf8 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -233,12 +233,6 @@ route_writer (KeyfileWriterInfo *info, write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); } -static int -sort_hash_keys (gconstpointer a, gconstpointer b, gpointer user_data) -{ - return g_strcmp0 (*((const char **) a), *((const char **) b)); -} - static void write_hash_of_string (GKeyFile *file, NMSetting *setting, @@ -262,7 +256,7 @@ write_hash_of_string (GKeyFile *file, if (!keys) return; - g_qsort_with_data (keys, l, sizeof (const char *), sort_hash_keys, NULL); + g_qsort_with_data (keys, l, sizeof (const char *), nm_strcmp_p_with_data, NULL); for (i = 0; keys[i]; i++) { const char *property, *data; diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c index 09df246957..bc72ca3911 100644 --- a/libnm-core/nm-vpn-editor-plugin.c +++ b/libnm-core/nm-vpn-editor-plugin.c @@ -74,6 +74,159 @@ nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface) /*********************************************************************/ +typedef struct { + NMVpnPluginInfo *plugin_info; +} NMVpnEditorPluginPrivate; + +static void +_private_destroy (gpointer data) +{ + NMVpnEditorPluginPrivate *priv = data; + + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + g_slice_free (NMVpnEditorPluginPrivate, priv); +} + +static NMVpnEditorPluginPrivate * +_private_get (NMVpnEditorPlugin *plugin, gboolean create) +{ + static GQuark quark = 0; + NMVpnEditorPluginPrivate *priv; + + nm_assert (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_string ("nm-vpn-editor-plugin-private"); + + priv = g_object_get_qdata ((GObject *) plugin, quark); + if (G_LIKELY (priv)) + return priv; + if (!create) + return NULL; + priv = g_slice_new0 (NMVpnEditorPluginPrivate); + g_object_set_qdata_full ((GObject *) plugin, quark, priv, _private_destroy); + return priv; +} + +#define NM_VPN_EDITOR_PLUGIN_GET_PRIVATE(plugin) _private_get (plugin, TRUE) +#define NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE(plugin) _private_get (plugin, FALSE) + +/*********************************************************************/ + +/** + * nm_vpn_editor_plugin_get_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * + * Returns: (transfer-none): if set, return the #NMVpnPluginInfo instance. + * + * Since: 1.4 + */ +NMVpnPluginInfo * +nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin) +{ + NMVpnEditorPluginPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL); + + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + return priv ? priv->plugin_info : NULL; +} + +/** + * nm_vpn_editor_plugin_set_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * @plugin_info: (allow-none): a #NMVpnPluginInfo instance or %NULL + * + * Returns: (transfer-none): set or clear the plugin-info instance. + * This takes a weak reference on @plugin_info, to avoid circular + * reference as the plugin-info might also reference the editor-plugin. + * + * Since: 1.4 + */ +void +nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, NMVpnPluginInfo *plugin_info) +{ + NMVpnEditorPluginInterface *interface; + NMVpnEditorPluginPrivate *priv; + + g_return_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (!plugin_info) { + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + if (!priv) + return; + } else { + g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info)); + priv = NM_VPN_EDITOR_PLUGIN_GET_PRIVATE (plugin); + } + + if (priv->plugin_info == plugin_info) + return; + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + priv->plugin_info = plugin_info; + if (priv->plugin_info) + g_object_add_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + if (plugin_info) { + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->notify_plugin_info_set) + interface->notify_plugin_info_set (plugin, plugin_info); + } + +} + +/*********************************************************************/ + +/** + * nm_vpn_editor_plugin_get_vt: + * @plugin: the #NMVpnEditorPlugin + * @vt: (out): buffer to be filled with the VT table of the plugin + * @vt_size: the size of the buffer. Can be 0 to only query the + * size of plugin's VT. + * + * Returns an opaque VT function table for the plugin to extend + * functionality. The actual meaning of NMVpnEditorPluginVT is not + * defined in public API of libnm, instead it must be agreed by + * both the plugin and the caller. See the header-only file + * 'nm-vpn-editor-plugin-call.h' which defines the meaning. + * + * Returns: the actual size of the @plugin's virtual function table. + * + * Since: 1.4 + **/ +gsize +nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size) +{ + const NMVpnEditorPluginVT *p_vt = NULL; + gsize p_vt_size = 0; + NMVpnEditorPluginInterface *interface; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), 0); + + if (vt_size) { + g_return_val_if_fail (vt, 0); + memset (vt, 0, vt_size); + } + + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->get_vt) { + p_vt = interface->get_vt (plugin, &p_vt_size); + if (!p_vt) + p_vt_size = 0; + g_return_val_if_fail (p_vt_size, 0); + memcpy (vt, p_vt, MIN (vt_size, p_vt_size)); + } + + return p_vt_size; +} + +/*********************************************************************/ + static NMVpnEditorPlugin * _nm_vpn_editor_plugin_load (const char *plugin_name, gboolean do_file_checks, diff --git a/libnm-core/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h index 82c609b969..fb51f7e621 100644 --- a/libnm-core/nm-vpn-editor-plugin.h +++ b/libnm-core/nm-vpn-editor-plugin.h @@ -34,6 +34,8 @@ G_BEGIN_DECLS +struct _NMVpnPluginInfo; + typedef struct _NMVpnEditorPlugin NMVpnEditorPlugin; typedef struct _NMVpnEditor NMVpnEditor; @@ -80,6 +82,8 @@ typedef enum /*< flags >*/ { /* D-Bus service name of the plugin's VPN service */ #define NM_VPN_EDITOR_PLUGIN_SERVICE "service" +typedef struct _NMVpnEditorPluginVT NMVpnEditorPluginVT; + /** * NMVpnEditorPluginInterface: * @g_iface: the parent interface @@ -97,6 +101,8 @@ typedef enum /*< flags >*/ { * @get_suggested_filename: For a given connection, return a suggested file * name. Returned value will be %NULL or a suggested file name to be freed by * the caller. + * @get_vt: return a virtual function table to implement further functions in + * the plugin, without requiring to update libnm. Used by nm_vpn_editor_plugin_get_vt(). * * Interface for VPN editor plugins. */ @@ -119,6 +125,12 @@ typedef struct { GError **error); char * (*get_suggested_filename) (NMVpnEditorPlugin *plugin, NMConnection *connection); + + void (*notify_plugin_info_set) (NMVpnEditorPlugin *plugin, + struct _NMVpnPluginInfo *plugin_info); + + const NMVpnEditorPluginVT *(*get_vt) (NMVpnEditorPlugin *plugin, + gsize *out_vt_size); } NMVpnEditorPluginInterface; GType nm_vpn_editor_plugin_get_type (void); @@ -129,6 +141,11 @@ NMVpnEditor *nm_vpn_editor_plugin_get_editor (NMVpnEditorPlugin *plugin, NMVpnEditorPluginCapability nm_vpn_editor_plugin_get_capabilities (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +gsize nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size); + NMConnection *nm_vpn_editor_plugin_import (NMVpnEditorPlugin *plugin, const char *path, GError **error); @@ -152,6 +169,13 @@ NMVpnEditorPlugin *nm_vpn_editor_plugin_load (const char *plugin_name, const char *check_service, GError **error); +NM_AVAILABLE_IN_1_4 +struct _NMVpnPluginInfo *nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +void nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, struct _NMVpnPluginInfo *plugin_info); + +#include "nm-vpn-plugin-info.h" + G_END_DECLS #endif /* __NM_VPN_EDITOR_PLUGIN_H__ */ diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 11393b54f1..7a3198c3d9 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -491,7 +491,7 @@ nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError } /* the plugin must have unique values for certain properties. E.g. two different - * plugins cannot share the same service name. */ + * plugins cannot share the same service type. */ if (!_check_no_conflict (plugin_info, iter->data, error)) return FALSE; } @@ -573,10 +573,24 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) return NULL; } +static NMVpnPluginInfo * +_list_find_by_service (GSList *list, const char *service) +{ + for (; list; list = list->next) { + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (list->data); + + if ( nm_streq (priv->service, service) + || _nm_utils_strv_find_first (priv->aliases, -1, service) >= 0) + return list->data; + } + return NULL; +} + /** * nm_vpn_plugin_info_list_find_by_service: * @list: (element-type NMVpnPluginInfo): list of plugins - * @service: service to search + * @service: service to search. This can be the main service-type + * or one of the provided aliases. * * Returns: (transfer none): the first plugin with a matching @service (or %NULL). * @@ -585,27 +599,180 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) NMVpnPluginInfo * nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service) { + if (!service) + g_return_val_if_reached (NULL); + return _list_find_by_service (list, service); +} + +/* known_names are well known short names for the service-type. They all implicitly + * have a prefix "org.freedesktop.NetworkManager." + known_name. */ +static const char *known_names[] = { + "openvpn", + "vpnc", + "pptp", + "openconnect", + "openswan", + "libreswan", + "strongswan", + "ssh", + "l2tp", + "iodine", + "fortisslvpn", +}; + +/** + * nm_vpn_plugin_info_list_find_service_type: + * @list: a possibly empty #GSList of #NMVpnPluginInfo instances + * @name: a name to lookup the service-type. + * + * A VPN plugin provides one or several service-types, like org.freedesktop.NetworkManager.libreswan + * Certain plugins provide more then one service type, via aliases (org.freedesktop.NetworkManager.openswan). + * This function looks up a service-type (or an alias) based on a name. + * + * Preferably, the name can be a full service-type/alias of an installed + * plugin. Otherwise, it can be the name of a VPN plugin (in which case, the + * primary, non-aliased service-type is returned). Otherwise, it can be + * one of several well known short-names (which is a hard-coded list of + * types in libnm). On success, this returns a full qualified service-type + * (or an alias). It doesn't say, that such an plugin is actually available, + * but it could be retrieved via nm_vpn_plugin_info_list_find_by_service(). + * + * Returns: (transfer-full): the resolved service-type or %NULL on failure. + * + * Since: 1.4 + */ +char * +nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name) +{ GSList *iter; + char *n; - if (!service) + if (!name) g_return_val_if_reached (NULL); + if (!*name) + return NULL; - /* First, consider the primary service name. */ + /* First, try to interpret @name as a full service-type (or alias). */ + if (_list_find_by_service (list, name)) + return g_strdup (name); + + /* try to interpret @name as plugin name, in which case we return + * the main service-type (not an alias). */ for (iter = list; iter; iter = iter->next) { - if (strcmp (nm_vpn_plugin_info_get_service (iter->data), service) == 0) - return iter->data; + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); + + if (nm_streq (priv->name, name)) + return g_strdup (priv->service); } - /* Then look into the aliases. */ + /* check the hard-coded list of short-names. They all have have the same + * well-known prefix org.freedesktop.NetworkManager and the name. */ + if (_nm_utils_strv_find_first ((char **) known_names, G_N_ELEMENTS (known_names), name) >= 0) + return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + + /* try, if there exists a plugin with @name under org.freedesktop.NetworkManager. + * Allow this to be a valid abbreviation. */ + n = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + if (_list_find_by_service (list, n)) + return n; + g_free (n); + + /* currently, VPN plugins have no way to define a short-name for their + * alias name, unless the alias name is prefixed by org.freedesktop.NetworkManager. */ + + return NULL; +} + +static const char * +_service_type_get_default_abbreviation (const char *service_type) +{ + if (!g_str_has_prefix (service_type, NM_DBUS_INTERFACE)) + return NULL; + service_type += NM_STRLEN (NM_DBUS_INTERFACE); + if (service_type[0] != '.') + return NULL; + service_type++; + if (!service_type[0]) + return NULL; + return service_type; +} + +/** + * nm_vpn_plugin_info_list_get_service_types: + * @list: a possibly empty #GSList of #NMVpnPluginInfo + * @only_existing: only include results that are actually in @list. + * Otherwise, the result is extended with a hard-code list or + * well-known plugins + * @with_abbreviations: if %FALSE, only full service types are returned. + * Otherwise, this also includes abbreviated names that can be used + * with nm_vpn_plugin_info_list_find_service_type(). + * + * Returns: (transfer-full): a %NULL terminated strv list of strings. + * The list itself and the values must be freed with g_strfreev(). + * + * Since: 1.4 + */ +char ** +nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations) +{ + GSList *iter; + GPtrArray *l; + guint i, j; + const char *n; + + l = g_ptr_array_sized_new (20); + for (iter = list; iter; iter = iter->next) { - char **aliases = (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data))->aliases; + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); - if (!aliases) - continue; - if (_nm_utils_strv_find_first (aliases, -1, service) >= 0) - return iter->data; + g_ptr_array_add (l, g_strdup (priv->service)); + if (priv->aliases) { + for (i = 0; priv->aliases[i]; i++) + g_ptr_array_add (l, g_strdup (priv->aliases[i])); + } + + if (with_abbreviations) { + g_ptr_array_add (l, g_strdup (priv->name)); + n = _service_type_get_default_abbreviation (priv->service); + if (n) + g_ptr_array_add (l, g_strdup (n)); + for (i = 0; priv->aliases[i]; i++) { + n = _service_type_get_default_abbreviation (priv->aliases[i]); + if (n) + g_ptr_array_add (l, g_strdup (n)); + } + } } - return NULL; + + if (!only_existing) { + for (i = 0; i < G_N_ELEMENTS (known_names); i++) { + g_ptr_array_add (l, g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, known_names[i])); + if (with_abbreviations) + g_ptr_array_add (l, g_strdup (known_names[i])); + } + } + + if (l->len <= 0) { + g_ptr_array_free (l, TRUE); + return g_new0 (char *, 1); + } + + /* sort the result and remove duplicates. */ + g_ptr_array_sort (l, nm_strcmp_p); + for (i = 1, j = 1; i < l->len; i++) { + if (nm_streq (l->pdata[j-1], l->pdata[i])) + g_free (l->pdata[i]); + else + l->pdata[j++] = l->pdata[i]; + } + + if (j == l->len) + g_ptr_array_add (l, NULL); + else + l->pdata[j] = NULL; + return (char **) g_ptr_array_free (l, FALSE); } /*********************************************************************/ @@ -768,6 +935,32 @@ nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self) /** + * nm_vpn_plugin_info_get_aliases: + * @self: plugin info instance + * + * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): + * the aliases from the name-file. + * + * Since: 1.4 + */ +const char *const* +nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self) +{ + NMVpnPluginInfoPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL); + + priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self); + if (priv->aliases) + return (const char *const*) priv->aliases; + + /* For convenience, we always want to return non-NULL, even for empty + * aliases. Hack around that, by making a NULL terminated array using + * the NULL of priv->aliases. */ + return (const char *const*) &priv->aliases; +} + +/** * nm_vpn_plugin_info_lookup_property: * @self: plugin info instance * @group: group name @@ -896,6 +1089,8 @@ nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error) NULL, NULL, error); + if (priv->editor_plugin) + nm_vpn_editor_plugin_set_plugin_info (priv->editor_plugin, self); return priv->editor_plugin; } @@ -1003,6 +1198,8 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) } priv->aliases = g_key_file_get_string_list (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "aliases", NULL, NULL); + if (priv->aliases && !priv->aliases[0]) + g_clear_pointer (&priv->aliases, g_free); priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash, _nm_utils_strstrdictkey_equal, diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h index b9ed83f1dc..908ee4827c 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -44,10 +44,12 @@ G_BEGIN_DECLS #define NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM "libnm" #define NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME "GNOME" +struct _NMVpnPluginInfo; + /** * NMVpnPluginInfo: */ -typedef struct { +typedef struct _NMVpnPluginInfo { NM_AVAILABLE_IN_1_2 GObject parent; } NMVpnPluginInfo; @@ -93,6 +95,8 @@ NM_AVAILABLE_IN_1_4 gboolean nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 gboolean nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self); +NM_AVAILABLE_IN_1_4 +const char *const*nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 const char *nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key); @@ -112,6 +116,12 @@ NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_filename (GSList *list, const c NM_AVAILABLE_IN_1_2 NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service); +NM_AVAILABLE_IN_1_4 +char *nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name); +NM_AVAILABLE_IN_1_4 +char **nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations); NM_AVAILABLE_IN_1_2 NMVpnEditorPlugin *nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 4bdef30186..41f650f008 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1068,9 +1068,15 @@ global: nm_device_team_get_config; nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; + nm_vpn_editor_plugin_get_plugin_info; + nm_vpn_editor_plugin_get_vt; nm_vpn_editor_plugin_load; + nm_vpn_editor_plugin_set_plugin_info; + nm_vpn_plugin_info_get_aliases; nm_vpn_plugin_info_get_auth_dialog; nm_vpn_plugin_info_get_service; + nm_vpn_plugin_info_list_get_service_types; + nm_vpn_plugin_info_list_find_service_type; nm_vpn_plugin_info_new_search_file; nm_vpn_plugin_info_supports_hints; } libnm_1_2_0; diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index bf4369e4ad..df99eb28d9 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -1306,3 +1306,11 @@ nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface) { iface->init = init_sync; } + +/*****************************************************************************/ + +/* this header is intended to be copied to users of nm_vpn_editor_plugin_call(), + * to simplify invocation of generic functions. Include it here, to complile + * the code. */ +#include "nm-vpn-editor-plugin-call.h" + diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 70e222c6a9..842f22a568 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -6,5 +6,6 @@ vpn-daemons/openvpn vpn-daemons/pptp vpn-daemons/vpnc contrib/fedora/rpm/ +shared/nm-vpn-editor-plugin-call.h # https://bugs.launchpad.net/intltool/+bug/1117944 sub/policy/org.freedesktop.NetworkManager.policy.in diff --git a/shared/Makefile.am b/shared/Makefile.am index c6ede00b6a..01818d0cd1 100644 --- a/shared/Makefile.am +++ b/shared/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = \ nm-test-libnm-utils.h \ nm-test-utils.h \ nm-test-utils-impl.c \ - nm-version-macros.h.in + nm-version-macros.h.in \ + nm-vpn-editor-plugin-call.h CLEANFILES=nm-version.h diff --git a/shared/nm-macros-internal.h b/shared/nm-macros-internal.h index 04e6a1ab6c..4622af9fd8 100644 --- a/shared/nm-macros-internal.h +++ b/shared/nm-macros-internal.h @@ -484,6 +484,36 @@ nm_strstrip (char *str) return str ? g_strstrip (str) : NULL; } +/* g_ptr_array_sort()'s compare function takes pointers to the + * value. Thus, you cannot use strcmp directly. You can use + * nm_strcmp_p(). + * + * Like strcmp(), this function is not forgiving to accept %NULL. */ +static inline int +nm_strcmp_p (gconstpointer a, gconstpointer b) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). + * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different + * signature. I guess, in glib there are knowledgeable people that ensure + * that this additional argument doesn't cause problems due to different ABI + * for every architecture that glib supports. + * For NetworkManager, we'd rather avoid such stunts. + **/ +static inline int +nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + /*****************************************************************************/ static inline guint diff --git a/shared/nm-vpn-editor-plugin-call.h b/shared/nm-vpn-editor-plugin-call.h new file mode 100644 index 0000000000..c6b7ee76ab --- /dev/null +++ b/shared/nm-vpn-editor-plugin-call.h @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NM_VPN_EDITOR_PLUGIN_CALL_H__ +#define __NM_VPN_EDITOR_PLUGIN_CALL_H__ + +/* This header is an internal, header-only file that can be copied to + * other projects to call well-known service functions on VPN plugins. + * + * This uses the NMVpnEditorPluginVT and allows a user (nm-applet) + * to directly communicate with a VPN plugin using API that is newer + * then the current libnm version. That is, it allows to call to a VPN + * plugin bypassing libnm. */ + +#include <NetworkManager.h> + +/* we make use of other internal header files, you need those too. */ +#include "gsystem-local-alloc.h" +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +/** + * NMVpnEditorPluginServiceFlags: + * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE: no flags + * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD: whether the plugin can + * add a new connection for the given service-type. + **/ +typedef enum { /*< skip >*/ + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE = 0x00, + NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01, +} NMVpnEditorPluginServiceFlags; + +struct _NMVpnEditorPluginVT { + gboolean (*fcn_get_service_info) (NMVpnEditorPlugin *plugin, + const char *service_type, + char **out_short_name, + char **out_pretty_name, + char **out_description, + NMVpnEditorPluginServiceFlags *out_flags); + char **(*fcn_get_service_add_details) (NMVpnEditorPlugin *plugin, + const char *service_name); + gboolean (*fcn_get_service_add_detail) (NMVpnEditorPlugin *plugin, + const char *service_type, + const char *add_detail, + char **out_pretty_name, + char **out_description, + char **out_add_detail_key, + char **out_add_detail_val, + guint *out_flags); +}; + +/***************************************************************************** + * Call + * + * The following wrap the calling of generic functions for a VPN plugin. + * They are used by callers (for example nm-connection-editor). + *****************************************************************************/ + +static inline gboolean +nm_vpn_editor_plugin_get_service_info (NMVpnEditorPlugin *plugin, + const char *service_type, + char **out_short_name, + char **out_pretty_name, + char **out_description, + NMVpnEditorPluginServiceFlags *out_flags) +{ + NMVpnEditorPluginVT vt; + gs_free char *short_name_local = NULL; + gs_free char *pretty_name_local = NULL; + gs_free char *description_local = NULL; + guint flags_local = 0; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), FALSE); + g_return_val_if_fail (service_type, FALSE); + + nm_vpn_editor_plugin_get_vt (plugin, &vt, sizeof (vt)); + if ( !vt.fcn_get_service_info + || !vt.fcn_get_service_info (plugin, + service_type, + out_short_name ? &short_name_local : NULL, + out_pretty_name ? &pretty_name_local : NULL, + out_description ? &description_local : NULL, + out_flags ? &flags_local : NULL)) + return FALSE; + NM_SET_OUT (out_short_name, g_steal_pointer (&short_name_local)); + NM_SET_OUT (out_pretty_name, g_steal_pointer (&pretty_name_local)); + NM_SET_OUT (out_description, g_steal_pointer (&description_local)); + NM_SET_OUT (out_flags, flags_local); + return TRUE; +} + +static inline char ** +nm_vpn_editor_plugin_get_service_add_details (NMVpnEditorPlugin *plugin, + const char *service_name) +{ + NMVpnEditorPluginVT vt; + char **details = NULL; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL); + g_return_val_if_fail (service_name, NULL); + + nm_vpn_editor_plugin_get_vt (plugin, &vt, sizeof (vt)); + if (vt.fcn_get_service_add_details) + details = vt.fcn_get_service_add_details (plugin, service_name); + if (!details) + return g_new0 (char *, 1); + return details; +} + +static inline gboolean +nm_vpn_editor_plugin_get_service_add_detail (NMVpnEditorPlugin *plugin, + const char *service_type, + const char *add_detail, + char **out_pretty_name, + char **out_description, + char **out_add_detail_key, + char **out_add_detail_val, + guint *out_flags) +{ + NMVpnEditorPluginVT vt; + gs_free char *pretty_name_local = NULL; + gs_free char *description_local = NULL; + gs_free char *add_detail_key_local = NULL; + gs_free char *add_detail_val_local = NULL; + guint flags_local = 0; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), FALSE); + g_return_val_if_fail (service_type, FALSE); + g_return_val_if_fail (add_detail, FALSE); + + nm_vpn_editor_plugin_get_vt (plugin, &vt, sizeof (vt)); + if ( !vt.fcn_get_service_add_detail + || !vt.fcn_get_service_add_detail (plugin, + service_type, + add_detail, + out_pretty_name ? &pretty_name_local : NULL, + out_description ? &description_local : NULL, + out_add_detail_key ? &add_detail_key_local : NULL, + out_add_detail_val ? &add_detail_val_local : NULL, + out_flags ? &flags_local : NULL)) + return FALSE; + NM_SET_OUT (out_pretty_name, g_steal_pointer (&pretty_name_local)); + NM_SET_OUT (out_description, g_steal_pointer (&description_local)); + NM_SET_OUT (out_add_detail_key, g_steal_pointer (&add_detail_key_local)); + NM_SET_OUT (out_add_detail_val, g_steal_pointer (&add_detail_val_local)); + NM_SET_OUT (out_flags, flags_local); + return TRUE; +} + +/***************************************************************************** + * Implementation + * + * The following glue code can be used to implement calls in a VPN plugin. + *****************************************************************************/ + +#define NM_VPN_EDITOR_PLUGIN_VT_DEFINE(vt_name, get_vt, ...) \ +static const NMVpnEditorPluginVT vt_name = { \ + __VA_ARGS__ \ + }; \ +static const NMVpnEditorPluginVT * \ +get_vt (NMVpnEditorPlugin *plugin, \ + gsize *out_vt_size) \ +{ \ + nm_assert (NM_IS_VPN_EDITOR_PLUGIN (plugin)); \ + nm_assert (out_vt_size); \ + \ + *out_vt_size = sizeof (vt_name); \ + return &vt_name; \ +} + +#endif /* __NM_VPN_EDITOR_PLUGIN_CALL_H__ */ diff --git a/src/nm-config.c b/src/nm-config.c index 67c0497810..4d0ebc1f8f 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -880,15 +880,6 @@ read_base_config (GKeyFile *keyfile, return TRUE; } -static int -sort_asciibetically (gconstpointer a, gconstpointer b) -{ - const char *s1 = *(const char **)a; - const char *s2 = *(const char **)b; - - return strcmp (s1, s2); -} - static GPtrArray * _get_config_dir_files (const char *config_dir) { @@ -917,7 +908,7 @@ _get_config_dir_files (const char *config_dir) } g_object_unref (dir); - g_ptr_array_sort (confs, sort_asciibetically); + g_ptr_array_sort (confs, nm_strcmp_p); return confs; } |