|
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 |
}
|