File networkmanager-mdv.patch of Package NetworkManager
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) ⦥
+ 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_ */