[evolution-data-server] Add OAuth support to Google Contacts backend.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Add OAuth support to Google Contacts backend.
- Date: Tue, 28 Jun 2011 17:41:33 +0000 (UTC)
commit a8a17cfbcd82447e670c508c4dc89c201fbcef91
Author: Matthew Barnes <mbarnes redhat com>
Date: Sun Jun 26 07:54:44 2011 -0400
Add OAuth support to Google Contacts backend.
For ESources tagged with a "goa-account-id" property set by Evolution,
authenticate using OAuth 1.0 via libgoa-1.0 and libgdata 0.9.
addressbook/backends/google/Makefile.am | 49 ++-
.../backends/google/e-book-backend-google.c | 339 ++++++++-----
.../backends/google/e-gdata-goa-authorizer.c | 524 ++++++++++++++++++++
.../backends/google/e-gdata-goa-authorizer.h | 68 +++
addressbook/libedata-book/Makefile.am | 6 +-
addressbook/libedata-book/e-data-book-factory.c | 86 ++++
configure.ac | 36 ++-
7 files changed, 963 insertions(+), 145 deletions(-)
---
diff --git a/addressbook/backends/google/Makefile.am b/addressbook/backends/google/Makefile.am
index 581e662..c4a4684 100644
--- a/addressbook/backends/google/Makefile.am
+++ b/addressbook/backends/google/Makefile.am
@@ -1,29 +1,40 @@
ebook_backend_LTLIBRARIES = libebookbackendgoogle.la
+if HAVE_GOA
+GOA_SOURCES = \
+ e-gdata-goa-authorizer.c \
+ e-gdata-goa-authorizer.h
+endif
+
libebookbackendgoogle_la_CPPFLAGS = \
- $(AM_CPPFLAGS) \
- -DG_LOG_DOMAIN=\"libebookbackendgoogle\" \
- -I$(top_srcdir) \
- -I$(top_builddir) \
- -I$(top_srcdir)/addressbook \
- -I$(top_builddir)/addressbook \
- $(SOUP_CFLAGS) \
- $(EVOLUTION_ADDRESSBOOK_CFLAGS) \
- $(GDATA_CFLAGS)
+ $(AM_CPPFLAGS) \
+ -DG_LOG_DOMAIN=\"libebookbackendgoogle\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/addressbook \
+ -I$(top_builddir)/addressbook \
+ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \
+ $(SOUP_CFLAGS) \
+ $(GDATA_CFLAGS) \
+ $(GOA_CFLAGS) \
+ $(OAUTH_CFLAGS)
libebookbackendgoogle_la_SOURCES = \
- e-book-backend-google-factory.c \
- e-book-backend-google.h \
- e-book-backend-google.c
+ e-book-backend-google-factory.c \
+ e-book-backend-google.c \
+ e-book-backend-google.h \
+ $(GOA_SOURCES)
libebookbackendgoogle_la_LIBADD = \
- $(top_builddir)/addressbook/libebook/libebook-1.2.la \
- $(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la \
- $(top_builddir)/libedataserver/libedataserver-1.2.la \
- $(top_builddir)/libebackend/libebackend-1.2.la \
- $(SOUP_LIBS) \
- $(EVOLUTION_ADDRESSBOOK_LIBS) \
- $(GDATA_LIBS)
+ $(top_builddir)/addressbook/libebook/libebook-1.2.la \
+ $(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(EVOLUTION_ADDRESSBOOK_LIBS) \
+ $(SOUP_LIBS) \
+ $(GDATA_LIBS) \
+ $(GOA_LIBS) \
+ $(OAUTH_LIBS)
libebookbackendgoogle_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED)
diff --git a/addressbook/backends/google/e-book-backend-google.c b/addressbook/backends/google/e-book-backend-google.c
index 428bb52..568acc9 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -37,6 +37,12 @@
#include "e-book-backend-google.h"
+#ifdef HAVE_GOA
+#include "e-gdata-goa-authorizer.h"
+#endif
+
+#define CLIENT_ID "evolution-client-0.1.0"
+
#define URI_GET_CONTACTS "://www.google.com/m8/feeds/contacts/default/full"
#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
@@ -402,6 +408,32 @@ cache_needs_update (EBookBackend *backend, guint *remaining_secs)
return FALSE;
}
+static gboolean
+backend_is_authorized (EBookBackend *backend)
+{
+ EBookBackendGooglePrivate *priv;
+
+ priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+ if (priv->service == NULL)
+ return FALSE;
+
+#ifdef HAVE_GOA
+ /* If we're using OAuth tokens, then as far as the backend
+ * is concerned it's always authorized. The GDataAuthorizer
+ * will take care of everything in the background without
+ * bothering clients with "auth-required" signals. */
+ if (E_IS_GDATA_GOA_AUTHORIZER (priv->authorizer))
+ return TRUE;
+#endif
+
+#ifdef HAVE_LIBGDATA_0_9
+ return gdata_service_is_authorized (priv->service);
+#else
+ return gdata_service_is_authenticated (priv->service);
+#endif
+}
+
static void
on_contact_added (EBookBackend *backend, EContact *contact)
{
@@ -545,7 +577,9 @@ get_new_contacts_cb (GDataService *service, GAsyncResult *result, EBookBackend *
GList *entries = gdata_feed_get_entries (feed);
__debug__ ("Feed has %d entries", g_list_length (entries));
}
- g_object_unref (feed);
+
+ if (feed != NULL)
+ g_object_unref (feed);
if (!gdata_error) {
/* Finish updating the cache */
@@ -573,11 +607,7 @@ get_new_contacts (EBookBackend *backend)
GCancellable *cancellable;
__debug__ (G_STRFUNC);
- #ifdef HAVE_LIBGDATA_0_9
- g_return_if_fail (priv->service && gdata_service_is_authorized (priv->service));
- #else
- g_return_if_fail (priv->service && gdata_service_is_authenticated (priv->service));
- #endif
+ g_return_if_fail (backend_is_authorized (backend));
/* Sort out update times */
last_updated = cache_get_last_update (backend);
@@ -692,7 +722,9 @@ get_groups_cb (GDataService *service, GAsyncResult *result, EBookBackend *backen
GList *entries = gdata_feed_get_entries (feed);
__debug__ ("Group feed has %d entries", g_list_length (entries));
}
- g_object_unref (feed);
+
+ if (feed != NULL)
+ g_object_unref (feed);
if (!gdata_error) {
/* Update the update time */
@@ -712,11 +744,7 @@ get_groups (EBookBackend *backend)
GCancellable *cancellable;
__debug__ (G_STRFUNC);
- #ifdef HAVE_LIBGDATA_0_9
- g_return_if_fail (priv->service && gdata_service_is_authorized (priv->service));
- #else
- g_return_if_fail (priv->service && gdata_service_is_authenticated (priv->service));
- #endif
+ g_return_if_fail (backend_is_authorized (backend));
/* Build our query */
query = GDATA_QUERY (gdata_contacts_query_new_with_limits (NULL, 0, G_MAXINT));
@@ -799,11 +827,7 @@ cache_refresh_if_needed (EBookBackend *backend)
__debug__ (G_STRFUNC);
- #ifdef HAVE_LIBGDATA_0_9
- if (!priv->is_online || !priv->service || !gdata_service_is_authorized (priv->service)) {
- #else
- if (!priv->is_online || !priv->service || !gdata_service_is_authenticated (priv->service)) {
- #endif
+ if (!priv->is_online || !backend_is_authorized (backend)) {
__debug__ ("We are not connected to Google%s.", (!priv->is_online) ? " (offline mode)" : "");
return TRUE;
}
@@ -846,6 +870,93 @@ cache_destroy (EBookBackend *backend)
priv->cache_type = NO_CACHE;
}
+static void
+proxy_settings_changed (EProxy *proxy,
+ EBookBackend *backend)
+{
+ EBookBackendGooglePrivate *priv;
+ SoupURI *proxy_uri = NULL;
+ gchar *uri;
+
+ priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+ if (!priv || !priv->service)
+ return;
+
+ /* Build the URI which libgdata would use to query contacts */
+ uri = g_strconcat (
+ priv->use_ssl ? "https" : "http",
+ URI_GET_CONTACTS, NULL);
+
+ /* use proxy if necessary */
+ if (e_proxy_require_proxy_for_uri (proxy, uri))
+ proxy_uri = e_proxy_peek_uri_for (proxy, uri);
+ gdata_service_set_proxy_uri (priv->service, proxy_uri);
+
+ g_free (uri);
+}
+
+static void
+request_authorization (EBookBackend *backend)
+{
+ EBookBackendGooglePrivate *priv;
+
+ priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
+
+ /* Make sure we have the GDataService configured
+ * before requesting authorization. */
+
+#ifdef HAVE_GOA
+ /* If this is associated with a GNOME Online Account,
+ * use OAuth authentication instead of ClientLogin. */
+ if (priv->authorizer == NULL) {
+ EGDataGoaAuthorizer *authorizer;
+ GoaObject *goa_object;
+
+ goa_object = g_object_get_data (
+ G_OBJECT (backend), "GNOME Online Account");
+ if (GOA_IS_OBJECT (goa_object)) {
+ authorizer = e_gdata_goa_authorizer_new (goa_object);
+ priv->authorizer = GDATA_AUTHORIZER (authorizer);
+ }
+ }
+#endif
+
+#ifdef HAVE_LIBGDATA_0_9
+ if (priv->authorizer == NULL) {
+ GDataClientLoginAuthorizer *authorizer;
+
+ authorizer = gdata_client_login_authorizer_new (
+ CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
+ priv->authorizer = GDATA_AUTHORIZER (authorizer);
+ }
+#endif
+
+ if (priv->service == NULL) {
+ GDataContactsService *contacts_service;
+
+#ifdef HAVE_LIBGDATA_0_9
+ contacts_service =
+ gdata_contacts_service_new (priv->authorizer);
+#else
+ contacts_service = gdata_contacts_service_new (CLIENT_ID);
+#endif
+ priv->service = GDATA_SERVICE (contacts_service);
+ proxy_settings_changed (priv->proxy, backend);
+ }
+
+#ifdef HAVE_GOA
+ /* If we're using OAuth tokens, then as far as the backend
+ * is concerned it's always authorized. The GDataAuthorizer
+ * will take care of everything in the background without
+ * bothering clients with "auth-required" signals. */
+ if (E_IS_GDATA_GOA_AUTHORIZER (priv->authorizer))
+ return;
+#endif
+
+ e_book_backend_notify_auth_required (backend, TRUE, NULL);
+}
+
typedef struct {
EBookBackend *backend;
EDataBook *book;
@@ -904,11 +1015,7 @@ e_book_backend_google_create_contact (EBookBackend *backend, EDataBook *book, gu
return;
}
- #ifdef HAVE_LIBGDATA_0_9
- g_return_if_fail (priv->service && gdata_service_is_authorized (priv->service));
- #else
- g_return_if_fail (priv->service && gdata_service_is_authenticated (priv->service));
- #endif
+ g_return_if_fail (backend_is_authorized (backend));
/* Build the GDataEntry from the vCard */
contact = e_contact_new_from_vcard (vcard_str);
@@ -990,11 +1097,7 @@ e_book_backend_google_remove_contacts (EBookBackend *backend, EDataBook *book, g
return;
}
- #ifdef HAVE_LIBGDATA_0_9
- g_return_if_fail (priv->service && gdata_service_is_authorized (priv->service));
- #else
- g_return_if_fail (priv->service && gdata_service_is_authenticated (priv->service));
- #endif
+ g_return_if_fail (backend_is_authorized (backend));
/* We make the assumption that the ID list we're passed is always exactly one element long, since we haven't specified "bulk-removes"
* in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
@@ -1098,11 +1201,7 @@ e_book_backend_google_modify_contact (EBookBackend *backend, EDataBook *book, gu
return;
}
- #ifdef HAVE_LIBGDATA_0_9
- g_return_if_fail (priv->service && gdata_service_is_authorized (priv->service));
- #else
- g_return_if_fail (priv->service && gdata_service_is_authenticated (priv->service));
- #endif
+ g_return_if_fail (backend_is_authorized (backend));
/* Get the new contact and its UID */
contact = e_contact_new_from_vcard (vcard_str);
@@ -1292,13 +1391,9 @@ e_book_backend_google_start_book_view (EBookBackend *backend, EDataBookView *boo
/* Update the cache if necessary */
if (cache_needs_update (backend, NULL)) {
- #ifdef HAVE_LIBGDATA_0_9
- if (!priv->service || !gdata_service_is_authorized (priv->service)) {
- #else
- if (!priv->service || !gdata_service_is_authenticated (priv->service)) {
- #endif
+ if (!backend_is_authorized (backend)) {
/* We need authorization first */
- e_book_backend_notify_auth_required (backend, TRUE, NULL);
+ request_authorization (backend);
} else {
/* Update in an idle function, so that this call doesn't block */
priv->idle_id = g_idle_add ((GSourceFunc) on_refresh_idle, backend);
@@ -1338,38 +1433,44 @@ e_book_backend_google_stop_book_view (EBookBackend *backend, EDataBookView *book
set_live_mode (backend, FALSE);
}
+typedef struct {
+ EBookBackend *backend;
+ guint32 opid;
+} AuthenticateUserData;
+
+#ifdef HAVE_LIBGDATA_0_9
static void
-proxy_settings_changed (EProxy *proxy, EBookBackend *backend)
+authenticate_client_login_cb (GDataClientLoginAuthorizer *authorizer,
+ GAsyncResult *result,
+ AuthenticateUserData *data)
{
- EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
- SoupURI *proxy_uri = NULL;
- gchar *uri;
-
- if (!priv || !priv->service)
- return;
+ GError *gdata_error = NULL;
+ GError *book_error = NULL;
- /* Build the URI which libgdata would use to query contacts */
- uri = g_strconcat (priv->use_ssl ? "https" : "http", URI_GET_CONTACTS, NULL);
+ __debug__ (G_STRFUNC);
- /* use proxy if necessary */
- if (e_proxy_require_proxy_for_uri (proxy, uri))
- proxy_uri = e_proxy_peek_uri_for (proxy, uri);
- gdata_service_set_proxy_uri (GDATA_SERVICE (priv->service), proxy_uri);
+ /* Finish authenticating */
+ gdata_client_login_authorizer_authenticate_finish (
+ authorizer, result, &gdata_error);
- g_free (uri);
-}
+ if (gdata_error != NULL) {
+ data_book_error_from_gdata_error (&book_error, gdata_error);
+ __debug__ ("Authentication failed: %s", gdata_error->message);
+ g_error_free (gdata_error);
+ }
-typedef struct {
- EBookBackend *backend;
- guint32 opid;
-} AuthenticateUserData;
+ finish_operation (data->backend, data->opid);
+ e_book_backend_notify_readonly (data->backend, gdata_error != NULL);
+ e_book_backend_notify_opened (data->backend, book_error);
-static void
-#ifdef HAVE_LIBGDATA_0_9
-authenticate_user_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *result, AuthenticateUserData *data)
+ g_object_unref (data->backend);
+ g_slice_free (AuthenticateUserData, data);
+}
#else
-authenticate_user_cb (GDataService *service, GAsyncResult *result, AuthenticateUserData *data)
-#endif
+static void
+authenticate_client_login_cb (GDataService *service,
+ GAsyncResult *result,
+ AuthenticateUserData *data)
{
GError *gdata_error = NULL;
GError *book_error = NULL;
@@ -1377,23 +1478,25 @@ authenticate_user_cb (GDataService *service, GAsyncResult *result, AuthenticateU
__debug__ (G_STRFUNC);
/* Finish authenticating */
- #ifdef HAVE_LIBGDATA_0_9
- if (!gdata_client_login_authorizer_authenticate_finish (authorizer, result, &gdata_error)) {
- #else
- if (!gdata_service_authenticate_finish (service, result, &gdata_error)) {
- #endif
+ gdata_service_authenticate_finish (service, result, &gdata_error);
+
+ if (gdata_error != NULL) {
data_book_error_from_gdata_error (&book_error, gdata_error);
__debug__ ("Authentication failed: %s", gdata_error->message);
g_error_free (gdata_error);
}
finish_operation (data->backend, data->opid);
- e_book_backend_notify_readonly (data->backend, gdata_error ? TRUE : FALSE);
+ e_book_backend_notify_readonly (data->backend, gdata_error != NULL);
e_book_backend_notify_opened (data->backend, book_error);
g_object_unref (data->backend);
g_slice_free (AuthenticateUserData, data);
}
+#endif
+
+#ifdef HAVE_LIBGDATA_0_9
+#endif
static void
e_book_backend_google_authenticate_user (EBookBackend *backend, GCancellable *cancellable, ECredentials *credentials)
@@ -1411,11 +1514,7 @@ e_book_backend_google_authenticate_user (EBookBackend *backend, GCancellable *ca
return;
}
- #ifdef HAVE_LIBGDATA_0_9
- if (priv->service && gdata_service_is_authorized (priv->service)) {
- #else
- if (priv->service && gdata_service_is_authenticated (priv->service)) {
- #endif
+ if (backend_is_authorized (backend)) {
g_warning ("Connection to Google already established.");
e_book_backend_notify_readonly (backend, FALSE);
e_book_backend_notify_opened (backend, NULL);
@@ -1427,26 +1526,6 @@ e_book_backend_google_authenticate_user (EBookBackend *backend, GCancellable *ca
return;
}
- #ifdef HAVE_LIBGDATA_0_9
- /* Set up the service, authorizer and proxy */
- if (!priv->service) {
- priv->authorizer = GDATA_AUTHORIZER (gdata_client_login_authorizer_new ("evolution-client-0.1.0", GDATA_TYPE_CONTACTS_SERVICE));
- priv->service = GDATA_SERVICE (gdata_contacts_service_new (priv->authorizer));
- }
- #else
- /* Set up the service and proxy */
- if (!priv->service)
- priv->service = GDATA_SERVICE (gdata_contacts_service_new ("evolution-client-0.1.0"));
- #endif
-
- if (!priv->proxy) {
- priv->proxy = e_proxy_new ();
- e_proxy_setup_proxy (priv->proxy);
-
- proxy_settings_changed (priv->proxy, backend);
- g_signal_connect (priv->proxy, "changed", G_CALLBACK (proxy_settings_changed), backend);
- }
-
opid = -1;
while (g_hash_table_lookup (priv->cancellables, GUINT_TO_POINTER (opid)))
opid--;
@@ -1456,15 +1535,28 @@ e_book_backend_google_authenticate_user (EBookBackend *backend, GCancellable *ca
data->backend = g_object_ref (backend);
data->opid = opid;
- cancellable = start_operation (backend, opid, cancellable, _("Authenticating with the serverâ"));
- #ifdef HAVE_LIBGDATA_0_9
- gdata_client_login_authorizer_authenticate_async (GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
- e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME),
- e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD),
- cancellable, (GAsyncReadyCallback) authenticate_user_cb, data);
- #else
- gdata_service_authenticate_async (priv->service, e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME), e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD), cancellable, (GAsyncReadyCallback) authenticate_user_cb, data);
- #endif
+ cancellable = start_operation (
+ backend, opid, cancellable,
+ _("Authenticating with the serverâ"));
+
+#ifdef HAVE_LIBGDATA_0_9
+ gdata_client_login_authorizer_authenticate_async (
+ GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
+ e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME),
+ e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD),
+ cancellable,
+ (GAsyncReadyCallback) authenticate_client_login_cb,
+ data);
+#else
+ gdata_service_authenticate_async (
+ priv->service,
+ e_credentials_peek (credentials, E_CREDENTIALS_KEY_USERNAME),
+ e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD),
+ cancellable,
+ (GAsyncReadyCallback) authenticate_client_login_cb,
+ data);
+#endif
+
g_object_unref (cancellable);
}
@@ -1529,13 +1621,18 @@ e_book_backend_google_open (EBookBackend *backend, EDataBook *book, guint opid,
e_book_backend_notify_readonly (backend, TRUE);
if (priv->is_online) {
- /* We're going online, so we need to authenticate and create the service and proxy.
- * This is done in e_book_backend_google_authenticate_user() when it gets the authentication data. */
- e_book_backend_notify_auth_required (backend, TRUE, NULL);
- } else {
- e_book_backend_notify_opened (backend, NULL /* Success */);
+ request_authorization (backend);
+
+#ifdef HAVE_LIBGDATA_0_9
+ /* Refresh the authorizer. This may block. */
+ gdata_authorizer_refresh_authorization (
+ priv->authorizer, cancellable, NULL);
+#endif
}
+ if (!priv->is_online || backend_is_authorized (backend))
+ e_book_backend_notify_opened (backend, NULL /* Success */);
+
e_data_book_respond_open (book, opid, NULL /* Success */);
}
@@ -1714,9 +1811,7 @@ e_book_backend_google_set_online (EBookBackend *backend, gboolean is_online)
e_book_backend_notify_online (backend, is_online);
if (is_online && e_book_backend_is_opened (backend)) {
- /* Going online, so we need to re-authenticate and re-create the service and proxy.
- * This is done in e_book_backend_google_authenticate_user() when it gets the authentication data. */
- e_book_backend_notify_auth_required (backend, TRUE, NULL);
+ request_authorization (backend);
} else {
/* Going offline, so cancel all running operations */
google_cancel_all_operations (backend);
@@ -1725,20 +1820,10 @@ e_book_backend_google_set_online (EBookBackend *backend, gboolean is_online)
* e_book_backend_google_authenticate_user() will mark us as writeable again once the user's authenticated again. */
e_book_backend_notify_readonly (backend, TRUE);
- /* We can free our service and proxy */
+ /* We can free our service. */
if (priv->service)
g_object_unref (priv->service);
priv->service = NULL;
-
- #ifdef HAVE_LIBGDATA_0_9
- if (priv->authorizer != NULL)
- g_object_unref (priv->authorizer);
- priv->authorizer = NULL;
- #endif
-
- if (priv->proxy)
- g_object_unref (priv->proxy);
- priv->proxy = NULL;
}
}
@@ -1766,11 +1851,11 @@ e_book_backend_google_dispose (GObject *object)
g_object_unref (priv->service);
priv->service = NULL;
- #ifdef HAVE_LIBGDATA_0_9
+#ifdef HAVE_LIBGDATA_0_9
if (priv->authorizer != NULL)
g_object_unref (priv->authorizer);
priv->authorizer = NULL;
- #endif
+#endif
if (priv->proxy)
g_object_unref (priv->proxy);
@@ -1833,6 +1918,14 @@ e_book_backend_google_init (EBookBackendGoogle *backend)
backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (
backend, E_TYPE_BOOK_BACKEND_GOOGLE,
EBookBackendGooglePrivate);
+
+ /* Set up our EProxy. */
+ backend->priv->proxy = e_proxy_new ();
+ e_proxy_setup_proxy (backend->priv->proxy);
+
+ g_signal_connect (
+ backend->priv->proxy, "changed",
+ G_CALLBACK (proxy_settings_changed), backend);
}
EBookBackend *
diff --git a/addressbook/backends/google/e-gdata-goa-authorizer.c b/addressbook/backends/google/e-gdata-goa-authorizer.c
new file mode 100644
index 0000000..b43df79
--- /dev/null
+++ b/addressbook/backends/google/e-gdata-goa-authorizer.c
@@ -0,0 +1,524 @@
+/*
+ * e-gdata-goa-authorizer.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-gdata-goa-authorizer.h"
+
+#include <string.h>
+#include <oauth.h>
+
+#define E_GDATA_GOA_AUTHORIZER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizerPrivate))
+
+struct _EGDataGoaAuthorizerPrivate {
+
+ /* GDataAuthorizer methods must be thread-safe. */
+ GMutex *mutex;
+
+ /* GoaObject is already thread-safe. */
+ GoaObject *goa_object;
+
+ /* These members are protected by the GMutex. */
+ gchar *access_token;
+ gchar *access_token_secret;
+ GHashTable *authorization_domains;
+};
+
+enum {
+ PROP_0,
+ PROP_GOA_OBJECT
+};
+
+/* Forward Declarations */
+static void e_gdata_goa_authorizer_interface_init
+ (GDataAuthorizerInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EGDataGoaAuthorizer,
+ e_gdata_goa_authorizer,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (
+ GDATA_TYPE_AUTHORIZER,
+ e_gdata_goa_authorizer_interface_init))
+
+static GHashTable *
+gdata_goa_authorizer_get_parameters (SoupMessage *message,
+ const gchar *consumer_key,
+ const gchar *consumer_secret,
+ const gchar *access_token,
+ const gchar *access_token_secret)
+{
+ GString *query;
+ GString *base_string;
+ GString *signing_key;
+ GHashTable *parameters;
+ GHashTable *hash_table;
+ GHashTableIter iter;
+ SoupURI *soup_uri;
+ GList *keys, *link;
+ gchar *string;
+ gchar *request_uri;
+ gpointer key, value;
+
+ parameters = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) g_free);
+
+ /* soup_form_decode() uses an awkward allocation style for
+ * its hash table entries, so it's easier to copy its content
+ * into our own hash table than try to use the returned hash
+ * table directly. */
+
+ soup_uri = soup_message_get_uri (message);
+ hash_table = soup_form_decode (soup_uri->query);
+ g_hash_table_iter_init (&iter, hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ key = (gpointer) g_intern_string (key);
+ g_hash_table_insert (parameters, key, g_strdup (value));
+ }
+ g_hash_table_destroy (hash_table);
+
+ /* Add OAuth parameters. */
+
+ key = (gpointer) "oauth_version";
+ g_hash_table_insert (parameters, key, g_strdup ("1.0"));
+
+ string = oauth_gen_nonce ();
+ key = (gpointer) "oauth_nonce";
+ g_hash_table_insert (parameters, key, g_strdup (string));
+ free (string); /* do not use g_free() */
+
+ key = (gpointer) "oauth_timestamp";
+ string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
+ g_hash_table_insert (parameters, key, string); /* takes ownership */
+
+ key = (gpointer) "oauth_consumer_key";
+ g_hash_table_insert (parameters, key, g_strdup (consumer_key));
+
+ key = (gpointer) "oauth_token";
+ g_hash_table_insert (parameters, key, g_strdup (access_token));
+
+ key = (gpointer) "oauth_signature_method";
+ g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));
+
+ /* Build the query part of the signature base string. */
+
+ query = g_string_sized_new (512);
+ keys = g_hash_table_get_keys (parameters);
+ keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
+ for (link = keys; link != NULL; link = g_list_next (link)) {
+ const gchar *key = link->data;
+ const gchar *val;
+
+ val = g_hash_table_lookup (parameters, key);
+
+ if (link != keys)
+ g_string_append_c (query, '&');
+
+ g_string_append_uri_escaped (query, key, NULL, FALSE);
+ g_string_append_c (query, '=');
+ g_string_append_uri_escaped (query, val, NULL, FALSE);
+ }
+ g_list_free (keys);
+
+ /* Build the signature base string. */
+
+ soup_uri = soup_uri_copy (soup_uri);
+ soup_uri_set_query (soup_uri, NULL);
+ soup_uri_set_fragment (soup_uri, NULL);
+ request_uri = soup_uri_to_string (soup_uri, FALSE);
+ soup_uri_free (soup_uri);
+
+ base_string = g_string_sized_new (512);
+ g_string_append_uri_escaped (base_string, message->method, NULL, FALSE);
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
+ g_string_append_c (base_string, '&');
+ g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);
+
+ /* Build the HMAC-SHA1 signing key. */
+
+ signing_key = g_string_sized_new (512);
+ g_string_append_uri_escaped (
+ signing_key, consumer_secret, NULL, FALSE);
+ g_string_append_c (signing_key, '&');
+ g_string_append_uri_escaped (
+ signing_key, access_token_secret, NULL, FALSE);
+
+ /* Sign the request. */
+
+ key = (gpointer) "oauth_signature";
+ string = oauth_sign_hmac_sha1 (base_string->str, signing_key->str);
+ g_hash_table_insert (parameters, key, g_strdup (string));
+ free (string); /* do not use g_free() */
+
+ g_free (request_uri);
+
+ g_string_free (query, TRUE);
+ g_string_free (base_string, TRUE);
+ g_string_free (signing_key, TRUE);
+
+ return parameters;
+}
+
+static void
+gdata_goa_authorizer_add_authorization (GDataAuthorizer *authorizer,
+ SoupMessage *message)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+ GoaOAuthBased *goa_oauth_based;
+ GHashTable *parameters;
+ GString *authorization;
+ const gchar *consumer_key;
+ const gchar *consumer_secret;
+ gint ii;
+
+ const gchar *oauth_keys[] = {
+ "oauth_version",
+ "oauth_nonce",
+ "oauth_timestamp",
+ "oauth_consumer_key",
+ "oauth_token",
+ "oauth_signature_method",
+ "oauth_signature"
+ };
+
+ /* This MUST be called with the mutex already locked. */
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ /* We can't add an Authorization header without an access token.
+ * Let the request fail. GData should refresh us if it gets back
+ * a "401 Authorization required" response from Google, and then
+ * automatically retry the request. */
+ if (priv->access_token == NULL)
+ return;
+
+ goa_oauth_based = goa_object_get_oauth_based (priv->goa_object);
+
+ consumer_key = goa_oauth_based_get_consumer_key (goa_oauth_based);
+ consumer_secret = goa_oauth_based_get_consumer_secret (goa_oauth_based);
+
+ parameters = gdata_goa_authorizer_get_parameters (
+ message,
+ consumer_key,
+ consumer_secret,
+ priv->access_token,
+ priv->access_token_secret);
+
+ authorization = g_string_new ("OAuth ");
+
+ for (ii = 0; ii < G_N_ELEMENTS (oauth_keys); ii++) {
+ const gchar *key;
+ const gchar *val;
+
+ key = oauth_keys[ii];
+ val = g_hash_table_lookup (parameters, key);
+
+ if (ii > 0)
+ g_string_append (authorization, ", ");
+
+ g_string_append (authorization, key);
+ g_string_append_c (authorization, '=');
+ g_string_append_c (authorization, '"');
+ g_string_append_uri_escaped (authorization, val, NULL, FALSE);
+ g_string_append_c (authorization, '"');
+ }
+
+ /* Use replace here, not append, to make sure
+ * there's only one "Authorization" header. */
+ soup_message_headers_replace (
+ message->request_headers,
+ "Authorization", authorization->str);
+
+ g_string_free (authorization, TRUE);
+ g_hash_table_destroy (parameters);
+
+ g_object_unref (goa_oauth_based);
+}
+
+static gboolean
+gdata_goa_authorizer_is_authorized (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+
+ /* This MUST be called with the mutex already locked. */
+
+ if (domain == NULL)
+ return TRUE;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+ domain = g_hash_table_lookup (priv->authorization_domains, domain);
+
+ return (domain != NULL);
+}
+
+static void
+gdata_goa_authorizer_set_goa_object (EGDataGoaAuthorizer *authorizer,
+ GoaObject *goa_object)
+{
+ g_return_if_fail (GOA_IS_OBJECT (goa_object));
+ g_return_if_fail (authorizer->priv->goa_object == NULL);
+
+ authorizer->priv->goa_object = g_object_ref (goa_object);
+}
+
+static void
+gdata_goa_authorizer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_GOA_OBJECT:
+ gdata_goa_authorizer_set_goa_object (
+ E_GDATA_GOA_AUTHORIZER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+gdata_goa_authorizer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_GOA_OBJECT:
+ g_value_set_object (
+ value,
+ e_gdata_goa_authorizer_get_goa_object (
+ E_GDATA_GOA_AUTHORIZER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+gdata_goa_authorizer_dispose (GObject *object)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ if (priv->goa_object != NULL) {
+ g_object_unref (priv->goa_object);
+ priv->goa_object = NULL;
+ }
+
+ g_hash_table_remove_all (priv->authorization_domains);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_gdata_goa_authorizer_parent_class)->dispose (object);
+}
+
+static void
+gdata_goa_authorizer_finalize (GObject *object)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ g_mutex_free (priv->mutex);
+ g_free (priv->access_token);
+ g_free (priv->access_token_secret);
+ g_hash_table_destroy (priv->authorization_domains);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_gdata_goa_authorizer_parent_class)->finalize (object);
+}
+
+static void
+gdata_goa_authorizer_constructed (GObject *object)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+ GType service_type;
+ GList *domains;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_gdata_goa_authorizer_parent_class)->
+ constructed (object);
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (object);
+
+ /* XXX We would need to generalize this to make the class
+ * reusable for other service types, probably by adding
+ * a "service-type" constructor property. */
+ service_type = GDATA_TYPE_CONTACTS_SERVICE;
+ domains = gdata_service_get_authorization_domains (service_type);
+
+ while (domains != NULL) {
+ g_hash_table_insert (
+ priv->authorization_domains,
+ g_object_ref (domains->data),
+ domains->data);
+ domains = g_list_delete_link (domains, domains);
+ }
+}
+
+static void
+gdata_goa_authorizer_process_request (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain,
+ SoupMessage *message)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (priv->mutex);
+
+ if (gdata_goa_authorizer_is_authorized (authorizer, domain))
+ gdata_goa_authorizer_add_authorization (authorizer, message);
+
+ g_mutex_unlock (priv->mutex);
+}
+
+static gboolean
+gdata_goa_authorizer_is_authorized_for_domain (GDataAuthorizer *authorizer,
+ GDataAuthorizationDomain *domain)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+ gboolean authorized;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (priv->mutex);
+
+ authorized = gdata_goa_authorizer_is_authorized (authorizer, domain);
+
+ g_mutex_unlock (priv->mutex);
+
+ return authorized;
+}
+
+static gboolean
+gdata_goa_authorizer_refresh_authorization (GDataAuthorizer *authorizer,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EGDataGoaAuthorizerPrivate *priv;
+ GoaOAuthBased *goa_oauth_based;
+ GoaAccount *goa_account;
+ gboolean success = TRUE;
+
+ priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+
+ g_mutex_lock (priv->mutex);
+
+ g_free (priv->access_token);
+ priv->access_token = NULL;
+
+ g_free (priv->access_token_secret);
+ priv->access_token_secret = NULL;
+
+ goa_account = goa_object_get_account (priv->goa_object);
+ goa_oauth_based = goa_object_get_oauth_based (priv->goa_object);
+
+ success &= goa_account_call_ensure_credentials_sync (
+ goa_account, NULL, cancellable, error);
+
+ success &= goa_oauth_based_call_get_access_token_sync (
+ goa_oauth_based, &priv->access_token,
+ &priv->access_token_secret, NULL, cancellable, error);
+
+ g_object_unref (goa_account);
+ g_object_unref (goa_oauth_based);
+
+ g_mutex_unlock (priv->mutex);
+
+ return success;
+}
+
+static void
+e_gdata_goa_authorizer_class_init (EGDataGoaAuthorizerClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EGDataGoaAuthorizerPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = gdata_goa_authorizer_set_property;
+ object_class->get_property = gdata_goa_authorizer_get_property;
+ object_class->dispose = gdata_goa_authorizer_dispose;
+ object_class->finalize = gdata_goa_authorizer_finalize;
+ object_class->constructed = gdata_goa_authorizer_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_GOA_OBJECT,
+ g_param_spec_object (
+ "goa-object",
+ "GoaObject",
+ "The GOA account to authenticate",
+ GOA_TYPE_OBJECT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_gdata_goa_authorizer_interface_init (GDataAuthorizerInterface *interface)
+{
+ interface->process_request =
+ gdata_goa_authorizer_process_request;
+ interface->is_authorized_for_domain =
+ gdata_goa_authorizer_is_authorized_for_domain;
+ interface->refresh_authorization =
+ gdata_goa_authorizer_refresh_authorization;
+}
+
+static void
+e_gdata_goa_authorizer_init (EGDataGoaAuthorizer *authorizer)
+{
+ GHashTable *authorization_domains;
+
+ authorization_domains = g_hash_table_new_full (
+ (GHashFunc) g_direct_hash,
+ (GEqualFunc) g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) NULL);
+
+ authorizer->priv = E_GDATA_GOA_AUTHORIZER_GET_PRIVATE (authorizer);
+ authorizer->priv->mutex = g_mutex_new ();
+ authorizer->priv->authorization_domains = authorization_domains;
+}
+
+EGDataGoaAuthorizer *
+e_gdata_goa_authorizer_new (GoaObject *goa_object)
+{
+ g_return_val_if_fail (GOA_IS_OBJECT (goa_object), NULL);
+
+ return g_object_new (
+ E_TYPE_GDATA_GOA_AUTHORIZER,
+ "goa-object", goa_object, NULL);
+}
+
+GoaObject *
+e_gdata_goa_authorizer_get_goa_object (EGDataGoaAuthorizer *authorizer)
+{
+ g_return_val_if_fail (E_IS_GDATA_GOA_AUTHORIZER (authorizer), NULL);
+
+ return authorizer->priv->goa_object;
+}
diff --git a/addressbook/backends/google/e-gdata-goa-authorizer.h b/addressbook/backends/google/e-gdata-goa-authorizer.h
new file mode 100644
index 0000000..fb50a1f
--- /dev/null
+++ b/addressbook/backends/google/e-gdata-goa-authorizer.h
@@ -0,0 +1,68 @@
+/*
+ * e-gdata-goa-authorizer.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/>
+ *
+ */
+
+#ifndef E_GDATA_GOA_AUTHORIZER_H
+#define E_GDATA_GOA_AUTHORIZER_H
+
+#include <gdata/gdata.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+/* Standard GObject macros */
+#define E_TYPE_GDATA_GOA_AUTHORIZER \
+ (e_gdata_goa_authorizer_get_type ())
+#define E_GDATA_GOA_AUTHORIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizer))
+#define E_GDATA_GOA_AUTHORIZER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizerClass))
+#define E_IS_GDATA_GOA_AUTHORIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_GDATA_GOA_AUTHORIZER))
+#define E_IS_GDATA_GOA_AUTHORIZER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_GDATA_GOA_AUTHORIZER))
+#define E_GDATA_GOA_AUTHORIZER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EGDataGoaAuthorizer EGDataGoaAuthorizer;
+typedef struct _EGDataGoaAuthorizerClass EGDataGoaAuthorizerClass;
+typedef struct _EGDataGoaAuthorizerPrivate EGDataGoaAuthorizerPrivate;
+
+struct _EGDataGoaAuthorizer {
+ GObject parent;
+ EGDataGoaAuthorizerPrivate *priv;
+};
+
+struct _EGDataGoaAuthorizerClass {
+ GObjectClass parent_class;
+};
+
+GType e_gdata_goa_authorizer_get_type (void);
+EGDataGoaAuthorizer *
+ e_gdata_goa_authorizer_new
+ (GoaObject *goa_object);
+GoaObject * e_gdata_goa_authorizer_get_goa_object
+ (EGDataGoaAuthorizer *authorizer);
+
+#endif /* E_GDATA_GOA_AUTHORIZER_H */
diff --git a/addressbook/libedata-book/Makefile.am b/addressbook/libedata-book/Makefile.am
index f25c1d6..bd04300 100644
--- a/addressbook/libedata-book/Makefile.am
+++ b/addressbook/libedata-book/Makefile.am
@@ -86,7 +86,8 @@ e_addressbook_factory_CPPFLAGS = \
-I$(top_srcdir)/addressbook/libegdbus \
-I$(top_builddir) \
-I$(top_builddir)/addressbook \
- $(EVOLUTION_ADDRESSBOOK_CFLAGS)
+ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \
+ $(GOA_CFLAGS)
e_addressbook_factory_SOURCES = \
e-data-book-factory.c \
@@ -97,7 +98,8 @@ e_addressbook_factory_LDADD = \
$(top_builddir)/addressbook/libedata-book/libedata-book-1.2.la \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
$(top_builddir)/libebackend/libebackend-1.2.la \
- $(EVOLUTION_ADDRESSBOOK_LIBS)
+ $(EVOLUTION_ADDRESSBOOK_LIBS) \
+ $(GOA_LIBS)
%-$(API_VERSION).pc: %.pc
cp $< $@
diff --git a/addressbook/libedata-book/e-data-book-factory.c b/addressbook/libedata-book/e-data-book-factory.c
index d395f0b..133652a 100644
--- a/addressbook/libedata-book/e-data-book-factory.c
+++ b/addressbook/libedata-book/e-data-book-factory.c
@@ -32,6 +32,15 @@
#endif
#endif
+#if HAVE_GOA
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+/* This is the property name or URL parameter under which we
+ * embed the GoaAccount ID into an EAccount or ESource object. */
+#define GOA_KEY "goa-account-id"
+#endif
+
#include <libebackend/e-data-server-module.h>
#include <libebackend/e-offline-listener.h>
#include "e-book-backend-factory.h"
@@ -98,6 +107,11 @@ struct _EDataBookFactoryPrivate {
/* whether should be online */
gboolean is_online;
+
+#ifdef HAVE_GOA
+ GoaClient *goa_client;
+ GHashTable *goa_accounts;
+#endif
};
/* Forward Declarations */
@@ -425,6 +439,27 @@ impl_BookFactory_get_book (EGdbusBookFactory *object,
priv->exit_timeout = 0;
}
+#ifdef HAVE_GOA
+ {
+ GoaObject *goa_object = NULL;
+ const gchar *goa_account_id;
+
+ /* Embed the corresponding GoaObject in the EBookBackend
+ * so the backend can retrieve it. We're not ready to add
+ * formal API for this to EBookBackend just yet. */
+ goa_account_id = e_source_get_property (source, GOA_KEY);
+ if (goa_account_id != NULL)
+ goa_object = g_hash_table_lookup (
+ factory->priv->goa_accounts, goa_account_id);
+ if (GOA_IS_OBJECT (goa_object))
+ g_object_set_data_full (
+ G_OBJECT (backend),
+ "GNOME Online Account",
+ g_object_ref (goa_object),
+ (GDestroyNotify) g_object_unref);
+ }
+#endif
+
path = construct_book_factory_path ();
book = e_data_book_new (backend, source);
g_hash_table_insert (priv->books, g_strdup (path), book);
@@ -485,6 +520,15 @@ e_data_book_factory_dispose (GObject *object)
priv->gdbus_object = NULL;
}
+#ifdef HAVE_GOA
+ if (priv->goa_client != NULL) {
+ g_object_unref (priv->goa_client);
+ priv->goa_client = NULL;
+ }
+
+ g_hash_table_remove_all (priv->goa_accounts);
+#endif
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
}
@@ -505,6 +549,10 @@ e_data_book_factory_finalize (GObject *object)
g_mutex_free (priv->books_lock);
g_mutex_free (priv->connections_lock);
+#ifdef HAVE_GOA
+ g_hash_table_destroy (priv->goa_accounts);
+#endif
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_data_book_factory_parent_class)->finalize (object);
}
@@ -560,6 +608,44 @@ e_data_book_factory_init (EDataBookFactory *factory)
g_error ("%s", error->message);
e_data_book_factory_register_backends (factory);
+
+#ifdef HAVE_GOA
+ factory->priv->goa_accounts = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ /* Failure here is non-fatal, just emit a terminal warning. */
+ factory->priv->goa_client = goa_client_new_sync (NULL, &error);
+
+ if (factory->priv->goa_client != NULL) {
+ GList *list, *iter;
+
+ list = goa_client_get_accounts (factory->priv->goa_client);
+
+ for (iter = list; iter != NULL; iter = g_list_next (iter)) {
+ GoaObject *goa_object;
+ GoaAccount *goa_account;
+ const gchar *goa_account_id;
+
+ goa_object = GOA_OBJECT (iter->data);
+ goa_account = goa_object_peek_account (goa_object);
+ goa_account_id = goa_account_get_id (goa_account);
+
+ /* Takes ownership of the GoaObject. */
+ g_hash_table_insert (
+ factory->priv->goa_accounts,
+ g_strdup (goa_account_id), goa_object);
+ }
+
+ g_list_free (list);
+
+ } else if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+#endif
}
static guint
diff --git a/configure.ac b/configure.ac
index c4e3e7f..ba30c7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,10 +35,12 @@ dnl Required Package Versions
m4_define([glib_minimum_version], [2.28])
m4_define([gtk_minimum_version], [3.0])
m4_define([gconf_minimum_version], [2.0.0]) dnl XXX Just a Guess
+m4_define([gnome_keyring_minimum_version], [2.20.1])
+m4_define([goa_minimum_version], [3.1.1])
m4_define([libxml_minimum_version], [2.0.0]) dnl XXX Just a Guess
m4_define([libsoup_minimum_version], [2.31.2])
m4_define([libgdata_minimum_version], [0.7.0])
-m4_define([gnome_keyring_minimum_version], [2.20.1])
+m4_define([oauth_minimum_version], [0.9.4])
m4_define([sqlite_minimum_version], [3.5])
m4_define([libical_minimum_version], [0.43])
m4_define([gweather_minimum_version], [2.90.0])
@@ -342,6 +344,37 @@ if `$PKG_CONFIG --atleast-version=0.9 libgdata`; then
AC_DEFINE(HAVE_LIBGDATA_0_9,1,[libgdata is 0.9 or higher])
fi
+dnl *******************************
+dnl Check for GNOME Online Accounts
+dnl *******************************
+AC_ARG_ENABLE([goa],
+ [AS_HELP_STRING([--enable-goa],
+ [enable GNOME Online Accounts support (default=yes)])],
+ [enable_goa=$enableval], [enable_goa=yes])
+AC_MSG_CHECKING([if GNOME Online Accounts support is enabled])
+AC_MSG_RESULT([$enable_goa])
+if test "x$enable_goa" = xyes; then
+ PKG_CHECK_MODULES([GOA], [goa-1.0 >= goa_minimum_version],,
+ [AC_MSG_ERROR([goa-1.0 not found (or version < goa_minimum_version),
+ If you want to disable GNOME Online Accounts support,
+ please append --disable-goa to configure.])])
+
+ PKG_CHECK_MODULES([OAUTH], [oauth >= oauth_minimum_version],,
+ [AC_MSG_ERROR([oauth not found (or version < oauth_minimum_version),
+ If you want to disable GNOME Online Accounts support,
+ please append --disable-goa to configure.])])
+
+ if `$PKG_CONFIG --atleast-version=0.9 libgdata`; then
+ AC_DEFINE(HAVE_GOA,1,[Have goa-1.0 and libgdata >= 0.9])
+ else
+ AC_MSG_ERROR([
+ libgdata 0.9 is required for GNOME Online Accounts support.
+ If you want to disable GNOME Online Accounts support, please
+ append --disable-goa to configure.])
+ fi
+fi
+AM_CONDITIONAL(HAVE_GOA, [test x$enable_goa = xyes])
+
if test x$os_win32 = xno; then
dnl ***********************************
dnl Check for GNOME Keyring.
@@ -1567,4 +1600,5 @@ echo "
Gtk Doc: $enable_gtk_doc
Introspection: $enable_introspection
Vala bindings: $enable_vala_bindings
+ GNOME Online Accounts $enable_goa
"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]