Blame gdata/gdata-goa-authorizer.c

Packit 4b6dd7
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit 4b6dd7
/*
Packit 4b6dd7
 * GData Client
Packit 4b6dd7
 * Copyright (C) Matthew Barnes 2011 <mbarnes@redhat.com>
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is free software; you can redistribute it and/or
Packit 4b6dd7
 * modify it under the terms of the GNU Lesser General Public
Packit 4b6dd7
 * License as published by the Free Software Foundation; either
Packit 4b6dd7
 * version 2.1 of the License, or (at your option) any later version.
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is distributed in the hope that it will be useful,
Packit 4b6dd7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4b6dd7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 4b6dd7
 * Lesser General Public License for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * You should have received a copy of the GNU Lesser General Public
Packit 4b6dd7
 * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * SECTION:gdata-goa-authorizer
Packit 4b6dd7
 * @short_description: GData GOA authorization interface
Packit 4b6dd7
 * @stability: Stable
Packit 4b6dd7
 * @include: gdata/gdata-goa-authorizer.h
Packit 4b6dd7
 *
Packit 4b6dd7
 * #GDataGoaAuthorizer provides an implementation of the #GDataAuthorizer interface for authentication and authorization using GNOME Online Accounts
Packit 4b6dd7
 * (GOA) over D-Bus. This allows a single login session (managed by the GOA daemon) to be used by multiple applications simultaneously, without each
Packit 4b6dd7
 * of those applications having to go through the authentication process themselves. Applications making use of #GDataGoaAuthorizer don't get access
Packit 4b6dd7
 * to the user's password (it's handled solely by the GOA daemon).
Packit 4b6dd7
 *
Packit 4b6dd7
 * Internally, GOA authenticates with the Google servers using the
Packit 4b6dd7
 * <ulink type="http" url="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html">OAuth 1.0</ulink> or
Packit 4b6dd7
 * <ulink type="http" url="https://developers.google.com/identity/protocols/OAuth2">OAuth 2.0</ulink> processes.
Packit 4b6dd7
 *
Packit 4b6dd7
 * #GDataGoaAuthorizer natively supports authorization against multiple services (unlike #GDataClientLoginAuthorizer), depending entirely on which
Packit 4b6dd7
 * services the user has enabled for their Google account in GOA. #GDataGoaAuthorizer cannot authenticate for more services than are enabled in GOA.
Packit 4b6dd7
 *
Packit 4b6dd7
 * <example>
Packit 4b6dd7
 *	<title>Authenticating Using GOA</title>
Packit 4b6dd7
 *	<programlisting>
Packit 4b6dd7
 *	GDataSomeService *service;
Packit 4b6dd7
 *	GoaObject *goa_object;
Packit 4b6dd7
 *	GDataGoaAuthorizer *authorizer;
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Create an authorizer and pass it an existing #GoaObject. */
Packit 4b6dd7
 *	goa_object = get_goa_object ();
Packit 4b6dd7
 *	authorizer = gdata_goa_authorizer_new (goa_object);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Create a service object and link it with the authorizer */
Packit 4b6dd7
 *	service = gdata_some_service_new (GDATA_AUTHORIZER (authorizer));
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Use the service! */
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_object_unref (service);
Packit 4b6dd7
 *	g_object_unref (authorizer);
Packit 4b6dd7
 *	g_object_unref (goa_object);
Packit 4b6dd7
 *	</programlisting>
Packit 4b6dd7
 * </example>
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.13.1
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
#include <stdlib.h>
Packit 4b6dd7
#include <string.h>
Packit 4b6dd7
#include <oauth.h>
Packit 4b6dd7
#include <glib.h>
Packit 4b6dd7
Packit 4b6dd7
#include "gdata-goa-authorizer.h"
Packit 4b6dd7
#include "gdata-authorizer.h"
Packit 4b6dd7
#include "gdata-service.h"
Packit 4b6dd7
Packit 4b6dd7
#include "services/calendar/gdata-calendar-service.h"
Packit 4b6dd7
#include "services/contacts/gdata-contacts-service.h"
Packit 4b6dd7
#include "services/documents/gdata-documents-service.h"
Packit 4b6dd7
#include "services/picasaweb/gdata-picasaweb-service.h"
Packit 4b6dd7
Packit 4b6dd7
#define HMAC_SHA1_LEN 20 /* bytes, raw */
Packit 4b6dd7
Packit 4b6dd7
static void gdata_goa_authorizer_interface_init (GDataAuthorizerInterface *interface);
Packit 4b6dd7
Packit 4b6dd7
/* GDataAuthorizer methods must be thread-safe. */
Packit 4b6dd7
static GMutex mutex;
Packit 4b6dd7
Packit 4b6dd7
struct _GDataGoaAuthorizerPrivate {
Packit 4b6dd7
	/* GoaObject is already thread-safe. */
Packit 4b6dd7
	GoaObject *goa_object;
Packit 4b6dd7
Packit 4b6dd7
	/* These members are protected by the global mutex (above). */
Packit 4b6dd7
	gchar *access_token;
Packit 4b6dd7
	gchar *access_token_secret;
Packit 4b6dd7
	GHashTable *authorization_domains;
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
enum {
Packit 4b6dd7
	PROP_GOA_OBJECT = 1,
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
G_DEFINE_TYPE_WITH_CODE (GDataGoaAuthorizer, gdata_goa_authorizer, G_TYPE_OBJECT,
Packit 4b6dd7
                         G_IMPLEMENT_INTERFACE (GDATA_TYPE_AUTHORIZER, gdata_goa_authorizer_interface_init))
Packit 4b6dd7
Packit 4b6dd7
static GHashTable *
Packit 4b6dd7
gdata_goa_authorizer_get_oauth1_parameters (SoupMessage *message, const gchar *consumer_key, const gchar *consumer_secret, const gchar *access_token,
Packit 4b6dd7
                                            const gchar *access_token_secret)
Packit 4b6dd7
{
Packit 4b6dd7
	GString *query;
Packit 4b6dd7
	GString *base_string;
Packit 4b6dd7
	GString *signing_key;
Packit 4b6dd7
	GHashTable *parameters;
Packit 4b6dd7
	GHashTableIter iter;
Packit 4b6dd7
	SoupURI *soup_uri;
Packit 4b6dd7
	GList *keys, *i;
Packit 4b6dd7
	gchar *string;
Packit 4b6dd7
	gchar *request_uri;
Packit 4b6dd7
	gpointer key;
Packit 4b6dd7
	guchar signature_buf[HMAC_SHA1_LEN];
Packit 4b6dd7
	gsize signature_buf_len;
Packit 4b6dd7
	GHmac *signature_hmac;
Packit 4b6dd7
Packit 4b6dd7
	parameters = g_hash_table_new_full ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, (GDestroyNotify) NULL, (GDestroyNotify) g_free);
Packit 4b6dd7
Packit 4b6dd7
	/* soup_form_decode() uses an awkward allocation style for
Packit 4b6dd7
	 * its hash table entries, so it's easier to copy its content
Packit 4b6dd7
	 * into our own hash table than try to use the returned hash
Packit 4b6dd7
	 * table directly. */
Packit 4b6dd7
Packit 4b6dd7
	soup_uri = soup_message_get_uri (message);
Packit 4b6dd7
	if (soup_uri->query != NULL) {
Packit 4b6dd7
		GHashTable *hash_table;
Packit 4b6dd7
		gpointer value;
Packit 4b6dd7
Packit 4b6dd7
		hash_table = soup_form_decode (soup_uri->query);
Packit 4b6dd7
		g_hash_table_iter_init (&iter, hash_table);
Packit 4b6dd7
		while (g_hash_table_iter_next (&iter, &key, &value)) {
Packit 4b6dd7
			key = (gpointer) g_intern_string (key);
Packit 4b6dd7
			value = g_strdup (value);
Packit 4b6dd7
			g_hash_table_insert (parameters, key, value);
Packit 4b6dd7
		}
Packit 4b6dd7
		g_hash_table_destroy (hash_table);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Add OAuth parameters. */
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_version";
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup ("1.0"));
Packit 4b6dd7
Packit 4b6dd7
	string = oauth_gen_nonce ();
Packit 4b6dd7
	key = (gpointer) "oauth_nonce";
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup (string));
Packit 4b6dd7
	free (string);  /* do not use g_free () */
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_timestamp";
Packit 4b6dd7
	string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
Packit 4b6dd7
	g_hash_table_insert (parameters, key, string); /* takes ownership */
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_consumer_key";
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup (consumer_key));
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_token";
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup (access_token));
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_signature_method";
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));
Packit 4b6dd7
Packit 4b6dd7
	/* Build the query part of the signature base string. */
Packit 4b6dd7
Packit 4b6dd7
	query = g_string_sized_new (512);
Packit 4b6dd7
	keys = g_hash_table_get_keys (parameters);
Packit 4b6dd7
	keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
Packit 4b6dd7
	for (i = keys; i != NULL; i = g_list_next (i)) {
Packit 4b6dd7
		const gchar *_key = i->data;
Packit 4b6dd7
		const gchar *val;
Packit 4b6dd7
Packit 4b6dd7
		val = g_hash_table_lookup (parameters, _key);
Packit 4b6dd7
Packit 4b6dd7
		if (i != keys) {
Packit 4b6dd7
			g_string_append_c (query, '&';;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		g_string_append_uri_escaped (query, _key, NULL, FALSE);
Packit 4b6dd7
		g_string_append_c (query, '=');
Packit 4b6dd7
		g_string_append_uri_escaped (query, val, NULL, FALSE);
Packit 4b6dd7
	}
Packit 4b6dd7
	g_list_free (keys);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the signature base string. */
Packit 4b6dd7
Packit 4b6dd7
	soup_uri = soup_uri_copy (soup_uri);
Packit 4b6dd7
	soup_uri_set_query (soup_uri, NULL);
Packit 4b6dd7
	soup_uri_set_fragment (soup_uri, NULL);
Packit 4b6dd7
	request_uri = soup_uri_to_string (soup_uri, FALSE);
Packit 4b6dd7
	soup_uri_free (soup_uri);
Packit 4b6dd7
Packit 4b6dd7
	base_string = g_string_sized_new (512);
Packit 4b6dd7
	g_string_append_uri_escaped (base_string, message->method, NULL, FALSE);
Packit 4b6dd7
	g_string_append_c (base_string, '&';;
Packit 4b6dd7
	g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
Packit 4b6dd7
	g_string_append_c (base_string, '&';;
Packit 4b6dd7
	g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the HMAC-SHA1 signing key. */
Packit 4b6dd7
Packit 4b6dd7
	signing_key = g_string_sized_new (512);
Packit 4b6dd7
	g_string_append_uri_escaped (signing_key, consumer_secret, NULL, FALSE);
Packit 4b6dd7
	g_string_append_c (signing_key, '&';;
Packit 4b6dd7
	g_string_append_uri_escaped (signing_key, access_token_secret, NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Sign the request. */
Packit 4b6dd7
	signature_hmac = g_hmac_new (G_CHECKSUM_SHA1, (const guchar*) signing_key->str, signing_key->len);
Packit 4b6dd7
	g_hmac_update (signature_hmac, (const guchar*) base_string->str, base_string->len);
Packit 4b6dd7
Packit 4b6dd7
	signature_buf_len = G_N_ELEMENTS (signature_buf);
Packit 4b6dd7
	g_hmac_get_digest (signature_hmac, signature_buf, &signature_buf_len);
Packit 4b6dd7
Packit 4b6dd7
	g_hmac_unref (signature_hmac);
Packit 4b6dd7
Packit 4b6dd7
	key = (gpointer) "oauth_signature";
Packit 4b6dd7
	string = g_base64_encode (signature_buf, signature_buf_len);
Packit 4b6dd7
	g_hash_table_insert (parameters, key, g_strdup (string));
Packit 4b6dd7
	g_free (string);
Packit 4b6dd7
Packit 4b6dd7
	g_free (request_uri);
Packit 4b6dd7
Packit 4b6dd7
	g_string_free (query, TRUE);
Packit 4b6dd7
	g_string_free (base_string, TRUE);
Packit 4b6dd7
	g_string_free (signing_key, TRUE);
Packit 4b6dd7
Packit 4b6dd7
	return parameters;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_add_oauth1_authorization (GDataAuthorizer *authorizer, SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
	GoaOAuthBased *goa_oauth_based;
Packit 4b6dd7
	GHashTable *parameters;
Packit 4b6dd7
	GString *authorization;
Packit 4b6dd7
	const gchar *consumer_key;
Packit 4b6dd7
	const gchar *consumer_secret;
Packit 4b6dd7
	guint ii;
Packit 4b6dd7
Packit 4b6dd7
	const gchar *oauth_keys[] = {
Packit 4b6dd7
		"oauth_version",
Packit 4b6dd7
		"oauth_nonce",
Packit 4b6dd7
		"oauth_timestamp",
Packit 4b6dd7
		"oauth_consumer_key",
Packit 4b6dd7
		"oauth_token",
Packit 4b6dd7
		"oauth_signature_method",
Packit 4b6dd7
		"oauth_signature"
Packit 4b6dd7
	};
Packit 4b6dd7
Packit 4b6dd7
	/* This MUST be called with the mutex already locked. */
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (authorizer)->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* We can't add an Authorization header without an access token.
Packit 4b6dd7
	 * Let the request fail.  GData should refresh us if it gets back
Packit 4b6dd7
	 * a "401 Authorization required" response from Google, and then
Packit 4b6dd7
	 * automatically retry the request. */
Packit 4b6dd7
	if (priv->access_token == NULL) {
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	goa_oauth_based = goa_object_get_oauth_based (priv->goa_object);
Packit 4b6dd7
Packit 4b6dd7
	consumer_key = goa_oauth_based_get_consumer_key (goa_oauth_based);
Packit 4b6dd7
	consumer_secret = goa_oauth_based_get_consumer_secret (goa_oauth_based);
Packit 4b6dd7
Packit 4b6dd7
	parameters = gdata_goa_authorizer_get_oauth1_parameters (message, consumer_key, consumer_secret,
Packit 4b6dd7
	                                                         priv->access_token, priv->access_token_secret);
Packit 4b6dd7
Packit 4b6dd7
	authorization = g_string_new ("OAuth ");
Packit 4b6dd7
Packit 4b6dd7
	for (ii = 0; ii < G_N_ELEMENTS (oauth_keys); ii++) {
Packit 4b6dd7
		const gchar *key;
Packit 4b6dd7
		const gchar *val;
Packit 4b6dd7
Packit 4b6dd7
		key = oauth_keys[ii];
Packit 4b6dd7
		val = g_hash_table_lookup (parameters, key);
Packit 4b6dd7
Packit 4b6dd7
		if (ii > 0) {
Packit 4b6dd7
			g_string_append (authorization, ", ");
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		g_string_append (authorization, key);
Packit 4b6dd7
		g_string_append_c (authorization, '=');
Packit 4b6dd7
		g_string_append_c (authorization, '"');
Packit 4b6dd7
		g_string_append_uri_escaped (authorization, val, NULL, FALSE);
Packit 4b6dd7
		g_string_append_c (authorization, '"');
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Use replace here, not append, to make sure
Packit 4b6dd7
	 * there's only one "Authorization" header. */
Packit 4b6dd7
	soup_message_headers_replace (message->request_headers, "Authorization", authorization->str);
Packit 4b6dd7
Packit 4b6dd7
	g_string_free (authorization, TRUE);
Packit 4b6dd7
	g_hash_table_destroy (parameters);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (goa_oauth_based);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_add_oauth2_authorization (GDataAuthorizer *authorizer, SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
	GString *authorization;
Packit 4b6dd7
Packit 4b6dd7
	/* This MUST be called with the mutex already locked. */
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (authorizer)->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* We can't add an Authorization header without an access token. Let the request fail. GData should refresh us if it gets back a
Packit 4b6dd7
	 * "401 Authorization required" response from Google, and then automatically retry the request. */
Packit 4b6dd7
	if (priv->access_token == NULL) {
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	authorization = g_string_new ("Bearer ");
Packit 4b6dd7
	g_string_append (authorization, priv->access_token);
Packit 4b6dd7
Packit 4b6dd7
	/* Use replace here, not append, to make sure there's only one "Authorization" header. */
Packit 4b6dd7
	soup_message_headers_replace (message->request_headers, "Authorization", authorization->str);
Packit 4b6dd7
Packit 4b6dd7
	g_string_free (authorization, TRUE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_add_authorization (GDataAuthorizer *authorizer, SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	/* This MUST be called with the mutex already locked. */
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (authorizer)->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* Prefer OAuth 2.0 over OAuth 1.0. */
Packit 4b6dd7
	if (goa_object_peek_oauth2_based (priv->goa_object) != NULL) {
Packit 4b6dd7
		gdata_goa_authorizer_add_oauth2_authorization (authorizer, message);
Packit 4b6dd7
	} else if (goa_object_peek_oauth_based (priv->goa_object) != NULL) {
Packit 4b6dd7
		gdata_goa_authorizer_add_oauth1_authorization (authorizer, message);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gboolean
Packit 4b6dd7
gdata_goa_authorizer_is_authorized (GDataAuthorizer *authorizer, GDataAuthorizationDomain *domain)
Packit 4b6dd7
{
Packit 4b6dd7
	/* This MUST be called with the mutex already locked. */
Packit 4b6dd7
Packit 4b6dd7
	if (domain == NULL) {
Packit 4b6dd7
		return TRUE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	domain = g_hash_table_lookup (GDATA_GOA_AUTHORIZER (authorizer)->priv->authorization_domains, domain);
Packit 4b6dd7
Packit 4b6dd7
	return (domain != NULL);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
add_authorization_domains (GDataGoaAuthorizer *self, GType service_type)
Packit 4b6dd7
{
Packit 4b6dd7
	GList/*<GDataAuthorizationDomain>*/ *domains;
Packit 4b6dd7
Packit 4b6dd7
	domains = gdata_service_get_authorization_domains (service_type);
Packit 4b6dd7
Packit 4b6dd7
	while (domains != NULL) {
Packit 4b6dd7
		g_hash_table_insert (self->priv->authorization_domains, g_object_ref (domains->data), domains->data);
Packit 4b6dd7
		domains = g_list_delete_link (domains, domains);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_set_goa_object (GDataGoaAuthorizer *self, GoaObject *goa_object)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GOA_IS_OBJECT (goa_object));
Packit 4b6dd7
	g_return_if_fail (self->priv->goa_object == NULL);
Packit 4b6dd7
Packit 4b6dd7
	self->priv->goa_object = g_object_ref (goa_object);
Packit 4b6dd7
Packit 4b6dd7
	/* Add authorisation domains for all the services supported by our GoaObject. */
Packit 4b6dd7
	if (goa_object_peek_calendar (goa_object) != NULL) {
Packit 4b6dd7
		add_authorization_domains (self, GDATA_TYPE_CALENDAR_SERVICE);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	if (goa_object_peek_contacts (goa_object) != NULL) {
Packit 4b6dd7
		add_authorization_domains (self, GDATA_TYPE_CONTACTS_SERVICE);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	if (goa_object_peek_documents (goa_object) != NULL || goa_object_peek_files (goa_object) != NULL) {
Packit 4b6dd7
		add_authorization_domains (self, GDATA_TYPE_DOCUMENTS_SERVICE);
Packit 4b6dd7
	}
Packit 4b6dd7
	
Packit 4b6dd7
	if (goa_object_peek_photos (goa_object) != NULL) {
Packit 4b6dd7
		add_authorization_domains (self, GDATA_TYPE_PICASAWEB_SERVICE);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_GOA_OBJECT:
Packit 4b6dd7
			gdata_goa_authorizer_set_goa_object (GDATA_GOA_AUTHORIZER (object), g_value_get_object (value));
Packit 4b6dd7
			return;
Packit 4b6dd7
		default:
Packit 4b6dd7
			g_assert_not_reached ();
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_GOA_OBJECT:
Packit 4b6dd7
			g_value_set_object (value, gdata_goa_authorizer_get_goa_object (GDATA_GOA_AUTHORIZER (object)));
Packit 4b6dd7
			return;
Packit 4b6dd7
		default:
Packit 4b6dd7
			g_assert_not_reached ();
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_dispose (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_clear_object (&priv->goa_object);
Packit 4b6dd7
	g_hash_table_remove_all (priv->authorization_domains);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to parent's dispose() method. */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_goa_authorizer_parent_class)->dispose (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_finalize (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->access_token);
Packit 4b6dd7
	g_free (priv->access_token_secret);
Packit 4b6dd7
	g_hash_table_destroy (priv->authorization_domains);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to parent's finalize() method. */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_goa_authorizer_parent_class)->finalize (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_process_request (GDataAuthorizer *authorizer, GDataAuthorizationDomain *domain, SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	g_mutex_lock (&mutex);
Packit 4b6dd7
Packit 4b6dd7
	if (gdata_goa_authorizer_is_authorized (authorizer, domain)) {
Packit 4b6dd7
		gdata_goa_authorizer_add_authorization (authorizer, message);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&mutex);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gboolean
Packit 4b6dd7
gdata_goa_authorizer_is_authorized_for_domain (GDataAuthorizer *authorizer, GDataAuthorizationDomain *domain)
Packit 4b6dd7
{
Packit 4b6dd7
	gboolean authorized;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&mutex);
Packit 4b6dd7
Packit 4b6dd7
	authorized = gdata_goa_authorizer_is_authorized (authorizer, domain);
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&mutex);
Packit 4b6dd7
Packit 4b6dd7
	return authorized;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gboolean
Packit 4b6dd7
gdata_goa_authorizer_refresh_authorization (GDataAuthorizer *authorizer, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataGoaAuthorizerPrivate *priv;
Packit 4b6dd7
	GoaOAuthBased *goa_oauth1_based;
Packit 4b6dd7
	GoaOAuth2Based *goa_oauth2_based;
Packit 4b6dd7
	GoaAccount *goa_account;
Packit 4b6dd7
	gboolean success = FALSE;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_GOA_AUTHORIZER (authorizer)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&mutex);
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->access_token);
Packit 4b6dd7
	priv->access_token = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->access_token_secret);
Packit 4b6dd7
	priv->access_token_secret = NULL;
Packit 4b6dd7
Packit 4b6dd7
	goa_account = goa_object_get_account (priv->goa_object);
Packit 4b6dd7
	goa_oauth1_based = goa_object_get_oauth_based (priv->goa_object);
Packit 4b6dd7
	goa_oauth2_based = goa_object_get_oauth2_based (priv->goa_object);
Packit 4b6dd7
Packit 4b6dd7
	success = goa_account_call_ensure_credentials_sync (goa_account, NULL, cancellable, error);
Packit 4b6dd7
Packit 4b6dd7
	if (success == FALSE) {
Packit 4b6dd7
		goto exit;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Prefer OAuth 2.0 over OAuth 1.0. */
Packit 4b6dd7
	if (goa_oauth2_based != NULL) {
Packit 4b6dd7
		success = goa_oauth2_based_call_get_access_token_sync (goa_oauth2_based, &priv->access_token, NULL, cancellable, error);
Packit 4b6dd7
	} else if (goa_oauth1_based != NULL) {
Packit 4b6dd7
		success = goa_oauth_based_call_get_access_token_sync (goa_oauth1_based, &priv->access_token, &priv->access_token_secret, NULL,
Packit 4b6dd7
		                                                      cancellable, error);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		g_warn_if_reached (); /* should never happen */
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
exit:
Packit 4b6dd7
	g_clear_object (&goa_account);
Packit 4b6dd7
	g_clear_object (&goa_oauth1_based);
Packit 4b6dd7
	g_clear_object (&goa_oauth2_based);
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&mutex);
Packit 4b6dd7
Packit 4b6dd7
	return success;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_class_init (GDataGoaAuthorizerClass *class)
Packit 4b6dd7
{
Packit 4b6dd7
	GObjectClass *object_class;
Packit 4b6dd7
Packit 4b6dd7
	g_type_class_add_private (class, sizeof (GDataGoaAuthorizerPrivate));
Packit 4b6dd7
Packit 4b6dd7
	object_class = G_OBJECT_CLASS (class);
Packit 4b6dd7
	object_class->set_property = gdata_goa_authorizer_set_property;
Packit 4b6dd7
	object_class->get_property = gdata_goa_authorizer_get_property;
Packit 4b6dd7
	object_class->dispose = gdata_goa_authorizer_dispose;
Packit 4b6dd7
	object_class->finalize = gdata_goa_authorizer_finalize;
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataGoaAuthorizer:goa-object:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The GOA account providing authentication. This should have all the necessary services enabled on it.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.13.1
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (object_class, PROP_GOA_OBJECT,
Packit 4b6dd7
	                                 g_param_spec_object ("goa-object", "GOA object", "The GOA account providing authentication.",
Packit 4b6dd7
	                                                      GOA_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_interface_init (GDataAuthorizerInterface *interface)
Packit 4b6dd7
{
Packit 4b6dd7
	interface->process_request = gdata_goa_authorizer_process_request;
Packit 4b6dd7
	interface->is_authorized_for_domain = gdata_goa_authorizer_is_authorized_for_domain;
Packit 4b6dd7
	interface->refresh_authorization = gdata_goa_authorizer_refresh_authorization;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_goa_authorizer_init (GDataGoaAuthorizer *authorizer)
Packit 4b6dd7
{
Packit 4b6dd7
	GHashTable *authorization_domains;
Packit 4b6dd7
Packit 4b6dd7
	authorization_domains = g_hash_table_new_full ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal,
Packit 4b6dd7
	                                               (GDestroyNotify) g_object_unref, (GDestroyNotify) NULL);
Packit 4b6dd7
Packit 4b6dd7
	authorizer->priv = G_TYPE_INSTANCE_GET_PRIVATE (authorizer, GDATA_TYPE_GOA_AUTHORIZER, GDataGoaAuthorizerPrivate);
Packit 4b6dd7
	authorizer->priv->authorization_domains = authorization_domains;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_goa_authorizer_new:
Packit 4b6dd7
 * @goa_object: (transfer none): the GOA account providing authentication
Packit 4b6dd7
 *
Packit 4b6dd7
 * Create a new #GDataGoaAuthorizer using the authentication token from the given @goa_object.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a new #GDataGoaAuthorizer; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.13.1
Packit 4b6dd7
 */
Packit 4b6dd7
GDataGoaAuthorizer *
Packit 4b6dd7
gdata_goa_authorizer_new (GoaObject *goa_object)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GOA_IS_OBJECT (goa_object), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_object_new (GDATA_TYPE_GOA_AUTHORIZER, "goa-object", goa_object, NULL);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_goa_authorizer_get_goa_object:
Packit 4b6dd7
 * @self: a #GDataGoaAuthorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * The GOA account providing authentication. This is the same as #GDataGoaAuthorizer:goa-object.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none): the GOA account providing authentication
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.13.1
Packit 4b6dd7
 */
Packit 4b6dd7
GoaObject *
Packit 4b6dd7
gdata_goa_authorizer_get_goa_object (GDataGoaAuthorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_GOA_AUTHORIZER (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return self->priv->goa_object;
Packit 4b6dd7
}