libnm: add keyfile support to libnm-core
authorThomas Haller <thaller@redhat.com>
Wed, 18 Feb 2015 17:59:35 +0000 (18:59 +0100)
committerThomas Haller <thaller@redhat.com>
Thu, 12 Mar 2015 17:12:26 +0000 (18:12 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=744699

libnm-core/Makefile.libnm-core
libnm-core/nm-keyfile-reader.c
libnm-core/nm-keyfile-reader.h
libnm-core/nm-keyfile-utils.c
libnm-core/nm-keyfile-utils.h
libnm-core/nm-keyfile-writer.c
libnm-core/nm-keyfile-writer.h
po/POTFILES.in

index fc6d779..7987146 100644 (file)
@@ -48,6 +48,9 @@ libnm_core_private_headers =                  \
        $(core)/crypto.h                        \
        $(core)/nm-connection-private.h         \
        $(core)/nm-core-internal.h              \
+       $(core)/nm-keyfile-reader.h         \
+       $(core)/nm-keyfile-utils.h          \
+       $(core)/nm-keyfile-writer.h         \
        $(core)/nm-property-compare.h           \
        $(core)/nm-setting-private.h            \
        $(core)/nm-utils-private.h
@@ -57,6 +60,9 @@ libnm_core_sources =                          \
        $(core)/crypto.c                        \
        $(core)/nm-connection.c                 \
        $(core)/nm-errors.c                     \
+       $(core)/nm-keyfile-reader.c         \
+       $(core)/nm-keyfile-utils.c          \
+       $(core)/nm-keyfile-writer.c         \
        $(core)/nm-property-compare.c           \
        $(core)/nm-setting-8021x.c              \
        $(core)/nm-setting-adsl.c               \
index 97b6567..9da3e1e 100644 (file)
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2008 - 2011 Red Hat, Inc.
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 #include <sys/types.h>
 #include <arpa/inet.h>
 #include <string.h>
+#include <glib/gi18n-lib.h>
 
 #include "nm-core-internal.h"
-#include "nm-dbus-glib-types.h"
+#include "gsystem-local-alloc.h"
 #include "nm-glib-compat.h"
-#include "nm-system-config-interface.h"
-#include "nm-logging.h"
-#include "reader.h"
-#include "common.h"
-#include "utils.h"
-#include "nm-core-internal.h"
-#include "NetworkManagerUtils.h"
+#include "nm-keyfile-reader.h"
+#include "nm-keyfile-utils.h"
+
+
+typedef struct {
+       NMConnection *connection;
+       GKeyFile *keyfile;
+       const char *base_dir;
+       NMKeyfileReadHandler handler;
+       void *user_data;
+       GError *error;
+       const char *group;
+       NMSetting *setting;
+} KeyfileReaderInfo;
+
+
+static void
+_handle_warn (KeyfileReaderInfo *info,
+              const char *property_name,
+              NMKeyfileWarnSeverity severity,
+              char *message)
+{
+       NMKeyfileReadTypeDataWarn type_data = {
+               .group = info->group,
+               .setting = info->setting,
+               .property_name = property_name,
+               .severity = severity,
+               .message = message,
+       };
+
+       info->handler (info->keyfile,
+                      info->connection,
+                      NM_KEYFILE_READ_TYPE_WARN,
+                      &type_data,
+                      info->user_data,
+                      &info->error);
+       g_free (message);
+}
+#define handle_warn(arg_info, arg_property_name, arg_severity, ...) \
+       ({ \
+               KeyfileReaderInfo *_info = (arg_info); \
+               \
+               if (_info->handler) { \
+                       _handle_warn (_info, (arg_property_name), (arg_severity), \
+                                     g_strdup_printf (__VA_ARGS__)); \
+               } \
+               _info->error == NULL; \
+       })
 
 /* Some setting properties also contain setting names, such as
  * NMSettingConnection's 'type' property (which specifies the base type of the
  * properties' values to the real setting name if they are an alias.
  */
 static void
-setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        char *s;
        const char *key_setting_name;
 
-       s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+       s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
        if (s) {
                key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
                g_object_set (G_OBJECT (setting),
@@ -63,7 +105,7 @@ setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, co
        }
 }
 
-static gboolean
+static void
 read_array_of_uint (GKeyFile *file,
                     NMSetting *setting,
                     const char *key)
@@ -75,34 +117,36 @@ read_array_of_uint (GKeyFile *file,
 
        tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
        array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
-       g_return_val_if_fail (array != NULL, FALSE);
 
        for (i = 0; i < length; i++)
                g_array_append_val (array, tmp[i]);
 
        g_object_set (setting, key, array, NULL);
        g_array_unref (array);
-
-       return TRUE;
 }
 
 static gboolean
-get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out)
+get_one_int (KeyfileReaderInfo *info, const char *property_name, const char *str, guint32 max_val, guint32 *out)
 {
        long tmp;
        char *endptr;
 
+       g_return_val_if_fail (!info == !property_name, FALSE);
+
        if (!str || !str[0]) {
-               if (key_name)
-                       nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name);
+               if (property_name)
+                       handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("ignoring missing number"));
                return FALSE;
        }
 
        errno = 0;
        tmp = strtol (str, &endptr, 10);
        if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) {
-               if (key_name)
-                       nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid number %s '%s'", __func__, key_name, str);
+               if (property_name)
+                       handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("ignoring invalid number '%s'"),
+                                   str);
                return FALSE;
        }
 
@@ -111,7 +155,7 @@ get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *ou
 }
 
 static gpointer
-build_address (int family, const char *address_str, guint32 plen)
+build_address (KeyfileReaderInfo *info, int family, const char *address_str, guint32 plen, const char *property_name)
 {
        NMIPAddress *addr;
        GError *error = NULL;
@@ -120,9 +164,9 @@ build_address (int family, const char *address_str, guint32 plen)
 
        addr = nm_ip_address_new (family, address_str, plen, &error);
        if (!addr) {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s address: %s", __func__,
-                            family == AF_INET ? "IPv4" : "IPv6",
-                            error->message);
+               handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("ignoring invalid %s addresss: %s"),
+                           family == AF_INET ? "IPv4" : "IPv6", error->message);
                g_error_free (error);
        }
 
@@ -130,10 +174,11 @@ build_address (int family, const char *address_str, guint32 plen)
 }
 
 static gpointer
-build_route (int family,
+build_route (KeyfileReaderInfo *info,
+             const char *property_name,
+             int family,
              const char *dest_str, guint32 plen,
-             const char *gateway_str, const char *metric_str,
-             const char *key_name)
+             const char *gateway_str, const char *metric_str)
 {
        NMIPRoute *route;
        guint32 metric = 0;
@@ -155,10 +200,14 @@ build_route (int family,
                         **/
                        if (   family == AF_INET6
                            && !metric_str
-                           && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric))
+                           && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &metric))
                                gateway_str = NULL;
                        else {
-                               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid gateway '%s'", __func__, gateway_str);
+                               if (!info->error) {
+                                       handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                                    _("ignoring invalid gateway '%s' for %s route"),
+                                                    gateway_str, family == AF_INET ? "IPv4" : "IPv6");
+                               }
                                return NULL;
                        }
                }
@@ -167,7 +216,7 @@ build_route (int family,
 
        /* parse metric, default to 0 */
        if (metric_str) {
-               if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
+               if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &metric))
                        return NULL;
        }
 
@@ -175,7 +224,8 @@ build_route (int family,
                                 metric ? (gint64) metric : -1,
                                 &error);
        if (!route) {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s route: %s", __func__,
+               handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("ignoring invalid %s route: %s"),
                             family == AF_INET ? "IPv4" : "IPv6",
                             error->message);
                g_error_free (error);
@@ -269,46 +319,52 @@ read_field (char **current, char **error, const char *characters, const char *de
  * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
  */
 static gpointer
-read_one_ip_address_or_route (GKeyFile *file,
+read_one_ip_address_or_route (KeyfileReaderInfo *info,
+                              const char *property_name,
                               const char *setting_name,
                               const char *key_name,
                               gboolean ipv6,
                               gboolean route,
-                              char **out_gateway)
+                              char **out_gateway,
+                              NMSetting *setting)
 {
        guint32 plen = G_MAXUINT32;
        gpointer result;
-       char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
+       char *address_str, *plen_str, *gateway_str, *metric_str, *current, *error;
+       gs_free char *value = NULL, *value_orig = NULL;
+
+#define VALUE_ORIG()   (value_orig ? value_orig : (value_orig = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL)))
 
-       current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL);
+       current = value = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL);
        if (!value)
                return NULL;
 
        /* get address field */
        address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
        if (error) {
-               nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
-                            *error, setting_name, key_name, error - current, current);
-               goto error;
+               handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("unexpected character '%c' for address %s: '%s' (position %td)"),
+                            *error, key_name, VALUE_ORIG (), error - current);
+               return NULL;
        }
        /* get prefix length field (skippable) */
        plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
        /* get gateway field */
        gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
        if (error) {
-               nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
-                            *error, setting_name, key_name,
-                            plen_str ? "gateway" : "gateway or prefix length",
-                            error - current, current);
-               goto error;
+               handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("unexpected character '%c' for %s: '%s' (position %td)"),
+                            *error, key_name, VALUE_ORIG (), error - current);
+               return NULL;
        }
        /* for routes, get metric */
        if (route) {
                metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
                if (error) {
-                       nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
-                                    *error, setting_name, key_name, error - current, current);
-                       goto error;
+                       handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"),
+                                    *error, key_name, VALUE_ORIG (), error - current);
+                       return NULL;
                }
        } else
                metric_str = NULL;
@@ -316,13 +372,16 @@ read_one_ip_address_or_route (GKeyFile *file,
                /* there is still some data */
                if (*current) {
                        /* another field follows */
-                       nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: Garbage at the and of the line: %s",
-                                    setting_name, key_name, current);
-                       goto error;
+                       handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("garbage at the end of value %s: '%s'"),
+                                    key_name, VALUE_ORIG ());
+                       return NULL;
                } else {
                        /* semicolon at the end of input */
-                       nm_log_info (LOGD_SETTINGS, "keyfile: %s.%s: Deprecated semicolon at the end of value.",
-                                    setting_name, key_name);
+                       if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_INFO,
+                                         _("deprecated semicolon at the end of value %s: '%s'"),
+                                         key_name, VALUE_ORIG ()))
+                               return NULL;
                }
        }
 
@@ -330,39 +389,44 @@ read_one_ip_address_or_route (GKeyFile *file,
 
        /* parse plen, fallback to defaults */
        if (plen_str) {
-               if (!get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &plen)
+               if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)
                    || (route && plen == 0)) {
                        plen = DEFAULT_PREFIX (route, ipv6);
-                       nm_log_warn (LOGD_SETTINGS, "keyfile: invalid prefix length '%s' in '%s.%s', defaulting to %d",
-                                    plen_str, setting_name, key_name, plen);
+                       if (   info->error
+                           || !handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                            _("invalid prefix length for %s '%s', defaulting to %d"),
+                                            key_name, VALUE_ORIG (), plen))
+                               return NULL;
                }
        } else {
                plen = DEFAULT_PREFIX (route, ipv6);
-               nm_log_warn (LOGD_SETTINGS, "keyfile: Missing prefix length in '%s.%s', defaulting to %d",
-                            setting_name, key_name, plen);
+               if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                 _("missing prefix length for %s '%s', defaulting to %d"),
+                                 key_name, VALUE_ORIG (), plen))
+                       return NULL;
        }
 
        /* build the appropriate data structure for NetworkManager settings */
        if (route) {
-               result = build_route (ipv6 ? AF_INET6 : AF_INET,
-                                     address_str, plen, gateway_str, metric_str,
-                                     key_name);
+               result = build_route (info, property_name,
+                                     ipv6 ? AF_INET6 : AF_INET,
+                                     address_str, plen, gateway_str, metric_str);
        } else {
-               result = build_address (ipv6 ? AF_INET6 : AF_INET,
-                                       address_str, plen);
+               result = build_address (info, ipv6 ? AF_INET6 : AF_INET,
+                                       address_str, plen, property_name);
+               if (!result)
+                       return NULL;
                if (out_gateway && gateway_str)
                        *out_gateway = g_strdup (gateway_str);
        }
 
-       g_free (value);
+#undef VALUE_ORIG
+
        return result;
-error:
-       g_free (value);
-       return NULL;
 }
 
 static void
-ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        gboolean ipv6 = !strcmp (setting_name, "ipv6");
@@ -383,7 +447,7 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi
 
        for (i = -1; i < 1000; i++) {
                const char **key_basename;
-               
+
                for (key_basename = key_names; *key_basename; key_basename++) {
                        char *key_name;
                        gpointer item;
@@ -394,12 +458,18 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi
                        else
                                key_name = g_strdup (*key_basename);
 
-                       item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes,
-                                                            gateway ? NULL : &gateway);
+                       item = read_one_ip_address_or_route (info, key, setting_name, key_name, ipv6, routes,
+                                                            gateway ? NULL : &gateway, setting);
+                       g_free (key_name);
+
+                       if (info->error) {
+                               g_ptr_array_unref (list);
+                               g_free (gateway);
+                               return;
+                       }
                        if (item)
                                g_ptr_array_add (list, item);
 
-                       g_free (key_name);
                }
        }
 
@@ -415,7 +485,7 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi
 }
 
 static void
-ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+ip4_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        GPtrArray *array;
@@ -423,7 +493,7 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
        char **list, **iter;
        int ret;
 
-       list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
+       list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
        if (!list || !g_strv_length (list))
                return;
 
@@ -433,7 +503,13 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 
                ret = inet_pton (AF_INET, *iter, &addr);
                if (ret <= 0) {
-                       nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter);
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("ignoring invalid DNS server IPv4 address '%s'"),
+                                         *iter)) {
+                               g_ptr_array_unref (array);
+                               g_strfreev (list);
+                               return;
+                       }
                        continue;
                }
 
@@ -447,7 +523,7 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 }
 
 static void
-ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        GPtrArray *array = NULL;
@@ -455,7 +531,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
        char **list, **iter;
        int ret;
 
-       list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
+       list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
        if (!list || !g_strv_length (list))
                return;
 
@@ -466,7 +542,13 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 
                ret = inet_pton (AF_INET6, *iter, &addr);
                if (ret <= 0) {
-                       nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter);
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("ignoring invalid DNS server IPv6 address '%s'"),
+                                         *iter)) {
+                               g_ptr_array_unref (array);
+                               g_strfreev (list);
+                               return;
+                       }
                        continue;
                }
 
@@ -480,7 +562,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 }
 
 static void
-mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length)
+mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length)
 {
        const char *setting_name = nm_setting_get_name (setting);
        char *tmp_string = NULL, *p, *mac_str;
@@ -488,7 +570,7 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
        GByteArray *array = NULL;
        gsize length;
 
-       p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+       p = tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
        if (tmp_string && tmp_string[0]) {
                /* Look for enough ':' characters to signify a MAC address */
                guint i = 0;
@@ -513,7 +595,7 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
 
        if (array == NULL) {
                /* Old format; list of ints */
-               tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
+               tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
                if (length > 0 && (enforce_length == 0 || enforce_length == length)) {
                        gsize i;
 
@@ -523,12 +605,12 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
                                const guint8 v = (guint8) (val & 0xFF);
 
                                if (val < 0 || val > 255) {
-                                       nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-                                                    " between 0 and 255 inclusive)", __func__, setting_name,
-                                                    key, val);
+                                       handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                                    _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"),
+                                                    val);
                                        g_byte_array_free (array, TRUE);
-                                       array = NULL;
-                                       break;
+                                       g_free (tmp_list);
+                                       return;
                                }
                                g_byte_array_append (array, &v, 1);
                        }
@@ -537,8 +619,8 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
        }
 
        if (!array) {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s",
-                            __func__, setting_name, key);
+               handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("ignoring invalid MAC address"));
                return;
        }
 
@@ -549,15 +631,15 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
 }
 
 static void
-mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
-       mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN);
+       mac_address_parser (info, setting, key, ETH_ALEN);
 }
 
 static void
-mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
-       mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN);
+       mac_address_parser (info, setting, key, INFINIBAND_ALEN);
 }
 
 static void
@@ -606,7 +688,7 @@ unescape_semicolons (char *str)
 }
 
 static GBytes *
-get_bytes (GKeyFile *keyfile,
+get_bytes (KeyfileReaderInfo *info,
            const char *setting_name,
            const char *key,
            gboolean zero_terminate,
@@ -618,13 +700,13 @@ get_bytes (GKeyFile *keyfile,
        gsize length;
        int i;
 
-       if (!nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, NULL))
+       if (!nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, NULL))
                return NULL;
 
        /* New format: just a string
         * Old format: integer list; e.g. 11;25;38;
         */
-       tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+       tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
        if (tmp_string) {
                GRegex *regex;
                GMatchInfo *match_info;
@@ -648,11 +730,13 @@ get_bytes (GKeyFile *keyfile,
        }
 
        if (!array) {
+               gboolean already_warned = FALSE;
+
                /* Old format; list of ints */
-               tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
+               tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
                if (!tmp_list) {
-                       nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid binary property",
-                                                   __func__, setting_name, key);
+                       handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("ignoring invalid binary property"));
                        return NULL;
                }
                array = g_byte_array_sized_new (length);
@@ -661,9 +745,15 @@ get_bytes (GKeyFile *keyfile,
                        unsigned char v = (unsigned char) (val & 0xFF);
 
                        if (val < 0 || val > 255) {
-                               nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-                                            " between 0 and 255 inclusive)", __func__, setting_name,
-                                            key, val);
+                               if (   !already_warned
+                                   && !handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                                    _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"),
+                                                    val)) {
+                                       g_free (tmp_list);
+                                       g_byte_array_free (array, TRUE);
+                                       return NULL;
+                               }
+                               already_warned = TRUE;
                        } else
                                g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
                }
@@ -678,44 +768,44 @@ get_bytes (GKeyFile *keyfile,
 }
 
 static void
-ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+ssid_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        GBytes *bytes;
 
-       bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE);
+       bytes = get_bytes (info, setting_name, key, FALSE, TRUE);
        if (bytes) {
                g_object_set (setting, key, bytes, NULL);
                g_bytes_unref (bytes);
-       } else {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid SSID for %s / %s",
-                            __func__, setting_name, key);
+       } else if (!info->error) {
+               handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("ignoring invalid SSID"));
        }
 }
 
 static void
-password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+password_raw_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        GBytes *bytes;
 
-       bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE);
+       bytes = get_bytes (info, setting_name, key, FALSE, TRUE);
        if (bytes) {
                g_object_set (setting, key, bytes, NULL);
                g_bytes_unref (bytes);
-       } else {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid raw password for %s / %s",
-                            __func__, setting_name, key);
+       } else if (!info->error) {
+               handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("ignoring invalid raw password"));
        }
 }
 
 static char *
-get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_path_len)
+get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_len)
 {
        const char *base;
-       char *p = NULL, *path, *dirname, *tmp;
+       char *p = NULL, *path, *tmp;
 
-       g_return_val_if_fail (keyfile_path != NULL, NULL);
+       g_return_val_if_fail (base_dir != NULL, NULL);
        g_return_val_if_fail (cert_path != NULL, NULL);
 
        base = path = g_malloc0 (cert_path_len + 1);
@@ -728,9 +818,7 @@ get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_pat
        if (p)
                base = p + 1;
 
-       dirname = g_path_get_dirname (keyfile_path);
-       tmp = g_build_path ("/", dirname, base, NULL);
-       g_free (dirname);
+       tmp = g_build_path ("/", base_dir, base, NULL);
        g_free (path);
        return tmp;
 }
@@ -770,10 +858,10 @@ handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key)
 }
 
 static gboolean
-handle_as_path (GBytes *bytes,
+handle_as_path (KeyfileReaderInfo *info,
+                GBytes *bytes,
                 NMSetting *setting,
-                const char *key,
-                const char *keyfile_path)
+                const char *key)
 {
        const guint8 *data;
        gsize data_len;
@@ -802,7 +890,7 @@ handle_as_path (GBytes *bytes,
         * relative path to the current directory.
         */
 
-       path = get_cert_path (keyfile_path, data, data_len);
+       path = get_cert_path (info->base_dir, data, data_len);
        exists = g_file_test (path, G_FILE_TEST_EXISTS);
        if (   exists
            || memchr (data, '/', data_len)
@@ -822,7 +910,9 @@ handle_as_path (GBytes *bytes,
 
                /* Warn if the certificate didn't exist */
                if (exists == FALSE)
-                       nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path);
+                       handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                    _("certificate or key '%s' does not exist"),
+                                    path);
        }
        g_free (path);
 
@@ -830,48 +920,51 @@ handle_as_path (GBytes *bytes,
 }
 
 static void
-cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        GBytes *bytes;
        gboolean success = FALSE;
 
-       bytes = get_bytes (keyfile, setting_name, key, TRUE, FALSE);
+       bytes = get_bytes (info, setting_name, key, TRUE, FALSE);
        if (bytes) {
                /* Try as a path + scheme (ie, starts with "file://") */
                success = handle_as_scheme (bytes, setting, key);
 
                /* If not, it might be a plain path */
                if (success == FALSE)
-                       success = handle_as_path (bytes, setting, key, keyfile_path);
+                       success = handle_as_path (info, bytes, setting, key);
+               if (info->error)
+                       goto out_error;
 
                /* If neither of those two, assume blob with certificate data */
                if (success == FALSE)
                        g_object_set (setting, key, bytes, NULL);
-       } else {
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid key/cert value for %s / %s",
-                            __func__, setting_name, key);
+       } else if (!info->error) {
+               handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("invalid key/cert value"));
        }
 
+out_error:
        if (bytes)
                g_bytes_unref (bytes);
 }
 
 static void
-parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
        const char *setting_name = nm_setting_get_name (setting);
        NMSettingSerialParity parity;
        int int_val;
-       char *str_val;
+       gs_free char *str_val = NULL;
 
        /* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'.
         * We now accept either that or the (case-insensitive) character itself (but
         * still always write it the old way, for backward compatibility).
         */
-       int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
+       int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
        if (!int_val) {
-               str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+               str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
                if (str_val) {
                        if (str_val[0] && !str_val[1])
                                int_val = str_val[0];
@@ -880,7 +973,6 @@ parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const cha
                                int_val = 'X';
                        }
                }
-               g_free (str_val);
        }
 
        if (!int_val)
@@ -900,8 +992,9 @@ parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const cha
                parity = NM_SETTING_SERIAL_PARITY_NONE;
                break;
        default:
-               nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid value for %s / %s",
-                            __func__, setting_name, key);
+               handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("invalid parity value '%s'"),
+                            str_val ? str_val : "");
                return;
        }
 
@@ -912,7 +1005,7 @@ typedef struct {
        const char *setting_name;
        const char *key;
        gboolean check_for_key;
-       void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path);
+       void (*parser) (KeyfileReaderInfo *info, NMSetting *setting, const char *key);
 } KeyParser;
 
 /* A table of keys that require further parsing/conversion because they are
@@ -1025,11 +1118,6 @@ static KeyParser key_parsers[] = {
        { NULL, NULL, FALSE }
 };
 
-typedef struct {
-       GKeyFile *keyfile;
-       const char *keyfile_path;
-} ReadInfo;
-
 static void
 read_one_setting_value (NMSetting *setting,
                         const char *key,
@@ -1037,14 +1125,18 @@ read_one_setting_value (NMSetting *setting,
                         GParamFlags flags,
                         gpointer user_data)
 {
-       ReadInfo *info = user_data;
+       KeyfileReaderInfo *info = user_data;
+       GKeyFile *keyfile = info->keyfile;
        const char *setting_name;
        int errsv;
        GType type;
-       GError *err = NULL;
+       gs_free_error GError *err = NULL;
        gboolean check_for_key = TRUE;
        KeyParser *parser = &key_parsers[0];
 
+       if (info->error)
+               return;
+
        /* Property is not writable */
        if (!(flags & G_PARAM_WRITABLE))
                return;
@@ -1082,11 +1174,13 @@ read_one_setting_value (NMSetting *setting,
         * like IP addresses and routes where more than one value is actually
         * encoded by the setting property, this won't be true.
         */
-       if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) {
+       if (check_for_key && !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) {
                /* Key doesn't exist or an error ocurred, thus nothing to do. */
                if (err) {
-                       nm_log_warn (LOGD_SETTINGS, "Error loading setting '%s' value: %s", setting_name, err->message);
-                       g_error_free (err);
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("error loading setting value: %s"),
+                                         err->message))
+                               goto out_error;
                }
                return;
        }
@@ -1095,7 +1189,7 @@ read_one_setting_value (NMSetting *setting,
         * parsers below.
         */
        if (parser->setting_name) {
-               (*parser->parser) (setting, key, info->keyfile, info->keyfile_path);
+               (*parser->parser) (info, setting, key);
                return;
        }
 
@@ -1104,62 +1198,73 @@ read_one_setting_value (NMSetting *setting,
        if (type == G_TYPE_STRING) {
                char *str_val;
 
-               str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
+               str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
                g_object_set (setting, key, str_val, NULL);
                g_free (str_val);
        } else if (type == G_TYPE_UINT) {
                int int_val;
 
-               int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-               if (int_val < 0)
-                       nm_log_warn (LOGD_SETTINGS, "Casting negative value (%i) to uint", int_val);
+               int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
+               if (int_val < 0) {
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("invalid negative value (%i)"),
+                                         int_val))
+                               goto out_error;
+               }
                g_object_set (setting, key, int_val, NULL);
        } else if (type == G_TYPE_INT) {
                int int_val;
 
-               int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
+               int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
                g_object_set (setting, key, int_val, NULL);
        } else if (type == G_TYPE_BOOLEAN) {
                gboolean bool_val;
 
-               bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL);
+               bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, NULL);
                g_object_set (setting, key, bool_val, NULL);
        } else if (type == G_TYPE_CHAR) {
                int int_val;
 
-               int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-               if (int_val < G_MININT8 || int_val > G_MAXINT8)
-                       nm_log_warn (LOGD_SETTINGS, "Casting value (%i) to char", int_val);
+               int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
+               if (int_val < G_MININT8 || int_val > G_MAXINT8) {
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("invalid char value (%i)"),
+                                         int_val))
+                               goto out_error;
+               }
 
                g_object_set (setting, key, int_val, NULL);
        } else if (type == G_TYPE_UINT64) {
                char *tmp_str;
                guint64 uint_val;
 
-               tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
+               tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL);
                uint_val = g_ascii_strtoull (tmp_str, NULL, 10);
                g_free (tmp_str);
                g_object_set (setting, key, uint_val, NULL);
        } else if (type == G_TYPE_INT64) {
-               char *tmp_str;
+               gs_free char *tmp_str = NULL;
                gint64 int_val;
 
-               tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
+               tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL);
                int_val = _nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0);
                errsv = errno;
-               if (errsv)
-                       nm_log_warn (LOGD_SETTINGS, "Invalid int64 value (%s)", tmp_str);
-               else
+               if (errsv) {
+                       if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                         _("invalid int64 value (%s)"),
+                                         tmp_str))
+                               goto out_error;
+               } else
                        g_object_set (setting, key, int_val, NULL);
-               g_free (tmp_str);
-       } else if (type == G_TYPE_BYTES) {
+       } else if (type == G_TYPE_BYTES) {
                gint *tmp;
                GByteArray *array;
                GBytes *bytes;
                gsize length;
                int i;
+               gboolean already_warned = FALSE;
 
-               tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
+               tmp = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
 
                array = g_byte_array_sized_new (length);
                for (i = 0; i < length; i++) {
@@ -1167,9 +1272,15 @@ read_one_setting_value (NMSetting *setting,
                        unsigned char v = (unsigned char) (val & 0xFF);
 
                        if (val < 0 || val > 255) {
-                               nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
-                                            " between 0 and 255 inclusive)", __func__, setting_name,
-                                            key, val);
+                               if (   !already_warned
+                                   && !handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                                    _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"),
+                                                    val)) {
+                                       g_byte_array_unref (array);
+                                       g_free (tmp);
+                                       goto out_error;
+                               }
+                               already_warned = TRUE;
                        } else
                                g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
                }
@@ -1182,75 +1293,83 @@ read_one_setting_value (NMSetting *setting,
                gchar **sa;
                gsize length;
 
-               sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+               sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
                g_object_set (setting, key, sa, NULL);
                g_strfreev (sa);
        } else if (type == G_TYPE_HASH_TABLE) {
-               read_hash_of_string (info->keyfile, setting, key);
+               read_hash_of_string (keyfile, setting, key);
        } else if (type == G_TYPE_ARRAY) {
-               if (!read_array_of_uint (info->keyfile, setting, key)) {
-                       nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
-                                    setting_name, key, G_VALUE_TYPE_NAME (value));
-               }
+               read_array_of_uint (keyfile, setting, key);
        } else if (G_VALUE_HOLDS_FLAGS (value)) {
                guint64 uint_val;
 
                /* Flags are guint but GKeyFile has no uint reader, just uint64 */
-               uint_val = nm_keyfile_plugin_kf_get_uint64 (info->keyfile, setting_name, key, &err);
+               uint_val = nm_keyfile_plugin_kf_get_uint64 (keyfile, setting_name, key, &err);
                if (!err) {
                        if (uint_val <= G_MAXUINT)
                                g_object_set (setting, key, (guint) uint_val, NULL);
                        else {
-                               nm_log_warn (LOGD_SETTINGS, "Too large FLAGS property (read): '%s/%s' : '%s'",
-                                            setting_name, key, G_VALUE_TYPE_NAME (value));
+                               if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                                 _("too large FLAGS property '%s' (%lld)"),
+                                                 G_VALUE_TYPE_NAME (value), (long long unsigned) uint_val))
+                                       goto out_error;
                        }
                }
-               g_clear_error (&err);
        } else if (G_VALUE_HOLDS_ENUM (value)) {
                gint int_val;
 
-               int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, &err);
+               int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err);
                if (!err)
                        g_object_set (setting, key, (gint) int_val, NULL);
-               g_clear_error (&err);
        } else {
-               nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
-                            setting_name, key, G_VALUE_TYPE_NAME (value));
+               if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+                                _("unhandled setting property type '%s'"),
+                                G_VALUE_TYPE_NAME (value)))
+                       goto out_error;
        }
+out_error:
+       return;
 }
 
 static NMSetting *
-read_setting (GKeyFile *file, const char *keyfile_path, const char *group)
+read_setting (KeyfileReaderInfo *info)
 {
-       NMSetting *setting = NULL;
-       ReadInfo info = { file, keyfile_path };
        const char *alias;
        GType type;
 
-       alias = nm_keyfile_plugin_get_setting_name_for_alias (group);
-       if (alias)
-               group = alias;
+       alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group);
+       if (!alias)
+               alias = info->group;
 
-       type = nm_setting_lookup_type (group);
+       type = nm_setting_lookup_type (alias);
        if (type) {
-               setting = g_object_new (type, NULL);
-               nm_setting_enumerate_values (setting, read_one_setting_value, &info);
-       } else
-               nm_log_warn (LOGD_SETTINGS, "Invalid setting name '%s'", group);
+               NMSetting *setting = g_object_new (type, NULL);
+
+               info->setting = setting;
+               nm_setting_enumerate_values (setting, read_one_setting_value, info);
+               info->setting = NULL;
+               if (!info->error)
+                       return setting;
+
+               g_object_unref (setting);
+       } else {
+               handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
+                            _("invalid setting name '%s'"), info->group);
+       }
 
-       return setting;
+       return NULL;
 }
 
 static void
-read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn)
+read_vpn_secrets (KeyfileReaderInfo *info, NMSettingVpn *s_vpn)
 {
        char **keys, **iter;
 
-       keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL);
+       keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, VPN_SECRETS_GROUP, NULL, NULL);
        for (iter = keys; *iter; iter++) {
                char *secret;
 
-               secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL);
+               secret = nm_keyfile_plugin_kf_get_string (info->keyfile, VPN_SECRETS_GROUP, *iter, NULL);
                if (secret) {
                        nm_setting_vpn_add_secret (s_vpn, *iter, secret);
                        g_free (secret);
@@ -1259,12 +1378,37 @@ read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn)
        g_strfreev (keys);
 }
 
+/**
+ * nm_keyfile_read:
+ * @keyfile: the keyfile from which to create the connection
+ * @keyfile_name: keyfile allows missing connection id and uuid
+ *   and NetworkManager will create those when reading a connection
+ *   from file. By providing a filename you can reproduce that behavior,
+ *   but of course, it can only recreate the same UUID if you provide the
+ *   same filename as NetworkManager core daemon would.
+ *   @keyfile_name has only a relevance for setting the id or uuid if it
+ *   is missing and as fallback for @base_dir.
+ * @base_dir: when reading certificates from files with relative name,
+ *   the relative path is made absolute using @base_dir.
+ *   If @base_dir is missing, first try to get the pathname from @keyfile_name
+ *   (if it is given as absolute path). As last, fallback to the current path.
+ * @handler: read handler
+ * @user_data: user data for read handler
+ * @error: error
+ *
+ * Tries to create a NMConnection from a keyfile. The resulting keyfile is
+ * not normalized and might not even verify.
+ *
+ * Returns: (transfer full): on success, returns the created connection.
+ */
 NMConnection *
-nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
+nm_keyfile_read (GKeyFile *keyfile,
+                 const char *keyfile_name,
+                 const char *base_dir,
+                 NMKeyfileReadHandler handler,
+                 void *user_data,
+                 GError **error)
 {
-       GKeyFile *key_file;
-       struct stat statbuf;
-       gboolean bad_permissions;
        NMConnection *connection = NULL;
        NMSettingConnection *s_con;
        NMSetting *setting;
@@ -1272,30 +1416,33 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
        gsize length;
        int i;
        gboolean vpn_secrets = FALSE;
-       GError *verify_error = NULL;
+       KeyfileReaderInfo info = { 0 };
+       gs_free char *base_dir_free = NULL;
 
-       if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
-               g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-                                    "File did not exist or was not a regular file");
-               return NULL;
-       }
+       g_return_val_if_fail (keyfile, NULL);
+       g_return_val_if_fail (!error || !*error, NULL);
 
-       bad_permissions = statbuf.st_mode & 0077;
-
-       if (bad_permissions) {
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-                            "File permissions (%o) were insecure",
-                            statbuf.st_mode);
-               return NULL;
-       }
-
-       key_file = g_key_file_new ();
-       if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
-               goto out;
+       if (!base_dir) {
+               /* basedir is not given. Prefer it from the keyfile_name */
+               if (keyfile_name && keyfile_name[0] == '/') {
+                       base_dir = base_dir_free = g_path_get_dirname (keyfile_name);
+               } else {
+                       /* if keyfile is not given or not an absolute path, fallback
+                        * to current working directory. */
+                       base_dir = base_dir_free = g_get_current_dir ();
+               }
+       } else
+               g_return_val_if_fail ("/", NULL);
 
        connection = nm_simple_connection_new ();
 
-       groups = g_key_file_get_groups (key_file, &length);
+       info.connection = connection;
+       info.keyfile = (GKeyFile *) keyfile;
+       info.base_dir = base_dir;
+       info.handler = handler;
+       info.user_data = user_data;
+
+       groups = g_key_file_get_groups (keyfile, &length);
        for (i = 0; i < length; i++) {
                /* Only read out secrets when needed */
                if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
@@ -1303,10 +1450,15 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
                        continue;
                }
 
-               setting = read_setting (key_file, filename, groups[i]);
+               info.group = groups[i];
+               setting = read_setting (&info);
+               info.group = NULL;
+               if (info.error)
+                       goto out_error;
                if (setting)
                        nm_connection_add_setting (connection, setting);
        }
+       g_strfreev (groups);
 
        s_con = nm_connection_get_setting_connection (connection);
        if (!s_con) {
@@ -1315,19 +1467,21 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
        }
 
        /* Make sure that we have 'id' even if not explictly specified in the keyfile */
-       if (!nm_setting_connection_get_id (s_con)) {
+       if (   keyfile_name
+           && !nm_setting_connection_get_id (s_con)) {
                char *base_name;
 
-               base_name = g_path_get_basename (filename);
+               base_name = g_path_get_basename (keyfile_name);
                g_object_set (s_con, NM_SETTING_CONNECTION_ID, base_name, NULL);
                g_free (base_name);
        }
 
        /* Make sure that we have 'uuid' even if not explictly specified in the keyfile */
-       if (!nm_setting_connection_get_uuid (s_con)) {
+       if (   keyfile_name
+           && !nm_setting_connection_get_uuid (s_con)) {
                char *hashed_uuid;
 
-               hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", filename, NULL);
+               hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", keyfile_name, NULL);
                g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL);
                g_free (hashed_uuid);
        }
@@ -1339,7 +1493,7 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
            && nm_setting_connection_get_connection_type (s_con)) {
                char *interface_name;
 
-               interface_name = g_key_file_get_string (key_file,
+               interface_name = g_key_file_get_string (keyfile,
                                                        nm_setting_connection_get_connection_type (s_con),
                                                        "interface-name",
                                                        NULL);
@@ -1354,23 +1508,16 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
                NMSettingVpn *s_vpn;
 
                s_vpn = nm_connection_get_setting_vpn (connection);
-               if (s_vpn)
-                       read_vpn_secrets (key_file, s_vpn);
-       }
-
-       g_strfreev (groups);
-
-       /* Normalize and verify the connection */
-       if (!nm_connection_normalize (connection, NULL, NULL, &verify_error)) {
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-                                "invalid connection: %s",
-                            verify_error->message);
-               g_clear_error (&verify_error);
-               g_object_unref (connection);
-               connection = NULL;
+               if (s_vpn) {
+                       read_vpn_secrets (&info, s_vpn);
+                       if (info.error)
+                               goto out_error;
+               }
        }
 
-out:
-       g_key_file_free (key_file);
        return connection;
+out_error:
+       g_propagate_error (error, info.error);
+       g_free (connection);
+       return NULL;
 }
index 5581963..73752ff 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#ifndef _KEYFILE_PLUGIN_READER_H
-#define _KEYFILE_PLUGIN_READER_H
+#ifndef __NM_KEYFILE_READER_H__
+#define __NM_KEYFILE_READER_H__
 
 #include <glib.h>
-#include <nm-connection.h>
 
-NMConnection *nm_keyfile_plugin_connection_from_file (const char *filename, GError **error);
+#include "nm-connection.h"
 
-#endif /* _KEYFILE_PLUGIN_READER_H */
+
+typedef enum {
+       NM_KEYFILE_READ_TYPE_WARN               = 1,
+}  NMKeyfileReadType;
+
+/**
+ * NMKeyfileReadHandler:
+ *
+ * Hook to nm_keyfile_read(). The user might fail the reading by setting
+ * @error.
+ *
+ * Returns: should return TRUE, if the reading was handled. Otherwise,
+ * a default action will be performed that depends on the @type.
+ * For %NM_KEYFILE_READ_TYPE_WARN type, the default action is doing nothing.
+ */
+typedef gboolean (*NMKeyfileReadHandler) (GKeyFile *keyfile,
+                                          NMConnection *connection,
+                                          NMKeyfileReadType type,
+                                          void *type_data,
+                                          void *user_data,
+                                          GError **error);
+
+typedef enum {
+       NM_KEYFILE_WARN_SEVERITY_DEBUG                  = 1000,
+       NM_KEYFILE_WARN_SEVERITY_INFO                   = 2000,
+       NM_KEYFILE_WARN_SEVERITY_WARN                   = 3000,
+} NMKeyfileWarnSeverity;
+
+/**
+ * NMKeyfileReadTypeDataWarn:
+ *
+ * this struct is passed as @type_data for the @NMKeyfileReadHandler of
+ * type %NM_KEYFILE_READ_TYPE_WARN.
+ */
+typedef struct {
+       /* might be %NULL, if the warning is not about a group. */
+       const char *group;
+
+       /* might be %NULL, if the warning is not about a setting. */
+       NMSetting *setting;
+
+       /* might be %NULL, if the warning is not about a property. */
+       const char *property_name;
+
+       NMKeyfileWarnSeverity severity;
+       const char *message;
+} NMKeyfileReadTypeDataWarn;
+
+
+NMConnection *nm_keyfile_read (GKeyFile *keyfile,
+                               const char *keyfile_name,
+                               const char *base_dir,
+                               NMKeyfileReadHandler handler,
+                               void *user_data,
+                               GError **error);
+
+#endif /* __NM_KEYFILE_READER_H__ */
index 30400b1..e219871 100644 (file)
 #include <glib.h>
 #include <stdlib.h>
 #include <string.h>
-#include "gsystem-local-alloc.h"
-#include "utils.h"
-#include <nm-setting-wired.h>
-#include <nm-setting-wireless.h>
-#include <nm-setting-wireless-security.h>
 
-
-static const char temp_letters[] =
-"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
-/*
- * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()).
- */
-static gboolean
-check_mkstemp_suffix (const char *path)
-{
-       const char *ptr;
-
-       g_return_val_if_fail (path != NULL, FALSE);
-
-       /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */
-       ptr = strrchr (path, '.');
-       if (ptr && (strspn (ptr + 1, temp_letters) == 6) && (! ptr[7]))
-               return TRUE;
-       return FALSE;
-}
-
-static gboolean
-check_prefix (const char *base, const char *tag)
-{
-       int len, tag_len;
-
-       g_return_val_if_fail (base != NULL, TRUE);
-       g_return_val_if_fail (tag != NULL, TRUE);
-
-       len = strlen (base);
-       tag_len = strlen (tag);
-       if ((len > tag_len) && !g_ascii_strncasecmp (base, tag, tag_len))
-               return TRUE;
-       return FALSE;
-}
-
-static gboolean
-check_suffix (const char *base, const char *tag)
-{
-       int len, tag_len;
-
-       g_return_val_if_fail (base != NULL, TRUE);
-       g_return_val_if_fail (tag != NULL, TRUE);
-
-       len = strlen (base);
-       tag_len = strlen (tag);
-       if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag))
-               return TRUE;
-       return FALSE;
-}
-
-#define SWP_TAG ".swp"
-#define SWPX_TAG ".swpx"
-#define PEM_TAG ".pem"
-#define DER_TAG ".der"
-
-gboolean
-nm_keyfile_plugin_utils_should_ignore_file (const char *filename)
-{
-       gs_free char *base = NULL;
-
-       g_return_val_if_fail (filename != NULL, TRUE);
-
-       base = g_path_get_basename (filename);
-       g_return_val_if_fail (base != NULL, TRUE);
-
-       /* Ignore hidden and backup files */
-       /* should_ignore_file() must mirror escape_filename() */
-       if (check_prefix (base, ".") || check_suffix (base, "~"))
-               return TRUE;
-       /* Ignore temporary files */
-       if (check_mkstemp_suffix (base))
-               return TRUE;
-       /* Ignore 802.1x certificates and keys */
-       if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG))
-               return TRUE;
-
-       return FALSE;
-}
-
-char *
-nm_keyfile_plugin_utils_escape_filename (const char *filename)
-{
-       GString *str;
-       const char *f = filename;
-       const char ESCAPE_CHAR = '*';
-
-       /* keyfile used to escape with '*', do not change that behavior.
-        * But for newly added escapings, use '_' instead. */
-       const char ESCAPE_CHAR2 = '_';
-
-       g_return_val_if_fail (filename && filename[0], NULL);
-
-       str = g_string_sized_new (60);
-
-       /* Convert '/' to ESCAPE_CHAR */
-       for (f = filename; f[0]; f++) {
-               if (f[0] == '/')
-                       g_string_append_c (str, ESCAPE_CHAR);
-               else
-                       g_string_append_c (str, f[0]);
-       }
-
-       /* escape_filename() must avoid anything that should_ignore_file() would reject.
-        * We can escape here more aggressivly then what we would read back. */
-       if (check_prefix (str->str, "."))
-               str->str[0] = ESCAPE_CHAR2;
-       if (check_suffix (str->str, "~"))
-               str->str[str->len - 1] = ESCAPE_CHAR2;
-       if (   check_mkstemp_suffix (str->str)
-           || check_suffix (str->str, PEM_TAG)
-           || check_suffix (str->str, DER_TAG))
-               g_string_append_c (str, ESCAPE_CHAR2);
-
-       return g_string_free (str, FALSE);;
-}
+#include "nm-keyfile-utils.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
 
 
 typedef struct {
index 456cd1a..fd4334d 100644 (file)
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2010 Red Hat, Inc.
+ * (C) Copyright 2010-2015 Red Hat, Inc.
  */
 
-#ifndef _UTILS_H_
-#define _UTILS_H_
+#ifndef __NM_KEYFILE_UTILS_H__
+#define __NM_KEYFILE_UTILS_H__
 
 #include <glib.h>
-#include "common.h"
-#include "NetworkManagerUtils.h"
 
-#define NM_KEYFILE_CONNECTION_LOG_PATH(path)  str_if_set (path,"in-memory")
-#define NM_KEYFILE_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
-#define NM_KEYFILE_CONNECTION_LOG_ARG(con)    NM_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con))
-#define NM_KEYFILE_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
-#define NM_KEYFILE_CONNECTION_LOG_ARGD(con)   NM_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)), (con)
-
-gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename);
-
-char *nm_keyfile_plugin_utils_escape_filename (const char *filename);
+#define VPN_SECRETS_GROUP "vpn-secrets"
 
 const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
 
@@ -85,5 +75,5 @@ gboolean nm_keyfile_plugin_kf_has_key     (GKeyFile *kf,
                                            const char *key,
                                            GError **error);
 
-#endif  /* _UTILS_H_ */
+#endif  /* __NM_KEYFILE_UTILS_H__ */
 
index a707ac3..ec934fa 100644 (file)
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 #include <unistd.h>
 #include <stdio.h>
 #include <errno.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 <string.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 "nm-glib-compat.h"
-#include "nm-logging.h"
-#include "writer.h"
-#include "common.h"
-#include "utils.h"
+#include "nm-keyfile-writer.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
@@ -55,9 +63,7 @@
  * from the real setting name to the more-readable alias.
  */
 static void
-setting_alias_writer (GKeyFile *file,
-                      const char *keyfile_dir,
-                      const char *uuid,
+setting_alias_writer (KeyfileWriterInfo *info,
                       NMSetting *setting,
                       const char *key,
                       const GValue *value)
@@ -66,13 +72,13 @@ setting_alias_writer (GKeyFile *file,
 
        str = g_value_get_string (value);
        alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
-       nm_keyfile_plugin_kf_set_string (file,
+       nm_keyfile_plugin_kf_set_string (info->keyfile,
                                         nm_setting_get_name (setting),
                                         key,
                                         alias ? alias : str);
 }
 
-static gboolean
+static void
 write_array_of_uint (GKeyFile *file,
                      NMSetting *setting,
                      const char *key,
@@ -84,7 +90,7 @@ write_array_of_uint (GKeyFile *file,
 
        array = (GArray *) g_value_get_boxed (value);
        if (!array || !array->len)
-               return TRUE;
+               return;
 
        tmp_array = g_new (gint, array->len);
        for (i = 0; i < array->len; i++)
@@ -92,13 +98,10 @@ write_array_of_uint (GKeyFile *file,
 
        nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
        g_free (tmp_array);
-       return TRUE;
 }
 
 static void
-dns_writer (GKeyFile *file,
-            const char *keyfile_dir,
-            const char *uuid,
+dns_writer (KeyfileWriterInfo *info,
             NMSetting *setting,
             const char *key,
             const GValue *value)
@@ -107,7 +110,7 @@ dns_writer (GKeyFile *file,
 
        list = g_value_get_boxed (value);
        if (list && list[0]) {
-               nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key,
+               nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key,
                                                      (const char **) list, g_strv_length (list));
        }
 }
@@ -178,9 +181,7 @@ write_ip_values (GKeyFile *file,
 }
 
 static void
-addr_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
+addr_writer (KeyfileWriterInfo *info,
              NMSetting *setting,
              const char *key,
              const GValue *value)
@@ -191,13 +192,11 @@ addr_writer (GKeyFile *file,
 
        array = (GPtrArray *) g_value_get_boxed (value);
        if (array && array->len)
-               write_ip_values (file, setting_name, array, gateway, FALSE);
+               write_ip_values (info->keyfile, setting_name, array, gateway, FALSE);
 }
 
 static void
-ip4_addr_label_writer (GKeyFile *file,
-                       const char *keyfile_dir,
-                       const char *uuid,
+ip4_addr_label_writer (KeyfileWriterInfo *info,
                        NMSetting *setting,
                        const char *key,
                        const GValue *value)
@@ -206,9 +205,7 @@ ip4_addr_label_writer (GKeyFile *file,
 }
 
 static void
-gateway_writer (GKeyFile *file,
-                const char *keyfile_dir,
-                const char *uuid,
+gateway_writer (KeyfileWriterInfo *info,
                 NMSetting *setting,
                 const char *key,
                 const GValue *value)
@@ -217,9 +214,7 @@ gateway_writer (GKeyFile *file,
 }
 
 static void
-route_writer (GKeyFile *file,
-              const char *keyfile_dir,
-              const char *uuid,
+route_writer (KeyfileWriterInfo *info,
               NMSetting *setting,
               const char *key,
               const GValue *value)
@@ -229,7 +224,7 @@ route_writer (GKeyFile *file,
 
        array = (GPtrArray *) g_value_get_boxed (value);
        if (array && array->len)
-               write_ip_values (file, setting_name, array, NULL, TRUE);
+               write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
 }
 
 static void
@@ -271,9 +266,7 @@ write_hash_of_string (GKeyFile *file,
 }
 
 static void
-ssid_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
+ssid_writer (KeyfileWriterInfo *info,
              NMSetting *setting,
              const char *key,
              const GValue *value)
@@ -323,21 +316,19 @@ ssid_writer (GKeyFile *file,
                                ssid[j++] = ssid_data[i];
                        }
                }
-               nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid);
+               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 (file, setting_name, key, tmp_array, ssid_len);
+               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 (GKeyFile *file,
-                     const char *keyfile_dir,
-                     const char *uuid,
+password_raw_writer (KeyfileWriterInfo *info,
                      NMSetting *setting,
                      const char *key,
                      const GValue *value)
@@ -360,7 +351,7 @@ password_raw_writer (GKeyFile *file,
        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 (file, setting_name, key, tmp_array, len);
+       nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len);
        g_free (tmp_array);
 }
 
@@ -419,83 +410,15 @@ static const ObjectType objtypes[10] = {
        { NULL },
 };
 
-static gboolean
-write_cert_key_file (const char *path,
-                     const guint8 *data,
-                     gsize data_len,
-                     GError **error)
-{
-       char *tmppath;
-       int fd = -1, written;
-       gboolean success = FALSE;
-
-       tmppath = g_malloc0 (strlen (path) + 10);
-       g_assert (tmppath);
-       memcpy (tmppath, path, strlen (path));
-       strcat (tmppath, ".XXXXXX");
-
-       errno = 0;
-       fd = mkstemp (tmppath);
-       if (fd < 0) {
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "Could not create temporary file for '%s': %d",
-                            path, errno);
-               goto out;
-       }
-
-       /* Only readable by root */
-       errno = 0;
-       if (fchmod (fd, S_IRUSR | S_IWUSR) != 0) {
-               close (fd);
-               unlink (tmppath);
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "Could not set permissions for temporary file '%s': %d",
-                            path, errno);
-               goto out;
-       }
-
-       errno = 0;
-       written = write (fd, data, data_len);
-       if (written != data_len) {
-               close (fd);
-               unlink (tmppath);
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "Could not write temporary file for '%s': %d",
-                            path, errno);
-               goto out;
-       }
-       close (fd);
-
-       /* Try to rename */
-       errno = 0;
-       if (rename (tmppath, path) == 0)
-               success = TRUE;
-       else {
-               unlink (tmppath);
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "Could not rename temporary file to '%s': %d",
-                            path, errno);
-       }
-
-out:
-       g_free (tmppath);
-       return success;
-}
-
 static void
-cert_writer (GKeyFile *file,
-             const char *keyfile_dir,
-             const char *uuid,
+cert_writer (KeyfileWriterInfo *info,
              NMSetting *setting,
              const char *key,
              const GValue *value)
 {
-       const char *setting_name = nm_setting_get_name (setting);
-       NMSetting8021xCKScheme scheme;
-       NMSetting8021xCKFormat format;
-       const char *path = NULL, *ext = "pem";
        const ObjectType *objtype = NULL;
-       int i;
+       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) {
@@ -503,82 +426,48 @@ cert_writer (GKeyFile *file,
                        break;
                }
        }
-       if (!objtype) {
-               g_return_if_fail (objtype);
-               return;
-       }
-
-       scheme = objtype->scheme_func (NM_SETTING_802_1X (setting));
-       if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
-               path = objtype->path_func (NM_SETTING_802_1X (setting));
-               g_assert (path);
-
-               /* If the path is rooted in the keyfile directory, just use a
-                * relative path instead of an absolute one.
-                */
-               if (g_str_has_prefix (path, keyfile_dir)) {
-                       path += strlen (keyfile_dir);
-                       while (*path == '/')
-                               path++;
-               }
-
-               nm_keyfile_plugin_kf_set_string (file, setting_name, key, path);
-       } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-               GBytes *blob;
-               const guint8 *blob_data;
-               gsize blob_len;
-               gboolean success;
-               GError *error = NULL;
-               char *new_path;
-
-               blob = objtype->blob_func (NM_SETTING_802_1X (setting));
-               g_assert (blob);
-               blob_data = g_bytes_get_data (blob, &blob_len);
-
-               if (objtype->format_func) {
-                       /* Get the extension for a private key */
-                       format = objtype->format_func (NM_SETTING_802_1X (setting));
-                       if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
-                               ext = "p12";
-               } else {
-                       /* DER or PEM format certificate? */
-                       if (blob_len > 2 && blob_data[0] == 0x30 && blob_data[1] == 0x82)
-                               ext = "der";
-               }
+       if (!objtype)
+               g_return_if_reached ();
 
-               /* Write the raw data out to the standard file so that we can use paths
-                * from now on instead of pushing around the certificate data.
-                */
-               new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext);
-               g_assert (new_path);
+       if (!info->handler)
+               goto out_unhandled;
+
+       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 (info->connection,
+                          info->keyfile,
+                          NM_KEYFILE_WRITE_TYPE_CERT,
+                          &type_data,
+                          info->user_data,
+                          &info->error))
+               return;
 
-               success = write_cert_key_file (new_path, blob_data, blob_len, &error);
-               if (success) {
-                       /* Write the path value to the keyfile */
-                       nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path);
-               } else {
-                       nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s",
-                                    new_path, error->message);
-                       g_error_free (error);
-               }
-               g_free (new_path);
-       } 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 ();
+out_unhandled:
+
+       /* scheme_func() would not return UNKNOWN, because UNKNOWN happens only
+        * if the cert is unset (1) or if the cert is invalid (2).
+        * (1) cannot happen, because we only reach cert_writer() for non-default
+        * properties. (2) cannot happen, because we verified the connection.
+        *
+        * Hence, at this point we do have a certifiacte, but no default implementation
+        * to write it. The handler *must* do something with these certifications. */
+       if (!info->error) {
+               g_set_error (&info->error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+                            _("Failed to write unhandled certificate property %s.%s"),
+                            nm_setting_get_name (setting), key);
        }
 }
 
 typedef struct {
        const char *setting_name;
        const char *key;
-       void (*writer) (GKeyFile *keyfile,
-                       const char *keyfile_dir,
-                       const char *uuid,
+       void (*writer) (KeyfileWriterInfo *info,
                        NMSetting *setting,
                        const char *key,
                        const GValue *value);
@@ -648,12 +537,6 @@ static KeyWriter key_writers[] = {
        { NULL, NULL, NULL }
 };
 
-typedef struct {
-       GKeyFile *keyfile;
-       const char *keyfile_dir;
-       const char *uuid;
-} WriteInfo;
-
 static void
 write_setting_value (NMSetting *setting,
                      const char *key,
@@ -661,12 +544,15 @@ write_setting_value (NMSetting *setting,
                      GParamFlags flag,
                      gpointer user_data)
 {
-       WriteInfo *info = 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;
@@ -704,7 +590,7 @@ write_setting_value (NMSetting *setting,
        /* 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->keyfile, info->keyfile_dir, info->uuid, setting, key, value);
+                       (*writer->writer) (info, setting, key, value);
                        return;
                }
                writer++;
@@ -763,184 +649,42 @@ write_setting_value (NMSetting *setting,
        } else if (type == G_TYPE_HASH_TABLE) {
                write_hash_of_string (info->keyfile, setting, key, value);
        } else if (type == G_TYPE_ARRAY) {
-               if (!write_array_of_uint (info->keyfile, setting, key, value)) {
-                       nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
-                                    setting_name, key, g_type_name (type));
-               }
+               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 {
-               nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
-                            setting_name, key, g_type_name (type));
-       }
+       else
+               g_warn_if_reached ();
 }
 
-static gboolean
-_internal_write_connection (NMConnection *connection,
-                            const char *keyfile_dir,
-                            uid_t owner_uid,
-                            pid_t owner_grp,
-                            const char *existing_path,
-                            char **out_path,
-                            GError **error)
+GKeyFile *
+nm_keyfile_write (NMConnection *connection,
+                  NMKeyfileWriteHandler handler,
+                  void *user_data,
+                  GError **error)
 {
-       GKeyFile *key_file;
-       char *data;
-       gsize len;
-       gboolean success = FALSE;
-       char *path;
-       const char *id;
-       WriteInfo info;
-       GError *local_err = NULL;
+       KeyfileWriterInfo info = { 0 };
 
-       g_return_val_if_fail (!out_path || !*out_path, FALSE);
+       g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+       g_return_val_if_fail (!error || !*error, NULL);
 
        if (!nm_connection_verify (connection, error))
-               g_return_val_if_reached (FALSE);
-
-       id = nm_connection_get_id (connection);
-       g_assert (id && *id);
+               return NULL;
 
-       info.keyfile = key_file = g_key_file_new ();
-       info.keyfile_dir = keyfile_dir;
-       info.uuid = nm_connection_get_uuid (connection);
-       g_assert (info.uuid);
+       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);
-       data = g_key_file_to_data (key_file, &len, error);
-       if (!data)
-               goto out;
 
-       /* If we have existing file path, use it. Else generate one from
-        * connection's ID.
-        */
-       if (existing_path != NULL) {
-               path = g_strdup (existing_path);
-       } else {
-               char *filename_escaped = nm_keyfile_plugin_utils_escape_filename (id);
-
-               path = g_build_filename (keyfile_dir, filename_escaped, NULL);
-               g_free (filename_escaped);
+       if (info.error) {
+               g_propagate_error (error, info.error);
+               g_key_file_unref (info.keyfile);
+               return NULL;
        }
-
-       /* If a file with this path already exists (but isn't the existing path
-        * of the connection) then we need another name.  Multiple connections
-        * can have the same ID (ie if two connections with the same ID are visible
-        * to different users) but of course can't have the same path.  Yeah,
-        * there's a race here, but there's not a lot we can do about it, and
-        * we shouldn't get more than one connection with the same UUID either.
-        */
-       if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) {
-               guint i;
-               gboolean name_found = FALSE;
-
-               /* A keyfile with this connection's ID already exists. Pick another name. */
-               for (i = 0; i < 100; i++) {
-                       char *filename, *filename_escaped;
-
-                       if (i == 0)
-                               filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection));
-                       else
-                               filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i);
-
-                       filename_escaped = nm_keyfile_plugin_utils_escape_filename (filename);
-
-                       g_free (path);
-                       path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped);
-                       g_free (filename);
-                       g_free (filename_escaped);
-                       if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) {
-                               name_found = TRUE;
-                               break;
-                       }
-               }
-               if (!name_found) {
-                       if (existing_path == NULL) {
-                               /* this really should not happen, we tried hard to find an unused name... bail out. */
-                               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                                                   "could not find suitable keyfile file name (%s already used)", path);
-                               g_free (path);
-                               goto out;
-                       }
-                       /* Both our preferred path based on connection id and id-uuid are taken.
-                        * Fallback to @existing_path */
-                       g_free (path);
-                       path = g_strdup (existing_path);
-               }
-       }
-
-       /* In case of updating the connection and changing the file path,
-        * we need to remove the old one, not to end up with two connections.
-        */
-       if (existing_path != NULL && strcmp (path, existing_path) != 0)
-               unlink (existing_path);
-
-       g_file_set_contents (path, data, len, &local_err);
-       if (local_err) {
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "%s.%d: error writing to file '%s': %s", __FILE__, __LINE__,
-                            path, local_err->message);
-               g_error_free (local_err);
-               g_free (path);
-               goto out;
-       }
-
-       if (chown (path, owner_uid, owner_grp) < 0) {
-               g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            "%s.%d: error chowning '%s': %d", __FILE__, __LINE__,
-                            path, errno);
-               unlink (path);
-       } else {
-               if (chmod (path, S_IRUSR | S_IWUSR) < 0) {
-                       g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                                    "%s.%d: error setting permissions on '%s': %d", __FILE__,
-                                    __LINE__, path, errno);
-                       unlink (path);
-               } else {
-                       if (out_path && g_strcmp0 (existing_path, path)) {
-                               *out_path = path;  /* pass path out to caller */
-                               path = NULL;
-                       }
-                       success = TRUE;
-               }
-       }
-       g_free (path);
-
-out:
-       g_free (data);
-       g_key_file_free (key_file);
-       return success;
-}
-
-gboolean
-nm_keyfile_plugin_write_connection (NMConnection *connection,
-                                    const char *existing_path,
-                                    char **out_path,
-                                    GError **error)
-{
-       return _internal_write_connection (connection,
-                                          KEYFILE_DIR,
-                                          0, 0,
-                                          existing_path,
-                                          out_path,
-                                          error);
-}
-
-gboolean
-nm_keyfile_plugin_write_test_connection (NMConnection *connection,
-                                         const char *keyfile_dir,
-                                         uid_t owner_uid,
-                                         pid_t owner_grp,
-                                         char **out_path,
-                                         GError **error)
-{
-       return _internal_write_connection (connection,
-                                          keyfile_dir,
-                                          owner_uid, owner_grp,
-                                          NULL,
-                                          out_path,
-                                          error);
+       return info.keyfile;
 }
 
index a602f2f..891778a 100644 (file)
  * Copyright (C) 2008 - 2011 Red Hat, Inc.
  */
 
-#ifndef _KEYFILE_PLUGIN_WRITER_H
-#define _KEYFILE_PLUGIN_WRITER_H
+#ifndef __NM_KEYFILE_WRITER_H__
+#define __NM_KEYFILE_WRITER_H__
 
 #include <sys/types.h>
 #include <glib.h>
-#include <nm-connection.h>
 
-gboolean nm_keyfile_plugin_write_connection (NMConnection *connection,
-                                             const char *existing_path,
-                                             char **out_path,
-                                             GError **error);
+#include "nm-connection.h"
+#include "nm-setting-8021x.h"
 
-gboolean nm_keyfile_plugin_write_test_connection (NMConnection *connection,
-                                                  const char *keyfile_dir,
-                                                  uid_t owner_uid,
-                                                  pid_t owner_grp,
-                                                  char **out_path,
-                                                  GError **error);
 
-#endif /* _KEYFILE_PLUGIN_WRITER_H */
+typedef enum {
+       NM_KEYFILE_WRITE_TYPE_CERT              = 1,
+} NMKeyfileWriteType;
+
+/**
+ * NMKeyfileWriteHandler:
+ *
+ * This is a hook to tweak the serialization.
+ *
+ * Handler for certain properties or events that are not entirely contained
+ * within the keyfile or that might be serialized differently. The @type and
+ * @type_data arguments tell which kind of argument we have at hand.
+ *
+ * Currently only the type %NM_KEYFILE_WRITE_TYPE_CERT is supported, which provides
+ * @type_data as %NMKeyfileWriteTypeDataCert. However, this handler should be generic enough
+ * to support other types as well.
+ *
+ * This don't have to be only "properties". For example, nm_keyfile_read() uses
+ * a similar handler to push warnings to the caller.
+ *
+ * If the handler raises an error, it should set the @error value. This causes
+ * the an overall failure.
+ *
+ * Returns: whether the issue was handled. If the type was unhandled,
+ * a default action will be performed. This might be raise an error,
+ * do some fallback parsing, or do nothing.
+ */
+typedef gboolean (*NMKeyfileWriteHandler) (NMConnection *connection,
+                                           GKeyFile *keyfile,
+                                           NMKeyfileWriteType type,
+                                           void *type_data,
+                                           void *user_data,
+                                           GError **error);
+
+/**
+ * NMKeyfileWriteTypeDataCert:
+ *
+ * this struct is passed as @type_data for the @NMKeyfileWriteHandler of
+ * type %NM_KEYFILE_WRITE_TYPE_CERT.
+ */
+typedef struct {
+       NMSetting8021x *setting;
+       const char *property_name;
+
+       /* The following functions are helpers that simplify the implementation
+        * of the handler. */
+       const char *suffix;
+       NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
+       NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
+       const char *           (*path_func)   (NMSetting8021x *setting);
+       GBytes *               (*blob_func)   (NMSetting8021x *setting);
+} NMKeyfileWriteTypeDataCert;
+
+
+GKeyFile *nm_keyfile_write (NMConnection *connection,
+                            NMKeyfileWriteHandler handler,
+                            void *user_data,
+                            GError **error);
+
+#endif /* __NM_KEYFILE_WRITER_H__ */
index 926dd6c..6c09b21 100644 (file)
@@ -48,6 +48,8 @@ libnm-core/crypto.c
 libnm-core/crypto_gnutls.c
 libnm-core/crypto_nss.c
 libnm-core/nm-connection.c
+libnm-core/nm-keyfile-reader.c
+libnm-core/nm-keyfile-writer.c
 libnm-core/nm-setting-8021x.c
 libnm-core/nm-setting-adsl.c
 libnm-core/nm-setting-bluetooth.c