Blame gdata/gdata-oauth2-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) Philip Withnall 2011, 2014, 2015 <philip@tecnocode.co.uk>
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-oauth2-authorizer
Packit 4b6dd7
 * @short_description: GData OAuth 2.0 authorization interface
Packit 4b6dd7
 * @stability: Stable
Packit 4b6dd7
 * @include: gdata/gdata-oauth2-authorizer.h
Packit 4b6dd7
 *
Packit 4b6dd7
 * #GDataOAuth2Authorizer provides an implementation of the #GDataAuthorizer
Packit 4b6dd7
 * interface for authentication and authorization using the
Packit 4b6dd7
 * <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2InstalledApp">OAuth 2.0</ulink>
Packit 4b6dd7
 * process, which is Google’s currently preferred authentication and
Packit 4b6dd7
 * authorization process.
Packit 4b6dd7
 *
Packit 4b6dd7
 * OAuth 2.0 replaces the deprecated OAuth 1.0 and ClientLogin processes. One of
Packit 4b6dd7
 * the main reasons for this is to allow two-factor authentication to be
Packit 4b6dd7
 * supported, by moving the authentication interface to a web page under
Packit 4b6dd7
 * Google’s control.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The OAuth 2.0 process as implemented by Google follows the
Packit 4b6dd7
 * <ulink type="http" url="http://tools.ietf.org/html/rfc6749">OAuth 2.0
Packit 4b6dd7
 * protocol as specified by IETF in RFC 6749</ulink>, with a few additions to
Packit 4b6dd7
 * support scopes (implemented in libgdata by #GDataAuthorizationDomains),
Packit 4b6dd7
 * locales and custom domains. Briefly, the process is initiated by building an
Packit 4b6dd7
 * authentication URI (using gdata_oauth2_authorizer_build_authentication_uri())
Packit 4b6dd7
 * and opening it in the user’s web browser. The user authenticates and
Packit 4b6dd7
 * authorizes the requested scopes on Google’s website, then an authorization
Packit 4b6dd7
 * code is returned (via #GDataOAuth2Authorizer:redirect-uri) to the
Packit 4b6dd7
 * application, which then converts the code into an access and refresh token
Packit 4b6dd7
 * (using gdata_oauth2_authorizer_request_authorization()). The access token is
Packit 4b6dd7
 * then attached to all future requests to the online service, and the refresh
Packit 4b6dd7
 * token can be used in future (with gdata_authorizer_refresh_authorization())
Packit 4b6dd7
 * to refresh authorization after the access token expires.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The refresh token may also be accessed as
Packit 4b6dd7
 * #GDataOAuth2Authorizer:refresh-token and saved by the application. It may
Packit 4b6dd7
 * later be set on a new instance of #GDataOAuth2Authorizer, and
Packit 4b6dd7
 * gdata_authorizer_refresh_authorization_async() called to establish a new
Packit 4b6dd7
 * access token without requiring the user to re-authenticate unless they have
Packit 4b6dd7
 * explicitly revoked the refresh token.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For an overview of the standard OAuth 2.0 flow, see
Packit 4b6dd7
 * <ulink type="http" url="http://tools.ietf.org/html/rfc6749#section-1.2">RFC 6749</ulink>.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Before an application can be authorized using OAuth 2.0, it must be
Packit 4b6dd7
 * registered with
Packit 4b6dd7
 * <ulink type="http" url="https://console.developers.google.com/project">Google’s
Packit 4b6dd7
 * Developer Console</ulink>, and a client ID, client secret and redirect URI
Packit 4b6dd7
 * retrieved. These must be built into your application, and knowledge of them
Packit 4b6dd7
 * will allow any application to impersonate yours, so it is recommended that
Packit 4b6dd7
 * they are kept secret (e.g. as a configure-time option).
Packit 4b6dd7
 *
Packit 4b6dd7
 * libgdata supports
Packit 4b6dd7
 * <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2InstalledApp#incrementalAuth">incremental
Packit 4b6dd7
 * authorization</ulink>, where multiple #GDataOAuth2Authorizers can be used to
Packit 4b6dd7
 * incrementally build up authorizations against multiple scopes. Typically,
Packit 4b6dd7
 * you should use one #GDataOAuth2Authorizer per #GDataService your application
Packit 4b6dd7
 * uses, limit the scope of each authorizer, and enable incremental
Packit 4b6dd7
 * authorization when calling
Packit 4b6dd7
 * gdata_oauth2_authorizer_build_authentication_uri().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Each access token is long lived, so reauthorization is rarely necessary with
Packit 4b6dd7
 * #GDataOAuth2Authorizer. It is supported using
Packit 4b6dd7
 * gdata_authorizer_refresh_authorization().
Packit 4b6dd7
 *
Packit 4b6dd7
 * <example>
Packit 4b6dd7
 *	<title>Authenticating Asynchronously Using OAuth 2.0</title>
Packit 4b6dd7
 *	<programlisting>
Packit 4b6dd7
 *	GDataSomeService *service;
Packit 4b6dd7
 *	GDataOAuth2Authorizer *authorizer;
Packit 4b6dd7
 *	gchar *authentication_uri, *authorization_code;
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Create an authorizer and authenticate and authorize the service we're using, asynchronously. */
Packit 4b6dd7
 *	authorizer = gdata_oauth2_authorizer_new ("some-client-id", "some-client-secret",
Packit 4b6dd7
 *	                                          GDATA_OAUTH2_REDIRECT_URI_OOB, GDATA_TYPE_SOME_SERVICE);
Packit 4b6dd7
 *	authentication_uri = gdata_oauth2_authorizer_build_authentication_uri (authorizer, NULL, FALSE);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* (Present the page at the authentication URI to the user, either in an embedded or stand-alone web browser, and
Packit 4b6dd7
 *	 * ask them to grant access to the application and return the code Google gives them.) */
Packit 4b6dd7
 *	authorization_code = ask_user_for_code (authentication_uri);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	gdata_oauth2_authorizer_request_authorization_async (authorizer, authorization_code, cancellable,
Packit 4b6dd7
 *	                                                     (GAsyncReadyCallback) request_authorization_cb, user_data);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_free (authentication_uri);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Zero out the code before freeing. */
Packit 4b6dd7
 *	if (token_secret != NULL) {
Packit 4b6dd7
 *		memset (authorization_code, 0, strlen (authorization_code));
Packit 4b6dd7
 *	}
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_free (authorization_code);
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
 *	static void
Packit 4b6dd7
 *	request_authorization_cb (GDataOAuth2Authorizer *authorizer, GAsyncResult *async_result, gpointer user_data)
Packit 4b6dd7
 *	{
Packit 4b6dd7
 *		GError *error = NULL;
Packit 4b6dd7
 *
Packit 4b6dd7
 *		if (gdata_oauth2_authorizer_request_authorization_finish (authorizer, async_result, &error) == FALSE) {
Packit 4b6dd7
 *			/* Notify the user of all errors except cancellation errors */
Packit 4b6dd7
 *			if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
Packit 4b6dd7
 *				g_error ("Authorization failed: %s", error->message);
Packit 4b6dd7
 *			}
Packit 4b6dd7
 *
Packit 4b6dd7
 *			g_error_free (error);
Packit 4b6dd7
 *			return;
Packit 4b6dd7
 *		}
Packit 4b6dd7
 *
Packit 4b6dd7
 *		/* (The client is now authenticated and authorized against the service.
Packit 4b6dd7
 *		 * It can now proceed to execute queries on the service object which require the user to be authenticated.) */
Packit 4b6dd7
 *	}
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_object_unref (service);
Packit 4b6dd7
 *	g_object_unref (authorizer);
Packit 4b6dd7
 *	</programlisting>
Packit 4b6dd7
 * </example>
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
#include <config.h>
Packit 4b6dd7
#include <string.h>
Packit 4b6dd7
#include <glib.h>
Packit 4b6dd7
#include <glib/gi18n-lib.h>
Packit 4b6dd7
Packit 4b6dd7
#include "gdata-oauth2-authorizer.h"
Packit 4b6dd7
#include "gdata-private.h"
Packit 4b6dd7
Packit 4b6dd7
static void authorizer_init (GDataAuthorizerInterface *iface);
Packit 4b6dd7
static void dispose (GObject *object);
Packit 4b6dd7
static void finalize (GObject *object);
Packit 4b6dd7
static void get_property (GObject *object, guint property_id, GValue *value,
Packit 4b6dd7
                          GParamSpec *pspec);
Packit 4b6dd7
static void set_property (GObject *object, guint property_id,
Packit 4b6dd7
                          const GValue *value, GParamSpec *pspec);
Packit 4b6dd7
Packit 4b6dd7
static void process_request (GDataAuthorizer *self,
Packit 4b6dd7
                             GDataAuthorizationDomain *domain,
Packit 4b6dd7
                             SoupMessage *message);
Packit 4b6dd7
static void sign_message_locked (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                 SoupMessage *message,
Packit 4b6dd7
                                 const gchar *access_token);
Packit 4b6dd7
static gboolean is_authorized_for_domain (GDataAuthorizer *self,
Packit 4b6dd7
                                          GDataAuthorizationDomain *domain);
Packit 4b6dd7
static gboolean refresh_authorization (GDataAuthorizer *self,
Packit 4b6dd7
                                       GCancellable *cancellable,
Packit 4b6dd7
                                       GError **error);
Packit 4b6dd7
Packit 4b6dd7
static void parse_grant_response (GDataOAuth2Authorizer *self, guint status,
Packit 4b6dd7
                                  const gchar *reason_phrase,
Packit 4b6dd7
                                  const gchar *response_body, gssize length,
Packit 4b6dd7
                                  GError **error);
Packit 4b6dd7
static void parse_grant_error (GDataOAuth2Authorizer *self, guint status,
Packit 4b6dd7
                               const gchar *reason_phrase,
Packit 4b6dd7
                               const gchar *response_body, gssize length,
Packit 4b6dd7
                               GError **error);
Packit 4b6dd7
Packit 4b6dd7
static void notify_timeout_cb (GObject *gobject, GParamSpec *pspec,
Packit 4b6dd7
                               GObject *self);
Packit 4b6dd7
Packit 4b6dd7
struct _GDataOAuth2AuthorizerPrivate {
Packit 4b6dd7
	SoupSession *session;  /* owned */
Packit 4b6dd7
	GProxyResolver *proxy_resolver;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	gchar *client_id;  /* owned */
Packit 4b6dd7
	gchar *redirect_uri;  /* owned */
Packit 4b6dd7
	gchar *client_secret;  /* owned */
Packit 4b6dd7
	gchar *locale;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	/* Mutex for access_token, refresh_token and authentication_domains. */
Packit 4b6dd7
	GMutex mutex;
Packit 4b6dd7
Packit 4b6dd7
	/* These are both non-NULL when authorised. refresh_token may be
Packit 4b6dd7
	 * non-NULL if access_token is NULL and refresh_authorization() has not
Packit 4b6dd7
	 * yet been called on this authorizer. They may be both NULL. */
Packit 4b6dd7
	gchar *access_token;  /* owned */
Packit 4b6dd7
	gchar *refresh_token;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	/* Mapping from GDataAuthorizationDomain to itself; a set of domains for
Packit 4b6dd7
	 * which ->access_token is valid. */
Packit 4b6dd7
	GHashTable *authentication_domains;  /* owned */
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
enum {
Packit 4b6dd7
	PROP_CLIENT_ID = 1,
Packit 4b6dd7
	PROP_REDIRECT_URI,
Packit 4b6dd7
	PROP_CLIENT_SECRET,
Packit 4b6dd7
	PROP_LOCALE,
Packit 4b6dd7
	PROP_TIMEOUT,
Packit 4b6dd7
	PROP_PROXY_RESOLVER,
Packit 4b6dd7
	PROP_REFRESH_TOKEN,
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
G_DEFINE_TYPE_WITH_CODE (GDataOAuth2Authorizer, gdata_oauth2_authorizer,
Packit 4b6dd7
                         G_TYPE_OBJECT,
Packit 4b6dd7
                         G_IMPLEMENT_INTERFACE (GDATA_TYPE_AUTHORIZER,
Packit 4b6dd7
                                                authorizer_init))
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_oauth2_authorizer_class_init (GDataOAuth2AuthorizerClass *klass)
Packit 4b6dd7
{
Packit 4b6dd7
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit 4b6dd7
Packit 4b6dd7
	g_type_class_add_private (klass, sizeof (GDataOAuth2AuthorizerPrivate));
Packit 4b6dd7
Packit 4b6dd7
	gobject_class->get_property = get_property;
Packit 4b6dd7
	gobject_class->set_property = set_property;
Packit 4b6dd7
	gobject_class->dispose = dispose;
Packit 4b6dd7
	gobject_class->finalize = finalize;
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:client-id:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * A client ID for your application (see the
Packit 4b6dd7
	 * <ulink url="https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse" type="http">reference documentation</ulink>).
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * It is recommended that the ID is of the form
Packit 4b6dd7
	 * <literal><replaceable>company name</replaceable>-
Packit 4b6dd7
	 * <replaceable>application name</replaceable>-
Packit 4b6dd7
	 * <replaceable>version ID</replaceable></literal>.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_CLIENT_ID,
Packit 4b6dd7
	                                 g_param_spec_string ("client-id",
Packit 4b6dd7
	                                                      "Client ID",
Packit 4b6dd7
	                                                      "A client ID for your application.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:redirect-uri:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Redirect URI to send the response from the authorisation request to.
Packit 4b6dd7
	 * This must either be %GDATA_OAUTH2_REDIRECT_URI_OOB,
Packit 4b6dd7
	 * %GDATA_OAUTH2_REDIRECT_URI_OOB_AUTO, or a
Packit 4b6dd7
	 * http://localhost URI with any port number (optionally)
Packit 4b6dd7
	 * specified.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * This URI is where the authorisation server will redirect the user
Packit 4b6dd7
	 * after they have completed interacting with the authentication page
Packit 4b6dd7
	 * (gdata_oauth2_authorizer_build_authentication_uri()). If it is
Packit 4b6dd7
	 * %GDATA_OAUTH2_REDIRECT_URI_OOB, a page will be returned in the user’s
Packit 4b6dd7
	 * browser with the authorisation code in its title and also embedded in
Packit 4b6dd7
	 * the page for the user to copy if it is not possible to automatically
Packit 4b6dd7
	 * extract the code from the page title. If it is
Packit 4b6dd7
	 * %GDATA_OAUTH2_REDIRECT_URI_OOB_AUTO, a similar page will be returned
Packit 4b6dd7
	 * with the authorisation code in its title, but without displaying the
Packit 4b6dd7
	 * code to the user — the user will simply be asked to close the page.
Packit 4b6dd7
	 * If it is a localhost URI, the authentication page will redirect to
Packit 4b6dd7
	 * that URI with the authorisation code appended as a code
Packit 4b6dd7
	 * query parameter. If the user denies the authentication request, the
Packit 4b6dd7
	 * authentication page will redirect to that URI with
Packit 4b6dd7
	 * error=access_denied appended as a query parameter.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Note that the redirect URI used must match that registered in
Packit 4b6dd7
	 * Google’s Developer Console for your application.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * See the <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi">reference
Packit 4b6dd7
	 * documentation</ulink> for details about choosing a redirect URI.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_REDIRECT_URI,
Packit 4b6dd7
	                                 g_param_spec_string ("redirect-uri",
Packit 4b6dd7
	                                                      "Redirect URI",
Packit 4b6dd7
	                                                      "Redirect URI to send the response from the authorisation request to.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:client-secret:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Client secret provided by Google. This is unique for each application
Packit 4b6dd7
	 * and is accessible from Google’s Developer Console when registering
Packit 4b6dd7
	 * an application. It must be paired with the
Packit 4b6dd7
	 * #GDataOAuth2Authorizer:client-id.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * See the
Packit 4b6dd7
	 * <ulink url="https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse" type="http">reference
Packit 4b6dd7
	 * documentation</ulink> for details.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_CLIENT_SECRET,
Packit 4b6dd7
	                                 g_param_spec_string ("client-secret",
Packit 4b6dd7
	                                                      "Client secret",
Packit 4b6dd7
	                                                      "Client secret provided by Google.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:locale:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The locale to use for network requests, in UNIX locale format.
Packit 4b6dd7
	 * (e.g. "en_GB", "cs", "de_DE".) Use %NULL for the default "C" locale
Packit 4b6dd7
	 * (typically "en_US").
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * This locale will be used by the server-side software to localise the
Packit 4b6dd7
	 * authentication and authorization pages at the URI returned by
Packit 4b6dd7
	 * gdata_oauth2_authorizer_build_authentication_uri().
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The server-side behaviour is undefined if it doesn't support a given
Packit 4b6dd7
	 * locale.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_LOCALE,
Packit 4b6dd7
	                                 g_param_spec_string ("locale",
Packit 4b6dd7
	                                                      "Locale",
Packit 4b6dd7
	                                                      "The locale to use for network requests, in UNIX locale format.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:timeout:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * A timeout, in seconds, for network operations. If the timeout is
Packit 4b6dd7
	 * exceeded, the operation will be cancelled and
Packit 4b6dd7
	 * %GDATA_SERVICE_ERROR_NETWORK_ERROR will be returned.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * If the timeout is 0, operations will
Packit 4b6dd7
	 * never time out.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_TIMEOUT,
Packit 4b6dd7
	                                 g_param_spec_uint ("timeout",
Packit 4b6dd7
	                                                    "Timeout",
Packit 4b6dd7
	                                                    "A timeout, in seconds, for network operations.",
Packit 4b6dd7
	                                                    0, G_MAXUINT, 0,
Packit 4b6dd7
	                                                    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:proxy-resolver:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The #GProxyResolver used to determine a proxy URI.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
Packit 4b6dd7
	                                 g_param_spec_object ("proxy-resolver",
Packit 4b6dd7
	                                                      "Proxy Resolver",
Packit 4b6dd7
	                                                      "A GProxyResolver used to determine a proxy URI.",
Packit 4b6dd7
	                                                      G_TYPE_PROXY_RESOLVER,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataOAuth2Authorizer:refresh-token:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The server provided refresh token, which can be stored and passed in
Packit 4b6dd7
	 * to new #GDataOAuth2Authorizer instances before calling
Packit 4b6dd7
	 * gdata_authorizer_refresh_authorization_async() to create a new
Packit 4b6dd7
	 * short-lived access token.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The refresh token is opaque data and must not be parsed.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.17.2
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_REFRESH_TOKEN,
Packit 4b6dd7
	                                 g_param_spec_string ("refresh-token",
Packit 4b6dd7
	                                                      "Refresh Token",
Packit 4b6dd7
	                                                      "The server provided refresh token.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
authorizer_init (GDataAuthorizerInterface *iface)
Packit 4b6dd7
{
Packit 4b6dd7
	iface->process_request = process_request;
Packit 4b6dd7
	iface->is_authorized_for_domain = is_authorized_for_domain;
Packit 4b6dd7
Packit 4b6dd7
	/* We only implement the synchronous version, as GDataAuthorizer will
Packit 4b6dd7
	 * automatically wrap it in a thread for the asynchronous versions if
Packit 4b6dd7
	 * they’re not specifically implemented, which is fine for our needs. We
Packit 4b6dd7
	 * couldn’t do any better by implementing the asynchronous versions
Packit 4b6dd7
	 * ourselves. */
Packit 4b6dd7
	iface->refresh_authorization = refresh_authorization;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_oauth2_authorizer_init (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
Packit 4b6dd7
	                                          GDATA_TYPE_OAUTH2_AUTHORIZER,
Packit 4b6dd7
	                                          GDataOAuth2AuthorizerPrivate);
Packit 4b6dd7
Packit 4b6dd7
	/* Set up the authorizer's mutex */
Packit 4b6dd7
	g_mutex_init (&self->priv->mutex);
Packit 4b6dd7
	self->priv->authentication_domains = g_hash_table_new_full (g_direct_hash,
Packit 4b6dd7
	                                                            g_direct_equal,
Packit 4b6dd7
	                                                            g_object_unref,
Packit 4b6dd7
	                                                            NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Set up the session */
Packit 4b6dd7
	self->priv->session = _gdata_service_build_session ();
Packit 4b6dd7
Packit 4b6dd7
	/* Proxy the SoupSession’s timeout property. */
Packit 4b6dd7
	g_signal_connect (self->priv->session, "notify::timeout",
Packit 4b6dd7
	                  (GCallback) notify_timeout_cb, self);
Packit 4b6dd7
Packit 4b6dd7
	/* Keep our GProxyResolver synchronized with SoupSession’s. */
Packit 4b6dd7
	g_object_bind_property (self->priv->session, "proxy-resolver",
Packit 4b6dd7
	                        self, "proxy-resolver",
Packit 4b6dd7
	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
dispose (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_OAUTH2_AUTHORIZER (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_clear_object (&priv->session);
Packit 4b6dd7
	g_clear_object (&priv->proxy_resolver);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_oauth2_authorizer_parent_class)->dispose (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
finalize (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_OAUTH2_AUTHORIZER (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->client_id);
Packit 4b6dd7
	g_free (priv->client_secret);
Packit 4b6dd7
	g_free (priv->redirect_uri);
Packit 4b6dd7
	g_free (priv->locale);
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->access_token);
Packit 4b6dd7
	g_free (priv->refresh_token);
Packit 4b6dd7
Packit 4b6dd7
	g_hash_table_unref (priv->authentication_domains);
Packit 4b6dd7
	g_mutex_clear (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_oauth2_authorizer_parent_class)->finalize (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
get_property (GObject *object, guint property_id, GValue *value,
Packit 4b6dd7
              GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2Authorizer *self;
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	self = GDATA_OAUTH2_AUTHORIZER (object);
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
	case PROP_CLIENT_ID:
Packit 4b6dd7
		g_value_set_string (value, priv->client_id);
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_REDIRECT_URI:
Packit 4b6dd7
		g_value_set_string (value, priv->redirect_uri);
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_CLIENT_SECRET:
Packit 4b6dd7
		g_value_set_string (value, priv->client_secret);
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_LOCALE:
Packit 4b6dd7
		g_value_set_string (value, priv->locale);
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_TIMEOUT:
Packit 4b6dd7
		g_value_set_uint (value,
Packit 4b6dd7
		                  gdata_oauth2_authorizer_get_timeout (self));
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_PROXY_RESOLVER:
Packit 4b6dd7
		g_value_set_object (value,
Packit 4b6dd7
		                    gdata_oauth2_authorizer_get_proxy_resolver (self));
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_REFRESH_TOKEN:
Packit 4b6dd7
		g_mutex_lock (&priv->mutex);
Packit 4b6dd7
		g_value_set_string (value, priv->refresh_token);
Packit 4b6dd7
		g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
		break;
Packit 4b6dd7
	default:
Packit 4b6dd7
		/* We don't have any other property... */
Packit 4b6dd7
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
		break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2Authorizer *self;
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	self = GDATA_OAUTH2_AUTHORIZER (object);
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
	/* Construct only. */
Packit 4b6dd7
	case PROP_CLIENT_ID:
Packit 4b6dd7
		priv->client_id = g_value_dup_string (value);
Packit 4b6dd7
		break;
Packit 4b6dd7
	/* Construct only. */
Packit 4b6dd7
	case PROP_REDIRECT_URI:
Packit 4b6dd7
		priv->redirect_uri = g_value_dup_string (value);
Packit 4b6dd7
		break;
Packit 4b6dd7
	/* Construct only. */
Packit 4b6dd7
	case PROP_CLIENT_SECRET:
Packit 4b6dd7
		priv->client_secret = g_value_dup_string (value);
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_LOCALE:
Packit 4b6dd7
		gdata_oauth2_authorizer_set_locale (self,
Packit 4b6dd7
		                                    g_value_get_string (value));
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_TIMEOUT:
Packit 4b6dd7
		gdata_oauth2_authorizer_set_timeout (self,
Packit 4b6dd7
		                                     g_value_get_uint (value));
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_PROXY_RESOLVER:
Packit 4b6dd7
		gdata_oauth2_authorizer_set_proxy_resolver (self,
Packit 4b6dd7
		                                            g_value_get_object (value));
Packit 4b6dd7
		break;
Packit 4b6dd7
	case PROP_REFRESH_TOKEN:
Packit 4b6dd7
		gdata_oauth2_authorizer_set_refresh_token (self,
Packit 4b6dd7
		                                           g_value_get_string (value));
Packit 4b6dd7
		break;
Packit 4b6dd7
	default:
Packit 4b6dd7
		/* We don't have any other property... */
Packit 4b6dd7
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
		break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain,
Packit 4b6dd7
                 SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* Set the authorisation header */
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* Sanity check */
Packit 4b6dd7
	g_assert ((priv->access_token == NULL) ||
Packit 4b6dd7
	          (priv->refresh_token != NULL));
Packit 4b6dd7
Packit 4b6dd7
	if (priv->access_token != NULL &&
Packit 4b6dd7
	    g_hash_table_lookup (priv->authentication_domains,
Packit 4b6dd7
	                         domain) != NULL) {
Packit 4b6dd7
		sign_message_locked (GDATA_OAUTH2_AUTHORIZER (self), message,
Packit 4b6dd7
		                     priv->access_token);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gboolean
Packit 4b6dd7
is_authorized_for_domain (GDataAuthorizer *self,
Packit 4b6dd7
                          GDataAuthorizationDomain *domain)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	gpointer result;
Packit 4b6dd7
	const gchar *access_token;
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
	access_token = priv->access_token;
Packit 4b6dd7
	result = g_hash_table_lookup (priv->authentication_domains, domain);
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* Sanity check */
Packit 4b6dd7
	g_assert (result == NULL || result == domain);
Packit 4b6dd7
Packit 4b6dd7
	return (access_token != NULL && result != NULL);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Sign the message and add the Authorization header to it containing the
Packit 4b6dd7
 * signature.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#callinganapi
Packit 4b6dd7
 *
Packit 4b6dd7
 * NOTE: This must be called with the mutex locked. */
Packit 4b6dd7
static void
Packit 4b6dd7
sign_message_locked (GDataOAuth2Authorizer *self, SoupMessage *message,
Packit 4b6dd7
                     const gchar *access_token)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupURI *message_uri;  /* unowned */
Packit 4b6dd7
	gchar *auth_header = NULL;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
	g_return_if_fail (SOUP_IS_MESSAGE (message));
Packit 4b6dd7
	g_return_if_fail (access_token != NULL && *access_token != '\0');
Packit 4b6dd7
Packit 4b6dd7
	/* Ensure that we’re using HTTPS: if not, we shouldn’t set the
Packit 4b6dd7
	 * Authorization header or we could be revealing the access
Packit 4b6dd7
	 * token to anyone snooping the connection, which would give
Packit 4b6dd7
	 * them the same rights as us on the user’s data. Generally a
Packit 4b6dd7
	 * bad thing to happen. */
Packit 4b6dd7
	message_uri = soup_message_get_uri (message);
Packit 4b6dd7
Packit 4b6dd7
	if (message_uri->scheme != SOUP_URI_SCHEME_HTTPS) {
Packit 4b6dd7
		g_warning ("Not authorizing a non-HTTPS message with the "
Packit 4b6dd7
		           "user’s OAuth 2.0 access token as the connection "
Packit 4b6dd7
		           "isn’t secure.");
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Add the authorisation header. */
Packit 4b6dd7
	auth_header = g_strdup_printf ("Bearer %s", access_token);
Packit 4b6dd7
	soup_message_headers_append (message->request_headers,
Packit 4b6dd7
	                             "Authorization", auth_header);
Packit 4b6dd7
	g_free (auth_header);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gboolean
Packit 4b6dd7
refresh_authorization (GDataAuthorizer *self, GCancellable *cancellable,
Packit 4b6dd7
                       GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	/* See http://code.google.com/apis/accounts/docs/OAuth2.html#IAMoreToken */
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	SoupMessage *message = NULL;  /* owned */
Packit 4b6dd7
	SoupURI *_uri = NULL;  /* owned */
Packit 4b6dd7
	gchar *request_body;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	GError *child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
Packit 4b6dd7
Packit 4b6dd7
	priv = GDATA_OAUTH2_AUTHORIZER (self)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* If we don’t have a refresh token, we can’t refresh the
Packit 4b6dd7
	 * authorisation. Do not set @error, as we haven’t been successfully
Packit 4b6dd7
	 * authorised previously. */
Packit 4b6dd7
	if (priv->refresh_token == NULL) {
Packit 4b6dd7
		g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Prepare the request */
Packit 4b6dd7
	request_body = soup_form_encode ("client_id", priv->client_id,
Packit 4b6dd7
	                                 "client_secret", priv->client_secret,
Packit 4b6dd7
	                                 "refresh_token", priv->refresh_token,
Packit 4b6dd7
	                                 "grant_type", "refresh_token",
Packit 4b6dd7
	                                 NULL);
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the message */
Packit 4b6dd7
	_uri = soup_uri_new ("https://accounts.google.com/o/oauth2/token");
Packit 4b6dd7
	soup_uri_set_port (_uri, _gdata_service_get_https_port ());
Packit 4b6dd7
	message = soup_message_new_from_uri (SOUP_METHOD_POST, _uri);
Packit 4b6dd7
	soup_uri_free (_uri);
Packit 4b6dd7
Packit 4b6dd7
	soup_message_set_request (message, "application/x-www-form-urlencoded",
Packit 4b6dd7
	                          SOUP_MEMORY_TAKE, request_body,
Packit 4b6dd7
	                          strlen (request_body));
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	_gdata_service_actually_send_message (priv->session, message,
Packit 4b6dd7
	                                      cancellable, error);
Packit 4b6dd7
	status = message->status_code;
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Cancelled (the error has already been set) */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_OK) {
Packit 4b6dd7
		parse_grant_error (GDATA_OAUTH2_AUTHORIZER (self),
Packit 4b6dd7
		                   status, message->reason_phrase,
Packit 4b6dd7
		                   message->response_body->data,
Packit 4b6dd7
		                   message->response_body->length,
Packit 4b6dd7
		                   error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Parse and handle the response */
Packit 4b6dd7
	parse_grant_response (GDATA_OAUTH2_AUTHORIZER (self),
Packit 4b6dd7
	                      status, message->reason_phrase,
Packit 4b6dd7
	                      message->response_body->data,
Packit 4b6dd7
	                      message->response_body->length, &child_error);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_propagate_error (error, child_error);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return TRUE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_new:
Packit 4b6dd7
 * @client_id: your application’s client ID
Packit 4b6dd7
 * @client_secret: your application’s client secret
Packit 4b6dd7
 * @redirect_uri: authorisation redirect URI
Packit 4b6dd7
 * @service_type: the #GType of a #GDataService subclass which the
Packit 4b6dd7
 * #GDataOAuth2Authorizer will be used with
Packit 4b6dd7
 *
Packit 4b6dd7
 * Creates a new #GDataOAuth2Authorizer. The @client_id must be unique for your
Packit 4b6dd7
 * application, and as registered with Google, and the @client_secret must be
Packit 4b6dd7
 * paired with it.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a new #GDataOAuth2Authorizer; unref with
Packit 4b6dd7
 * g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataOAuth2Authorizer *
Packit 4b6dd7
gdata_oauth2_authorizer_new (const gchar *client_id, const gchar *client_secret,
Packit 4b6dd7
                             const gchar *redirect_uri, GType service_type)
Packit 4b6dd7
{
Packit 4b6dd7
	GList/*<unowned GDataAuthorizationDomain>*/ *domains;  /* owned */
Packit 4b6dd7
	GDataOAuth2Authorizer *retval = NULL;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
Packit 4b6dd7
	g_return_val_if_fail (client_secret != NULL && *client_secret != '\0',
Packit 4b6dd7
	                      NULL);
Packit 4b6dd7
	g_return_val_if_fail (redirect_uri != NULL && *redirect_uri != '\0',
Packit 4b6dd7
	                      NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE),
Packit 4b6dd7
	                      NULL);
Packit 4b6dd7
Packit 4b6dd7
	domains = gdata_service_get_authorization_domains (service_type);
Packit 4b6dd7
Packit 4b6dd7
	retval = gdata_oauth2_authorizer_new_for_authorization_domains (client_id,
Packit 4b6dd7
	                                                                client_secret,
Packit 4b6dd7
	                                                                redirect_uri,
Packit 4b6dd7
	                                                                domains);
Packit 4b6dd7
	g_list_free (domains);
Packit 4b6dd7
Packit 4b6dd7
	return retval;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_new_for_authorization_domains:
Packit 4b6dd7
 * @client_id: your application’s client ID
Packit 4b6dd7
 * @client_secret: your application’s client secret
Packit 4b6dd7
 * @redirect_uri: authorisation redirect URI
Packit 4b6dd7
 * @authorization_domains: (element-type GDataAuthorizationDomain) (transfer none):
Packit 4b6dd7
 * a non-empty list of #GDataAuthorizationDomains to be authorized against by
Packit 4b6dd7
 * the #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Creates a new #GDataOAuth2Authorizer. The @client_id must be unique for your
Packit 4b6dd7
 * application, and as registered with Google, and the @client_secret must be
Packit 4b6dd7
 * paired with it.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a new #GDataOAuth2Authorizer; unref with
Packit 4b6dd7
 * g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataOAuth2Authorizer *
Packit 4b6dd7
gdata_oauth2_authorizer_new_for_authorization_domains (const gchar *client_id,
Packit 4b6dd7
                                                       const gchar *client_secret,
Packit 4b6dd7
                                                       const gchar *redirect_uri,
Packit 4b6dd7
                                                       GList *authorization_domains)
Packit 4b6dd7
{
Packit 4b6dd7
	GList *i;
Packit 4b6dd7
	GDataOAuth2Authorizer *authorizer;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
Packit 4b6dd7
	g_return_val_if_fail (client_secret != NULL && *client_secret != '\0',
Packit 4b6dd7
	                      NULL);
Packit 4b6dd7
	g_return_val_if_fail (redirect_uri != NULL && *redirect_uri != '\0',
Packit 4b6dd7
	                      NULL);
Packit 4b6dd7
	g_return_val_if_fail (authorization_domains != NULL, NULL);
Packit 4b6dd7
Packit 4b6dd7
	authorizer = GDATA_OAUTH2_AUTHORIZER (g_object_new (GDATA_TYPE_OAUTH2_AUTHORIZER,
Packit 4b6dd7
	                                                    "client-id", client_id,
Packit 4b6dd7
	                                                    "client-secret", client_secret,
Packit 4b6dd7
	                                                    "redirect-uri", redirect_uri,
Packit 4b6dd7
	                                                    NULL));
Packit 4b6dd7
Packit 4b6dd7
	/* Register all the domains with the authorizer */
Packit 4b6dd7
	for (i = authorization_domains; i != NULL; i = i->next) {
Packit 4b6dd7
		GDataAuthorizationDomain *domain;  /* unowned */
Packit 4b6dd7
Packit 4b6dd7
		g_return_val_if_fail (GDATA_IS_AUTHORIZATION_DOMAIN (i->data),
Packit 4b6dd7
		                      NULL);
Packit 4b6dd7
Packit 4b6dd7
		/* We don’t have to lock the authoriser’s mutex here as no other
Packit 4b6dd7
		 * code has seen the authoriser yet */
Packit 4b6dd7
		domain = GDATA_AUTHORIZATION_DOMAIN (i->data);
Packit 4b6dd7
		g_hash_table_insert (authorizer->priv->authentication_domains,
Packit 4b6dd7
		                     g_object_ref (domain), domain);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return authorizer;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_build_authentication_uri:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @login_hint: (nullable): optional e-mail address or sub identifier for the
Packit 4b6dd7
 * user
Packit 4b6dd7
 * @include_granted_scopes: %TRUE to enable incremental authorisation
Packit 4b6dd7
 *
Packit 4b6dd7
 * Build an authentication URI to open in the user’s web browser (or an embedded
Packit 4b6dd7
 * browser widget). This will display an authentication page from Google,
Packit 4b6dd7
 * including an authentication form and confirmation of the authorisation
Packit 4b6dd7
 * domains being requested by this #GDataAuthorizer. The user will authenticate
Packit 4b6dd7
 * in the browser, then an authorisation code will be returned via the
Packit 4b6dd7
 * #GDataOAuth2Authorizer:redirect-uri, ready to be passed to
Packit 4b6dd7
 * gdata_oauth2_authorizer_request_authorization().
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @login_hint is non-%NULL, it will be passed to the server as a hint of
Packit 4b6dd7
 * which user is attempting to authenticate, which can be used to pre-fill the
Packit 4b6dd7
 * e-mail address box in the authentication form.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @include_granted_scopes is %TRUE, the authentication request will
Packit 4b6dd7
 * automatically include all authorisation domains previously granted to this
Packit 4b6dd7
 * user/application pair, allowing for incremental authentication — asking for
Packit 4b6dd7
 * permissions as needed, rather than all in one large bundle at the first
Packit 4b6dd7
 * opportunity. If @include_granted_scopes is %FALSE, incremental authentication
Packit 4b6dd7
 * will not be enabled, and only the domains passed to the
Packit 4b6dd7
 * #GDataOAuth2Authorizer constructor will eventually be authenticated.
Packit 4b6dd7
 * See the
Packit 4b6dd7
 * <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth">reference
Packit 4b6dd7
 * documentation</ulink> for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): the authentication URI to open in a web
Packit 4b6dd7
 * browser; free with g_free()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_build_authentication_uri (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                                  const gchar *login_hint,
Packit 4b6dd7
                                                  gboolean include_granted_scopes)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	GString *uri = NULL;  /* owned */
Packit 4b6dd7
	GDataAuthorizationDomain *domain;  /* unowned */
Packit 4b6dd7
	GHashTableIter iter;
Packit 4b6dd7
	gboolean is_first = TRUE;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	/* Build and memoise the URI.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#formingtheurl
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_assert (g_hash_table_size (priv->authentication_domains) > 0);
Packit 4b6dd7
Packit 4b6dd7
	uri = g_string_new ("https://accounts.google.com/o/oauth2/auth"
Packit 4b6dd7
	                    "?response_type=code"
Packit 4b6dd7
	                    "&client_id=");
Packit 4b6dd7
	g_string_append_uri_escaped (uri, priv->client_id, NULL, TRUE);
Packit 4b6dd7
	g_string_append (uri, "&redirect_uri=");
Packit 4b6dd7
	g_string_append_uri_escaped (uri, priv->redirect_uri, NULL, TRUE);
Packit 4b6dd7
	g_string_append (uri, "&scope=");
Packit 4b6dd7
Packit 4b6dd7
	/* Add the scopes of all our domains */
Packit 4b6dd7
	g_hash_table_iter_init (&iter, priv->authentication_domains);
Packit 4b6dd7
Packit 4b6dd7
	while (g_hash_table_iter_next (&iter, (gpointer *) &domain, NULL)) {
Packit 4b6dd7
		const gchar *scope;
Packit 4b6dd7
Packit 4b6dd7
		if (!is_first) {
Packit 4b6dd7
			/* Delimiter */
Packit 4b6dd7
			g_string_append (uri, "%20");
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		scope = gdata_authorization_domain_get_scope (domain);
Packit 4b6dd7
		g_string_append_uri_escaped (uri, scope, NULL, TRUE);
Packit 4b6dd7
Packit 4b6dd7
		is_first = FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	if (login_hint != NULL && *login_hint != '\0') {
Packit 4b6dd7
		g_string_append (uri, "&login_hint=");
Packit 4b6dd7
		g_string_append_uri_escaped (uri, login_hint, NULL, TRUE);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	if (priv->locale != NULL) {
Packit 4b6dd7
		g_string_append (uri, "&hl=");
Packit 4b6dd7
		g_string_append_uri_escaped (uri, priv->locale, NULL, TRUE);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	if (include_granted_scopes) {
Packit 4b6dd7
		g_string_append (uri, "&include_granted_scopes=true");
Packit 4b6dd7
	} else {
Packit 4b6dd7
		g_string_append (uri, "&include_granted_scopes=false");
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	return g_string_free (uri, FALSE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* NOTE: This has to be thread safe, as it can be called from
Packit 4b6dd7
 * refresh_authorization() at any time.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
Packit 4b6dd7
 */
Packit 4b6dd7
static void
Packit 4b6dd7
parse_grant_response (GDataOAuth2Authorizer *self, guint status,
Packit 4b6dd7
                      const gchar *reason_phrase, const gchar *response_body,
Packit 4b6dd7
                      gssize length, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	JsonParser *parser = NULL;  /* owned */
Packit 4b6dd7
	JsonNode *root_node;  /* unowned */
Packit 4b6dd7
	JsonObject *root_object;  /* unowned */
Packit 4b6dd7
	const gchar *access_token = NULL, *refresh_token = NULL;
Packit 4b6dd7
	GError *child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the successful response */
Packit 4b6dd7
	parser = json_parser_new ();
Packit 4b6dd7
Packit 4b6dd7
	json_parser_load_from_data (parser, response_body, length,
Packit 4b6dd7
	                            &child_error);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_clear_error (&child_error);
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Extract the access token, TTL and refresh token */
Packit 4b6dd7
	root_node = json_parser_get_root (parser);
Packit 4b6dd7
Packit 4b6dd7
	if (JSON_NODE_HOLDS_OBJECT (root_node) == FALSE) {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	root_object = json_node_get_object (root_node);
Packit 4b6dd7
Packit 4b6dd7
	if (json_object_has_member (root_object, "access_token")) {
Packit 4b6dd7
		access_token = json_object_get_string_member (root_object,
Packit 4b6dd7
		                                              "access_token");
Packit 4b6dd7
	}
Packit 4b6dd7
	if (json_object_has_member (root_object, "refresh_token")) {
Packit 4b6dd7
		refresh_token = json_object_get_string_member (root_object,
Packit 4b6dd7
		                                               "refresh_token");
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Always require an access token. */
Packit 4b6dd7
	if (access_token == NULL || *access_token == '\0') {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		access_token = NULL;
Packit 4b6dd7
		refresh_token = NULL;
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Only require a refresh token if this is the first authentication.
Packit 4b6dd7
	 * See the documentation for refreshing authentication:
Packit 4b6dd7
	 * https://developers.google.com/accounts/docs/OAuth2InstalledApp#refresh
Packit 4b6dd7
	 */
Packit 4b6dd7
	if ((refresh_token == NULL || *refresh_token == '\0') &&
Packit 4b6dd7
	    priv->refresh_token == NULL) {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		access_token = NULL;
Packit 4b6dd7
		refresh_token = NULL;
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
done:
Packit 4b6dd7
	/* Postconditions. */
Packit 4b6dd7
	g_assert ((refresh_token == NULL) || (access_token != NULL));
Packit 4b6dd7
	g_assert ((child_error != NULL) == (access_token == NULL));
Packit 4b6dd7
Packit 4b6dd7
	/* Update state. */
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->access_token);
Packit 4b6dd7
	priv->access_token = g_strdup (access_token);
Packit 4b6dd7
Packit 4b6dd7
	if (refresh_token != NULL) {
Packit 4b6dd7
		g_free (priv->refresh_token);
Packit 4b6dd7
		priv->refresh_token = g_strdup (refresh_token);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_propagate_error (error, child_error);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (parser);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* NOTE: This has to be thread safe, as it can be called from
Packit 4b6dd7
 * refresh_authorization() at any time.
Packit 4b6dd7
 *
Packit 4b6dd7
 * There is no reference for this, because Google apparently don’t deem it
Packit 4b6dd7
 * necessary to document.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Example response:
Packit 4b6dd7
 *     HTTP/1.1 400 Bad Request
Packit 4b6dd7
 *     Content-Type: application/json
Packit 4b6dd7
 *
Packit 4b6dd7
 *     {
Packit 4b6dd7
 *       "error" : "invalid_grant"
Packit 4b6dd7
 *     }
Packit 4b6dd7
 */
Packit 4b6dd7
static void
Packit 4b6dd7
parse_grant_error (GDataOAuth2Authorizer *self, guint status,
Packit 4b6dd7
                   const gchar *reason_phrase, const gchar *response_body,
Packit 4b6dd7
                   gssize length, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	JsonParser *parser = NULL;  /* owned */
Packit 4b6dd7
	JsonNode *root_node;  /* unowned */
Packit 4b6dd7
	JsonObject *root_object;  /* unowned */
Packit 4b6dd7
	const gchar *error_code = NULL;
Packit 4b6dd7
	GError *child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the error response */
Packit 4b6dd7
	parser = json_parser_new ();
Packit 4b6dd7
Packit 4b6dd7
	if (response_body == NULL) {
Packit 4b6dd7
		g_clear_error (&child_error);
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	json_parser_load_from_data (parser, response_body, length,
Packit 4b6dd7
	                            &child_error);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_clear_error (&child_error);
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Extract the error code. */
Packit 4b6dd7
	root_node = json_parser_get_root (parser);
Packit 4b6dd7
Packit 4b6dd7
	if (JSON_NODE_HOLDS_OBJECT (root_node) == FALSE) {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	root_object = json_node_get_object (root_node);
Packit 4b6dd7
Packit 4b6dd7
	if (json_object_has_member (root_object, "error")) {
Packit 4b6dd7
		error_code = json_object_get_string_member (root_object,
Packit 4b6dd7
		                                            "error");
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Always require an error_code. */
Packit 4b6dd7
	if (error_code == NULL || *error_code == '\0') {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
Packit 4b6dd7
		error_code = NULL;
Packit 4b6dd7
Packit 4b6dd7
		goto done;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the error code. */
Packit 4b6dd7
	if (strcmp (error_code, "invalid_grant") == 0) {
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_FORBIDDEN,
Packit 4b6dd7
		                     _("Access was denied by the user or server."));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		/* Unknown error code. */
Packit 4b6dd7
		g_set_error_literal (&child_error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
		                     GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
		                     _("The server returned a malformed response."));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
done:
Packit 4b6dd7
	/* Postconditions. */
Packit 4b6dd7
	g_assert (child_error != NULL);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_propagate_error (error, child_error);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (parser);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_request_authorization:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @authorization_code: code returned from the authentication page
Packit 4b6dd7
 * @cancellable: (allow-none): a #GCancellable, or %NULL
Packit 4b6dd7
 * @error: return location for a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Request an authorisation code from the user’s web browser is converted to
Packit 4b6dd7
 * authorisation (access and refresh) tokens. This is the final step in the
Packit 4b6dd7
 * authentication process; once complete, the #GDataOAuth2Authorizer should be
Packit 4b6dd7
 * fully authorised for its domains.
Packit 4b6dd7
 *
Packit 4b6dd7
 * On failure, %GDATA_SERVICE_ERROR_FORBIDDEN will be returned if the user or
Packit 4b6dd7
 * server denied the authorisation request. %GDATA_SERVICE_ERROR_PROTOCOL_ERROR
Packit 4b6dd7
 * will be returned if the server didn’t follow the expected protocol.
Packit 4b6dd7
 * %G_IO_ERROR_CANCELLED will be returned if the operation was cancelled using
Packit 4b6dd7
 * @cancellable.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE on success, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_oauth2_authorizer_request_authorization (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                               const gchar *authorization_code,
Packit 4b6dd7
                                               GCancellable *cancellable,
Packit 4b6dd7
                                               GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	SoupMessage *message = NULL;  /* owned */
Packit 4b6dd7
	SoupURI *_uri = NULL;  /* owned */
Packit 4b6dd7
	gchar *request_body = NULL;  /* owned */
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	GError *child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (authorization_code != NULL &&
Packit 4b6dd7
	                      *authorization_code != '\0', FALSE);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL ||
Packit 4b6dd7
	                      G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	/* Prepare the request.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Reference: https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
Packit 4b6dd7
	 */
Packit 4b6dd7
	request_body = soup_form_encode ("client_id", priv->client_id,
Packit 4b6dd7
	                                 "client_secret", priv->client_secret,
Packit 4b6dd7
	                                 "code", authorization_code,
Packit 4b6dd7
	                                 "redirect_uri", priv->redirect_uri,
Packit 4b6dd7
	                                 "grant_type", "authorization_code",
Packit 4b6dd7
	                                 NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the message */
Packit 4b6dd7
	_uri = soup_uri_new ("https://accounts.google.com/o/oauth2/token");
Packit 4b6dd7
	soup_uri_set_port (_uri, _gdata_service_get_https_port ());
Packit 4b6dd7
	message = soup_message_new_from_uri (SOUP_METHOD_POST, _uri);
Packit 4b6dd7
	soup_uri_free (_uri);
Packit 4b6dd7
Packit 4b6dd7
	soup_message_set_request (message, "application/x-www-form-urlencoded",
Packit 4b6dd7
	                          SOUP_MEMORY_TAKE, request_body,
Packit 4b6dd7
	                          strlen (request_body));
Packit 4b6dd7
	request_body = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	_gdata_service_actually_send_message (priv->session, message,
Packit 4b6dd7
	                                      cancellable, error);
Packit 4b6dd7
	status = message->status_code;
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Cancelled (the error has already been set) */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_OK) {
Packit 4b6dd7
		parse_grant_error (self, status, message->reason_phrase,
Packit 4b6dd7
		                   message->response_body->data,
Packit 4b6dd7
		                   message->response_body->length,
Packit 4b6dd7
		                   error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Parse and handle the response */
Packit 4b6dd7
	parse_grant_response (self, status, message->reason_phrase,
Packit 4b6dd7
	                      message->response_body->data,
Packit 4b6dd7
	                      message->response_body->length, &child_error);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	if (child_error != NULL) {
Packit 4b6dd7
		g_propagate_error (error, child_error);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return TRUE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
request_authorization_thread (GTask        *task,
Packit 4b6dd7
                              gpointer      source_object,
Packit 4b6dd7
                              gpointer      task_data,
Packit 4b6dd7
                              GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2Authorizer *authorizer = GDATA_OAUTH2_AUTHORIZER (source_object);
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	const gchar *authorization_code = task_data;
Packit 4b6dd7
Packit 4b6dd7
	if (!gdata_oauth2_authorizer_request_authorization (authorizer,
Packit 4b6dd7
	                                                    authorization_code,
Packit 4b6dd7
	                                                    cancellable,
Packit 4b6dd7
	                                                    &error))
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_boolean (task, TRUE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_request_authorization_async:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @authorization_code: code returned from the authentication page
Packit 4b6dd7
 * @cancellable: (allow-none): an optional #GCancellable, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when authorization is finished
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Asynchronous version of gdata_oauth2_authorizer_request_authorization().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_oauth2_authorizer_request_authorization_async (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                                     const gchar *authorization_code,
Packit 4b6dd7
                                                     GCancellable *cancellable,
Packit 4b6dd7
                                                     GAsyncReadyCallback callback,
Packit 4b6dd7
                                                     gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
	g_return_if_fail (authorization_code != NULL &&
Packit 4b6dd7
	                  *authorization_code != '\0');
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL ||
Packit 4b6dd7
	                  G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_oauth2_authorizer_request_authorization_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_strdup (authorization_code),
Packit 4b6dd7
	                      (GDestroyNotify) g_free);
Packit 4b6dd7
	g_task_run_in_thread (task, request_authorization_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_request_authorization_finish:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous authorization operation started with
Packit 4b6dd7
 * gdata_oauth2_authorizer_request_authorization_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE if authorization was successful, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_oauth2_authorizer_request_authorization_finish (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                                      GAsyncResult *async_result,
Packit 4b6dd7
                                                      GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_oauth2_authorizer_request_authorization_async), FALSE);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_boolean (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_client_id:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the authorizer's client ID, #GDataOAuth2Authorizer:client-id, as
Packit 4b6dd7
 * specified on constructing the #GDataOAuth2Authorizer.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the authorizer's client ID
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_get_client_id (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
	return self->priv->client_id;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_redirect_uri:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the authorizer’s redirect URI, #GDataOAuth2Authorizer:redirect-uri,
Packit 4b6dd7
 * as specified on constructing the #GDataOAuth2Authorizer.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the authorizer’s redirect URI
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_get_redirect_uri (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
	return self->priv->redirect_uri;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_client_secret:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the authorizer's client secret, #GDataOAuth2Authorizer:client-secret,
Packit 4b6dd7
 * as specified on constructing the #GDataOAuth2Authorizer.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the authorizer's client secret
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_get_client_secret (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
	return self->priv->client_secret;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_dup_refresh_token:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the authorizer's refresh token, #GDataOAuth2Authorizer:refresh-token,
Packit 4b6dd7
 * as set by client code previously on the #GDataOAuth2Authorizer, or as
Packit 4b6dd7
 * returned by the most recent authentication operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): the authorizer's refresh token
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.2
Packit 4b6dd7
 */
Packit 4b6dd7
gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_dup_refresh_token (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
	gchar *refresh_token;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
	refresh_token = g_strdup (priv->refresh_token);
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	return refresh_token;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_set_refresh_token:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @refresh_token: (nullable): the new refresh token, or %NULL to clear
Packit 4b6dd7
 *   authorization
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the authorizer's refresh token, #GDataOAuth2Authorizer:refresh-token.
Packit 4b6dd7
 * This is used to periodically refresh the access token. Set it to %NULL to
Packit 4b6dd7
 * clear the current authentication from the authorizer.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.2
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_oauth2_authorizer_set_refresh_token (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                           const gchar *refresh_token)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataOAuth2AuthorizerPrivate *priv;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
Packit 4b6dd7
	priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_lock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	if (g_strcmp0 (priv->refresh_token, refresh_token) == 0) {
Packit 4b6dd7
		g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Clear the access token; if the refresh token has changed, it can
Packit 4b6dd7
	 * no longer be valid, and we must avoid the situation:
Packit 4b6dd7
	 *    (access_token != NULL) && (refresh_token == NULL) */
Packit 4b6dd7
	g_free (priv->access_token);
Packit 4b6dd7
	priv->access_token = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Update the refresh token. */
Packit 4b6dd7
	g_free (priv->refresh_token);
Packit 4b6dd7
	priv->refresh_token = g_strdup (refresh_token);
Packit 4b6dd7
Packit 4b6dd7
	g_mutex_unlock (&priv->mutex);
Packit 4b6dd7
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "refresh-token");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_locale:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the locale currently being used for network requests, or %NULL if the
Packit 4b6dd7
 * locale is the default.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (allow-none): the current locale
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_oauth2_authorizer_get_locale (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
	return self->priv->locale;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_set_locale:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @locale: (allow-none): the new locale in UNIX locale format, or %NULL for the
Packit 4b6dd7
 * default locale
Packit 4b6dd7
 *
Packit 4b6dd7
 * Set the locale used for network requests to @locale, given in standard UNIX
Packit 4b6dd7
 * locale format. See #GDataOAuth2Authorizer:locale for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that while it’s possible to change the locale after sending network
Packit 4b6dd7
 * requests (i.e. calling gdata_oauth2_authorizer_build_authentication_uri() for
Packit 4b6dd7
 * the first time), it is unsupported, as the server-side software may behave
Packit 4b6dd7
 * unexpectedly. The only supported use of this method is after creation of the
Packit 4b6dd7
 * authorizer, but before any network requests are made.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_oauth2_authorizer_set_locale (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                    const gchar *locale)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
Packit 4b6dd7
	if (g_strcmp0 (locale, self->priv->locale) == 0) {
Packit 4b6dd7
		/* Already has this value */
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (self->priv->locale);
Packit 4b6dd7
	self->priv->locale = g_strdup (locale);
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "locale");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_object_notify (self, "timeout");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_timeout:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataOAuth2Authorizer:timeout property; the network timeout, in
Packit 4b6dd7
 * seconds.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the timeout, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_oauth2_authorizer_get_timeout (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	guint timeout;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), 0);
Packit 4b6dd7
Packit 4b6dd7
	g_object_get (self->priv->session,
Packit 4b6dd7
	              SOUP_SESSION_TIMEOUT, &timeout,
Packit 4b6dd7
	              NULL);
Packit 4b6dd7
Packit 4b6dd7
	return timeout;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_set_timeout:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @timeout: the timeout, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the #GDataOAuth2Authorizer:timeout property; the network timeout, in
Packit 4b6dd7
 * seconds.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @timeout is 0, network operations will never
Packit 4b6dd7
 * time out.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_oauth2_authorizer_set_timeout (GDataOAuth2Authorizer *self, guint timeout)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
Packit 4b6dd7
	if (timeout == gdata_oauth2_authorizer_get_timeout (self)) {
Packit 4b6dd7
		return;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_set (self->priv->session, SOUP_SESSION_TIMEOUT, timeout, NULL);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_get_proxy_resolver:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GProxyResolver on the #GDataOAuth2Authorizer's #SoupSession.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none) (allow-none): a #GProxyResolver, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
GProxyResolver *
Packit 4b6dd7
gdata_oauth2_authorizer_get_proxy_resolver (GDataOAuth2Authorizer *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return self->priv->proxy_resolver;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_oauth2_authorizer_set_proxy_resolver:
Packit 4b6dd7
 * @self: a #GDataOAuth2Authorizer
Packit 4b6dd7
 * @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the #GProxyResolver on the #SoupSession used internally by the given
Packit 4b6dd7
 * #GDataOAuth2Authorizer.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.17.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_oauth2_authorizer_set_proxy_resolver (GDataOAuth2Authorizer *self,
Packit 4b6dd7
                                            GProxyResolver *proxy_resolver)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_OAUTH2_AUTHORIZER (self));
Packit 4b6dd7
	g_return_if_fail (proxy_resolver == NULL ||
Packit 4b6dd7
	                  G_IS_PROXY_RESOLVER (proxy_resolver));
Packit 4b6dd7
Packit 4b6dd7
	if (proxy_resolver != NULL) {
Packit 4b6dd7
		g_object_ref (proxy_resolver);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_clear_object (&self->priv->proxy_resolver);
Packit 4b6dd7
	self->priv->proxy_resolver = proxy_resolver;
Packit 4b6dd7
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "proxy-resolver");
Packit 4b6dd7
}