From 10661abe174862c71603cb385e20fee5a6671997 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 11 Aug 2008 17:13:22 +0000 Subject: [PATCH] 2008-08-11 Dan Williams Merge the vpn-properties setting with the vpn setting since it was pointless to keep both of them around. Convert the vpn 'data' hash table to a hash of string:string (instead of string:variant) so that system settings plugins can have an easier time dealing with the arbitrary key/value pairs. git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@3923 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- libnm-util/Makefile.am | 2 - libnm-util/nm-connection.c | 6 - libnm-util/nm-setting-vpn-properties.c | 203 ----------------------- libnm-util/nm-setting-vpn-properties.h | 80 --------- libnm-util/nm-setting-vpn.c | 42 ++++- libnm-util/nm-setting-vpn.h | 15 ++ src/vpn-manager/nm-vpn-connection.c | 17 +- system-settings/plugins/keyfile/reader.c | 32 +++- system-settings/plugins/keyfile/writer.c | 42 ++++- 9 files changed, 132 insertions(+), 307 deletions(-) delete mode 100644 libnm-util/nm-setting-vpn-properties.c delete mode 100644 libnm-util/nm-setting-vpn-properties.h diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index a406e9fc3e..21fd6b10a4 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -24,7 +24,6 @@ libnm_util_include_HEADERS = \ nm-setting-wireless.h \ nm-setting-wireless-security.h \ nm-setting-vpn.h \ - nm-setting-vpn-properties.h \ nm-utils.h libnm_util_la_SOURCES= \ @@ -47,7 +46,6 @@ libnm_util_la_SOURCES= \ nm-setting-wireless.c \ nm-setting-wireless-security.c \ nm-setting-vpn.c \ - nm-setting-vpn-properties.c \ nm-utils.c \ $(libnm_util_include_HEADERS) diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index f8e7a71d62..3064e3488d 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -39,7 +39,6 @@ #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-setting-vpn.h" -#include "nm-setting-vpn-properties.h" #include "nm-setting-serial.h" #include "nm-setting-gsm.h" @@ -197,11 +196,6 @@ register_default_settings (void) NM_SETTING_VPN_ERROR, 4); - register_one_setting (NM_SETTING_VPN_PROPERTIES_SETTING_NAME, - NM_TYPE_SETTING_VPN_PROPERTIES, - NM_SETTING_VPN_PROPERTIES_ERROR, - 5); - register_one_setting (NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_ERROR, diff --git a/libnm-util/nm-setting-vpn-properties.c b/libnm-util/nm-setting-vpn-properties.c deleted file mode 100644 index c7bf5bcc8f..0000000000 --- a/libnm-util/nm-setting-vpn-properties.c +++ /dev/null @@ -1,203 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* - * Dan Williams - * Tambet Ingo - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2007 - 2008 Red Hat, Inc. - * (C) Copyright 2007 - 2008 Novell, Inc. - */ - -#include -#include "nm-setting-vpn-properties.h" -#include "nm-param-spec-specialized.h" -#include "nm-dbus-glib-types.h" - -GQuark -nm_setting_vpn_properties_error_quark (void) -{ - static GQuark quark; - - if (G_UNLIKELY (!quark)) - quark = g_quark_from_static_string ("nm-setting-vpn-properties-error-quark"); - return quark; -} - -/* This should really be standard. */ -#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } - -GType -nm_setting_vpn_properties_error_get_type (void) -{ - static GType etype = 0; - - if (etype == 0) { - static const GEnumValue values[] = { - /* Unknown error. */ - ENUM_ENTRY (NM_SETTING_VPN_PROPERTIES_ERROR_UNKNOWN, "UnknownError"), - /* The specified property was invalid. */ - ENUM_ENTRY (NM_SETTING_VPN_PROPERTIES_ERROR_INVALID_PROPERTY, "InvalidProperty"), - /* The specified property was missing and is required. */ - ENUM_ENTRY (NM_SETTING_VPN_PROPERTIES_ERROR_MISSING_PROPERTY, "MissingProperty"), - { 0, 0, 0 } - }; - etype = g_enum_register_static ("NMSettingVPNPropertiesError", values); - } - return etype; -} - - -G_DEFINE_TYPE (NMSettingVPNProperties, nm_setting_vpn_properties, NM_TYPE_SETTING) - -enum { - PROP_0, - PROP_DATA, - - LAST_PROP -}; - -NMSetting * -nm_setting_vpn_properties_new (void) -{ - return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN_PROPERTIES, NULL); -} - -static gboolean -verify (NMSetting *setting, GSList *all_settings, GError **error) -{ - NMSettingVPNProperties *self = NM_SETTING_VPN_PROPERTIES (setting); - - g_return_val_if_fail (self->data != NULL, FALSE); - - /* FIXME: actually check the data as well */ - - return TRUE; -} - -static void -nm_gvalue_destroy (gpointer data) -{ - GValue *value = (GValue *) data; - - g_value_unset (value); - g_slice_free (GValue, value); -} - -static void -update_one_secret (NMSetting *setting, const char *key, GValue *value) -{ - NMSettingVPNProperties *self = NM_SETTING_VPN_PROPERTIES (setting); - GValue *copy_val; - - g_return_if_fail (key != NULL); - g_return_if_fail (value != NULL); - - /* Secrets are really only known to the VPNs themselves. */ - copy_val = g_slice_new0 (GValue); - g_value_init (copy_val, G_VALUE_TYPE (value)); - g_value_copy (value, copy_val); - g_hash_table_insert (self->data, g_strdup (key), copy_val); -} - -static void -nm_setting_vpn_properties_init (NMSettingVPNProperties *self) -{ - g_object_set (NM_SETTING (self), "name", NM_SETTING_VPN_PROPERTIES_SETTING_NAME, NULL); - - self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy); -} - -static void -finalize (GObject *object) -{ - NMSettingVPNProperties *self = NM_SETTING_VPN_PROPERTIES (object); - - g_hash_table_destroy (self->data); - - G_OBJECT_CLASS (nm_setting_vpn_properties_parent_class)->finalize (object); -} - -static void -copy_hash (gpointer key, gpointer data, gpointer user_data) -{ - GHashTable *hash = (GHashTable *) user_data; - GValue *src_val = (GValue *) data; - GValue *copy_val; - - copy_val = g_slice_new0 (GValue); - g_value_init (copy_val, G_VALUE_TYPE (src_val)); - g_value_copy (src_val, copy_val); - g_hash_table_insert (hash, g_strdup (key), copy_val); -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMSettingVPNProperties *setting = NM_SETTING_VPN_PROPERTIES (object); - - switch (prop_id) { - case PROP_DATA: - /* Must make a deep copy of the hash table here... */ - g_hash_table_remove_all (setting->data); - g_hash_table_foreach (g_value_get_boxed (value), copy_hash, setting->data); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMSettingVPNProperties *setting = NM_SETTING_VPN_PROPERTIES (object); - - switch (prop_id) { - case PROP_DATA: - g_value_set_boxed (value, setting->data); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nm_setting_vpn_properties_class_init (NMSettingVPNPropertiesClass *setting_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); - - /* virtual methods */ - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->finalize = finalize; - parent_class->verify = verify; - parent_class->update_one_secret = update_one_secret; - - /* Properties */ - g_object_class_install_property - (object_class, PROP_DATA, - nm_param_spec_specialized (NM_SETTING_VPN_PROPERTIES_DATA, - "Data", - "VPN Service specific data", - DBUS_TYPE_G_MAP_OF_VARIANT, - G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); -} diff --git a/libnm-util/nm-setting-vpn-properties.h b/libnm-util/nm-setting-vpn-properties.h deleted file mode 100644 index 8a9ed8593d..0000000000 --- a/libnm-util/nm-setting-vpn-properties.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* - * Dan Williams - * Tambet Ingo - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2007 - 2008 Red Hat, Inc. - * (C) Copyright 2007 - 2008 Novell, Inc. - */ - -#ifndef NM_SETTING_VPN_PROPERTIES_H -#define NM_SETTING_VPN_PROPERTIES_H - -#include - -G_BEGIN_DECLS - -#define NM_TYPE_SETTING_VPN_PROPERTIES (nm_setting_vpn_properties_get_type ()) -#define NM_SETTING_VPN_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VPN_PROPERTIES, NMSettingVPNProperties)) -#define NM_SETTING_VPN_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VPN_PROPERTIES, NMSettingVPNPropertiesClass)) -#define NM_IS_SETTING_VPN_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VPN_PROPERTIES)) -#define NM_IS_SETTING_VPN_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_VPN_PROPERTIES)) -#define NM_SETTING_VPN_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VPN_PROPERTIES, NMSettingVPNPropertiesClass)) - -#define NM_SETTING_VPN_PROPERTIES_SETTING_NAME "vpn-properties" - -typedef enum -{ - NM_SETTING_VPN_PROPERTIES_ERROR_UNKNOWN = 0, - NM_SETTING_VPN_PROPERTIES_ERROR_INVALID_PROPERTY, - NM_SETTING_VPN_PROPERTIES_ERROR_MISSING_PROPERTY, -} NMSettingVPNPropertiesError; - -#define NM_TYPE_SETTING_VPN_PROPERTIES_ERROR (nm_setting_vpn_properties_error_get_type ()) -GType nm_setting_vpn_properties_error_get_type (void); - -#define NM_SETTING_VPN_PROPERTIES_ERROR nm_setting_vpn_properties_error_quark () -GQuark nm_setting_vpn_properties_error_quark (void); - -#define NM_SETTING_VPN_PROPERTIES_DATA "data" - -typedef struct { - NMSetting parent; - - /* The hash table is created at setting object - * init time and should not be replaced. It is - * a char * -> GValue * mapping, and both the key - * and value are owned by the hash table. GValues - * inserted into the hash table must be allocated - * with the g_slice_* functions. - */ - GHashTable *data; -} NMSettingVPNProperties; - -typedef struct { - NMSettingClass parent; -} NMSettingVPNPropertiesClass; - -GType nm_setting_vpn_properties_get_type (void); - -NMSetting *nm_setting_vpn_properties_new (void); - -G_END_DECLS - -#endif /* NM_SETTING_VPN_PROPERTIES_H */ diff --git a/libnm-util/nm-setting-vpn.c b/libnm-util/nm-setting-vpn.c index 9a8258b847..aa2fcfd6d2 100644 --- a/libnm-util/nm-setting-vpn.c +++ b/libnm-util/nm-setting-vpn.c @@ -70,6 +70,7 @@ enum { PROP_0, PROP_SERVICE_TYPE, PROP_USER_NAME, + PROP_DATA, LAST_PROP }; @@ -113,10 +114,25 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return TRUE; } +static void +update_one_secret (NMSetting *setting, const char *key, GValue *value) +{ + NMSettingVPN *self = NM_SETTING_VPN (setting); + + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (G_VALUE_HOLDS_STRING (value)); + + /* Secrets are really only known to the VPNs themselves. */ + g_hash_table_insert (self->data, g_strdup (key), g_value_dup_string (value)); +} + static void nm_setting_vpn_init (NMSettingVPN *setting) { - ((NMSetting *) setting)->name = g_strdup (NM_SETTING_VPN_SETTING_NAME); + NM_SETTING (setting)->name = g_strdup (NM_SETTING_VPN_SETTING_NAME); + + setting->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void @@ -126,10 +142,17 @@ finalize (GObject *object) g_free (self->service_type); g_free (self->user_name); + g_hash_table_destroy (self->data); G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object); } +static void +copy_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -145,6 +168,11 @@ set_property (GObject *object, guint prop_id, g_free (setting->user_name); setting->user_name = g_value_dup_string (value); break; + case PROP_DATA: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (setting->data); + g_hash_table_foreach (g_value_get_boxed (value), copy_hash, setting->data); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -164,6 +192,9 @@ get_property (GObject *object, guint prop_id, case PROP_USER_NAME: g_value_set_string (value, setting->user_name); break; + case PROP_DATA: + g_value_set_boxed (value, setting->data); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -181,6 +212,7 @@ nm_setting_vpn_class_init (NMSettingVPNClass *setting_class) object_class->get_property = get_property; object_class->finalize = finalize; parent_class->verify = verify; + parent_class->update_one_secret = update_one_secret; /* Properties */ g_object_class_install_property @@ -198,4 +230,12 @@ nm_setting_vpn_class_init (NMSettingVPNClass *setting_class) "User name", NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_DATA, + nm_param_spec_specialized (NM_SETTING_VPN_DATA, + "Data", + "VPN Service specific data", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); } diff --git a/libnm-util/nm-setting-vpn.h b/libnm-util/nm-setting-vpn.h index a52627ae9f..f87365e63d 100644 --- a/libnm-util/nm-setting-vpn.h +++ b/libnm-util/nm-setting-vpn.h @@ -54,12 +54,27 @@ GQuark nm_setting_vpn_error_quark (void); #define NM_SETTING_VPN_SERVICE_TYPE "service-type" #define NM_SETTING_VPN_USER_NAME "user-name" +#define NM_SETTING_VPN_DATA "data" typedef struct { NMSetting parent; char *service_type; + + /* username of the user requesting this connection, thus + * it's really only valid for user connections, and it also + * should never be saved out to persistent config. + */ char *user_name; + + /* The hash table is created at setting object + * init time and should not be replaced. It is + * a char * -> char * mapping, and both the key + * and value are owned by the hash table, and should + * be allocated with functions whose value can be + * freed with g_free() + */ + GHashTable *data; } NMSettingVPN; typedef struct { diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index be32ead556..7152a13b18 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * @@ -35,7 +35,6 @@ #include "nm-vpn-connection.h" #include "nm-setting-connection.h" #include "nm-setting-vpn.h" -#include "nm-setting-vpn-properties.h" #include "nm-setting-ip4-config.h" #include "nm-dbus-manager.h" #include "nm-manager.h" @@ -477,7 +476,7 @@ nm_vpn_connection_connect_cb (DBusGProxy *proxy, GError *err, gpointer user_data nm_vpn_connection_get_name (connection)); if (err) { - nm_warning ("(VPN connection '%s' could not start. dbus says: '%s'.", + nm_warning ("(VPN connection '%s' failed to connect: '%s'.", nm_vpn_connection_get_name (connection), err->message); nm_vpn_connection_set_vpn_state (connection, NM_VPN_CONNECTION_STATE_FAILED, @@ -660,11 +659,11 @@ update_vpn_properties_secrets (gpointer key, gpointer data, gpointer user_data) { NMConnection *connection = NM_CONNECTION (user_data); - if (strcmp (key, NM_SETTING_VPN_PROPERTIES_SETTING_NAME)) + if (strcmp (key, NM_SETTING_VPN_SETTING_NAME)) return; nm_connection_update_secrets (connection, - NM_SETTING_VPN_PROPERTIES_SETTING_NAME, + NM_SETTING_VPN_SETTING_NAME, (GHashTable *) data); } @@ -802,9 +801,9 @@ call_need_secrets (NMVPNConnection *vpn_connection) } static void -connection_vpn_state_changed (NMVPNConnection *connection, - NMVPNConnectionState state, - NMVPNConnectionStateReason reason) +connection_state_changed (NMVPNConnection *connection, + NMVPNConnectionState state, + NMVPNConnectionStateReason reason) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); @@ -977,7 +976,7 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate)); /* virtual methods */ - connection_class->vpn_state_changed = connection_vpn_state_changed; + connection_class->vpn_state_changed = connection_state_changed; object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; diff --git a/system-settings/plugins/keyfile/reader.c b/system-settings/plugins/keyfile/reader.c index 14b974ea7c..e43486a209 100644 --- a/system-settings/plugins/keyfile/reader.c +++ b/system-settings/plugins/keyfile/reader.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -288,6 +289,32 @@ read_array_of_array_of_uint (GKeyFile *file, return success; } +static void +read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key) +{ + char **keys, **iter; + char *value; + + keys = g_key_file_get_keys (file, setting->name, NULL, NULL); + if (!keys || !*keys) + return; + + for (iter = keys; *iter; iter++) { + value = g_key_file_get_string (file, setting->name, *iter, NULL); + if (!value) + continue; + + if (NM_IS_SETTING_VPN (setting)) { + NMSettingVPN *s_vpn = NM_SETTING_VPN (setting); + + if (strcmp (*iter, NM_SETTING_VPN_SERVICE_TYPE)) + g_hash_table_insert (s_vpn->data, g_strdup (*iter), g_strdup (value)); + } + g_free (value); + } + g_strfreev (keys); +} + static void read_one_setting_value (NMSetting *setting, const char *key, @@ -395,9 +422,8 @@ read_one_setting_value (NMSetting *setting, g_slist_free (list); g_strfreev (sa); - } else if (type == dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) { - /* FIXME */ - g_warning ("Implement me"); + } else if (type == dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) { + read_hash_of_string (file, setting, key); } else if (type == DBUS_TYPE_G_UINT_ARRAY) { if (!read_array_of_uint (file, setting, key)) { g_warning ("Unhandled setting property type (read): '%s/%s' : '%s'", diff --git a/system-settings/plugins/keyfile/writer.c b/system-settings/plugins/keyfile/writer.c index eb5508d5b8..599f0773f6 100644 --- a/system-settings/plugins/keyfile/writer.c +++ b/system-settings/plugins/keyfile/writer.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -141,6 +142,42 @@ write_array_of_array_of_uint (GKeyFile *file, return TRUE; } +typedef struct { + GKeyFile *file; + const char *setting_name; +} WriteStringHashInfo; + +static void +write_hash_of_string_helper (gpointer key, gpointer data, gpointer user_data) +{ + WriteStringHashInfo *info = (WriteStringHashInfo *) user_data; + const char *property = (const char *) key; + const char *value = (const char *) data; + + if ( !strcmp (info->setting_name, NM_SETTING_VPN_SETTING_NAME) + && !strcmp (property, NM_SETTING_VPN_SERVICE_TYPE)) + return; + + g_key_file_set_string (info->file, + info->setting_name, + property, + value); +} + +static void +write_hash_of_string (GKeyFile *file, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GHashTable *hash = g_value_get_boxed (value); + WriteStringHashInfo info; + + info.file = file; + info.setting_name = setting->name; + g_hash_table_foreach (hash, write_hash_of_string_helper, &info); +} + static void write_setting_value (NMSetting *setting, const char *key, @@ -208,9 +245,8 @@ write_setting_value (NMSetting *setting, g_key_file_set_string_list (file, setting->name, key, (const gchar **const) array, i); g_free (array); } - } else if (type == dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) { - /* FIXME */ - g_warning ("Implement me"); + } else if (type == dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) { + write_hash_of_string (file, setting, key, value); } else if (type == DBUS_TYPE_G_UINT_ARRAY) { if (!write_array_of_uint (file, setting, key, value)) { g_warning ("Unhandled setting property type (write) '%s/%s' : '%s'",