Sophie

Sophie

distrib > Mandriva > 2010.2 > i586 > media > contrib-release-src > by-pkgid > 3b4d2e1e3990999df1b5a5eb8d79a049 > files > 4

networkmanager-0.8-3mdv2010.1.src.rpm

diff --git a/README.mdv b/README.mdv
new file mode 100644
index 0000000..989ca01
--- /dev/null
+++ b/README.mdv
@@ -0,0 +1,14 @@
+The ifcfg-mdv is a BETA plugin, which adds support for Mandriva-specific
+variables in /etc/sysconfig/network-scripts/ifcfg-* files to network
+manager.
+
+This is not a production-ready version, nor it is officially supported as of
+now. However, it works fine for me, so - at least in theory - it should work
+for some of you out there as well.
+
+To get the most up-to-date patch against mainstream NetworkManager code,
+just run:
+# git diff master mdv
+
+The 'patches' directory contains mostly up-to-date patches to be used with
+Mandriva's networkmanager package.
diff --git a/configure.ac b/configure.ac
index 124f47c..8667cb9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -461,6 +461,7 @@ system-settings/plugins/ifupdown/Makefile
 system-settings/plugins/ifcfg-rh/Makefile
 system-settings/plugins/ifcfg-rh/tests/Makefile
 system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile
+system-settings/plugins/ifcfg-mdv/Makefile
 system-settings/plugins/ifcfg-suse/Makefile
 system-settings/plugins/keyfile/Makefile
 system-settings/plugins/keyfile/io/Makefile
diff --git a/initscript/Mandriva/networkmanager.in b/initscript/Mandriva/networkmanager.in
index dac14e7..1214563 100644
--- a/initscript/Mandriva/networkmanager.in
+++ b/initscript/Mandriva/networkmanager.in
@@ -12,6 +12,7 @@
 ### BEGIN INIT INFO
 # Provides: networkmanager
 # Required-Start: $network
+# Should-Start: $named
 # Required-Stop: $network
 # Default-Start: 3 4 5
 # Short-Description: Daemon for automatically switching to best network connection.
@@ -46,10 +47,6 @@ start()
 	echo $"Setting network parameters... "
 	sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
 
-	if [ ! -e /var/lock/subsys/named ]; then
-		service named start >/dev/null 2>&1
-	fi
-
 	echo -n $"Starting NetworkManager daemon: "
 	daemon --check $servicename $processname --pid-file=$pidfile
 	RETVAL=$?
diff --git a/system-settings/plugins/Makefile.am b/system-settings/plugins/Makefile.am
index 94f7560..40bc052 100644
--- a/system-settings/plugins/Makefile.am
+++ b/system-settings/plugins/Makefile.am
@@ -9,6 +9,7 @@ SUBDIRS+=ifcfg-suse
 endif
 
 if TARGET_MANDRIVA
+SUBDIRS+=ifcfg-mdv
 SUBDIRS+=ifcfg-rh
 endif
 
diff --git a/system-settings/plugins/ifcfg-mdv/Makefile.am b/system-settings/plugins/ifcfg-mdv/Makefile.am
new file mode 100644
index 0000000..3646ac7
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/Makefile.am
@@ -0,0 +1,64 @@
+SUBDIRS=.
+
+pkglib_LTLIBRARIES = libnm-settings-plugin-ifcfg-mdv.la
+
+noinst_LTLIBRARIES = libifcfg-mdv-io.la
+
+libifcfg_mdv_io_la_SOURCES = \
+	parse_wpa_supplicant_conf.c \
+	parse_wpa_supplicant_conf.h \
+	reader.c \
+	reader.h \
+	writer.c \
+	writer.h \
+	common.h \
+	../ifcfg-rh/shvar.c \
+	../ifcfg-rh/shvar.h \
+	../ifcfg-rh/errors.c \
+	../ifcfg-rh/utils.c \
+	../ifcfg-rh/utils.h
+
+INCLUDES = \
+	-I$(top_srcdir)/system-settings/plugins/ifcfg-rh \
+	-I$(top_srcdir)/src/system-settings \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-glib \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_builddir)/marshallers
+
+libifcfg_mdv_io_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(NSS_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DSBINDIR=\"$(sbindir)\"
+
+libifcfg_mdv_io_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(NSS_LIBS)
+
+libnm_settings_plugin_ifcfg_mdv_la_SOURCES = \
+	plugin.c \
+	plugin.h \
+	nm-ifcfg-connection.c \
+	nm-ifcfg-connection.h
+
+libnm_settings_plugin_ifcfg_mdv_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DSYSCONFDIR=\"$(sysconfdir)\"
+
+libnm_settings_plugin_ifcfg_mdv_la_LDFLAGS = -module -avoid-version
+libnm_settings_plugin_ifcfg_mdv_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/libnm-glib/libnm-glib.la \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	libifcfg-mdv-io.la \
+	$(GLIB_LIBS) \
+	$(GMODULE_LIBS) \
+	$(GIO_LIBS)
+
diff --git a/system-settings/plugins/ifcfg-mdv/common.h b/system-settings/plugins/ifcfg-mdv/common.h
new file mode 100644
index 0000000..edd5e69
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/common.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2008 - 2009 Red Hat, Inc.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <glib.h>
+
+#define IFCFG_TAG "ifcfg-"
+#define KEYS_TAG "keys-"
+#define ROUTE_TAG "route-"
+#define ROUTE6_TAG "route6-"
+
+#define BAK_TAG ".bak"
+#define TILDE_TAG "~"
+#define ORIG_TAG ".orig"
+#define REJ_TAG ".rej"
+#define RPMNEW_TAG ".rpmnew"
+
+#define IFCFG_DIR SYSCONFDIR"/sysconfig/network-scripts"
+
+#define IFCFG_PLUGIN_NAME "ifcfg-mdv"
+#define IFCFG_PLUGIN_INFO "(c) 2009 - 2010 Eugeni Dodonov <eugeni@mandriva.com>."
+
+#define TYPE_ETHERNET "Ethernet"
+#define TYPE_WIRELESS "Wireless"
+
+GQuark ifcfg_plugin_error_quark (void);
+
+
+#endif  /* __COMMON_H__ */
+
diff --git a/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.c b/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.c
new file mode 100644
index 0000000..42bf1e6
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.c
@@ -0,0 +1,374 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2009 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include <glib/gstdio.h>
+
+#include <NetworkManager.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-8021x.h>
+#include <nm-settings-connection-interface.h>
+
+#include "common.h"
+#include "nm-ifcfg-connection.h"
+#include "reader.h"
+#include "utils.h"
+#include "writer.h"
+#include "nm-inotify-helper.h"
+
+#include "parse_wpa_supplicant_conf.h"
+
+static NMSettingsConnectionInterface *parent_settings_connection_iface;
+
+static void settings_connection_interface_init (NMSettingsConnectionInterface *klass);
+
+G_DEFINE_TYPE_EXTENDED (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SYSCONFIG_CONNECTION, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_CONNECTION_INTERFACE,
+                                               settings_connection_interface_init))
+
+#define NM_IFCFG_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionPrivate))
+
+typedef struct {
+	gulong ih_event_id;
+
+	char *filename;
+	int file_wd;
+
+	char *keyfile;
+	int keyfile_wd;
+
+	char *routefile;
+	int routefile_wd;
+
+	char *route6file;
+	int route6file_wd;
+
+	char *udi;
+	char *unmanaged;
+} NMIfcfgConnectionPrivate;
+
+enum {
+	PROP_0,
+	PROP_FILENAME,
+	PROP_UNMANAGED,
+	PROP_UDI,
+
+	LAST_PROP
+};
+
+/* Signals */
+enum {
+	IFCFG_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+files_changed_cb (NMInotifyHelper *ih,
+                  struct inotify_event *evt,
+                  const char *path,
+                  gpointer user_data)
+{
+	NMIfcfgConnection *self = NM_IFCFG_CONNECTION (user_data);
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
+
+	if ((evt->wd != priv->file_wd) && (evt->wd != priv->keyfile_wd) && (evt->wd != priv->routefile_wd) && (evt->wd != priv->route6file_wd))
+		return;
+
+	/* push the event up to the plugin */
+	g_signal_emit (self, signals[IFCFG_CHANGED], 0);
+}
+
+NMIfcfgConnection *
+nm_ifcfg_connection_new (const char *filename,
+                         GError **error,
+                         gboolean *ignore_error)
+{
+	GObject *object;
+	NMIfcfgConnectionPrivate *priv;
+	NMConnection *tmp;
+	char *unmanaged = NULL;
+	char *keyfile = NULL;
+	char *routefile = NULL;
+	char *route6file = NULL;
+	NMInotifyHelper *ih;
+
+	g_return_val_if_fail (filename != NULL, NULL);
+
+	tmp = connection_from_file (filename, NULL, NULL, NULL, &unmanaged, &keyfile, &routefile, &route6file, error, ignore_error);
+	if (!tmp)
+		return NULL;
+
+	object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION,
+	                                   NM_IFCFG_CONNECTION_FILENAME, filename,
+	                                   NM_IFCFG_CONNECTION_UNMANAGED, unmanaged,
+	                                   NULL);
+	if (!object) {
+		g_object_unref (tmp);
+		return NULL;
+	}
+
+	/* Update our settings with what was read from the file */
+	nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (object), tmp, FALSE, NULL);
+	g_object_unref (tmp);
+
+	priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
+
+	ih = nm_inotify_helper_get ();
+	priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), object);
+
+	priv->file_wd = nm_inotify_helper_add_watch (ih, filename);
+
+	priv->keyfile = keyfile;
+	priv->keyfile_wd = nm_inotify_helper_add_watch (ih, keyfile);
+
+	priv->routefile = routefile;
+	priv->routefile_wd = nm_inotify_helper_add_watch (ih, routefile);
+
+	priv->route6file = route6file;
+	priv->route6file_wd = nm_inotify_helper_add_watch (ih, route6file);
+
+	return NM_IFCFG_CONNECTION (object);
+}
+
+const char *
+nm_ifcfg_connection_get_filename (NMIfcfgConnection *self)
+{
+	g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL);
+
+	return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->filename;
+}
+
+const char *
+nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self)
+{
+	g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), FALSE);
+
+	return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unmanaged;
+}
+
+static gboolean
+update (NMSettingsConnectionInterface *connection,
+	    NMSettingsConnectionInterfaceUpdateFunc callback,
+	    gpointer user_data)
+{
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection);
+	GError *error = NULL;
+
+	if (!writer_update_connection (NM_CONNECTION (connection),
+	                               IFCFG_DIR,
+	                               priv->filename,
+	                               priv->keyfile,
+	                               &error)) {
+		callback (connection, error, user_data);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return parent_settings_connection_iface->update (connection, callback, user_data);
+}
+
+static gboolean 
+do_delete (NMSettingsConnectionInterface *connection,
+	       NMSettingsConnectionInterfaceDeleteFunc callback,
+	       gpointer user_data)
+{
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection);
+	NMSettingWireless *s_wireless;
+	WPANetwork *wpan;
+	const GByteArray *ssid = NULL;
+
+	s_wireless = (NMSettingWireless *)nm_connection_get_setting(NM_CONNECTION(connection), NM_TYPE_SETTING_WIRELESS);
+	if (s_wireless)
+		ssid = nm_setting_wireless_get_ssid(s_wireless);
+
+	/* Delete network block from wpa_supplicant.conf */
+	if (ssid) {
+		wpan = ifcfg_mdv_wpa_network_new(NULL);
+		if (wpan) {
+			ifcfg_mdv_wpa_network_set_ssid(wpan, ssid);
+			ifcfg_mdv_wpa_network_set_val(wpan, "__DELETE__", "yes");
+			ifcfg_mdv_wpa_network_save(wpan, "/etc/wpa_supplicant.conf", NULL);
+			ifcfg_mdv_wpa_network_free(wpan);
+		}
+	}
+
+	g_unlink (priv->filename);
+	if (priv->keyfile)
+		g_unlink (priv->keyfile);
+	if (priv->routefile)
+		g_unlink (priv->routefile);
+
+	if (priv->route6file)
+		g_unlink (priv->route6file);
+
+	return parent_settings_connection_iface->delete (connection, callback, user_data);
+}
+
+/* GObject */
+
+static void
+settings_connection_interface_init (NMSettingsConnectionInterface *iface)
+{
+	parent_settings_connection_iface = g_type_interface_peek_parent (iface);
+	iface->update = update;
+	iface->delete = do_delete;
+}
+
+static void
+nm_ifcfg_connection_init (NMIfcfgConnection *connection)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
+	NMInotifyHelper *ih;
+
+	g_free (priv->udi);
+
+	nm_connection_clear_secrets (NM_CONNECTION (object));
+
+	ih = nm_inotify_helper_get ();
+
+	g_signal_handler_disconnect (ih, priv->ih_event_id);
+
+	g_free (priv->filename);
+	if (priv->file_wd >= 0)
+		nm_inotify_helper_remove_watch (ih, priv->file_wd);
+
+	g_free (priv->keyfile);
+	if (priv->keyfile_wd >= 0)
+		nm_inotify_helper_remove_watch (ih, priv->keyfile_wd);
+
+	g_free (priv->routefile);
+	if (priv->routefile_wd >= 0)
+		nm_inotify_helper_remove_watch (ih, priv->routefile_wd);
+
+	g_free (priv->route6file);
+	if (priv->route6file_wd >= 0)
+		nm_inotify_helper_remove_watch (ih, priv->route6file_wd);
+
+	G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		/* Construct only */
+		priv->filename = g_value_dup_string (value);
+		break;
+	case PROP_UNMANAGED:
+		priv->unmanaged = g_value_dup_string (value);
+		break;
+	case PROP_UDI:
+		/* Construct only */
+		priv->udi = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	case PROP_UNMANAGED:
+		g_value_set_string (value, priv->unmanaged);
+		break;
+	case PROP_UDI:
+		g_value_set_string (value, priv->udi);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ifcfg_connection_class);
+
+	g_type_class_add_private (ifcfg_connection_class, sizeof (NMIfcfgConnectionPrivate));
+
+	/* Virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_FILENAME,
+		 g_param_spec_string (NM_IFCFG_CONNECTION_FILENAME,
+						  "FileName",
+						  "File name",
+						  NULL,
+						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_UNMANAGED,
+		 g_param_spec_string (NM_IFCFG_CONNECTION_UNMANAGED,
+						  "Unmanaged",
+						  "Unmanaged",
+						  NULL,
+						  G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_UDI,
+		 g_param_spec_string (NM_IFCFG_CONNECTION_UDI,
+						  "UDI",
+						  "UDI",
+						  NULL,
+						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	signals[IFCFG_CHANGED] =
+		g_signal_new ("ifcfg-changed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              0, NULL, NULL,
+		              g_cclosure_marshal_VOID__VOID,
+		              G_TYPE_NONE, 0);
+}
+
diff --git a/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.h b/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.h
new file mode 100644
index 0000000..5cac5d9
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/nm-ifcfg-connection.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_IFCFG_CONNECTION_H
+#define NM_IFCFG_CONNECTION_H
+
+G_BEGIN_DECLS
+
+#include <NetworkManager.h>
+#include <nm-sysconfig-connection.h>
+
+#define NM_TYPE_IFCFG_CONNECTION            (nm_ifcfg_connection_get_type ())
+#define NM_IFCFG_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection))
+#define NM_IFCFG_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
+#define NM_IS_IFCFG_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION))
+#define NM_IS_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION))
+#define NM_IFCFG_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass))
+
+#define NM_IFCFG_CONNECTION_FILENAME  "filename"
+#define NM_IFCFG_CONNECTION_UNMANAGED "unmanaged"
+#define NM_IFCFG_CONNECTION_UDI       "udi"
+
+typedef struct {
+	NMSysconfigConnection parent;
+} NMIfcfgConnection;
+
+typedef struct {
+	NMSysconfigConnectionClass parent;
+} NMIfcfgConnectionClass;
+
+GType nm_ifcfg_connection_get_type (void);
+
+NMIfcfgConnection *nm_ifcfg_connection_new (const char *filename,
+                                            GError **error,
+                                            gboolean *ignore_error);
+
+const char *nm_ifcfg_connection_get_filename (NMIfcfgConnection *self);
+
+const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self);
+
+gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self,
+                                     GHashTable *new_settings,
+                                     GError **error);
+
+G_END_DECLS
+
+#endif /* NM_IFCFG_CONNECTION_H */
diff --git a/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.c b/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.c
new file mode 100644
index 0000000..e879c26
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.c
@@ -0,0 +1,536 @@
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "utils.h"
+#include "common.h"
+
+#include "parse_wpa_supplicant_conf.h"
+
+struct _WPAConfig {
+	gchar		*file;	/* wpa_supplicant.conf file name */
+	GString		*line;	/* Input buffer */
+	GRegex		*skip;	/* Filter for comments */
+	GRegex		*network;	/* Start of network definition */
+	GRegex		*fini;		/* Closing curly bracket */
+	GRegex		*keyval;	/* (key, val) pair in network def */
+	GSList		*list;		/* list of networks */
+	GSList		*next;		/* list iterator */
+};
+
+struct _WPANetwork {
+	WPAConfig	*parent;	/* IO channel etc */
+	GHashTable	*keyvals;	/* content */
+};
+
+static gchar *
+parse_wpa_string(const gchar *value, gsize *len)
+{
+	gchar *str;
+	gsize l;
+
+	if (*value == '"') {
+		const gchar *pos;
+		value++;
+		pos = strrchr(value, '"');
+		if (pos == NULL || pos[1] != '\0')
+			return NULL;
+		l = pos - value;
+		str = g_malloc(l + 1);
+		if (str == NULL)
+			return NULL;
+		memcpy(str, value, l);
+		str[l] = '\0';
+	} else {
+		l = strlen(value);
+		str = utils_hexstr2bin(value, l);
+		if (str == NULL)
+			return NULL;
+	}
+
+	if (len)
+		*len = l;
+	return str;
+}
+
+
+WPAConfig *
+ifcfg_mdv_wpa_config_new(gchar *file)
+{
+	WPAConfig *wpac;
+
+	g_return_val_if_fail(file != NULL, NULL);
+
+	wpac = g_new(WPAConfig, 1);
+	if (!wpac)
+		return NULL;
+
+	wpac->file = g_strdup(file);
+	wpac->line = g_string_new("");
+
+	wpac->skip = g_regex_new("^\\s*(#.*)?$", 0, 0, NULL);
+	wpac->network = g_regex_new("^\\s*network\\s*=\\s*{\\s*$", 0, 0, NULL);
+	wpac->fini = g_regex_new("^\\s*}\\s*$", 0, 0, NULL);
+	wpac->keyval = g_regex_new("^\\s*([\\w\\d]+)\\s*=\\s*(\\S+.*\\S*)\\s*$", 0, 0, NULL);
+	wpac->list = NULL;
+	wpac->next = NULL;
+
+	if (!wpac->file || !wpac->line || !wpac->skip ||
+	    !wpac->network || !wpac->fini || !wpac->keyval) {
+		ifcfg_mdv_wpa_config_free(wpac);
+		return NULL;
+	}
+
+	return wpac;
+}
+
+void
+ifcfg_mdv_wpa_config_free(WPAConfig *wpac)
+{
+	GSList *l;
+
+	if (!wpac)
+		return;
+
+	for (l = wpac->list; l; l = g_slist_next(l))
+		ifcfg_mdv_wpa_network_free(l->data);
+
+	g_slist_free(wpac->list);
+
+	g_regex_unref(wpac->skip);
+	g_regex_unref(wpac->network);
+	g_regex_unref(wpac->fini);
+	g_regex_unref(wpac->keyval);
+
+	g_string_free(wpac->line, TRUE);
+	g_free(wpac->file);
+
+	g_free(wpac);
+}
+
+WPANetwork *
+ifcfg_mdv_wpa_network_new(WPAConfig *wpac)
+{
+	WPANetwork *wpan;
+
+	// g_return_val_if_fail(wpac != NULL, NULL);
+
+	wpan = g_new(WPANetwork, 1);
+	if (!wpan)
+		return NULL;
+
+	wpan->keyvals = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	if (!wpan->keyvals) {
+		g_free(wpan);
+		return NULL;
+	}
+	wpan->parent = wpac;
+
+	return wpan;
+}
+
+void
+ifcfg_mdv_wpa_network_free(WPANetwork *wpan)
+{
+	if (!wpan)
+		return;
+
+	g_hash_table_destroy(wpan->keyvals);
+	g_free(wpan);
+}
+
+static void
+free_list(GSList **list)
+{
+	GSList *n;
+
+	for (n = *list; n; n = g_slist_next(n))
+		g_free(n->data);
+	if (*list)
+		g_slist_free(*list);
+	*list = NULL;
+}
+
+gboolean
+ifcfg_mdv_wpa_config_parse(WPAConfig *wpac)
+{
+	WPANetwork *wpan = NULL;
+	GIOChannel *ioc;
+	GMatchInfo *mi;
+	GError *error = NULL;
+
+	g_return_val_if_fail(wpac != NULL, FALSE);
+
+	ioc = g_io_channel_new_file(wpac->file, "r", &error);
+	if (!ioc) {
+		if (error->code == G_FILE_ERROR_NOENT) {
+			g_error_free(error);
+			return TRUE;
+		}
+		return FALSE;
+	}
+
+	while (g_io_channel_read_line_string(ioc, wpac->line, NULL, NULL) == G_IO_STATUS_NORMAL) {
+
+		if (g_regex_match(wpac->skip, wpac->line->str, 0, NULL))
+			continue;
+
+		if (!wpan && g_regex_match(wpac->network, wpac->line->str, 0, NULL)) {
+			wpan = ifcfg_mdv_wpa_network_new(wpac);
+			if (!wpan)
+				return FALSE;
+			continue;
+		}
+
+		if (wpan && g_regex_match(wpac->keyval, wpac->line->str, 0, &mi)) {
+			gchar *key = g_match_info_fetch(mi, 1);
+			gchar *val = g_match_info_fetch(mi, 2);
+			ifcfg_mdv_wpa_network_set_val(wpan, key, val);
+			g_free(key);
+			g_free(val);
+			continue;
+		}
+
+		if (wpan && g_regex_match(wpac->fini, wpac->line->str, 0, NULL)) {
+			wpac->list = g_slist_prepend(wpac->list, wpan);
+			wpac->next = wpac->list;
+			wpan = NULL;
+		}
+	}
+
+	g_match_info_free(mi);
+	g_io_channel_unref(ioc);
+
+	return TRUE;
+}
+
+WPANetwork *
+ifcfg_mdv_wpa_config_next(WPAConfig *wpac)
+{
+	GSList *l = wpac->next;
+
+	if (l)
+		wpac->next = g_slist_next(l);
+	else
+		wpac->next = wpac->list;
+
+	return l == NULL ? NULL : l->data;
+}
+
+void
+ifcfg_mdv_wpa_config_rewind(WPAConfig *wpac)
+{
+		wpac->next = wpac->list;
+}
+
+gpointer
+ifcfg_mdv_wpa_network_get_val(WPANetwork *wpan, const gchar *key)
+{
+	g_return_val_if_fail(wpan != NULL, NULL);
+
+	return g_hash_table_lookup(wpan->keyvals, key);
+}
+
+void
+ifcfg_mdv_wpa_network_set_val(WPANetwork *wpan, const gchar *key, const gchar *val)
+{
+	gchar *k, *v;
+
+	g_return_if_fail(wpan != NULL);
+	g_return_if_fail(key != NULL);
+	g_return_if_fail(val != NULL);
+
+	k = g_strdup(key);
+	v = g_strdup(val);
+	g_hash_table_replace(wpan->keyvals, k, v);
+}
+
+gchar *
+ifcfg_mdv_wpa_network_get_str(WPANetwork *wpan, const gchar *key)
+{
+	gchar *value;
+
+	g_return_val_if_fail(wpan != NULL, NULL);
+	g_return_val_if_fail(key != NULL, NULL);
+
+	value = ifcfg_mdv_wpa_network_get_val(wpan, key);
+	if (!value)
+		return NULL;
+
+	return parse_wpa_string(value, NULL);
+}
+
+GByteArray *
+ifcfg_mdv_wpa_network_get_ssid(WPANetwork *wpan)
+{
+	gchar *value, *ssid;
+	gsize len;
+	GByteArray *a;
+
+	g_return_val_if_fail(wpan != NULL, NULL);
+
+	value = ifcfg_mdv_wpa_network_get_val(wpan, "ssid");
+	if (!value)
+		return NULL;
+
+	ssid = parse_wpa_string(value, &len);
+	if (!ssid)
+		return NULL;
+	if (len == 0 || len > 32)
+		goto error;
+
+	a = g_byte_array_sized_new (len);
+	if (!a)
+		goto error;
+
+	g_byte_array_append (a, (const guint8 *) ssid, len);
+	g_free(ssid);
+	return a;
+
+error:
+	g_free(ssid);
+	return NULL;
+}
+
+void
+ifcfg_mdv_wpa_network_set_ssid(WPANetwork *wpan, const GByteArray *val)
+{
+	gchar buf[33];
+
+	g_return_if_fail(wpan != NULL);
+	g_return_if_fail(wpan != NULL);
+
+	if (val->len == 0 || val->len > 32)
+		return;
+
+	memcpy(buf, val->data, val->len);
+	buf[val->len] = '\0';
+	ifcfg_mdv_wpa_network_set_str(wpan, "ssid", buf);
+}
+
+void
+ifcfg_mdv_wpa_network_set_str(WPANetwork *wpan, const gchar *key, const gchar *val)
+{
+	const gchar *p;
+	gchar *str;
+	gboolean need_hex = FALSE;
+
+	/* We may get NULL for non-existing values */
+	if (!val) {
+		ifcfg_mdv_wpa_network_unset(wpan, key);
+		return;
+	}
+
+	for (p = val; *p; p++)
+		if (!g_ascii_isprint(*p)) {
+			need_hex = TRUE;
+			break;
+		}
+
+	if (need_hex)
+		str = utils_bin2hexstr(val, strlen(val), -1);
+	else
+		str = g_strdup_printf("\"%s\"", val);
+
+	if (str)
+		ifcfg_mdv_wpa_network_set_val(wpan, key, str);
+#if 0
+	else
+		PLUGIN_WARN(IFCFG_PLUGIN_NAME, "    warning: could not set value for wpa key %s", key);
+#endif
+	g_free(str);
+}
+
+void
+ifcfg_mdv_wpa_network_unset(WPANetwork *wpan, const gchar *key)
+{
+	g_return_if_fail(wpan != NULL);
+	g_return_if_fail(key != NULL);
+
+	g_hash_table_remove(wpan->keyvals, key);
+}
+
+static gboolean
+add_line(GSList **list, gchar *s)
+{
+	gchar *n;
+
+	g_return_val_if_fail(list != NULL, FALSE);
+	g_return_val_if_fail(s != NULL, FALSE);
+
+	n = g_strdup(s);
+	if (!n)
+		return FALSE;
+
+	*list = g_slist_append(*list, n);
+	if (!*list)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+dump_network(GSList **list, WPANetwork *wpan, GError **error)
+{
+	GHashTableIter iter;
+	gpointer key, val;
+	gchar *s;
+
+	g_return_val_if_fail(list != NULL, FALSE);
+	g_return_val_if_fail(wpan != NULL, FALSE);
+
+	if (!add_line(list, "network={\n"))
+		return FALSE;
+
+	g_hash_table_iter_init(&iter, wpan->keyvals);
+	while (g_hash_table_iter_next(&iter, &key, &val)) {
+
+		s = g_strdup_printf("\t%s=%s\n", (gchar *)key, (gchar *)val);
+		if (!s) {
+			g_set_error(error, ifcfg_plugin_error_quark(), 0,
+				"Out of memory");
+			return FALSE;
+		}
+		*list = g_slist_append(*list, s);
+		if (!*list)
+			return FALSE;
+	}
+
+	if (!add_line(list, "}\n"))
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+ifcfg_mdv_wpa_network_save(WPANetwork *wpan, gchar *file, GError **error)
+{
+	WPAConfig *wpac = NULL;
+	WPANetwork *o_wpan = NULL;
+	GIOStatus ret;
+	GSList *network = NULL, *wpa_rest = NULL, *l;
+	GIOChannel *ioc = NULL;
+	gsize written;
+	gchar *ssid;
+	gboolean result = FALSE, found = FALSE, delete = FALSE;
+	GMatchInfo *mi = NULL;
+
+	g_return_val_if_fail(wpan != NULL, FALSE);
+	g_return_val_if_fail(file != NULL, FALSE);
+
+	ssid = ifcfg_mdv_wpa_network_get_val(wpan, "ssid");
+	if (!ssid || !*ssid) {
+		g_set_error(error, ifcfg_plugin_error_quark(), 0,
+				"SSID is missing, unable to store wpa_supplicant configuration");
+		goto error;
+	}
+
+	/* Looks like a hack but it probably is not worth extra function */
+	if (ifcfg_mdv_wpa_network_get_val(wpan, "__DELETE__"))
+			delete = TRUE;
+
+	ioc = g_io_channel_new_file(file, "r", error);
+	if (!ioc) {
+		if ((*error)->code == G_FILE_ERROR_NOENT) {
+			g_error_free(*error);
+			*error = NULL;
+			goto no_input;
+		}
+		goto error;
+	}
+
+	wpac = ifcfg_mdv_wpa_config_new("");
+	if (!wpac)
+		goto error;
+
+	/* Read original file skipping network in wpan */
+	while ((ret = g_io_channel_read_line_string(ioc, wpac->line, NULL, error)) == G_IO_STATUS_NORMAL) {
+
+		/* shortcut */
+		if (found) {
+			if (!add_line(&wpa_rest, wpac->line->str))
+				goto error;
+			continue;
+		}
+
+		if (!o_wpan && g_regex_match(wpac->network, wpac->line->str, 0, NULL)) {
+			if (!add_line(&network, wpac->line->str))
+				goto error;
+			o_wpan = ifcfg_mdv_wpa_network_new(wpac);
+			if (!o_wpan)
+				goto error;
+			continue;
+		}
+
+		if (o_wpan && g_regex_match(wpac->keyval, wpac->line->str, 0, &mi)) {
+			gchar *key = g_match_info_fetch(mi, 1);
+			gchar *val = g_match_info_fetch(mi, 2);
+			ifcfg_mdv_wpa_network_set_val(o_wpan, key, val);
+			g_free(key);
+			g_free(val);
+
+			if (!add_line(&network, wpac->line->str))
+				goto error;
+			continue;
+		}
+
+		if (o_wpan && g_regex_match(wpac->fini, wpac->line->str, 0, NULL)) {
+			gchar *o_ssid;
+
+			if (!add_line(&network, wpac->line->str))
+				goto error;
+			o_ssid = ifcfg_mdv_wpa_network_get_val(o_wpan, "ssid");
+			if (!o_ssid || g_strcmp0(ssid, o_ssid)) {
+				wpa_rest = g_slist_concat(wpa_rest, network);
+				ifcfg_mdv_wpa_network_free(o_wpan);
+				o_wpan = NULL;
+				network = NULL;
+			} else {
+				ifcfg_mdv_wpa_network_free(o_wpan);
+				o_wpan = NULL;
+				found = TRUE;
+			}
+			continue;
+		}
+
+		if (!add_line(&wpa_rest, wpac->line->str))
+			goto error;
+	}
+
+	if (ret != G_IO_STATUS_EOF)
+		goto error;
+
+	g_io_channel_unref(ioc);
+	ioc = NULL;
+
+no_input:
+	if (!delete && !dump_network(&wpa_rest, wpan, error))
+		goto error;
+
+
+	ioc = g_io_channel_new_file(file, "w", error);
+	if (!ioc)
+		goto error;
+	g_chmod(file, 0600);
+
+	for (l = wpa_rest; l; l = g_slist_next(l))
+		if (g_io_channel_write_chars(ioc, l->data, -1, &written, error) != G_IO_STATUS_NORMAL)
+			goto error;
+	if (g_io_channel_flush(ioc, error) != G_IO_STATUS_NORMAL)
+		goto error;
+
+	result = TRUE;
+
+error:
+	ifcfg_mdv_wpa_config_free(wpac);
+	ifcfg_mdv_wpa_network_free(o_wpan);
+	free_list(&wpa_rest);
+	free_list(&network);
+	if (ioc)
+		g_io_channel_unref(ioc);
+	if (mi)
+		g_match_info_free(mi);
+
+	return result;
+}
diff --git a/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.h b/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.h
new file mode 100644
index 0000000..3222f85
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/parse_wpa_supplicant_conf.h
@@ -0,0 +1,27 @@
+#ifndef PARSE_WPA_SUPPLICANT_CONF_H
+#define PARSE_WPA_SUPPLICANT_CONF_H
+
+typedef struct _WPAConfig WPAConfig;
+typedef struct _WPANetwork WPANetwork;
+
+gboolean ifcfg_mdv_wpa_config_parse(WPAConfig *);
+void ifcfg_mdv_wpa_config_free(WPAConfig *);
+WPAConfig *ifcfg_mdv_wpa_config_new(gchar *);
+
+WPANetwork *ifcfg_mdv_wpa_config_next(WPAConfig *);
+void ifcfg_mdv_wpa_config_rewind(WPAConfig *);
+
+WPANetwork *ifcfg_mdv_wpa_network_new(WPAConfig *);
+void ifcfg_mdv_wpa_network_free(WPANetwork *);
+
+gpointer ifcfg_mdv_wpa_network_get_val(WPANetwork *, const gchar *);
+void ifcfg_mdv_wpa_network_set_val(WPANetwork *, const gchar *, const gchar *);
+gchar *ifcfg_mdv_wpa_network_get_str(WPANetwork *, const gchar *);
+void ifcfg_mdv_wpa_network_set_str(WPANetwork *, const gchar *, const gchar *);
+GByteArray *ifcfg_mdv_wpa_network_get_ssid(WPANetwork *);
+void ifcfg_mdv_wpa_network_set_ssid(WPANetwork *, const GByteArray *);
+void ifcfg_mdv_wpa_network_unset(WPANetwork *, const gchar *);
+
+gboolean ifcfg_mdv_wpa_network_save(WPANetwork *, gchar *, GError **);
+
+#endif /* PARSE_WPA_SUPPLICANT_CONF_H */
diff --git a/system-settings/plugins/ifcfg-mdv/plugin.c b/system-settings/plugins/ifcfg-mdv/plugin.c
new file mode 100644
index 0000000..9640025
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/plugin.c
@@ -0,0 +1,657 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include <gmodule.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <nm-setting-connection.h>
+
+#include "common.h"
+#include "nm-dbus-glib-types.h"
+#include "plugin.h"
+#include "nm-system-config-interface.h"
+#include "nm-ifcfg-connection.h"
+#include "nm-inotify-helper.h"
+#include "shvar.h"
+#include "writer.h"
+#include "utils.h"
+
+static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
+
+static void connection_changed_handler (SCPluginIfcfg *plugin,
+                                        const char *path,
+                                        NMIfcfgConnection *connection,
+                                        gboolean *do_remove,
+                                        gboolean *do_new);
+
+static void handle_connection_remove_or_new (SCPluginIfcfg *plugin,
+                                             const char *path,
+                                             NMIfcfgConnection *connection,
+                                             gboolean do_remove,
+                                             gboolean do_new);
+
+G_DEFINE_TYPE_EXTENDED (SCPluginIfcfg, sc_plugin_ifcfg, G_TYPE_OBJECT, 0,
+						G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE,
+											   system_config_interface_init))
+
+#define SC_PLUGIN_IFCFG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgPrivate))
+
+
+typedef struct {
+	GHashTable *connections;
+
+	gulong ih_event_id;
+	int sc_network_wd;
+	char *hostname;
+
+	GFileMonitor *monitor;
+	guint monitor_id;
+} SCPluginIfcfgPrivate;
+
+
+static void
+connection_unmanaged_changed (NMIfcfgConnection *connection,
+                              GParamSpec *pspec,
+                              gpointer user_data)
+{
+	g_signal_emit_by_name (SC_PLUGIN_IFCFG (user_data), NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
+}
+
+static void
+connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
+	gboolean do_remove = FALSE, do_new = FALSE;
+	const char *path;
+
+	path = nm_ifcfg_connection_get_filename (connection);
+	g_return_if_fail (path != NULL);
+
+	connection_changed_handler (plugin, path, connection, &do_remove, &do_new);
+	handle_connection_remove_or_new (plugin, path, connection, do_remove, do_new);
+}
+
+static NMIfcfgConnection *
+read_one_connection (SCPluginIfcfg *plugin, const char *filename)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	NMIfcfgConnection *connection;
+	GError *error = NULL;
+	gboolean ignore_error = FALSE;
+
+	PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "parsing %s ... ", filename);
+
+	connection = nm_ifcfg_connection_new (filename, &error, &ignore_error);
+	if (connection) {
+		NMSettingConnection *s_con;
+		const char *cid;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		cid = nm_setting_connection_get_id (s_con);
+		g_assert (cid);
+
+		g_hash_table_insert (priv->connections,
+		                     (gpointer) nm_ifcfg_connection_get_filename (connection),
+		                     g_object_ref (connection));
+		PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "    read connection '%s'", cid);
+
+		if (nm_ifcfg_connection_get_unmanaged_spec (connection)) {
+			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Ignoring connection '%s' and its "
+			              "device because NM_CONTROLLED was not true or ONBOOT was set.", cid);
+			g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
+		} else {
+			/* Wait for the connection to become unmanaged once it knows the
+			 * UDI of it's device, if/when the device gets plugged in.
+			 */
+			g_signal_connect (G_OBJECT (connection), "notify::unmanaged",
+			                  G_CALLBACK (connection_unmanaged_changed), plugin);
+		}
+
+		/* watch changes of ifcfg hardlinks */
+		g_signal_connect (G_OBJECT (connection), "ifcfg-changed",
+		                  G_CALLBACK (connection_ifcfg_changed), plugin);
+	} else {
+		if (!ignore_error) {
+			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "    error: %s",
+			              (error && error->message) ? error->message : "(unknown)");
+		}
+		g_error_free (error);
+	}
+
+	return connection;
+}
+
+static void
+read_connections (SCPluginIfcfg *plugin)
+{
+	GDir *dir;
+	GError *err = NULL;
+
+	dir = g_dir_open (IFCFG_DIR, 0, &err);
+	if (dir) {
+		const char *item;
+
+		while ((item = g_dir_read_name (dir))) {
+			char *full_path;
+
+			if (utils_should_ignore_file (item, TRUE))
+				continue;
+
+			full_path = g_build_filename (IFCFG_DIR, item, NULL);
+			read_one_connection (plugin, full_path);
+			g_free (full_path);
+		}
+
+		g_dir_close (dir);
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Can not read directory '%s': %s", IFCFG_DIR, err->message);
+		g_error_free (err);
+	}
+}
+
+/* Monitoring */
+
+static void
+connection_changed_handler (SCPluginIfcfg *plugin,
+                            const char *path,
+                            NMIfcfgConnection *connection,
+                            gboolean *do_remove,
+                            gboolean *do_new)
+{
+	NMIfcfgConnection *new;
+	GError *error = NULL;
+	gboolean ignore_error = FALSE;
+	const char *new_unmanaged = NULL, *old_unmanaged = NULL;
+
+	g_return_if_fail (plugin != NULL);
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (connection != NULL);
+	g_return_if_fail (do_remove != NULL);
+	g_return_if_fail (do_new != NULL);
+
+	PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path);
+
+	new = (NMIfcfgConnection *) nm_ifcfg_connection_new (path, &error, &ignore_error);
+	if (!new) {
+		/* errors reading connection; remove it */
+		if (!ignore_error) {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    error: %s",
+			             (error && error->message) ? error->message : "(unknown)");
+		}
+		g_clear_error (&error);
+
+		PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", path);
+		*do_remove = TRUE;
+		return;
+	}
+
+	/* Successfully read connection changes */
+
+	old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (connection));
+	new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new));
+
+	if (new_unmanaged) {
+		if (!old_unmanaged) {
+			/* Unexport the connection by destroying it, then re-creating it as unmanaged */
+			*do_remove = *do_new = TRUE;
+		}
+	} else {
+		if (old_unmanaged) {  /* now managed */
+			NMSettingConnection *s_con;
+			const char *cid;
+
+			s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (new), NM_TYPE_SETTING_CONNECTION);
+			g_assert (s_con);
+
+			cid = nm_setting_connection_get_id (s_con);
+			g_assert (cid);
+
+			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Managing connection '%s' and its "
+			              "device because NM_CONTROLLED was true.", cid);
+			g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, connection);
+		}
+
+		if (!nm_sysconfig_connection_update (NM_SYSCONFIG_CONNECTION (connection),
+		                                     NM_CONNECTION (new),
+		                                     TRUE,
+		                                     &error)) {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    error updating: %s",
+			             (error && error->message) ? error->message : "(unknown)");
+			g_clear_error (&error);
+		}
+
+		/* Update unmanaged status */
+		g_object_set (connection, "unmanaged", new_unmanaged, NULL);
+		g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
+	}
+	g_object_unref (new);
+}
+
+static void
+handle_connection_remove_or_new (SCPluginIfcfg *plugin,
+                                 const char *path,
+                                 NMIfcfgConnection *connection,
+                                 gboolean do_remove,
+                                 gboolean do_new)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+
+	g_return_if_fail (plugin != NULL);
+	g_return_if_fail (path != NULL);
+
+	if (do_remove) {
+		const char *unmanaged;
+
+		g_return_if_fail (connection != NULL);
+
+		unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection);
+		g_hash_table_remove (priv->connections, path);
+		g_signal_emit_by_name (connection, "removed");
+
+		/* Emit unmanaged changes _after_ removing the connection */
+		if (unmanaged)
+			g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
+	}
+
+	if (do_new) {
+		connection = read_one_connection (plugin, path);
+		if (connection) {
+			if (!nm_ifcfg_connection_get_unmanaged_spec (connection))
+				g_signal_emit_by_name (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, connection);
+		}
+	}
+}
+static void
+dir_changed (GFileMonitor *monitor,
+		   GFile *file,
+		   GFile *other_file,
+		   GFileMonitorEvent event_type,
+		   gpointer user_data)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	char *path, *name;
+	NMIfcfgConnection *connection;
+	gboolean do_remove = FALSE, do_new = FALSE;
+
+	path = g_file_get_path (file);
+	if (utils_should_ignore_file (path, FALSE)) {
+		g_free (path);
+		return;
+	}
+
+	/* Given any ifcfg, keys, or routes file, get the ifcfg file path */
+	name = utils_get_ifcfg_path (path);
+	g_free (path);
+
+	connection = g_hash_table_lookup (priv->connections, name);
+	if (!connection) {
+		do_new = TRUE;
+	} else {
+		switch (event_type) {
+		case G_FILE_MONITOR_EVENT_DELETED:
+			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", name);
+			do_remove = TRUE;
+			break;
+		case G_FILE_MONITOR_EVENT_CREATED:
+		case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+			/* Update */
+			connection_changed_handler (plugin, name, connection, &do_remove, &do_new);
+			break;
+		default:
+			break;
+		}
+	}
+
+	handle_connection_remove_or_new (plugin, name, connection, do_remove, do_new);
+
+	g_free (name);
+}
+
+static void
+setup_ifcfg_monitoring (SCPluginIfcfg *plugin)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	GFile *file;
+	GFileMonitor *monitor;
+
+	priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+	file = g_file_new_for_path (IFCFG_DIR "/");
+	monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+	g_object_unref (file);
+
+	if (monitor) {
+		priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), plugin);
+		priv->monitor = monitor;
+	}
+}
+
+static GSList *
+get_connections (NMSystemConfigInterface *config)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	GSList *list = NULL;
+	GHashTableIter iter;
+	gpointer value;
+
+	if (!priv->connections) {
+		setup_ifcfg_monitoring (plugin);
+		read_connections (plugin);
+	}
+
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		NMIfcfgConnection *exported = NM_IFCFG_CONNECTION (value);
+
+		if (!nm_ifcfg_connection_get_unmanaged_spec (exported))
+			list = g_slist_prepend (list, value);
+	}
+
+	return list;
+}
+
+static void
+check_unmanaged (gpointer key, gpointer data, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+	NMIfcfgConnection *connection = NM_IFCFG_CONNECTION (data);
+	const char *unmanaged_spec;
+	GSList *iter;
+
+	unmanaged_spec = nm_ifcfg_connection_get_unmanaged_spec (connection);
+	if (!unmanaged_spec)
+		return;
+
+	/* Just return if the unmanaged spec is already in the list */
+	for (iter = *list; iter; iter = g_slist_next (iter)) {
+		if (!strcmp ((char *) iter->data, unmanaged_spec))
+			return;
+	}
+
+	*list = g_slist_prepend (*list, g_strdup (unmanaged_spec));
+}
+
+static GSList *
+get_unmanaged_specs (NMSystemConfigInterface *config)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
+	GSList *list = NULL;
+
+	if (!priv->connections) {
+		setup_ifcfg_monitoring (plugin);
+		read_connections (plugin);
+	}
+
+	g_hash_table_foreach (priv->connections, check_unmanaged, &list);
+	return list;
+}
+
+static gboolean
+add_connection (NMSystemConfigInterface *config,
+                NMConnection *connection,
+                GError **error)
+{
+	return writer_new_connection (connection, IFCFG_DIR, NULL, error);
+}
+
+#define SC_NETWORK_FILE SYSCONFDIR"/sysconfig/network"
+
+static char *
+plugin_get_hostname (SCPluginIfcfg *plugin)
+{
+	shvarFile *network;
+	char *hostname;
+	gboolean ignore_localhost;
+
+	network = svNewFile (SC_NETWORK_FILE);
+	if (!network) {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Could not get hostname: failed to read " SC_NETWORK_FILE);
+		return FALSE;
+	}
+
+	hostname = svGetValue (network, "HOSTNAME", FALSE);
+	ignore_localhost = svTrueValue (network, "NM_IGNORE_HOSTNAME_LOCALHOST", FALSE);
+	if (ignore_localhost) {
+		/* Ignore a hostname of 'localhost' or 'localhost.localdomain' to preserve
+		 * 'network' service behavior.
+		 */
+		if (hostname && (!strcmp (hostname, "localhost") || !strcmp (hostname, "localhost.localdomain"))) {
+			g_free (hostname);
+			hostname = NULL;
+		}
+	}
+
+	svCloseFile (network);
+	return hostname;
+}
+
+static gboolean
+plugin_set_hostname (SCPluginIfcfg *plugin, const char *hostname)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	shvarFile *network;
+
+	network = svCreateFile (SC_NETWORK_FILE);
+	if (!network) {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Could not save hostname: failed to create/open " SC_NETWORK_FILE);
+		return FALSE;
+	}
+
+	svSetValue (network, "HOSTNAME", hostname, FALSE);
+	svWriteFile (network, 0644);
+	svCloseFile (network);
+
+	g_free (priv->hostname);
+	priv->hostname = hostname ? g_strdup (hostname) : NULL;
+	return TRUE;
+}
+
+static void
+sc_network_changed_cb (NMInotifyHelper *ih,
+                       struct inotify_event *evt,
+                       const char *path,
+                       gpointer user_data)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	char *new_hostname;
+
+	if (evt->wd != priv->sc_network_wd)
+		return;
+
+	new_hostname = plugin_get_hostname (plugin);
+	if (   (new_hostname && !priv->hostname)
+	    || (!new_hostname && priv->hostname)
+	    || (priv->hostname && new_hostname && strcmp (priv->hostname, new_hostname))) {
+		g_free (priv->hostname);
+		priv->hostname = new_hostname;
+		g_object_notify (G_OBJECT (plugin), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
+	} else
+		g_free (new_hostname);
+}
+
+static void
+init (NMSystemConfigInterface *config)
+{
+}
+
+static void
+sc_plugin_ifcfg_init (SCPluginIfcfg *plugin)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	NMInotifyHelper *ih;
+
+	ih = nm_inotify_helper_get ();
+	priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (sc_network_changed_cb), plugin);
+	priv->sc_network_wd = nm_inotify_helper_add_watch (ih, SC_NETWORK_FILE);
+
+	priv->hostname = plugin_get_hostname (plugin);
+}
+
+static void
+dispose (GObject *object)
+{
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (object);
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+	NMInotifyHelper *ih;
+
+	ih = nm_inotify_helper_get ();
+
+	g_signal_handler_disconnect (ih, priv->ih_event_id);
+
+	if (priv->sc_network_wd >= 0)
+		nm_inotify_helper_remove_watch (ih, priv->sc_network_wd);
+
+	g_free (priv->hostname);
+
+	if (priv->connections)
+		g_hash_table_destroy (priv->connections);
+
+	if (priv->monitor) {
+		if (priv->monitor_id)
+			g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
+
+		g_file_monitor_cancel (priv->monitor);
+		g_object_unref (priv->monitor);
+	}
+
+	G_OBJECT_CLASS (sc_plugin_ifcfg_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	G_OBJECT_CLASS (sc_plugin_ifcfg_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME:
+		g_value_set_string (value, IFCFG_PLUGIN_NAME);
+		break;
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO:
+		g_value_set_string (value, IFCFG_PLUGIN_INFO);
+		break;
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES:
+		g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS | NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME);
+		break;
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME:
+		g_value_set_string (value, priv->hostname);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	const char *hostname;
+
+	switch (prop_id) {
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME:
+		hostname = g_value_get_string (value);
+		if (hostname && strlen (hostname) < 1)
+			hostname = NULL;
+		plugin_set_hostname (SC_PLUGIN_IFCFG (object), hostname);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+sc_plugin_ifcfg_class_init (SCPluginIfcfgClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	g_type_class_add_private (req_class, sizeof (SCPluginIfcfgPrivate));
+
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	g_object_class_override_property (object_class,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_NAME);
+
+	g_object_class_override_property (object_class,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_INFO);
+
+	g_object_class_override_property (object_class,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES);
+
+	g_object_class_override_property (object_class,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME,
+	                                  NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
+}
+
+static void
+system_config_interface_init (NMSystemConfigInterface *system_config_interface_class)
+{
+	/* interface implementation */
+	system_config_interface_class->get_connections = get_connections;
+	system_config_interface_class->add_connection = add_connection;
+	system_config_interface_class->get_unmanaged_specs = get_unmanaged_specs;
+	system_config_interface_class->init = init;
+}
+
+G_MODULE_EXPORT GObject *
+nm_system_config_factory (void)
+{
+	static SCPluginIfcfg *singleton = NULL;
+
+	if (!singleton)
+		singleton = SC_PLUGIN_IFCFG (g_object_new (SC_TYPE_PLUGIN_IFCFG, NULL));
+	else
+		g_object_ref (singleton);
+
+	return G_OBJECT (singleton);
+}
diff --git a/system-settings/plugins/ifcfg-mdv/plugin.h b/system-settings/plugins/ifcfg-mdv/plugin.h
new file mode 100644
index 0000000..d820a26
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/plugin.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+#include <glib-object.h>
+
+#define SC_TYPE_PLUGIN_IFCFG            (sc_plugin_ifcfg_get_type ())
+#define SC_PLUGIN_IFCFG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfg))
+#define SC_PLUGIN_IFCFG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgClass))
+#define SC_IS_PLUGIN_IFCFG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_PLUGIN_IFCFG))
+#define SC_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SC_TYPE_PLUGIN_IFCFG))
+#define SC_PLUGIN_IFCFG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgClass))
+
+typedef struct _SCPluginIfcfg SCPluginIfcfg;
+typedef struct _SCPluginIfcfgClass SCPluginIfcfgClass;
+
+struct _SCPluginIfcfg {
+	GObject parent;
+};
+
+struct _SCPluginIfcfgClass {
+	GObjectClass parent;
+};
+
+GType sc_plugin_ifcfg_get_type (void);
+
+#endif	/* _PLUGIN_H_ */
+
diff --git a/system-settings/plugins/ifcfg-mdv/reader.c b/system-settings/plugins/ifcfg-mdv/reader.c
new file mode 100644
index 0000000..5097953
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/reader.c
@@ -0,0 +1,3452 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2009 Red Hat, Inc.
+ * Mandriva-specific changes by Eugeni Dodonov <eugeni@mandriva.com>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+
+#ifndef __user
+#define __user
+#endif
+#include <linux/types.h>
+#include <wireless.h>
+#undef __user
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <nm-connection.h>
+#include <NetworkManager.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-8021x.h>
+#include <nm-utils.h>
+
+#include "common.h"
+#include "shvar.h"
+#include "utils.h"
+
+#include "reader.h"
+#include "parse_wpa_supplicant_conf.h"
+
+#define PLUGIN_PRINT(pname, fmt, args...) \
+	{ g_message ("   " pname ": " fmt, ##args); }
+
+#define PLUGIN_WARN(pname, fmt, args...) \
+	{ g_warning ("   " pname ": " fmt, ##args); }
+
+static gboolean eap_simple_reader (const char *eap_method,
+				   WPANetwork *wpan,
+                                   shvarFile *ifcfg,
+                                   shvarFile *keys,
+                                   NMSetting8021x *s_8021x,
+                                   gboolean phase2,
+                                   GError **error);
+
+static gboolean eap_tls_reader (const char *eap_method,
+				WPANetwork *wpan,
+                                shvarFile *ifcfg,
+                                shvarFile *keys,
+                                NMSetting8021x *s_8021x,
+                                gboolean phase2,
+                                GError **error);
+
+static gboolean eap_peap_reader (const char *eap_method,
+				 WPANetwork *wpan,
+                                 shvarFile *ifcfg,
+                                 shvarFile *keys,
+                                 NMSetting8021x *s_8021x,
+                                 gboolean phase2,
+                                 GError **error);
+
+static gboolean eap_ttls_reader (const char *eap_method,
+				 WPANetwork *wpan,
+                                 shvarFile *ifcfg,
+                                 shvarFile *keys,
+                                 NMSetting8021x *s_8021x,
+                                 gboolean phase2,
+                                 GError **error);
+
+static gboolean
+get_int (const char *str, int *value)
+{
+	char *e;
+
+	errno = 0;
+	*value = strtol (str, &e, 0);
+	if (errno || *e != '\0')
+		return FALSE;
+
+	return TRUE;
+}
+
+static NMSetting *
+make_connection_setting (const char *file,
+                         shvarFile *ifcfg,
+                         const char *type,
+                         const char *suggested)
+{
+	NMSettingConnection *s_con;
+	const char *ifcfg_name = NULL;
+	char *new_id = NULL, *uuid = NULL, *value;
+	// char *ifcfg_id;
+
+	ifcfg_name = utils_get_ifcfg_name (file, TRUE);
+	if (!ifcfg_name)
+		return NULL;
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+	/* Try the ifcfg file's internally defined name if available */
+#if 0
+	/* Mandriva does not use or set NAME */
+	// ifcfg_id = svGetValue (ifcfg, "NAME", FALSE);
+	if (ifcfg_id && strlen (ifcfg_id))
+		g_object_set (s_con, NM_SETTING_CONNECTION_ID, ifcfg_id, NULL);
+#endif
+
+	if (!nm_setting_connection_get_id (s_con)) {
+		if (suggested) {
+			/* For cosmetic reasons, if the suggested name is the same as
+			 * the ifcfg files name, don't use it.  Mainly for wifi so that
+			 * the SSID is shown in the connection ID instead of just "wlan0".
+			 */
+			if (strcmp (ifcfg_name, suggested)) {
+				new_id = g_strdup_printf ("%s %s (%s)", reader_get_prefix (), suggested, ifcfg_name);
+				g_object_set (s_con, NM_SETTING_CONNECTION_ID, new_id, NULL);
+			}
+		}
+
+		/* Use the ifcfg file's name as a last resort */
+		if (!nm_setting_connection_get_id (s_con)) {
+			new_id = g_strdup_printf ("%s %s", reader_get_prefix (), ifcfg_name);
+			g_object_set (s_con, NM_SETTING_CONNECTION_ID, new_id, NULL);
+		}
+	}
+
+	g_free (new_id);
+	// g_free (ifcfg_id);
+
+#if 0
+	/* Try for a UUID key before falling back to hashing the file name */
+	uuid = svGetValue (ifcfg, "UUID", FALSE);
+#endif
+	if (!uuid || !strlen (uuid)) {
+		g_free (uuid);
+		uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName);
+	}
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_TYPE, type,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NULL);
+	g_free (uuid);
+
+	/* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */
+	/* FIXME temporary until we can use ONBOOT again */
+	g_object_set (s_con, NM_SETTING_CONNECTION_AUTOCONNECT,
+	              svTrueValue (ifcfg, "_NM_ONBOOT", TRUE),
+	              NULL);
+
+	value = svGetValue (ifcfg, "LAST_CONNECT", FALSE);
+	if (value) {
+		unsigned long int tmp;
+		guint64 timestamp;
+
+		errno = 0;
+		tmp = strtoul (value, NULL, 10);
+		if (errno == 0) {
+			timestamp = (guint64) tmp;
+			g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
+		} else
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: invalid LAST_CONNECT time");
+		g_free (value);
+	}
+
+	return NM_SETTING (s_con);
+}
+
+static gboolean
+discover_mac_address(char *device, GByteArray **array, GError **error)
+{
+	int fd, ret;
+	struct ifreq ifr;
+
+	g_return_val_if_fail (device != NULL, FALSE);
+	g_return_val_if_fail (array != NULL, FALSE);
+	g_return_val_if_fail (*array == NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		g_set_error(error, ifcfg_plugin_error_quark(), errno,
+				"Unable to discover MAC address: socket error");
+		return FALSE;
+	}
+
+	ifr.ifr_addr.sa_family = AF_INET;
+	strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
+
+	ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
+	if (ret < 0) {
+		g_set_error(error, ifcfg_plugin_error_quark(), errno,
+				"Unable to discover MAC address: ioctl error");
+		return FALSE;
+	}
+	close(fd);
+
+	*array = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (*array, (guint8 *) ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+	return TRUE;
+}
+
+static gboolean
+read_mac_address (shvarFile *ifcfg, GByteArray **array, GError **error)
+{
+	char *value = NULL;
+	struct ether_addr *mac;
+
+	g_return_val_if_fail (ifcfg != NULL, FALSE);
+	g_return_val_if_fail (array != NULL, FALSE);
+	g_return_val_if_fail (*array == NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	value = svGetValue (ifcfg, "HWADDR", FALSE);
+	if (!value || !strlen (value)) {
+		g_free (value);
+		return TRUE;
+	}
+
+	mac = ether_aton (value);
+	if (!mac) {
+		g_free (value);
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "The MAC address '%s' was invalid.", value);
+		return FALSE;
+	}
+
+	g_free (value);
+	*array = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (*array, (guint8 *) mac->ether_addr_octet, ETH_ALEN);
+	return TRUE;
+}
+
+/* Mandriva does not seem to ever hex-encode SSID in ifcfg. So do not bother
+ * as well - just get what we have. This highly simplifies the logic */
+/* FIXME this currently fails for '\0' which is not accepted as input either */
+GByteArray *
+ifcfg_mdv_parse_ssid(char *value, GError **error)
+{
+	gsize ssid_len;
+	gchar *ssid = NULL;
+	GByteArray *a;
+
+	ssid = g_strdup(value);
+	if (!ssid) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "Cannot duplicate SSID");
+		goto error;
+	}
+	svUnescape (ssid);
+	ssid_len = strlen (ssid);
+	if (ssid_len > 32 || ssid_len == 0) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
+			     value, ssid_len);
+		goto error;
+	}
+
+	a = g_byte_array_sized_new (ssid_len);
+	if (!a) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "Cannot allocate SSID");
+		goto error;
+	}
+
+	g_byte_array_append (a, (const guint8 *) ssid, ssid_len);
+	g_free(ssid);
+
+	return a;
+
+error:
+	g_free(ssid);
+	return NULL;
+}
+
+#if 0
+/* no iSCSI on Mandriva */
+static void
+iscsiadm_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process here; set a different process group to
+	 * ensure signal isolation between child and parent.
+	 */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static char *
+match_iscsiadm_tag (const char *line, const char *tag, gboolean *skip)
+{
+	char *p;
+
+	if (g_ascii_strncasecmp (line, tag, strlen (tag)))
+		return NULL;
+
+	p = strchr (line, '=');
+	if (!p) {
+		g_warning ("%s: malformed iscsiadm record: no = in '%s'.",
+		           __func__, line);
+		*skip = TRUE;
+		return NULL;
+	}
+
+	p++; /* advance past = */
+	return g_strstrip (p);
+}
+
+#define ISCSI_HWADDR_TAG    "iface.hwaddress"
+#define ISCSI_BOOTPROTO_TAG "iface.bootproto"
+#define ISCSI_IPADDR_TAG    "iface.ipaddress"
+#define ISCSI_SUBNET_TAG    "iface.subnet_mask"
+#define ISCSI_GATEWAY_TAG   "iface.gateway"
+#define ISCSI_DNS1_TAG      "iface.primary_dns"
+#define ISCSI_DNS2_TAG      "iface.secondary_dns"
+
+static gboolean
+fill_ip4_setting_from_ibft (shvarFile *ifcfg,
+                            NMSettingIP4Config *s_ip4,
+                            const char *iscsiadm_path,
+                            GError **error)
+{
+	const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL };
+	const char *envp[1] = { NULL };
+	gboolean success = FALSE, in_record = FALSE, hwaddr_matched = FALSE, skip = FALSE;
+	char *out = NULL, *err = NULL;
+	gint status = 0;
+	GByteArray *ifcfg_mac = NULL;
+	char **lines = NULL, **iter;
+	const char *method = NULL;
+	struct in_addr ipaddr;
+	struct in_addr gateway;
+	struct in_addr dns1;
+	struct in_addr dns2;
+	guint32 prefix = 0;
+
+	g_return_val_if_fail (s_ip4 != NULL, FALSE);
+	g_return_val_if_fail (iscsiadm_path != NULL, FALSE);
+
+	if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0,
+	                   iscsiadm_child_setup, NULL, &out, &err, &status, error))
+		return FALSE;
+
+	if (!WIFEXITED (status)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "%s exited abnormally.", iscsiadm_path);
+		goto done;
+	}
+
+	if (WEXITSTATUS (status) != 0) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "%s exited with error %d.  Message: '%s'",
+		             iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)");
+		goto done;
+	}
+
+	if (!read_mac_address (ifcfg, &ifcfg_mac, error))
+		goto done;
+	/* Ensure we got a MAC */
+	if (!ifcfg_mac) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing device MAC address (no HWADDR tag present).");
+		goto done;
+	}
+
+	memset (&ipaddr, 0, sizeof (ipaddr));
+	memset (&gateway, 0, sizeof (gateway));
+	memset (&dns1, 0, sizeof (dns1));
+	memset (&dns2, 0, sizeof (dns2));
+
+	/* Success, lets parse the output */
+	lines = g_strsplit_set (out, "\n\r", -1);
+	for (iter = lines; iter && *iter; iter++) {
+		char *p;
+
+		if (!g_ascii_strcasecmp (*iter, "# BEGIN RECORD")) {
+			if (in_record) {
+				g_warning ("%s: malformed iscsiadm record: already parsing record.", __func__);
+				skip = TRUE;
+			}
+		} else if (!g_ascii_strcasecmp (*iter, "# END RECORD")) {
+			if (!skip && hwaddr_matched) {
+				/* Record is good; fill IP4 config with its info */
+				if (!method) {
+					g_warning ("%s: malformed iscsiadm record: missing BOOTPROTO.", __func__);
+					return FALSE;
+				}
+
+				g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, method, NULL);
+
+				if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+					NMIP4Address *addr;
+
+				    if (!ipaddr.s_addr || !prefix) {
+						g_warning ("%s: malformed iscsiadm record: BOOTPROTO=static "
+						           "but missing IP address or prefix.", __func__);
+						return FALSE;
+					}
+
+					addr = nm_ip4_address_new ();
+					nm_ip4_address_set_address (addr, ipaddr.s_addr);
+					nm_ip4_address_set_prefix (addr, prefix);
+					nm_ip4_address_set_gateway (addr, gateway.s_addr);
+					nm_setting_ip4_config_add_address (s_ip4, addr);
+					nm_ip4_address_unref (addr);
+
+					if (dns1.s_addr)
+						nm_setting_ip4_config_add_dns (s_ip4, dns1.s_addr);
+					if (dns2.s_addr)
+						nm_setting_ip4_config_add_dns (s_ip4, dns2.s_addr);
+
+					// FIXME: DNS search domains?
+				}
+				return TRUE;
+			}
+			skip = FALSE;
+			hwaddr_matched = FALSE;
+			memset (&ipaddr, 0, sizeof (ipaddr));
+			memset (&gateway, 0, sizeof (gateway));
+			memset (&dns1, 0, sizeof (dns1));
+			memset (&dns2, 0, sizeof (dns2));
+			prefix = 0;
+			method = NULL;
+		}
+
+		if (skip)
+			continue;
+
+		/* HWADDR */
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_HWADDR_TAG, &skip))) {
+			struct ether_addr *ibft_mac;
+
+			ibft_mac = ether_aton (p);
+			if (!ibft_mac) {
+				g_warning ("%s: malformed iscsiadm record: invalid hwaddress.", __func__);
+				skip = TRUE;
+				continue;
+			}
+
+			if (memcmp (ifcfg_mac->data, (guint8 *) ibft_mac->ether_addr_octet, ETH_ALEN)) {
+				/* This record isn't for the current device, ignore it */
+				skip = TRUE;
+				continue;
+			}
+
+			/* Success, this record is for this device */
+			hwaddr_matched = TRUE;
+		}
+
+		/* BOOTPROTO */
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_BOOTPROTO_TAG, &skip))) {
+			if (!g_ascii_strcasecmp (p, "dhcp"))
+				method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+			else if (!g_ascii_strcasecmp (p, "static"))
+				method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
+			else {
+				g_warning ("%s: malformed iscsiadm record: unknown BOOTPROTO '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+		}
+
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_IPADDR_TAG, &skip))) {
+			if (inet_pton (AF_INET, p, &ipaddr) < 1) {
+				g_warning ("%s: malformed iscsiadm record: invalid IP address '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+		}
+
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_SUBNET_TAG, &skip))) {
+			struct in_addr mask;
+
+			if (inet_pton (AF_INET, p, &mask) < 1) {
+				g_warning ("%s: malformed iscsiadm record: invalid subnet mask '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+
+			prefix = nm_utils_ip4_netmask_to_prefix (mask.s_addr);
+		}
+
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_GATEWAY_TAG, &skip))) {
+			if (inet_pton (AF_INET, p, &gateway) < 1) {
+				g_warning ("%s: malformed iscsiadm record: invalid IP gateway '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+		}
+
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_DNS1_TAG, &skip))) {
+			if (inet_pton (AF_INET, p, &dns1) < 1) {
+				g_warning ("%s: malformed iscsiadm record: invalid DNS1 address '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+		}
+
+		if (!skip && (p = match_iscsiadm_tag (*iter, ISCSI_DNS2_TAG, &skip))) {
+			if (inet_pton (AF_INET, p, &dns2) < 1) {
+				g_warning ("%s: malformed iscsiadm record: invalid DNS2 address '%s'.",
+				           __func__, p);
+				skip = TRUE;
+				continue;
+			}
+		}
+	}
+
+	success = TRUE;
+
+done:
+	if (ifcfg_mac)
+		g_byte_array_free (ifcfg_mac, TRUE);
+	g_strfreev (lines);
+	g_free (out);
+	g_free (err);
+	return success;
+}
+#endif
+
+static gboolean
+read_ip4_address (shvarFile *ifcfg,
+                  const char *tag,
+                  guint32 *out_addr,
+                  GError **error)
+{
+	char *value = NULL;
+	struct in_addr ip4_addr;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (ifcfg != NULL, FALSE);
+	g_return_val_if_fail (tag != NULL, FALSE);
+	g_return_val_if_fail (out_addr != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	*out_addr = 0;
+
+	value = svGetValue (ifcfg, tag, FALSE);
+	if (!value)
+		return TRUE;
+
+	if (inet_pton (AF_INET, value, &ip4_addr) > 0) {
+		*out_addr = ip4_addr.s_addr;
+		success = TRUE;
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid %s IP4 address '%s'", tag, value);
+	}
+	g_free (value);
+	return success;
+}
+
+#if 0
+No IPv6 on Mandriva
+static gboolean
+parse_ip6_address (const char *value,
+                  struct in6_addr *out_addr,
+                  GError **error)
+{
+	struct in6_addr ip6_addr;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (value != NULL, FALSE);
+	g_return_val_if_fail (out_addr != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	*out_addr = in6addr_any;
+
+	if (inet_pton (AF_INET6, value, &ip6_addr) > 0) {
+		*out_addr = ip6_addr;
+		success = TRUE;
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid IP6 address '%s'", value);
+	}
+	return success;
+}
+#endif
+
+static NMIP4Address *
+read_full_ip4_address (shvarFile *ifcfg,
+                       const char *network_file,
+                       guint32 which,
+                       GError **error)
+{
+	NMIP4Address *addr;
+	char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag;
+	guint32 tmp;
+	gboolean success = FALSE;
+	shvarFile *network_ifcfg;
+	char *value;
+
+	g_return_val_if_fail (which > 0, NULL);
+	g_return_val_if_fail (ifcfg != NULL, NULL);
+	g_return_val_if_fail (network_file != NULL, NULL);
+
+	/* Mandriva does not seem to use more than one address */
+	if (which != 1)
+		return NULL;
+
+	addr = nm_ip4_address_new ();
+	if (which == 1) {
+		ip_tag = g_strdup ("IPADDR");
+		prefix_tag = g_strdup ("PREFIX");
+		netmask_tag = g_strdup ("NETMASK");
+		gw_tag = g_strdup ("GATEWAY");
+	} else {
+		ip_tag = g_strdup_printf ("IPADDR%u", which);
+		prefix_tag = g_strdup_printf ("PREFIX%u", which);
+		netmask_tag = g_strdup_printf ("NETMASK%u", which);
+		gw_tag = g_strdup_printf ("GATEWAY%u", which);
+	}
+
+	/* IP address */
+	if (!read_ip4_address (ifcfg, ip_tag, &tmp, error))
+		goto error;
+	if (!tmp) {
+		nm_ip4_address_unref (addr);
+		addr = NULL;
+		success = TRUE;  /* done */
+		goto error;
+	}
+	nm_ip4_address_set_address (addr, tmp);
+
+	/* Gateway */
+	if (!read_ip4_address (ifcfg, gw_tag, &tmp, error))
+		goto error;
+	if (tmp)
+		nm_ip4_address_set_gateway (addr, tmp);
+	else {
+		gboolean read_success;
+
+		/* If no gateway in the ifcfg, try /etc/sysconfig/network instead */
+		network_ifcfg = svNewFile (network_file);
+		if (network_ifcfg) {
+			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &tmp, error);
+			svCloseFile (network_ifcfg);
+			if (!read_success)
+				goto error;
+			nm_ip4_address_set_gateway (addr, tmp);
+		}
+	}
+
+	/* NETMASK */
+	if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error))
+		goto error;
+	nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp));
+
+
+	/* Fall back to PERFIX if no NETMASK was specified */
+	if (!nm_ip4_address_get_prefix (addr)) {
+		value = svGetValue (ifcfg, prefix_tag, FALSE);
+		if (value) {
+			long int prefix;
+
+			errno = 0;
+			prefix = strtol (value, NULL, 10);
+			if (errno || prefix <= 0 || prefix > 32) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Invalid IP4 prefix '%s'", value);
+				g_free (value);
+				goto error;
+			}
+			nm_ip4_address_set_prefix (addr, (guint32) prefix);
+			g_free (value);
+		}
+	}
+
+	/* Try to autodetermine the prefix for the address' class */
+	if (!nm_ip4_address_get_prefix (addr)) {
+		guint32 tmp_addr, prefix = 0;
+
+		tmp_addr = nm_ip4_address_get_address (addr);
+		if (((ntohl(tmp_addr) & 0xFF000000) >> 24) <= 127)
+			prefix = 8;
+		else if (((ntohl(tmp_addr) & 0xFF000000) >> 24) <= 191)
+			prefix = 16;
+		else
+			prefix = 24;
+
+		nm_ip4_address_set_prefix (addr, prefix);
+
+		value = svGetValue (ifcfg, ip_tag, FALSE);
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: missing %s, assuming %s/%u",
+		             prefix_tag, value, prefix);
+		g_free (value);
+	}
+
+	/* Validate the prefix */
+	if (nm_ip4_address_get_prefix (addr) > 32) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing or invalid IP4 prefix '%d'",
+		             nm_ip4_address_get_prefix (addr));
+		goto error;
+	}
+
+	success = TRUE;
+
+error:
+	if (!success) {
+		nm_ip4_address_unref (addr);
+		addr = NULL;
+	}
+
+	g_free (ip_tag);
+	g_free (prefix_tag);
+	g_free (netmask_tag);
+	g_free (gw_tag);
+	return addr;
+}
+
+#if 0
+/* No routes on Mandriva */
+static NMIP4Route *
+read_one_ip4_route (shvarFile *ifcfg,
+                    const char *network_file,
+                    guint32 which,
+                    GError **error)
+{
+	NMIP4Route *route;
+	char *ip_tag, *netmask_tag, *gw_tag, *metric_tag, *value;
+	guint32 tmp;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (ifcfg != NULL, NULL);
+	g_return_val_if_fail (network_file != NULL, NULL);
+	g_return_val_if_fail (which >= 0, NULL);
+
+	route = nm_ip4_route_new ();
+
+	ip_tag = g_strdup_printf ("ADDRESS%u", which);
+	netmask_tag = g_strdup_printf ("NETMASK%u", which);
+	gw_tag = g_strdup_printf ("GATEWAY%u", which);
+	metric_tag = g_strdup_printf ("METRIC%u", which);
+
+	/* Destination */
+	if (!read_ip4_address (ifcfg, ip_tag, &tmp, error))
+		goto out;
+	if (!tmp) {
+		/* Check whether IP is missing or 0.0.0.0 */
+		char *val;
+		val = svGetValue (ifcfg, ip_tag, FALSE);
+		if (!val) {
+			nm_ip4_route_unref (route);
+			route = NULL;
+			success = TRUE;  /* done */
+			goto out;
+		}
+		g_free (val);
+	}
+	nm_ip4_route_set_dest (route, tmp);
+
+	/* Next hop */
+	if (!read_ip4_address (ifcfg, gw_tag, &tmp, error))
+		goto out;
+	if (!tmp) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing or invalid IP4 gateway address '%d'",
+		             tmp);
+		goto out;
+	}
+	nm_ip4_route_set_next_hop (route, tmp);
+
+	/* Prefix */
+	if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error))
+		goto out;
+	nm_ip4_route_set_prefix (route, nm_utils_ip4_netmask_to_prefix (tmp));
+
+	/* Validate the prefix */
+	if (  !nm_ip4_route_get_prefix (route)
+	    || nm_ip4_route_get_prefix (route) > 32) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing or invalid IP4 prefix '%d'",
+		             nm_ip4_route_get_prefix (route));
+		goto out;
+	}
+
+	/* Metric */
+	value = svGetValue (ifcfg, metric_tag, FALSE);
+	if (value) {
+		long int metric;
+
+		errno = 0;
+		metric = strtol (value, NULL, 10);
+		if (errno || metric < 0) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid IP4 route metric '%s'", value);
+			g_free (value);
+			goto out;
+		}
+		nm_ip4_route_set_metric (route, (guint32) metric);
+		g_free (value);
+	}
+
+	success = TRUE;
+
+out:
+	if (!success) {
+		nm_ip4_route_unref (route);
+		route = NULL;
+	}
+
+	g_free (ip_tag);
+	g_free (netmask_tag);
+	g_free (gw_tag);
+	g_free (metric_tag);
+	return route;
+}
+
+static gboolean
+read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError **error)
+{
+	char *contents = NULL;
+	gsize len = 0;
+	char **lines = NULL, **iter;
+	GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
+	GMatchInfo *match_info;
+	NMIP4Route *route;
+	struct in_addr ip4_addr;
+	char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
+	long int prefix_int, metric_int;
+	gboolean success = FALSE;
+
+	const char *pattern_empty = "^\\s*(\\#.*)?$";
+	const char *pattern_to1 = "^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)"  /* IP or 'default' keyword */
+	                          "(?:/(\\d{1,2}))?";                                         /* optional prefix */
+	const char *pattern_to2 = "to\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */
+	                          "(?:/(\\d{1,2}))?";                                         /* optional prefix */
+	const char *pattern_via = "via\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";       /* IP of gateway */
+	const char *pattern_metric = "metric\\s+(\\d+)";                                      /* metric */
+
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (s_ip4 != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	/* Read the route file */
+	if (!g_file_get_contents (filename, &contents, &len, NULL))
+		return FALSE;
+
+	if (len == 0) {
+		g_free (contents);
+		return FALSE;
+	}
+
+	/* Create regexes for pieces to be matched */
+	regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL);
+	regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL);
+	regex_via = g_regex_new (pattern_via, 0, 0, NULL);
+	regex_metric = g_regex_new (pattern_metric, 0, 0, NULL);
+
+	/* New NMIP4Route structure */
+	route = nm_ip4_route_new ();
+
+	/* Iterate through file lines */
+	lines = g_strsplit_set (contents, "\n\r", -1);
+	for (iter = lines; iter && *iter; iter++) {
+
+		/* Skip empty lines */
+		if (g_regex_match_simple (pattern_empty, *iter, 0, 0))
+			continue;
+
+		/* Destination */
+		g_regex_match (regex_to1, *iter, 0, &match_info);
+		if (!g_match_info_matches (match_info)) {
+			g_match_info_free (match_info);
+			g_regex_match (regex_to2, *iter, 0, &match_info);
+			if (!g_match_info_matches (match_info)) {
+				g_match_info_free (match_info);
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Missing IP4 route destination address in record: '%s'", *iter);
+				goto error;
+			}
+		}
+		dest = g_match_info_fetch (match_info, 1);
+		g_match_info_free (match_info);
+		if (!strcmp (dest, "default"))
+			strcpy (dest, "0.0.0.0");
+		if (inet_pton (AF_INET, dest, &ip4_addr) != 1) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Invalid IP4 route destination address '%s'", dest);
+			g_free (dest);
+			goto error;
+		}
+		nm_ip4_route_set_dest (route, ip4_addr.s_addr);
+		g_free (dest);
+
+		/* Prefix - is optional; 32 if missing */
+		prefix = g_match_info_fetch (match_info, 2);
+		prefix_int = 32;
+		if (prefix) {
+			errno = 0;
+			prefix_int = strtol (prefix, NULL, 10);
+			if (errno || prefix_int < 0 || prefix_int > 32) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Invalid IP4 route destination prefix '%s'", prefix);
+				g_free (prefix);
+				goto error;
+			}
+		}
+
+		nm_ip4_route_set_prefix (route, (guint32) prefix_int);
+		g_free (prefix);
+
+		/* Next hop */
+		g_regex_match (regex_via, *iter, 0, &match_info);
+		if (!g_match_info_matches (match_info)) {
+			g_match_info_free (match_info);
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Missing IP4 route gateway address in record: '%s'", *iter);
+			goto error;
+		}
+		next_hop = g_match_info_fetch (match_info, 1);
+		g_match_info_free (match_info);
+		if (inet_pton (AF_INET, next_hop, &ip4_addr) != 1) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid IP4 route gateway address '%s'", next_hop);
+			g_free (next_hop);
+			goto error;
+		}
+		nm_ip4_route_set_next_hop (route, ip4_addr.s_addr);
+		g_free (next_hop);
+
+		/* Metric */
+		g_regex_match (regex_metric, *iter, 0, &match_info);
+		metric_int = 0;
+		if (g_match_info_matches (match_info)) {
+			metric = g_match_info_fetch (match_info, 1);
+			errno = 0;
+			metric_int = strtol (metric, NULL, 10);
+			if (errno || metric_int < 0) {
+				g_match_info_free (match_info);
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				             "Invalid IP4 route metric '%s'", metric);
+				g_free (metric);
+				goto error;
+			}
+			g_free (metric);
+		}
+
+		nm_ip4_route_set_metric (route, (guint32) metric_int);
+		g_match_info_free (match_info);
+
+		if (!nm_setting_ip4_config_add_route (s_ip4, route))
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate IP4 route");
+
+	}
+
+	success = TRUE;
+
+error:
+	g_free (contents);
+	g_strfreev (lines);
+	nm_ip4_route_unref (route);
+	g_regex_unref (regex_to1);
+	g_regex_unref (regex_to2);
+	g_regex_unref (regex_via);
+	g_regex_unref (regex_metric);
+
+	return success;
+}
+#endif
+
+#if 0
+No IPv6 on Mandriva
+static NMIP6Address *
+parse_full_ip6_address (const char *addr_str, GError **error)
+{
+	NMIP6Address *addr;
+	char **list;
+	char *ip_tag, *prefix_tag;
+	struct in6_addr tmp = IN6ADDR_ANY_INIT;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (addr_str != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	/* Split the adddress and prefix */
+	list = g_strsplit_set (addr_str, "/", 2);
+	if (g_strv_length (list) < 1) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid IP6 address '%s'", addr_str);
+		goto error;
+	}
+
+	ip_tag = list[0];
+	prefix_tag = list[1];
+
+	addr = nm_ip6_address_new ();
+	/* IP address */
+	if (ip_tag) {
+		if (!parse_ip6_address (ip_tag, &tmp, error))
+			goto error;
+	}
+
+	nm_ip6_address_set_address (addr, &tmp);
+
+	/* Prefix */
+	if (prefix_tag) {
+		long int prefix;
+
+		errno = 0;
+		prefix = strtol (prefix_tag, NULL, 10);
+		if (errno || prefix <= 0 || prefix > 128) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid IP6 prefix '%s'", prefix_tag);
+			goto error;
+		}
+		nm_ip6_address_set_prefix (addr, (guint32) prefix);
+	} else {
+		/* Missing prefix is treated as prefix of 64 */
+		nm_ip6_address_set_prefix (addr, 64);
+	}
+
+	success = TRUE;
+
+error:
+	if (!success) {
+		nm_ip6_address_unref (addr);
+		addr = NULL;
+	}
+
+	g_strfreev (list);
+	return addr;
+}
+
+/* IPv6 address is very complex to describe completely by a regular expression,
+ * so don't try to, rather use looser syntax to comprise all possibilities
+ * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
+ * namely destination IP without 'to' keyword is recognized just at line start.
+ */
+#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
+
+static gboolean
+read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **error)
+{
+	char *contents = NULL;
+	gsize len = 0;
+	char **lines = NULL, **iter;
+	GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
+	GMatchInfo *match_info;
+	NMIP6Route *route;
+	struct in6_addr ip6_addr;
+	char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
+	long int prefix_int, metric_int;
+	gboolean success = FALSE;
+
+	const char *pattern_empty = "^\\s*(\\#.*)?$";
+	const char *pattern_to1 = "^\\s*(" IPV6_ADDR_REGEX "|default)"  /* IPv6 or 'default' keyword */
+	                          "(?:/(\\d{1,2}))?";                   /* optional prefix */
+	const char *pattern_to2 = "to\\s+(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */
+	                          "(?:/(\\d{1,2}))?";                   /* optional prefix */
+	const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")";       /* IPv6 of gateway */
+	const char *pattern_metric = "metric\\s+(\\d+)";                /* metric */
+
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (s_ip6 != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	/* Read the route file */
+	if (!g_file_get_contents (filename, &contents, &len, NULL))
+		return FALSE;
+
+	if (len == 0) {
+		g_free (contents);
+		return FALSE;
+	}
+
+	/* Create regexes for pieces to be matched */
+	regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL);
+	regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL);
+	regex_via = g_regex_new (pattern_via, 0, 0, NULL);
+	regex_metric = g_regex_new (pattern_metric, 0, 0, NULL);
+
+	/* New NMIP6Route structure */
+	route = nm_ip6_route_new ();
+
+	/* Iterate through file lines */
+	lines = g_strsplit_set (contents, "\n\r", -1);
+	for (iter = lines; iter && *iter; iter++) {
+
+		/* Skip empty lines */
+		if (g_regex_match_simple (pattern_empty, *iter, 0, 0))
+			continue;
+
+		/* Destination */
+		g_regex_match (regex_to1, *iter, 0, &match_info);
+		if (!g_match_info_matches (match_info)) {
+			g_match_info_free (match_info);
+			g_regex_match (regex_to2, *iter, 0, &match_info);
+			if (!g_match_info_matches (match_info)) {
+				g_match_info_free (match_info);
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Missing IP6 route destination address in record: '%s'", *iter);
+				goto error;
+			}
+		}
+		dest = g_match_info_fetch (match_info, 1);
+		g_match_info_free (match_info);
+		if (!strcmp (dest, "default"))
+			strcpy (dest, "::");
+		if (inet_pton (AF_INET6, dest, &ip6_addr) != 1) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Invalid IP6 route destination address '%s'", dest);
+			g_free (dest);
+			goto error;
+		}
+		nm_ip6_route_set_dest (route, &ip6_addr);
+		g_free (dest);
+
+		/* Prefix - is optional; 128 if missing */
+		prefix = g_match_info_fetch (match_info, 2);
+		prefix_int = 128;
+		if (prefix) {
+			errno = 0;
+			prefix_int = strtol (prefix, NULL, 10);
+			if (errno || prefix_int < 0 || prefix_int > 128) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Invalid IP6 route destination prefix '%s'", prefix);
+				g_free (prefix);
+				goto error;
+			}
+		}
+
+		nm_ip6_route_set_prefix (route, (guint32) prefix_int);
+		g_free (prefix);
+
+		/* Next hop */
+		g_regex_match (regex_via, *iter, 0, &match_info);
+		if (!g_match_info_matches (match_info)) {
+			g_match_info_free (match_info);
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Missing IP6 route gateway address in record: '%s'", *iter);
+			goto error;
+		}
+		next_hop = g_match_info_fetch (match_info, 1);
+		g_match_info_free (match_info);
+		if (inet_pton (AF_INET6, next_hop, &ip6_addr) != 1) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid IP6 route gateway address '%s'", next_hop);
+			g_free (next_hop);
+			goto error;
+		}
+		nm_ip6_route_set_next_hop (route, &ip6_addr);
+		g_free (next_hop);
+
+		/* Metric */
+		g_regex_match (regex_metric, *iter, 0, &match_info);
+		metric_int = 0;
+		if (g_match_info_matches (match_info)) {
+			metric = g_match_info_fetch (match_info, 1);
+			errno = 0;
+			metric_int = strtol (metric, NULL, 10);
+			if (errno || metric_int < 0 || metric_int > G_MAXUINT32) {
+				g_match_info_free (match_info);
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				             "Invalid IP6 route metric '%s'", metric);
+				g_free (metric);
+				goto error;
+			}
+			g_free (metric);
+		}
+
+		nm_ip6_route_set_metric (route, (guint32) metric_int);
+		g_match_info_free (match_info);
+
+		if (!nm_setting_ip6_config_add_route (s_ip6, route))
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate IP6 route");
+	}
+
+	success = TRUE;
+
+error:
+	g_free (contents);
+	g_strfreev (lines);
+	nm_ip6_route_unref (route);
+	g_regex_unref (regex_to1);
+	g_regex_unref (regex_to2);
+	g_regex_unref (regex_via);
+	g_regex_unref (regex_metric);
+
+	return success;
+}
+#endif
+
+
+static NMSetting *
+make_ip4_setting (shvarFile *ifcfg,
+                  const char *network_file,
+                  const char *iscsiadm_path,
+                  GError **error)
+{
+	NMSettingIP4Config *s_ip4 = NULL;
+	char *value = NULL;
+	// char *route_path = NULL;
+	char *method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
+	guint32 i;
+	shvarFile *network_ifcfg;
+	// shvarFile *route_ifcfg;
+	gboolean never_default = FALSE, tmp_success;
+
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	if (!s_ip4) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not allocate IP4 setting");
+		return NULL;
+	}
+
+#if 0
+	/* Mandriva sets DEFROUTE for PPP only */
+	/* First check if DEFROUTE is set for this device; DEFROUTE has the
+	 * opposite meaning from never-default. The default if DEFROUTE is not
+	 * specified is DEFROUTE=yes which means that this connection can be used
+	 * as a default route
+	 */
+	never_default = !svTrueValue (ifcfg, "DEFROUTE", TRUE);
+#endif
+
+	/* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
+	 network_ifcfg = svNewFile (network_file);
+	 if (network_ifcfg) {
+		char *gatewaydev;
+
+		/* Get the connection ifcfg device name and the global gateway device */
+		value = svGetValue (ifcfg, "DEVICE", FALSE);
+		gatewaydev = svGetValue (network_ifcfg, "GATEWAYDEV", FALSE);
+
+		/* If there was a global gateway device specified, then only connections
+		 * for that device can be the default connection.
+		 */
+		if (gatewaydev && value)
+			never_default = !!strcmp (value, gatewaydev);
+
+		g_free (gatewaydev);
+		g_free (value);
+		// svCloseFile (network_ifcfg);
+	}
+
+	value = svGetValue (ifcfg, "BOOTPROTO", FALSE);
+	if (value) {
+		if (!g_ascii_strcasecmp (value, "bootp") || !g_ascii_strcasecmp (value, "dhcp")) {
+			method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+#if 0
+		/* Is not used by Mandriva */
+		} else if (!g_ascii_strcasecmp (value, "ibft")) {
+			/* iSCSI Boot Firmware Table: need to read values from the iSCSI 
+			 * firmware for this device and create the IP4 setting using those.
+			 */
+			if (fill_ip4_setting_from_ibft (ifcfg, s_ip4, iscsiadm_path, error))
+				return NM_SETTING (s_ip4);
+			g_object_unref (s_ip4);
+			return NULL;
+		} else if (!g_ascii_strcasecmp (value, "autoip")) {
+			g_free (value);
+			g_object_set (s_ip4,
+			              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
+			              NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, never_default,
+			              NULL);
+			return NM_SETTING (s_ip4);
+		} else if (!g_ascii_strcasecmp (value, "shared")) {
+			g_free (value);
+			g_object_set (s_ip4,
+			              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED,
+			              NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, never_default,
+			              NULL);
+			return NM_SETTING (s_ip4);
+#endif
+		} else if (!g_ascii_strcasecmp (value, "none") || !g_ascii_strcasecmp (value, "static")) {
+			/* Static IP */
+		} else if (strlen (value)) {
+			/* FIXME actually it is static on Mandriva */
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Unknown BOOTPROTO '%s'", value);
+			g_free (value);
+			goto error;
+		}
+		g_free (value);
+	} else {
+		char *tmp_ip4, *tmp_prefix, *tmp_netmask;
+
+		/* If there is no BOOTPROTO, no IPADDR, no PREFIX, and no NETMASK,
+		 * assume DHCP is to be used.  Happens with minimal ifcfg files like:
+		 *
+		 * DEVICE=eth0
+		 * HWADDR=11:22:33:44:55:66
+		 *
+		 */
+		/* FIXME
+		 * This is not strictly speaking true on Mandriva. Interface
+		 * will be up (ip link up) and zeroconf address will be set
+		 * but no DHCP started */
+		tmp_ip4 = svGetValue (ifcfg, "IPADDR", FALSE);
+		tmp_prefix = svGetValue (ifcfg, "PREFIX", FALSE);
+		tmp_netmask = svGetValue (ifcfg, "NETMASK", FALSE);
+		if (!tmp_ip4 && !tmp_prefix && !tmp_netmask)
+			method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+		g_free (tmp_ip4);
+		g_free (tmp_prefix);
+		g_free (tmp_netmask);
+	}
+
+	g_object_set (s_ip4,
+	              NM_SETTING_IP4_CONFIG_METHOD, method,
+	              NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "PEERDNS", TRUE),
+		      // Not exists on Mandriva
+	              // NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "PEERROUTES", TRUE),
+	              NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, never_default,
+	              NULL);
+
+	/* Handle manual settings */
+	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+		NMIP4Address *addr;
+
+		for (i = 1; i < 256; i++) {
+			addr = read_full_ip4_address (ifcfg, network_file, i, error);
+			if (error && *error)
+				goto error;
+			if (!addr)
+				break;
+
+			if (!nm_setting_ip4_config_add_address (s_ip4, addr))
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate IP4 address");
+			nm_ip4_address_unref (addr);
+		}
+	} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		value = svGetValue (ifcfg, "DHCP_HOSTNAME", FALSE);
+		if (value && strlen (value))
+			g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, value, NULL);
+		g_free (value);
+
+#if 0
+		/* Does not seem to be used on Mandriva */
+		value = svGetValue (ifcfg, "DHCP_CLIENT_ID", FALSE);
+		if (value && strlen (value))
+			g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL);
+		g_free (value);
+#endif
+	}
+
+	/* DNS servers
+	 * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
+	 */
+	for (i = 1, tmp_success = TRUE; i <= 10 && tmp_success; i++) {
+		char *tag;
+		guint32 dns;
+		// struct in6_addr ip6_dns;
+		// GError *tmp_err = NULL;
+
+		tag = g_strdup_printf ("DNS%u", i);
+		tmp_success = read_ip4_address (ifcfg, tag, &dns, error);
+#if 0
+		/* No IPv6 on Mandriva */
+		if (!tmp_success) {
+			/* if it's IPv6, don't exit */
+			dns = 0;
+			value = svGetValue (ifcfg, tag, FALSE);
+			if (value) {
+				tmp_success = parse_ip6_address (value, &ip6_dns, &tmp_err);
+				g_clear_error (&tmp_err);
+				g_free (value);
+			}
+			if (!tmp_success) {
+				g_free (tag);
+				goto error;
+			}
+			g_clear_error (error);
+		}
+#endif
+
+		if (dns && !nm_setting_ip4_config_add_dns (s_ip4, dns))
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate DNS server %s", tag);
+		g_free (tag);
+	}
+
+	/* DNS searches */
+	value = svGetValue (ifcfg, "DOMAIN", FALSE);
+	if (value) {
+		char **searches = NULL;
+
+		searches = g_strsplit (value, " ", 0);
+		if (searches) {
+			char **item;
+			for (item = searches; *item; item++) {
+				if (strlen (*item)) {
+					if (!nm_setting_ip4_config_add_dns_search (s_ip4, *item))
+						PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate DNS domain '%s'", *item);
+				}
+			}
+			g_strfreev (searches);
+		}
+		g_free (value);
+	}
+
+#if 0
+	/* Some support is present on Mandriva but no GUI to configure */
+	/* Static routes  - route-<name> file */
+	route_path = utils_get_route_path (ifcfg->fileName);
+	if (!route_path) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not get route file path for '%s'", ifcfg->fileName);
+		goto error;
+	}
+
+	/* First test new/legacy syntax */
+	if (utils_has_route_file_new_syntax (route_path)) {
+		/* Parse route file in new syntax */
+		g_free (route_path);
+		route_ifcfg = utils_get_route_ifcfg (ifcfg->fileName, FALSE);
+		if (route_ifcfg) {
+			NMIP4Route *route;
+			for (i = 0; i < 256; i++) {
+				route = read_one_ip4_route (route_ifcfg, network_file, i, error);
+				if (error && *error) {
+					svCloseFile (route_ifcfg);
+					goto error;
+				}
+				if (!route)
+					break;
+
+				if (!nm_setting_ip4_config_add_route (s_ip4, route))
+					PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate IP4 route");
+				nm_ip4_route_unref (route);
+			}
+			svCloseFile (route_ifcfg);
+		}
+	} else {
+		read_route_file_legacy (route_path, s_ip4, error);
+		g_free (route_path);
+		if (error && *error)
+			goto error;
+	}
+#endif
+
+#if 0
+	/* Does not seem to be used anyhwere on Mandriva */
+	/* Legacy value NM used for a while but is incorrect (rh #459370) */
+	if (!nm_setting_ip4_config_get_num_dns_searches (s_ip4)) {
+		value = svGetValue (ifcfg, "SEARCH", FALSE);
+		if (value) {
+			char **searches = NULL;
+
+			searches = g_strsplit (value, " ", 0);
+			if (searches) {
+				char **item;
+				for (item = searches; *item; item++) {
+					if (strlen (*item)) {
+						if (!nm_setting_ip4_config_add_dns_search (s_ip4, *item))
+							PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate DNS search '%s'", *item);
+					}
+				}
+				g_strfreev (searches);
+			}
+			g_free (value);
+		}
+	}
+#endif
+
+	return NM_SETTING (s_ip4);
+
+error:
+	g_object_unref (s_ip4);
+	return NULL;
+}
+
+#if 0
+No IPv6 on Mandriva
+static NMSetting *
+make_ip6_setting (shvarFile *ifcfg,
+                  const char *network_file,
+                  const char *iscsiadm_path,
+                  GError **error)
+{
+	NMSettingIP6Config *s_ip6 = NULL;
+	char *value = NULL;
+	char *str_value;
+	char *route6_path = NULL;
+	gboolean bool_value, ipv6forwarding, ipv6_autoconf;
+	char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+	guint32 i;
+	shvarFile *network_ifcfg;
+	gboolean never_default = FALSE, tmp_success;
+
+	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+	if (!s_ip6) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not allocate IP6 setting");
+		return NULL;
+	}
+
+	/* Is IPV6 enabled? Set method to "ignored", when not enabled */
+	str_value = svGetValue (ifcfg, "IPV6INIT", FALSE);
+	bool_value = svTrueValue (ifcfg, "IPV6INIT", FALSE);
+	if (!str_value) {
+		network_ifcfg = svNewFile (network_file);
+		if (network_ifcfg) {
+			bool_value = svTrueValue (network_ifcfg, "IPV6INIT", FALSE);
+			svCloseFile (network_ifcfg);
+		}
+	}
+	g_free (str_value);
+
+	if (!bool_value) {
+		/* IPv6 is disabled */
+		g_object_set (s_ip6,
+		              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+		              NULL);
+		return NM_SETTING (s_ip6);
+	}
+
+	/* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the
+	 * opposite meaning from never-default. The default if IPV6_DEFROUTE is not
+	 * specified is IPV6_DEFROUTE=yes which means that this connection can be used
+	 * as a default route
+	 */
+	never_default = !svTrueValue (ifcfg, "IPV6_DEFROUTE", TRUE);
+
+	/* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified;
+	 * they are global and override IPV6_DEFROUTE
+	 * When both are set, the device specified in IPV6_DEFAULTGW takes preference.
+	 */
+	network_ifcfg = svNewFile (network_file);
+	if (network_ifcfg) {
+		char *ipv6_defaultgw, *ipv6_defaultdev;
+		char *default_dev = NULL;
+
+		/* Get the connection ifcfg device name and the global default route device */
+		value = svGetValue (ifcfg, "DEVICE", FALSE);
+		ipv6_defaultgw = svGetValue (network_ifcfg, "IPV6_DEFAULTGW", FALSE);
+		ipv6_defaultdev = svGetValue (network_ifcfg, "IPV6_DEFAULTDEV", FALSE);
+
+		if (ipv6_defaultgw) {
+			default_dev = strchr (ipv6_defaultgw, '%');
+			if (default_dev)
+				default_dev++;
+		}
+		if (!default_dev)
+			default_dev = ipv6_defaultdev;
+
+		/* If there was a global default route device specified, then only connections
+		 * for that device can be the default connection.
+		 */
+		if (default_dev && value)
+			never_default = !!strcmp (value, default_dev);
+
+		g_free (ipv6_defaultgw);
+		g_free (ipv6_defaultdev);
+		g_free (value);
+		svCloseFile (network_ifcfg);
+	}
+
+	/* Find out method property */
+	ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
+	ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
+
+	if (ipv6_autoconf)
+		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+	else {
+		/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
+		str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
+		if (!str_value)
+			str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
+
+		if (!str_value)
+			method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+		g_free (str_value);
+	}
+	/* TODO - handle other methods */
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP6_CONFIG_METHOD, method,
+	              NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "IPV6_PEERDNS", TRUE),
+	              NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "IPV6_PEERROUTES", TRUE),
+	              NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, never_default,
+	              NULL);
+
+	if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		NMIP6Address *addr;
+		char *val;
+		char *ipv6addr, *ipv6addr_secondaries;
+		char **list = NULL, **iter;
+
+		ipv6addr = svGetValue (ifcfg, "IPV6ADDR", FALSE);
+		ipv6addr_secondaries = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
+
+		val = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
+		                 ipv6addr ? ipv6addr : "",
+		                 ipv6addr_secondaries ? ipv6addr_secondaries : "",
+		                 NULL);
+		g_free (ipv6addr);
+		g_free (ipv6addr_secondaries);
+
+		list = g_strsplit_set (val, " ", 0);
+		g_free (val);
+		for (iter = list; iter && *iter; iter++, i++) {
+			addr = parse_full_ip6_address (*iter, error);
+			if (!addr) {
+				g_strfreev (list);
+				goto error;
+			}
+
+			if (!nm_setting_ip6_config_add_address (s_ip6, addr))
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate IP6 address");
+			nm_ip6_address_unref (addr);
+		}
+		g_strfreev (list);
+	} else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		/* TODO - autoconf or DHCPv6 stuff goes here */
+	}
+
+	/* DNS servers
+	 * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
+	 */
+	for (i = 1, tmp_success = TRUE; i <= 10 && tmp_success; i++) {
+		char *tag;
+		struct in6_addr ip6_dns;
+
+		ip6_dns = in6addr_any;
+		tag = g_strdup_printf ("DNS%u", i);
+		value = svGetValue (ifcfg, tag, FALSE);
+		if (value)
+			tmp_success = parse_ip6_address (value, &ip6_dns, error);
+
+		if (!tmp_success) {
+			struct in_addr ip4_addr;
+			if (inet_pton (AF_INET, value, &ip4_addr) != 1) {
+				g_free (tag);
+				g_free (value);
+				goto error;
+			}
+			/* ignore error - it is IPv4 address */
+			tmp_success = TRUE;
+			g_clear_error (error);
+		}
+
+		if (!IN6_IS_ADDR_UNSPECIFIED (&ip6_dns) && !nm_setting_ip6_config_add_dns (s_ip6, &ip6_dns))
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: duplicate DNS server %s", tag);
+		g_free (tag);
+		g_free (value);
+	}
+
+	/* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIP4Config */
+
+	/* Read static routes from route6-<interface> file */
+	route6_path = utils_get_route6_path (ifcfg->fileName);
+	if (!route6_path) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not get route6 file path for '%s'", ifcfg->fileName);
+		goto error;
+	}
+
+	read_route6_file (route6_path, s_ip6, error);
+	g_free (route6_path);
+	if (error && *error)
+		goto error;
+
+	return NM_SETTING (s_ip6);
+
+error:
+	g_object_unref (s_ip6);
+	return NULL;
+}
+#endif
+
+static gboolean
+add_one_wep_key (shvarFile *ifcfg,
+                 const char *shvar_key,
+                 guint8 key_idx,
+                 NMSettingWirelessSecurity *s_wsec,
+                 GError **error)
+{
+	char *key = NULL;
+	char *value = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (ifcfg != NULL, FALSE);
+	g_return_val_if_fail (shvar_key != NULL, FALSE);
+	g_return_val_if_fail (key_idx <= 3, FALSE);
+	g_return_val_if_fail (s_wsec != NULL, FALSE);
+
+	value = svGetValue (ifcfg, shvar_key, FALSE);
+	if (!value || !strlen (value)) {
+		g_free (value);
+		return TRUE;
+	}
+
+	/* Validate keys */
+	if (strlen (value) == 10 || strlen (value) == 26) {
+		/* Hexadecimal WEP key */
+		char *p = value;
+
+		while (*p) {
+			if (!g_ascii_isxdigit (*p)) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				             "Invalid hexadecimal WEP key.");
+				goto out;
+			}
+			p++;
+		}
+		key = g_strdup (value);
+	} else if ( !strncmp (value, "s:", 2)
+	           && (strlen (value) == 7 || strlen (value) == 15)) {
+		/* ASCII passphrase */
+		char *p = value + 2;
+
+		while (*p) {
+			if (!isascii ((int) (*p))) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				             "Invalid ASCII WEP passphrase.");
+				goto out;
+			}
+			p++;
+		}
+
+		key = utils_bin2hexstr (value + 2, strlen (value + 2), strlen (value + 2) * 2);
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid WEP key length.");
+	}
+
+	if (key) {
+		nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
+		success = TRUE;
+	}
+
+out:
+	g_free (value);
+	return success;
+}
+
+static gboolean
+read_wep_keys (shvarFile *ifcfg,
+               guint8 def_idx,
+               NMSettingWirelessSecurity *s_wsec,
+               GError **error)
+{
+	if (!add_one_wep_key (ifcfg, "WIRELESS_ENC_KEY", 0, s_wsec, error))
+		return FALSE;
+#if 0
+	/* Mandriva is using only one key */
+	if (!add_one_wep_key (ifcfg, "KEY1", 0, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ifcfg, "KEY2", 1, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ifcfg, "KEY3", 2, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ifcfg, "KEY4", 3, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ifcfg, "KEY", def_idx, s_wsec, error))
+		return FALSE;
+#endif
+
+	return TRUE;
+}
+
+static NMSetting *
+make_wep_setting (shvarFile *ifcfg,
+                  const char *file,
+                  GError **error)
+{
+	NMSettingWirelessSecurity *s_wireless_sec;
+	char *value;
+	// shvarFile *keys_ifcfg = NULL;
+	int default_key_idx = 0;
+
+	s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+	g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL);
+
+#if 0
+	/* Mandriva always assumes defalt key #0 */
+	value = svGetValue (ifcfg, "DEFAULTKEY", FALSE);
+	if (value) {
+		gboolean success;
+
+		success = get_int (value, &default_key_idx);
+		if (success && (default_key_idx >= 1) && (default_key_idx <= 4)) {
+			default_key_idx--;  /* convert to [0...3] */
+			g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, default_key_idx, NULL);
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid default WEP key '%s'", value);
+	 		g_free (value);
+			goto error;
+		}
+ 		g_free (value);
+	}
+#endif
+
+	/* Read keys in the ifcfg file */
+	if (!read_wep_keys (ifcfg, default_key_idx, s_wireless_sec, error))
+		goto error;
+
+#if 0
+	/* No shadow on Mandriva */
+	/* Try to get keys from the "shadow" key file */
+	keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
+	if (keys_ifcfg) {
+		if (!read_wep_keys (keys_ifcfg, default_key_idx, s_wireless_sec, error)) {
+			svCloseFile (keys_ifcfg);
+			goto error;
+		}
+		svCloseFile (keys_ifcfg);
+	}
+#endif
+
+#if 0
+	/* Only one key on Mandriva */
+	/* If there's a default key, ensure that key exists */
+	if ((default_key_idx == 1) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Default WEP key index was 2, but no valid KEY2 exists.");
+		goto error;
+	} else if ((default_key_idx == 2) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Default WEP key index was 3, but no valid KEY3 exists.");
+		goto error;
+	} else if ((default_key_idx == 3) && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Default WEP key index was 4, but no valid KEY4 exists.");
+		goto error;
+	}
+#endif
+
+	value = svGetValue (ifcfg, "WIRELESS_ENC_MODE", FALSE);
+	if (value) {
+		char *lcase;
+
+		lcase = g_ascii_strdown (value, -1);
+		g_free (value);
+
+		if (!strcmp (lcase, "open")) {
+			g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL);
+		} else if (!strcmp (lcase, "restricted")) {
+			g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL);
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid WEP authentication algorithm '%s'",
+			             lcase);
+			g_free (lcase);
+			goto error;
+		}
+		g_free (lcase);
+	}
+
+	if (   !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)
+	    && !nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)) {
+		const char *auth_alg;
+
+		auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec);
+		if (auth_alg && !strcmp (auth_alg, "shared")) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "WEP Shared Key authentication is invalid for "
+			             "unencrypted connections.");
+			goto error;
+		}
+
+		/* Unencrypted */
+		g_object_unref (s_wireless_sec);
+		s_wireless_sec = NULL;
+	}
+
+	return (NMSetting *) s_wireless_sec;
+
+error:
+	if (s_wireless_sec)
+		g_object_unref (s_wireless_sec);
+	return NULL;
+}
+
+static gboolean
+fill_wpa_ciphers (WPANetwork *wpan,
+                  NMSettingWirelessSecurity *wsec,
+                  gboolean group,
+                  gboolean adhoc)
+{
+	char *value;
+	char **list = NULL, **iter;
+	int i = 0;
+
+	value = ifcfg_mdv_wpa_network_get_val (wpan, group ? "group" : "pairwise");
+	if (!value)
+		return TRUE;
+
+	list = g_strsplit_set (value, " ", 0);
+	for (iter = list; iter && *iter; iter++, i++) {
+		if (!*iter)
+			continue;
+
+		/* Ad-Hoc configurations cannot have pairwise ciphers, and can only
+		 * have one group cipher.  Ignore any additional group ciphers and
+		 * any pairwise ciphers specified.
+		 */
+		if (adhoc) {
+			if (group && (i > 0)) {
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)",
+				             *iter);
+				continue;
+			} else if (!group) {
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignoring pairwise cipher '%s' (pairwise not used in Ad-Hoc mode)",
+				             *iter);
+				continue;
+			}
+		}
+
+		if (!strcmp (*iter, "CCMP")) {
+			if (group)
+				nm_setting_wireless_security_add_group (wsec, "ccmp");
+			else
+				nm_setting_wireless_security_add_pairwise (wsec, "ccmp");
+		} else if (!strcmp (*iter, "TKIP")) {
+			if (group)
+				nm_setting_wireless_security_add_group (wsec, "tkip");
+			else
+				nm_setting_wireless_security_add_pairwise (wsec, "tkip");
+		} else if (group && !strcmp (*iter, "WEP104"))
+			nm_setting_wireless_security_add_group (wsec, "wep104");
+		else if (group && !strcmp (*iter, "WEP40"))
+			nm_setting_wireless_security_add_group (wsec, "wep40");
+		else {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignoring invalid %s cipher '%s'",
+			             group ? "group" : "pairwise",
+			             *iter);
+		}
+	}
+
+	if (list)
+		g_strfreev (list);
+	return TRUE;
+}
+
+static char *
+parse_wpa_psk (WPANetwork *wpan,
+               const char *file,
+               const GByteArray *ssid,
+               GError **error)
+{
+	char *psk = NULL, *p, *hashed = NULL;
+	gboolean quoted = FALSE;
+
+	/* Passphrase must be between 10 and 66 characters in length becuase WPA
+	 * hex keys are exactly 64 characters (no quoting), and WPA passphrases
+	 * are between 8 and 63 characters (inclusive), plus optional quoting if
+	 * the passphrase contains spaces.
+	 */
+
+	psk = ifcfg_mdv_wpa_network_get_val (wpan, "psk");
+
+	if (!psk) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing WPA psk for WPA-PSK key management");
+		return NULL;
+	}
+
+	psk = p = g_strdup (psk);
+
+	if (p[0] == '"') {
+		if (psk[strlen (psk) - 1] != '"') {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Invalid WPA psk (unterminated quote)");
+			goto out;
+		}
+		quoted = TRUE;
+	}
+
+	if (!quoted) {
+		/* Verify the hex PSK; 64 digits */
+	       	if (strlen (psk) == 64) {
+			while (*p) {
+				if (!isxdigit (*p++)) {
+					g_set_error (error, ifcfg_plugin_error_quark (), 0,
+						     "Invalid WPA psk (contains non-hexadecimal characters)");
+					goto out;
+				}
+			}
+		} else {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Invalid WPA psk (hex key not equal 64 characters)");
+				goto out;
+		}
+		hashed = g_strdup (psk);
+	} else {
+		/* Prior to 4f6eef9e77265484555663cf666cde4fa8323469 and
+		 * 28e2e446868b94b92edc4a82aa0bf1e3eda8ec54 the writer may not have
+		 * properly quoted passphrases, so just handle anything that's unquoted
+		 * and between 8 and 63 characters as a passphrase.
+		 */
+
+		/* Get rid of the quotes */
+		p++;
+		p[strlen (p) - 1] = '\0';
+
+		/* Length check */
+		if (strlen (p) < 8 || strlen (p) > 63) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid WPA psk (passphrases must be between "
+			             "8 and 63 characters long (inclusive))");
+			goto out;
+		}
+
+		hashed = g_strdup (p);
+	}
+
+	if (!hashed) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid WPA psk (doesn't look like a passphrase or hex key)");
+	}
+
+out:
+	g_free (psk);
+	return hashed;
+}
+
+typedef struct {
+	const char *method;
+	gboolean (*reader)(const char *eap_method,
+			   WPANetwork *wpan,
+	                   shvarFile *ifcfg,
+	                   shvarFile *keys,
+	                   NMSetting8021x *s_8021x,
+	                   gboolean phase2,
+	                   GError **error);
+	gboolean wifi_phase2_only;
+} EAPReader;
+
+static EAPReader eap_readers[] = {
+	{ "md5", eap_simple_reader, TRUE },
+	{ "pap", eap_simple_reader, TRUE },
+	{ "chap", eap_simple_reader, TRUE },
+	{ "mschap", eap_simple_reader, TRUE },
+	{ "mschapv2", eap_simple_reader, TRUE },
+	{ "leap", eap_simple_reader, TRUE },
+	{ "tls", eap_tls_reader, FALSE },
+	{ "peap", eap_peap_reader, FALSE },
+	{ "ttls", eap_ttls_reader, FALSE },
+	{ NULL, NULL }
+};
+
+static gboolean
+eap_simple_reader (const char *eap_method,
+		   WPANetwork *wpan,
+                   shvarFile *ifcfg,
+                   shvarFile *keys,
+                   NMSetting8021x *s_8021x,
+                   gboolean phase2,
+                   GError **error)
+{
+	char *value = NULL;
+
+	/* FIXME wpa identity can contain '\0' */
+	value = ifcfg_mdv_wpa_network_get_str(wpan, "identity");
+	if (!value) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing identity for EAP method '%s'.",
+		             eap_method);
+		return FALSE;
+	}
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
+	g_free(value);
+
+	/* FIXME can we expect hash:XXX password? */
+	value = ifcfg_mdv_wpa_network_get_str (wpan, "password");
+	if (!value) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing password for EAP method '%s'.",
+		             eap_method);
+		return FALSE;
+	}
+	g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL);
+	g_free(value);
+
+	return TRUE;
+}
+
+static gboolean
+eap_tls_reader (const char *eap_method,
+		WPANetwork *wpan,
+                shvarFile *ifcfg,
+                shvarFile *keys,
+                NMSetting8021x *s_8021x,
+                gboolean phase2,
+                GError **error)
+{
+	char *value = NULL;
+	char *ca_cert = NULL;
+	char *client_cert = NULL;
+	char *privkey = NULL;
+	char *privkey_password = NULL;
+	gboolean success = FALSE;
+	NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	/* FIXME wpa identity can contain '\0' */
+	value = ifcfg_mdv_wpa_network_get_str(wpan, "identity");
+	if (!value) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing identity for EAP method '%s'.",
+		             eap_method);
+		return FALSE;
+	}
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
+
+	ca_cert = ifcfg_mdv_wpa_network_get_str(wpan,
+	                      phase2 ? "ca_cert2" : "ca_cert");
+	if (ca_cert) {
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
+			                                           ca_cert,
+			                                           NM_SETTING_802_1X_CK_SCHEME_PATH,
+			                                           NULL,
+			                                           error))
+				goto done;
+		} else {
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+			                                    ca_cert,
+			                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
+			                                    NULL,
+			                                    error))
+				goto done;
+		}
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: missing %s for EAP"
+		             " method '%s'; this is insecure!",
+	                     phase2 ? "ca_cert2" : "ca_cert",
+		             eap_method);
+	}
+
+	/* Private key password */
+	privkey_password = ifcfg_mdv_wpa_network_get_str(wpan,
+	                               phase2 ? "private_key2_password": "private_key_password");
+	if (!privkey_password) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing %s for EAP method '%s'.",
+		             phase2 ? "private_key2_password" : "private_key_password",
+		             eap_method);
+		goto done;
+	}
+
+	/* The private key itself */
+	privkey = ifcfg_mdv_wpa_network_get_str(wpan,
+	                      phase2 ? "private_key2" : "private_key");
+	if (!privkey) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing %s for EAP method '%s'.",
+	                      phase2 ? "private_key2" : "private_key",
+		             eap_method);
+		goto done;
+	}
+
+	if (phase2) {
+		if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
+		                                               privkey,
+		                                               privkey_password,
+			                                           NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                                               &privkey_format,
+		                                               error))
+			goto done;
+	} else {
+		if (!nm_setting_802_1x_set_private_key (s_8021x,
+		                                        privkey,
+		                                        privkey_password,
+			                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                                        &privkey_format,
+		                                        error))
+			goto done;
+	}
+
+	/* Only set the client certificate if the private key is not PKCS#12 format,
+	 * as NM (due to supplicant restrictions) requires.  If the key was PKCS#12,
+	 * then nm_setting_802_1x_set_private_key() already set the client certificate
+	 * to the same value as the private key.
+	 */
+	if (   privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
+	    || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) {
+		client_cert = ifcfg_mdv_wpa_network_get_str(wpan,
+		                          phase2 ? "client_cert2" : "client_cert");
+		if (!client_cert) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Missing %s for EAP method '%s'.",
+			             phase2 ? "client_cert2" : "client_cert",
+			             eap_method);
+			goto done;
+		}
+
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x,
+			                                               client_cert,
+			                                               NM_SETTING_802_1X_CK_SCHEME_PATH,
+			                                               NULL,
+			                                               error))
+				goto done;
+		} else {
+			if (!nm_setting_802_1x_set_client_cert (s_8021x,
+			                                        client_cert,
+			                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
+			                                        NULL,
+			                                        error))
+				goto done;
+		}
+	}
+
+	success = TRUE;
+
+done:
+	g_free(value);
+	g_free(ca_cert);
+	g_free(privkey_password);
+	g_free(privkey);
+	g_free(client_cert);
+	return success;
+}
+
+static gboolean
+eap_peap_reader (const char *eap_method,
+		 WPANetwork *wpan,
+                 shvarFile *ifcfg,
+                 shvarFile *keys,
+                 NMSetting8021x *s_8021x,
+                 gboolean dummy,
+                 GError **error)
+{
+	char *ca_cert = NULL;
+	char *phase1 = NULL;
+	char *phase2 = NULL;
+	char *lower;
+	char **list = NULL, **iter;
+	gboolean success = FALSE;
+
+	ca_cert = ifcfg_mdv_wpa_network_get_str(wpan, "ca_cert");
+	if (ca_cert) {
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+		                                    ca_cert,
+		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                                    NULL,
+		                                    error))
+			goto done;
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: missing "
+		             "ca_cert for EAP method '%s'; this is"
+		             " insecure!",
+		             eap_method);
+	}
+
+	phase1 = ifcfg_mdv_wpa_network_get_str(wpan, "phase1");
+	if (phase1) {
+		list = g_strsplit_set(phase1, " ", 0);
+		for (iter = list; iter && *iter; iter++) {
+			char *p;
+
+		       if (!**iter)
+		       		continue;
+
+			p = strchr(*iter, '=');
+			if (p) {
+				*p++ = '\0';
+				if (!strcmp(*iter, "peapver")) {
+					if (!strcmp (p, "0"))
+						g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL);
+					else if (!strcmp (p, "1"))
+						g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL);
+					else {
+						g_set_error (error, ifcfg_plugin_error_quark (), 0,
+						     "Unknown peapver value '%s'",
+			             p);
+						goto done;
+					}
+				} else if (!strcmp(*iter, "peaplabel")) {
+					if (!strcmp(p, "1")) {
+						g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
+					}
+				}
+			} else
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: incorrect "
+		             "phase1 parameter '%s' EAP method '%s';"
+			     " key=value expected!",
+		             *iter, eap_method);
+		}
+		if (list)
+			g_strfreev(list);
+	}
+
+	phase2 = ifcfg_mdv_wpa_network_get_str(wpan, "phase2");
+	if (!phase2) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing phase2 (PEAP inner authentication parameters).");
+		goto done;
+	}
+
+	/* Handle options for the inner auth method */
+	list = g_strsplit (phase2, " ", 0);
+	for (iter = list; iter && *iter; iter++) {
+		char *p;
+
+		if (!**iter)
+			continue;
+
+		p = strchr(*iter, '=');
+		if (!p) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Incorrect phase2 parameter '%s'; key=value expected.", *iter);
+			goto done;
+		}
+		*p++ = '\0';
+		if (!strcmp(*iter, "auth")) {
+			if (   !strcmp (p, "MSCHAPV2")
+			    || !strcmp (p, "MD5")
+			    || !strcmp (p, "GTC")) {
+				if (!eap_simple_reader (p, wpan, ifcfg, keys, s_8021x, TRUE, error))
+					goto done;
+			} else if (!strcmp (p, "TLS")) {
+				if (!eap_tls_reader (p, wpan, ifcfg, keys, s_8021x, TRUE, error))
+					goto done;
+			} else {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Unknown PEAP inner authentication method 'auth=%s'.",
+				  p);
+				goto done;
+			}
+		} else if (!strcmp (*iter, "autheap")) {
+			/* These parameters are for EAP-TTLS */
+			continue;
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Unknown phase2 inner authentication method '%s=%s'.",
+			             *iter, p);
+			goto done;
+		}
+
+		lower = g_ascii_strdown (p, -1);
+		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
+		g_free (lower);
+		break;
+	}
+
+	if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "No valid inner authentication methods found.");
+		goto done;
+	}
+
+	success = TRUE;
+
+done:
+	g_free(ca_cert);
+	g_free(phase1);
+	g_free(phase2);
+	if (list)
+		g_strfreev (list);
+	return success;
+}
+
+static gboolean
+eap_ttls_reader (const char *eap_method,
+		 WPANetwork *wpan,
+                 shvarFile *ifcfg,
+                 shvarFile *keys,
+                 NMSetting8021x *s_8021x,
+                 gboolean dummy,
+                 GError **error)
+{
+	gboolean success = FALSE;
+	char *anon_ident = NULL;
+	char *ca_cert = NULL;
+	char *phase2 = NULL;
+	char **list = NULL, **iter;
+
+	ca_cert = ifcfg_mdv_wpa_network_get_str(wpan, "ca_cert");
+	if (ca_cert) {
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+		                                    ca_cert,
+		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                                    NULL,
+		                                    error))
+			goto done;
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: missing "
+		             "ca_cert for EAP method '%s'; this is"
+		             " insecure!",
+		             eap_method);
+	}
+
+	anon_ident = ifcfg_mdv_wpa_network_get_str(wpan, "anonymous_identity");
+	if (anon_ident && strlen (anon_ident))
+		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
+
+	phase2 = ifcfg_mdv_wpa_network_get_str(wpan, "phase2");
+	if (!phase2) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing phase2 (TTLS inner authentication parameters).");
+		goto done;
+	}
+
+	/* Handle options for the inner auth method */
+	list = g_strsplit (phase2, " ", 0);
+	for (iter = list; iter && *iter; iter++) {
+		gboolean  auth = FALSE, autheap = FALSE;
+		char *p, *lower = NULL;
+
+		if (!**iter)
+			continue;
+
+		p = strchr(*iter, '=');
+		if (!p) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Incorrect phase2 parameter '%s'; key=value expected.", *iter);
+			goto done;
+		}
+		*p++ = '\0';
+
+		if (!strcmp(*iter, "auth")) {
+			auth = TRUE;
+			if (   !strcmp (p, "MSCHAPV2")
+			    || !strcmp (p, "MSCHAP")
+			    || !strcmp (p, "PAP")
+			    || !strcmp (p, "CHAP")) {
+				if (!eap_simple_reader (p, wpan, ifcfg, keys, s_8021x, TRUE, error))
+					goto done;
+			} else {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Unknown TTLS inner authentication method 'auth=%s'.",
+					     p);
+				goto done;
+			}
+		} else if (!strcmp(*iter, "autheap")) {
+			autheap = TRUE;
+			if (!strcmp (p, "TLS")) {
+				if (!eap_tls_reader (p, wpan, ifcfg, keys, s_8021x, TRUE, error))
+					goto done;
+			} else if (!strcmp (p, "MSCHAPV2")
+				|| !strcmp (p, "MD5")) {
+				if (!eap_simple_reader (p, wpan, ifcfg, keys, s_8021x, TRUE, error))
+					goto done;
+			} else {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					     "Unknown TTLS inner authentication method 'autheap=%s'.",
+					     p);
+				goto done;
+			}
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Unknown phase2 inner authentication method '%s=%s'.",
+			             *iter, p);
+			goto done;
+		}
+		lower = g_ascii_strdown (p, -1);
+		if (auth)
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
+		if (autheap)
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, p, NULL);
+		g_free (lower);
+		break;
+	}
+
+	success = TRUE;
+
+done:
+	g_free(ca_cert);
+	g_free(anon_ident);
+	g_free(phase2);
+	if (list)
+		g_strfreev (list);
+	return success;
+}
+
+static NMSetting8021x *
+fill_8021x (shvarFile *ifcfg,
+	    WPANetwork *wpan,
+            const char *file,
+            const char *key_mgmt,
+            gboolean wifi,
+            GError **error)
+{
+	NMSetting8021x *s_8021x;
+	shvarFile *keys = NULL;
+	char *value;
+	char **list, **iter;
+
+	value = ifcfg_mdv_wpa_network_get_val(wpan, "eap");
+	if (!value) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing eap methods for key management '%s'",
+		             key_mgmt);
+		return NULL;
+	}
+
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+
+	/* Validate and handle each EAP method */
+	list = g_strsplit (value, " ", 0);
+	for (iter = list; iter && *iter; iter++) {
+		EAPReader *eap = &eap_readers[0];
+		gboolean found = FALSE;
+		char *lower = NULL;
+
+		lower = g_ascii_strdown (*iter, -1);
+		while (eap->method && !found) {
+			if (strcmp (eap->method, lower))
+				goto next;
+
+			/* Some EAP methods don't provide keying material, thus they
+			 * cannot be used with WiFi unless they are an inner method
+			 * used with TTLS or PEAP or whatever.
+			 */
+			if (wifi && eap->wifi_phase2_only) {
+				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignored invalid "
+				             "IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.",
+				             lower);
+				goto next;
+			}
+
+			/* Parse EAP method specific options */
+			if (!(*eap->reader)(lower, wpan, ifcfg, keys, s_8021x, FALSE, error)) {
+				g_free (lower);
+				g_strfreev(list);
+				goto error;
+			}
+			nm_setting_802_1x_add_eap_method (s_8021x, lower);
+			found = TRUE;
+
+		next:
+			eap++;
+		}
+
+		if (!found) {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignored unknown"
+			             "IEEE_8021X_EAP_METHOD '%s'.",
+			             lower);
+		}
+		g_free (lower);
+	}
+	g_strfreev (list);
+
+	if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "No valid EAP methods found in IEEE_8021X_EAP_METHODS.");
+		goto error;
+	}
+
+	return s_8021x;
+
+error:
+	g_object_unref (s_8021x);
+	return NULL;
+}
+
+static NMSetting *
+make_wpa_setting (shvarFile *ifcfg,
+		  WPANetwork *wpan,
+                  const char *file,
+                  const GByteArray *ssid,
+                  gboolean adhoc,
+                  NMSetting8021x **s_8021x,
+                  GError **error)
+{
+	NMSettingWirelessSecurity *wsec;
+	char *key_mgmt, *psk, *lower, *proto;
+	char **list = NULL, **iter;
+	int np;
+
+	key_mgmt = ifcfg_mdv_wpa_network_get_val (wpan, "key_mgmt");
+	/*
+	 * Can NM support two alternative methods?
+	 */
+	if (!key_mgmt)
+		key_mgmt = "WPA-PSK";
+
+	/* Is it WEP? */
+	if (!strcmp(key_mgmt, "NONE"))
+		return NULL;
+
+	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+
+	/* Pairwise and Group ciphers */
+	fill_wpa_ciphers (wpan, wsec, FALSE, adhoc);
+	fill_wpa_ciphers (wpan, wsec, TRUE, adhoc);
+
+	/*
+	 * WPA and/or RSN
+	 * Default to both WPA and RSN allowed.
+	 */
+	proto = ifcfg_mdv_wpa_network_get_val(wpan, "proto");
+	if (!proto)
+		proto="WPA RSN";
+
+	list = g_strsplit_set (proto, " ", 0);
+	for (np = 0, iter = list; iter && *iter; iter++) {
+		if (!*iter)
+			continue;
+
+		if (!strcmp (*iter, "WPA")) {
+			np++;
+			nm_setting_wireless_security_add_proto (wsec, "wpa");
+		} else if (!strcmp (*iter, "RSN")) {
+			if (adhoc) {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Ad-Hoc mode cannot be used with proto 'RSN'");
+				goto free_list;
+			}
+			np++;
+			nm_setting_wireless_security_add_proto (wsec, "rsn");
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "Unknown proto '%s'", *iter);
+			goto free_list;
+		}
+	}
+	if (list)
+		g_strfreev(list);
+
+	if (!np) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "Empty proto");
+		goto error;
+	}
+
+	/*
+	 * Mandriva adds by default list of available protocols
+	 * FIXME be more intelligent - do not fail completely if
+	 * multiple methods are present; configure the best
+	 * available
+	 */
+	if (strstr (key_mgmt, "WPA-EAP") || strstr (key_mgmt, "IEEE8021X")) {
+		/* Adhoc mode is mutually exclusive with any 802.1x-based authentication */
+		if (adhoc) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Ad-Hoc mode cannot be used with key_mgmt type '%s'", key_mgmt);
+			goto error;
+		}
+
+		*s_8021x = fill_8021x (ifcfg, wpan, file, key_mgmt, TRUE, error);
+		if (!*s_8021x)
+			goto error;
+
+	} else if (strstr (key_mgmt, "WPA-PSK")) {
+		if (adhoc) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Ad-Hoc mode cannot be used with key_mgmt type 'WPA-PSK'");
+			goto error;
+		}
+		psk = parse_wpa_psk (wpan, file, ssid, error);
+		if (!psk)
+			goto error;
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL);
+		g_free (psk);
+	} else if (strstr (key_mgmt, "WPA-NONE")) {
+		if (!adhoc) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "key_mgmt type 'WPA_NONE' allowed only in Ad-Hoc mode");
+			goto error;
+		}
+		psk = parse_wpa_psk (wpan, file, ssid, error);
+		if (!psk)
+			goto error;
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL);
+		g_free (psk);
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Unknown wireless KEY_MGMT type '%s'", key_mgmt);
+		goto error;
+	}
+	lower = g_ascii_strdown (key_mgmt, -1);
+	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL);
+	g_free (lower);
+
+	return (NMSetting *) wsec;
+
+free_list:
+	if (list)
+		g_strfreev(list);
+error:
+	if (wsec)
+		g_object_unref (wsec);
+	return NULL;
+}
+
+#if 0
+// LEAP does not seem to be supported by Mandriva
+static NMSetting *
+make_leap_setting (shvarFile *ifcfg,
+                   const char *file,
+                   GError **error)
+{
+	NMSettingWirelessSecurity *wsec;
+	shvarFile *keys_ifcfg;
+	char *value;
+
+	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+
+	value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
+	if (!value || strcmp (value, "IEEE8021X"))
+		goto error; /* Not LEAP */
+
+	g_free (value);
+	value = svGetValue (ifcfg, "WIRELESS_ENC_MODE", FALSE);
+	if (!value || strcasecmp (value, "leap"))
+		goto error; /* Not LEAP */
+
+	g_free (value);
+
+	value = svGetValue (ifcfg, "IEEE_8021X_PASSWORD", FALSE);
+	if (!value) {
+		/* Try to get keys from the "shadow" key file */
+		keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
+		if (keys_ifcfg) {
+			value = svGetValue (keys_ifcfg, "IEEE_8021X_PASSWORD", FALSE);
+			svCloseFile (keys_ifcfg);
+		}
+	}
+	if (value && strlen (value))
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL);
+	g_free (value);
+
+	value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE);
+	if (!value || !strlen (value)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing LEAP identity");
+		goto error;
+	}
+	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL);
+	g_free (value);
+
+	g_object_set (wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+	              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap",
+	              NULL);
+
+	return (NMSetting *) wsec;
+
+error:
+	g_free (value);
+	if (wsec)
+		g_object_unref (wsec);
+	return NULL;
+}
+#endif
+
+static NMSetting *
+make_wireless_security_setting (shvarFile *ifcfg,
+                                const char *file,
+                                const GByteArray *ssid,
+                                gboolean adhoc,
+                                NMSetting8021x **s_8021x,
+                                GError **error)
+{
+	NMSetting *wsec = NULL; /* unencrypted by default */
+	char *driver;
+	WPAConfig *wpac = NULL;
+	WPANetwork *wpan = NULL;
+
+	/*
+	 * Mandriva saves WPA parameters directly in wpa_supplicant.conf
+	 */
+	driver = svGetValue (ifcfg, "WIRELESS_WPA_DRIVER", FALSE);
+	if (driver) {
+		g_free (driver);
+
+		wpac = ifcfg_mdv_wpa_config_new("/etc/wpa_supplicant.conf");
+		if (wpac && ifcfg_mdv_wpa_config_parse(wpac)) {
+			gboolean found = FALSE;
+
+			ifcfg_mdv_wpa_config_rewind(wpac);
+			while (!found && (wpan = ifcfg_mdv_wpa_config_next(wpac)) != NULL) {
+				GByteArray *b_ssid = ifcfg_mdv_wpa_network_get_ssid(wpan);
+
+				if (b_ssid) {
+					if (b_ssid->len == ssid->len && !memcmp(b_ssid->data, ssid->data, ssid->len))
+						found = TRUE;
+					g_byte_array_unref(b_ssid);
+				} else
+					PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: no SSID in wpa_supplicant.conf network block");
+			}
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "WIRELESS_WPA_DRIVER set but /etc/wpa_supplicant.conf missing");
+			goto done;
+		}
+
+		if (!wpan) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "WIRELESS_WPA_DRIVER set but SSID missing in /etc/wpa_supplicant.conf");
+			goto done;
+		}
+	}
+
+	if (wpan) {
+#if 0
+		// LEAP does not seem to be supported by Mandriva
+		if (!adhoc) {
+			wsec = make_leap_setting (ifcfg, file, error);
+			if (wsec)
+				return wsec;
+			else if (*error)
+				goto error;
+		}
+#endif
+
+		wsec = make_wpa_setting (ifcfg, wpan, file, ssid, adhoc, s_8021x, error);
+		if (wsec || *error)
+			goto done;
+	}
+
+	wsec = make_wep_setting (ifcfg, file, error);
+
+done:
+	ifcfg_mdv_wpa_config_free (wpac);
+	return wsec;
+}
+
+static NMSetting *
+make_wireless_setting (shvarFile *ifcfg,
+                       gboolean nm_controlled,
+                       char **unmanaged,
+                       char *device,
+                       GError **error)
+{
+	NMSettingWireless *s_wireless;
+	GByteArray *array = NULL;
+	char *value;
+
+	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+
+	if (read_mac_address (ifcfg, &array, error)) {
+		/* if we don't have a HWADDR saved in ifcfg file, try to discover it manually */
+		if (!array) {
+			discover_mac_address(device, &array, error);
+		}
+		if (array) {
+			g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, array, NULL);
+
+			/* A connection can only be unmanaged if we know the MAC address */
+			if (!nm_controlled) {
+				*unmanaged = g_strdup_printf ("mac:%02x:%02x:%02x:%02x:%02x:%02x",
+				                              array->data[0], array->data[1], array->data[2],
+				                              array->data[3], array->data[4], array->data[5]);
+			}
+
+			g_byte_array_free (array, TRUE);
+		} else if (!nm_controlled) {
+			/* If NM_CONTROLLED=no but there wasn't a MAC address, notify
+			 * the user that the device cannot be unmanaged.
+			 */
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: NM_CONTROLLED was false but HWADDR was missing; device will be managed");
+		}
+	} else {
+		g_object_unref (s_wireless);
+		return NULL;
+	}
+
+	value = svGetValue (ifcfg, "WIRELESS_ESSID", TRUE);
+	if (value) {
+		array = ifcfg_mdv_parse_ssid (value, error);
+		g_free (value);
+
+		if (array) {
+			g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, array, NULL);
+			g_byte_array_free (array, TRUE);
+		} else
+			goto error;
+	} else {
+		/* Only fail on lack of SSID if device is managed */
+		if (nm_controlled) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0, "Missing SSID");
+			goto error;
+		}
+	}
+
+	if (!nm_controlled)
+		goto done;
+
+	value = svGetValue (ifcfg, "WIRELESS_MODE", FALSE);
+	if (value) {
+		char *lcase;
+		const char *mode = NULL;
+
+		lcase = g_ascii_strdown (value, -1);
+		g_free (value);
+
+		if (!strcmp (lcase, "ad-hoc")) {
+			mode = "adhoc";
+		} else if (!strcmp (lcase, "managed") || !strcmp (lcase, "auto")) {
+			mode = "infrastructure";
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid mode '%s' (not 'Ad-Hoc', 'Managed', or 'Auto')",
+			             lcase);
+			g_free (lcase);
+			goto error;
+		}
+		g_free (lcase);
+
+		g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL);
+	}
+
+#if 0
+	value = svGetValue (ifcfg, "WIRELESS_BSSID", FALSE);
+	if (value) {
+		struct ether_addr *eth;
+		GByteArray *bssid;
+
+		eth = ether_aton (value);
+		if (!eth) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid BSSID '%s'", value);
+			goto error;
+		}
+
+		bssid = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (bssid, eth->ether_addr_octet, ETH_ALEN);
+		g_object_set (s_wireless, NM_SETTING_WIRELESS_BSSID, bssid, NULL);
+		g_byte_array_free (bssid, TRUE);
+	}
+#endif
+
+	value = svGetValue (ifcfg, "WIRELESS_CHANNEL", FALSE);
+	if (value) {
+		long int chan;
+
+		errno = 0;
+		chan = strtol (value, NULL, 10);
+		if (errno || chan <= 0 || chan > 196) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid wireless channel '%s'", value);
+			g_free (value);
+			goto error;
+		}
+		g_object_set (s_wireless, NM_SETTING_WIRELESS_CHANNEL, (guint32) chan, NULL);
+		if (chan > 14)
+			g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "a", NULL);
+		else
+			g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL);
+	}
+
+	value = svGetValue (ifcfg, "MTU", FALSE);
+	if (value) {
+		long int mtu;
+
+		errno = 0;
+		mtu = strtol (value, NULL, 10);
+		if (errno || mtu < 0 || mtu > 50000) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Invalid wireless MTU '%s'", value);
+			g_free (value);
+			goto error;
+		}
+		g_object_set (s_wireless, NM_SETTING_WIRELESS_MTU, (guint32) mtu, NULL);
+	}
+
+done:
+	return NM_SETTING (s_wireless);
+
+error:
+	if (s_wireless)
+		g_object_unref (s_wireless);
+	return NULL;
+}
+
+static NMConnection *
+wireless_connection_from_ifcfg (const char *file,
+                                shvarFile *ifcfg,
+                                gboolean nm_controlled,
+                                char **unmanaged,
+                                char *device,
+                                GError **error)
+{
+	NMConnection *connection = NULL;
+	NMSetting *con_setting = NULL;
+	NMSetting *wireless_setting = NULL;
+	NMSetting8021x *s_8021x = NULL;
+	const GByteArray *ssid;
+	NMSetting *security_setting = NULL;
+	char *printable_ssid = NULL;
+	const char *mode;
+	gboolean adhoc = FALSE;
+
+	g_return_val_if_fail (file != NULL, NULL);
+	g_return_val_if_fail (ifcfg != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	connection = nm_connection_new ();
+	if (!connection) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Failed to allocate new connection for %s.", file);
+		return NULL;
+	}
+
+	/* Wireless */
+	wireless_setting = make_wireless_setting (ifcfg, nm_controlled, unmanaged, device, error);
+	if (!wireless_setting) {
+		g_object_unref (connection);
+		return NULL;
+	}
+	nm_connection_add_setting (connection, wireless_setting);
+
+	ssid = nm_setting_wireless_get_ssid (NM_SETTING_WIRELESS (wireless_setting));
+	if (ssid)
+		printable_ssid = nm_utils_ssid_to_utf8 ((const char *) ssid->data, ssid->len);
+	else
+		printable_ssid = g_strdup_printf ("unmanaged");
+
+	if (nm_controlled) {
+		mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (wireless_setting));
+		if (mode && !strcmp (mode, "adhoc"))
+			adhoc = TRUE;
+
+		/* Wireless security */
+		security_setting = make_wireless_security_setting (ifcfg, file, ssid, adhoc, &s_8021x, error);
+		if (*error) {
+			g_object_unref (connection);
+			return NULL;
+		}
+		if (security_setting) {
+			nm_connection_add_setting (connection, security_setting);
+			if (s_8021x)
+				nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+			g_object_set (wireless_setting, NM_SETTING_WIRELESS_SEC,
+			              NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NULL);
+		}
+	}
+
+	/* Connection */
+	con_setting = make_connection_setting (file, ifcfg,
+	                                       NM_SETTING_WIRELESS_SETTING_NAME,
+	                                       printable_ssid);
+	g_free (printable_ssid);
+	if (!con_setting) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Failed to create connection setting.");
+		g_object_unref (connection);
+		return NULL;
+	}
+	nm_connection_add_setting (connection, con_setting);
+
+	/* Don't verify if unmanaged since we may not have an SSID or whatever */
+	if (nm_controlled) {
+		if (!nm_connection_verify (connection, error)) {
+			g_object_unref (connection);
+			return NULL;
+		}
+	}
+
+	return connection;
+}
+
+static NMSetting *
+make_wired_setting (shvarFile *ifcfg,
+                    const char *file,
+                    gboolean nm_controlled,
+                    char **unmanaged,
+                    NMSetting8021x **s_8021x,
+                    char *device,
+                    GError **error)
+{
+	NMSettingWired *s_wired;
+	char *value = NULL;
+	int mtu;
+	GByteArray *mac = NULL;
+
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+
+	value = svGetValue (ifcfg, "MTU", FALSE);
+	if (value) {
+		if (get_int (value, &mtu)) {
+			if (mtu >= 0 && mtu < 65536)
+				g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu, NULL);
+		} else {
+			/* Shouldn't be fatal... */
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: invalid MTU '%s'", value);
+		}
+		g_free (value);
+	}
+
+	if (read_mac_address (ifcfg, &mac, error)) {
+		/* if we don't have a HWADDR saved in ifcfg file, try to discover it manually */
+		if (!mac) {
+			discover_mac_address(device, &mac, error);
+		}
+		if (mac) {
+			g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
+
+			/* A connection can only be unmanaged if we know the MAC address */
+			if (!nm_controlled) {
+				*unmanaged = g_strdup_printf ("mac:%02x:%02x:%02x:%02x:%02x:%02x",
+				                              mac->data[0], mac->data[1], mac->data[2],
+				                              mac->data[3], mac->data[4], mac->data[5]);
+			}
+
+			g_byte_array_free (mac, TRUE);
+		} else if (!nm_controlled) {
+			/* If NM_CONTROLLED=no but there wasn't a MAC address, notify
+			 * the user that the device cannot be unmanaged.
+			 */
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: NM_CONTROLLED was false but HWADDR was missing; device will be managed");
+		}
+	} else {
+		g_object_unref (s_wired);
+		s_wired = NULL;
+	}
+
+#if 0
+	/* Mandriva does not support IEEE802.1x on wired connections */
+	value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
+	if (value) {
+		if (!strcmp (value, "IEEE8021X")) {
+			*s_8021x = fill_8021x (ifcfg, file, value, FALSE, error);
+			if (!*s_8021x)
+				goto error;
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Unknown wired KEY_MGMT type '%s'", value);
+			goto error;
+		}
+		g_free (value);
+	}
+#endif
+
+	return (NMSetting *) s_wired;
+
+#if 0
+	/* Mandriva does not support IEEE802.1x on wired connections;
+	 * this is unreacheable */
+error:
+	g_free (value);
+	g_object_unref (s_wired);
+	return NULL;
+#endif
+}
+
+static NMConnection *
+wired_connection_from_ifcfg (const char *file,
+                             shvarFile *ifcfg,
+                             gboolean nm_controlled,
+                             char **unmanaged,
+                             char *device,
+                             GError **error)
+{
+	NMConnection *connection = NULL;
+	NMSetting *con_setting = NULL;
+	NMSetting *wired_setting = NULL;
+	NMSetting8021x *s_8021x = NULL;
+
+	g_return_val_if_fail (file != NULL, NULL);
+	g_return_val_if_fail (ifcfg != NULL, NULL);
+
+	connection = nm_connection_new ();
+	if (!connection) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Failed to allocate new connection for %s.", file);
+		return NULL;
+	}
+
+	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL);
+	if (!con_setting) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Failed to create connection setting.");
+		g_object_unref (connection);
+		return NULL;
+	}
+	nm_connection_add_setting (connection, con_setting);
+
+	wired_setting = make_wired_setting (ifcfg, file, nm_controlled, unmanaged, &s_8021x, device, error);
+	if (!wired_setting) {
+		g_object_unref (connection);
+		return NULL;
+	}
+	nm_connection_add_setting (connection, wired_setting);
+
+#if 0
+	/* Always NULL on Mandriva */
+	if (s_8021x)
+		nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+#endif
+
+	if (!nm_connection_verify (connection, error)) {
+		g_object_unref (connection);
+		return NULL;
+	}
+
+	return connection;
+}
+
+static gboolean
+is_wireless_device (const char *iface)
+{
+	int fd;
+	struct iw_range range;
+	struct iwreq wrq;
+	gboolean is_wireless = FALSE;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (!fd)
+		return FALSE;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	memset (&range, 0, sizeof (struct iw_range));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	wrq.u.data.pointer = (caddr_t) &range;
+	wrq.u.data.length = sizeof (struct iw_range);
+
+	if (ioctl (fd, SIOCGIWRANGE, &wrq) == 0)
+		is_wireless = TRUE;
+	else {
+		if (errno == EOPNOTSUPP)
+			is_wireless = FALSE;
+		else {
+			/* Sigh... some wired devices (kvm/qemu) return EINVAL when the
+			 * device is down even though it's not a wireless device.  So try
+			 * IWNAME as a fallback.
+			 */
+			memset (&wrq, 0, sizeof (struct iwreq));
+			strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+			if (ioctl (fd, SIOCGIWNAME, &wrq) == 0)
+				is_wireless = TRUE;
+		}
+	}
+
+	close (fd);
+	return is_wireless;
+}
+
+NMConnection *
+connection_from_file (const char *filename,
+                      const char *network_file,  /* for unit tests only */
+                      const char *test_type,     /* for unit tests only */
+                      const char *iscsiadm_path, /* for unit tests only */
+                      char **unmanaged,
+                      char **keyfile,
+                      char **routefile,
+                      char **route6file,
+                      GError **error,
+                      gboolean *ignore_error)
+{
+	NMConnection *connection = NULL;
+	shvarFile *parsed;
+	char *type, *nmc = NULL;
+	NMSetting *s_ip4;
+	const char *ifcfg_name = NULL;
+	gboolean nm_controlled = FALSE, onboot;
+	char *device;
+
+	g_return_val_if_fail (filename != NULL, NULL);
+	g_return_val_if_fail (unmanaged != NULL, NULL);
+	g_return_val_if_fail (*unmanaged == NULL, NULL);
+	g_return_val_if_fail (keyfile != NULL, NULL);
+	g_return_val_if_fail (*keyfile == NULL, NULL);
+	g_return_val_if_fail (routefile != NULL, NULL);
+	g_return_val_if_fail (*routefile == NULL, NULL);
+	g_return_val_if_fail (route6file != NULL, NULL);
+	g_return_val_if_fail (*route6file == NULL, NULL);
+
+	/* Non-NULL only for unit tests; normally use /etc/sysconfig/network */
+	if (!network_file)
+		network_file = SYSCONFDIR "/sysconfig/network";
+
+	if (!iscsiadm_path)
+		iscsiadm_path = SBINDIR "/iscsiadm";
+
+	ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
+	if (!ifcfg_name) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Ignoring connection '%s' because it's not an ifcfg file.", filename);
+		return NULL;
+	}
+
+	parsed = svNewFile (filename);
+	if (!parsed) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Couldn't parse file '%s'", filename);
+		return NULL;
+	}
+
+	/* Read the device */
+	device = svGetValue (parsed, "DEVICE", FALSE);
+	if (!device) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					 "File '%s' had neither TYPE nor DEVICE keys.", filename);
+		goto done;
+	}
+
+	if (!strcmp (device, "lo")) {
+		*ignore_error = TRUE;
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+					 "Ignoring loopback device config.");
+		g_free (device);
+		goto done;
+	}
+
+	type = svGetValue (parsed, "TYPE", FALSE);
+	if (!type) {
+
+		/* If no type, if the device has wireless extensions, it's wifi,
+		 * otherwise it's ethernet.
+		 */
+		if (!test_type) {
+			/* Test wireless extensions */
+			if (is_wireless_device (device))
+				type = g_strdup (TYPE_WIRELESS);
+			else
+				type = g_strdup (TYPE_ETHERNET);
+		} else {
+			/* For the unit tests, there won't necessarily be any
+			 * adapters of the connection's type in the system so the
+			 * type can't be tested with ioctls.
+			 */
+			type = g_strdup (test_type);
+		}
+
+	}
+
+	nmc = svGetValue (parsed, "NM_CONTROLLED", FALSE);
+	if (nmc) {
+		char *lower;
+
+		lower = g_ascii_strdown (nmc, -1);
+		g_free (nmc);
+
+		if (!strcmp (lower, "yes") || !strcmp (lower, "y") || !strcmp (lower, "true"))
+			nm_controlled = TRUE;
+		g_free (lower);
+	}
+
+       /*
+        * FIXME
+        * ONBOOT is used by Mandriva initscripts. For now use different
+        * variable; otherwise both initscripts and NM will try to
+        * bring interface online. Do not try to control interface
+        * if ONBOOT was set to true
+        */
+       onboot = svTrueValue (parsed, "ONBOOT", TRUE);
+       nm_controlled = nm_controlled && !onboot;
+
+	if (!strcasecmp (type, TYPE_ETHERNET))
+		connection = wired_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, device, error);
+	else if (!strcasecmp (type, TYPE_WIRELESS))
+		connection = wireless_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, device, error);
+	else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Unknown connection type '%s'", type);
+	}
+
+	if (nm_controlled) {
+		g_free (*unmanaged);
+		*unmanaged = NULL;
+	}
+
+	g_free (type);
+	g_free (device);
+
+	/* Don't bother reading the connection fully if it's unmanaged */
+	if (!connection || *unmanaged)
+		goto done;
+
+	s_ip4 = make_ip4_setting (parsed, network_file, iscsiadm_path, error);
+	if (*error) {
+		g_object_unref (connection);
+		connection = NULL;
+		goto done;
+	} else if (s_ip4) {
+		nm_connection_add_setting (connection, s_ip4);
+	}
+
+#if 0
+	/* No IPv6 on Mandriva */
+	s_ip6 = make_ip6_setting (parsed, network_file, iscsiadm_path, error);
+	if (*error) {
+		g_object_unref (connection);
+		connection = NULL;
+		goto done;
+	} else if (s_ip6)
+		nm_connection_add_setting (connection, s_ip6);
+#endif
+
+#if 0
+	/* no iSCSI on Mandriva */
+	/* iSCSI / ibft connections are read-only since their settings are
+	 * stored in NVRAM and can only be changed in BIOS.
+	 */
+	bootproto = svGetValue (parsed, "BOOTPROTO", FALSE);
+	if (   bootproto
+	    && connection
+	    && !g_ascii_strcasecmp (bootproto, "ibft")) {
+		NMSettingConnection *s_con;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_READ_ONLY, TRUE, NULL);
+	}
+#endif
+
+	if (!nm_connection_verify (connection, error)) {
+		g_object_unref (connection);
+		connection = NULL;
+	}
+
+	*keyfile = utils_get_keys_path (filename);
+	*routefile = utils_get_route_path (filename);
+	*route6file = utils_get_route6_path (filename);
+
+done:
+	svCloseFile (parsed);
+	return connection;
+}
+
+const char *
+reader_get_prefix (void)
+{
+	return _("System");
+}
+
diff --git a/system-settings/plugins/ifcfg-mdv/reader.h b/system-settings/plugins/ifcfg-mdv/reader.h
new file mode 100644
index 0000000..94fb235
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/reader.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef __READER_H__
+#define __READER_H__
+
+#include <glib.h>
+#include <nm-connection.h>
+
+#include "shvar.h"
+
+NMConnection *connection_from_file (const char *filename,
+                                    const char *network_file,  /* for unit tests only */
+                                    const char *test_type,     /* for unit tests only */
+                                    const char *iscsiadm_path, /* for unit tests only */
+                                    char **unmanaged,
+                                    char **keyfile,
+                                    char **routefile,
+                                    char **route6file,
+                                    GError **error,
+                                    gboolean *ignore_error);
+
+const char *reader_get_prefix (void);
+GByteArray *ifcfg_mdv_parse_ssid(char *, GError **);
+
+#endif  /* __READER_H__ */
diff --git a/system-settings/plugins/ifcfg-mdv/writer.c b/system-settings/plugins/ifcfg-mdv/writer.c
new file mode 100644
index 0000000..8d80a84
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/writer.c
@@ -0,0 +1,1721 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Mandriva-specific changes by Eugeni Dodonov <eugeni@mandriva.com>.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-8021x.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-pppoe.h>
+#include <nm-utils.h>
+
+#include "common.h"
+#include "shvar.h"
+#include "reader.h"
+#include "writer.h"
+#include "utils.h"
+#include "crypto.h"
+
+#include "parse_wpa_supplicant_conf.h"
+
+#define PLUGIN_WARN(pname, fmt, args...) \
+	{ g_warning ("   " pname ": " fmt, ##args); }
+
+
+/*
+ * ifcfg reader converts ASCII to HEX. This converts key back to ASCII
+ * before writing
+ */
+
+static gchar *
+wep4ifcfg(const gchar *value)
+{
+	gchar *s;
+	gsize len;
+	GString *str;
+
+	if (!value)
+		return NULL;
+
+	len = strlen(value);
+	str = g_string_new("");
+
+	if (len == 5 || len == 13) {
+		g_string_printf(str, "s:%s", value);
+	} else if (len == 10 || len == 26) {
+		gchar *p;
+		s = utils_hexstr2bin (value, len);
+		for (p = s; *p; p++) {
+			if (!isascii (*p)) {
+				g_free(s);
+				g_string_free(str, TRUE);
+				return g_strdup(value);
+			}
+		}
+		g_string_printf(str, "s:%s", s);
+		g_free(s);
+	} else {
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: invalid WEP key length");
+		g_string_free(str, TRUE);
+		return NULL;
+	}
+
+	s = svEscape(str->str);
+	g_string_free(str, TRUE);
+	return s;
+}
+static void
+set_wep_secret (shvarFile *ifcfg, const char *key, const char *value, gboolean verbatim)
+{
+	char *v = 0;
+	
+	/* Clear the secret from the actual ifcfg */
+	svSetValue (ifcfg, key, NULL, FALSE);
+
+	/* WEP -> WPA will set empty key */
+	if (!value)
+		return;
+
+	v = wep4ifcfg(value);
+	if (!v)
+		return;
+
+	/* Try setting the secret in the actual ifcfg */
+	svSetValue (ifcfg, key, v, TRUE);
+	g_free(v);
+}
+
+#if 0
+static gboolean
+write_secret_file (const char *path,
+                   const char *data,
+                   gsize len,
+                   GError **error)
+{
+	char *tmppath;
+	int fd = -1, written;
+	gboolean success = FALSE;
+
+	tmppath = g_malloc0 (strlen (path) + 10);
+	if (!tmppath) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not allocate memory for temporary file for '%s'",
+		             path);
+		return FALSE;
+	}
+
+	memcpy (tmppath, path, strlen (path));
+	strcat (tmppath, ".XXXXXX");
+
+	errno = 0;
+	fd = mkstemp (tmppath);
+	if (fd < 0) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "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)) {
+		close (fd);
+		unlink (tmppath);
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not set permissions for temporary file '%s': %d",
+		             path, errno);
+		goto out;
+	}
+
+	errno = 0;
+	written = write (fd, data, len);
+	if (written != len) {
+		close (fd);
+		unlink (tmppath);
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not write temporary file for '%s': %d",
+		             path, errno);
+		goto out;
+	}
+	close (fd);
+
+	/* Try to rename */
+	errno = 0;
+	if (rename (tmppath, path)) {
+		unlink (tmppath);
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not rename temporary file to '%s': %d",
+		             path, errno);
+		goto out;
+	}
+	success = TRUE;
+
+out:
+	return success;
+}
+#endif
+
+typedef NMSetting8021xCKScheme (*SchemeFunc)(NMSetting8021x *setting);
+typedef const char *           (*PathFunc)  (NMSetting8021x *setting);
+typedef const GByteArray *     (*BlobFunc)  (NMSetting8021x *setting);
+
+typedef struct ObjectType {
+	const char *setting_key;
+	SchemeFunc scheme_func;
+	PathFunc path_func;
+	BlobFunc blob_func;
+	const char *ifcfg_key;
+	const char *suffix;
+} ObjectType;
+
+static const ObjectType ca_type = {
+	NM_SETTING_802_1X_CA_CERT,
+	nm_setting_802_1x_get_ca_cert_scheme,
+	nm_setting_802_1x_get_ca_cert_path,
+	nm_setting_802_1x_get_ca_cert_blob,
+	"ca_cert",
+	"ca-cert.der"
+};
+
+static const ObjectType phase2_ca_type = {
+	NM_SETTING_802_1X_PHASE2_CA_CERT,
+	nm_setting_802_1x_get_phase2_ca_cert_scheme,
+	nm_setting_802_1x_get_phase2_ca_cert_path,
+	nm_setting_802_1x_get_phase2_ca_cert_blob,
+	"ca_cert2",
+	"inner-ca-cert.der"
+};
+
+static const ObjectType client_type = {
+	NM_SETTING_802_1X_CLIENT_CERT,
+	nm_setting_802_1x_get_client_cert_scheme,
+	nm_setting_802_1x_get_client_cert_path,
+	nm_setting_802_1x_get_client_cert_blob,
+	"client_cert",
+	"client-cert.der"
+};
+
+static const ObjectType phase2_client_type = {
+	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+	nm_setting_802_1x_get_phase2_client_cert_scheme,
+	nm_setting_802_1x_get_phase2_client_cert_path,
+	nm_setting_802_1x_get_phase2_client_cert_blob,
+	"client_cert2",
+	"inner-client-cert.der"
+};
+
+static const ObjectType pk_type = {
+	NM_SETTING_802_1X_PRIVATE_KEY,
+	nm_setting_802_1x_get_private_key_scheme,
+	nm_setting_802_1x_get_private_key_path,
+	nm_setting_802_1x_get_private_key_blob,
+	"private_key",
+	"private-key.pem"
+};
+
+static const ObjectType phase2_pk_type = {
+	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+	nm_setting_802_1x_get_phase2_private_key_scheme,
+	nm_setting_802_1x_get_phase2_private_key_path,
+	nm_setting_802_1x_get_phase2_private_key_blob,
+	"private_key2",
+	"inner-private-key.pem"
+};
+
+static const ObjectType p12_type = {
+	NM_SETTING_802_1X_PRIVATE_KEY,
+	nm_setting_802_1x_get_private_key_scheme,
+	nm_setting_802_1x_get_private_key_path,
+	nm_setting_802_1x_get_private_key_blob,
+	"private_key",
+	"private-key.p12"
+};
+
+static const ObjectType phase2_p12_type = {
+	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+	nm_setting_802_1x_get_phase2_private_key_scheme,
+	nm_setting_802_1x_get_phase2_private_key_path,
+	nm_setting_802_1x_get_phase2_private_key_blob,
+	"private_key2",
+	"inner-private-key.p12"
+};
+
+/*
+ * FIXME the name is misleading and should be changed
+ * Mandriva does not use explicit certifcate store so we do nto either
+ * If given BLOB - fail, informing user
+ */
+static gboolean
+write_object (NMSetting8021x *s_8021x,
+	      WPANetwork *wpan,
+              shvarFile *ifcfg,
+              const GByteArray *override_data,
+              const ObjectType *objtype,
+              GError **error)
+{
+	NMSetting8021xCKScheme scheme;
+	const char *path = NULL;
+	const GByteArray *blob = NULL;
+
+	g_return_val_if_fail (ifcfg != NULL, FALSE);
+	g_return_val_if_fail (objtype != NULL, FALSE);
+
+	if (override_data) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "ifcfg-mdv does not support raw certificate data");
+		return FALSE;
+		/* if given explicit data to save, always use that instead of asking
+		 * the setting what to do.
+		 */
+		// blob = override_data;
+	} else {
+		scheme = (*(objtype->scheme_func))(s_8021x);
+		switch (scheme) {
+		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "ifcfg-mdv does not support raw certificate data");
+			return FALSE;
+		// 	blob = (*(objtype->blob_func))(s_8021x);
+		// 	break;
+		case NM_SETTING_802_1X_CK_SCHEME_PATH:
+			path = (*(objtype->path_func))(s_8021x);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* If certificate/private key wasn't sent, the connection may no longer be
+	 * 802.1x and thus we clear out the paths and certs.
+	 */
+	if (!path && !blob) {
+		// char *standard_file;
+		// int ignored;
+
+		/* Since no cert/private key is now being used, delete any standard file
+		 * that was created for this connection, but leave other files alone.
+		 * Thus, for example,
+		 * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der
+		 * will be deleted, but /etc/pki/tls/cert.pem will not.
+		 */
+#if 0
+		standard_file = utils_cert_path (ifcfg->fileName, objtype->suffix);
+		if (g_file_test (standard_file, G_FILE_TEST_EXISTS))
+			ignored = unlink (standard_file);
+		g_free (standard_file);
+#endif
+
+		ifcfg_mdv_wpa_network_unset(wpan, objtype->ifcfg_key);
+		return TRUE;
+	}
+
+	/* If the object path was specified, prefer that over any raw cert data that
+	 * may have been sent.
+	 */
+	if (path) {
+		ifcfg_mdv_wpa_network_set_str(wpan, objtype->ifcfg_key, path);
+		return TRUE;
+	}
+
+#if 0
+	/* If it's raw certificate data, write the cert data out to the standard file */
+	if (blob) {
+		gboolean success;
+		char *new_file;
+		GError *write_error = NULL;
+
+		new_file = utils_cert_path (ifcfg->fileName, objtype->suffix);
+		if (!new_file) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Could not create file path for %s / %s",
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key);
+			return FALSE;
+		}
+
+		/* Write the raw certificate data out to the standard file so that we
+		 * can use paths from now on instead of pushing around the certificate
+		 * data itself.
+		 */
+		success = write_secret_file (new_file, (const char *) blob->data, blob->len, &write_error);
+		if (success) {
+			svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE);
+			return TRUE;
+		} else {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Could not write certificate/key for %s / %s: %s",
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key,
+			             (write_error && write_error->message) ? write_error->message : "(unknown)");
+			g_clear_error (&write_error);
+		}
+		g_free (new_file);
+	}
+#endif
+
+	return FALSE;
+}
+
+static gboolean
+write_8021x_certs (NMSetting8021x *s_8021x,
+		   WPANetwork *wpan,
+                   gboolean phase2,
+                   shvarFile *ifcfg,
+                   GError **error)
+{
+	GByteArray *enc_key = NULL;
+	const char *password = NULL;
+	// char *generated_pw = NULL;
+	gboolean success = FALSE, is_pkcs12 = FALSE;
+	const ObjectType *otype = NULL;
+	const GByteArray *blob = NULL;
+
+	/* CA certificate */
+	if (phase2)
+		otype = &phase2_ca_type;
+	else
+		otype = &ca_type;
+
+	if (!write_object (s_8021x, wpan, ifcfg, NULL, otype, error))
+		return FALSE;
+
+	/* Private key */
+	if (phase2) {
+		if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+			if (nm_setting_802_1x_get_phase2_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+				is_pkcs12 = TRUE;
+		}
+		password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
+	} else {
+		if (nm_setting_802_1x_get_private_key_scheme (s_8021x) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+			if (nm_setting_802_1x_get_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+				is_pkcs12 = TRUE;
+		}
+		password = nm_setting_802_1x_get_private_key_password (s_8021x);
+	}
+
+	if (is_pkcs12)
+		otype = phase2 ? &phase2_p12_type : &p12_type;
+	else
+		otype = phase2 ? &phase2_pk_type : &pk_type;
+
+	if ((*(otype->scheme_func))(s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "ifcfg-mdv does not support raw certificate data");
+		return FALSE;
+	//	blob = (*(otype->blob_func))(s_8021x);
+	}
+
+#if 0
+	/* Only do the private key re-encrypt dance if we got the raw key data, which
+	 * by definition will be unencrypted.  If we're given a direct path to the
+	 * private key file, it'll be encrypted, so we don't need to re-encrypt.
+	 */
+	if (blob && !is_pkcs12) {
+		/* Encrypt the unencrypted private key with the fake password */
+		enc_key = nm_utils_rsa_key_encrypt (blob, password, &generated_pw, error);
+		if (!enc_key)
+			goto out;
+
+		if (generated_pw)
+			password = generated_pw;
+	}
+#endif
+
+	/* Save the private key */
+	if (!write_object (s_8021x, wpan, ifcfg, enc_key ? enc_key : blob, otype, error))
+		goto out;
+
+	/* Private key password */
+	/* FIXME what about hash:XXX? */
+	if (phase2)
+		ifcfg_mdv_wpa_network_set_str(wpan, "private_key2_password", password);
+	else
+		ifcfg_mdv_wpa_network_set_str(wpan, "private_key_password", password);
+
+	/* Client certificate */
+	if (is_pkcs12) {
+		ifcfg_mdv_wpa_network_unset(wpan,
+		            phase2 ? "client_cert2" : "client_cert");
+	} else {
+		if (phase2)
+			otype = &phase2_client_type;
+		else
+			otype = &client_type;
+
+		/* Save the client certificate */
+		if (!write_object (s_8021x, wpan, ifcfg, NULL, otype, error))
+			goto out;
+	}
+
+	success = TRUE;
+
+out:
+#if 0
+	if (generated_pw) {
+		memset (generated_pw, 0, strlen (generated_pw));
+		g_free (generated_pw);
+	}
+	if (enc_key) {
+		memset (enc_key->data, 0, enc_key->len);
+		g_byte_array_free (enc_key, TRUE);
+	}
+#endif
+	return success;
+}
+
+static gboolean
+write_8021x_setting (NMConnection *connection,
+		     WPANetwork *wpan,
+                     shvarFile *ifcfg,
+                     gboolean wired,
+                     GError **error)
+{
+	NMSetting8021x *s_8021x;
+	const char *value;
+	char *tmp = NULL;
+	gboolean success = FALSE;
+	GString *phase2_auth;
+	GString *str;
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	if (!s_8021x) {
+#if 0
+		/* No wired security in Mandriva */
+		/* If wired, clear KEY_MGMT */
+		if (wired)
+			svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE);
+#endif
+		return TRUE;
+	}
+
+#if 0
+		/* No wired security in Mandriva */
+	/* If wired, write KEY_MGMT */
+	if (wired)
+		svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE);
+#endif
+
+	/* EAP method */
+	if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) {
+		value = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+		if (value)
+			tmp = g_ascii_strup (value, -1);
+	}
+	ifcfg_mdv_wpa_network_set_val(wpan, "eap", tmp ? tmp : NULL);
+	g_free (tmp);
+
+	ifcfg_mdv_wpa_network_set_str(wpan, "identity",
+	            nm_setting_802_1x_get_identity (s_8021x));
+
+	ifcfg_mdv_wpa_network_set_str(wpan, "anonymous_identity",
+	            nm_setting_802_1x_get_anonymous_identity (s_8021x));
+
+	ifcfg_mdv_wpa_network_set_str(wpan, "password", nm_setting_802_1x_get_password (s_8021x));
+
+	str = g_string_new("");
+
+	/* PEAP version */
+	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
+	if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
+		g_string_printf(str, "peapver=%s", value);
+
+	/* Force new PEAP label */
+	value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
+	if (value && !strcmp (value, "1")) {
+		if (str->len)
+			g_string_append_c(str, ' ');
+		g_string_printf(str, "peaplabel=%s", value);
+	}
+
+	if (str->len)
+		ifcfg_mdv_wpa_network_set_str(wpan, "phase1", str->str);
+	g_string_free(str, TRUE);
+
+	/* Phase2 auth methods */
+	phase2_auth = g_string_new (NULL);
+
+	value = nm_setting_802_1x_get_phase2_auth (s_8021x);
+	if (value) {
+		tmp = g_ascii_strup (value, -1);
+		g_string_printf (phase2_auth, "auth=%s", tmp);
+		g_free (tmp);
+	}
+
+	value = nm_setting_802_1x_get_phase2_autheap (s_8021x);
+	if (value) {
+		if (phase2_auth->len)
+			g_string_append_c (phase2_auth, ' ');
+
+		tmp = g_ascii_strup (value, -1);
+		g_string_append_printf (phase2_auth, "autheap=%s", tmp);
+		g_free (tmp);
+	}
+
+	if (phase2_auth->len)
+		ifcfg_mdv_wpa_network_set_str(wpan, "phase2", phase2_auth->str);
+	g_string_free (phase2_auth, TRUE);
+
+	success = write_8021x_certs (s_8021x, wpan, FALSE, ifcfg, error);
+	if (success) {
+		/* phase2/inner certs */
+		success = write_8021x_certs (s_8021x, wpan, TRUE, ifcfg, error);
+	}
+
+	return success;
+}
+
+static gboolean
+write_wireless_security_setting (NMConnection *connection,
+                                 shvarFile *ifcfg,
+				 WPANetwork *wpan,
+                                 gboolean adhoc,
+                                 gboolean *no_8021x,
+                                 GError **error)
+{
+	NMSettingWirelessSecurity *s_wsec;
+	const char *key_mgmt, *auth_alg, *key, *proto, *cipher;
+	gboolean wep = FALSE, wpa = FALSE;
+	char *tmp;
+	guint32 i, num;
+	GString *str;
+
+	s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+	if (!s_wsec) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+		return FALSE;
+	}
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	g_assert (key_mgmt);
+
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+
+	// svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE);
+
+	if (!strcmp (key_mgmt, "none")) {
+		wep = TRUE;
+		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
+		ifcfg_mdv_wpa_network_set_val(wpan, "key_mgmt", "WPA-PSK");
+		wpa = TRUE;
+		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "ieee8021x")) {
+		ifcfg_mdv_wpa_network_set_val(wpan, "key_mgmt", "IEEE8021X");
+	} else if (!strcmp (key_mgmt, "wpa-eap")) {
+		ifcfg_mdv_wpa_network_set_val(wpan, "key_mgmt", "WPA-EAP");
+		wpa = TRUE;
+	}
+
+	/* TODO add additional fields to private object to store extra
+	 * values during parsing configuration */
+	if (strcmp(key_mgmt, "none"))
+		ifcfg_mdv_wpa_network_set_val(wpan, "priority", "1");
+
+	svSetValue (ifcfg, "WIRELESS_ENC_MODE", NULL, FALSE);
+	if (auth_alg) {
+		if (!strcmp (auth_alg, "shared")) {
+			if (wep)
+				svSetValue (ifcfg, "WIRELESS_ENC_MODE", "restricted", FALSE);
+			ifcfg_mdv_wpa_network_set_val(wpan, "auth_alg", "SHARED");
+		} else if (!strcmp (auth_alg, "open")) {
+			if (wep)
+				svSetValue (ifcfg, "WIRELESS_ENC_MODE", "open", FALSE);
+			ifcfg_mdv_wpa_network_set_val(wpan, "auth_alg", "OPEN");
+		} else if (!strcmp (auth_alg, "leap")) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "ifcfg-mdv does not support LEAP authentication");
+			return FALSE;
+#if 0
+			/* Not used by Mandriva */
+			svSetValue (ifcfg, "WIRELESS_ENC_MODE", "leap", FALSE);
+			svSetValue (ifcfg, "IEEE_8021X_IDENTITY",
+			            nm_setting_wireless_security_get_leap_username (s_wsec),
+			            FALSE);
+			set_secret (ifcfg, "IEEE_8021X_PASSWORD",
+			            nm_setting_wireless_security_get_leap_password (s_wsec),
+			            FALSE);
+			*no_8021x = TRUE;
+#endif
+		}
+	}
+
+#if 0
+	if (wep) {
+		/* Default WEP TX key index */
+		tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1);
+		svSetValue (ifcfg, "DEFAULTKEY", tmp, FALSE);
+		g_free (tmp);
+	}
+#endif
+
+	/* WEP keys */
+	/* Mandriva always sets key_idx == 0 */
+	key = nm_setting_wireless_security_get_wep_key (s_wsec, 0);
+	set_wep_secret (ifcfg, "WIRELESS_ENC_KEY", (wep && key) ? key : NULL, FALSE);
+
+	/* FIXME What about roaming mode? */
+	if (wep) {
+		/* remove WPA driver to indicate WEP mode */
+		svSetValue (ifcfg, "WIRELESS_WPA_DRIVER", NULL, FALSE);
+
+		/* remove network from wpa_suplicant.conf */
+		ifcfg_mdv_wpa_network_set_val(wpan, "__DELETE__", "yes");
+
+		return TRUE;
+	}
+
+	/* wpa_supplicant driver. NM always uses wext for wireless */
+	svSetValue (ifcfg, "WIRELESS_WPA_DRIVER", "wext", FALSE);
+
+	/* WPA protos */
+	str = g_string_new (NULL);
+	num = nm_setting_wireless_security_get_num_protos (s_wsec);
+	for (i = 0; i < num; i++) {
+		gchar *p = NULL;
+
+		proto = nm_setting_wireless_security_get_proto (s_wsec, i);
+		if (proto && !strcmp (proto, "wpa"))
+			p = "WPA";
+		else if (proto && !strcmp (proto, "rsn"))
+			p = "RSN";
+		if (p) {
+			if (i > 0)
+				g_string_append_c(str, ' ');
+			g_string_append(str, p);
+		}
+	}
+	if (strlen (str->str))
+		ifcfg_mdv_wpa_network_set_val(wpan, "proto", str->str);
+
+	/* WPA Pairwise ciphers */
+	g_string_set_size (str, 0);
+	num = nm_setting_wireless_security_get_num_pairwise (s_wsec);
+	for (i = 0; i < num; i++) {
+		if (i > 0)
+			g_string_append_c (str, ' ');
+		cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i);
+		tmp = g_ascii_strup (cipher, -1);
+		g_string_append (str, tmp);
+		g_free (tmp);
+	}
+	if (strlen (str->str))
+		ifcfg_mdv_wpa_network_set_val(wpan, "pairwise", str->str);
+
+	/* WPA Group ciphers */
+	g_string_set_size (str, 0);
+	num = nm_setting_wireless_security_get_num_groups (s_wsec);
+	for (i = 0; i < num; i++) {
+		if (i > 0)
+			g_string_append_c (str, ' ');
+		cipher = nm_setting_wireless_security_get_group (s_wsec, i);
+		tmp = g_ascii_strup (cipher, -1);
+		g_string_append (str, tmp);
+		g_free (tmp);
+	}
+	if (strlen (str->str))
+		ifcfg_mdv_wpa_network_set_val(wpan, "group", str->str);
+
+
+	/* WPA Passphrase */
+	if (wpa) {
+		const char *psk = nm_setting_wireless_security_get_psk(s_wsec);
+		if (psk) {
+			g_string_assign(str, psk);
+			if (str->len != 64) {
+				/* Quote the PSK since it's a passphrase */
+				g_string_prepend_c (str, '"');
+				g_string_append_c (str, '"');
+			}
+
+			ifcfg_mdv_wpa_network_set_val(wpan, "psk", str->str);
+		}
+	}
+	g_string_free (str, TRUE);
+
+	return TRUE;
+}
+
+static gboolean
+write_wireless_setting (NMConnection *connection,
+                        shvarFile *ifcfg,
+			WPANetwork *wpan,
+                        gboolean *no_8021x,
+                        GError **error)
+{
+	NMSettingWireless *s_wireless;
+	char *tmp = NULL;
+	const GByteArray *ssid, *bssid;
+	GByteArray *old_ssid = NULL;
+	const char *mode;
+	guint32 mtu, chan, i;
+	gboolean adhoc = FALSE;
+	gchar buf[33];
+
+	s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+	if (!s_wireless) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	/* Mandriva does not store HWADDR in ifcfg-XXX */
+#if 0
+	svSetValue (ifcfg, "HWADDR", NULL, FALSE);
+	mac = nm_setting_wireless_get_mac_address (s_wireless);
+	if (mac) {
+		tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+		                       mac->data[0], mac->data[1], mac->data[2],
+		                       mac->data[3], mac->data[4], mac->data[5]);
+		svSetValue (ifcfg, "HWADDR", tmp, FALSE);
+		g_free (tmp);
+	}
+#endif
+
+	svSetValue (ifcfg, "MTU", NULL, FALSE);
+	mtu = nm_setting_wireless_get_mtu (s_wireless);
+	if (mtu) {
+		tmp = g_strdup_printf ("%u", mtu);
+		svSetValue (ifcfg, "MTU", tmp, FALSE);
+		g_free (tmp);
+	}
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	if (!ssid) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+	if (!ssid->len || ssid->len > 32) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	/*
+	 * Mandriva is using SSID as part of file name; check for characters
+	 * that cannot included */
+	for (i = 0; i < ssid->len; i++)
+		if (G_DIR_SEPARATOR == ssid->data[i] || ssid->data[i] == '\0') {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
+			return FALSE;
+		}
+
+	/*
+	 * If SID changed we have to remove it from wpa_supplicant.conf
+	 */
+	tmp = svGetValue(ifcfg, "WIRELESS_ESSID", TRUE);
+	if (!tmp) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "Missing WIRELESS_SSID in '%s'", ifcfg->fileName);
+		return FALSE;
+	}
+	old_ssid = ifcfg_mdv_parse_ssid(tmp, error);
+	if (!old_ssid)
+		goto free;
+
+	if (ssid->len != old_ssid->len || !memcmp(ssid->data, old_ssid->data, ssid->len)) {
+		WPANetwork *del = ifcfg_mdv_wpa_network_new(NULL);
+
+		if (!del) {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: could not allocate WPANetwork to remove SSID '%s'",
+		             tmp);
+			goto free;
+		}
+
+		ifcfg_mdv_wpa_network_set_ssid(del, old_ssid);
+		ifcfg_mdv_wpa_network_set_val(del, "__DELETE__", "yes");
+		ifcfg_mdv_wpa_network_save(del, "/etc/wpa_supplicant.conf", error);
+		ifcfg_mdv_wpa_network_free(del);
+		if (*error) {
+			goto free;
+		}
+	}
+	g_free(tmp);
+	g_byte_array_free(old_ssid, TRUE);
+
+	/* we just verified that it does not contain '\0' and fits in buf */
+	memcpy(buf, ssid->data, ssid->len);
+	buf[ssid->len] = '\0';
+	tmp = svEscape(buf);
+	svSetValue (ifcfg, "WIRELESS_ESSID", tmp, TRUE);
+	g_free(tmp);
+	ifcfg_mdv_wpa_network_set_ssid(wpan, ssid);
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (!mode || !strcmp (mode, "infrastructure")) {
+		svSetValue (ifcfg, "WIRELESS_MODE", "Managed", FALSE);
+	} else if (!strcmp (mode, "adhoc")) {
+		svSetValue (ifcfg, "WIRELESS_MODE", "Ad-Hoc", FALSE);
+		adhoc = TRUE;
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Invalid mode '%s' in '%s' setting",
+		             mode, NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	svSetValue (ifcfg, "WIRELESS_CHANNEL", NULL, FALSE);
+	chan = nm_setting_wireless_get_channel (s_wireless);
+	if (chan) {
+		tmp = g_strdup_printf ("%u", chan);
+		svSetValue (ifcfg, "WIRELESS_CHANNEL", tmp, FALSE);
+		g_free (tmp);
+	}
+
+	if (nm_setting_wireless_get_security (s_wireless)) {
+		if (!write_wireless_security_setting (connection, ifcfg, wpan, adhoc, no_8021x, error))
+			return FALSE;
+	}
+
+	// svSetValue (ifcfg, "BSSID", NULL, FALSE);
+	bssid = nm_setting_wireless_get_bssid (s_wireless);
+	if (bssid) {
+		tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+				       bssid->data[0], bssid->data[1], bssid->data[2],
+				       bssid->data[3], bssid->data[4], bssid->data[5]);
+		ifcfg_mdv_wpa_network_set_val(wpan, "bssid", tmp);
+		// svSetValue (ifcfg, "BSSID", tmp, FALSE);
+		g_free (tmp);
+	}
+
+
+	// svSetValue (ifcfg, "TYPE", TYPE_WIRELESS, FALSE);
+
+	return TRUE;
+free:
+	g_free(tmp);
+	if (old_ssid)
+		g_byte_array_free(old_ssid, TRUE);
+
+	return FALSE;
+}
+
+static gboolean
+write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingWired *s_wired;
+	// const GByteArray *mac;
+	char *tmp;
+	guint32 mtu;
+
+	s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	if (!s_wired) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_WIRED_SETTING_NAME);
+		return FALSE;
+	}
+
+	/* Mandriva does not store HWADDR in ifcfg-XXX */
+#if 0
+	mac = nm_setting_wired_get_mac_address (s_wired);
+	if (mac) {
+		tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+		                       mac->data[0], mac->data[1], mac->data[2],
+		                       mac->data[3], mac->data[4], mac->data[5]);
+		svSetValue (ifcfg, "HWADDR", tmp, FALSE);
+		g_free (tmp);
+	}
+#endif
+
+	mtu = nm_setting_wired_get_mtu (s_wired);
+	if (mtu) {
+		tmp = g_strdup_printf ("%u", mtu);
+		svSetValue (ifcfg, "MTU", tmp, FALSE);
+		g_free (tmp);
+	}
+
+	// svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE);
+
+	return TRUE;
+}
+
+static void
+write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
+{
+	char *tmp;
+
+	// svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE);
+	// svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE);
+	/* FIXME temporary until we can use ONBOOT again */
+	svSetValue (ifcfg, "ONBOOT", "no", FALSE);
+	svSetValue (ifcfg, "_NM_ONBOOT",
+	            nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no",
+	            FALSE);
+
+	svSetValue (ifcfg, "LAST_CONNECT", NULL, FALSE);
+	if (nm_setting_connection_get_timestamp (s_con)) {
+		tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, nm_setting_connection_get_timestamp (s_con));
+		svSetValue (ifcfg, "LAST_CONNECT", tmp, FALSE);
+		g_free (tmp);
+	}
+}
+
+#if 0
+No route file on Mandriva
+static gboolean
+write_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError **error)
+{
+	char dest[INET_ADDRSTRLEN];
+	char next_hop[INET_ADDRSTRLEN];
+	char **route_items;
+	char *route_contents;
+	NMIP4Route *route;
+	guint32 ip, prefix, metric;
+	guint32 i, num;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (s_ip4 != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	num = nm_setting_ip4_config_get_num_routes (s_ip4);
+	if (num == 0) {
+		unlink (filename);
+		return TRUE;
+	}
+
+	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	for (i = 0; i < num; i++) {
+		route = nm_setting_ip4_config_get_route (s_ip4, i);
+
+		memset (dest, 0, sizeof (dest));
+		ip = nm_ip4_route_get_dest (route);
+		inet_ntop (AF_INET, (const void *) &ip, &dest[0], sizeof (dest));
+
+		prefix = nm_ip4_route_get_prefix (route);
+
+		memset (next_hop, 0, sizeof (next_hop));
+		ip = nm_ip4_route_get_next_hop (route);
+		inet_ntop (AF_INET, (const void *) &ip, &next_hop[0], sizeof (next_hop));
+
+		metric = nm_ip4_route_get_metric (route);
+
+		route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, metric);
+	}
+	route_items[num] = NULL;
+	route_contents = g_strjoinv (NULL, route_items);
+	g_strfreev (route_items);
+
+	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Writing route file '%s' failed", filename);
+		goto error;
+	}
+
+	success = TRUE;
+
+error:
+	g_free (route_contents);
+
+	return success;
+}
+#endif
+
+static char *
+ip4_address_as_string (guint32 ip)
+{
+	char *ip_string;
+	struct in_addr tmp_addr;
+
+	tmp_addr.s_addr = ip;
+	ip_string = g_malloc0 (INET_ADDRSTRLEN + 1);
+	if (!inet_ntop (AF_INET, &tmp_addr, ip_string, INET_ADDRSTRLEN))
+	strcpy (ip_string, "(none)");
+	return ip_string;
+}
+
+static gboolean
+write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingIP4Config *s_ip4;
+	const char *value;
+	char *addr_key, *prefix_key, *netmask_key, *gw_key, /**metric_key,*/ *tmp;
+	// char *route_path = NULL;
+	guint32 i, num;
+	GString *searches;
+	gboolean success = FALSE;
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (!s_ip4) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_IP4_CONFIG_SETTING_NAME);
+		return FALSE;
+	}
+
+	value = nm_setting_ip4_config_get_method (s_ip4);
+	g_assert (value);
+	if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
+		svSetValue (ifcfg, "BOOTPROTO", "dhcp", FALSE);
+	else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+		svSetValue (ifcfg, "BOOTPROTO", "static", FALSE);
+#if 0
+	else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+		svSetValue (ifcfg, "BOOTPROTO", "autoip", FALSE);
+	else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+		svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE);
+#endif
+	else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "ifcfg-mdv: unsupported activation method '%s'", value);
+		return FALSE;
+	}
+
+	num = nm_setting_ip4_config_get_num_addresses (s_ip4);
+	if (num > 1) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "ifcfg-mdv: multiple IPADDRs per interface are not supported");
+			return FALSE;
+	}
+	//for (i = 0; i < 254; i++) {
+	{
+		char buf[INET_ADDRSTRLEN + 1];
+		NMIP4Address *addr;
+		guint32 ip, netmask;
+
+		// if (i == 0) {
+			addr_key = g_strdup ("IPADDR");
+			prefix_key = g_strdup ("PREFIX");
+			netmask_key = g_strdup ("NETMASK");
+			gw_key = g_strdup ("GATEWAY");
+#if 0
+		} else {
+			addr_key = g_strdup_printf ("IPADDR%d", i + 1);
+			prefix_key = g_strdup_printf ("PREFIX%d", i + 1);
+			gw_key = g_strdup_printf ("GATEWAY%d", i + 1);
+		}
+#endif
+		/* Clean PREFIX in case it was present, otherwise it
+		 * will fool reader.c next time */
+		svSetValue (ifcfg, prefix_key, NULL, FALSE);
+
+		// if (i >= num) {
+		if (num == 0) {
+			svSetValue (ifcfg, addr_key, NULL, FALSE);
+			svSetValue (ifcfg, netmask_key, NULL, FALSE);
+			svSetValue (ifcfg, gw_key, NULL, FALSE);
+		} else {
+			addr = nm_setting_ip4_config_get_address (s_ip4, i);
+
+			memset (buf, 0, sizeof (buf));
+			ip = nm_ip4_address_get_address (addr);
+			inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+			svSetValue (ifcfg, addr_key, &buf[0], FALSE);
+
+			netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (addr));
+			tmp = ip4_address_as_string(netmask);
+			svSetValue (ifcfg, netmask_key, tmp, FALSE);
+			g_free (tmp);
+
+			if (nm_ip4_address_get_gateway (addr)) {
+				memset (buf, 0, sizeof (buf));
+				ip = nm_ip4_address_get_gateway (addr);
+				inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+				svSetValue (ifcfg, gw_key, &buf[0], FALSE);
+			} else
+				svSetValue (ifcfg, gw_key, NULL, FALSE);
+		}
+
+		g_free (addr_key);
+		g_free (prefix_key);
+		g_free (netmask_key);
+		g_free (gw_key);
+	}
+
+	num = nm_setting_ip4_config_get_num_dns (s_ip4);
+	if (num > 2) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "ifcfg-mdv: max two DNS servers per interface are supported");
+			return FALSE;
+	}
+	for (i = 0; i <= 2; i++) {
+		char buf[INET_ADDRSTRLEN + 1];
+		guint32 ip;
+
+		addr_key = g_strdup_printf ("DNS%d", i + 1);
+
+		if (i >= num)
+			svSetValue (ifcfg, addr_key, NULL, FALSE);
+		else {
+			ip = nm_setting_ip4_config_get_dns (s_ip4, i);
+
+			memset (buf, 0, sizeof (buf));
+			inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+			svSetValue (ifcfg, addr_key, &buf[0], FALSE);
+		}
+		g_free (addr_key);
+	}
+
+	num = nm_setting_ip4_config_get_num_dns_searches (s_ip4);
+	if (num > 0) {
+		searches = g_string_new (NULL);
+		for (i = 0; i < num; i++) {
+			if (i > 0)
+				g_string_append_c (searches, ' ');
+			g_string_append (searches, nm_setting_ip4_config_get_dns_search (s_ip4, i));
+		}
+		svSetValue (ifcfg, "DOMAIN", searches->str, FALSE);
+		g_string_free (searches, TRUE);
+	} else
+		svSetValue (ifcfg, "DOMAIN", NULL, FALSE);
+
+	/*
+	 * Mandriva supports DEFROUTE for PPP connections only, which are
+	 * currently not implemented by ifcfg-mdv
+	 */
+	if (nm_setting_ip4_config_get_never_default (s_ip4)){
+		PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignoring unsupported setting DEFROUTE=no");
+	}
+#if 0
+	/* DEFROUTE; remember that it has the opposite meaning from never-default */
+	svSetValue (ifcfg, "DEFROUTE",
+	            nm_setting_ip4_config_get_never_default (s_ip4) ? "no" : "yes",
+	            FALSE);
+#endif
+
+	/* Mandriva does not support PEERROUTES at all */
+	svSetValue (ifcfg, "PEERDNS", NULL, FALSE);
+	// svSetValue (ifcfg, "PEERROUTES", NULL, FALSE);
+	svSetValue (ifcfg, "DHCP_HOSTNAME", NULL, FALSE);
+	// svSetValue (ifcfg, "DHCP_CLIENT_ID", NULL, FALSE);
+	if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		svSetValue (ifcfg, "PEERDNS",
+		            nm_setting_ip4_config_get_ignore_auto_dns (s_ip4) ? "no" : "yes",
+		            FALSE);
+
+		if (nm_setting_ip4_config_get_ignore_auto_routes (s_ip4)) {
+			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: ignoring unsupported setting PEERROUTESno");
+		}
+#if 0
+		svSetValue (ifcfg, "PEERROUTES",
+		            nm_setting_ip4_config_get_ignore_auto_routes (s_ip4) ? "no" : "yes",
+		            FALSE);
+#endif
+
+		value = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
+		if (value)
+			svSetValue (ifcfg, "DHCP_HOSTNAME", value, FALSE);
+
+		/* Mandriva does not support client ID */
+		value = nm_setting_ip4_config_get_dhcp_client_id (s_ip4);
+		if (value) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			     "ifcfg-mdv: DHCP_CLIENT_ID is not supported");
+				return FALSE;
+			// svSetValue (ifcfg, "DHCP_CLIENT_ID", value, FALSE);
+		}
+	}
+
+#if 0
+	No routes on Mandriva
+	/* Static routes - route-<name> file */
+	route_path = utils_get_route_path (ifcfg->fileName);
+	if (!route_path) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not get route file path for '%s'", ifcfg->fileName);
+		goto out;
+	}
+#endif
+
+	num = nm_setting_ip4_config_get_num_routes (s_ip4);
+	if (num > 0) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		     "ifcfg-mdv: static routes are not supported");
+			return FALSE;
+	}
+#if 0
+	if (utils_has_route_file_new_syntax (route_path)) {
+		shvarFile *routefile;
+
+		g_free (route_path);
+		routefile = utils_get_route_ifcfg (ifcfg->fileName, TRUE);
+		if (!routefile) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Could not create route file '%s'", routefile->fileName);
+			goto out;
+		}
+
+		num = nm_setting_ip4_config_get_num_routes (s_ip4);
+		for (i = 0; i < 256; i++) {
+			char buf[INET_ADDRSTRLEN];
+			NMIP4Route *route;
+			guint32 ip, metric;
+
+			addr_key = g_strdup_printf ("ADDRESS%d", i);
+			netmask_key = g_strdup_printf ("NETMASK%d", i);
+			gw_key = g_strdup_printf ("GATEWAY%d", i);
+			metric_key = g_strdup_printf ("METRIC%d", i);
+
+			if (i >= num) {
+				svSetValue (routefile, addr_key, NULL, FALSE);
+				svSetValue (routefile, netmask_key, NULL, FALSE);
+				svSetValue (routefile, gw_key, NULL, FALSE);
+				svSetValue (routefile, metric_key, NULL, FALSE);
+			} else {
+				route = nm_setting_ip4_config_get_route (s_ip4, i);
+
+				memset (buf, 0, sizeof (buf));
+				ip = nm_ip4_route_get_dest (route);
+				inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+				svSetValue (routefile, addr_key, &buf[0], FALSE);
+
+				memset (buf, 0, sizeof (buf));
+				ip = nm_utils_ip4_prefix_to_netmask (nm_ip4_route_get_prefix (route));
+				inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+				svSetValue (routefile, netmask_key, &buf[0], FALSE);
+
+				memset (buf, 0, sizeof (buf));
+				ip = nm_ip4_route_get_next_hop (route);
+				inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf));
+				svSetValue (routefile, gw_key, &buf[0], FALSE);
+
+				memset (buf, 0, sizeof (buf));
+				metric = nm_ip4_route_get_metric (route);
+				if (metric == 0)
+					svSetValue (routefile, metric_key, NULL, FALSE);
+				else {
+					tmp = g_strdup_printf ("%u", metric);
+					svSetValue (routefile, metric_key, tmp, FALSE);
+					g_free (tmp);
+				}
+			}
+
+			g_free (addr_key);
+			g_free (netmask_key);
+			g_free (gw_key);
+			g_free (metric_key);
+		}
+		if (svWriteFile (routefile, 0644)) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Could not update route file '%s'", routefile->fileName);
+			svCloseFile (routefile);
+			goto out;
+		}
+		svCloseFile (routefile);
+	} else {
+		write_route_file_legacy (route_path, s_ip4, error);
+		g_free (route_path);
+		if (error && *error)
+			goto out;
+	}
+#endif
+
+	success = TRUE;
+
+// out:
+	return success;
+}
+
+#if 0
+No IPv6 on Mandriva
+static gboolean
+write_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **error)
+{
+	char dest[INET6_ADDRSTRLEN];
+	char next_hop[INET6_ADDRSTRLEN];
+	char **route_items;
+	char *route_contents;
+	NMIP6Route *route;
+	const struct in6_addr *ip;
+	guint32 prefix, metric;
+	guint32 i, num;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (s_ip6 != NULL, FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	num = nm_setting_ip6_config_get_num_routes (s_ip6);
+	if (num == 0) {
+		unlink (filename);
+		return TRUE;
+	}
+
+	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	for (i = 0; i < num; i++) {
+		route = nm_setting_ip6_config_get_route (s_ip6, i);
+
+		memset (dest, 0, sizeof (dest));
+		ip = nm_ip6_route_get_dest (route);
+		inet_ntop (AF_INET6, (const void *) ip, &dest[0], sizeof (dest));
+
+		prefix = nm_ip6_route_get_prefix (route);
+
+		memset (next_hop, 0, sizeof (next_hop));
+		ip = nm_ip6_route_get_next_hop (route);
+		inet_ntop (AF_INET6, (const void *) ip, &next_hop[0], sizeof (next_hop));
+
+		metric = nm_ip6_route_get_metric (route);
+
+		route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, metric);
+	}
+	route_items[num] = NULL;
+	route_contents = g_strjoinv (NULL, route_items);
+	g_strfreev (route_items);
+
+	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Writing route6 file '%s' failed", filename);
+		goto error;
+	}
+
+	success = TRUE;
+
+error:
+	g_free (route_contents);
+	return success;
+}
+#endif
+
+static gboolean
+write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingIP6Config *s_ip6;
+	// NMSettingIP4Config *s_ip4;
+	const char *value;
+#if 0
+	char *addr_key, *prefix;
+	guint32 i, num, num4;
+	GString *searches;
+	char buf[INET6_ADDRSTRLEN];
+	NMIP6Address *addr;
+	const struct in6_addr *ip;
+	GString *ip_str1, *ip_str2, *ip_ptr;
+	char *route6_path;
+#endif
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	if (!s_ip6) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_IP6_CONFIG_SETTING_NAME);
+		return FALSE;
+	}
+
+	value = nm_setting_ip6_config_get_method (s_ip6);
+	g_assert (value);
+	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
+		//svSetValue (ifcfg, "IPV6INIT", "no", FALSE);
+		return TRUE;
+#if 0
+	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+		svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
+	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+		svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
+	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
+		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+		svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
+	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+		svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+		/* TODO */
+#endif
+	}
+	return TRUE;
+
+#if 0
+	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		/* Write out IP addresses */
+		num = nm_setting_ip6_config_get_num_addresses (s_ip6);
+
+		ip_str1 = g_string_new (NULL);
+		ip_str2 = g_string_new (NULL);
+		for (i = 0; i < num; i++) {
+			if (i == 0)
+				ip_ptr = ip_str1;
+			else
+				ip_ptr = ip_str2;
+
+			addr = nm_setting_ip6_config_get_address (s_ip6, i);
+			ip = nm_ip6_address_get_address (addr);
+			prefix = g_strdup_printf ("%u", nm_ip6_address_get_prefix (addr));
+			memset (buf, 0, sizeof (buf));
+			inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf));
+			if (i > 1)
+				g_string_append_c (ip_ptr, ' ');  /* separate addresses in IPV6ADDR_SECONDARIES */
+			g_string_append (ip_ptr, buf);
+			g_string_append_c (ip_ptr, '/');
+			g_string_append (ip_ptr, prefix);
+			g_free (prefix);
+		}
+
+		svSetValue (ifcfg, "IPV6ADDR", ip_str1->str, FALSE);
+		svSetValue (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str, FALSE);
+		g_string_free (ip_str1, TRUE);
+		g_string_free (ip_str2, TRUE);
+	} else {
+		svSetValue (ifcfg, "IPV6ADDR", NULL, FALSE);
+		svSetValue (ifcfg, "IPV6ADDR_SECONDARIES", NULL, FALSE);
+	}
+
+	/* Write out DNS - 'DNS' key is used both for IPv4 and IPv6 */
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	num4 = s_ip4 ? nm_setting_ip4_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
+	num = nm_setting_ip6_config_get_num_dns (s_ip6);
+	for (i = 0; i < 254; i++) {
+		addr_key = g_strdup_printf ("DNS%d", i + num4 + 1);
+
+		if (i >= num)
+			svSetValue (ifcfg, addr_key, NULL, FALSE);
+		else {
+			ip = nm_setting_ip6_config_get_dns (s_ip6, i);
+
+			memset (buf, 0, sizeof (buf));
+			inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf));
+			svSetValue (ifcfg, addr_key, buf, FALSE);
+		}
+		g_free (addr_key);
+	}
+
+	/* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
+	num = nm_setting_ip6_config_get_num_dns_searches (s_ip6);
+	if (num > 0) {
+		char *ip4_domains;
+		ip4_domains = svGetValue (ifcfg, "DOMAIN", FALSE);
+		searches = g_string_new (ip4_domains);
+		for (i = 0; i < num; i++) {
+			if (searches->len > 0)
+				g_string_append_c (searches, ' ');
+			g_string_append (searches, nm_setting_ip6_config_get_dns_search (s_ip6, i));
+		}
+		svSetValue (ifcfg, "DOMAIN", searches->str, FALSE);
+		g_string_free (searches, TRUE);
+		g_free (ip4_domains);
+	}
+
+	/* handle IPV6_DEFROUTE */
+	/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
+	if (nm_setting_ip6_config_get_never_default(s_ip6))
+		svSetValue (ifcfg, "IPV6_DEFROUTE", "no", FALSE);
+	else
+		svSetValue (ifcfg, "IPV6_DEFROUTE", "yes", FALSE);
+
+	svSetValue (ifcfg, "IPV6_PEERDNS", NULL, FALSE);
+	svSetValue (ifcfg, "IPV6_PEERROUTES", NULL, FALSE);
+	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		svSetValue (ifcfg, "IPV6_PEERDNS",
+		            nm_setting_ip6_config_get_ignore_auto_dns (s_ip6) ? "no" : "yes",
+		            FALSE);
+
+		svSetValue (ifcfg, "IPV6_PEERROUTES",
+		            nm_setting_ip6_config_get_ignore_auto_routes (s_ip6) ? "no" : "yes",
+		            FALSE);
+	}
+
+	/* Static routes go to route6-<dev> file */
+	route6_path = utils_get_route6_path (ifcfg->fileName);
+	if (!route6_path) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Could not get route6 file path for '%s'", ifcfg->fileName);
+		goto error;
+	}
+	write_route6_file (route6_path, s_ip6, error);
+	g_free (route6_path);
+	if (error && *error)
+		goto error;
+
+	return TRUE;
+
+error:
+	return FALSE;
+#endif
+}
+
+static char *
+escape_id (const char *id)
+{
+	char *escaped = g_strdup (id);
+	char *p = escaped;
+
+	/* Escape random stuff */
+	while (*p) {
+		if (*p == ' ')
+			*p = '_';
+		else if (*p == '/')
+			*p = '-';
+		else if (*p == '\\')
+			*p = '-';
+		p++;
+	}
+
+	return escaped;
+}
+
+static gboolean
+write_connection (NMConnection *connection,
+                  const char *ifcfg_dir,
+                  const char *filename,
+                  const char *keyfile,
+                  char **out_filename,
+                  GError **error)
+{
+	NMSettingConnection *s_con;
+	NMSettingIP6Config *s_ip6;
+	gboolean success = FALSE;
+	shvarFile *ifcfg = NULL;
+	char *ifcfg_name = NULL;
+	const char *type;
+	gboolean no_8021x = FALSE;
+	gboolean wired = FALSE;
+	WPANetwork *wpan = NULL;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	if (!s_con) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing '%s' setting", NM_SETTING_CONNECTION_SETTING_NAME);
+		return FALSE;
+	}
+
+	if (filename) {
+		/* For existing connections, 'filename' should be full path to ifcfg file */
+		ifcfg = svNewFile (filename);
+		ifcfg_name = g_strdup (filename);
+	} else {
+		char *escaped;
+
+		escaped = escape_id (nm_setting_connection_get_id (s_con));
+		ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped);
+		ifcfg = svCreateFile (ifcfg_name);
+		g_free (escaped);
+	}
+
+	if (!ifcfg) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Failed to open/create ifcfg file '%s'", ifcfg_name);
+		goto out;
+	}
+
+	type = nm_setting_connection_get_connection_type (s_con);
+	if (!type) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Missing connection type!");
+		goto out;
+	}
+
+	/* Indicate that NM will manage this connection */
+	svSetValue (ifcfg, "NM_CONTROLLED", "yes", FALSE);
+
+	if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
+		// FIXME: can't write PPPoE at this time
+		if (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE)) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+			             "Can't write connection type '%s'",
+			             NM_SETTING_PPPOE_SETTING_NAME);
+			goto out;
+		}
+
+		if (!write_wired_setting (connection, ifcfg, error))
+			goto out;
+		wired = TRUE;
+	} else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
+		wpan = ifcfg_mdv_wpa_network_new(NULL);
+		if (!wpan) {
+			g_set_error (error, ifcfg_plugin_error_quark (), 0,
+				     "Unable to allocate WPA network");
+			goto out;
+		}
+
+		if (!write_wireless_setting (connection, ifcfg, wpan, &no_8021x, error))
+			goto out;
+	} else {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Can't write connection type '%s'", type);
+		goto out;
+	}
+
+	if (!no_8021x) {
+		if (!write_8021x_setting (connection, wpan, ifcfg, wired, error))
+			goto out;
+	}
+
+	if (!write_ip4_setting (connection, ifcfg, error))
+		goto out;
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	if (s_ip6) {
+		if (!write_ip6_setting (connection, ifcfg, error))
+			goto out;
+	}
+
+	write_connection_setting (s_con, ifcfg);
+
+	if (svWriteFile (ifcfg, 0600)) {
+		g_set_error (error, ifcfg_plugin_error_quark (), 0,
+		             "Can't write connection '%s'", ifcfg->fileName);
+		goto out;
+	}
+	if (wpan)
+		if (!ifcfg_mdv_wpa_network_save(wpan, "/etc/wpa_supplicant.conf", error)) {
+		goto out;
+		}
+
+
+	/* Only return the filename if this was a newly written ifcfg */
+	if (out_filename && !filename)
+		*out_filename = g_strdup (ifcfg_name);
+
+	success = TRUE;
+
+out:
+	if (ifcfg)
+		svCloseFile (ifcfg);
+	g_free (ifcfg_name);
+	ifcfg_mdv_wpa_network_free(wpan);
+	return success;
+}
+
+gboolean
+writer_new_connection (NMConnection *connection,
+                       const char *ifcfg_dir,
+                       char **out_filename,
+                       GError **error)
+{
+	// return write_connection (connection, ifcfg_dir, NULL, NULL, out_filename, error);
+	/* For now, disable creation of system connection on Mandriva */
+	g_set_error (error, ifcfg_plugin_error_quark (), 0,
+	     "Creation of system connection not yet implemented in ifcfg-mdv");
+	return FALSE;
+}
+
+gboolean
+writer_update_connection (NMConnection *connection,
+                          const char *ifcfg_dir,
+                          const char *filename,
+                          const char *keyfile,
+                          GError **error)
+{
+	return write_connection (connection, ifcfg_dir, filename, keyfile, NULL, error);
+}
+
diff --git a/system-settings/plugins/ifcfg-mdv/writer.h b/system-settings/plugins/ifcfg-mdv/writer.h
new file mode 100644
index 0000000..edeac0c
--- /dev/null
+++ b/system-settings/plugins/ifcfg-mdv/writer.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef _WRITER_H_
+#define _WRITER_H_
+
+#include <sys/types.h>
+#include <glib.h>
+#include <nm-connection.h>
+
+gboolean writer_new_connection (NMConnection *connection,
+                                const char *ifcfg_dir,
+                                char **out_filename,
+                                GError **error);
+
+gboolean writer_update_connection (NMConnection *connection,
+                                   const char *ifcfg_dir,
+                                   const char *filename,
+                                   const char *keyfile,
+                                   GError **error);
+
+#endif /* _WRITER_H_ */