network-manager-openvpn r2 - in trunk: . auth-dialog common-gnome doc po properties src



Author: dcbw
Date: Thu Dec 11 19:50:59 2008
New Revision: 2
URL: http://svn.gnome.org/viewvc/network-manager-openvpn?rev=2&view=rev

Log:
2008-12-11  Dan Williams  <dcbw redhat com>

	* Split into separate repository



Added:
   trunk/AUTHORS
   trunk/ChangeLog
   trunk/MAINTAINERS
   trunk/Makefile.am
   trunk/NEWS
   trunk/README
   trunk/auth-dialog/
   trunk/auth-dialog/.cvsignore
   trunk/auth-dialog/Makefile.am
   trunk/auth-dialog/gnome-two-password-dialog.c
   trunk/auth-dialog/gnome-two-password-dialog.h
   trunk/auth-dialog/main.c
   trunk/autogen.sh   (contents, props changed)
   trunk/common-gnome/
   trunk/common-gnome/Makefile.am
   trunk/common-gnome/keyring-helpers.c
   trunk/common-gnome/keyring-helpers.h
   trunk/configure.in
   trunk/doc/
   trunk/doc/example-vpnc-system-wide-connection.sh   (contents, props changed)
   trunk/gnome-mime-application-x-openvpn-settings.png   (contents, props changed)
   trunk/nm-openvpn-service.conf
   trunk/nm-openvpn-service.name.in
   trunk/nm-openvpn.desktop.in
   trunk/po/
   trunk/po/.cvsignore
   trunk/po/ChangeLog
   trunk/po/LINGUAS
   trunk/po/POTFILES.in
   trunk/po/ar.po
   trunk/po/bg.po
   trunk/po/ca.po
   trunk/po/cs.po
   trunk/po/da.po
   trunk/po/de.po
   trunk/po/dz.po
   trunk/po/el.po
   trunk/po/en_GB.po
   trunk/po/es.po
   trunk/po/et.po
   trunk/po/eu.po
   trunk/po/fi.po
   trunk/po/fr.po
   trunk/po/gl.po
   trunk/po/hu.po
   trunk/po/it.po
   trunk/po/ja.po
   trunk/po/ka.po
   trunk/po/ko.po
   trunk/po/lt.po
   trunk/po/lv.po
   trunk/po/mk.po
   trunk/po/nb.po
   trunk/po/nl.po
   trunk/po/pa.po
   trunk/po/pl.po
   trunk/po/ps.po
   trunk/po/pt.po
   trunk/po/pt_BR.po
   trunk/po/ru.po
   trunk/po/sk.po
   trunk/po/sl.po
   trunk/po/sv.po
   trunk/po/th.po
   trunk/po/uk.po
   trunk/po/vi.po
   trunk/po/zh_CN.po
   trunk/po/zh_HK.po
   trunk/po/zh_TW.po
   trunk/properties/
   trunk/properties/.cvsignore
   trunk/properties/Makefile.am
   trunk/properties/auth-helpers.c
   trunk/properties/auth-helpers.h
   trunk/properties/import-export.c
   trunk/properties/import-export.h
   trunk/properties/nm-openvpn-dialog.glade
   trunk/properties/nm-openvpn.c
   trunk/properties/nm-openvpn.h
   trunk/src/
   trunk/src/.cvsignore
   trunk/src/Makefile.am
   trunk/src/nm-openvpn-service-openvpn-helper.c
   trunk/src/nm-openvpn-service.c
   trunk/src/nm-openvpn-service.h

Added: trunk/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/AUTHORS	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,4 @@
+Tim Niemueller <tim niemueller de>
+Dan Williams <dcbw redhat com>
+David Zeuthen <davidz redhat com>
+

Added: trunk/MAINTAINERS
==============================================================================
--- (empty file)
+++ trunk/MAINTAINERS	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,4 @@
+Dan Williams
+E-mail: dcbw redhat com
+Userid: dcbw
+

Added: trunk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/Makefile.am	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,37 @@
+AUTOMAKE_OPTIONS = foreign
+
+if WITH_GNOME
+SUBDIRS = src common-gnome auth-dialog properties po
+else
+SUBDIRS = src
+endif
+
+dbusservicedir = $(sysconfdir)/dbus-1/system.d
+dbusservice_DATA = nm-openvpn-service.conf
+
+nmvpnservicedir = $(sysconfdir)/NetworkManager/VPN
+nmvpnservice_DATA = nm-openvpn-service.name
+
+if WITH_GNOME
+desktopdir = $(datadir)/applications
+desktop_in_files = nm-openvpn.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+ INTLTOOL_DESKTOP_RULE@
+
+icondir = $(datadir)/icons/hicolor/48x48/apps
+icon_DATA = gnome-mime-application-x-openvpn-settings.png
+endif
+
+nm-openvpn-service.name: $(srcdir)/nm-openvpn-service.name.in
+	sed -e 's|[ ]LIBEXECDIR[@]|$(libexecdir)|g' $< >$@
+
+EXTRA_DIST = nm-openvpn-service.name.in \
+             $(dbusservice_DATA)  \
+             $(desktop_in_files)  \
+             $(icon_DATA)         \
+             intltool-extract.in  \
+             intltool-merge.in    \
+             intltool-update.in
+
+CLEANFILES = $(nmvpnservice_DATA) $(desktop_DATA) *~
+DISTCLEANFILES = intltool-extract intltool-merge intltool-update

Added: trunk/NEWS
==============================================================================

Added: trunk/README
==============================================================================
--- (empty file)
+++ trunk/README	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,15 @@
+OpenVPN support for NetworkManager
+
+Added by Tim Niemueller http://www.niemueller.de
+
+Assumes that you have a running OpenVPN X.509 setup as mentioned
+in the OpenVPN 2.0 HOWOT on the OpenVPN homepage.
+
+TODO: Support for static keys, support for password authentication,
+only present auth-dialog if needed.
+
+Code released under the GPL. See COPYING file in the NetworkManager
+directory.
+
+Aachen, 2005/11/12
+

Added: trunk/auth-dialog/.cvsignore
==============================================================================
--- (empty file)
+++ trunk/auth-dialog/.cvsignore	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,3 @@
+Makefile.in
+Makefile
+nm-openvpn-auth-dialog

Added: trunk/auth-dialog/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/auth-dialog/Makefile.am	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,33 @@
+libexec_PROGRAMS = nm-openvpn-auth-dialog
+
+nm_openvpn_auth_dialog_CPPFLAGS =			\
+	$(GTHREAD_CFLAGS)			\
+	$(GTK_CFLAGS)				\
+	$(LIBGNOMEUI_CFLAGS)			\
+	$(NETWORK_MANAGER_CFLAGS)		\
+	$(GNOMEKEYRING_CFLAGS) \
+	-I$(top_srcdir)/
+	-DICONDIR=\""$(datadir)/pixmaps"\"	\
+	-DGLADEDIR=\""$(gladedir)"\"		\
+	-DBINDIR=\""$(bindir)"\"		\
+	-DDBUS_API_SUBJECT_TO_CHANGE		\
+	-DG_DISABLE_DEPRECATED			\
+	-DGDK_DISABLE_DEPRECATED		\
+	-DGNOME_DISABLE_DEPRECATED		\
+	-DGNOMELOCALEDIR=\"$(datadir)/locale\"	\
+	-DVERSION=\"$(VERSION)\"			\
+	$(NULL)
+
+nm_openvpn_auth_dialog_SOURCES =			\
+	main.c					\
+	gnome-two-password-dialog.c		\
+	gnome-two-password-dialog.h		\
+	$(NULL)
+
+nm_openvpn_auth_dialog_LDADD =			\
+	$(GTK_LIBS)				\
+	$(LIBGNOMEUI_LIBS)			\
+	$(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la \
+	$(NULL)
+
+CLEANFILES = *~

Added: trunk/auth-dialog/gnome-two-password-dialog.c
==============================================================================
--- (empty file)
+++ trunk/auth-dialog/gnome-two-password-dialog.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,762 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* gnome-password-dialog.c - A use password prompting dialog widget.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the ree Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+//#include "gnome-i18nP.h"
+#include "gnome-two-password-dialog.h"
+#include <gtk/gtkbox.h>
+#include <gtk/gtkcheckbutton.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtkstock.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+
+struct GnomeTwoPasswordDialogDetails
+{
+	/* Attributes */
+	gboolean readonly_username;
+	gboolean readonly_domain;
+
+	gboolean show_username;
+	gboolean show_domain;
+	gboolean show_password;
+	gboolean show_password_secondary;
+	
+	/* TODO: */
+	gboolean remember;
+	char *remember_label_text;
+
+	/* Internal widgetry and flags */
+	GtkWidget *username_entry;
+	GtkWidget *password_entry;
+	GtkWidget *password_entry_secondary;
+	GtkWidget *domain_entry;
+
+	GtkWidget *table_alignment;
+	GtkWidget *table;
+	
+	GtkWidget *remember_session_button;
+	GtkWidget *remember_forever_button;
+
+	GtkWidget *radio_vbox;
+	GtkWidget *connect_with_no_userpass_button;
+	GtkWidget *connect_with_userpass_button;
+
+	gboolean anon_support_on;
+
+	char *primary_password_label;
+	char *secondary_password_label;
+};
+
+/* Caption table rows indices */
+static const guint CAPTION_TABLE_USERNAME_ROW = 0;
+static const guint CAPTION_TABLE_PASSWORD_ROW = 1;
+
+/* GnomeTwoPasswordDialogClass methods */
+static void gnome_two_password_dialog_class_init (GnomeTwoPasswordDialogClass *password_dialog_class);
+static void gnome_two_password_dialog_init       (GnomeTwoPasswordDialog      *password_dialog);
+
+/* GObjectClass methods */
+static void gnome_two_password_dialog_finalize         (GObject                *object);
+
+
+/* GtkDialog callbacks */
+static void dialog_show_callback                 (GtkWidget              *widget,
+						  gpointer                callback_data);
+static void dialog_close_callback                (GtkWidget              *widget,
+						  gpointer                callback_data);
+
+static gpointer parent_class;
+
+GType
+gnome_two_password_dialog_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GnomeTwoPasswordDialogClass),
+                        NULL, NULL,
+			(GClassInitFunc) gnome_two_password_dialog_class_init,
+                        NULL, NULL,
+			sizeof (GnomeTwoPasswordDialog), 0,
+			(GInstanceInitFunc) gnome_two_password_dialog_init,
+			NULL
+		};
+
+                type = g_type_register_static (gtk_dialog_get_type(), 
+					       "GnomeTwoPasswordDialog", 
+					       &info, 0);
+
+		parent_class = g_type_class_ref (gtk_dialog_get_type());
+	}
+
+	return type;
+}
+
+
+static void
+gnome_two_password_dialog_class_init (GnomeTwoPasswordDialogClass * klass)
+{
+	G_OBJECT_CLASS (klass)->finalize = gnome_two_password_dialog_finalize;
+}
+
+static void
+gnome_two_password_dialog_init (GnomeTwoPasswordDialog *password_dialog)
+{
+	password_dialog->details = g_new0 (GnomeTwoPasswordDialogDetails, 1);
+	password_dialog->details->show_username = TRUE;
+	password_dialog->details->show_password = TRUE;
+	password_dialog->details->show_password_secondary = TRUE;
+	password_dialog->details->anon_support_on = FALSE;
+
+	password_dialog->details->primary_password_label = g_strdup ( _("_Password:") );
+	password_dialog->details->secondary_password_label = g_strdup ( _("_Secondary Password:") );
+}
+
+/* GObjectClass methods */
+static void
+gnome_two_password_dialog_finalize (GObject *object)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+	
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (object);
+
+	g_object_unref (password_dialog->details->username_entry);
+	g_object_unref (password_dialog->details->domain_entry);
+	g_object_unref (password_dialog->details->password_entry);
+	g_object_unref (password_dialog->details->password_entry_secondary);
+
+	g_free (password_dialog->details->remember_label_text);
+
+	g_free (password_dialog->details->primary_password_label);
+	g_free (password_dialog->details->secondary_password_label);
+
+	g_free (password_dialog->details);
+
+	if (G_OBJECT_CLASS (parent_class)->finalize != NULL)
+		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+/* GtkDialog callbacks */
+static void
+dialog_show_callback (GtkWidget *widget, gpointer callback_data)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (callback_data);
+
+	if (GTK_WIDGET_VISIBLE (password_dialog->details->username_entry) &&
+	    !password_dialog->details->readonly_username) {
+		gtk_widget_grab_focus (password_dialog->details->username_entry);
+	} else if (GTK_WIDGET_VISIBLE (password_dialog->details->domain_entry) &&
+		   !password_dialog->details->readonly_domain) {
+		gtk_widget_grab_focus (password_dialog->details->domain_entry);
+	} else if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry)) {
+		gtk_widget_grab_focus (password_dialog->details->password_entry);
+	} else if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry_secondary)) {
+		gtk_widget_grab_focus (password_dialog->details->password_entry_secondary);
+	}
+}
+
+static void
+dialog_close_callback (GtkWidget *widget, gpointer callback_data)
+{
+	gtk_widget_hide (widget);
+}
+
+static void
+userpass_radio_button_clicked (GtkWidget *widget, gpointer callback_data)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (callback_data);
+
+	if (widget == password_dialog->details->connect_with_no_userpass_button) {
+		gtk_widget_set_sensitive (
+			password_dialog->details->table, FALSE);
+	}
+	else { /* the other button */
+		gtk_widget_set_sensitive (
+                        password_dialog->details->table, TRUE);
+	}	
+}
+
+static void
+add_row (GtkWidget *table, int row, const char *label_text, GtkWidget *entry)
+{
+	GtkWidget *label;
+
+	label = gtk_label_new_with_mnemonic (label_text);
+	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+	gtk_table_attach_defaults (GTK_TABLE (table), label,
+				   0, 1, row, row + 1);
+	gtk_table_attach_defaults (GTK_TABLE (table), entry,
+				   1, 2, row, row + 1);
+
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+}
+
+static void
+remove_child (GtkWidget *child, GtkWidget *table)
+{
+	gtk_container_remove (GTK_CONTAINER (table), child);
+}
+
+static void
+add_table_rows (GnomeTwoPasswordDialog *password_dialog)
+{
+	int row;
+	GtkWidget *table;
+	int offset;
+
+	if (password_dialog->details->anon_support_on) {
+		offset = 12;
+	}
+	else {
+		offset = 0;
+	}
+
+	gtk_alignment_set_padding (GTK_ALIGNMENT (password_dialog->details->table_alignment),
+				   0, 0, offset, 0);
+
+	table = password_dialog->details->table;
+	/* This will not kill the entries, since they are ref:ed */
+	gtk_container_foreach (GTK_CONTAINER (table),
+			       (GtkCallback)remove_child, table);
+	
+	row = 0;
+	if (password_dialog->details->show_username)
+		add_row (table, row++, _("_Username:"), password_dialog->details->username_entry);
+	if (password_dialog->details->show_domain)
+		add_row (table, row++, _("_Domain:"), password_dialog->details->domain_entry);
+	if (password_dialog->details->show_password)
+		add_row (table, row++, password_dialog->details->primary_password_label,
+			 password_dialog->details->password_entry);
+	if (password_dialog->details->show_password_secondary)
+		add_row (table, row++, password_dialog->details->secondary_password_label, 
+			 password_dialog->details->password_entry_secondary);
+
+	gtk_widget_show_all (table);
+}
+
+static void
+username_entry_activate (GtkWidget *widget, GtkWidget *dialog)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (dialog);
+	
+	if (GTK_WIDGET_VISIBLE (password_dialog->details->domain_entry) &&
+	    GTK_WIDGET_SENSITIVE (password_dialog->details->domain_entry))
+		gtk_widget_grab_focus (password_dialog->details->domain_entry);
+	else if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry) &&
+		 GTK_WIDGET_SENSITIVE (password_dialog->details->password_entry))
+		gtk_widget_grab_focus (password_dialog->details->password_entry);
+	else if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry_secondary) &&
+		 GTK_WIDGET_SENSITIVE (password_dialog->details->password_entry_secondary))
+		gtk_widget_grab_focus (password_dialog->details->password_entry_secondary);
+}
+
+static void
+domain_entry_activate (GtkWidget *widget, GtkWidget *dialog)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (dialog);
+	
+	if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry) &&
+	    GTK_WIDGET_SENSITIVE (password_dialog->details->password_entry))
+		gtk_widget_grab_focus (password_dialog->details->password_entry);
+	else if (GTK_WIDGET_VISIBLE (password_dialog->details->password_entry_secondary) &&
+		 GTK_WIDGET_SENSITIVE (password_dialog->details->password_entry_secondary))
+		gtk_widget_grab_focus (password_dialog->details->password_entry_secondary);
+}
+
+
+/* Public GnomeTwoPasswordDialog methods */
+GtkWidget *
+gnome_two_password_dialog_new (const char	*dialog_title,
+			   const char	*message,
+			   const char	*username,
+			   const char	*password,
+			   gboolean	 readonly_username)
+{
+	GnomeTwoPasswordDialog *password_dialog;
+	GtkDialog *dialog;
+	GtkWidget *table;
+	GtkLabel *message_label;
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *main_vbox;
+	GtkWidget *dialog_icon;
+	GSList *group;
+
+	password_dialog = GNOME_TWO_PASSWORD_DIALOG (gtk_widget_new (gnome_two_password_dialog_get_type (), NULL));
+	dialog = GTK_DIALOG (password_dialog);
+
+	gtk_window_set_title (GTK_WINDOW (password_dialog), dialog_title);
+	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+	gtk_dialog_add_buttons (dialog,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				GTK_STOCK_OK, GTK_RESPONSE_OK,
+				NULL);
+	gtk_dialog_set_default_response (GTK_DIALOG (password_dialog), GTK_RESPONSE_OK);
+
+	/* Setup the dialog */
+	gtk_dialog_set_has_separator (dialog, FALSE);
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+        gtk_box_set_spacing (GTK_BOX (dialog->vbox), 2); /* 2 * 5 + 2 = 12 */
+        gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 5);
+        gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
+
+ 	gtk_window_set_position (GTK_WINDOW (password_dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_modal (GTK_WINDOW (password_dialog), TRUE);
+
+	g_signal_connect (password_dialog, "show",
+			  G_CALLBACK (dialog_show_callback), password_dialog);
+	g_signal_connect (password_dialog, "close",
+			  G_CALLBACK (dialog_close_callback), password_dialog);
+
+	/* the radio buttons for anonymous login */
+	password_dialog->details->connect_with_no_userpass_button =
+                gtk_radio_button_new_with_mnemonic (NULL, _("Connect _anonymously"));
+	group = gtk_radio_button_get_group (
+			GTK_RADIO_BUTTON (password_dialog->details->connect_with_no_userpass_button));
+        password_dialog->details->connect_with_userpass_button =
+                gtk_radio_button_new_with_mnemonic (
+			group, _("Connect as _user:"));
+
+	if (username != NULL && *username != 0) {
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (password_dialog->details->connect_with_userpass_button), TRUE);
+	} else {
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (password_dialog->details->connect_with_no_userpass_button), TRUE);
+	}
+	
+	password_dialog->details->radio_vbox = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (password_dialog->details->radio_vbox),
+		password_dialog->details->connect_with_no_userpass_button,
+		FALSE, FALSE, 0);	
+	gtk_box_pack_start (GTK_BOX (password_dialog->details->radio_vbox),
+                password_dialog->details->connect_with_userpass_button,
+                FALSE, FALSE, 0);
+	g_signal_connect (password_dialog->details->connect_with_no_userpass_button, "clicked",
+                          G_CALLBACK (userpass_radio_button_clicked), password_dialog);
+	g_signal_connect (password_dialog->details->connect_with_userpass_button, "clicked",
+                          G_CALLBACK (userpass_radio_button_clicked), password_dialog);	
+
+	/* The table that holds the captions */
+	password_dialog->details->table_alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+
+	password_dialog->details->table = table = gtk_table_new (3, 2, FALSE);
+	gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	gtk_container_add (GTK_CONTAINER (password_dialog->details->table_alignment), table);
+
+	password_dialog->details->username_entry = gtk_entry_new ();
+	password_dialog->details->domain_entry = gtk_entry_new ();
+	password_dialog->details->password_entry = gtk_entry_new ();
+	password_dialog->details->password_entry_secondary = gtk_entry_new ();
+
+	/* We want to hold on to these during the table rearrangement */
+#if GLIB_CHECK_VERSION (2, 10, 0)
+	g_object_ref_sink (password_dialog->details->username_entry);
+	g_object_ref_sink (password_dialog->details->domain_entry);
+        g_object_ref_sink (password_dialog->details->password_entry);
+        g_object_ref_sink (password_dialog->details->password_entry_secondary);
+#else
+	g_object_ref (password_dialog->details->username_entry);
+	gtk_object_sink (GTK_OBJECT (password_dialog->details->username_entry));
+	g_object_ref (password_dialog->details->domain_entry);
+	gtk_object_sink (GTK_OBJECT (password_dialog->details->domain_entry));
+        g_object_ref (password_dialog->details->password_entry);
+	gtk_object_sink (GTK_OBJECT (password_dialog->details->password_entry));
+        g_object_ref (password_dialog->details->password_entry_secondary);
+	gtk_object_sink (GTK_OBJECT (password_dialog->details->password_entry_secondary));
+#endif
+	
+	gtk_entry_set_visibility (GTK_ENTRY (password_dialog->details->password_entry), FALSE);
+	gtk_entry_set_visibility (GTK_ENTRY (password_dialog->details->password_entry_secondary), FALSE);
+
+	g_signal_connect (password_dialog->details->username_entry,
+			  "activate",
+			  G_CALLBACK (username_entry_activate),
+			  password_dialog);
+	g_signal_connect (password_dialog->details->domain_entry,
+			  "activate",
+			  G_CALLBACK (domain_entry_activate),
+			  password_dialog);
+	g_signal_connect_swapped (password_dialog->details->password_entry,
+				  "activate",
+				  G_CALLBACK (gtk_window_activate_default),
+				  password_dialog);
+	g_signal_connect_swapped (password_dialog->details->password_entry_secondary,
+				  "activate",
+				  G_CALLBACK (gtk_window_activate_default),
+				  password_dialog);
+	add_table_rows (password_dialog);
+
+	/* Adds some eye-candy to the dialog */
+	hbox = gtk_hbox_new (FALSE, 12);
+ 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+	dialog_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (dialog_icon), 0.5, 0.0);
+	gtk_box_pack_start (GTK_BOX (hbox), dialog_icon, FALSE, FALSE, 0);
+
+	/* Fills the vbox */
+	main_vbox = gtk_vbox_new (FALSE, 18);
+
+	if (message) {
+		message_label = GTK_LABEL (gtk_label_new (message));
+		gtk_label_set_justify (message_label, GTK_JUSTIFY_LEFT);
+		gtk_label_set_line_wrap (message_label, TRUE);
+
+		gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label),
+				    FALSE, FALSE, 0);
+	}
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (vbox), password_dialog->details->radio_vbox,
+                            FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), password_dialog->details->table_alignment,
+			    FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (password_dialog)->vbox),
+			    hbox,
+			    TRUE,	/* expand */
+			    TRUE,	/* fill */
+			    0);       	/* padding */
+	
+	gtk_widget_show_all (GTK_DIALOG (password_dialog)->vbox);
+
+	password_dialog->details->remember_session_button =
+		gtk_check_button_new_with_mnemonic (_("_Remember password for this session"));
+	password_dialog->details->remember_forever_button =
+		gtk_check_button_new_with_mnemonic (_("_Save password in keyring"));
+
+	gtk_box_pack_start (GTK_BOX (vbox), password_dialog->details->remember_session_button, 
+			    FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), password_dialog->details->remember_forever_button, 
+			    FALSE, FALSE, 0);
+	
+	
+	gnome_two_password_dialog_set_username (password_dialog, username);
+	gnome_two_password_dialog_set_password (password_dialog, password);
+	gnome_two_password_dialog_set_readonly_domain (password_dialog, readonly_username);
+	
+	return GTK_WIDGET (password_dialog);
+}
+
+gboolean
+gnome_two_password_dialog_run_and_block (GnomeTwoPasswordDialog *password_dialog)
+{
+	gint button_clicked;
+
+	g_return_val_if_fail (password_dialog != NULL, FALSE);
+	g_return_val_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog), FALSE);
+
+	button_clicked = gtk_dialog_run (GTK_DIALOG (password_dialog));
+	gtk_widget_hide (GTK_WIDGET (password_dialog));
+
+	return button_clicked == GTK_RESPONSE_OK;
+}
+
+void
+gnome_two_password_dialog_set_username (GnomeTwoPasswordDialog	*password_dialog,
+				       const char		*username)
+{
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+	g_return_if_fail (password_dialog->details->username_entry != NULL);
+
+	gtk_entry_set_text (GTK_ENTRY (password_dialog->details->username_entry),
+			    username?username:"");
+}
+
+void
+gnome_two_password_dialog_set_password (GnomeTwoPasswordDialog	*password_dialog,
+				       const char		*password)
+{
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	gtk_entry_set_text (GTK_ENTRY (password_dialog->details->password_entry),
+			    password ? password : "");
+}
+
+void
+gnome_two_password_dialog_set_password_secondary (GnomeTwoPasswordDialog	*password_dialog,
+						  const char		        *password_secondary)
+{
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	gtk_entry_set_text (GTK_ENTRY (password_dialog->details->password_entry_secondary),
+			    password_secondary ? password_secondary : "");
+}
+
+void
+gnome_two_password_dialog_set_domain (GnomeTwoPasswordDialog	*password_dialog,
+				  const char		*domain)
+{
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+	g_return_if_fail (password_dialog->details->domain_entry != NULL);
+
+	gtk_entry_set_text (GTK_ENTRY (password_dialog->details->domain_entry),
+			    domain ? domain : "");
+}
+
+
+void
+gnome_two_password_dialog_set_show_username (GnomeTwoPasswordDialog *password_dialog,
+					 gboolean             show)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	show = !!show;
+	if (password_dialog->details->show_username != show) {
+		password_dialog->details->show_username = show;
+		add_table_rows (password_dialog);
+	}
+}
+
+void
+gnome_two_password_dialog_set_show_domain (GnomeTwoPasswordDialog *password_dialog,
+				       gboolean             show)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	show = !!show;
+	if (password_dialog->details->show_domain != show) {
+		password_dialog->details->show_domain = show;
+		add_table_rows (password_dialog);
+	}
+}
+
+void
+gnome_two_password_dialog_set_show_password (GnomeTwoPasswordDialog *password_dialog,
+					 gboolean             show)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	show = !!show;
+	if (password_dialog->details->show_password != show) {
+		password_dialog->details->show_password = show;
+		add_table_rows (password_dialog);
+	}
+}
+
+void
+gnome_two_password_dialog_set_show_password_secondary (GnomeTwoPasswordDialog *password_dialog,
+						       gboolean             show)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	show = !!show;
+	if (password_dialog->details->show_password_secondary != show) {
+		password_dialog->details->show_password_secondary = show;
+		add_table_rows (password_dialog);
+	}
+}
+
+void
+gnome_two_password_dialog_set_readonly_username (GnomeTwoPasswordDialog	*password_dialog,
+						gboolean		readonly)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	password_dialog->details->readonly_username = readonly;
+
+	gtk_widget_set_sensitive (password_dialog->details->username_entry,
+				  !readonly);
+}
+
+void
+gnome_two_password_dialog_set_readonly_domain (GnomeTwoPasswordDialog	*password_dialog,
+					   gboolean		readonly)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	password_dialog->details->readonly_domain = readonly;
+
+	gtk_widget_set_sensitive (password_dialog->details->domain_entry,
+				  !readonly);
+}
+
+char *
+gnome_two_password_dialog_get_username (GnomeTwoPasswordDialog *password_dialog)
+{
+	g_return_val_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog), NULL);
+
+	return g_strdup (gtk_entry_get_text (GTK_ENTRY (password_dialog->details->username_entry)));
+}
+
+char *
+gnome_two_password_dialog_get_domain (GnomeTwoPasswordDialog *password_dialog)
+{
+	g_return_val_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog), NULL);
+	
+	return g_strdup (gtk_entry_get_text (GTK_ENTRY (password_dialog->details->domain_entry)));
+}
+
+char *
+gnome_two_password_dialog_get_password (GnomeTwoPasswordDialog *password_dialog)
+{
+	g_return_val_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog), NULL);
+
+	return g_strdup (gtk_entry_get_text (GTK_ENTRY (password_dialog->details->password_entry)));
+}
+
+char *
+gnome_two_password_dialog_get_password_secondary (GnomeTwoPasswordDialog *password_dialog)
+{
+	g_return_val_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog), NULL);
+
+	return g_strdup (gtk_entry_get_text (GTK_ENTRY (password_dialog->details->password_entry_secondary)));
+}
+
+void
+gnome_two_password_dialog_set_show_userpass_buttons (GnomeTwoPasswordDialog         *password_dialog,
+						     gboolean                     show_userpass_buttons)
+{
+        if (show_userpass_buttons) {
+                password_dialog->details->anon_support_on = TRUE;
+                gtk_widget_show (password_dialog->details->radio_vbox);
+                if (gtk_toggle_button_get_active (
+                        GTK_TOGGLE_BUTTON (password_dialog->details->connect_with_no_userpass_button))) {
+                        gtk_widget_set_sensitive (password_dialog->details->table, FALSE);
+                }
+                else {
+                        gtk_widget_set_sensitive (password_dialog->details->table, TRUE);
+                }
+        } else {
+                password_dialog->details->anon_support_on = FALSE;
+                gtk_widget_hide (password_dialog->details->radio_vbox);
+                gtk_widget_set_sensitive (password_dialog->details->table, TRUE);
+        }
+                                                                                                                             
+        add_table_rows (password_dialog);
+}
+
+gboolean
+gnome_two_password_dialog_anon_selected (GnomeTwoPasswordDialog *password_dialog)
+{
+	return password_dialog->details->anon_support_on &&
+		gtk_toggle_button_get_active (
+        		GTK_TOGGLE_BUTTON (
+				password_dialog->details->connect_with_no_userpass_button));
+}
+
+void
+gnome_two_password_dialog_set_show_remember (GnomeTwoPasswordDialog         *password_dialog,
+					 gboolean                     show_remember)
+{
+	if (show_remember) {
+		gtk_widget_show (password_dialog->details->remember_session_button);
+		gtk_widget_show (password_dialog->details->remember_forever_button);
+	} else {
+		gtk_widget_hide (password_dialog->details->remember_session_button);
+		gtk_widget_hide (password_dialog->details->remember_forever_button);
+	}
+}
+
+void
+gnome_two_password_dialog_set_remember      (GnomeTwoPasswordDialog         *password_dialog,
+					 GnomeTwoPasswordDialogRemember  remember)
+{
+	gboolean session, forever;
+
+	session = FALSE;
+	forever = FALSE;
+	if (remember == GNOME_TWO_PASSWORD_DIALOG_REMEMBER_SESSION) {
+		session = TRUE;
+	} else if (remember == GNOME_TWO_PASSWORD_DIALOG_REMEMBER_FOREVER){
+		forever = TRUE;
+	}
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (password_dialog->details->remember_session_button),
+				      session);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (password_dialog->details->remember_forever_button),
+				      forever);
+}
+
+GnomeTwoPasswordDialogRemember
+gnome_two_password_dialog_get_remember (GnomeTwoPasswordDialog         *password_dialog)
+{
+	gboolean session, forever;
+
+	session = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (password_dialog->details->remember_session_button));
+	forever = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (password_dialog->details->remember_forever_button));
+	if (forever) {
+		return GNOME_TWO_PASSWORD_DIALOG_REMEMBER_FOREVER;
+	} else if (session) {
+		return GNOME_TWO_PASSWORD_DIALOG_REMEMBER_SESSION;
+	}
+	return GNOME_TWO_PASSWORD_DIALOG_REMEMBER_NOTHING;
+}
+
+void gnome_two_password_dialog_set_password_primary_label (GnomeTwoPasswordDialog  *password_dialog,
+							   const char              *password_primary_label)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	g_free (password_dialog->details->primary_password_label);
+	password_dialog->details->primary_password_label = g_strdup (password_primary_label);
+
+	add_table_rows (password_dialog);
+}
+
+void gnome_two_password_dialog_set_password_secondary_label (GnomeTwoPasswordDialog  *password_dialog,
+							     const char              *password_secondary_label)
+{
+	g_return_if_fail (password_dialog != NULL);
+	g_return_if_fail (GNOME_IS_TWO_PASSWORD_DIALOG (password_dialog));
+
+	g_free (password_dialog->details->secondary_password_label);
+	password_dialog->details->secondary_password_label = g_strdup (password_secondary_label);
+
+	if (password_dialog->details->show_password_secondary) {
+		add_table_rows (password_dialog);
+	}
+}

Added: trunk/auth-dialog/gnome-two-password-dialog.h
==============================================================================
--- (empty file)
+++ trunk/auth-dialog/gnome-two-password-dialog.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,118 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* gnome-two-password-dialog.h - A use password prompting dialog widget
+                                 asking for two passwords. Based of
+                                 gnome-password-dialog.[ch] from libgnomeui
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+   Copyright (C) 2005, Red Hat, Inc.
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef GNOME_TWO_PASSWORD_DIALOG_H
+#define GNOME_TWO_PASSWORD_DIALOG_H
+
+#include <gtk/gtkdialog.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_TWO_PASSWORD_DIALOG            (gnome_two_password_dialog_get_type ())
+#define GNOME_TWO_PASSWORD_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_TWO_PASSWORD_DIALOG, GnomeTwoPasswordDialog))
+#define GNOME_TWO_PASSWORD_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_TWO_PASSWORD_DIALOG, GnomeTwoPasswordDialogClass))
+#define GNOME_IS_TWO_PASSWORD_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_TWO_PASSWORD_DIALOG))
+#define GNOME_IS_TWO_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_TWO_PASSWORD_DIALOG))
+
+typedef struct GnomeTwoPasswordDialog        GnomeTwoPasswordDialog;
+typedef struct GnomeTwoPasswordDialogClass   GnomeTwoPasswordDialogClass;
+typedef struct GnomeTwoPasswordDialogDetails GnomeTwoPasswordDialogDetails;
+
+struct GnomeTwoPasswordDialog
+{
+	GtkDialog gtk_dialog;
+
+	GnomeTwoPasswordDialogDetails *details;
+};
+
+struct GnomeTwoPasswordDialogClass
+{
+	GtkDialogClass parent_class;
+};
+
+typedef enum {
+	GNOME_TWO_PASSWORD_DIALOG_REMEMBER_NOTHING,
+	GNOME_TWO_PASSWORD_DIALOG_REMEMBER_SESSION,
+	GNOME_TWO_PASSWORD_DIALOG_REMEMBER_FOREVER
+} GnomeTwoPasswordDialogRemember;
+
+
+GType    gnome_two_password_dialog_get_type (void);
+GtkWidget* gnome_two_password_dialog_new      (const char *dialog_title,
+					   const char *message,
+					   const char *username,
+					   const char *password,
+					   gboolean    readonly_username);
+
+gboolean   gnome_two_password_dialog_run_and_block           (GnomeTwoPasswordDialog *password_dialog);
+
+/* Attribute mutators */
+void gnome_two_password_dialog_set_show_username           (GnomeTwoPasswordDialog *password_dialog,
+							    gboolean                show);
+void gnome_two_password_dialog_set_show_domain             (GnomeTwoPasswordDialog *password_dialog,
+							    gboolean                show);
+void gnome_two_password_dialog_set_show_password           (GnomeTwoPasswordDialog *password_dialog,
+							    gboolean                show);
+void gnome_two_password_dialog_set_show_password_secondary (GnomeTwoPasswordDialog *password_dialog,
+							    gboolean                show);
+void gnome_two_password_dialog_set_username                (GnomeTwoPasswordDialog  *password_dialog,
+							    const char              *username);
+void gnome_two_password_dialog_set_domain                  (GnomeTwoPasswordDialog  *password_dialog,
+							    const char              *domain);
+void gnome_two_password_dialog_set_password                (GnomeTwoPasswordDialog  *password_dialog,
+							    const char              *password);
+void gnome_two_password_dialog_set_password_secondary      (GnomeTwoPasswordDialog  *password_dialog,
+							    const char              *password_secondary);
+void gnome_two_password_dialog_set_readonly_username       (GnomeTwoPasswordDialog  *password_dialog,
+							    gboolean                 readonly);
+void gnome_two_password_dialog_set_readonly_domain         (GnomeTwoPasswordDialog  *password_dialog,
+							    gboolean                 readonly);
+
+void gnome_two_password_dialog_set_password_primary_label (GnomeTwoPasswordDialog  *password_dialog,
+							   const char              *password_primary_description);
+void gnome_two_password_dialog_set_password_secondary_label (GnomeTwoPasswordDialog  *password_dialog,
+							     const char              *password_secondary_description);
+
+void                           gnome_two_password_dialog_set_show_remember         (GnomeTwoPasswordDialog         *password_dialog,
+										    gboolean                        show_remember);
+void                           gnome_two_password_dialog_set_remember              (GnomeTwoPasswordDialog         *password_dialog,
+										    GnomeTwoPasswordDialogRemember  remember);
+GnomeTwoPasswordDialogRemember gnome_two_password_dialog_get_remember              (GnomeTwoPasswordDialog         *password_dialog);
+void                           gnome_two_password_dialog_set_show_userpass_buttons (GnomeTwoPasswordDialog         *password_dialog,
+										    gboolean                        show_userpass_buttons);
+
+/* Attribute accessors */
+char *     gnome_two_password_dialog_get_username            (GnomeTwoPasswordDialog *password_dialog);
+char *     gnome_two_password_dialog_get_domain              (GnomeTwoPasswordDialog *password_dialog);
+char *     gnome_two_password_dialog_get_password            (GnomeTwoPasswordDialog *password_dialog);
+char *     gnome_two_password_dialog_get_password_secondary  (GnomeTwoPasswordDialog *password_dialog);
+
+gboolean   gnome_two_password_dialog_anon_selected           (GnomeTwoPasswordDialog *password_dialog);
+
+G_END_DECLS
+
+#endif /* GNOME_TWO_PASSWORD_DIALOG_H */

Added: trunk/auth-dialog/main.c
==============================================================================
--- (empty file)
+++ trunk/auth-dialog/main.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,390 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ * Tim Niemueller <tim niemueller de>
+ *
+ * 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 2004 - 2008 Red Hat, Inc.
+ *               2005 Tim Niemueller [www.niemueller.de]
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libgnomeui/libgnomeui.h>
+#include <gconf/gconf-client.h>
+#include <gnome-keyring.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-connection.h>
+
+#include "common-gnome/keyring-helpers.h"
+#include "src/nm-openvpn-service.h"
+#include "gnome-two-password-dialog.h"
+
+typedef struct {
+	char *vpn_uuid;
+	char *vpn_name;
+
+	gboolean need_password;
+	char *password;
+
+	gboolean need_certpass;
+	char *certpass;
+} PasswordsInfo;
+
+#define PROC_TYPE_TAG "Proc-Type: 4,ENCRYPTED"
+
+/** Checks if a key is encrypted
+ * The key file is read and it is checked if it contains a line reading
+ * Proc-Type: 4,ENCRYPTED
+ * This is defined in RFC 1421 (PEM)
+ * @param filename the path to the file
+ * @return returns true if the key is encrypted, false otherwise
+ */
+static gboolean
+pem_is_encrypted (const char *filename)
+{
+	GIOChannel *pem_chan;
+	char       *str = NULL;
+	gboolean encrypted = FALSE;
+
+	pem_chan = g_io_channel_new_file (filename, "r", NULL);
+	if (!pem_chan)
+		return FALSE;
+
+	while (g_io_channel_read_line (pem_chan, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
+		if (strncmp (str, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG)) == 0) {
+			encrypted = TRUE;
+			break;
+		}
+		g_free (str);
+	}
+
+	g_io_channel_shutdown (pem_chan, FALSE, NULL);
+	g_io_channel_unref (pem_chan);
+	return encrypted;
+}
+
+
+static void
+clear_secrets (PasswordsInfo *info)
+{
+	if (info->password) {
+		memset (info->password, 0, strlen (info->password));
+		g_free (info->password);
+	}
+	if (info->certpass) {
+		memset (info->certpass, 0, strlen (info->certpass));
+		g_free (info->certpass);
+	}
+}
+
+static gboolean
+get_secrets (PasswordsInfo *info, gboolean retry)
+{
+	GnomeTwoPasswordDialog *dialog;
+	gboolean is_session = TRUE;
+	char *prompt;
+	gboolean success = FALSE, need_secret = FALSE;
+
+	g_return_val_if_fail (info->vpn_uuid != NULL, FALSE);
+	g_return_val_if_fail (info->vpn_name != NULL, FALSE);
+
+	if (info->need_password) {
+		info->password = keyring_helpers_lookup_secret (info->vpn_uuid, NM_OPENVPN_KEY_PASSWORD, &is_session);
+		if (!info->password)
+			need_secret = TRUE;
+	}
+
+	if (info->need_certpass) {
+		info->certpass = keyring_helpers_lookup_secret (info->vpn_uuid, NM_OPENVPN_KEY_CERTPASS, &is_session);
+		if (!info->certpass)
+			need_secret = TRUE;
+	}
+
+	/* Have all passwords and we're not supposed to ask the user again */
+	if (!need_secret && !retry)
+		return TRUE;
+
+	prompt = g_strdup_printf (_("You need to authenticate to access the Virtual Private Network '%s'."), info->vpn_name);
+	dialog = GNOME_TWO_PASSWORD_DIALOG (gnome_two_password_dialog_new (_("Authenticate VPN"), prompt, NULL, NULL, FALSE));
+	g_free (prompt);
+
+	gnome_two_password_dialog_set_show_username (dialog, FALSE);
+	gnome_two_password_dialog_set_show_userpass_buttons (dialog, FALSE);
+	gnome_two_password_dialog_set_show_domain (dialog, FALSE);
+	gnome_two_password_dialog_set_show_remember (dialog, TRUE);
+
+	/* If nothing was found in the keyring, default to not remembering any secrets */
+	if (info->password || info->certpass) {
+		/* Otherwise set default remember based on which keyring the secrets were found in */
+		if (is_session)
+			gnome_two_password_dialog_set_remember (dialog, GNOME_TWO_PASSWORD_DIALOG_REMEMBER_SESSION);
+		else
+			gnome_two_password_dialog_set_remember (dialog, GNOME_TWO_PASSWORD_DIALOG_REMEMBER_FOREVER);
+	} else
+		gnome_two_password_dialog_set_remember (dialog, GNOME_TWO_PASSWORD_DIALOG_REMEMBER_NOTHING);
+
+	/* pre-fill dialog with the password */
+	if (info->need_password && info->need_certpass) {
+		gnome_two_password_dialog_set_show_password_secondary (dialog, TRUE);
+		gnome_two_password_dialog_set_password_secondary_label (dialog, _("Certificate pass_word:") );
+
+		/* if retrying, put in the passwords from the keyring */
+		if (info->password)
+			gnome_two_password_dialog_set_password (dialog, info->password);
+		if (info->certpass)
+			gnome_two_password_dialog_set_password_secondary (dialog, info->certpass);
+	} else {
+		gnome_two_password_dialog_set_show_password_secondary (dialog, FALSE);
+		if (info->need_password) {
+			/* if retrying, put in the passwords from the keyring */
+			if (info->password)
+				gnome_two_password_dialog_set_password (dialog, info->password);
+		} else if (info->need_certpass) {
+			gnome_two_password_dialog_set_password_primary_label (dialog, _("Certificate password:"));
+			/* if retrying, put in the passwords from the keyring */
+			if (info->certpass)
+				gnome_two_password_dialog_set_password (dialog, info->certpass);
+		}
+	}
+	clear_secrets (info);
+
+	gtk_widget_show (GTK_WIDGET (dialog));
+
+	if (gnome_two_password_dialog_run_and_block (dialog)) {
+		gboolean save = FALSE;
+		char *keyring = NULL;
+
+		if (info->need_password)
+			info->password = g_strdup (gnome_two_password_dialog_get_password (dialog));
+		if (info->need_certpass) {
+			info->certpass = g_strdup (info->need_password ? 
+								  gnome_two_password_dialog_get_password_secondary (dialog) :
+								  gnome_two_password_dialog_get_password (dialog));
+		}
+
+		switch (gnome_two_password_dialog_get_remember (dialog)) {
+		case GNOME_TWO_PASSWORD_DIALOG_REMEMBER_SESSION:
+			keyring = "session";
+			/* Fall through */
+		case GNOME_TWO_PASSWORD_DIALOG_REMEMBER_FOREVER:
+			save = TRUE;
+			break;
+		default:
+			break;
+		}
+
+		if (save) {
+			if (info->password) {
+				keyring_helpers_save_secret (info->vpn_uuid, info->vpn_name,
+											 keyring, NM_OPENVPN_KEY_PASSWORD, info->password);
+			}
+			if (info->certpass) {
+				keyring_helpers_save_secret (info->vpn_uuid, info->vpn_name,
+											 keyring, NM_OPENVPN_KEY_CERTPASS, info->certpass);
+			}
+		}
+
+		success = TRUE;
+	}
+
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	return success;
+}
+
+static gboolean
+get_password_types (PasswordsInfo *info)
+{
+	GConfClient *gconf_client = NULL;
+	GSList *conf_list;
+	GSList *iter;
+	char *key;
+	char *str;
+	char *connection_path = NULL;
+	gboolean success = FALSE;
+	char *connection_type;
+
+	/* FIXME: This whole thing sucks: we should not go around poking gconf
+	   directly, but there's nothing that does it for us right now */
+
+	gconf_client = gconf_client_get_default ();
+
+	conf_list = gconf_client_all_dirs (gconf_client, "/system/networking/connections", NULL);
+	if (!conf_list)
+		return FALSE;
+
+	for (iter = conf_list; iter; iter = iter->next) {
+		const char *path = (const char *) iter->data;
+
+		key = g_strdup_printf ("%s/%s/%s", 
+		                       path,
+		                       NM_SETTING_CONNECTION_SETTING_NAME,
+		                       NM_SETTING_CONNECTION_TYPE);
+		str = gconf_client_get_string (gconf_client, key, NULL);
+		g_free (key);
+
+		if (!str || strcmp (str, "vpn")) {
+			g_free (str);
+			continue;
+		}
+		g_free (str);
+
+		key = g_strdup_printf ("%s/%s/%s", 
+		                       path,
+		                       NM_SETTING_CONNECTION_SETTING_NAME,
+		                       NM_SETTING_CONNECTION_UUID);
+		str = gconf_client_get_string (gconf_client, key, NULL);
+		g_free (key);
+
+		if (!str || strcmp (str, info->vpn_uuid)) {
+			g_free (str);
+			continue;
+		}
+		g_free (str);
+
+		/* Woo, found the connection */
+		connection_path = g_strdup (path);
+		break;
+	}
+
+	g_slist_foreach (conf_list, (GFunc) g_free, NULL);
+	g_slist_free (conf_list);
+
+	if (!connection_path)
+		goto out;
+
+	key = g_strdup_printf ("%s/%s/%s", connection_path,
+	                       NM_SETTING_VPN_SETTING_NAME,
+	                       NM_OPENVPN_KEY_CONNECTION_TYPE);
+	connection_type = gconf_client_get_string (gconf_client, key, NULL);
+	g_free (key);
+	
+	if (   !strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)
+	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		success = TRUE;
+
+		if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
+			info->need_password = TRUE;
+
+		key = g_strdup_printf ("%s/%s/%s", connection_path, NM_SETTING_VPN_SETTING_NAME,
+		                       NM_OPENVPN_KEY_KEY);
+		str = gconf_client_get_string (gconf_client, key, NULL);
+		if (str)
+			info->need_certpass = pem_is_encrypted (str);
+		g_free (str);
+		g_free (key);
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
+		success = TRUE;
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		success = TRUE;
+		info->need_password = TRUE;
+	}
+
+	g_free (connection_type);
+	g_free (connection_path);
+
+out:
+	g_object_unref (gconf_client);
+	return success;
+}
+
+int 
+main (int argc, char *argv[])
+{
+	PasswordsInfo info;
+	gboolean retry = FALSE;
+	gchar *vpn_name = NULL;
+	gchar *vpn_uuid = NULL;
+	gchar *vpn_service = NULL;
+	char buf[1];
+	int ret, exit_status = 1;
+	GOptionContext *context;
+	GnomeProgram *program;
+	GOptionEntry entries[] = {
+			{ "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL},
+			{ "uuid", 'u', 0, G_OPTION_ARG_STRING, &vpn_uuid, "UUID of VPN connection", NULL},
+			{ "name", 'n', 0, G_OPTION_ARG_STRING, &vpn_name, "Name of VPN connection", NULL},
+			{ "service", 's', 0, G_OPTION_ARG_STRING, &vpn_service, "VPN service type", NULL},
+			{ NULL }
+		};
+
+	bindtextdomain (GETTEXT_PACKAGE, NULL);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new ("- openvpn auth dialog");
+	g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+	program = gnome_program_init ("nm-openvpn-auth-dialog", VERSION,
+							LIBGNOMEUI_MODULE,
+							argc, argv,
+							GNOME_PARAM_GOPTION_CONTEXT, context,
+							GNOME_PARAM_NONE);
+
+	if (vpn_uuid == NULL || vpn_name == NULL || vpn_service == NULL) {
+		fprintf (stderr, "Have to supply ID, name, and service\n");
+		goto out;
+	}
+
+	if (strcmp (vpn_service, NM_DBUS_SERVICE_OPENVPN) != 0) {
+		fprintf (stderr, "This dialog only works with the '%s' service\n", NM_DBUS_SERVICE_OPENVPN);
+		goto out;		
+	}
+
+	memset (&info, 0, sizeof (PasswordsInfo));
+	info.vpn_uuid = vpn_uuid;
+	info.vpn_name = vpn_name;
+
+	if (!get_password_types (&info)) {
+		fprintf (stderr, "Invalid connection");
+		goto out;
+	}
+
+	exit_status = 0;
+
+	if (!info.need_password && !info.need_certpass) {
+		printf ("%s\n%s\n\n\n", NM_OPENVPN_KEY_NOSECRET, "true");
+		goto out;
+	}
+
+	if (get_secrets (&info, retry)) {
+		if (info.need_password)
+			printf ("%s\n%s\n", NM_OPENVPN_KEY_PASSWORD, info.password);
+		if (info.need_certpass)
+			printf ("%s\n%s\n", NM_OPENVPN_KEY_CERTPASS, info.certpass);
+	}
+	printf ("\n\n");
+
+	clear_secrets (&info);
+
+	/* for good measure, flush stdout since Kansas is going Bye-Bye */
+	fflush (stdout);
+
+	/* wait for data on stdin  */
+	ret = fread (buf, sizeof (char), sizeof (buf), stdin);
+
+ out:
+	g_object_unref (program);
+
+	return exit_status;
+}

Added: trunk/autogen.sh
==============================================================================
--- (empty file)
+++ trunk/autogen.sh	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+REQUIRED_AUTOMAKE_VERSION=1.7
+PKG_NAME=NetworkManager-openvpn
+
+(test -f $srcdir/configure.in \
+  && test -f $srcdir/auth-dialog/main.c) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level $PKG_NAME directory"
+    exit 1
+}
+
+
+which gnome-autogen.sh || {
+    echo "You need to install gnome-common from the GNOME CVS"
+    exit 1
+}
+USE_GNOME2_MACROS=1 . gnome-autogen.sh
+
+

Added: trunk/common-gnome/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/common-gnome/Makefile.am	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,15 @@
+noinst_LTLIBRARIES=libnm-openvpn-common-gnome.la
+
+libnm_openvpn_common_gnome_la_CPPFLAGS = \
+	$(NETWORK_MANAGER_CFLAGS) \
+	$(GNOMEKEYRING_CFLAGS) \
+	-DG_DISABLE_DEPRECATED
+
+libnm_openvpn_common_gnome_la_SOURCES= \
+	keyring-helpers.c \
+	keyring-helpers.h
+
+libnm_openvpn_common_gnome_la_LIBADD = \
+	$(NETWORK_MANAGER_LIBS) \
+	$(GNOMEKEYRING_LIBS)
+

Added: trunk/common-gnome/keyring-helpers.c
==============================================================================
--- (empty file)
+++ trunk/common-gnome/keyring-helpers.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,156 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * 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 2004 - 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <gnome-keyring-memory.h>
+
+#include <nm-setting-vpn.h>
+
+#include "keyring-helpers.h"
+#include "../src/nm-openvpn-service.h"
+
+#define KEYRING_UUID_TAG "connection-uuid"
+#define KEYRING_SN_TAG "setting-name"
+#define KEYRING_SK_TAG "setting-key"
+
+char *
+keyring_helpers_lookup_secret (const char *vpn_uuid,
+							   const char *secret_name,
+							   gboolean *is_session)
+{
+	GList *found_list = NULL;
+	GnomeKeyringResult ret;
+	GnomeKeyringFound *found;
+	char *secret;
+
+	ret = gnome_keyring_find_itemsv_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                      &found_list,
+	                                      KEYRING_UUID_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      vpn_uuid,
+	                                      KEYRING_SN_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      NM_SETTING_VPN_SETTING_NAME,
+	                                      KEYRING_SK_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      secret_name,
+	                                      NULL);
+	if ((ret != GNOME_KEYRING_RESULT_OK) || (g_list_length (found_list) == 0))
+		return NULL;
+
+	found = (GnomeKeyringFound *) found_list->data;
+
+	if (strcmp (found->keyring, "session") == 0)
+		*is_session = TRUE;
+	else
+		*is_session = FALSE;
+
+	secret = found->secret ? g_strdup (found->secret) : NULL;
+	gnome_keyring_found_list_free (found_list);
+
+	return secret;
+}
+
+GnomeKeyringResult
+keyring_helpers_save_secret (const char *vpn_uuid,
+                             const char *vpn_name,
+                             const char *keyring,
+                             const char *secret_name,
+                             const char *secret)
+{
+	char *display_name;
+	GnomeKeyringResult ret;
+	GnomeKeyringAttributeList *attrs = NULL;
+	guint32 id = 0;
+
+	display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME,
+	                                secret_name,
+	                                vpn_name,
+	                                NM_DBUS_SERVICE_OPENVPN);
+
+	attrs = gnome_keyring_attribute_list_new ();
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_UUID_TAG,
+	                                            vpn_uuid);
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_SN_TAG,
+	                                            NM_SETTING_VPN_SETTING_NAME);
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_SK_TAG,
+	                                            secret_name);
+
+	ret = gnome_keyring_item_create_sync (keyring,
+	                                      GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                      display_name,
+	                                      attrs,
+	                                      secret,
+	                                      TRUE,
+	                                      &id);
+	gnome_keyring_attribute_list_free (attrs);
+	g_free (display_name);
+	return ret;
+}
+
+static void
+ignore_callback (GnomeKeyringResult result, gpointer data)
+{
+}
+
+gboolean
+keyring_helpers_delete_secret (const char *vpn_uuid,
+                               const char *secret_name)
+{
+	GList *found = NULL, *iter;
+	GnomeKeyringResult ret;
+
+	g_return_val_if_fail (vpn_uuid != NULL, FALSE);
+	g_return_val_if_fail (secret_name != NULL, FALSE);
+
+	ret = gnome_keyring_find_itemsv_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                      &found,
+	                                      KEYRING_UUID_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      vpn_uuid,
+	                                      KEYRING_SN_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      NM_SETTING_VPN_SETTING_NAME,
+	                                      KEYRING_SK_TAG,
+	                                      GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                      secret_name,
+	                                      NULL);
+	if (ret != GNOME_KEYRING_RESULT_OK && ret != GNOME_KEYRING_RESULT_NO_MATCH)
+		return FALSE;
+	if (g_list_length (found) == 0)
+		return TRUE;
+
+	/* delete them all */
+	for (iter = found; iter; iter = g_list_next (iter)) {
+		GnomeKeyringFound *item = (GnomeKeyringFound *) iter->data;
+
+		gnome_keyring_item_delete (item->keyring, item->item_id,
+		                           ignore_callback, NULL, NULL);
+	}
+
+	gnome_keyring_found_list_free (found);
+	return TRUE;
+}
+

Added: trunk/common-gnome/keyring-helpers.h
==============================================================================
--- (empty file)
+++ trunk/common-gnome/keyring-helpers.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * 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 2004 - 2008 Red Hat, Inc.
+ */
+
+#ifndef KEYRING_HELPERS_H
+#define KEYRING_HELPERS_H
+
+#include <glib.h>
+#include <gnome-keyring.h>
+
+char *keyring_helpers_lookup_secret (const char *vpn_uuid,
+									 const char *secret_name,
+									 gboolean *is_session);
+
+GnomeKeyringResult keyring_helpers_save_secret (const char *vpn_uuid,
+                                                const char *vpn_name,
+                                                const char *keyring,
+                                                const char *secret_name,
+                                                const char *secret);
+
+gboolean keyring_helpers_delete_secret (const char *vpn_uuid,
+                                        const char *secret_name);
+
+#endif  /* KEYRING_HELPERS_H */
+

Added: trunk/configure.in
==============================================================================
--- (empty file)
+++ trunk/configure.in	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,129 @@
+AC_PREREQ(2.52)
+
+AC_INIT(NetworkManager-openvpn, 0.7.0, tim niemueller de, NetworkManager-openvpn)
+AC_CONFIG_AUX_DIR(.)
+AM_INIT_AUTOMAKE([subdir-objects])
+AM_MAINTAINER_MODE
+
+AM_CONFIG_HEADER(config.h)
+
+dnl
+dnl Require programs
+dnl
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+dnl
+dnl Required headers
+dnl
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h paths.h sys/ioctl.h sys/time.h syslog.h unistd.h)
+
+dnl
+dnl Checks for typedefs, structures, and compiler characteristics.
+dnl
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_HEADER_TIME
+
+dnl
+dnl Checks for library functions.
+dnl
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_CHECK_FUNCS(select socket uname)
+
+dnl
+dnl GNOME support
+dnl
+AC_ARG_WITH(gnome, AC_HELP_STRING([--without-gnome], [Build NetworkManager-openvpn without GNOME support, e.g. vpn service only]))
+AM_CONDITIONAL(WITH_GNOME, test x"$with_gnome" != xno)
+
+GETTEXT_PACKAGE=NetworkManager-openvpn
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+
+IT_PROG_INTLTOOL([0.35])
+AM_GLIB_GNU_GETTEXT
+
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
+AC_SUBST(GTHREAD_CFLAGS)
+AC_SUBST(GTHREAD_LIBS)
+
+PKG_CHECK_MODULES(DBUS, dbus-glib-1 >= 0.30)
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+PKG_CHECK_MODULES(NETWORK_MANAGER, NetworkManager >= 0.7.0 libnm-util >= 0.7.0 libnm_glib libnm_glib_vpn)
+AC_SUBST(NETWORK_MANAGER_CFLAGS)
+AC_SUBST(NETWORK_MANAGER_LIBS)
+
+if test x"$with_gnome" != xno; then
+	PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.6)
+	AC_SUBST(GTK_CFLAGS)
+	AC_SUBST(GTK_LIBS)
+
+	PKG_CHECK_MODULES(GDK_PIXBUF, gdk-pixbuf-2.0)
+	AC_SUBST(GDK_PIXBUF_CFLAGS)
+	AC_SUBST(GDK_PIXBUF_LIBS)
+
+	PKG_CHECK_MODULES(GLADE, libglade-2.0)
+	AC_SUBST(GLADE_CFLAGS)
+	AC_SUBST(GLADE_LIBS)
+
+	PKG_CHECK_MODULES(LIBGNOMEUI, libgnomeui-2.0)
+	AC_SUBST(LIBGNOMEUI_CFLAGS)
+	AC_SUBST(LIBGNOMEUI_LIBS)
+
+	PKG_CHECK_MODULES(GCONF, gconf-2.0)
+	AC_SUBST(GCONF_CFLAGS)
+	AC_SUBST(GCONF_LIBS)
+
+	PKG_CHECK_MODULES(GNOMEKEYRING, gnome-keyring-1)
+	AC_SUBST(GNOMEKEYRING_CFLAGS)
+	AC_SUBST(GNOMEKEYRING_LIBS)
+fi
+
+AC_ARG_ENABLE(more-warnings,
+AC_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]),
+set_more_warnings="$enableval",[
+if test -d "$srcdir/{arch}" || test -d "$srcdir/CVS"; then
+	set_more_warnings=yes
+else
+	set_more_warnings=no
+fi
+])
+AC_MSG_CHECKING(for more warnings, including -Werror)
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+	AC_MSG_RESULT(yes)
+	CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS"
+
+	for option in -Wno-unused -Wno-strict-aliasing -Wno-sign-compare -Wdeclaration-after-statement -Wno-pointer-sign ; do
+		SAVE_CFLAGS="$CFLAGS"
+		CFLAGS="$CFLAGS $option"
+		AC_MSG_CHECKING([whether gcc understands $option])
+		AC_TRY_COMPILE([], [],
+			has_option=yes,
+			has_option=no,)
+		if test $has_option = no; then
+			CFLAGS="$SAVE_CFLAGS"
+		fi
+		AC_MSG_RESULT($has_option)
+		unset has_option
+		unset SAVE_CFLAGS
+	done
+	unset option
+else
+	AC_MSG_RESULT(no)
+fi
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+common-gnome/Makefile
+auth-dialog/Makefile
+properties/Makefile
+po/Makefile.in
+])

Added: trunk/doc/example-vpnc-system-wide-connection.sh
==============================================================================
--- (empty file)
+++ trunk/doc/example-vpnc-system-wide-connection.sh	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# This is an example of how to install a system-wide connection that
+# cannot be edited by unprivileged users using nm-vpn-properties. This
+# script needs to be run as root and you may need to restart any
+# gconfd daemons after the script has run (logging in and out will
+# suffice)
+
+NAME="openvpn-system-wide"
+ESCAPED_NAME="openvpn-system-wide"
+REMOTE="1.2.3.4"
+CA="/etc/openvpn/CA.crt"
+CERT="/etc/openvpn/thisclient.crt"
+KEY="/etc/openvpn/thisclient.key"
+# must be either yes or no
+USELZO="no"
+
+IPSEC_ROUTES="[172.16.0.0/16,192.168.4.0/24]"
+
+GCONF_PATH="/system/networking/vpn_connections/$ESCAPED_NAME"
+
+GCONFTOOL2_OPTS="--direct --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory"
+
+gconftool-2 $GCONFTOOL2_OPTS --type string --set $GCONF_PATH/name "$NAME"
+gconftool-2 $GCONFTOOL2_OPTS --type string --set $GCONF_PATH/service_name "org.freedesktop.NetworkManager.openvpn"
+gconftool-2 $GCONFTOOL2_OPTS --type list --list-type=string --set $GCONF_PATH/vpn_data ["remote","$REMOTE","ca","$CA","cert",$CERT,"key",$KEY,"comp-lzo",$USELZO]
+gconftool-2 $GCONFTOOL2_OPTS --type list --list-type=string --set $GCONF_PATH/routes $IPSEC_ROUTES
+

Added: trunk/gnome-mime-application-x-openvpn-settings.png
==============================================================================
Binary file. No diff available.

Added: trunk/nm-openvpn-service.conf
==============================================================================
--- (empty file)
+++ trunk/nm-openvpn-service.conf	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,16 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd";>
+<busconfig>
+	<policy user="root">
+		<allow own="org.freedesktop.NetworkManager.openvpn"/>
+		<allow send_destination="org.freedesktop.NetworkManager.openvpn"/>
+		<allow send_interface="org.freedesktop.NetworkManager.openvpn"/>
+	</policy>
+	<policy context="default">
+		<deny own="org.freedesktop.NetworkManager.openvpn"/>
+		<deny send_destination="org.freedesktop.NetworkManager.openvpn"/>
+		<deny send_interface="org.freedesktop.NetworkManager.openvpn"/>
+	</policy>
+</busconfig>
+

Added: trunk/nm-openvpn-service.name.in
==============================================================================
--- (empty file)
+++ trunk/nm-openvpn-service.name.in	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,9 @@
+[VPN Connection]
+name=openvpn
+service=org.freedesktop.NetworkManager.openvpn
+program= LIBEXECDIR@/nm-openvpn-service
+
+[GNOME]
+auth-dialog=nm-openvpn-auth-dialog
+properties=libnm-openvpn-properties
+

Added: trunk/nm-openvpn.desktop.in
==============================================================================
--- (empty file)
+++ trunk/nm-openvpn.desktop.in	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=VPN Connection Manager (OpenVPN)
+GenericName=VPN Connection Manager (OpenVPN)
+Comment=Add, Remove, and Edit VPN Connections
+Exec=nm-vpn-properties --import-service org.freedesktop.NetworkManager.openvpn --import-file %f
+Icon=gnome-mime-application-x-openvpn-settings
+Terminal=false
+Type=Application
+Categories=GNOME;Network;
+MimeType=application/x-openvpn-settings;
+NoDisplay=true

Added: trunk/po/.cvsignore
==============================================================================
--- (empty file)
+++ trunk/po/.cvsignore	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,7 @@
+Makefile.in.in
+Makefile
+Makefile.in
+POTFILES
+*.pot
+stamp-it
+*.gmo

Added: trunk/po/LINGUAS
==============================================================================
--- (empty file)
+++ trunk/po/LINGUAS	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,42 @@
+# please keep this list sorted alphabetically
+#
+ar
+bg
+ca
+cs
+da
+de
+dz
+el
+en_GB
+es
+et
+eu
+fi
+fr
+gl
+hu
+it
+ja
+ka
+ko
+lt
+lv
+mk
+nb
+nl
+pa
+pl
+ps
+pt
+pt_BR
+ru
+sk
+sl
+sv
+th
+uk
+vi
+zh_CN
+zh_HK
+zh_TW

Added: trunk/po/POTFILES.in
==============================================================================
--- (empty file)
+++ trunk/po/POTFILES.in	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,11 @@
+# List of source files containing translatable strings.
+# Please keep this file sorted alphabetically.
+auth-dialog/gnome-two-password-dialog.c
+auth-dialog/main.c
+nm-openvpn.desktop.in
+properties/auth-helpers.c
+properties/nm-openvpn.c
+properties/nm-openvpn-dialog.glade
+src/nm-openvpn-service.c
+src/nm-openvpn-service-openvpn-helper.c
+

Added: trunk/properties/.cvsignore
==============================================================================
--- (empty file)
+++ trunk/properties/.cvsignore	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile

Added: trunk/properties/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/properties/Makefile.am	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,45 @@
+plugindir = $(libdir)/NetworkManager
+plugin_LTLIBRARIES = libnm-openvpn-properties.la
+
+libnm_openvpn_properties_la_SOURCES = \
+	nm-openvpn.c \
+	nm-openvpn.h \
+	auth-helpers.c \
+	auth-helpers.h \
+	import-export.c \
+	import-export.h
+
+gladedir = $(datadir)/gnome-vpn-properties/openvpn
+glade_DATA = nm-openvpn-dialog.glade
+
+libnm_openvpn_properties_la_CFLAGS =                    \
+        $(GLADE_CFLAGS)                                 \
+        $(GTK_CFLAGS)                                   \
+        $(GCONF_CFLAGS)                                 \
+        $(LIBGNOMEUI_CFLAGS)                            \
+        $(NETWORK_MANAGER_CFLAGS)                       \
+        $(GNOMEKEYRING_CFLAGS)                          \
+        -I$(top_srcdir)/                                \
+        -DICONDIR=\""$(datadir)/pixmaps"\"              \
+        -DGLADEDIR=\""$(gladedir)"\"                    \
+        -DG_DISABLE_DEPRECATED                          \
+        -DGDK_DISABLE_DEPRECATED                        \
+        -DGNOME_DISABLE_DEPRECATED                      \
+        -DGNOMELOCALEDIR=\"$(datadir)/locale\"          \
+        -DVERSION=\"$(VERSION)\"
+
+libnm_openvpn_properties_la_LIBADD =    \
+        $(GLADE_LIBS)                   \
+        $(GTK_LIBS)                     \
+        $(GCONF_LIBS)                   \
+        $(LIBGNOMEUI_LIBS)              \
+        $(NETWORK_MANAGER_LIBS)         \
+        $(top_builddir)/common-gnome/libnm-openvpn-common-gnome.la
+
+libnm_openvpn_properties_la_LDFLAGS =   \
+        -avoid-version
+
+CLEANFILES = *.bak *.gladep *~
+
+EXTRA_DIST =                            \
+        $(glade_DATA)

Added: trunk/properties/auth-helpers.c
==============================================================================
--- (empty file)
+++ trunk/properties/auth-helpers.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,1107 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ *
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ * Copyright (C) 2008 Tambet Ingo, <tambet gmail com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib/gi18n-lib.h>
+#include <gnome-keyring-memory.h>
+#include <nm-setting-connection.h>
+
+#include "auth-helpers.h"
+#include "nm-openvpn.h"
+#include "src/nm-openvpn-service.h"
+#include "common-gnome/keyring-helpers.h"
+
+static void
+show_password (GtkToggleButton *togglebutton, GtkEntry *password_entry)
+{
+	gtk_entry_set_visibility (password_entry, gtk_toggle_button_get_active (togglebutton));
+}
+
+static GtkWidget *
+fill_password (GladeXML *xml,
+			   const char *widget_name,
+			   NMConnection *connection,
+			   gboolean priv_key_password)
+{
+	GtkWidget *widget;
+	GtkWidget *show_passwords;
+	char *password;
+
+	widget = glade_xml_get_widget (xml, widget_name);
+	g_assert (widget);
+
+	show_passwords = glade_xml_get_widget (xml, "show_passwords");
+	g_signal_connect (show_passwords, "toggled", G_CALLBACK (show_password), widget);
+
+	if (!connection)
+		return widget;
+
+	password = NULL;
+
+	if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM) {
+		NMSettingVPN *s_vpn;
+
+		s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+		if (s_vpn) {
+			const char *tmp;
+
+			tmp = nm_setting_vpn_get_secret (s_vpn, priv_key_password ? NM_OPENVPN_KEY_CERTPASS : NM_OPENVPN_KEY_PASSWORD);
+			if (tmp)
+				password = gnome_keyring_memory_strdup (tmp);
+		}
+	} else {
+		NMSettingConnection *s_con;
+		gboolean unused;
+
+		s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+		password = keyring_helpers_lookup_secret (nm_setting_connection_get_uuid (s_con),
+		                                          priv_key_password ? NM_OPENVPN_KEY_CERTPASS : NM_OPENVPN_KEY_PASSWORD,
+		                                          &unused);
+	}
+
+	if (password) {
+		gtk_entry_set_text (GTK_ENTRY (widget), password);
+		gnome_keyring_memory_free (password);
+	}
+
+	return widget;
+}
+
+void
+fill_vpn_passwords (GladeXML *xml,
+					GtkSizeGroup *group,
+					NMConnection *connection,
+					const char *contype,
+					ChangedCallback changed_cb,
+					gpointer user_data)
+{
+	GtkWidget *w = NULL;
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS))
+		w = fill_password (xml, "tls_private_key_password_entry", connection, TRUE);
+	else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD))
+		w = fill_password (xml, "pw_password_entry", connection, FALSE);
+	else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		GtkWidget *w2 = NULL;
+
+		w = fill_password (xml, "pw_tls_password_entry", connection, FALSE);
+	
+		w2 = fill_password (xml, "pw_tls_private_key_password_entry", connection, TRUE);
+		if (w2) {
+			gtk_size_group_add_widget (group, w2);
+			g_signal_connect (w2, "changed", G_CALLBACK (changed_cb), user_data);
+		}
+	}
+
+	if (w) {
+		gtk_size_group_add_widget (group, w);
+		g_signal_connect (w, "changed", G_CALLBACK (changed_cb), user_data);
+	}
+}
+
+void
+tls_pw_init_auth_widget (GladeXML *xml,
+                         GtkSizeGroup *group,
+                         NMSettingVPN *s_vpn,
+                         const char *contype,
+                         const char *prefix,
+                         ChangedCallback changed_cb,
+                         gpointer user_data)
+{
+	GtkWidget *widget;
+	const char *value;
+	char *tmp;
+	GtkFileFilter *filter;
+
+	g_return_if_fail (xml != NULL);
+	g_return_if_fail (group != NULL);
+	g_return_if_fail (changed_cb != NULL);
+	g_return_if_fail (prefix != NULL);
+
+	tmp = g_strdup_printf ("%s_ca_cert_chooser", prefix);
+	widget = glade_xml_get_widget (xml, tmp);
+	g_free (tmp);
+
+	gtk_size_group_add_widget (group, widget);
+	filter = tls_file_chooser_filter_new ();
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
+	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
+	gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+	                                   _("Choose a Certificate Authority certificate..."));
+	g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+
+	if (s_vpn) {
+		value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
+		if (value && strlen (value))
+			gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+	}
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		tmp = g_strdup_printf ("%s_user_cert_chooser", prefix);
+		widget = glade_xml_get_widget (xml, tmp);
+		g_free (tmp);
+
+		gtk_size_group_add_widget (group, widget);
+		filter = tls_file_chooser_filter_new ();
+		gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
+		gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
+		gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+		                                   _("Choose your personal certificate..."));
+		g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+
+		if (s_vpn) {
+			value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
+			if (value && strlen (value))
+				gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+		}
+
+		tmp = g_strdup_printf ("%s_private_key_chooser", prefix);
+		widget = glade_xml_get_widget (xml, tmp);
+		g_free (tmp);
+
+		gtk_size_group_add_widget (group, widget);
+		filter = tls_file_chooser_filter_new ();
+		gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
+		gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
+		gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+		                                   _("Choose your private key..."));
+		g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+
+		if (s_vpn) {
+			value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+			if (value && strlen (value))
+				gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+		}
+	}
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		tmp = g_strdup_printf ("%s_username_entry", prefix);
+		widget = glade_xml_get_widget (xml, tmp);
+		g_free (tmp);
+
+		gtk_size_group_add_widget (group, widget);
+		if (s_vpn) {
+			value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME);
+			if (value && strlen (value))
+				gtk_entry_set_text (GTK_ENTRY (widget), value);
+		}
+		g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (changed_cb), user_data);
+	}
+}
+
+#define SK_DIR_COL_NAME 0
+#define SK_DIR_COL_NUM  1
+
+void
+sk_init_auth_widget (GladeXML *xml,
+                     GtkSizeGroup *group,
+                     NMSettingVPN *s_vpn,
+                     ChangedCallback changed_cb,
+                     gpointer user_data)
+{
+	GtkWidget *widget;
+	const char *value = NULL;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	gint active = -1;
+	gint direction = -1;
+	GtkFileFilter *filter;
+
+	g_return_if_fail (xml != NULL);
+	g_return_if_fail (group != NULL);
+	g_return_if_fail (changed_cb != NULL);
+
+	widget = glade_xml_get_widget (xml, "sk_key_chooser");
+	gtk_size_group_add_widget (group, widget);
+	filter = sk_file_chooser_filter_new ();
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
+	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
+	gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
+	                                   _("Choose an OpenVPN static key..."));
+	g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
+
+	if (s_vpn) {
+		value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY);
+		if (value && strlen (value))
+			gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+	}
+
+	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+
+	if (s_vpn) {
+		value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY_DIRECTION);
+		if (value && strlen (value)) {
+			long int tmp;
+
+			errno = 0;
+			tmp = strtol (value, NULL, 10);
+			if (errno == 0 && (tmp == 0 || tmp == 1))
+				direction = (guint32) tmp;
+		}
+	}
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, SK_DIR_COL_NAME, _("None"), SK_DIR_COL_NUM, -1, -1);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, SK_DIR_COL_NAME, "0", SK_DIR_COL_NUM, 0, -1);
+	if (direction == 0)
+		active = 1;
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, SK_DIR_COL_NAME, "1", SK_DIR_COL_NUM, 1, -1);
+	if (direction == 1)
+		active = 2;
+
+	widget = glade_xml_get_widget (xml, "sk_direction_combo");
+	gtk_size_group_add_widget (group, widget);
+
+	gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
+	g_object_unref (store);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? 0 : active);
+
+	widget = glade_xml_get_widget (xml, "sk_dir_help_label");
+	gtk_size_group_add_widget (group, widget);
+
+	widget = glade_xml_get_widget (xml, "sk_local_address_entry");
+	gtk_size_group_add_widget (group, widget);
+	g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (changed_cb), user_data);
+	if (s_vpn) {
+		value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_LOCAL_IP);
+		if (value && strlen (value))
+			gtk_entry_set_text (GTK_ENTRY (widget), value);
+	}
+}
+
+static gboolean
+validate_file_chooser (GladeXML *xml, const char *name)
+{
+	GtkWidget *widget;
+	char *str;
+
+	widget = glade_xml_get_widget (xml, name);
+	str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+	if (!str || !strlen (str))
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean
+validate_tls (GladeXML *xml, const char *prefix, GError **error)
+{
+	char *tmp;
+	gboolean valid;
+
+	tmp = g_strdup_printf ("%s_ca_cert_chooser", prefix);
+	valid = validate_file_chooser (xml, tmp);
+	g_free (tmp);
+	if (!valid) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+		             NM_OPENVPN_KEY_CA);
+		return FALSE;
+	}
+
+	tmp = g_strdup_printf ("%s_user_cert_chooser", prefix);
+	valid = validate_file_chooser (xml, tmp);
+	g_free (tmp);
+	if (!valid) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+		             NM_OPENVPN_KEY_CERT);
+		return FALSE;
+	}
+
+	tmp = g_strdup_printf ("%s_private_key_chooser", prefix);
+	valid = validate_file_chooser (xml, tmp);
+	g_free (tmp);
+	if (!valid) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+		             NM_OPENVPN_KEY_KEY);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+auth_widget_check_validity (GladeXML *xml, const char *contype, GError **error)
+{
+	GtkWidget *widget;
+	const char *str;
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS)) {
+		if (!validate_tls (xml, "tls", error))
+			return FALSE;
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		if (!validate_tls (xml, "pw_tls", error))
+			return FALSE;
+
+		widget = glade_xml_get_widget (xml, "pw_tls_username_entry");
+		str = gtk_entry_get_text (GTK_ENTRY (widget));
+		if (!str || !strlen (str)) {
+			g_set_error (error,
+			             OPENVPN_PLUGIN_UI_ERROR,
+			             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+			             NM_OPENVPN_KEY_USERNAME);
+			return FALSE;
+		}
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		if (!validate_file_chooser (xml, "pw_ca_cert_chooser")) {
+			g_set_error (error,
+			             OPENVPN_PLUGIN_UI_ERROR,
+			             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+			             NM_OPENVPN_KEY_CA);
+			return FALSE;
+		}
+		widget = glade_xml_get_widget (xml, "pw_username_entry");
+		str = gtk_entry_get_text (GTK_ENTRY (widget));
+		if (!str || !strlen (str)) {
+			g_set_error (error,
+			             OPENVPN_PLUGIN_UI_ERROR,
+			             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+			             NM_OPENVPN_KEY_USERNAME);
+			return FALSE;
+		}
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
+		if (!validate_file_chooser (xml, "sk_key_chooser")) {
+			g_set_error (error,
+			             OPENVPN_PLUGIN_UI_ERROR,
+			             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+			             NM_OPENVPN_KEY_STATIC_KEY);
+			return FALSE;
+		}
+
+		widget = glade_xml_get_widget (xml, "sk_local_address_entry");
+		str = gtk_entry_get_text (GTK_ENTRY (widget));
+		if (!str || !strlen (str)) {
+			g_set_error (error,
+			             OPENVPN_PLUGIN_UI_ERROR,
+			             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+			             NM_OPENVPN_KEY_LOCAL_IP);
+			return FALSE;
+		}
+	} else
+		g_assert_not_reached ();
+
+	return TRUE;
+}
+
+static void
+update_from_filechooser (GladeXML *xml,
+                         const char *key,
+                         const char *prefix,
+                         const char *widget_name,
+                         NMSettingVPN *s_vpn)
+{
+	GtkWidget *widget;
+	char *tmp, *filename;
+
+	g_return_if_fail (xml != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (prefix != NULL);
+	g_return_if_fail (widget_name != NULL);
+	g_return_if_fail (s_vpn != NULL);
+
+	tmp = g_strdup_printf ("%s_%s", prefix, widget_name);
+	widget = glade_xml_get_widget (xml, tmp);
+	g_free (tmp);
+
+	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+	if (!filename)
+		return;
+
+	if (strlen (filename))
+		nm_setting_vpn_add_data_item (s_vpn, key, filename);
+	
+	g_free (filename);
+}
+
+static void
+update_tls (GladeXML *xml, const char *prefix, NMSettingVPN *s_vpn)
+{
+	update_from_filechooser (xml, NM_OPENVPN_KEY_CA, prefix, "ca_cert_chooser", s_vpn);
+	update_from_filechooser (xml, NM_OPENVPN_KEY_CERT, prefix, "user_cert_chooser", s_vpn);
+	update_from_filechooser (xml, NM_OPENVPN_KEY_KEY, prefix, "private_key_chooser", s_vpn);
+}
+
+static void
+update_username (GladeXML *xml, const char *prefix, NMSettingVPN *s_vpn)
+{
+	GtkWidget *widget;
+	char *tmp;
+	const char *str;
+
+	g_return_if_fail (xml != NULL);
+	g_return_if_fail (prefix != NULL);
+	g_return_if_fail (s_vpn != NULL);
+
+	tmp = g_strdup_printf ("%s_username_entry", prefix);
+	widget = glade_xml_get_widget (xml, tmp);
+	g_free (tmp);
+
+	str = gtk_entry_get_text (GTK_ENTRY (widget));
+	if (str && strlen (str))
+		nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME, str);
+}
+
+gboolean
+auth_widget_update_connection (GladeXML *xml,
+                               const char *contype,
+                               NMSettingVPN *s_vpn)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkWidget *widget;
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS)) {
+		update_tls (xml, "tls", s_vpn);
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		update_from_filechooser (xml, NM_OPENVPN_KEY_CA, "pw", "ca_cert_chooser", s_vpn);
+		update_username (xml, "pw", s_vpn);
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		update_tls (xml, "pw_tls", s_vpn);
+		update_username (xml, "pw_tls", s_vpn);
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
+		update_from_filechooser (xml, NM_OPENVPN_KEY_STATIC_KEY, "sk", "key_chooser", s_vpn);
+		widget = glade_xml_get_widget (xml, "sk_direction_combo");
+		g_assert (widget);
+		model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+		if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
+			int direction = -1;
+
+			gtk_tree_model_get (model, &iter, SK_DIR_COL_NUM, &direction, -1);
+			if (direction > -1) {
+				char *tmp = g_strdup_printf ("%d", direction);
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY_DIRECTION, tmp);
+				g_free (tmp);
+			}
+		}
+	} else
+		g_assert_not_reached ();
+
+	return TRUE;
+}
+
+static gboolean
+save_secret (GladeXML *xml,
+			 const char *widget_name,
+			 const char *vpn_uuid,
+			 const char *vpn_name,
+			 const char *secret_name)
+{
+	GtkWidget *w;
+	const char *secret;
+	GnomeKeyringResult result;
+	gboolean ret;
+
+	w = glade_xml_get_widget (xml, widget_name);
+	g_assert (w);
+	secret = gtk_entry_get_text (GTK_ENTRY (w));
+	if (secret && strlen (secret)) {
+		result = keyring_helpers_save_secret (vpn_uuid, vpn_name, NULL, secret_name, secret);
+		ret = result == GNOME_KEYRING_RESULT_OK;
+		if (!ret)
+			g_warning ("%s: failed to save user password to keyring.", __func__);
+	} else
+		ret = keyring_helpers_delete_secret (vpn_uuid, secret_name);
+
+	return ret;
+}
+
+gboolean
+auth_widget_save_secrets (GladeXML *xml,
+						  const char *contype,
+						  const char *uuid,
+						  const char *name)
+{
+	gboolean ret;
+
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS))
+		ret = save_secret (xml, "tls_private_key_password_entry", uuid, name, NM_OPENVPN_KEY_CERTPASS);
+	else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD))
+		ret = save_secret (xml, "pw_password_entry", uuid, name, NM_OPENVPN_KEY_PASSWORD);
+	else if (!strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		ret = save_secret (xml, "pw_tls_password_entry", uuid, name, NM_OPENVPN_KEY_PASSWORD);
+		ret = save_secret (xml, "pw_tls_private_key_password_entry", uuid, name, NM_OPENVPN_KEY_CERTPASS);
+	} else if (!strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY))
+		/* No secrets here */
+		ret = TRUE;
+	else
+		g_assert_not_reached ();
+
+	return ret;
+}
+
+static const char *
+find_tag (const char *tag, const char *buf, gsize len)
+{
+	gsize i, taglen;
+
+	taglen = strlen (tag);
+	if (len < taglen)
+		return NULL;
+
+	for (i = 0; i < len - taglen; i++) {
+		if (memcmp (buf + i, tag, taglen) == 0)
+			return buf + i;
+	}
+	return NULL;
+}
+
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+
+static gboolean
+tls_default_filter (const GtkFileFilterInfo *filter_info, gpointer data)
+{
+	char *contents = NULL, *p, *ext;
+	gsize bytes_read = 0;
+	gboolean show = FALSE;
+	struct stat statbuf;
+
+	if (!filter_info->filename)
+		return FALSE;
+
+	p = strrchr (filter_info->filename, '.');
+	if (!p)
+		return FALSE;
+
+	ext = g_ascii_strdown (p, -1);
+	if (!ext)
+		return FALSE;
+	if (strcmp (ext, ".pem") && strcmp (ext, ".crt") && strcmp (ext, ".key") && strcmp (ext, ".cer")) {
+		g_free (ext);
+		return FALSE;
+	}
+	g_free (ext);
+
+	/* Ignore files that are really large */
+	if (!stat (filter_info->filename, &statbuf)) {
+		if (statbuf.st_size > 500000)
+			return FALSE;
+	}
+
+	if (!g_file_get_contents (filter_info->filename, &contents, &bytes_read, NULL))
+		return FALSE;
+
+	if (bytes_read < 400)  /* needs to be lower? */
+		goto out;
+
+	/* Check for PEM signatures */
+	if (find_tag (pem_rsa_key_begin, (const char *) contents, bytes_read)) {
+		show = TRUE;
+		goto out;
+	}
+
+	if (find_tag (pem_dsa_key_begin, (const char *) contents, bytes_read)) {
+		show = TRUE;
+		goto out;
+	}
+
+	if (find_tag (pem_cert_begin, (const char *) contents, bytes_read)) {
+		show = TRUE;
+		goto out;
+	}
+
+out:
+	g_free (contents);
+	return show;
+}
+
+GtkFileFilter *
+tls_file_chooser_filter_new (void)
+{
+	GtkFileFilter *filter;
+
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, tls_default_filter, NULL, NULL);
+	gtk_file_filter_set_name (filter, _("PEM certificates (*.pem, *.crt, *.key, *.cer)"));
+	return filter;
+}
+
+
+static const char *sk_key_begin = "-----BEGIN OpenVPN Static key V1-----";
+
+static gboolean
+sk_default_filter (const GtkFileFilterInfo *filter_info, gpointer data)
+{
+	int fd;
+	unsigned char buffer[1024];
+	ssize_t bytes_read;
+	gboolean show = FALSE;
+	char *p;
+	char *ext;
+
+	if (!filter_info->filename)
+		return FALSE;
+
+	p = strrchr (filter_info->filename, '.');
+	if (!p)
+		return FALSE;
+
+	ext = g_ascii_strdown (p, -1);
+	if (!ext)
+		return FALSE;
+	if (strcmp (ext, ".key")) {
+		g_free (ext);
+		return FALSE;
+	}
+	g_free (ext);
+
+	fd = open (filter_info->filename, O_RDONLY);
+	if (fd < 0)
+		return FALSE;
+
+	bytes_read = read (fd, buffer, sizeof (buffer) - 1);
+	if (bytes_read < 400)  /* needs to be lower? */
+		goto out;
+	buffer[bytes_read] = '\0';
+
+	/* Check for PEM signatures */
+	if (find_tag (sk_key_begin, (const char *) buffer, bytes_read)) {
+		show = TRUE;
+		goto out;
+	}
+
+out:
+	close (fd);
+	return show;
+}
+
+GtkFileFilter *
+sk_file_chooser_filter_new (void)
+{
+	GtkFileFilter *filter;
+
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, sk_default_filter, NULL, NULL);
+	gtk_file_filter_set_name (filter, _("OpenVPN Static Keys (*.key)"));
+	return filter;
+}
+
+static const char *advanced_keys[] = {
+	NM_OPENVPN_KEY_PORT,
+	NM_OPENVPN_KEY_COMP_LZO,
+	NM_OPENVPN_KEY_TAP_DEV,
+	NM_OPENVPN_KEY_PROTO_TCP,
+	NM_OPENVPN_KEY_CIPHER,
+	NM_OPENVPN_KEY_TA_DIR,
+	NM_OPENVPN_KEY_TA,
+	NULL
+};
+
+static void
+copy_values (const char *key, const char *value, gpointer user_data)
+{
+	GHashTable *hash = (GHashTable *) user_data;
+	const char **i;
+
+	for (i = &advanced_keys[0]; *i; i++) {
+		if (strcmp (key, *i))
+			continue;
+
+		g_hash_table_insert (hash, g_strdup (key), g_strdup (value));
+	}
+}
+
+GHashTable *
+advanced_dialog_new_hash_from_connection (NMConnection *connection,
+                                          GError **error)
+{
+	GHashTable *hash;
+	NMSettingVPN *s_vpn;
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+	s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+	nm_setting_vpn_foreach_data_item (s_vpn, copy_values, hash);
+	return hash;
+}
+
+static void
+port_toggled_cb (GtkWidget *check, gpointer user_data)
+{
+	GladeXML *xml = (GladeXML *) user_data;
+	GtkWidget *widget;
+
+	widget = glade_xml_get_widget (xml, "port_spinbutton");
+	gtk_widget_set_sensitive (widget, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)));
+}
+
+static const char *
+nm_find_openvpn (void)
+{
+	static const char *openvpn_binary_paths[] = {
+		"/usr/sbin/openvpn",
+		"/sbin/openvpn",
+		NULL
+	};
+	const char  **openvpn_binary = openvpn_binary_paths;
+
+	while (*openvpn_binary != NULL) {
+		if (g_file_test (*openvpn_binary, G_FILE_TEST_EXISTS))
+			break;
+		openvpn_binary++;
+	}
+
+	return *openvpn_binary;
+}
+
+#define TLS_CIPHER_COL_NAME 0
+#define TLS_CIPHER_COL_DEFAULT 1
+
+static void
+populate_cipher_combo (GtkComboBox *box, const char *user_cipher)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+	const char *openvpn_binary = NULL;
+	gchar *tmp, **items, **item;
+	gboolean user_added = FALSE;
+	char *argv[3];
+	GError *error = NULL;
+	gboolean success, found_blank = FALSE;
+
+	openvpn_binary = nm_find_openvpn ();
+	if (!openvpn_binary)
+		return;
+
+	argv[0] = (char *) openvpn_binary;
+	argv[1] = "--show-ciphers";
+	argv[2] = NULL;
+
+	success = g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, &tmp, NULL, NULL, &error);
+	if (!success) {
+		g_warning ("%s: couldn't determine ciphers: %s", __func__, error->message);
+		g_error_free (error);
+		return;
+	}
+
+	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+	gtk_combo_box_set_model (box, GTK_TREE_MODEL (store));
+
+	/* Add default option which won't pass --cipher to openvpn */
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+	                    TLS_CIPHER_COL_NAME, _("Default"),
+	                    TLS_CIPHER_COL_DEFAULT, TRUE, -1);
+
+	items = g_strsplit (tmp, "\n", 0);
+	g_free (tmp);
+
+	for (item = items; *item; item++) {
+		char *space = strchr (*item, ' ');
+
+		/* Don't add anything until after the first blank line */
+		if (!found_blank) {
+			if (!strlen (*item))
+				found_blank = TRUE;
+			continue;
+		}
+
+		if (space)
+			*space = '\0';
+
+		if (strlen (*item)) {
+			gtk_list_store_append (store, &iter);
+			gtk_list_store_set (store, &iter,
+			                    TLS_CIPHER_COL_NAME, *item,
+			                    TLS_CIPHER_COL_DEFAULT, FALSE, -1);
+			if (user_cipher && !strcmp (*item, user_cipher)) {
+				gtk_combo_box_set_active_iter (box, &iter);
+				user_added = TRUE;
+			}
+		}
+	}
+
+	/* Add the user-specified cipher if it exists wasn't found by openvpn */
+	if (user_cipher && !user_added) {
+		gtk_list_store_insert (store, &iter, 1);
+		gtk_list_store_set (store, &iter,
+		                    TLS_CIPHER_COL_NAME, user_cipher,
+		                    TLS_CIPHER_COL_DEFAULT, FALSE -1);
+		gtk_combo_box_set_active_iter (box, &iter);
+	} else if (!user_added) {
+		gtk_combo_box_set_active (box, 0);
+	}
+
+	g_object_unref (G_OBJECT (store));
+	g_strfreev (items);
+}
+
+static void
+tls_auth_toggled_cb (GtkWidget *widget, gpointer user_data)
+{
+	GladeXML *xml = (GladeXML *) user_data;
+	gboolean use_auth = FALSE;
+
+	widget = glade_xml_get_widget (xml, "tls_auth_checkbutton");
+	use_auth = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+	widget = glade_xml_get_widget (xml, "tls_dir_help_label");
+	gtk_widget_set_sensitive (widget, use_auth);
+	widget = glade_xml_get_widget (xml, "direction_label");
+	gtk_widget_set_sensitive (widget, use_auth);
+	widget = glade_xml_get_widget (xml, "tls_auth_label");
+	gtk_widget_set_sensitive (widget, use_auth);
+	widget = glade_xml_get_widget (xml, "tls_auth_chooser");
+	gtk_widget_set_sensitive (widget, use_auth);
+	widget = glade_xml_get_widget (xml, "direction_combo");
+	gtk_widget_set_sensitive (widget, use_auth);
+}
+
+#define TA_DIR_COL_NAME 0
+#define TA_DIR_COL_NUM 1
+
+GtkWidget *
+advanced_dialog_new (GHashTable *hash, const char *contype)
+{
+	GladeXML *xml;
+	GtkWidget *dialog = NULL;
+	char *glade_file = NULL;
+	GtkWidget *widget;
+	const char *value;
+
+	g_return_val_if_fail (hash != NULL, NULL);
+
+	glade_file = g_strdup_printf ("%s/%s", GLADEDIR, "nm-openvpn-dialog.glade");
+	xml = glade_xml_new (glade_file, "openvpn-advanced-dialog", GETTEXT_PACKAGE);
+	if (xml == NULL)
+		goto out;
+
+	dialog = glade_xml_get_widget (xml, "openvpn-advanced-dialog");
+	if (!dialog) {
+		g_object_unref (G_OBJECT (xml));
+		goto out;
+	}
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+	g_object_set_data_full (G_OBJECT (dialog), "glade-xml",
+	                        xml, (GDestroyNotify) g_object_unref);
+	g_object_set_data (G_OBJECT (dialog), "connection-type", GINT_TO_POINTER (contype));
+
+	widget = glade_xml_get_widget (xml, "port_checkbutton");
+	g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (port_toggled_cb), xml);
+
+	value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_PORT);
+	if (value && strlen (value)) {
+		long int tmp;
+
+		errno = 0;
+		tmp = strtol (value, NULL, 10);
+		if (errno == 0 && tmp > 0 && tmp < 65536 && tmp != 1194) {
+			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+			widget = glade_xml_get_widget (xml, "port_spinbutton");
+			gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget),
+			                           (gdouble) tmp);
+		}
+		gtk_widget_set_sensitive (widget, TRUE);
+	} else {
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+
+		widget = glade_xml_get_widget (xml, "port_spinbutton");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 1194.0);
+		gtk_widget_set_sensitive (widget, FALSE);
+	}
+
+	value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_COMP_LZO);
+	if (value && !strcmp (value, "yes")) {
+		widget = glade_xml_get_widget (xml, "lzo_checkbutton");
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+	}
+
+	value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_PROTO_TCP);
+	if (value && !strcmp (value, "yes")) {
+		widget = glade_xml_get_widget (xml, "tcp_checkbutton");
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+	}
+
+	value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_TAP_DEV);
+	if (value && !strcmp (value, "yes")) {
+		widget = glade_xml_get_widget (xml, "tap_checkbutton");
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+	}
+
+	if (   !strcmp (contype, NM_OPENVPN_CONTYPE_TLS)
+	    || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
+	    || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		GtkListStore *store;
+		GtkTreeIter iter;
+		int direction = -1, active = -1;
+
+		widget = glade_xml_get_widget (xml, "cipher_combo");
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_CIPHER);
+		populate_cipher_combo (GTK_COMBO_BOX (widget), value);
+
+		widget = glade_xml_get_widget (xml, "tls_auth_checkbutton");
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_TA);
+		if (value && strlen (value))
+			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+		g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (tls_auth_toggled_cb), xml);
+		tls_auth_toggled_cb (widget, xml);
+
+		widget = glade_xml_get_widget (xml, "direction_combo");
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_TA_DIR);
+		if (value && strlen (value)) {
+			direction = (int) strtol (value, NULL, 10);
+			/* If direction is not 0 or 1, use no direction */
+			if (direction != 0 && direction != 1)
+				direction = -1;
+		}
+
+		store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, TA_DIR_COL_NAME, _("None"), TA_DIR_COL_NUM, -1, -1);
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, TA_DIR_COL_NAME, "0", TA_DIR_COL_NUM, 0, -1);
+		if (direction == 0)
+			active = 1;
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, TA_DIR_COL_NAME, "1", TA_DIR_COL_NUM, 1, -1);
+		if (direction == 1)
+			active = 2;
+
+		gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
+		g_object_unref (store);
+		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? 0 : active);
+
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_TA);
+		if (value && strlen (value)) {
+			widget = glade_xml_get_widget (xml, "tls_auth_chooser");
+			gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), value);
+		}
+	} else {
+		widget = glade_xml_get_widget (xml, "options_notebook");
+		gtk_notebook_remove_page (GTK_NOTEBOOK (widget), 1);
+	}
+
+out:
+	g_free (glade_file);
+	return dialog;
+}
+
+GHashTable *
+advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error)
+{
+	GHashTable *hash;
+	GtkWidget *widget;
+	GladeXML *xml;
+	const char *contype = NULL;
+
+	g_return_val_if_fail (dialog != NULL, NULL);
+	if (error)
+		g_return_val_if_fail (*error == NULL, NULL);
+
+	xml = g_object_get_data (G_OBJECT (dialog), "glade-xml");
+	g_return_val_if_fail (xml != NULL, NULL);
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+	widget = glade_xml_get_widget (xml, "port_checkbutton");
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+		int port;
+
+		widget = glade_xml_get_widget (xml, "port_spinbutton");
+		port = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_PORT), g_strdup_printf ("%d", port));
+	}
+
+	widget = glade_xml_get_widget (xml, "lzo_checkbutton");
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_COMP_LZO), g_strdup ("yes"));
+
+	widget = glade_xml_get_widget (xml, "tcp_checkbutton");
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_PROTO_TCP), g_strdup ("yes"));
+
+	widget = glade_xml_get_widget (xml, "tap_checkbutton");
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_TAP_DEV), g_strdup ("yes"));
+
+	contype = g_object_get_data (G_OBJECT (dialog), "connection-type");
+	if (!strcmp (contype, NM_OPENVPN_CONTYPE_TLS) || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		GtkTreeModel *model;
+		GtkTreeIter iter;
+
+		widget = glade_xml_get_widget (xml, "cipher_combo");
+		model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+		if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
+			char *cipher = NULL;
+			gboolean is_default = TRUE;
+
+			gtk_tree_model_get (model, &iter,
+			                    TLS_CIPHER_COL_NAME, &cipher,
+			                    TLS_CIPHER_COL_DEFAULT, &is_default, -1);
+			if (!is_default && cipher) {
+				g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_CIPHER), g_strdup (cipher));
+			}
+		}
+		
+		widget = glade_xml_get_widget (xml, "tls_auth_checkbutton");
+		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+			char *filename;
+
+			widget = glade_xml_get_widget (xml, "tls_auth_chooser");
+			filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+			if (filename && strlen (filename)) {
+				g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_TA), g_strdup (filename));
+			}
+			g_free (filename);
+
+			widget = glade_xml_get_widget (xml, "direction_combo");
+			model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+			if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
+				int direction = -1;
+
+				gtk_tree_model_get (model, &iter, TA_DIR_COL_NUM, &direction, -1);
+				if (direction >= 0) {
+					g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_TA_DIR),
+					                     g_strdup_printf ("%d", direction));					
+				}
+			}
+		}
+	}
+
+	return hash;
+}
+

Added: trunk/properties/auth-helpers.h
==============================================================================
--- (empty file)
+++ trunk/properties/auth-helpers.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ *
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef _AUTH_HELPERS_H_
+#define _AUTH_HELPERS_H_
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkfilefilter.h>
+#include <glade/glade.h>
+
+#include <nm-connection.h>
+#include <nm-setting-vpn.h>
+
+typedef void (*ChangedCallback) (GtkWidget *widget, gpointer user_data);
+
+void fill_vpn_passwords (GladeXML *xml,
+						 GtkSizeGroup *group,
+						 NMConnection *connection,
+						 const char *contype,
+						 ChangedCallback changed_cb,
+						 gpointer user_data);
+
+void tls_pw_init_auth_widget (GladeXML *xml,
+                              GtkSizeGroup *group,
+                              NMSettingVPN *s_vpn,
+                              const char *contype,
+                              const char *prefix,
+                              ChangedCallback changed_cb,
+                              gpointer user_data);
+
+void sk_init_auth_widget (GladeXML *xml,
+                          GtkSizeGroup *group,
+                          NMSettingVPN *s_vpn,
+                          ChangedCallback changed_cb,
+                          gpointer user_data);
+
+gboolean auth_widget_check_validity (GladeXML *xml, const char *contype, GError **error);
+
+gboolean auth_widget_update_connection (GladeXML *xml,
+                                        const char *contype,
+                                        NMSettingVPN *s_vpn);
+
+gboolean auth_widget_save_secrets (GladeXML *xml,
+								   const char *contype,
+								   const char *uuid,
+								   const char *name);
+
+GtkFileFilter *tls_file_chooser_filter_new (void);
+
+GtkFileFilter *sk_file_chooser_filter_new (void);
+
+GtkWidget *advanced_dialog_new (GHashTable *hash, const char *contype);
+
+GHashTable *advanced_dialog_new_hash_from_connection (NMConnection *connection, GError **error);
+
+GHashTable *advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error);
+
+#endif

Added: trunk/properties/import-export.c
==============================================================================
--- (empty file)
+++ trunk/properties/import-export.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,379 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ *
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <nm-setting-vpn.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+
+#include "import-export.h"
+#include "nm-openvpn.h"
+#include "../src/nm-openvpn-service.h"
+
+#define CLIENT_TAG "client"
+#define DEV_TAG "dev"
+#define PROTO_TAG "proto"
+#define REMOTE_TAG "remote"
+#define CA_TAG "ca"
+#define CERT_TAG "cert"
+#define KEY_TAG "key"
+#define CIPHER_TAG "cipher"
+#define COMP_TAG "comp-lzo"
+#define IFCONFIG_TAG "ifconfig"
+#define SECRET_TAG "secret"
+#define AUTH_USER_PASS_TAG "auth-user-pass"
+#define TLS_AUTH_TAG "tls-auth"
+
+static gboolean
+handle_path_item (const char *line,
+                  const char *tag,
+                  const char *key,
+                  NMSettingVPN *s_vpn,
+                  const char *path,
+                  char **leftover)
+{
+	char *tmp, *file, *unquoted, *p, *full_path = NULL;
+	gboolean quoted = FALSE;
+
+	if (leftover)
+		g_return_val_if_fail (*leftover == NULL, FALSE);
+
+	if (strncmp (line, tag, strlen (tag)))
+		return FALSE;
+
+	tmp = g_strdup (line + strlen (tag));
+	file = g_strstrip (tmp);
+	if (!strlen (file))
+		goto out;
+
+	/* If file isn't an absolute file name, add the default path */
+	if (!g_path_is_absolute (file)) {
+		full_path = g_build_filename (path, file, NULL);
+		file = full_path;
+	}
+
+	/* Simple unquote */
+	if ((file[0] == '"') || (file[0] == '\'')) {
+		quoted = TRUE;
+		file++;
+	}
+
+	/* Unquote stuff using openvpn unquoting rules */
+	unquoted = g_malloc0 (strlen (file) + 1);
+	for (p = unquoted; *file; file++, p++) {
+		if (quoted && ((*file == '"') || (*file == '\'')))
+			break;
+		else if (!quoted && isspace (*file))
+			break;
+
+		if (*file == '\\' && *(file+1) == '\\')
+			*p = *(++file);
+		else if (*file == '\\' && *(file+1) == '"')
+			*p = *(++file);
+		else if (*file == '\\' && *(file+1) == ' ')
+			*p = *(++file);
+		else
+			*p = *file;
+	}
+	if (leftover && *file)
+		*leftover = file + 1;
+
+	nm_setting_vpn_add_data_item (s_vpn, key, unquoted);
+	g_free (unquoted);
+
+out:
+	g_free (tmp);
+	if (full_path)
+		g_free (full_path);
+	return TRUE;
+}
+
+static char **
+get_args (const char *line)
+{
+	char **split, **sanitized, **tmp, **tmp2;
+
+	split = g_strsplit_set (line, " \t", 0);
+	sanitized = g_malloc0 (sizeof (char *) * (g_strv_length (split) + 1));
+
+	for (tmp = split, tmp2 = sanitized; *tmp; tmp++) {
+		if (strlen (*tmp))
+			*tmp2++ = g_strdup (*tmp);
+	}
+
+	g_strfreev (split);
+	return sanitized;
+}
+
+static void
+handle_direction (const char *tag, const char *key, char *leftover, NMSettingVPN *s_vpn)
+{
+	glong direction;
+
+	if (!leftover)
+		return;
+
+	leftover = g_strstrip (leftover);
+	if (!strlen (leftover))
+		return;
+
+	errno = 0;
+	direction = strtol (leftover, NULL, 10);
+	if (errno == 0) {
+		if (direction == 0)
+			nm_setting_vpn_add_data_item (s_vpn, key, "0");
+		else if (direction == 1)
+			nm_setting_vpn_add_data_item (s_vpn, key, "1");
+	} else
+		g_warning ("%s: unknown %s direction '%s'", __func__, tag, leftover);
+}
+
+NMConnection *
+do_import (const char *path, char **lines, GError **error)
+{
+	NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
+	NMSettingVPN *s_vpn;
+	char *last_dot;
+	char **line;
+	gboolean have_client = FALSE, have_remote = FALSE;
+	gboolean have_pass = FALSE, have_sk = FALSE;
+	const char *ctype = NULL;
+	char *basename;
+	char *default_path;
+
+	connection = nm_connection_new ();
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
+
+	g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_DBUS_SERVICE_OPENVPN, NULL);
+	
+	/* Get the default path for ca, cert, key file, these files maybe
+	 * in same path with the configuration file */
+	default_path = g_path_get_dirname (path);
+
+	basename = g_path_get_basename (path);
+	last_dot = strrchr (basename, '.');
+	if (last_dot)
+		*last_dot = '\0';
+	g_object_set (s_con, NM_SETTING_CONNECTION_ID, basename, NULL);
+	g_free (basename);
+
+	for (line = lines; *line; line++) {
+		char *comment, **items, *leftover = NULL;
+
+		if ((comment = strchr (*line, '#')))
+			*comment = '\0';
+		if ((comment = strchr (*line, ';')))
+			*comment = '\0';
+		if (!strlen (*line))
+			continue;
+
+		if (!strncmp (*line, CLIENT_TAG, strlen (CLIENT_TAG)))
+			have_client = TRUE;
+
+		if (!strncmp (*line, DEV_TAG, strlen (DEV_TAG))) {
+			if (strstr (*line, "tun")) {
+				/* ignore; default is tun */
+			} else if (strstr (*line, "tap")) {
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_TAP_DEV, "yes");
+			} else
+				g_warning ("%s: unknown dev option '%s'", __func__, *line);
+
+			continue;
+		}
+
+		if (!strncmp (*line, PROTO_TAG, strlen (PROTO_TAG))) {
+			if (strstr (*line, "udp")) {
+				/* ignore; udp is default */
+			} else if (strstr (*line, "tcp")) {
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_PROTO_TCP, "yes");
+			} else
+				g_warning ("%s: unknown proto option '%s'", __func__, *line);
+
+			continue;
+		}
+
+		if (!strncmp (*line, COMP_TAG, strlen (COMP_TAG))) {
+			nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_COMP_LZO, "yes");
+			continue;
+		}
+
+		if (!strncmp (*line, REMOTE_TAG, strlen (REMOTE_TAG))) {
+			items = get_args (*line + strlen (REMOTE_TAG));
+			if (!items)
+				continue;
+
+			if (g_strv_length (items) >= 1) {
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE, items[0]);
+				have_remote = TRUE;
+
+				if (g_strv_length (items) >= 2) {
+					glong port;
+
+					errno = 0;
+					port = strtol (items[1], NULL, 10);
+					if ((errno == 0) && (port > 0) && (port < 65536)) {
+						char *tmp = g_strdup_printf ("%d", (guint32) port);
+						nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_PORT, tmp);
+						g_free (tmp);
+					} else
+						g_warning ("%s: invalid remote port in option '%s'", __func__, *line);
+				}
+			}
+			g_strfreev (items);
+
+			if (!nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE))
+				g_warning ("%s: unknown remote option '%s'", __func__, *line);
+			continue;
+		}
+
+		if (handle_path_item (*line, CA_TAG, NM_OPENVPN_KEY_CA, s_vpn, default_path, NULL))
+			continue;
+
+		if (handle_path_item (*line, CERT_TAG, NM_OPENVPN_KEY_CERT, s_vpn, default_path, NULL))
+			continue;
+
+		if (handle_path_item (*line, KEY_TAG, NM_OPENVPN_KEY_KEY, s_vpn, default_path, NULL))
+			continue;
+
+		if (handle_path_item (*line, SECRET_TAG, NM_OPENVPN_KEY_STATIC_KEY,
+		                      s_vpn, default_path, &leftover)) {
+			handle_direction ("secret",
+			                  NM_OPENVPN_KEY_STATIC_KEY_DIRECTION,
+			                  leftover,
+			                  s_vpn);
+			continue;
+		}
+
+		if (handle_path_item (*line, TLS_AUTH_TAG, NM_OPENVPN_KEY_TA,
+		                      s_vpn, default_path, &leftover)) {
+			handle_direction ("tls-auth",
+			                  NM_OPENVPN_KEY_TA_DIR,
+			                  leftover,
+			                  s_vpn);
+			continue;
+		}
+
+		if (!strncmp (*line, CIPHER_TAG, strlen (CIPHER_TAG))) {
+			items = get_args (*line + strlen (CIPHER_TAG));
+			if (!items)
+				continue;
+
+			if (g_strv_length (items))
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_CIPHER, items[0]);
+
+			g_strfreev (items);
+			continue;
+		}
+
+		if (!strncmp (*line, IFCONFIG_TAG, strlen (IFCONFIG_TAG))) {
+			items = get_args (*line + strlen (IFCONFIG_TAG));
+			if (!items)
+				continue;
+
+			if (g_strv_length (items) == 2) {
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_LOCAL_IP, items[0]);
+				nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE_IP, items[1]);
+			} else
+				g_warning ("%s: unknown ifconfig option '%s'", __func__, *line);
+			g_strfreev (items);
+			continue;
+		}
+
+		if (!strncmp (*line, AUTH_USER_PASS_TAG, strlen (AUTH_USER_PASS_TAG)))
+			have_pass = TRUE;
+	}
+
+	if (nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY))
+		have_sk = TRUE;
+
+	if (!have_client && !have_sk) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN,
+		             "The file to import wasn't a valid OpenVPN client configuration.");
+		g_object_unref (connection);
+		connection = NULL;
+	} else if (!have_remote) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN,
+		             "The file to import wasn't a valid OpenVPN configure (no remote).");
+		g_object_unref (connection);
+		connection = NULL;
+	} else {
+		gboolean have_certs = FALSE, have_ca = FALSE;
+
+		if (nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA))
+			have_ca = TRUE;
+
+		if (   have_ca
+		    && nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT)
+		    && nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY))
+			have_certs = TRUE;
+
+		/* Determine connection type */
+		if (have_pass) {
+			if (have_certs)
+				ctype = NM_OPENVPN_CONTYPE_PASSWORD_TLS;
+			else if (have_ca)
+				ctype = NM_OPENVPN_CONTYPE_PASSWORD;
+		} else if (have_certs) {
+			ctype = NM_OPENVPN_CONTYPE_TLS;
+		} else if (have_sk)
+			ctype = NM_OPENVPN_CONTYPE_STATIC_KEY;
+
+		if (!ctype)
+			ctype = NM_OPENVPN_CONTYPE_TLS;
+
+		nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE, ctype);
+	}
+
+	g_free (default_path);
+
+	nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+	return connection;
+}
+
+gboolean
+do_export (const char *path, NMConnection *connection, GError **error)
+{
+	return FALSE;
+}
+
+

Added: trunk/properties/import-export.h
==============================================================================
--- (empty file)
+++ trunk/properties/import-export.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ *
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef _IMPORT_EXPORT_H_
+#define _IMPORT_EXPORT_H_
+
+#include <glib.h>
+#include <nm-connection.h>
+
+NMConnection *do_import (const char *path, char **lines, GError **error);
+
+gboolean do_export (const char *path, NMConnection *connection, GError **error);
+
+#endif

Added: trunk/properties/nm-openvpn-dialog.glade
==============================================================================
--- (empty file)
+++ trunk/properties/nm-openvpn-dialog.glade	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,1134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkWindow" id="openvpn-widget">
+    <property name="title" translatable="yes">window1</property>
+    <child>
+      <widget class="GtkVBox" id="openvpn-vbox">
+        <property name="visible">True</property>
+        <property name="border_width">12</property>
+        <property name="spacing">16</property>
+        <child>
+          <widget class="GtkVBox" id="vbox8">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <widget class="GtkLabel" id="label22">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">&lt;b&gt;General&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkAlignment" id="alignment8">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <widget class="GtkTable" id="table2">
+                    <property name="visible">True</property>
+                    <property name="n_rows">1</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment2">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <widget class="GtkEntry" id="gateway_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label23">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Gateway:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">gateway_entry</property>
+                      </widget>
+                      <packing>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVBox" id="vbox11">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <widget class="GtkLabel" id="label25">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">&lt;b&gt;Authentication&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkAlignment" id="alignment9">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <widget class="GtkTable" id="table3">
+                    <property name="visible">True</property>
+                    <property name="n_rows">3</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment3">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <widget class="GtkComboBox" id="auth_combo">
+                            <property name="visible">True</property>
+                            <property name="items" translatable="yes"> </property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label26">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Type:</property>
+                      </widget>
+                      <packing>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkNotebook" id="auth_notebook">
+                        <property name="visible">True</property>
+                        <property name="show_tabs">False</property>
+                        <property name="show_border">False</property>
+                        <child>
+                          <widget class="GtkTable" id="table1">
+                            <property name="visible">True</property>
+                            <property name="n_rows">4</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">6</property>
+                            <property name="row_spacing">6</property>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment5">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="tls_user_cert_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label3">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">User Certificate:</property>
+                              </widget>
+                              <packing>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label2">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">CA Certificate:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment4">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="tls_ca_cert_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label4">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Private Key:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment6">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0.019999999552965164</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="tls_private_key_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label29">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Private Key Password:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment22">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="tls_private_key_password_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="visibility">False</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label14">
+                            <property name="visible">True</property>
+                            <property name="label">page 1</property>
+                          </widget>
+                          <packing>
+                            <property name="type">tab</property>
+                            <property name="tab_fill">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkTable" id="table4">
+                            <property name="visible">True</property>
+                            <property name="n_rows">3</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">6</property>
+                            <property name="row_spacing">6</property>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment7">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="pw_username_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label5">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">User name:</property>
+                              </widget>
+                              <packing>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label7">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">CA Certificate:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label27">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Password:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment10">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="pw_ca_cert_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment20">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="pw_password_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="visibility">False</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label15">
+                            <property name="visible">True</property>
+                            <property name="label">page 2</property>
+                          </widget>
+                          <packing>
+                            <property name="type">tab</property>
+                            <property name="position">1</property>
+                            <property name="tab_fill">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkTable" id="table5">
+                            <property name="visible">True</property>
+                            <property name="n_rows">6</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">6</property>
+                            <property name="row_spacing">6</property>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment11">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="pw_tls_username_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label10">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">User name:</property>
+                              </widget>
+                              <packing>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label6">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">CA Certificate:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label8">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">User Certificate:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment13">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="pw_tls_ca_cert_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment14">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="pw_tls_user_cert_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment21">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="pw_tls_password_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="visibility">False</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label28">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Password:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label9">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Private Key:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment12">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="pw_tls_private_key_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label30">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Private Key Password:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment23">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="pw_tls_private_key_password_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="visibility">False</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label16">
+                            <property name="visible">True</property>
+                            <property name="label">page 3</property>
+                          </widget>
+                          <packing>
+                            <property name="type">tab</property>
+                            <property name="position">2</property>
+                            <property name="tab_fill">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkTable" id="table6">
+                            <property name="visible">True</property>
+                            <property name="n_rows">4</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">6</property>
+                            <property name="row_spacing">6</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment17">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkLabel" id="sk_dir_help_label">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">&lt;i&gt;If key direction is used, it must be the opposite of that used on the VPN peer.  For example, if the peer uses '1', this connection must use '0'.  If you are unsure what value to use, contact your system administrator.&lt;/i&gt;</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="wrap">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment16">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkComboBox" id="sk_direction_combo">
+                                    <property name="visible">True</property>
+                                    <property name="items" translatable="yes"> </property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment15">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkFileChooserButton" id="sk_key_chooser">
+                                    <property name="visible">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label11">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Static Key:</property>
+                              </widget>
+                              <packing>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label12">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Key Direction:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="label20">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Local IP Address:</property>
+                              </widget>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkAlignment" id="alignment18">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="xscale">0</property>
+                                <child>
+                                  <widget class="GtkEntry" id="sk_local_address_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                  </widget>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                                <property name="y_options"></property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label17">
+                            <property name="visible">True</property>
+                            <property name="label">page 4</property>
+                          </widget>
+                          <packing>
+                            <property name="type">tab</property>
+                            <property name="position">3</property>
+                            <property name="tab_fill">False</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="show_passwords">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="label" translatable="yes">Show passwords</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="xalign">1</property>
+            <property name="xscale">0</property>
+            <child>
+              <widget class="GtkButton" id="advanced_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="response_id">0</property>
+                <child>
+                  <widget class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkImage" id="image1">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-preferences</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Ad_vanced...</property>
+                        <property name="use_markup">True</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="openvpn-advanced-dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">OpenVPN Advanced Options</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="icon_name">stock-preferences</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_pager_hint">True</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkNotebook" id="options_notebook">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <widget class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="spacing">6</property>
+                <child>
+                  <widget class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkCheckButton" id="port_checkbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="label" translatable="yes">Use custom gateway p_ort:</property>
+                        <property name="use_underline">True</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkSpinButton" id="port_spinbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="adjustment">1194 1 65535 1 10 10</property>
+                        <property name="climb_rate">1</property>
+                        <property name="numeric">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="lzo_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Use L_ZO data compression</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="tcp_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Use a _TCP connection</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="tap_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Use a TA_P device</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label13">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">General</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="table7">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="n_rows">3</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">12</property>
+                <property name="row_spacing">6</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <widget class="GtkTable" id="table8">
+                    <property name="visible">True</property>
+                    <property name="n_rows">3</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">12</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="tls_auth_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Key File:</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkComboBox" id="direction_combo">
+                        <property name="visible">True</property>
+                        <property name="items" translatable="yes"> </property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkFileChooserButton" id="tls_auth_chooser">
+                        <property name="visible">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment19">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <widget class="GtkLabel" id="tls_dir_help_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">&lt;i&gt;If key direction is used, it must be the opposite of that used on the VPN peer.  For example, if the peer uses '1', this connection must use '0'.  If you are unsure what value to use, contact your system administrator.&lt;/i&gt;</property>
+                            <property name="use_markup">True</property>
+                            <property name="wrap">True</property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="direction_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Key Direction:</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="tls_auth_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Use additional TLS authentication</property>
+                    <property name="use_underline">True</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label19">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Cipher:</property>
+                  </widget>
+                  <packing>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="cipher_combo">
+                    <property name="visible">True</property>
+                    <property name="items" translatable="yes"> </property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label18">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Certificates (TLS)</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="ok_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/properties/nm-openvpn.c
==============================================================================
--- (empty file)
+++ trunk/properties/nm-openvpn.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,766 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ * CVSID: $Id: nm-openvpn.c 4232 2008-10-29 09:13:40Z tambeti $
+ *
+ * nm-openvpn.c : GNOME UI dialogs for configuring openvpn VPN connections
+ *
+ * Copyright (C) 2005 Tim Niemueller <tim niemueller de>
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ * Based on work by David Zeuthen, <davidz redhat com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#define NM_VPN_API_SUBJECT_TO_CHANGE
+
+#include <nm-vpn-plugin-ui-interface.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+
+#include "common-gnome/keyring-helpers.h"
+#include "src/nm-openvpn-service.h"
+#include "nm-openvpn.h"
+#include "auth-helpers.h"
+#include "import-export.h"
+
+#define OPENVPN_PLUGIN_NAME    _("OpenVPN")
+#define OPENVPN_PLUGIN_DESC    _("Compatible with the OpenVPN server.")
+#define OPENVPN_PLUGIN_SERVICE NM_DBUS_SERVICE_OPENVPN 
+
+
+/************** plugin class **************/
+
+static void openvpn_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (OpenvpnPluginUi, openvpn_plugin_ui, G_TYPE_OBJECT, 0,
+						G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_INTERFACE,
+											   openvpn_plugin_ui_interface_init))
+
+/************** UI widget class **************/
+
+static void openvpn_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (OpenvpnPluginUiWidget, openvpn_plugin_ui_widget, G_TYPE_OBJECT, 0,
+						G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE,
+											   openvpn_plugin_ui_widget_interface_init))
+
+#define OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), OPENVPN_TYPE_PLUGIN_UI_WIDGET, OpenvpnPluginUiWidgetPrivate))
+
+typedef struct {
+	GladeXML *xml;
+	GtkWidget *widget;
+	GtkSizeGroup *group;
+	GtkWindowGroup *window_group;
+	gboolean window_added;
+	GHashTable *advanced;
+} OpenvpnPluginUiWidgetPrivate;
+
+
+#define COL_AUTH_NAME 0
+#define COL_AUTH_PAGE 1
+#define COL_AUTH_TYPE 2
+
+GQuark
+openvpn_plugin_ui_error_quark (void)
+{
+	static GQuark error_quark = 0;
+
+	if (G_UNLIKELY (error_quark == 0))
+		error_quark = g_quark_from_static_string ("openvpn-plugin-ui-error-quark");
+
+	return error_quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+openvpn_plugin_ui_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_UNKNOWN, "UnknownError"),
+			/* The connection was missing invalid. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_INVALID_CONNECTION, "InvalidConnection"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The file to import could not be read. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_READABLE, "FileNotReadable"),
+			/* The file to import could was not an OpenVPN client file. */
+			ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN, "FileNotOpenVPN"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("OpenvpnPluginUiError", values);
+	}
+	return etype;
+}
+
+static gboolean
+check_validity (OpenvpnPluginUiWidget *self, GError **error)
+{
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	GtkWidget *widget;
+	const char *str;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	const char *contype = NULL;
+
+	widget = glade_xml_get_widget (priv->xml, "gateway_entry");
+	str = gtk_entry_get_text (GTK_ENTRY (widget));
+	if (!str || !strlen (str)) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+		             NM_OPENVPN_KEY_REMOTE);
+		return FALSE;
+	}
+
+	widget = glade_xml_get_widget (priv->xml, "auth_combo");
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+	g_assert (model);
+	g_assert (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter));
+
+	gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &contype, -1);
+	if (!auth_widget_check_validity (priv->xml, contype, error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+stuff_changed_cb (GtkWidget *widget, gpointer user_data)
+{
+	g_signal_emit_by_name (OPENVPN_PLUGIN_UI_WIDGET (user_data), "changed");
+}
+
+static void
+auth_combo_changed_cb (GtkWidget *combo, gpointer user_data)
+{
+	OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	GtkWidget *auth_notebook;
+	GtkWidget *show_passwords;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gint new_page = 0;
+
+	auth_notebook = glade_xml_get_widget (priv->xml, "auth_notebook");
+	g_assert (auth_notebook);
+	show_passwords = glade_xml_get_widget (priv->xml, "show_passwords");
+	g_assert (auth_notebook);
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+	g_assert (model);
+	g_assert (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter));
+
+	gtk_tree_model_get (model, &iter, COL_AUTH_PAGE, &new_page, -1);
+
+	/* Static key page doesn't have any passwords */
+	gtk_widget_set_sensitive (show_passwords, new_page != 3);
+
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (auth_notebook), new_page);
+
+	stuff_changed_cb (combo, self);
+}
+
+static void
+advanced_dialog_close_cb (GtkWidget *dialog, gpointer user_data)
+{
+	gtk_widget_hide (dialog);
+	/* gtk_widget_destroy() will remove the window from the window group */
+	gtk_widget_destroy (dialog);
+}
+
+static void
+advanced_dialog_response_cb (GtkWidget *dialog, gint response, gpointer user_data)
+{
+	OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	if (response != GTK_RESPONSE_OK) {
+		advanced_dialog_close_cb (dialog, self);
+		return;
+	}
+
+	if (priv->advanced)
+		g_hash_table_destroy (priv->advanced);
+	priv->advanced = advanced_dialog_new_hash_from_dialog (dialog, &error);
+	if (!priv->advanced) {
+		g_message ("%s: error reading advanced settings: %s", __func__, error->message);
+		g_error_free (error);
+	}
+	advanced_dialog_close_cb (dialog, self);
+
+	stuff_changed_cb (NULL, self);
+}
+
+static void
+advanced_button_clicked_cb (GtkWidget *button, gpointer user_data)
+{
+	OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	GtkWidget *dialog, *toplevel, *widget;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	const char *contype = NULL;
+
+	toplevel = gtk_widget_get_toplevel (priv->widget);
+	g_return_if_fail (GTK_WIDGET_TOPLEVEL (toplevel));
+
+	widget = glade_xml_get_widget (priv->xml, "auth_combo");
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
+		gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &contype, -1);
+
+	dialog = advanced_dialog_new (priv->advanced, contype);
+	if (!dialog) {
+		g_warning ("%s: failed to create the Advanced dialog!", __func__);
+		return;
+	}
+
+	gtk_window_group_add_window (priv->window_group, GTK_WINDOW (dialog));
+	if (!priv->window_added) {
+		gtk_window_group_add_window (priv->window_group, GTK_WINDOW (toplevel));
+		priv->window_added = TRUE;
+	}
+
+	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
+	g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (advanced_dialog_response_cb), self);
+	g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (advanced_dialog_close_cb), self);
+
+	gtk_widget_show_all (dialog);
+}
+
+static gboolean
+init_plugin_ui (OpenvpnPluginUiWidget *self, NMConnection *connection, GError **error)
+{
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	NMSettingVPN *s_vpn;
+	GtkWidget *widget;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	int active = -1;
+	const char *value;
+	const char *contype = NM_OPENVPN_CONTYPE_TLS;
+
+	s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+
+	priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+	widget = glade_xml_get_widget (priv->xml, "gateway_entry");
+	if (!widget)
+		return FALSE;
+	gtk_size_group_add_widget (priv->group, widget);
+	if (s_vpn) {
+		value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE);
+		if (value)
+			gtk_entry_set_text (GTK_ENTRY (widget), value);
+	}
+	g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
+
+	widget = glade_xml_get_widget (priv->xml, "auth_combo");
+	if (!widget)
+		return FALSE;
+	gtk_size_group_add_widget (priv->group, widget);
+
+	store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
+
+	if (s_vpn) {
+		contype = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+		if (contype) {
+			if (   strcmp (contype, NM_OPENVPN_CONTYPE_TLS)
+			    && strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY)
+			    && strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)
+			    && strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
+				contype = NM_OPENVPN_CONTYPE_TLS;
+		} else
+			contype = NM_OPENVPN_CONTYPE_TLS;
+	}
+
+	/* TLS auth widget */
+	tls_pw_init_auth_widget (priv->xml, priv->group, s_vpn,
+	                         NM_OPENVPN_CONTYPE_TLS, "tls",
+	                         stuff_changed_cb, self);
+	fill_vpn_passwords (priv->xml, priv->group, connection,
+						NM_OPENVPN_CONTYPE_TLS, stuff_changed_cb, self);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+	                    COL_AUTH_NAME, _("Certificates (TLS)"),
+	                    COL_AUTH_PAGE, 0,
+	                    COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_TLS,
+	                    -1);
+
+	/* Password auth widget */
+	tls_pw_init_auth_widget (priv->xml, priv->group, s_vpn,
+	                         NM_OPENVPN_CONTYPE_PASSWORD, "pw",
+	                         stuff_changed_cb, self);
+	fill_vpn_passwords (priv->xml, priv->group, connection,
+						NM_OPENVPN_CONTYPE_PASSWORD, stuff_changed_cb, self);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+	                    COL_AUTH_NAME, _("Password"),
+	                    COL_AUTH_PAGE, 1,
+	                    COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_PASSWORD,
+	                    -1);
+	if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD))
+		active = 1;
+
+	/* Password+TLS auth widget */
+	tls_pw_init_auth_widget (priv->xml, priv->group, s_vpn,
+	                         NM_OPENVPN_CONTYPE_PASSWORD_TLS, "pw_tls",
+	                         stuff_changed_cb, self);
+	fill_vpn_passwords (priv->xml, priv->group, connection,
+						NM_OPENVPN_CONTYPE_PASSWORD_TLS, stuff_changed_cb, self);
+
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+	                    COL_AUTH_NAME, _("Password with Certificates (TLS)"),
+	                    COL_AUTH_PAGE, 2,
+	                    COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_PASSWORD_TLS,
+	                    -1);
+	if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
+		active = 2;
+
+	/* Static key auth widget */
+	sk_init_auth_widget (priv->xml, priv->group, s_vpn, stuff_changed_cb, self);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+	                    COL_AUTH_NAME, _("Static Key"),
+	                    COL_AUTH_PAGE, 3,
+	                    COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_STATIC_KEY,
+	                    -1);
+	if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY))
+		active = 3;
+
+	gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
+	g_object_unref (store);
+	g_signal_connect (widget, "changed", G_CALLBACK (auth_combo_changed_cb), self);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? 0 : active);
+
+	widget = glade_xml_get_widget (priv->xml, "advanced_button");
+	g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (advanced_button_clicked_cb), self);
+
+	return TRUE;
+}
+
+static GObject *
+get_widget (NMVpnPluginUiWidgetInterface *iface)
+{
+	OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (iface);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+
+	return G_OBJECT (priv->widget);
+}
+
+static void
+hash_copy_advanced (gpointer key, gpointer data, gpointer user_data)
+{
+	NMSettingVPN *s_vpn = NM_SETTING_VPN (user_data);
+	const char *value = (const char *) data;
+
+	nm_setting_vpn_add_data_item (s_vpn, (const char *) key, value);
+}
+
+static const char *
+get_auth_type (GladeXML *glade_xml)
+{
+	GtkComboBox *combo;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	const char *auth_type = NULL;
+
+	combo = GTK_COMBO_BOX (glade_xml_get_widget (glade_xml, "auth_combo"));
+	model = gtk_combo_box_get_model (combo);
+	if (gtk_combo_box_get_active_iter (combo, &iter))
+		gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &auth_type, -1);
+
+	return auth_type;
+}
+
+static gboolean
+update_connection (NMVpnPluginUiWidgetInterface *iface,
+                   NMConnection *connection,
+                   GError **error)
+{
+	OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (iface);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
+	NMSettingVPN *s_vpn;
+	GtkWidget *widget;
+	char *str;
+	const char *auth_type;
+	gboolean valid = FALSE;
+
+	if (!check_validity (self, error))
+		return FALSE;
+
+	s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
+	g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_DBUS_SERVICE_OPENVPN, NULL);
+
+	/* Gateway */
+	widget = glade_xml_get_widget (priv->xml, "gateway_entry");
+	str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+	if (str && strlen (str))
+		nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE, str);
+
+	auth_type = get_auth_type (priv->xml);
+	if (auth_type) {
+		nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE, auth_type);
+		auth_widget_update_connection (priv->xml, auth_type, s_vpn);
+	}
+
+	if (priv->advanced)
+		g_hash_table_foreach (priv->advanced, hash_copy_advanced, s_vpn);
+
+	nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+	valid = TRUE;
+
+	return valid;
+}
+
+static gboolean
+save_secrets (NMVpnPluginUiWidgetInterface *iface,
+              NMConnection *connection,
+              GError **error)
+{
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (iface);
+	NMSettingConnection *s_con;
+	const char *auth_type, *uuid, *id;
+	gboolean ret = FALSE;
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	if (!s_con) {
+		g_set_error (error,
+					 OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_CONNECTION,
+		             "%s", "missing 'connection' setting");
+		return FALSE;
+	}
+
+	id = nm_setting_connection_get_id (s_con);
+	uuid = nm_setting_connection_get_uuid (s_con);
+
+	auth_type = get_auth_type (priv->xml);
+	if (auth_type)
+		ret = auth_widget_save_secrets (priv->xml, auth_type, uuid, id);
+
+	if (!ret)
+		g_set_error (error, OPENVPN_PLUGIN_UI_ERROR,
+					 OPENVPN_PLUGIN_UI_ERROR_UNKNOWN,
+					 "%s", "Saving secrets to gnome keyring failed.");
+	return ret;
+}
+
+static NMVpnPluginUiWidgetInterface *
+nm_vpn_plugin_ui_widget_interface_new (NMConnection *connection, GError **error)
+{
+	NMVpnPluginUiWidgetInterface *object;
+	OpenvpnPluginUiWidgetPrivate *priv;
+	char *glade_file;
+
+	if (error)
+		g_return_val_if_fail (*error == NULL, NULL);
+
+	object = NM_VPN_PLUGIN_UI_WIDGET_INTERFACE (g_object_new (OPENVPN_TYPE_PLUGIN_UI_WIDGET, NULL));
+	if (!object) {
+		g_set_error (error, OPENVPN_PLUGIN_UI_ERROR, 0, "could not create openvpn object");
+		return NULL;
+	}
+
+	priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (object);
+
+	glade_file = g_strdup_printf ("%s/%s", GLADEDIR, "nm-openvpn-dialog.glade");
+	priv->xml = glade_xml_new (glade_file, "openvpn-vbox", GETTEXT_PACKAGE);
+	if (priv->xml == NULL) {
+		g_set_error (error, OPENVPN_PLUGIN_UI_ERROR, 0,
+		             "could not load required resources at %s", glade_file);
+		g_free (glade_file);
+		g_object_unref (object);
+		return NULL;
+	}
+	g_free (glade_file);
+
+	priv->widget = glade_xml_get_widget (priv->xml, "openvpn-vbox");
+	if (!priv->widget) {
+		g_set_error (error, OPENVPN_PLUGIN_UI_ERROR, 0, "could not load UI widget");
+		g_object_unref (object);
+		return NULL;
+	}
+	g_object_ref_sink (priv->widget);
+
+	priv->window_group = gtk_window_group_new ();
+
+	if (!init_plugin_ui (OPENVPN_PLUGIN_UI_WIDGET (object), connection, error)) {
+		g_object_unref (object);
+		return NULL;
+	}
+
+	priv->advanced = advanced_dialog_new_hash_from_connection (connection, error);
+	if (!priv->advanced) {
+		g_object_unref (object);
+		return NULL;
+	}
+
+	return object;
+}
+
+static void
+dispose (GObject *object)
+{
+	OpenvpnPluginUiWidget *plugin = OPENVPN_PLUGIN_UI_WIDGET (object);
+	OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (plugin);
+
+	if (priv->group)
+		g_object_unref (priv->group);
+
+	if (priv->window_group)
+		g_object_unref (priv->window_group);
+
+	if (priv->widget)
+		g_object_unref (priv->widget);
+
+	if (priv->xml)
+		g_object_unref (priv->xml);
+
+	if (priv->advanced)
+		g_hash_table_destroy (priv->advanced);
+
+	G_OBJECT_CLASS (openvpn_plugin_ui_widget_parent_class)->dispose (object);
+}
+
+static void
+openvpn_plugin_ui_widget_class_init (OpenvpnPluginUiWidgetClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	g_type_class_add_private (req_class, sizeof (OpenvpnPluginUiWidgetPrivate));
+
+	object_class->dispose = dispose;
+}
+
+static void
+openvpn_plugin_ui_widget_init (OpenvpnPluginUiWidget *plugin)
+{
+}
+
+static void
+openvpn_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class)
+{
+	/* interface implementation */
+	iface_class->get_widget = get_widget;
+	iface_class->update_connection = update_connection;
+	iface_class->save_secrets = save_secrets;
+}
+
+static NMConnection *
+import (NMVpnPluginUiInterface *iface, const char *path, GError **error)
+{
+	NMConnection *connection = NULL;
+	char *contents = NULL;
+	char **lines = NULL;
+	char *ext;
+
+	ext = strrchr (path, '.');
+	if (!ext) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN,
+		             "unknown OpenVPN file extension");
+		goto out;
+	}
+
+	if (strcmp (ext, ".ovpn") && strcmp (ext, ".conf") && strcmp (ext, ".cnf")) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN,
+		             "unknown OpenVPN file extension");
+		goto out;
+	}
+
+	if (!g_file_get_contents (path, &contents, NULL, error))
+		return NULL;
+
+	lines = g_strsplit_set (contents, "\r\n", 0);
+	if (g_strv_length (lines) <= 1) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_READABLE,
+		             "not a valid OpenVPN configuration file");
+		goto out;
+	}
+
+	connection = do_import (path, lines, error);
+
+out:
+	if (lines)
+		g_strfreev (lines);
+	g_free (contents);
+	return connection;
+}
+
+static gboolean
+export (NMVpnPluginUiInterface *iface,
+        const char *path,
+        NMConnection *connection,
+        GError **error)
+{
+	return do_export (path, connection, error);
+}
+
+static char *
+get_suggested_name (NMVpnPluginUiInterface *iface, NMConnection *connection)
+{
+	NMSettingConnection *s_con;
+	const char *id;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_return_val_if_fail (s_con != NULL, NULL);
+
+	id = nm_setting_connection_get_id (s_con);
+	g_return_val_if_fail (id != NULL, NULL);
+
+	return g_strdup_printf ("%s (openvpn).conf", id);
+}
+
+static guint32
+get_capabilities (NMVpnPluginUiInterface *iface)
+{
+	return (NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT | NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT);
+}
+
+static gboolean
+delete_connection (NMVpnPluginUiInterface *iface,
+                   NMConnection *connection,
+                   GError **error)
+{
+	NMSettingConnection *s_con;
+	const char *uuid;
+
+	/* Remove any secrets in the keyring associated with this connection's UUID */
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	if (!s_con) {
+		g_set_error (error,
+		             OPENVPN_PLUGIN_UI_ERROR,
+		             OPENVPN_PLUGIN_UI_ERROR_INVALID_CONNECTION,
+		             "missing 'connection' setting");
+		return FALSE;
+	}
+
+	uuid = nm_setting_connection_get_uuid (s_con);
+	keyring_helpers_delete_secret (uuid, NM_OPENVPN_KEY_PASSWORD);
+	keyring_helpers_delete_secret (uuid, NM_OPENVPN_KEY_CERTPASS);
+
+	return TRUE;
+}
+
+static NMVpnPluginUiWidgetInterface *
+ui_factory (NMVpnPluginUiInterface *iface, NMConnection *connection, GError **error)
+{
+	return nm_vpn_plugin_ui_widget_interface_new (connection, error);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME:
+		g_value_set_string (value, OPENVPN_PLUGIN_NAME);
+		break;
+	case NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC:
+		g_value_set_string (value, OPENVPN_PLUGIN_DESC);
+		break;
+	case NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE:
+		g_value_set_string (value, OPENVPN_PLUGIN_SERVICE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+openvpn_plugin_ui_class_init (OpenvpnPluginUiClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	object_class->get_property = get_property;
+
+	g_object_class_override_property (object_class,
+									  NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME,
+									  NM_VPN_PLUGIN_UI_INTERFACE_NAME);
+
+	g_object_class_override_property (object_class,
+									  NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC,
+									  NM_VPN_PLUGIN_UI_INTERFACE_DESC);
+
+	g_object_class_override_property (object_class,
+									  NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE,
+									  NM_VPN_PLUGIN_UI_INTERFACE_SERVICE);
+}
+
+static void
+openvpn_plugin_ui_init (OpenvpnPluginUi *plugin)
+{
+}
+
+static void
+openvpn_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class)
+{
+	/* interface implementation */
+	iface_class->ui_factory = ui_factory;
+	iface_class->get_capabilities = get_capabilities;
+	iface_class->import = import;
+	iface_class->export = export;
+	iface_class->get_suggested_name = get_suggested_name;
+	iface_class->delete_connection = delete_connection;
+}
+
+
+G_MODULE_EXPORT NMVpnPluginUiInterface *
+nm_vpn_plugin_ui_factory (GError **error)
+{
+	if (error)
+		g_return_val_if_fail (*error == NULL, NULL);
+
+	return NM_VPN_PLUGIN_UI_INTERFACE (g_object_new (OPENVPN_TYPE_PLUGIN_UI, NULL));
+}
+

Added: trunk/properties/nm-openvpn.h
==============================================================================
--- (empty file)
+++ trunk/properties/nm-openvpn.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/***************************************************************************
+ * nm-openvpn.h : GNOME UI dialogs for configuring openvpn VPN connections
+ *
+ * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef _NM_OPENVPN_H_
+#define _NM_OPENVPN_H_
+
+#include <glib-object.h>
+
+typedef enum
+{
+	OPENVPN_PLUGIN_UI_ERROR_UNKNOWN = 0,
+	OPENVPN_PLUGIN_UI_ERROR_INVALID_CONNECTION,
+	OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
+	OPENVPN_PLUGIN_UI_ERROR_MISSING_PROPERTY,
+	OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_READABLE,
+	OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN
+} OpenvpnPluginUiError;
+
+#define OPENVPN_TYPE_PLUGIN_UI_ERROR (openvpn_plugin_ui_error_get_type ()) 
+GType openvpn_plugin_ui_error_get_type (void);
+
+#define OPENVPN_PLUGIN_UI_ERROR (openvpn_plugin_ui_error_quark ())
+GQuark openvpn_plugin_ui_error_quark (void);
+
+
+#define OPENVPN_TYPE_PLUGIN_UI            (openvpn_plugin_ui_get_type ())
+#define OPENVPN_PLUGIN_UI(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), OPENVPN_TYPE_PLUGIN_UI, OpenvpnPluginUi))
+#define OPENVPN_PLUGIN_UI_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), OPENVPN_TYPE_PLUGIN_UI, OpenvpnPluginUiClass))
+#define OPENVPN_IS_PLUGIN_UI(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OPENVPN_TYPE_PLUGIN_UI))
+#define OPENVPN_IS_PLUGIN_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), OPENVPN_TYPE_PLUGIN_UI))
+#define OPENVPN_PLUGIN_UI_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), OPENVPN_TYPE_PLUGIN_UI, OpenvpnPluginUiClass))
+
+typedef struct _OpenvpnPluginUi OpenvpnPluginUi;
+typedef struct _OpenvpnPluginUiClass OpenvpnPluginUiClass;
+
+struct _OpenvpnPluginUi {
+	GObject parent;
+};
+
+struct _OpenvpnPluginUiClass {
+	GObjectClass parent;
+};
+
+GType openvpn_plugin_ui_get_type (void);
+
+
+#define OPENVPN_TYPE_PLUGIN_UI_WIDGET            (openvpn_plugin_ui_widget_get_type ())
+#define OPENVPN_PLUGIN_UI_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), OPENVPN_TYPE_PLUGIN_UI_WIDGET, OpenvpnPluginUiWidget))
+#define OPENVPN_PLUGIN_UI_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), OPENVPN_TYPE_PLUGIN_UI_WIDGET, OpenvpnPluginUiWidgetClass))
+#define OPENVPN_IS_PLUGIN_UI_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OPENVPN_TYPE_PLUGIN_UI_WIDGET))
+#define OPENVPN_IS_PLUGIN_UI_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), OPENVPN_TYPE_PLUGIN_UI_WIDGET))
+#define OPENVPN_PLUGIN_UI_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), OPENVPN_TYPE_PLUGIN_UI_WIDGET, OpenvpnPluginUiWidgetClass))
+
+typedef struct _OpenvpnPluginUiWidget OpenvpnPluginUiWidget;
+typedef struct _OpenvpnPluginUiWidgetClass OpenvpnPluginUiWidgetClass;
+
+struct _OpenvpnPluginUiWidget {
+	GObject parent;
+};
+
+struct _OpenvpnPluginUiWidgetClass {
+	GObjectClass parent;
+};
+
+GType openvpn_plugin_ui_widget_get_type (void);
+
+#endif	/* _NM_OPENVPN_H_ */
+

Added: trunk/src/.cvsignore
==============================================================================
--- (empty file)
+++ trunk/src/.cvsignore	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,4 @@
+Makefile.in
+Makefile
+nm-openvpn-service
+nm-openvpn-service-openvpn-helper

Added: trunk/src/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile.am	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,32 @@
+AM_CPPFLAGS =							\
+	$(DBUS_CFLAGS)						\
+	$(NETWORK_MANAGER_CFLAGS)				\
+	-DDBUS_API_SUBJECT_TO_CHANGE				\
+	-DG_DISABLE_DEPRECATED					\
+	-DBINDIR=\"$(bindir)\"					\
+	-DPREFIX=\""$(prefix)"\"				\
+	-DSYSCONFDIR=\""$(sysconfdir)"\"			\
+	-DVERSION="\"$(VERSION)\""				\
+	-DLIBDIR=\""$(libdir)"\"				\
+	-DLIBEXECDIR=\""$(libexecdir)"\"			\
+	-DLOCALSTATEDIR=\""$(localstatedir)"\"		 	\
+	-DDATADIR=\"$(datadir)\"
+
+libexec_PROGRAMS = nm-openvpn-service nm-openvpn-service-openvpn-helper
+
+nm_openvpn_service_SOURCES =				\
+				nm-openvpn-service.c	\
+				nm-openvpn-service.h
+
+
+nm_openvpn_service_LDADD = $(NETWORK_MANAGER_LIBS) -lnm_glib_vpn
+
+
+nm_openvpn_service_openvpn_helper_SOURCES = 					\
+				nm-openvpn-service-openvpn-helper.c
+
+nm_openvpn_service_openvpn_helper_LDADD = 				\
+				$(DBUS_LIBS)				\
+				$(NETWORK_MANAGER_LIBS)
+
+CLEANFILES = *~

Added: trunk/src/nm-openvpn-service-openvpn-helper.c
==============================================================================
--- (empty file)
+++ trunk/src/nm-openvpn-service-openvpn-helper.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,398 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-openvpn-service-openvpn-helper - helper called after OpenVPN established
+ * a connection, uses DBUS to send information back to nm-openvpn-service
+ *
+ * Tim Niemueller [www.niemueller.de]
+ * Based on work by Dan Williams <dcbw redhat com>
+ *
+ * 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 2005 Red Hat, Inc.
+ * (C) Copyright 2005 Tim Niemueller
+ *
+ * $Id: nm-openvpn-service-openvpn-helper.c 4170 2008-10-11 14:44:45Z dcbw $
+ * 
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+#include <NetworkManager.h>
+
+#include "nm-openvpn-service.h"
+#include "nm-utils.h"
+
+/* These are here because nm-dbus-glib-types.h isn't exported */
+#define DBUS_TYPE_G_ARRAY_OF_UINT          (dbus_g_type_get_collection ("GArray", G_TYPE_UINT))
+#define DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_ARRAY_OF_UINT))
+
+static void
+helper_failed (DBusGConnection *connection, const char *reason)
+{
+	DBusGProxy *proxy;
+	GError *err = NULL;
+
+	nm_warning ("nm-openvpn-service-openvpn-helper did not receive a valid %s from openvpn", reason);
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+								NM_DBUS_SERVICE_OPENVPN,
+								NM_VPN_DBUS_PLUGIN_PATH,
+								NM_VPN_DBUS_PLUGIN_INTERFACE);
+
+	dbus_g_proxy_call (proxy, "SetFailure", &err,
+				    G_TYPE_STRING, reason,
+				    G_TYPE_INVALID,
+				    G_TYPE_INVALID);
+
+	if (err) {
+		nm_warning ("Could not send failure information: %s", err->message);
+		g_error_free (err);
+	}
+
+	g_object_unref (proxy);
+
+	exit (1);
+}
+
+static void
+send_ip4_config (DBusGConnection *connection, GHashTable *config)
+{
+	DBusGProxy *proxy;
+	GError *err = NULL;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+								NM_DBUS_SERVICE_OPENVPN,
+								NM_VPN_DBUS_PLUGIN_PATH,
+								NM_VPN_DBUS_PLUGIN_INTERFACE);
+
+	dbus_g_proxy_call (proxy, "SetIp4Config", &err,
+				    dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
+				    config,
+				    G_TYPE_INVALID,
+				    G_TYPE_INVALID);
+
+	if (err) {
+		nm_warning ("Could not send failure information: %s", err->message);
+		g_error_free (err);
+	}
+
+	g_object_unref (proxy);
+}
+
+static GValue *
+str_to_gvalue (const char *str, gboolean try_convert)
+{
+	GValue *val;
+
+	/* Empty */
+	if (!str || strlen (str) < 1)
+		return NULL;
+
+	if (!g_utf8_validate (str, -1, NULL)) {
+		if (try_convert && !(str = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL)))
+			str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
+
+		if (!str)
+			/* Invalid */
+			return NULL;
+	}
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, str);
+
+	return val;
+}
+
+static GValue *
+uint_to_gvalue (guint32 num)
+{
+	GValue *val;
+
+	if (num == 0)
+		return NULL;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_UINT);
+	g_value_set_uint (val, num);
+
+	return val;
+}
+
+static GValue *
+addr_to_gvalue (const char *str)
+{
+	struct in_addr	temp_addr;
+	GValue *val;
+
+	/* Empty */
+	if (!str || strlen (str) < 1)
+		return NULL;
+
+	if (inet_pton (AF_INET, str, &temp_addr) <= 0)
+		return NULL;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_UINT);
+	g_value_set_uint (val, temp_addr.s_addr);
+
+	return val;
+}
+
+static GValue *
+parse_addr_list (GValue *value_array, const char *str)
+{
+	char **split;
+	int i;
+	struct in_addr	temp_addr;
+	GArray *array;
+
+	/* Empty */
+	if (!str || strlen (str) < 1)
+		return value_array;
+
+	if (value_array)
+		array = (GArray *) g_value_get_boxed (value_array);
+	else
+		array = g_array_new (FALSE, FALSE, sizeof (guint));
+
+	split = g_strsplit (str, " ", -1);
+	for (i = 0; split[i]; i++) {
+		if (inet_pton (AF_INET, split[i], &temp_addr) > 0)
+			g_array_append_val (array, temp_addr.s_addr);
+	}
+
+	g_strfreev (split);
+
+	if (!value_array && array->len > 0) {
+		value_array = g_slice_new0 (GValue);
+		g_value_init (value_array, DBUS_TYPE_G_UINT_ARRAY);
+		g_value_set_boxed (value_array, array);
+	}
+
+	return value_array;
+}
+
+static GValue *
+get_routes (void)
+{
+	GValue *value = NULL;
+	GPtrArray *routes;
+	char *tmp;
+	int i;
+
+#define BUFLEN 256
+
+	routes = g_ptr_array_new ();
+
+	for (i = 1; i < 256; i++) {
+		GArray *array;
+		char buf[BUFLEN];
+		struct in_addr network;
+		struct in_addr netmask;
+		struct in_addr gateway = { 0, };
+		guint32 prefix, metric = 0;
+
+		snprintf (buf, BUFLEN, "route_network_%d", i);
+		tmp = getenv (buf);
+		if (!tmp || strlen (tmp) < 1)
+			break;
+
+		if (inet_pton (AF_INET, tmp, &network) <= 0) {
+			nm_warning ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
+			continue;
+		}
+
+		snprintf (buf, BUFLEN, "route_netmask_%d", i);
+		tmp = getenv (buf);
+		if (!tmp || inet_pton (AF_INET, tmp, &netmask) <= 0) {
+			nm_warning ("Ignoring invalid static route netmask '%s'", tmp ? tmp : "NULL");
+			continue;
+		}
+
+		snprintf (buf, BUFLEN, "route_gateway_%d", i);
+		tmp = getenv (buf);
+		/* gateway can be missing */
+		if (tmp && (inet_pton (AF_INET, tmp, &gateway) <= 0)) {
+			nm_warning ("Ignoring invalid static route gateway '%s'", tmp ? tmp : "NULL");
+			continue;
+		}
+
+		snprintf (buf, BUFLEN, "route_metric_%d", i);
+		tmp = getenv (buf);
+		/* metric can be missing */
+		if (tmp && strlen (tmp)) {
+			long int tmp_metric;
+
+			errno = 0;
+			tmp_metric = strtol (tmp, NULL, 10);
+			if (errno || tmp_metric < 0 || tmp_metric > G_MAXUINT32) {
+				nm_warning ("Ignoring invalid static route metric '%s'", tmp);
+				continue;
+			}
+			metric = (guint32) tmp_metric;
+		}
+
+		array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
+		g_array_append_val (array, network.s_addr);
+		prefix = nm_utils_ip4_netmask_to_prefix (netmask.s_addr);
+		g_array_append_val (array, prefix);
+		g_array_append_val (array, gateway.s_addr);
+		g_array_append_val (array, metric);
+		g_ptr_array_add (routes, array);
+	}
+
+	if (routes->len > 0) {
+		value = g_new0 (GValue, 1);
+		g_value_init (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT);
+		g_value_take_boxed (value, routes);
+	} else
+		g_ptr_array_free (routes, TRUE);
+
+	return value;
+}
+
+int
+main (int argc, char *argv[])
+{
+	DBusGConnection *connection;
+	GHashTable *config;
+	char *tmp;
+	GValue *val;
+	int i;
+	GError *err = NULL;
+	GValue *dns_list = NULL;
+	GValue *nbns_list = NULL;
+	GValue *dns_domain = NULL;
+	struct in_addr temp_addr;
+
+	g_type_init ();
+
+	connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+	if (!connection) {
+		nm_warning ("Could not get the system bus: %s", err->message);
+		exit (1);
+	}
+
+	config = g_hash_table_new (g_str_hash, g_str_equal);
+
+	/* External world-visible VPN gateway */
+	val = addr_to_gvalue (getenv ("trusted_ip"));
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, val);
+	else
+		helper_failed (connection, "VPN Gateway");
+
+	/* Internal VPN subnet gateway */
+	val = addr_to_gvalue (getenv ("route_vpn_gateway"));
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
+
+	/* Tunnel device */
+	val = str_to_gvalue (getenv ("dev"), FALSE);
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
+	else
+		helper_failed (connection, "Tunnel Device");
+
+	/* IP address */
+	val = addr_to_gvalue (getenv ("ifconfig_local"));
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
+	else
+		helper_failed (connection, "IP4 Address");
+
+	/* PTP address; for vpnc PTP address == internal IP4 address */
+	val = addr_to_gvalue (getenv ("ifconfig_remote"));
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
+
+	/* Netmask */
+	tmp = getenv ("route_netmask_1");
+	if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
+		GValue *val;
+
+		val = g_slice_new0 (GValue);
+		g_value_init (val, G_TYPE_UINT);
+		g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
+
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
+	}
+
+	val = get_routes ();
+	if (val)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
+
+    	/* DNS and WINS servers */
+	for (i = 1; i < 256; i++) {
+		char *env_name;
+
+		env_name = g_strdup_printf ("foreign_option_%d", i);
+		tmp = getenv (env_name);
+		g_free (env_name);
+
+		if (!tmp || strlen (tmp) < 1)
+			break;
+
+		if (!g_str_has_prefix (tmp, "dhcp-option "))
+			continue;
+
+		tmp += 12; /* strlen ("dhcp-option ") */
+
+		if (g_str_has_prefix (tmp, "DNS "))
+			dns_list = parse_addr_list (dns_list, tmp + 4);
+		else if (g_str_has_prefix (tmp, "WINS "))
+			nbns_list = parse_addr_list (nbns_list, tmp + 5);
+		else if (g_str_has_prefix (tmp, "DOMAIN ") && !dns_domain)
+			dns_domain = str_to_gvalue (tmp + 7, FALSE);
+	}
+
+	if (dns_list)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, dns_list);
+	if (nbns_list)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, nbns_list);
+	if (dns_domain)
+		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, dns_domain);
+
+	/* Tunnel MTU */
+	tmp = getenv ("tun_mtu");
+	if (tmp && strlen (tmp)) {
+		long int mtu;
+
+		errno = 0;
+		mtu = strtol (tmp, NULL, 10);
+		if (errno || mtu < 0 || mtu > 20000) {
+			nm_warning ("Ignoring invalid tunnel MTU '%s'", tmp);
+		} else {
+			val = uint_to_gvalue ((guint32) mtu);
+			g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, val);
+		}
+	}
+
+	/* Send the config info to nm-openvpn-service */
+	send_ip4_config (connection, config);
+
+	return 0;
+}

Added: trunk/src/nm-openvpn-service.c
==============================================================================
--- (empty file)
+++ trunk/src/nm-openvpn-service.c	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,1081 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-openvpn-service - openvpn integration with NetworkManager
+ *
+ * Tim Niemueller <tim niemueller de>
+ * Dan Williams <dcbw redhat com>
+ *
+ * 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.
+ *
+ * $Id: nm-openvpn-service.c 4232 2008-10-29 09:13:40Z tambeti $
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <NetworkManager.h>
+#include <NetworkManagerVPN.h>
+#include <nm-setting-vpn.h>
+
+#include "nm-openvpn-service.h"
+#include "nm-utils.h"
+
+#define NM_OPENVPN_HELPER_PATH		LIBEXECDIR"/nm-openvpn-service-openvpn-helper"
+
+G_DEFINE_TYPE (NMOpenvpnPlugin, nm_openvpn_plugin, NM_TYPE_VPN_PLUGIN)
+
+#define NM_OPENVPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OPENVPN_PLUGIN, NMOpenvpnPluginPrivate))
+
+typedef struct {
+	char *username;
+	char *password;
+	char *priv_key_pass;
+	GIOChannel *socket_channel;
+	guint socket_channel_eventid;
+} NMOpenvpnPluginIOData;
+
+typedef struct {
+	GPid	pid;
+	guint connect_timer;
+	guint connect_count;
+	NMOpenvpnPluginIOData *io_data;
+} NMOpenvpnPluginPrivate;
+
+typedef struct {
+	const char *name;
+	GType type;
+	gint int_min;
+	gint int_max;
+	gboolean address;
+} ValidProperty;
+
+static ValidProperty valid_properties[] = {
+	{ NM_OPENVPN_KEY_CA,                   G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_CERT,                 G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_CIPHER,               G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_COMP_LZO,             G_TYPE_BOOLEAN, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_CONNECTION_TYPE,      G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_TAP_DEV,              G_TYPE_BOOLEAN, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_KEY,                  G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_LOCAL_IP,             G_TYPE_STRING, 0, 0, TRUE },
+	{ NM_OPENVPN_KEY_PROTO_TCP,            G_TYPE_BOOLEAN, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_PORT,                 G_TYPE_INT, 1, 65535, FALSE },
+	{ NM_OPENVPN_KEY_REMOTE,               G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_REMOTE_IP,            G_TYPE_STRING, 0, 0, TRUE },
+	{ NM_OPENVPN_KEY_STATIC_KEY,           G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_STATIC_KEY_DIRECTION, G_TYPE_INT, 0, 1, FALSE },
+	{ NM_OPENVPN_KEY_TA,                   G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_TA_DIR,               G_TYPE_INT, 0, 1, FALSE },
+	{ NM_OPENVPN_KEY_USERNAME,             G_TYPE_STRING, 0, 0, FALSE },
+	{ NULL,                                G_TYPE_NONE, FALSE }
+};
+
+static ValidProperty valid_secrets[] = {
+	{ NM_OPENVPN_KEY_PASSWORD,             G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_CERTPASS,             G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_NOSECRET,             G_TYPE_STRING, 0, 0, FALSE },
+	{ NULL,                                G_TYPE_NONE, FALSE }
+};
+
+static gboolean
+validate_address (const char *address)
+{
+	const char *p = address;
+
+	if (!address || !strlen (address))
+		return FALSE;
+
+	/* Ensure it's a valid DNS name or IP address */
+	while (*p) {
+		if (!isalnum (*p) && (*p != '-') && (*p != '.'))
+			return FALSE;
+		p++;
+	}
+	return TRUE;
+}
+
+typedef struct ValidateInfo {
+	ValidProperty *table;
+	GError **error;
+	gboolean have_items;
+} ValidateInfo;
+
+static void
+validate_one_property (const char *key, const char *value, gpointer user_data)
+{
+	ValidateInfo *info = (ValidateInfo *) user_data;
+	int i;
+
+	if (*(info->error))
+		return;
+
+	info->have_items = TRUE;
+
+	/* 'name' is the setting name; always allowed but unused */
+	if (!strcmp (key, NM_SETTING_NAME))
+		return;
+
+	for (i = 0; info->table[i].name; i++) {
+		ValidProperty prop = info->table[i];
+		long int tmp;
+
+		if (strcmp (prop.name, key))
+			continue;
+
+		switch (prop.type) {
+		case G_TYPE_STRING:
+			if (!prop.address || validate_address (value))
+				return; /* valid */
+
+			g_set_error (info->error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "invalid address '%s'",
+			             key);
+			break;
+		case G_TYPE_INT:
+			errno = 0;
+			tmp = strtol (value, NULL, 10);
+			if (errno == 0 && tmp >= prop.int_min && tmp <= prop.int_max)
+				return; /* valid */
+
+			g_set_error (info->error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "invalid integer property '%s' or out of range [%d -> %d]",
+			             key, prop.int_min, prop.int_max);
+			break;
+		case G_TYPE_BOOLEAN:
+			if (!strcmp (value, "yes") || !strcmp (value, "no"))
+				return; /* valid */
+
+			g_set_error (info->error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "invalid boolean property '%s' (not yes or no)",
+			             key);
+			break;
+		default:
+			g_set_error (info->error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "unhandled property '%s' type %s",
+			             key, g_type_name (prop.type));
+			break;
+		}
+	}
+
+	/* Did not find the property from valid_properties or the type did not match */
+	if (!info->table[i].name) {
+		g_set_error (info->error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "property '%s' invalid or not supported",
+		             key);
+	}
+}
+
+static gboolean
+nm_openvpn_properties_validate (NMSettingVPN *s_vpn, GError **error)
+{
+	ValidateInfo info = { &valid_properties[0], error, FALSE };
+
+	nm_setting_vpn_foreach_data_item (s_vpn, validate_one_property, &info);
+	if (!info.have_items) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "%s",
+		             "No VPN configuration options.");
+		return FALSE;
+	}
+
+	return *error ? FALSE : TRUE;
+}
+
+static gboolean
+nm_openvpn_secrets_validate (NMSettingVPN *s_vpn, GError **error)
+{
+	ValidateInfo info = { &valid_secrets[0], error, FALSE };
+
+	nm_setting_vpn_foreach_secret (s_vpn, validate_one_property, &info);
+	if (!info.have_items) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "%s",
+		             "No VPN secrets!");
+		return FALSE;
+	}
+
+	return *error ? FALSE : TRUE;
+}
+
+static void
+nm_openvpn_disconnect_management_socket (NMOpenvpnPlugin *plugin)
+{
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+	NMOpenvpnPluginIOData *io_data = priv->io_data;
+
+	/* This should not throw a warning since this can happen in
+	   non-password modes */
+	if (!io_data)
+		return;
+
+	g_source_remove (io_data->socket_channel_eventid);
+	g_io_channel_shutdown (io_data->socket_channel, FALSE, NULL);
+	g_io_channel_unref (io_data->socket_channel);
+
+	g_free (io_data->username);
+	g_free (io_data->password);
+
+	g_free (priv->io_data);
+	priv->io_data = NULL;
+}
+
+static char *
+ovpn_quote_string (const char *unquoted)
+{
+	char *quoted = NULL, *q;
+	char *u = (char *) unquoted;
+
+	g_return_val_if_fail (unquoted != NULL, NULL);
+
+	/* FIXME: use unpaged memory */
+	quoted = q = g_malloc0 (strlen (unquoted) * 2);
+	while (*u) {
+		/* Escape certain characters */
+		if (*u == ' ' || *u == '\\' || *u == '"')
+			*q++ = '\\';
+		*q++ = *u++;
+	}
+
+	return quoted;
+}
+
+static gboolean
+handle_management_socket (NMVPNPlugin *plugin,
+                          GIOChannel *source,
+                          GIOCondition condition,
+                          NMVPNPluginFailure *out_failure)
+{
+	NMOpenvpnPluginIOData *io_data = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin)->io_data;
+	gboolean again = TRUE;
+	char *str = NULL, *auth, *buf;
+	gsize written;
+
+	if (!(condition & G_IO_IN))
+		return TRUE;
+
+	if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL)
+		return TRUE;
+
+	if (strlen (str) < 1)
+		goto out;
+
+	if (sscanf (str, ">PASSWORD:Need '%a[^']'", &auth) > 0) {
+		if (strcmp (auth, "Auth") == 0) {
+			if (io_data->username != NULL && io_data->password != NULL) {
+				char *quser, *qpass;
+
+				/* Quote strings passed back to openvpn */
+				quser = ovpn_quote_string (io_data->username);
+				qpass = ovpn_quote_string (io_data->password);
+				buf = g_strdup_printf ("username \"%s\" \"%s\"\n"
+				                       "password \"%s\" \"%s\"\n",
+				                       auth, quser,
+				                       auth, qpass);
+				memset (qpass, 0, strlen (qpass));
+				g_free (qpass);
+				g_free (quser);
+
+				/* Will always write everything in blocking channels (on success) */
+				g_io_channel_write_chars (source, buf, strlen (buf), &written, NULL);
+				g_io_channel_flush (source, NULL);
+				g_free (buf);
+			} else
+				nm_warning ("Auth requested but one of username or password is missing");
+		} else if (!strcmp (auth, "Private Key")) {
+			if (io_data->priv_key_pass) {
+				char *qpass;
+
+				/* Quote strings passed back to openvpn */
+				qpass = ovpn_quote_string (io_data->priv_key_pass);
+				buf = g_strdup_printf ("password \"%s\" \"%s\"\n", auth, qpass);
+				memset (qpass, 0, strlen (qpass));
+				g_free (qpass);
+
+				/* Will always write everything in blocking channels (on success) */
+				g_io_channel_write_chars (source, buf, strlen (buf), &written, NULL);
+				g_io_channel_flush (source, NULL);
+				g_free (buf);
+			} else
+				nm_warning ("Certificate password requested but private key password == NULL");
+		} else {
+			nm_warning ("No clue what to send for username/password request for '%s'", auth);
+			if (out_failure)
+				*out_failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+			again = FALSE;
+		}
+		free (auth);
+	} else if (sscanf (str, ">PASSWORD:Verification Failed: '%a[^']'", &auth) > 0) {
+		if (!strcmp (auth, "Auth"))
+			nm_warning ("Password verification failed");
+		else if (!strcmp (auth, "Private Key"))
+			nm_warning ("Private key verification failed");
+		else
+			nm_warning ("Unknown verification failed: %s", auth);
+
+		free (auth);
+
+		if (out_failure)
+			*out_failure = NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED;
+		again = FALSE;
+	}
+
+out:
+	g_free (str);
+	return again;
+}
+
+static gboolean
+nm_openvpn_socket_data_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
+{
+	NMVPNPlugin *plugin = NM_VPN_PLUGIN (user_data);
+	NMVPNPluginFailure failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+
+	if (!handle_management_socket (plugin, source, condition, &failure)) {
+		nm_vpn_plugin_failure (plugin, failure);
+		nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+nm_openvpn_connect_timer_cb (gpointer data)
+{
+	NMOpenvpnPlugin *plugin = NM_OPENVPN_PLUGIN (data);
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+	struct sockaddr_in     serv_addr;
+	gboolean               connected = FALSE;
+	gint                   socket_fd = -1;
+	NMOpenvpnPluginIOData *io_data = priv->io_data;
+
+	priv->connect_count++;
+
+	/* open socket and start listener */
+	socket_fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (socket_fd < 0)
+		return FALSE;
+
+	serv_addr.sin_family = AF_INET;
+	if (inet_pton (AF_INET, "127.0.0.1", &(serv_addr.sin_addr)) <= 0)
+		nm_warning ("%s: could not convert 127.0.0.1", __func__);
+	serv_addr.sin_port = htons (1194);
+ 
+	connected = (connect (socket_fd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) == 0);
+	if (!connected) {
+		close (socket_fd);
+		if (priv->connect_count <= 30)
+			return TRUE;
+
+		priv->connect_timer = 0;
+
+		nm_warning ("Could not open management socket");
+		nm_vpn_plugin_failure (NM_VPN_PLUGIN (plugin), NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
+		nm_vpn_plugin_set_state (NM_VPN_PLUGIN (plugin), NM_VPN_SERVICE_STATE_STOPPED);
+	} else {
+		GIOChannel *openvpn_socket_channel;
+		guint openvpn_socket_channel_eventid;
+
+		openvpn_socket_channel = g_io_channel_unix_new (socket_fd);
+		openvpn_socket_channel_eventid = g_io_add_watch (openvpn_socket_channel,
+		                                                 G_IO_IN,
+		                                                 nm_openvpn_socket_data_cb,
+		                                                 plugin);
+
+		g_io_channel_set_encoding (openvpn_socket_channel, NULL, NULL);
+		io_data->socket_channel = openvpn_socket_channel;
+		io_data->socket_channel_eventid = openvpn_socket_channel_eventid;
+	}
+
+	priv->connect_timer = 0;
+	return FALSE;
+}
+
+static void
+nm_openvpn_schedule_connect_timer (NMOpenvpnPlugin *plugin)
+{
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+
+	if (priv->connect_timer == 0)
+		priv->connect_timer = g_timeout_add (200, nm_openvpn_connect_timer_cb, plugin);
+}
+
+static void
+openvpn_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMVPNPlugin *plugin = NM_VPN_PLUGIN (user_data);
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+	NMVPNPluginFailure failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+	guint error = 0;
+	gboolean good_exit = FALSE;
+
+	if (WIFEXITED (status)) {
+		error = WEXITSTATUS (status);
+		if (error != 0)
+			nm_warning ("openvpn exited with error code %d", error);
+    }
+	else if (WIFSTOPPED (status))
+		nm_warning ("openvpn stopped unexpectedly with signal %d", WSTOPSIG (status));
+	else if (WIFSIGNALED (status))
+		nm_warning ("openvpn died with signal %d", WTERMSIG (status));
+	else
+		nm_warning ("openvpn died from an unknown cause");
+  
+	/* Reap child if needed. */
+	waitpid (priv->pid, NULL, WNOHANG);
+	priv->pid = 0;
+
+	/* OpenVPN doesn't supply useful exit codes :( */
+	switch (error) {
+	case 0:
+		good_exit = TRUE;
+		break;
+	default:
+		failure = NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED;
+		break;
+	}
+
+	/* Try to get the last bits of data from openvpn */
+	if (priv->io_data && priv->io_data->socket_channel) {
+		GIOChannel *channel = priv->io_data->socket_channel;
+		GIOCondition condition;
+
+		while ((condition = g_io_channel_get_buffer_condition (channel)) & G_IO_IN) {
+			if (!handle_management_socket (plugin, channel, condition, &failure)) {
+				good_exit = FALSE;
+				break;
+			}
+		}
+	}
+
+	if (!good_exit)
+		nm_vpn_plugin_failure (plugin, failure);
+
+	nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
+}
+
+static const char *
+validate_connection_type (const char *ctype)
+{
+	if (ctype) {
+		if (   !strcmp (ctype, NM_OPENVPN_CONTYPE_TLS)
+		    || !strcmp (ctype, NM_OPENVPN_CONTYPE_STATIC_KEY)
+		    || !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD)
+		    || !strcmp (ctype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
+			return ctype;
+	}
+	return NULL;
+}
+
+static const char *
+nm_find_openvpn (void)
+{
+	static const char *openvpn_binary_paths[] = {
+		"/usr/sbin/openvpn",
+		"/sbin/openvpn",
+		NULL
+	};
+	const char  **openvpn_binary = openvpn_binary_paths;
+
+	while (*openvpn_binary != NULL) {
+		if (g_file_test (*openvpn_binary, G_FILE_TEST_EXISTS))
+			break;
+		openvpn_binary++;
+	}
+
+	return *openvpn_binary;
+}
+
+static void
+free_openvpn_args (GPtrArray *args)
+{
+	g_ptr_array_foreach (args, (GFunc) g_free, NULL);
+	g_ptr_array_free (args, TRUE);
+}
+
+static void
+add_openvpn_arg (GPtrArray *args, const char *arg)
+{
+	g_return_if_fail (args != NULL);
+	g_return_if_fail (arg != NULL);
+
+	g_ptr_array_add (args, (gpointer) g_strdup (arg));
+}
+
+static gboolean
+add_openvpn_arg_int (GPtrArray *args, const char *arg)
+{
+	long int tmp_int;
+
+	g_return_val_if_fail (args != NULL, FALSE);
+	g_return_val_if_fail (arg != NULL, FALSE);
+
+	/* Convert -> int and back to string for security's sake since
+	 * strtol() ignores some leading and trailing characters.
+	 */
+	errno = 0;
+	tmp_int = strtol (arg, NULL, 10);
+	if (errno != 0)
+		return FALSE;
+	g_ptr_array_add (args, (gpointer) g_strdup_printf ("%d", (guint32) tmp_int));
+	return TRUE;
+}
+
+static gboolean
+nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
+                                 NMSettingVPN *s_vpn,
+                                 const char *default_username,
+                                 GError **error)
+{
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+	const char *openvpn_binary, *connection_type, *tmp;
+	GPtrArray *args;
+	GSource *openvpn_watch;
+	GPid pid;
+
+	/* Find openvpn */
+	openvpn_binary = nm_find_openvpn ();
+	if (!openvpn_binary) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "%s",
+		             "Could not find the openvpn binary.");
+		return FALSE;
+	}
+
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+	connection_type = validate_connection_type (tmp);
+	if (!connection_type) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "%s",
+		             "Invalid connection type.");
+		return FALSE;
+	}
+
+	args = g_ptr_array_new ();
+	add_openvpn_arg (args, openvpn_binary);
+
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE);
+	if (tmp && strlen (tmp)) {
+		add_openvpn_arg (args, "--remote");
+		add_openvpn_arg (args, tmp);
+	}
+
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_COMP_LZO);
+	if (tmp && !strcmp (tmp, "yes"))
+		add_openvpn_arg (args, "--comp-lzo");
+
+	add_openvpn_arg (args, "--nobind");
+
+	/* Device, either tun or tap */
+	add_openvpn_arg (args, "--dev");
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_TAP_DEV);
+	if (tmp && !strcmp (tmp, "yes"))
+		add_openvpn_arg (args, "tap");
+	else
+		add_openvpn_arg (args, "tun");
+
+	/* Protocol, either tcp or udp */
+	add_openvpn_arg (args, "--proto");
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_PROTO_TCP);
+	if (tmp && !strcmp (tmp, "yes"))
+		add_openvpn_arg (args, "tcp-client");
+	else
+		add_openvpn_arg (args, "udp");
+
+	/* Port */
+	add_openvpn_arg (args, "--port");
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_PORT);
+	if (tmp && strlen (tmp)) {
+		if (!add_openvpn_arg_int (args, tmp)) {
+			g_set_error (error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "Invalid port number '%s'.",
+			             tmp);
+			free_openvpn_args (args);
+			return FALSE;
+		}
+	} else {
+		/* Default to IANA assigned port 1194 */
+		add_openvpn_arg (args, "1194");
+	}
+
+	/* Cipher */
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CIPHER);
+	if (tmp && strlen (tmp)) {
+		add_openvpn_arg (args, "--cipher");
+		add_openvpn_arg (args, tmp);
+	}
+
+	/* TA */
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_TA);
+	if (tmp && strlen (tmp)) {
+		add_openvpn_arg (args, "--tls-auth");
+		add_openvpn_arg (args, tmp);
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_TA_DIR);
+		if (tmp && strlen (tmp))
+			add_openvpn_arg (args, tmp);
+	}
+
+	/* Syslog */
+	add_openvpn_arg (args, "--syslog");
+	add_openvpn_arg (args, "nm-openvpn");
+
+	/* Punch script security in the face; this option was added to OpenVPN 2.1-rc9
+	 * and defaults to disallowing any scripts, a behavior change from previous
+	 * versions.
+	 */
+	add_openvpn_arg (args, "--script-security");
+	add_openvpn_arg (args, "2");
+
+	/* Up script, called when connection has been established or has been restarted */
+	add_openvpn_arg (args, "--up");
+	add_openvpn_arg (args, NM_OPENVPN_HELPER_PATH);
+	add_openvpn_arg (args, "--up-restart");
+
+	/* Keep key and tun if restart is needed */
+	add_openvpn_arg (args, "--persist-key");
+	add_openvpn_arg (args, "--persist-tun");
+
+	/* Management socket for localhost access to supply username and password */
+	add_openvpn_arg (args, "--management");
+	add_openvpn_arg (args, "127.0.0.1");
+	/* with have nobind, thus 1194 should be free, it is the IANA assigned port */
+	add_openvpn_arg (args, "1194");
+	/* Query on the management socket for user/pass */
+	add_openvpn_arg (args, "--management-query-passwords");
+
+	/* do not let openvpn setup routes, NM will handle it */
+	add_openvpn_arg (args, "--route-noexec");
+
+	/* Now append configuration options which are dependent on the configuration type */
+	if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)) {
+		add_openvpn_arg (args, "--client");
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--ca");
+			add_openvpn_arg (args, tmp);
+		}
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--cert");
+			add_openvpn_arg (args, tmp);
+		}
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--key");
+			add_openvpn_arg (args, tmp);
+		}
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--secret");
+			add_openvpn_arg (args, tmp);
+
+			tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_STATIC_KEY_DIRECTION);
+			if (tmp && strlen (tmp))
+				add_openvpn_arg (args, tmp);
+		}
+
+		add_openvpn_arg (args, "--ifconfig");
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_LOCAL_IP);
+		if (!tmp) {
+			/* Insufficient data (FIXME: this should really be detected when validating the properties */
+			g_set_error (error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "%s",
+			             "Missing required local IP address for static key mode.");
+			free_openvpn_args (args);
+			return FALSE;
+		}
+		add_openvpn_arg (args, tmp);
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE_IP);
+		if (!tmp) {
+			/* Insufficient data (FIXME: this should really be detected when validating the properties */
+			g_set_error (error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+			             "%s",
+			             "Missing required remote IP address for static key mode.");
+			free_openvpn_args (args);
+			return FALSE;
+		}
+		add_openvpn_arg (args, tmp);
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		/* Client mode */
+		add_openvpn_arg (args, "--client");
+		/* Use user/path authentication */
+		add_openvpn_arg (args, "--auth-user-pass");
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--ca");
+			add_openvpn_arg (args, tmp);
+		}
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		add_openvpn_arg (args, "--client");
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CA);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--ca");
+			add_openvpn_arg (args, tmp);
+		}
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CERT);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--cert");
+			add_openvpn_arg (args, tmp);
+		}
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_KEY);
+		if (tmp && strlen (tmp)) {
+			add_openvpn_arg (args, "--key");
+			add_openvpn_arg (args, tmp);
+		}
+
+		/* Use user/path authentication */
+		add_openvpn_arg (args, "--auth-user-pass");
+	} else {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+		             "Unknown connection type '%s'.",
+		             connection_type);
+		free_openvpn_args (args);
+		return FALSE;
+	}
+
+	g_ptr_array_add (args, NULL);
+
+	if (!g_spawn_async (NULL, (char **) args->pdata, NULL,
+	                    G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, error)) {
+		free_openvpn_args (args);
+		return FALSE;
+	}
+	free_openvpn_args (args);
+
+	nm_info ("openvpn started with pid %d", pid);
+
+	priv->pid = pid;
+	openvpn_watch = g_child_watch_source_new (pid);
+	g_source_set_callback (openvpn_watch, (GSourceFunc) openvpn_watch_cb, plugin, NULL);
+	g_source_attach (openvpn_watch, NULL);
+	g_source_unref (openvpn_watch);
+
+	/* Listen to the management socket for a few connection types:
+	   PASSWORD: Will require username and password
+	   X509USERPASS: Will require username and password and maybe certificate password
+	   X509: May require certificate password
+	*/
+	if (   !strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)
+	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)
+	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		NMOpenvpnPluginIOData *io_data;
+
+		io_data = g_new0 (NMOpenvpnPluginIOData, 1);
+
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME);
+		io_data->username = tmp ? g_strdup (tmp) : NULL;
+		/* Use the default username if it wasn't overridden by the user */
+		if (!io_data->username && default_username)
+			io_data->username = g_strdup (default_username);
+
+		tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD);
+		io_data->password = tmp ? g_strdup (tmp) : NULL;
+
+		tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS);
+		io_data->priv_key_pass = tmp ? g_strdup (tmp) : NULL;
+
+		priv->io_data = io_data;
+
+		nm_openvpn_schedule_connect_timer (plugin);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+real_connect (NMVPNPlugin   *plugin,
+              NMConnection  *connection,
+              GError       **error)
+{
+	NMSettingVPN *s_vpn;
+	const char *connection_type;
+	const char *user_name;
+	const char *tmp;
+
+	s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
+	if (!s_vpn) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+		             "%s",
+		             "Could not process the request because the VPN connection settings were invalid.");
+		return FALSE;
+	}
+
+	user_name = nm_setting_vpn_get_user_name (s_vpn);
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+	connection_type = validate_connection_type (tmp);
+
+	/* Need a username for any password-based connection types */
+	if (   !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
+	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		if (!user_name && !nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME)) {
+			g_set_error (error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+			             "%s",
+			             "Could not process the request because no username was provided.");
+			return FALSE;
+		}
+	}
+
+	/* Validate the properties */
+	if (!nm_openvpn_properties_validate (s_vpn, error))
+		return FALSE;
+
+	/* Static Key doesn't need secrets; the rest do */
+	if (strcmp (connection_type, NM_OPENVPN_CONTYPE_STATIC_KEY)) {
+		if (!nm_openvpn_secrets_validate (s_vpn, error))
+			return FALSE;
+	}
+
+	/* Finally try to start OpenVPN */
+	if (!nm_openvpn_start_openvpn_binary (NM_OPENVPN_PLUGIN (plugin), s_vpn, user_name, error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+real_need_secrets (NMVPNPlugin *plugin,
+                   NMConnection *connection,
+                   char **setting_name,
+                   GError **error)
+{
+	NMSettingVPN *s_vpn;
+	const char *connection_type;
+	gboolean need_secrets = FALSE;
+	const char *tmp;
+
+	g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
+	if (!s_vpn) {
+		g_set_error (error,
+		             NM_VPN_PLUGIN_ERROR,
+		             NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+		             "%s",
+		             "Could not process the request because the VPN connection settings were invalid.");
+		return FALSE;
+	}
+
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
+	connection_type = validate_connection_type (tmp);
+
+	if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
+		/* Will require a password and maybe private key password */
+		if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
+			need_secrets = TRUE;
+
+		if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD))
+			need_secrets = TRUE;
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)) {
+		/* Will require a password */
+		if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD))
+			need_secrets = TRUE;
+	} else if (!strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)) {
+		/* May require private key password */
+		if (!nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS))
+			need_secrets = TRUE;
+	}
+
+	if (need_secrets)
+		*setting_name = NM_SETTING_VPN_SETTING_NAME;
+
+	return need_secrets;
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+	int pid = GPOINTER_TO_INT (data);
+
+	if (kill (pid, 0) == 0)
+		kill (pid, SIGKILL);
+
+	return FALSE;
+}
+
+static gboolean
+real_disconnect (NMVPNPlugin	 *plugin,
+			  GError		**err)
+{
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+
+	if (priv->pid) {
+		if (kill (priv->pid, SIGTERM) == 0)
+			g_timeout_add (2000, ensure_killed, GINT_TO_POINTER (priv->pid));
+		else
+			kill (priv->pid, SIGKILL);
+
+		nm_info ("Terminated openvpn daemon with PID %d.", priv->pid);
+		priv->pid = 0;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_openvpn_plugin_init (NMOpenvpnPlugin *plugin)
+{
+}
+
+static void
+nm_openvpn_plugin_class_init (NMOpenvpnPluginClass *plugin_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
+	NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS (plugin_class);
+
+	g_type_class_add_private (object_class, sizeof (NMOpenvpnPluginPrivate));
+
+	/* virtual methods */
+	parent_class->connect      = real_connect;
+	parent_class->need_secrets = real_need_secrets;
+	parent_class->disconnect   = real_disconnect;
+}
+
+static void
+plugin_state_changed (NMOpenvpnPlugin *plugin,
+                      NMVPNServiceState state,
+                      gpointer user_data)
+{
+	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
+
+	switch (state) {
+	case NM_VPN_SERVICE_STATE_UNKNOWN:
+	case NM_VPN_SERVICE_STATE_INIT:
+	case NM_VPN_SERVICE_STATE_SHUTDOWN:
+	case NM_VPN_SERVICE_STATE_STOPPING:
+	case NM_VPN_SERVICE_STATE_STOPPED:
+		/* Cleanup on failure */
+		if (priv->connect_timer) {
+			g_source_remove (priv->connect_timer);
+			priv->connect_timer = 0;
+		}
+		nm_openvpn_disconnect_management_socket (plugin);
+		break;
+	default:
+		break;
+	}
+}
+
+NMOpenvpnPlugin *
+nm_openvpn_plugin_new (void)
+{
+	NMOpenvpnPlugin *plugin;
+
+	plugin =  (NMOpenvpnPlugin *) g_object_new (NM_TYPE_OPENVPN_PLUGIN,
+	                                            NM_VPN_PLUGIN_DBUS_SERVICE_NAME,
+	                                            NM_DBUS_SERVICE_OPENVPN,
+	                                            NULL);
+	if (plugin)
+		g_signal_connect (G_OBJECT (plugin), "state-changed", G_CALLBACK (plugin_state_changed), NULL);
+
+	return plugin;
+}
+
+static void
+quit_mainloop (NMVPNPlugin *plugin, gpointer user_data)
+{
+	g_main_loop_quit ((GMainLoop *) user_data);
+}
+
+int
+main (int argc, char *argv[])
+{
+	NMOpenvpnPlugin *plugin;
+	GMainLoop *main_loop;
+
+	g_type_init ();
+
+	if (system ("/sbin/modprobe tun") == -1)
+		exit (EXIT_FAILURE);
+
+	plugin = nm_openvpn_plugin_new ();
+	if (!plugin)
+		exit (EXIT_FAILURE);
+
+	main_loop = g_main_loop_new (NULL, FALSE);
+
+	g_signal_connect (plugin, "quit",
+				   G_CALLBACK (quit_mainloop),
+				   main_loop);
+
+	g_main_loop_run (main_loop);
+
+	g_main_loop_unref (main_loop);
+	g_object_unref (plugin);
+
+	exit (EXIT_SUCCESS);
+}

Added: trunk/src/nm-openvpn-service.h
==============================================================================
--- (empty file)
+++ trunk/src/nm-openvpn-service.h	Thu Dec 11 19:50:59 2008
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* nm-openvpn-service - openvpn integration with NetworkManager
+ *
+ * Tim Niemueller <tim niemueller de>
+ * Based on work by Dan Williams <dcbw redhat com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NM_OPENVPN_SERVICE_H
+#define NM_OPENVPN_SERVICE_H
+
+#include <glib/gtypes.h>
+#include <glib-object.h>
+#include <nm-vpn-plugin.h>
+
+#define NM_TYPE_OPENVPN_PLUGIN            (nm_openvpn_plugin_get_type ())
+#define NM_OPENVPN_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OPENVPN_PLUGIN, NMOpenvpnPlugin))
+#define NM_OPENVPN_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OPENVPN_PLUGIN, NMOpenvpnPluginClass))
+#define NM_IS_OPENVPN_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OPENVPN_PLUGIN))
+#define NM_IS_OPENVPN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_OPENVPN_PLUGIN))
+#define NM_OPENVPN_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OPENVPN_PLUGIN, NMOpenvpnPluginClass))
+
+#define NM_DBUS_SERVICE_OPENVPN    "org.freedesktop.NetworkManager.openvpn"
+#define NM_DBUS_INTERFACE_OPENVPN  "org.freedesktop.NetworkManager.openvpn"
+#define NM_DBUS_PATH_OPENVPN       "/org/freedesktop/NetworkManager/openvpn"
+
+#define NM_OPENVPN_KEY_CA "ca"
+#define NM_OPENVPN_KEY_CERT "cert"
+#define NM_OPENVPN_KEY_CIPHER "cipher"
+#define NM_OPENVPN_KEY_COMP_LZO "comp-lzo"
+#define NM_OPENVPN_KEY_CONNECTION_TYPE "connection-type"
+#define NM_OPENVPN_KEY_TAP_DEV "tap-dev"
+#define NM_OPENVPN_KEY_KEY "key"
+#define NM_OPENVPN_KEY_LOCAL_IP "local-ip"
+#define NM_OPENVPN_KEY_PROTO_TCP "proto-tcp"
+#define NM_OPENVPN_KEY_PORT "port"
+#define NM_OPENVPN_KEY_REMOTE "remote"
+#define NM_OPENVPN_KEY_REMOTE_IP "remote-ip"
+#define NM_OPENVPN_KEY_STATIC_KEY "static-key"
+#define NM_OPENVPN_KEY_STATIC_KEY_DIRECTION "static-key-direction"
+#define NM_OPENVPN_KEY_TA "ta"
+#define NM_OPENVPN_KEY_TA_DIR "ta-dir"
+#define NM_OPENVPN_KEY_USERNAME "username"
+
+#define NM_OPENVPN_KEY_PASSWORD "password"
+#define NM_OPENVPN_KEY_CERTPASS "cert-pass"
+/* Internal auth-dialog -> service token indicating that no secrets are
+ * required for the connection.
+ */
+#define NM_OPENVPN_KEY_NOSECRET "no-secret"
+
+#define NM_OPENVPN_CONTYPE_TLS          "tls"
+#define NM_OPENVPN_CONTYPE_STATIC_KEY   "static-key"
+#define NM_OPENVPN_CONTYPE_PASSWORD     "password"
+#define NM_OPENVPN_CONTYPE_PASSWORD_TLS "password-tls"
+
+typedef struct {
+	NMVPNPlugin parent;
+} NMOpenvpnPlugin;
+
+typedef struct {
+	NMVPNPluginClass parent;
+} NMOpenvpnPluginClass;
+
+GType nm_openvpn_plugin_get_type (void);
+
+NMOpenvpnPlugin *nm_openvpn_plugin_new (void);
+
+#endif /* NM_OPENVPN_SERVICE_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]