[evolution-data-server] Bug 698726 - Handle goa-daemon crashes/restarts gracefully



commit 725e976e67a62bf00820c70c47a3f439811d6c0d
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sat Apr 27 08:47:18 2013 -0400

    Bug 698726 - Handle goa-daemon crashes/restarts gracefully
    
    EGoaClient is an improved GoaClient that handles daemon crashes and
    restarts gracefully so E-D-S accounts are not destroyed unnecessarily.

 modules/gnome-online-accounts/Makefile.am          |    2 +
 modules/gnome-online-accounts/e-goa-client.c       |  536 ++++++++++++++++++++
 modules/gnome-online-accounts/e-goa-client.h       |   93 ++++
 .../module-gnome-online-accounts.c                 |  147 ++++--
 4 files changed, 722 insertions(+), 56 deletions(-)
---
diff --git a/modules/gnome-online-accounts/Makefile.am b/modules/gnome-online-accounts/Makefile.am
index 547cc37..67448c3 100644
--- a/modules/gnome-online-accounts/Makefile.am
+++ b/modules/gnome-online-accounts/Makefile.am
@@ -14,6 +14,8 @@ module_gnome_online_accounts_la_CPPFLAGS = \
 
 module_gnome_online_accounts_la_SOURCES = \
        module-gnome-online-accounts.c \
+       e-goa-client.c \
+       e-goa-client.h \
        e-goa-password-based.c \
        e-goa-password-based.h \
        goaewsclient.c \
diff --git a/modules/gnome-online-accounts/e-goa-client.c b/modules/gnome-online-accounts/e-goa-client.c
new file mode 100644
index 0000000..2575933
--- /dev/null
+++ b/modules/gnome-online-accounts/e-goa-client.c
@@ -0,0 +1,536 @@
+/*
+ * e-goa-client.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-goa-client.h"
+
+#define E_GOA_CLIENT_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_GOA_CLIENT, EGoaClientPrivate))
+
+struct _EGoaClientPrivate {
+       GDBusObjectManager *object_manager;
+       gulong object_added_handler_id;
+       gulong object_removed_handler_id;
+       gulong notify_name_owner_handler_id;
+
+       /* ID -> GoaObject */
+       GHashTable *orphans;
+       GMutex orphans_lock;
+};
+
+enum {
+       PROP_0,
+       PROP_OBJECT_MANAGER
+};
+
+enum {
+       ACCOUNT_ADDED,
+       ACCOUNT_REMOVED,
+       ACCOUNT_SWAPPED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void    e_goa_client_interface_init
+                                       (GInitableIface *interface);
+
+/* By default, the GAsyncInitable interface calls GInitable.init()
+ * from a separate thread, so we only have to override GInitable. */
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (
+       EGoaClient,
+       e_goa_client,
+       G_TYPE_OBJECT,
+       0,
+       G_IMPLEMENT_INTERFACE_DYNAMIC (
+               G_TYPE_INITABLE,
+               e_goa_client_interface_init)
+       G_IMPLEMENT_INTERFACE_DYNAMIC (
+               G_TYPE_ASYNC_INITABLE,
+               NULL))
+
+static void
+e_goa_client_stash_orphan (EGoaClient *client,
+                           GoaObject *goa_object)
+{
+       GoaAccount *goa_account;
+       const gchar *goa_account_id;
+
+       goa_account = goa_object_peek_account (goa_object);
+       g_return_if_fail (goa_account != NULL);
+
+       goa_account_id = goa_account_get_id (goa_account);
+       g_return_if_fail (goa_account_id != NULL);
+
+       g_print ("GOA: Stashing orphaned account '%s'\n", goa_account_id);
+
+       g_mutex_lock (&client->priv->orphans_lock);
+
+       g_hash_table_replace (
+               client->priv->orphans,
+               g_strdup (goa_account_id),
+               g_object_ref (goa_object));
+
+       g_mutex_unlock (&client->priv->orphans_lock);
+}
+
+static GoaObject *
+e_goa_client_claim_one_orphan (EGoaClient *client,
+                               GoaObject *new_goa_object)
+{
+       GHashTable *orphans;
+       GoaAccount *goa_account;
+       GoaObject *old_goa_object;
+       const gchar *goa_account_id;
+
+       orphans = client->priv->orphans;
+
+       goa_account = goa_object_peek_account (new_goa_object);
+       g_return_val_if_fail (goa_account != NULL, NULL);
+
+       goa_account_id = goa_account_get_id (goa_account);
+       g_return_val_if_fail (goa_account_id != NULL, NULL);
+
+       g_print ("GOA: Claiming orphaned account '%s'\n", goa_account_id);
+
+       g_mutex_lock (&client->priv->orphans_lock);
+
+       old_goa_object = g_hash_table_lookup (orphans, goa_account_id);
+
+       if (old_goa_object != NULL) {
+               g_object_ref (old_goa_object);
+               g_hash_table_remove (orphans, goa_account_id);
+       }
+
+       g_mutex_unlock (&client->priv->orphans_lock);
+
+       return old_goa_object;
+}
+
+static GList *
+e_goa_client_claim_all_orphans (EGoaClient *client)
+{
+       GList *list;
+
+       g_mutex_lock (&client->priv->orphans_lock);
+
+       list = g_hash_table_get_values (client->priv->orphans);
+       g_list_foreach (list, (GFunc) g_object_ref, NULL);
+       g_hash_table_remove_all (client->priv->orphans);
+
+       g_mutex_unlock (&client->priv->orphans_lock);
+
+       if (list != NULL)
+               g_print ("GOA: Claiming orphaned account(s)\n");
+
+       return list;
+}
+
+static void
+e_goa_client_object_added_cb (GDBusObjectManager *manager,
+                              GDBusObject *object,
+                              EGoaClient *client)
+{
+       GoaObject *new_goa_object;
+       GoaObject *old_goa_object;
+
+       new_goa_object = GOA_OBJECT (object);
+
+       /* Only interested in objects with GoaAccount interfaces. */
+       if (goa_object_peek_account (new_goa_object) == NULL)
+               return;
+
+       old_goa_object =
+               e_goa_client_claim_one_orphan (client, new_goa_object);
+
+       if (old_goa_object != NULL) {
+               g_signal_emit (
+                       client,
+                       signals[ACCOUNT_SWAPPED], 0,
+                       old_goa_object,
+                       new_goa_object);
+       } else {
+               g_signal_emit (
+                       client,
+                       signals[ACCOUNT_ADDED], 0,
+                       new_goa_object);
+       }
+
+       g_clear_object (&old_goa_object);
+}
+
+static void
+e_goa_client_object_removed_cb (GDBusObjectManager *manager,
+                                GDBusObject *object,
+                                EGoaClient *client)
+{
+       GoaObject *goa_object;
+       gchar *name_owner;
+
+       goa_object = GOA_OBJECT (object);
+
+       /* Only interested in objects with GoaAccount interfaces. */
+       if (goa_object_peek_account (goa_object) == NULL)
+               return;
+
+       name_owner = g_dbus_object_manager_client_get_name_owner (
+               G_DBUS_OBJECT_MANAGER_CLIENT (manager));
+
+       if (name_owner != NULL) {
+               g_signal_emit (
+                       client,
+                       signals[ACCOUNT_REMOVED], 0,
+                       goa_object);
+       } else {
+               /* The goa-daemon went bye-bye. */
+               e_goa_client_stash_orphan (client, goa_object);
+       }
+
+       g_free (name_owner);
+}
+
+static void
+e_goa_client_notify_name_owner_cb (GDBusObjectManager *manager,
+                                   GParamSpec *pspec,
+                                   EGoaClient *client)
+{
+       gchar *name_owner;
+
+       name_owner = g_dbus_object_manager_client_get_name_owner (
+               G_DBUS_OBJECT_MANAGER_CLIENT (manager));
+
+       if (name_owner != NULL)
+               g_print ("GOA: 'org.gnome.OnlineAccounts' name appeared\n");
+       else
+               g_print ("GOA: 'org.gnome.OnlineAccounts' name vanished\n");
+
+       if (name_owner != NULL) {
+               GList *list, *link;
+
+               /* The goa-daemon (re)started.  Any unclaimed accounts
+                * from the previous session were legitimately removed. */
+
+               list = e_goa_client_claim_all_orphans (client);
+
+               for (link = list; link != NULL; link = g_list_next (link)) {
+                       g_signal_emit (
+                               client,
+                               signals[ACCOUNT_REMOVED], 0,
+                               GOA_OBJECT (link->data));
+               }
+
+               g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+               g_free (name_owner);
+       }
+}
+
+static void
+e_goa_client_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_OBJECT_MANAGER:
+                       g_value_take_object (
+                               value,
+                               e_goa_client_ref_object_manager (
+                               E_GOA_CLIENT (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_goa_client_dispose (GObject *object)
+{
+       EGoaClientPrivate *priv;
+
+       priv = E_GOA_CLIENT_GET_PRIVATE (object);
+
+       if (priv->object_added_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->object_manager,
+                       priv->object_added_handler_id);
+               priv->object_added_handler_id = 0;
+       }
+
+       if (priv->object_removed_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->object_manager,
+                       priv->object_removed_handler_id);
+               priv->object_removed_handler_id = 0;
+       }
+
+       if (priv->notify_name_owner_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->object_manager,
+                       priv->notify_name_owner_handler_id);
+               priv->notify_name_owner_handler_id = 0;
+       }
+
+       g_clear_object (&priv->object_manager);
+
+       g_hash_table_remove_all (priv->orphans);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_goa_client_parent_class)->dispose (object);
+}
+
+static void
+e_goa_client_finalize (GObject *object)
+{
+       EGoaClientPrivate *priv;
+
+       priv = E_GOA_CLIENT_GET_PRIVATE (object);
+
+       g_hash_table_destroy (priv->orphans);
+       g_mutex_clear (&priv->orphans_lock);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_goa_client_parent_class)->finalize (object);
+}
+
+static gboolean
+e_goa_client_initable_init (GInitable *initable,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       EGoaClientPrivate *priv;
+       gulong handler_id;
+
+       priv = E_GOA_CLIENT_GET_PRIVATE (initable);
+
+       priv->object_manager = goa_object_manager_client_new_for_bus_sync (
+               G_BUS_TYPE_SESSION,
+               G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+               "org.gnome.OnlineAccounts",
+               "/org/gnome/OnlineAccounts",
+               cancellable, error);
+
+       if (priv->object_manager == NULL)
+               return FALSE;
+
+       handler_id = g_signal_connect (
+               priv->object_manager, "object-added",
+               G_CALLBACK (e_goa_client_object_added_cb),
+               E_GOA_CLIENT (initable));
+       priv->object_added_handler_id = handler_id;
+
+       handler_id = g_signal_connect (
+               priv->object_manager, "object-removed",
+               G_CALLBACK (e_goa_client_object_removed_cb),
+               E_GOA_CLIENT (initable));
+       priv->object_removed_handler_id = handler_id;
+
+       handler_id = g_signal_connect (
+               priv->object_manager, "notify::name-owner",
+               G_CALLBACK (e_goa_client_notify_name_owner_cb),
+               E_GOA_CLIENT (initable));
+       priv->notify_name_owner_handler_id = handler_id;
+
+       return TRUE;
+}
+
+static void
+e_goa_client_class_init (EGoaClientClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EGoaClientPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = e_goa_client_get_property;
+       object_class->dispose = e_goa_client_dispose;
+       object_class->finalize = e_goa_client_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_OBJECT_MANAGER,
+               g_param_spec_object (
+                       "object-manager",
+                       "Object Manager",
+                       "The GDBusObjectManager used by the EGoaClient",
+                       G_TYPE_DBUS_OBJECT_MANAGER,
+                       G_PARAM_READABLE));
+
+       signals[ACCOUNT_ADDED] = g_signal_new (
+               "account-added",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EGoaClientClass, account_added),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               GOA_TYPE_OBJECT);
+
+       signals[ACCOUNT_REMOVED] = g_signal_new (
+               "account-removed",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EGoaClientClass, account_removed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               GOA_TYPE_OBJECT);
+
+       signals[ACCOUNT_SWAPPED] = g_signal_new (
+               "account-swapped",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EGoaClientClass, account_swapped),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 2,
+               GOA_TYPE_OBJECT,
+               GOA_TYPE_OBJECT);
+}
+
+static void
+e_goa_client_class_finalize (EGoaClientClass *class)
+{
+}
+
+static void
+e_goa_client_interface_init (GInitableIface *interface)
+{
+       interface->init = e_goa_client_initable_init;
+}
+
+static void
+e_goa_client_init (EGoaClient *client)
+{
+       client->priv = E_GOA_CLIENT_GET_PRIVATE (client);
+
+       client->priv->orphans = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_object_unref);
+       g_mutex_init (&client->priv->orphans_lock);
+}
+
+void
+e_goa_client_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_goa_client_register_type (type_module);
+}
+
+void
+e_goa_client_new (GCancellable *cancellable,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+       g_async_initable_new_async (
+               E_TYPE_GOA_CLIENT,
+               G_PRIORITY_DEFAULT, cancellable,
+               callback, user_data, NULL);
+}
+
+EGoaClient *
+e_goa_client_new_finish (GAsyncResult *result,
+                         GError **error)
+{
+       GObject *source_object;
+       GObject *result_object;
+
+       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+       source_object = g_async_result_get_source_object (result);
+       g_return_val_if_fail (source_object != NULL, NULL);
+
+       result_object = g_async_initable_new_finish (
+               G_ASYNC_INITABLE (source_object), result, error);
+
+       g_object_unref (source_object);
+
+       if (result_object == NULL)
+               return NULL;
+
+       return E_GOA_CLIENT (result_object);
+}
+
+GDBusObjectManager *
+e_goa_client_ref_object_manager (EGoaClient *client)
+{
+       g_return_val_if_fail (E_IS_GOA_CLIENT (client), NULL);
+
+       return g_object_ref (client->priv->object_manager);
+}
+
+GList *
+e_goa_client_list_accounts (EGoaClient *client)
+{
+       GDBusObjectManager *object_manager;
+       GQueue queue = G_QUEUE_INIT;
+       GList *list, *link;
+
+       g_return_val_if_fail (E_IS_GOA_CLIENT (client), NULL);
+
+       object_manager = e_goa_client_ref_object_manager (client);
+       list = g_dbus_object_manager_get_objects (object_manager);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               GoaObject *goa_object = GOA_OBJECT (link->data);
+
+               if (goa_object_peek_account (goa_object) != NULL)
+                       g_queue_push_tail (&queue, g_object_ref (goa_object));
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
+       g_object_unref (object_manager);
+
+       return g_queue_peek_head_link (&queue);
+}
+
+GoaObject *
+e_goa_client_lookup_by_id (EGoaClient *client,
+                           const gchar *id)
+{
+       GList *list, *link;
+       GoaObject *match = NULL;
+
+       g_return_val_if_fail (E_IS_GOA_CLIENT (client), NULL);
+       g_return_val_if_fail (id != NULL, NULL);
+
+       list = e_goa_client_list_accounts (client);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               GoaObject *goa_object = GOA_OBJECT (link->data);
+               GoaAccount *goa_account;
+               const gchar *goa_account_id;
+
+               goa_account = goa_object_peek_account (goa_object);
+               if (goa_account == NULL)
+                       continue;
+
+               goa_account_id = goa_account_get_id (goa_account);
+               if (g_strcmp0 (goa_account_id, id) == 0) {
+                       match = g_object_ref (goa_object);
+                       break;
+               }
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+       return match;
+}
+
diff --git a/modules/gnome-online-accounts/e-goa-client.h b/modules/gnome-online-accounts/e-goa-client.h
new file mode 100644
index 0000000..3ccc793
--- /dev/null
+++ b/modules/gnome-online-accounts/e-goa-client.h
@@ -0,0 +1,93 @@
+/*
+ * e-goa-client.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* This is an improved GoaClient.  It handles goa-daemon crashes/restarts
+ * gracefully by emitting an "account-swapped" signal for each pair of old
+ * and new proxy objects for the same "online account" once the goa-daemon
+ * restarts.  Contrast with GoaClient, which emits false "account-removed"
+ * and "account-added" signals and forces apps to distinguish them from an
+ * actual removal or addition of an "online account". */
+
+#ifndef E_GOA_CLIENT_H
+#define E_GOA_CLIENT_H
+
+/* XXX Yeah, yeah... */
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+
+#include <goa/goa.h>
+
+/* Standard GObject macros */
+#define E_TYPE_GOA_CLIENT \
+       (e_goa_client_get_type ())
+#define E_GOA_CLIENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_GOA_CLIENT, EGoaClient))
+#define E_GOA_CLIENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_GOA_CLIENT, EGoaClientClass))
+#define E_IS_GOA_CLIENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_GOA_CLIENT))
+#define E_IS_GOA_CLIENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_GOA_CLIENT))
+#define E_GOA_CLIENT_CLASS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_GOA_CLIENT, EGoaClientClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EGoaClient EGoaClient;
+typedef struct _EGoaClientClass EGoaClientClass;
+typedef struct _EGoaClientPrivate EGoaClientPrivate;
+
+struct _EGoaClient {
+       GObject parent;
+       EGoaClientPrivate *priv;
+};
+
+struct _EGoaClientClass {
+       GObjectClass parent_class;
+
+       /* Signals */
+       void            (*account_added)        (EGoaClient *client,
+                                                GoaObject *object);
+       void            (*account_removed)      (EGoaClient *client,
+                                                GoaObject *object);
+       void            (*account_swapped)      (EGoaClient *client,
+                                                GoaObject *old_object,
+                                                GoaObject *new_object);
+};
+
+GType          e_goa_client_get_type           (void) G_GNUC_CONST;
+void           e_goa_client_type_register      (GTypeModule *type_module);
+void           e_goa_client_new                (GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+EGoaClient *   e_goa_client_new_finish         (GAsyncResult *result,
+                                                GError **error);
+GDBusObjectManager *
+               e_goa_client_ref_object_manager (EGoaClient *client);
+GList *                e_goa_client_list_accounts      (EGoaClient *client);
+GoaObject *    e_goa_client_lookup_by_id       (EGoaClient *client,
+                                                const gchar *id);
+
+G_END_DECLS
+
+#endif /* E_GOA_CLIENT_H */
+
diff --git a/modules/gnome-online-accounts/module-gnome-online-accounts.c 
b/modules/gnome-online-accounts/module-gnome-online-accounts.c
index c9ad646..66001ac 100644
--- a/modules/gnome-online-accounts/module-gnome-online-accounts.c
+++ b/modules/gnome-online-accounts/module-gnome-online-accounts.c
@@ -16,17 +16,14 @@
  *
  */
 
-/* XXX Yeah, yeah... */
-#define GOA_API_IS_SUBJECT_TO_CHANGE
-
 #include <config.h>
-#include <goa/goa.h>
 #include <glib/gi18n-lib.h>
 #include <libsoup/soup.h>
 
 #include <libebackend/libebackend.h>
 
 #include "goaewsclient.h"
+#include "e-goa-client.h"
 #include "e-goa-password-based.h"
 
 /* Standard GObject macros */
@@ -48,7 +45,11 @@ typedef struct _EGnomeOnlineAccountsClass EGnomeOnlineAccountsClass;
 struct _EGnomeOnlineAccounts {
        EExtension parent;
 
-       GoaClient *goa_client;
+       EGoaClient *goa_client;
+       gulong account_added_handler_id;
+       gulong account_removed_handler_id;
+       gulong account_swapped_handler_id;
+
        GCancellable *create_client;
 
        /* GoaAccount ID -> ESource UID */
@@ -154,7 +155,6 @@ gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
 {
        ESourceRegistryServer *server;
        GoaObject *match = NULL;
-       GList *list, *iter;
        const gchar *extension_name;
        gchar *account_id = NULL;
 
@@ -173,35 +173,12 @@ gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
                g_object_unref (source);
        }
 
-       if (account_id == NULL)
-               return NULL;
-
-       /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
-
-       list = goa_client_get_accounts (extension->goa_client);
-
-       for (iter = list; iter != NULL; iter = g_list_next (iter)) {
-               GoaObject *goa_object;
-               GoaAccount *goa_account;
-               const gchar *candidate_id;
-
-               goa_object = GOA_OBJECT (iter->data);
-               goa_account = goa_object_get_account (goa_object);
-               candidate_id = goa_account_get_id (goa_account);
-
-               if (g_strcmp0 (account_id, candidate_id) == 0)
-                       match = g_object_ref (goa_object);
-
-               g_object_unref (goa_account);
-
-               if (match != NULL)
-                       break;
+       if (account_id != NULL) {
+               match = e_goa_client_lookup_by_id (
+                       extension->goa_client, account_id);
+               g_free (account_id);
        }
 
-       g_list_free_full (list, (GDestroyNotify) g_object_unref);
-
-       g_free (account_id);
-
        return match;
 }
 
@@ -905,7 +882,7 @@ gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
 }
 
 static void
-gnome_online_accounts_account_added_cb (GoaClient *goa_client,
+gnome_online_accounts_account_added_cb (EGoaClient *goa_client,
                                         GoaObject *goa_object,
                                         EGnomeOnlineAccounts *extension)
 {
@@ -940,7 +917,7 @@ gnome_online_accounts_account_added_cb (GoaClient *goa_client,
 }
 
 static void
-gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
+gnome_online_accounts_account_removed_cb (EGoaClient *goa_client,
                                           GoaObject *goa_object,
                                           EGnomeOnlineAccounts *extension)
 {
@@ -969,6 +946,42 @@ gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
        g_object_unref (goa_account);
 }
 
+static void
+gnome_online_accounts_account_swapped_cb (EGoaClient *goa_client,
+                                          GoaObject *old_goa_object,
+                                          GoaObject *new_goa_object,
+                                          EGnomeOnlineAccounts *extension)
+{
+       ESource *source = NULL;
+       ESourceRegistryServer *server;
+       GoaAccount *goa_account;
+       const gchar *account_id;
+       const gchar *source_uid;
+
+       /* The old GoaObject is about to be destroyed so we should
+        * not need to bother with undoing property bindings on it.
+        * Just set up new property bindings on the new GoaObject. */
+
+       server = gnome_online_accounts_get_server (extension);
+
+       goa_account = goa_object_get_account (new_goa_object);
+
+       account_id = goa_account_get_id (goa_account);
+       source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
+
+       if (source_uid != NULL)
+               source = e_source_registry_server_ref_source (
+                       server, source_uid);
+
+       if (source != NULL) {
+               gnome_online_accounts_config_sources (
+                       extension, source, new_goa_object);
+               g_object_unref (source);
+       }
+
+       g_object_unref (goa_account);
+}
+
 static gint
 gnome_online_accounts_compare_id (GoaObject *goa_object,
                                   const gchar *target_id)
@@ -1055,15 +1068,16 @@ gnome_online_accounts_create_client_cb (GObject *source_object,
                                         gpointer user_data)
 {
        EGnomeOnlineAccounts *extension;
-       GoaClient *goa_client;
+       EGoaClient *goa_client;
        GList *list, *link;
+       gulong handler_id;
        GError *error = NULL;
 
        /* If we get back a G_IO_ERROR_CANCELLED then it means the
         * EGnomeOnlineAccounts is already finalized, so be careful
-        * not to touch it until after we have a valid GoaClient. */
+        * not to touch it until after we have a valid EGoaClient. */
 
-       goa_client = goa_client_new_finish (result, &error);
+       goa_client = e_goa_client_new_finish (result, &error);
 
        if (error != NULL) {
                g_warn_if_fail (goa_client == NULL);
@@ -1074,7 +1088,7 @@ gnome_online_accounts_create_client_cb (GObject *source_object,
                return;
        }
 
-       g_return_if_fail (GOA_IS_CLIENT (goa_client));
+       g_return_if_fail (E_IS_GOA_CLIENT (goa_client));
 
        /* Should be safe to dereference the EGnomeOnlineAccounts now. */
 
@@ -1085,7 +1099,7 @@ gnome_online_accounts_create_client_cb (GObject *source_object,
        g_object_unref (extension->create_client);
        extension->create_client = NULL;
 
-       list = goa_client_get_accounts (extension->goa_client);
+       list = e_goa_client_list_accounts (extension->goa_client);
 
        /* This populates a hash table of GOA ID -> ESource UID strings by
         * searching through available data sources for ones with a "GNOME
@@ -1104,14 +1118,23 @@ gnome_online_accounts_create_client_cb (GObject *source_object,
        g_list_free_full (list, (GDestroyNotify) g_object_unref);
 
        /* Listen for Online Account changes. */
-       g_signal_connect (
+
+       handler_id = g_signal_connect (
                extension->goa_client, "account-added",
                G_CALLBACK (gnome_online_accounts_account_added_cb),
                extension);
-       g_signal_connect (
+       extension->account_added_handler_id = handler_id;
+
+       handler_id = g_signal_connect (
                extension->goa_client, "account-removed",
                G_CALLBACK (gnome_online_accounts_account_removed_cb),
                extension);
+       extension->account_removed_handler_id = handler_id;
+
+       handler_id = g_signal_connect (
+               extension->goa_client, "account-swapped",
+               G_CALLBACK (gnome_online_accounts_account_swapped_cb),
+               extension);
 }
 
 static void
@@ -1124,7 +1147,7 @@ gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
        /* Note we don't reference the extension.  If the
         * extension gets destroyed before this completes
         * we cancel the operation from dispose(). */
-       goa_client_new (
+       e_goa_client_new (
                extension->create_client,
                gnome_online_accounts_create_client_cb,
                extension);
@@ -1137,23 +1160,34 @@ gnome_online_accounts_dispose (GObject *object)
 
        extension = E_GNOME_ONLINE_ACCOUNTS (object);
 
-       if (extension->goa_client != NULL) {
-               g_signal_handlers_disconnect_matched (
+       if (extension->account_added_handler_id > 0) {
+               g_signal_handler_disconnect (
                        extension->goa_client,
-                       G_SIGNAL_MATCH_DATA,
-                       0, 0, NULL, NULL, object);
-               g_object_unref (extension->goa_client);
-               extension->goa_client = NULL;
+                       extension->account_added_handler_id);
+               extension->account_added_handler_id = 0;
        }
 
-       /* This cancels goa_client_new() in case it still
-        * hasn't completed.  We're no longer interested. */
-       if (extension->create_client != NULL) {
-               g_cancellable_cancel (extension->create_client);
-               g_object_unref (extension->create_client);
-               extension->create_client = NULL;
+       if (extension->account_removed_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       extension->goa_client,
+                       extension->account_removed_handler_id);
+               extension->account_removed_handler_id = 0;
        }
 
+       if (extension->account_swapped_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       extension->goa_client,
+                       extension->account_swapped_handler_id);
+               extension->account_swapped_handler_id = 0;
+       }
+
+       /* This cancels e_goa_client_new() in case it still
+        * hasn't completed.  We're no longer interested. */
+       g_cancellable_cancel (extension->create_client);
+
+       g_clear_object (&extension->goa_client);
+       g_clear_object (&extension->create_client);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
                dispose (object);
@@ -1277,7 +1311,7 @@ e_gnome_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
 static void
 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
 {
-       /* Used to cancel unfinished goa_client_new(). */
+       /* Used to cancel unfinished e_goa_client_new(). */
        extension->create_client = g_cancellable_new ();
 
        extension->goa_to_eds = g_hash_table_new_full (
@@ -1290,6 +1324,7 @@ e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
 G_MODULE_EXPORT void
 e_module_load (GTypeModule *type_module)
 {
+       e_goa_client_type_register (type_module);
        e_goa_password_based_type_register (type_module);
        e_gnome_online_accounts_register_type (type_module);
 }


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