[network-manager-applet/dcbw/libnm-bgo736911: 7/16] libnma: new libnm-based version of libnm-gtk



commit b402d982de37d6bd2a279e32b0d457b1e0ad8282
Author: Dan Winship <danw redhat com>
Date:   Tue Nov 25 10:00:33 2014 -0500

    libnma: new libnm-based version of libnm-gtk
    
    Add a new libnm-based version of libnm-gtk, called "libnma" (which
    matches the naming convention of the functions better anyway). Also,
    rename the files from nm-* to nma-*.
    
    Drop nm-wireless-dialog (which was deprecated in favor of
    nma-wifi-dialog) and nm-ui-utils.h (whose functionality has been
    merged into NMDevice in libnm).
    
    (libnm-glib to libnm porting mostly based on a patch by Jiří Klimeš.)

 .gitignore                           |    3 +
 configure.ac                         |    2 +
 src/Makefile.am                      |    2 +-
 src/libnm-gtk/libnm-gtk.pc.in        |    2 +-
 src/libnma/Makefile.am               |   75 ++
 src/libnma/init.c                    |   38 +
 src/libnma/libnma.pc.in              |   11 +
 src/libnma/nma-mobile-providers.c    | 1575 ++++++++++++++++++++++++++++++++
 src/libnma/nma-mobile-providers.h    |  144 +++
 src/libnma/nma-mobile-wizard.c       | 1675 ++++++++++++++++++++++++++++++++++
 src/libnma/nma-mobile-wizard.h       |   62 ++
 src/libnma/nma-vpn-password-dialog.c |  446 +++++++++
 src/libnma/nma-vpn-password-dialog.h |   80 ++
 src/libnma/nma-wifi-dialog.c         | 1407 ++++++++++++++++++++++++++++
 src/libnma/nma-wifi-dialog.h         |   80 ++
 src/libnma/wifi.ui                   |  234 +++++
 16 files changed, 5834 insertions(+), 2 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 1977994..403ef86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,9 @@ org.gnome.nm-applet.gschema.xml
 org.gnome.nm-applet.gschema.valid
 src/connection-editor/nm-connection-editor
 src/connection-editor/nm-connection-editor-service-glue.h
+src/libnma/libnma.pc
+src/libnma/NMA-1.0.gir
+src/libnma/NMA-1.0.typelib
 src/libnm-gtk/libnm-gtk.pc
 src/libnm-gtk/NMGtk-1.0.gir
 src/libnm-gtk/NMGtk-1.0.typelib
diff --git a/configure.ac b/configure.ac
index 7dffcfe..d209e2d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -147,6 +147,8 @@ src/libnm-gtk/Makefile
 src/libnm-gtk/tests/Makefile
 src/libnm-gtk/examples/Makefile
 src/libnm-gtk/libnm-gtk.pc
+src/libnma/Makefile
+src/libnma/libnma.pc
 src/utils/Makefile
 src/utils/tests/Makefile
 src/wireless-security/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 80b23a7..c22756d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = utils wireless-security libnm-gtk connection-editor
+SUBDIRS = utils wireless-security libnm-gtk libnma connection-editor
 
 bin_PROGRAMS = nm-applet
 
diff --git a/src/libnm-gtk/libnm-gtk.pc.in b/src/libnm-gtk/libnm-gtk.pc.in
index 33ca9f3..c032a97 100644
--- a/src/libnm-gtk/libnm-gtk.pc.in
+++ b/src/libnm-gtk/libnm-gtk.pc.in
@@ -5,7 +5,7 @@ libdir= libdir@
 nmversion=0.9.10
 
 Name: libnm-gtk
-Description: NetworkManager Gtk+ dialogs
+Description: NetworkManager UI utilities (libnm-glib version)
 Version: @PACKAGE_VERSION@
 Requires: NetworkManager >= ${nmversion} libnm-util >= ${nmversion} libnm-glib >= ${nmversion} gtk+-3.0 >= 
3.4
 Requires.private: dbus-glib-1
diff --git a/src/libnma/Makefile.am b/src/libnma/Makefile.am
new file mode 100644
index 0000000..2584822
--- /dev/null
+++ b/src/libnma/Makefile.am
@@ -0,0 +1,75 @@
+uidir = $(datadir)/libnma
+ui_DATA = wifi.ui
+
+libnmadir = $(includedir)/libnma
+
+libnma_HEADERS = \
+       nma-wifi-dialog.h \
+       nma-mobile-wizard.h \
+       nma-mobile-providers.h \
+       nma-vpn-password-dialog.h
+
+lib_LTLIBRARIES = libnma.la
+
+libnma_la_SOURCES = \
+       nma-wifi-dialog.c \
+       nma-mobile-wizard.c \
+       nma-mobile-providers.c \
+       nma-vpn-password-dialog.c \
+       init.c
+
+libnma_la_CFLAGS = \
+       $(GTK_CFLAGS) \
+       $(LIBNM_CFLAGS) \
+       $(GUDEV_CFLAGS) \
+       -DLIBNM_BUILD \
+       -DICONDIR=\""$(datadir)/icons"\" \
+       -DUIDIR=\""$(uidir)"\" \
+       -DBINDIR=\""$(bindir)"\" \
+       -DSYSCONFDIR=\""$(sysconfdir)"\" \
+       -DLIBEXECDIR=\""$(libexecdir)"\" \
+       -DDATADIR=\""$(datadir)"\" \
+       -DAUTOSTARTDIR=\""$(sysconfdir)/xdg/autostart"\" \
+       -DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\" \
+       -DNMALOCALEDIR=\"$(datadir)/locale\" \
+       -I${top_srcdir}/src/utils \
+       -I${top_srcdir}/src/wireless-security
+
+libnma_la_LIBADD = \
+       $(GTK_LIBS) \
+       $(LIBNM_LIBS) \
+       $(GUDEV_LIBS) \
+       $(top_builddir)/src/wireless-security/libwireless-security-libnm.la
+
+libnma_la_LDFLAGS = -Wl,-no-undefined \
+       -export-symbols-regex '^nma_.*'
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnma.pc
+
+-include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS =
+
+if HAVE_INTROSPECTION
+introspection_sources = $(libnma_HEADERS) $(libnma_la_SOURCES)
+
+NMA-1.0.gir: libnma.la
+NMA_1_0_gir_INCLUDES = NMClient-1.0 NetworkManager-1.0 Gtk-3.0
+NMA_1_0_gir_EXPORT_PACKAGES = libnma
+NMA_1_0_gir_CFLAGS = $(libnma_la_CFLAGS)
+NMA_1_0_gir_LIBS = libnma.la
+NMA_1_0_gir_FILES = $(introspection_sources)
+NMA_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=NMA --symbol-prefix=nma
+INTROSPECTION_GIRS += NMA-1.0.gir
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES = $(gir_DATA) $(typelib_DATA)
+endif
+
+EXTRA_DIST = libnma.pc.in $(ui_DATA)
diff --git a/src/libnma/init.c b/src/libnma/init.c
new file mode 100644
index 0000000..a120a2b
--- /dev/null
+++ b/src/libnma/init.c
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <libintl.h>
+#include <glib/gi18n-lib.h>
+
+static void __attribute__((constructor))
+_libnma_init (void)
+{
+       static gboolean initialized = FALSE;
+
+       if (initialized)
+               return;
+       initialized = TRUE;
+
+       bindtextdomain (GETTEXT_PACKAGE, NMALOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+}
+
diff --git a/src/libnma/libnma.pc.in b/src/libnma/libnma.pc.in
new file mode 100644
index 0000000..b1755b5
--- /dev/null
+++ b/src/libnma/libnma.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+includedir= includedir@
+libdir= libdir@
+
+Name: libnma
+Description: NetworkManager UI utilities (libnm version)
+Version: @PACKAGE_VERSION@
+Requires: libnm-1.0
+Cflags: -I${includedir}/libnma
+Libs: -L${libdir} -lnma
\ No newline at end of file
diff --git a/src/libnma/nma-mobile-providers.c b/src/libnma/nma-mobile-providers.c
new file mode 100644
index 0000000..86d9d06
--- /dev/null
+++ b/src/libnma/nma-mobile-providers.c
@@ -0,0 +1,1575 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Lanedo GmbH
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nma-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 ISO_CODES_PREFIX"/share/xml/iso-codes/iso_3166.xml"
+#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX"/share/locale"
+
+/******************************************************************************/
+/* Access method type */
+
+G_DEFINE_BOXED_TYPE (NMAMobileAccessMethod,
+                     nma_mobile_access_method,
+                     nma_mobile_access_method_ref,
+                     nma_mobile_access_method_unref)
+
+struct _NMAMobileAccessMethod {
+       volatile gint refs;
+
+       char *name;
+       /* maps lang (char *) -> name (char *) */
+       GHashTable *lcl_names;
+
+       char *username;
+       char *password;
+       char *gateway;
+       GPtrArray *dns; /* GPtrArray of 'char *' */
+
+       /* Only used with 3GPP family type providers */
+       char *apn;
+
+       NMAMobileFamily family;
+};
+
+static NMAMobileAccessMethod *
+access_method_new (void)
+{
+       NMAMobileAccessMethod *method;
+
+       method = g_slice_new0 (NMAMobileAccessMethod);
+       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;
+}
+
+NMAMobileAccessMethod *
+nma_mobile_access_method_ref (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+       g_return_val_if_fail (method->refs > 0, NULL);
+
+       g_atomic_int_inc (&method->refs);
+
+       return method;
+}
+
+void
+nma_mobile_access_method_unref (NMAMobileAccessMethod *method)
+{
+       g_return_if_fail (method != NULL);
+       g_return_if_fail (method->refs > 0);
+
+       if (g_atomic_int_dec_and_test (&method->refs)) {
+               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->apn);
+
+               if (method->dns)
+                       g_ptr_array_unref (method->dns);
+
+               g_slice_free (NMAMobileAccessMethod, method);
+       }
+}
+
+/**
+ * nma_mobile_access_method_get_name:
+ *
+ * Returns: (transfer none): the name of the method.
+ */
+const gchar *
+nma_mobile_access_method_get_name (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->name;
+}
+
+/**
+ * nma_mobile_access_method_get_username:
+ *
+ * Returns: (transfer none): the username.
+ */
+const gchar *
+nma_mobile_access_method_get_username (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->username;
+}
+
+/**
+ * nma_mobile_access_method_get_password:
+ *
+ * Returns: (transfer none): the password.
+ */
+const gchar *
+nma_mobile_access_method_get_password (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->password;
+}
+
+/**
+ * nma_mobile_access_method_get_gateway:
+ *
+ * Returns: (transfer none): the gateway.
+ */
+const gchar *
+nma_mobile_access_method_get_gateway (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->gateway;
+}
+
+/**
+ * nma_mobile_access_method_get_dns:
+ *
+ * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): the list of DNS.
+ */
+const gchar **
+nma_mobile_access_method_get_dns (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->dns ? (const gchar **)method->dns->pdata : NULL;
+}
+
+/**
+ * nma_mobile_access_method_get_3gpp_apn:
+ *
+ * Returns: (transfer none): the 3GPP APN.
+ */
+const gchar *
+nma_mobile_access_method_get_3gpp_apn (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NULL);
+
+       return method->apn;
+}
+
+/**
+ * nma_mobile_access_method_get_family:
+ *
+ * Returns: a #NMAMobileFamily.
+ */
+NMAMobileFamily
+nma_mobile_access_method_get_family (NMAMobileAccessMethod *method)
+{
+       g_return_val_if_fail (method != NULL, NMA_MOBILE_FAMILY_UNKNOWN);
+
+       return method->family;
+}
+
+/******************************************************************************/
+/* Mobile provider type */
+
+G_DEFINE_BOXED_TYPE (NMAMobileProvider,
+                     nma_mobile_provider,
+                     nma_mobile_provider_ref,
+                     nma_mobile_provider_unref)
+
+struct _NMAMobileProvider {
+       volatile gint refs;
+
+       char *name;
+       /* maps lang (char *) -> name (char *) */
+       GHashTable *lcl_names;
+
+       GSList *methods; /* GSList of NmaMobileAccessMethod */
+
+       GPtrArray *mcc_mnc;  /* GPtrArray of strings */
+       GArray *cdma_sid; /* GArray of guint32 */
+};
+
+static NMAMobileProvider *
+provider_new (void)
+{
+       NMAMobileProvider *provider;
+
+       provider = g_slice_new0 (NMAMobileProvider);
+       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;
+}
+
+NMAMobileProvider *
+nma_mobile_provider_ref (NMAMobileProvider *provider)
+{
+       g_return_val_if_fail (provider != NULL, NULL);
+       g_return_val_if_fail (provider->refs > 0, NULL);
+
+       g_atomic_int_inc (&provider->refs);
+
+       return provider;
+}
+
+void
+nma_mobile_provider_unref (NMAMobileProvider *provider)
+{
+       if (g_atomic_int_dec_and_test (&provider->refs)) {
+               g_free (provider->name);
+               g_hash_table_destroy (provider->lcl_names);
+
+               g_slist_foreach (provider->methods, (GFunc) nma_mobile_access_method_unref, NULL);
+               g_slist_free (provider->methods);
+
+               if (provider->mcc_mnc)
+                       g_ptr_array_unref (provider->mcc_mnc);
+
+               if (provider->cdma_sid)
+                       g_array_unref (provider->cdma_sid);
+
+               g_slice_free (NMAMobileProvider, provider);
+       }
+}
+
+/**
+ * nma_mobile_provider_get_name:
+ *
+ * Returns: (transfer none): the name of the provider.
+ */
+const gchar *
+nma_mobile_provider_get_name (NMAMobileProvider *provider)
+{
+       g_return_val_if_fail (provider != NULL, NULL);
+
+       return provider->name;
+}
+
+/**
+ * nma_mobile_provider_get_methods:
+ * @provider: a #NMAMobileProvider
+ *
+ * Returns: (element-type NMAMobileAccessMethod) (transfer none): the
+ *      list of #NMAMobileAccessMethod this provider exposes.
+ */
+GSList *
+nma_mobile_provider_get_methods (NMAMobileProvider *provider)
+{
+       g_return_val_if_fail (provider != NULL, NULL);
+
+       return provider->methods;
+}
+
+/**
+ * nma_mobile_provider_get_3gpp_mcc_mnc:
+ * @provider: a #NMAMobileProvider
+ *
+ * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): a
+ *      list of strings with the MCC and MNC codes this provider exposes.
+ */
+const gchar **
+nma_mobile_provider_get_3gpp_mcc_mnc (NMAMobileProvider *provider)
+{
+       g_return_val_if_fail (provider != NULL, NULL);
+
+       return provider->mcc_mnc ? (const gchar **)provider->mcc_mnc->pdata : NULL;
+}
+
+/**
+ * nma_mobile_provider_get_cdma_sid:
+ * @provider: a #NMAMobileProvider
+ *
+ * Returns: (transfer none) (array zero-terminated=1) (element-type guint32): the
+ *      list of CDMA SIDs this provider exposes
+ */
+const guint32 *
+nma_mobile_provider_get_cdma_sid (NMAMobileProvider *provider)
+{
+       g_return_val_if_fail (provider != NULL, NULL);
+
+       return provider->cdma_sid ? (const guint32 *)provider->cdma_sid->data : NULL;
+}
+
+/******************************************************************************/
+/* Country Info type */
+
+G_DEFINE_BOXED_TYPE (NMACountryInfo,
+                     nma_country_info,
+                     nma_country_info_ref,
+                     nma_country_info_unref)
+
+struct _NMACountryInfo {
+       volatile gint refs;
+
+       char *country_code;
+       char *country_name;
+       GSList *providers;
+};
+
+static NMACountryInfo *
+country_info_new (const char *country_code,
+                  const gchar *country_name)
+{
+       NMACountryInfo *country_info;
+
+       country_info = g_slice_new0 (NMACountryInfo);
+       country_info->refs = 1;
+       country_info->country_code = g_strdup (country_code);
+       country_info->country_name = g_strdup (country_name);
+       return country_info;
+}
+
+NMACountryInfo *
+nma_country_info_ref (NMACountryInfo *country_info)
+{
+       g_return_val_if_fail (country_info != NULL, NULL);
+       g_return_val_if_fail (country_info->refs > 0, NULL);
+
+       g_atomic_int_inc (&country_info->refs);
+
+       return country_info;
+}
+
+void
+nma_country_info_unref (NMACountryInfo *country_info)
+{
+       if (g_atomic_int_dec_and_test (&country_info->refs)) {
+               g_free (country_info->country_code);
+               g_free (country_info->country_name);
+               g_slist_free_full (country_info->providers,
+                                  (GDestroyNotify) nma_mobile_provider_unref);
+               g_slice_free (NMACountryInfo, country_info);
+       }
+}
+
+/**
+ * nma_country_info_get_country_code:
+ *
+ * Returns: (transfer none): the code of the country.
+ */
+const gchar *
+nma_country_info_get_country_code (NMACountryInfo *country_info)
+{
+       g_return_val_if_fail (country_info != NULL, NULL);
+
+       return country_info->country_code;
+}
+
+/**
+ * nma_country_info_get_country_name:
+ *
+ * Returns: (transfer none): the name of the country.
+ */
+const gchar *
+nma_country_info_get_country_name (NMACountryInfo *country_info)
+{
+       g_return_val_if_fail (country_info != NULL, NULL);
+
+       return country_info->country_name;
+}
+
+/**
+ * nma_country_info_get_providers:
+ *
+ * Returns: (element-type NMAMobileProvider) (transfer none): the
+ *      list of #NMAMobileProvider this country exposes.
+ */
+GSList *
+nma_country_info_get_providers (NMACountryInfo *country_info)
+{
+       g_return_val_if_fail (country_info != NULL, NULL);
+
+       return country_info->providers;
+}
+
+/******************************************************************************/
+/* XML Parser for iso_3166.xml */
+
+static void
+iso_3166_parser_start_element (GMarkupParseContext *context,
+                               const gchar *element_name,
+                               const gchar **attribute_names,
+                               const gchar **attribute_values,
+                               gpointer data,
+                               GError **error)
+{
+       int i;
+       const char *country_code = NULL;
+       const char *common_name = NULL;
+       const char *name = NULL;
+       GHashTable *table = (GHashTable *) data;
+
+       if (!strcmp (element_name, "iso_3166_entry")) {
+               NMACountryInfo *country_info;
+
+               for (i = 0; attribute_names && attribute_names[i]; i++) {
+                       if (!strcmp (attribute_names[i], "alpha_2_code"))
+                               country_code = attribute_values[i];
+                       else if (!strcmp (attribute_names[i], "common_name"))
+                               common_name = attribute_values[i];
+                       else if (!strcmp (attribute_names[i], "name"))
+                               name = attribute_values[i];
+               }
+               if (!country_code) {
+                       g_warning ("%s: missing mandatory 'alpha_2_code' atribute in '%s'"
+                                  " element.", __func__, element_name);
+                       return;
+               }
+               if (!name) {
+                       g_warning ("%s: missing mandatory 'name' atribute in '%s'"
+                                  " element.", __func__, element_name);
+                       return;
+               }
+
+               country_info = country_info_new (country_code,
+                                                dgettext ("iso_3166", common_name ? common_name : name));
+
+               g_hash_table_insert (table, g_strdup (country_code), country_info);
+       }
+}
+
+static const GMarkupParser iso_3166_parser = {
+       iso_3166_parser_start_element,
+       NULL, /* end element */
+       NULL, /* text */
+       NULL, /* passthrough */
+       NULL  /* error */
+};
+
+static GHashTable *
+read_country_codes (const gchar *country_codes_file,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+       GHashTable *table = NULL;
+       GMarkupParseContext *ctx;
+       char *buf;
+       gsize buf_len;
+
+       /* Set domain to iso_3166 for country name translation */
+       bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR);
+       bind_textdomain_codeset ("iso_3166", "UTF-8");
+
+       if (!g_file_get_contents (country_codes_file, &buf, &buf_len, error)) {
+               g_prefix_error (error,
+                               "Failed to load '%s' from 'iso-codes': ",
+                               country_codes_file);
+               return NULL;
+       }
+
+       table = g_hash_table_new_full (g_str_hash,
+                                      g_str_equal,
+                                      g_free,
+                                      (GDestroyNotify)nma_country_info_unref);
+
+       ctx = g_markup_parse_context_new (&iso_3166_parser, 0, table, NULL);
+       if (!g_markup_parse_context_parse (ctx, buf, buf_len, error)) {
+               g_prefix_error (error,
+                               "Failed to parse '%s' from 'iso-codes': ",
+                               country_codes_file);
+               g_hash_table_destroy (table);
+               return NULL;
+       }
+
+       g_markup_parse_context_free (ctx);
+       g_free (buf);
+       return table;
+}
+
+/******************************************************************************/
+/* XML Parser for serviceproviders.xml */
+
+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 *table;
+
+       char *current_country;
+       GSList *current_providers;
+       NMAMobileProvider *current_provider;
+       NMAMobileAccessMethod *current_method;
+
+       char *text_buffer;
+       MobileContextState state;
+} MobileParser;
+
+static void
+provider_list_free (gpointer data)
+{
+       GSList *list = (GSList *) data;
+
+       while (list) {
+               nma_mobile_provider_unref ((NMAMobileProvider *) 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;
+                               NMACountryInfo *country_info;
+
+                               country_code = g_ascii_strup (attribute_values[i], -1);
+                               country_info = g_hash_table_lookup (parser->table, country_code);
+                               /* Ensure we have a country provider for this country code */
+                               if (!country_info) {
+                                       g_warning ("%s: adding providers for unknown country '%s'", __func__, 
country_code);
+                                       country_info = country_info_new (country_code, NULL);
+                                       g_hash_table_insert (parser->table, country_code, country_info);
+                               }
+                               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)) {
+                               gchar *mccmnc;
+
+                               if (!parser->current_provider->mcc_mnc)
+                                       parser->current_provider->mcc_mnc = g_ptr_array_new_full (2, g_free);
+
+                               mccmnc = g_strdup_printf ("%s%s", mcc, mnc);
+                               g_ptr_array_add (parser->current_provider->mcc_mnc, mccmnc);
+                               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->apn = g_strstrip (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")) {
+                               guint32 tmp;
+
+                               errno = 0;
+                               tmp = (guint32) strtoul (attribute_values[i], NULL, 10);
+                               if (errno == 0 && tmp > 0) {
+                                       if (!parser->current_provider->cdma_sid)
+                                               parser->current_provider->cdma_sid = g_array_sized_new (TRUE, 
FALSE, sizeof (guint32), 2);
+                                       g_array_append_val (parser->current_provider->cdma_sid, 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")) {
+               NMACountryInfo *country_info;
+
+               country_info = g_hash_table_lookup (parser->table, parser->current_country);
+               g_assert (country_info);
+
+               /* Store providers for this country */
+               country_info->providers = parser->current_providers;
+
+               g_free (parser->current_country);
+               parser->current_country = NULL;
+               parser->current_providers = NULL;
+               g_free (parser->text_buffer);
+               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")) {
+               if (parser->current_provider->mcc_mnc)
+                       g_ptr_array_add (parser->current_provider->mcc_mnc, NULL);
+
+               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;
+               g_free (parser->text_buffer);
+               parser->text_buffer = NULL;
+               parser->state = PARSER_COUNTRY;
+       }
+}
+
+static void
+parser_gsm_end (MobileParser *parser,
+                const char *name)
+{
+       if (!strcmp (name, "gsm")) {
+               g_free (parser->text_buffer);
+               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")) {
+               if (!parser->current_method->dns)
+                       parser->current_method->dns = g_ptr_array_new_full (2, g_free);
+               g_ptr_array_add (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->family = NMA_MOBILE_FAMILY_3GPP;
+
+               if (!parser->current_method->name)
+                       parser->current_method->name = g_strdup (_("Default"));
+
+               if (parser->current_method->dns)
+                       g_ptr_array_add (parser->current_method->dns, NULL);
+
+               parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
+                                                                    parser->current_method);
+               parser->current_method = NULL;
+               g_free (parser->text_buffer);
+               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")) {
+               if (!parser->current_method->dns)
+                       parser->current_method->dns = g_ptr_array_new_full (2, g_free);
+               g_ptr_array_add (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->family = NMA_MOBILE_FAMILY_CDMA;
+
+               if (!parser->current_method->name)
+                       parser->current_method->name = g_strdup (parser->current_provider->name);
+
+               if (parser->current_method->dns)
+                       g_ptr_array_add (parser->current_method->dns, NULL);
+
+               parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
+                                                                    parser->current_method);
+               parser->current_method = NULL;
+               g_free (parser->text_buffer);
+               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 */
+};
+
+static gboolean
+read_service_providers (GHashTable *countries,
+                        const gchar *service_providers,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       GMarkupParseContext *ctx;
+       GIOChannel *channel;
+       MobileParser parser;
+       char buffer[4096];
+       GIOStatus status;
+       gsize len = 0;
+
+       memset (&parser, 0, sizeof (MobileParser));
+    parser.table = countries;
+
+       channel = g_io_channel_new_file (service_providers, "r", error);
+       if (!channel) {
+               g_prefix_error (error,
+                               "Could not read '%s': ",
+                               service_providers);
+               return FALSE;
+       }
+
+       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_prefix_error (error,
+                                               "Error while parsing XML at '%s': ",
+                                               service_providers);
+                       }
+                       break;
+               case G_IO_STATUS_EOF:
+                       break;
+               case G_IO_STATUS_ERROR:
+                       g_prefix_error (error,
+                                       "Error while reading '%s': ",
+                                       service_providers);
+                       break;
+               case G_IO_STATUS_AGAIN:
+                       /* FIXME: Try again a few times, but really, it never happens, right? */
+                       break;
+               }
+
+               if (g_cancellable_set_error_if_cancelled (cancellable, error))
+                       status = G_IO_STATUS_ERROR;
+       }
+
+       g_io_channel_unref (channel);
+       g_markup_parse_context_free (ctx);
+
+       if (parser.current_provider) {
+               g_warning ("pending current provider");
+               nma_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);
+
+       return (status == G_IO_STATUS_EOF);
+}
+
+static GHashTable *
+mobile_providers_parse_sync (const gchar *country_codes,
+                             const gchar *service_providers,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       GHashTable *countries;
+
+       /* Use default paths if none given */
+       if (!country_codes)
+               country_codes = ISO_3166_COUNTRY_CODES;
+       if (!service_providers)
+               service_providers = MOBILE_BROADBAND_PROVIDER_INFO;
+
+       countries = read_country_codes (country_codes,
+                                       cancellable,
+                                       error);
+       if (!countries)
+               return NULL;
+
+       if (!read_service_providers (countries,
+                                    service_providers,
+                                    cancellable,
+                                    error)) {
+               g_hash_table_unref (countries);
+               return NULL;
+       }
+
+       return countries;
+}
+
+/******************************************************************************/
+/* Dump to stdout contents */
+
+static void
+dump_generic (NMAMobileAccessMethod *method)
+{
+       g_print ("                username: %s\n", method->username ? method->username : "");
+       g_print ("                password: %s\n", method->password ? method->password : "");
+
+       if (method->dns) {
+               guint i;
+               const gchar **dns;
+               GString *dns_str;
+
+               dns = nma_mobile_access_method_get_dns (method);
+               dns_str = g_string_new (NULL);
+               for (i = 0; dns[i]; i++)
+                       g_string_append_printf (dns_str, "%s%s", i == 0 ? "" : ", ", dns[i]);
+               g_print ("                dns     : %s\n", dns_str->str);
+               g_string_free (dns_str, TRUE);
+       }
+
+       g_print ("                gateway : %s\n", method->gateway ? method->gateway : "");
+}
+
+static void
+dump_cdma (NMAMobileAccessMethod *method)
+{
+       g_print ("         CDMA: %s\n", method->name);
+
+       dump_generic (method);
+}
+
+static void
+dump_3gpp (NMAMobileAccessMethod *method)
+{
+       g_print ("         APN: %s (%s)\n", method->name, method->apn);
+
+       dump_generic (method);
+}
+
+static void
+dump_country (gpointer key, gpointer value, gpointer user_data)
+{
+       GSList *miter, *citer;
+       NMACountryInfo *country_info = value;
+
+       g_print ("Country: %s (%s)\n",
+                country_info->country_code,
+                country_info->country_name);
+
+       for (citer = country_info->providers; citer; citer = g_slist_next (citer)) {
+               NMAMobileProvider *provider = citer->data;
+               const gchar **mcc_mnc;
+               const guint *sid;
+               guint n;
+
+               g_print ("        Provider: %s (%s)\n", provider->name, (const char *) key);
+
+               mcc_mnc = nma_mobile_provider_get_3gpp_mcc_mnc (provider);
+               if (mcc_mnc) {
+                       for (n = 0; mcc_mnc[n]; n++)
+                               g_print ("                MCC/MNC: %s\n", mcc_mnc[n]);
+               }
+
+               sid = nma_mobile_provider_get_cdma_sid (provider);
+               if (sid) {
+                       for (n = 0; sid[n]; n++)
+                               g_print ("                SID: %u\n", sid[n]);
+               }
+
+               for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
+                       NMAMobileAccessMethod *method = miter->data;
+
+                       switch (method->family) {
+                       case NMA_MOBILE_FAMILY_CDMA:
+                               dump_cdma (method);
+                               break;
+                       case NMA_MOBILE_FAMILY_3GPP:
+                               dump_3gpp (method);
+                               break;
+                       default:
+                               break;
+                       }
+                       g_print ("\n");
+               }
+       }
+}
+
+/******************************************************************************/
+/* Mobile providers database type */
+
+static void initable_iface_init       (GInitableIface      *iface);
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (NMAMobileProvidersDatabase, nma_mobile_providers_database, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))
+
+enum {
+       PROP_0,
+       PROP_COUNTRY_CODES_PATH,
+       PROP_SERVICE_PROVIDERS_PATH,
+       PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _NMAMobileProvidersDatabasePrivate {
+       /* Paths to input files */
+       gchar *country_codes_path;
+       gchar *service_providers_path;
+
+       /* The HT with country code as key and NMACountryInfo as value. */
+       GHashTable *countries;
+};
+
+/**********************************/
+
+/**
+ * nma_mobile_providers_database_get_countries:
+ * @self: a #NMAMobileProvidersDatabase.
+ *
+ * Returns: (element-type utf8 NMACountryInfo) (transfer none): a
+ *      hash table where keys are country names #gchar and values are #NMACountryInfos.
+ */
+GHashTable *
+nma_mobile_providers_database_get_countries (NMAMobileProvidersDatabase *self)
+{
+       g_return_val_if_fail (NMA_IS_MOBILE_PROVIDERS_DATABASE (self), NULL);
+
+       /* Warn if the object hasn't been initialized */
+       g_return_val_if_fail (self->priv->countries != NULL, NULL);
+
+       return self->priv->countries;
+}
+
+/**
+ * nma_mobile_providers_database_dump:
+ * @self: a #NMAMobileProvidersDatabase.
+ *
+ */
+void
+nma_mobile_providers_database_dump (NMAMobileProvidersDatabase *self)
+{
+       g_return_if_fail (NMA_IS_MOBILE_PROVIDERS_DATABASE (self));
+
+       /* Warn if the object hasn't been initialized */
+       g_return_if_fail (self->priv->countries != NULL);
+
+       g_hash_table_foreach (self->priv->countries, dump_country, NULL);
+}
+
+/**
+ * nma_mobile_providers_database_lookup_country:
+ * @self: a #NMAMobileProvidersDatabase.
+ * @country_code: the country code string to look for.
+ *
+ * Returns: (transfer none): a #NMACountryInfo or %NULL if not found.
+ */
+NMACountryInfo *
+nma_mobile_providers_database_lookup_country (NMAMobileProvidersDatabase *self,
+                                              const gchar *country_code)
+{
+       g_return_val_if_fail (NMA_IS_MOBILE_PROVIDERS_DATABASE (self), NULL);
+
+       /* Warn if the object hasn't been initialized */
+       g_return_val_if_fail (self->priv->countries != NULL, NULL);
+
+       return (NMACountryInfo *) g_hash_table_lookup (self->priv->countries, country_code);
+}
+
+/**
+ * nma_mobile_providers_database_lookup_3gpp_mcc_mnc:
+ * @self: a #NMAMobileProvidersDatabase.
+ * @mccmnc: the MCC/MNC string to look for.
+ *
+ * Returns: (transfer none): a #NMAMobileProvider or %NULL if not found.
+ */
+NMAMobileProvider *
+nma_mobile_providers_database_lookup_3gpp_mcc_mnc (NMAMobileProvidersDatabase *self,
+                                                   const gchar *mccmnc)
+{
+       GHashTableIter iter;
+       gpointer value;
+       GSList *piter;
+       NMAMobileProvider *provider_match_2mnc = NULL;
+       guint mccmnc_len;
+
+       g_return_val_if_fail (NMA_IS_MOBILE_PROVIDERS_DATABASE (self), NULL);
+       g_return_val_if_fail (mccmnc != NULL, NULL);
+       /* Warn if the object hasn't been initialized */
+       g_return_val_if_fail (self->priv->countries != NULL, NULL);
+
+       /* Expect only 5 or 6 digit MCCMNC strings */
+       mccmnc_len = strlen (mccmnc);
+       if (mccmnc_len != 5 && mccmnc_len != 6)
+               return NULL;
+
+       g_hash_table_iter_init (&iter, self->priv->countries);
+       /* Search through each country */
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               NMACountryInfo *country_info = value;
+
+               /* Search through each country's providers */
+               for (piter = nma_country_info_get_providers (country_info);
+                    piter;
+                    piter = g_slist_next (piter)) {
+                       NMAMobileProvider *provider = piter->data;
+                       const gchar **mccmnc_list;
+                       guint i;
+
+                       /* Search through MCC/MNC list */
+                       mccmnc_list = nma_mobile_provider_get_3gpp_mcc_mnc (provider);
+                       if (!mccmnc_list)
+                               continue;
+
+                       for (i = 0; mccmnc_list[i]; i++) {
+                               const gchar *mccmnc_iter;
+                               guint mccmnc_iter_len;
+
+                               mccmnc_iter = mccmnc_list[i];
+                               mccmnc_iter_len = strlen (mccmnc_iter);
+
+                               /* Match both 2-digit and 3-digit MNC; prefer a
+                                * 3-digit match if found, otherwise a 2-digit one.
+                                */
+
+                               if (strncmp (mccmnc_iter, mccmnc, 3))
+                                       /* MCC was wrong */
+                                       continue;
+
+                               /* Now we have the following match cases, examples given:
+                                *  a) input: 123/456 --> iter: 123/456 (3-digit match)
+                                *  b) input: 123/45  --> iter: 123/045 (3-digit match)
+                                *  c) input: 123/045 --> iter: 123/45  (2-digit match)
+                                *  d) input: 123/45  --> iter: 123/45  (2-digit match)
+                                */
+
+                               if (mccmnc_iter_len == 6) {
+                                       /* Covers cases a) and b) */
+                                       if (   (mccmnc_len == 6 && !strncmp (mccmnc + 3, mccmnc_iter + 3, 3))
+                                           || (mccmnc_len == 5 && mccmnc_iter[3] == '0' && !strncmp (mccmnc 
+ 3, mccmnc_iter + 4, 2)))
+                                               /* 3-digit MNC match! */
+                                               return provider;
+
+                                       /* MNC was wrong */
+                                       continue;
+                               }
+
+                               if (   !provider_match_2mnc
+                                   && mccmnc_iter_len == 5) {
+                                       if (   (mccmnc_len == 5 && !strncmp (mccmnc + 3, mccmnc_iter + 3, 2))
+                                           || (mccmnc_len == 6 && mccmnc[3] == '0' && !strncmp (mccmnc + 4, 
mccmnc_iter + 3, 2))) {
+                                               /* Store the 2-digit MNC match, but keep looking,
+                                                * we may have a 3-digit MNC match */
+                                               provider_match_2mnc = provider;
+                                               continue;
+                                       }
+
+                                       /* MNC was wrong */
+                                       continue;
+                               }
+                       }
+               }
+       }
+
+       return provider_match_2mnc;
+}
+
+/**
+ * nma_mobile_providers_database_lookup_cdma_sid:
+ * @self: a #NMAMobileProvidersDatabase.
+ * @sid: the SID to look for.
+ *
+ * Returns: (transfer none): a #NMAMobileProvider, or %NULL if not found.
+ */
+NMAMobileProvider *
+nma_mobile_providers_database_lookup_cdma_sid (NMAMobileProvidersDatabase *self,
+                                               guint32 sid)
+{
+       GHashTableIter iter;
+       gpointer value;
+       GSList *piter;
+
+       g_return_val_if_fail (NMA_IS_MOBILE_PROVIDERS_DATABASE (self), NULL);
+       g_return_val_if_fail (sid > 0, NULL);
+       /* Warn if the object hasn't been initialized */
+       g_return_val_if_fail (self->priv->countries != NULL, NULL);
+
+       g_hash_table_iter_init (&iter, self->priv->countries);
+       /* Search through each country */
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               NMACountryInfo *country_info = value;
+
+               /* Search through each country's providers */
+               for (piter = nma_country_info_get_providers (country_info);
+                    piter;
+                    piter = g_slist_next (piter)) {
+                       NMAMobileProvider *provider = piter->data;
+                       const guint32 *sid_list;
+                       guint i;
+
+                       /* Search through CDMA SID list */
+                       sid_list = nma_mobile_provider_get_cdma_sid (provider);
+                       if (!sid_list)
+                               continue;
+
+                       for (i = 0; sid_list[i]; i++) {
+                               if (sid == sid_list[i])
+                                       return provider;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+/**********************************/
+
+static gboolean
+initable_init_sync (GInitable     *initable,
+                    GCancellable  *cancellable,
+                    GError       **error)
+{
+       NMAMobileProvidersDatabase *self = NMA_MOBILE_PROVIDERS_DATABASE (initable);
+
+       /* Parse the files */
+       self->priv->countries = mobile_providers_parse_sync (self->priv->country_codes_path,
+                                                            self->priv->service_providers_path,
+                                                            cancellable,
+                                                            error);
+       if (!self->priv->countries)
+               return FALSE;
+
+       /* All good */
+       return TRUE;
+}
+
+/**********************************/
+
+/**
+ * nma_mobile_providers_database_new:
+ * @country_codes: (allow-none): Path to the country codes file.
+ * @service_providers: (allow-none): Path to the service providers file.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: User data to pass to @callback.
+ *
+ */
+void
+nma_mobile_providers_database_new (const gchar *country_codes,
+                                   const gchar *service_providers,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+       g_async_initable_new_async (NMA_TYPE_MOBILE_PROVIDERS_DATABASE,
+                                   G_PRIORITY_DEFAULT,
+                                   cancellable,
+                                   callback,
+                                   user_data,
+                                   "country-codes",     country_codes,
+                                   "service-providers", service_providers,
+                                   NULL);
+}
+
+/**
+ * nma_mobile_providers_database_new_finish:
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to 
nma_mobile_providers_database_new().
+ * @error: Return location for error or %NULL.
+ *
+ * Returns: (transfer full) (type NMAMobileProvidersDatabase): The constructed object or %NULL if @error is 
set.
+ */
+NMAMobileProvidersDatabase *
+nma_mobile_providers_database_new_finish (GAsyncResult *res,
+                                          GError **error)
+{
+       GObject *initable;
+       GObject *out;
+
+       initable = g_async_result_get_source_object (res);
+       out = g_async_initable_new_finish (G_ASYNC_INITABLE (initable), res, error);
+       g_object_unref (initable);
+
+       return out ? NMA_MOBILE_PROVIDERS_DATABASE (out) : NULL;
+}
+
+/**
+ * nma_mobile_providers_database_new_sync:
+ * @country_codes: (allow-none): Path to the country codes file.
+ * @service_providers: (allow-none): Path to the service providers file.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Returns: (transfer full) (type NMAMobileProvidersDatabase): The constructed object or %NULL if @error is 
set.
+ */
+NMAMobileProvidersDatabase *
+nma_mobile_providers_database_new_sync (const gchar *country_codes,
+                                        const gchar *service_providers,
+                                        GCancellable *cancellable,
+                                        GError **error)
+{
+       GObject *out;
+
+       out = g_initable_new (NMA_TYPE_MOBILE_PROVIDERS_DATABASE,
+                             cancellable,
+                             error,
+                             "country-codes",     country_codes,
+                             "service-providers", service_providers,
+                             NULL);
+
+       return out ? NMA_MOBILE_PROVIDERS_DATABASE (out) : NULL;
+}
+
+/**********************************/
+
+static void
+set_property (GObject *object,
+              guint prop_id,
+              const GValue *value,
+              GParamSpec *pspec)
+{
+       NMAMobileProvidersDatabase *self = NMA_MOBILE_PROVIDERS_DATABASE (object);
+
+       switch (prop_id) {
+       case PROP_COUNTRY_CODES_PATH:
+               self->priv->country_codes_path = g_value_dup_string (value);
+               break;
+       case PROP_SERVICE_PROVIDERS_PATH:
+               self->priv->service_providers_path = g_value_dup_string (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+get_property (GObject *object,
+              guint prop_id,
+              GValue *value,
+              GParamSpec *pspec)
+{
+       NMAMobileProvidersDatabase *self = NMA_MOBILE_PROVIDERS_DATABASE (object);
+
+       switch (prop_id) {
+       case PROP_COUNTRY_CODES_PATH:
+               g_value_set_string (value, self->priv->country_codes_path);
+               break;
+       case PROP_SERVICE_PROVIDERS_PATH:
+               g_value_set_string (value, self->priv->service_providers_path);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+nma_mobile_providers_database_init (NMAMobileProvidersDatabase *self)
+{
+       /* Setup private data */
+       self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                                 NMA_TYPE_MOBILE_PROVIDERS_DATABASE,
+                                                 NMAMobileProvidersDatabasePrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+       NMAMobileProvidersDatabase *self = NMA_MOBILE_PROVIDERS_DATABASE (object);
+
+       g_free (self->priv->country_codes_path);
+       g_free (self->priv->service_providers_path);
+
+       if (self->priv->countries)
+               g_hash_table_unref (self->priv->countries);
+
+       G_OBJECT_CLASS (nma_mobile_providers_database_parent_class)->finalize (object);
+}
+
+static void
+initable_iface_init (GInitableIface *iface)
+{
+       iface->init = initable_init_sync;
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+       /* Just use defaults (run sync init() in a thread) */
+}
+
+static void
+nma_mobile_providers_database_class_init (NMAMobileProvidersDatabaseClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (object_class, sizeof (NMAMobileProvidersDatabasePrivate));
+
+    /* Virtual methods */
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->finalize = finalize;
+
+    properties[PROP_COUNTRY_CODES_PATH] =
+           g_param_spec_string ("country-codes",
+                             "Country Codes",
+                             "Path to the country codes file",
+                                NULL,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+    g_object_class_install_property (object_class, PROP_COUNTRY_CODES_PATH, 
properties[PROP_COUNTRY_CODES_PATH]);
+
+    properties[PROP_SERVICE_PROVIDERS_PATH] =
+           g_param_spec_string ("service-providers",
+                                "Service Providers",
+                                "Path to the service providers file",
+                                NULL,
+                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+    g_object_class_install_property (object_class, PROP_SERVICE_PROVIDERS_PATH, 
properties[PROP_SERVICE_PROVIDERS_PATH]);
+}
+
+/******************************************************************************/
+/* Utils */
+
+/**
+ * nma_mobile_providers_split_3gpp_mcc_mnc:
+ * @mccmnc: input MCCMNC string.
+ * @mcc: (out) (transfer full): the MCC.
+ * @mnc: (out) (transfer full): the MNC.
+ *
+ * Splits the input MCCMNC string into separate MCC and MNC strings.
+ *
+ * Returns: %TRUE if correctly split and @mcc and @mnc are set; %FALSE otherwise.
+ */
+gboolean
+nma_mobile_providers_split_3gpp_mcc_mnc (const gchar *mccmnc,
+                                         gchar **mcc,
+                                         gchar **mnc)
+{
+       gint len;
+
+       g_return_val_if_fail (mccmnc != NULL, FALSE);
+       g_return_val_if_fail (mcc != NULL, FALSE);
+       g_return_val_if_fail (mnc != NULL, FALSE);
+
+       len = strlen (mccmnc);
+       if (len != 5 && len != 6)
+               return FALSE;
+
+       /* MCCMNC is all digits */
+       while (len > 0) {
+               if (!g_ascii_isdigit (mccmnc[--len]))
+                       return FALSE;
+       }
+
+       *mcc = g_strndup (mccmnc, 3);
+       *mnc = g_strdup (mccmnc + 3);
+       return TRUE;
+}
diff --git a/src/libnma/nma-mobile-providers.h b/src/libnma/nma-mobile-providers.h
new file mode 100644
index 0000000..4442d7f
--- /dev/null
+++ b/src/libnma/nma-mobile-providers.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Lanedo GmbH.
+ */
+
+/* WARNING: this file is private API between nm-applet and various GNOME
+ * bits; it may change without notice and is not guaranteed to be stable.
+ */
+
+#ifndef NM_MOBILE_PROVIDERS_H
+#define NM_MOBILE_PROVIDERS_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+/******************************************************************************/
+/* Access method type */
+
+typedef enum {
+    NMA_MOBILE_FAMILY_UNKNOWN = 0,
+    NMA_MOBILE_FAMILY_3GPP,
+    NMA_MOBILE_FAMILY_CDMA
+} NMAMobileFamily;
+
+#define NMA_TYPE_MOBILE_ACCESS_METHOD (nma_mobile_access_method_get_type ())
+
+typedef struct _NMAMobileAccessMethod NMAMobileAccessMethod;
+
+GType                   nma_mobile_access_method_get_type     (void);
+NMAMobileAccessMethod  *nma_mobile_access_method_ref          (NMAMobileAccessMethod *method);
+void                    nma_mobile_access_method_unref        (NMAMobileAccessMethod *method);
+const gchar            *nma_mobile_access_method_get_name     (NMAMobileAccessMethod *method);
+const gchar            *nma_mobile_access_method_get_username (NMAMobileAccessMethod *method);
+const gchar            *nma_mobile_access_method_get_password (NMAMobileAccessMethod *method);
+const gchar            *nma_mobile_access_method_get_gateway  (NMAMobileAccessMethod *method);
+const gchar           **nma_mobile_access_method_get_dns      (NMAMobileAccessMethod *method);
+const gchar            *nma_mobile_access_method_get_3gpp_apn (NMAMobileAccessMethod *method);
+NMAMobileFamily         nma_mobile_access_method_get_family   (NMAMobileAccessMethod *method);
+
+/******************************************************************************/
+/* Mobile provider type */
+
+#define NMA_TYPE_MOBILE_PROVIDER (nma_mobile_provider_get_type ())
+
+typedef struct _NMAMobileProvider NMAMobileProvider;
+
+GType               nma_mobile_provider_get_type         (void);
+NMAMobileProvider  *nma_mobile_provider_ref              (NMAMobileProvider *provider);
+void                nma_mobile_provider_unref            (NMAMobileProvider *provider);
+const gchar        *nma_mobile_provider_get_name         (NMAMobileProvider *provider);
+GSList             *nma_mobile_provider_get_methods      (NMAMobileProvider *provider);
+const gchar       **nma_mobile_provider_get_3gpp_mcc_mnc (NMAMobileProvider *provider);
+const guint32      *nma_mobile_provider_get_cdma_sid     (NMAMobileProvider *provider);
+
+/******************************************************************************/
+/* Country Info type */
+
+#define NMA_TYPE_COUNTRY_INFO (nma_country_info_get_type ())
+
+typedef struct _NMACountryInfo NMACountryInfo;
+
+GType           nma_country_info_get_type         (void);
+NMACountryInfo *nma_country_info_ref              (NMACountryInfo *country_info);
+void            nma_country_info_unref            (NMACountryInfo *country_info);
+const gchar    *nma_country_info_get_country_code (NMACountryInfo *country_info);
+const gchar    *nma_country_info_get_country_name (NMACountryInfo *country_info);
+GSList         *nma_country_info_get_providers    (NMACountryInfo *country_info);
+
+/******************************************************************************/
+/* Mobile providers database type */
+
+#define NMA_TYPE_MOBILE_PROVIDERS_DATABASE            (nma_mobile_providers_database_get_type ())
+#define NMA_MOBILE_PROVIDERS_DATABASE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
NMA_TYPE_MOBILE_PROVIDERS_DATABASE, NMAMobileProvidersDatabase))
+#define NMA_MOBILE_PROVIDERS_DATABASE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
NMA_TYPE_MOBILE_PROVIDERS_DATABASE, NMAMobileProvidersDatabaseClass))
+#define NMA_IS_MOBILE_PROVIDERS_DATABASE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
NMA_TYPE_MOBILE_PROVIDERS_DATABASE))
+#define NMA_IS_MOBILE_PROVIDERS_DATABASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
NMA_TYPE_MOBILE_PROVIDERS_DATABASE))
+#define NMA_MOBILE_PROVIDERS_DATABASE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
NMA_TYPE_MOBILE_PROVIDERS_DATABASE, NMAMobileProvidersDatabaseClass))
+
+typedef struct _NMAMobileProvidersDatabase NMAMobileProvidersDatabase;
+typedef struct _NMAMobileProvidersDatabaseClass NMAMobileProvidersDatabaseClass;
+typedef struct _NMAMobileProvidersDatabasePrivate NMAMobileProvidersDatabasePrivate;
+
+struct _NMAMobileProvidersDatabase {
+       GObject parent;
+       NMAMobileProvidersDatabasePrivate *priv;
+};
+
+struct _NMAMobileProvidersDatabaseClass {
+       GObjectClass parent;
+};
+
+GType nma_mobile_providers_database_get_type (void);
+
+void                        nma_mobile_providers_database_new        (const gchar *country_codes,
+                                                                      const gchar *service_providers,
+                                                                      GCancellable *cancellable,
+                                                                      GAsyncReadyCallback callback,
+                                                                      gpointer user_data);
+NMAMobileProvidersDatabase *nma_mobile_providers_database_new_finish (GAsyncResult *res,
+                                                                      GError **error);
+NMAMobileProvidersDatabase *nma_mobile_providers_database_new_sync   (const gchar *country_codes,
+                                                                      const gchar *service_providers,
+                                                                      GCancellable *cancellable,
+                                                                      GError **error);
+
+GHashTable        *nma_mobile_providers_database_get_countries       (NMAMobileProvidersDatabase *self);
+
+void               nma_mobile_providers_database_dump                (NMAMobileProvidersDatabase *self);
+
+NMACountryInfo    *nma_mobile_providers_database_lookup_country      (NMAMobileProvidersDatabase *self,
+                                                                      const gchar *country_code);
+NMAMobileProvider *nma_mobile_providers_database_lookup_3gpp_mcc_mnc (NMAMobileProvidersDatabase *self,
+                                                                      const gchar *mccmnc);
+NMAMobileProvider *nma_mobile_providers_database_lookup_cdma_sid     (NMAMobileProvidersDatabase *self,
+                                                                      guint32 sid);
+
+/******************************************************************************/
+/* Utils */
+
+gboolean nma_mobile_providers_split_3gpp_mcc_mnc (const gchar *mccmnc,
+                                                  gchar **mcc,
+                                                  gchar **mnc);
+
+#endif /* NM_MOBILE_PROVIDERS_H */
diff --git a/src/libnma/nma-mobile-wizard.c b/src/libnma/nma-mobile-wizard.c
new file mode 100644
index 0000000..d1daa7c
--- /dev/null
+++ b/src/libnma/nma-mobile-wizard.c
@@ -0,0 +1,1675 @@
+/* -*- 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 - 2012 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+
+#include <NetworkManager.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-client.h>
+#include <nm-device-modem.h>
+
+#include "nma-mobile-wizard.h"
+#include "nma-mobile-providers.h"
+#include "utils.h"
+
+#define DEVICE_TAG "device"
+#define TYPE_TAG "setting-type"
+
+static NMACountryInfo *get_selected_country (NMAMobileWizard *self);
+static NMAMobileProvider *get_selected_provider (NMAMobileWizard *self);
+static NMAMobileFamily get_provider_unlisted_type (NMAMobileWizard *self);
+static NMAMobileAccessMethod *get_selected_method (NMAMobileWizard *self, gboolean *manual);
+
+struct NMAMobileWizard {
+       GtkWidget *assistant;
+       NMAMobileWizardCallback callback;
+       gpointer user_data;
+       NMAMobileProvidersDatabase *mobile_providers_database;
+       NMAMobileFamily family;
+       gboolean initial_family;
+       gboolean will_connect_after;
+
+       /* Intro page */
+       GtkWidget *dev_combo;
+       GtkTreeStore *dev_store;
+       char *dev_desc;
+       NMClient *client;
+
+       /* Country page */
+       guint32 country_idx;
+       NMACountryInfo *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 void
+assistant_closed (GtkButton *button, gpointer user_data)
+{
+       NMAMobileWizard *self = user_data;
+       NMAMobileProvider *provider;
+       NMAMobileAccessMethod *method;
+       NMAMobileWizardAccessMethod *wiz_method;
+       NMAMobileFamily family = self->family;
+
+       wiz_method = g_malloc0 (sizeof (NMAMobileWizardAccessMethod));
+
+       provider = get_selected_provider (self);
+       if (!provider) {
+               if (family == NMA_MOBILE_FAMILY_UNKNOWN)
+                       family = get_provider_unlisted_type (self);
+
+               wiz_method->provider_name = g_strdup (gtk_entry_get_text (GTK_ENTRY 
(self->provider_unlisted_entry)));
+               if (family == NMA_MOBILE_FAMILY_3GPP)
+                       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 (nma_mobile_provider_get_name (provider));
+               method = get_selected_method (self, &manual);
+               if (method) {
+                       family = nma_mobile_access_method_get_family (method);
+                       wiz_method->plan_name = g_strdup (nma_mobile_access_method_get_name (method));
+                       wiz_method->username = g_strdup (nma_mobile_access_method_get_username (method));
+                       wiz_method->password = g_strdup (nma_mobile_access_method_get_password (method));
+                       if (family == NMA_MOBILE_FAMILY_3GPP)
+                               wiz_method->gsm_apn = g_strdup (nma_mobile_access_method_get_3gpp_apn 
(method));
+               } else {
+                       if (self->provider_only_cdma) {
+                               GSList *methods;
+
+                               family = NMA_MOBILE_FAMILY_CDMA;
+
+                               methods = nma_mobile_provider_get_methods (provider);
+                               /* Take username and password from the first (only) method for CDMA only 
provider */
+                               if (methods) {
+                                       method = methods->data;
+                                       wiz_method->username = g_strdup 
(nma_mobile_access_method_get_username (method));
+                                       wiz_method->password = g_strdup 
(nma_mobile_access_method_get_password (method));
+                               }
+                       } else {
+                               family = NMA_MOBILE_FAMILY_3GPP;
+                               wiz_method->gsm_apn = g_strdup (gtk_entry_get_text (GTK_ENTRY 
(self->plan_unlisted_entry)));
+                       }
+               }
+       }
+
+       switch (family) {
+       case NMA_MOBILE_FAMILY_3GPP:
+               wiz_method->devtype = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
+               break;
+       case NMA_MOBILE_FAMILY_CDMA:
+               wiz_method->devtype = NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       (*(self->callback)) (self, FALSE, wiz_method, self->user_data);
+
+       if (provider)
+               nma_mobile_provider_unref (provider);
+       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)
+{
+       NMAMobileWizard *self = user_data;
+
+       (*(self->callback)) (self, TRUE, NULL, self->user_data);
+}
+
+/**********************************************************/
+/* Confirm page */
+/**********************************************************/
+
+static void
+confirm_setup (NMAMobileWizard *self)
+{
+       GtkWidget *vbox, *label, *alignment, *pbox;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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->will_connect_after) {
+               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_label_set_max_width_chars (GTK_LABEL (label), 60);
+               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 (NMAMobileWizard *self)
+{
+       NMAMobileProvider *provider = NULL;
+       NMAMobileAccessMethod *method = NULL;
+       NMACountryInfo *country_info;
+       gboolean manual = FALSE;
+       GString *str;
+
+       country_info = 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, nma_mobile_provider_get_name (provider));
+               nma_mobile_provider_unref (provider);
+       } 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_info) {
+               g_string_append_printf (str, ", %s", nma_country_info_get_country_name (country_info));
+               nma_country_info_unref (country_info);
+       }
+       gtk_label_set_text (GTK_LABEL (self->confirm_provider), str->str);
+       g_string_free (str, TRUE);
+
+       if (self->dev_desc)
+               gtk_label_set_text (GTK_LABEL (self->confirm_device), self->dev_desc);
+       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 {
+               const char *apn = NULL;
+
+               /* 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), nma_mobile_access_method_get_name 
(method));
+                       apn = nma_mobile_access_method_get_3gpp_apn (method);
+               } else {
+                       gtk_label_set_text (GTK_LABEL (self->confirm_plan), _("Unlisted"));
+                       apn = gtk_entry_get_text (GTK_ENTRY (self->plan_unlisted_entry));
+               }
+
+               str = g_string_new (NULL);
+               g_string_append_printf (str, "<span color=\"#999999\">APN: %s</span>", 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 NMAMobileAccessMethod *
+get_selected_method (NMAMobileWizard *self, gboolean *manual)
+{
+       GtkTreeModel *model;
+       NMAMobileAccessMethod *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)
+                       nma_mobile_access_method_unref (method);
+               method = NULL;
+       }
+
+       return method;
+}
+
+static void
+plan_update_complete (NMAMobileWizard *self)
+{
+       GtkAssistant *assistant = GTK_ASSISTANT (self->assistant);
+       gboolean is_manual = FALSE;
+       NMAMobileAccessMethod *method;
+
+       method = get_selected_method (self, &is_manual);
+       if (method) {
+               gtk_assistant_set_page_complete (assistant, self->plan_page, TRUE);
+               nma_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 (NMAMobileWizard *self)
+{
+       NMAMobileAccessMethod *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), 
nma_mobile_access_method_get_3gpp_apn (method));
+               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)
+               nma_mobile_access_method_unref (method);
+
+       plan_update_complete (self);
+}
+
+static gboolean
+plan_row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+       NMAMobileAccessMethod *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)
+               nma_mobile_access_method_unref (method);
+       return draw_separator;
+}
+
+static void
+apn_filter_cb (GtkEditable *editable,
+               gchar *text,
+               gint length,
+               gint *position,
+               gpointer user_data)
+{
+       utils_filter_editable_on_insert_text (editable,
+                                             text, length, position, user_data,
+                                             utils_char_is_ascii_apn,
+                                             apn_filter_cb);
+}
+
+static void
+plan_setup (NMAMobileWizard *self)
+{
+       GtkWidget *vbox, *label, *alignment, *hbox, *image;
+       GtkCellRenderer *renderer;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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, NMA_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), 64);
+       g_signal_connect (self->plan_unlisted_entry, "insert-text", G_CALLBACK (apn_filter_cb), self);
+       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_box_new (GTK_ORIENTATION_HORIZONTAL, 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_label_set_max_width_chars (GTK_LABEL (label), 60);
+       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 (NMAMobileWizard *self)
+{
+       NMAMobileProvider *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 = nma_mobile_provider_get_methods (provider); iter; iter = g_slist_next (iter)) {
+                       NMAMobileAccessMethod *method = iter->data;
+
+                       if (   (self->family != NMA_MOBILE_FAMILY_UNKNOWN)
+                           && (nma_mobile_access_method_get_family (method) != self->family))
+                               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,
+                                           nma_mobile_access_method_get_name (method),
+                                           PLAN_COL_METHOD,
+                                           method,
+                                           -1);
+                       count++;
+               }
+               nma_mobile_provider_unref (provider);
+
+               /* 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 NMAMobileProvider *
+get_selected_provider (NMAMobileWizard *self)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model = NULL;
+       GtkTreeIter iter;
+       NMAMobileProvider *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 (NMAMobileWizard *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) {
+               NMAMobileProvider *provider;
+
+               provider = get_selected_provider (self);
+               gtk_assistant_set_page_complete (assistant, self->providers_page, !!provider);
+               if (provider)
+                       nma_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)
+{
+       NMAMobileWizard *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)
+{
+       NMAMobileWizard *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)
+{
+       NMAMobileWizard *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 NMAMobileFamily
+get_provider_unlisted_type (NMAMobileWizard *self)
+{
+       switch (gtk_combo_box_get_active (GTK_COMBO_BOX (self->provider_unlisted_type_combo))) {
+       case 0:
+               return NMA_MOBILE_FAMILY_3GPP;
+       case 1:
+               return NMA_MOBILE_FAMILY_CDMA;
+       default:
+               return NMA_MOBILE_FAMILY_UNKNOWN;
+       }
+}
+
+static void
+providers_setup (NMAMobileWizard *self)
+{
+       GtkWidget *vbox, *scroll, *alignment, *unlisted_grid, *label;
+       GtkCellRenderer *renderer;
+       GtkTreeViewColumn *column;
+       GtkTreeSelection *selection;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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, NMA_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_grid = gtk_grid_new ();
+       gtk_grid_set_row_spacing (GTK_GRID (unlisted_grid), 12);
+       gtk_grid_set_column_spacing (GTK_GRID (unlisted_grid), 12);
+       gtk_container_add (GTK_CONTAINER (alignment), unlisted_grid);
+
+       label = gtk_label_new (_("Provider:"));
+       gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
+       gtk_grid_attach (GTK_GRID (unlisted_grid), label, 0, 0, 1, 1);
+
+       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_widget_set_hexpand (alignment, TRUE);
+       gtk_container_add (GTK_CONTAINER (alignment), self->provider_unlisted_entry);
+       gtk_grid_attach (GTK_GRID (unlisted_grid), alignment,
+                        1, 0, 1, 1);
+
+       self->provider_unlisted_type_combo = gtk_combo_box_text_new ();
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->provider_unlisted_type_combo);
+       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (self->provider_unlisted_type_combo),
+                                  _("My provider uses GSM technology (GPRS, EDGE, UMTS, HSPA)"));
+       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (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_grid_attach (GTK_GRID (unlisted_grid), self->provider_unlisted_type_combo,
+                        1, 1, 1, 1);
+
+       /* Only show the CDMA/GSM combo if we don't know the device type */
+       if (self->family != NMA_MOBILE_FAMILY_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 (NMAMobileWizard *self)
+{
+       GtkTreeSelection *selection;
+       NMACountryInfo *country_info;
+       GSList *piter;
+
+       gtk_tree_store_clear (self->providers_store);
+
+       country_info = get_selected_country (self);
+       if (!country_info) {
+               /* Unlisted country */
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->provider_unlisted_radio), TRUE);
+               gtk_widget_set_sensitive (GTK_WIDGET (self->providers_view_radio), FALSE);
+               goto done;
+       }
+       gtk_widget_set_sensitive (GTK_WIDGET (self->providers_view_radio), TRUE);
+
+       for (piter = nma_country_info_get_providers (country_info);
+            piter;
+            piter = g_slist_next (piter)) {
+               NMAMobileProvider *provider = piter->data;
+               GtkTreeIter provider_iter;
+
+               /* Ignore providers that don't match the current device type */
+               if (self->family != NMA_MOBILE_FAMILY_UNKNOWN) {
+                       GSList *miter;
+                       guint32 count = 0;
+
+                       for (miter = nma_mobile_provider_get_methods (provider); miter; miter = g_slist_next 
(miter)) {
+                               NMAMobileAccessMethod *method = miter->data;
+
+                               if (self->family == nma_mobile_access_method_get_family (method))
+                                       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,
+                                   nma_mobile_provider_get_name (provider),
+                                   PROVIDER_COL_PROVIDER,
+                                   provider,
+                                   -1);
+       }
+
+       nma_country_info_unref (country_info);
+
+       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);
+                       }
+               }
+       }
+
+done:
+       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->family != NMA_MOBILE_FAMILY_UNKNOWN)
+               gtk_widget_hide (self->provider_unlisted_type_combo);
+       else
+               gtk_widget_show (self->provider_unlisted_type_combo);
+}
+
+/**********************************************************/
+/* Country page */
+/**********************************************************/
+
+#define COUNTRIES_COL_NAME 0
+#define COUNTRIES_COL_INFO 1
+
+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)
+{
+       NMAMobileWizard *self = user_data;
+       NMACountryInfo *country_info = value;
+       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,
+                           COUNTRIES_COL_NAME,
+                           nma_country_info_get_country_name (country_info),
+                           COUNTRIES_COL_INFO,
+                           country_info,
+                           -1);
+
+       /* If this country is the same country as the user's current locale,
+        * select it by default.
+        */
+       if (self->country != country_info)
+               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);
+}
+
+NMACountryInfo *
+get_selected_country (NMAMobileWizard *self)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model = NULL;
+       GtkTreeIter iter;
+       NMACountryInfo *country_info = 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))
+               return NULL;
+
+       gtk_tree_model_get (model, &iter, COUNTRIES_COL_INFO, &country_info, -1);
+       return country_info;
+}
+
+static void
+country_update_complete (NMAMobileWizard *self)
+{
+       NMACountryInfo *country_info;
+
+       country_info = get_selected_country (self);
+       gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant),
+                                        self->country_page,
+                                        (!!country_info));
+       if (country_info)
+               nma_country_info_unref (country_info);
+}
+
+static void
+country_update_continue (NMAMobileWizard *self)
+{
+       gtk_assistant_set_page_complete (GTK_ASSISTANT (self->assistant),
+                                        self->country_page,
+                                        TRUE);
+
+       gtk_assistant_next_page (GTK_ASSISTANT (self->assistant));
+}
+
+static gint
+country_sort_func (GtkTreeModel *model,
+                   GtkTreeIter *a,
+                   GtkTreeIter *b,
+                   gpointer user_data)
+{
+       char *a_str = NULL, *b_str = NULL;
+       NMACountryInfo *a_country_info = NULL, *b_country_info = NULL;
+       gint ret = 0;
+
+       gtk_tree_model_get (model, a, COUNTRIES_COL_NAME, &a_str, COUNTRIES_COL_INFO, &a_country_info, -1);
+       gtk_tree_model_get (model, b, COUNTRIES_COL_NAME, &b_str, COUNTRIES_COL_INFO, &b_country_info, -1);
+
+       if (!a_country_info) {
+               ret = -1;
+               goto out;
+       } else if (!b_country_info) {
+               ret = 1;
+               goto out;
+       }
+
+       if (a_str && !b_str)
+               ret = -1;
+       else if (!a_str && b_str)
+               ret = 1;
+       else if (!a_str && !b_str)
+               ret = 0;
+       else
+               ret = g_utf8_collate (a_str, b_str);
+
+out:
+       if (a_country_info)
+               nma_country_info_unref (a_country_info);
+       if (b_country_info)
+               nma_country_info_unref (b_country_info);
+       g_free (a_str);
+       g_free (b_str);
+       return ret;
+}
+
+static void
+country_setup (NMAMobileWizard *self)
+{
+       GtkWidget *vbox, *label, *scroll, *alignment;
+       GtkCellRenderer *renderer;
+       GtkTreeViewColumn *column;
+       GtkTreeSelection *selection;
+       GtkTreeIter unlisted_iter;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+       label = gtk_label_new (_("Country or Region 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 (2, G_TYPE_STRING, NMA_TYPE_COUNTRY_INFO);
+
+       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),
+                                             COUNTRIES_COL_NAME, 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 or region"),
+                                                          renderer,
+                                                          "text", COUNTRIES_COL_NAME,
+                                                          NULL);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (self->country_view), column);
+       gtk_tree_view_column_set_clickable (column, TRUE);
+
+       /* My country is not listed... */
+       gtk_tree_store_append (GTK_TREE_STORE (self->country_store), &unlisted_iter, NULL);
+       gtk_tree_store_set (GTK_TREE_STORE (self->country_store), &unlisted_iter,
+                           PROVIDER_COL_NAME, _("My country is not listed"),
+                           PROVIDER_COL_PROVIDER, NULL,
+                           -1);
+       gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self->country_sort),
+                                        COUNTRIES_COL_NAME,
+                                        country_sort_func,
+                                        NULL,
+                                        NULL);
+
+       /* Add the rest of the providers */
+       if (self->mobile_providers_database) {
+               GHashTable *countries;
+
+               countries = nma_mobile_providers_database_get_countries (self->mobile_providers_database);
+               g_hash_table_foreach (countries, 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 or Region"));
+       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;
+
+       /* If the user presses the ENTER key after selecting the country, continue to the next page */
+       g_signal_connect_swapped (self->country_view, "row-activated", G_CALLBACK (country_update_continue), 
self);
+
+       /* Initial completeness state */
+       country_update_complete (self);
+}
+
+static gboolean
+focus_country_view (gpointer user_data)
+{
+       NMAMobileWizard *self = user_data;
+
+       self->country_focus_id = 0;
+       gtk_widget_grab_focus (self->country_view);
+       return FALSE;
+}
+
+static void
+country_prepare (NMAMobileWizard *self)
+{
+       gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->country_view), COUNTRIES_COL_NAME);
+       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 (NMAMobileWizard *self, NMDevice *device, gboolean select_it)
+{
+       GtkTreeIter iter;
+       const char *desc = nm_device_get_description (device);
+       NMDeviceModemCapabilities caps;
+
+       if (!NM_IS_DEVICE_MODEM (device))
+               return FALSE;
+
+       caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device));
+       if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) {
+               if (!desc)
+                       desc = _("Installed GSM device");
+       } else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) {
+               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, NMAMobileWizard *self)
+{
+       __intro_device_added (self, device, TRUE);
+}
+
+static void
+intro_device_removed_cb (NMClient *client, NMDevice *device, NMAMobileWizard *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);
+                               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 (NMAMobileWizard *self)
+{
+       const GPtrArray *devices;
+       gboolean selected_first = FALSE;
+       int i;
+
+       devices = self->client ? nm_client_get_devices (self->client) : NULL;
+       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 (NMAMobileWizard *self)
+{
+       gtk_tree_store_clear (self->dev_store);
+
+       /* 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, NMAMobileWizard *self)
+{
+       if (nm_client_get_nm_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 (NMAMobileWizard *self)
+{
+       GtkTreeIter iter;
+       NMDevice *selected = NULL;
+       NMDeviceModemCapabilities caps;
+
+       g_free (self->dev_desc);
+       self->dev_desc = 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) {
+               self->dev_desc = g_strdup (nm_device_get_description (selected));
+               caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (selected));
+               if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
+                       self->family = NMA_MOBILE_FAMILY_3GPP;
+               else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
+                       self->family = NMA_MOBILE_FAMILY_CDMA;
+               else
+                       g_warning ("%s: unknown modem capabilities 0x%X", __func__, caps);
+
+               g_object_unref (selected);
+       }
+}
+
+static void
+intro_setup (NMAMobileWizard *self)
+{
+       GtkWidget *vbox, *label, *alignment, *info_vbox;
+       GtkCellRenderer *renderer;
+       char *s;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_label_set_max_width_chars (GTK_LABEL (label), 60);
+       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_box_new (GTK_ORIENTATION_VERTICAL, 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->initial_family) {
+               GtkTreeIter iter;
+
+               self->client = nm_client_new (NULL, NULL);
+               if (self->client) {
+                       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 (NMAMobileWizard *self)
+{
+       if (self->providers_focus_id) {
+               g_source_remove (self->providers_focus_id);
+               self->providers_focus_id = 0;
+       }
+}
+
+static void
+remove_country_focus_idle (NMAMobileWizard *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)
+{
+       NMAMobileWizard *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)
+{
+       NMAMobileWizard *self = user_data;
+
+       if (current_page == self->providers_idx) {
+               NMAMobileFamily family = self->family;
+
+               /* 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 (family == NMA_MOBILE_FAMILY_UNKNOWN)
+                               family = get_provider_unlisted_type (self);
+               } else {
+                       /* Or, if the provider is only CDMA, then we can also skip ahead */
+                       NMAMobileProvider *provider;
+                       GSList *iter;
+                       gboolean gsm = FALSE, cdma = FALSE;
+
+                       provider = get_selected_provider (self);
+                       if (provider) {
+                               for (iter = nma_mobile_provider_get_methods (provider); iter; iter = 
g_slist_next (iter)) {
+                                       NMAMobileAccessMethod *method = iter->data;
+
+                                       if (nma_mobile_access_method_get_family (method) == 
NMA_MOBILE_FAMILY_CDMA)
+                                               cdma = TRUE;
+                                       else if (nma_mobile_access_method_get_family (method) == 
NMA_MOBILE_FAMILY_3GPP)
+                                               gsm = TRUE;
+                               }
+                               nma_mobile_provider_unref (provider);
+
+                               if (cdma && !gsm)
+                                       family = NMA_MOBILE_FAMILY_CDMA;
+                       }
+               }
+
+               /* Skip to the confirm page if we know its CDMA */
+               if (family == NMA_MOBILE_FAMILY_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;
+}
+
+/**
+ * nma_mobile_wizard_new: (skip)
+ * @cb: (scope async):
+ */
+NMAMobileWizard *
+nma_mobile_wizard_new (GtkWindow *parent,
+                       GtkWindowGroup *window_group,
+                       NMDeviceModemCapabilities modem_caps,
+                       gboolean will_connect_after,
+                       NMAMobileWizardCallback cb,
+                       gpointer user_data)
+{
+       NMAMobileWizard *self;
+       char *cc;
+       GError *error = NULL;
+
+       self = g_malloc0 (sizeof (NMAMobileWizard));
+       g_return_val_if_fail (self != NULL, NULL);
+
+       self->mobile_providers_database = nma_mobile_providers_database_new_sync (NULL, NULL, NULL, &error);
+       if (!self->mobile_providers_database) {
+               g_warning ("Cannot create mobile providers database: %s",
+                          error->message);
+               g_error_free (error);
+               nma_mobile_wizard_destroy (self);
+               return NULL;
+       }
+
+       cc = get_country_from_locale ();
+       if (cc) {
+               self->country = nma_mobile_providers_database_lookup_country 
(self->mobile_providers_database, cc);
+               g_free (cc);
+       }
+
+       self->will_connect_after = will_connect_after;
+       self->callback = cb;
+       self->user_data = user_data;
+       if (modem_caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
+               self->family = NMA_MOBILE_FAMILY_3GPP;
+       else if (modem_caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
+               self->family = NMA_MOBILE_FAMILY_CDMA;
+       if (self->family)
+               self->initial_family = TRUE;  /* Skip device selection */
+
+       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);
+
+       if (window_group)
+               gtk_window_group_add_window (window_group, GTK_WINDOW (self->assistant));
+
+       return self;
+}
+
+void
+nma_mobile_wizard_present (NMAMobileWizard *self)
+{
+       g_return_if_fail (self != NULL);
+
+       gtk_window_present (GTK_WINDOW (self->assistant));
+       gtk_widget_show_all (self->assistant);
+}
+
+void
+nma_mobile_wizard_destroy (NMAMobileWizard *self)
+{
+       g_return_if_fail (self != NULL);
+
+       g_free (self->dev_desc);
+
+       if (self->assistant) {
+               gtk_widget_hide (self->assistant);
+               gtk_widget_destroy (self->assistant);
+       }
+
+       if (self->client)
+               g_object_unref (self->client);
+
+       remove_provider_focus_idle (self);
+       remove_country_focus_idle (self);
+
+       if (self->mobile_providers_database)
+               g_object_unref (self->mobile_providers_database);
+
+       g_free (self);
+}
diff --git a/src/libnma/nma-mobile-wizard.h b/src/libnma/nma-mobile-wizard.h
new file mode 100644
index 0000000..5f626f6
--- /dev/null
+++ b/src/libnma/nma-mobile-wizard.h
@@ -0,0 +1,62 @@
+/* -*- 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 - 2011 Red Hat, Inc.
+ */
+
+/* WARNING: this file is private API between nm-applet and various GNOME
+ * bits; it may change without notice and is not guaranteed to be stable.
+ */
+
+#ifndef MOBILE_WIZARD_H
+#define MOBILE_WIZARD_H
+
+#include <glib.h>
+#include <NetworkManager.h>
+#include <nm-device.h>
+
+typedef struct NMAMobileWizard NMAMobileWizard;
+
+typedef struct {
+       char *provider_name;
+       char *plan_name;
+       NMDeviceModemCapabilities devtype;
+       char *username;
+       char *password;
+       char *gsm_apn;
+} NMAMobileWizardAccessMethod;
+
+typedef void (*NMAMobileWizardCallback) (NMAMobileWizard *self,
+                                                                                gboolean canceled,
+                                                                                NMAMobileWizardAccessMethod 
*method,
+                                                                                gpointer user_data);
+
+NMAMobileWizard *nma_mobile_wizard_new (GtkWindow *parent,
+                                                                               GtkWindowGroup *window_group,
+                                                                               NMDeviceModemCapabilities 
modem_caps,
+                                                                               gboolean will_connect_after,
+                                                                               NMAMobileWizardCallback cb,
+                                                                               gpointer user_data);
+
+void nma_mobile_wizard_present (NMAMobileWizard *wizard);
+
+void nma_mobile_wizard_destroy (NMAMobileWizard *self);
+
+#endif /* MOBILE_WIZARD_H */
+
diff --git a/src/libnma/nma-vpn-password-dialog.c b/src/libnma/nma-vpn-password-dialog.c
new file mode 100644
index 0000000..e9234cf
--- /dev/null
+++ b/src/libnma/nma-vpn-password-dialog.c
@@ -0,0 +1,446 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nma-vpn-password-dialog.c - A password prompting dialog widget.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the ree Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * Authors: Ramiro Estrugo <ramiro eazel com>
+ *          Dan Williams <dcbw redhat com>
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "nma-vpn-password-dialog.h"
+
+G_DEFINE_TYPE (NMAVpnPasswordDialog, nma_vpn_password_dialog, GTK_TYPE_DIALOG)
+
+#define NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                                NMA_VPN_TYPE_PASSWORD_DIALOG, \
+                                                NMAVpnPasswordDialogPrivate))
+
+typedef struct {
+       /* Attributes */
+       gboolean show_password;
+       gboolean show_password_secondary;
+       
+       /* Internal widgetry and flags */
+       GtkWidget *password_entry;
+       GtkWidget *password_entry_secondary;
+       GtkWidget *show_passwords_checkbox;
+
+       GtkWidget *grid_alignment;
+       GtkWidget *grid;
+       GtkSizeGroup *group;
+       
+       char *primary_password_label;
+       char *secondary_password_label;
+} NMAVpnPasswordDialogPrivate;
+
+/* NMAVpnPasswordDialogClass methods */
+static void nma_vpn_password_dialog_class_init (NMAVpnPasswordDialogClass *password_dialog_class);
+static void nma_vpn_password_dialog_init       (NMAVpnPasswordDialog      *password_dialog);
+
+/* GtkDialog callbacks */
+static void dialog_show_callback (GtkWidget *widget, gpointer callback_data);
+static void dialog_close_callback (GtkWidget *widget, gpointer callback_data);
+
+static void
+finalize (GObject *object)
+{
+       NMAVpnPasswordDialogPrivate *priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (object);
+       
+       g_object_unref (priv->password_entry);
+       g_object_unref (priv->password_entry_secondary);
+       g_object_unref (priv->group);
+
+       g_free (priv->primary_password_label);
+       g_free (priv->secondary_password_label);
+
+       G_OBJECT_CLASS (nma_vpn_password_dialog_parent_class)->finalize (object);
+}
+
+static void
+nma_vpn_password_dialog_class_init (NMAVpnPasswordDialogClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       g_type_class_add_private (object_class, sizeof (NMAVpnPasswordDialogPrivate));
+
+       object_class->finalize = finalize;
+}
+
+static void
+nma_vpn_password_dialog_init (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       priv->show_password = TRUE;
+       priv->primary_password_label = g_strdup ( _("_Password:") );
+       priv->show_password_secondary = TRUE;
+       priv->secondary_password_label = g_strdup ( _("_Secondary Password:") );
+}
+
+/* GtkDialog callbacks */
+static void
+dialog_show_callback (GtkWidget *widget, gpointer callback_data)
+{
+       NMAVpnPasswordDialog *dialog = NMA_VPN_PASSWORD_DIALOG (callback_data);
+       NMAVpnPasswordDialogPrivate *priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       if (gtk_widget_get_visible (priv->password_entry))
+               gtk_widget_grab_focus (priv->password_entry);
+       else if (gtk_widget_get_visible (priv->password_entry_secondary))
+               gtk_widget_grab_focus (priv->password_entry_secondary);
+}
+
+static void
+dialog_close_callback (GtkWidget *widget, gpointer callback_data)
+{
+       gtk_widget_hide (widget);
+}
+
+static void
+add_row (GtkWidget *grid, int row, const char *label_text, GtkWidget *entry)
+{
+       GtkWidget *label;
+
+       label = gtk_label_new_with_mnemonic (label_text);
+       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+       gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+       gtk_grid_attach (GTK_GRID (grid), entry, 1, row, 1, 1);
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+}
+
+static void
+remove_child (GtkWidget *child, GtkWidget *grid)
+{
+       gtk_container_remove (GTK_CONTAINER (grid), child);
+}
+
+static void
+add_grid_rows (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       int row;
+       int offset = 0;
+
+       gtk_alignment_set_padding (GTK_ALIGNMENT (priv->grid_alignment), 0, 0, offset, 0);
+
+       /* This will not kill the entries, since they are ref:ed */
+       gtk_container_foreach (GTK_CONTAINER (priv->grid), (GtkCallback) remove_child, priv->grid);
+       
+       row = 0;
+       if (priv->show_password)
+               add_row (priv->grid, row++, priv->primary_password_label, priv->password_entry);
+       if (priv->show_password_secondary)
+               add_row (priv->grid, row++, priv->secondary_password_label,  priv->password_entry_secondary);
+
+       gtk_grid_attach (GTK_GRID (priv->grid), priv->show_passwords_checkbox, 1, row, 1, 1);
+
+       gtk_widget_show_all (priv->grid);
+}
+
+static void
+show_passwords_toggled_cb (GtkWidget *widget, gpointer user_data)
+{
+       NMAVpnPasswordDialog *dialog = NMA_VPN_PASSWORD_DIALOG (user_data);
+       NMAVpnPasswordDialogPrivate *priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       gboolean visible;
+
+       visible = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), visible);
+       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry_secondary), visible);
+}
+
+/* Public NMAVpnPasswordDialog methods */
+GtkWidget *
+nma_vpn_password_dialog_new (const char *title,
+                             const char *message,
+                             const char *password)
+{
+       GtkWidget *dialog;
+       NMAVpnPasswordDialogPrivate *priv;
+       GtkLabel *message_label;
+       GtkWidget *hbox;
+       GtkWidget *vbox;
+       GtkWidget *main_vbox;
+       GtkWidget *dialog_icon;
+       GtkBox *content, *action_area;
+
+       dialog = gtk_widget_new (NMA_VPN_TYPE_PASSWORD_DIALOG, NULL);
+       if (!dialog)
+               return NULL;
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       gtk_window_set_title (GTK_WINDOW (dialog), title);
+       gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                               GTK_STOCK_OK, GTK_RESPONSE_OK,
+                               NULL);
+       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+       content = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
+       action_area = GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dialog)));
+
+       /* Set up the dialog */
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+       gtk_box_set_spacing (content, 2); /* 2 * 5 + 2 = 12 */
+       gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+       gtk_box_set_spacing (action_area, 6);
+
+       gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+       g_signal_connect (dialog, "show",
+                         G_CALLBACK (dialog_show_callback),
+                         dialog);
+       g_signal_connect (dialog, "close",
+                         G_CALLBACK (dialog_close_callback),
+                         dialog);
+
+       /* The grid that holds the captions */
+       priv->grid_alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+
+       priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+       priv->grid = gtk_grid_new ();
+       gtk_grid_set_column_spacing (GTK_GRID (priv->grid), 12);
+       gtk_grid_set_row_spacing (GTK_GRID (priv->grid), 6);
+       gtk_container_add (GTK_CONTAINER (priv->grid_alignment), priv->grid);
+
+       priv->password_entry = gtk_entry_new ();
+       priv->password_entry_secondary = gtk_entry_new ();
+
+       priv->show_passwords_checkbox = gtk_check_button_new_with_mnemonic (_("Sh_ow passwords"));
+
+       /* We want to hold on to these during the grid rearrangement */
+       g_object_ref_sink (priv->password_entry);
+       g_object_ref_sink (priv->password_entry_secondary);
+       g_object_ref_sink (priv->show_passwords_checkbox);
+       
+       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE);
+       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry_secondary), FALSE);
+
+       g_signal_connect_swapped (priv->password_entry, "activate",
+                                 G_CALLBACK (gtk_window_activate_default),
+                                 dialog);
+       g_signal_connect_swapped (priv->password_entry_secondary, "activate",
+                                 G_CALLBACK (gtk_window_activate_default),
+                                 dialog);
+
+       g_signal_connect (priv->show_passwords_checkbox, "toggled",
+                         G_CALLBACK (show_passwords_toggled_cb),
+                         dialog);
+
+       add_grid_rows (NMA_VPN_PASSWORD_DIALOG (dialog));
+
+       /* Adds some eye-candy to the dialog */
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+       gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+       dialog_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG);
+       gtk_misc_set_alignment (GTK_MISC (dialog_icon), 0.5, 0.0);
+       gtk_box_pack_start (GTK_BOX (hbox), dialog_icon, FALSE, FALSE, 0);
+
+       /* Fills the vbox */
+       main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
+
+       if (message) {
+               message_label = GTK_LABEL (gtk_label_new (message));
+               gtk_label_set_justify (message_label, GTK_JUSTIFY_LEFT);
+               gtk_label_set_line_wrap (message_label, TRUE);
+               gtk_label_set_max_width_chars (message_label, 35);
+               gtk_size_group_add_widget (priv->group, GTK_WIDGET (message_label));
+               gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label), FALSE, FALSE, 0);
+               gtk_size_group_add_widget (priv->group, priv->grid_alignment);
+       }
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+       gtk_box_pack_start (GTK_BOX (vbox), priv->grid_alignment, FALSE, FALSE, 0);
+       gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
+       gtk_box_pack_start (content, hbox, FALSE, FALSE, 0);
+       gtk_widget_show_all (GTK_WIDGET (content));
+
+       nma_vpn_password_dialog_set_password (NMA_VPN_PASSWORD_DIALOG (dialog), password);
+       
+       return GTK_WIDGET (dialog);
+}
+
+gboolean
+nma_vpn_password_dialog_run_and_block (NMAVpnPasswordDialog *dialog)
+{
+       gint button_clicked;
+
+       g_return_val_if_fail (dialog != NULL, FALSE);
+       g_return_val_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog), FALSE);
+
+       button_clicked = gtk_dialog_run (GTK_DIALOG (dialog));
+       gtk_widget_hide (GTK_WIDGET (dialog));
+
+       return button_clicked == GTK_RESPONSE_OK;
+}
+
+void
+nma_vpn_password_dialog_set_password (NMAVpnPasswordDialog     *dialog,
+                                      const char *password)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       gtk_entry_set_text (GTK_ENTRY (priv->password_entry), password ? password : "");
+}
+
+void
+nma_vpn_password_dialog_set_password_secondary (NMAVpnPasswordDialog *dialog,
+                                                const char *password_secondary)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       gtk_entry_set_text (GTK_ENTRY (priv->password_entry_secondary),
+                           password_secondary ? password_secondary : "");
+}
+
+void
+nma_vpn_password_dialog_set_show_password (NMAVpnPasswordDialog *dialog, gboolean show)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       show = !!show;
+       if (priv->show_password != show) {
+               priv->show_password = show;
+               add_grid_rows (dialog);
+       }
+}
+
+void
+nma_vpn_password_dialog_set_show_password_secondary (NMAVpnPasswordDialog *dialog,
+                                                     gboolean show)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       show = !!show;
+       if (priv->show_password_secondary != show) {
+               priv->show_password_secondary = show;
+               add_grid_rows (dialog);
+       }
+}
+
+void
+nma_vpn_password_dialog_focus_password (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       if (priv->show_password)
+               gtk_widget_grab_focus (priv->password_entry);
+}
+
+void
+nma_vpn_password_dialog_focus_password_secondary (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       if (priv->show_password_secondary)
+               gtk_widget_grab_focus (priv->password_entry_secondary);
+}
+
+const char *
+nma_vpn_password_dialog_get_password (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_val_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog), NULL);
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       return gtk_entry_get_text (GTK_ENTRY (priv->password_entry));
+}
+
+const char *
+nma_vpn_password_dialog_get_password_secondary (NMAVpnPasswordDialog *dialog)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_val_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog), NULL);
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+       return gtk_entry_get_text (GTK_ENTRY (priv->password_entry_secondary));
+}
+
+void nma_vpn_password_dialog_set_password_label (NMAVpnPasswordDialog *dialog,
+                                                 const char *label)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       g_free (priv->primary_password_label);
+       priv->primary_password_label = g_strdup (label);
+
+       if (priv->show_password)
+               add_grid_rows (dialog);
+}
+
+void nma_vpn_password_dialog_set_password_secondary_label (NMAVpnPasswordDialog *dialog,
+                                                           const char *label)
+{
+       NMAVpnPasswordDialogPrivate *priv;
+
+       g_return_if_fail (dialog != NULL);
+       g_return_if_fail (NMA_VPN_IS_PASSWORD_DIALOG (dialog));
+
+       priv = NMA_VPN_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+       g_free (priv->secondary_password_label);
+       priv->secondary_password_label = g_strdup (label);
+
+       if (priv->show_password_secondary)
+               add_grid_rows (dialog);
+}
+
diff --git a/src/libnma/nma-vpn-password-dialog.h b/src/libnma/nma-vpn-password-dialog.h
new file mode 100644
index 0000000..f9869af
--- /dev/null
+++ b/src/libnma/nma-vpn-password-dialog.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* vpn-password-dialog.c - A use password prompting dialog widget.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the ree Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * Authors: Ramiro Estrugo <ramiro eazel com>
+ *          Dan Williams <dcbw redhat com>
+ */
+
+#ifndef NMA_VPN_PASSWORD_DIALOG_H
+#define NMA_VPN_PASSWORD_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NMA_VPN_TYPE_PASSWORD_DIALOG            (nma_vpn_password_dialog_get_type ())
+#define NMA_VPN_PASSWORD_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
NMA_VPN_TYPE_PASSWORD_DIALOG, NMAVpnPasswordDialog))
+#define NMA_VPN_PASSWORD_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
NMA_VPN_TYPE_PASSWORD_DIALOG, NMAVpnPasswordDialogClass))
+#define NMA_VPN_IS_PASSWORD_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
NMA_VPN_TYPE_PASSWORD_DIALOG))
+#define NMA_VPN_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
NMA_VPN_TYPE_PASSWORD_DIALOG))
+
+typedef struct NMAVpnPasswordDialog        NMAVpnPasswordDialog;
+typedef struct NMAVpnPasswordDialogClass   NMAVpnPasswordDialogClass;
+
+struct NMAVpnPasswordDialog {
+       GtkDialog parent;
+};
+
+struct NMAVpnPasswordDialogClass {
+       GtkDialogClass parent_class;
+};
+
+GType      nma_vpn_password_dialog_get_type      (void);
+GtkWidget* nma_vpn_password_dialog_new           (const char *title,
+                                                  const char *message,
+                                                  const char *password);
+
+gboolean   nma_vpn_password_dialog_run_and_block (NMAVpnPasswordDialog *dialog);
+
+/* Attribute mutators */
+void nma_vpn_password_dialog_set_show_password            (NMAVpnPasswordDialog *dialog,
+                                                           gboolean              show);
+void nma_vpn_password_dialog_focus_password               (NMAVpnPasswordDialog *dialog);
+void nma_vpn_password_dialog_set_password                 (NMAVpnPasswordDialog *dialog,
+                                                           const char           *password);
+void nma_vpn_password_dialog_set_password_label           (NMAVpnPasswordDialog *dialog,
+                                                           const char           *label);
+
+void nma_vpn_password_dialog_set_show_password_secondary  (NMAVpnPasswordDialog *dialog,
+                                                           gboolean              show);
+void nma_vpn_password_dialog_focus_password_secondary     (NMAVpnPasswordDialog *dialog);
+void nma_vpn_password_dialog_set_password_secondary       (NMAVpnPasswordDialog *dialog,
+                                                           const char           *password_secondary);
+void nma_vpn_password_dialog_set_password_secondary_label (NMAVpnPasswordDialog *dialog,
+                                                           const char           *label);
+
+/* Attribute accessors */
+const char *nma_vpn_password_dialog_get_password           (NMAVpnPasswordDialog *dialog);
+
+const char *nma_vpn_password_dialog_get_password_secondary (NMAVpnPasswordDialog *dialog);
+
+G_END_DECLS
+
+#endif /* NMA_VPN_PASSWORD_DIALOG_H */
diff --git a/src/libnma/nma-wifi-dialog.c b/src/libnma/nma-wifi-dialog.c
new file mode 100644
index 0000000..e14a2fe
--- /dev/null
+++ b/src/libnma/nma-wifi-dialog.c
@@ -0,0 +1,1407 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Applet -- allow user control over networking
+ *
+ * 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.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <netinet/ether.h>
+
+#include <NetworkManager.h>
+
+#include "nma-wifi-dialog.h"
+#include "wireless-security.h"
+#include "eap-method.h"
+
+G_DEFINE_TYPE (NMAWifiDialog, nma_wifi_dialog, GTK_TYPE_DIALOG)
+
+#define NMA_WIFI_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                        NMA_TYPE_WIFI_DIALOG, \
+                                        NMAWifiDialogPrivate))
+
+typedef struct {
+       NMAWifiDialog *self;
+       NMConnection *connection;
+       gboolean canceled;
+} GetSecretsInfo;
+
+typedef struct {
+       NMClient *client;
+
+       GtkBuilder *builder;
+
+       NMConnection *connection;
+       NMDevice *device;
+       NMAccessPoint *ap;
+       guint operation;
+
+       GtkTreeModel *device_model;
+       GtkTreeModel *connection_model;
+       GtkSizeGroup *group;
+       GtkWidget *sec_combo;
+
+       gboolean network_name_focus;
+
+       gboolean secrets_only;
+
+       guint revalidate_id;
+
+       GetSecretsInfo *secrets_info;
+
+       gboolean disposed;
+} NMAWifiDialogPrivate;
+
+enum {
+       OP_NONE = 0,
+       OP_CREATE_ADHOC,
+       OP_CONNECT_HIDDEN,
+};
+
+#define D_NAME_COLUMN          0
+#define D_DEV_COLUMN           1
+
+#define S_NAME_COLUMN          0
+#define S_SEC_COLUMN           1
+
+#define C_NAME_COLUMN          0
+#define C_CON_COLUMN           1
+#define C_SEP_COLUMN           2
+#define C_NEW_COLUMN           3
+
+static gboolean security_combo_init (NMAWifiDialog *self, gboolean secrets_only);
+static void ssid_entry_changed (GtkWidget *entry, gpointer user_data);
+
+void
+nma_wifi_dialog_set_nag_ignored (NMAWifiDialog *self, gboolean ignored)
+{
+}
+
+gboolean
+nma_wifi_dialog_get_nag_ignored (NMAWifiDialog *self)
+{
+       return TRUE;
+}
+
+static void
+size_group_clear (GtkSizeGroup *group)
+{
+       GSList *iter;
+
+       g_return_if_fail (group != NULL);
+
+       iter = gtk_size_group_get_widgets (group);
+       while (iter) {
+               gtk_size_group_remove_widget (group, GTK_WIDGET (iter->data));
+               iter = gtk_size_group_get_widgets (group);
+       }
+}
+
+static void
+size_group_add_permanent (GtkSizeGroup *group,
+                          GtkBuilder *builder)
+{
+       GtkWidget *widget;
+
+       g_return_if_fail (group != NULL);
+       g_return_if_fail (builder != NULL);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "network_name_label"));
+       gtk_size_group_add_widget (group, widget);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "security_combo_label"));
+       gtk_size_group_add_widget (group, widget);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (builder, "device_label"));
+       gtk_size_group_add_widget (group, widget);
+}
+
+static void
+security_combo_changed (GtkWidget *combo,
+                        gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkWidget *vbox, *sec_widget, *def_widget;
+       GList *elt, *children;
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+       WirelessSecurity *sec = NULL;
+
+       vbox = GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_vbox"));
+       g_assert (vbox);
+
+       size_group_clear (priv->group);
+
+       /* Remove any previous wireless security widgets */
+       children = gtk_container_get_children (GTK_CONTAINER (vbox));
+       for (elt = children; elt; elt = g_list_next (elt))
+               gtk_container_remove (GTK_CONTAINER (vbox), GTK_WIDGET (elt->data));
+       g_list_free (children);
+
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+       if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
+               g_warning ("%s: no active security combo box item.", __func__);
+               return;
+       }
+
+       gtk_tree_model_get (model, &iter, S_SEC_COLUMN, &sec, -1);
+       if (!sec) {
+               /* Revalidate dialog if the user picked "None" so the OK button
+                * gets enabled if there's already a valid SSID.
+                */
+               ssid_entry_changed (NULL, self);
+               return;
+       }
+
+       sec_widget = wireless_security_get_widget (sec);
+       g_assert (sec_widget);
+       gtk_widget_unparent (sec_widget);
+
+       size_group_add_permanent (priv->group, priv->builder);
+       wireless_security_add_to_size_group (sec, priv->group);
+
+       gtk_container_add (GTK_CONTAINER (vbox), sec_widget);
+
+       /* Re-validate */
+       wireless_security_changed_cb (NULL, sec);
+
+       /* Set focus to the security method's default widget, but only if the
+        * network name entry should not be focused.
+        */
+       if (!priv->network_name_focus && sec->default_field) {
+               def_widget = GTK_WIDGET (gtk_builder_get_object (sec->builder, sec->default_field));
+               if (def_widget)
+                       gtk_widget_grab_focus (def_widget);
+       }
+
+       wireless_security_unref (sec);
+}
+
+static void
+security_combo_changed_manually (GtkWidget *combo,
+                                 gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+
+       /* Flag that the combo was changed manually to allow focus to move
+        * to the security method's default widget instead of the network name.
+        */
+       priv->network_name_focus = FALSE;
+       security_combo_changed (combo, user_data);
+}
+
+static GBytes *
+validate_dialog_ssid (NMAWifiDialog *self)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkWidget *widget;
+       const char *ssid;
+       GBytes *ssid_bytes;
+
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry"));
+
+       ssid = gtk_entry_get_text (GTK_ENTRY (widget));
+       
+       if (!ssid || strlen (ssid) == 0 || strlen (ssid) > 32)
+               return NULL;
+
+       ssid_bytes = g_bytes_new (ssid, strlen (ssid));
+       return ssid_bytes;
+}
+
+static void
+stuff_changed_cb (WirelessSecurity *sec, gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GBytes *ssid = NULL;
+       gboolean free_ssid = TRUE;
+       gboolean valid = FALSE;
+       
+       if (priv->connection) {
+               NMSettingWireless *s_wireless;
+               s_wireless = nm_connection_get_setting_wireless (priv->connection);
+               g_assert (s_wireless);
+               ssid = nm_setting_wireless_get_ssid (s_wireless);
+               free_ssid = FALSE;
+       } else {
+               ssid = validate_dialog_ssid (self);
+       }
+
+       if (ssid) {
+               valid = wireless_security_validate (sec);
+               if (free_ssid)
+                       g_bytes_unref (ssid);
+       }
+
+       /* But if there's an in-progress secrets call (which might require authorization)
+        * then we don't want to enable the OK button because we don't have all the
+        * connection details yet.
+        */
+       if (priv->secrets_info)
+               valid = FALSE;
+
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, valid);
+}
+
+static void
+ssid_entry_changed (GtkWidget *entry, gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkTreeIter iter;
+       WirelessSecurity *sec = NULL;
+       GtkTreeModel *model;
+       gboolean valid = FALSE;
+       GBytes *ssid;
+
+       /* If the network name entry was touched at all, allow focus to go to
+        * the default widget of the security method now.
+        */
+       priv->network_name_focus = FALSE;
+
+       ssid = validate_dialog_ssid (self);
+       if (!ssid)
+               goto out;
+
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->sec_combo));
+       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->sec_combo), &iter))
+               gtk_tree_model_get (model, &iter, S_SEC_COLUMN, &sec, -1);
+
+       if (sec) {
+               valid = wireless_security_validate (sec);
+               wireless_security_unref (sec);
+       } else {
+               valid = TRUE;
+       }
+
+out:
+       /* But if there's an in-progress secrets call (which might require authorization)
+        * then we don't want to enable the OK button because we don't have all the
+        * connection details yet.
+        */
+       if (priv->secrets_info)
+               valid = FALSE;
+
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, valid);
+}
+
+static void
+connection_combo_changed (GtkWidget *combo,
+                          gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+       gboolean is_new = FALSE;
+       NMSettingWireless *s_wireless;
+       char *utf8_ssid;
+       GtkWidget *widget;
+
+       if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
+               g_warning ("%s: no active connection combo box item.", __func__);
+               return;
+       }
+
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+       if (priv->connection)
+               g_object_unref (priv->connection);
+
+       gtk_tree_model_get (model, &iter,
+                           C_CON_COLUMN, &priv->connection,
+                           C_NEW_COLUMN, &is_new, -1);
+
+       if (priv->connection)
+               eap_method_ca_cert_ignore_load (priv->connection);
+
+       if (!security_combo_init (self, priv->secrets_only)) {
+               g_warning ("Couldn't change Wi-Fi security combo box.");
+               return;
+       }
+       security_combo_changed (priv->sec_combo, self);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry"));
+       if (priv->connection) {
+               GBytes *ssid;
+
+               s_wireless = nm_connection_get_setting_wireless (priv->connection);
+               ssid = nm_setting_wireless_get_ssid (s_wireless);
+               utf8_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
+               gtk_entry_set_text (GTK_ENTRY (widget), utf8_ssid);
+               g_free (utf8_ssid);
+       } else {
+               gtk_entry_set_text (GTK_ENTRY (widget), "");
+       }
+
+       gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry")), 
is_new);
+       gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_label")), 
is_new);
+       gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_combo")), 
is_new);
+       gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (priv->builder, 
"security_combo_label")), is_new);
+       gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_vbox")), 
is_new);
+}
+
+static gboolean
+connection_combo_separator_cb (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+       gboolean is_separator = FALSE;
+
+       gtk_tree_model_get (model, iter, C_SEP_COLUMN, &is_separator, -1);
+       return is_separator;
+}
+
+static gint
+alphabetize_connections (NMConnection *a, NMConnection *b)
+{
+       NMSettingConnection *asc, *bsc;
+
+       asc = nm_connection_get_setting_connection (a);
+       bsc = nm_connection_get_setting_connection (b);
+
+       return strcmp (nm_setting_connection_get_id (asc),
+                      nm_setting_connection_get_id (bsc));
+}
+
+static gboolean
+connection_combo_init (NMAWifiDialog *self, NMConnection *connection)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkListStore *store;
+       int num_added = 0;
+       GtkTreeIter tree_iter;
+       GtkWidget *widget;
+       NMSettingConnection *s_con;
+       GtkCellRenderer *renderer;
+       const char *id;
+
+       g_return_val_if_fail (priv->connection == NULL, FALSE);
+
+       /* Clear any old model */
+       if (priv->connection_model)
+               g_object_unref (priv->connection_model);
+
+       /* New model */
+       store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+       priv->connection_model = GTK_TREE_MODEL (store);
+
+       if (connection) {
+               s_con = nm_connection_get_setting_connection (connection);
+               g_assert (s_con);
+               id = nm_setting_connection_get_id (s_con);
+               if (id == NULL) {
+                       /* New connections which will be completed by NM won't have an ID
+                        * yet, but that doesn't matter because we don't show the connection
+                        * combo anyway when there's a predefined connection.
+                        */
+                       id = "blahblah";
+               }
+
+               gtk_list_store_append (store, &tree_iter);
+               gtk_list_store_set (store, &tree_iter,
+                                   C_NAME_COLUMN, id,
+                                   C_CON_COLUMN, connection, -1);
+       } else {
+               GSList *to_add, *iter;
+               const GPtrArray *connections;
+               int i;
+
+               gtk_list_store_append (store, &tree_iter);
+               gtk_list_store_set (store, &tree_iter,
+                                   C_NAME_COLUMN, _("New..."),
+                                   C_NEW_COLUMN, TRUE, -1);
+
+               gtk_list_store_append (store, &tree_iter);
+               gtk_list_store_set (store, &tree_iter, C_SEP_COLUMN, TRUE, -1);
+
+               connections = nm_client_get_connections (priv->client);
+               for (i = 0; i < connections->len; i++) {
+                       NMConnection *candidate = NM_CONNECTION (connections->pdata[i]);
+                       NMSettingWireless *s_wireless;
+                       const char *connection_type;
+                       const char *mode;
+                       const char *setting_mac, *hw_addr;
+
+                       s_con = nm_connection_get_setting_connection (candidate);
+                       connection_type = s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;
+                       if (!connection_type)
+                               continue;
+
+                       if (strcmp (connection_type, NM_SETTING_WIRELESS_SETTING_NAME))
+                               continue;
+
+                       s_wireless = nm_connection_get_setting_wireless (candidate);
+                       if (!s_wireless)
+                               continue;
+
+                       /* If creating a new Ad-Hoc network, only show shared network connections */
+                       if (priv->operation == OP_CREATE_ADHOC) {
+                               NMSettingIPConfig *s_ip4;
+                               const char *method = NULL;
+
+                               s_ip4 = nm_connection_get_setting_ip4_config (candidate);
+                               if (s_ip4)
+                                       method = nm_setting_ip_config_get_method (s_ip4);
+
+                               if (!s_ip4 || strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+                                       continue;
+
+                               /* Ignore non-Ad-Hoc connections too */
+                               mode = nm_setting_wireless_get_mode (s_wireless);
+                               if (!mode || (strcmp (mode, "adhoc") && strcmp (mode, "ap")))
+                                       continue;
+                       }
+
+                       /* Ignore connections that don't apply to the selected device */
+                       setting_mac = nm_setting_wireless_get_mac_address (s_wireless);
+                       hw_addr = nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (priv->device));
+                       if (   setting_mac
+                           && hw_addr 
+                           && !nm_utils_hwaddr_matches (setting_mac, -1, hw_addr, -1))
+                               continue;
+
+                       to_add = g_slist_append (to_add, candidate);
+               }
+
+               /* Alphabetize the list then add the connections */
+               to_add = g_slist_sort (to_add, (GCompareFunc) alphabetize_connections);
+               for (iter = to_add; iter; iter = g_slist_next (iter)) {
+                       NMConnection *candidate = NM_CONNECTION (iter->data);
+
+                       s_con = nm_connection_get_setting_connection (candidate);
+                       gtk_list_store_append (store, &tree_iter);
+                       gtk_list_store_set (store, &tree_iter,
+                                           C_NAME_COLUMN, nm_setting_connection_get_id (s_con),
+                                           C_CON_COLUMN, candidate, -1);
+                       num_added++;
+               }
+               g_slist_free (to_add);
+       }
+
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "connection_combo"));
+
+       gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget));
+       renderer = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE);
+       gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), renderer,
+                                      "text", C_NAME_COLUMN);
+       gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (widget), 1);
+
+       gtk_combo_box_set_model (GTK_COMBO_BOX (widget), priv->connection_model);
+
+       gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (widget),
+                                             connection_combo_separator_cb,
+                                             NULL,
+                                             NULL);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+       g_signal_connect (G_OBJECT (widget), "changed",
+                         G_CALLBACK (connection_combo_changed), self);
+       if (connection || !num_added) {
+               gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (priv->builder, "connection_label")));
+               gtk_widget_hide (widget);
+       }
+       if (gtk_tree_model_get_iter_first (priv->connection_model, &tree_iter))
+               gtk_tree_model_get (priv->connection_model, &tree_iter, C_CON_COLUMN, &priv->connection, -1);
+
+       return TRUE;
+}
+
+static void
+device_combo_changed (GtkWidget *combo,
+                      gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+
+       if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
+               g_warning ("%s: no active device combo box item.", __func__);
+               return;
+       }
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+       g_object_unref (priv->device);
+       gtk_tree_model_get (model, &iter, D_DEV_COLUMN, &priv->device, -1);
+
+       if (!connection_combo_init (self, NULL)) {
+               g_warning ("Couldn't change connection combo box.");
+               return;
+       }
+
+       if (!security_combo_init (self, priv->secrets_only)) {
+               g_warning ("Couldn't change Wi-Fi security combo box.");
+               return;
+       }
+
+       security_combo_changed (priv->sec_combo, self);
+}
+
+static void
+add_device_to_model (GtkListStore *model, NMDevice *device)
+{
+       GtkTreeIter iter;
+       const char *desc;
+
+       desc = nm_device_get_description (device);
+       gtk_list_store_append (model, &iter);
+       gtk_list_store_set (model, &iter, D_NAME_COLUMN, desc, D_DEV_COLUMN, device, -1);
+}
+
+static gboolean
+can_use_device (NMDevice *device)
+{
+       /* Ignore unsupported devices */
+       if (!(nm_device_get_capabilities (device) & NM_DEVICE_CAP_NM_SUPPORTED))
+               return FALSE;
+
+       if (!NM_IS_DEVICE_WIFI (device))
+               return FALSE;
+
+       if (nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean
+device_combo_init (NMAWifiDialog *self, NMDevice *device)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       const GPtrArray *devices;
+       GtkListStore *store;
+       int i, num_added = 0;
+
+       g_return_val_if_fail (priv->device == NULL, FALSE);
+
+       store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
+       priv->device_model = GTK_TREE_MODEL (store);
+
+       if (device) {
+               if (!can_use_device (device))
+                       return FALSE;
+               add_device_to_model (store, device);
+               num_added++;
+       } else {
+               devices = nm_client_get_devices (priv->client);
+               if (!devices)
+                       return FALSE;
+
+               for (i = 0; devices && (i < devices->len); i++) {
+                       device = NM_DEVICE (g_ptr_array_index (devices, i));
+                       if (can_use_device (device)) {
+                               add_device_to_model (store, device);
+                               num_added++;
+                       }
+               }
+       }
+
+       if (num_added > 0) {
+               GtkWidget *widget;
+               GtkTreeIter iter;
+
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "device_combo"));
+               gtk_combo_box_set_model (GTK_COMBO_BOX (widget), priv->device_model);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+               g_signal_connect (G_OBJECT (widget), "changed",
+                                 G_CALLBACK (device_combo_changed), self);
+               if (num_added == 1) {
+                       gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (priv->builder, "device_label")));
+                       gtk_widget_hide (widget);
+               }
+               if (gtk_tree_model_get_iter_first (priv->device_model, &iter))
+                       gtk_tree_model_get (priv->device_model, &iter, D_DEV_COLUMN, &priv->device, -1);
+       }
+
+       return num_added > 0 ? TRUE : FALSE;
+}
+
+static gboolean
+find_proto (NMSettingWirelessSecurity *sec, const char *item)
+{
+       guint32 i;
+
+       for (i = 0; i < nm_setting_wireless_security_get_num_protos (sec); i++) {
+               if (!strcmp (item, nm_setting_wireless_security_get_proto (sec, i)))
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+static NMUtilsSecurityType
+get_default_type_for_security (NMSettingWirelessSecurity *sec,
+                               gboolean have_ap,
+                               guint32 ap_flags,
+                               guint32 dev_caps)
+{
+       const char *key_mgmt, *auth_alg;
+
+       g_return_val_if_fail (sec != NULL, NMU_SEC_NONE);
+
+       key_mgmt = nm_setting_wireless_security_get_key_mgmt (sec);
+       auth_alg = nm_setting_wireless_security_get_auth_alg (sec);
+
+       /* No IEEE 802.1x */
+       if (!strcmp (key_mgmt, "none"))
+               return NMU_SEC_STATIC_WEP;
+
+       if (   !strcmp (key_mgmt, "ieee8021x")
+           && (!have_ap || (ap_flags & NM_802_11_AP_FLAGS_PRIVACY))) {
+               if (auth_alg && !strcmp (auth_alg, "leap"))
+                       return NMU_SEC_LEAP;
+               return NMU_SEC_DYNAMIC_WEP;
+       }
+
+       if (   !strcmp (key_mgmt, "wpa-none")
+           || !strcmp (key_mgmt, "wpa-psk")) {
+               if (!have_ap || (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) {
+                       if (find_proto (sec, "rsn"))
+                               return NMU_SEC_WPA2_PSK;
+                       else if (find_proto (sec, "wpa"))
+                               return NMU_SEC_WPA_PSK;
+                       else
+                               return NMU_SEC_WPA_PSK;
+               }
+       }
+
+       if (   !strcmp (key_mgmt, "wpa-eap")
+           && (!have_ap || (ap_flags & NM_802_11_AP_FLAGS_PRIVACY))) {
+                       if (find_proto (sec, "rsn"))
+                               return NMU_SEC_WPA2_ENTERPRISE;
+                       else if (find_proto (sec, "wpa"))
+                               return NMU_SEC_WPA_ENTERPRISE;
+                       else
+                               return NMU_SEC_WPA_ENTERPRISE;
+       }
+
+       return NMU_SEC_INVALID;
+}
+
+static void
+add_security_item (NMAWifiDialog *self,
+                   WirelessSecurity *sec,
+                   GtkListStore *model,
+                   GtkTreeIter *iter,
+                   const char *text)
+{
+       wireless_security_set_changed_notify (sec, stuff_changed_cb, self);
+       gtk_list_store_append (model, iter);
+       gtk_list_store_set (model, iter, S_NAME_COLUMN, text, S_SEC_COLUMN, sec, -1);
+       wireless_security_unref (sec);
+}
+
+static void
+get_secrets_cb (GObject *object,
+                GAsyncResult *result,
+                gpointer user_data)
+{
+       GetSecretsInfo *info = user_data;
+       NMRemoteConnection *connection = NM_REMOTE_CONNECTION (object);
+       NMAWifiDialogPrivate *priv;
+       GVariant *secrets;
+       GVariantIter variant_iter;
+       const char *setting_name;
+       GVariant *setting_dict;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GError *error = NULL;
+
+       if (info->canceled)
+               goto out;
+
+       secrets = nm_remote_connection_get_secrets_finish (connection, result, &error);
+       if (error) {
+               g_warning ("%s: error getting connection secrets: (%d) %s",
+                          __func__,
+                          error ? error->code : -1,
+                          error && error->message ? error->message : "(unknown)");
+               goto out;
+       }
+
+       priv = NMA_WIFI_DIALOG_GET_PRIVATE (info->self);
+       if (priv->secrets_info == info) {
+               priv->secrets_info = NULL;
+
+               /* Buttons should only be re-enabled if this secrets response is the
+                * in-progress one.
+                */
+               gtk_dialog_set_response_sensitive (GTK_DIALOG (info->self), GTK_RESPONSE_CANCEL, TRUE);
+               gtk_dialog_set_response_sensitive (GTK_DIALOG (info->self), GTK_RESPONSE_OK, TRUE);
+       }
+
+       /* User might have changed the connection while the secrets call was in
+        * progress, so don't try to update the wrong connection with the secrets
+        * we just received.
+        */
+       if (   (priv->connection != info->connection)
+           || !secrets)
+               goto out;
+
+       /* Try to update the connection's secrets; log errors but we don't care */
+       g_variant_iter_init (&variant_iter, secrets);
+       while (g_variant_iter_next (&variant_iter, "{&s a{sv}}", &setting_name, &setting_dict)) {
+               GError *update_error = NULL;
+
+               if (!nm_connection_update_secrets (priv->connection,
+                                                  setting_name,
+                                                  setting_dict,
+                                                  &update_error)) {
+                       g_warning ("%s: error updating connection secrets: (%d) %s",
+                                  __func__,
+                                  update_error ? update_error->code : -1,
+                                  update_error && update_error->message ? update_error->message : 
"(unknown)");
+                       g_clear_error (&update_error);
+               }
+               g_variant_unref (setting_dict);
+       }
+
+       /* Update each security method's UI elements with the new secrets */
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->sec_combo));
+       if (gtk_tree_model_get_iter_first (model, &iter)) {
+               do {
+                       WirelessSecurity *sec = NULL;
+
+                       gtk_tree_model_get (model, &iter, S_SEC_COLUMN, &sec, -1);
+                       if (sec) {
+                               wireless_security_update_secrets (sec, priv->connection);
+                               wireless_security_unref (sec);
+                       }
+               } while (gtk_tree_model_iter_next (model, &iter));
+       }
+
+out:
+       g_object_unref (info->connection);
+       g_free (info);
+}
+
+static gboolean
+security_combo_init (NMAWifiDialog *self, gboolean secrets_only)
+{
+       NMAWifiDialogPrivate *priv;
+       GtkListStore *sec_model;
+       GtkTreeIter iter;
+       guint32 ap_flags = 0;
+       guint32 ap_wpa = 0;
+       guint32 ap_rsn = 0;
+       guint32 dev_caps;
+       NMSettingWirelessSecurity *wsec = NULL;
+       NMUtilsSecurityType default_type = NMU_SEC_NONE;
+       NMWepKeyType wep_type = NM_WEP_KEY_TYPE_KEY;
+       int active = -1;
+       int item = 0;
+       NMSettingWireless *s_wireless = NULL;
+       gboolean is_adhoc;
+       const char *setting_name;
+
+       g_return_val_if_fail (self != NULL, FALSE);
+
+       priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       g_return_val_if_fail (priv->device != NULL, FALSE);
+       g_return_val_if_fail (priv->sec_combo != NULL, FALSE);
+
+       is_adhoc = (priv->operation == OP_CREATE_ADHOC);
+
+       /* The security options displayed are filtered based on device
+        * capabilities, and if provided, additionally by access point capabilities.
+        * If a connection is given, that connection's options should be selected
+        * by default.
+        */
+       dev_caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (priv->device));
+       if (priv->ap != NULL) {
+               ap_flags = nm_access_point_get_flags (priv->ap);
+               ap_wpa = nm_access_point_get_wpa_flags (priv->ap);
+               ap_rsn = nm_access_point_get_rsn_flags (priv->ap);
+       }
+
+       if (priv->connection) {
+               const char *mode;
+
+               s_wireless = nm_connection_get_setting_wireless (priv->connection);
+
+               mode = nm_setting_wireless_get_mode (s_wireless);
+               if (mode && (!strcmp (mode, "adhoc") || !strcmp (mode, "ap")))
+                       is_adhoc = TRUE;
+
+               wsec = nm_connection_get_setting_wireless_security (priv->connection);
+
+               if (wsec) {
+                       default_type = get_default_type_for_security (wsec, !!priv->ap, ap_flags, dev_caps);
+                       if (default_type == NMU_SEC_STATIC_WEP)
+                               wep_type = nm_setting_wireless_security_get_wep_key_type (wsec);
+                       if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN)
+                               wep_type = NM_WEP_KEY_TYPE_KEY;
+               }
+       } else if (is_adhoc) {
+               default_type = NMU_SEC_STATIC_WEP;
+               wep_type = NM_WEP_KEY_TYPE_PASSPHRASE;
+       }
+
+       sec_model = gtk_list_store_new (2, G_TYPE_STRING, wireless_security_get_type ());
+
+       if (nm_utils_security_valid (NMU_SEC_NONE, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, ap_rsn)) 
{
+               gtk_list_store_append (sec_model, &iter);
+               gtk_list_store_set (sec_model, &iter,
+                                   S_NAME_COLUMN, C_("Wifi/wired security", "None"),
+                                   -1);
+               if (default_type == NMU_SEC_NONE)
+                       active = item;
+               item++;
+       }
+
+       /* Don't show Static WEP if both the AP and the device are capable of WPA,
+        * even though technically it's possible to have this configuration.
+        */
+       if (   nm_utils_security_valid (NMU_SEC_STATIC_WEP, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, 
ap_rsn)
+           && ((!ap_wpa && !ap_rsn) || !(dev_caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)))) {
+               WirelessSecurityWEPKey *ws_wep;
+
+               ws_wep = ws_wep_key_new (priv->connection, NM_WEP_KEY_TYPE_KEY, is_adhoc, secrets_only);
+               if (ws_wep) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_wep), sec_model,
+                                          &iter, _("WEP 40/128-bit Key (Hex or ASCII)"));
+                       if ((active < 0) && (default_type == NMU_SEC_STATIC_WEP) && (wep_type == 
NM_WEP_KEY_TYPE_KEY))
+                               active = item;
+                       item++;
+               }
+
+               ws_wep = ws_wep_key_new (priv->connection, NM_WEP_KEY_TYPE_PASSPHRASE, is_adhoc, 
secrets_only);
+               if (ws_wep) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_wep), sec_model,
+                                          &iter, _("WEP 128-bit Passphrase"));
+                       if ((active < 0) && (default_type == NMU_SEC_STATIC_WEP) && (wep_type == 
NM_WEP_KEY_TYPE_PASSPHRASE))
+                               active = item;
+                       item++;
+               }
+       }
+
+       /* Don't show LEAP if both the AP and the device are capable of WPA,
+        * even though technically it's possible to have this configuration.
+        */
+       if (   nm_utils_security_valid (NMU_SEC_LEAP, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, 
ap_rsn)
+           && ((!ap_wpa && !ap_rsn) || !(dev_caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)))) {
+               WirelessSecurityLEAP *ws_leap;
+
+               ws_leap = ws_leap_new (priv->connection, secrets_only);
+               if (ws_leap) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_leap), sec_model,
+                                          &iter, _("LEAP"));
+                       if ((active < 0) && (default_type == NMU_SEC_LEAP))
+                               active = item;
+                       item++;
+               }
+       }
+
+       if (nm_utils_security_valid (NMU_SEC_DYNAMIC_WEP, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, 
ap_rsn)) {
+               WirelessSecurityDynamicWEP *ws_dynamic_wep;
+
+               ws_dynamic_wep = ws_dynamic_wep_new (priv->connection, FALSE, secrets_only);
+               if (ws_dynamic_wep) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_dynamic_wep), sec_model,
+                                          &iter, _("Dynamic WEP (802.1x)"));
+                       if ((active < 0) && (default_type == NMU_SEC_DYNAMIC_WEP))
+                               active = item;
+                       item++;
+               }
+       }
+
+       if (   nm_utils_security_valid (NMU_SEC_WPA_PSK, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, 
ap_rsn)
+           || nm_utils_security_valid (NMU_SEC_WPA2_PSK, dev_caps, !!priv->ap, is_adhoc, ap_flags, ap_wpa, 
ap_rsn)) {
+               WirelessSecurityWPAPSK *ws_wpa_psk;
+
+               ws_wpa_psk = ws_wpa_psk_new (priv->connection, secrets_only);
+               if (ws_wpa_psk) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_wpa_psk), sec_model,
+                                          &iter, _("WPA & WPA2 Personal"));
+                       if ((active < 0) && ((default_type == NMU_SEC_WPA_PSK) || (default_type == 
NMU_SEC_WPA2_PSK)))
+                               active = item;
+                       item++;
+               }
+       }
+
+       if (   nm_utils_security_valid (NMU_SEC_WPA_ENTERPRISE, dev_caps, !!priv->ap, is_adhoc, ap_flags, 
ap_wpa, ap_rsn)
+           || nm_utils_security_valid (NMU_SEC_WPA2_ENTERPRISE, dev_caps, !!priv->ap, is_adhoc, ap_flags, 
ap_wpa, ap_rsn)) {
+               WirelessSecurityWPAEAP *ws_wpa_eap;
+
+               ws_wpa_eap = ws_wpa_eap_new (priv->connection, FALSE, secrets_only);
+               if (ws_wpa_eap) {
+                       add_security_item (self, WIRELESS_SECURITY (ws_wpa_eap), sec_model,
+                                          &iter, _("WPA & WPA2 Enterprise"));
+                       if ((active < 0) && ((default_type == NMU_SEC_WPA_ENTERPRISE) || (default_type == 
NMU_SEC_WPA2_ENTERPRISE)))
+                               active = item;
+                       item++;
+               }
+       }
+
+       gtk_combo_box_set_model (GTK_COMBO_BOX (priv->sec_combo), GTK_TREE_MODEL (sec_model));
+       gtk_combo_box_set_active (GTK_COMBO_BOX (priv->sec_combo), active < 0 ? 0 : (guint32) active);
+       g_object_unref (G_OBJECT (sec_model));
+
+       /* If the dialog was given a connection when it was created, that connection
+        * will already be populated with secrets.  If no connection was given,
+        * then we need to get any existing secrets to populate the dialog with.
+        */
+       setting_name = priv->connection ? nm_connection_need_secrets (priv->connection, NULL) : NULL;
+       if (setting_name && NM_IS_REMOTE_CONNECTION (priv->connection)) {
+               GetSecretsInfo *info;
+
+               /* Desensitize the dialog's buttons while we wait for the secrets
+                * operation to complete.
+                */
+               gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, FALSE);
+               gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_CANCEL, FALSE);
+
+               info = g_malloc0 (sizeof (GetSecretsInfo));
+               info->self = self;
+               info->connection = g_object_ref (priv->connection);
+               priv->secrets_info = info;
+
+               nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (priv->connection),
+                                                       setting_name, NULL, get_secrets_cb, info);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+revalidate (gpointer user_data)
+{
+       NMAWifiDialog *self = NMA_WIFI_DIALOG (user_data);
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+
+       priv->revalidate_id = 0;
+       security_combo_changed (priv->sec_combo, self);
+       return FALSE;
+}
+
+static gboolean
+internal_init (NMAWifiDialog *self,
+               NMConnection *specific_connection,
+               NMDevice *specific_device,
+               gboolean secrets_only)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GtkWidget *widget;
+       char *label, *icon_name = "network-wireless";
+       gboolean security_combo_focus = FALSE;
+
+       gtk_window_set_position (GTK_WINDOW (self), GTK_WIN_POS_CENTER_ALWAYS);
+       gtk_container_set_border_width (GTK_CONTAINER (self), 6);
+       gtk_window_set_default_size (GTK_WINDOW (self), 488, -1);
+       gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+
+       priv->secrets_only = secrets_only;
+       if (secrets_only)
+               icon_name = "dialog-password";
+       else
+               icon_name = "network-wireless";
+
+       gtk_window_set_icon_name (GTK_WINDOW (self), icon_name);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "image1"));
+       gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_DIALOG);
+
+       gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 2);
+
+       widget = gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+       gtk_box_set_child_packing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (self))), widget,
+                                  FALSE, TRUE, 0, GTK_PACK_END);
+
+       /* Connect/Create button */
+       if (priv->operation == OP_CREATE_ADHOC) {
+               GtkWidget *image;
+
+               widget = gtk_button_new_with_mnemonic (_("C_reate"));
+               image = gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_BUTTON);
+               gtk_button_set_image (GTK_BUTTON (widget), image);
+
+               gtk_widget_show (widget);
+               gtk_dialog_add_action_widget (GTK_DIALOG (self), widget, GTK_RESPONSE_OK);
+       } else
+               widget = gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CONNECT, GTK_RESPONSE_OK);
+
+       gtk_box_set_child_packing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (self))), widget,
+                                  FALSE, TRUE, 0, GTK_PACK_END);
+       g_object_set (G_OBJECT (widget), "can-default", TRUE, NULL);
+       gtk_widget_grab_default (widget);
+
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "hbox1"));
+       if (!widget) {
+               g_warning ("Couldn't find Wi-Fi_dialog widget.");
+               return FALSE;
+       }
+       gtk_widget_unparent (widget);
+
+       gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (self))), widget);
+
+       /* If given a valid connection, hide the SSID bits and connection combo */
+       if (specific_connection) {
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_label"));
+               gtk_widget_hide (widget);
+
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry"));
+               gtk_widget_hide (widget);
+
+               security_combo_focus = TRUE;
+               priv->network_name_focus = FALSE;
+       } else {
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry"));
+               g_signal_connect (G_OBJECT (widget), "changed", (GCallback) ssid_entry_changed, self);
+               priv->network_name_focus = TRUE;
+       }
+
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, FALSE);
+
+       if (!device_combo_init (self, specific_device)) {
+               g_warning ("No Wi-Fi devices available.");
+               return FALSE;
+       }
+
+       if (!connection_combo_init (self, specific_connection)) {
+               g_warning ("Couldn't set up connection combo box.");
+               return FALSE;
+       }
+
+       if (!security_combo_init (self, priv->secrets_only)) {
+               g_warning ("Couldn't set up Wi-Fi security combo box.");
+               return FALSE;
+       }
+
+       security_combo_changed (priv->sec_combo, self);
+       g_signal_connect (G_OBJECT (priv->sec_combo), "changed",
+                         G_CALLBACK (security_combo_changed_manually), self);
+
+       if (secrets_only) {
+               gtk_widget_hide (priv->sec_combo);
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_combo_label"));
+               gtk_widget_hide (widget);
+       }
+
+       if (security_combo_focus)
+               gtk_widget_grab_focus (priv->sec_combo);
+       else if (priv->network_name_focus) {
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "network_name_entry"));
+               gtk_widget_grab_focus (widget);
+       }
+
+       if (priv->connection) {
+               char *tmp;
+               char *esc_ssid = NULL;
+               NMSettingWireless *s_wireless;
+               GBytes *ssid;
+
+               s_wireless = nm_connection_get_setting_wireless (priv->connection);
+               ssid = s_wireless ? nm_setting_wireless_get_ssid (s_wireless) : NULL;
+               if (ssid)
+                       esc_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size 
(ssid));
+
+               tmp = g_strdup_printf (_("Passwords or encryption keys are required to access the Wi-Fi 
network '%s'."),
+                                      esc_ssid ? esc_ssid : "<unknown>");
+               gtk_window_set_title (GTK_WINDOW (self), _("Wi-Fi Network Authentication Required"));
+               label = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>\n\n%s",
+                                        _("Authentication required by Wi-Fi network"),
+                                        tmp);
+               g_free (esc_ssid);
+               g_free (tmp);
+       } else if (priv->operation == OP_CREATE_ADHOC) {
+               gtk_window_set_title (GTK_WINDOW (self), _("Create New Wi-Fi Network"));
+               label = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>\n\n%s",
+                                        _("New Wi-Fi network"),
+                                        _("Enter a name for the Wi-Fi network you wish to create."));
+       } else if (priv->operation == OP_CONNECT_HIDDEN) {
+               gtk_window_set_title (GTK_WINDOW (self), _("Connect to Hidden Wi-Fi Network"));
+               label = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>\n\n%s",
+                                        _("Hidden Wi-Fi network"),
+                                        _("Enter the name and security details of the hidden Wi-Fi network 
you wish to connect to."));
+       } else
+               g_assert_not_reached ();
+
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "caption_label"));
+       gtk_label_set_markup (GTK_LABEL (widget), label);
+       g_free (label);
+
+       /* Re-validate from an idle handler so that widgets like file choosers
+        * have had time to find their files.
+        */
+       priv->revalidate_id = g_idle_add (revalidate, self);
+
+       return TRUE;
+}
+
+/**
+ * nma_wifi_dialog_get_connection:
+ * @self: an #NMAWifiDialog
+ * @device: (out):
+ * @ap: (out):
+ *
+ * Returns: (transfer full):
+ */
+NMConnection *
+nma_wifi_dialog_get_connection (NMAWifiDialog *self,
+                                NMDevice **device,
+                                NMAccessPoint **ap)
+{
+       NMAWifiDialogPrivate *priv;
+       GtkWidget *combo;
+       GtkTreeModel *model;
+       WirelessSecurity *sec = NULL;
+       GtkTreeIter iter;
+       NMConnection *connection;
+       NMSettingWireless *s_wireless;
+
+       g_return_val_if_fail (self != NULL, NULL);
+
+       priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+
+       if (!priv->connection) {
+               NMSettingConnection *s_con;
+               char *uuid;
+
+               connection = nm_simple_connection_new ();
+
+               s_con = (NMSettingConnection *) nm_setting_connection_new ();
+               uuid = nm_utils_uuid_generate ();
+               g_object_set (s_con,
+                             NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+                             NM_SETTING_CONNECTION_UUID, uuid,
+                             NULL);
+               g_free (uuid);
+               nm_connection_add_setting (connection, (NMSetting *) s_con);
+
+               s_wireless = (NMSettingWireless *) nm_setting_wireless_new ();
+               g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, validate_dialog_ssid (self), NULL);
+
+               if (priv->operation == OP_CREATE_ADHOC) {
+                       NMSetting *s_ip4;
+
+                       g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, "adhoc", NULL);
+
+                       s_ip4 = nm_setting_ip4_config_new ();
+                       g_object_set (s_ip4,
+                                     NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED,
+                                     NULL);
+                       nm_connection_add_setting (connection, s_ip4);
+               } else if (priv->operation == OP_CONNECT_HIDDEN) {
+                       /* Mark as a hidden SSID network */
+                       g_object_set (s_wireless, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
+               } else
+                       g_assert_not_reached ();
+
+               nm_connection_add_setting (connection, (NMSetting *) s_wireless);
+       } else
+               connection = g_object_ref (priv->connection);
+
+       /* Fill security */
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->sec_combo));
+       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->sec_combo), &iter))
+               gtk_tree_model_get (model, &iter, S_SEC_COLUMN, &sec, -1);
+       if (sec) {
+               wireless_security_fill_connection (sec, connection);
+               wireless_security_unref (sec);
+       }
+
+       /* Save new CA cert ignore values to GSettings */
+       eap_method_ca_cert_ignore_save (connection);
+
+       /* Fill device */
+       if (device) {
+               combo = GTK_WIDGET (gtk_builder_get_object (priv->builder, "device_combo"));
+               gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter);
+               gtk_tree_model_get (priv->device_model, &iter, D_DEV_COLUMN, device, -1);
+               g_object_unref (*device);
+       }
+
+       if (ap)
+               *ap = priv->ap;
+
+       return connection;
+}
+
+GtkWidget *
+nma_wifi_dialog_new (NMClient *client,
+                     NMConnection *connection,
+                     NMDevice *device,
+                     NMAccessPoint *ap,
+                     gboolean secrets_only)
+{
+       NMAWifiDialog *self;
+       NMAWifiDialogPrivate *priv;
+       guint32 dev_caps;
+
+       g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+       g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+       /* Ensure device validity */
+       if (device) {
+               dev_caps = nm_device_get_capabilities (device);
+               g_return_val_if_fail (dev_caps & NM_DEVICE_CAP_NM_SUPPORTED, NULL);
+               g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+       }
+
+       self = NMA_WIFI_DIALOG (g_object_new (NMA_TYPE_WIFI_DIALOG, NULL));
+       if (self) {
+               priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+
+               priv->client = g_object_ref (client);
+               if (ap)
+                       priv->ap = g_object_ref (ap);
+
+               priv->sec_combo = GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_combo"));
+               priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+               /* Handle CA cert ignore stuff */
+               eap_method_ca_cert_ignore_load (connection);
+
+               if (!internal_init (self, connection, device, secrets_only)) {
+                       g_warning ("Couldn't create Wi-Fi security dialog.");
+                       gtk_widget_destroy (GTK_WIDGET (self));
+                       self = NULL;
+               }
+       }
+
+       return GTK_WIDGET (self);
+}
+
+static GtkWidget *
+internal_new_operation (NMClient *client,
+                        guint operation)
+{
+       NMAWifiDialog *self;
+       NMAWifiDialogPrivate *priv;
+
+       g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+       self = NMA_WIFI_DIALOG (g_object_new (NMA_TYPE_WIFI_DIALOG, NULL));
+       if (!self)
+               return NULL;
+
+       priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+
+       priv->client = g_object_ref (client);
+       priv->sec_combo = GTK_WIDGET (gtk_builder_get_object (priv->builder, "security_combo"));
+       priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+       priv->operation = operation;
+
+       if (!internal_init (self, NULL, NULL, FALSE)) {
+               g_warning ("Couldn't create Wi-Fi security dialog.");
+               gtk_widget_destroy (GTK_WIDGET (self));
+               return NULL;
+       }
+
+       return GTK_WIDGET (self);
+}
+
+GtkWidget *
+nma_wifi_dialog_new_for_hidden (NMClient *client)
+{
+       return internal_new_operation (client, OP_CONNECT_HIDDEN);
+}
+
+GtkWidget *
+nma_wifi_dialog_new_for_other (NMClient *client)
+{
+       return internal_new_operation (client, OP_CONNECT_HIDDEN);
+}
+
+GtkWidget *
+nma_wifi_dialog_new_for_create (NMClient *client)
+{
+       return internal_new_operation (client, OP_CREATE_ADHOC);
+}
+
+/**
+ * nma_wifi_dialog_nag_user:
+ * @self:
+ *
+ * Returns: (transfer full):
+ */
+GtkWidget *
+nma_wifi_dialog_nag_user (NMAWifiDialog *self)
+{
+       return NULL;
+}
+
+static void
+nma_wifi_dialog_init (NMAWifiDialog *self)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (self);
+       GError *error = NULL;
+
+       priv->builder = gtk_builder_new ();
+
+       if (!gtk_builder_add_from_file (priv->builder, UIDIR "/wifi.ui", &error)) {
+               g_warning ("Couldn't load builder file: %s", error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+dispose (GObject *object)
+{
+       NMAWifiDialogPrivate *priv = NMA_WIFI_DIALOG_GET_PRIVATE (object);
+
+       if (priv->disposed) {
+               G_OBJECT_CLASS (nma_wifi_dialog_parent_class)->dispose (object);
+               return;
+       }
+
+       priv->disposed = TRUE;
+
+       if (priv->secrets_info)
+               priv->secrets_info->canceled = TRUE;
+
+       g_object_unref (priv->client);
+       g_object_unref (priv->builder);
+
+       g_object_unref (priv->device_model);
+       g_object_unref (priv->connection_model);
+
+       if (priv->group)
+               g_object_unref (priv->group);
+
+       if (priv->connection)
+               g_object_unref (priv->connection);
+
+       if (priv->device)
+               g_object_unref (priv->device);
+
+       if (priv->ap)
+               g_object_unref (priv->ap);
+
+       if (priv->revalidate_id)
+               g_source_remove (priv->revalidate_id);
+
+       G_OBJECT_CLASS (nma_wifi_dialog_parent_class)->dispose (object);
+}
+
+static void
+nma_wifi_dialog_class_init (NMAWifiDialogClass *nmad_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (nmad_class);
+
+       g_type_class_add_private (nmad_class, sizeof (NMAWifiDialogPrivate));
+
+       /* virtual methods */
+       object_class->dispose = dispose;
+}
diff --git a/src/libnma/nma-wifi-dialog.h b/src/libnma/nma-wifi-dialog.h
new file mode 100644
index 0000000..6f02bfc
--- /dev/null
+++ b/src/libnma/nma-wifi-dialog.h
@@ -0,0 +1,80 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+
+/* WARNING: this file is private API between nm-applet and various GNOME
+ * bits; it may change without notice and is not guaranteed to be stable.
+ */
+
+#ifndef NMA_WIFI_DIALOG_H
+#define NMA_WIFI_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <NetworkManager.h>
+
+#define NMA_TYPE_WIFI_DIALOG            (nma_wifi_dialog_get_type ())
+#define NMA_WIFI_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMA_TYPE_WIFI_DIALOG, 
NMAWifiDialog))
+#define NMA_WIFI_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMA_TYPE_WIFI_DIALOG, 
NMAWifiDialogClass))
+#define NMA_IS_WIFI_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMA_TYPE_WIFI_DIALOG))
+#define NMA_IS_WIFI_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMA_TYPE_WIFI_DIALOG))
+#define NMA_WIFI_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMA_TYPE_WIFI_DIALOG, 
NMAWifiDialogClass))
+
+typedef struct {
+       GtkDialog parent;
+} NMAWifiDialog;
+
+typedef struct {
+       GtkDialogClass parent;
+} NMAWifiDialogClass;
+
+GType nma_wifi_dialog_get_type (void);
+
+GtkWidget *nma_wifi_dialog_new (NMClient *client,
+                                NMConnection *connection,
+                                NMDevice *device,
+                                NMAccessPoint *ap,
+                                gboolean secrets_only);
+
+GtkWidget *nma_wifi_dialog_new_for_hidden (NMClient *client);
+
+GtkWidget *nma_wifi_dialog_new_for_create (NMClient *client);
+
+NMConnection * nma_wifi_dialog_get_connection (NMAWifiDialog *self,
+                                               NMDevice **device,
+                                               NMAccessPoint **ap);
+
+GLIB_DEPRECATED
+GtkWidget * nma_wifi_dialog_nag_user (NMAWifiDialog *self);
+
+GLIB_DEPRECATED
+void nma_wifi_dialog_set_nag_ignored (NMAWifiDialog *self, gboolean ignored);
+
+GLIB_DEPRECATED
+gboolean nma_wifi_dialog_get_nag_ignored (NMAWifiDialog *self);
+
+GLIB_DEPRECATED_FOR(nma_wifi_dialog_new_for_hidden)
+GtkWidget *nma_wifi_dialog_new_for_other (NMClient *client);
+
+#endif /* NMA_WIFI_DIALOG_H */
+
diff --git a/src/libnma/wifi.ui b/src/libnma/wifi.ui
new file mode 100644
index 0000000..7b96f88
--- /dev/null
+++ b/src/libnma/wifi.ui
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.16.1 -->
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <object class="GtkListStore" id="model1">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes"> </col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="model2">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes"> </col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkHBox" id="hbox1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="spacing">12</property>
+    <child>
+      <object class="GtkImage" id="image1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="yalign">0</property>
+        <property name="icon_name">network-wireless</property>
+        <property name="icon_size">6</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="caption_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="use_markup">True</property>
+            <property name="wrap">True</property>
+            <property name="max_width_chars">50</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="n_rows">5</property>
+            <property name="n_columns">2</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">6</property>
+            <child>
+              <object class="GtkVBox" id="security_vbox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="right_attach">2</property>
+                <property name="top_attach">4</property>
+                <property name="bottom_attach">5</property>
+                <property name="x_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="security_combo_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Wi-Fi _security:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">security_combo</property>
+              </object>
+              <packing>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="security_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="model">model1</property>
+                <child>
+                  <object class="GtkCellRendererText" id="renderer1"/>
+                  <attributes>
+                    <attribute name="text">0</attribute>
+                  </attributes>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="network_name_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Network name:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">network_name_entry</property>
+              </object>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="network_name_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="activates_default">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="connection_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">C_onnection:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">connection_combo</property>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="connection_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="device_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Wi-Fi _adapter:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">device_combo</property>
+              </object>
+              <packing>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="device_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="model">model2</property>
+                <child>
+                  <object class="GtkCellRendererText" id="renderer2"/>
+                  <attributes>
+                    <attribute name="text">0</attribute>
+                  </attributes>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
+</interface>


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