=?utf-8?q?=5Blibgdata=5D_Bug_656976_=E2=80=94_Merge_eds_and_GNOME_Documen?= =?utf-8?q?ts_GOA_authorisers?=



commit 56a39743779f63791e27886aaa3a9338602406f3
Author: Philip Withnall <philip tecnocode co uk>
Date:   Mon Jul 16 21:08:00 2012 +0100

    Bug 656976 â Merge eds and GNOME Documents GOA authorisers
    
    Copy the GOA authoriser from EDS into libgdata, modifying it a bit to add
    support for non-Contacts services and remove use of the liboauth HMAC function
    in favour of GLibâs implementation.
    
    This is essentially the same code as used by GNOME Documents. Originally
    written by Matthew Barnes. Heâs given permission for it to be relicenced from
    LGPL v2 or v3 to LGPL v2.1+. (See the bug report.)
    
    This adds the following API:
     â GDataGoaAuthorizer
    
    This is currently untested; waiting to port EDS and GNOME Documents to it
    to test it.
    
    Closes: https://bugzilla.gnome.org/show_bug.cgi?id=656976

 Makefile.am                       |   20 ++-
 configure.ac                      |   13 +-
 docs/reference/gdata-docs.xml     |    4 +
 docs/reference/gdata-sections.txt |   20 ++
 gdata/gdata-goa-authorizer.c      |  543 +++++++++++++++++++++++++++++++++++++
 gdata/gdata-goa-authorizer.h      |   70 +++++
 gdata/gdata.h                     |    4 +
 gdata/gdata.symbols               |    3 +
 gdata/tests/Makefile.am           |    6 +-
 libgdata.pc.in                    |    2 +-
 10 files changed, 678 insertions(+), 7 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0e9053c..4971369 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -167,6 +167,13 @@ gdata_headers = \
 	gdata/gdata-authorization-domain.h	\
 	gdata/gdata-client-login-authorizer.h	\
 	gdata/gdata-oauth1-authorizer.h
+
+if ENABLE_GNOME
+gdata_headers += \
+	gdata/gdata-goa-authorizer.h \
+	$(NULL)
+endif
+
 # The following headers are private, and shouldn't be installed:
 private_headers = \
 	gdata/gdata-private.h		\
@@ -393,6 +400,12 @@ gdata_sources = \
 	gdata/services/youtube/gdata-youtube-category.c		\
 	gdata/services/youtube/gdata-youtube-comment.c
 
+if ENABLE_GNOME
+gdata_sources += \
+	gdata/gdata-goa-authorizer.c \
+	$(NULL)
+endif
+
 main_header = gdata/gdata.h
 public_headers = \
 	$(gdatainclude_HEADERS)			\
@@ -473,7 +486,10 @@ EXTRA_DIST += introspection.m4
 if HAVE_INTROSPECTION
 gdata/GData- GDATA_API_VERSION_MAJOR@  GDATA_API_VERSION_MINOR@.gir: gdata/libgdata.la
 gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_INCLUDES = GObject-2.0 libxml2-2.0 Soup-2.4
-gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_CFLAGS = $(GDATA_CFLAGS) $(gdata_libgdata_la_CPPFLAGS)
+if ENABLE_GNOME
+gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_INCLUDES += Goa-1.0
+endif
+gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_CFLAGS = $(GDATA_CFLAGS) $(GNOME_CFLAGS) $(gdata_libgdata_la_CPPFLAGS)
 gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_LIBS = gdata/libgdata.la
 gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_FILES = $(gdata_sources) $(public_headers)
 gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_NAMESPACE = GData
@@ -557,6 +573,7 @@ demos_scrapbook_scrapbook_CFLAGS = \
 	$(WARN_CFLAGS) \
 	$(GTK_CFLAGS) \
 	$(GDATA_CFLAGS) \
+	$(GNOME_CFLAGS) \
 	$(AM_CFLAGS) \
 	$(NULL)
 
@@ -564,6 +581,7 @@ demos_scrapbook_scrapbook_LDADD = \
 	$(top_builddir)/gdata/libgdata.la \
 	$(GTK_LIBS) \
 	$(GDATA_LIBS) \
+	$(GNOME_LIBS) \
 	$(AM_LDADD) \
 	$(NULL)
 
diff --git a/configure.ac b/configure.ac
index ae9547d..2e4adf0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,7 @@ GIO_REQS=2.17.3
 SOUP_REQS=2.37.91
 OAUTH_REQS=0.9.4
 GTK_REQS=2.91.2
+GOA_REQS=3.2
 
 # Before making a release, the GDATA_LT_VERSION string should be modified. The string is of the form c:r:a. Follow these instructions sequentially:
 #
@@ -61,8 +62,10 @@ AC_SUBST(GDATA_API_VERSION)
 AC_SUBST(GDATA_API_VERSION_MAJOR)
 AC_SUBST(GDATA_API_VERSION_MINOR)
 
-pkg_modules="glib-2.0 >= $GLIB_REQS libxml-2.0 gthread-2.0 gio-2.0 >= $GIO_REQS libsoup-2.4 >= $SOUP_REQS oauth >= $OAUTH_REQS"
-PKG_CHECK_MODULES(GDATA, [$pkg_modules])
+GDATA_PACKAGES="glib-2.0 >= $GLIB_REQS libxml-2.0 gthread-2.0 gio-2.0 >= $GIO_REQS libsoup-2.4 >= $SOUP_REQS oauth >= $OAUTH_REQS"
+AC_SUBST([GDATA_PACKAGES])
+
+PKG_CHECK_MODULES(GDATA, [$GDATA_PACKAGES])
 AC_SUBST(GDATA_CFLAGS)
 AC_SUBST(GDATA_LIBS)
 
@@ -82,10 +85,14 @@ AM_CONDITIONAL([HAVE_GTK], [test "x$have_gtk" = "xyes"])
 AC_MSG_CHECKING(whether to build with GNOME support)
 AC_ARG_ENABLE(gnome, AS_HELP_STRING([--enable-gnome], [Whether to enable GNOME support]),, enable_gnome=yes)
 AC_MSG_RESULT($enable_gnome)
+AM_CONDITIONAL([ENABLE_GNOME], [test "x$enable_gnome" = "xyes"])
+
+GNOME_PACKAGES="libsoup-gnome-2.4 gnome-keyring-1 goa-1.0 >= $GOA_REQS"
+AC_SUBST([GNOME_PACKAGES])
 
 if test "x$enable_gnome" = "xyes"; then
 	AC_DEFINE(HAVE_GNOME, 1, [Defined if GNOME support is enabled])
-	PKG_CHECK_MODULES([GNOME], [libsoup-gnome-2.4 gnome-keyring-1])
+	PKG_CHECK_MODULES([GNOME], [$GNOME_PACKAGES])
 fi
 
 # Various necessary functions and headers
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index d0f5884..5358e6c 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -237,6 +237,10 @@
 			<title>Index of new symbols in 0.13.0</title>
 			<xi:include href="xml/api-index-0.13.0.xml"><xi:fallback/></xi:include>
 		</index>
+		<index role="0.13.1">
+			<title>Index of new symbols in 0.13.1</title>
+			<xi:include href="xml/api-index-0.13.1.xml"><xi:fallback/></xi:include>
+		</index>
 		<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 	</part>
 </book>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index eb83965..49ddfb3 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -2323,6 +2323,26 @@ GDataOAuth1AuthorizerPrivate
 </SECTION>
 
 <SECTION>
+<FILE>gdata-goa-authorizer</FILE>
+<TITLE>GDataGoaAuthorizer</TITLE>
+GDataGoaAuthorizer
+GDataGoaAuthorizerClass
+gdata_goa_authorizer_new
+gdata_goa_authorizer_get_goa_object
+<SUBSECTION Standard>
+GDATA_TYPE_GOA_AUTHORIZER
+GDATA_GOA_AUTHORIZER
+GDATA_GOA_AUTHORIZER_CLASS
+GDATA_IS_GOA_AUTHORIZER
+GDATA_IS_GOA_AUTHORIZER_CLASS
+GDATA_GOA_AUTHORIZER_GET_CLASS
+gdata_goa_authorizer_get_type
+<SUBSECTION Private>
+GDataGoaAuthorizerPrivate
+GOA_API_IS_SUBJECT_TO_CHANGE
+</SECTION>
+
+<SECTION>
 <FILE>gdata-commentable</FILE>
 <TITLE>GDataCommentable</TITLE>
 GDataCommentable
diff --git a/gdata/gdata-goa-authorizer.c b/gdata/gdata-goa-authorizer.c
new file mode 100644
index 0000000..6f0c525
--- /dev/null
+++ b/gdata/gdata-goa-authorizer.c
@@ -0,0 +1,543 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Matthew Barnes 2011 <mbarnes redhat com>
+ *
+ * GData Client 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.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-goa-authorizer
+ * @short_description: GData GOA authorization interface
+ * @stability: Unstable
+ * @include: gdata/gdata-goa-authorizer.h
+ *
+ * #GDataGoaAuthorizer provides an implementation of the #GDataAuthorizer interface for authentication and authorization using GNOME Online Accounts
+ * (GOA) over D-Bus. This allows a single login session (managed by the GOA daemon) to be used by multiple applications simultaneously, without each
+ * of those applications having to go through the authentication process themselves. Applications making use of #GDataGoaAuthorizer don't get access
+ * to the user's password (it's handled solely by the GOA daemon).
+ *
+ * Internally, GOA authenticates with the Google servers using the
+ * <ulink type="http" url="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html";>OAuth 1.0</ulink> process.
+ *
+ * #GDataGoaAuthorizer natively supports authorization against multiple services (unlike #GDataClientLoginAuthorizer), depending entirely on which
+ * services the user has enabled for their Google account in GOA. #GDataGoaAuthorizer cannot authenticate for more services than are enabled in GOA.
+ *
+ * <example>
+ *	<title>Authenticating Using GOA</title>
+ *	<programlisting>
+ *	GDataSomeService *service;
+ *	GoaObject *goa_object;
+ *	GDataGoaAuthorizer *authorizer;
+ *
+ *	/<!-- -->* Create an authorizer and pass it an existing #GoaObject. *<!-- -->/
+ *	goa_object = get_goa_object ();
+ *	authorizer = gdata_goa_authorizer_new (goa_object);
+ *
+ *	/<!-- -->* Create a service object and link it with the authorizer *<!-- -->/
+ *	service = gdata_some_service_new (GDATA_AUTHORIZER (authorizer));
+ *
+ *	/<!-- -->* Use the service! *<!-- -->/
+ *
+ *	g_object_unref (service);
+ *	g_object_unref (authorizer);
+ *	g_object_unref (goa_object);
+ *	</programlisting>
+ * </example>
+ *
+ * Since: 0.13.1
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <oauth.h>
+#include <glib.h>
+
+#include "gdata-goa-authorizer.h"
+#include "gdata-authorizer.h"
+#include "gdata-service.h"
+
+#include "services/calendar/gdata-calendar-service.h"
+#include "services/contacts/gdata-contacts-service.h"
+#include "services/documents/gdata-documents-service.h"
+
+#define HMAC_SHA1_LEN 20 /* bytes, raw */
+
+static void gdata_goa_authorizer_interface_init (GDataAuthorizerInterface *interface);
+
+/* GDataAuthorizer methods must be thread-safe. */
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+
+struct _GDataGoaAuthorizerPrivate {
+	/* GoaObject is already thread-safe. */
+	GoaObject *goa_object;
+
+	/* These members are protected by the global mutex (above). */
+	gchar *access_token;
+	gchar *access_token_secret;
+	GHashTable *authorization_domains;
+};
+
+enum {
+	PROP_GOA_OBJECT = 1,
+};
+
+G_DEFINE_TYPE_WITH_CODE (GDataGoaAuthorizer, gdata_goa_authorizer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDATA_TYPE_AUTHORIZER, 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;
+	GHashTableIter iter;
+	SoupURI *soup_uri;
+	GList *keys, *i;
+	gchar *string;
+	gchar *request_uri;
+	gpointer key;
+	guchar signature_buf[HMAC_SHA1_LEN];
+	gsize signature_buf_len;
+	GHmac *signature_hmac;
+
+	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);
+	if (soup_uri->query != NULL) {
+		GHashTable *hash_table;
+		gpointer value;
+
+		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);
+			value = g_strdup (value);
+			g_hash_table_insert (parameters, key, 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 (i = keys; i != NULL; i = g_list_next (i)) {
+		const gchar *_key = i->data;
+		const gchar *val;
+
+		val = g_hash_table_lookup (parameters, _key);
+
+		if (i != 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. */
+	signature_hmac = g_hmac_new (G_CHECKSUM_SHA1, (const guchar*) signing_key->str, signing_key->len);
+	g_hmac_update (signature_hmac, (const guchar*) base_string->str, base_string->len);
+
+	signature_buf_len = G_N_ELEMENTS (signature_buf);
+	g_hmac_get_digest (signature_hmac, signature_buf, &signature_buf_len);
+
+	g_hmac_unref (signature_hmac);
+
+	key = (gpointer) "oauth_signature";
+	string = g_base64_encode (signature_buf, signature_buf_len);
+	g_hash_table_insert (parameters, key, g_strdup (string));
+	g_free (string);
+
+	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)
+{
+	GDataGoaAuthorizerPrivate *priv;
+	GoaOAuthBased *goa_oauth_based;
+	GHashTable *parameters;
+	GString *authorization;
+	const gchar *consumer_key;
+	const gchar *consumer_secret;
+	guint 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 = GDATA_GOA_AUTHORIZER (authorizer)->priv;
+
+	/* 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)
+{
+	/* This MUST be called with the mutex already locked. */
+
+	if (domain == NULL) {
+		return TRUE;
+	}
+
+	domain = g_hash_table_lookup (GDATA_GOA_AUTHORIZER (authorizer)->priv->authorization_domains, domain);
+
+	return (domain != NULL);
+}
+
+static void
+add_authorization_domains (GDataGoaAuthorizer *self, GType service_type)
+{
+	GList/*<GDataAuthorizationDomain>*/ *domains;
+
+	domains = gdata_service_get_authorization_domains (service_type);
+
+	while (domains != NULL) {
+		g_hash_table_insert (self->priv->authorization_domains, g_object_ref (domains->data), domains->data);
+		domains = g_list_delete_link (domains, domains);
+	}
+}
+
+static void
+gdata_goa_authorizer_set_goa_object (GDataGoaAuthorizer *self, GoaObject *goa_object)
+{
+	g_return_if_fail (GOA_IS_OBJECT (goa_object));
+	g_return_if_fail (self->priv->goa_object == NULL);
+
+	self->priv->goa_object = g_object_ref (goa_object);
+
+	/* Add authorisation domains for all the services supported by our GoaObject. */
+	if (goa_object_peek_calendar (goa_object) != NULL) {
+		add_authorization_domains (self, GDATA_TYPE_CALENDAR_SERVICE);
+	}
+
+	if (goa_object_peek_contacts (goa_object) != NULL) {
+		add_authorization_domains (self, GDATA_TYPE_CONTACTS_SERVICE);
+	}
+
+	if (goa_object_peek_documents (goa_object) != NULL) {
+		add_authorization_domains (self, GDATA_TYPE_DOCUMENTS_SERVICE);
+	}
+}
+
+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 (GDATA_GOA_AUTHORIZER (object), g_value_get_object (value));
+			return;
+		default:
+			g_assert_not_reached ();
+	}
+
+	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, gdata_goa_authorizer_get_goa_object (GDATA_GOA_AUTHORIZER (object)));
+			return;
+		default:
+			g_assert_not_reached ();
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+gdata_goa_authorizer_dispose (GObject *object)
+{
+	GDataGoaAuthorizerPrivate *priv;
+
+	priv = GDATA_GOA_AUTHORIZER (object)->priv;
+
+	g_clear_object (&priv->goa_object);
+	g_hash_table_remove_all (priv->authorization_domains);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (gdata_goa_authorizer_parent_class)->dispose (object);
+}
+
+static void
+gdata_goa_authorizer_finalize (GObject *object)
+{
+	GDataGoaAuthorizerPrivate *priv;
+
+	priv = GDATA_GOA_AUTHORIZER (object)->priv;
+
+	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 (gdata_goa_authorizer_parent_class)->finalize (object);
+}
+
+static void
+gdata_goa_authorizer_process_request (GDataAuthorizer *authorizer, GDataAuthorizationDomain *domain, SoupMessage *message)
+{
+	g_static_mutex_lock (&mutex);
+
+	if (gdata_goa_authorizer_is_authorized (authorizer, domain)) {
+		gdata_goa_authorizer_add_authorization (authorizer, message);
+	}
+
+	g_static_mutex_unlock (&mutex);
+}
+
+static gboolean
+gdata_goa_authorizer_is_authorized_for_domain (GDataAuthorizer *authorizer, GDataAuthorizationDomain *domain)
+{
+	gboolean authorized;
+
+	g_static_mutex_lock (&mutex);
+
+	authorized = gdata_goa_authorizer_is_authorized (authorizer, domain);
+
+	g_static_mutex_unlock (&mutex);
+
+	return authorized;
+}
+
+static gboolean
+gdata_goa_authorizer_refresh_authorization (GDataAuthorizer *authorizer, GCancellable *cancellable, GError **error)
+{
+	GDataGoaAuthorizerPrivate *priv;
+	GoaOAuthBased *goa_oauth_based;
+	GoaAccount *goa_account;
+	gboolean success = TRUE;
+
+	priv = GDATA_GOA_AUTHORIZER (authorizer)->priv;
+
+	g_static_mutex_lock (&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_static_mutex_unlock (&mutex);
+
+	return success;
+}
+
+static void
+gdata_goa_authorizer_class_init (GDataGoaAuthorizerClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (GDataGoaAuthorizerPrivate));
+
+	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;
+
+	/**
+	 * GDataGoaAuthorizer:goa-object:
+	 *
+	 * The GOA account providing authentication. This should have all the necessary services enabled on it.
+	 *
+	 * Since: 0.13.1
+	 */
+	g_object_class_install_property (object_class, PROP_GOA_OBJECT,
+	                                 g_param_spec_object ("goa-object", "GOA object", "The GOA account providing authentication.",
+	                                                      GOA_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+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
+gdata_goa_authorizer_init (GDataGoaAuthorizer *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 = G_TYPE_INSTANCE_GET_PRIVATE (authorizer, GDATA_TYPE_GOA_AUTHORIZER, GDataGoaAuthorizerPrivate);
+	authorizer->priv->authorization_domains = authorization_domains;
+}
+
+/**
+ * gdata_goa_authorizer_new:
+ * @goa_object: (transfer none): the GOA account providing authentication
+ *
+ * Create a new #GDataGoaAuthorizer using the authentication token from the given @goa_object.
+ *
+ * Return value: (transfer full): a new #GDataGoaAuthorizer; unref with g_object_unref()
+ *
+ * Since: 0.13.1
+ */
+GDataGoaAuthorizer *
+gdata_goa_authorizer_new (GoaObject *goa_object)
+{
+	g_return_val_if_fail (GOA_IS_OBJECT (goa_object), NULL);
+
+	return g_object_new (GDATA_TYPE_GOA_AUTHORIZER, "goa-object", goa_object, NULL);
+}
+
+/**
+ * gdata_goa_authorizer_get_goa_object:
+ * @self: a #GDataGoaAuthorizer
+ *
+ * The GOA account providing authentication. This is the same as #GDataGoaAuthorizer:goa-object.
+ *
+ * Return value: (transfer none): the GOA account providing authentication
+ *
+ * Since: 0.13.1
+ */
+GoaObject *
+gdata_goa_authorizer_get_goa_object (GDataGoaAuthorizer *self)
+{
+	g_return_val_if_fail (GDATA_IS_GOA_AUTHORIZER (self), NULL);
+
+	return self->priv->goa_object;
+}
diff --git a/gdata/gdata-goa-authorizer.h b/gdata/gdata-goa-authorizer.h
new file mode 100644
index 0000000..3ce6e6e
--- /dev/null
+++ b/gdata/gdata-goa-authorizer.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Matthew Barnes 2011 <mbarnes redhat com>
+ *
+ * GData Client 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.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GOA_AUTHORIZER_H
+#define GDATA_GOA_AUTHORIZER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+/* Standard GObject macros */
+#define GDATA_TYPE_GOA_AUTHORIZER		(gdata_goa_authorizer_get_type ())
+#define GDATA_GOA_AUTHORIZER(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GDATA_TYPE_GOA_AUTHORIZER, GDataGoaAuthorizer))
+#define GDATA_GOA_AUTHORIZER_CLASS(cls)		(G_TYPE_CHECK_CLASS_CAST ((cls), GDATA_TYPE_GOA_AUTHORIZER, GDataGoaAuthorizerClass))
+#define GDATA_IS_GOA_AUTHORIZER(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDATA_TYPE_GOA_AUTHORIZER))
+#define GDATA_IS_GOA_AUTHORIZER_CLASS(cls)	(G_TYPE_CHECK_CLASS_TYPE ((cls), GDATA_TYPE_GOA_AUTHORIZER))
+#define GDATA_GOA_AUTHORIZER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GDATA_TYPE_GOA_AUTHORIZER, GDataGoaAuthorizerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GDataGoaAuthorizerPrivate	GDataGoaAuthorizerPrivate;
+
+/**
+ * GDataGoaAuthorizer:
+ *
+ * All the fields in the #GDataGoaAuthorizer structure are private and should never be accessed directly.
+ *
+ * Since: 0.13.1
+ */
+typedef struct {
+	/*< private >*/
+	GObject parent;
+	GDataGoaAuthorizerPrivate *priv;
+} GDataGoaAuthorizer;
+
+/**
+ * GDataGoaAuthorizerClass:
+ *
+ * All the fields in the #GDataGoaAuthorizerClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.13.1
+ */
+typedef struct {
+	/*< private >*/
+	GObjectClass parent_class;
+} GDataGoaAuthorizerClass;
+
+GType gdata_goa_authorizer_get_type (void) G_GNUC_CONST;
+GDataGoaAuthorizer *gdata_goa_authorizer_new (GoaObject *goa_object) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+GoaObject *gdata_goa_authorizer_get_goa_object (GDataGoaAuthorizer *self) G_GNUC_PURE;
+
+#endif /* GDATA_GOA_AUTHORIZER_H */
diff --git a/gdata/gdata.h b/gdata/gdata.h
index d0f2537..2039750 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -39,6 +39,10 @@
 #include <gdata/gdata-authorization-domain.h>
 #include <gdata/gdata-client-login-authorizer.h>
 #include <gdata/gdata-oauth1-authorizer.h>
+#ifdef GOA_API_IS_SUBJECT_TO_CHANGE
+/* You need to define GOA_API_IS_SUBJECT_TO_CHANGE in order to use the GOA authoriser. */
+#include <gdata/gdata-goa-authorizer.h>
+#endif /* GOA_API_IS_SUBJECT_TO_CHANGE */
 #include <gdata/gdata-commentable.h>
 #include <gdata/gdata-comment.h>
 #include <gdata/gdata-version.h>
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 103867e..29151f1 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -955,3 +955,6 @@ gdata_documents_entry_get_quota_used
 gdata_documents_service_copy_document
 gdata_documents_service_copy_document_async
 gdata_documents_service_copy_document_finish
+gdata_goa_authorizer_get_type
+gdata_goa_authorizer_new
+gdata_goa_authorizer_get_goa_object
diff --git a/gdata/tests/Makefile.am b/gdata/tests/Makefile.am
index 3c40451..d2ed75e 100644
--- a/gdata/tests/Makefile.am
+++ b/gdata/tests/Makefile.am
@@ -9,12 +9,14 @@ INCLUDES = \
 	-DLIBGDATA_DISABLE_DEPRECATED			\
 	$(DISABLE_DEPRECATED)				\
 	$(WARN_CFLAGS)					\
-	$(GDATA_CFLAGS)
+	$(GDATA_CFLAGS)					\
+	$(GNOME_CFLAGS)
 
 LIBS = \
 	$(top_builddir)/gdata/libgdata.la	\
 	$(GDK_PIXBUF_LIBS)			\
-	$(GDATA_LIBS)
+	$(GDATA_LIBS)				\
+	$(GNOME_LIBS)
 
 noinst_PROGRAMS = $(TEST_PROGS)
 
diff --git a/libgdata.pc.in b/libgdata.pc.in
index 1ffb628..ed1f4a4 100644
--- a/libgdata.pc.in
+++ b/libgdata.pc.in
@@ -6,6 +6,6 @@ includedir= includedir@
 Name: libgdata
 Description: GData client library
 Version: @VERSION@
-Requires: libsoup-2.4 libxml-2.0
+Requires: @GDATA_PACKAGES@ @GNOME_PACKAGES@
 Libs: -L${libdir} -lgdata
 Cflags: -I${includedir}/libgdata



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