[evolution-data-server/f19] Add patch for Red Hat bug #956908 (GOA accounts forgotten on GOA crash)

Milan Crha mcrha at fedoraproject.org
Thu May 2 08:42:03 UTC 2013


commit be1d08418382034b6a42f85a5c9535bbe5367c8c
Author: Milan Crha <mcrha at redhat.com>
Date:   Thu May 2 10:41:49 2013 +0200

    Add patch for Red Hat bug #956908 (GOA accounts forgotten on GOA crash)

 evolution-data-server-3.8.1-goa-crash.patch |  983 +++++++++++++++++++++++++++
 evolution-data-server.spec                  |    9 +-
 2 files changed, 991 insertions(+), 1 deletions(-)
---
diff --git a/evolution-data-server-3.8.1-goa-crash.patch b/evolution-data-server-3.8.1-goa-crash.patch
new file mode 100644
index 0000000..25dbebc
--- /dev/null
+++ b/evolution-data-server-3.8.1-goa-crash.patch
@@ -0,0 +1,983 @@
+From 0e87aafea9a370b6f27ea720729dadb572b03d72 Mon Sep 17 00:00:00 2001
+From: Matthew Barnes <mbarnes at redhat.com>
+Date: Sat, 27 Apr 2013 12:47:18 +0000
+Subject: 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.
+
+(cherry picked from commit 725e976e67a62bf00820c70c47a3f439811d6c0d)
+---
+diff --git a/modules/gnome-online-accounts/Makefile.am b/modules/gnome-online-accounts/Makefile.am
+index 4be1ffa..6b2b129 100644
+--- a/modules/gnome-online-accounts/Makefile.am
++++ b/modules/gnome-online-accounts/Makefile.am
+@@ -19,6 +19,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 e6907f4..1143f67 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;
+ }
+ 
+@@ -925,7 +902,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)
+ {
+@@ -960,7 +937,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)
+ {
+@@ -989,6 +966,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)
+@@ -1075,15 +1088,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);
+@@ -1094,7 +1108,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. */
+ 
+@@ -1105,7 +1119,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
+@@ -1124,14 +1138,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
+@@ -1144,7 +1167,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);
+@@ -1157,23 +1180,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);
+@@ -1297,7 +1331,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 (
+@@ -1310,6 +1344,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);
+ }
+diff --git a/modules/gnome-online-accounts/e-goa-client.c b/modules/gnome-online-accounts/e-goa-client.c
+index 2575933..093ce07 100644
+--- a/modules/gnome-online-accounts/e-goa-client.c
++++ b/modules/gnome-online-accounts/e-goa-client.c
+@@ -48,8 +48,7 @@ enum {
+ static guint signals[LAST_SIGNAL];
+ 
+ /* Forward Declarations */
+-static void	e_goa_client_interface_init
+-					(GInitableIface *interface);
++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. */
+@@ -107,8 +106,6 @@ e_goa_client_claim_one_orphan (EGoaClient *client,
+ 	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);
+@@ -120,6 +117,11 @@ e_goa_client_claim_one_orphan (EGoaClient *client,
+ 
+ 	g_mutex_unlock (&client->priv->orphans_lock);
+ 
++	if (old_goa_object != NULL)
++		g_print (
++			"GOA: Claiming orphaned account '%s'\n",
++			goa_account_id);
++
+ 	return old_goa_object;
+ }
+ 
+--
+cgit v0.9.1
diff --git a/evolution-data-server.spec b/evolution-data-server.spec
index ee8c195..7148e90 100644
--- a/evolution-data-server.spec
+++ b/evolution-data-server.spec
@@ -27,7 +27,7 @@
 
 Name: evolution-data-server
 Version: 3.8.1
-Release: 1%{?dist}
+Release: 2%{?dist}
 Group: System Environment/Libraries
 Summary: Backend data server for Evolution
 License: LGPLv2+
@@ -43,6 +43,9 @@ Obsoletes: evolution-webcal < 2.24.0
 # RH bug #243296
 Patch01: evolution-data-server-1.11.5-fix-64bit-acinclude.patch
 
+# RH bug #956908
+Patch02: evolution-data-server-3.8.1-goa-crash.patch
+
 ### Build Dependencies ###
 
 BuildRequires: libdb-devel
@@ -118,6 +121,7 @@ This package contains developer documentation for %{name}.
 %setup -q
 
 %patch01 -p1 -b .fix-64bit-acinclude
+%patch02 -p1 -b .goa-crash
 
 mkdir -p krb5-fakeprefix/include
 mkdir -p krb5-fakeprefix/lib
@@ -366,6 +370,9 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &>/dev/null || :
 %{_datadir}/gtk-doc/html/libedataserver
 
 %changelog
+* Thu May 02 2013 Milan Crha <mcrha at redhat.com> - 3.8.1-2
+- Add patch for Red Hat bug #956908 (GOA accounts forgotten on GOA crash)
+
 * Sun Apr 14 2013 Matthew Barnes <mbarnes at redhat.com> - 3.8.1-1
 - Update to 3.8.1
 - Remove stuck message retrieval patch (fixed upstream).


More information about the scm-commits mailing list