device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm-core / nm-keyfile-writer.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager system settings service - keyfile plugin
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2008 Novell, Inc.
19  * Copyright (C) 2008 - 2015 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-keyfile-internal.h"
25
26 #include <stdlib.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <arpa/inet.h>
32 #include <string.h>
33
34 #include "nm-core-internal.h"
35 #include "nm-keyfile-utils.h"
36
37 typedef struct {
38         NMConnection *connection;
39         GKeyFile *keyfile;
40         GError *error;
41         NMKeyfileWriteHandler handler;
42         void *user_data;
43 } KeyfileWriterInfo;
44
45
46 /* Some setting properties also contain setting names, such as
47  * NMSettingConnection's 'type' property (which specifies the base type of the
48  * connection, eg ethernet or wifi) or the 802-11-wireless setting's
49  * 'security' property which specifies whether or not the AP requires
50  * encrpytion.  This function handles translating those properties' values
51  * from the real setting name to the more-readable alias.
52  */
53 static void
54 setting_alias_writer (KeyfileWriterInfo *info,
55                       NMSetting *setting,
56                       const char *key,
57                       const GValue *value)
58 {
59         const char *str, *alias;
60
61         str = g_value_get_string (value);
62         alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
63         nm_keyfile_plugin_kf_set_string (info->keyfile,
64                                          nm_setting_get_name (setting),
65                                          key,
66                                          alias ? alias : str);
67 }
68
69 static void
70 write_array_of_uint (GKeyFile *file,
71                      NMSetting *setting,
72                      const char *key,
73                      const GValue *value)
74 {
75         GArray *array;
76         int i;
77         int *tmp_array;
78
79         array = (GArray *) g_value_get_boxed (value);
80         if (!array || !array->len)
81                 return;
82
83         tmp_array = g_new (gint, array->len);
84         for (i = 0; i < array->len; i++)
85                 tmp_array[i] = g_array_index (array, int, i);
86
87         nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
88         g_free (tmp_array);
89 }
90
91 static void
92 dns_writer (KeyfileWriterInfo *info,
93             NMSetting *setting,
94             const char *key,
95             const GValue *value)
96 {
97         char **list;
98
99         list = g_value_get_boxed (value);
100         if (list && list[0]) {
101                 nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key,
102                                                       (const char **) list, g_strv_length (list));
103         }
104 }
105
106 static void
107 ip6_addr_gen_mode_writer (KeyfileWriterInfo *info,
108                           NMSetting *setting,
109                           const char *key,
110                           const GValue *value)
111 {
112         NMSettingIP6ConfigAddrGenMode addr_gen_mode;
113         gs_free char *str = NULL;
114
115         addr_gen_mode = (NMSettingIP6ConfigAddrGenMode) g_value_get_int (value);
116         str = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
117                                     addr_gen_mode);
118         nm_keyfile_plugin_kf_set_string (info->keyfile,
119                                          nm_setting_get_name (setting),
120                                          key,
121                                          str);
122 }
123
124 static void
125 write_ip_values (GKeyFile *file,
126                  const char *setting_name,
127                  GPtrArray *array,
128                  const char *gateway,
129                  gboolean is_route)
130 {
131         GString *output;
132         int family, i;
133         const char *addr, *gw;
134         guint32 plen, metric;
135         char key_name[30], *key_name_idx;
136
137         if (!array->len)
138                 return;
139
140         family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
141
142         strcpy (key_name, is_route ? "route" : "address");
143         key_name_idx = key_name + strlen (key_name);
144
145         output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
146         for (i = 0; i < array->len; i++) {
147                 if (is_route) {
148                         NMIPRoute *route = array->pdata[i];
149
150                         addr = nm_ip_route_get_dest (route);
151                         plen = nm_ip_route_get_prefix (route);
152                         gw = nm_ip_route_get_next_hop (route);
153                         metric = MAX (0, nm_ip_route_get_metric (route));
154                 } else {
155                         NMIPAddress *address = array->pdata[i];
156
157                         addr = nm_ip_address_get_address (address);
158                         plen = nm_ip_address_get_prefix (address);
159                         gw = i == 0 ? gateway : NULL;
160                         metric = 0;
161                 }
162
163                 g_string_set_size (output, 0);
164                 g_string_append_printf (output, "%s/%u", addr, plen);
165                 if (metric || gw) {
166                         /* Older versions of the plugin do not support the form
167                          * "a.b.c.d/plen,,metric", so, we always have to write the
168                          * gateway, even if there isn't one.
169                          * The current version supports reading of the above form.
170                          */
171                         if (!gw) {
172                                 if (family == AF_INET)
173                                         gw = "0.0.0.0";
174                                 else
175                                         gw = "::";
176                         }
177
178                         g_string_append_printf (output, ",%s", gw);
179                         if (metric)
180                                 g_string_append_printf (output, ",%lu", (unsigned long) metric);
181                 }
182
183                 sprintf (key_name_idx, "%d", i + 1);
184                 nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
185         }
186         g_string_free (output, TRUE);
187 }
188
189 static void
190 addr_writer (KeyfileWriterInfo *info,
191              NMSetting *setting,
192              const char *key,
193              const GValue *value)
194 {
195         GPtrArray *array;
196         const char *setting_name = nm_setting_get_name (setting);
197         const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
198
199         array = (GPtrArray *) g_value_get_boxed (value);
200         if (array && array->len)
201                 write_ip_values (info->keyfile, setting_name, array, gateway, FALSE);
202 }
203
204 static void
205 ip4_addr_label_writer (KeyfileWriterInfo *info,
206                        NMSetting *setting,
207                        const char *key,
208                        const GValue *value)
209 {
210         /* skip */
211 }
212
213 static void
214 gateway_writer (KeyfileWriterInfo *info,
215                 NMSetting *setting,
216                 const char *key,
217                 const GValue *value)
218 {
219         /* skip */
220 }
221
222 static void
223 route_writer (KeyfileWriterInfo *info,
224               NMSetting *setting,
225               const char *key,
226               const GValue *value)
227 {
228         GPtrArray *array;
229         const char *setting_name = nm_setting_get_name (setting);
230
231         array = (GPtrArray *) g_value_get_boxed (value);
232         if (array && array->len)
233                 write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
234 }
235
236 static void
237 write_hash_of_string (GKeyFile *file,
238                       NMSetting *setting,
239                       const char *key,
240                       const GValue *value)
241 {
242         GHashTableIter iter;
243         const char *property = NULL, *data = NULL;
244         const char *group_name = nm_setting_get_name (setting);
245         gboolean vpn_secrets = FALSE;
246
247         /* Write VPN secrets out to a different group to keep them separate */
248         if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
249                 group_name = NM_KEYFILE_GROUP_VPN_SECRETS;
250                 vpn_secrets = TRUE;
251         }
252
253         g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
254         while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
255                 gboolean write_item = TRUE;
256
257                 /* Handle VPN secrets specially; they are nested in the property's hash;
258                  * we don't want to write them if the secret is not saved, not required,
259                  * or owned by a user's secret agent.
260                  */
261                 if (vpn_secrets) {
262                         NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
263
264                         nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
265                         if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
266                                 write_item = FALSE;
267                 }
268
269                 if (write_item)
270                         nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
271         }
272 }
273
274 static void
275 ssid_writer (KeyfileWriterInfo *info,
276              NMSetting *setting,
277              const char *key,
278              const GValue *value)
279 {
280         GBytes *bytes;
281         const guint8 *ssid_data;
282         gsize ssid_len;
283         const char *setting_name = nm_setting_get_name (setting);
284         gboolean new_format = TRUE;
285         unsigned int semicolons = 0;
286         int i, *tmp_array;
287         char *ssid;
288
289         g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
290
291         bytes = g_value_get_boxed (value);
292         if (!bytes)
293                 return;
294         ssid_data = g_bytes_get_data (bytes, &ssid_len);
295         if (ssid_len == 0)
296                 return;
297
298         /* Check whether each byte is printable.  If not, we have to use an
299          * integer list, otherwise we can just use a string.
300          */
301         for (i = 0; i < ssid_len; i++) {
302                 char c = ssid_data[i] & 0xFF;
303                 if (!g_ascii_isprint (c)) {
304                         new_format = FALSE;
305                         break;
306                 }
307                 if (c == ';')
308                         semicolons++;
309         }
310
311         if (new_format) {
312                 ssid = g_malloc0 (ssid_len + semicolons + 1);
313                 if (semicolons == 0)
314                         memcpy (ssid, ssid_data, ssid_len);
315                 else {
316                         /* Escape semicolons with backslashes to make strings
317                          * containing ';', such as '16;17;' unambiguous */
318                         int j = 0;
319                         for (i = 0; i < ssid_len; i++) {
320                                 if (ssid_data[i] == ';')
321                                         ssid[j++] = '\\';
322                                 ssid[j++] = ssid_data[i];
323                         }
324                 }
325                 nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid);
326                 g_free (ssid);
327         } else {
328                 tmp_array = g_new (gint, ssid_len);
329                 for (i = 0; i < ssid_len; i++)
330                         tmp_array[i] = (int) ssid_data[i];
331                 nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, ssid_len);
332                 g_free (tmp_array);
333         }
334 }
335
336 static void
337 password_raw_writer (KeyfileWriterInfo *info,
338                      NMSetting *setting,
339                      const char *key,
340                      const GValue *value)
341 {
342         const char *setting_name = nm_setting_get_name (setting);
343         GBytes *array;
344         int *tmp_array;
345         gsize i, len;
346         const char *data;
347
348         g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
349
350         array = (GBytes *) g_value_get_boxed (value);
351         if (!array)
352                 return;
353         data = g_bytes_get_data (array, &len);
354         if (!data || !len)
355                 return;
356
357         tmp_array = g_new (gint, len);
358         for (i = 0; i < len; i++)
359                 tmp_array[i] = (int) data[i];
360         nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len);
361         g_free (tmp_array);
362 }
363
364 typedef struct ObjectType {
365         const char *key;
366         const char *suffix;
367         NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
368         NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
369         const char *           (*path_func)   (NMSetting8021x *setting);
370         GBytes *               (*blob_func)   (NMSetting8021x *setting);
371 } ObjectType;
372
373 static const ObjectType objtypes[10] = {
374         { NM_SETTING_802_1X_CA_CERT,
375           "ca-cert",
376           nm_setting_802_1x_get_ca_cert_scheme,
377           NULL,
378           nm_setting_802_1x_get_ca_cert_path,
379           nm_setting_802_1x_get_ca_cert_blob },
380
381         { NM_SETTING_802_1X_PHASE2_CA_CERT,
382           "inner-ca-cert",
383           nm_setting_802_1x_get_phase2_ca_cert_scheme,
384           NULL,
385           nm_setting_802_1x_get_phase2_ca_cert_path,
386           nm_setting_802_1x_get_phase2_ca_cert_blob },
387
388         { NM_SETTING_802_1X_CLIENT_CERT,
389           "client-cert",
390           nm_setting_802_1x_get_client_cert_scheme,
391           NULL,
392           nm_setting_802_1x_get_client_cert_path,
393           nm_setting_802_1x_get_client_cert_blob },
394
395         { NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
396           "inner-client-cert",
397           nm_setting_802_1x_get_phase2_client_cert_scheme,
398           NULL,
399           nm_setting_802_1x_get_phase2_client_cert_path,
400           nm_setting_802_1x_get_phase2_client_cert_blob },
401
402         { NM_SETTING_802_1X_PRIVATE_KEY,
403           "private-key",
404           nm_setting_802_1x_get_private_key_scheme,
405           nm_setting_802_1x_get_private_key_format,
406           nm_setting_802_1x_get_private_key_path,
407           nm_setting_802_1x_get_private_key_blob },
408
409         { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
410           "inner-private-key",
411           nm_setting_802_1x_get_phase2_private_key_scheme,
412           nm_setting_802_1x_get_phase2_private_key_format,
413           nm_setting_802_1x_get_phase2_private_key_path,
414           nm_setting_802_1x_get_phase2_private_key_blob },
415
416         { NULL },
417 };
418
419 /**************************************************************************/
420
421 static void
422 cert_writer_default (NMConnection *connection,
423                      GKeyFile *file,
424                      NMKeyfileWriteTypeDataCert *cert_data)
425 {
426         const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
427         NMSetting8021xCKScheme scheme;
428
429         scheme = cert_data->scheme_func (cert_data->setting);
430         if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
431                 const char *path;
432                 char *path_free = NULL, *tmp;
433                 gs_free char *base_dir = NULL;
434
435                 path = cert_data->path_func (cert_data->setting);
436                 g_assert (path);
437
438                 /* If the path is relative, make it an absolute path.
439                  * Relative paths make a keyfile not easily usable in another
440                  * context. */
441                 if (path[0] && path[0] != '/') {
442                         base_dir = g_get_current_dir ();
443                         path = path_free = g_strconcat (base_dir, "/", path, NULL);
444                 } else
445                         base_dir = g_path_get_dirname (path);
446
447                 /* path cannot start with "file://" or "data:;base64,", because it is an absolute path.
448                  * Still, make sure that a prefix-less path will be recognized. This can happen
449                  * for example if the path is longer then 500 chars. */
450                 tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL);
451                 if (tmp)
452                         g_clear_pointer (&tmp, g_free);
453                 else
454                         path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
455
456                 /* Path contains at least a '/', hence it cannot be recognized as the old
457                  * binary format consisting of a list of integers. */
458
459                 nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, path);
460                 g_free (tmp);
461                 g_free (path_free);
462         } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
463                 GBytes *blob;
464                 const guint8 *blob_data;
465                 gsize blob_len;
466                 char *blob_base64, *val;
467
468                 blob = cert_data->blob_func (cert_data->setting);
469                 g_assert (blob);
470                 blob_data = g_bytes_get_data (blob, &blob_len);
471
472                 blob_base64 = g_base64_encode (blob_data, blob_len);
473                 val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL);
474
475                 nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, val);
476                 g_free (val);
477                 g_free (blob_base64);
478         } else {
479                 /* scheme_func() returns UNKNOWN in all other cases. The only valid case
480                  * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this
481                  * case, we don't expect the writer to be called, because the default value
482                  * will not be serialized.
483                  * The only other reason for the scheme to be UNKNOWN is an invalid cert.
484                  * But our connection verifies, so that cannot happen either. */
485                 g_return_if_reached ();
486         }
487 }
488
489 static void
490 cert_writer (KeyfileWriterInfo *info,
491              NMSetting *setting,
492              const char *key,
493              const GValue *value)
494 {
495         const ObjectType *objtype = NULL;
496         guint i;
497         NMKeyfileWriteTypeDataCert type_data = { 0 };
498
499         for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
500                 if (g_strcmp0 (objtypes[i].key, key) == 0) {
501                         objtype = &objtypes[i];
502                         break;
503                 }
504         }
505         if (!objtype)
506                 g_return_if_reached ();
507
508         type_data.setting = NM_SETTING_802_1X (setting);
509         type_data.property_name = key;
510         type_data.suffix = objtype->suffix;
511         type_data.scheme_func = objtype->scheme_func;
512         type_data.format_func = objtype->format_func;
513         type_data.path_func = objtype->path_func;
514         type_data.blob_func = objtype->blob_func;
515
516         if (info->handler) {
517                 if (info->handler (info->connection,
518                                    info->keyfile,
519                                    NM_KEYFILE_WRITE_TYPE_CERT,
520                                    &type_data,
521                                    info->user_data,
522                                    &info->error))
523                         return;
524                 if (info->error)
525                         return;
526         }
527
528         cert_writer_default (info->connection, info->keyfile, &type_data);
529 }
530
531 /**************************************************************************/
532
533 typedef struct {
534         const char *setting_name;
535         const char *key;
536         void (*writer) (KeyfileWriterInfo *info,
537                         NMSetting *setting,
538                         const char *key,
539                         const GValue *value);
540 } KeyWriter;
541
542 /* A table of keys that require further parsing/conversion because they are
543  * stored in a format that can't be automatically read using the key's type.
544  * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
545  * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored 
546  * in struct in6_addr internally, but as string in keyfiles.
547  */
548 static KeyWriter key_writers[] = {
549         { NM_SETTING_CONNECTION_SETTING_NAME,
550           NM_SETTING_CONNECTION_TYPE,
551           setting_alias_writer },
552         { NM_SETTING_IP4_CONFIG_SETTING_NAME,
553           NM_SETTING_IP_CONFIG_ADDRESSES,
554           addr_writer },
555         { NM_SETTING_IP4_CONFIG_SETTING_NAME,
556           "address-labels",
557           ip4_addr_label_writer },
558         { NM_SETTING_IP6_CONFIG_SETTING_NAME,
559           NM_SETTING_IP_CONFIG_ADDRESSES,
560           addr_writer },
561         { NM_SETTING_IP4_CONFIG_SETTING_NAME,
562           NM_SETTING_IP_CONFIG_GATEWAY,
563           gateway_writer },
564         { NM_SETTING_IP6_CONFIG_SETTING_NAME,
565           NM_SETTING_IP_CONFIG_GATEWAY,
566           gateway_writer },
567         { NM_SETTING_IP4_CONFIG_SETTING_NAME,
568           NM_SETTING_IP_CONFIG_ROUTES,
569           route_writer },
570         { NM_SETTING_IP6_CONFIG_SETTING_NAME,
571           NM_SETTING_IP_CONFIG_ROUTES,
572           route_writer },
573         { NM_SETTING_IP4_CONFIG_SETTING_NAME,
574           NM_SETTING_IP_CONFIG_DNS,
575           dns_writer },
576         { NM_SETTING_IP6_CONFIG_SETTING_NAME,
577           NM_SETTING_IP_CONFIG_DNS,
578           dns_writer },
579         { NM_SETTING_IP6_CONFIG_SETTING_NAME,
580           NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
581           ip6_addr_gen_mode_writer },
582         { NM_SETTING_WIRELESS_SETTING_NAME,
583           NM_SETTING_WIRELESS_SSID,
584           ssid_writer },
585         { NM_SETTING_802_1X_SETTING_NAME,
586           NM_SETTING_802_1X_PASSWORD_RAW,
587           password_raw_writer },
588         { NM_SETTING_802_1X_SETTING_NAME,
589           NM_SETTING_802_1X_CA_CERT,
590           cert_writer },
591         { NM_SETTING_802_1X_SETTING_NAME,
592           NM_SETTING_802_1X_CLIENT_CERT,
593           cert_writer },
594         { NM_SETTING_802_1X_SETTING_NAME,
595           NM_SETTING_802_1X_PRIVATE_KEY,
596           cert_writer },
597         { NM_SETTING_802_1X_SETTING_NAME,
598           NM_SETTING_802_1X_PHASE2_CA_CERT,
599           cert_writer },
600         { NM_SETTING_802_1X_SETTING_NAME,
601           NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
602           cert_writer },
603         { NM_SETTING_802_1X_SETTING_NAME,
604           NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
605           cert_writer },
606         { NULL, NULL, NULL }
607 };
608
609 static gboolean
610 can_omit_default_value (NMSetting *setting, const char *property)
611 {
612         if (NM_IS_SETTING_VLAN (setting)) {
613                 if (!strcmp (property, NM_SETTING_VLAN_FLAGS))
614                         return FALSE;
615         } else if (NM_IS_SETTING_IP6_CONFIG (setting)) {
616                 if (!strcmp (property, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE))
617                         return FALSE;
618         } else if (NM_IS_SETTING_WIRELESS (setting)) {
619                 if (!strcmp (property, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION))
620                         return FALSE;
621         }
622
623         return TRUE;
624 }
625
626 static void
627 write_setting_value (NMSetting *setting,
628                      const char *key,
629                      const GValue *value,
630                      GParamFlags flag,
631                      gpointer user_data)
632 {
633         KeyfileWriterInfo *info = user_data;
634         const char *setting_name;
635         GType type = G_VALUE_TYPE (value);
636         KeyWriter *writer = &key_writers[0];
637         GParamSpec *pspec;
638
639         if (info->error)
640                 return;
641
642         /* Setting name gets picked up from the keyfile's section name instead */
643         if (!strcmp (key, NM_SETTING_NAME))
644                 return;
645
646         /* Don't write the NMSettingConnection object's 'read-only' property */
647         if (   NM_IS_SETTING_CONNECTION (setting)
648             && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
649                 return;
650
651         setting_name = nm_setting_get_name (setting);
652
653         /* If the value is the default value, remove the item from the keyfile */
654         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
655         if (pspec) {
656                 if (   can_omit_default_value (setting, key)
657                     && g_param_value_defaults (pspec, (GValue *) value)) {
658                         g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
659                         return;
660                 }
661         }
662
663         /* Don't write secrets that are owned by user secret agents or aren't
664          * supposed to be saved.  VPN secrets are handled specially though since
665          * the secret flags there are in a third-level hash in the 'secrets'
666          * property.
667          */
668         if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
669                 NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
670
671                 if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
672                         g_assert_not_reached ();
673                 if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
674                         return;
675         }
676
677         /* Look through the list of handlers for non-standard format key values */
678         while (writer->setting_name) {
679                 if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) {
680                         (*writer->writer) (info, setting, key, value);
681                         return;
682                 }
683                 writer++;
684         }
685
686         if (type == G_TYPE_STRING) {
687                 const char *str;
688
689                 str = g_value_get_string (value);
690                 if (str)
691                         nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
692         } else if (type == G_TYPE_UINT)
693                 nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
694         else if (type == G_TYPE_INT)
695                 nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
696         else if (type == G_TYPE_UINT64) {
697                 char *numstr;
698
699                 numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
700                 nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
701                 g_free (numstr);
702         } else if (type == G_TYPE_INT64) {
703                 char *numstr;
704
705                 numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
706                 nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
707                 g_free (numstr);
708         } else if (type == G_TYPE_BOOLEAN) {
709                 nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
710         } else if (type == G_TYPE_CHAR) {
711                 nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
712         } else if (type == G_TYPE_BYTES) {
713                 GBytes *bytes;
714                 const guint8 *data;
715                 gsize len = 0;
716
717                 bytes = g_value_get_boxed (value);
718                 data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
719
720                 if (data != NULL && len > 0) {
721                         int *tmp_array;
722                         int i;
723
724                         tmp_array = g_new (gint, len);
725                         for (i = 0; i < len; i++)
726                                 tmp_array[i] = (int) data[i];
727
728                         nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len);
729                         g_free (tmp_array);
730                 }
731         } else if (type == G_TYPE_STRV) {
732                 char **array;
733
734                 array = (char **) g_value_get_boxed (value);
735                 nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
736         } else if (type == G_TYPE_HASH_TABLE) {
737                 write_hash_of_string (info->keyfile, setting, key, value);
738         } else if (type == G_TYPE_ARRAY) {
739                 write_array_of_uint (info->keyfile, setting, key, value);
740         } else if (G_VALUE_HOLDS_FLAGS (value)) {
741                 /* Flags are guint but GKeyFile has no uint reader, just uint64 */
742                 nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
743         } else if (G_VALUE_HOLDS_ENUM (value))
744                 nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
745         else
746                 g_warn_if_reached ();
747 }
748
749 GKeyFile *
750 nm_keyfile_write (NMConnection *connection,
751                   NMKeyfileWriteHandler handler,
752                   void *user_data,
753                   GError **error)
754 {
755         KeyfileWriterInfo info = { 0 };
756
757         g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
758         g_return_val_if_fail (!error || !*error, NULL);
759
760         if (!nm_connection_verify (connection, error))
761                 return NULL;
762
763         info.connection = connection;
764         info.keyfile = g_key_file_new ();
765         info.error = NULL;
766         info.handler = handler;
767         info.user_data = user_data;
768         nm_connection_for_each_setting_value (connection, write_setting_value, &info);
769
770         if (info.error) {
771                 g_propagate_error (error, info.error);
772                 g_key_file_unref (info.keyfile);
773                 return NULL;
774         }
775         return info.keyfile;
776 }
777