[network-manager-applet/rm-userset] applet: implement agent API for secrets handling



commit 203d74540d39682761328e7e75ab96c517e84f96
Author: Dan Williams <dcbw redhat com>
Date:   Tue Jan 18 17:20:32 2011 -0600

    applet: implement agent API for secrets handling
    
    Still a number of issues to work out, but it compiles.

 src/Makefile.am                   |    4 +-
 src/applet-agent.c                |  563 ++++++++++++++++++++++++++++++++++++-
 src/applet-agent.h                |   20 ++
 src/applet-device-bt.c            |  148 +++-------
 src/applet-device-cdma.c          |  131 +++------
 src/applet-device-gsm.c           |  243 +++++------------
 src/applet-device-wifi.c          |  104 ++------
 src/applet-device-wired.c         |  371 +++++++-----------------
 src/applet-dialogs.c              |   10 +-
 src/applet-dialogs.h              |    3 +-
 src/applet-vpn-request.c          |  377 +++++++++++++++++++++++++
 src/applet-vpn-request.h          |   58 ++++
 src/applet.c                      |  366 +++++++++++++++++++-----
 src/applet.h                      |   46 +++-
 src/gconf-helpers/gconf-helpers.c |   24 +--
 src/marshallers/nma-marshal.list  |    3 +-
 src/utils/utils.c                 |   45 +++-
 src/utils/utils.h                 |   14 +-
 src/vpn-password-dialog.c         |  334 ----------------------
 src/vpn-password-dialog.h         |   35 ---
 src/wired-dialog.c                |   19 +-
 src/wired-dialog.h                |   12 +-
 22 files changed, 1699 insertions(+), 1231 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 5c8e043..18cea49 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -27,8 +27,8 @@ nm_applet_SOURCES = \
 	applet.h \
 	applet-agent.c \
 	applet-agent.h \
-	vpn-password-dialog.c \
-	vpn-password-dialog.h \
+	applet-vpn-request.c \
+	applet-vpn-request.h \
 	wired-dialog.h \
 	wired-dialog.c \
 	wireless-dialog.h \
diff --git a/src/applet-agent.c b/src/applet-agent.c
index c95d220..e342e5f 100644
--- a/src/applet-agent.c
+++ b/src/applet-agent.c
@@ -25,38 +25,507 @@
 
 #include <glib/gi18n.h>
 #include <string.h>
+#include <gnome-keyring.h>
+#include <dbus/dbus-glib.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-8021x.h>
+#include <nm-setting-vpn.h>
 
 #include "applet-agent.h"
+#include "utils.h"
+#include "nma-marshal.h"
+#include "gconf-helpers.h"
 
 G_DEFINE_TYPE (AppletAgent, applet_agent, NM_TYPE_SECRET_AGENT);
 
 #define APPLET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APPLET_TYPE_AGENT, AppletAgentPrivate))
 
 typedef struct {
-
+	GHashTable *requests;
 
 	gboolean disposed;
 } AppletAgentPrivate;
 
+enum {
+	GET_SECRETS,
+	CANCEL_SECRETS,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+/*******************************************************/
+
+static const char *applet_8021x_cert_keys[] = {
+	"ca-cert",
+	"client-cert",
+	"private-key",
+	"phase2-ca-cert",
+	"phase2-client-cert",
+	"phase2-private-key",
+	NULL
+};
+
+static gboolean
+string_in_list (const char *str, const char **valid_strings)
+{
+	int i;
+
+	for (i = 0; valid_strings[i]; i++) {
+		if (!g_strcmp0 (str, valid_strings[i]))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+/*******************************************************/
+
+#define DBUS_TYPE_G_MAP_OF_STRING (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING))
+
+typedef struct {
+	guint id;
+
+	NMSecretAgent *agent;
+	NMConnection *connection;
+	char *path;
+	char *setting_name;
+	char **hints;
+	guint32 flags;
+	NMSecretAgentGetSecretsFunc get_callback;
+	NMSecretAgentSaveSecretsFunc save_callback;
+	NMSecretAgentDeleteSecretsFunc delete_callback;
+	gpointer callback_data;
+
+	gpointer keyring_id;
+	guint32 op_count;
+	gboolean canceled;
+} Request;
+
+static Request *
+request_new (NMSecretAgent *agent,
+             NMConnection *connection,
+             const char *connection_path,
+             const char *setting_name,
+             const char **hints,
+             guint32 flags,
+             NMSecretAgentGetSecretsFunc get_callback,
+             NMSecretAgentSaveSecretsFunc save_callback,
+             NMSecretAgentDeleteSecretsFunc delete_callback,
+             gpointer callback_data)
+{
+	static guint32 counter = 1;
+	Request *r;
+
+	r = g_slice_new0 (Request);
+	r->id = counter++;
+	r->connection = g_object_ref (connection);
+	r->path = g_strdup (connection_path);
+	r->setting_name = g_strdup (setting_name);
+	if (hints)
+		r->hints = g_strdupv ((gchar **) hints);
+	r->flags = flags;
+	r->get_callback = get_callback;
+	r->save_callback = save_callback;
+	r->delete_callback = delete_callback;
+	r->callback_data = callback_data;
+	return r;
+}
+
+static void
+request_free (Request *r)
+{
+	if (r->canceled == FALSE)
+		g_hash_table_remove (APPLET_AGENT_GET_PRIVATE (r->agent)->requests, GUINT_TO_POINTER (r->id));
+
+	g_object_unref (r->connection);
+	g_free (r->path);
+	g_free (r->setting_name);
+	g_strfreev (r->hints);
+	memset (r, 0, sizeof (*r));
+	g_slice_free (Request, r);
+}
+
 /*******************************************************/
 
 static void
+get_secrets_cb (AppletAgent *self,
+                GHashTable *secrets,
+                GError *error,
+                gpointer user_data)
+{
+	Request *r = user_data;
+
+	if (r->canceled == FALSE)
+		r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, error ? NULL : secrets, error, r->callback_data);
+	request_free (r);
+}
+
+static void
+ask_for_secrets (Request *r)
+{
+	/* FIXME: clear the actual NMRemoteConnection matching the requests' NMConnection */
+	nm_connection_clear_secrets (r->connection);
+
+	/* Ask the applet to get some secrets for us */
+	g_signal_emit (r->agent,
+	               signals[GET_SECRETS],
+	               0,
+	               r->connection,
+	               r->setting_name,
+	               r->hints,
+	               r->flags,
+	               get_secrets_cb,
+	               r);
+}
+
+static gboolean
+is_otp_always_ask (NMConnection *connection)
+{
+	NMSetting8021x *s_8021x;
+	NMSettingConnection *s_con;
+	const char *uuid, *eap_method, *phase2;
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	if (s_8021x) {
+		gboolean can_always_ask = FALSE;
+
+		/* Check if PEAP or TTLS is used */
+		eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		if (!strcmp (eap_method, "peap"))
+			can_always_ask = TRUE;
+		else if (!strcmp (eap_method, "ttls")) {
+			/* Now make sure the phase2 method isn't TLS */
+			phase2 = nm_setting_802_1x_get_phase2_auth (s_8021x);
+			if (phase2 && strcmp (phase2, "tls"))
+				can_always_ask = TRUE;
+			else {
+				phase2 = nm_setting_802_1x_get_phase2_autheap (s_8021x);
+				if (phase2 && strcmp (phase2, "tls"))
+					can_always_ask = TRUE;
+			}
+		}
+
+		if (can_always_ask) {
+			uuid = nm_setting_connection_get_uuid (s_con);
+			if (nm_gconf_get_8021x_password_always_ask (uuid))
+				return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static GValue *
+string_to_gvalue (const char *str)
+{
+	GValue *val;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, str);
+	return val;
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+	g_value_unset ((GValue *) data);
+	g_slice_free (GValue, data);
+}
+
+static void
+keyring_find_secrets_cb (GnomeKeyringResult result,
+                         GList *list,
+                         gpointer user_data)
+{
+	Request *r = user_data;
+	GError *error = NULL;
+	NMSettingConnection *s_con;
+	const char *connection_id = NULL;
+	GHashTable *secrets = NULL, *settings = NULL;
+	GList *iter;
+	gboolean hint_found = FALSE;
+
+	if (r->canceled) {
+		/* Callback already called by cancelation handler */
+		request_free (r);
+		return;
+	}
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (r->connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	connection_id = nm_setting_connection_get_id (s_con);
+
+	if (result == GNOME_KEYRING_RESULT_CANCELLED) {
+		error = g_error_new_literal (NM_SECRET_AGENT_ERROR,
+		                             NM_SECRET_AGENT_ERROR_USER_CANCELED,
+		                             "The secrets request was canceled by the user");
+		goto done;
+	} else if (result != GNOME_KEYRING_RESULT_OK) {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		                     "%s.%d - failed to read secrets from keyring (result %d)",
+		                     __FILE__, __LINE__, result);
+		goto done;
+	}
+
+	if (g_list_length (list) == 0) {
+		g_message ("No keyring secrets found for %s/%s; asking user.", connection_id, r->setting_name);
+		ask_for_secrets (r);
+		return;
+	}
+
+	secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+
+	/* Extract the secrets from the list of matching keyring items */
+	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
+		GnomeKeyringFound *found = iter->data;
+		GnomeKeyringAttribute *attr;
+		const char *key_name = NULL;
+		int i;
+
+		for (i = 0; i < found->attributes->len; i++) {
+			attr = &(gnome_keyring_attribute_list_index (found->attributes, i));
+			if (   (strcmp (attr->name, KEYRING_SK_TAG) == 0)
+			    && (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)) {
+
+				key_name = attr->value.string;
+				g_hash_table_insert (secrets, g_strdup (key_name), string_to_gvalue (found->secret));
+
+				/* See if this property matches a given hint */
+				if (r->hints && r->hints[0]) {
+					if (!g_strcmp0 (r->hints[0], key_name) || !g_strcmp0 (r->hints[1], key_name))
+						hint_found = TRUE;
+				}
+				break;
+			}
+		}
+	}
+
+	/* If there were hints, and none of the hints were returned by the keyring,
+	 * get some new secrets.
+	 */
+	if (r->hints && r->hints[0] && !hint_found) {
+		g_hash_table_destroy (secrets);
+		ask_for_secrets (r);
+		return;
+	}
+
+	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
+	 * will contain all the individual settings hashes.
+	 */
+	settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
+	g_hash_table_insert (settings, g_strdup (r->setting_name), secrets);
+
+done:
+	r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, error ? NULL : settings, error, r->callback_data);
+	request_free (r);
+
+	if (settings)
+		g_hash_table_destroy (settings);
+	g_clear_error (&error);
+}
+
+static void
 get_secrets (NMSecretAgent *agent,
              NMConnection *connection,
              const char *connection_path,
              const char *setting_name,
              const char **hints,
-             gboolean request_new,
+             guint32 flags,
              NMSecretAgentGetSecretsFunc callback,
              gpointer callback_data)
 {
+	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
+	Request *r;
+	GError *error = NULL;
+	NMSettingConnection *s_con;
+	NMSetting *setting;
+	const char *id;
+	const char *ctype;
+	gboolean ask = FALSE;
+
+	setting = nm_connection_get_setting_by_name (connection, setting_name);
+	if (!setting) {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+		                     "%s.%d - Connection didn't have requested setting '%s'.",
+		                     __FILE__, __LINE__, setting_name);
+		callback (agent, connection, NULL, error, callback_data);
+		g_error_free (error);
+		return;
+	}
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	id = nm_setting_connection_get_id (s_con);
+	ctype = nm_setting_connection_get_connection_type (s_con);
+
+	if (!s_con || !id || !strlen (id) || !ctype) {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+		                     "%s.%d - Connection didn't have required '"
+		                     NM_SETTING_CONNECTION_SETTING_NAME
+		                     "' setting , or the connection name was invalid.",
+		                     __FILE__, __LINE__);
+		callback (agent, connection, NULL, error, callback_data);
+		g_error_free (error);
+		return;
+	}
+
+	/* Track the secrets request */
+	r = request_new (agent, connection, connection_path, setting_name, hints, flags, callback, NULL, NULL, callback_data);
+	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
+
+	/* VPN passwords are handled by the VPN plugin's auth dialog */
+	if (!strcmp (ctype, NM_SETTING_VPN_SETTING_NAME))
+		ask = TRUE;
+	else if (flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) {
+		ask = TRUE;
+		g_message ("New secrets for %s/%s requested; ask the user", id, setting_name);
+	} else if ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) && is_otp_always_ask (connection))
+		ask = TRUE;
+
+	/* VPN passwords are handled by the VPN plugin's auth dialog; and we always
+	 * get secrets for OTP connections marked as 'always ask'.
+	 */
+	if (ask)
+		ask_for_secrets (r);
+	else {
+		r->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+		                                           keyring_find_secrets_cb,
+		                                           r,
+		                                           NULL,
+		                                           KEYRING_UUID_TAG,
+		                                           GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+		                                           nm_setting_connection_get_uuid (s_con),
+		                                           KEYRING_SN_TAG,
+		                                           GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+		                                           setting_name,
+		                                           NULL);
+	}
 }
 
+/*******************************************************/
+
 static void
 cancel_get_secrets (NMSecretAgent *agent,
                     const char *connection_path,
                     const char *setting_name)
 {
+	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
+	GHashTableIter iter;
+	gpointer data;
+	GError *error;
+
+	error = g_error_new_literal (NM_SECRET_AGENT_ERROR,
+	                             NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+	                             "Canceled by NetworkManager");
+
+	g_hash_table_iter_init (&iter, priv->requests);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		Request *r = data;
+
+		if (r->get_callback == NULL)
+			continue;
+
+		/* Cancel any matching GetSecrets call */
+		if (!g_strcmp0 (r->path, connection_path) && !g_strcmp0 (r->setting_name, setting_name)) {
+			r->canceled = TRUE;
+			if (r->keyring_id) {
+				gnome_keyring_cancel_request (r->keyring_id);
+				r->keyring_id = NULL;
+			}
+			r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, NULL, error, r->callback_data);
+			g_hash_table_remove (priv->requests, GUINT_TO_POINTER (r->id));
+		}
+	}
+
+	g_error_free (error);
+}
+
+/*******************************************************/
+
+static void
+save_secret_cb (GnomeKeyringResult result, guint val, gpointer user_data)
+{
+	Request *r = user_data;
+
+	/* Only call the SaveSecrets callback and free the request when all the
+	 * secrets have been saved to the keyring.
+	 */
+	r->op_count--;
+	if (r->op_count == 0) {
+		r->save_callback (NM_SECRET_AGENT (r->agent), r->connection, NULL, r->callback_data);
+		request_free (r);
+	}
+}
+
+static void
+write_one_secret_to_keyring (NMSetting *setting,
+                             const char *key,
+                             const GValue *value,
+                             GParamFlags flags,
+                             gpointer user_data)
+{
+	Request *r = user_data;
+	GType type = G_VALUE_TYPE (value);
+	const char *secret;
+	const char *setting_name;
+	GnomeKeyringAttributeList *attrs;
+	char *display_name = NULL;
+
+	/* non-secrets and private key paths don't get stored in the keyring */
+	if (   !(flags & NM_SETTING_PARAM_SECRET)
+	    || (NM_IS_SETTING_802_1X (setting) && string_in_list (key, applet_8021x_cert_keys)))
+		return;
+
+	/* VPN secrets are handled by the VPN plugins */
+	if (   (type == DBUS_TYPE_G_MAP_OF_STRING)
+	    && NM_IS_SETTING_VPN (setting)
+	    && !strcmp (key, NM_SETTING_VPN_SECRETS))
+		return;
+
+	setting_name = nm_setting_get_name (setting);
+	if (type != G_TYPE_STRING) {
+		g_warning ("Unhandled setting secret type (write) '%s/%s' : '%s'", 
+				 setting_name, key, g_type_name (type));
+		return;
+	}
+
+	secret = g_value_get_string (value);
+	if (!secret || !strlen (secret))
+		return;
+		
+	attrs = utils_create_keyring_add_attr_list (r->connection, NULL, NULL,
+	                                            setting_name,
+	                                            key,
+	                                            &display_name);
+	g_assert (attrs);
+	r->keyring_id = gnome_keyring_item_create (NULL,
+	                                           GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                           display_name,
+	                                           attrs,
+	                                           secret,
+	                                           TRUE,
+	                                           save_secret_cb,
+	                                           r,
+	                                           NULL);
+	r->op_count++;
+	gnome_keyring_attribute_list_free (attrs);
+	g_free (display_name);
+}
+
+static void
+save_delete_cb (NMSecretAgent *agent,
+                NMConnection *connection,
+                GError *error,
+                gpointer user_data)
+{
+	Request *r = user_data;
+
+	/* Ignore errors; now save all new secrets */
+	nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
 }
 
 static void
@@ -66,6 +535,40 @@ save_secrets (NMSecretAgent *agent,
               NMSecretAgentSaveSecretsFunc callback,
               gpointer callback_data)
 {
+	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
+	Request *r;
+
+	r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, callback, NULL, callback_data);
+	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
+
+	/* First delete any existing items in the keyring */
+	nm_secret_agent_delete_secrets (agent, connection, save_delete_cb, r);
+}
+
+/*******************************************************/
+
+static void
+delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer data)
+{
+	Request *r = data;
+	GList *iter;
+	GError *error = NULL;
+
+	if (result == GNOME_KEYRING_RESULT_OK) {
+		for (iter = list; iter != NULL; iter = g_list_next (iter)) {
+			GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
+
+			gnome_keyring_item_delete (found->keyring, found->item_id, NULL, NULL, NULL);
+		}
+	} else {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		                     "The request could not be completed.  Keyring result: %d",
+		                     result);
+	}
+
+	r->delete_callback (r->agent, r->connection, error, r->callback_data);
+	request_free (r);
 }
 
 static void
@@ -75,6 +578,27 @@ delete_secrets (NMSecretAgent *agent,
                 NMSecretAgentDeleteSecretsFunc callback,
                 gpointer callback_data)
 {
+	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
+	Request *r;
+	NMSettingConnection *s_con;
+	const char *uuid;
+
+	r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, NULL, callback, callback_data);
+	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	uuid = nm_setting_connection_get_uuid (s_con);
+	g_assert (uuid);
+
+	r->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                                           delete_find_items_cb,
+	                                           r,
+	                                           NULL,
+	                                           KEYRING_UUID_TAG,
+	                                           GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+	                                           uuid,
+	                                           NULL);
 }
 
 /*******************************************************/
@@ -90,6 +614,9 @@ applet_agent_new (void)
 static void
 applet_agent_init (AppletAgent *self)
 {
+	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self);
+
+	priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -99,6 +626,18 @@ dispose (GObject *object)
 	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self);
 
 	if (!priv->disposed) {
+		GHashTableIter iter;
+		gpointer data;
+
+		/* Mark any outstanding requests as canceled */
+		g_hash_table_iter_init (&iter, priv->requests);
+		while (g_hash_table_iter_next (&iter, NULL, &data)) {
+			Request *r = data;
+
+			r->canceled = TRUE;
+		}
+
+		g_hash_table_destroy (priv->requests);
 		priv->disposed = TRUE;
 	}
 
@@ -119,5 +658,25 @@ applet_agent_class_init (AppletAgentClass *agent_class)
 	parent_class->cancel_get_secrets = cancel_get_secrets;
 	parent_class->save_secrets = save_secrets;
 	parent_class->delete_secrets = delete_secrets;
+
+	/* Signals */
+	signals[GET_SECRETS] =
+		g_signal_new (APPLET_AGENT_GET_SECRETS,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (AppletAgentClass, get_secrets),
+		              NULL, NULL,
+		              nma_marshal_VOID__POINTER_POINTER_STRING_POINTER_BOOLEAN_POINTER_POINTER,
+		              G_TYPE_NONE, 7,
+		              G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
+
+	signals[CANCEL_SECRETS] =
+		g_signal_new (APPLET_AGENT_CANCEL_SECRETS,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (AppletAgentClass, cancel_secrets),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
 }
 
diff --git a/src/applet-agent.h b/src/applet-agent.h
index 5b29417..6e8af97 100644
--- a/src/applet-agent.h
+++ b/src/applet-agent.h
@@ -31,12 +31,32 @@
 #define APPLET_IS_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APPLET_TYPE_AGENT))
 #define APPLET_AGENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), APPLET_TYPE_AGENT, AppletAgentClass))
 
+#define APPLET_AGENT_GET_SECRETS "get-secrets"
+#define APPLET_AGENT_CANCEL_SECRETS "cancel-secrets"
+
 typedef struct {
 	NMSecretAgent parent;
 } AppletAgent;
 
+typedef void (*AppletAgentSecretsCallback) (AppletAgent *self,
+                                            GHashTable *secrets,
+                                            GError *error,
+                                            gpointer user_data);
+
 typedef struct {
 	NMSecretAgentClass parent_class;
+
+	void (*get_secrets)        (AppletAgent *self,
+	                            void *request_id,
+	                            NMConnection *connection,
+	                            const char *setting_name,
+	                            const char **hints,
+	                            guint32 flags,
+	                            AppletAgentSecretsCallback callback,
+	                            gpointer callback_data);
+
+	void (*cancel_secrets)     (AppletAgent *self,
+	                            void *request_id);
 } AppletAgentClass;
 
 
diff --git a/src/applet-device-bt.c b/src/applet-device-bt.c
index b5bbc5e..4e0caee 100644
--- a/src/applet-device-bt.c
+++ b/src/applet-device-bt.c
@@ -252,29 +252,22 @@ bt_get_icon (NMDevice *device,
 }
 
 typedef struct {
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
-	NMApplet *applet;
-	NMRemoteConnection *connection;
-	NMActiveConnection *active_connection;
+	SecretsRequest req;
 	GtkWidget *dialog;
 	GtkEntry *secret_entry;
 	char *secret_name;
-	char *setting_name;
 } NMBtSecretsInfo;
 
 static void
-destroy_secrets_dialog (gpointer user_data, GObject *finalized)
+free_bt_secrets_info (SecretsRequest *req)
 {
-	NMBtSecretsInfo *info = user_data;
-
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
+	NMBtSecretsInfo *info = (NMBtSecretsInfo *) req;
 
-	g_object_unref (info->connection);
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
+	}
 	g_free (info->secret_name);
-	g_free (info->setting_name);
-	g_free (info);
 }
 
 static void
@@ -282,92 +275,55 @@ get_bt_secrets_cb (GtkDialog *dialog,
                    gint response,
                    gpointer user_data)
 {
-	NMBtSecretsInfo *info = user_data;
+	SecretsRequest *req = user_data;
+	NMBtSecretsInfo *info = (NMBtSecretsInfo *) req;
 	NMSetting *setting;
-	GHashTable *settings_hash = NULL;
-	GHashTable *secrets;
 	GError *error = NULL;
 
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_secrets_dialog, info);
-
-	if (response != GTK_RESPONSE_OK) {
+	if (response == GTK_RESPONSE_OK) {
+		setting = nm_connection_get_setting_by_name (req->connection, req->setting_name);
+		if (setting) {
+			/* Normally we'd want to get all the settings's secrets and return those
+			 * to NM too (since NM wants them), but since the only other secrets for 3G
+			 * connections are PINs, and since the phone obviously has to be unlocked
+			 * to even make the Bluetooth connection, we can skip doing that here for
+			 * Bluetooth devices.
+			 */
+
+			/* Update the password */
+			g_object_set (G_OBJECT (setting),
+					      info->secret_name, gtk_entry_get_text (info->secret_entry),
+					      NULL);
+		} else {
+			g_set_error (&error,
+				         NM_SECRET_AGENT_ERROR,
+				         NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+				         "%s.%d (%s): unhandled setting '%s'",
+				         __FILE__, __LINE__, __func__, req->setting_name);
+		}
+	} else {
 		g_set_error (&error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): canceled",
 		             __FILE__, __LINE__, __func__);
-		goto done;
-	}
-
-	setting = nm_connection_get_setting_by_name (NM_CONNECTION (info->connection), info->setting_name);
-	if (!setting) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): unhandled setting '%s'",
-		             __FILE__, __LINE__, __func__, info->setting_name);
-		goto done;
-	}
-
-	/* Normally we'd want to get all the settings's secrets and return those
-	 * to NM too (since NM wants them), but since the only other secrets for 3G
-	 * connections are PINs, and since the phone obviously has to be unlocked
-	 * to even make the Bluetooth connection, we can skip doing that here for
-	 * Bluetooth devices.
-	 */
-
-	/* Update the password */
-	g_object_set (G_OBJECT (setting),
-	              info->secret_name, gtk_entry_get_text (info->secret_entry),
-	              NULL);
-
-	secrets = nm_setting_to_hash (NM_SETTING (setting));
-	if (!secrets) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): failed to hash setting '%s'.",
-		             __FILE__, __LINE__, __func__,
-		             nm_setting_get_name (NM_SETTING (setting)));
-		goto done;
 	}
 
-	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
-	 * will contain all the individual settings hashes.
-	 */
-	settings_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-	                                       g_free, (GDestroyNotify) g_hash_table_destroy);
-	g_hash_table_insert (settings_hash, g_strdup (nm_setting_get_name (NM_SETTING (setting))), secrets);
-
- done:
-	info->callback (info->connection, settings_hash, error, info->callback_data);
-	if (settings_hash)
-		g_hash_table_destroy (settings_hash);
+	applet_secrets_request_complete_setting (req, req->setting_name, error);
+	applet_secrets_request_free (req);
 	g_clear_error (&error);
-
-	nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-	destroy_secrets_dialog (info, NULL);
 }
 
 static gboolean
-bt_get_secrets (NMDevice *device,
-                NMRemoteConnection *connection,
-                NMActiveConnection *active_connection,
-                const char *setting_name,
-                const char **hints,
-                NMANewSecretsRequestedFunc callback,
-                gpointer callback_data,
-                NMApplet *applet,
-                GError **error)
+bt_get_secrets (SecretsRequest *req, GError **error)
 {
-	NMBtSecretsInfo *info;
+	NMBtSecretsInfo *info = (NMBtSecretsInfo *) req;
 	GtkWidget *widget;
 	GtkEntry *secret_entry = NULL;
 
-	if (!hints || !g_strv_length ((char **) hints)) {
+	applet_secrets_request_set_free_func (req, free_bt_secrets_info);
+
+	if (!req->hints || !g_strv_length (req->hints)) {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
@@ -375,18 +331,21 @@ bt_get_secrets (NMDevice *device,
 		             __FILE__, __LINE__, __func__);
 		return FALSE;
 	}
+	info->secret_name = g_strdup (req->hints[0]);
 
-	if (   (!strcmp (setting_name, NM_SETTING_CDMA_SETTING_NAME) && !strcmp (hints[0], NM_SETTING_CDMA_PASSWORD))
-	    || (!strcmp (setting_name, NM_SETTING_GSM_SETTING_NAME) && !strcmp (hints[0], NM_SETTING_GSM_PASSWORD)))
-		widget = applet_mobile_password_dialog_new (device, NM_CONNECTION (connection), &secret_entry);
+	if (   (!strcmp (req->setting_name, NM_SETTING_CDMA_SETTING_NAME) && !strcmp (info->secret_name, NM_SETTING_CDMA_PASSWORD))
+	    || (!strcmp (req->setting_name, NM_SETTING_GSM_SETTING_NAME) && !strcmp (info->secret_name, NM_SETTING_GSM_PASSWORD)))
+		widget = applet_mobile_password_dialog_new (req->connection, &secret_entry);
 	else {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): unknown secrets hint '%s'.",
-		             __FILE__, __LINE__, __func__, hints[0]);
+		             __FILE__, __LINE__, __func__, info->secret_name);
 		return FALSE;
 	}
+	info->dialog = widget;
+	info->secret_entry = secret_entry;
 
 	if (!widget || !secret_entry) {
 		g_set_error (error,
@@ -397,24 +356,8 @@ bt_get_secrets (NMDevice *device,
 		return FALSE;
 	}
 
-	info = g_malloc0 (sizeof (NMBtSecretsInfo));
-	info->callback = callback;
-	info->callback_data = callback_data;
-	info->applet = applet;
-	info->active_connection = active_connection;
-	info->connection = g_object_ref (connection);
-	info->secret_name = g_strdup (hints[0]);
-	info->setting_name = g_strdup (setting_name);
-	info->dialog = widget;
-	info->secret_entry = secret_entry;
-
 	g_signal_connect (widget, "response", G_CALLBACK (get_bt_secrets_cb), info);
 
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_secrets_dialog, info);
-
 	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ALWAYS);
 	gtk_widget_realize (GTK_WIDGET (widget));
 	gtk_window_present (GTK_WINDOW (widget));
@@ -436,6 +379,7 @@ applet_device_bt_get_class (NMApplet *applet)
 	dclass->device_state_changed = bt_device_state_changed;
 	dclass->get_icon = bt_get_icon;
 	dclass->get_secrets = bt_get_secrets;
+	dclass->secrets_request_size = sizeof (NMBtSecretsInfo);
 
 	return dclass;
 }
diff --git a/src/applet-device-cdma.c b/src/applet-device-cdma.c
index 7b9c7d7..a7256fb 100644
--- a/src/applet-device-cdma.c
+++ b/src/applet-device-cdma.c
@@ -450,27 +450,22 @@ cdma_get_icon (NMDevice *device,
 }
 
 typedef struct {
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
-	NMApplet *applet;
-	NMRemoteConnection *connection;
-	NMActiveConnection *active_connection;
+	SecretsRequest req;
 	GtkWidget *dialog;
 	GtkEntry *secret_entry;
 	char *secret_name;
-} NMCdmaInfo;
+} NMCdmaSecretsInfo;
 
 static void
-destroy_cdma_dialog (gpointer user_data, GObject *finalized)
+free_cdma_secrets_info (SecretsRequest *req)
 {
-	NMCdmaInfo *info = user_data;
+	NMCdmaSecretsInfo *info = (NMCdmaSecretsInfo *) req;
 
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
-
-	g_object_unref (info->connection);
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
+	}
 	g_free (info->secret_name);
-	g_free (info);
 }
 
 static void
@@ -478,76 +473,45 @@ get_cdma_secrets_cb (GtkDialog *dialog,
                      gint response,
                      gpointer user_data)
 {
-	NMCdmaInfo *info = (NMCdmaInfo *) user_data;
-	NMSettingCdma *setting;
-	GHashTable *settings_hash = NULL;
-	GHashTable *secrets;
+	SecretsRequest *req = user_data;
+	NMCdmaSecretsInfo *info = (NMCdmaSecretsInfo *) req;
+	NMSetting *setting;
 	GError *error = NULL;
 
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_cdma_dialog, info);
-
-	if (response != GTK_RESPONSE_OK) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): canceled", __FILE__, __LINE__, __func__);
-		goto done;
-	}
-
-	setting = NM_SETTING_CDMA (nm_connection_get_setting (NM_CONNECTION (info->connection), NM_TYPE_SETTING_CDMA));
-
-	if (!strcmp (info->secret_name, NM_SETTING_CDMA_PASSWORD)) {
-		g_object_set (setting, 
-		              NM_SETTING_CDMA_PASSWORD, gtk_entry_get_text (info->secret_entry),
-		              NULL);
-	}
-
-	secrets = nm_setting_to_hash (NM_SETTING (setting));
-	if (!secrets) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): failed to hash setting '%s'.",
-		             __FILE__, __LINE__, __func__, nm_setting_get_name (NM_SETTING (setting)));
-		goto done;
+	if (response == GTK_RESPONSE_OK) {
+		setting = nm_connection_get_setting (req->connection, NM_TYPE_SETTING_CDMA);
+		if (setting) {
+			g_object_set (G_OBJECT (setting),
+				          info->secret_name, gtk_entry_get_text (info->secret_entry),
+				          NULL);
+		} else {
+			error = g_error_new (NM_SECRET_AGENT_ERROR,
+				                 NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+				                 "%s.%d (%s): no GSM setting",
+				                 __FILE__, __LINE__, __func__);
+		}
+	} else {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_USER_CANCELED,
+		                     "%s.%d (%s): canceled",
+		                     __FILE__, __LINE__, __func__);
 	}
 
-	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
-	 * will contain all the individual settings hashes.
-	 */
-	settings_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-								    g_free, (GDestroyNotify) g_hash_table_destroy);
-	g_hash_table_insert (settings_hash, g_strdup (nm_setting_get_name (NM_SETTING (setting))), secrets);
-
- done:
-	info->callback (info->connection, settings_hash, error, info->callback_data);
-	if (settings_hash)
-		g_hash_table_destroy (settings_hash);
+	applet_secrets_request_complete_setting (req, NM_SETTING_CDMA_SETTING_NAME, error);
+	applet_secrets_request_free (req);
 	g_clear_error (&error);
-	
-	nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-	destroy_cdma_dialog (info, NULL);
 }
 
 static gboolean
-cdma_get_secrets (NMDevice *device,
-                  NMRemoteConnection *connection,
-                  NMActiveConnection *active_connection,
-                  const char *setting_name,
-                  const char **hints,
-                  NMANewSecretsRequestedFunc callback,
-                  gpointer callback_data,
-                  NMApplet *applet,
-                  GError **error)
+cdma_get_secrets (SecretsRequest *req, GError **error)
 {
-	NMCdmaInfo *info;
+	NMCdmaSecretsInfo *info = (NMCdmaSecretsInfo *) req;
 	GtkWidget *widget;
 	GtkEntry *secret_entry = NULL;
 
-	if (!hints || !g_strv_length ((char **) hints)) {
+	applet_secrets_request_set_free_func (req, free_cdma_secrets_info);
+
+	if (!req->hints || !g_strv_length (req->hints)) {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
@@ -555,17 +519,20 @@ cdma_get_secrets (NMDevice *device,
 		             __FILE__, __LINE__, __func__);
 		return FALSE;
 	}
+	info->secret_name = g_strdup (req->hints[0]);
 
-	if (!strcmp (hints[0], NM_SETTING_CDMA_PASSWORD))
-		widget = applet_mobile_password_dialog_new (device, NM_CONNECTION (connection), &secret_entry);
+	if (!strcmp (info->secret_name, NM_SETTING_CDMA_PASSWORD))
+		widget = applet_mobile_password_dialog_new (req->connection, &secret_entry);
 	else {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): unknown secrets hint '%s'.",
-		             __FILE__, __LINE__, __func__, hints[0]);
+		             __FILE__, __LINE__, __func__, info->secret_name);
 		return FALSE;
 	}
+	info->dialog = widget;
+	info->secret_entry = secret_entry;
 
 	if (!widget || !secret_entry) {
 		g_set_error (error,
@@ -576,23 +543,8 @@ cdma_get_secrets (NMDevice *device,
 		return FALSE;
 	}
 
-	info = g_new (NMCdmaInfo, 1);
-	info->callback = callback;
-	info->callback_data = callback_data;
-	info->applet = applet;
-	info->active_connection = active_connection;
-	info->connection = g_object_ref (connection);
-	info->secret_name = g_strdup (hints[0]);
-	info->dialog = widget;
-	info->secret_entry = secret_entry;
-
 	g_signal_connect (widget, "response", G_CALLBACK (get_cdma_secrets_cb), info);
 
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_cdma_dialog, info);
-
 	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ALWAYS);
 	gtk_widget_realize (GTK_WIDGET (widget));
 	gtk_window_present (GTK_WINDOW (widget));
@@ -969,6 +921,7 @@ applet_device_cdma_get_class (NMApplet *applet)
 	dclass->device_state_changed = cdma_device_state_changed;
 	dclass->get_icon = cdma_get_icon;
 	dclass->get_secrets = cdma_get_secrets;
+	dclass->secrets_request_size = sizeof (NMCdmaSecretsInfo);
 	dclass->device_added = cdma_device_added;
 
 	return dclass;
diff --git a/src/applet-device-gsm.c b/src/applet-device-gsm.c
index 33b4746..51a62a3 100644
--- a/src/applet-device-gsm.c
+++ b/src/applet-device-gsm.c
@@ -493,122 +493,24 @@ gsm_get_icon (NMDevice *device,
 }
 
 typedef struct {
-	/* General stuff */
+	SecretsRequest req;
+
 	GtkWidget *dialog;
 	GtkEntry *secret_entry;
 	char *secret_name;
-	NMApplet *applet;
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
-	NMActiveConnection *active_connection;
-	NMRemoteConnection *connection;
 } NMGsmSecretsInfo;
 
-
-static void
-secrets_dialog_destroy (gpointer user_data, GObject *finalized)
-{
-	NMGsmSecretsInfo *info = user_data;
-
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
-
-	g_object_unref (info->connection);
-	g_free (info->secret_name);
-	g_free (info);
-}
-
 static void
-get_existing_secrets_cb (NMSecretAgent *agent,
-                         NMConnection *connection,
-                         GHashTable *existing_secrets,
-                         GError *secrets_error,
-                         gpointer user_data)
+free_gsm_secrets_info (SecretsRequest *req)
 {
-	NMGsmSecretsInfo *info = (NMGsmSecretsInfo *) user_data;
-	GHashTable *settings_hash = NULL;
-	GError *error = NULL;
-	gboolean save_secret = FALSE;
-	const char *new_secret = NULL;
-
-	if (secrets_error) {
-		error = g_error_copy (secrets_error);
-		goto done;
-	}
-
-	/* Be a bit paranoid */
-	if (NM_REMOTE_CONNECTION (connection) != info->connection) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): unexpected reply for wrong connection.",
-		             __FILE__, __LINE__, __func__);
-		goto done;
-	}
-
-	/* Update connection's secrets so we can hash them for sending back to
-	 * NM, and so we can save them all back out to GConf if needed.
-	 */
-	if (existing_secrets) {
-		GHashTableIter iter;
-		gpointer key, value;
-
-		g_hash_table_iter_init (&iter, existing_secrets);
-		while (g_hash_table_iter_next (&iter, &key, &value)) {
-			GError *update_error = NULL;
-			const char *setting_name = key;
-			GHashTable *setting_hash = value;
-
-			/* Keep track of whether or not the user originally saved the secret */
-			if (!strcmp (setting_name, NM_SETTING_GSM_SETTING_NAME)) {
-				if (g_hash_table_lookup (setting_hash, info->secret_name))
-					save_secret = TRUE;
-			}
-
-			if (!nm_connection_update_secrets (NM_CONNECTION (info->connection),
-			                                   setting_name,
-			                                   setting_hash,
-			                                   &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);
-			}
-		}
-	}
-
-	/* Now update the secret the user just entered */
-	if (   !strcmp (info->secret_name, NM_SETTING_GSM_PIN)
-	    || !strcmp (info->secret_name, NM_SETTING_GSM_PASSWORD)) {
-		NMSetting *s_gsm;
+	NMGsmSecretsInfo *info = (NMGsmSecretsInfo *) req;
 
-		s_gsm = (NMSetting *) nm_connection_get_setting (NM_CONNECTION (info->connection),
-		                                                 NM_TYPE_SETTING_GSM);
-		if (s_gsm) {
-			new_secret = gtk_entry_get_text (info->secret_entry);
-			g_object_set (G_OBJECT (s_gsm), info->secret_name, new_secret, NULL);
-		}
-	}
-
-	settings_hash = nm_connection_to_hash (NM_CONNECTION (info->connection));
-
- done:
-	info->callback (info->connection, settings_hash, error, info->callback_data);
-	if (settings_hash)
-		g_hash_table_destroy (settings_hash);
-	g_clear_error (&error);
-
-	/* Save secrets back to GConf if the user had entered them into the
-	 * connection originally.  This lets users enter their secret every time if
-	 * they want.
-	 */
-	if (new_secret && save_secret) {
-		/* FIXME: save secret back to the keyring */
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
 	}
 
-	nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-	secrets_dialog_destroy (info, NULL);
+	g_free (info->secret_name);
 }
 
 static void
@@ -616,39 +518,33 @@ get_gsm_secrets_cb (GtkDialog *dialog,
                     gint response,
                     gpointer user_data)
 {
-	NMGsmSecretsInfo *info = (NMGsmSecretsInfo *) user_data;
+	SecretsRequest *req = user_data;
+	NMGsmSecretsInfo *info = (NMGsmSecretsInfo *) req;
+	NMSetting *setting;
 	GError *error = NULL;
-	const char *hints[2] = { info->secret_name, NULL };
-
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), secrets_dialog_destroy, info);
-
-	if (response != GTK_RESPONSE_OK) {
-		g_set_error (&error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): canceled",
-		             __FILE__, __LINE__, __func__);
 
-		g_warning ("%s", error->message);
-		info->callback (info->connection, NULL, error, info->callback_data);
-		g_error_free (error);
-
-		nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-		secrets_dialog_destroy (info, NULL);
-		return;
+	if (response == GTK_RESPONSE_OK) {
+		setting = nm_connection_get_setting (req->connection, NM_TYPE_SETTING_GSM);
+		if (setting) {
+			g_object_set (G_OBJECT (setting),
+				          info->secret_name, gtk_entry_get_text (info->secret_entry),
+				          NULL);
+		} else {
+			error = g_error_new (NM_SECRET_AGENT_ERROR,
+				                 NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+				                 "%s.%d (%s): no GSM setting",
+				                 __FILE__, __LINE__, __func__);
+		}
+	} else {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_USER_CANCELED,
+		                     "%s.%d (%s): canceled",
+		                     __FILE__, __LINE__, __func__);
 	}
 
-	/* Get existing connection secrets since NM will want those too */
-	nm_secret_agent_get_secrets (NM_SECRET_AGENT (info->applet->agent),
-	                             NM_CONNECTION (info->connection),
-	                             NM_SETTING_GSM_SETTING_NAME,
-	                             (const char **) &hints,
-	                             FALSE,
-	                             get_existing_secrets_cb,
-	                             info);
+	applet_secrets_request_complete_setting (req, NM_SETTING_GSM_SETTING_NAME, error);
+	applet_secrets_request_free (req);
+	g_clear_error (&error);
 }
 
 static void
@@ -678,12 +574,11 @@ pin_entry_changed (GtkEditable *editable, gpointer user_data)
 }
 
 static GtkWidget *
-ask_for_pin (NMDevice *device, GtkEntry **out_secret_entry)
+ask_for_pin (GtkEntry **out_secret_entry)
 {
 	GtkDialog *dialog;
 	GtkWidget *w = NULL, *ok_button = NULL;
 	GtkBox *box = NULL, *vbox = NULL;
-	char *dev_str;
 
 	dialog = GTK_DIALOG (gtk_dialog_new ());
 	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
@@ -698,12 +593,6 @@ ask_for_pin (NMDevice *device, GtkEntry **out_secret_entry)
 	w = gtk_label_new (_("PIN code is needed for the mobile broadband device"));
 	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
 
-	dev_str = g_strdup_printf ("<b>%s</b>", utils_get_device_description (device));
-	w = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (w), dev_str);
-	g_free (dev_str);
-	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
-
 	w = gtk_alignment_new (0.5, 0.5, 0, 1.0);
 	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
 
@@ -728,21 +617,15 @@ ask_for_pin (NMDevice *device, GtkEntry **out_secret_entry)
 }
 
 static gboolean
-gsm_get_secrets (NMDevice *device,
-                 NMRemoteConnection *connection,
-                 NMActiveConnection *active_connection,
-                 const char *setting_name,
-                 const char **hints,
-                 NMANewSecretsRequestedFunc callback,
-                 gpointer callback_data,
-                 NMApplet *applet,
-                 GError **error)
+gsm_get_secrets (SecretsRequest *req, GError **error)
 {
-	NMGsmSecretsInfo *secrets_info;
+	NMGsmSecretsInfo *info = (NMGsmSecretsInfo *) req;
 	GtkWidget *widget;
 	GtkEntry *secret_entry = NULL;
 
-	if (!hints || !g_strv_length ((char **) hints)) {
+	applet_secrets_request_set_free_func (req, free_gsm_secrets_info);
+
+	if (!req->hints || !g_strv_length (req->hints)) {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
@@ -750,26 +633,42 @@ gsm_get_secrets (NMDevice *device,
 		             __FILE__, __LINE__, __func__);
 		return FALSE;
 	}
+	info->secret_name = g_strdup (req->hints[0]);
+
+	if (!strcmp (info->secret_name, NM_SETTING_GSM_PIN)) {
+		NMDevice *device;
+		GsmDeviceInfo *devinfo;
+
+		device = applet_get_device_for_connection (req->applet, req->connection);
+		if (!device) {
+			g_set_error (error,
+				         NM_SECRET_AGENT_ERROR,
+				         NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+				         "%s.%d (%s): failed to find device for active connection.",
+				         __FILE__, __LINE__, __func__);
+			return FALSE;
+		}
 
-	if (!strcmp (hints[0], NM_SETTING_GSM_PIN)) {
-		GsmDeviceInfo *info = g_object_get_data (G_OBJECT (device), "devinfo");
+		devinfo = g_object_get_data (G_OBJECT (device), "devinfo");
+		g_assert (devinfo);
 
-		g_assert (info);
 		/* A GetSecrets PIN dialog overrides the initial unlock dialog */
-		if (info->dialog)
-			unlock_dialog_destroy (info);
+		if (devinfo->dialog)
+			unlock_dialog_destroy (devinfo);
 
-		widget = ask_for_pin (device, &secret_entry);
-	} else if (!strcmp (hints[0], NM_SETTING_GSM_PASSWORD))
-		widget = applet_mobile_password_dialog_new (device, NM_CONNECTION (connection), &secret_entry);
+		widget = ask_for_pin (&secret_entry);
+	} else if (!strcmp (info->secret_name, NM_SETTING_GSM_PASSWORD))
+		widget = applet_mobile_password_dialog_new (req->connection, &secret_entry);
 	else {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): unknown secrets hint '%s'.",
-		             __FILE__, __LINE__, __func__, hints[0]);
+		             __FILE__, __LINE__, __func__, info->secret_name);
 		return FALSE;
 	}
+	info->dialog = widget;
+	info->secret_entry = secret_entry;
 
 	if (!widget || !secret_entry) {
 		g_set_error (error,
@@ -780,22 +679,7 @@ gsm_get_secrets (NMDevice *device,
 		return FALSE;
 	}
 
-	secrets_info = g_malloc0 (sizeof (NMGsmSecretsInfo));
-	secrets_info->callback = callback;
-	secrets_info->callback_data = callback_data;
-	secrets_info->applet = applet;
-	secrets_info->active_connection = active_connection;
-	secrets_info->connection = g_object_ref (connection);
-	secrets_info->secret_name = g_strdup (hints[0]);
-	secrets_info->dialog = widget;
-	secrets_info->secret_entry = secret_entry;
-
-	g_signal_connect (widget, "response", G_CALLBACK (get_gsm_secrets_cb), secrets_info);
-
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), secrets_dialog_destroy, secrets_info);
+	g_signal_connect (widget, "response", G_CALLBACK (get_gsm_secrets_cb), info);
 
 	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ALWAYS);
 	gtk_widget_realize (GTK_WIDGET (widget));
@@ -1516,6 +1400,7 @@ applet_device_gsm_get_class (NMApplet *applet)
 	dclass->device_state_changed = gsm_device_state_changed;
 	dclass->get_icon = gsm_get_icon;
 	dclass->get_secrets = gsm_get_secrets;
+	dclass->secrets_request_size = sizeof (NMGsmSecretsInfo);
 	dclass->device_added = gsm_device_added;
 
 	return dclass;
diff --git a/src/applet-device-wifi.c b/src/applet-device-wifi.c
index d2028f9..449f02e 100644
--- a/src/applet-device-wifi.c
+++ b/src/applet-device-wifi.c
@@ -1504,24 +1504,21 @@ add_one_setting (GHashTable *settings,
 }
 
 typedef struct {
-	NMApplet *applet;
-	NMActiveConnection *active_connection;
+	SecretsRequest req;
+
 	GtkWidget *dialog;
 	GtkWidget *nag_dialog;
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
-	char *setting_name;
 } NMWifiInfo;
 
 static void
-destroy_wifi_dialog (gpointer user_data, GObject *finalized)
+free_wifi_info (SecretsRequest *req)
 {
-	NMWifiInfo *info = user_data;
+	NMWifiInfo *info = (NMWifiInfo *) req;
 
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
-	g_free (info->setting_name);
-	g_free (info);
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
+	}
 }
 
 static void
@@ -1529,11 +1526,11 @@ get_secrets_dialog_response_cb (GtkDialog *foo,
                                 gint response,
                                 gpointer user_data)
 {
-	NMWifiInfo *info = user_data;
+	SecretsRequest *req = user_data;
+	NMWifiInfo *info = (NMWifiInfo *) req;
 	NMAWirelessDialog *dialog = NMA_WIRELESS_DIALOG (info->dialog);
 	NMConnection *connection = NULL;
 	NMSettingWirelessSecurity *s_wireless_sec;
-	NMDevice *device = NULL;
 	GHashTable *settings = NULL;
 	const char *key_mgmt, *auth_alg;
 	GError *error = NULL;
@@ -1557,11 +1554,6 @@ get_secrets_dialog_response_cb (GtkDialog *foo,
 		}
 	}
 
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_wifi_dialog, info);
-
 	if (response != GTK_RESPONSE_OK) {
 		g_set_error (&error,
 		             NM_SECRET_AGENT_ERROR,
@@ -1571,7 +1563,7 @@ get_secrets_dialog_response_cb (GtkDialog *foo,
 		goto done;
 	}
 
-	connection = nma_wireless_dialog_get_connection (dialog, &device, NULL);
+	connection = nma_wireless_dialog_get_connection (dialog, NULL, NULL);
 	if (!connection) {
 		g_set_error (&error,
 		             NM_SECRET_AGENT_ERROR,
@@ -1638,91 +1630,38 @@ get_secrets_dialog_response_cb (GtkDialog *foo,
 	}
 
 	/* Add the 802-11-wireless-security setting no matter what */
-	if (!add_one_setting (settings, connection, NM_SETTING (s_wireless_sec), &error))
-		goto done;
-
-	info->callback (NM_REMOTE_CONNECTION (connection), settings, NULL, info->callback_data);
+	add_one_setting (settings, connection, NM_SETTING (s_wireless_sec), &error);
 
 done:
+	applet_secrets_request_complete (req, settings, error);
+	applet_secrets_request_free (req);
+
 	if (settings)
 		g_hash_table_destroy (settings);
-
-	if (error) {
-		g_warning ("%s", error->message);
-		info->callback (NM_REMOTE_CONNECTION (connection), NULL, error, info->callback_data);
-		g_error_free (error);
-	}
-
 	if (connection)
 		nm_connection_clear_secrets (connection);
-
-	destroy_wifi_dialog (info, NULL);
 }
 
 static gboolean
-wireless_get_secrets (NMDevice *device,
-                      NMRemoteConnection *connection,
-                      NMActiveConnection *active_connection,
-                      const char *setting_name,
-                      const char **hints,
-                      NMANewSecretsRequestedFunc callback,
-                      gpointer callback_data,
-                      NMApplet *applet,
-                      GError **error)
+wireless_get_secrets (SecretsRequest *req, GError **error)
 {
-	NMWifiInfo *info;
-	NMAccessPoint *ap;
-	const char *specific_object;
+	NMWifiInfo *info = (NMWifiInfo *) req;
 
-	if (!setting_name || !active_connection) {
-		g_set_error (error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): setting name and active connection object required",
-		             __FILE__, __LINE__, __func__);
-		return FALSE;
-	}
-
-	specific_object = nm_active_connection_get_specific_object (active_connection);
-	if (!specific_object) {
-		g_set_error (error,
-		             NM_SECRET_AGENT_ERROR,
-		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
-		             "%s.%d (%s): could not determine AP for specific object",
-		             __FILE__, __LINE__, __func__);
-		return FALSE;
-	}
+	applet_secrets_request_set_free_func (req, free_wifi_info);
 
-	info = g_malloc0 (sizeof (NMWifiInfo));
-
-	ap = nm_device_wifi_get_access_point_by_path (NM_DEVICE_WIFI (device), specific_object);
-	info->dialog = nma_wireless_dialog_new (applet, NM_CONNECTION (connection), device, ap);
+	info->dialog = nma_wireless_dialog_new (req->applet, req->connection, NULL, NULL);
 	if (!info->dialog) {
 		g_set_error (error,
 		             NM_SECRET_AGENT_ERROR,
 		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): couldn't display secrets UI",
 		             __FILE__, __LINE__, __func__);
-		g_free (info);
 		return FALSE;
 	}
 
-	info->applet = applet;
-	info->active_connection = active_connection;
-	info->callback = callback;
-	info->callback_data = callback_data;
-	info->setting_name = g_strdup (setting_name);
-
-	g_signal_connect (info->dialog, "response",
-	                  G_CALLBACK (get_secrets_dialog_response_cb),
-	                  info);
-
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_wifi_dialog, info);
-
+	g_signal_connect (info->dialog, "response", G_CALLBACK (get_secrets_dialog_response_cb), info);
 	show_ignore_focus_stealing_prevention (info->dialog);
+
 	return TRUE;
 }
 
@@ -1742,6 +1681,7 @@ applet_device_wifi_get_class (NMApplet *applet)
 	dclass->get_icon = wireless_get_icon;
 	dclass->get_more_info = wireless_get_more_info;
 	dclass->get_secrets = wireless_get_secrets;
+	dclass->secrets_request_size = sizeof (NMWifiInfo);
 
 	return dclass;
 }
diff --git a/src/applet-device-wired.c b/src/applet-device-wired.c
index 31f0c95..43ca50b 100644
--- a/src/applet-device-wired.c
+++ b/src/applet-device-wired.c
@@ -17,7 +17,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2008 Red Hat, Inc.
+ * (C) Copyright 2008 - 2011 Red Hat, Inc.
  * (C) Copyright 2008 Novell, Inc.
  */
 
@@ -323,18 +323,13 @@ wired_get_icon (NMDevice *device,
 /* PPPoE */
 
 typedef struct {
+	SecretsRequest req;
+
+	GtkWidget *dialog;
 	GtkEntry *username_entry;
 	GtkEntry *service_entry;
 	GtkEntry *password_entry;
 	GtkWidget *ok_button;
-
-	NMApplet *applet;
-	NMRemoteConnection *connection;
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
-
-	GtkWidget *dialog;
-	NMActiveConnection *active_connection;
 } NMPppoeInfo;
 
 static void
@@ -398,129 +393,60 @@ pppoe_update_ui (NMConnection *connection, NMPppoeInfo *info)
 		gtk_entry_set_text (info->password_entry, s);
 }
 
-static NMPppoeInfo *
-pppoe_info_new (GtkBuilder *builder,
-                NMApplet *applet,
-				NMANewSecretsRequestedFunc callback,
-				gpointer callback_data,
-                NMRemoteConnection *connection,
-                NMActiveConnection *active_connection)
-{
-	NMPppoeInfo *info;
-
-	info = g_new0 (NMPppoeInfo, 1);
-	
-	info->username_entry = GTK_ENTRY (GTK_WIDGET (gtk_builder_get_object (builder, "dsl_username")));
-	g_signal_connect (info->username_entry, "changed", G_CALLBACK (pppoe_verify), info);
-
-	info->service_entry = GTK_ENTRY (GTK_WIDGET (gtk_builder_get_object (builder, "dsl_service")));
-
-	info->password_entry = GTK_ENTRY (GTK_WIDGET (gtk_builder_get_object (builder, "dsl_password")));
-	g_signal_connect (info->password_entry, "changed", G_CALLBACK (pppoe_verify), info);
-
-	info->applet = applet;
-	info->callback = callback;
-	info->callback_data = callback_data;
-	info->connection = g_object_ref (connection);
-	info->active_connection = active_connection;
-
-	return info;
-}
-
-static void
-pppoe_info_destroy (gpointer data, GObject *destroyed_object)
-{
-	NMPppoeInfo *info = (NMPppoeInfo *) data;
-
-	g_object_unref (info->connection);	
-	g_free (info);
-}
-
 static void
-destroy_pppoe_dialog (gpointer data, GObject *finalized)
+free_pppoe_info (SecretsRequest *req)
 {
-	NMPppoeInfo *info = data;
-
-	/* When the active connection object is destroyed, try to destroy the
-	 * dialog too, if it's still around.
-	 */
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
-	pppoe_info_destroy (info, NULL);
-}
+	NMPppoeInfo *info = (NMPppoeInfo *) req;
 
-static void
-update_cb (NMRemoteConnection *connection,
-           GError *error,
-           gpointer user_data)
-{
-	if (error)
-		g_warning ("Error saving connection secrets: (%d) %s", error->code, error->message);
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
+	}
 }
 
 static void
-get_pppoe_secrets_cb (GtkDialog *dialog,
-					  gint response,
-					  gpointer user_data)
+get_pppoe_secrets_cb (GtkDialog *dialog, gint response, gpointer user_data)
 {
-	NMPppoeInfo *info = (NMPppoeInfo *) user_data;
+	SecretsRequest *req = user_data;
+	NMPppoeInfo *info = (NMPppoeInfo *) req;
 	NMSetting *setting;
-	GHashTable *settings_hash;
+	GHashTable *settings = NULL;
 	GHashTable *secrets;
 	GError *error = NULL;
 
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_pppoe_dialog, info);
-
 	if (response != GTK_RESPONSE_OK) {
 		g_set_error (&error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_USER_CANCELED,
 		             "%s.%d (%s): canceled",
 		             __FILE__, __LINE__, __func__);
 		goto done;
 	}
 
-	setting = nm_connection_get_setting (NM_CONNECTION (info->connection), NM_TYPE_SETTING_PPPOE);
+	setting = nm_connection_get_setting (req->connection, NM_TYPE_SETTING_PPPOE);
 	pppoe_update_setting (NM_SETTING_PPPOE (setting), info);
 
 	secrets = nm_setting_to_hash (setting);
 	if (!secrets) {
 		g_set_error (&error,
-		             0,
-		             0,
-					 "%s.%d (%s): failed to hash setting '%s'.",
-					 __FILE__, __LINE__, __func__, nm_setting_get_name (setting));
-		goto done;
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+					 "%s.%d (%s): failed to hash setting " NM_SETTING_PPPOE_SETTING_NAME,
+					 __FILE__, __LINE__, __func__);
+	} else {
+		/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
+		 * will contain all the individual settings hashes.
+		 */
+		settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy);
+		g_hash_table_insert (settings, NM_SETTING_PPPOE_SETTING_NAME, secrets);
 	}
 
-	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
-	 * will contain all the individual settings hashes.
-	 */
-	settings_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-										   g_free, (GDestroyNotify) g_hash_table_destroy);
-
-	g_hash_table_insert (settings_hash, g_strdup (nm_setting_get_name (setting)), secrets);
-	info->callback (info->connection, settings_hash, NULL, info->callback_data);
-	g_hash_table_destroy (settings_hash);
-
-	/* Save the connection back to GConf _after_ hashing it, because
-	 * saving to GConf might trigger the GConf change notifiers, resulting
-	 * in the connection being read back in from GConf which clears secrets.
-	 */
-	nm_remote_connection_commit_changes (info->connection, update_cb, NULL);
-
 done:
-	if (error) {
-		g_warning ("%s", error->message);
-		info->callback (info->connection, NULL, error, info->callback_data);
-		g_error_free (error);
-	}
+	applet_secrets_request_complete (req, settings, error);
+	applet_secrets_request_free (req);
 
-	nm_connection_clear_secrets (NM_CONNECTION (info->connection));
-	destroy_pppoe_dialog (info, NULL);
+	if (settings)
+		g_hash_table_destroy (settings);
 }
 
 static void
@@ -535,33 +461,34 @@ show_password_toggled (GtkToggleButton *button, gpointer user_data)
 }
 
 static gboolean
-pppoe_get_secrets (NMDevice *device,
-				   NMRemoteConnection *connection,
-				   NMActiveConnection *active_connection,
-				   const char *setting_name,
-				   NMANewSecretsRequestedFunc callback,
-				   gpointer callback_data,
-				   NMApplet *applet,
-				   GError **error)
+pppoe_get_secrets (SecretsRequest *req, GError **error)
 {
-	NMPppoeInfo *info;
+	NMPppoeInfo *info = (NMPppoeInfo *) req;
 	GtkWidget *w;
 	GtkBuilder* builder;
+	GError *tmp_error = NULL;
 
 	builder = gtk_builder_new ();
 
-	if (!gtk_builder_add_from_file (builder, UIDIR "/ce-page-dsl.ui", error))
-	{
-		g_warning ("Couldn't load builder file: %s", (*error)->message);
+	if (!gtk_builder_add_from_file (builder, UIDIR "/ce-page-dsl.ui", &tmp_error)) {
 		g_set_error (error,
-		             0,
-		             0,
-					 "%s.%d (%s): couldn't display secrets UI",
-		             __FILE__, __LINE__, __func__);
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+					 "%s.%d (%s): couldn't display secrets UI: %s",
+		             __FILE__, __LINE__, __func__, tmp_error->message);
+		g_error_free (tmp_error);
 		return FALSE;
 	}
 
-	info = pppoe_info_new (builder, applet, callback, callback_data, connection, active_connection);
+	applet_secrets_request_set_free_func (req, free_pppoe_info);
+
+	info->username_entry = GTK_ENTRY (gtk_builder_get_object (builder, "dsl_username"));
+	g_signal_connect (info->username_entry, "changed", G_CALLBACK (pppoe_verify), info);
+
+	info->service_entry = GTK_ENTRY (gtk_builder_get_object (builder, "dsl_service"));
+
+	info->password_entry = GTK_ENTRY (gtk_builder_get_object (builder, "dsl_password"));
+	g_signal_connect (info->password_entry, "changed", G_CALLBACK (pppoe_verify), info);
 
 	/* Create the dialog */
 	info->dialog = gtk_dialog_new ();
@@ -576,19 +503,12 @@ pppoe_get_secrets (NMDevice *device,
 	                    GTK_WIDGET (gtk_builder_get_object (builder, "DslPage")),
 	                    TRUE, TRUE, 0);
 
-	pppoe_update_ui (NM_CONNECTION (connection), info);
+	pppoe_update_ui (req->connection, info);
 
 	w = GTK_WIDGET (gtk_builder_get_object (builder, "dsl_show_password"));
-	g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (show_password_toggled), info);
+	g_signal_connect (w, "toggled", G_CALLBACK (show_password_toggled), info);
 
-	g_signal_connect (info->dialog, "response",
-	                  G_CALLBACK (get_pppoe_secrets_cb),
-	                  info);
-
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_pppoe_dialog, info);
+	g_signal_connect (info->dialog, "response", G_CALLBACK (get_pppoe_secrets_cb), info);
 
 	gtk_window_set_position (GTK_WINDOW (info->dialog), GTK_WIN_POS_CENTER_ALWAYS);
 	gtk_widget_realize (info->dialog);
@@ -600,44 +520,34 @@ pppoe_get_secrets (NMDevice *device,
 /* 802.1x */
 
 typedef struct {
-	NMApplet *applet;
-	NMActiveConnection *active_connection;
+	SecretsRequest req;
 	GtkWidget *dialog;
-	NMANewSecretsRequestedFunc callback;
-	gpointer callback_data;
 } NM8021xInfo;
 
 static void
-destroy_8021x_dialog (gpointer user_data, GObject *finalized)
+free_8021x_info (SecretsRequest *req)
 {
-	NM8021xInfo *info = user_data;
+	NM8021xInfo *info = (NM8021xInfo *) req;
 
-	gtk_widget_hide (info->dialog);
-	gtk_widget_destroy (info->dialog);
-	g_free (info);
+	if (info->dialog) {
+		gtk_widget_hide (info->dialog);
+		gtk_widget_destroy (info->dialog);
+	}
 }
 
 static void
-get_8021x_secrets_cb (GtkDialog *dialog,
-					  gint response,
-					  gpointer user_data)
+get_8021x_secrets_cb (GtkDialog *dialog, gint response, gpointer user_data)
 {
-	NM8021xInfo *info = user_data;
-	NMRemoteConnection *connection = NULL;
+	SecretsRequest *req = user_data;
+	NM8021xInfo *info = (NM8021xInfo *) req;
+	NMConnection *connection = NULL;
 	NMSetting *setting;
-	GHashTable *settings_hash;
-	GHashTable *secrets;
 	GError *error = NULL;
 
-	/* Got a user response, clear the NMActiveConnection destroy handler for
-	 * this dialog since this function will now take over dialog destruction.
-	 */
-	g_object_weak_unref (G_OBJECT (info->active_connection), destroy_8021x_dialog, info);
-
 	if (response != GTK_RESPONSE_OK) {
 		g_set_error (&error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_USER_CANCELED,
 		             "%s.%d (%s): canceled",
 		             __FILE__, __LINE__, __func__);
 		goto done;
@@ -646,157 +556,87 @@ get_8021x_secrets_cb (GtkDialog *dialog,
 	connection = nma_wired_dialog_get_connection (info->dialog);
 	if (!connection) {
 		g_set_error (&error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): couldn't get connection from wired dialog.",
 		             __FILE__, __LINE__, __func__);
 		goto done;
 	}
 
-	setting = nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_802_1X);
-	if (!setting) {
+	setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	if (setting) {
+		nm_connection_add_setting (req->connection, g_object_ref (setting));
+	} else {
 		g_set_error (&error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 					 "%s.%d (%s): requested setting '802-1x' didn't"
 					 " exist in the connection.",
 					 __FILE__, __LINE__, __func__);
-		goto done;
-	}
-
-	secrets = nm_setting_to_hash (setting);
-	if (!secrets) {
-		g_set_error (&error,
-		             0,
-		             0,
-					 "%s.%d (%s): failed to hash setting '%s'.",
-					 __FILE__, __LINE__, __func__, nm_setting_get_name (setting));
-		goto done;
 	}
 
-	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
-	 * will contain all the individual settings hashes.
-	 */
-	settings_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-										   g_free, (GDestroyNotify) g_hash_table_destroy);
-
-	g_hash_table_insert (settings_hash, g_strdup (nm_setting_get_name (setting)), secrets);
-	info->callback (connection, settings_hash, NULL, info->callback_data);
-	g_hash_table_destroy (settings_hash);
-
-	/* Save the connection back to GConf _after_ hashing it, because
-	 * saving to GConf might trigger the GConf change notifiers, resulting
-	 * in the connection being read back in from GConf which clears secrets.
-	 */
-	nm_remote_connection_commit_changes (connection, update_cb, NULL);
-
 done:
-	if (error) {
-		g_warning ("%s", error->message);
-		info->callback (connection, NULL, error, info->callback_data);
-		g_error_free (error);
-	}
-
-	if (connection)
-		nm_connection_clear_secrets (NM_CONNECTION (connection));
-
-	destroy_8021x_dialog (info, NULL);
+	applet_secrets_request_complete_setting (req, NM_SETTING_802_1X_SETTING_NAME, error);
+	applet_secrets_request_free (req);
+	g_clear_error (&error);
 }
 
 static gboolean
-nm_8021x_get_secrets (NMDevice *device,
-					  NMRemoteConnection *connection,
-					  NMActiveConnection *active_connection,
-					  const char *setting_name,
-					  NMANewSecretsRequestedFunc callback,
-					  gpointer callback_data,
-					  NMApplet *applet,
-					  GError **error)
+nm_8021x_get_secrets (SecretsRequest *req, const char *ui_file, GError **error)
 {
-	GtkWidget *dialog;
-	NM8021xInfo *info;
+	NM8021xInfo *info = (NM8021xInfo *) req;
+
+	applet_secrets_request_set_free_func (req, free_8021x_info);
 
-	dialog = nma_wired_dialog_new (applet->ui_file,
-								   applet->nm_client,
-								   g_object_ref (connection),
-								   device);
-	if (!dialog) {
+	info->dialog = nma_wired_dialog_new (ui_file, g_object_ref (req->connection));
+	if (!info->dialog) {
 		g_set_error (error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
 		             "%s.%d (%s): couldn't display secrets UI",
 		             __FILE__, __LINE__, __func__);
 		return FALSE;
 	}
 
-	info = g_malloc0 (sizeof (NM8021xInfo));
-	info->applet = applet;
-	info->active_connection = active_connection;
-	info->dialog = dialog;
-	info->callback = callback;
-	info->callback_data = callback_data;
-
-	g_signal_connect (dialog, "response", G_CALLBACK (get_8021x_secrets_cb), info);
-
-	/* Attach a destroy notifier to the NMActiveConnection so we can destroy
-	 * the dialog when the active connection goes away.
-	 */
-	g_object_weak_ref (G_OBJECT (active_connection), destroy_8021x_dialog, info);
+	g_signal_connect (info->dialog, "response", G_CALLBACK (get_8021x_secrets_cb), info);
 
-	gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ALWAYS);
-	gtk_widget_realize (dialog);
-	gtk_window_present (GTK_WINDOW (dialog));
+	gtk_window_set_position (GTK_WINDOW (info->dialog), GTK_WIN_POS_CENTER_ALWAYS);
+	gtk_widget_realize (info->dialog);
+	gtk_window_present (GTK_WINDOW (info->dialog));
 
 	return TRUE;
 }
 
 static gboolean
-wired_get_secrets (NMDevice *device,
-				   NMRemoteConnection *connection,
-				   NMActiveConnection *active_connection,
-				   const char *setting_name,
-				   const char **hints,
-				   NMANewSecretsRequestedFunc callback,
-				   gpointer callback_data,
-				   NMApplet *applet,
-				   GError **error)
+wired_get_secrets (SecretsRequest *req, GError **error)
 {
 	NMSettingConnection *s_con;
-	const char *connection_type;
-	gboolean success = FALSE;
+	const char *ctype;
 
-	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION));
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (req->connection, NM_TYPE_SETTING_CONNECTION));
 	if (!s_con) {
 		g_set_error (error,
-		             0,
-		             0,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
 		             "%s.%d (%s): Invalid connection",
 		             __FILE__, __LINE__, __func__);
 		return FALSE;
 	}
 
-	connection_type = nm_setting_connection_get_connection_type (s_con);
-	if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) {
-		success = nm_8021x_get_secrets (device,
-		                                connection,
-		                                active_connection,
-		                                setting_name,
-		                                callback,
-		                                callback_data,
-		                                applet,
-		                                error);
-	} else if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
-		success = pppoe_get_secrets (device,
-		                             connection,
-		                             active_connection,
-		                             setting_name,
-		                             callback,
-		                             callback_data,
-		                             applet,
-		                             error);
+	ctype = nm_setting_connection_get_connection_type (s_con);
+	if (!strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME))
+		return nm_8021x_get_secrets (req, req->applet->ui_file, error);
+	else if (!strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
+		return pppoe_get_secrets (req, error);
+	else {
+		g_set_error (error,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		             "%s.%d (%s): unhandled wired connection type '%s'",
+		             __FILE__, __LINE__, __func__, ctype);
 	}
 
-	return success;
+	return FALSE;
 }
 
 NMADeviceClass *
@@ -813,6 +653,7 @@ applet_device_wired_get_class (NMApplet *applet)
 	dclass->device_state_changed = wired_device_state_changed;
 	dclass->get_icon = wired_get_icon;
 	dclass->get_secrets = wired_get_secrets;
+	dclass->secrets_request_size = MAX (sizeof (NM8021xInfo), sizeof (NMPppoeInfo));
 
 	return dclass;
 }
diff --git a/src/applet-dialogs.c b/src/applet-dialogs.c
index 4fe0a89..d2bcf51 100644
--- a/src/applet-dialogs.c
+++ b/src/applet-dialogs.c
@@ -728,14 +728,12 @@ applet_warning_dialog_show (const char *message)
 }
 
 GtkWidget *
-applet_mobile_password_dialog_new (NMDevice *device,
-                                   NMConnection *connection,
+applet_mobile_password_dialog_new (NMConnection *connection,
                                    GtkEntry **out_secret_entry)
 {
 	GtkDialog *dialog;
 	GtkWidget *w;
 	GtkBox *box = NULL, *vbox = NULL;
-	char *dev_str;
 	NMSettingConnection *s_con;
 	char *tmp;
 	const char *id;
@@ -759,12 +757,6 @@ applet_mobile_password_dialog_new (NMDevice *device,
 
 	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
 
-	dev_str = g_strdup_printf ("<b>%s</b>", utils_get_device_description (device));
-	w = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (w), dev_str);
-	g_free (dev_str);
-	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
-
 	w = gtk_alignment_new (0.5, 0.5, 0, 1.0);
 	gtk_box_pack_start (vbox, w, TRUE, TRUE, 0);
 
diff --git a/src/applet-dialogs.h b/src/applet-dialogs.h
index 6f22ba2..c3dbcca 100644
--- a/src/applet-dialogs.h
+++ b/src/applet-dialogs.h
@@ -33,8 +33,7 @@ void applet_about_dialog_show (NMApplet *applet);
 
 GtkWidget *applet_warning_dialog_show (const char *message);
 
-GtkWidget *applet_mobile_password_dialog_new (NMDevice *device,
-                                              NMConnection *connection,
+GtkWidget *applet_mobile_password_dialog_new (NMConnection *connection,
                                               GtkEntry **out_secret_entry);
 
 /******** Mobile PIN dialog ********/
diff --git a/src/applet-vpn-request.c b/src/applet-vpn-request.c
new file mode 100644
index 0000000..dbc4f94
--- /dev/null
+++ b/src/applet-vpn-request.c
@@ -0,0 +1,377 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 - 2011 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <unistd.h>
+
+#include "applet-vpn-request.h"
+#include "nma-marshal.h"
+#include <nm-connection.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-vpn.h>
+#include <nm-secret-agent.h>
+
+G_DEFINE_TYPE (AppletVpnRequest, applet_vpn_request, G_TYPE_OBJECT)
+
+#define APPLET_VPN_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                           APPLET_TYPE_VPN_REQUEST, \
+                                           AppletVpnRequestPrivate))
+
+typedef struct {
+	gboolean disposed;
+
+	char *bin_path;
+	char *uuid;
+	char *id;
+	char *service_type;
+
+	guint watch_id;
+	GPid pid;
+
+	GSList *lines;
+	int child_stdin;
+	int child_stdout;
+	int num_newlines;
+	GIOChannel *channel;
+	guint channel_eventid;
+} AppletVpnRequestPrivate;
+
+enum {
+	DONE,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/****************************************************************/
+
+static void
+destroy_gvalue (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static void 
+child_finished_cb (GPid pid, gint status, gpointer user_data)
+{
+	AppletVpnRequest *self = APPLET_VPN_REQUEST (user_data);
+	AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
+	GError *error = NULL;
+	GHashTable *settings = NULL;
+
+	if (status == 0) {
+		GHashTable *secrets;
+		GSList *iter;
+
+		secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+
+		/* The length of 'lines' must be divisible by 2 since it must contain
+		 * key:secret pairs with the key on one line and the associated secret
+		 * on the next line.
+		 */
+		for (iter = priv->lines; iter; iter = g_slist_next (iter)) {
+			GValue *val;
+
+			if (!iter->next)
+				break;
+
+			val = g_slice_new0 (GValue);
+			g_value_init (val, G_TYPE_STRING);
+			g_value_set_string (val, iter->next->data);
+
+			g_hash_table_insert (secrets, g_strdup (iter->data), val);
+			iter = iter->next;
+		}
+
+		settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy);
+		g_hash_table_insert (settings, NM_SETTING_VPN_SETTING_NAME, secrets);
+	} else {
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_USER_CANCELED,
+		                     "%s.%d (%s): canceled", __FILE__, __LINE__, __func__);
+	}
+
+	/* Send secrets back to listeners */
+	g_signal_emit (self, signals[DONE], 0, error ? NULL : settings, error);
+
+	if (settings)
+		g_hash_table_destroy (settings);
+}
+
+static gboolean 
+child_stdout_data_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
+{
+	AppletVpnRequest *self = APPLET_VPN_REQUEST (user_data);
+	AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
+	const char buf[1] = { 0x01 };
+	char *str;
+	int len;
+
+	if (!(condition & G_IO_IN))
+		return TRUE;
+
+	if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) {
+		len = strlen (str);
+		if (len == 1 && str[0] == '\n') {
+			/* on second line with a newline newline */
+			if (++priv->num_newlines == 2) {
+				/* terminate the child */
+				if (write (priv->child_stdin, buf, sizeof (buf)) == -1)
+					return TRUE;
+			}
+		} else if (len > 0) {
+			/* remove terminating newline */
+			str[len - 1] = '\0';
+			priv->lines = g_slist_append (priv->lines, str);
+		}
+	}
+	return TRUE;
+}
+
+static char *
+find_auth_dialog_binary (const char *service, GError **error)
+{
+	GDir *dir;
+	char *prog = NULL;
+	const char *f;
+
+	dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
+	if (!dir) {
+		g_set_error (error,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		             "Failed to open VPN plugin file configuration directory " VPN_NAME_FILES_DIR);
+		return NULL;
+	}
+
+	while (prog == NULL && (f = g_dir_read_name (dir)) != NULL) {
+		char *path;
+		GKeyFile *keyfile;
+
+		if (!g_str_has_suffix (f, ".name"))
+			continue;
+
+		path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f);
+
+		keyfile = g_key_file_new ();
+		if (g_key_file_load_from_file (keyfile, path, 0, NULL)) {
+			char *thisservice;
+
+			thisservice = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL);
+			if (g_strcmp0 (thisservice, service) == 0)
+				prog = g_key_file_get_string (keyfile, "GNOME", "auth-dialog", NULL);
+			g_free (thisservice);
+		}
+		g_key_file_free (keyfile);
+		g_free (path);
+	}
+	g_dir_close (dir);
+
+	if (prog == NULL) {
+		g_set_error (error,
+		             NM_SECRET_AGENT_ERROR,
+		             NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		             "Could not find the authentication dialog for VPN connection type '%s'",
+		             service);
+	} else {
+		char *prog_basename;
+
+		/* Remove any path component, then reconstruct path to the auth
+		 * dialog in LIBEXECDIR.
+		 */
+		prog_basename = g_path_get_basename (prog);
+		g_free (prog);
+		prog = g_strdup_printf ("%s/%s", LIBEXECDIR, prog_basename);
+		g_free (prog_basename);
+	}
+
+	return prog;
+}
+
+AppletVpnRequest *
+applet_vpn_request_new (NMConnection *connection, GError **error)
+{
+	AppletVpnRequest *self;
+	AppletVpnRequestPrivate *priv;
+	NMSettingConnection *s_con;
+	NMSettingVPN *s_vpn;
+	const char *connection_type;
+	const char *service_type;
+	char *bin_path;
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_return_val_if_fail (s_con != NULL, FALSE);
+
+	connection_type = nm_setting_connection_get_connection_type (s_con);
+	g_return_val_if_fail (connection_type != NULL, FALSE);
+	g_return_val_if_fail (strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME) == 0, FALSE);
+
+	s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
+	g_return_val_if_fail (s_vpn != NULL, FALSE);
+
+	service_type = nm_setting_vpn_get_service_type (s_vpn);
+	g_return_val_if_fail (service_type != NULL, FALSE);
+
+	/* find the auth-dialog binary */
+	bin_path = find_auth_dialog_binary (service_type, error);
+	if (!bin_path)
+		return NULL;
+
+	self = (AppletVpnRequest *) g_object_new (APPLET_TYPE_VPN_REQUEST, NULL);
+	if (self) {
+		priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
+		priv->bin_path = g_strdup (bin_path);
+		priv->uuid = g_strdup (nm_setting_connection_get_uuid (s_con));
+		priv->id = g_strdup (nm_setting_connection_get_id (s_con));
+		priv->service_type = g_strdup (service_type);
+	}
+	g_free (bin_path);
+
+	return self;
+}
+
+gboolean
+applet_vpn_request_get_secrets (AppletVpnRequest *self,
+                                gboolean retry,
+                                GError **error)
+{
+	AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
+	gboolean success;
+	const char *argv[] =
+		{ priv->bin_path            /*"/usr/libexec/nm-vpnc-auth-dialog"*/, 
+		  "-u", priv->uuid          /*"2a5d52b5-95b4-4431-b96e-3dd46128f9a7"*/, 
+		  "-n", priv->id            /*"davidznet42"*/,
+		  "-s", priv->service_type  /*"org.freedesktop.vpnc"*/, 
+		  "-r",
+		  NULL
+		};
+
+	if (!retry)
+		argv[7] = NULL;
+
+	success = g_spawn_async_with_pipes (NULL,                       /* working_directory */
+	                                    (gchar **) argv,            /* argv */
+	                                    NULL,                       /* envp */
+	                                    G_SPAWN_DO_NOT_REAP_CHILD,  /* flags */
+	                                    NULL,                       /* child_setup */
+	                                    NULL,                       /* user_data */
+	                                    &priv->pid,                 /* child_pid */
+	                                    &priv->child_stdin,         /* standard_input */
+	                                    &priv->child_stdout,        /* standard_output */
+	                                    NULL,                       /* standard_error */
+	                                    error);                     /* error */
+	if (success) {
+		/* catch when child is reaped */
+		priv->watch_id = g_child_watch_add (priv->pid, child_finished_cb, self);
+
+		/* listen to what child has to say */
+		priv->channel = g_io_channel_unix_new (priv->child_stdout);
+		priv->channel_eventid = g_io_add_watch (priv->channel, G_IO_IN, child_stdout_data_cb, self);
+		g_io_channel_set_encoding (priv->channel, NULL, NULL);
+	}
+
+	return success;
+}
+
+static void
+applet_vpn_request_init (AppletVpnRequest *self)
+{
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+	pid_t pid = GPOINTER_TO_INT (data);
+
+	if (kill (pid, 0) == 0)
+		kill (pid, SIGKILL);
+	/* ensure the child is reaped */
+	waitpid (pid, NULL, 0);
+	return FALSE;
+}
+
+static void
+dispose (GObject *object)
+{
+	AppletVpnRequest *self = APPLET_VPN_REQUEST (object);
+	AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
+
+	if (!priv->disposed) {
+		priv->disposed = TRUE;
+
+		g_free (priv->bin_path);
+		g_free (priv->uuid);
+		g_free (priv->id);
+		g_free (priv->service_type);
+
+		if (priv->watch_id)
+			g_source_remove (priv->watch_id);
+
+		if (priv->channel_eventid)
+			g_source_remove (priv->channel_eventid);
+		if (priv->channel)
+			g_io_channel_unref (priv->channel);
+
+		if (priv->pid) {
+			g_spawn_close_pid (priv->pid);
+			if (kill (priv->pid, SIGTERM) == 0)
+				g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+			else {
+				kill (priv->pid, SIGKILL);
+				/* ensure the child is reaped */
+				waitpid (priv->pid, NULL, 0);
+			}
+		}
+
+		g_slist_foreach (priv->lines, (GFunc) g_free, NULL);
+		g_slist_free (priv->lines);
+	}
+
+	G_OBJECT_CLASS (applet_vpn_request_parent_class)->dispose (object);
+}
+
+static void
+applet_vpn_request_class_init (AppletVpnRequestClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	g_type_class_add_private (req_class, sizeof (AppletVpnRequestPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	signals[DONE] =
+		g_signal_new ("done",
+					  G_OBJECT_CLASS_TYPE (req_class),
+					  G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
+					  nma_marshal_VOID__POINTER_POINTER,
+					  G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
diff --git a/src/applet-vpn-request.h b/src/applet-vpn-request.h
new file mode 100644
index 0000000..cf81ed7
--- /dev/null
+++ b/src/applet-vpn-request.h
@@ -0,0 +1,58 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 - 2011 Red Hat, Inc.
+ */
+
+#ifndef APPLET_VPN_REQEUST_H
+#define APPLET_VPN_REQUEST_H
+
+#include <glib-object.h>
+
+#include <nm-connection.h>
+
+#define APPLET_TYPE_VPN_REQUEST            (applet_vpn_request_get_type ())
+#define APPLET_VPN_REQUEST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), APPLET_TYPE_VPN_REQUEST, AppletVpnRequest))
+#define APPLET_VPN_REQUEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), APPLET_TYPE_VPN_REQUEST, AppletVpnRequestClass))
+#define APPLET_IS_VPN_REQUEST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APPLET_TYPE_VPN_REQUEST))
+#define APPLET_IS_VPN_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), APPLET_TYPE_VPN_REQUEST))
+#define APPLET_VPN_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), APPLET_TYPE_VPN_REQUEST, AppletVpnRequestClass))
+
+typedef struct {
+	GObject parent;
+} AppletVpnRequest;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*done) (AppletVpnRequest *self,
+	              GHashTable *secrets,
+	              GError *error);
+} AppletVpnRequestClass;
+
+GType applet_vpn_request_get_type (void);
+
+AppletVpnRequest *applet_vpn_request_new (NMConnection *connection, GError **error);
+
+gboolean applet_vpn_request_get_secrets (AppletVpnRequest *req,
+                                         gboolean retry,
+                                         GError **error);
+
+#endif  /* APPLET_VPN_REQUEST_H */
+
diff --git a/src/applet.c b/src/applet.c
index 65f4230..1167282 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -52,6 +52,12 @@
 #include <nm-connection.h>
 #include <nm-vpn-connection.h>
 #include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-bluetooth.h>
 #include <nm-setting-vpn.h>
 #include <nm-active-connection.h>
 #include <nm-setting-wireless.h>
@@ -68,7 +74,7 @@
 #include "applet-device-cdma.h"
 #include "applet-device-bt.h"
 #include "applet-dialogs.h"
-#include "vpn-password-dialog.h"
+#include "applet-vpn-request.h"
 #include "utils.h"
 #include "gconf-helpers.h"
 
@@ -235,6 +241,26 @@ applet_get_active_for_connection (NMApplet *applet, NMConnection *connection)
 	return NULL;
 }
 
+NMDevice *
+applet_get_device_for_connection (NMApplet *applet, NMConnection *connection)
+{
+	const GPtrArray *active_list;
+	const char *cpath;
+	int i;
+
+	cpath = nm_connection_get_path (connection);
+	g_return_val_if_fail (cpath != NULL, NULL);
+
+	active_list = nm_client_get_active_connections (applet->nm_client);
+	for (i = 0; active_list && (i < active_list->len); i++) {
+		NMActiveConnection *active = NM_ACTIVE_CONNECTION (g_ptr_array_index (active_list, i));
+
+		if (!g_strcmp0 (nm_active_connection_get_connection (active), cpath))
+			return g_ptr_array_index (nm_active_connection_get_devices (active), 0);
+	}
+	return NULL;
+}
+
 static inline NMADeviceClass *
 get_device_class (NMDevice *device, NMApplet *applet)
 {
@@ -256,6 +282,36 @@ get_device_class (NMDevice *device, NMApplet *applet)
 	return NULL;
 }
 
+static inline NMADeviceClass *
+get_device_class_from_connection (NMConnection *connection, NMApplet *applet)
+{
+	NMSettingConnection *s_con;
+	const char *ctype;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (applet != NULL, NULL);
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_return_val_if_fail (s_con != NULL, NULL);
+
+	ctype = nm_setting_connection_get_connection_type (s_con);
+	g_return_val_if_fail (ctype != NULL, NULL);
+
+	if (!strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) || !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
+		return applet->wired_class;
+	else if (!strcmp (ctype, NM_SETTING_WIRELESS_SETTING_NAME))
+		return applet->wifi_class;
+	else if (!strcmp (ctype, NM_SETTING_GSM_SETTING_NAME))
+		return applet->gsm_class;
+	else if (!strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME))
+		return applet->cdma_class;
+	else if (!strcmp (ctype, NM_SETTING_BLUETOOTH_SETTING_NAME))
+		return applet->bt_class;
+	else
+		g_warning ("%s: unhandled connection type '%s'", __func__, ctype);
+	return NULL;
+}
+
 typedef struct {
 	NMApplet *applet;
 	NMDevice *device;
@@ -2449,120 +2505,271 @@ applet_schedule_update_icon (NMApplet *applet)
 		applet->update_icon_id = g_idle_add (applet_update_icon, applet);
 }
 
-#if 0
-static NMDevice *
-find_active_device (NMRemoteConnection *connection,
-                    NMApplet *applet,
-                    NMActiveConnection **out_active_connection)
+/*****************************************************************************/
+
+static SecretsRequest *
+applet_secrets_request_new (size_t totsize,
+                            NMConnection *connection,
+                            gpointer request_id,
+                            const char *setting_name,
+                            const char **hints,
+                            AppletAgentSecretsCallback callback,
+                            gpointer callback_data,
+                            NMApplet *applet)
 {
-	const GPtrArray *active_connections;
-	int i;
+	SecretsRequest *req;
 
+	g_return_val_if_fail (totsize >= sizeof (SecretsRequest), NULL);
 	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (applet != NULL, NULL);
-	g_return_val_if_fail (out_active_connection != NULL, NULL);
-	g_return_val_if_fail (*out_active_connection == NULL, NULL);
 
-	/* Look through the active connection list trying to find the D-Bus
-	 * object path of applet_connection.
-	 */
-	active_connections = nm_client_get_active_connections (applet->nm_client);
-	for (i = 0; active_connections && (i < active_connections->len); i++) {
-		NMActiveConnection *active;
-		const char *connection_path;
-		const GPtrArray *devices;
+	req = g_malloc0 (totsize);
+	req->totsize = totsize;
+	req->connection = g_object_ref (connection);
+	req->reqid = request_id;
+	req->setting_name = g_strdup (setting_name);
+	req->hints = g_strdupv ((char **) hints);
+	req->callback = callback;
+	req->callback_data = callback_data;
+	req->applet = applet;
+	return req;
+}
 
-		active = NM_ACTIVE_CONNECTION (g_ptr_array_index (active_connections, i));
+void
+applet_secrets_request_set_free_func (SecretsRequest *req,
+                                      SecretsRequestFreeFunc free_func)
+{
+	req->free_func = free_func;
+}
 
-		connection_path = nm_active_connection_get_connection (active);
-		if (!connection_path) {
-			/* Shouldn't happen; but we shouldn't crash either */
-			g_warning ("%s: couldn't get connection path for active connection!", __func__);
-			continue;
-		}
+void
+applet_secrets_request_complete (SecretsRequest *req,
+                                 GHashTable *settings,
+                                 GError *error)
+{
+	req->callback (req->applet->agent, error ? NULL : settings, error, req->callback_data);
+}
 
-		if (!strcmp (connection_path, nm_connection_get_path (NM_CONNECTION (connection)))) {
-			devices = nm_active_connection_get_devices (active);
-			if (devices)
-				*out_active_connection = active;
-			return devices ? NM_DEVICE (g_ptr_array_index (devices, 0)) : NULL;
+void
+applet_secrets_request_complete_setting (SecretsRequest *req,
+                                         const char *setting_name,
+                                         GError *error)
+{
+	NMSetting *setting;
+	GHashTable *settings = NULL, *secrets;
+
+	if (setting_name && !error) {
+		setting = nm_connection_get_setting_by_name (req->connection, setting_name);
+		if (setting) {
+			secrets = nm_setting_to_hash (NM_SETTING (setting));
+			if (secrets) {
+				/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
+				 * will contain all the individual settings hashes.
+				 */
+				settings = g_hash_table_new_full (g_str_hash,
+				                                  g_str_equal,
+				                                  g_free,
+				                                  (GDestroyNotify) g_hash_table_destroy);
+				g_hash_table_insert (settings, g_strdup (setting_name), secrets);
+			} else {
+				g_set_error (&error,
+						     NM_SECRET_AGENT_ERROR,
+						     NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+						     "%s.%d (%s): failed to hash setting '%s'.",
+						     __FILE__, __LINE__, __func__, setting_name);
+			}
+		} else {
+			g_set_error (&error,
+				         NM_SECRET_AGENT_ERROR,
+				         NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+				         "%s.%d (%s): unhandled setting '%s'",
+				         __FILE__, __LINE__, __func__, setting_name);
 		}
 	}
 
-	return NULL;
+	req->callback (req->applet->agent, settings, error, req->callback_data);
+}
+
+void
+applet_secrets_request_free (SecretsRequest *req)
+{
+	g_return_if_fail (req != NULL);
+
+	if (req->free_func)
+		req->free_func (req);
+
+	req->applet->secrets_reqs = g_slist_remove (req->applet->secrets_reqs, req);
+
+	g_object_unref (req->connection);
+	g_free (req->setting_name);
+	g_strfreev (req->hints);
+	memset (req, 0, req->totsize);
+	g_free (req);
+}
+
+typedef struct {
+	SecretsRequest req;
+	AppletVpnRequest *vpn;
+} VpnSecretsRequest;
+
+static void
+vpn_request_done_cb (AppletVpnRequest *foo,
+                     GHashTable *settings,
+                     GError *error,
+                     gpointer user_data)
+{
+	SecretsRequest *req = user_data;
+
+	applet_secrets_request_complete (req, settings, error);
+	applet_secrets_request_free (req);
+}
+
+static void
+vpn_request_free_cb (SecretsRequest *req)
+{
+	VpnSecretsRequest *vpn_req = (VpnSecretsRequest *) req;
+
+	if (vpn_req->vpn)
+		g_object_unref (vpn_req->vpn);
 }
 
 static void
-applet_settings_new_secrets_requested_cb (NMRemoteSettings *settings,
-                                          NMRemoteConnection *connection,
-                                          const char *setting_name,
-                                          const char **hints,
-                                          gboolean ask_user,
-                                          NMANewSecretsRequestedFunc callback,
-                                          gpointer callback_data,
-                                          gpointer user_data)
+get_existing_secrets_cb (NMSecretAgent *agent,
+                         NMConnection *connection,
+                         GHashTable *secrets,
+                         GError *secrets_error,
+                         gpointer user_data)
+{
+	SecretsRequest *req = user_data;
+	NMADeviceClass *dclass;
+	GError *error = NULL;
+
+	/* Merge existing secrets into connection; ignore errors */
+	nm_connection_update_secrets (connection, req->setting_name, secrets, NULL);
+
+	dclass = get_device_class_from_connection (connection, req->applet);
+	g_assert (dclass);
+
+	/* Let the device class handle secrets */
+	if (!dclass->get_secrets (req, &error)) {
+		g_warning ("%s:%d - %s", __func__, __LINE__, error ? error->message : "(unknown)");
+		applet_secrets_request_complete (req, NULL, error);
+		applet_secrets_request_free (req);
+		g_error_free (error);
+	}
+	/* Otherwise success; wait for the secrets callback */
+}
+
+static void
+applet_agent_get_secrets_cb (AppletAgent *agent,
+                             void *request_id,
+                             NMConnection *connection,
+                             const char *setting_name,
+                             const char **hints,
+                             guint32 flags,
+                             AppletAgentSecretsCallback callback,
+                             gpointer callback_data,
+                             gpointer user_data)
 {
 	NMApplet *applet = NM_APPLET (user_data);
-	NMActiveConnection *active_connection = NULL;
 	NMSettingConnection *s_con;
-	NMDevice *device;
 	NMADeviceClass *dclass;
 	GError *error = NULL;
+	SecretsRequest *req = NULL;
 
-	s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION);
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
 	g_return_if_fail (s_con != NULL);
 
 	/* VPN secrets get handled a bit differently */
 	if (!strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_VPN_SETTING_NAME)) {
-		nma_vpn_request_password (connection, ask_user, callback, callback_data);
+		VpnSecretsRequest *vpnreq;
+
+		req = applet_secrets_request_new (sizeof (VpnSecretsRequest),
+		                                  connection,
+		                                  request_id,
+		                                  setting_name,
+		                                  hints,
+		                                  callback,
+		                                  callback_data,
+		                                  applet);
+		applet_secrets_request_set_free_func (req, vpn_request_free_cb);
+
+		vpnreq = (VpnSecretsRequest *) req;
+		vpnreq->vpn = applet_vpn_request_new (connection, &error);
+		if (!vpnreq->vpn)
+			goto error;
+		if (!applet_vpn_request_get_secrets (vpnreq->vpn, !!flags, &error))
+			goto error;
+
+		/* Track this VPN password request */
+		g_signal_connect (vpnreq->vpn, "done", G_CALLBACK (vpn_request_done_cb), vpnreq);
+		applet->secrets_reqs = g_slist_prepend (applet->secrets_reqs, vpnreq);
 		return;
 	}
 
-	/* Find the active device for this connection */
-	device = find_active_device (connection, applet, &active_connection);
-	if (!device || !active_connection) {
-		g_set_error (&error,
-		             0,
-		             0,
-		             "%s.%d (%s): couldn't find details for connection",
-		             __FILE__, __LINE__, __func__);
-		goto error;
-	}
-
-	dclass = get_device_class (device, applet);
+	dclass = get_device_class_from_connection (connection, applet);
 	if (!dclass) {
-		g_set_error (&error,
-		             0,
-		             0,
-		             "%s.%d (%s): device type unknown",
-		             __FILE__, __LINE__, __func__);
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		                     "%s.%d (%s): device type unknown",
+		                     __FILE__, __LINE__, __func__);
 		goto error;
 	}
 
 	if (!dclass->get_secrets) {
-		g_set_error (&error,
-		             0,
-		             0,
-		             "%s.%d (%s): no secrets found",
-		             __FILE__, __LINE__, __func__);
+		error = g_error_new (NM_SECRET_AGENT_ERROR,
+		                     NM_SECRET_AGENT_ERROR_NO_SECRETS,
+		                     "%s.%d (%s): no secrets found",
+		                     __FILE__, __LINE__, __func__);
 		goto error;
 	}
 
-	// FIXME: get secrets locally and populate connection with previous secrets
-	// before asking user for other secrets
-
-	/* Let the device class handle secrets */
-	if (dclass->get_secrets (device, connection,
-	                         active_connection, setting_name, hints, callback,
-	                         callback_data, applet, &error))
-		return;  /* success */
+	g_assert (dclass->secrets_request_size);
+	req = applet_secrets_request_new (dclass->secrets_request_size,
+	                                  connection,
+	                                  request_id,
+	                                  setting_name,
+	                                  hints,
+	                                  callback,
+	                                  callback_data,
+	                                  applet);
+	applet->secrets_reqs = g_slist_prepend (applet->secrets_reqs, req);
+
+	/* Get existing secrets, if any */
+	nm_secret_agent_get_secrets (NM_SECRET_AGENT (applet->agent),
+			                     connection,
+			                     setting_name,
+			                     hints,
+			                     FALSE,
+			                     get_existing_secrets_cb,
+			                     req);
+	return;
 
 error:
 	g_warning ("%s", error->message);
-	callback (connection, NULL, error, callback_data);
+	callback (agent, NULL, error, callback_data);
 	g_error_free (error);
+
+	if (req)
+		applet_secrets_request_free (req);
+}
+
+static void
+applet_agent_cancel_secrets_cb (AppletAgent *agent,
+                                void *request_id,
+                                gpointer user_data)
+{
+	NMApplet *applet = NM_APPLET (user_data);
+	GSList *iter;
+
+	for (iter = applet->secrets_reqs; iter; iter = g_slist_next (iter)) {
+		SecretsRequest *req = iter->data;
+
+		if (req->reqid == request_id) {
+			/* cancel and free this password request */
+			applet_secrets_request_free (req);
+		}
+	}
 }
-#endif
 
 /*****************************************************************************/
 
@@ -2926,6 +3133,10 @@ constructor (GType type,
 
 	applet->agent = applet_agent_new ();
 	g_assert (applet->agent);
+	g_signal_connect (applet->agent, APPLET_AGENT_GET_SECRETS,
+	                  G_CALLBACK (applet_agent_get_secrets_cb), applet);
+	g_signal_connect (applet->agent, APPLET_AGENT_CANCEL_SECRETS,
+	                  G_CALLBACK (applet_agent_cancel_secrets_cb), applet);
 
 	/* Initialize device classes */
 	applet->wired_class = applet_device_wired_get_class (applet);
@@ -2981,6 +3192,9 @@ static void finalize (GObject *object)
 
 	g_free (applet->tip);
 
+	g_slist_foreach (applet->secrets_reqs, (GFunc) g_object_unref, NULL);
+	g_slist_free (applet->secrets_reqs);
+
 	if (applet->notification) {
 		notify_notification_close (applet->notification, NULL);
 		g_object_unref (applet->notification);
diff --git a/src/applet.h b/src/applet.h
index 87c762e..569cd72 100644
--- a/src/applet.h
+++ b/src/applet.h
@@ -163,6 +163,9 @@ typedef struct
 	GtkBuilder *	info_dialog_ui;
 	NotifyNotification*	notification;
 	gboolean        notify_actions;
+
+	/* Tracker objects for secrets requests */
+	GSList *        secrets_reqs;
 } NMApplet;
 
 typedef void (*AppletNewAutoConnectionCallback) (NMConnection *connection,
@@ -170,10 +173,33 @@ typedef void (*AppletNewAutoConnectionCallback) (NMConnection *connection,
                                                  gboolean canceled,
                                                  gpointer user_data);
 
-typedef void (*NMANewSecretsRequestedFunc) (NMRemoteConnection *connection,
-                                            GHashTable *settings,
-                                            GError *error,
-                                            gpointer user_data);
+typedef struct _SecretsRequest SecretsRequest;
+typedef void (*SecretsRequestFreeFunc) (SecretsRequest *req);
+
+struct _SecretsRequest {
+	size_t totsize;
+	gpointer reqid;
+	char *setting_name;
+	char **hints;
+	NMApplet *applet;
+	AppletAgentSecretsCallback callback;
+	gpointer callback_data;
+
+	NMConnection *connection;
+
+	/* Class-specific stuff */
+	SecretsRequestFreeFunc free_func;
+};
+
+void applet_secrets_request_set_free_func (SecretsRequest *req,
+                                           SecretsRequestFreeFunc free_func);
+void applet_secrets_request_complete (SecretsRequest *req,
+                                      GHashTable *settings,
+                                      GError *error);
+void applet_secrets_request_complete_setting (SecretsRequest *req,
+                                              const char *setting_name,
+                                              GError *error);
+void applet_secrets_request_free (SecretsRequest *req);
 
 struct NMADeviceClass {
 	gboolean       (*new_auto_connection)  (NMDevice *device,
@@ -206,14 +232,8 @@ struct NMADeviceClass {
 	                                        NMApplet *applet,
 	                                        gpointer user_data);
 
-	gboolean       (*get_secrets)          (NMDevice *device,
-	                                        NMRemoteConnection *connection,
-	                                        NMActiveConnection *active_connection,
-	                                        const char *setting_name,
-	                                        const char **hints,
-	                                        NMANewSecretsRequestedFunc callback,
-	                                        gpointer callback_data,
-	                                        NMApplet *applet,
+	size_t         secrets_request_size;
+	gboolean       (*get_secrets)          (SecretsRequest *req,
 	                                        GError **error);
 };
 
@@ -254,6 +274,8 @@ applet_menu_item_create_device_item_helper (NMDevice *device,
 
 NMRemoteConnection *applet_get_exported_connection_for_device (NMDevice *device, NMApplet *applet);
 
+NMDevice *applet_get_device_for_connection (NMApplet *applet, NMConnection *connection);
+
 void applet_do_notify (NMApplet *applet,
                        NotifyUrgency urgency,
                        const char *summary,
diff --git a/src/gconf-helpers/gconf-helpers.c b/src/gconf-helpers/gconf-helpers.c
index b0865e0..1db2d7a 100644
--- a/src/gconf-helpers/gconf-helpers.c
+++ b/src/gconf-helpers/gconf-helpers.c
@@ -1989,24 +1989,15 @@ nm_gconf_add_keyring_item (const char *connection_uuid,
 	g_return_if_fail (setting_key != NULL);
 	g_return_if_fail (secret != NULL);
 
-	display_name = g_strdup_printf ("Network secret for %s/%s/%s",
-	                                connection_name,
-	                                setting_name,
-	                                setting_key);
-
-	attrs = gnome_keyring_attribute_list_new ();
-	gnome_keyring_attribute_list_append_string (attrs,
-	                                            KEYRING_UUID_TAG,
-	                                            connection_uuid);
-	gnome_keyring_attribute_list_append_string (attrs,
-	                                            KEYRING_SN_TAG,
-	                                            setting_name);
-	gnome_keyring_attribute_list_append_string (attrs,
-	                                            KEYRING_SK_TAG,
-	                                            setting_key);
-
 	pre_keyring_callback ();
 
+	attrs = utils_create_keyring_add_attr_list (NULL,
+	                                            connection_uuid,
+	                                            connection_name,
+	                                            setting_name,
+	                                            setting_key,
+	                                            &display_name);
+	g_assert (attrs);
 	ret = gnome_keyring_item_create_sync (NULL,
 	                                      GNOME_KEYRING_ITEM_GENERIC_SECRET,
 	                                      display_name,
@@ -2014,7 +2005,6 @@ nm_gconf_add_keyring_item (const char *connection_uuid,
 	                                      secret,
 	                                      TRUE,
 	                                      &id);
-
 	gnome_keyring_attribute_list_free (attrs);
 	g_free (display_name);
 }
diff --git a/src/marshallers/nma-marshal.list b/src/marshallers/nma-marshal.list
index 5aef346..33cbac8 100644
--- a/src/marshallers/nma-marshal.list
+++ b/src/marshallers/nma-marshal.list
@@ -1,7 +1,6 @@
 VOID:POINTER
 VOID:STRING,STRING,STRING
-VOID:STRING,POINTER,BOOLEAN,POINTER,POINTER
-VOID:OBJECT,STRING,POINTER,BOOLEAN,POINTER,POINTER
+VOID:POINTER,POINTER,STRING,POINTER,BOOLEAN,POINTER,POINTER
 VOID:POINTER,POINTER
 VOID:INT,POINTER
 VOID:STRING,BOXED
diff --git a/src/utils/utils.c b/src/utils/utils.c
index de99a2a..7b1642d 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -17,7 +17,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2011 Red Hat, Inc.
  */
 
 #include <config.h>
@@ -761,3 +761,46 @@ utils_escape_notify_message (const char *src)
 	return g_string_free (escaped, FALSE);
 }
 
+GnomeKeyringAttributeList *
+utils_create_keyring_add_attr_list (NMConnection *connection,
+                                    const char *connection_uuid,
+                                    const char *connection_id,
+                                    const char *setting_name,
+                                    const char *setting_key,
+                                    char **out_display_name)
+{
+	GnomeKeyringAttributeList *attrs = NULL;
+	NMSettingConnection *s_con;
+
+	if (connection) {
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_return_val_if_fail (s_con != NULL, NULL);
+		connection_uuid = nm_setting_connection_get_uuid (s_con);
+		connection_id = nm_setting_connection_get_id (s_con);
+	}
+
+	g_return_val_if_fail (connection_uuid != NULL, NULL);
+	g_return_val_if_fail (connection_id != NULL, NULL);
+	g_return_val_if_fail (setting_name != NULL, NULL);
+	g_return_val_if_fail (setting_key != NULL, NULL);
+
+	if (out_display_name) {
+		*out_display_name = g_strdup_printf ("Network secret for %s/%s/%s",
+		                                     connection_id,
+		                                     setting_name,
+		                                     setting_key);
+	}
+
+	attrs = gnome_keyring_attribute_list_new ();
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_UUID_TAG,
+	                                            connection_uuid);
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_SN_TAG,
+	                                            setting_name);
+	gnome_keyring_attribute_list_append_string (attrs,
+	                                            KEYRING_SK_TAG,
+	                                            setting_key);
+	return attrs;
+}
+
diff --git a/src/utils/utils.h b/src/utils/utils.h
index 07d29d8..052bb3f 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -17,7 +17,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2007 Red Hat, Inc.
+ * (C) Copyright 2007 - 2011 Red Hat, Inc.
  */
 
 #ifndef UTILS_H
@@ -28,6 +28,7 @@
 #include <nm-device.h>
 #include <net/ethernet.h>
 #include <nm-access-point.h>
+#include <gnome-keyring.h>
 
 const char *utils_get_device_description (NMDevice *device);
 
@@ -57,5 +58,16 @@ char *utils_hash_ap (const GByteArray *ssid,
 
 char *utils_escape_notify_message (const char *src);
 
+#define KEYRING_UUID_TAG "connection-uuid"
+#define KEYRING_SN_TAG "setting-name"
+#define KEYRING_SK_TAG "setting-key"
+
+GnomeKeyringAttributeList *utils_create_keyring_add_attr_list (NMConnection *connection,
+                                                               const char *connection_uuid,
+                                                               const char *connection_id,
+                                                               const char *setting_name,
+                                                               const char *setting_key,
+                                                               char **out_display_name);
+
 #endif /* UTILS_H */
 
diff --git a/src/wired-dialog.c b/src/wired-dialog.c
index 9a8efae..502fbdf 100644
--- a/src/wired-dialog.c
+++ b/src/wired-dialog.c
@@ -18,7 +18,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 - 2010 Red Hat, Inc.
+ * (C) Copyright 2008 - 2011 Red Hat, Inc.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -79,7 +79,6 @@ dialog_set_security (NMConnection *connection,
 static gboolean
 dialog_init (GtkWidget *dialog,
              GtkBuilder *builder,
-             NMClient *nm_client,
              NMConnection *connection)
 {
 	WirelessSecurity *security;
@@ -114,10 +113,7 @@ dialog_init (GtkWidget *dialog,
 }
 
 GtkWidget *
-nma_wired_dialog_new (const char *ui_file,
-					  NMClient *nm_client,
-					  NMRemoteConnection *connection,
-					  NMDevice *device)
+nma_wired_dialog_new (const char *ui_file, NMConnection *connection)
 {
 	GtkBuilder *builder;
 	GtkWidget *dialog;
@@ -141,7 +137,7 @@ nma_wired_dialog_new (const char *ui_file,
 		return NULL;
 	}
 
-	success = dialog_init (dialog, builder, nm_client, NM_CONNECTION (connection));
+	success = dialog_init (dialog, builder, connection);
 	if (!success) {
 		nm_warning ("Couldn't create wired security dialog.");
 		gtk_widget_destroy (dialog);
@@ -161,12 +157,11 @@ nma_wired_dialog_new (const char *ui_file,
 	return dialog;
 }
 					  
-NMRemoteConnection *
+NMConnection *
 nma_wired_dialog_get_connection (GtkWidget *dialog)
 {
-	NMRemoteConnection *connection;
+	NMConnection *connection, *tmp_connection;
 	WirelessSecurity *security;
-	NMConnection *tmp_connection;
 	NMSetting *s_8021x, *s_con;
 
 	g_return_val_if_fail (dialog != NULL, NULL);
@@ -180,7 +175,7 @@ nma_wired_dialog_get_connection (GtkWidget *dialog)
 	tmp_connection = nm_connection_new ();
 
 	/* Add the fake connection setting (mainly for the UUID for cert ignore checking) */
-	s_con = nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION);
+	s_con = nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
 	g_assert (s_con);
 	nm_connection_add_setting (tmp_connection, NM_SETTING (g_object_ref (s_con)));
 
@@ -192,7 +187,7 @@ nma_wired_dialog_get_connection (GtkWidget *dialog)
 
 	/* Grab it and add it to our original connection */
 	s_8021x = nm_connection_get_setting (tmp_connection, NM_TYPE_SETTING_802_1X);
-	nm_connection_add_setting (NM_CONNECTION (connection), NM_SETTING (g_object_ref (s_8021x)));
+	nm_connection_add_setting (connection, NM_SETTING (g_object_ref (s_8021x)));
 
 	g_object_unref (tmp_connection);
 
diff --git a/src/wired-dialog.h b/src/wired-dialog.h
index 954b11a..81a84dd 100644
--- a/src/wired-dialog.h
+++ b/src/wired-dialog.h
@@ -18,23 +18,17 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 Red Hat, Inc.
+ * (C) Copyright 2008 - 2011 Red Hat, Inc.
  */
 
 #ifndef WIRED_DIALOG_H
 #define WIRED_DIALOG_H
 
 #include <gtk/gtk.h>
-#include <nm-client.h>
 #include <nm-connection.h>
-#include <nm-device.h>
-#include <nm-remote-connection.h>
 
-GtkWidget *nma_wired_dialog_new (const char *ui_file,
-								 NMClient *nm_client,
-								 NMRemoteConnection *connection,
-								 NMDevice *device);
+GtkWidget *nma_wired_dialog_new (const char *ui_file, NMConnection *connection);
 
-NMRemoteConnection *nma_wired_dialog_get_connection (GtkWidget *dialog);
+NMConnection *nma_wired_dialog_get_connection (GtkWidget *dialog);
 
 #endif /* WIRED_DIALOG_H */



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