[libgdata/646285-oauth2] core: UNFINISHED work on OAuth2
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgdata/646285-oauth2] core: UNFINISHED work on OAuth2
- Date: Mon, 15 Sep 2014 22:58:20 +0000 (UTC)
commit 9e2ea12bafa7a48918873388a699cbde582cbc1b
Author: Philip Withnall <philip tecnocode co uk>
Date: Sun Jul 3 21:37:49 2011 +0100
core: UNFINISHED work on OAuth2
Makefile.am | 5 +-
docs/reference/gdata-docs.xml | 1 +
docs/reference/gdata-sections.txt | 32 +
gdata/gdata-oauth2-authorizer.c | 1176 +++++++++++++++++++++++++++++++++++++
gdata/gdata-oauth2-authorizer.h | 100 ++++
gdata/gdata.h | 1 +
gdata/gdata.symbols | 16 +
gdata/tests/Makefile.am | 3 +
gdata/tests/oauth2-authorizer.c | 1048 +++++++++++++++++++++++++++++++++
9 files changed, 2381 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 9940f4a..e5c5d07 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -191,7 +191,9 @@ gdata_headers = \
gdata/gdata-authorizer.h \
gdata/gdata-authorization-domain.h \
gdata/gdata-client-login-authorizer.h \
- gdata/gdata-oauth1-authorizer.h
+ gdata/gdata-oauth1-authorizer.h \
+ gdata/gdata-oauth2-authorizer.h \
+ $(NULL)
if ENABLE_GOA
gdata_headers += \
@@ -366,6 +368,7 @@ gdata_sources = \
gdata/gdata-authorization-domain.c \
gdata/gdata-client-login-authorizer.c \
gdata/gdata-oauth1-authorizer.c \
+ gdata/gdata-oauth2-authorizer.c \
\
gdata/atom/gdata-author.c \
gdata/atom/gdata-category.c \
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index d3b4366..65c77fd 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -56,6 +56,7 @@
<xi:include href="xml/gdata-client-login-authorizer.xml"/>
<xi:include href="xml/gdata-goa-authorizer.xml"/>
<xi:include href="xml/gdata-oauth1-authorizer.xml"/>
+ <xi:include href="xml/gdata-oauth2-authorizer.xml"/>
</chapter>
<chapter>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index fdcac2f..b7865cf 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -2756,3 +2756,35 @@ GDATA_TYPE_FREEBASE_SEARCH_RESULT
<SUBSECTION Private>
GDataFreebaseSearchResultPrivate
</SECTION>
+
+<SECTION>
+<FILE>gdata-oauth2-authorizer</FILE>
+<TITLE>GDataOAuth2Authorizer</TITLE>
+GDataOAuth2Authorizer
+GDataOAuth2AuthorizerClass
+gdata_oauth2_authorizer_new
+gdata_oauth2_authorizer_new_for_authorization_domains
+gdata_oauth2_authorizer_build_authentication_uri
+gdata_oauth2_authorizer_request_authorization
+gdata_oauth2_authorizer_request_authorization_async
+gdata_oauth2_authorizer_request_authorization_finish
+gdata_oauth2_authorizer_get_client_id
+gdata_oauth2_authorizer_get_redirect_uri
+gdata_oauth2_authorizer_get_client_secret
+gdata_oauth2_authorizer_get_locale
+gdata_oauth2_authorizer_set_locale
+gdata_oauth2_authorizer_get_timeout
+gdata_oauth2_authorizer_set_timeout
+gdata_oauth2_authorizer_get_proxy_resolver
+gdata_oauth2_authorizer_set_proxy_resolver
+<SUBSECTION Standard>
+GDATA_TYPE_OAUTH2_AUTHORIZER
+GDATA_OAUTH2_AUTHORIZER
+GDATA_OAUTH2_AUTHORIZER_CLASS
+GDATA_IS_OAUTH2_AUTHORIZER
+GDATA_IS_OAUTH2_AUTHORIZER_CLASS
+GDATA_OAUTH2_AUTHORIZER_GET_CLASS
+gdata_oauth2_authorizer_get_type
+<SUBSECTION Private>
+GDataOAuth2AuthorizerPrivate
+</SECTION>
diff --git a/gdata/gdata-oauth2-authorizer.c b/gdata/gdata-oauth2-authorizer.c
new file mode 100644
index 0000000..27f7c3f
--- /dev/null
+++ b/gdata/gdata-oauth2-authorizer.c
@@ -0,0 +1,1176 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2011, 2014 <philip tecnocode co uk>
+ *
+ * GData Client is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-oauth2-authorizer
+ * @short_description: GData OAuth 2.0 authorization interface
+ * @stability: Unstable
+ * @include: gdata/gdata-oauth2-authorizer.h
+ *
+ * TODO
+ * note we don't handle TTLs
+ *
+ * Since: UNRELEASED
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "gdata-oauth2-authorizer.h"
+#include "gdata-private.h"
+
+/* TODO: Add OAuth 2.0 to the documentation at the head of gdata-authorizer.c, and write a new example for
GDataService with multiple services with it */
+
+static void authorizer_init (GDataAuthorizerInterface *iface);
+static void dispose (GObject *object);
+static void finalize (GObject *object);
+static void get_property (GObject *object, guint property_id, GValue *value,
+ GParamSpec *pspec);
+static void set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec);
+
+static void process_request (GDataAuthorizer *self,
+ GDataAuthorizationDomain *domain,
+ SoupMessage *message);
+static void sign_message_locked (GDataOAuth2Authorizer *self,
+ SoupMessage *message,
+ const gchar *access_token);
+static gboolean is_authorized_for_domain (GDataAuthorizer *self,
+ GDataAuthorizationDomain *domain);
+static gboolean refresh_authorization (GDataAuthorizer *self,
+ GCancellable *cancellable,
+ GError **error);
+
+static void parse_grant_response (GDataOAuth2Authorizer *self, guint status,
+ const gchar *reason_phrase,
+ const gchar *response_body, gssize length,
+ GError **error);
+
+static void notify_timeout_cb (GObject *gobject, GParamSpec *pspec,
+ GObject *self);
+
+struct _GDataOAuth2AuthorizerPrivate {
+ SoupSession *session; /* owned */
+ GProxyResolver *proxy_resolver; /* owned */
+
+ gchar *client_id; /* owned */
+ gchar *redirect_uri; /* owned */
+ gchar *client_secret; /* owned */
+ gchar *locale; /* owned */
+
+ /* Mutex for access_token, refresh_token and authentication_domains. */
+ GMutex mutex;
+
+ /* These are both non-NULL when authorised, and both NULL otherwise. */
+ gchar *access_token; /* owned */
+ gchar *refresh_token; /* owned */
+
+ /* Mapping from GDataAuthorizationDomain to itself; a set of domains for
+ * which ->access_token is valid. */
+ GHashTable *authentication_domains; /* owned */
+};
+
+enum {
+ PROP_CLIENT_ID = 1,
+ PROP_REDIRECT_URI,
+ PROP_CLIENT_SECRET,
+ PROP_LOCALE,
+ PROP_TIMEOUT,
+ PROP_PROXY_RESOLVER,
+};
+
+G_DEFINE_TYPE_WITH_CODE (GDataOAuth2Authorizer, gdata_oauth2_authorizer,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GDATA_TYPE_AUTHORIZER,
+ authorizer_init))
+
+static void
+gdata_oauth2_authorizer_class_init (GDataOAuth2AuthorizerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataOAuth2AuthorizerPrivate));
+
+ gobject_class->get_property = get_property;
+ gobject_class->set_property = set_property;
+ gobject_class->dispose = dispose;
+ gobject_class->finalize = finalize;
+
+ /**
+ * GDataOAuth2Authorizer:client-id:
+ *
+ * A client ID for your application (see the
+ * <ulink url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request"
type="http">reference documentation</ulink>).
+ *
+ * It is recommended that the ID is of the form
+ * <literal><replaceable>company name</replaceable>-
+ * <replaceable>application name</replaceable>-
+ * <replaceable>version ID</replaceable></literal>.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_CLIENT_ID,
+ g_param_spec_string ("client-id",
+ "Client ID",
+ "A client ID for your application.",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataOAuth2Authorizer:redirect-uri:
+ *
+ * TODO.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_REDIRECT_URI,
+ g_param_spec_string ("redirect-uri",
+ "Redirect URI",
+ "TODO",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataOAuth2Authorizer:client-secret:
+ *
+ * TODO
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_CLIENT_SECRET,
+ g_param_spec_string ("client-secret",
+ "Client secret",
+ "TODO",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataOAuth2Authorizer:locale:
+ *
+ * The locale to use for network requests, in UNIX locale format.
+ * (e.g. "en_GB", "cs", "de_DE".) Use %NULL for the default "C" locale
+ * (typically "en_US").
+ *
+ * This locale will be used by the server-side software to localise the
+ * authentication and authorization pages at the URI returned by
+ * gdata_oauth2_authorizer_build_authentication_uri().
+ *
+ * The server-side behaviour is undefined if it doesn't support a given
+ * locale.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_LOCALE,
+ g_param_spec_string ("locale",
+ "Locale",
+ "The locale to use for network requests, in
UNIX locale format.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataOAuth2Authorizer:timeout:
+ *
+ * A timeout, in seconds, for network operations. If the timeout is
+ * exceeded, the operation will be cancelled and
+ * %GDATA_SERVICE_ERROR_NETWORK_ERROR will be returned.
+ *
+ * If the timeout is <code class="literal">0</code>, operations will
+ * never time out.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_uint ("timeout",
+ "Timeout",
+ "A timeout, in seconds, for network operations.",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataOAuth2Authorizer:proxy-resolver:
+ *
+ * The #GProxyResolver used to determine a proxy URI.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
+ g_param_spec_object ("proxy-resolver",
+ "Proxy Resolver",
+ "A GProxyResolver used to determine a proxy
URI.",
+ G_TYPE_PROXY_RESOLVER,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+authorizer_init (GDataAuthorizerInterface *iface)
+{
+ iface->process_request = process_request;
+ iface->is_authorized_for_domain = is_authorized_for_domain;
+
+ /* We only implement the synchronous version, as GDataAuthorizer will
+ * automatically wrap it in a thread for the asynchronous versions if
+ * they’re not specifically implemented, which is fine for our needs. We
+ * couldn’t do any better by implementing the asynchronous versions
+ * ourselves. */
+ iface->refresh_authorization = refresh_authorization;
+}
+
+static void
+gdata_oauth2_authorizer_init (GDataOAuth2Authorizer *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GDATA_TYPE_OAUTH2_AUTHORIZER,
+ GDataOAuth2AuthorizerPrivate);
+
+ /* Set up the authorizer's mutex */
+ g_mutex_init (&self->priv->mutex);
+ self->priv->authentication_domains = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ g_object_unref,
+ NULL);
+
+ /* Set up the session */
+ self->priv->session = _gdata_service_build_session ();
+
+ /* Proxy the SoupSession’s timeout property. */
+ g_signal_connect (self->priv->session, "notify::timeout",
+ (GCallback) notify_timeout_cb, self);
+
+ /* Keep our GProxyResolver synchronized with SoupSession’s. */
+ g_object_bind_property (self->priv->session, "proxy-resolver",
+ self, "proxy-resolver",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+}
+
+static void
+dispose (GObject *object)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+
+ priv = GDATA_OAUTH2_AUTHORIZER (object)->priv;
+
+ g_clear_object (&priv->session);
+ g_clear_object (&priv->proxy_resolver);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_oauth2_authorizer_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+
+ priv = GDATA_OAUTH2_AUTHORIZER (object)->priv;
+
+ g_free (priv->client_id);
+ g_free (priv->redirect_uri);
+ g_free (priv->client_secret);
+ g_free (priv->locale);
+
+ g_hash_table_unref (priv->authentication_domains);
+ g_mutex_clear (&priv->mutex);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_oauth2_authorizer_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint property_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GDataOAuth2Authorizer *self;
+ GDataOAuth2AuthorizerPrivate *priv;
+
+ self = GDATA_OAUTH2_AUTHORIZER (object);
+ priv = self->priv;
+
+ switch (property_id) {
+ case PROP_CLIENT_ID:
+ g_value_set_string (value, priv->client_id);
+ break;
+ case PROP_REDIRECT_URI:
+ g_value_set_string (value, priv->redirect_uri);
+ break;
+ case PROP_CLIENT_SECRET:
+ g_value_set_string (value, priv->client_secret);
+ break;
+ case PROP_LOCALE:
+ g_value_set_string (value, priv->locale);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value,
+ gdata_oauth2_authorizer_get_timeout (self));
+ break;
+ case PROP_PROXY_RESOLVER:
+ g_value_set_object (value,
+ gdata_oauth2_authorizer_get_proxy_resolver (self));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataOAuth2Authorizer *self;
+ GDataOAuth2AuthorizerPrivate *priv;
+
+ self = GDATA_OAUTH2_AUTHORIZER (object);
+ priv = self->priv;
+
+ switch (property_id) {
+ /* Construct only. */
+ case PROP_CLIENT_ID:
+ priv->client_id = g_value_dup_string (value);
+ break;
+ /* Construct only. */
+ case PROP_REDIRECT_URI:
+ priv->redirect_uri = g_value_dup_string (value);
+ break;
+ /* Construct only. */
+ case PROP_CLIENT_SECRET:
+ priv->client_secret = g_value_dup_string (value);
+ break;
+ case PROP_LOCALE:
+ gdata_oauth2_authorizer_set_locale (self,
+ g_value_get_string (value));
+ break;
+ case PROP_TIMEOUT:
+ gdata_oauth2_authorizer_set_timeout (self,
+ g_value_get_uint (value));
+ break;
+ case PROP_PROXY_RESOLVER:
+ gdata_oauth2_authorizer_set_proxy_resolver (self,
+ g_value_get_object (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain,
+ SoupMessage *message)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+
+ priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
+
+ /* Set the authorisation header */
+ g_mutex_lock (&priv->mutex);
+
+ /* Sanity check */
+ g_assert ((priv->access_token == NULL) ==
+ (priv->refresh_token == NULL));
+
+ if (priv->access_token != NULL &&
+ g_hash_table_lookup (priv->authentication_domains,
+ domain) != NULL) {
+ sign_message_locked (GDATA_OAUTH2_AUTHORIZER (self), message,
+ priv->access_token);
+ }
+
+ g_mutex_unlock (&priv->mutex);
+}
+
+static gboolean
+is_authorized_for_domain (GDataAuthorizer *self,
+ GDataAuthorizationDomain *domain)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+ gpointer result;
+ const gchar *access_token;
+
+ priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
+
+ g_mutex_lock (&priv->mutex);
+ access_token = priv->access_token;
+ result = g_hash_table_lookup (priv->authentication_domains, domain);
+ g_mutex_unlock (&priv->mutex);
+
+ /* Sanity check */
+ g_assert (result == NULL || result == domain);
+
+ return (access_token != NULL && result != NULL);
+}
+
+/* Sign the message and add the Authorization header to it containing the
+ * signature.
+ *
+ * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#callinganapi
+ *
+ * NOTE: This must be called with the mutex locked. */
+static void
+sign_message_locked (GDataOAuth2Authorizer *self, SoupMessage *message,
+ const gchar *access_token)
+{
+ SoupURI *message_uri; /* unowned */
+ gchar *auth_header = NULL; /* owned */
+
+ g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
+ g_return_if_fail (SOUP_IS_MESSAGE (message));
+ g_return_if_fail (access_token != NULL && *access_token != '\0');
+
+ /* Ensure that we’re using HTTPS: if not, we shouldn’t set the
+ * Authorization header or we could be revealing the access
+ * token to anyone snooping the connection, which would give
+ * them the same rights as us on the user’s data. Generally a
+ * bad thing to happen. */
+ message_uri = soup_message_get_uri (message);
+
+ if (message_uri->scheme != SOUP_URI_SCHEME_HTTPS) {
+ g_warning ("Not authorizing a non-HTTPS message with the "
+ "user’s OAuth 2.0 access token as the connection "
+ "isn’t secure.");
+ return;
+ }
+
+ /* Add the authorisation header. */
+ auth_header = g_strdup_printf ("Bearer %s", access_token);
+ soup_message_headers_append (message->request_headers,
+ "Authorization", auth_header);
+ g_free (auth_header);
+}
+
+/* TODO: Link to the OAuth docs more */
+
+static gboolean
+refresh_authorization (GDataAuthorizer *self, GCancellable *cancellable,
+ GError **error)
+{
+ /* See http://code.google.com/apis/accounts/docs/OAuth2.html#IAMoreToken */
+ GDataOAuth2AuthorizerPrivate *priv;
+ SoupMessage *message;
+ gchar *request_body;
+ guint status;
+ GError *child_error = NULL;
+
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
+
+ priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
+
+ g_mutex_lock (&priv->mutex);
+
+ /* If we don’t have a refresh token, we can’t refresh the
+ * authorisation */
+ if (priv->refresh_token == NULL) {
+ g_mutex_unlock (&priv->mutex);
+ return FALSE;
+ }
+
+ /* Prepare the request */
+ request_body = soup_form_encode ("client_id", priv->client_id,
+ "client_secret", priv->client_secret,
+ "refresh_token", priv->refresh_token,
+ "grant_type", "refresh_token",
+ NULL);
+
+ g_mutex_unlock (&priv->mutex);
+
+ /* Build the message */
+ message = soup_message_new (SOUP_METHOD_POST,
+ "https://accounts.google.com/o/oauth2/token");
+ soup_message_set_request (message, "application/x-www-form-urlencoded",
+ SOUP_MEMORY_TAKE, request_body,
+ strlen (request_body));
+
+ /* Send the message */
+ _gdata_service_actually_send_message (priv->session, message,
+ cancellable, error);
+ status = message->status_code;
+
+ if (status == SOUP_STATUS_CANCELLED) {
+ /* Cancelled (the error has already been set) */
+ g_object_unref (message);
+ return FALSE;
+ } else if (status != SOUP_STATUS_OK) {
+ /* TODO */
+ g_object_unref (message);
+
+ return FALSE;
+ }
+
+ g_assert (message->response_body->data != NULL);
+
+ /* Parse and handle the response */
+ parse_grant_response (GDATA_OAUTH2_AUTHORIZER (self),
+ status, message->reason_phrase,
+ message->response_body->data,
+ message->response_body->length, &child_error);
+
+ g_object_unref (message);
+
+ if (child_error != NULL) {
+ g_propagate_error (error, child_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdata_oauth2_authorizer_new:
+ * @client_id: your application’s client ID
+ * @redirect_uri: TODO
+ * @client_secret: TODO
+ * @service_type: TODO
+ *
+ * Creates a new #GDataOAuth2Authorizer. The @client_id must be unique for your
+ * application, and as registered with Google.
+ *
+ * Return value: (transfer full): a new #GDataOAuth2Authorizer; unref with
+ * g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataOAuth2Authorizer *
+gdata_oauth2_authorizer_new (const gchar *client_id, const gchar *redirect_uri,
+ const gchar *client_secret, GType service_type)
+{
+ g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
+ g_return_val_if_fail (redirect_uri != NULL && *redirect_uri != '\0',
+ NULL);
+ g_return_val_if_fail (client_secret != NULL && *client_secret != '\0',
+ NULL);
+ g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE),
+ NULL);
+
+ return gdata_oauth2_authorizer_new_for_authorization_domains (client_id,
+ redirect_uri,
+ client_secret,
+ gdata_service_get_authorization_domains
(service_type));
+}
+
+/**
+ * gdata_oauth2_authorizer_new_for_authorization_domains:
+ * @client_id: your application’s client ID
+ * @redirect_uri: TODO
+ * @client_secret: TODO
+ * @authorization_domains: (element-type GDataAuthorizationDomain): TODO
+ *
+ * Creates a new #GDataOAuth2Authorizer. The @client_id must be unique for your
+ * application, and as registered with Google.
+ *
+ * Return value: (transfer full): a new #GDataOAuth2Authorizer; unref with
+ * g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataOAuth2Authorizer *
+gdata_oauth2_authorizer_new_for_authorization_domains (const gchar *client_id,
+ const gchar *redirect_uri,
+ const gchar *client_secret,
+ GList *authorization_domains)
+{
+ GList *i;
+ GDataOAuth2Authorizer *authorizer;
+
+ g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
+ g_return_val_if_fail (redirect_uri != NULL && *redirect_uri != '\0',
+ NULL);
+ g_return_val_if_fail (client_secret != NULL && *client_secret != '\0',
+ NULL);
+ g_return_val_if_fail (authorization_domains != NULL, NULL);
+
+ authorizer = GDATA_OAUTH2_AUTHORIZER (g_object_new (GDATA_TYPE_OAUTH2_AUTHORIZER,
+ "client-id", client_id,
+ "redirect-uri", redirect_uri,
+ "client-secret", client_secret,
+ NULL));
+
+ /* Register all the domains with the authorizer */
+ for (i = authorization_domains; i != NULL; i = i->next) {
+ GDataAuthorizationDomain *domain; /* unowned */
+
+ g_return_val_if_fail (GDATA_IS_AUTHORIZATION_DOMAIN (i->data),
+ NULL);
+
+ /* We don’t have to lock the authoriser’s mutex here as no other
+ * code has seen the authoriser yet */
+ domain = GDATA_AUTHORIZATION_DOMAIN (i->data);
+ g_hash_table_insert (authorizer->priv->authentication_domains,
+ g_object_ref (domain), domain);
+ }
+
+ return authorizer;
+}
+
+/**
+ * gdata_oauth2_authorizer_build_authentication_uri:
+ * @self: a #GDataOAuth2Authorizer
+ * @login_hint: (nullable): TODO
+ * @include_granted_scopes: TODO; this won't actually work because scopes are
+ * defined at construction time
+ *
+ * TODO
+ *
+ * Return value: the authentication URI to open in a web browser
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_oauth2_authorizer_build_authentication_uri (GDataOAuth2Authorizer *self,
+ const gchar *login_hint,
+ gboolean include_granted_scopes)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+ GString *uri = NULL; /* owned */
+ GDataAuthorizationDomain *domain; /* unowned */
+ GHashTableIter iter;
+ gboolean is_first = TRUE;
+
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+
+ priv = self->priv;
+
+ g_mutex_lock (&priv->mutex);
+
+ /* Build and memoise the URI.
+ *
+ * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#formingtheurl
+ */
+ g_assert (g_hash_table_size (priv->authentication_domains) > 0);
+
+ uri = g_string_new ("https://accounts.google.com/o/oauth2/auth"
+ "?response_type=code"
+ "&client_id=");
+ g_string_append_uri_escaped (uri, priv->client_id, NULL, TRUE);
+ g_string_append (uri, "&redirect_uri=");
+ g_string_append_uri_escaped (uri, priv->redirect_uri, NULL, TRUE);
+ g_string_append (uri, "&scope=");
+
+ /* Add the scopes of all our domains */
+ g_hash_table_iter_init (&iter, priv->authentication_domains);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &domain, NULL)) {
+ const gchar *scope;
+
+ if (!is_first) {
+ /* Delimiter */
+ g_string_append (uri, "%20");
+ }
+
+ scope = gdata_authorization_domain_get_scope (domain);
+ g_string_append_uri_escaped (uri, scope, NULL, TRUE);
+
+ is_first = FALSE;
+ }
+
+ if (login_hint != NULL && *login_hint != '\0') {
+ g_string_append (uri, "&login_hint=");
+ g_string_append_uri_escaped (uri, login_hint, NULL, TRUE);
+ }
+
+ if (priv->locale != NULL) {
+ g_string_append (uri, "&hl=");
+ g_string_append_uri_escaped (uri, priv->locale, NULL, TRUE);
+ }
+
+ if (include_granted_scopes) {
+ g_string_append (uri, "&include_granted_scopes=true");
+ } else {
+ g_string_append (uri, "&include_granted_scopes=false");
+ }
+
+ g_mutex_unlock (&priv->mutex);
+
+ return g_string_free (uri, FALSE);
+}
+
+/* NOTE: This has to be thread safe, as it can be called from
+ * refresh_authorization() at any time.
+ *
+ * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
+ */
+static void
+parse_grant_response (GDataOAuth2Authorizer *self, guint status,
+ const gchar *reason_phrase, const gchar *response_body,
+ gssize length, GError **error)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+ JsonParser *parser = NULL; /* owned */
+ JsonNode *root_node; /* unowned */
+ JsonObject *root_object; /* unowned */
+ const gchar *access_token = NULL, *refresh_token = NULL;
+ GError *child_error = NULL;
+
+ priv = self->priv;
+
+ /* Parse the successful response */
+ parser = json_parser_new ();
+
+ json_parser_load_from_data (parser, response_body, length,
+ &child_error);
+
+ if (child_error != NULL) {
+ /* TODO: Convert JSON error to libgdata error */
+ goto done;
+ }
+
+ /* Extract the access token, TTL and refresh token */
+ root_node = json_parser_get_root (parser);
+
+ if (JSON_NODE_HOLDS_OBJECT (root_node) == FALSE) {
+ /* TODO: Error */
+ goto done;
+ }
+
+ root_object = json_node_get_object (root_node);
+
+ access_token = json_object_get_string_member (root_object,
+ "access_token");
+ refresh_token = json_object_get_string_member (root_object,
+ "refresh_token");
+
+ /* Always require an access token. */
+ if (access_token == NULL || *access_token == '\0') {
+ /* TODO: error */
+ access_token = NULL;
+ refresh_token = NULL;
+
+ goto done;
+ }
+
+ /* Only require a refresh token if this is the first authentication.
+ * See the documentation for refreshing authentication:
+ * https://developers.google.com/accounts/docs/OAuth2InstalledApp#refresh
+ */
+ if ((refresh_token == NULL || *refresh_token == '\0') &&
+ priv->refresh_token == NULL) {
+ /* TODO: error */
+ access_token = NULL;
+ refresh_token = NULL;
+
+ goto done;
+ }
+
+done:
+ /* Postconditions. */
+ g_assert ((access_token == NULL) == (refresh_token == NULL));
+ g_assert ((child_error != NULL) == (access_token == NULL));
+
+ /* Update state. */
+ g_mutex_lock (&priv->mutex);
+
+ g_free (priv->access_token);
+ priv->access_token = g_strdup (access_token);
+
+ g_free (priv->refresh_token);
+ priv->refresh_token = g_strdup (refresh_token);
+
+ g_mutex_unlock (&priv->mutex);
+
+ if (child_error != NULL) {
+ g_propagate_error (error, child_error);
+ }
+
+ g_object_unref (parser);
+}
+
+/**
+ * gdata_oauth2_authorizer_request_authorization:
+ * @self: a #GDataOAuth2Authorizer
+ * @authorization_code: TODO
+ * @cancellable: (allow-none): TODO
+ * @error: TODO
+ *
+ * TODO
+ *
+ * Return value: TODO
+ *
+ * Since: UNRELEASED
+ */
+gboolean
+gdata_oauth2_authorizer_request_authorization (GDataOAuth2Authorizer *self,
+ const gchar *authorization_code,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDataOAuth2AuthorizerPrivate *priv;
+ SoupMessage *message = NULL; /* owned */
+ gchar *request_body = NULL; /* owned */
+ guint status;
+ GError *child_error = NULL;
+
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
+ g_return_val_if_fail (authorization_code != NULL &&
+ *authorization_code != '\0', FALSE);
+ g_return_val_if_fail (cancellable == NULL ||
+ G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ priv = self->priv;
+
+ /* Prepare the request.
+ *
+ * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
+ */
+ request_body = soup_form_encode ("client_id", priv->client_id,
+ "client_secret", priv->client_secret,
+ "code", authorization_code,
+ "redirect_uri", priv->redirect_uri,
+ "grant_type", "authorization_code",
+ NULL);
+
+ /* Build the message */
+ message = soup_message_new (SOUP_METHOD_POST,
+ "https://accounts.google.com/o/oauth2/token");
+ soup_message_set_request (message, "application/x-www-form-urlencoded",
+ SOUP_MEMORY_TAKE, request_body,
+ strlen (request_body));
+ request_body = NULL;
+
+ /* Send the message */
+ _gdata_service_actually_send_message (priv->session, message,
+ cancellable, error);
+ status = message->status_code;
+
+ if (status == SOUP_STATUS_CANCELLED) {
+ /* Cancelled (the error has already been set) */
+ g_object_unref (message);
+ return FALSE;
+ } else if (status != SOUP_STATUS_OK) {
+ /* TODO */
+ g_object_unref (message);
+
+ return FALSE;
+ }
+
+ g_assert (message->response_body->data != NULL);
+
+ /* Parse and handle the response */
+ parse_grant_response (self, status, message->reason_phrase,
+ message->response_body->data,
+ message->response_body->length, &child_error);
+
+ g_object_unref (message);
+
+ if (child_error != NULL) {
+ g_propagate_error (error, child_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+request_authorization_thread (GSimpleAsyncResult *result,
+ GDataOAuth2Authorizer *authorizer,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ const gchar *authorization_code;
+
+ authorization_code = g_simple_async_result_get_op_res_gpointer (result);
+
+ if (gdata_oauth2_authorizer_request_authorization (authorizer,
+ authorization_code,
+ cancellable,
+ &error) == FALSE) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+}
+
+/**
+ * gdata_oauth2_authorizer_request_authorization_async:
+ * @self: a #GDataOAuth2Authorizer
+ * @authorization_code: TODO
+ * @cancellable: (allow-none): an optional #GCancellable, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when authorization is finished
+ * @user_data: (closure): data to pass to the @callback function
+ *
+ * TODO
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_oauth2_authorizer_request_authorization_async (GDataOAuth2Authorizer *self,
+ const gchar *authorization_code,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+
+ g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
+ g_return_if_fail (authorization_code != NULL &&
+ *authorization_code != '\0');
+ g_return_if_fail (cancellable == NULL ||
+ G_IS_CANCELLABLE (cancellable));
+
+ result = g_simple_async_result_new (G_OBJECT (self), callback,
+ user_data,
+ gdata_oauth2_authorizer_request_authorization_async);
+ g_simple_async_result_set_op_res_gpointer (result,
+ g_strdup (authorization_code),
+ (GDestroyNotify) g_free);
+ g_simple_async_result_run_in_thread (result,
+ (GSimpleAsyncThreadFunc) request_authorization_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+ g_object_unref (result);
+}
+
+/**
+ * gdata_oauth2_authorizer_request_authorization_finish:
+ * @self: a #GDataOAuth2Authorizer
+ * @async_result: a #GAsyncResult
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous authorization operation started with
+ * gdata_oauth2_authorizer_request_authorization_async().
+ *
+ * Return value: %TRUE if authorization was successful, %FALSE otherwise
+ *
+ * Since: UNRELEASED
+ */
+gboolean
+gdata_oauth2_authorizer_request_authorization_finish (GDataOAuth2Authorizer *self,
+ GAsyncResult *async_result,
+ GError **error)
+{
+ GSimpleAsyncResult *result;
+
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ result = G_SIMPLE_ASYNC_RESULT (async_result);
+
+ g_warn_if_fail (g_simple_async_result_is_valid (async_result,
+ G_OBJECT (self),
+ gdata_oauth2_authorizer_request_authorization_async));
+
+ if (g_simple_async_result_propagate_error (result, error)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdata_oauth2_authorizer_get_client_id:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Returns the authorizer's client ID, #GDataOAuth2Authorizer:client-id, as
+ * specified on constructing the #GDataOAuth2Authorizer.
+ *
+ * Return value: the authorizer's client ID
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_oauth2_authorizer_get_client_id (GDataOAuth2Authorizer *self)
+{
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+ return self->priv->client_id;
+}
+
+/**
+ * gdata_oauth2_authorizer_get_redirect_uri:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Returns the authorizer’s redirect URI, #GDataOAuth2Authorizer:redirect-uri,
+ * as specified on constructing the #GDataOAuth2Authorizer.
+ *
+ * Return value: the authorizer’s redirect URI
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_oauth2_authorizer_get_redirect_uri (GDataOAuth2Authorizer *self)
+{
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+ return self->priv->redirect_uri;
+}
+
+/**
+ * gdata_oauth2_authorizer_get_client_secret:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Returns the authorizer's client secret, #GDataOAuth2Authorizer:client-secret,
+ * as specified on constructing the #GDataOAuth2Authorizer.
+ *
+ * Return value: the authorizer's client secret
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_oauth2_authorizer_get_client_secret (GDataOAuth2Authorizer *self)
+{
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+ return self->priv->client_secret;
+}
+
+/**
+ * gdata_oauth2_authorizer_get_locale:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Returns the locale currently being used for network requests, or %NULL if the
+ * locale is the default.
+ *
+ * Return value: (allow-none): the current locale
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_oauth2_authorizer_get_locale (GDataOAuth2Authorizer *self)
+{
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+ return self->priv->locale;
+}
+
+/**
+ * gdata_oauth2_authorizer_set_locale:
+ * @self: a #GDataOAuth2Authorizer
+ * @locale: (allow-none): the new locale in UNIX locale format, or %NULL for the
+ * default locale
+ *
+ * Set the locale used for network requests to @locale, given in standard UNIX
+ * locale format. See #GDataOAuth2Authorizer:locale for more details.
+ *
+ * Note that while it’s possible to change the locale after sending network
+ * requests (i.e. calling gdata_oauth2_authorizer_build_authentication_uri() for
+ * the first time), it is unsupported, as the server-side software may behave
+ * unexpectedly. The only supported use of this method is after creation of the
+ * authorizer, but before any network requests are made.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_oauth2_authorizer_set_locale (GDataOAuth2Authorizer *self,
+ const gchar *locale)
+{
+ g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
+
+ if (g_strcmp0 (locale, self->priv->locale) == 0) {
+ /* Already has this value */
+ return;
+ }
+
+ g_free (self->priv->locale);
+ self->priv->locale = g_strdup (locale);
+ g_object_notify (G_OBJECT (self), "locale");
+}
+
+static void
+notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self)
+{
+ g_object_notify (self, "timeout");
+}
+
+/**
+ * gdata_oauth2_authorizer_get_timeout:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Gets the #GDataOAuth2Authorizer:timeout property; the network timeout, in
+ * seconds.
+ *
+ * Return value: the timeout, or <code class="literal">0</code>
+ *
+ * Since: UNRELEASED
+ */
+guint
+gdata_oauth2_authorizer_get_timeout (GDataOAuth2Authorizer *self)
+{
+ guint timeout;
+
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), 0);
+
+ g_object_get (self->priv->session,
+ SOUP_SESSION_TIMEOUT, &timeout,
+ NULL);
+
+ return timeout;
+}
+
+/**
+ * gdata_oauth2_authorizer_set_timeout:
+ * @self: a #GDataOAuth2Authorizer
+ * @timeout: the timeout, or <code class="literal">0</code>
+ *
+ * Sets the #GDataOAuth2Authorizer:timeout property; the network timeout, in
+ * seconds.
+ *
+ * If @timeout is <code class="literal">0</code>, network operations will never
+ * time out.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_oauth2_authorizer_set_timeout (GDataOAuth2Authorizer *self, guint timeout)
+{
+ g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
+ g_object_set (self->priv->session, SOUP_SESSION_TIMEOUT, timeout, NULL);
+ g_object_notify (G_OBJECT (self), "timeout");
+}
+
+/**
+ * gdata_oauth2_authorizer_get_proxy_resolver:
+ * @self: a #GDataOAuth2Authorizer
+ *
+ * Gets the #GProxyResolver on the #GDataOAuth2Authorizer's #SoupSession.
+ *
+ * Return value: (transfer none) (allow-none): a #GProxyResolver, or %NULL
+ *
+ * Since: UNRELEASED
+ */
+GProxyResolver *
+gdata_oauth2_authorizer_get_proxy_resolver (GDataOAuth2Authorizer *self)
+{
+ g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
+
+ return self->priv->proxy_resolver;
+}
+
+/**
+ * gdata_oauth2_authorizer_set_proxy_resolver:
+ * @self: a #GDataOAuth2Authorizer
+ * @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL
+ *
+ * Sets the #GProxyResolver on the #SoupSession used internally by the given
+ * #GDataOAuth2Authorizer.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_oauth2_authorizer_set_proxy_resolver (GDataOAuth2Authorizer *self,
+ GProxyResolver *proxy_resolver)
+{
+ g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
+ g_return_if_fail (proxy_resolver == NULL ||
+ G_IS_PROXY_RESOLVER (proxy_resolver));
+
+ if (proxy_resolver != NULL) {
+ g_object_ref (proxy_resolver);
+ }
+
+ g_clear_object (&self->priv->proxy_resolver);
+ self->priv->proxy_resolver = proxy_resolver;
+
+ g_object_notify (G_OBJECT (self), "proxy-resolver");
+}
diff --git a/gdata/gdata-oauth2-authorizer.h b/gdata/gdata-oauth2-authorizer.h
new file mode 100644
index 0000000..bd96f4a
--- /dev/null
+++ b/gdata/gdata-oauth2-authorizer.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2011, 2014 <philip tecnocode co uk>
+ *
+ * GData Client is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_OAUTH2_AUTHORIZER_H
+#define GDATA_OAUTH2_AUTHORIZER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdata-authorizer.h"
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_OAUTH2_AUTHORIZER (gdata_oauth2_authorizer_get_type ())
+#define GDATA_OAUTH2_AUTHORIZER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o),
GDATA_TYPE_OAUTH2_AUTHORIZER, GDataOAuth2Authorizer))
+#define GDATA_OAUTH2_AUTHORIZER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_OAUTH2_AUTHORIZER,
GDataOAuth2AuthorizerClass))
+#define GDATA_IS_OAUTH2_AUTHORIZER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o),
GDATA_TYPE_OAUTH2_AUTHORIZER))
+#define GDATA_IS_OAUTH2_AUTHORIZER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_OAUTH2_AUTHORIZER))
+#define GDATA_OAUTH2_AUTHORIZER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),
GDATA_TYPE_OAUTH2_AUTHORIZER, GDataOAuth2AuthorizerClass))
+
+typedef struct _GDataOAuth2AuthorizerPrivate GDataOAuth2AuthorizerPrivate;
+
+/**
+ * GDataOAuth2Authorizer:
+ *
+ * All the fields in the #GDataOAuth2Authorizer structure are private and should never be accessed directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ /*< private >*/
+ GObject parent;
+ GDataOAuth2AuthorizerPrivate *priv;
+} GDataOAuth2Authorizer;
+
+/**
+ * GDataOAuth2AuthorizerClass:
+ *
+ * All the fields in the #GDataOAuth2AuthorizerClass structure are private and should never be accessed
directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ /*< private >*/
+ GObjectClass parent;
+} GDataOAuth2AuthorizerClass;
+
+GType gdata_oauth2_authorizer_get_type (void) G_GNUC_CONST;
+
+GDataOAuth2Authorizer *gdata_oauth2_authorizer_new (const gchar *client_id,
+ const gchar *redirect_uri,
+ const gchar *client_secret,
+ GType service_type) G_GNUC_WARN_UNUSED_RESULT
G_GNUC_MALLOC;
+GDataOAuth2Authorizer *gdata_oauth2_authorizer_new_for_authorization_domains (const gchar *client_id,
+ const gchar *redirect_uri,
+ const gchar *client_secret,
+ GList *authorization_domains)
G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+
+const gchar *gdata_oauth2_authorizer_build_authentication_uri (GDataOAuth2Authorizer *self,
+ const gchar *login_hint,
+ gboolean include_granted_scopes) G_GNUC_PURE;
+
+gboolean gdata_oauth2_authorizer_request_authorization (GDataOAuth2Authorizer *self, const gchar
*authorization_code,
+ GCancellable *cancellable, GError **error);
+void gdata_oauth2_authorizer_request_authorization_async (GDataOAuth2Authorizer *self, const gchar
*authorization_code,
+ GCancellable *cancellable, GAsyncReadyCallback
callback, gpointer user_data);
+gboolean gdata_oauth2_authorizer_request_authorization_finish (GDataOAuth2Authorizer *self, GAsyncResult
*async_result, GError **error);
+
+const gchar *gdata_oauth2_authorizer_get_client_id (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+const gchar *gdata_oauth2_authorizer_get_redirect_uri (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+const gchar *gdata_oauth2_authorizer_get_client_secret (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+
+const gchar *gdata_oauth2_authorizer_get_locale (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+void gdata_oauth2_authorizer_set_locale (GDataOAuth2Authorizer *self, const gchar *locale);
+
+guint gdata_oauth2_authorizer_get_timeout (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+void gdata_oauth2_authorizer_set_timeout (GDataOAuth2Authorizer *self, guint timeout);
+
+GProxyResolver *gdata_oauth2_authorizer_get_proxy_resolver (GDataOAuth2Authorizer *self) G_GNUC_PURE;
+void gdata_oauth2_authorizer_set_proxy_resolver (GDataOAuth2Authorizer *self, GProxyResolver
*proxy_resolver);
+
+G_END_DECLS
+
+#endif /* !GDATA_OAUTH2_AUTHORIZER_H */
diff --git a/gdata/gdata.h b/gdata/gdata.h
index 158a765..fe7a4c3 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -39,6 +39,7 @@
#include <gdata/gdata-authorization-domain.h>
#include <gdata/gdata-client-login-authorizer.h>
#include <gdata/gdata-oauth1-authorizer.h>
+#include <gdata/gdata-oauth2-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>
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 5b83a42..020d136 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -1093,3 +1093,19 @@ gdata_freebase_result_error_quark
gdata_freebase_result_get_type
gdata_freebase_result_new
gdata_freebase_result_dup_variant
+gdata_oauth2_authorizer_get_type
+gdata_oauth2_authorizer_new
+gdata_oauth2_authorizer_new_for_authorization_domains
+gdata_oauth2_authorizer_build_authentication_uri
+gdata_oauth2_authorizer_request_authorization
+gdata_oauth2_authorizer_request_authorization_async
+gdata_oauth2_authorizer_request_authorization_finish
+gdata_oauth2_authorizer_get_client_id
+gdata_oauth2_authorizer_get_redirect_uri
+gdata_oauth2_authorizer_get_client_secret
+gdata_oauth2_authorizer_get_locale
+gdata_oauth2_authorizer_set_locale
+gdata_oauth2_authorizer_get_timeout
+gdata_oauth2_authorizer_set_timeout
+gdata_oauth2_authorizer_get_proxy_resolver
+gdata_oauth2_authorizer_set_proxy_resolver
diff --git a/gdata/tests/Makefile.am b/gdata/tests/Makefile.am
index 77ac9fe..b0b45ba 100644
--- a/gdata/tests/Makefile.am
+++ b/gdata/tests/Makefile.am
@@ -67,6 +67,9 @@ client_login_authorizer_SOURCES = client-login-authorizer.c $(TEST_SRCS)
TESTS += oauth1-authorizer
oauth1_authorizer_SOURCES = oauth1-authorizer.c $(TEST_SRCS)
+TESTS += oauth2-authorizer
+oauth2_authorizer_SOURCES = oauth2-authorizer.c $(TEST_SRCS)
+
EXTRA_DIST += \
photo.jpg \
sample.ogg \
diff --git a/gdata/tests/oauth2-authorizer.c b/gdata/tests/oauth2-authorizer.c
new file mode 100644
index 0000000..370a009
--- /dev/null
+++ b/gdata/tests/oauth2-authorizer.c
@@ -0,0 +1,1048 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2011, 2014 <philip tecnocode co uk>
+ *
+ * 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/>.
+ */
+
+#include <glib.h>
+#include <gdata/gdata.h>
+
+#include "common.h"
+
+static GThread *main_thread = NULL;
+
+static void
+test_oauth2_authorizer_constructor (void)
+{
+ GDataOAuth2Authorizer *authorizer;
+
+ authorizer = gdata_oauth2_authorizer_new ("Application name", GDATA_TYPE_CONTACTS_SERVICE);
+
+ g_assert (authorizer != NULL);
+ g_assert (GDATA_IS_OAUTH2_AUTHORIZER (authorizer));
+ g_assert (GDATA_IS_AUTHORIZER (authorizer));
+
+ g_object_unref (authorizer);
+}
+
+static void
+test_oauth2_authorizer_constructor_for_domains (void)
+{
+ GDataOAuth2Authorizer *authorizer;
+ GDataAuthorizationDomain *domain;
+ GList *domains;
+
+ /* Try with standard domains first */
+ domains = gdata_service_get_authorization_domains (GDATA_TYPE_CONTACTS_SERVICE);
+ authorizer = gdata_oauth2_authorizer_new_for_authorization_domains ("Application name", domains);
+ g_list_free (domains);
+
+ g_assert (authorizer != NULL);
+ g_assert (GDATA_IS_OAUTH2_AUTHORIZER (authorizer));
+ g_assert (GDATA_IS_AUTHORIZER (authorizer));
+
+ g_object_unref (authorizer);
+
+ /* Try again with a custom domain. Note that, as in test_authorization_domain_properties() this
should not normally happen in client code. */
+ domain = GDATA_AUTHORIZATION_DOMAIN (g_object_new (GDATA_TYPE_AUTHORIZATION_DOMAIN,
+ "service-name", "test",
+ "scope", "test",
+ NULL));
+
+ domains = g_list_prepend (NULL, domain);
+ authorizer = gdata_oauth2_authorizer_new_for_authorization_domains ("Application name", domains);
+ g_list_free (domains);
+
+ g_assert (authorizer != NULL);
+ g_assert (GDATA_IS_OAUTH2_AUTHORIZER (authorizer));
+ g_assert (GDATA_IS_AUTHORIZER (authorizer));
+
+ g_object_unref (authorizer);
+ g_object_unref (domain);
+}
+
+typedef struct {
+ GDataOAuth2Authorizer *authorizer;
+
+ guint locale_notification_count;
+ gulong locale_signal_handler;
+ guint proxy_uri_notification_count;
+ gulong proxy_uri_signal_handler;
+ guint timeout_notification_count;
+ gulong timeout_signal_handler;
+} OAuth2AuthorizerData;
+
+/* Used to count that exactly the right number of notify signals are emitted when setting properties */
+static void
+notify_cb (GObject *object, GParamSpec *pspec, guint *notification_count)
+{
+ /* Check we're running in the main thread */
+ g_assert (g_thread_self () == main_thread);
+
+ /* Increment the notification count */
+ *notification_count = *notification_count + 1;
+}
+
+static void
+connect_to_oauth2_authorizer (OAuth2AuthorizerData *data)
+{
+ /* Connect to notifications from the object to verify they're only emitted the correct number of
times */
+ data->locale_signal_handler = g_signal_connect (data->authorizer, "notify::locale", (GCallback)
notify_cb,
+ &(data->locale_notification_count));
+ data->proxy_uri_signal_handler = g_signal_connect (data->authorizer, "notify::proxy-uri", (GCallback)
notify_cb,
+ &(data->proxy_uri_notification_count));
+ data->timeout_signal_handler = g_signal_connect (data->authorizer, "notify::timeout", (GCallback)
notify_cb,
+ &(data->timeout_notification_count));
+}
+
+static void
+set_up_oauth2_authorizer_data (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ data->authorizer = gdata_oauth2_authorizer_new ("Application name", GDATA_TYPE_CONTACTS_SERVICE);
+ connect_to_oauth2_authorizer (data);
+}
+
+static void
+set_up_oauth2_authorizer_data_fallback_application_name (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ g_set_application_name ("Fallback name");
+ data->authorizer = gdata_oauth2_authorizer_new (NULL, GDATA_TYPE_CONTACTS_SERVICE);
+ connect_to_oauth2_authorizer (data);
+}
+
+static void
+set_up_oauth2_authorizer_data_multiple_domains (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ GList *authorization_domains = NULL;
+
+ authorization_domains = g_list_prepend (authorization_domains,
gdata_contacts_service_get_primary_authorization_domain ());
+ authorization_domains = g_list_prepend (authorization_domains,
gdata_picasaweb_service_get_primary_authorization_domain ());
+ data->authorizer = gdata_oauth2_authorizer_new_for_authorization_domains ("Application name",
authorization_domains);
+ g_list_free (authorization_domains);
+
+ connect_to_oauth2_authorizer (data);
+}
+
+static void
+set_up_oauth2_authorizer_data_locale (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ data->authorizer = gdata_oauth2_authorizer_new ("Application name", GDATA_TYPE_CONTACTS_SERVICE);
+ gdata_oauth2_authorizer_set_locale (data->authorizer, "en_GB");
+ connect_to_oauth2_authorizer (data);
+}
+
+/* Given an authentication URI, prompt the user to go to that URI, grant access to the test application and
enter the resulting verifier */
+static gchar *
+query_user_for_verifier (const gchar *authentication_uri)
+{
+ char verifier[100];
+
+ /* Wait for the user to retrieve and enter the verifier */
+ g_print ("Please navigate to the following URI and grant access: %s\n", authentication_uri);
+ g_print ("Enter verifier (EOF to skip test): ");
+ if (scanf ("%100s", verifier) != 1) {
+ /* Skip the test */
+ g_test_message ("Skipping test on user request.");
+ return NULL;
+ }
+
+ g_test_message ("Proceeding with user-provided verifier “%s”.", verifier);
+
+ return g_strdup (verifier);
+}
+
+static void
+set_up_oauth2_authorizer_data_authenticated (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gboolean *skip_test = (gboolean*) user_data;
+ gchar *authentication_uri, *token, *token_secret, *verifier;
+
+ /* Chain up */
+ set_up_oauth2_authorizer_data (data, NULL);
+
+ /* Get an authentication URI */
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri (data->authorizer, &token,
&token_secret, NULL, NULL);
+ g_assert (authentication_uri != NULL);
+
+ /* Get the verifier off the user */
+ verifier = query_user_for_verifier (authentication_uri);
+
+ g_free (authentication_uri);
+
+ if (verifier == NULL) {
+ *skip_test = TRUE;
+ goto skip_test;
+ }
+
+ /* Authorise the token */
+ g_assert (gdata_oauth2_authorizer_request_authorization (data->authorizer, token, token_secret,
verifier, NULL, NULL) == TRUE);
+
+skip_test:
+ g_free (token);
+ g_free (token_secret);
+ g_free (verifier);
+}
+
+static void
+tear_down_oauth2_authorizer_data (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ /* Clean up signal handlers */
+ g_signal_handler_disconnect (data->authorizer, data->timeout_signal_handler);
+ g_signal_handler_disconnect (data->authorizer, data->proxy_uri_signal_handler);
+ g_signal_handler_disconnect (data->authorizer, data->locale_signal_handler);
+
+ g_object_unref (data->authorizer);
+}
+
+/* Test getting and setting the application-name property */
+static void
+test_oauth2_authorizer_properties_application_name (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gchar *application_name;
+
+ /* Verifying the normal state of the property in a newly-constructed instance of
GDataOAuth2Authorizer */
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_application_name (data->authorizer), ==, "Application
name");
+
+ g_object_get (data->authorizer, "application-name", &application_name, NULL);
+ g_assert_cmpstr (application_name, ==, "Application name");
+ g_free (application_name);
+}
+
+/* Test the fallback for the application-name property */
+static void
+test_oauth2_authorizer_properties_application_name_fallback (OAuth2AuthorizerData *data, gconstpointer
user_data)
+{
+ gchar *application_name;
+
+ /* Verifying the normal state of the property in a newly-constructed instance of
GDataOAuth2Authorizer */
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_application_name (data->authorizer), ==, "Fallback
name");
+
+ g_object_get (data->authorizer, "application-name", &application_name, NULL);
+ g_assert_cmpstr (application_name, ==, "Fallback name");
+ g_free (application_name);
+}
+
+/* Test getting and setting the locale property */
+static void
+test_oauth2_authorizer_properties_locale (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gchar *locale;
+
+ /* Verifying the normal state of the property in a newly-constructed instance of
GDataOAuth2Authorizer */
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_locale (data->authorizer), ==, NULL);
+
+ g_object_get (data->authorizer, "locale", &locale, NULL);
+ g_assert_cmpstr (locale, ==, NULL);
+ g_free (locale);
+
+ g_assert_cmpuint (data->locale_notification_count, ==, 0);
+
+ /* Check setting it works and emits a notification */
+ gdata_oauth2_authorizer_set_locale (data->authorizer, "en");
+
+ g_assert_cmpuint (data->locale_notification_count, ==, 1);
+
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_locale (data->authorizer), ==, "en");
+
+ g_object_get (data->authorizer, "locale", &locale, NULL);
+ g_assert_cmpstr (locale, ==, "en");
+ g_free (locale);
+
+ /* Check setting it to the same value is a no-op */
+ gdata_oauth2_authorizer_set_locale (data->authorizer, "en");
+ g_assert_cmpuint (data->locale_notification_count, ==, 1);
+
+ /* Check setting it back to NULL works */
+ gdata_oauth2_authorizer_set_locale (data->authorizer, NULL);
+
+ g_assert_cmpuint (data->locale_notification_count, ==, 2);
+
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_locale (data->authorizer), ==, NULL);
+
+ g_object_get (data->authorizer, "locale", &locale, NULL);
+ g_assert_cmpstr (locale, ==, NULL);
+ g_free (locale);
+
+ /* Test that setting it using g_object_set() works */
+ g_object_set (data->authorizer, "locale", "de", NULL);
+ g_assert_cmpstr (gdata_oauth2_authorizer_get_locale (data->authorizer), ==, "de");
+}
+
+/* Test getting and setting the proxy-uri property */
+static void
+test_oauth2_authorizer_properties_proxy_uri (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ SoupURI *proxy_uri, *new_proxy_uri;
+
+ /* Verifying the normal state of the property in a newly-constructed instance of
GDataOAuth2Authorizer */
+ g_assert (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer) == NULL);
+
+ g_object_get (data->authorizer, "proxy-uri", &proxy_uri, NULL);
+ g_assert (proxy_uri == NULL);
+
+ g_assert_cmpuint (data->proxy_uri_notification_count, ==, 0);
+
+ /* Check setting it works and emits a notification */
+ new_proxy_uri = soup_uri_new ("http://example.com/");
+ gdata_oauth2_authorizer_set_proxy_uri (data->authorizer, new_proxy_uri);
+
+ g_assert_cmpuint (data->proxy_uri_notification_count, ==, 1);
+
+ g_assert (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer) != NULL);
+ g_assert (soup_uri_equal (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer), new_proxy_uri) ==
TRUE);
+
+ g_object_get (data->authorizer, "proxy-uri", &proxy_uri, NULL);
+ g_assert (proxy_uri != NULL);
+ g_assert (soup_uri_equal (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer), new_proxy_uri) ==
TRUE);
+ soup_uri_free (proxy_uri);
+
+ soup_uri_free (new_proxy_uri);
+
+ /* Check setting it back to NULL works */
+ gdata_oauth2_authorizer_set_proxy_uri (data->authorizer, NULL);
+
+ g_assert_cmpuint (data->proxy_uri_notification_count, ==, 2);
+
+ g_assert (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer) == NULL);
+
+ g_object_get (data->authorizer, "proxy-uri", &proxy_uri, NULL);
+ g_assert (proxy_uri == NULL);
+
+ /* Test that setting it using g_object_set() works */
+ new_proxy_uri = soup_uri_new ("http://example.com/");
+ g_object_set (data->authorizer, "proxy-uri", new_proxy_uri, NULL);
+ soup_uri_free (new_proxy_uri);
+
+ g_assert (gdata_oauth2_authorizer_get_proxy_uri (data->authorizer) != NULL);
+}
+
+/* Test getting and setting the timeout property */
+static void
+test_oauth2_authorizer_properties_timeout (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ guint timeout;
+
+ /* Verifying the normal state of the property in a newly-constructed instance of
GDataOAuth2Authorizer */
+ g_assert_cmpuint (gdata_oauth2_authorizer_get_timeout (data->authorizer), ==, 0);
+
+ g_object_get (data->authorizer, "timeout", &timeout, NULL);
+ g_assert_cmpuint (timeout, ==, 0);
+
+ g_assert_cmpuint (data->timeout_notification_count, ==, 0);
+
+ /* Check setting it works and emits a notification */
+ gdata_oauth2_authorizer_set_timeout (data->authorizer, 30);
+
+ g_assert_cmpuint (data->timeout_notification_count, ==, 1);
+
+ g_assert_cmpuint (gdata_oauth2_authorizer_get_timeout (data->authorizer), ==, 30);
+
+ g_object_get (data->authorizer, "timeout", &timeout, NULL);
+ g_assert_cmpuint (timeout, ==, 30);
+
+ /* Check setting it to the same value is a no-op */
+ gdata_oauth2_authorizer_set_timeout (data->authorizer, 30);
+ g_assert_cmpuint (data->timeout_notification_count, ==, 1);
+
+ /* Check setting it back to 0 works */
+ gdata_oauth2_authorizer_set_timeout (data->authorizer, 0);
+
+ g_assert_cmpuint (data->timeout_notification_count, ==, 2);
+
+ g_assert_cmpuint (gdata_oauth2_authorizer_get_timeout (data->authorizer), ==, 0);
+
+ g_object_get (data->authorizer, "timeout", &timeout, NULL);
+ g_assert_cmpuint (timeout, ==, 0);
+
+ /* Test that setting it using g_object_set() works */
+ g_object_set (data->authorizer, "timeout", 15, NULL);
+ g_assert_cmpuint (gdata_oauth2_authorizer_get_timeout (data->authorizer), ==, 15);
+}
+
+/* Test that gdata_authorizer_refresh_authorization() is a no-op (whether authorised or not) */
+static void
+test_oauth2_authorizer_refresh_authorization (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gboolean *skip_test = (gboolean*) user_data;
+ GError *error = NULL;
+
+ /* Skip the test if the user's requested */
+ if (skip_test != NULL && *skip_test == TRUE) {
+ return;
+ }
+
+ g_assert (gdata_authorizer_refresh_authorization (GDATA_AUTHORIZER (data->authorizer), NULL, &error)
== FALSE);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+}
+
+/* Test that processing a request with a NULL domain will not change the request. */
+static void
+test_oauth2_authorizer_process_request_null (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ SoupMessage *message;
+ SoupMessageHeadersIter iter;
+ guint header_count = 0;
+ const gchar *name, *value;
+
+ /* Create a new message with an empty set of request headers */
+ message = soup_message_new (SOUP_METHOD_GET, "https://example.com/");
+
+ /* Process the message */
+ gdata_authorizer_process_request (GDATA_AUTHORIZER (data->authorizer), NULL, message);
+
+ /* Check that the set of request headers is still empty */
+ soup_message_headers_iter_init (&iter, message->request_headers);
+
+ while (soup_message_headers_iter_next (&iter, &name, &value) == TRUE) {
+ header_count++;
+ }
+
+ g_assert_cmpuint (header_count, ==, 0);
+
+ g_object_unref (message);
+}
+
+/* Test that processing a request with an authorizer which hasn't been authenticated yet will not change the
request. */
+static void
+test_oauth2_authorizer_process_request_unauthenticated (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ SoupMessage *message;
+ SoupMessageHeadersIter iter;
+ guint header_count = 0;
+ const gchar *name, *value;
+
+ /* Create a new message with an empty set of request headers */
+ message = soup_message_new (SOUP_METHOD_GET, "https://example.com/");
+
+ /* Process the message */
+ gdata_authorizer_process_request (GDATA_AUTHORIZER (data->authorizer),
gdata_contacts_service_get_primary_authorization_domain (), message);
+
+ /* Check that the set of request headers is still empty */
+ soup_message_headers_iter_init (&iter, message->request_headers);
+
+ while (soup_message_headers_iter_next (&iter, &name, &value) == TRUE) {
+ header_count++;
+ }
+
+ g_assert_cmpuint (header_count, ==, 0);
+
+ g_object_unref (message);
+}
+
+/* Test that processing a request with an authorizer which has been authenticated will change the request. */
+static void
+test_oauth2_authorizer_process_request_authenticated (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gboolean *skip_test = (gboolean*) user_data;
+ SoupMessage *message;
+ SoupMessageHeadersIter iter;
+ guint header_count = 0;
+ const gchar *name, *value;
+
+ /* Skip the test if the user's requested */
+ if (skip_test != NULL && *skip_test == TRUE) {
+ return;
+ }
+
+ /* Create a new message with an empty set of request headers */
+ message = soup_message_new (SOUP_METHOD_GET, "http://example.com/");
+
+ /* Process the message */
+ gdata_authorizer_process_request (GDATA_AUTHORIZER (data->authorizer),
gdata_contacts_service_get_primary_authorization_domain (), message);
+
+ /* Check that at least one new header has been set */
+ soup_message_headers_iter_init (&iter, message->request_headers);
+
+ while (soup_message_headers_iter_next (&iter, &name, &value) == TRUE) {
+ header_count++;
+ }
+
+ g_assert_cmpuint (header_count, >, 0);
+
+ g_object_unref (message);
+}
+
+/* Test that requesting an authentication URI synchronously works correctly */
+static void
+test_oauth2_authorizer_request_authentication_uri_sync (OAuth2AuthorizerData *data, gconstpointer user_data)
+{
+ gchar *authentication_uri, *token, *token_secret;
+ GError *error = NULL;
+
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri (data->authorizer, &token,
&token_secret, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (authentication_uri != NULL && *authentication_uri != '\0');
+ g_assert (token != NULL && *token != '\0');
+ g_assert (token_secret != NULL && *token != '\0');
+ g_clear_error (&error);
+
+ g_test_message ("Requesting an authentication URI gave “%s” with request token “%s” and request token
secret “%s”.",
+ authentication_uri, token, token_secret);
+
+ g_free (authentication_uri);
+ g_free (token);
+ g_free (token_secret);
+}
+
+/* Test that requesting an authentication URI synchronously can be cancelled */
+static void
+test_oauth2_authorizer_request_authentication_uri_sync_cancellation (OAuth2AuthorizerData *data,
gconstpointer user_data)
+{
+ /* Initialise token and token_secret so we check that
gdata_oauth2_authorizer_request_authentication_uri() NULLifies them on error */
+ gchar *authentication_uri, *token = (gchar*) "error", *token_secret = (gchar*) "error";
+ GCancellable *cancellable;
+ GError *error = NULL;
+
+ /* Set up the cancellable */
+ cancellable = g_cancellable_new ();
+
+ /* Get a request token. This should return immediately as the cancellable was cancelled beforehand. */
+ g_cancellable_cancel (cancellable);
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri (data->authorizer, &token,
&token_secret, cancellable, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (authentication_uri == NULL);
+ g_assert (token == NULL);
+ g_assert (token_secret == NULL);
+ g_clear_error (&error);
+
+ g_free (authentication_uri);
+ g_free (token);
+ g_free (token_secret);
+
+ g_object_unref (cancellable);
+}
+
+typedef struct {
+ OAuth2AuthorizerData parent;
+ GMainLoop *main_loop;
+} OAuth2AuthorizerAsyncData;
+
+static void
+set_up_oauth2_authorizer_async_data (OAuth2AuthorizerAsyncData *data, gconstpointer user_data)
+{
+ /* Chain up */
+ set_up_oauth2_authorizer_data ((OAuth2AuthorizerData*) data, user_data);
+
+ /* Set up the main loop */
+ data->main_loop = g_main_loop_new (NULL, FALSE);
+}
+
+static void
+set_up_oauth2_authorizer_async_data_multiple_domains (OAuth2AuthorizerAsyncData *data, gconstpointer
user_data)
+{
+ /* Chain up */
+ set_up_oauth2_authorizer_data_multiple_domains ((OAuth2AuthorizerData*) data, user_data);
+
+ /* Set up the main loop */
+ data->main_loop = g_main_loop_new (NULL, FALSE);
+}
+
+static void
+tear_down_oauth2_authorizer_async_data (OAuth2AuthorizerAsyncData *data, gconstpointer user_data)
+{
+ g_main_loop_unref (data->main_loop);
+
+ /* Chain up */
+ tear_down_oauth2_authorizer_data ((OAuth2AuthorizerData*) data, user_data);
+}
+
+static void
+test_oauth2_authorizer_request_authentication_uri_async_cb (GDataOAuth2Authorizer *authorizer, GAsyncResult
*async_result,
+ OAuth2AuthorizerAsyncData *data)
+{
+ gchar *authentication_uri, *token, *token_secret;
+ GError *error = NULL;
+
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri_finish (authorizer,
async_result, &token, &token_secret, &error);
+ g_assert_no_error (error);
+ g_assert (authentication_uri != NULL && *authentication_uri != '\0');
+ g_assert (token != NULL && *token != '\0');
+ g_assert (token_secret != NULL && *token != '\0');
+ g_clear_error (&error);
+
+ g_test_message ("Requesting an authentication URI asynchronously gave “%s” with request token “%s”
and request token secret “%s”.",
+ authentication_uri, token, token_secret);
+
+ g_free (authentication_uri);
+ g_free (token);
+ g_free (token_secret);
+
+ g_main_loop_quit (data->main_loop);
+}
+
+/* Test that asynchronously requesting an authentication URI for a single authorization domain works */
+static void
+test_oauth2_authorizer_request_authentication_uri_async (OAuth2AuthorizerAsyncData *data, gconstpointer
user_data)
+{
+ /* Create a main loop and request an authentication URI */
+ gdata_oauth2_authorizer_request_authentication_uri_async (data->parent.authorizer, NULL,
+ (GAsyncReadyCallback)
test_oauth2_authorizer_request_authentication_uri_async_cb,
+ data);
+
+ g_main_loop_run (data->main_loop);
+}
+
+static void
+test_oauth2_authorizer_request_authentication_uri_async_cancellation_cb (GDataOAuth2Authorizer *authorizer,
GAsyncResult *async_result,
+ OAuth2AuthorizerAsyncData *data)
+{
+ /* Initialise token and token_secret so we check that
gdata_oauth2_authorizer_request_authentication_uri_finish() NULLifies them on error */
+ gchar *authentication_uri, *token = (gchar*) "error", *token_secret = (gchar*) "error";
+ GError *error = NULL;
+
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri_finish (authorizer,
async_result, &token, &token_secret, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (authentication_uri == NULL);
+ g_assert (token == NULL);
+ g_assert (token_secret == NULL);
+ g_clear_error (&error);
+
+ g_free (authentication_uri);
+ g_free (token);
+ g_free (token_secret);
+
+ g_main_loop_quit (data->main_loop);
+}
+
+/* Test that cancellation of asynchronous authentication URI requests work */
+static void
+test_oauth2_authorizer_request_authentication_uri_async_cancellation (OAuth2AuthorizerAsyncData *data,
gconstpointer user_data)
+{
+ GCancellable *cancellable;
+
+ /* Set up the cancellable */
+ cancellable = g_cancellable_new ();
+
+ /* Create a main loop and request an authentication URI */
+ gdata_oauth2_authorizer_request_authentication_uri_async (data->parent.authorizer, cancellable,
+ (GAsyncReadyCallback)
+
test_oauth2_authorizer_request_authentication_uri_async_cancellation_cb,
+ data);
+ g_cancellable_cancel (cancellable);
+
+ g_main_loop_run (data->main_loop);
+
+ g_object_unref (cancellable);
+}
+
+typedef struct {
+ OAuth2AuthorizerData parent;
+ gchar *token;
+ gchar *token_secret;
+ gchar *verifier;
+} OAuth2AuthorizerInteractiveData;
+
+/* NOTE: Any consumer of this data has to check for (data->verifier == NULL) and skip the test in that case
*/
+static void
+set_up_oauth2_authorizer_interactive_data (OAuth2AuthorizerInteractiveData *data, gconstpointer user_data)
+{
+ gchar *authentication_uri;
+
+ /* Chain up */
+ set_up_oauth2_authorizer_data ((OAuth2AuthorizerData*) data, user_data);
+
+ /* Get an authentication URI */
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri (data->parent.authorizer,
&(data->token), &(data->token_secret),
+ NULL, NULL);
+ g_assert (authentication_uri != NULL);
+
+ /* Wait for the user to retrieve and enter the verifier */
+ data->verifier = query_user_for_verifier (authentication_uri);
+
+ g_free (authentication_uri);
+}
+
+static void
+set_up_oauth2_authorizer_interactive_data_bad_credentials (OAuth2AuthorizerInteractiveData *data,
gconstpointer user_data)
+{
+ gchar *authentication_uri;
+
+ /* Chain up */
+ set_up_oauth2_authorizer_data ((OAuth2AuthorizerData*) data, user_data);
+
+ /* Get an authentication URI */
+ authentication_uri = gdata_oauth2_authorizer_request_authentication_uri (data->parent.authorizer,
&(data->token), &(data->token_secret),
+ NULL, NULL);
+ g_assert (authentication_uri != NULL);
+
+ /* Give a bogus verifier */
+ data->verifier = g_strdup ("test");
+
+ g_free (authentication_uri);
+}
+
+static void
+tear_down_oauth2_authorizer_interactive_data (OAuth2AuthorizerInteractiveData *data, gconstpointer user_data)
+{
+ g_free (data->token);
+ g_free (data->token_secret);
+ g_free (data->verifier);
+
+ /* Chain up */
+ tear_down_oauth2_authorizer_data ((OAuth2AuthorizerData*) data, user_data);
+}
+
+/* Test that synchronously authorizing a request token is successful. Note that this test has to be
interactive, as the user has to visit the
+ * authentication URI to retrieve a verifier for the request token. */
+static void
+test_oauth2_authorizer_request_authorization_sync (OAuth2AuthorizerInteractiveData *data, gconstpointer
user_data)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ /* Skip the test if the user's requested */
+ if (data->verifier == NULL) {
+ return;
+ }
+
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Test that authorising the token retrieved previously is successful */
+ success = gdata_oauth2_authorizer_request_authorization (data->parent.authorizer, data->token,
data->token_secret, data->verifier, NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == TRUE);
+}
+
+/* Test that synchronously authorizing a request token fails if an invalid verifier is provided. */
+static void
+test_oauth2_authorizer_request_authorization_sync_bad_credentials (OAuth2AuthorizerInteractiveData *data,
gconstpointer user_data)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Test that authorising the token retrieved above fails */
+ success = gdata_oauth2_authorizer_request_authorization (data->parent.authorizer, data->token,
data->token_secret, data->verifier, NULL,
+ &error);
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN);
+ g_assert (success == FALSE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+}
+
+/* Test that cancellation of synchronously authorizing a request token works. Note that this test has to be
interactive, as the user has to visit the
+ * authentication URI to retrieve a verifier for the request token. */
+static void
+test_oauth2_authorizer_request_authorization_sync_cancellation (OAuth2AuthorizerInteractiveData *data,
gconstpointer user_data)
+{
+ gboolean success;
+ GCancellable *cancellable;
+ GError *error = NULL;
+
+ /* Skip the test if the user's requested */
+ if (data->verifier == NULL) {
+ return;
+ }
+
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Create the cancellable */
+ cancellable = g_cancellable_new ();
+
+ /* Test that authorising the token retrieved above is successful */
+ g_cancellable_cancel (cancellable);
+ success = gdata_oauth2_authorizer_request_authorization (data->parent.authorizer, data->token,
data->token_secret, data->verifier,
+ cancellable, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (success == FALSE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (data->parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ g_object_unref (cancellable);
+}
+
+typedef struct {
+ OAuth2AuthorizerInteractiveData parent;
+ GMainLoop *main_loop;
+} OAuth2AuthorizerInteractiveAsyncData;
+
+/* NOTE: Any consumer of this data has to check for (data->verifier == NULL) and skip the test in that case
*/
+static void
+set_up_oauth2_authorizer_interactive_async_data (OAuth2AuthorizerInteractiveAsyncData *data, gconstpointer
user_data)
+{
+ /* Chain up */
+ set_up_oauth2_authorizer_interactive_data ((OAuth2AuthorizerInteractiveData*) data, user_data);
+
+ /* Set up the main loop */
+ data->main_loop = g_main_loop_new (NULL, FALSE);
+}
+
+static void
+set_up_oauth2_authorizer_interactive_async_data_bad_credentials (OAuth2AuthorizerInteractiveAsyncData *data,
gconstpointer user_data)
+{
+ /* Chain up */
+ set_up_oauth2_authorizer_interactive_data_bad_credentials ((OAuth2AuthorizerInteractiveData*) data,
user_data);
+
+ /* Set up the main loop */
+ data->main_loop = g_main_loop_new (NULL, FALSE);
+}
+
+static void
+tear_down_oauth2_authorizer_interactive_async_data (OAuth2AuthorizerInteractiveAsyncData *data,
gconstpointer user_data)
+{
+ g_main_loop_unref (data->main_loop);
+
+ /* Chain up */
+ tear_down_oauth2_authorizer_interactive_data ((OAuth2AuthorizerInteractiveData*) data, user_data);
+}
+
+static void
+test_oauth2_authorizer_request_authorization_async_cb (GDataOAuth2Authorizer *authorizer, GAsyncResult
*async_result,
+ OAuth2AuthorizerInteractiveAsyncData *data)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ success = gdata_oauth2_authorizer_request_authorization_finish (authorizer, async_result, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == TRUE);
+
+ g_main_loop_quit (data->main_loop);
+}
+
+/* Test that asynchronously authorizing a request token works. Note that this test has to be interactive, as
the user has to visit the
+ * authentication URI to retrieve a verifier for the request token. */
+static void
+test_oauth2_authorizer_request_authorization_async (OAuth2AuthorizerInteractiveAsyncData *data,
gconstpointer user_data)
+{
+ /* Skip the test if the user's requested */
+ if (data->parent.verifier == NULL) {
+ return;
+ }
+
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER
(data->parent.parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Create a main loop and request authorization */
+ gdata_oauth2_authorizer_request_authorization_async (data->parent.parent.authorizer,
data->parent.token, data->parent.token_secret,
+ data->parent.verifier, NULL,
+ (GAsyncReadyCallback)
test_oauth2_authorizer_request_authorization_async_cb, data);
+
+ g_main_loop_run (data->main_loop);
+}
+
+static void
+test_oauth2_authorizer_request_authorization_async_bad_credentials_cb (GDataOAuth2Authorizer *authorizer,
GAsyncResult *async_result,
+ OAuth2AuthorizerInteractiveAsyncData
*data)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ success = gdata_oauth2_authorizer_request_authorization_finish (authorizer, async_result, &error);
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN);
+ g_assert (success == FALSE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ g_main_loop_quit (data->main_loop);
+}
+
+/* Test that asynchronously authorizing a request token fails if an invalid verifier is provided. */
+static void
+test_oauth2_authorizer_request_authorization_async_bad_credentials (OAuth2AuthorizerInteractiveAsyncData
*data, gconstpointer user_data)
+{
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER
(data->parent.parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Create a main loop and request authorization */
+ gdata_oauth2_authorizer_request_authorization_async (data->parent.parent.authorizer,
data->parent.token, data->parent.token_secret,
+ data->parent.verifier, NULL,
+ (GAsyncReadyCallback)
+
test_oauth2_authorizer_request_authorization_async_bad_credentials_cb,
+ data);
+
+ g_main_loop_run (data->main_loop);
+}
+
+static void
+test_oauth2_authorizer_request_authorization_async_cancellation_cb (GDataOAuth2Authorizer *authorizer,
GAsyncResult *async_result,
+ OAuth2AuthorizerInteractiveAsyncData
*data)
+{
+ gboolean success;
+ GError *error = NULL;
+
+ success = gdata_oauth2_authorizer_request_authorization_finish (authorizer, async_result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (success == FALSE);
+ g_clear_error (&error);
+
+ /* Are we authorised now? */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ g_main_loop_quit (data->main_loop);
+}
+
+/* Test that cancelling asynchronously authorizing a request token works. Note that this test has to be
interactive, as the user has to visit the
+ * authentication URI to retrieve a verifier for the request token. */
+static void
+test_oauth2_authorizer_request_authorization_async_cancellation (OAuth2AuthorizerInteractiveAsyncData *data,
gconstpointer user_data)
+{
+ GCancellable *cancellable;
+
+ /* Skip the test if the user's requested */
+ if (data->parent.verifier == NULL) {
+ return;
+ }
+
+ /* Check we're not authorised beforehand */
+ g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER
(data->parent.parent.authorizer),
+ gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+
+ /* Create the cancellable */
+ cancellable = g_cancellable_new ();
+
+ /* Create a main loop and request authorization */
+ gdata_oauth2_authorizer_request_authorization_async (data->parent.parent.authorizer,
data->parent.token, data->parent.token_secret,
+ data->parent.verifier, cancellable,
+ (GAsyncReadyCallback)
test_oauth2_authorizer_request_authorization_async_cancellation_cb,
+ data);
+ g_cancellable_cancel (cancellable);
+
+ g_main_loop_run (data->main_loop);
+
+ g_object_unref (cancellable);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gdata_test_init (argc, argv);
+
+ main_thread = g_thread_self ();
+
+ g_test_add_func ("/oauth2-authorizer/constructor", test_oauth2_authorizer_constructor);
+ g_test_add_func ("/oauth2-authorizer/constructor/for-domains",
test_oauth2_authorizer_constructor_for_domains);
+
+ g_test_add ("/oauth2-authorizer/properties/application-name", OAuth2AuthorizerData, NULL,
set_up_oauth2_authorizer_data,
+ test_oauth2_authorizer_properties_application_name, tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/properties/application-name/fallback", OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data_fallback_application_name,
test_oauth2_authorizer_properties_application_name_fallback,
+ tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/properties/locale", OAuth2AuthorizerData, NULL,
set_up_oauth2_authorizer_data,
+ test_oauth2_authorizer_properties_locale, tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/properties/proxy-uri", OAuth2AuthorizerData, NULL,
set_up_oauth2_authorizer_data,
+ test_oauth2_authorizer_properties_proxy_uri, tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/properties/timeout", OAuth2AuthorizerData, NULL,
set_up_oauth2_authorizer_data,
+ test_oauth2_authorizer_properties_timeout, tear_down_oauth2_authorizer_data);
+
+ g_test_add ("/oauth2-authorizer/refresh-authorization/unauthenticated", OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data, test_oauth2_authorizer_refresh_authorization,
tear_down_oauth2_authorizer_data);
+
+ g_test_add ("/oauth2-authorizer/process-request/null", OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data, test_oauth2_authorizer_process_request_null,
tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/process-request/unauthenticated", OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data, test_oauth2_authorizer_process_request_unauthenticated,
tear_down_oauth2_authorizer_data);
+
+ if (gdata_test_internet () == TRUE) {
+ /* Sync request-authentication-uri tests */
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/sync", OAuth2AuthorizerData, NULL,
set_up_oauth2_authorizer_data,
+ test_oauth2_authorizer_request_authentication_uri_sync,
tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/sync/multiple-domains",
OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data_multiple_domains,
test_oauth2_authorizer_request_authentication_uri_sync,
+ tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/sync/multiple-domains",
OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data_locale,
test_oauth2_authorizer_request_authentication_uri_sync,
+ tear_down_oauth2_authorizer_data);
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/sync/cancellation",
OAuth2AuthorizerData, NULL,
+ set_up_oauth2_authorizer_data,
test_oauth2_authorizer_request_authentication_uri_sync_cancellation,
+ tear_down_oauth2_authorizer_data);
+
+ /* Async request-authentication-uri tests */
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/async", OAuth2AuthorizerAsyncData,
NULL,
+ set_up_oauth2_authorizer_async_data,
test_oauth2_authorizer_request_authentication_uri_async,
+ tear_down_oauth2_authorizer_async_data);
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/async/multiple-domains",
OAuth2AuthorizerAsyncData, NULL,
+ set_up_oauth2_authorizer_async_data_multiple_domains,
test_oauth2_authorizer_request_authentication_uri_async,
+ tear_down_oauth2_authorizer_async_data);
+ g_test_add ("/oauth2-authorizer/request-authentication-uri/async/cancellation",
OAuth2AuthorizerAsyncData, NULL,
+ set_up_oauth2_authorizer_async_data,
test_oauth2_authorizer_request_authentication_uri_async_cancellation,
+ tear_down_oauth2_authorizer_async_data);
+
+ /* Sync request-authorization tests */
+ if (gdata_test_interactive () == TRUE) {
+ g_test_add ("/oauth2-authorizer/request-authorization/sync",
OAuth2AuthorizerInteractiveData, NULL,
+ set_up_oauth2_authorizer_interactive_data,
test_oauth2_authorizer_request_authorization_sync,
+ tear_down_oauth2_authorizer_interactive_data);
+ g_test_add ("/oauth2-authorizer/request-authorization/sync/cancellation",
OAuth2AuthorizerInteractiveData, NULL,
+ set_up_oauth2_authorizer_interactive_data,
test_oauth2_authorizer_request_authorization_sync_cancellation,
+ tear_down_oauth2_authorizer_interactive_data);
+ }
+
+ g_test_add ("/oauth2-authorizer/request-authorization/sync/bad-credentials",
OAuth2AuthorizerInteractiveData, NULL,
+ set_up_oauth2_authorizer_interactive_data_bad_credentials,
+ test_oauth2_authorizer_request_authorization_sync_bad_credentials,
tear_down_oauth2_authorizer_interactive_data);
+
+ /* Async request-authorization tests */
+ if (gdata_test_interactive () == TRUE) {
+ g_test_add ("/oauth2-authorizer/request-authorization/async",
OAuth2AuthorizerInteractiveAsyncData, NULL,
+ set_up_oauth2_authorizer_interactive_async_data,
test_oauth2_authorizer_request_authorization_async,
+ tear_down_oauth2_authorizer_interactive_async_data);
+ g_test_add ("/oauth2-authorizer/request-authorization/async/cancellation",
OAuth2AuthorizerInteractiveAsyncData, NULL,
+ set_up_oauth2_authorizer_interactive_async_data,
test_oauth2_authorizer_request_authorization_async_cancellation,
+ tear_down_oauth2_authorizer_interactive_async_data);
+ }
+
+ g_test_add ("/oauth2-authorizer/request-authorization/async/bad-credentials",
OAuth2AuthorizerInteractiveAsyncData, NULL,
+ set_up_oauth2_authorizer_interactive_async_data_bad_credentials,
+ test_oauth2_authorizer_request_authorization_async_bad_credentials,
tear_down_oauth2_authorizer_interactive_async_data);
+
+ /* Miscellaneous tests */
+ if (gdata_test_interactive () == TRUE) {
+ gboolean skip_test = FALSE;
+
+ g_test_add ("/oauth2-authorizer/refresh-authorization/authenticated",
OAuth2AuthorizerData, &skip_test,
+ set_up_oauth2_authorizer_data_authenticated,
test_oauth2_authorizer_refresh_authorization,
+ tear_down_oauth2_authorizer_data);
+
+ g_test_add ("/oauth2-authorizer/process-request/authenticated", OAuth2AuthorizerData,
&skip_test,
+ set_up_oauth2_authorizer_data_authenticated,
test_oauth2_authorizer_process_request_authenticated,
+ tear_down_oauth2_authorizer_data);
+ }
+ }
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]