[network-manager-applet] Add a mobile broadband connection setup wizard



commit ea96ccc94a438a74f034563f5c5f16f060b0e8c9
Author: Dan Williams <dcbw redhat com>
Date:   Tue Jun 2 16:07:14 2009 -0400

    Add a mobile broadband connection setup wizard
    
    Interaction design by Máirín Duffy <duffy redhat com>
---
 configure.ac                               |   12 +-
 po/POTFILES.in                             |    3 +-
 src/applet-device-cdma.c                   |  143 ++-
 src/applet-device-gsm.c                    |  144 ++-
 src/applet-device-wifi.c                   |   18 +-
 src/applet-device-wired.c                  |   10 +-
 src/applet.c                               |  111 ++-
 src/applet.h                               |   13 +-
 src/connection-editor/Makefile.am          |    3 +-
 src/connection-editor/ce-page.c            |   42 +
 src/connection-editor/ce-page.h            |   19 +
 src/connection-editor/main.c               |   30 +-
 src/connection-editor/mobile-wizard.c      |  255 -----
 src/connection-editor/mobile-wizard.h      |   31 -
 src/connection-editor/nm-connection-list.c |  455 +++------
 src/connection-editor/nm-connection-list.h |    6 +-
 src/connection-editor/page-dsl.c           |   24 +
 src/connection-editor/page-dsl.h           |    5 +
 src/connection-editor/page-mobile.c        |  178 ++++
 src/connection-editor/page-mobile.h        |    5 +
 src/connection-editor/page-vpn.c           |   32 +
 src/connection-editor/page-vpn.h           |    5 +
 src/connection-editor/page-wired.c         |   21 +
 src/connection-editor/page-wired.h         |    5 +
 src/connection-editor/page-wireless.c      |   24 +
 src/connection-editor/page-wireless.h      |    6 +
 src/connection-editor/vpn-helpers.c        |    3 +-
 src/connection-editor/vpn-helpers.h        |    3 +-
 src/utils/Makefile.am                      |    9 +-
 src/utils/mobile-wizard.c                  | 1545 ++++++++++++++++++++++++++++
 src/utils/mobile-wizard.h                  |   56 +
 src/utils/nmn-mobile-providers.c           |  771 ++++++++++++++
 src/utils/nmn-mobile-providers.h           |   94 ++
 src/utils/utils.c                          |   52 +
 src/utils/utils.h                          |    2 +
 35 files changed, 3388 insertions(+), 747 deletions(-)

diff --git a/configure.ac b/configure.ac
index bc93f4c..79d0c06 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,13 +68,13 @@ AC_C_BIGENDIAN
 PKG_CHECK_MODULES(GOBJECT, gobject-2.0)
 
 PKG_CHECK_MODULES(NMA,
-		[dbus-glib-1 >= 0.72
+		[dbus-glib-1 >= 0.74
 		 glib-2.0 >= 2.10
-		 NetworkManager >= 0.7.0
-		 libnm_glib >= 0.7.0
-		 libnm-util >= 0.7.0
-		 libnm_glib_vpn >= 0.7.0
-		 gtk+-2.0 >= 2.10
+		 NetworkManager >= 0.7.1
+		 libnm_glib >= 0.7.1
+		 libnm-util >= 0.7.1
+		 libnm_glib_vpn >= 0.7.1
+		 gtk+-2.0 >= 2.14
 		 libglade-2.0
 		 gmodule-export-2.0
 		 gconf-2.0
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a752399..cfda6c1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -23,7 +23,6 @@ src/connection-editor/ce-page-wireless.glade
 src/connection-editor/ce-page-wireless-security.glade
 src/connection-editor/ce-vpn-wizard.glade
 src/connection-editor/ip4-routes-dialog.c
-src/connection-editor/mobile-wizard.c
 src/connection-editor/page-dsl.c
 src/connection-editor/page-ip4.c
 src/connection-editor/page-mobile.c
@@ -40,6 +39,8 @@ src/connection-editor/nm-connection-list.c
 src/connection-editor/vpn-helpers.c
 src/keyring.png
 src/main.c
+src/utils/mobile-wizard.c
+src/utils/nmn-mobile-providers.c
 src/vpn-password-dialog.c
 src/vpn-password-dialog.h
 src/wired-dialog.c
diff --git a/src/applet-device-cdma.c b/src/applet-device-cdma.c
index 58742fe..91caf41 100644
--- a/src/applet-device-cdma.c
+++ b/src/applet-device-cdma.c
@@ -38,6 +38,7 @@
 #include "applet.h"
 #include "applet-device-cdma.h"
 #include "utils.h"
+#include "mobile-wizard.h"
 
 typedef struct {
 	NMApplet *applet;
@@ -51,53 +52,105 @@ cdma_menu_item_info_destroy (gpointer data)
 	g_slice_free (CdmaMenuItemInfo, data);
 }
 
-#define DEFAULT_CDMA_NAME _("Auto Mobile Broadband (CDMA) connection")
+typedef struct {
+	AppletNewAutoConnectionCallback callback;
+	gpointer callback_data;
+} AutoCdmaWizardInfo;
+
+static void
+mobile_wizard_done (MobileWizard *wizard,
+                    gboolean canceled,
+                    MobileWizardAccessMethod *method,
+                    gpointer user_data)
+{
+	AutoCdmaWizardInfo *info = user_data;
+	NMConnection *connection = NULL;
+
+	if (!canceled && method) {
+		NMSetting *setting;
+		char *uuid, *id;
+
+		if (method->devtype != NM_DEVICE_TYPE_CDMA) {
+			g_warning ("Unexpected device type (not CDMA).");
+			canceled = TRUE;
+			goto done;
+		}
+
+		connection = nm_connection_new ();
+
+		setting = nm_setting_cdma_new ();
+		g_object_set (setting,
+		              NM_SETTING_CDMA_NUMBER, "#777",
+		              NM_SETTING_CDMA_USERNAME, method->username,
+		              NM_SETTING_CDMA_PASSWORD, method->password,
+		              NULL);
+		nm_connection_add_setting (connection, setting);
+
+		/* Serial setting */
+		setting = nm_setting_serial_new ();
+		g_object_set (setting,
+		              NM_SETTING_SERIAL_BAUD, 115200,
+		              NM_SETTING_SERIAL_BITS, 8,
+		              NM_SETTING_SERIAL_PARITY, 'n',
+		              NM_SETTING_SERIAL_STOPBITS, 1,
+		              NULL);
+		nm_connection_add_setting (connection, setting);
+
+		nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+		setting = nm_setting_connection_new ();
+		if (method->plan_name)
+			id = g_strdup_printf ("%s %s", method->provider_name, method->plan_name);
+		else
+			id = g_strdup_printf ("%s connection", method->provider_name);
+		uuid = nm_utils_uuid_generate ();
+		g_object_set (setting,
+		              NM_SETTING_CONNECTION_ID, id,
+		              NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME,
+		              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+		              NM_SETTING_CONNECTION_UUID, uuid,
+		              NULL);
+		g_free (uuid);
+		g_free (id);
+		nm_connection_add_setting (connection, setting);
+	}
+
+done:
+	(*(info->callback)) (connection, TRUE, canceled, info->callback_data);
 
-static NMConnection *
+	if (wizard)
+		mobile_wizard_destroy (wizard);
+	g_free (info);
+}
+
+static gboolean
 cdma_new_auto_connection (NMDevice *device,
-                          NMApplet *applet,
-                          gpointer user_data)
+                          gpointer dclass_data,
+                          AppletNewAutoConnectionCallback callback,
+                          gpointer callback_data)
 {
-	NMConnection *connection;
-	NMSettingCdma *s_cdma;
-	NMSettingSerial *s_serial;
-	NMSettingPPP *s_ppp;
-	NMSettingConnection *s_con;
-	char *uuid;
-
-	connection = nm_connection_new ();
-
-	s_cdma = NM_SETTING_CDMA (nm_setting_cdma_new ());
-	/* De-facto standard for CDMA */
-	g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
-	nm_connection_add_setting (connection, NM_SETTING (s_cdma));
-
-	/* Serial setting */
-	s_serial = (NMSettingSerial *) nm_setting_serial_new ();
-	g_object_set (s_serial,
-	              NM_SETTING_SERIAL_BAUD, 115200,
-	              NM_SETTING_SERIAL_BITS, 8,
-	              NM_SETTING_SERIAL_PARITY, 'n',
-	              NM_SETTING_SERIAL_STOPBITS, 1,
-	              NULL);
-
-	nm_connection_add_setting (connection, NM_SETTING (s_serial));
-
-	s_ppp = (NMSettingPPP *) nm_setting_ppp_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_ppp));
-
-	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (s_con,
-		      NM_SETTING_CONNECTION_ID, DEFAULT_CDMA_NAME,
-		      NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (NM_SETTING (s_cdma)),
-		      NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-		      NM_SETTING_CONNECTION_UUID, uuid,
-		      NULL);
-	g_free (uuid);
-	nm_connection_add_setting (connection, NM_SETTING (s_con));
-
-	return connection;
+	MobileWizard *wizard;
+	AutoCdmaWizardInfo *info;
+	MobileWizardAccessMethod *method;
+
+	info = g_malloc0 (sizeof (AutoCdmaWizardInfo));
+	info->callback = callback;
+	info->callback_data = callback_data;
+
+	wizard = mobile_wizard_new (NULL, device, mobile_wizard_done, info);
+	if (wizard) {
+		mobile_wizard_present (wizard);
+		return TRUE;
+	}
+
+	/* Fall back to something */
+	method = g_malloc0 (sizeof (MobileWizardAccessMethod));
+	method->devtype = NM_DEVICE_TYPE_CDMA;
+	method->provider_name = _("CDMA");
+	mobile_wizard_done (NULL, FALSE, method, info);
+	g_free (method);
+
+	return TRUE;
 }
 
 static void
@@ -156,7 +209,7 @@ add_default_connection_item (NMDevice *device,
 	CdmaMenuItemInfo *info;
 	GtkWidget *item;
 	
-	item = gtk_check_menu_item_new_with_label (DEFAULT_CDMA_NAME);
+	item = gtk_check_menu_item_new_with_label (_("New Mobile Broadband (CDMA) connection..."));
 	gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
 
 	info = g_slice_new0 (CdmaMenuItemInfo);
diff --git a/src/applet-device-gsm.c b/src/applet-device-gsm.c
index a01d57c..8a93f49 100644
--- a/src/applet-device-gsm.c
+++ b/src/applet-device-gsm.c
@@ -39,6 +39,7 @@
 #include "applet.h"
 #include "applet-device-gsm.h"
 #include "utils.h"
+#include "mobile-wizard.h"
 
 typedef struct {
 	NMApplet *applet;
@@ -52,53 +53,106 @@ gsm_menu_item_info_destroy (gpointer data)
 	g_slice_free (GSMMenuItemInfo, data);
 }
 
-#define DEFAULT_GSM_NAME _("Auto Mobile Broadband (GSM) connection")
+typedef struct {
+	AppletNewAutoConnectionCallback callback;
+	gpointer callback_data;
+} AutoGsmWizardInfo;
+
+static void
+mobile_wizard_done (MobileWizard *wizard,
+                    gboolean canceled,
+                    MobileWizardAccessMethod *method,
+                    gpointer user_data)
+{
+	AutoGsmWizardInfo *info = user_data;
+	NMConnection *connection = NULL;
+
+	if (!canceled && method) {
+		NMSetting *setting;
+		char *uuid, *id;
+
+		if (method->devtype != NM_DEVICE_TYPE_GSM) {
+			g_warning ("Unexpected device type (not GSM).");
+			canceled = TRUE;
+			goto done;
+		}
+
+		connection = nm_connection_new ();
+
+		setting = nm_setting_gsm_new ();
+		g_object_set (setting,
+		              NM_SETTING_GSM_NUMBER, "*99#",
+		              NM_SETTING_GSM_USERNAME, method->username,
+		              NM_SETTING_GSM_PASSWORD, method->password,
+		              NM_SETTING_GSM_APN, method->gsm_apn,
+		              NULL);
+		nm_connection_add_setting (connection, setting);
+
+		/* Serial setting */
+		setting = nm_setting_serial_new ();
+		g_object_set (setting,
+		              NM_SETTING_SERIAL_BAUD, 115200,
+		              NM_SETTING_SERIAL_BITS, 8,
+		              NM_SETTING_SERIAL_PARITY, 'n',
+		              NM_SETTING_SERIAL_STOPBITS, 1,
+		              NULL);
+		nm_connection_add_setting (connection, setting);
+
+		nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+		setting = nm_setting_connection_new ();
+		if (method->plan_name)
+			id = g_strdup_printf ("%s %s", method->provider_name, method->plan_name);
+		else
+			id = g_strdup_printf ("%s connection", method->provider_name);
+		uuid = nm_utils_uuid_generate ();
+		g_object_set (setting,
+		              NM_SETTING_CONNECTION_ID, id,
+		              NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME,
+		              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+		              NM_SETTING_CONNECTION_UUID, uuid,
+		              NULL);
+		g_free (uuid);
+		g_free (id);
+		nm_connection_add_setting (connection, setting);
+	}
+
+done:
+	(*(info->callback)) (connection, TRUE, canceled, info->callback_data);
 
-static NMConnection *
+	if (wizard)
+		mobile_wizard_destroy (wizard);
+	g_free (info);
+}
+
+static gboolean
 gsm_new_auto_connection (NMDevice *device,
-                         NMApplet *applet,
-                         gpointer user_data)
+                         gpointer dclass_data,
+                         AppletNewAutoConnectionCallback callback,
+                         gpointer callback_data)
 {
-	NMConnection *connection;
-	NMSettingGsm *s_gsm;
-	NMSettingSerial *s_serial;
-	NMSettingPPP *s_ppp;
-	NMSettingConnection *s_con;
-	char *uuid;
-
-	connection = nm_connection_new ();
-
-	s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ());
-	/* This should be a sensible default as it's seems to be quite standard */
-	g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-	nm_connection_add_setting (connection, NM_SETTING (s_gsm));
-
-	/* Serial setting */
-	s_serial = (NMSettingSerial *) nm_setting_serial_new ();
-	g_object_set (s_serial,
-	              NM_SETTING_SERIAL_BAUD, 115200,
-	              NM_SETTING_SERIAL_BITS, 8,
-	              NM_SETTING_SERIAL_PARITY, 'n',
-	              NM_SETTING_SERIAL_STOPBITS, 1,
-	              NULL);
-
-	nm_connection_add_setting (connection, NM_SETTING (s_serial));
-
-	s_ppp = (NMSettingPPP *) nm_setting_ppp_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_ppp));
-
-	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (s_con,
-			    NM_SETTING_CONNECTION_ID, DEFAULT_GSM_NAME,
-			    NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (NM_SETTING (s_gsm)),
-			    NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-			    NM_SETTING_CONNECTION_UUID, uuid,
-			    NULL);
-	g_free (uuid);
-	nm_connection_add_setting (connection, NM_SETTING (s_con));
-
-	return connection;
+	MobileWizard *wizard;
+	AutoGsmWizardInfo *info;
+	MobileWizardAccessMethod *method;
+
+	info = g_malloc0 (sizeof (AutoGsmWizardInfo));
+	info->callback = callback;
+	info->callback_data = callback_data;
+
+	wizard = mobile_wizard_new (NULL, device, mobile_wizard_done, info);
+	if (wizard) {
+		mobile_wizard_present (wizard);
+		return TRUE;
+	}
+
+	/* Fall back to something */
+	method = g_malloc0 (sizeof (MobileWizardAccessMethod));
+	method->devtype = NM_DEVICE_TYPE_GSM;
+	method->provider_name = _("GSM");
+	mobile_wizard_done (NULL, FALSE, method, info);
+	g_free (method);
+
+	return TRUE;
 }
 
 static void
@@ -157,7 +211,7 @@ add_default_connection_item (NMDevice *device,
 	GSMMenuItemInfo *info;
 	GtkWidget *item;
 	
-	item = gtk_check_menu_item_new_with_label (DEFAULT_GSM_NAME);
+	item = gtk_check_menu_item_new_with_label (_("New Mobile Broadband (GSM) connection..."));
 	gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
 
 	info = g_slice_new0 (GSMMenuItemInfo);
diff --git a/src/applet-device-wifi.c b/src/applet-device-wifi.c
index a4cc157..bc00a76 100644
--- a/src/applet-device-wifi.c
+++ b/src/applet-device-wifi.c
@@ -316,12 +316,13 @@ none:
 	return NULL;
 }
 
-static NMConnection *
+static gboolean
 wireless_new_auto_connection (NMDevice *device,
-                              NMApplet *applet,
-                              gpointer user_data)
+                              gpointer dclass_data,
+                              AppletNewAutoConnectionCallback callback,
+                              gpointer callback_data)
 {
-	WirelessMenuItemInfo *info = (WirelessMenuItemInfo *) user_data;
+	WirelessMenuItemInfo *info = (WirelessMenuItemInfo *) dclass_data;
 	NMConnection *connection = NULL;
 	NMSettingConnection *s_con = NULL;
 	NMSettingWireless *s_wireless = NULL;
@@ -337,7 +338,7 @@ wireless_new_auto_connection (NMDevice *device,
 
 	if (!info->ap) {
 		g_warning ("%s: AP not set", __func__);
-		return NULL;
+		return FALSE;
 	}
 
 	s_wireless = (NMSettingWireless *) nm_setting_wireless_new ();
@@ -357,7 +358,7 @@ wireless_new_auto_connection (NMDevice *device,
 	s_wireless_sec = get_security_for_ap (info->ap, dev_caps, &supported, &s_8021x);
 	if (!supported) {
 		g_object_unref (s_wireless);
-		goto out;
+		goto done;
 	} else if (s_wireless_sec)
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NULL);
 
@@ -387,8 +388,9 @@ wireless_new_auto_connection (NMDevice *device,
 
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
-out:
-	return connection;
+done:
+	(*callback) (connection, TRUE, FALSE, callback_data);
+	return TRUE;
 }
 
 static void
diff --git a/src/applet-device-wired.c b/src/applet-device-wired.c
index 09ca5e2..d116326 100644
--- a/src/applet-device-wired.c
+++ b/src/applet-device-wired.c
@@ -62,10 +62,11 @@ wired_menu_item_info_destroy (gpointer data)
 
 #define DEFAULT_WIRED_NAME _("Auto Ethernet")
 
-static NMConnection *
+static gboolean
 wired_new_auto_connection (NMDevice *device,
-                           NMApplet *applet,
-                           gpointer user_data)
+                           gpointer dclass_data,
+                           AppletNewAutoConnectionCallback callback,
+                           gpointer callback_data)
 {
 	NMConnection *connection;
 	NMSettingWired *s_wired = NULL;
@@ -89,7 +90,8 @@ wired_new_auto_connection (NMDevice *device,
 
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
-	return connection;
+	(*callback) (connection, TRUE, FALSE, callback_data);
+	return TRUE;
 }
 
 static void
diff --git a/src/applet.c b/src/applet.c
index 55fa9fc..9fcb012 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -296,44 +296,62 @@ activate_connection_cb (gpointer user_data, const char *path, GError *error)
 	applet_schedule_update_icon (NM_APPLET (user_data));
 }
 
-void
-applet_menu_item_activate_helper (NMDevice *device,
-                                  NMConnection *connection,
-                                  const char *specific_object,
-                                  NMApplet *applet,
-                                  gpointer user_data)
+typedef struct {
+	NMApplet *applet;
+	NMDevice *device;
+	char *specific_object;
+	gpointer dclass_data;
+} AppletItemActivateInfo;
+
+static void
+applet_item_activate_info_destroy (AppletItemActivateInfo *info)
 {
-	NMAGConfConnection *exported;
+	g_return_if_fail (info != NULL);
+
+	if (info->device)
+		g_object_unref (info->device);
+	g_free (info->specific_object);
+	memset (info, 0, sizeof (AppletItemActivateInfo));
+	g_free (info);
+}
+
+static void
+applet_menu_item_activate_helper_part2 (NMConnection *connection,
+                                        gboolean auto_created,
+                                        gboolean canceled,
+                                        gpointer user_data)
+{
+	AppletItemActivateInfo *info = user_data;
 	const char *con_path;
 	gboolean is_system = FALSE;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	if (canceled) {
+		applet_item_activate_info_destroy (info);
+		return;
+	}
 
-	if (connection) {
-		is_system = is_system_connection (connection);
-	} else {
-		NMADeviceClass *dclass = get_device_class (device, applet);
+	g_assert (connection);
 
-		/* If no connection was given, create a new default connection for this
-		 * device type.
-		 */
-		g_assert (dclass);
-		connection = dclass->new_auto_connection (device, applet, user_data);
-		if (!connection) {
-			nm_warning ("Couldn't create default connection.");
-			return;
-		}
+	if (!auto_created)
+		is_system = is_system_connection (connection);
+	else {
+		NMAGConfConnection *exported;
 
-		exported = nma_gconf_settings_add_connection (applet->gconf_settings, connection);
+		exported = nma_gconf_settings_add_connection (info->applet->gconf_settings, connection);
 		if (!exported) {
+			NMADeviceClass *dclass = get_device_class (info->device, info->applet);
+
 			/* If the setting isn't valid, because it needs more authentication
 			 * or something, ask the user for it.
 			 */
 
+			g_assert (dclass);
 			nm_warning ("Invalid connection; asking for more information.");
 			if (dclass->get_more_info)
-				dclass->get_more_info (device, connection, applet, user_data);
+				dclass->get_more_info (info->device, connection, info->applet, info->dclass_data);
 			g_object_unref (connection);
+
+			applet_item_activate_info_destroy (info);
 			return;
 		}
 		g_object_unref (connection);
@@ -344,13 +362,52 @@ applet_menu_item_activate_helper (NMDevice *device,
 	g_assert (con_path);
 
 	/* Finally, tell NM to activate the connection */
-	nm_client_activate_connection (applet->nm_client,
+	nm_client_activate_connection (info->applet->nm_client,
 	                               is_system ? NM_DBUS_SERVICE_SYSTEM_SETTINGS : NM_DBUS_SERVICE_USER_SETTINGS,
 	                               con_path,
-	                               device,
-	                               specific_object,
+	                               info->device,
+	                               info->specific_object,
 	                               activate_connection_cb,
-	                               applet);
+	                               info->applet);
+	applet_item_activate_info_destroy (info);
+}
+
+void
+applet_menu_item_activate_helper (NMDevice *device,
+                                  NMConnection *connection,
+                                  const char *specific_object,
+                                  NMApplet *applet,
+                                  gpointer dclass_data)
+{
+	AppletItemActivateInfo *info;
+	NMADeviceClass *dclass;
+
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	info = g_malloc0 (sizeof (AppletItemActivateInfo));
+	info->applet = applet;
+	info->specific_object = g_strdup (specific_object);
+	info->device = g_object_ref (device);
+	info->dclass_data = dclass_data;
+
+	if (connection) {
+		applet_menu_item_activate_helper_part2 (connection, FALSE, FALSE, info);
+		return;
+	}
+
+	dclass = get_device_class (device, applet);
+
+	/* If no connection was passed in, ask the device class to create a new
+	 * default connection for this device type.  This could be a wizard,
+	 * and thus take a while.
+	 */
+	g_assert (dclass);
+	if (!dclass->new_auto_connection (device, dclass_data,
+	                                  applet_menu_item_activate_helper_part2,
+	                                  info)) {
+		nm_warning ("Couldn't create default connection.");
+		applet_item_activate_info_destroy (info);
+	}
 }
 
 static void
diff --git a/src/applet.h b/src/applet.h
index f333447..acacc2e 100644
--- a/src/applet.h
+++ b/src/applet.h
@@ -146,11 +146,16 @@ typedef struct
 	gboolean		notify_with_actions;
 } NMApplet;
 
+typedef void (*AppletNewAutoConnectionCallback) (NMConnection *connection,
+                                                 gboolean created,
+                                                 gboolean canceled,
+                                                 gpointer user_data);
 
 struct NMADeviceClass {
-	NMConnection * (*new_auto_connection)  (NMDevice *device,
-	                                        NMApplet *applet,
-	                                        gpointer user_data);
+	gboolean       (*new_auto_connection)  (NMDevice *device,
+	                                        gpointer user_data,
+	                                        AppletNewAutoConnectionCallback callback,
+	                                        gpointer callback_data);
 
 	void           (*add_menu_item)        (NMDevice *device,
 	                                        guint32 num_devices,
@@ -204,7 +209,7 @@ void applet_menu_item_activate_helper (NMDevice *device,
                                        NMConnection *connection,
                                        const char *specific_object,
                                        NMApplet *applet,
-                                       gpointer user_data);
+                                       gpointer dclass_data);
 
 NMAGConfConnection *applet_get_exported_connection_for_device (NMDevice *device, NMApplet *applet);
 
diff --git a/src/connection-editor/Makefile.am b/src/connection-editor/Makefile.am
index aace784..7a3f8b5 100644
--- a/src/connection-editor/Makefile.am
+++ b/src/connection-editor/Makefile.am
@@ -7,6 +7,7 @@ nm_connection_editor_CPPFLAGS = \
 	-DBINDIR=\""$(bindir)"\" \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
 	-DLIBDIR=\""$(libdir)"\" \
+	-DDATADIR=\""$(datadir)"\" \
 	-DNMALOCALEDIR=\"$(datadir)/locale\" \
 	$(DBUS_CFLAGS) \
 	$(POLKIT_CFLAGS) \
@@ -43,8 +44,6 @@ nm_connection_editor_SOURCES = \
 	page-dsl.c \
 	page-mobile.h \
 	page-mobile.c \
-	mobile-wizard.h \
-	mobile-wizard.c \
 	page-ppp.h \
 	page-ppp.c \
 	page-vpn.h \
diff --git a/src/connection-editor/ce-page.c b/src/connection-editor/ce-page.c
index 96e1d4a..45e86cb 100644
--- a/src/connection-editor/ce-page.c
+++ b/src/connection-editor/ce-page.c
@@ -27,6 +27,9 @@
 
 #include <glib/gi18n.h>
 
+#include <nm-setting-connection.h>
+#include <nm-utils.h>
+
 #include "ce-page.h"
 #include "nma-marshal.h"
 #include "utils.h"
@@ -487,3 +490,42 @@ ce_page_class_init (CEPageClass *page_class)
 	                      nma_marshal_VOID__POINTER_POINTER,
 	                      G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
 }
+
+
+NMConnection *
+ce_page_new_connection (const char *format,
+                        const char *ctype,
+                        gboolean autoconnect,
+                        PageGetConnectionsFunc get_connections_func,
+                        gpointer user_data)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	char *uuid, *id;
+	GSList *connections;
+
+	connection = nm_connection_new ();
+	nm_connection_set_scope (connection, NM_CONNECTION_SCOPE_USER);
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+
+	connections = (*get_connections_func) (user_data);
+	id = utils_next_available_name (connections, format);
+	g_slist_free (connections);
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_TYPE, ctype,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, autoconnect,
+	              NULL);
+
+	g_free (uuid);
+	g_free (id);
+
+	return connection;
+}
+
diff --git a/src/connection-editor/ce-page.h b/src/connection-editor/ce-page.h
index 17dcbd6..63fabc0 100644
--- a/src/connection-editor/ce-page.h
+++ b/src/connection-editor/ce-page.h
@@ -32,6 +32,18 @@
 #include <dbus/dbus-glib.h>
 #include <nm-connection.h>
 
+typedef void (*PageNewConnectionResultFunc) (NMConnection *connection,
+                                             gboolean canceled,
+                                             GError *error,
+                                             gpointer user_data);
+
+typedef GSList * (*PageGetConnectionsFunc) (gpointer user_data);
+
+typedef void (*PageNewConnectionFunc) (GtkWindow *parent,
+                                       PageNewConnectionResultFunc result_func,
+                                       PageGetConnectionsFunc get_connections_func,
+                                       gpointer user_data);
+
 #define CE_TYPE_PAGE            (ce_page_get_type ())
 #define CE_PAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CE_TYPE_PAGE, CEPage))
 #define CE_PAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CE_TYPE_PAGE, CEPageClass))
@@ -100,5 +112,12 @@ gboolean ce_page_initialize (CEPage *self,
 
 gboolean ce_page_get_initialized (CEPage *self);
 
+/* Only for subclasses */
+NMConnection *ce_page_new_connection (const char *format,
+                                      const char *ctype,
+                                      gboolean autoconnect,
+                                      PageGetConnectionsFunc get_connections_func,
+                                      gpointer user_data);
+
 #endif  /* __CE_PAGE_H__ */
 
diff --git a/src/connection-editor/main.c b/src/connection-editor/main.c
index 5983b84..c2e0b15 100644
--- a/src/connection-editor/main.c
+++ b/src/connection-editor/main.c
@@ -37,6 +37,8 @@
 #include <dbus/dbus-glib-lowlevel.h>
 
 #include <nm-setting-wired.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
 #include "nm-connection-list.h"
 
 static GMainLoop *loop = NULL;
@@ -130,10 +132,19 @@ static gboolean
 impl_start (NMCEService *self, GHashTable *table, GError **error)
 {
 	GValue *value;
+	GType def_type;
+	const char *str_type;
 
 	value = g_hash_table_lookup (table, ARG_TYPE);
-	if (value && G_VALUE_HOLDS_STRING (value))
-		nm_connection_list_set_type (self->list, g_value_get_string (value));
+	if (value && G_VALUE_HOLDS_STRING (value)) {
+		str_type = g_value_get_string (value);
+		g_assert (str_type);
+
+		if (!strcmp (str_type, NM_SETTING_CDMA_SETTING_NAME))
+			str_type = NM_SETTING_GSM_SETTING_NAME;
+		def_type = nm_connection_lookup_setting_type (str_type);
+		nm_connection_list_set_type (self->list, def_type);
+	}
 	nm_connection_list_present (self->list);
 
 	return TRUE;
@@ -213,6 +224,7 @@ main (int argc, char *argv[])
 	NMConnectionList *list;
 	DBusGConnection *bus;
 	char *type = NULL;
+	GType ctype;
 	NMCEService *service = NULL;
 	DBusGProxy *proxy = NULL;
 
@@ -244,9 +256,9 @@ main (int argc, char *argv[])
 	/* Inits the dbus-glib type system too */
 	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
 	proxy = dbus_g_proxy_new_for_name (bus,
-									 "org.freedesktop.DBus",
-									 "/org/freedesktop/DBus",
-									 "org.freedesktop.DBus");
+	                                   "org.freedesktop.DBus",
+	                                   "/org/freedesktop/DBus",
+	                                   "org.freedesktop.DBus");
 
 	/* Check for an existing instance on the bus */
 	if (proxy) {
@@ -256,7 +268,13 @@ main (int argc, char *argv[])
 
 	loop = g_main_loop_new (NULL, FALSE);
 
-	list = nm_connection_list_new (type);
+	if (!type)
+		type = (char *) NM_SETTING_WIRED_SETTING_NAME;
+	if (!strcmp (type, NM_SETTING_CDMA_SETTING_NAME))
+		type = (char *) NM_SETTING_GSM_SETTING_NAME;
+
+	ctype = nm_connection_lookup_setting_type (type);
+	list = nm_connection_list_new (ctype);
 	if (!list) {
 		g_warning ("Failed to initialize the UI, exiting...");
 		return 1;
diff --git a/src/connection-editor/mobile-wizard.c b/src/connection-editor/mobile-wizard.c
deleted file mode 100644
index 23e5e65..0000000
--- a/src/connection-editor/mobile-wizard.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager Connection editor -- Connection editor for NetworkManager
- *
- * 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 2008 Red Hat, Inc.
- */
-
-#include <glib.h>
-#include <glib/gi18n.h>
-
-#include <glade/glade.h>
-#include <gtk/gtk.h>
-
-#include <NetworkManager.h>
-#include <nm-client.h>
-#include <nm-setting-gsm.h>
-#include <nm-setting-cdma.h>
-#include <nm-gsm-device.h>
-#include <nm-cdma-device.h>
-
-#include "mobile-wizard.h"
-#include "utils.h"
-
-#define DEVICE_TAG "device"
-#define TYPE_TAG "setting-type"
-
-static void
-ok_clicked (GtkButton *button, gpointer user_data)
-{
-	gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_OK);
-}
-
-static void
-cancel_clicked (GtkButton *button, gpointer user_data)
-{
-	gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_CANCEL);
-}
-
-typedef struct {
-	NMClient *client;
-	GladeXML *xml;
-	GtkWidget *dialog;
-	GtkWidget *radio;
-} MobileWizardInfo;
-
-static void
-device_added_cb (NMClient *client, NMDevice *device, MobileWizardInfo *info)
-{
-	GtkWidget *vbox;
-	GtkWidget *radio = NULL;
-	char *desc = (char *) utils_get_device_description (device);
-
-	vbox = glade_xml_get_widget (info->xml, "options_vbox");
-	g_assert (vbox);
-
-	if (G_OBJECT_TYPE (device) == NM_TYPE_GSM_DEVICE) {
-		if (!desc)
-			desc = _("Installed GSM device");
-
-		radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (info->radio), desc);
-
-		g_object_set_data (G_OBJECT (radio), TYPE_TAG,
-		                   GUINT_TO_POINTER (NM_TYPE_SETTING_GSM));
-		g_object_set_data_full (G_OBJECT (radio), DEVICE_TAG,
-		                        g_object_ref (device),
-		                        (GDestroyNotify) g_object_unref);
-	} else if (G_OBJECT_TYPE (device) == NM_TYPE_CDMA_DEVICE) {
-		if (!desc)
-			desc = _("Installed CDMA device");
-
-		radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (info->radio), desc);
-
-		g_object_set_data (G_OBJECT (radio), TYPE_TAG,
-		                   GUINT_TO_POINTER (NM_TYPE_SETTING_CDMA));
-		g_object_set_data_full (G_OBJECT (radio), DEVICE_TAG,
-		                        g_object_ref (device),
-		                        (GDestroyNotify) g_object_unref);
-	}
-
-	if (radio) {
-		gtk_box_pack_end (GTK_BOX (vbox), radio, FALSE, FALSE, 0);
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
-		gtk_widget_show_all (radio);
-	}
-}
-
-static void
-device_removed_cb (NMClient *client, NMDevice *device, MobileWizardInfo *info)
-{
-	GtkWidget *vbox, *prev = NULL;
-	GList *children, *iter;
-
-	vbox = glade_xml_get_widget (info->xml, "options_vbox");
-	g_assert (vbox);
-
-	children = gtk_container_get_children (GTK_CONTAINER (vbox));
-	for (iter = children; iter; iter = g_list_next (iter)) {
-		if (g_object_get_data (G_OBJECT (iter->data), DEVICE_TAG) == device) {
-			gtk_widget_destroy (GTK_WIDGET (iter->data));
-			break;
-		}
-		prev = iter->data;
-	}
-
-	/* Select the item just above the removed item, or if there aren't
-	 * any device-based items left in the list, the default one.
-	 */
-	if (!prev)
-		prev = info->radio;
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prev), TRUE);
-}
-
-static void
-add_initial_devices (MobileWizardInfo *info)
-{
-	GtkWidget *vbox;
-	const GPtrArray *devices;
-	int i;
-
-	vbox = glade_xml_get_widget (info->xml, "options_vbox");
-	g_assert (vbox);
-
-	devices = nm_client_get_devices (info->client);
-	for (i = 0; devices && (i < devices->len); i++)
-		device_added_cb (info->client, g_ptr_array_index (devices, i), info);
-
-	gtk_widget_show_all (vbox);
-}
-
-static GtkWidget *
-add_generic_options (GladeXML *xml)
-{
-	GtkWidget *vbox;
-	GtkWidget *radio;
-	const char *gsm_desc = _("Create a GSM connection");
-	const char *cdma_desc = _("Create a CDMA connection");
-
-	vbox = glade_xml_get_widget (xml, "options_vbox");
-	g_assert (vbox);
-
-	radio = gtk_radio_button_new_with_label (NULL, cdma_desc);
-	g_object_set_data (G_OBJECT (radio), TYPE_TAG, GUINT_TO_POINTER (NM_TYPE_SETTING_CDMA));
-	gtk_box_pack_end (GTK_BOX (vbox), radio, FALSE, FALSE, 0);
-	gtk_widget_show (radio);
-
-	radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio), gsm_desc);
-	g_object_set_data (G_OBJECT (radio), TYPE_TAG, GUINT_TO_POINTER (NM_TYPE_SETTING_GSM));
-	gtk_box_pack_end (GTK_BOX (vbox), radio, FALSE, FALSE, 0);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
-	gtk_widget_show (radio);
-
-	return radio;
-}
-
-static void
-remove_all_devices (GladeXML *xml)
-{
-	GtkWidget *vbox;
-	GList *children, *iter;
-
-	vbox = glade_xml_get_widget (xml, "options_vbox");
-	g_assert (vbox);
-
-	children = gtk_container_get_children (GTK_CONTAINER (vbox));
-	for (iter = children; iter; iter = g_list_next (iter)) {
-		if (g_object_get_data (G_OBJECT (iter->data), DEVICE_TAG))
-			gtk_widget_destroy (GTK_WIDGET (iter->data));
-	}
-}
-
-static void
-manager_running_cb (NMClient *client, GParamSpec *pspec, MobileWizardInfo *info)
-{
-	if (nm_client_get_manager_running (client))
-		add_initial_devices (info);
-	else
-		remove_all_devices (info->xml);
-}
-
-GType
-mobile_wizard_ask_connection_type (void)
-{
-	MobileWizardInfo info;
-	GType choice = 0;
-	gint response;
-	GtkWidget *widget;
-	GtkWidget *vbox;
-
-	info.xml = glade_xml_new (GLADEDIR "/ce-mobile-wizard.glade", "new_connection_dialog", NULL);
-	if (!info.xml) {
-		g_warning ("Could not load Glade file for new mobile broadband connection dialog");
-		return 0;
-	}
-
-	info.dialog = glade_xml_get_widget (info.xml, "new_connection_dialog");
-	if (!info.dialog) {
-		g_warning ("Could not load Glade file for new mobile broadband connection dialog");
-		return 0;
-	}
-
-	widget = glade_xml_get_widget (info.xml, "ok_button");
-	g_signal_connect (widget, "clicked", G_CALLBACK (ok_clicked), info.dialog);
-
-	widget = glade_xml_get_widget (info.xml, "cancel_button");
-	g_signal_connect (widget, "clicked", G_CALLBACK (cancel_clicked), info.dialog);
-
-	info.client = nm_client_new ();
-	g_signal_connect (info.client, "device-added", G_CALLBACK (device_added_cb), &info);
-	g_signal_connect (info.client, "device-removed", G_CALLBACK (device_removed_cb), &info);
-	g_signal_connect (info.client, "notify::manager-running", G_CALLBACK (manager_running_cb), &info);
-
-	vbox = glade_xml_get_widget (info.xml, "options_vbox");
-
-	info.radio = add_generic_options (info.xml);
-
-	/* If NM is running, add the mobile broadband devices it knows about */
-	if (nm_client_get_manager_running (info.client))
-		add_initial_devices (&info);
-
-	/* Ask the user which type of connection they want to create */
-	response = gtk_dialog_run (GTK_DIALOG (info.dialog));
-	gtk_widget_hide (info.dialog);
-	if (response == GTK_RESPONSE_OK) {
-		GList *children, *iter;
-
-		/* Get the radio button the user selected */
-		children = gtk_container_get_children (GTK_CONTAINER (vbox));
-		for (iter = children; iter; iter = g_list_next (iter)) {
-			if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (iter->data))) {
-				choice = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iter->data), TYPE_TAG));
-				break;
-			}
-		}
-	}
-
-	g_object_unref (info.client);
-	g_object_unref (info.xml);	
-	return choice;
-}
-
diff --git a/src/connection-editor/mobile-wizard.h b/src/connection-editor/mobile-wizard.h
deleted file mode 100644
index ccb7914..0000000
--- a/src/connection-editor/mobile-wizard.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager Connection editor -- Connection editor for NetworkManager
- *
- * 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 2008 Red Hat, Inc.
- */
-
-#ifndef MOBILE_WIZARD_H
-#define MOBILE_WIZARD_H
-
-#include <glib.h>
-
-GType mobile_wizard_ask_connection_type (void);
-
-
-#endif /* MOBILE_WIZARD_H */
diff --git a/src/connection-editor/nm-connection-list.c b/src/connection-editor/nm-connection-list.c
index d0128aa..ae99aa7 100644
--- a/src/connection-editor/nm-connection-list.c
+++ b/src/connection-editor/nm-connection-list.c
@@ -18,7 +18,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
  */
 
 #include <config.h>
@@ -51,10 +51,15 @@
 #include <nm-vpn-plugin-ui-interface.h>
 #include <nm-utils.h>
 
+#include "ce-page.h"
+#include "page-wired.h"
+#include "page-wireless.h"
+#include "page-mobile.h"
+#include "page-dsl.h"
+#include "page-vpn.h"
 #include "nm-connection-editor.h"
 #include "nm-connection-list.h"
 #include "gconf-helpers.h"
-#include "mobile-wizard.h"
 #include "utils.h"
 #include "vpn-helpers.h"
 #include "polkit-helpers.h"
@@ -80,6 +85,7 @@ typedef struct {
 	GtkWidget *button;
 	PolKitAction *action;
 	PolKitGnomeAction *gnome_action;
+	PageNewConnectionFunc new_func;
 } ActionInfo;
 
 static void
@@ -132,6 +138,25 @@ get_active_connection (GtkTreeView *treeview)
 	return exported;
 }
 
+#define TV_TYPE_TAG "ctype"
+
+static GtkTreeView *
+get_treeview_for_type (NMConnectionList *list, GType ctype)
+{
+	GSList *iter;
+
+	for (iter = list->treeviews; iter; iter = g_slist_next (iter)) {
+		GtkTreeView *candidate = GTK_TREE_VIEW (iter->data);
+		GType candidate_type;
+
+		candidate_type = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (candidate), TV_TYPE_TAG));
+		if (candidate_type == ctype)
+			return candidate;
+	}
+
+	return NULL;
+}
+
 static GtkListStore *
 get_model_for_connection (NMConnectionList *list, NMExportedConnection *exported)
 {
@@ -139,20 +164,23 @@ get_model_for_connection (NMConnectionList *list, NMExportedConnection *exported
 	NMSettingConnection *s_con;
 	GtkTreeView *treeview;
 	GtkTreeModel *model;
-	const char *connection_type;
+	const char *str_type;
 
 	connection = nm_exported_connection_get_connection (exported);
 	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
-	connection_type = s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;
+	str_type = s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;
 
-	if (!connection_type) {
+	if (!str_type) {
 		g_warning ("Ignoring incomplete connection");
 		return NULL;
 	}
 
-	treeview = (GtkTreeView *) g_hash_table_lookup (list->treeviews, connection_type);
+	if (!strcmp (str_type, NM_SETTING_CDMA_SETTING_NAME))
+		str_type = NM_SETTING_GSM_SETTING_NAME;
+
+	treeview = get_treeview_for_type (list, nm_connection_lookup_setting_type (str_type));
 	if (!treeview) {
-		g_warning ("No registered treeview for connection type '%s'", connection_type);
+		g_warning ("No registered treeview for connection type '%s'", str_type);
 		return NULL;
 	}
 
@@ -750,277 +778,80 @@ add_done_cb (NMConnectionEditor *editor, gint response, GError *error, gpointer
 	g_hash_table_remove (info->list->editors, connection);
 }
 
-static void
-add_one_name (gpointer data, gpointer user_data)
-{
-	NMExportedConnection *exported = NM_EXPORTED_CONNECTION (data);
-	NMConnection *connection;
-	NMSettingConnection *s_con;
-	const char *id;
-	GSList **list = (GSList **) user_data;
-
-	connection = nm_exported_connection_get_connection (exported);
-	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
-	id = nm_setting_connection_get_id (s_con);
-	g_assert (id);
-	*list = g_slist_append (*list, (gpointer) id);
-}
-
-static char *
-get_next_available_name (NMConnectionList *list, const char *format)
-{
-	GSList *connections;
-	GSList *names = NULL, *iter;
-	char *cname = NULL;
-	int i = 0;
-
-	connections = nm_settings_list_connections (NM_SETTINGS (list->system_settings));
-	connections = g_slist_concat (connections, nm_settings_list_connections (NM_SETTINGS (list->gconf_settings)));
-
-	g_slist_foreach (connections, add_one_name, &names);
-	g_slist_free (connections);
-
-	if (g_slist_length (names) == 0)
-		return g_strdup_printf (format, 1);
-
-	/* Find the next available unique connection name */
-	while (!cname && (i++ < 10000)) {
-		char *temp;
-		gboolean found = FALSE;
-
-		temp = g_strdup_printf (format, i);
-		for (iter = names; iter; iter = g_slist_next (iter)) {
-			if (!strcmp (iter->data, temp)) {
-				found = TRUE;
-				break;
-			}
-		}
-		if (!found)
-			cname = temp;
-		else
-			g_free (temp);
-	}
-
-	g_slist_free (names);
-	return cname;
-}
+typedef struct {
+	NMConnection *new;
+	NMConnectionList *list;
+} AddConnectionInfo;
 
 static void
-add_default_serial_setting (NMConnection *connection)
+really_add_connection (NMConnection *connection,
+                       gboolean canceled,
+                       GError *error,
+                       gpointer user_data)
 {
-	NMSettingSerial *s_serial;
-
-	s_serial = NM_SETTING_SERIAL (nm_setting_serial_new ());
-	g_object_set (s_serial,
-	              NM_SETTING_SERIAL_BAUD, 115200,
-	              NM_SETTING_SERIAL_BITS, 8,
-	              NM_SETTING_SERIAL_PARITY, 'n',
-	              NM_SETTING_SERIAL_STOPBITS, 1,
-	              NULL);
+	ActionInfo *info = user_data;
+	NMConnectionEditor *editor;
+	GError *editor_error = NULL;
+	const char *message = _("The connection editor dialog could not be initialized due to an unknown error.");
+	gboolean can_modify;
 
-	nm_connection_add_setting (connection, NM_SETTING (s_serial));
-}
+	g_return_if_fail (info != NULL);
 
-static NMConnection *
-create_new_connection_for_type (NMConnectionList *list, const char *connection_type)
-{
-	GType ctype;
-	NMConnection *connection = NULL;
-	NMSettingConnection *s_con;
-	NMSetting *type_setting = NULL;
-	char *id, *uuid;
-	GType mb_type;
-
-	ctype = nm_connection_lookup_setting_type (connection_type);
-
-	connection = nm_connection_new ();
-	nm_connection_set_scope (connection, NM_CONNECTION_SCOPE_USER);
-	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (s_con, NM_SETTING_CONNECTION_UUID, uuid, NULL);
-	g_free (uuid);
-	nm_connection_add_setting (connection, NM_SETTING (s_con));
-
-	if (ctype == NM_TYPE_SETTING_WIRED) {
-		id = get_next_available_name (list, _("Wired connection %d"));
-		g_object_set (s_con,
-		              NM_SETTING_CONNECTION_ID, id,
-		              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
-		              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
-		              NULL);
-		g_free (id);
-
-		type_setting = nm_setting_wired_new ();
-	} else if (ctype == NM_TYPE_SETTING_WIRELESS) {
-		NMSettingWireless *s_wireless;
-
-		id = get_next_available_name (list, _("Wireless connection %d"));
-		g_object_set (s_con,
-		              NM_SETTING_CONNECTION_ID, id,
-		              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
-		              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
-		              NULL);
-		g_free (id);
-
-		type_setting = nm_setting_wireless_new ();
-		s_wireless = NM_SETTING_WIRELESS (type_setting);
-		g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, "infrastructure", NULL);
-	} else if ((ctype == NM_TYPE_SETTING_GSM) || (ctype == NM_TYPE_SETTING_CDMA)) {
-		/* Since GSM is a placeholder for both GSM and CDMA; ask the user which
-		 * one they really want.
-		 */
-		mb_type = mobile_wizard_ask_connection_type ();
-		if (mb_type == NM_TYPE_SETTING_GSM) {
-			NMSettingGsm *s_gsm;
-
-			id = get_next_available_name (list, _("GSM connection %d"));
-			g_object_set (s_con,
-					    NM_SETTING_CONNECTION_ID, id,
-					    NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME,
-					    NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-					    NULL);
-			g_free (id);
-
-			add_default_serial_setting (connection);
-
-			type_setting = nm_setting_gsm_new ();
-			s_gsm = NM_SETTING_GSM (type_setting);
-			/* De-facto standard for GSM */
-			g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
-			nm_connection_add_setting (connection, nm_setting_ppp_new ());
-		} else if (mb_type == NM_TYPE_SETTING_CDMA) {
-			NMSettingCdma *s_cdma;
-
-			id = get_next_available_name (list, _("CDMA connection %d"));
-			g_object_set (s_con,
-					    NM_SETTING_CONNECTION_ID, id,
-					    NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME,
-					    NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-					    NULL);
-			g_free (id);
-
-			add_default_serial_setting (connection);
-
-			type_setting = nm_setting_cdma_new ();
-			s_cdma = NM_SETTING_CDMA (type_setting);
-
-			/* De-facto standard for CDMA */
-			g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
-
-			nm_connection_add_setting (connection, nm_setting_ppp_new ());
-		} else {
-			/* user canceled; do nothing */
-		}
-	} else if (ctype == NM_TYPE_SETTING_VPN) {
-		char *service = NULL;
-
-		service = vpn_ask_connection_type ();
-		if (service) {
-			NMSettingVPN *s_vpn;
-
-			id = get_next_available_name (list, _("VPN connection %d"));
-			g_object_set (s_con,
-					    NM_SETTING_CONNECTION_ID, id,
-					    NM_SETTING_CONNECTION_TYPE, NM_SETTING_VPN_SETTING_NAME,
-					    NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-					    NULL);
-			g_free (id);
-
-			type_setting = nm_setting_vpn_new ();
-			s_vpn = NM_SETTING_VPN (type_setting);
-			g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service, NULL);
-			g_free (service);
-		}		
-	} else if (ctype == NM_TYPE_SETTING_PPPOE) {
-		id = get_next_available_name (list, _("DSL connection %d"));
-		g_object_set (s_con,
-				    NM_SETTING_CONNECTION_ID, id,
-				    NM_SETTING_CONNECTION_TYPE, NM_SETTING_PPPOE_SETTING_NAME,
-				    NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-				    NULL);
-		g_free (id);
-
-		type_setting = nm_setting_pppoe_new ();
-
-		nm_connection_add_setting (connection, nm_setting_wired_new ());
-		nm_connection_add_setting (connection, nm_setting_ppp_new ());
-	} else {
-		g_warning ("%s: unhandled connection type '%s'", __func__, g_type_name (ctype)); 
-	}
+	if (canceled)
+		return;
 
-	if (type_setting) {
-		nm_connection_add_setting (connection, type_setting);
-	} else {
-		g_object_unref (connection);
-		connection = NULL;
+	if (!connection) {
+		error_dialog (info->list_window,
+		              _("Could not create new connection"),
+		              "%s",
+		              (error && error->message) ? error->message : message);
+		return;
 	}
 
-	return connection;
-}
-
-typedef struct {
-	const char *type;
-	GtkTreeView *treeview;
-} LookupTreeViewInfo;
+	can_modify = nm_dbus_settings_system_get_can_modify (info->list->system_settings);
+	editor = nm_connection_editor_new (connection, can_modify, &error);
+	if (!editor) {
+		error_dialog (info->list_window,
+		              _("Could not edit new connection"),
+		              "%s",
+		              (editor_error && editor_error->message) ? editor_error->message : message);
+		g_clear_error (&editor_error);
+		return;
+	}
 
-static void
-lookup_treeview (gpointer key, gpointer value, gpointer user_data)
-{
-	LookupTreeViewInfo *info = (LookupTreeViewInfo *) user_data;
+	g_signal_connect (G_OBJECT (editor), "done", G_CALLBACK (add_done_cb), info);
+	g_hash_table_insert (info->list->editors, connection, editor);
 
-	if (!info->type && info->treeview == value)
-		info->type = (const char *) key;
+	nm_connection_editor_run (editor);
 }
 
-static const char *
-get_connection_type_from_treeview (NMConnectionList *self,
-							GtkTreeView *treeview)
+static GSList *
+page_get_connections (gpointer user_data)
 {
-	LookupTreeViewInfo info;
-
-	info.type = NULL;
-	info.treeview = treeview;
-
-	g_hash_table_foreach (self->treeviews, lookup_treeview, &info);
+	ActionInfo *info = (ActionInfo *) user_data;
 
-	return info.type;
+	return g_slist_concat (nm_settings_list_connections (NM_SETTINGS (info->list->system_settings)),
+	                       nm_settings_list_connections (NM_SETTINGS (info->list->gconf_settings)));
 }
 
 static void
 add_connection_clicked (GtkButton *button, gpointer user_data)
 {
 	ActionInfo *info = (ActionInfo *) user_data;
-	const char *connection_type;
-	NMConnection *connection;
-	NMConnectionEditor *editor;
-	GError *error = NULL;
-	const char *message = _("The connection editor dialog could not be initialized due to an unknown error.");
-
-	connection_type = get_connection_type_from_treeview (info->list, info->treeview);
-	g_assert (connection_type);
+	NMConnectionList *list = info->list;
+	GType ctype;
 
-	connection = create_new_connection_for_type (info->list, connection_type);
-	if (!connection) {
-		g_warning ("Can't add new connection of type '%s'", connection_type);
+	if (!info->new_func) {
+		ctype = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (info->treeview), TV_TYPE_TAG));
+		g_warning ("No new-connection function registered for type '%s'",
+		           g_type_name (ctype));
 		return;
 	}
 
-	editor = nm_connection_editor_new (connection,
-	                                   nm_dbus_settings_system_get_can_modify (info->list->system_settings),
-	                                   &error);
-	if (!editor) {
-		error_dialog (info->list_window,
-		              _("Could not edit new connection"),
-		              "%s",
-		              (error && error->message) ? error->message : message);
-		return;
-	}
-
-	g_signal_connect (G_OBJECT (editor), "done", G_CALLBACK (add_done_cb), info);
-	g_hash_table_insert (info->list->editors, connection, editor);
-
-	nm_connection_editor_run (editor);
+	(*(info->new_func)) (GTK_WINDOW (list->dialog),
+	                     really_add_connection,
+	                     page_get_connections,
+	                     info);
 }
 
 typedef struct {
@@ -1321,9 +1152,17 @@ import_success_cb (NMConnection *connection, gpointer user_data)
 
 	s = (char *) nm_setting_connection_get_id (s_con);
 	if (!s) {
-		s = get_next_available_name (info->list, _("VPN connection %d"));
+		GSList *connections;
+
+		connections = nm_settings_list_connections (NM_SETTINGS (info->list->system_settings));
+		connections = g_slist_concat (connections,
+		                              nm_settings_list_connections (NM_SETTINGS (info->list->gconf_settings)));
+
+		s = utils_next_available_name (connections, _("VPN connection %d"));
 		g_object_set (s_con, NM_SETTING_CONNECTION_ID, s, NULL);
 		g_free (s);
+
+		g_slist_free (connections);
 	}
 
 	s = (char *) nm_setting_connection_get_connection_type (s_con);
@@ -1418,8 +1257,6 @@ dialog_response_cb (GtkDialog *dialog, guint response, gpointer user_data)
 static void
 nm_connection_list_init (NMConnectionList *list)
 {
-	list->treeviews = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
 	list->system_action = polkit_action_new ();
 	polkit_action_set_action_id (list->system_action, "org.freedesktop.network-manager-settings.system.modify");
 }
@@ -1455,7 +1292,7 @@ dispose (GObject *object)
 	if (list->client)
 		g_object_unref (list->client);
 
-	g_hash_table_destroy (list->treeviews);
+	g_slist_free (list->treeviews);
 
 	if (list->gconf_settings)
 		g_object_unref (list->gconf_settings);
@@ -1576,6 +1413,15 @@ action_info_set_button (ActionInfo *info,
 }
 
 static void
+action_info_set_new_func (ActionInfo *info,
+                          PageNewConnectionFunc func)
+{
+	g_return_if_fail (info != NULL);
+
+	info->new_func = func;
+}
+
+static void
 check_vpn_import_supported (gpointer key, gpointer data, gpointer user_data)
 {
 	NMVpnPluginUiInterface *plugin = NM_VPN_PLUGIN_UI_INTERFACE (data);
@@ -1654,7 +1500,8 @@ static void
 add_connection_buttons (NMConnectionList *self,
                         const char *prefix,
                         GtkTreeView *treeview,
-                        gboolean is_vpn)
+                        GType ctype,
+                        PageNewConnectionFunc new_func)
 {
 	char *name;
 	GtkWidget *button, *hbox;
@@ -1670,13 +1517,15 @@ add_connection_buttons (NMConnectionList *self,
 	g_free (name);
 	info = action_info_new (self, treeview, GTK_WINDOW (self->dialog), NULL, self->system_action);
 	g_signal_connect (button, "clicked", G_CALLBACK (add_connection_clicked), info);
-	if (is_vpn) {
+	if (ctype == NM_TYPE_SETTING_VPN) {
 		GHashTable *plugins;
 
 		/* disable the "Add..." button if there aren't any VPN plugins */
 		plugins = vpn_get_plugins (NULL);
 		gtk_widget_set_sensitive (button, (plugins && g_hash_table_size (plugins)));
 	}
+	if (new_func)
+		action_info_set_new_func (info, new_func);
 
 	name = g_strdup_printf ("%s_button_box", prefix);
 	hbox = glade_xml_get_widget (self->gui, name);
@@ -1749,17 +1598,17 @@ add_connection_buttons (NMConnectionList *self,
 
 static void
 add_connection_tab (NMConnectionList *self,
-                    const char *def_type,
-                    GSList *connection_types,
+                    GType def_type,
+                    GType ctype,
                     GdkPixbuf *pixbuf,
                     const char *prefix,
                     const char *label_text,
-                    gboolean is_vpn)
+                    PageNewConnectionFunc new_func)
 {
 	char *name;
 	GtkWidget *child, *hbox, *notebook;
 	GtkTreeView *treeview;
-	GSList *iter;
+	int pnum;
 
 	name = g_strdup_printf ("%s_child", prefix);
 	child = glade_xml_get_widget (self->gui, name);
@@ -1780,42 +1629,39 @@ add_connection_tab (NMConnectionList *self,
 	gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), child, hbox);
 
 	treeview = add_connection_treeview (self, prefix);
-	add_connection_buttons (self, prefix, treeview, is_vpn);
-
-	g_object_set_data_full (G_OBJECT (child), "types",
-	                        connection_types, (GDestroyNotify) g_slist_free);
+	add_connection_buttons (self, prefix, treeview, ctype, new_func);
 
-	for (iter = connection_types; iter; iter = iter->next) {
-		g_hash_table_insert (self->treeviews, g_strdup ((const char *) iter->data), treeview);
-		if (def_type && !strcmp ((const char *) iter->data, def_type)) {
-			int pnum;
+	g_object_set_data (G_OBJECT (treeview), TV_TYPE_TAG, GUINT_TO_POINTER (ctype));
+	self->treeviews = g_slist_prepend (self->treeviews, treeview);
 
-			pnum = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), child);
-			gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), pnum);
-		}
+	if (def_type == ctype) {
+		pnum = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), child);
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), pnum);
 	}
 }
 
 static void
-add_connection_tabs (NMConnectionList *self, const char *def_type)
+add_connection_tabs (NMConnectionList *self, GType def_type)
 {
-	GSList *types;
+	add_connection_tab (self, def_type, NM_TYPE_SETTING_WIRED,
+	                    self->wired_icon, "wired", _("Wired"),
+	                    wired_connection_new);
 
-	types = g_slist_append (NULL, NM_SETTING_WIRED_SETTING_NAME);
-	add_connection_tab (self, def_type, types, self->wired_icon, "wired", _("Wired"), FALSE);
+	add_connection_tab (self, def_type, NM_TYPE_SETTING_WIRELESS,
+	                    self->wireless_icon, "wireless", _("Wireless"),
+	                    wifi_connection_new);
 
-	types = g_slist_append (NULL, NM_SETTING_WIRELESS_SETTING_NAME);
-	add_connection_tab (self, def_type, types, self->wireless_icon, "wireless", _("Wireless"), FALSE);
+	add_connection_tab (self, def_type, NM_TYPE_SETTING_GSM,
+	                    self->wwan_icon, "wwan", _("Mobile Broadband"),
+	                    mobile_connection_new);
 
-	types = g_slist_append (NULL, NM_SETTING_GSM_SETTING_NAME);
-	types = g_slist_append (types, NM_SETTING_CDMA_SETTING_NAME);
-	add_connection_tab (self, def_type, types, self->wwan_icon, "wwan", _("Mobile Broadband"), FALSE);
+	add_connection_tab (self, def_type, NM_TYPE_SETTING_VPN,
+	                    self->vpn_icon, "vpn", _("VPN"),
+	                    vpn_connection_new);
 
-	types = g_slist_append (NULL, NM_SETTING_VPN_SETTING_NAME);
-	add_connection_tab (self, def_type, types, self->vpn_icon, "vpn", _("VPN"), TRUE);
-
-	types = g_slist_append (NULL, NM_SETTING_PPPOE_SETTING_NAME);
-	add_connection_tab (self, def_type, types, self->wired_icon, "dsl", _("DSL"), FALSE);
+	add_connection_tab (self, def_type, NM_TYPE_SETTING_PPPOE,
+	                    self->wired_icon, "dsl", _("DSL"),
+	                    dsl_connection_new);
 }
 
 static void
@@ -1886,7 +1732,7 @@ connection_added (NMSettings *settings,
 	}
 
 NMConnectionList *
-nm_connection_list_new (const char *def_type)
+nm_connection_list_new (GType def_type)
 {
 	NMConnectionList *list;
 	DBusGConnection *dbus_connection;
@@ -1966,31 +1812,26 @@ nm_connection_list_present (NMConnectionList *list)
 }
 
 void
-nm_connection_list_set_type (NMConnectionList *self, const char *type)
+nm_connection_list_set_type (NMConnectionList *self, GType ctype)
 {
-	GtkWidget *notebook;
-	int i, num;
-	gboolean found = FALSE;
+	GtkNotebook *notebook;
+	int i;
 
 	g_return_if_fail (NM_IS_CONNECTION_LIST (self));
 
 	/* If a notebook page is found that owns the requested type, set it
 	 * as the current page.
 	 */
-	notebook = glade_xml_get_widget (self->gui, "list_notebook");
-	num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
-	for (i = 0; i < num && !found; i++) {
+	notebook = GTK_NOTEBOOK (glade_xml_get_widget (self->gui, "list_notebook"));
+	for (i = 0; i < gtk_notebook_get_n_pages (notebook); i++) {
 		GtkWidget *child;
-		GSList *types, *iter;
-
-		child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
-		types = g_object_get_data (G_OBJECT (child), "types");
-		for (iter = types; iter; iter = g_slist_next (iter)) {
-			if (!strcmp (type, (const char *) iter->data)) {
-				gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
-				found = TRUE;
-				break;
-			}
+		GType child_type;
+
+		child = gtk_notebook_get_nth_page (notebook, i);
+		child_type = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), TV_TYPE_TAG));
+		if (child_type == ctype) {
+			gtk_notebook_set_current_page (notebook, i);
+			break;
 		}
 	}
 
diff --git a/src/connection-editor/nm-connection-list.h b/src/connection-editor/nm-connection-list.h
index 800873a..8038207 100644
--- a/src/connection-editor/nm-connection-list.h
+++ b/src/connection-editor/nm-connection-list.h
@@ -45,7 +45,7 @@ typedef struct {
 
 	/* private data */
 	GHashTable *editors;
-	GHashTable *treeviews;
+	GSList *treeviews;
 
 	GConfClient *client;
 	NMAGConfSettings *gconf_settings;
@@ -72,11 +72,11 @@ typedef struct {
 } NMConnectionListClass;
 
 GType             nm_connection_list_get_type (void);
-NMConnectionList *nm_connection_list_new (const char *def_type);
+NMConnectionList *nm_connection_list_new (GType def_type);
 
 void              nm_connection_list_run (NMConnectionList *list);
 
-void              nm_connection_list_set_type (NMConnectionList *list, const char *type);
+void              nm_connection_list_set_type (NMConnectionList *list, GType ctype);
 
 void              nm_connection_list_present (NMConnectionList *list);
 
diff --git a/src/connection-editor/page-dsl.c b/src/connection-editor/page-dsl.c
index 93d5175..0de10c6 100644
--- a/src/connection-editor/page-dsl.c
+++ b/src/connection-editor/page-dsl.c
@@ -28,6 +28,7 @@
 #include <nm-setting-connection.h>
 #include <nm-setting-pppoe.h>
 #include <nm-setting-ppp.h>
+#include <nm-setting-wired.h>
 
 #include "page-dsl.h"
 #include "nm-connection-editor.h"
@@ -242,3 +243,26 @@ ce_page_dsl_class_init (CEPageDslClass *dsl_class)
 	/* virtual methods */
 	parent_class->validate = validate;
 }
+
+
+void
+dsl_connection_new (GtkWindow *parent,
+                    PageNewConnectionResultFunc result_func,
+                    PageGetConnectionsFunc get_connections_func,
+                    gpointer user_data)
+{
+	NMConnection *connection;
+
+	connection = ce_page_new_connection (_("DSL connection %d"),
+	                                     NM_SETTING_PPPOE_SETTING_NAME,
+	                                     FALSE,
+	                                     get_connections_func,
+	                                     user_data);
+	nm_connection_add_setting (connection, nm_setting_pppoe_new ());
+	nm_connection_add_setting (connection, nm_setting_wired_new ());
+	nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+	(*result_func) (connection, FALSE, NULL, user_data);
+}
+
+
diff --git a/src/connection-editor/page-dsl.h b/src/connection-editor/page-dsl.h
index be5b522..8e19e14 100644
--- a/src/connection-editor/page-dsl.h
+++ b/src/connection-editor/page-dsl.h
@@ -49,4 +49,9 @@ GType ce_page_dsl_get_type (void);
 
 CEPage *ce_page_dsl_new (NMConnection *connection, GtkWindow *parent, GError **error);
 
+void dsl_connection_new (GtkWindow *parent,
+                         PageNewConnectionResultFunc callback,
+                         PageGetConnectionsFunc get_connections_func,
+                         gpointer user_data);
+
 #endif  /* __PAGE_DSL_H__ */
diff --git a/src/connection-editor/page-mobile.c b/src/connection-editor/page-mobile.c
index 9c599c7..c4efaee 100644
--- a/src/connection-editor/page-mobile.c
+++ b/src/connection-editor/page-mobile.c
@@ -28,10 +28,13 @@
 #include <nm-setting-connection.h>
 #include <nm-setting-gsm.h>
 #include <nm-setting-cdma.h>
+#include <nm-setting-serial.h>
+#include <nm-setting-ppp.h>
 
 #include "page-mobile.h"
 #include "nm-connection-editor.h"
 #include "gconf-helpers.h"
+#include "mobile-wizard.h"
 
 G_DEFINE_TYPE (CEPageMobile, ce_page_mobile, CE_TYPE_PAGE)
 
@@ -431,3 +434,178 @@ ce_page_mobile_class_init (CEPageMobileClass *mobile_class)
 	/* virtual methods */
 	parent_class->validate = validate;
 }
+
+static void
+add_default_serial_setting (NMConnection *connection)
+{
+	NMSettingSerial *s_serial;
+
+	s_serial = NM_SETTING_SERIAL (nm_setting_serial_new ());
+	g_object_set (s_serial,
+	              NM_SETTING_SERIAL_BAUD, 115200,
+	              NM_SETTING_SERIAL_BITS, 8,
+	              NM_SETTING_SERIAL_PARITY, 'n',
+	              NM_SETTING_SERIAL_STOPBITS, 1,
+	              NULL);
+
+	nm_connection_add_setting (connection, NM_SETTING (s_serial));
+}
+
+typedef struct {
+    PageNewConnectionResultFunc result_func;
+    PageGetConnectionsFunc get_connections_func;
+    gpointer user_data;
+} WizardInfo;
+
+static void
+mobile_wizard_done (MobileWizard *wizard,
+                    gboolean canceled,
+                    MobileWizardAccessMethod *method,
+                    gpointer user_data)
+{
+	WizardInfo *info = user_data;
+	NMConnection *connection = NULL;
+
+	if (!canceled && method) {
+		NMSetting *type_setting;
+		const char *ctype = NULL;
+		char *detail = NULL;
+
+		switch (method->devtype) {
+		case NM_DEVICE_TYPE_GSM:
+			ctype = NM_SETTING_GSM_SETTING_NAME;
+			type_setting = nm_setting_gsm_new ();
+			/* De-facto standard for GSM */
+			g_object_set (type_setting,
+			              NM_SETTING_GSM_NUMBER, "*99#",
+			              NM_SETTING_GSM_USERNAME, method->username,
+			              NM_SETTING_GSM_PASSWORD, method->password,
+			              NM_SETTING_GSM_APN, method->gsm_apn,
+			              NULL);
+			break;
+		case NM_DEVICE_TYPE_CDMA:
+			ctype = NM_SETTING_CDMA_SETTING_NAME;
+			type_setting = nm_setting_cdma_new ();
+			/* De-facto standard for CDMA */
+			g_object_set (type_setting,
+			              NM_SETTING_CDMA_NUMBER, "#777",
+			              NM_SETTING_GSM_USERNAME, method->username,
+			              NM_SETTING_GSM_PASSWORD, method->password,
+			              NULL);
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+
+		if (method->plan_name)
+			detail = g_strdup_printf ("%s %s %%d", method->provider_name, method->plan_name);
+		else
+			detail = g_strdup_printf ("%s connection %%d", method->provider_name);
+		connection = ce_page_new_connection (detail, ctype, FALSE, info->get_connections_func, info->user_data);
+		g_free (detail);
+
+		nm_connection_add_setting (connection, type_setting);
+		add_default_serial_setting (connection);
+		nm_connection_add_setting (connection, nm_setting_ppp_new ());
+	}
+
+	(*info->result_func) (connection, canceled, NULL, info->user_data);
+
+	if (wizard)
+		mobile_wizard_destroy (wizard);
+	g_free (info);
+}
+
+static void
+cancel_dialog (GtkDialog *dialog)
+{
+	gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+void
+mobile_connection_new (GtkWindow *parent,
+                       PageNewConnectionResultFunc result_func,
+                       PageGetConnectionsFunc get_connections_func,
+                       gpointer user_data)
+{
+	MobileWizard *wizard;
+	WizardInfo *info;
+	GtkWidget *dialog, *vbox, *gsm_radio, *cdma_radio, *label, *content, *alignment;
+	GtkWidget *hbox, *image;
+	gint response;
+	MobileWizardAccessMethod method;
+
+	info = g_malloc0 (sizeof (WizardInfo));
+	info->result_func = result_func;
+	info->get_connections_func = get_connections_func;
+	info->user_data = user_data;
+
+	wizard = mobile_wizard_new (parent, NULL, mobile_wizard_done, info);
+	if (wizard) {
+		mobile_wizard_present (wizard);
+		return;
+	}
+
+	/* Fall back to just asking for GSM vs. CDMA */
+	dialog = gtk_dialog_new_with_buttons (_("Select Mobile Broadband Provider Type"),
+	                                      parent,
+	                                      GTK_DIALOG_MODAL,
+	                                      GTK_STOCK_CANCEL,
+	                                      GTK_RESPONSE_CANCEL,
+	                                      GTK_STOCK_OK,
+	                                      GTK_RESPONSE_OK,
+	                                      NULL);
+	g_signal_connect (dialog, "delete-event", G_CALLBACK (cancel_dialog), NULL);
+	gtk_window_set_icon_name (GTK_WINDOW (dialog), "nm-device-wwan");
+
+	content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+	alignment = gtk_alignment_new (0, 0, 0.5, 0.5);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 12, 12, 12);
+	gtk_box_pack_start (GTK_BOX (content), alignment, TRUE, FALSE, 6);
+
+	hbox = gtk_hbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (alignment), hbox);
+
+	image = gtk_image_new_from_icon_name ("nm-device-wwan", GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+	gtk_misc_set_padding (GTK_MISC (image), 0, 6);
+	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 6);
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, FALSE, 0);
+
+	label = gtk_label_new (_("Select the technology your mobile broadband provider uses.  If you are unsure, ask your provider."));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 12);
+
+	gsm_radio = gtk_radio_button_new_with_mnemonic (NULL, _("My provider uses _GSM-based technology (i.e. GPRS, EDGE, UMTS, HSDPA)"));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gsm_radio), TRUE);
+	gtk_box_pack_start (GTK_BOX (vbox), gsm_radio, FALSE, FALSE, 6);
+
+	cdma_radio = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (gsm_radio),
+                                           _("My provider uses _CDMA-based technology (i.e. 1xRTT, EVDO)"));
+	gtk_box_pack_start (GTK_BOX (vbox), cdma_radio, FALSE, FALSE, 6);
+
+	gtk_widget_show_all (dialog);
+
+	memset (&method, 0, sizeof (method));
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	if (response == GTK_RESPONSE_OK) {
+		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cdma_radio))) {
+			method.devtype = NM_DEVICE_TYPE_CDMA;
+			method.provider_name = _("CDMA");
+		} else {
+			method.devtype = NM_DEVICE_TYPE_GSM;
+			method.provider_name = _("GSM");
+		}
+	}
+	gtk_widget_destroy (dialog);
+
+	mobile_wizard_done (NULL,
+	                    (response != GTK_RESPONSE_OK),
+	                    (response == GTK_RESPONSE_OK) ? &method : NULL,
+	                    info);
+}
+
diff --git a/src/connection-editor/page-mobile.h b/src/connection-editor/page-mobile.h
index bc862f3..aca36cc 100644
--- a/src/connection-editor/page-mobile.h
+++ b/src/connection-editor/page-mobile.h
@@ -49,4 +49,9 @@ GType ce_page_mobile_get_type (void);
 
 CEPage *ce_page_mobile_new (NMConnection *connection, GtkWindow *parent, GError **error);
 
+void mobile_connection_new (GtkWindow *parent,
+                            PageNewConnectionResultFunc result_func,
+                            PageGetConnectionsFunc get_connections_func,
+                            gpointer user_data);
+
 #endif  /* __PAGE_MOBILE_H__ */
diff --git a/src/connection-editor/page-vpn.c b/src/connection-editor/page-vpn.c
index 59c5186..8f755af 100644
--- a/src/connection-editor/page-vpn.c
+++ b/src/connection-editor/page-vpn.c
@@ -191,3 +191,35 @@ ce_page_vpn_class_init (CEPageVpnClass *vpn_class)
 
 	parent_class->validate = validate;
 }
+
+
+void
+vpn_connection_new (GtkWindow *parent,
+                    PageNewConnectionResultFunc result_func,
+                    PageGetConnectionsFunc get_connections_func,
+                    gpointer user_data)
+{
+	char *service = NULL;
+	NMConnection *connection;
+	NMSetting *s_vpn;
+
+	service = vpn_ask_connection_type (parent);
+	if (!service) {
+		(*result_func) (NULL, TRUE, NULL, user_data);
+		return;
+	}
+
+	connection = ce_page_new_connection (_("VPN connection %d"),
+	                                     NM_SETTING_VPN_SETTING_NAME,
+	                                     FALSE,
+	                                     get_connections_func,
+	                                     user_data);
+	s_vpn = nm_setting_vpn_new ();
+	g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service, NULL);
+	g_free (service);
+	nm_connection_add_setting (connection, s_vpn);
+
+	(*result_func) (connection, FALSE, NULL, user_data);
+}
+
+
diff --git a/src/connection-editor/page-vpn.h b/src/connection-editor/page-vpn.h
index 9be71c8..28026f1 100644
--- a/src/connection-editor/page-vpn.h
+++ b/src/connection-editor/page-vpn.h
@@ -51,4 +51,9 @@ CEPage *ce_page_vpn_new (NMConnection *connection, GtkWindow *parent, GError **e
 
 gboolean ce_page_vpn_save_secrets (CEPage *page, NMConnection *connection);
 
+void vpn_connection_new (GtkWindow *parent,
+                         PageNewConnectionResultFunc result_func,
+                         PageGetConnectionsFunc get_connections_func,
+                         gpointer user_data);
+
 #endif  /* __PAGE_VPN_H__ */
diff --git a/src/connection-editor/page-wired.c b/src/connection-editor/page-wired.c
index d824ce8..df85dcd 100644
--- a/src/connection-editor/page-wired.c
+++ b/src/connection-editor/page-wired.c
@@ -327,3 +327,24 @@ ce_page_wired_class_init (CEPageWiredClass *wired_class)
 	/* virtual methods */
 	parent_class->validate = validate;
 }
+
+
+void
+wired_connection_new (GtkWindow *parent,
+                      PageNewConnectionResultFunc result_func,
+                      PageGetConnectionsFunc get_connections_func,
+                      gpointer user_data)
+{
+	NMConnection *connection;
+
+	connection = ce_page_new_connection (_("Wired connection %d"),
+	                                     NM_SETTING_WIRED_SETTING_NAME,
+	                                     TRUE,
+	                                     get_connections_func,
+	                                     user_data);
+	nm_connection_add_setting (connection, nm_setting_wired_new ());
+
+	(*result_func) (connection, FALSE, NULL, user_data);
+}
+
+
diff --git a/src/connection-editor/page-wired.h b/src/connection-editor/page-wired.h
index ae45d38..1d10f2f 100644
--- a/src/connection-editor/page-wired.h
+++ b/src/connection-editor/page-wired.h
@@ -49,5 +49,10 @@ GType ce_page_wired_get_type (void);
 
 CEPage *ce_page_wired_new (NMConnection *connection, GtkWindow *parent, GError **error);
 
+void wired_connection_new (GtkWindow *parent,
+                           PageNewConnectionResultFunc result_func,
+                           PageGetConnectionsFunc get_connections_func,
+                           gpointer user_data);
+
 #endif  /* __PAGE_WIRED_H__ */
 
diff --git a/src/connection-editor/page-wireless.c b/src/connection-editor/page-wireless.c
index 74c5997..4fb3b81 100644
--- a/src/connection-editor/page-wireless.c
+++ b/src/connection-editor/page-wireless.c
@@ -498,3 +498,27 @@ ce_page_wireless_class_init (CEPageWirelessClass *wireless_class)
 	/* virtual methods */
 	parent_class->validate = validate;
 }
+
+
+void
+wifi_connection_new (GtkWindow *parent,
+                     PageNewConnectionResultFunc result_func,
+                     PageGetConnectionsFunc get_connections_func,
+                     gpointer user_data)
+{
+	NMConnection *connection;
+	NMSetting *s_wifi;
+
+	connection = ce_page_new_connection (_("Wireless connection %d"),
+	                                     NM_SETTING_WIRELESS_SETTING_NAME,
+	                                     TRUE,
+	                                     get_connections_func,
+	                                     user_data);
+	s_wifi = nm_setting_wireless_new ();
+	g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, "infrastructure", NULL);
+	nm_connection_add_setting (connection, s_wifi);
+
+	(*result_func) (connection, FALSE, NULL, user_data);
+}
+
+
diff --git a/src/connection-editor/page-wireless.h b/src/connection-editor/page-wireless.h
index 6a11d40..fb0a632 100644
--- a/src/connection-editor/page-wireless.h
+++ b/src/connection-editor/page-wireless.h
@@ -52,5 +52,11 @@ CEPage *ce_page_wireless_new (NMConnection *connection, GtkWindow *parent, GErro
 /* Caller must free returned array */
 GByteArray *ce_page_wireless_get_ssid (CEPageWireless *self);
 
+
+void wifi_connection_new (GtkWindow *parent,
+                          PageNewConnectionResultFunc result_func,
+                          PageGetConnectionsFunc get_connections_func,
+                          gpointer user_data);
+
 #endif  /* __PAGE_WIRELESS_H__ */
 
diff --git a/src/connection-editor/vpn-helpers.c b/src/connection-editor/vpn-helpers.c
index 1baa96b..a0313d7 100644
--- a/src/connection-editor/vpn-helpers.c
+++ b/src/connection-editor/vpn-helpers.c
@@ -471,7 +471,7 @@ error:
 }
 
 char *
-vpn_ask_connection_type (void)
+vpn_ask_connection_type (GtkWindow *parent)
 {
 	GladeXML *xml;
 	GtkWidget *dialog, *combo, *widget;
@@ -521,6 +521,7 @@ vpn_ask_connection_type (void)
 	gtk_combo_box_set_model (GTK_COMBO_BOX (combo), model);
 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
 
+	gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
 	gtk_widget_show_all (dialog);
 	response = gtk_dialog_run (GTK_DIALOG (dialog));
 	if (response != GTK_RESPONSE_OK)
diff --git a/src/connection-editor/vpn-helpers.h b/src/connection-editor/vpn-helpers.h
index cd196fb..f7cd1cb 100644
--- a/src/connection-editor/vpn-helpers.h
+++ b/src/connection-editor/vpn-helpers.h
@@ -24,6 +24,7 @@
 #define _VPN_HELPERS_H_
 
 #include <glib.h>
+#include <gtk/gtk.h>
 #include <nm-connection.h>
 
 #define NM_VPN_API_SUBJECT_TO_CHANGE
@@ -38,6 +39,6 @@ void vpn_import (VpnImportSuccessCallback callback, gpointer user_data);
 
 void vpn_export (NMConnection *connection);
 
-char *vpn_ask_connection_type (void);
+char *vpn_ask_connection_type (GtkWindow *parent);
 
 #endif  /* _VPN_HELPERS_H_ */
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index 89bf39b..fafab34 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -4,10 +4,17 @@ libutils_la_SOURCES = \
 	gnome-keyring-md5.h \
 	gnome-keyring-md5.c \
 	utils.c \
-	utils.h
+	utils.h \
+	nmn-mobile-providers.h \
+	nmn-mobile-providers.c \
+	mobile-wizard.h \
+	mobile-wizard.c
 
 libutils_la_CPPFLAGS = \
 	$(NMA_CFLAGS) \
+	-DGLADEDIR=\""$(gladedir)"\" \
+	-DDATADIR=\""$(datadir)"\" \
+	$(DISABLE_DEPRECATED) \
 	-I${top_srcdir}/src/gconf-helpers \
 	-I${top_srcdir}/src
 
diff --git a/src/utils/mobile-wizard.c b/src/utils/mobile-wizard.c
new file mode 100644
index 0000000..2c416bb
--- /dev/null
+++ b/src/utils/mobile-wizard.c
@@ -0,0 +1,1545 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * 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 2008 - 2009 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+
+#include <NetworkManager.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-client.h>
+#include <nm-gsm-device.h>
+#include <nm-cdma-device.h>
+
+#include "mobile-wizard.h"
+#include "nmn-mobile-providers.h"
+#include "utils.h"
+
+#define DEVICE_TAG "device"
+#define TYPE_TAG "setting-type"
+
+static char *get_selected_country (MobileWizard *self);
+static NmnMobileProvider *get_selected_provider (MobileWizard *self);
+static NmnMobileProviderType get_provider_unlisted_type (MobileWizard *self);
+static NmnMobileAccessMethod *get_selected_method (MobileWizard *self, gboolean *manual);
+
+struct MobileWizard {
+	GtkWidget *assistant;
+	MobileWizardCallback callback;
+	gpointer user_data;
+	GHashTable *providers;
+	GHashTable *country_codes;
+	NMDevice *device;
+	gboolean began_with_device;
+
+	/* Intro page */
+	guint32 intro_idx;
+	GtkWidget *intro_page;
+	GtkWidget *dev_combo;
+	GtkTreeStore *dev_store;
+	NMClient *client;
+
+	/* Country page */
+	guint32 country_idx;
+	const char *country;
+	GtkWidget *country_page;
+	GtkWidget *country_view;
+	GtkTreeStore *country_store;
+	GtkTreeModelSort *country_sort;
+	guint32 country_focus_id;
+
+	/* Providers page */
+	guint32 providers_idx;
+	GtkWidget *providers_page;
+	GtkWidget *providers_view;
+	GtkTreeStore *providers_store;
+	GtkTreeModelSort *providers_sort;
+	guint32 providers_focus_id;
+	GtkWidget *providers_view_radio;
+
+	GtkWidget *provider_unlisted_radio;
+	GtkWidget *provider_unlisted_entry;
+	GtkWidget *provider_unlisted_type_combo;
+
+	gboolean provider_only_cdma;
+
+	/* Plan page */
+	guint32 plan_idx;
+	GtkWidget *plan_page;
+	GtkWidget *plan_combo;
+	GtkTreeStore *plan_store;
+	guint32 plan_focus_id;
+
+	GtkWidget *plan_unlisted_entry;
+
+	/* Confirm page */
+	GtkWidget *confirm_page;
+	GtkWidget *confirm_provider;
+	GtkWidget *confirm_plan;
+	GtkWidget *confirm_apn;
+	GtkWidget *confirm_plan_label;
+	GtkWidget *confirm_device;
+	GtkWidget *confirm_device_label;
+	guint32 confirm_idx;
+};
+
+static NmnMobileProviderType
+get_devtype (NMDevice *device)
+{
+	if (device) {
+		if (NM_IS_GSM_DEVICE (device))
+			return NMN_MOBILE_ACCESS_METHOD_TYPE_GSM;
+		else if (NM_IS_CDMA_DEVICE (device))
+			return NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA;
+	}
+
+	return NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN;
+}
+
+static void
+assistant_closed (GtkButton *button, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+	NmnMobileProvider *provider;
+	NmnMobileAccessMethod *method;
+	MobileWizardAccessMethod *wiz_method;
+	NmnMobileProviderType method_type = get_devtype (self->device);
+
+	wiz_method = g_malloc0 (sizeof (MobileWizardAccessMethod));
+
+	provider = get_selected_provider (self);
+	if (!provider) {
+		if (method_type == NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN)
+			method_type = get_provider_unlisted_type (self);
+
+		wiz_method->provider_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->provider_unlisted_entry)));
+		if (method_type == NMN_MOBILE_ACCESS_METHOD_TYPE_GSM)
+			wiz_method->gsm_apn = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->plan_unlisted_entry)));
+	} else {
+		gboolean manual = FALSE;
+
+		wiz_method->provider_name = g_strdup (provider->name);
+		method = get_selected_method (self, &manual);
+		if (method) {
+			if (method->name)
+				wiz_method->plan_name = g_strdup (method->name);
+			method_type = method->type;
+			if (method_type == NMN_MOBILE_ACCESS_METHOD_TYPE_GSM)
+				wiz_method->gsm_apn = g_strdup (method->gsm_apn);
+			wiz_method->username = method->username ? g_strdup (method->username) : NULL;
+			wiz_method->password = method->password ? g_strdup (method->password) : NULL;
+		} else {
+			if (self->provider_only_cdma)
+				method_type = NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA;
+			else {
+				method_type = NMN_MOBILE_ACCESS_METHOD_TYPE_GSM;
+				wiz_method->gsm_apn = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->plan_unlisted_entry)));
+			}
+		}
+	}
+
+	switch (method_type) {
+	case NMN_MOBILE_ACCESS_METHOD_TYPE_GSM:
+		wiz_method->devtype = NM_DEVICE_TYPE_GSM;
+		break;
+	case NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA:
+		wiz_method->devtype = NM_DEVICE_TYPE_CDMA;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	(*(self->callback)) (self, FALSE, wiz_method, self->user_data);
+
+	g_free (wiz_method->provider_name);
+	g_free (wiz_method->plan_name);
+	g_free (wiz_method->username);
+	g_free (wiz_method->password);
+	g_free (wiz_method->gsm_apn);
+	g_free (wiz_method);
+}
+
+static void
+assistant_cancel (GtkButton *button, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	(*(self->callback)) (self, TRUE, NULL, self->user_data);
+}
+
+/**********************************************************/
+/* Confirm page */
+/**********************************************************/
+
+static void
+confirm_setup (MobileWizard *self)
+{
+	GtkWidget *vbox, *label, *alignment, *pbox;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+	label = gtk_label_new (_("Your mobile broadband connection is configured with the following settings:"));
+	gtk_widget_set_size_request (label, 500, -1);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 6);
+
+	/* Device */
+	self->confirm_device_label = gtk_label_new (_("Your Device:"));
+	gtk_misc_set_alignment (GTK_MISC (self->confirm_device_label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), self->confirm_device_label, FALSE, FALSE, 0);
+
+	alignment = gtk_alignment_new (0, 0.5, 0, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 12, 25, 0);
+	self->confirm_device = gtk_label_new (NULL);
+	gtk_container_add (GTK_CONTAINER (alignment), self->confirm_device);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	/* Provider */
+	label = gtk_label_new (_("Your Provider:"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	alignment = gtk_alignment_new (0, 0.5, 0, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 12, 25, 0);
+	self->confirm_provider = gtk_label_new (NULL);
+	gtk_container_add (GTK_CONTAINER (alignment), self->confirm_provider);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	/* Plan and APN */
+	self->confirm_plan_label = gtk_label_new (_("Your Plan:"));
+	gtk_misc_set_alignment (GTK_MISC (self->confirm_plan_label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), self->confirm_plan_label, FALSE, FALSE, 0);
+
+	alignment = gtk_alignment_new (0, 0.5, 0, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 25, 0);
+	pbox = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (alignment), pbox);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	self->confirm_plan = gtk_label_new (NULL);
+	gtk_misc_set_alignment (GTK_MISC (self->confirm_plan), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (pbox), self->confirm_plan, FALSE, FALSE, 0);
+
+	self->confirm_apn = gtk_label_new (NULL);
+	gtk_misc_set_alignment (GTK_MISC (self->confirm_apn), 0, 0.5);
+	gtk_misc_set_padding (GTK_MISC (self->confirm_apn), 0, 6);
+	gtk_box_pack_start (GTK_BOX (pbox), self->confirm_apn, FALSE, FALSE, 0);
+
+	if (self->began_with_device) {
+		alignment = gtk_alignment_new (0, 0.5, 1, 0);
+		label = gtk_label_new (_("A connection will now be made to your mobile broadband provider using the settings you selected.  If the connection fails or you cannot access network resources, double-check your settings.  To modify your mobile broadband connection settings, choose \"Network Connections\" from the System >> Preferences menu."));
+		gtk_widget_set_size_request (label, 500, -1);
+		gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+		gtk_misc_set_padding (GTK_MISC (label), 0, 6);
+		gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+		gtk_container_add (GTK_CONTAINER (alignment), label);
+		gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 6);
+	}
+
+	gtk_widget_show_all (vbox);
+	self->confirm_idx = gtk_assistant_append_page (GTK_ASSISTANT (self->assistant), vbox);
+	gtk_assistant_set_page_title (GTK_ASSISTANT (self->assistant),
+	                              vbox, _("Confirm Mobile Broadband Settings"));
+
+	gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant), vbox, TRUE);
+	gtk_assistant_set_page_type (GTK_ASSISTANT (self->assistant), vbox, GTK_ASSISTANT_PAGE_CONFIRM);
+
+	self->confirm_page = vbox;
+}
+
+static void
+confirm_prepare (MobileWizard *self)
+{
+	NmnMobileProvider *provider = NULL;
+	NmnMobileAccessMethod *method = NULL;
+	char *country = NULL;
+	gboolean manual = FALSE;
+	GString *str;
+
+	country = get_selected_country (self);
+	provider = get_selected_provider (self);
+	if (provider)
+		method = get_selected_method (self, &manual);
+
+	/* Provider */
+	str = g_string_new (NULL);
+	if (provider)
+		g_string_append (str, provider->name);
+	else {
+		const char *unlisted_provider;
+
+		unlisted_provider = gtk_entry_get_text (GTK_ENTRY (self->provider_unlisted_entry));
+		g_string_append (str, unlisted_provider);
+	}
+
+	if (country)
+		g_string_append_printf (str, ", %s", country);
+	gtk_label_set_text (GTK_LABEL (self->confirm_provider), str->str);
+	g_string_free (str, TRUE);
+
+	if (self->device) {
+		gtk_label_set_text (GTK_LABEL (self->confirm_device),
+		                    utils_get_device_description (self->device));
+	} else {
+		gtk_widget_hide (self->confirm_device_label);
+		gtk_widget_hide (self->confirm_device);
+	}
+
+	if (self->provider_only_cdma) {
+		gtk_widget_hide (self->confirm_plan_label);
+		gtk_widget_hide (self->confirm_plan);
+		gtk_widget_hide (self->confirm_apn);
+	} else {
+		/* Plan */
+		gtk_widget_show (self->confirm_plan_label);
+		gtk_widget_show (self->confirm_plan);
+		gtk_widget_show (self->confirm_apn);
+
+		if (method)
+			gtk_label_set_text (GTK_LABEL (self->confirm_plan), method->name);
+		else
+			gtk_label_set_text (GTK_LABEL (self->confirm_plan), _("Unlisted"));
+
+		str = g_string_new (NULL);
+		g_string_append_printf (str, "<span color=\"#999999\">APN: %s</span>", method->gsm_apn);
+		gtk_label_set_markup (GTK_LABEL (self->confirm_apn), str->str);
+		g_string_free (str, TRUE);
+	}
+}
+
+/**********************************************************/
+/* Plan page */
+/**********************************************************/
+
+#define PLAN_COL_NAME 0
+#define PLAN_COL_METHOD 1
+#define PLAN_COL_MANUAL 2
+
+static NmnMobileAccessMethod *
+get_selected_method (MobileWizard *self, gboolean *manual)
+{
+	GtkTreeModel *model;
+	NmnMobileAccessMethod *method = NULL;
+	GtkTreeIter iter;
+	gboolean is_manual = FALSE;
+
+	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->plan_combo), &iter))
+		return NULL;
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (self->plan_combo));
+	if (!model)
+		return NULL;
+
+	gtk_tree_model_get (model, &iter,
+	                    PLAN_COL_METHOD, &method,
+	                    PLAN_COL_MANUAL, &is_manual,
+	                    -1);
+	if (is_manual) {
+		if (manual)
+			*manual = is_manual;
+		if (method)
+			nmn_mobile_access_method_unref (method);
+		method = NULL;
+	}
+
+	return method;
+}
+
+static void
+plan_update_complete (MobileWizard *self)
+{
+	GtkAssistant *assistant = GTK_ASSISTANT (self->assistant);
+	gboolean is_manual = FALSE;
+	NmnMobileAccessMethod *method;
+
+	method = get_selected_method (self, &is_manual);
+	if (method) {
+		gtk_assistant_set_page_complete (assistant, self->plan_page, TRUE);
+		nmn_mobile_access_method_unref (method);
+	} else {
+		const char *manual_apn;
+
+		manual_apn = gtk_entry_get_text (GTK_ENTRY (self->plan_unlisted_entry));
+		gtk_assistant_set_page_complete (assistant, self->plan_page,
+		                                 (manual_apn && strlen (manual_apn)));
+	}
+}
+
+static void
+plan_combo_changed (MobileWizard *self)
+{
+	NmnMobileAccessMethod *method = NULL;
+	gboolean is_manual = FALSE;
+
+	method = get_selected_method (self, &is_manual);
+	if (method) {
+		gtk_entry_set_text (GTK_ENTRY (self->plan_unlisted_entry), method->gsm_apn);
+		gtk_widget_set_sensitive (self->plan_unlisted_entry, FALSE);
+	} else {
+		gtk_entry_set_text (GTK_ENTRY (self->plan_unlisted_entry), "");
+		gtk_widget_set_sensitive (self->plan_unlisted_entry, TRUE);
+		gtk_widget_grab_focus (self->plan_unlisted_entry);
+	}
+
+	if (method)
+		nmn_mobile_access_method_unref (method);
+
+	plan_update_complete (self);
+}
+
+static gboolean
+plan_row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	NmnMobileAccessMethod *method = NULL;
+	gboolean is_manual = FALSE;
+	gboolean draw_separator = FALSE;
+
+	gtk_tree_model_get (model, iter,
+	                    PLAN_COL_METHOD, &method,
+	                    PLAN_COL_MANUAL, &is_manual,
+	                    -1);
+	if (!method && !is_manual)
+		draw_separator = TRUE;
+	if (method)
+		nmn_mobile_access_method_unref (method);
+	return draw_separator;
+}
+
+static void
+plan_setup (MobileWizard *self)
+{
+	GtkWidget *vbox, *label, *alignment, *hbox, *image;
+	GtkCellRenderer *renderer;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+	label = gtk_label_new_with_mnemonic (_("_Select your plan:"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	self->plan_store = gtk_tree_store_new (3, G_TYPE_STRING, NMN_TYPE_MOBILE_ACCESS_METHOD, G_TYPE_BOOLEAN);
+
+	self->plan_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (self->plan_store));
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->plan_combo);
+	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (self->plan_combo),
+	                                      plan_row_separator_func,
+	                                      NULL,
+	                                      NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->plan_combo), renderer, TRUE);
+	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->plan_combo), renderer, "text", PLAN_COL_NAME);
+
+	g_signal_connect_swapped (self->plan_combo, "changed", G_CALLBACK (plan_combo_changed), self);
+
+	alignment = gtk_alignment_new (0, 0.5, 0.5, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 12, 0, 0);
+	gtk_container_add (GTK_CONTAINER (alignment), self->plan_combo);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	label = gtk_label_new_with_mnemonic (_("Selected plan _APN (Access Point Name):"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	self->plan_unlisted_entry = gtk_entry_new ();
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->plan_unlisted_entry);
+	gtk_entry_set_max_length (GTK_ENTRY (self->plan_unlisted_entry), 40);
+	g_signal_connect_swapped (self->plan_unlisted_entry, "changed", G_CALLBACK (plan_update_complete), self);
+
+	alignment = gtk_alignment_new (0, 0.5, 0.5, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 24, 0, 0);
+	gtk_container_add (GTK_CONTAINER (alignment), self->plan_unlisted_entry);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 6);
+	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+	label = gtk_label_new (_("Warning: Selecting an incorrect plan may result in billing issues for your broadband account or may prevent connectivity.\n\nIf you are unsure of your plan please ask your provider for your plan's APN."));
+	gtk_widget_set_size_request (label, 500, -1);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+	self->plan_idx = gtk_assistant_append_page (GTK_ASSISTANT (self->assistant), vbox);
+	gtk_assistant_set_page_title (GTK_ASSISTANT (self->assistant), vbox, _("Choose your Billing Plan"));
+	gtk_assistant_set_page_type (GTK_ASSISTANT (self->assistant), vbox, GTK_ASSISTANT_PAGE_CONTENT);
+	gtk_widget_show_all (vbox);
+
+	self->plan_page = vbox;
+}
+
+static void
+plan_prepare (MobileWizard *self)
+{
+	NmnMobileProvider *provider;
+	GtkTreeIter method_iter;
+
+	gtk_tree_store_clear (self->plan_store);
+
+	provider = get_selected_provider (self);
+	if (provider) {
+		GSList *iter;
+		guint32 count = 0;
+
+		for (iter = provider->methods; iter; iter = g_slist_next (iter)) {
+			NmnMobileAccessMethod *method = iter->data;
+			NmnMobileProviderType devtype = get_devtype (self->device);
+
+			if (   (devtype != NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN)
+			    && (method->type != devtype))
+				continue;
+
+			gtk_tree_store_append (GTK_TREE_STORE (self->plan_store), &method_iter, NULL);
+			gtk_tree_store_set (GTK_TREE_STORE (self->plan_store),
+			                    &method_iter,
+			                    PLAN_COL_NAME,
+			                    method->name,
+			                    PLAN_COL_METHOD,
+			                    method,
+			                    -1);
+			count++;
+		}
+
+		/* Draw the separator */
+		if (count)
+			gtk_tree_store_append (GTK_TREE_STORE (self->plan_store), &method_iter, NULL);
+	}
+
+	/* Add the "My plan is not listed..." item */
+	gtk_tree_store_append (GTK_TREE_STORE (self->plan_store), &method_iter, NULL);
+	gtk_tree_store_set (GTK_TREE_STORE (self->plan_store),
+	                    &method_iter,
+	                    PLAN_COL_NAME,
+	                    _("My plan is not listed..."),
+	                    PLAN_COL_MANUAL,
+	                    TRUE,
+	                    -1);
+
+	/* Select the first item by default if nothing is yet selected */
+	if (gtk_combo_box_get_active (GTK_COMBO_BOX (self->plan_combo)) < 0)
+		gtk_combo_box_set_active (GTK_COMBO_BOX (self->plan_combo), 0);
+
+	plan_combo_changed (self);
+}
+
+/**********************************************************/
+/* Providers page */
+/**********************************************************/
+
+#define PROVIDER_COL_NAME 0
+#define PROVIDER_COL_PROVIDER 1
+
+static gboolean
+providers_search_func (GtkTreeModel *model,
+                       gint column,
+                       const char *key,
+                       GtkTreeIter *iter,
+                       gpointer search_data)
+{
+	gboolean unmatched = TRUE;
+	char *provider = NULL;
+
+	if (!key)
+		return TRUE;
+
+	gtk_tree_model_get (model, iter, column, &provider, -1);
+	if (!provider)
+		return TRUE;
+
+	unmatched = !!g_ascii_strncasecmp (provider, key, strlen (key));
+	g_free (provider);
+	return unmatched;
+}
+
+static NmnMobileProvider *
+get_selected_provider (MobileWizard *self)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+	NmnMobileProvider *provider = NULL;
+
+	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->providers_view_radio)))
+		return NULL;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->providers_view));
+	g_assert (selection);
+
+	if (!gtk_tree_selection_get_selected (GTK_TREE_SELECTION (selection), &model, &iter))
+		return NULL;
+
+	gtk_tree_model_get (model, &iter, PROVIDER_COL_PROVIDER, &provider, -1);
+	return provider;
+}
+
+static void
+providers_update_complete (MobileWizard *self)
+{
+	GtkAssistant *assistant = GTK_ASSISTANT (self->assistant);
+	gboolean use_view;
+
+	use_view = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->providers_view_radio));
+	if (use_view) {
+		NmnMobileProvider *provider;
+
+		provider = get_selected_provider (self);
+		gtk_assistant_set_page_complete (assistant, self->providers_page, !!provider);
+		if (provider)
+			nmn_mobile_provider_unref (provider);
+	} else {
+		const char *manual_provider;
+
+		manual_provider = gtk_entry_get_text (GTK_ENTRY (self->provider_unlisted_entry));
+		gtk_assistant_set_page_complete (assistant, self->providers_page,
+		                                 (manual_provider && strlen (manual_provider)));
+	}
+}
+
+static gboolean
+focus_providers_view (gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	self->providers_focus_id = 0;
+	gtk_widget_grab_focus (self->providers_view);
+	return FALSE;
+}
+
+static gboolean
+focus_provider_unlisted_entry (gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	self->providers_focus_id = 0;
+	gtk_widget_grab_focus (self->provider_unlisted_entry);
+	return FALSE;
+}
+
+static void
+providers_radio_toggled (GtkToggleButton *button, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+	gboolean use_view;
+
+	use_view = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->providers_view_radio));
+	if (use_view) {
+		if (!self->providers_focus_id)
+			self->providers_focus_id = g_idle_add (focus_providers_view, self);
+		gtk_widget_set_sensitive (self->providers_view, TRUE);
+		gtk_widget_set_sensitive (self->provider_unlisted_entry, FALSE);
+		gtk_widget_set_sensitive (self->provider_unlisted_type_combo, FALSE);
+	} else {
+		if (!self->providers_focus_id)
+			self->providers_focus_id = g_idle_add (focus_provider_unlisted_entry, self);
+		gtk_widget_set_sensitive (self->providers_view, FALSE);
+		gtk_widget_set_sensitive (self->provider_unlisted_entry, TRUE);
+		gtk_widget_set_sensitive (self->provider_unlisted_type_combo, TRUE);
+	}
+
+	providers_update_complete (self);
+}
+
+static NmnMobileProviderType
+get_provider_unlisted_type (MobileWizard *self)
+{
+	switch (gtk_combo_box_get_active (GTK_COMBO_BOX (self->provider_unlisted_type_combo))) {
+	case 0:
+		return NMN_MOBILE_ACCESS_METHOD_TYPE_GSM;
+	case 1:
+		return NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA;
+	default:
+		return NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN;
+	}
+}
+
+static void
+providers_setup (MobileWizard *self)
+{
+	GtkWidget *vbox, *scroll, *alignment, *unlisted_table, *label;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+	self->providers_view_radio = gtk_radio_button_new_with_mnemonic (NULL, _("Select your provider from a _list:"));
+	g_signal_connect (self->providers_view_radio, "toggled", G_CALLBACK (providers_radio_toggled), self);
+	gtk_box_pack_start (GTK_BOX (vbox), self->providers_view_radio, FALSE, TRUE, 0);
+
+	self->providers_store = gtk_tree_store_new (2, G_TYPE_STRING, NMN_TYPE_MOBILE_PROVIDER);
+
+	self->providers_sort = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->providers_store)));
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->providers_sort),
+	                                      PROVIDER_COL_NAME, GTK_SORT_ASCENDING);
+
+	self->providers_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->providers_sort));
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (_("Provider"),
+	                                                   renderer,
+	                                                   "text", PROVIDER_COL_NAME,
+	                                                   NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self->providers_view), column);
+	gtk_tree_view_column_set_clickable (column, TRUE);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->providers_view));
+	g_assert (selection);
+	g_signal_connect_swapped (selection, "changed", G_CALLBACK (providers_update_complete), self);
+
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+	                                GTK_POLICY_NEVER,
+	                                GTK_POLICY_AUTOMATIC);
+	gtk_widget_set_size_request (scroll, -1, 140);
+	gtk_container_add (GTK_CONTAINER (scroll), self->providers_view);
+
+	alignment = gtk_alignment_new (0, 0, 1, 1);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 12, 25, 0);
+	gtk_container_add (GTK_CONTAINER (alignment), scroll);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, TRUE, TRUE, 0);
+
+	self->provider_unlisted_radio = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (self->providers_view_radio),
+	                                            _("I can't find my provider and I wish to enter it _manually:"));
+	g_signal_connect (self->providers_view_radio, "toggled", G_CALLBACK (providers_radio_toggled), self);
+	gtk_box_pack_start (GTK_BOX (vbox), self->provider_unlisted_radio, FALSE, TRUE, 0);
+
+	alignment = gtk_alignment_new (0, 0, 0, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 15, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+	unlisted_table = gtk_table_new (2, 2, FALSE);
+	gtk_container_add (GTK_CONTAINER (alignment), unlisted_table);
+
+	label = gtk_label_new (_("Provider:"));
+	gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
+	gtk_table_attach (GTK_TABLE (unlisted_table), label, 0, 1, 0, 1, 0, 0, 6, 6);
+
+	self->provider_unlisted_entry = gtk_entry_new ();
+	gtk_entry_set_width_chars (GTK_ENTRY (self->provider_unlisted_entry), 40);
+	g_signal_connect_swapped (self->provider_unlisted_entry, "changed", G_CALLBACK (providers_update_complete), self);
+
+	alignment = gtk_alignment_new (0, 0.5, 0.66, 0);
+	gtk_container_add (GTK_CONTAINER (alignment), self->provider_unlisted_entry);
+	gtk_table_attach (GTK_TABLE (unlisted_table), alignment,
+	                  1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 6, 6);
+
+	self->provider_unlisted_type_combo = gtk_combo_box_new_text ();
+	gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->provider_unlisted_type_combo);
+	gtk_combo_box_append_text (GTK_COMBO_BOX (self->provider_unlisted_type_combo),
+	                           _("My provider uses GSM technology (GPRS, EDGE, UMTS, HSPA)"));
+	gtk_combo_box_append_text (GTK_COMBO_BOX (self->provider_unlisted_type_combo),
+	                           _("My provider uses CDMA technology (1xRTT, EVDO)"));
+	gtk_combo_box_set_active (GTK_COMBO_BOX (self->provider_unlisted_type_combo), 0);
+
+	gtk_table_attach (GTK_TABLE (unlisted_table), self->provider_unlisted_type_combo,
+	                  1, 2, 1, 2, 0, 0, 6, 6);
+
+	/* Only show the CDMA/GSM combo if we don't know the device type */
+	if (get_devtype (self->device) != NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN)
+		gtk_widget_hide (self->provider_unlisted_type_combo);
+
+	self->providers_idx = gtk_assistant_append_page (GTK_ASSISTANT (self->assistant), vbox);
+	gtk_assistant_set_page_title (GTK_ASSISTANT (self->assistant), vbox, _("Choose your Provider"));
+	gtk_assistant_set_page_type (GTK_ASSISTANT (self->assistant), vbox, GTK_ASSISTANT_PAGE_CONTENT);
+	gtk_widget_show_all (vbox);
+
+	self->providers_page = vbox;
+}
+
+static void
+providers_prepare (MobileWizard *self)
+{
+	GtkTreeSelection *selection;
+	GSList *providers, *piter;
+	char *country;
+
+	gtk_tree_store_clear (self->providers_store);
+
+	country = get_selected_country (self);
+	providers = g_hash_table_lookup (self->providers, country);
+	g_free (country);
+
+	for (piter = providers; piter; piter = g_slist_next (piter)) {
+		NmnMobileProvider *provider = piter->data;
+		GtkTreeIter provider_iter;
+		NmnMobileProviderType devtype = get_devtype (self->device);
+
+		/* Ignore providers that don't match the current device type */
+		if (devtype != NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN) {
+			GSList *miter;
+			guint32 count = 0;
+
+			for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
+				NmnMobileAccessMethod *method = miter->data;
+
+				if (devtype == method->type)
+					count++;
+			}
+
+			if (!count)
+				continue;
+		}
+
+		gtk_tree_store_append (GTK_TREE_STORE (self->providers_store), &provider_iter, NULL);
+		gtk_tree_store_set (GTK_TREE_STORE (self->providers_store),
+		                    &provider_iter,
+		                    PROVIDER_COL_NAME,
+		                    provider->name,
+		                    PROVIDER_COL_PROVIDER,
+		                    provider,
+		                    -1);
+	}
+
+	g_object_set (G_OBJECT (self->providers_view), "enable-search", TRUE, NULL);
+
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->providers_view), PROVIDER_COL_NAME);
+	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->providers_view),
+	                                     providers_search_func, self, NULL);
+
+	/* If no row has focus yet, focus the first row so that the user can start
+	 * incremental search without clicking.
+	 */
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->providers_view));
+	g_assert (selection);
+	if (!gtk_tree_selection_count_selected_rows (selection)) {
+		GtkTreeIter first_iter;
+		GtkTreePath *first_path;
+
+		if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->providers_sort), &first_iter)) {
+			first_path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->providers_sort), &first_iter);
+			if (first_path) {
+				gtk_tree_selection_select_path (selection, first_path);
+				gtk_tree_path_free (first_path);
+			}
+		}
+	}
+
+	providers_radio_toggled (NULL, self);
+
+	/* Initial completeness state */
+	providers_update_complete (self);
+
+	/* If there's already a selected device, hide the GSM/CDMA radios */
+	if (self->device)
+		gtk_widget_hide (self->provider_unlisted_type_combo);
+	else
+		gtk_widget_show (self->provider_unlisted_type_combo);
+}
+
+/**********************************************************/
+/* Country page */
+/**********************************************************/
+
+static gboolean
+country_search_func (GtkTreeModel *model,
+                     gint column,
+                     const char *key,
+                     GtkTreeIter *iter,
+                     gpointer search_data)
+{
+	gboolean unmatched = TRUE;
+	char *country = NULL;
+
+	if (!key)
+		return TRUE;
+
+	gtk_tree_model_get (model, iter, column, &country, -1);
+	if (!country)
+		return TRUE;
+
+	unmatched = !!g_ascii_strncasecmp (country, key, strlen (key));
+	g_free (country);
+	return unmatched;
+}
+
+static void
+add_one_country (gpointer key, gpointer value, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+	GtkTreeIter country_iter;
+	GtkTreePath *country_path, *country_view_path;
+
+	g_assert (key);
+
+	gtk_tree_store_append (GTK_TREE_STORE (self->country_store), &country_iter, NULL);
+	gtk_tree_store_set (GTK_TREE_STORE (self->country_store), &country_iter, 0, key, -1);
+
+	/* If this country is the same country as the user's current locale,
+	 * select it by default.
+	 */
+	if (!self->country || g_ascii_strcasecmp (self->country, key))
+		return;
+
+	country_path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->country_store), &country_iter);
+	if (!country_path)
+		return;
+
+	country_view_path = gtk_tree_model_sort_convert_child_path_to_path (self->country_sort, country_path);
+	if (country_view_path) {
+		GtkTreeSelection *selection;
+
+		gtk_tree_view_expand_row (GTK_TREE_VIEW (self->country_view), country_view_path, TRUE);
+
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->country_view));
+		g_assert (selection);
+		gtk_tree_selection_select_path (selection, country_view_path);
+		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self->country_view),
+		                              country_view_path, NULL, TRUE, 0, 0);
+		gtk_tree_path_free (country_view_path);
+	}
+	gtk_tree_path_free (country_path);
+}
+
+static char *
+get_selected_country (MobileWizard *self)
+{
+	GtkTreeSelection *selection;
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+	char *country = NULL;
+
+	if (!self->country_codes)
+		return NULL;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->country_view));
+	g_assert (selection);
+
+	if (gtk_tree_selection_get_selected (GTK_TREE_SELECTION (selection), &model, &iter))
+		gtk_tree_model_get (model, &iter, 0, &country, -1);
+
+	return country;
+}
+
+static void
+country_update_complete (MobileWizard *self)
+{
+	char *country = NULL;
+
+	country = get_selected_country (self);
+	gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant),
+	                                 self->country_page,
+	                                 !!country);
+	g_free (country);
+}
+
+static void
+country_setup (MobileWizard *self)
+{
+	GtkWidget *vbox, *label, *scroll, *alignment;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+	label = gtk_label_new (_("Country List:"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
+
+	self->country_store = gtk_tree_store_new (1, G_TYPE_STRING);
+
+	self->country_sort = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->country_store)));
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->country_sort), 0, GTK_SORT_ASCENDING);
+
+	self->country_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->country_sort));
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (_("Country"), renderer, "text", 0, NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self->country_view), column);
+	gtk_tree_view_column_set_clickable (column, TRUE);
+
+	if (self->providers)
+		g_hash_table_foreach (self->providers, add_one_country, self);
+	g_object_set (G_OBJECT (self->country_view), "enable-search", TRUE, NULL);
+
+	/* If no row has focus yet, focus the first row so that the user can start
+	 * incremental search without clicking.
+	 */
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->country_view));
+	g_assert (selection);
+	if (!gtk_tree_selection_count_selected_rows (selection)) {
+		GtkTreeIter first_iter;
+		GtkTreePath *first_path;
+
+		if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->country_sort), &first_iter)) {
+			first_path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->country_sort), &first_iter);
+			if (first_path) {
+				gtk_tree_selection_select_path (selection, first_path);
+				gtk_tree_path_free (first_path);
+			}
+		}
+	}
+
+	g_signal_connect_swapped (selection, "changed", G_CALLBACK (country_update_complete), self);
+
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+	                                GTK_POLICY_NEVER,
+	                                GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (scroll), self->country_view);
+
+	alignment = gtk_alignment_new (0, 0, 1, 1);
+	gtk_container_add (GTK_CONTAINER (alignment), scroll);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, TRUE, TRUE, 6);
+
+	self->country_idx = gtk_assistant_append_page (GTK_ASSISTANT (self->assistant), vbox);
+	gtk_assistant_set_page_title (GTK_ASSISTANT (self->assistant), vbox, _("Choose your Provider's Country"));
+	gtk_assistant_set_page_type (GTK_ASSISTANT (self->assistant), vbox, GTK_ASSISTANT_PAGE_CONTENT);
+	gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant), vbox, TRUE);
+	gtk_widget_show_all (vbox);
+
+	self->country_page = vbox;
+
+	/* Initial completeness state */
+	country_update_complete (self);
+}
+
+static gboolean
+focus_country_view (gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	self->country_focus_id = 0;
+	gtk_widget_grab_focus (self->country_view);
+	return FALSE;
+}
+
+static void
+country_prepare (MobileWizard *self)
+{
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->country_view), 0);
+	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->country_view), country_search_func, self, NULL);
+
+	if (!self->country_focus_id)
+		self->country_focus_id = g_idle_add (focus_country_view, self);
+
+	country_update_complete (self);
+}
+
+/**********************************************************/
+/* Intro page */
+/**********************************************************/
+
+#define INTRO_COL_NAME 0
+#define INTRO_COL_DEVICE 1
+#define INTRO_COL_SEPARATOR 2
+
+static gboolean
+__intro_device_added (MobileWizard *self, NMDevice *device, gboolean select_it)
+{
+	GtkTreeIter iter;
+	const char *desc = utils_get_device_description (device);
+
+	if (NM_IS_GSM_DEVICE (device)) {
+		if (!desc)
+			desc = _("Installed GSM device");
+	} else if (NM_IS_CDMA_DEVICE (device)) {
+		if (!desc)
+			desc = _("Installed CDMA device");
+	} else
+		return FALSE;
+
+	gtk_tree_store_append (GTK_TREE_STORE (self->dev_store), &iter, NULL);
+	gtk_tree_store_set (GTK_TREE_STORE (self->dev_store),
+	                    &iter,
+	                    INTRO_COL_NAME, desc,
+	                    INTRO_COL_DEVICE, device,
+	                    -1);
+
+	/* Select the device just added */
+	if (select_it)
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self->dev_combo), &iter);
+
+	gtk_widget_set_sensitive (self->dev_combo, TRUE);
+	return TRUE;
+}
+
+static void
+intro_device_added_cb (NMClient *client, NMDevice *device, MobileWizard *self)
+{
+	__intro_device_added (self, device, TRUE);
+}
+
+static void
+intro_device_removed_cb (NMClient *client, NMDevice *device, MobileWizard *self)
+{
+	GtkTreeIter iter;
+	gboolean have_device = FALSE, removed = FALSE;
+
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->dev_store), &iter))
+		return;
+
+	do {
+		NMDevice *candidate = NULL;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (self->dev_store), &iter,
+		                    INTRO_COL_DEVICE, &candidate, -1);
+		if (candidate) {
+			if (candidate == device) {
+				gtk_tree_store_remove (GTK_TREE_STORE (self->dev_store), &iter);
+				if (self->device == candidate) {
+					g_object_unref (self->device);
+					self->device = NULL;
+				}
+				removed = TRUE;
+			}
+			g_object_unref (candidate);
+		}
+	} while (!removed && gtk_tree_model_iter_next (GTK_TREE_MODEL (self->dev_store), &iter));
+
+	/* There's already a selected device item; nothing more to do */
+	if (gtk_combo_box_get_active (GTK_COMBO_BOX (self->dev_combo)) > 1)
+		return;
+
+	/* If there are no more devices, select the "Any" item and disable the
+	 * combo box.  If there is no selected item and there is at least one device
+	 * item, select the first one.
+	 */
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->dev_store), &iter))
+		return;
+
+	do {
+		NMDevice *candidate = NULL;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (self->dev_store), &iter,
+		                    INTRO_COL_DEVICE, &candidate, -1);
+		if (candidate) {
+			have_device = TRUE;
+			g_object_unref (candidate);
+		}
+	} while (!have_device && gtk_tree_model_iter_next (GTK_TREE_MODEL (self->dev_store), &iter));
+
+	if (have_device) {
+		/* Iter should point to the last device item in the combo */
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self->dev_combo), &iter);
+	} else {
+		gtk_combo_box_set_active (GTK_COMBO_BOX (self->dev_combo), 0);
+		gtk_widget_set_sensitive (self->dev_combo, FALSE);
+	}
+}
+
+static void
+intro_add_initial_devices (MobileWizard *self)
+{
+	const GPtrArray *devices;
+	gboolean selected_first = FALSE;
+	int i;
+
+	devices = nm_client_get_devices (self->client);
+	for (i = 0; devices && (i < devices->len); i++) {
+		if (__intro_device_added (self, g_ptr_array_index (devices, i), !selected_first)) {
+			if (selected_first == FALSE)
+				selected_first = TRUE;
+		}
+	}
+
+	/* Otherwise the "Any device" item */
+	if (!selected_first) {
+		/* Select the first device item by default */
+		gtk_combo_box_set_active (GTK_COMBO_BOX (self->dev_combo), 0);
+		gtk_widget_set_sensitive (self->dev_combo, FALSE);
+	}
+}
+
+static void
+intro_remove_all_devices (MobileWizard *self)
+{
+	gtk_tree_store_clear (self->dev_store);
+
+	if (self->device) {
+		g_object_unref (self->device);
+		self->device = NULL;
+	}
+
+	/* Select the "Any device" item */
+	gtk_combo_box_set_active (GTK_COMBO_BOX (self->dev_combo), 0);
+	gtk_widget_set_sensitive (self->dev_combo, FALSE);
+}
+
+static void
+intro_manager_running_cb (NMClient *client, GParamSpec *pspec, MobileWizard *self)
+{
+	if (nm_client_get_manager_running (client))
+		intro_add_initial_devices (self);
+	else
+		intro_remove_all_devices (self);
+}
+
+static gboolean
+intro_row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gboolean separator = FALSE;
+	gtk_tree_model_get (model, iter, INTRO_COL_SEPARATOR, &separator, -1);
+	return separator;
+}
+
+static void
+intro_combo_changed (MobileWizard *self)
+{
+	GtkTreeIter iter;
+	NMDevice *selected = NULL;
+
+	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->dev_combo), &iter))
+		return;
+
+	gtk_tree_model_get (GTK_TREE_MODEL (self->dev_store), &iter,
+	                    INTRO_COL_DEVICE, &selected, -1);
+	if (selected) {
+		if (self->device)
+			g_object_unref (self->device);
+		self->device = selected;
+	} else {
+		if (self->device)
+			g_object_unref (self->device);
+		self->device = NULL;
+	}
+}
+
+static void
+intro_setup (MobileWizard *self)
+{
+	GtkWidget *vbox, *label, *alignment, *info_vbox;
+	GtkCellRenderer *renderer;
+	char *s;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+	label = gtk_label_new (_("This assistant helps you easily set up a mobile broadband connection to a cellular (3G) network."));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 6);
+
+	label = gtk_label_new (_("You will need the following information:"));
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 6);
+
+	alignment = gtk_alignment_new (0, 0, 1, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 25, 25, 0);
+	info_vbox = gtk_vbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (alignment), info_vbox);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 6);
+
+	s = g_strdup_printf ("â?¢ %s", _("Your broadband provider's name"));
+	label = gtk_label_new (s);
+	g_free (s);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (info_vbox), label, FALSE, TRUE, 0);
+
+	s = g_strdup_printf ("â?¢ %s", _("Your broadband billing plan name"));
+	label = gtk_label_new (s);
+	g_free (s);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (info_vbox), label, FALSE, TRUE, 0);
+
+	s = g_strdup_printf ("â?¢ %s", _("(in some cases) Your broadband billing plan APN (Access Point Name)"));
+	label = gtk_label_new (s);
+	g_free (s);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	gtk_box_pack_start (GTK_BOX (info_vbox), label, FALSE, TRUE, 0);
+
+	/* Device combo; only built if the wizard's caller didn't pass one in */
+	if (!self->device) {
+		GtkTreeIter iter;
+
+		self->client = nm_client_new ();
+		g_signal_connect (self->client, "device-added",
+		                  G_CALLBACK (intro_device_added_cb), self);
+		g_signal_connect (self->client, "device-removed",
+		                  G_CALLBACK (intro_device_removed_cb), self);
+		g_signal_connect (self->client, "notify::manager-running",
+		                  G_CALLBACK (intro_manager_running_cb), self);
+
+		self->dev_store = gtk_tree_store_new (3, G_TYPE_STRING, NM_TYPE_DEVICE, G_TYPE_BOOLEAN);
+		self->dev_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (self->dev_store));
+		gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (self->dev_combo),
+		                                      intro_row_separator_func, NULL, NULL);
+
+		renderer = gtk_cell_renderer_text_new ();
+		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->dev_combo), renderer, TRUE);
+		gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->dev_combo), renderer, "text", INTRO_COL_NAME);
+
+		label = gtk_label_new_with_mnemonic (_("Create a connection for _this mobile broadband device:"));
+		gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->dev_combo);
+		gtk_misc_set_alignment (GTK_MISC (label), 0, 1);
+		gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+		alignment = gtk_alignment_new (0, 0, 0.5, 0);
+		gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 25, 0);
+		gtk_container_add (GTK_CONTAINER (alignment), self->dev_combo);
+		gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
+
+		g_signal_connect_swapped (self->dev_combo, "changed", G_CALLBACK (intro_combo_changed), self);
+
+		/* Any device */
+		gtk_tree_store_append (GTK_TREE_STORE (self->dev_store), &iter, NULL);
+		gtk_tree_store_set (GTK_TREE_STORE (self->dev_store), &iter,
+		                    INTRO_COL_NAME, _("Any device"), -1);
+
+		/* Separator */
+		gtk_tree_store_append (GTK_TREE_STORE (self->dev_store), &iter, NULL);
+		gtk_tree_store_set (GTK_TREE_STORE (self->dev_store), &iter,
+		                    INTRO_COL_SEPARATOR, TRUE, -1);
+
+		intro_add_initial_devices (self);
+	}
+
+	gtk_widget_show_all (vbox);
+	gtk_assistant_append_page (GTK_ASSISTANT (self->assistant), vbox);
+	gtk_assistant_set_page_title (GTK_ASSISTANT (self->assistant),
+	                              vbox, _("Set up a Mobile Broadband Connection"));
+
+	gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant), vbox, TRUE);
+	gtk_assistant_set_page_type (GTK_ASSISTANT (self->assistant), vbox, GTK_ASSISTANT_PAGE_INTRO);
+}
+
+/**********************************************************/
+/* General assistant stuff */
+/**********************************************************/
+
+static void
+remove_provider_focus_idle (MobileWizard *self)
+{
+	if (self->providers_focus_id) {
+		g_source_remove (self->providers_focus_id);
+		self->providers_focus_id = 0;
+	}
+}
+
+static void
+remove_country_focus_idle (MobileWizard *self)
+{
+	if (self->country_focus_id) {
+		g_source_remove (self->country_focus_id);
+		self->country_focus_id = 0;
+	}
+}
+
+static void
+assistant_prepare (GtkAssistant *assistant, GtkWidget *page, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	if (page != self->providers_page)
+		remove_provider_focus_idle (self);
+	if (page != self->country_page)
+		remove_country_focus_idle (self);
+
+	if (page == self->country_page)
+		country_prepare (self);
+	else if (page == self->providers_page)
+		providers_prepare (self);
+	else if (page == self->plan_page)
+		plan_prepare (self);
+	else if (page == self->confirm_page)
+		confirm_prepare (self);
+}
+
+static gint
+forward_func (gint current_page, gpointer user_data)
+{
+	MobileWizard *self = user_data;
+
+	if (current_page == self->providers_idx) {
+		NmnMobileProviderType method_type = get_devtype (self->device);
+
+		/* If the provider is unlisted, we can skip ahead of the user's
+		 * access technology is CDMA.
+		 */
+		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->provider_unlisted_radio))) {
+			if (method_type == NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN)
+				method_type = get_provider_unlisted_type (self);
+		} else {
+			/* Or, if the provider is only CDMA, then we can also skip ahead */
+			NmnMobileProvider *provider;
+			GSList *iter;
+			gboolean gsm = FALSE, cdma = FALSE;
+
+			provider = get_selected_provider (self);
+			if (provider) {
+				for (iter = provider->methods; iter; iter = g_slist_next (iter)) {
+					NmnMobileAccessMethod *method = iter->data;
+
+					if (method->type == NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA)
+						cdma = TRUE;
+					else if (method->type == NMN_MOBILE_ACCESS_METHOD_TYPE_GSM)
+						gsm = TRUE;
+				}
+				nmn_mobile_provider_unref (provider);
+
+				if (cdma && !gsm)
+					method_type = NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA;
+			}
+		}
+
+		/* Skip to the confirm page if we know its CDMA */
+		if (method_type == NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA) {
+			self->provider_only_cdma = TRUE;
+			return self->confirm_idx;
+		} else
+			self->provider_only_cdma = FALSE;
+	}
+
+	return current_page + 1;
+}
+
+static char *
+get_country_from_locale (void)
+{
+	char *p, *m, *cc, *lang;
+
+	lang = getenv ("LC_ALL");
+	if (!lang)
+		lang = getenv ("LANG");
+	if (!lang)
+		return NULL;
+
+	p = strchr (lang, '_');
+	if (!p || !strlen (p)) {
+		g_free (p);
+		return NULL;
+	}
+
+	p = cc = g_strdup (++p);
+	m = strchr (cc, '.');
+	if (m)
+		*m = '\0';
+
+	while (*p) {
+		*p = g_ascii_toupper (*p);
+		p++;
+	}
+
+	return cc;
+}
+
+MobileWizard *
+mobile_wizard_new (GtkWindow *parent,
+                   NMDevice *device,
+                   MobileWizardCallback cb,
+                   gpointer user_data)
+{
+	MobileWizard *self;
+	char *cc;
+
+	self = g_malloc0 (sizeof (MobileWizard));
+	g_return_val_if_fail (self != NULL, NULL);
+
+	self->providers = nmn_mobile_providers_parse (&(self->country_codes));
+	if (!self->providers || !self->country_codes) {
+		mobile_wizard_destroy (self);
+		return NULL;
+	}
+
+	cc = get_country_from_locale ();
+	if (cc)
+		self->country = g_hash_table_lookup (self->country_codes, cc);
+	g_free (cc);
+
+	self->callback = cb;
+	self->user_data = user_data;
+	if (device) {
+		self->device = g_object_ref (device);
+		self->began_with_device = TRUE;
+	}
+
+	self->assistant = gtk_assistant_new ();
+	gtk_assistant_set_forward_page_func (GTK_ASSISTANT (self->assistant),
+	                                     forward_func, self, NULL);
+	gtk_window_set_title (GTK_WINDOW (self->assistant), _("New Mobile Broadband Connection"));
+	gtk_window_set_position (GTK_WINDOW (self->assistant), GTK_WIN_POS_CENTER_ALWAYS);
+
+	intro_setup (self);
+	country_setup (self);
+	providers_setup (self);
+	plan_setup (self);
+	confirm_setup (self);
+
+	g_signal_connect (self->assistant, "close", G_CALLBACK (assistant_closed), self);
+	g_signal_connect (self->assistant, "cancel", G_CALLBACK (assistant_cancel), self);
+	g_signal_connect (self->assistant, "prepare", G_CALLBACK (assistant_prepare), self);
+
+	/* Run the wizard */
+	if (parent)
+		gtk_window_set_transient_for (GTK_WINDOW (self->assistant), parent);
+	gtk_window_set_modal (GTK_WINDOW (self->assistant), TRUE);
+	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (self->assistant), TRUE);
+	gtk_window_set_type_hint (GTK_WINDOW (self->assistant), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+	return self;
+}
+
+void
+mobile_wizard_present (MobileWizard *self)
+{
+	g_return_if_fail (self != NULL);
+
+	gtk_window_present (GTK_WINDOW (self->assistant));
+	gtk_widget_show_all (self->assistant);
+}
+
+void
+mobile_wizard_destroy (MobileWizard *self)
+{
+	g_return_if_fail (self != NULL);
+
+	if (self->assistant) {
+		gtk_widget_hide (self->assistant);
+		gtk_widget_destroy (self->assistant);
+	}
+
+	if (self->device)
+		g_object_unref (self->device);
+
+	if (self->client)
+		g_object_unref (self->client);
+
+	remove_provider_focus_idle (self);
+	remove_country_focus_idle (self);
+
+	if (self->providers)
+		g_hash_table_destroy (self->providers);
+
+	if (self->country_codes)
+		g_hash_table_destroy (self->country_codes);
+
+	g_free (self);
+}
+
+
diff --git a/src/utils/mobile-wizard.h b/src/utils/mobile-wizard.h
new file mode 100644
index 0000000..dc4e868
--- /dev/null
+++ b/src/utils/mobile-wizard.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * 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 2008 Red Hat, Inc.
+ */
+
+#ifndef MOBILE_WIZARD_H
+#define MOBILE_WIZARD_H
+
+#include <glib.h>
+#include <NetworkManager.h>
+#include <nm-device.h>
+
+typedef struct MobileWizard MobileWizard;
+
+typedef struct {
+	char *provider_name;
+	char *plan_name;
+	NMDeviceType devtype;
+	char *username;
+	char *password;
+	char *gsm_apn;
+} MobileWizardAccessMethod;
+
+typedef void (*MobileWizardCallback) (MobileWizard *self,
+                                      gboolean canceled,
+                                      MobileWizardAccessMethod *method,
+                                      gpointer user_data);
+
+MobileWizard *mobile_wizard_new (GtkWindow *parent,
+                                 NMDevice *device,
+                                 MobileWizardCallback cb,
+                                 gpointer user_data);
+
+void mobile_wizard_present (MobileWizard *wizard);
+
+void mobile_wizard_destroy (MobileWizard *self);
+
+#endif /* MOBILE_WIZARD_H */
+
diff --git a/src/utils/nmn-mobile-providers.c b/src/utils/nmn-mobile-providers.c
new file mode 100644
index 0000000..a496403
--- /dev/null
+++ b/src/utils/nmn-mobile-providers.c
@@ -0,0 +1,771 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Novell, Inc.
+ * Author: Tambet Ingo (tambet gmail com).
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+
+#include "nmn-mobile-providers.h"
+
+#ifndef MOBILE_BROADBAND_PROVIDER_INFO
+#define MOBILE_BROADBAND_PROVIDER_INFO DATADIR"/mobile-broadband-provider-info/serviceproviders.xml"
+#endif
+
+#define ISO_3166_COUNTRY_CODES DATADIR"/zoneinfo/iso3166.tab"
+
+static GHashTable *
+read_country_codes (void)
+{
+    GHashTable *table;
+    GIOChannel *channel;
+    GString *buffer;
+    GError *error = NULL;
+    GIOStatus status;
+
+    channel = g_io_channel_new_file (ISO_3166_COUNTRY_CODES, "r", &error);
+    if (!channel) {
+        if (error) {
+            g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": %s", error->message);
+            g_error_free (error);
+        } else
+            g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": Unknown error");
+
+        return NULL;
+    }
+
+    table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    buffer = g_string_sized_new (32);
+
+    status = G_IO_STATUS_NORMAL;
+    while (status == G_IO_STATUS_NORMAL) {
+        status = g_io_channel_read_line_string (channel, buffer, NULL, &error);
+
+        switch (status) {
+        case G_IO_STATUS_NORMAL:
+            if (buffer->str[0] != '#') {
+                char **pieces;
+
+                pieces = g_strsplit (buffer->str, "\t", 2);
+                g_hash_table_insert (table, pieces[0], g_strchomp (pieces[1]));
+                g_free (pieces);
+            }
+
+            g_string_truncate (buffer, 0);
+            break;
+        case G_IO_STATUS_EOF:
+            break;
+        case G_IO_STATUS_ERROR:
+            g_warning ("Error while reading: %s", error->message);
+            g_error_free (error);
+            break;
+        case G_IO_STATUS_AGAIN:
+            /* FIXME: Try again a few times, but really, it never happes, right? */
+            break;
+        }
+    }
+
+    g_string_free (buffer, TRUE);
+    g_io_channel_unref (channel);
+
+    return table;
+}
+
+/* XML Parser */
+
+typedef enum {
+    PARSER_TOPLEVEL = 0,
+    PARSER_COUNTRY,
+    PARSER_PROVIDER,
+    PARSER_METHOD_GSM,
+    PARSER_METHOD_GSM_APN,
+    PARSER_METHOD_CDMA,
+    PARSER_ERROR
+} MobileContextState;
+
+typedef struct {
+    GHashTable *country_codes;
+    GHashTable *table;
+
+    char *current_country;
+    GSList *current_providers;
+    NmnMobileProvider *current_provider;
+    NmnMobileAccessMethod *current_method;
+    GSList *mcc_mncs;
+
+    char *text_buffer;
+    MobileContextState state;
+} MobileParser;
+
+static NmnGsmMccMnc *
+mcc_mnc_new (const char *mcc, const char *mnc)
+{
+    NmnGsmMccMnc *m;
+
+    m = g_slice_new0 (NmnGsmMccMnc);
+    m->mcc = g_strdup (mcc);
+    m->mnc = g_strdup (mnc);
+    return m;
+}
+
+static void
+mcc_mnc_free (NmnGsmMccMnc *m)
+{
+    g_return_if_fail (m != NULL);
+    g_free (m->mcc);
+    g_free (m->mnc);
+    g_slice_free (NmnGsmMccMnc, m);
+}
+
+static GSList *
+mcc_mnc_list_copy (GSList *list)
+{
+    GSList *iter, *ret = NULL;
+
+    for (iter = list; iter; iter = g_slist_next (iter)) {
+        NmnGsmMccMnc *m = iter->data;
+
+        ret = g_slist_prepend (ret, mcc_mnc_new (m->mcc, m->mnc));
+    }
+
+    return g_slist_reverse (ret);
+}
+
+static NmnMobileAccessMethod *
+access_method_new (void)
+{
+    NmnMobileAccessMethod *method;
+
+    method = g_slice_new0 (NmnMobileAccessMethod);
+    method->refs = 1;
+    method->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) g_free);
+
+    return method;
+}
+
+NmnMobileAccessMethod *
+nmn_mobile_access_method_ref (NmnMobileAccessMethod *method)
+{
+    g_return_val_if_fail (method != NULL, NULL);
+    g_return_val_if_fail (method->refs > 0, NULL);
+ 
+    method->refs++;
+
+    return method;
+}
+
+void
+nmn_mobile_access_method_unref (NmnMobileAccessMethod *method)
+{
+    g_return_if_fail (method != NULL);
+    g_return_if_fail (method->refs > 0);
+
+    if (--method->refs == 0) {
+        g_free (method->name);
+        g_hash_table_destroy (method->lcl_names);
+        g_free (method->username);
+        g_free (method->password);
+        g_free (method->gateway);
+        g_free (method->gsm_apn);
+        g_slist_foreach (method->dns, (GFunc) g_free, NULL);
+        g_slist_free (method->dns);
+
+        g_slist_foreach (method->gsm_mcc_mnc, (GFunc) mcc_mnc_free, NULL);
+        g_slist_free (method->gsm_mcc_mnc);
+
+        g_slist_free (method->cdma_sid);
+
+        g_slice_free (NmnMobileAccessMethod, method);
+    }
+}
+
+GType
+nmn_mobile_access_method_get_type (void)
+{
+    static GType type = 0;
+
+    if (G_UNLIKELY (type == 0)) {
+        type = g_boxed_type_register_static ("NmnMobileAccessMethod",
+                                             (GBoxedCopyFunc) nmn_mobile_access_method_ref,
+                                             (GBoxedFreeFunc) nmn_mobile_access_method_unref);
+    }
+    return type;
+}
+
+
+static NmnMobileProvider *
+provider_new (void)
+{
+    NmnMobileProvider *provider;
+
+    provider = g_slice_new0 (NmnMobileProvider);
+    provider->refs = 1;
+    provider->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                 (GDestroyNotify) g_free,
+                                                 (GDestroyNotify) g_free);
+
+    return provider;
+}
+
+NmnMobileProvider *
+nmn_mobile_provider_ref (NmnMobileProvider *provider)
+{
+    provider->refs++;
+
+    return provider;
+}
+
+void
+nmn_mobile_provider_unref (NmnMobileProvider *provider)
+{
+    if (--provider->refs == 0) {
+        g_free (provider->name);
+        g_hash_table_destroy (provider->lcl_names);
+
+        g_slist_foreach (provider->methods, (GFunc) nmn_mobile_access_method_unref, NULL);
+        g_slist_free (provider->methods);
+
+        g_slice_free (NmnMobileProvider, provider);
+    }
+}
+
+GType
+nmn_mobile_provider_get_type (void)
+{
+    static GType type = 0;
+
+    if (G_UNLIKELY (type == 0)) {
+        type = g_boxed_type_register_static ("NmnMobileProvider",
+                                             (GBoxedCopyFunc) nmn_mobile_provider_ref,
+                                             (GBoxedFreeFunc) nmn_mobile_provider_unref);
+    }
+    return type;
+}
+
+static void
+provider_list_free (gpointer data)
+{
+    GSList *list = (GSList *) data;
+
+    while (list) {
+        nmn_mobile_provider_unref ((NmnMobileProvider *) list->data);
+        list = g_slist_delete_link (list, list);
+    }
+}
+
+static void
+parser_toplevel_start (MobileParser *parser,
+                       const char *name,
+                       const char **attribute_names,
+                       const char **attribute_values)
+{
+    int i;
+
+    if (!strcmp (name, "serviceproviders")) {
+        for (i = 0; attribute_names && attribute_names[i]; i++) {
+            if (!strcmp (attribute_names[i], "format")) {
+                if (strcmp (attribute_values[i], "2.0")) {
+                    g_warning ("%s: mobile broadband provider database format '%s'"
+                               " not supported.", __func__, attribute_values[i]);
+                    parser->state = PARSER_ERROR;
+                    break;
+                }
+            }
+        }
+    } else if (!strcmp (name, "country")) {
+        for (i = 0; attribute_names && attribute_names[i]; i++) {
+            if (!strcmp (attribute_names[i], "code")) {
+                char *country_code;
+                char *country;
+
+                country_code = g_ascii_strup (attribute_values[i], -1);
+                country = g_hash_table_lookup (parser->country_codes, country_code);
+                if (country) {
+                    parser->current_country = g_strdup (country);
+                    g_free (country_code);
+                } else
+                    parser->current_country = country_code;
+
+                parser->state = PARSER_COUNTRY;
+                break;
+            }
+        }
+    }
+}
+
+static void
+parser_country_start (MobileParser *parser,
+                      const char *name,
+                      const char **attribute_names,
+                      const char **attribute_values)
+{
+    if (!strcmp (name, "provider")) {
+        parser->state = PARSER_PROVIDER;
+        parser->current_provider = provider_new ();
+    }
+}
+
+static void
+parser_provider_start (MobileParser *parser,
+                       const char *name,
+                       const char **attribute_names,
+                       const char **attribute_values)
+{
+    if (!strcmp (name, "gsm"))
+        parser->state = PARSER_METHOD_GSM;
+    else if (!strcmp (name, "cdma")) {
+        parser->state = PARSER_METHOD_CDMA;
+        parser->current_method = access_method_new ();
+    }
+}
+
+static void
+parser_gsm_start (MobileParser *parser,
+                       const char *name,
+                       const char **attribute_names,
+                       const char **attribute_values)
+{
+    if (!strcmp (name, "network-id")) {
+        const char *mcc = NULL, *mnc = NULL;
+        int i;
+
+        for (i = 0; attribute_names && attribute_names[i]; i++) {
+            if (!strcmp (attribute_names[i], "mcc"))
+                mcc = attribute_values[i];
+            else if (!strcmp (attribute_names[i], "mnc"))
+                mnc = attribute_values[i];
+
+            if (mcc && strlen (mcc) && mnc && strlen (mnc)) {
+                parser->mcc_mncs = g_slist_append (parser->mcc_mncs, mcc_mnc_new (mcc, mnc));
+                break;
+            }
+        }
+    } else if (!strcmp (name, "apn")) {
+        int i;
+
+        for (i = 0; attribute_names && attribute_names[i]; i++) {
+            if (!strcmp (attribute_names[i], "value")) {
+
+                parser->state = PARSER_METHOD_GSM_APN;
+                parser->current_method = access_method_new ();
+                parser->current_method->gsm_apn = g_strdup (attribute_values[i]);
+                break;
+            }
+        }
+    }
+}
+
+static void
+parser_cdma_start (MobileParser *parser,
+                   const char *name,
+                   const char **attribute_names,
+                   const char **attribute_values)
+{
+    if (!strcmp (name, "sid")) {
+        int i;
+
+        for (i = 0; attribute_names && attribute_names[i]; i++) {
+            if (!strcmp (attribute_names[i], "value")) {
+                unsigned long tmp;
+
+                errno = 0;
+                tmp = strtoul (attribute_values[i], NULL, 10);
+                if (errno == 0 && tmp > 0)
+                    parser->current_method->cdma_sid = g_slist_prepend (parser->current_method->cdma_sid,
+                                                                        GUINT_TO_POINTER ((guint32) tmp));
+                break;
+            }
+        }
+    }
+}
+
+static void
+mobile_parser_start_element (GMarkupParseContext *context,
+                             const gchar *element_name,
+                             const gchar **attribute_names,
+                             const gchar **attribute_values,
+                             gpointer data,
+                             GError **error)
+{
+    MobileParser *parser = (MobileParser *) data;
+
+    if (parser->text_buffer) {
+        g_free (parser->text_buffer);
+        parser->text_buffer = NULL;
+    }
+
+    switch (parser->state) {
+    case PARSER_TOPLEVEL:
+        parser_toplevel_start (parser, element_name, attribute_names, attribute_values);
+        break;
+    case PARSER_COUNTRY:
+        parser_country_start (parser, element_name, attribute_names, attribute_values);
+        break;
+    case PARSER_PROVIDER:
+        parser_provider_start (parser, element_name, attribute_names, attribute_values);
+        break;
+    case PARSER_METHOD_GSM:
+        parser_gsm_start (parser, element_name, attribute_names, attribute_values);
+        break;
+    case PARSER_METHOD_CDMA:
+        parser_cdma_start (parser, element_name, attribute_names, attribute_values);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+parser_country_end (MobileParser *parser,
+                    const char *name)
+{
+    if (!strcmp (name, "country")) {
+        g_hash_table_insert (parser->table, parser->current_country, parser->current_providers);
+        parser->current_country = NULL;
+        parser->current_providers = NULL;
+        parser->text_buffer = NULL;
+        parser->state = PARSER_TOPLEVEL;
+    }
+}
+
+static void
+parser_provider_end (MobileParser *parser,
+                     const char *name)
+{
+    if (!strcmp (name, "name")) {
+        if (!parser->current_provider->name) {
+            /* Use the first one. */
+            parser->current_provider->name = parser->text_buffer;
+            parser->text_buffer = NULL;
+        }
+    } else if (!strcmp (name, "provider")) {
+        parser->current_provider->methods = g_slist_reverse (parser->current_provider->methods);
+
+        parser->current_providers = g_slist_prepend (parser->current_providers, parser->current_provider);
+        parser->current_provider = NULL;
+        parser->text_buffer = NULL;
+        parser->state = PARSER_COUNTRY;
+    }
+}
+
+static void
+parser_gsm_end (MobileParser *parser,
+                 const char *name)
+{
+    if (!strcmp (name, "gsm")) {
+        g_slist_foreach (parser->mcc_mncs, (GFunc) mcc_mnc_free, NULL);
+        g_slist_free (parser->mcc_mncs);
+        parser->mcc_mncs = NULL;
+        parser->text_buffer = NULL;
+        parser->state = PARSER_PROVIDER;
+    }
+}
+
+static void
+parser_gsm_apn_end (MobileParser *parser,
+                    const char *name)
+{
+    if (!strcmp (name, "name")) {
+        if (!parser->current_method->name) {
+            /* Use the first one. */
+            parser->current_method->name = parser->text_buffer;
+            parser->text_buffer = NULL;
+        }
+    } else if (!strcmp (name, "username")) {
+        parser->current_method->username = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "password")) {
+        parser->current_method->password = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "dns")) {
+        parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "gateway")) {
+        parser->current_method->gateway = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "apn")) {
+        parser->current_method->type = NMN_MOBILE_ACCESS_METHOD_TYPE_GSM;
+        parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
+        parser->current_method->gsm_mcc_mnc = mcc_mnc_list_copy (parser->mcc_mncs);
+
+        if (!parser->current_method->name)
+            parser->current_method->name = g_strdup (_("Default"));
+
+        parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
+                                                             parser->current_method);
+        parser->current_method = NULL;
+        parser->text_buffer = NULL;
+        parser->state = PARSER_METHOD_GSM;
+    }
+}
+
+static void
+parser_cdma_end (MobileParser *parser,
+                 const char *name)
+{
+    if (!strcmp (name, "username")) {
+        parser->current_method->username = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "password")) {
+        parser->current_method->password = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "dns")) {
+        parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "gateway")) {
+        parser->current_method->gateway = parser->text_buffer;
+        parser->text_buffer = NULL;
+    } else if (!strcmp (name, "cdma")) {
+        parser->current_method->type = NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA;
+        parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
+        parser->current_method->cdma_sid = g_slist_reverse (parser->current_method->cdma_sid);
+
+        if (!parser->current_method->name)
+            parser->current_method->name = g_strdup (parser->current_provider->name);
+
+        parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
+                                                             parser->current_method);
+        parser->current_method = NULL;
+        parser->text_buffer = NULL;
+        parser->state = PARSER_PROVIDER;
+    }
+}
+
+static void
+mobile_parser_end_element (GMarkupParseContext *context,
+                           const gchar *element_name,
+                           gpointer data,
+                           GError **error)
+{
+    MobileParser *parser = (MobileParser *) data;
+
+    switch (parser->state) {
+    case PARSER_COUNTRY:
+        parser_country_end (parser, element_name);
+        break;
+    case PARSER_PROVIDER:
+        parser_provider_end (parser, element_name);
+        break;
+    case PARSER_METHOD_GSM:
+        parser_gsm_end (parser, element_name);
+        break;
+    case PARSER_METHOD_GSM_APN:
+        parser_gsm_apn_end (parser, element_name);
+        break;
+    case PARSER_METHOD_CDMA:
+        parser_cdma_end (parser, element_name);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+mobile_parser_characters (GMarkupParseContext *context,
+                          const gchar *text,
+                          gsize text_len,
+                          gpointer data,
+                          GError **error)
+{
+    MobileParser *parser = (MobileParser *) data;
+
+    g_free (parser->text_buffer);
+    parser->text_buffer = g_strdup (text);
+}
+
+static const GMarkupParser mobile_parser = {
+    mobile_parser_start_element,
+    mobile_parser_end_element,
+    mobile_parser_characters,
+    NULL, /* passthrough */
+    NULL /* error */
+};
+
+GHashTable *
+nmn_mobile_providers_parse (GHashTable **out_ccs)
+{
+    GMarkupParseContext *ctx;
+    GIOChannel *channel;
+    MobileParser parser;
+    GError *error = NULL;
+    char buffer[4096];
+    GIOStatus status;
+    gsize len = 0;
+
+    memset (&parser, 0, sizeof (MobileParser));
+
+    parser.country_codes = read_country_codes ();
+    if (!parser.country_codes)
+        goto out;
+
+    channel = g_io_channel_new_file (MOBILE_BROADBAND_PROVIDER_INFO, "r", &error);
+    if (!channel) {
+        if (error) {
+            g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": %s", error->message);
+            g_error_free (error);
+        } else
+            g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": Unknown error");
+
+        goto out;
+    }
+
+    parser.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, provider_list_free);
+    parser.state = PARSER_TOPLEVEL;
+
+    ctx = g_markup_parse_context_new (&mobile_parser, 0, &parser, NULL);
+
+    status = G_IO_STATUS_NORMAL;
+    while (status == G_IO_STATUS_NORMAL) {
+        status = g_io_channel_read_chars (channel, buffer, sizeof (buffer), &len, &error);
+
+        switch (status) {
+        case G_IO_STATUS_NORMAL:
+            if (!g_markup_parse_context_parse (ctx, buffer, len, &error)) {
+                status = G_IO_STATUS_ERROR;
+                g_warning ("Error while parsing XML: %s", error->message);
+                g_error_free (error);;
+            }
+            break;
+        case G_IO_STATUS_EOF:
+            break;
+        case G_IO_STATUS_ERROR:
+            g_warning ("Error while reading: %s", error->message);
+            g_error_free (error);
+            break;
+        case G_IO_STATUS_AGAIN:
+            /* FIXME: Try again a few times, but really, it never happes, right? */
+            break;
+        }
+    }
+
+    g_io_channel_unref (channel);
+    g_markup_parse_context_free (ctx);
+
+    if (parser.current_provider) {
+        g_warning ("pending current provider");
+        nmn_mobile_provider_unref (parser.current_provider);
+    }
+
+    if (parser.current_providers) {
+        g_warning ("pending current providers");
+        provider_list_free (parser.current_providers);
+    }
+
+    g_free (parser.current_country);
+    g_free (parser.text_buffer);
+
+ out:
+    if (parser.country_codes) {
+        if (out_ccs)
+            *out_ccs = parser.country_codes;
+        else
+            g_hash_table_destroy (parser.country_codes);
+    }
+
+    return parser.table;
+}
+
+static void
+dump_generic (NmnMobileAccessMethod *method)
+{
+    GSList *iter;
+    GString *dns;
+
+    g_print ("        username: %s\n", method->username ? method->username : "");
+    g_print ("        password: %s\n", method->password ? method->password : "");
+
+    dns = g_string_new (NULL);
+    for (iter = method->dns; iter; iter = g_slist_next (iter))
+        g_string_append_printf (dns, "%s%s", dns->len ? ", " : "", (char *) iter->data);
+    g_print ("        dns     : %s\n", dns->str);
+    g_string_free (dns, TRUE);
+
+    g_print ("        gateway : %s\n", method->gateway ? method->gateway : "");
+}
+
+static void
+dump_cdma (NmnMobileAccessMethod *method)
+{
+    GSList *iter;
+
+    g_print ("     CDMA: %s\n", method->name);
+
+    dump_generic (method);
+
+    for (iter = method->cdma_sid; iter; iter = g_slist_next (iter))
+        g_print ("        SID: %d\n", GPOINTER_TO_UINT (iter->data));
+}
+
+static void
+dump_gsm (NmnMobileAccessMethod *method)
+{
+    GSList *iter;
+
+    g_print ("     APN: %s (%s)\n", method->name, method->gsm_apn);
+
+    dump_generic (method);
+
+    for (iter = method->gsm_mcc_mnc; iter; iter = g_slist_next (iter)) {
+        NmnGsmMccMnc *m = iter->data;
+        g_print ("        MCC/MNC: %s-%s\n", m->mcc, m->mnc);
+    }
+}
+
+static void
+dump_country (gpointer key, gpointer value, gpointer user_data)
+{
+    GSList *citer, *miter;
+
+    for (citer = value; citer; citer = g_slist_next (citer)) {
+        NmnMobileProvider *provider = citer->data;
+
+        g_print ("Provider: %s (%s)\n", provider->name, (const char *) key);
+        for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
+            NmnMobileAccessMethod *method = miter->data;
+
+            switch (method->type) {
+            case NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA:
+                dump_cdma (method);
+                break;
+            case NMN_MOBILE_ACCESS_METHOD_TYPE_GSM:
+                dump_gsm (method);
+                break;
+            default:
+                break;
+            }
+            g_print ("\n");
+        }
+    }
+}
+
+void
+nmn_mobile_providers_dump (GHashTable *providers)
+{
+    g_return_if_fail (providers != NULL);
+    g_hash_table_foreach (providers, dump_country, NULL);
+}
+
+
diff --git a/src/utils/nmn-mobile-providers.h b/src/utils/nmn-mobile-providers.h
new file mode 100644
index 0000000..dcd2e9a
--- /dev/null
+++ b/src/utils/nmn-mobile-providers.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Novell, Inc.
+ * Author: Tambet Ingo (tambet gmail com).
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef NMN_MOBILE_PROVIDERS_H
+#define NMN_MOBILE_PROVIDERS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NMN_TYPE_MOBILE_PROVIDER (nmn_mobile_provider_get_type ())
+#define NMN_TYPE_MOBILE_ACCESS_METHOD (nmn_mobile_access_method_get_type ())
+
+typedef enum {
+    NMN_MOBILE_ACCESS_METHOD_TYPE_UNKNOWN = 0,
+    NMN_MOBILE_ACCESS_METHOD_TYPE_GSM,
+    NMN_MOBILE_ACCESS_METHOD_TYPE_CDMA
+} NmnMobileProviderType;
+
+typedef struct {
+    char *mcc;
+    char *mnc;
+} NmnGsmMccMnc;
+
+typedef struct {
+    char *name;
+    /* maps lang (char *) -> name (char *) */
+    GHashTable *lcl_names;
+
+    char *username;
+    char *password;
+    char *gateway;
+    GSList *dns; /* GSList of 'char *' */
+
+    /* Only used with NMN_PROVIDER_TYPE_GSM */
+    char *gsm_apn;
+    GSList *gsm_mcc_mnc; /* GSList of NmnGsmMccMnc */
+
+    /* Only used with NMN_PROVIDER_TYPE_CDMA */
+    GSList *cdma_sid; /* GSList of guint32 */
+
+    NmnMobileProviderType type;
+
+    gint refs;
+} NmnMobileAccessMethod;
+
+typedef struct {
+    char *name;
+    /* maps lang (char *) -> name (char *) */
+    GHashTable *lcl_names;
+
+    GSList *methods; /* GSList of NmnMobileAccessMethod */
+
+    gint refs;
+} NmnMobileProvider;
+
+
+GType nmn_mobile_provider_get_type (void);
+GType nmn_mobile_access_method_get_type (void);
+
+NmnMobileProvider *nmn_mobile_provider_ref   (NmnMobileProvider *provider);
+void               nmn_mobile_provider_unref (NmnMobileProvider *provider);
+
+NmnMobileAccessMethod *nmn_mobile_access_method_ref   (NmnMobileAccessMethod *method);
+void                   nmn_mobile_access_method_unref (NmnMobileAccessMethod *method);
+
+/* Returns a hash table where keys are country names 'char *',
+   values are a 'GSList *' of 'NmnMobileProvider *'.
+   Everything is destroyed with g_hash_table_destroy (). */
+
+GHashTable *nmn_mobile_providers_parse (GHashTable **out_ccs);
+
+void nmn_mobile_providers_dump (GHashTable *providers);
+
+#endif /* NMN_MOBILE_PROVIDERS_H */
diff --git a/src/utils/utils.c b/src/utils/utils.c
index eee14e8..6dfdd0f 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -29,6 +29,7 @@
 #include <nm-gsm-device.h>
 #include <nm-cdma-device.h>
 #include <nm-access-point.h>
+#include <nm-settings.h>
 
 #include <nm-setting-connection.h>
 #include <nm-setting-wired.h>
@@ -826,3 +827,54 @@ utils_ether_ntop (const struct ether_addr *mac)
 	                        mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
 }
 
+
+static void
+add_one_name (gpointer data, gpointer user_data)
+{
+	NMExportedConnection *exported = NM_EXPORTED_CONNECTION (data);
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	const char *id;
+	GSList **list = (GSList **) user_data;
+
+	connection = nm_exported_connection_get_connection (exported);
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	id = nm_setting_connection_get_id (s_con);
+	g_assert (id);
+	*list = g_slist_append (*list, (gpointer) id);
+}
+
+char *
+utils_next_available_name (GSList *connections, const char *format)
+{
+	GSList *names = NULL, *iter;
+	char *cname = NULL;
+	int i = 0;
+
+	g_slist_foreach (connections, add_one_name, &names);
+
+	if (g_slist_length (names) == 0)
+		return g_strdup_printf (format, 1);
+
+	/* Find the next available unique connection name */
+	while (!cname && (i++ < 10000)) {
+		char *temp;
+		gboolean found = FALSE;
+
+		temp = g_strdup_printf (format, i);
+		for (iter = names; iter; iter = g_slist_next (iter)) {
+			if (!strcmp (iter->data, temp)) {
+				found = TRUE;
+				break;
+			}
+		}
+		if (!found)
+			cname = temp;
+		else
+			g_free (temp);
+	}
+
+	g_slist_free (names);
+	return cname;
+}
+
diff --git a/src/utils/utils.h b/src/utils/utils.h
index 97d1ca7..9e26442 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -50,5 +50,7 @@ char *utils_ether_ntop (const struct ether_addr *mac);
 
 gboolean utils_mac_valid (const struct ether_addr *addr);
 
+char *utils_next_available_name (GSList *connections, const char *format);
+
 #endif /* UTILS_H */
 



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