diff options
| author | Michael Biebl <biebl@debian.org> | 2016-08-26 02:18:32 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2016-08-26 02:18:32 +0200 |
| commit | 7514efc2f38c9ace4557d4e69d68e7d380389030 (patch) | |
| tree | 7fb00fda86cfcc2ca377f191633a7cfdbfea7ca3 /libnm-core/nm-vpn-plugin-info.c | |
| parent | d6201f5d8daada3d64a0a3e0038e14eebec683ce (diff) | |
Imported Upstream version 1.4.0 upstream/1.4.0
Diffstat (limited to 'libnm-core/nm-vpn-plugin-info.c')
| -rw-r--r-- | libnm-core/nm-vpn-plugin-info.c | 396 |
1 files changed, 366 insertions, 30 deletions
diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c index 2e6275e3..a4727619 100644 --- a/libnm-core/nm-vpn-plugin-info.c +++ b/libnm-core/nm-vpn-plugin-info.c @@ -45,6 +45,7 @@ typedef struct { char *filename; char *name; char *service; + char *auth_dialog; char **aliases; GKeyFile *keyfile; @@ -188,6 +189,23 @@ _sort_files (LoadDirInfo *a, LoadDirInfo *b) nm_vpn_plugin_info_get_filename (b->plugin_info)); } +#define DEFINE_DEFAULT_DIR_LIST(dir) \ + 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 (), \ + } + /** * _nm_vpn_plugin_info_get_default_dir_etc: * @@ -253,7 +271,10 @@ _nm_vpn_plugin_info_list_load_dir (const char *dirname, GSList *res = NULL; guint i; - g_return_val_if_fail (dirname && dirname[0], NULL); + g_return_val_if_fail (dirname, NULL); + + if (!dirname[0]) + return NULL; dir = g_dir_open (dirname, 0, NULL); if (!dir) @@ -312,21 +333,7 @@ nm_vpn_plugin_info_list_load () 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 (), - }; + DEFINE_DEFAULT_DIR_LIST (dir); uid = getuid (); @@ -345,6 +352,66 @@ nm_vpn_plugin_info_list_load () return list; } +/** + * nm_vpn_plugin_info_new_search_file: + * @name: (allow-none): the name to search for. Either @name or @service + * must be present. + * @service: (allow-none): the service to search for. Either @name or + * @service must be present. + * + * This has the same effect as doing a full nm_vpn_plugin_info_list_load() + * followed by a search for the first matching VPN plugin info that has the + * given @name and/or @service. + * + * Returns: (transfer full): a newly created instance of plugin info + * or %NULL if no matching value was found. + * + * Since: 1.4 + */ +NMVpnPluginInfo * +nm_vpn_plugin_info_new_search_file (const char *name, const char *service) +{ + int i; + gint64 uid; + NMVpnPluginInfo *plugin_info = NULL; + GSList *infos, *info; + DEFINE_DEFAULT_DIR_LIST (dir); + + if (!name && !service) + g_return_val_if_reached (NULL); + + uid = getuid (); + + for (i = 0; !plugin_info && i < G_N_ELEMENTS (dir); i++) { + if ( !dir[i] + || _nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0) + continue; + + /* We still must load the entire directory while searching for the matching + * plugin-info. The reason is that reading the directory has no stable + * order and we can only sort them after reading the entire directory -- + * which _nm_vpn_plugin_info_list_load_dir() does. */ + infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL); + + for (info = infos; info; info = info->next) { + NMVpnPluginInfo *p = info->data; + + if (name && !nm_streq (nm_vpn_plugin_info_get_name (p), name)) + continue; + if ( service + && !nm_streq (nm_vpn_plugin_info_get_service (p), service) + && (_nm_utils_strv_find_first (NM_VPN_PLUGIN_INFO_GET_PRIVATE (p)->aliases, + -1, service) < 0)) + continue; + plugin_info = g_object_ref (p); + break; + } + + g_slist_free_full (infos, g_object_unref); + } + return plugin_info; +} + /*********************************************************************/ static gboolean @@ -424,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; } @@ -506,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). * @@ -518,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: (element-type NMVpnPluginInfo): 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, try to interpret @name as a full service-type (or alias). */ + if (_list_find_by_service (list, name)) + return g_strdup (name); - /* First, consider the primary service 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_PRIVATE (iter->data)->service, 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: (element-type NMVpnPluginInfo): 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); } /*********************************************************************/ @@ -576,6 +810,77 @@ nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self) } /** + * nm_vpn_plugin_info_get_service: + * @self: plugin info instance + * + * Returns: (transfer none): the service. Cannot be %NULL. + * + * Since: 1.4 + */ +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_auth_dialog: + * @self: plugin info instance + * + * Returns: the absolute path to the auth-dialog helper or %NULL. + * + * Since: 1.4 + **/ +const char * +nm_vpn_plugin_info_get_auth_dialog (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 (G_UNLIKELY (priv->auth_dialog == NULL)) { + const char *s; + + s = g_hash_table_lookup (priv->keys, _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "auth-dialog")); + if (!s || !s[0]) + priv->auth_dialog = g_strdup (""); + else if (g_path_is_absolute (s)) + priv->auth_dialog = g_strdup (s); + else { + /* for relative paths, we take the basename and assume it's in LIBEXECDIR. */ + gs_free char *prog_basename = g_path_get_basename (s); + + priv->auth_dialog = g_build_filename (LIBEXECDIR, prog_basename, NULL); + } + } + + return priv->auth_dialog[0] ? priv->auth_dialog : NULL; +} + +/** + * nm_vpn_plugin_info_supports_hints: + * @self: plugin info instance + * + * Returns: %TRUE if the supports hints for secret requests, otherwise %FALSE + * + * Since: 1.4 + */ +gboolean +nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self) +{ + const char *s; + + g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE); + + s = nm_vpn_plugin_info_lookup_property (self, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "supports-hints"); + return _nm_utils_ascii_str_to_bool (s, FALSE); +} + +/** * nm_vpn_plugin_info_get_plugin: * @self: plugin info instance * @@ -630,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 @@ -753,11 +1084,13 @@ nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error) priv->editor_plugin_loaded = TRUE; priv->editor_plugin = nm_vpn_editor_plugin_load_from_file (plugin_filename, - priv->service, + nm_vpn_plugin_info_get_service (self), getuid (), NULL, NULL, error); + if (priv->editor_plugin) + nm_vpn_editor_plugin_set_plugin_info (priv->editor_plugin, self); return priv->editor_plugin; } @@ -865,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, @@ -947,6 +1282,7 @@ finalize (GObject *object) g_free (priv->name); g_free (priv->service); + g_free (priv->auth_dialog); g_strfreev (priv->aliases); g_free (priv->filename); g_hash_table_unref (priv->keys); |