[gnome-shell] Add a new network agent for the Shell



commit 2af5e851b37d036de3d30d3165cdd57f63a2f79a
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon May 16 20:12:17 2011 +0200

    Add a new network agent for the Shell
    
    A network agent is a component that stores network secrets (like
    wifi passwords) in the session keyring. This commit adds an
    implementation of it to be used by the shell network dialogs. It
    handles most of the keyring stuff, delegating the UI to upper layers.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=650244

 configure.ac              |    9 +-
 src/Makefile.am           |    6 +-
 src/shell-marshal.list    |    1 +
 src/shell-network-agent.c |  783 +++++++++++++++++++++++++++++++++++++++++++++
 src/shell-network-agent.h |   57 ++++
 5 files changed, 847 insertions(+), 9 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5ca8049..87e1e90 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,7 +93,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
 			       libcanberra
                                telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
                                telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
-                               polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
+                               polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
+                               libnm-glib libnm-util gnome-keyring-1)
 
 PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
 
@@ -105,12 +106,6 @@ AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
 
 GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
-# NM is the only typelib we use that we don't jhbuild
-PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
-        [NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
-	 if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
-	     JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
-	 fi])
 AC_SUBST(JHBUILD_TYPELIBDIR)
 
 saved_CFLAGS=$CFLAGS
diff --git a/src/Makefile.am b/src/Makefile.am
index b20e03d..be7351a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -110,10 +110,11 @@ shell_public_headers_h =		\
 	shell-global.h			\
 	shell-mobile-providers.h	\
 	shell-mount-operation.h		\
+	shell-network-agent.h		\
 	shell-perf-log.h		\
 	shell-slicer.h			\
 	shell-stack.h			\
-	shell-tp-client.h			\
+	shell-tp-client.h		\
 	shell-tray-icon.h		\
 	shell-tray-manager.h		\
 	shell-util.h			\
@@ -144,6 +145,7 @@ libgnome_shell_la_SOURCES =		\
 	shell-global.c			\
 	shell-mobile-providers.c	\
 	shell-mount-operation.c		\
+	shell-network-agent.c		\
 	shell-perf-log.c		\
 	shell-polkit-authentication-agent.h	\
 	shell-polkit-authentication-agent.c	\
@@ -269,7 +271,7 @@ libgnome_shell_la_LIBADD =		\
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 Shell-0.1.gir: libgnome-shell.la St-1.0.gir
-Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0
+Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
 Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
 Shell_0_1_gir_LIBS = libgnome-shell.la
 Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
diff --git a/src/shell-marshal.list b/src/shell-marshal.list
index 9bc1cb0..64b8235 100644
--- a/src/shell-marshal.list
+++ b/src/shell-marshal.list
@@ -6,3 +6,4 @@ VOID:OBJECT,OBJECT
 VOID:STRING,OBJECT,BOOLEAN
 VOID:INT,INT
 VOID:STRING,STRING,STRING,STRING,BOXED
+VOID:STRING,OBJECT,STRING,BOXED
diff --git a/src/shell-network-agent.c b/src/shell-network-agent.c
new file mode 100644
index 0000000..8a474ac
--- /dev/null
+++ b/src/shell-network-agent.c
@@ -0,0 +1,783 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *           2011 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+#include <string.h>
+#include <gnome-keyring.h>
+
+#include "shell-network-agent.h"
+#include "shell-marshal.h"
+
+enum {
+  SIGNAL_NEW_REQUEST,
+  SIGNAL_CANCEL_REQUEST,
+  SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+typedef struct {
+  gpointer                       keyring_op;
+  ShellNetworkAgent             *self;
+
+  gchar                         *request_id;
+  NMConnection                  *connection;
+  gchar                         *setting_name;
+  gchar                        **hints;
+  NMSecretAgentGetSecretsFlags   flags;
+  NMSecretAgentGetSecretsFunc    callback;
+  gpointer                       callback_data;
+
+  /* <gchar *setting_key, gchar *secret> */
+  GHashTable                    *entries;
+} ShellAgentRequest;
+
+struct _ShellNetworkAgentPrivate {
+  /* <gchar *request_id, ShellAgentRequest *request> */
+  GHashTable *requests;
+};
+
+G_DEFINE_TYPE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT)
+
+static void
+shell_agent_request_free (gpointer data)
+{
+  ShellAgentRequest *request = data;
+
+  if (request->keyring_op)
+    gnome_keyring_cancel_request (request->keyring_op);
+
+  g_object_unref (request->self);
+  g_object_unref (request->connection);
+  g_free (request->setting_name);
+  g_strfreev (request->hints);
+
+  g_hash_table_destroy (request->entries);
+
+  g_slice_free (ShellAgentRequest, request);
+}
+
+static void
+shell_network_agent_init (ShellNetworkAgent *agent)
+{
+  ShellNetworkAgentPrivate *priv;
+
+  priv = agent->priv = G_TYPE_INSTANCE_GET_PRIVATE (agent, SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentPrivate);
+
+  priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
+					  g_free, shell_agent_request_free);
+}
+
+static void
+shell_network_agent_finalize (GObject *object)
+{
+  ShellNetworkAgentPrivate *priv = SHELL_NETWORK_AGENT (object)->priv;
+  GError *error;
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+
+  error = g_error_new (NM_SECRET_AGENT_ERROR,
+                       NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+                       "The secret agent is going away");
+
+  g_hash_table_iter_init (&iter, priv->requests);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      ShellAgentRequest *request = value;
+
+      request->callback (NM_SECRET_AGENT (object),
+                         request->connection,
+                         NULL, error,
+                         request->callback_data);
+    }
+
+  g_hash_table_destroy (priv->requests);
+  g_error_free (error);
+
+  G_OBJECT_CLASS (shell_network_agent_parent_class)->finalize (object);
+}
+
+static void
+request_secrets_from_ui (ShellAgentRequest *request)
+{
+  g_signal_emit (request->self, signals[SIGNAL_NEW_REQUEST], 0,
+                 request->request_id,
+                 request->connection,
+                 request->setting_name,
+                 request->hints);
+}
+
+static void
+check_always_ask_cb (NMSetting    *setting,
+                     const gchar  *key,
+                     const GValue *value,
+                     GParamFlags   flags,
+                     gpointer      user_data)
+{
+  gboolean *always_ask = user_data;
+  NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+  if (flags & NM_SETTING_PARAM_SECRET)
+    {
+      if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
+        {
+          if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+            *always_ask = TRUE;
+        }
+    }
+}
+
+static gboolean
+has_always_ask (NMSetting *setting)
+{
+  gboolean always_ask = FALSE;
+
+  nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask);
+  return always_ask;
+}
+
+static gboolean
+is_connection_always_ask (NMConnection *connection)
+{
+  NMSettingConnection *s_con;
+  const gchar *ctype;
+  NMSetting *setting;
+
+  /* For the given connection type, check if the secrets for that connection
+   * are always-ask or not.
+   */
+  s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+  g_assert (s_con);
+  ctype = nm_setting_connection_get_connection_type (s_con);
+
+  setting = nm_connection_get_setting_by_name (connection, ctype);
+  g_return_val_if_fail (setting != NULL, FALSE);
+
+  if (has_always_ask (setting))
+    return TRUE;
+
+  /* Try type-specific settings too; be a bit paranoid and only consider
+   * secrets from settings relevant to the connection type.
+   */
+  if (NM_IS_SETTING_WIRELESS (setting))
+    {
+      setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+      if (setting && has_always_ask (setting))
+        return TRUE;
+      setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+      if (setting && has_always_ask (setting))
+        return TRUE;
+	}
+  else if (NM_IS_SETTING_WIRED (setting))
+    {
+      setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+      if (setting && has_always_ask (setting))
+        return TRUE;
+      setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+      if (setting && has_always_ask (setting))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gvalue_destroy_notify (gpointer data)
+{
+  GValue *value = data;
+  g_value_unset (value);
+  g_slice_free (GValue, value);
+}
+
+static gboolean
+strv_has (gchar **haystack,
+          gchar  *needle)
+{
+  gchar *iter;
+  for (iter = *haystack; iter; iter++)
+    {
+      if (g_strcmp0 (iter, needle) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+get_secrets_keyring_cb (GnomeKeyringResult  result,
+			GList              *list,
+			gpointer            user_data)
+{
+  ShellAgentRequest *closure = user_data;
+  ShellNetworkAgent *self = closure->self;
+  ShellNetworkAgentPrivate *priv = self->priv;
+  GError *error = NULL;
+  gint n_found = 0;
+  GList *iter;
+  GHashTable *outer;
+
+  closure->keyring_op = NULL;
+
+  if (result == GNOME_KEYRING_RESULT_CANCELLED)
+    {
+      g_set_error (&error,
+		   NM_SECRET_AGENT_ERROR,
+		   NM_SECRET_AGENT_ERROR_USER_CANCELED,
+		   "The secret request was cancelled by the user");
+
+      closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
+
+      goto out;
+    }
+
+  if (result != GNOME_KEYRING_RESULT_OK &&
+      result != GNOME_KEYRING_RESULT_NO_MATCH)
+    {
+      g_set_error (&error,
+		   NM_SECRET_AGENT_ERROR,
+		   NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+		   "Internal error while retrieving secrets from the keyring (result %d)", result);
+
+      closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
+
+      goto out;
+    }
+
+  for (iter = list; iter; iter = g_list_next (iter))
+    {
+      GnomeKeyringFound *item = iter->data;
+      int i;
+
+      for (i = 0; i < item->attributes->len; i++)
+        {
+          GnomeKeyringAttribute *attr = &gnome_keyring_attribute_list_index (item->attributes, i);
+
+          if (g_strcmp0 (attr->name, SHELL_KEYRING_SK_TAG) == 0
+              && (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING))
+            {
+              gchar *secret_name = g_strdup (attr->value.string);
+              GValue *secret_value = g_slice_new0 (GValue);
+              g_value_init (secret_value, G_TYPE_STRING);
+              g_value_set_string (secret_value, item->secret);
+
+              g_hash_table_insert (closure->entries, secret_name, secret_value);
+
+              if (closure->hints)
+                n_found += strv_has (closure->hints, secret_name);
+              else
+                n_found += 1;
+
+              break;
+            }
+        }
+    }
+
+  if (n_found == 0 &&
+      (closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION))
+    {
+      /* Even if n_found == 0, secrets is not necessarily empty */
+      nm_connection_update_secrets (closure->connection, closure->setting_name, closure->entries, NULL);
+
+      request_secrets_from_ui (closure);
+      return;
+    }
+
+  outer = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (outer, closure->setting_name, closure->entries);
+
+  closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, outer, NULL, closure->callback_data);
+
+  g_hash_table_destroy (outer);
+
+ out:
+  g_hash_table_remove (priv->requests, closure->request_id);
+  g_clear_error (&error);
+}
+
+static void
+shell_network_agent_get_secrets (NMSecretAgent                 *agent,
+				 NMConnection                  *connection,
+				 const gchar                   *connection_path,
+				 const gchar                   *setting_name,
+				 const gchar                  **hints,
+				 NMSecretAgentGetSecretsFlags   flags,
+				 NMSecretAgentGetSecretsFunc    callback,
+				 gpointer                       callback_data)
+{
+  ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
+  ShellAgentRequest *request = g_slice_new (ShellAgentRequest);
+
+  request->self = g_object_ref (self);
+  request->connection = g_object_ref (connection);
+  request->setting_name = g_strdup (setting_name);
+  request->hints = g_strdupv ((gchar **)hints);
+  request->flags = flags;
+  request->callback = callback;
+  request->callback_data = callback_data;
+  request->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gvalue_destroy_notify);
+
+  request->request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
+  g_hash_table_replace (self->priv->requests, request->request_id, request);
+
+  if ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) ||
+      (flags && NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+       && is_connection_always_ask (request->connection)))
+    {
+      request_secrets_from_ui (request);
+      return;
+    }
+
+  request->keyring_op = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+						   get_secrets_keyring_cb,
+						   request,
+						   NULL, /* GDestroyNotify */
+						   SHELL_KEYRING_UUID_TAG,
+						   GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+						   nm_connection_get_uuid (connection),
+						   SHELL_KEYRING_SN_TAG,
+						   GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+						   setting_name,
+						   NULL);  
+}
+
+void
+shell_network_agent_set_password (ShellNetworkAgent *self,
+                                  gchar             *request_id,
+                                  gchar             *setting_key,
+                                  gchar             *setting_value)
+{
+  ShellNetworkAgentPrivate *priv;
+  ShellAgentRequest *request;
+  GValue *value;
+
+  g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+
+  priv = self->priv;
+  request = g_hash_table_lookup (priv->requests, request_id);
+
+  value = g_slice_new0 (GValue);
+  g_value_init (value, G_TYPE_STRING);
+  g_value_set_string (value, setting_value);
+
+  g_hash_table_replace (request->entries, g_strdup (setting_key), value);
+}
+
+void
+shell_network_agent_respond (ShellNetworkAgent *self,
+                             gchar             *request_id,
+                             gboolean           canceled)
+{
+  ShellNetworkAgentPrivate *priv;
+  ShellAgentRequest *request;
+  NMConnection *dup;
+  GHashTable *outer;
+
+  g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
+
+  priv = self->priv;
+  request = g_hash_table_lookup (priv->requests, request_id);
+
+  if (canceled)
+    {
+      GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
+                                   NM_SECRET_AGENT_ERROR_USER_CANCELED,
+                                   "Network dialog was canceled by the user");
+
+      request->callback (NM_SECRET_AGENT (self), request->connection, NULL, error, request->callback_data);
+      g_error_free (error);
+      g_hash_table_remove (priv->requests, request_id);
+      return;
+    }
+
+  /* Save updated secrets */
+  dup = nm_connection_duplicate (request->connection);
+  nm_connection_update_secrets (dup, request->setting_name, request->entries, NULL);
+
+  nm_secret_agent_save_secrets (NM_SECRET_AGENT (self), dup, NULL, NULL);
+
+  outer = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (outer, request->setting_name, request->entries);
+
+  request->callback (NM_SECRET_AGENT (self), request->connection, outer, NULL, request->callback_data);
+
+  g_hash_table_destroy (outer);
+  g_object_unref (dup);
+  g_hash_table_remove (priv->requests, request_id);
+}
+
+static void
+shell_network_agent_cancel_get_secrets (NMSecretAgent *agent,
+                                        const gchar   *connection_path,
+                                        const gchar   *setting_name)
+{
+  ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
+  ShellNetworkAgentPrivate *priv = self->priv;
+
+  gchar *request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
+  ShellAgentRequest *request = g_hash_table_lookup (priv->requests, request_id);
+
+  GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
+                               NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+                               "Canceled by NetworkManager");
+  request->callback (agent, request->connection, NULL, error, request->callback_data);
+
+  g_signal_emit (self, signals[SIGNAL_CANCEL_REQUEST], 0, request_id);
+
+  g_hash_table_remove (priv->requests, request_id);
+  g_free (request_id);
+  g_error_free (error);
+}
+
+/************************* saving of secrets ****************************************/
+
+static GnomeKeyringAttributeList *
+create_keyring_add_attr_list (NMConnection *connection,
+                              const gchar  *connection_uuid,
+                              const gchar  *connection_id,
+                              const gchar  *setting_name,
+                              const gchar  *setting_key,
+                              gchar       **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,
+                                              SHELL_KEYRING_UUID_TAG,
+                                              connection_uuid);
+  gnome_keyring_attribute_list_append_string (attrs,
+                                              SHELL_KEYRING_SN_TAG,
+                                              setting_name);
+  gnome_keyring_attribute_list_append_string (attrs,
+                                              SHELL_KEYRING_SK_TAG,
+                                              setting_key);
+  return attrs;
+}
+
+typedef struct
+{
+  /* Sort of ref count, indicates the number of secrets we still need to save */
+  gint           n_secrets;
+
+  NMSecretAgent *self;
+  NMConnection  *connection;
+  gpointer       callback;
+  gpointer       callback_data;
+} KeyringRequest;
+
+static void
+keyring_request_free (KeyringRequest *r)
+{
+  g_object_unref (r->self);
+  g_object_unref (r->connection);
+
+  g_slice_free (KeyringRequest, r);
+}
+
+static void
+save_secret_cb (GnomeKeyringResult result,
+                guint              val,
+                gpointer           user_data)
+{
+  KeyringRequest *call = user_data;
+  NMSecretAgentSaveSecretsFunc callback = call->callback;
+
+  call->n_secrets--;
+
+  if (call->n_secrets == 0)
+    {
+      if (callback)
+        callback (call->self, call->connection, NULL, call->callback_data);
+      keyring_request_free (call);
+    }
+}
+
+static void
+save_one_secret (KeyringRequest *r,
+                 NMSetting      *setting,
+                 const gchar    *key,
+                 const gchar    *secret,
+                 const gchar    *display_name)
+{
+  GnomeKeyringAttributeList *attrs;
+  gchar *alt_display_name = NULL;
+  const gchar *setting_name;
+  NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+  /* Only save agent-owned secrets (not system-owned or always-ask) */
+  nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
+  if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+    return;
+
+  setting_name = nm_setting_get_name (setting);
+  g_assert (setting_name);
+
+  attrs = create_keyring_add_attr_list (r->connection, NULL, NULL,
+                                        setting_name,
+                                        key,
+                                        display_name ? NULL : &alt_display_name);
+  g_assert (attrs);
+  r->n_secrets++;
+  gnome_keyring_item_create (NULL,
+                             GNOME_KEYRING_ITEM_GENERIC_SECRET,
+                             display_name ? display_name : alt_display_name,
+                             attrs,
+                             secret,
+                             TRUE,
+                             save_secret_cb,
+                             r,
+                             NULL);
+
+  gnome_keyring_attribute_list_free (attrs);
+  g_free (alt_display_name);
+}
+
+static void
+vpn_secret_iter_cb (const gchar *key,
+                    const gchar *secret,
+                    gpointer     user_data)
+{
+  KeyringRequest *r = user_data;
+  NMSetting *setting;
+  const gchar *service_name, *id;
+  gchar *display_name;
+
+  if (secret && strlen (secret))
+    {
+      setting = nm_connection_get_setting (r->connection, NM_TYPE_SETTING_VPN);
+      g_assert (setting);
+      service_name = nm_setting_vpn_get_service_type (NM_SETTING_VPN (setting));
+      g_assert (service_name);
+      id = nm_connection_get_id (r->connection);
+      g_assert (id);
+
+      display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME,
+                                      key,
+                                      id,
+                                      service_name);
+      save_one_secret (r, setting, key, secret, display_name);
+      g_free (display_name);
+    }
+}
+
+static void
+write_one_secret_to_keyring (NMSetting    *setting,
+                             const gchar  *key,
+                             const GValue *value,
+                             GParamFlags   flags,
+                             gpointer      user_data)
+{
+  KeyringRequest *r = user_data;
+  const gchar *secret;
+
+  /* Non-secrets obviously don't get saved in the keyring */
+  if (!(flags & NM_SETTING_PARAM_SECRET))
+    return;
+
+  if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (key, NM_SETTING_VPN_SECRETS) == 0))
+    {
+      /* Process VPN secrets specially since it's a hash of secrets, not just one */
+      nm_setting_vpn_foreach_secret (NM_SETTING_VPN (setting),
+                                     vpn_secret_iter_cb,
+                                     r);
+    }
+  else
+    {
+      secret = g_value_get_string (value);
+      if (secret && strlen (secret))
+        save_one_secret (r, setting, key, secret, NULL);
+  }
+}
+
+static void
+save_delete_cb (NMSecretAgent *agent,
+                NMConnection  *connection,
+                GError        *error,
+                gpointer       user_data)
+{
+  KeyringRequest *r = user_data;
+
+  /* Ignore errors; now save all new secrets */
+  nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
+
+  /* If no secrets actually got saved there may be nothing to do so
+   * try to complete the request here. If there were secrets to save the
+   * request will get completed when those keyring calls return (at the next
+   * mainloop iteration).
+   */
+  if (r->n_secrets == 0)
+    {
+      if (r->callback)
+        ((NMSecretAgentSaveSecretsFunc)r->callback) (agent, connection, NULL, r->callback_data);
+      keyring_request_free (r);
+    }
+}
+
+static void
+shell_network_agent_save_secrets (NMSecretAgent                *agent,
+                                  NMConnection                 *connection,
+                                  const gchar                  *connection_path,
+                                  NMSecretAgentSaveSecretsFunc  callback,
+                                  gpointer                      callback_data)
+{
+  KeyringRequest *r;
+
+  r = g_slice_new (KeyringRequest);
+  r->n_secrets = 0;
+  r->self = g_object_ref (agent);
+  r->connection = g_object_ref (connection);
+  r->callback = callback;
+  r->callback_data = callback_data;
+
+  /* First delete any existing items in the keyring */
+  nm_secret_agent_delete_secrets (agent, connection, save_delete_cb, r);
+}
+
+static void
+keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
+{
+  /* Ignored */
+}
+
+static void
+delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
+{
+  KeyringRequest *r = user_data;
+  GList *iter;
+  GError *error = NULL;
+  NMSecretAgentDeleteSecretsFunc callback = r->callback;
+
+  if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH))
+    {
+      for (iter = list; iter != NULL; iter = g_list_next (iter))
+        {
+          GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
+
+          gnome_keyring_item_delete (found->keyring, found->item_id, keyring_delete_cb, 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);
+    }
+
+  callback (r->self, r->connection, error, r->callback_data);
+  g_clear_error (&error);
+}
+
+static void
+shell_network_agent_delete_secrets (NMSecretAgent                  *agent,
+                                    NMConnection                   *connection,
+                                    const gchar                    *connection_path,
+                                    NMSecretAgentDeleteSecretsFunc  callback,
+                                    gpointer                        callback_data)
+{
+  KeyringRequest *r;
+  NMSettingConnection *s_con;
+  const gchar *uuid;
+
+  r = g_slice_new (KeyringRequest);
+  r->n_secrets = 0; /* ignored by delete secrets calls */
+  r->self = g_object_ref (agent);
+  r->connection = g_object_ref (connection);
+  r->callback = callback;
+  r->callback_data = callback_data;
+
+  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);
+
+  gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+                             delete_find_items_cb,
+                             r,
+                             (GDestroyNotify)keyring_request_free,
+                             SHELL_KEYRING_UUID_TAG,
+                             GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+                             uuid,
+                             NULL);
+}
+
+void
+shell_network_agent_class_init (ShellNetworkAgentClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass);
+
+  gobject_class->finalize = shell_network_agent_finalize;
+
+  agent_class->get_secrets = shell_network_agent_get_secrets;
+  agent_class->cancel_get_secrets = shell_network_agent_cancel_get_secrets;
+  agent_class->save_secrets = shell_network_agent_save_secrets;
+  agent_class->delete_secrets = shell_network_agent_delete_secrets;
+
+  signals[SIGNAL_NEW_REQUEST] = g_signal_new ("new-request",
+					      G_TYPE_FROM_CLASS (klass),
+					      0, /* flags */
+					      0, /* class offset */
+					      NULL, /* accumulator */
+					      NULL, /* accu_data */
+					      _shell_marshal_VOID__STRING_OBJECT_STRING_BOXED,
+					      G_TYPE_NONE, /* return */
+					      3, /* n_params */
+					      G_TYPE_STRING,
+					      NM_TYPE_CONNECTION,
+					      G_TYPE_STRING,
+                                              G_TYPE_STRV);
+
+  signals[SIGNAL_CANCEL_REQUEST] = g_signal_new ("cancel-request",
+                                                 G_TYPE_FROM_CLASS (klass),
+                                                 0, /* flags */
+                                                 0, /* class offset */
+                                                 NULL, /* accumulator */
+                                                 NULL, /* accu_data */
+                                                 g_cclosure_marshal_VOID__STRING,
+                                                 G_TYPE_NONE,
+                                                 1, /* n_params */
+                                                 G_TYPE_STRING);
+
+  g_type_class_add_private (klass, sizeof (ShellNetworkAgentPrivate));
+}
diff --git a/src/shell-network-agent.h b/src/shell-network-agent.h
new file mode 100644
index 0000000..179939a
--- /dev/null
+++ b/src/shell-network-agent.h
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_NETWORK_AGENT_H__
+#define __SHELL_NETWORK_AGENT_H__
+
+#include <glib-object.h>
+#include <glib.h>
+#include <nm-connection.h>
+#include <nm-secret-agent.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ShellNetworkAgent         ShellNetworkAgent;
+typedef struct _ShellNetworkAgentClass    ShellNetworkAgentClass;
+typedef struct _ShellNetworkAgentPrivate  ShellNetworkAgentPrivate;
+
+#define SHELL_TYPE_NETWORK_AGENT                  (shell_network_agent_get_type ())
+#define SHELL_NETWORK_AGENT(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgent))
+#define SHELL_IS_NETWORK_AGENT(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_NETWORK_AGENT))
+#define SHELL_NETWORK_AGENT_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentClass))
+#define SHELL_IS_NETWORK_AGENT_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_NETWORK_AGENT))
+#define SHELL_NETWORK_AGENT_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentClass))
+
+struct _ShellNetworkAgent
+{
+  /*< private >*/
+  NMSecretAgent parent_instance;
+
+  ShellNetworkAgentPrivate *priv;
+};
+
+struct _ShellNetworkAgentClass
+{
+  /*< private >*/
+  NMSecretAgentClass parent_class;
+};
+
+/* used by SHELL_TYPE_NETWORK_AGENT */
+GType shell_network_agent_get_type (void);
+
+ShellNetworkAgent *shell_network_agent_new (void);
+
+void               shell_network_agent_set_password (ShellNetworkAgent *self,
+                                                     gchar             *request_id,
+                                                     gchar             *setting_key,
+                                                     gchar             *setting_value);
+void               shell_network_agent_respond      (ShellNetworkAgent *self,
+                                                     gchar             *request_id,
+                                                     gboolean           canceled);
+
+/* If these are kept in sync with nm-applet, secrets will be shared */
+#define SHELL_KEYRING_UUID_TAG "connection-uuid"
+#define SHELL_KEYRING_SN_TAG "setting-name"
+#define SHELL_KEYRING_SK_TAG "setting-key"
+
+G_END_DECLS
+
+#endif /* __SHELL_NETWORK_AGENT_H__ */



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