summary refs log tree commit diff
path: root/libnm-core/nm-vpn-plugin-info.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /libnm-core/nm-vpn-plugin-info.c
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'libnm-core/nm-vpn-plugin-info.c')
-rw-r--r--libnm-core/nm-vpn-plugin-info.c1354
1 files changed, 0 insertions, 1354 deletions
diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c
deleted file mode 100644
index cb739510..00000000
--- a/libnm-core/nm-vpn-plugin-info.c
+++ /dev/null
@@ -1,1354 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2015 Red Hat, Inc.
- */
-
-#include "libnm-core/nm-default-libnm-core.h"
-
-#include "nm-vpn-plugin-info.h"
-
-#include <sys/stat.h>
-
-#include "nm-errors.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;
-    char *    auth_dialog;
-    char **   aliases;
-    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;
-
-/**
- * NMVpnPluginInfo:
- */
-struct _NMVpnPluginInfo {
-    GObject                parent;
-    NMVpnPluginInfoPrivate _priv;
-};
-
-struct _NMVpnPluginInfoClass {
-    GObjectClass parent;
-};
-
-#define NM_VPN_PLUGIN_INFO_GET_PRIVATE(self) \
-    _NM_GET_PRIVATE(self, NMVpnPluginInfo, NM_IS_VPN_PLUGIN_INFO)
-
-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);)
-
-/*****************************************************************************/
-
-static NMVpnPluginInfo *_list_find_by_service(GSList *list, const char *name, const char *service);
-
-/*****************************************************************************/
-
-/**
- * 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: the file to check
- * @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 nm_str_not_empty(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) (element-type NMVpnPluginInfo): 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, NULL);
-
-    if (!dirname[0])
-        return 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: (element-type NMVpnPluginInfo) (transfer 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 *const 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;
-}
-
-/**
- * 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)
-{
-    NMVpnPluginInfo *info;
-    GSList *         infos;
-
-    if (!name && !service)
-        g_return_val_if_reached(NULL);
-
-    infos = nm_vpn_plugin_info_list_load();
-    info  = nm_g_object_ref(_list_find_by_service(infos, name, service));
-    g_slist_free_full(infos, g_object_unref);
-    return info;
-}
-
-/*****************************************************************************/
-
-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: (element-type NMVpnPluginInfo): 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 service type. */
-        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: (element-type NMVpnPluginInfo): 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)
-{
-    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: (element-type NMVpnPluginInfo): list of plugins
- * @name: name to search
- *
- * Returns: (transfer none): 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: (element-type NMVpnPluginInfo): list of plugins
- * @filename: filename to search
- *
- * Returns: (transfer none): 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;
-}
-
-static NMVpnPluginInfo *
-_list_find_by_service(GSList *list, const char *name, const char *service)
-{
-    for (; list; list = list->next) {
-        NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE(list->data);
-
-        if (name && !nm_streq(name, priv->name))
-            continue;
-        if (service && !nm_streq(priv->service, service)
-            && (nm_utils_strv_find_first(priv->aliases, -1, service) < 0))
-            continue;
-
-        return list->data;
-    }
-    return NULL;
-}
-
-/**
- * nm_vpn_plugin_info_list_find_by_service:
- * @list: (element-type NMVpnPluginInfo): list of plugins
- * @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).
- *
- * Since: 1.2
- */
-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, NULL, 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)
-{
-    NMVpnPluginInfo *info;
-    char *           n;
-
-    if (!name)
-        g_return_val_if_reached(NULL);
-    if (!*name)
-        return NULL;
-
-    /* First, try to interpret @name as a full service-type (or alias). */
-    info = _list_find_by_service(list, NULL, name);
-    if (info)
-        return g_strdup(name);
-
-    /* try to interpret @name as plugin name, in which case we return
-     * the main service-type (not an alias). */
-    info = _list_find_by_service(list, name, NULL);
-    if (info)
-        return g_strdup(NM_VPN_PLUGIN_INFO_GET_PRIVATE(info)->service);
-
-    /* check the hard-coded list of short-names. They all 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, NULL, 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) {
-        NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE(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 && priv->aliases[i]; i++) {
-                n = _service_type_get_default_abbreviation(priv->aliases[i]);
-                if (n)
-                    g_ptr_array_add(l, g_strdup(n));
-            }
-        }
-    }
-
-    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);
-}
-
-/*****************************************************************************/
-
-/**
- * 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.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
- *
- * 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_supports_multiple:
- * @self: plugin info instance
- *
- * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE
- *
- * Since: 1.2
- */
-gboolean
-nm_vpn_plugin_info_supports_multiple(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_CONNECTION,
-                                           "supports-multiple-connections");
-    return _nm_utils_ascii_str_to_bool(s, FALSE);
-}
-
-/**
- * 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
- * @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: (transfer none): 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: (transfer none): 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,
-                                                              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;
-}
-
-/*****************************************************************************/
-
-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
-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
-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->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])
-        nm_clear_g_free(&priv->aliases);
-
-    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 for all our
-             * values. */
-            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);
-        }
-    }
-
-    nm_clear_pointer(&priv->keyfile, g_key_file_unref);
-
-    return TRUE;
-}
-
-/**
- * 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
-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->auth_dialog);
-    g_strfreev(priv->aliases);
-    g_free(priv->filename);
-    g_hash_table_unref(priv->keys);
-
-    nm_clear_pointer(&priv->keyfile, g_key_file_unref);
-
-    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);
-
-    object_class->set_property = set_property;
-    object_class->get_property = get_property;
-    object_class->dispose      = dispose;
-    object_class->finalize     = finalize;
-
-    /**
-     * 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:
-     *
-     * Initialize 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;
-}