1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301 USA.
18 * Copyright 2015 Red Hat, Inc.
21 #include "nm-default.h"
23 #include "nm-vpn-plugin-info.h"
29 #include "nm-errors.h"
30 #include "nm-core-internal.h"
32 #define DEFAULT_DIR_ETC NMCONFDIR"/VPN"
33 #define DEFAULT_DIR_LIB NMLIBDIR"/VPN"
51 /* It is convenient for nm_vpn_plugin_info_lookup_property() to return a const char *,
52 * contrary to what g_key_file_get_string() does. Hence we must cache the returned
53 * value somewhere... let's put it in an internal hash table.
54 * This contains a clone of all the strings in keyfile. */
57 gboolean editor_plugin_loaded;
58 NMVpnEditorPlugin *editor_plugin;
59 } NMVpnPluginInfoPrivate;
61 static void nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface);
63 G_DEFINE_TYPE_WITH_CODE (NMVpnPluginInfo, nm_vpn_plugin_info, G_TYPE_OBJECT,
64 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_plugin_info_initable_iface_init);
67 #define NM_VPN_PLUGIN_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfoPrivate))
69 /*********************************************************************/
72 * nm_vpn_plugin_info_validate_filename:
73 * @filename: the filename to check
75 * Regular name files have a certain pattern. That basically means
76 * they have the file extension "name". Check if @filename
77 * is valid according to that pattern.
82 nm_vpn_plugin_info_validate_filename (const char *filename)
84 if (!filename || !g_str_has_suffix (filename, ".name"))
87 /* originally, we didn't do further checks... but here we go. */
88 if (filename[0] == '.') {
89 /* this also rejects name ".name" alone. */
96 nm_vpn_plugin_info_check_file_full (const char *filename,
97 gboolean check_absolute,
98 gboolean do_validate_filename,
100 NMUtilsCheckFilePredicate check_file,
105 if (!filename || !*filename) {
108 NM_VPN_PLUGIN_ERROR_FAILED,
109 _("missing filename"));
113 if (check_absolute && !g_path_is_absolute (filename)) {
116 NM_VPN_PLUGIN_ERROR_FAILED,
117 _("filename must be an absolute path (%s)"), filename);
121 if ( do_validate_filename
122 && !nm_vpn_plugin_info_validate_filename (filename)) {
125 NM_VPN_PLUGIN_ERROR_FAILED,
126 _("filename has invalid format (%s)"), filename);
130 return _nm_utils_check_file (filename,
139 * _nm_vpn_plugin_info_check_file:
140 * @filename: the file to check
141 * @check_absolute: if %TRUE, only allow absolute path names.
142 * @do_validate_filename: if %TRUE, only accept the filename if
143 * nm_vpn_plugin_info_validate_filename() succeeds.
144 * @check_owner: if non-negative, only accept the file if the
145 * owner UID is equal to @check_owner or if the owner is 0.
146 * In this case, also check that the file is not writable by
148 * @check_file: pass a callback to do your own validation.
149 * @user_data: user data for @check_file.
150 * @error: (allow-none): (out): the error reason if the check fails.
152 * Check whether the file exists and is a valid name file (in keyfile format).
153 * Additionally, also check for file permissions.
155 * Returns: %TRUE if a file @filename exists and has valid permissions.
160 _nm_vpn_plugin_info_check_file (const char *filename,
161 gboolean check_absolute,
162 gboolean do_validate_filename,
164 NMUtilsCheckFilePredicate check_file,
168 return nm_vpn_plugin_info_check_file_full (filename, check_absolute, do_validate_filename, check_owner, check_file, user_data, NULL, error);
172 NMVpnPluginInfo *plugin_info;
177 _sort_files (LoadDirInfo *a, LoadDirInfo *b)
181 ta = MAX (a->stat.st_mtime, a->stat.st_ctime);
182 tb = MAX (b->stat.st_mtime, b->stat.st_ctime);
187 return g_strcmp0 (nm_vpn_plugin_info_get_filename (a->plugin_info),
188 nm_vpn_plugin_info_get_filename (b->plugin_info));
192 * _nm_vpn_plugin_info_get_default_dir_etc:
194 * Returns: (transfer none): compile time constant of the default
195 * VPN plugin directory.
198 _nm_vpn_plugin_info_get_default_dir_etc ()
200 return DEFAULT_DIR_ETC;
204 * _nm_vpn_plugin_info_get_default_dir_lib:
206 * Returns: (transfer none): compile time constant of the default
207 * VPN plugin directory.
210 _nm_vpn_plugin_info_get_default_dir_lib ()
212 return DEFAULT_DIR_LIB;
216 * _nm_vpn_plugin_info_get_default_dir_user:
218 * Returns: The user can specify a different directory for VPN plugins
219 * by setting NM_VPN_PLUGIN_DIR environment variable. Return
223 _nm_vpn_plugin_info_get_default_dir_user ()
225 return g_getenv ("NM_VPN_PLUGIN_DIR");
229 * _nm_vpn_plugin_info_list_load_dir:
230 * @dirname: the name of the directory to load.
231 * @do_validate_filename: only consider filenames that have a certain
232 * pattern (i.e. end with ".name").
233 * @check_owner: if set to a non-negative number, check that the file
234 * owner is either the same uid or 0. In that case, also check
235 * that the file is not writable by group or other.
236 * @check_file: (allow-none): callback to check whether the file is valid.
237 * @user_data: data for @check_file
239 * Iterate over the content of @dirname and load name files.
241 * Returns: (transfer full) (element-type NMVpnPluginInfo): list of loaded plugin infos.
244 _nm_vpn_plugin_info_list_load_dir (const char *dirname,
245 gboolean do_validate_filename,
247 NMUtilsCheckFilePredicate check_file,
256 g_return_val_if_fail (dirname && dirname[0], NULL);
258 dir = g_dir_open (dirname, 0, NULL);
262 array = g_array_new (FALSE, FALSE, sizeof (LoadDirInfo));
264 while ((fn = g_dir_read_name (dir))) {
265 gs_free char *filename = NULL;
266 LoadDirInfo info = { 0 };
268 filename = g_build_filename (dirname, fn, NULL);
269 if (nm_vpn_plugin_info_check_file_full (filename,
271 do_validate_filename,
277 info.plugin_info = nm_vpn_plugin_info_new_from_file (filename, NULL);
278 if (info.plugin_info) {
279 g_array_append_val (array, info);
286 /* sort the files so that we have a stable behavior. The directory might contain
287 * duplicate VPNs, so while nm_vpn_plugin_info_list_load() would load them all, the
288 * caller probably wants to reject duplicates. Having a stable order means we always
289 * reject the same files in face of duplicates. */
290 g_array_sort (array, (GCompareFunc) _sort_files);
292 for (i = 0; i < array->len; i++)
293 res = g_slist_prepend (res, g_array_index (array, LoadDirInfo, i).plugin_info);
295 g_array_unref (array);
297 return g_slist_reverse (res);
301 * nm_vpn_plugin_info_list_load:
303 * Returns: (element-type NMVpnPluginInfo) (transfer full): list of plugins
304 * loaded from the default directories rejecting duplicates.
309 nm_vpn_plugin_info_list_load ()
314 GSList *infos, *info;
315 const char *dir[] = {
316 /* We load plugins from NM_VPN_PLUGIN_DIR *and* DEFAULT_DIR*, with
317 * preference to the former.
319 * load user directory with highest priority. */
320 _nm_vpn_plugin_info_get_default_dir_user (),
322 /* lib directory has higher priority then etc. The reason is that
323 * etc is deprecated and used by old plugins. We expect newer plugins
324 * to install their file in lib, where they have higher priority.
326 * Optimally, there are no duplicates anyway, so it doesn't really matter. */
327 _nm_vpn_plugin_info_get_default_dir_lib (),
328 _nm_vpn_plugin_info_get_default_dir_etc (),
333 for (i = 0; i < G_N_ELEMENTS (dir); i++) {
335 || _nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0)
338 infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL);
340 for (info = infos; info; info = info->next)
341 nm_vpn_plugin_info_list_add (&list, info->data, NULL);
343 g_slist_free_full (infos, g_object_unref);
348 /*********************************************************************/
351 _check_no_conflict (NMVpnPluginInfo *i1, NMVpnPluginInfo *i2, GError **error)
353 NMVpnPluginInfoPrivate *priv1, *priv2;
359 { NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service" },
360 { NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin" },
361 { NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties" },
364 priv1 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i1);
365 priv2 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i2);
367 for (i = 0; i < G_N_ELEMENTS (check_list); i++) {
368 gs_free NMUtilsStrStrDictKey *k = NULL;
371 k = _nm_utils_strstrdictkey_create (check_list[i].group, check_list[i].key);
372 s1 = g_hash_table_lookup (priv1->keys, k);
375 s2 = g_hash_table_lookup (priv2->keys, k);
379 if (strcmp (s1, s2) == 0) {
382 NM_VPN_PLUGIN_ERROR_FAILED,
383 _("there exists a conflicting plugin (%s) that has the same %s.%s value"),
385 check_list[i].group, check_list[i].key);
393 * nm_vpn_plugin_info_list_add:
394 * @list: (element-type NMVpnPluginInfo): list of plugins
395 * @plugin_info: instance to add
396 * @error: failure reason
398 * Returns: %TRUE if the plugin was added to @list. This will fail
399 * to add duplicate plugins.
404 nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError **error)
409 g_return_val_if_fail (list, FALSE);
410 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
412 name = nm_vpn_plugin_info_get_name (plugin_info);
413 for (iter = *list; iter; iter = iter->next) {
414 if (iter->data == plugin_info)
417 if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0) {
420 NM_VPN_PLUGIN_ERROR_FAILED,
421 _("there exists a conflicting plugin with the same name (%s)"),
426 /* the plugin must have unique values for certain properties. E.g. two different
427 * plugins cannot share the same service name. */
428 if (!_check_no_conflict (plugin_info, iter->data, error))
432 *list = g_slist_append (*list, g_object_ref (plugin_info));
437 * nm_vpn_plugin_info_list_remove:
438 * @list: (element-type NMVpnPluginInfo): list of plugins
439 * @plugin_info: instance
441 * Remove @plugin_info from @list.
443 * Returns: %TRUE if @plugin_info was in @list and successfully removed.
448 nm_vpn_plugin_info_list_remove (GSList **list, NMVpnPluginInfo *plugin_info)
450 g_return_val_if_fail (list, FALSE);
451 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
453 if (!g_slist_find (*list, plugin_info))
456 *list = g_slist_remove (*list, plugin_info);
457 g_object_unref (plugin_info);
462 * nm_vpn_plugin_info_list_find_by_name:
463 * @list: (element-type NMVpnPluginInfo): list of plugins
464 * @name: name to search
466 * Returns: (transfer none): the first plugin with a matching @name (or %NULL).
471 nm_vpn_plugin_info_list_find_by_name (GSList *list, const char *name)
476 g_return_val_if_reached (NULL);
478 for (iter = list; iter; iter = iter->next) {
479 if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0)
486 * nm_vpn_plugin_info_list_find_by_filename:
487 * @list: (element-type NMVpnPluginInfo): list of plugins
488 * @filename: filename to search
490 * Returns: (transfer none): the first plugin with a matching @filename (or %NULL).
495 nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename)
500 g_return_val_if_reached (NULL);
502 for (iter = list; iter; iter = iter->next) {
503 if (g_strcmp0 (nm_vpn_plugin_info_get_filename (iter->data), filename) == 0)
510 * nm_vpn_plugin_info_list_find_by_service:
511 * @list: (element-type NMVpnPluginInfo): list of plugins
512 * @service: service to search
514 * Returns: (transfer none): the first plugin with a matching @service (or %NULL).
519 nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service)
524 g_return_val_if_reached (NULL);
526 /* First, consider the primary service name. */
527 for (iter = list; iter; iter = iter->next) {
528 if (strcmp (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data)->service, service) == 0)
532 /* Then look into the aliases. */
533 for (iter = list; iter; iter = iter->next) {
534 char **aliases = (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data))->aliases;
538 if (_nm_utils_strv_find_first (aliases, -1, service) >= 0)
544 /*********************************************************************/
547 * nm_vpn_plugin_info_get_filename:
548 * @self: plugin info instance
550 * Returns: (transfer none): the filename. Can be %NULL.
555 nm_vpn_plugin_info_get_filename (NMVpnPluginInfo *self)
557 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
559 return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->filename;
563 * nm_vpn_plugin_info_get_name:
564 * @self: plugin info instance
566 * Returns: (transfer none): the name. Cannot be %NULL.
571 nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self)
573 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
575 return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->name;
579 * nm_vpn_plugin_info_get_plugin:
580 * @self: plugin info instance
582 * Returns: (transfer none): the plugin. Can be %NULL.
587 nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self)
589 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
591 return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
592 _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin"));
596 * nm_vpn_plugin_info_get_program:
597 * @self: plugin info instance
599 * Returns: (transfer none): the program. Can be %NULL.
604 nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self)
606 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
608 return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
609 _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "program"));
613 * nm_vpn_plugin_info_supports_multiple:
614 * @self: plugin info instance
616 * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE
621 nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self)
623 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE);
625 return g_key_file_get_boolean (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keyfile,
626 NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION,
627 "supports-multiple-connections",
633 * nm_vpn_plugin_info_lookup_property:
634 * @self: plugin info instance
636 * @key: name of the property
638 * Returns: (transfer none): #NMVpnPluginInfo is internally a #GKeyFile. Returns the matching
644 nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key)
646 NMVpnPluginInfoPrivate *priv;
647 gs_free NMUtilsStrStrDictKey *k = NULL;
649 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
650 g_return_val_if_fail (group, NULL);
651 g_return_val_if_fail (key, NULL);
653 priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
655 k = _nm_utils_strstrdictkey_create (group, key);
656 return g_hash_table_lookup (priv->keys, k);
659 /*********************************************************************/
662 * nm_vpn_plugin_info_get_editor_plugin:
663 * @self: plugin info instance
665 * Returns: (transfer none): the cached #NMVpnEditorPlugin instance.
670 nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self)
672 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
674 return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->editor_plugin;
678 * nm_vpn_plugin_info_set_editor_plugin:
679 * @self: plugin info instance
680 * @plugin: (allow-none): plugin instance
682 * Set the internal plugin instance. If %NULL, only clear the previous instance.
687 nm_vpn_plugin_info_set_editor_plugin (NMVpnPluginInfo *self, NMVpnEditorPlugin *plugin)
689 NMVpnPluginInfoPrivate *priv;
690 NMVpnEditorPlugin *old;
692 g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (self));
693 g_return_if_fail (!plugin || G_IS_OBJECT (plugin));
695 priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
698 priv->editor_plugin_loaded = FALSE;
699 g_clear_object (&priv->editor_plugin);
701 old = priv->editor_plugin;
702 priv->editor_plugin = g_object_ref (plugin);
703 priv->editor_plugin_loaded = TRUE;
705 g_object_unref (old);
710 * nm_vpn_plugin_info_load_editor_plugin:
711 * @self: plugin info instance
712 * @error: error reason on failure
714 * Returns: (transfer none): loads the plugin and returns the newly created
715 * instance. The plugin is owned by @self and can be later retrieved again
716 * via nm_vpn_plugin_info_get_editor_plugin(). You can load the
717 * plugin only once, unless you reset the state via
718 * nm_vpn_plugin_info_set_editor_plugin().
723 nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error)
725 NMVpnPluginInfoPrivate *priv;
726 const char *plugin_filename;
728 g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
730 priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
732 if (priv->editor_plugin)
733 return priv->editor_plugin;
735 plugin_filename = nm_vpn_plugin_info_get_plugin (self);
736 if (!plugin_filename || !*plugin_filename) {
739 NM_VPN_PLUGIN_ERROR_FAILED,
740 _("missing \"plugin\" setting"));
744 /* We only try once to load the plugin. If we previously tried and it was
745 * unsuccessful, error out immediately. */
746 if (priv->editor_plugin_loaded) {
749 NM_VPN_PLUGIN_ERROR_FAILED,
750 _("%s: don't retry loading plugin which already failed previously"), priv->name);
754 priv->editor_plugin_loaded = TRUE;
755 priv->editor_plugin = nm_vpn_editor_plugin_load_from_file (plugin_filename,
761 return priv->editor_plugin;
764 /*********************************************************************/
767 * nm_vpn_plugin_info_new_from_file:
768 * @filename: filename to read.
769 * @error: on failure, the error reason.
771 * Read the plugin info from file @filename. Does not do
772 * any further verification on the file. You might want to check
773 * file permissions and ownership of the file.
775 * Returns: %NULL if there is any error or a newly created
776 * #NMVpnPluginInfo instance.
781 nm_vpn_plugin_info_new_from_file (const char *filename,
784 g_return_val_if_fail (filename, NULL);
786 return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
789 NM_VPN_PLUGIN_INFO_FILENAME, filename,
794 * nm_vpn_plugin_info_new_with_data:
795 * @filename: optional filename.
796 * @keyfile: inject data for the plugin info instance.
797 * @error: construction may fail if the keyfile lacks mandatory fields.
798 * In this case, return the error reason.
800 * This constructor does not read any data from file but
801 * takes instead a @keyfile argument.
803 * Returns: new plugin info instance.
808 nm_vpn_plugin_info_new_with_data (const char *filename,
812 g_return_val_if_fail (keyfile, NULL);
814 return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
817 NM_VPN_PLUGIN_INFO_FILENAME, filename,
818 NM_VPN_PLUGIN_INFO_KEYFILE, keyfile,
822 /*********************************************************************/
825 nm_vpn_plugin_info_init (NMVpnPluginInfo *plugin)
830 init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
832 NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (initable);
833 NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
834 gs_strfreev char **groups = NULL;
837 if (!priv->keyfile) {
838 if (!priv->filename) {
839 g_set_error_literal (error,
841 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
842 _("missing filename to load VPN plugin info"));
845 priv->keyfile = g_key_file_new ();
846 if (!g_key_file_load_from_file (priv->keyfile, priv->filename, G_KEY_FILE_NONE, error))
850 /* we reqire at least a "name" */
851 priv->name = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "name", NULL);
852 if (!priv->name || !priv->name[0]) {
853 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
854 _("missing name for VPN plugin info"));
858 /* we also require "service", because that how we associate NMSettingVpn:service-type with the
859 * NMVpnPluginInfo. */
860 priv->service = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service", NULL);
861 if (!priv->service || !*priv->service) {
862 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
863 _("missing service for VPN plugin info"));
867 priv->aliases = g_key_file_get_string_list (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "aliases", NULL, NULL);
869 priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash,
870 _nm_utils_strstrdictkey_equal,
872 groups = g_key_file_get_groups (priv->keyfile, NULL);
873 for (i = 0; groups && groups[i]; i++) {
874 gs_strfreev char **keys = NULL;
876 keys = g_key_file_get_keys (priv->keyfile, groups[i], NULL, NULL);
877 for (j = 0; keys && keys[j]; j++) {
880 /* Lookup the value via get_string(). We want that behavior.
881 * You could still lookup the original values via g_key_file_get_value()
882 * based on priv->keyfile. */
883 s = g_key_file_get_string (priv->keyfile, groups[i], keys[j], NULL);
885 g_hash_table_insert (priv->keys, _nm_utils_strstrdictkey_create (groups[i], keys[j]), s);
893 set_property (GObject *object, guint prop_id,
894 const GValue *value, GParamSpec *pspec)
896 NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
900 priv->filename = g_value_dup_string (value);
903 priv->keyfile = g_value_dup_boxed (value);
906 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
912 get_property (GObject *object, guint prop_id,
913 GValue *value, GParamSpec *pspec)
915 NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
919 g_value_set_string (value, priv->name);
922 g_value_set_string (value, priv->filename);
925 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
931 dispose (GObject *object)
933 NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
934 NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
936 g_clear_object (&priv->editor_plugin);
938 G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->dispose (object);
942 finalize (GObject *object)
944 NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
945 NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
948 g_free (priv->service);
949 g_strfreev (priv->aliases);
950 g_free (priv->filename);
951 g_key_file_unref (priv->keyfile);
952 g_hash_table_unref (priv->keys);
954 G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->finalize (object);
958 nm_vpn_plugin_info_class_init (NMVpnPluginInfoClass *plugin_class)
960 GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
962 g_type_class_add_private (object_class, sizeof (NMVpnPluginInfoPrivate));
964 /* virtual methods */
965 object_class->set_property = set_property;
966 object_class->get_property = get_property;
967 object_class->dispose = dispose;
968 object_class->finalize = finalize;
973 * NMVpnPluginInfo:name:
975 * The name of the VPN plugin.
979 g_object_class_install_property
980 (object_class, PROP_NAME,
981 g_param_spec_string (NM_VPN_PLUGIN_INFO_NAME, "", "",
984 G_PARAM_STATIC_STRINGS));
987 * NMVpnPluginInfo:filename:
989 * The filename from which the info was loaded.
990 * Can be %NULL if the instance was not loaded from
991 * a file (i.e. the keyfile instance was passed to the
996 g_object_class_install_property
997 (object_class, PROP_FILENAME,
998 g_param_spec_string (NM_VPN_PLUGIN_INFO_FILENAME, "", "",
1001 G_PARAM_CONSTRUCT_ONLY |
1002 G_PARAM_STATIC_STRINGS));
1005 * NMVpnPluginInfo:keyfile:
1007 * Initalize the instance with a different keyfile instance.
1008 * When passing a keyfile instance, the constructor will not
1009 * try to read from filename.
1013 g_object_class_install_property
1014 (object_class, PROP_KEYFILE,
1015 g_param_spec_boxed (NM_VPN_PLUGIN_INFO_KEYFILE, "", "",
1018 G_PARAM_CONSTRUCT_ONLY |
1019 G_PARAM_STATIC_STRINGS));
1023 nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface)
1025 iface->init = init_sync;