diff options
Diffstat (limited to 'libnm-core/nm-keyfile-writer.c')
-rw-r--r-- | libnm-core/nm-keyfile-writer.c | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c new file mode 100644 index 000000000..ff433b264 --- /dev/null +++ b/libnm-core/nm-keyfile-writer.c @@ -0,0 +1,751 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service - keyfile plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 - 2015 Red Hat, Inc. + */ + +#include "config.h" + +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <arpa/inet.h> +#include <string.h> +#include <glib/gi18n-lib.h> + +#include "nm-setting.h" +#include "nm-setting-connection.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-vpn.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-8021x.h" +#include "nm-utils.h" + +#include "gsystem-local-alloc.h" +#include "nm-glib-compat.h" +#include "nm-keyfile-internal.h" +#include "nm-keyfile-utils.h" + +typedef struct { + NMConnection *connection; + GKeyFile *keyfile; + GError *error; + NMKeyfileWriteHandler handler; + void *user_data; +} KeyfileWriterInfo; + + +/* Some setting properties also contain setting names, such as + * NMSettingConnection's 'type' property (which specifies the base type of the + * connection, eg ethernet or wifi) or the 802-11-wireless setting's + * 'security' property which specifies whether or not the AP requires + * encrpytion. This function handles translating those properties' values + * from the real setting name to the more-readable alias. + */ +static void +setting_alias_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + const char *str, *alias; + + str = g_value_get_string (value); + alias = nm_keyfile_plugin_get_alias_for_setting_name (str); + nm_keyfile_plugin_kf_set_string (info->keyfile, + nm_setting_get_name (setting), + key, + alias ? alias : str); +} + +static void +write_array_of_uint (GKeyFile *file, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GArray *array; + int i; + int *tmp_array; + + array = (GArray *) g_value_get_boxed (value); + if (!array || !array->len) + return; + + tmp_array = g_new (gint, array->len); + for (i = 0; i < array->len; i++) + tmp_array[i] = g_array_index (array, int, i); + + nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len); + g_free (tmp_array); +} + +static void +dns_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + char **list; + + list = g_value_get_boxed (value); + if (list && list[0]) { + nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key, + (const char **) list, g_strv_length (list)); + } +} + +static void +write_ip_values (GKeyFile *file, + const char *setting_name, + GPtrArray *array, + const char *gateway, + gboolean is_route) +{ + GString *output; + int family, i; + const char *addr, *gw; + guint32 plen, metric; + char key_name[30], *key_name_idx; + + if (!array->len) + return; + + family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6; + + strcpy (key_name, is_route ? "route" : "address"); + key_name_idx = key_name + strlen (key_name); + + output = g_string_sized_new (2*INET_ADDRSTRLEN + 10); + for (i = 0; i < array->len; i++) { + if (is_route) { + NMIPRoute *route = array->pdata[i]; + + addr = nm_ip_route_get_dest (route); + plen = nm_ip_route_get_prefix (route); + gw = nm_ip_route_get_next_hop (route); + metric = MAX (0, nm_ip_route_get_metric (route)); + } else { + NMIPAddress *address = array->pdata[i]; + + addr = nm_ip_address_get_address (address); + plen = nm_ip_address_get_prefix (address); + gw = i == 0 ? gateway : NULL; + metric = 0; + } + + g_string_set_size (output, 0); + g_string_append_printf (output, "%s/%u", addr, plen); + if (metric || gw) { + /* Older versions of the plugin do not support the form + * "a.b.c.d/plen,,metric", so, we always have to write the + * gateway, even if there isn't one. + * The current version supports reading of the above form. + */ + if (!gw) { + if (family == AF_INET) + gw = "0.0.0.0"; + else + gw = "::"; + } + + g_string_append_printf (output, ",%s", gw); + if (metric) + g_string_append_printf (output, ",%lu", (unsigned long) metric); + } + + sprintf (key_name_idx, "%d", i + 1); + nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str); + } + g_string_free (output, TRUE); +} + +static void +addr_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GPtrArray *array; + const char *setting_name = nm_setting_get_name (setting); + const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting)); + + array = (GPtrArray *) g_value_get_boxed (value); + if (array && array->len) + write_ip_values (info->keyfile, setting_name, array, gateway, FALSE); +} + +static void +ip4_addr_label_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + /* skip */ +} + +static void +gateway_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + /* skip */ +} + +static void +route_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GPtrArray *array; + const char *setting_name = nm_setting_get_name (setting); + + array = (GPtrArray *) g_value_get_boxed (value); + if (array && array->len) + write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); +} + +static void +write_hash_of_string (GKeyFile *file, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GHashTableIter iter; + const char *property = NULL, *data = NULL; + const char *group_name = nm_setting_get_name (setting); + gboolean vpn_secrets = FALSE; + + /* Write VPN secrets out to a different group to keep them separate */ + if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { + group_name = VPN_SECRETS_GROUP; + vpn_secrets = TRUE; + } + + g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); + while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) { + gboolean write_item = TRUE; + + /* Handle VPN secrets specially; they are nested in the property's hash; + * we don't want to write them if the secret is not saved, not required, + * or owned by a user's secret agent. + */ + if (vpn_secrets) { + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + nm_setting_get_secret_flags (setting, property, &secret_flags, NULL); + if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) + write_item = FALSE; + } + + if (write_item) + nm_keyfile_plugin_kf_set_string (file, group_name, property, data); + } +} + +static void +ssid_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GBytes *bytes; + const guint8 *ssid_data; + gsize ssid_len; + const char *setting_name = nm_setting_get_name (setting); + gboolean new_format = TRUE; + unsigned int semicolons = 0; + int i, *tmp_array; + char *ssid; + + g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); + + bytes = g_value_get_boxed (value); + if (!bytes) + return; + ssid_data = g_bytes_get_data (bytes, &ssid_len); + if (ssid_len == 0) + return; + + /* Check whether each byte is printable. If not, we have to use an + * integer list, otherwise we can just use a string. + */ + for (i = 0; i < ssid_len; i++) { + char c = ssid_data[i] & 0xFF; + if (!g_ascii_isprint (c)) { + new_format = FALSE; + break; + } + if (c == ';') + semicolons++; + } + + if (new_format) { + ssid = g_malloc0 (ssid_len + semicolons + 1); + if (semicolons == 0) + memcpy (ssid, ssid_data, ssid_len); + else { + /* Escape semicolons with backslashes to make strings + * containing ';', such as '16;17;' unambiguous */ + int j = 0; + for (i = 0; i < ssid_len; i++) { + if (ssid_data[i] == ';') + ssid[j++] = '\\'; + ssid[j++] = ssid_data[i]; + } + } + nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid); + g_free (ssid); + } else { + tmp_array = g_new (gint, ssid_len); + for (i = 0; i < ssid_len; i++) + tmp_array[i] = (int) ssid_data[i]; + nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, ssid_len); + g_free (tmp_array); + } +} + +static void +password_raw_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + const char *setting_name = nm_setting_get_name (setting); + GBytes *array; + int *tmp_array; + gsize i, len; + const char *data; + + g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); + + array = (GBytes *) g_value_get_boxed (value); + if (!array) + return; + data = g_bytes_get_data (array, &len); + if (!data || !len) + return; + + tmp_array = g_new (gint, len); + for (i = 0; i < len; i++) + tmp_array[i] = (int) data[i]; + nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len); + g_free (tmp_array); +} + +typedef struct ObjectType { + const char *key; + const char *suffix; + NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting); + NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting); + const char * (*path_func) (NMSetting8021x *setting); + GBytes * (*blob_func) (NMSetting8021x *setting); +} ObjectType; + +static const ObjectType objtypes[10] = { + { NM_SETTING_802_1X_CA_CERT, + "ca-cert", + nm_setting_802_1x_get_ca_cert_scheme, + NULL, + nm_setting_802_1x_get_ca_cert_path, + nm_setting_802_1x_get_ca_cert_blob }, + + { NM_SETTING_802_1X_PHASE2_CA_CERT, + "inner-ca-cert", + nm_setting_802_1x_get_phase2_ca_cert_scheme, + NULL, + nm_setting_802_1x_get_phase2_ca_cert_path, + nm_setting_802_1x_get_phase2_ca_cert_blob }, + + { NM_SETTING_802_1X_CLIENT_CERT, + "client-cert", + nm_setting_802_1x_get_client_cert_scheme, + NULL, + nm_setting_802_1x_get_client_cert_path, + nm_setting_802_1x_get_client_cert_blob }, + + { NM_SETTING_802_1X_PHASE2_CLIENT_CERT, + "inner-client-cert", + nm_setting_802_1x_get_phase2_client_cert_scheme, + NULL, + nm_setting_802_1x_get_phase2_client_cert_path, + nm_setting_802_1x_get_phase2_client_cert_blob }, + + { NM_SETTING_802_1X_PRIVATE_KEY, + "private-key", + nm_setting_802_1x_get_private_key_scheme, + nm_setting_802_1x_get_private_key_format, + nm_setting_802_1x_get_private_key_path, + nm_setting_802_1x_get_private_key_blob }, + + { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + "inner-private-key", + nm_setting_802_1x_get_phase2_private_key_scheme, + nm_setting_802_1x_get_phase2_private_key_format, + nm_setting_802_1x_get_phase2_private_key_path, + nm_setting_802_1x_get_phase2_private_key_blob }, + + { NULL }, +}; + +/**************************************************************************/ + +static void +cert_writer_default (NMConnection *connection, + GKeyFile *file, + NMKeyfileWriteTypeDataCert *cert_data) +{ + const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting)); + NMSetting8021xCKScheme scheme; + + scheme = cert_data->scheme_func (cert_data->setting); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + const char *path; + char *path_free = NULL, *tmp; + gs_free char *base_dir = NULL; + + path = cert_data->path_func (cert_data->setting); + g_assert (path); + + /* If the path is relative, make it an absolute path. + * Relative paths make a keyfile not easily usable in another + * context. */ + if (path[0] && path[0] != '/') { + base_dir = g_get_current_dir (); + path = path_free = g_strconcat (base_dir, "/", path, NULL); + } else + base_dir = g_path_get_dirname (path); + + /* path cannot start with "file://" or "data:;base64,", because it is an absolute path. + * Still, make sure that a prefix-less path will be recognized. This can happen + * for example if the path is longer then 500 chars. */ + tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL); + if (tmp) + g_clear_pointer (&tmp, g_free); + else + path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL); + + /* Path contains at least a '/', hence it cannot be recognized as the old + * binary format consisting of a list of integers. */ + + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, path); + g_free (tmp); + g_free (path_free); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + GBytes *blob; + const guint8 *blob_data; + gsize blob_len; + char *blob_base64, *val; + + blob = cert_data->blob_func (cert_data->setting); + g_assert (blob); + blob_data = g_bytes_get_data (blob, &blob_len); + + blob_base64 = g_base64_encode (blob_data, blob_len); + val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL); + + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, val); + g_free (val); + g_free (blob_base64); + } else { + /* scheme_func() returns UNKNOWN in all other cases. The only valid case + * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this + * case, we don't expect the writer to be called, because the default value + * will not be serialized. + * The only other reason for the scheme to be UNKNOWN is an invalid cert. + * But our connection verifies, so that cannot happen either. */ + g_return_if_reached (); + } +} + +static void +cert_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + const ObjectType *objtype = NULL; + guint i; + NMKeyfileWriteTypeDataCert type_data = { 0 }; + + for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) { + if (g_strcmp0 (objtypes[i].key, key) == 0) { + objtype = &objtypes[i]; + break; + } + } + if (!objtype) + g_return_if_reached (); + + type_data.setting = NM_SETTING_802_1X (setting); + type_data.property_name = key; + type_data.suffix = objtype->suffix; + type_data.scheme_func = objtype->scheme_func; + type_data.format_func = objtype->format_func; + type_data.path_func = objtype->path_func; + type_data.blob_func = objtype->blob_func; + + if (info->handler) { + if (info->handler (info->connection, + info->keyfile, + NM_KEYFILE_WRITE_TYPE_CERT, + &type_data, + info->user_data, + &info->error)) + return; + if (info->error) + return; + } + + cert_writer_default (info->connection, info->keyfile, &type_data); +} + +/**************************************************************************/ + +typedef struct { + const char *setting_name; + const char *key; + void (*writer) (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value); +} KeyWriter; + +/* A table of keys that require further parsing/conversion because they are + * stored in a format that can't be automatically read using the key's type. + * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are + * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored + * in struct in6_addr internally, but as string in keyfiles. + */ +static KeyWriter key_writers[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_TYPE, + setting_alias_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_ADDRESSES, + addr_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + "address-labels", + ip4_addr_label_writer }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_ADDRESSES, + addr_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_GATEWAY, + gateway_writer }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_GATEWAY, + gateway_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_ROUTES, + route_writer }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_ROUTES, + route_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_DNS, + dns_writer }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_DNS, + dns_writer }, + { NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID, + ssid_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PASSWORD_RAW, + password_raw_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_CA_CERT, + cert_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_CLIENT_CERT, + cert_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PRIVATE_KEY, + cert_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PHASE2_CA_CERT, + cert_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT, + cert_writer }, + { NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + cert_writer }, + { NULL, NULL, NULL } +}; + +static void +write_setting_value (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flag, + gpointer user_data) +{ + KeyfileWriterInfo *info = user_data; + const char *setting_name; + GType type = G_VALUE_TYPE (value); + KeyWriter *writer = &key_writers[0]; + GParamSpec *pspec; + + if (info->error) + return; + + /* Setting name gets picked up from the keyfile's section name instead */ + if (!strcmp (key, NM_SETTING_NAME)) + return; + + /* Don't write the NMSettingConnection object's 'read-only' property */ + if ( NM_IS_SETTING_CONNECTION (setting) + && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY)) + return; + + setting_name = nm_setting_get_name (setting); + + /* If the value is the default value, remove the item from the keyfile */ + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); + if (pspec) { + if (g_param_value_defaults (pspec, (GValue *) value)) { + g_key_file_remove_key (info->keyfile, setting_name, key, NULL); + return; + } + } + + /* Don't write secrets that are owned by user secret agents or aren't + * supposed to be saved. VPN secrets are handled specially though since + * the secret flags there are in a third-level hash in the 'secrets' + * property. + */ + if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) { + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) + g_assert_not_reached (); + if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) + return; + } + + /* Look through the list of handlers for non-standard format key values */ + while (writer->setting_name) { + if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) { + (*writer->writer) (info, setting, key, value); + return; + } + writer++; + } + + if (type == G_TYPE_STRING) { + const char *str; + + str = g_value_get_string (value); + if (str) + nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str); + } else if (type == G_TYPE_UINT) + nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value)); + else if (type == G_TYPE_INT) + nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value)); + else if (type == G_TYPE_UINT64) { + char *numstr; + + numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + g_free (numstr); + } else if (type == G_TYPE_INT64) { + char *numstr; + + numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value)); + nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); + g_free (numstr); + } else if (type == G_TYPE_BOOLEAN) { + nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value)); + } else if (type == G_TYPE_CHAR) { + nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value)); + } else if (type == G_TYPE_BYTES) { + GBytes *bytes; + const guint8 *data; + gsize len = 0; + + bytes = g_value_get_boxed (value); + data = bytes ? g_bytes_get_data (bytes, &len) : NULL; + + if (data != NULL && len > 0) { + int *tmp_array; + int i; + + tmp_array = g_new (gint, len); + for (i = 0; i < len; i++) + tmp_array[i] = (int) data[i]; + + nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len); + g_free (tmp_array); + } + } else if (type == G_TYPE_STRV) { + char **array; + + array = (char **) g_value_get_boxed (value); + nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array)); + } else if (type == G_TYPE_HASH_TABLE) { + write_hash_of_string (info->keyfile, setting, key, value); + } else if (type == G_TYPE_ARRAY) { + write_array_of_uint (info->keyfile, setting, key, value); + } else if (G_VALUE_HOLDS_FLAGS (value)) { + /* Flags are guint but GKeyFile has no uint reader, just uint64 */ + nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value)); + } else if (G_VALUE_HOLDS_ENUM (value)) + nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value)); + else + g_warn_if_reached (); +} + +GKeyFile * +nm_keyfile_write (NMConnection *connection, + NMKeyfileWriteHandler handler, + void *user_data, + GError **error) +{ + KeyfileWriterInfo info = { 0 }; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + if (!nm_connection_verify (connection, error)) + return NULL; + + info.connection = connection; + info.keyfile = g_key_file_new (); + info.error = NULL; + info.handler = handler; + info.user_data = user_data; + nm_connection_for_each_setting_value (connection, write_setting_value, &info); + + if (info.error) { + g_propagate_error (error, info.error); + g_key_file_unref (info.keyfile); + return NULL; + } + return info.keyfile; +} + |