summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <pwithnall@endlessos.org>2021-06-17 13:55:14 +0100
committerPhilip Withnall <pwithnall@endlessos.org>2021-06-17 14:56:11 +0100
commit8eecbd9012986dce266eacdb9d280d9df1933147 (patch)
tree394a98fdb28835f91e0132d206bfa95c72a5f293
parentb0460823bfd19fb46dee9982e221ca2eeba3bc4a (diff)
downloadlibgdata-8eecbd9012986dce266eacdb9d280d9df1933147.tar.gz
core: Drop ClientLogin authorizer
It’s been deprecated by Google for a number of years, and is completely unsupported in all of their newer REST APIs. Its main flaws are that it requires the user’s password to be passed to potentially untrusted code (normalising the user typing their password into any old text entry), and that it can’t support the interactive authentication required for 2FA. Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
-rw-r--r--demos/scrapbook/scrapbook.c102
-rw-r--r--demos/scrapbook/scrapbook.h11
-rw-r--r--docs/reference/gdata-docs.xml1
-rw-r--r--docs/reference/gdata-sections.txt.in34
-rw-r--r--gdata/gdata-authorizer.c4
-rw-r--r--gdata/gdata-client-login-authorizer.c1343
-rw-r--r--gdata/gdata-client-login-authorizer.h141
-rw-r--r--gdata/gdata-core.symbols17
-rw-r--r--gdata/gdata-goa-authorizer.c2
-rw-r--r--gdata/gdata-service.c1
-rw-r--r--gdata/gdata.h1
-rw-r--r--gdata/meson.build2
-rw-r--r--gdata/services/picasaweb/gdata-picasaweb-service.c7
-rw-r--r--gdata/symbol.map17
-rw-r--r--po/POTFILES.in1
15 files changed, 62 insertions, 1622 deletions
diff --git a/demos/scrapbook/scrapbook.c b/demos/scrapbook/scrapbook.c
index d909ab05..9e330cb8 100644
--- a/demos/scrapbook/scrapbook.c
+++ b/demos/scrapbook/scrapbook.c
@@ -425,26 +425,62 @@ start_new_youtube_search (GtkWidget *widget, ScrapData *first) /* *first is a po
/* everything else is implemented somewhere else */
}
-
-static void
-properties_set (GtkWidget *widget, ScrapProps *self)
+static GDataAuthorizer *
+create_authorizer (GError **error)
{
- GDataClientLoginAuthorizer *authorizer;
+ GDataOAuth2Authorizer *authorizer = NULL; /* owned */
GList *domains = NULL; /* list of GDataAuthorizationDomains */
- GError *error = NULL;
-
- /* Get the username and password to use */
- self->main_data->username = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->username_entry)));
- self->main_data->password = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->password_entry)));
+ gchar *uri = NULL;
+ gchar code[100];
+ GError *child_error = NULL;
/* Domains we need to be authorised for */
domains = g_list_prepend (domains, gdata_youtube_service_get_primary_authorization_domain ());
domains = g_list_prepend (domains, gdata_picasaweb_service_get_primary_authorization_domain ());
- /* Authenticate */
- authorizer = gdata_client_login_authorizer_new_for_authorization_domains (CLIENT_ID, domains);
+ /* Go through the interactive OAuth dance. */
+ authorizer = gdata_oauth2_authorizer_new_for_authorization_domains (CLIENT_ID, CLIENT_SECRET,
+ REDIRECT_URI,
+ domains);
+
+ /* Get an authentication URI */
+ uri = gdata_oauth2_authorizer_build_authentication_uri (authorizer,
+ NULL, FALSE);
+
+ /* Wait for the user to retrieve and enter the verifier. */
+ g_print ("Please navigate to the following URI and grant access:\n"
+ " %s\n", uri);
+ g_print ("Enter verifier (EOF to abort): ");
+
+ g_free (uri);
+
+ if (scanf ("%100s", code) != 1) {
+ /* User chose to abort. */
+ g_print ("\n");
+ g_clear_object (&authorizer);
+ return NULL;
+ }
+
+ /* Authorise the token. */
+ gdata_oauth2_authorizer_request_authorization (authorizer, code, NULL,
+ &child_error);
+
+ if (child_error != NULL) {
+ g_propagate_error (error, child_error);
+ g_clear_object (&authorizer);
+ return NULL;
+ }
+
+ return GDATA_AUTHORIZER (authorizer);
+}
+
+static void
+properties_set (GtkWidget *widget, ScrapProps *self)
+{
+ GDataAuthorizer *authorizer;
+ GError *error = NULL;
- gdata_client_login_authorizer_authenticate (authorizer, self->main_data->username, self->main_data->password, NULL, &error);
+ authorizer = create_authorizer (&error);
if (error != NULL) { /* we show this to the user in case they mistyped their password */
GtkWidget *label;
@@ -458,8 +494,8 @@ properties_set (GtkWidget *widget, ScrapProps *self)
g_error_free (error);
}
- gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->youtube_service), GDATA_AUTHORIZER (authorizer));
- gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->picasaweb_service), GDATA_AUTHORIZER (authorizer));
+ gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->youtube_service), authorizer);
+ gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->picasaweb_service), authorizer);
gtk_widget_destroy (self->window);
g_object_unref (authorizer);
@@ -469,7 +505,7 @@ static void
properties_show (GtkWidget *widget, ScrapData *first)
{
ScrapProps *self;
- GtkWidget *label, *button, *box2;
+ GtkWidget *button;
self = g_slice_new (struct _ScrapProps);
self->main_data = first;
@@ -479,42 +515,6 @@ properties_show (GtkWidget *widget, ScrapData *first)
self->box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
- /* Username/Password labels box */
- box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
-
- label = gtk_label_new ("Username");
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0);
-
- label = gtk_label_new ("Password");
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0);
-
- gtk_widget_show (box2);
- gtk_box_pack_start (GTK_BOX (self->box1), box2, FALSE, FALSE, 0);
-
- /* Username/Password entries box */
- box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
- self->username_entry = gtk_entry_new ();
-
- if (self->main_data->username != NULL)
- gtk_entry_set_text (GTK_ENTRY(self->username_entry), self->main_data->username);
-
- gtk_widget_show (self->username_entry);
- gtk_box_pack_start (GTK_BOX (box2), self->username_entry, TRUE, TRUE, 0);
-
- self->password_entry = gtk_entry_new ();
- gtk_entry_set_visibility (GTK_ENTRY (self->password_entry), FALSE);
-
- if (self->main_data->password != NULL)
- gtk_entry_set_text (GTK_ENTRY(self->password_entry), self->main_data->password);
-
- gtk_widget_show (self->password_entry);
- gtk_box_pack_start (GTK_BOX (box2), self->password_entry, TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (self->box1), box2, FALSE, FALSE, 0);
- gtk_widget_show (box2);
-
/* OK button */
button = gtk_button_new_with_label ("_OK");
g_signal_connect (button, "clicked", (GCallback) properties_set, self);
diff --git a/demos/scrapbook/scrapbook.h b/demos/scrapbook/scrapbook.h
index 802e7369..032cb770 100644
--- a/demos/scrapbook/scrapbook.h
+++ b/demos/scrapbook/scrapbook.h
@@ -23,8 +23,12 @@
#include <gdata/gdata.h>
#include <glib.h>
#include <glib-object.h>
-#define DEVELOPER_KEY "AI39si5MkSF-0bzTmP5WETk1D-Z7inHaQJzX13PeG_5Uzeu8mz3vo40cFoqnxjejB-UqzYFrqzOSlsqJvHuPNEGqdycqnPo30A"
-#define CLIENT_ID "ytapi-GNOME-libgdata-444fubtt-0"
+
+#define DEVELOPER_KEY "AI39si7Me3Q7zYs6hmkFvpRBD2nrkVjYYsUO5lh_3HdOkGRc9g6Z4nzxZatk_aAo2EsA21k7vrda0OO6oFg2rnhMedZXPyXoEw"
+#define CLIENT_ID "352818697630-nqu2cmt5quqd6lr17ouoqmb684u84l1f.apps.googleusercontent.com"
+#define CLIENT_SECRET "-fA4pHQJxR3zJ-FyAMPQsikg"
+#define REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
+
#define THUMBNAIL_WIDTH 180
#define MAX_RESULTS 10
@@ -73,8 +77,6 @@ typedef struct _ScrapData {
ScrapPicSearch *pic_search;
ScrapPUpload *p_upload;
gint max_rows;
- gchar *username;
- gchar *password;
GtkListStore *lStore;
GDataYouTubeService *youtube_service;
@@ -131,7 +133,6 @@ struct _ScrapPSearch { /* for finding albums */
typedef struct _ScrapProps {
GtkWidget *window;
GtkWidget *box1;
- GtkWidget *username_entry, *password_entry;
ScrapData *main_data;
} ScrapProps;
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index eb279c84..8a533b07 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -54,7 +54,6 @@
<title>Authentication/Authorization API</title>
<xi:include href="xml/gdata-authorizer.xml"/>
<xi:include href="xml/gdata-authorization-domain.xml"/>
- <xi:include href="xml/gdata-client-login-authorizer.xml"/>
<xi:include href="xml/gdata-goa-authorizer.xml"><xi:fallback /></xi:include>
<xi:include href="xml/gdata-oauth2-authorizer.xml"/>
</chapter>
diff --git a/docs/reference/gdata-sections.txt.in b/docs/reference/gdata-sections.txt.in
index 135fe403..1d0ace2d 100644
--- a/docs/reference/gdata-sections.txt.in
+++ b/docs/reference/gdata-sections.txt.in
@@ -2139,40 +2139,6 @@ GDATA_IS_AUTHORIZATION_DOMAIN_CLASS
GDataAuthorizationDomainPrivate
</SECTION>
-<SECTION>
-<FILE>gdata-client-login-authorizer</FILE>
-<TITLE>GDataClientLoginAuthorizer</TITLE>
-GDataClientLoginAuthorizer
-GDataClientLoginAuthorizerClass
-GDataClientLoginAuthorizerError
-gdata_client_login_authorizer_new
-gdata_client_login_authorizer_new_for_authorization_domains
-gdata_client_login_authorizer_authenticate
-gdata_client_login_authorizer_authenticate_async
-gdata_client_login_authorizer_authenticate_finish
-gdata_client_login_authorizer_get_client_id
-gdata_client_login_authorizer_get_username
-gdata_client_login_authorizer_get_password
-gdata_client_login_authorizer_get_proxy_uri
-gdata_client_login_authorizer_set_proxy_uri
-gdata_client_login_authorizer_get_proxy_resolver
-gdata_client_login_authorizer_set_proxy_resolver
-gdata_client_login_authorizer_get_timeout
-gdata_client_login_authorizer_set_timeout
-<SUBSECTION Standard>
-GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER
-GDATA_CLIENT_LOGIN_AUTHORIZER
-GDATA_CLIENT_LOGIN_AUTHORIZER_CLASS
-GDATA_IS_CLIENT_LOGIN_AUTHORIZER
-GDATA_IS_CLIENT_LOGIN_AUTHORIZER_CLASS
-GDATA_CLIENT_LOGIN_AUTHORIZER_GET_CLASS
-gdata_client_login_authorizer_get_type
-GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR
-gdata_client_login_authorizer_error_quark
-<SUBSECTION Private>
-GDataClientLoginAuthorizerPrivate
-</SECTION>
-
@COMMENT@<SECTION>
@COMMENT@<FILE>gdata-goa-authorizer</FILE>
@COMMENT@<TITLE>GDataGoaAuthorizer</TITLE>
diff --git a/gdata/gdata-authorizer.c b/gdata/gdata-authorizer.c
index fbfff764..b51c8950 100644
--- a/gdata/gdata-authorizer.c
+++ b/gdata/gdata-authorizer.c
@@ -24,7 +24,7 @@
* @include: gdata/gdata-authorizer.h
*
* The #GDataAuthorizer interface provides a uniform way to implement authentication and authorization processes for use by #GDataServices.
- * Client code will construct a new #GDataAuthorizer instance of their choosing, such as #GDataClientLoginAuthorizer or #GDataOAuth2Authorizer, for
+ * Client code will construct a new #GDataAuthorizer instance of their choosing, such as #GDataOAuth2Authorizer, for
* the #GDataServices which will be used by the client, then authenticates and authorizes with the #GDataAuthorizer instead of the
* #GDataService. The #GDataService then uses the #GDataAuthorizer to authorize individual network requests using whatever authorization token was
* returned to the #GDataAuthorizer by the Google Accounts service.
@@ -38,8 +38,6 @@
* #GDataAuthorizer implementations are provided for some of the standard authorization processes supported by Google for installed applications, as
* listed in their <ulink type="http" url="http://code.google.com/apis/accounts/docs/GettingStarted.html">online documentation</ulink>:
* <itemizedlist>
- * <listitem>#GDataClientLoginAuthorizer for
- * <ulink type="http" url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html">ClientLogin</ulink> (deprecated)</listitem>
* <listitem>#GDataOAuth2Authorizer for
* <ulink type="http" url="https://developers.google.com/accounts/docs/OAuth2InstalledApp">OAuth 2.0</ulink> (preferred)</listitem>
* </itemizedlist>
diff --git a/gdata/gdata-client-login-authorizer.c b/gdata/gdata-client-login-authorizer.c
deleted file mode 100644
index 295f5106..00000000
--- a/gdata/gdata-client-login-authorizer.c
+++ /dev/null
@@ -1,1343 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/*
- * GData Client
- * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
- *
- * GData Client is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * GData Client is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * SECTION:gdata-client-login-authorizer
- * @short_description: GData ClientLogin authorization interface
- * @stability: Stable
- * @include: gdata/gdata-client-login-authorizer.h
- *
- * #GDataClientLoginAuthorizer provides an implementation of the #GDataAuthorizer interface for authentication and authorization using the deprecated
- * <ulink type="http" url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html">ClientLogin</ulink> process.
- *
- * As noted, the ClientLogin process is being deprecated in favour of OAuth 2.0. This API is not (yet) deprecated, however. One of the main reasons
- * for ClientLogin being deprecated is that it cannot support two-factor authentication as now available to Google Accounts. Any account which has
- * two-factor authentication enabled has to use a service-specific one-time password instead if a client is authenticating with
- * #GDataClientLoginAuthorizer. More documentation about this is
- * <ulink type="http" url="http://www.google.com/support/accounts/bin/static.py?page=guide.cs&guide=1056283&topic=1056286">available online</ulink>.
- * Note that newer services cannot be authenticated against using ClientLogin,
- * and a #GDataOAuth2Authorizer must be used instead.
- *
- * The ClientLogin process is a simple one whereby the user's Google Account username and password are sent over an HTTPS connection to the Google
- * Account servers (when gdata_client_login_authorizer_authenticate() is called), which return an authorization token. This token is then attached to
- * all future requests to the online service. A slight complication is that the Google Accounts service may return a CAPTCHA challenge instead of
- * immediately returning an authorization token. In this case, the #GDataClientLoginAuthorizer::captcha-challenge signal will be emitted, and the
- * user's response to the CAPTCHA should be returned by the handler.
- *
- * ClientLogin does not natively support authorization against multiple authorization domains concurrently with a single authorization token, so it
- * has to be simulated by maintaining multiple authorization tokens if multiple authorization domains are used. This means that proportionally more
- * network requests are made when gdata_client_login_authorizer_authenticate() is called, which will be proportionally slower. Handling of the
- * multiple authorization tokens is otherwise transparent to the client.
- *
- * Each authorization token is long lived, so reauthorization is rarely necessary with #GDataClientLoginAuthorizer. Consequently, refreshing
- * authorization using gdata_authorizer_refresh_authorization() is not supported by #GDataClientLoginAuthorizer, and will immediately return %FALSE
- * with no error set.
- *
- * <example>
- * <title>Authenticating Asynchronously Using ClientLogin</title>
- * <programlisting>
- * GDataSomeService *service;
- * GDataClientLoginAuthorizer *authorizer;
- *
- * /<!-- -->* Create an authorizer and authenticate and authorize the service we're using, asynchronously. *<!-- -->/
- * authorizer = gdata_client_login_authorizer_new ("companyName-applicationName-versionID", GDATA_TYPE_SOME_SERVICE);
- * gdata_client_login_authorizer_authenticate_async (authorizer, username, password, cancellable,
- * (GAsyncReadyCallback) authenticate_cb, user_data);
- *
- * /<!-- -->* Create a service object and link it with the authorizer *<!-- -->/
- * service = gdata_some_service_new (GDATA_AUTHORIZER (authorizer));
- *
- * static void
- * authenticate_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, gpointer user_data)
- * {
- * GError *error = NULL;
- *
- * if (gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error) == FALSE) {
- * /<!-- -->* Notify the user of all errors except cancellation errors *<!-- -->/
- * if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- * g_error ("Authentication failed: %s", error->message);
- * }
- * g_error_free (error);
- * return;
- * }
- *
- * /<!-- -->* (The client is now authenticated and authorized against the service.
- * * It can now proceed to execute queries on the service object which require the user to be authenticated.) *<!-- -->/
- * }
- *
- * g_object_unref (service);
- * g_object_unref (authorizer);
- * </programlisting>
- * </example>
- *
- * Since: 0.9.0
- */
-
-#include <config.h>
-#include <glib.h>
-#include <glib/gi18n-lib.h>
-#include <string.h>
-
-#include "gdata-service.h"
-#include "gdata-private.h"
-#include "gdata-marshal.h"
-#include "gdata-client-login-authorizer.h"
-
-/* The default e-mail domain to use for usernames */
-#define EMAIL_DOMAIN "gmail.com"
-
-GQuark
-gdata_client_login_authorizer_error_quark (void)
-{
- return g_quark_from_static_string ("gdata-client-login-authorizer-error-quark");
-}
-
-static void authorizer_init (GDataAuthorizerInterface *iface);
-static void dispose (GObject *object);
-static void finalize (GObject *object);
-static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
-static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
-
-static void process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMessage *message);
-static gboolean is_authorized_for_domain (GDataAuthorizer *self, GDataAuthorizationDomain *domain);
-
-static void notify_proxy_uri_cb (GObject *gobject, GParamSpec *pspec, GDataClientLoginAuthorizer *self);
-static void notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self);
-
-static SoupURI *_get_proxy_uri (GDataClientLoginAuthorizer *self);
-static void _set_proxy_uri (GDataClientLoginAuthorizer *self, SoupURI *proxy_uri);
-
-struct _GDataClientLoginAuthorizerPrivate {
- SoupSession *session;
- SoupURI *proxy_uri; /* cached version only set if gdata_client_login_authorizer_get_proxy_uri() is called */
- GProxyResolver *proxy_resolver;
-
- gchar *client_id;
-
- /* Mutex for username, password and auth_tokens. It has to be recursive as the top-level authentication functions need to hold a lock on
- * auth_tokens while looping over it, but lower-level functions also need to modify auth_tokens to add the auth_tokens themselves once they're
- * returned by the online service. */
- GRecMutex mutex;
-
- gchar *username;
- GDataSecureString password; /* must be allocated by _gdata_service_secure_strdup() */
-
- /* Mapping from GDataAuthorizationDomain to string? auth_token; auth_token is NULL for domains which aren't authorised at the moment */
- GHashTable *auth_tokens;
-};
-
-enum {
- PROP_CLIENT_ID = 1,
- PROP_USERNAME,
- PROP_PASSWORD,
- PROP_PROXY_URI,
- PROP_TIMEOUT,
- PROP_PROXY_RESOLVER,
-};
-
-enum {
- SIGNAL_CAPTCHA_CHALLENGE,
- LAST_SIGNAL
-};
-
-static guint authorizer_signals[LAST_SIGNAL] = { 0, };
-
-G_DEFINE_TYPE_WITH_CODE (GDataClientLoginAuthorizer, gdata_client_login_authorizer, G_TYPE_OBJECT,
- G_ADD_PRIVATE (GDataClientLoginAuthorizer)
- G_IMPLEMENT_INTERFACE (GDATA_TYPE_AUTHORIZER, authorizer_init))
-
-static void
-gdata_client_login_authorizer_class_init (GDataClientLoginAuthorizerClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->get_property = get_property;
- gobject_class->set_property = set_property;
- gobject_class->dispose = dispose;
- gobject_class->finalize = finalize;
-
- /**
- * GDataClientLoginAuthorizer:client-id:
- *
- * A client ID for your application (see the
- * <ulink url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request" type="http">reference documentation</ulink>).
- *
- * It is recommended that the ID is of the form <literal><replaceable>company name</replaceable>-<replaceable>application name</replaceable>-
- * <replaceable>version ID</replaceable></literal>.
- *
- * Since: 0.9.0
- */
- g_object_class_install_property (gobject_class, PROP_CLIENT_ID,
- g_param_spec_string ("client-id",
- "Client ID", "A client ID for your application.",
- NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer:username:
- *
- * The user's Google username for authentication. This will always be a full e-mail address.
- *
- * This will only be set after authentication using gdata_client_login_authorizer_authenticate() is completed successfully. It will
- * then be set to the username passed to gdata_client_login_authorizer_authenticate(), and a #GObject::notify signal will be emitted. If
- * authentication fails, it will be set to %NULL.
- *
- * Since: 0.9.0
- */
- g_object_class_install_property (gobject_class, PROP_USERNAME,
- g_param_spec_string ("username",
- "Username", "The user's Google username for authentication.",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer:password:
- *
- * The user's account password for authentication.
- *
- * This will only be set after authentication using gdata_client_login_authorizer_authenticate() is completed successfully. It will
- * then be set to the password passed to gdata_client_login_authorizer_authenticate(), and a #GObject::notify signal will be emitted. If
- * authentication fails, it will be set to %NULL.
- *
- * If libgdata is compiled with libgcr support, the password will be stored in non-pageable memory. However, if it is retrieved
- * using g_object_get() (or related functions) it will be copied to non-pageable memory and could end up being written to disk. Accessing
- * the password using gdata_client_login_authorizer_get_password() will not perform any copies, and so maintains privacy.
- *
- * Since: 0.9.0
- */
- g_object_class_install_property (gobject_class, PROP_PASSWORD,
- g_param_spec_string ("password",
- "Password", "The user's account password for authentication.",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer:proxy-uri:
- *
- * The proxy URI used internally for all network requests.
- *
- * Since: 0.9.0
- * Deprecated: 0.15.0: Use #GDataClientLoginAuthorizer:proxy-resolver instead, which gives more flexibility over the proxy used.
- */
- g_object_class_install_property (gobject_class, PROP_PROXY_URI,
- g_param_spec_boxed ("proxy-uri",
- "Proxy URI", "The proxy URI used internally for all network requests.",
- SOUP_TYPE_URI,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer:timeout:
- *
- * A timeout, in seconds, for network operations. If the timeout is exceeded, the operation will be cancelled and
- * %GDATA_SERVICE_ERROR_NETWORK_ERROR will be returned.
- *
- * If the timeout is <code class="literal">0</code>, operations will never time out.
- *
- * Since: 0.9.0
- */
- g_object_class_install_property (gobject_class, PROP_TIMEOUT,
- g_param_spec_uint ("timeout",
- "Timeout", "A timeout, in seconds, for network operations.",
- 0, G_MAXUINT, 0,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer:proxy-resolver:
- *
- * The #GProxyResolver used to determine a proxy URI. Setting this will clear the #GDataClientLoginAuthorizer:proxy-uri property.
- *
- * Since: 0.15.0
- */
- g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
- g_param_spec_object ("proxy-resolver",
- "Proxy Resolver", "A GProxyResolver used to determine a proxy URI.",
- G_TYPE_PROXY_RESOLVER,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GDataClientLoginAuthorizer::captcha-challenge:
- * @authorizer: the #GDataClientLoginAuthorizer which received the challenge
- * @uri: the URI of the CAPTCHA image to be used
- *
- * The #GDataClientLoginAuthorizer::captcha-challenge signal is emitted during the authentication process if the authorizer requires a CAPTCHA
- * to be completed. The URI of a CAPTCHA image is given, and the program should display this to the user, and return their response (the text
- * displayed in the image). There is no timeout imposed by the library for the response.
- *
- * Return value: a newly allocated string containing the text in the CAPTCHA image
- *
- * Since: 0.9.0
- */
- authorizer_signals[SIGNAL_CAPTCHA_CHALLENGE] = g_signal_new ("captcha-challenge",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- gdata_marshal_STRING__OBJECT_STRING,
- G_TYPE_STRING, 1, G_TYPE_STRING);
-}
-
-static void
-authorizer_init (GDataAuthorizerInterface *iface)
-{
- iface->process_request = process_request;
- iface->is_authorized_for_domain = is_authorized_for_domain;
-}
-
-static void
-gdata_client_login_authorizer_init (GDataClientLoginAuthorizer *self)
-{
- self->priv = gdata_client_login_authorizer_get_instance_private (self);
-
- /* Set up the authentication mutex */
- g_rec_mutex_init (&(self->priv->mutex));
- self->priv->auth_tokens = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) _gdata_service_secure_strfree);
-
- /* Set up the session */
- self->priv->session = _gdata_service_build_session ();
-
- /* Proxy the SoupSession's proxy-uri and timeout properties */
- g_signal_connect (self->priv->session, "notify::proxy-uri", (GCallback) notify_proxy_uri_cb, self);
- g_signal_connect (self->priv->session, "notify::timeout", (GCallback) notify_timeout_cb, self);
-
- /* Keep our GProxyResolver synchronized with SoupSession's. */
- g_object_bind_property (self->priv->session, "proxy-resolver", self, "proxy-resolver", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
-}
-
-static void
-dispose (GObject *object)
-{
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (object)->priv;
-
- if (priv->session != NULL) {
- g_object_unref (priv->session);
- }
- priv->session = NULL;
-
- g_clear_object (&priv->proxy_resolver);
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS (gdata_client_login_authorizer_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (object)->priv;
-
- g_free (priv->username);
- _gdata_service_secure_strfree (priv->password);
- g_free (priv->client_id);
- g_hash_table_destroy (priv->auth_tokens);
- g_rec_mutex_clear (&(priv->mutex));
-
- if (priv->proxy_uri != NULL) {
- soup_uri_free (priv->proxy_uri);
- }
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS (gdata_client_login_authorizer_parent_class)->finalize (object);
-}
-
-static void
-get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
-{
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (object)->priv;
-
- switch (property_id) {
- case PROP_CLIENT_ID:
- g_value_set_string (value, priv->client_id);
- break;
- case PROP_USERNAME:
- g_rec_mutex_lock (&(priv->mutex));
- g_value_set_string (value, priv->username);
- g_rec_mutex_unlock (&(priv->mutex));
- break;
- case PROP_PASSWORD:
- /* NOTE: This takes a pageable copy of non-pageable memory and thus could result in the password hitting disk. */
- g_rec_mutex_lock (&(priv->mutex));
- g_value_set_string (value, priv->password);
- g_rec_mutex_unlock (&(priv->mutex));
- break;
- case PROP_PROXY_URI:
- g_value_set_boxed (value, _get_proxy_uri (GDATA_CLIENT_LOGIN_AUTHORIZER (object)));
- break;
- case PROP_TIMEOUT:
- g_value_set_uint (value, gdata_client_login_authorizer_get_timeout (GDATA_CLIENT_LOGIN_AUTHORIZER (object)));
- break;
- case PROP_PROXY_RESOLVER:
- g_value_set_object (value, gdata_client_login_authorizer_get_proxy_resolver (GDATA_CLIENT_LOGIN_AUTHORIZER (object)));
- break;
- default:
- /* We don't have any other property... */
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
-{
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (object)->priv;
-
- switch (property_id) {
- case PROP_CLIENT_ID:
- priv->client_id = g_value_dup_string (value);
- break;
- case PROP_PROXY_URI:
- _set_proxy_uri (GDATA_CLIENT_LOGIN_AUTHORIZER (object), g_value_get_boxed (value));
- break;
- case PROP_TIMEOUT:
- gdata_client_login_authorizer_set_timeout (GDATA_CLIENT_LOGIN_AUTHORIZER (object), g_value_get_uint (value));
- break;
- case PROP_PROXY_RESOLVER:
- gdata_client_login_authorizer_set_proxy_resolver (GDATA_CLIENT_LOGIN_AUTHORIZER (object), g_value_get_object (value));
- break;
- default:
- /* We don't have any other property... */
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMessage *message)
-{
- GDataConstSecureString auth_token; /* privacy sensitive */
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (self)->priv;
-
- /* If the domain's NULL, return immediately */
- if (domain == NULL) {
- return;
- }
-
- /* Set the authorisation header */
- g_rec_mutex_lock (&(priv->mutex));
-
- auth_token = (GDataConstSecureString) g_hash_table_lookup (priv->auth_tokens, domain);
-
- if (auth_token != NULL) {
- /* Ensure that we're using HTTPS: if not, we shouldn't set the Authorization header or we could be revealing the auth token to
- * anyone snooping the connection, which would give them the same rights as us on the user's data. Generally a bad thing to happen. */
- if (soup_message_get_uri (message)->scheme != SOUP_URI_SCHEME_HTTPS) {
- g_warning ("Not authorizing a non-HTTPS message with the user's ClientLogin auth token as the connection isn't secure.");
- } else {
- /* Ideally, authorisation_header would be allocated in non-pageable memory. However, it's copied by
- * soup_message_headers_replace() immediately anyway, so there's not much point. However, we do ensure we zero it out before
- * freeing. */
- gchar *authorisation_header = g_strdup_printf ("GoogleLogin auth=%s", auth_token);
- soup_message_headers_replace (message->request_headers, "Authorization", authorisation_header);
- memset (authorisation_header, 0, strlen (authorisation_header));
- g_free (authorisation_header);
- }
- }
-
- g_rec_mutex_unlock (&(priv->mutex));
-}
-
-static gboolean
-is_authorized_for_domain (GDataAuthorizer *self, GDataAuthorizationDomain *domain)
-{
- GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (self)->priv;
- gpointer result;
-
- g_rec_mutex_lock (&(priv->mutex));
- result = g_hash_table_lookup (priv->auth_tokens, domain);
- g_rec_mutex_unlock (&(priv->mutex));
-
- return (result != NULL) ? TRUE : FALSE;
-}
-
-/**
- * gdata_client_login_authorizer_new:
- * @client_id: your application's client ID
- * @service_type: the #GType of a #GDataService subclass which the #GDataClientLoginAuthorizer will be used with
- *
- * Creates a new #GDataClientLoginAuthorizer. The @client_id must be unique for your application, and as registered with Google.
- *
- * The #GDataAuthorizationDomains for the given @service_type (i.e. as returned by gdata_service_get_authorization_domains()) are the ones the
- * user will be logged in to using the provided username and password when gdata_client_login_authorizer_authenticate() is called. Note that the same
- * username and password will be used for all domains.
- *
- * Return value: (transfer full): a new #GDataClientLoginAuthorizer, or %NULL; unref with g_object_unref()
- *
- * Since: 0.9.0
- */
-GDataClientLoginAuthorizer *
-gdata_client_login_authorizer_new (const gchar *client_id, GType service_type)
-{
- g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
- g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE), NULL);
-
- return gdata_client_login_authorizer_new_for_authorization_domains (client_id,
- gdata_service_get_authorization_domains (service_type));
-}
-
-/**
- * gdata_client_login_authorizer_new_for_authorization_domains:
- * @client_id: your application's client ID
- * @authorization_domains: (element-type GDataAuthorizationDomain) (transfer none): a non-empty list of #GDataAuthorizationDomains to be
- * authorized against by the #GDataClientLoginAuthorizer
- *
- * Creates a new #GDataClientLoginAuthorizer. The @client_id must be unique for your application, and as registered with Google. This function is
- * intended to be used only when the default authorization domain list for a single #GDataService, as used by gdata_client_login_authorizer_new(),
- * isn't suitable. For example, this could be because the #GDataClientLoginAuthorizer will be used with multiple #GDataService subclasses, or because
- * the client requires a specific set of authorization domains.
- *
- * The specified #GDataAuthorizationDomains are the ones the user will be logged in to using the provided username and password when
- * gdata_client_login_authorizer_authenticate() is called. Note that the same username and password will be used for all domains.
- *
- * Return value: (transfer full): a new #GDataClientLoginAuthorizer, or %NULL; unref with g_object_unref()
- *
- * Since: 0.9.0
- */
-GDataClientLoginAuthorizer *
-gdata_client_login_authorizer_new_for_authorization_domains (const gchar *client_id, GList *authorization_domains)
-{
- GList *i;
- GDataClientLoginAuthorizer *authorizer;
-
- g_return_val_if_fail (client_id != NULL && *client_id != '\0', NULL);
- g_return_val_if_fail (authorization_domains != NULL, NULL);
-
- authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (g_object_new (GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER,
- "client-id", client_id,
- NULL));
-
- /* Register all the domains with the authorizer */
- for (i = authorization_domains; i != NULL; i = i->next) {
- g_return_val_if_fail (GDATA_IS_AUTHORIZATION_DOMAIN (i->data), NULL);
-
- /* We don't have to lock the authoriser's mutex here as no other code has seen the authoriser yet */
- g_hash_table_insert (authorizer->priv->auth_tokens, g_object_ref (GDATA_AUTHORIZATION_DOMAIN (i->data)), NULL);
- }
-
- return authorizer;
-}
-
-/* Called in the main thread to notify of changes to the username and password properties from the authentication thread. It swallows a reference
- * the authoriser. */
-static gboolean
-notify_authentication_details_cb (GDataClientLoginAuthorizer *self)
-{
- GObject *authorizer = G_OBJECT (self);
-
- g_object_freeze_notify (authorizer);
- g_object_notify (authorizer, "username");
- g_object_notify (authorizer, "password");
- g_object_thaw_notify (authorizer);
-
- g_object_unref (self);
-
- /* Only execute once */
- return FALSE;
-}
-
-static void
-set_authentication_details (GDataClientLoginAuthorizer *self, const gchar *username, const gchar *password, GHashTable *new_auth_tokens,
- gboolean is_async)
-{
- GDataClientLoginAuthorizerPrivate *priv = self->priv;
- GHashTableIter iter;
-
- g_rec_mutex_lock (&(priv->mutex));
-
- /* Ensure the username is always a full e-mail address */
- g_free (priv->username);
- if (username != NULL && strchr (username, '@') == NULL) {
- priv->username = g_strdup_printf ("%s@" EMAIL_DOMAIN, username);
- } else {
- priv->username = g_strdup (username);
- }
-
- _gdata_service_secure_strfree (priv->password);
- priv->password = _gdata_service_secure_strdup (password);
-
- /* Transfer all successful auth. tokens to the object-wide auth. token store. */
- if (new_auth_tokens == NULL) {
- /* Reset ->auth_tokens to contain no auth. tokens, just the domains. */
- g_hash_table_iter_init (&iter, priv->auth_tokens);
-
- while (g_hash_table_iter_next (&iter, NULL, NULL) == TRUE) {
- g_hash_table_iter_replace (&iter, NULL);
- }
- } else {
- /* Replace the existing ->auth_tokens with the new one, which contains all the shiny new auth. tokens. */
- g_hash_table_ref (new_auth_tokens);
- g_hash_table_unref (priv->auth_tokens);
- priv->auth_tokens = new_auth_tokens;
- }
-
- g_rec_mutex_unlock (&(priv->mutex));
-
- /* Notify of the property changes in the main thread; i.e. if we're running an async operation, schedule the notification in an idle
- * callback; but if we're running a sync operation, emit them immediately.
- * This guarantees that:
- * • notifications will always be emitted before gdata_client_login_authorizer_authenticate() returns; and
- * • notifications will always be emitted in the main thread for calls to gdata_client_login_authorizer_authenticate_async(). */
- if (is_async == TRUE) {
- g_idle_add ((GSourceFunc) notify_authentication_details_cb, g_object_ref (self));
- } else {
- notify_authentication_details_cb (g_object_ref (self));
- }
-}
-
-static GDataSecureString
-parse_authentication_response (GDataClientLoginAuthorizer *self, GDataAuthorizationDomain *domain, guint status,
- const gchar *response_body, gint length, GError **error)
-{
- gchar *auth_start, *auth_end;
- GDataSecureString auth_token; /* NOTE: auth_token must be allocated using _gdata_service_secure_strdup() and friends */
-
- /* Parse the response */
- auth_start = strstr (response_body, "Auth=");
- if (auth_start == NULL) {
- goto protocol_error;
- }
- auth_start += strlen ("Auth=");
-
- auth_end = strstr (auth_start, "\n");
- if (auth_end == NULL) {
- goto protocol_error;
- }
-
- auth_token = _gdata_service_secure_strndup (auth_start, auth_end - auth_start);
- if (auth_token == NULL || strlen (auth_token) == 0) {
- _gdata_service_secure_strfree (auth_token);
- goto protocol_error;
- }
-
- return auth_token;
-
-protocol_error:
- g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
- _("The server returned a malformed response."));
- return NULL;
-}
-
-static void
-parse_error_response (GDataClientLoginAuthorizer *self, guint status, const gchar *reason_phrase, const gchar *response_body, gint length,
- GError **error)
-{
- /* We prefer to include the @response_body in the error message, but if it's empty, fall back to the @reason_phrase */
- if (response_body == NULL || *response_body == '\0') {
- response_body = reason_phrase;
- }
-
- /* See: http://code.google.com/apis/gdata/docs/2.0/reference.html#HTTPStatusCodes */
- switch (status) {
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_CONNECT:
- case SOUP_STATUS_SSL_FAILED:
- case SOUP_STATUS_IO_ERROR:
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_NETWORK_ERROR,
- _("Cannot connect to the service’s server."));
- return;
- case SOUP_STATUS_CANT_RESOLVE_PROXY:
- case SOUP_STATUS_CANT_CONNECT_PROXY:
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROXY_ERROR,
- _("Cannot connect to the proxy server."));
- return;
- case SOUP_STATUS_MALFORMED:
- case SOUP_STATUS_BAD_REQUEST: /* 400 */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
- /* Translators: the parameter is an error message returned by the server. */
- _("Invalid request URI or header, or unsupported nonstandard parameter: %s"), response_body);
- return;
- case SOUP_STATUS_UNAUTHORIZED: /* 401 */
- case SOUP_STATUS_FORBIDDEN: /* 403 */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
- /* Translators: the parameter is an error message returned by the server. */
- _("Authentication required: %s"), response_body);
- return;
- case SOUP_STATUS_NOT_FOUND: /* 404 */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_NOT_FOUND,
- /* Translators: the parameter is an error message returned by the server. */
- _("The requested resource was not found: %s"), response_body);
- return;
- case SOUP_STATUS_CONFLICT: /* 409 */
- case SOUP_STATUS_PRECONDITION_FAILED: /* 412 */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_CONFLICT,
- /* Translators: the parameter is an error message returned by the server. */
- _("The entry has been modified since it was downloaded: %s"), response_body);
- return;
- case SOUP_STATUS_INTERNAL_SERVER_ERROR: /* 500 */
- default:
- /* We'll fall back to a generic error, below */
- break;
- }
-
- /* If the error hasn't been handled already, throw a generic error */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
- /* Translators: the first parameter is an HTTP status,
- * and the second is an error message returned by the server. */
- _("Error code %u when authenticating: %s"), status, response_body);
-}
-
-static GDataSecureString
-authenticate (GDataClientLoginAuthorizer *self, GDataAuthorizationDomain *domain, const gchar *username, const gchar *password,
- gchar *captcha_token, gchar *captcha_answer, GCancellable *cancellable, GError **error)
-{
- GDataClientLoginAuthorizerPrivate *priv = self->priv;
- SoupMessage *message;
- gchar *request_body;
- const gchar *service_name;
- guint status;
- GDataSecureString auth_token;
- SoupURI *_uri;
-
- /* Prepare the request.
- * NOTE: At this point, our non-pageable password is copied into a pageable HTTP request structure. We can't do much about this
- * except note that the request is transient and so the chance of it getting paged out is low (but still positive). */
- service_name = gdata_authorization_domain_get_service_name (domain);
- request_body = soup_form_encode ("accountType", "HOSTED_OR_GOOGLE",
- "Email", username,
- "Passwd", password,
- "service", service_name,
- "source", priv->client_id,
- (captcha_token == NULL) ? NULL : "logintoken", captcha_token,
- "loginanswer", captcha_answer,
- NULL);
-
- /* Free the CAPTCHA token and answer if necessary */
- g_free (captcha_token);
- g_free (captcha_answer);
-
- /* Build the message */
- _uri = soup_uri_new ("https://www.google.com/accounts/ClientLogin");
- soup_uri_set_port (_uri, _gdata_service_get_https_port ());
- message = soup_message_new_from_uri (SOUP_METHOD_POST, _uri);
- soup_uri_free (_uri);
- soup_message_set_request (message, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, request_body, strlen (request_body));
-
- /* Send the message */
- _gdata_service_actually_send_message (priv->session, message, cancellable, error);
- status = message->status_code;
-
- if (status == SOUP_STATUS_CANCELLED) {
- /* Cancelled (the error has already been set) */
- g_object_unref (message);
- return NULL;
- } else if (status != SOUP_STATUS_OK) {
- const gchar *response_body = message->response_body->data;
- gchar *error_start, *error_end, *uri_start, *uri_end, *uri = NULL;
-
- /* Parse the error response; see: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Errors */
- if (response_body == NULL) {
- goto protocol_error;
- }
-
- /* Error */
- error_start = strstr (response_body, "Error=");
- if (error_start == NULL) {
- goto protocol_error;
- }
- error_start += strlen ("Error=");
-
- error_end = strstr (error_start, "\n");
- if (error_end == NULL) {
- goto protocol_error;
- }
-
- if (strncmp (error_start, "CaptchaRequired", error_end - error_start) == 0) {
- const gchar *captcha_base_uri = "http://www.google.com/accounts/";
- gchar *captcha_start, *captcha_end, *captcha_uri, *new_captcha_answer;
- guint captcha_base_uri_length;
-
- /* CAPTCHA required to log in */
- captcha_start = strstr (response_body, "CaptchaUrl=");
- if (captcha_start == NULL) {
- goto protocol_error;
- }
- captcha_start += strlen ("CaptchaUrl=");
-
- captcha_end = strstr (captcha_start, "\n");
- if (captcha_end == NULL) {
- goto protocol_error;
- }
-
- /* Do some fancy memory stuff to save ourselves another alloc */
- captcha_base_uri_length = strlen (captcha_base_uri);
- captcha_uri = g_malloc (captcha_base_uri_length + (captcha_end - captcha_start) + 1);
- memcpy (captcha_uri, captcha_base_uri, captcha_base_uri_length);
- memcpy (captcha_uri + captcha_base_uri_length, captcha_start, (captcha_end - captcha_start));
- captcha_uri[captcha_base_uri_length + (captcha_end - captcha_start)] = '\0';
-
- /* Request a CAPTCHA answer from the application */
- g_signal_emit (self, authorizer_signals[SIGNAL_CAPTCHA_CHALLENGE], 0, captcha_uri, &new_captcha_answer);
- g_free (captcha_uri);
-
- if (new_captcha_answer == NULL || *new_captcha_answer == '\0') {
- g_set_error_literal (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED,
- /* Translators: see http://en.wikipedia.org/wiki/CAPTCHA for information about CAPTCHAs */
- _("A CAPTCHA must be filled out to log in."));
- goto login_error;
- }
-
- /* Get the CAPTCHA token */
- captcha_start = strstr (response_body, "CaptchaToken=");
- if (captcha_start == NULL) {
- goto protocol_error;
- }
- captcha_start += strlen ("CaptchaToken=");
-
- captcha_end = strstr (captcha_start, "\n");
- if (captcha_end == NULL) {
- goto protocol_error;
- }
-
- /* Save the CAPTCHA token and answer, and attempt to log in with them */
- g_object_unref (message);
-
- return authenticate (self, domain, username, password,
- g_strndup (captcha_start, captcha_end - captcha_start), new_captcha_answer,
- cancellable, error);
- } else if (strncmp (error_start, "Unknown", error_end - error_start) == 0) {
- goto protocol_error;
- } else if (strncmp (error_start, "BadAuthentication", error_end - error_start) == 0) {
- /* Looks like Error=BadAuthentication errors don't return a URI */
- gchar *info_start, *info_end;
-
- info_start = strstr (response_body, "Info=");
- if (info_start != NULL) {
- info_start += strlen ("Info=");
- info_end = strstr (info_start, "\n");
- }
-
- /* If Info=InvalidSecondFactor, the user needs to generate an application-specific password and use that instead */
- if (info_start != NULL && info_end != NULL && strncmp (info_start, "InvalidSecondFactor", info_end - info_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_INVALID_SECOND_FACTOR,
- /* Translators: the parameter is a URI for further information. */
- _("This account requires an application-specific password. (%s)"),
- "http://www.google.com/support/accounts/bin/static.py?page=guide.cs&guide=1056283&topic=1056286");
- goto login_error;
- }
-
- /* Fall back to a generic "bad authentication details" message */
- g_set_error_literal (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION,
- _("Your username or password were incorrect."));
- goto login_error;
- }
-
- /* Get the information URI */
- uri_start = strstr (response_body, "Url=");
- if (uri_start == NULL) {
- goto protocol_error;
- }
- uri_start += strlen ("Url=");
-
- uri_end = strstr (uri_start, "\n");
- if (uri_end == NULL) {
- goto protocol_error;
- }
-
- uri = g_strndup (uri_start, uri_end - uri_start);
-
- if (strncmp (error_start, "NotVerified", error_end - error_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED,
- /* Translators: the parameter is a URI for further information. */
- _("Your account’s e-mail address has not been verified. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "TermsNotAgreed", error_end - error_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED,
- /* Translators: the parameter is a URI for further information. */
- _("You have not agreed to the service’s terms and conditions. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "AccountMigrated", error_end - error_start) == 0) {
- /* This is non-standard, and used by YouTube since it's got messed-up accounts */
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_MIGRATED,
- /* Translators: the parameter is a URI for further information. */
- _("This account has been migrated. Please log in online to receive your new username and password. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "AccountDeleted", error_end - error_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED,
- /* Translators: the parameter is a URI for further information. */
- _("This account has been deleted. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "AccountDisabled", error_end - error_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED,
- /* Translators: the parameter is a URI for further information. */
- _("This account has been disabled. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "ServiceDisabled", error_end - error_start) == 0) {
- g_set_error (error, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED,
- /* Translators: the parameter is a URI for further information. */
- _("This account’s access to this service has been disabled. (%s)"), uri);
- goto login_error;
- } else if (strncmp (error_start, "ServiceUnavailable", error_end - error_start) == 0) {
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_UNAVAILABLE,
- /* Translators: the parameter is a URI for further information. */
- _("This service is not available at the moment. (%s)"), uri);
- goto login_error;
- }
-
- /* Unknown error type! */
- goto protocol_error;
-
-login_error:
- g_free (uri);
- g_object_unref (message);
-
- return NULL;
- }
-
- g_assert (message->response_body->data != NULL);
-
- auth_token = parse_authentication_response (self, domain, status, message->response_body->data, message->response_body->length, error);
-
- /* Zero out the response body to lower the chance of it (with all the juicy passwords and auth. tokens it contains) hitting disk or getting
- * leaked in free memory. */
- memset ((void*) message->response_body->data, 0, message->response_body->length);
-
- g_object_unref (message);
-
- return auth_token;
-
-protocol_error:
- parse_error_response (self, status, message->reason_phrase, message->response_body->data, message->response_body->length, error);
-
- g_object_unref (message);
-
- return NULL;
-}
-
-static gboolean
-authenticate_loop (GDataClientLoginAuthorizer *authorizer, gboolean is_async, const gchar *username, const gchar *password, GCancellable *cancellable,
- GError **error)
-{
- GDataClientLoginAuthorizerPrivate *priv = authorizer->priv;
- gboolean cumulative_success = TRUE;
- GHashTable *new_auth_tokens;
- GHashTableIter iter;
- GDataAuthorizationDomain *domain;
- GDataSecureString auth_token;
-
- g_rec_mutex_lock (&(priv->mutex));
-
- /* Authenticate and authorize against each of the services registered with the authorizer */
- new_auth_tokens = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) _gdata_service_secure_strfree);
- g_hash_table_iter_init (&iter, priv->auth_tokens);
-
- while (g_hash_table_iter_next (&iter, (gpointer*) &domain, NULL) == TRUE) {
- GError *authenticate_error = NULL;
-
- auth_token = authenticate (authorizer, domain, username, password, NULL, NULL, cancellable, &authenticate_error);
-
- if (auth_token == NULL && cumulative_success == TRUE) {
- /* Only propagate the first error which occurs. */
- g_propagate_error (error, authenticate_error);
- authenticate_error = NULL;
- }
-
- cumulative_success = (auth_token != NULL) && cumulative_success;
-
- /* Store the auth. token (or lack thereof if authentication failed). */
- g_hash_table_insert (new_auth_tokens, g_object_ref (domain), auth_token);
-
- g_clear_error (&authenticate_error);
- }
-
- g_rec_mutex_unlock (&(priv->mutex));
-
- /* Set or clear the authentication details and return now that we're done */
- if (cumulative_success == TRUE) {
- set_authentication_details (authorizer, username, password, new_auth_tokens, is_async);
- } else {
- set_authentication_details (authorizer, NULL, NULL, NULL, is_async);
- }
-
- g_hash_table_unref (new_auth_tokens);
-
- return cumulative_success;
-}
-
-typedef struct {
- gchar *username;
- GDataSecureString password; /* NOTE: This must be allocated in non-pageable memory using _gdata_service_secure_strdup(). */
-} AuthenticateAsyncData;
-
-static void
-authenticate_async_data_free (AuthenticateAsyncData *self)
-{
- g_free (self->username);
- _gdata_service_secure_strfree (self->password);
-
- g_slice_free (AuthenticateAsyncData, self);
-}
-
-static void
-authenticate_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
-{
- GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (source_object);
- g_autoptr(GError) error = NULL;
- AuthenticateAsyncData *data = task_data;
-
- if (!authenticate_loop (authorizer, TRUE, data->username, data->password, cancellable, &error))
- g_task_return_error (task, g_steal_pointer (&error));
- else
- g_task_return_boolean (task, TRUE);
-}
-
-/**
- * gdata_client_login_authorizer_authenticate_async:
- * @self: a #GDataClientLoginAuthorizer
- * @username: the user's username
- * @password: the user's password
- * @cancellable: (allow-none): optional #GCancellable object, or %NULL
- * @callback: a #GAsyncReadyCallback to call when authentication is finished
- * @user_data: (closure): data to pass to the @callback function
- *
- * Authenticates the #GDataClientLoginAuthorizer with the Google accounts service using the given @username and @password. @self, @username and
- * @password are all reffed/copied when this function is called, so can safely be freed after this function returns.
- *
- * For more details, see gdata_client_login_authorizer_authenticate(), which is the synchronous version of this function.
- *
- * When the operation is finished, @callback will be called. You can then call gdata_client_login_authorizer_authenticate_finish()
- * to get the results of the operation.
- *
- * Since: 0.9.0
- */
-void
-gdata_client_login_authorizer_authenticate_async (GDataClientLoginAuthorizer *self, const gchar *username, const gchar *password,
- GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
-{
- g_autoptr(GTask) task = NULL;
- AuthenticateAsyncData *data;
-
- g_return_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self));
- g_return_if_fail (username != NULL);
- g_return_if_fail (password != NULL);
- g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-
- data = g_slice_new (AuthenticateAsyncData);
- data->username = g_strdup (username);
- data->password = _gdata_service_secure_strdup (password);
-
- task = g_task_new (self, cancellable, callback, user_data);
- g_task_set_source_tag (task, gdata_client_login_authorizer_authenticate_async);
- g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) authenticate_async_data_free);
- g_task_run_in_thread (task, authenticate_thread);
-}
-
-/**
- * gdata_client_login_authorizer_authenticate_finish:
- * @self: a #GDataClientLoginAuthorizer
- * @async_result: a #GAsyncResult
- * @error: a #GError, or %NULL
- *
- * Finishes an asynchronous authentication operation started with gdata_client_login_authorizer_authenticate_async().
- *
- * Return value: %TRUE if authentication was successful, %FALSE otherwise
- *
- * Since: 0.9.0
- */
-gboolean
-gdata_client_login_authorizer_authenticate_finish (GDataClientLoginAuthorizer *self, GAsyncResult *async_result, GError **error)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), FALSE);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (g_task_is_valid (async_result, self), FALSE);
- g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_client_login_authorizer_authenticate_async), FALSE);
-
- return g_task_propagate_boolean (G_TASK (async_result), error);
-}
-
-/**
- * gdata_client_login_authorizer_authenticate:
- * @self: a #GDataClientLoginAuthorizer
- * @username: the user's username
- * @password: the user's password
- * @cancellable: (allow-none): optional #GCancellable object, or %NULL
- * @error: a #GError, or %NULL
- *
- * Authenticates the #GDataClientLoginAuthorizer with the Google Accounts service using @username and @password and authorizes it against all the
- * service types passed to gdata_client_login_authorizer_new(); i.e. logs into the service with the given user account. @username should be a full
- * e-mail address (e.g. <literal>john.smith\@gmail.com</literal>). If a full e-mail address is not given, @username will have
- * <literal>\@gmail.com</literal> appended to create an e-mail address
- *
- * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
- * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
- *
- * If the operation errors or is cancelled part-way through, gdata_authorizer_is_authorized_for_domain() is guaranteed to return %FALSE
- * for all #GDataAuthorizationDomains, even if authentication has succeeded for some of them already.
- *
- * A %GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION will be returned if authentication failed due to an incorrect username or password.
- * Other #GDataClientLoginAuthorizerError errors can be returned for other conditions.
- *
- * If the service requires a CAPTCHA to be completed, the #GDataClientLoginAuthorizer::captcha-challenge signal will be emitted.
- * The return value from a signal handler for the signal should be a newly allocated string containing the text from the image. If the text is %NULL
- * or empty, authentication will fail with a %GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED error. Otherwise, authentication will be
- * automatically and transparently restarted with the new CAPTCHA details.
- *
- * A %GDATA_SERVICE_ERROR_PROTOCOL_ERROR will be returned if the server's responses were invalid.
- *
- * Return value: %TRUE if authentication and authorization was successful against all the services, %FALSE otherwise
- *
- * Since: 0.9.0
- */
-gboolean
-gdata_client_login_authorizer_authenticate (GDataClientLoginAuthorizer *self, const gchar *username, const gchar *password,
- GCancellable *cancellable, GError **error)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), FALSE);
- g_return_val_if_fail (username != NULL, FALSE);
- g_return_val_if_fail (password != NULL, FALSE);
- g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
- return authenticate_loop (self, FALSE, username, password, cancellable, error);
-}
-
-static void
-notify_proxy_uri_cb (GObject *object, GParamSpec *pspec, GDataClientLoginAuthorizer *self)
-{
- /* Flush our cached version */
- if (self->priv->proxy_uri != NULL) {
- soup_uri_free (self->priv->proxy_uri);
- self->priv->proxy_uri = NULL;
- }
-
- g_object_notify (G_OBJECT (self), "proxy-uri");
-}
-
-/* Static function which isn't deprecated so we can continue using it internally. */
-static SoupURI *
-_get_proxy_uri (GDataClientLoginAuthorizer *self)
-{
- SoupURI *proxy_uri;
-
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), NULL);
-
- /* If we have a cached version, return that */
- if (self->priv->proxy_uri != NULL) {
- return self->priv->proxy_uri;
- }
-
- g_object_get (self->priv->session, SOUP_SESSION_PROXY_URI, &proxy_uri, NULL);
-
- /* Update the cache; it takes ownership of the URI */
- self->priv->proxy_uri = proxy_uri;
-
- return proxy_uri;
-}
-
-/**
- * gdata_client_login_authorizer_get_proxy_uri:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Gets the proxy URI on the #GDataClientLoginAuthorizer's #SoupSession.
- *
- * Return value: (transfer full): the proxy URI, or %NULL; free with soup_uri_free()
- *
- * Since: 0.9.0
- * Deprecated: 0.15.0: Use gdata_client_login_authorizer_get_proxy_resolver() instead, which gives more flexibility over the proxy used.
- */
-SoupURI *
-gdata_client_login_authorizer_get_proxy_uri (GDataClientLoginAuthorizer *self)
-{
- return _get_proxy_uri (self);
-}
-
-/* Static function which isn't deprecated so we can continue using it internally. */
-static void
-_set_proxy_uri (GDataClientLoginAuthorizer *self, SoupURI *proxy_uri)
-{
- g_return_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self));
-
- g_object_set (self->priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
-
- /* Notification is handled in notify_proxy_uri_cb() which is called as a result of setting the property on the session */
-}
-
-/**
- * gdata_client_login_authorizer_set_proxy_uri:
- * @self: a #GDataClientLoginAuthorizer
- * @proxy_uri: (allow-none): the proxy URI, or %NULL
- *
- * Sets the proxy URI on the #SoupSession used internally by the #GDataClientLoginAuthorizer. This forces all requests through the given proxy.
- *
- * If @proxy_uri is %NULL, no proxy will be used.
- *
- * Since: 0.9.0
- * Deprecated: 0.15.0: Use gdata_client_login_authorizer_set_proxy_resolver() instead, which gives more flexibility over the proxy used.
- */
-void
-gdata_client_login_authorizer_set_proxy_uri (GDataClientLoginAuthorizer *self, SoupURI *proxy_uri)
-{
- _set_proxy_uri (self, proxy_uri);
-}
-
-/**
- * gdata_client_login_authorizer_get_proxy_resolver:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Gets the #GProxyResolver on the #GDataClientLoginAuthorizer's #SoupSession.
- *
- * Return value: (transfer none) (allow-none): a #GProxyResolver, or %NULL
- *
- * Since: 0.15.0
- */
-GProxyResolver *
-gdata_client_login_authorizer_get_proxy_resolver (GDataClientLoginAuthorizer *self)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), NULL);
-
- return self->priv->proxy_resolver;
-}
-
-/**
- * gdata_client_login_authorizer_set_proxy_resolver:
- * @self: a #GDataClientLoginAuthorizer
- * @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL
- *
- * Sets the #GProxyResolver on the #SoupSession used internally by the given #GDataClientLoginAuthorizer.
- *
- * Setting this will clear the #GDataClientLoginAuthorizer:proxy-uri property.
- *
- * Since: 0.15.0
- */
-void
-gdata_client_login_authorizer_set_proxy_resolver (GDataClientLoginAuthorizer *self, GProxyResolver *proxy_resolver)
-{
- g_return_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self));
- g_return_if_fail (proxy_resolver == NULL || G_IS_PROXY_RESOLVER (proxy_resolver));
-
- if (proxy_resolver != NULL) {
- g_object_ref (proxy_resolver);
- }
-
- g_clear_object (&self->priv->proxy_resolver);
- self->priv->proxy_resolver = proxy_resolver;
-
- g_object_notify (G_OBJECT (self), "proxy-resolver");
-}
-
-static void
-notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self)
-{
- g_object_notify (self, "timeout");
-}
-
-/**
- * gdata_client_login_authorizer_get_timeout:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Gets the #GDataClientLoginAuthorizer:timeout property; the network timeout, in seconds.
- *
- * Return value: the timeout, or <code class="literal">0</code>
- *
- * Since: 0.9.0
- */
-guint
-gdata_client_login_authorizer_get_timeout (GDataClientLoginAuthorizer *self)
-{
- guint timeout;
-
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), 0);
-
- g_object_get (self->priv->session, SOUP_SESSION_TIMEOUT, &timeout, NULL);
-
- return timeout;
-}
-
-/**
- * gdata_client_login_authorizer_set_timeout:
- * @self: a #GDataClientLoginAuthorizer
- * @timeout: the timeout, or <code class="literal">0</code>
- *
- * Sets the #GDataClientLoginAuthorizer:timeout property; the network timeout, in seconds.
- *
- * If @timeout is <code class="literal">0</code>, network operations will never time out.
- *
- * Since: 0.9.0
- */
-void
-gdata_client_login_authorizer_set_timeout (GDataClientLoginAuthorizer *self, guint timeout)
-{
- g_return_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self));
-
- g_object_set (self->priv->session, SOUP_SESSION_TIMEOUT, timeout, NULL);
-
- /* Notification is handled in notify_proxy_uri_cb() which is called as a result of setting the property on the session */
-}
-
-/**
- * gdata_client_login_authorizer_get_client_id:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Returns the authorizer's client ID, as specified on constructing the #GDataClientLoginAuthorizer.
- *
- * Return value: the authorizer's client ID
- *
- * Since: 0.9.0
- */
-const gchar *
-gdata_client_login_authorizer_get_client_id (GDataClientLoginAuthorizer *self)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), NULL);
-
- return self->priv->client_id;
-}
-
-/**
- * gdata_client_login_authorizer_get_username:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Returns the username of the currently authenticated user, or %NULL if nobody is authenticated.
- *
- * It is not safe to call this while an authentication operation is ongoing.
- *
- * Return value: the username of the currently authenticated user, or %NULL
- *
- * Since: 0.9.0
- */
-const gchar *
-gdata_client_login_authorizer_get_username (GDataClientLoginAuthorizer *self)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), NULL);
-
- /* There's little point protecting this with ->mutex, as the data's meaningless if accessed during an authentication operation,
- * and not being accessed concurrently otherwise. */
- return self->priv->username;
-}
-
-/**
- * gdata_client_login_authorizer_get_password:
- * @self: a #GDataClientLoginAuthorizer
- *
- * Returns the password of the currently authenticated user, or %NULL if nobody is authenticated.
- *
- * It is not safe to call this while an authentication operation is ongoing.
- *
- * If libgdata is compiled with libgcr support, the password will be stored in non-pageable memory. Since this function doesn't return
- * a copy of the password, the returned value is guaranteed to not hit disk. It's advised that any copies of the password made in client programs
- * also use non-pageable memory.
- *
- * Return value: the password of the currently authenticated user, or %NULL
- *
- * Since: 0.9.0
- */
-const gchar *
-gdata_client_login_authorizer_get_password (GDataClientLoginAuthorizer *self)
-{
- g_return_val_if_fail (GDATA_IS_CLIENT_LOGIN_AUTHORIZER (self), NULL);
-
- /* There's little point protecting this with ->mutex, as the data's meaningless if accessed during an authentication operation,
- * and not being accessed concurrently otherwise. */
- return self->priv->password;
-}
diff --git a/gdata/gdata-client-login-authorizer.h b/gdata/gdata-client-login-authorizer.h
deleted file mode 100644
index 7e13ca1c..00000000
--- a/gdata/gdata-client-login-authorizer.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/*
- * GData Client
- * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
- *
- * GData Client is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * GData Client is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef GDATA_CLIENT_LOGIN_AUTHORIZER_H
-#define GDATA_CLIENT_LOGIN_AUTHORIZER_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "gdata-authorizer.h"
-
-G_BEGIN_DECLS
-
-/**
- * GDataClientLoginAuthorizerError:
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION: The login request used a username or password that is not recognized.
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED: The account email address has not been verified. The user will need to access their Google
- * account directly to resolve the issue before logging in using a non-Google application.
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED: The user has not agreed to terms. The user will need to access their Google account directly
- * to resolve the issue before logging in using a non-Google application.
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED: A CAPTCHA is required. (A response with this error code will also contain an image URI and a
- * CAPTCHA token.)
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED: The user account has been deleted.
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED: The user account has been disabled.
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED: The user's access to the specified service has been disabled. (The user account may still be
- * valid.)
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_MIGRATED: The user's account login details have been migrated to a new system. (This is used for the
- * transition from the old YouTube login details to the new ones.)
- * @GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_INVALID_SECOND_FACTOR: The user's account requires an application-specific password to be used.
- *
- * Error codes for authentication and authorization operations on #GDataClientLoginAuthorizer. See the
- * <ulink type="http" url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Errors">online ClientLogin documentation</ulink> for
- * more information.
- *
- * Since: 0.9.0
- */
-typedef enum {
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION = 1,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_CAPTCHA_REQUIRED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_MIGRATED,
- GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_INVALID_SECOND_FACTOR
-} GDataClientLoginAuthorizerError;
-
-GQuark gdata_client_login_authorizer_error_quark (void) G_GNUC_CONST;
-
-#define GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR gdata_client_login_authorizer_error_quark ()
-
-#define GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER (gdata_client_login_authorizer_get_type ())
-#define GDATA_CLIENT_LOGIN_AUTHORIZER(o) \
- (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER, GDataClientLoginAuthorizer))
-#define GDATA_CLIENT_LOGIN_AUTHORIZER_CLASS(k) \
- (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER, GDataClientLoginAuthorizerClass))
-#define GDATA_IS_CLIENT_LOGIN_AUTHORIZER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER))
-#define GDATA_IS_CLIENT_LOGIN_AUTHORIZER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER))
-#define GDATA_CLIENT_LOGIN_AUTHORIZER_GET_CLASS(o) \
- (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_CLIENT_LOGIN_AUTHORIZER, GDataClientLoginAuthorizerClass))
-
-typedef struct _GDataClientLoginAuthorizerPrivate GDataClientLoginAuthorizerPrivate;
-
-/**
- * GDataClientLoginAuthorizer:
- *
- * All the fields in the #GDataClientLoginAuthorizer structure are private and should never be accessed directly.
- *
- * Since: 0.9.0
- */
-typedef struct {
- /*< private >*/
- GObject parent;
- GDataClientLoginAuthorizerPrivate *priv;
-} GDataClientLoginAuthorizer;
-
-/**
- * GDataClientLoginAuthorizerClass:
- *
- * All the fields in the #GDataClientLoginAuthorizerClass structure are private and should never be accessed directly.
- *
- * Since: 0.9.0
- */
-typedef struct {
- /*< private >*/
- GObjectClass parent;
-
- /*< private >*/
- /* Padding for future expansion */
- void (*_g_reserved0) (void);
- void (*_g_reserved1) (void);
-} GDataClientLoginAuthorizerClass;
-
-GType gdata_client_login_authorizer_get_type (void) G_GNUC_CONST;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDataClientLoginAuthorizer, g_object_unref)
-
-GDataClientLoginAuthorizer *gdata_client_login_authorizer_new (const gchar *client_id, GType service_type) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
-GDataClientLoginAuthorizer *gdata_client_login_authorizer_new_for_authorization_domains (const gchar *client_id, GList *authorization_domains)
- G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
-
-gboolean gdata_client_login_authorizer_authenticate (GDataClientLoginAuthorizer *self, const gchar *username, const gchar *password,
- GCancellable *cancellable, GError **error);
-void gdata_client_login_authorizer_authenticate_async (GDataClientLoginAuthorizer *self, const gchar *username, const gchar *password,
- GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean gdata_client_login_authorizer_authenticate_finish (GDataClientLoginAuthorizer *self, GAsyncResult *async_result, GError **error);
-
-const gchar *gdata_client_login_authorizer_get_client_id (GDataClientLoginAuthorizer *self) G_GNUC_PURE;
-const gchar *gdata_client_login_authorizer_get_username (GDataClientLoginAuthorizer *self) G_GNUC_PURE;
-const gchar *gdata_client_login_authorizer_get_password (GDataClientLoginAuthorizer *self) G_GNUC_PURE;
-
-#ifndef LIBGDATA_DISABLE_DEPRECATED
-SoupURI *gdata_client_login_authorizer_get_proxy_uri (GDataClientLoginAuthorizer *self) G_GNUC_PURE G_GNUC_DEPRECATED_FOR (gdata_client_login_authorizer_get_proxy_resolver);
-void gdata_client_login_authorizer_set_proxy_uri (GDataClientLoginAuthorizer *self, SoupURI *proxy_uri) G_GNUC_DEPRECATED_FOR (gdata_client_login_authorizer_set_proxy_resolver);
-#endif /* !LIBGDATA_DISABLE_DEPRECATED */
-
-GProxyResolver *gdata_client_login_authorizer_get_proxy_resolver (GDataClientLoginAuthorizer *self) G_GNUC_PURE;
-void gdata_client_login_authorizer_set_proxy_resolver (GDataClientLoginAuthorizer *self, GProxyResolver *proxy_resolver);
-
-guint gdata_client_login_authorizer_get_timeout (GDataClientLoginAuthorizer *self) G_GNUC_PURE;
-void gdata_client_login_authorizer_set_timeout (GDataClientLoginAuthorizer *self, guint timeout);
-
-G_END_DECLS
-
-#endif /* !GDATA_CLIENT_LOGIN_AUTHORIZER_H */
diff --git a/gdata/gdata-core.symbols b/gdata/gdata-core.symbols
index b9b4cb0f..df956027 100644
--- a/gdata/gdata-core.symbols
+++ b/gdata/gdata-core.symbols
@@ -759,23 +759,6 @@ gdata_authorizer_refresh_authorization_finish
gdata_authorization_domain_get_type
gdata_authorization_domain_get_service_name
gdata_authorization_domain_get_scope
-gdata_client_login_authorizer_get_type
-gdata_client_login_authorizer_new
-gdata_client_login_authorizer_new_for_authorization_domains
-gdata_client_login_authorizer_authenticate
-gdata_client_login_authorizer_authenticate_async
-gdata_client_login_authorizer_authenticate_finish
-gdata_client_login_authorizer_get_client_id
-gdata_client_login_authorizer_get_username
-gdata_client_login_authorizer_get_password
-gdata_client_login_authorizer_get_proxy_uri
-gdata_client_login_authorizer_set_proxy_uri
-gdata_client_login_authorizer_get_proxy_resolver
-gdata_client_login_authorizer_set_proxy_resolver
-gdata_client_login_authorizer_get_timeout
-gdata_client_login_authorizer_set_timeout
-gdata_client_login_authorizer_error_quark
-gdata_client_login_authorizer_error_get_type
gdata_download_stream_get_authorization_domain
gdata_upload_stream_get_authorization_domain
gdata_batch_operation_get_authorization_domain
diff --git a/gdata/gdata-goa-authorizer.c b/gdata/gdata-goa-authorizer.c
index e71f6145..88c014e9 100644
--- a/gdata/gdata-goa-authorizer.c
+++ b/gdata/gdata-goa-authorizer.c
@@ -32,7 +32,7 @@
* <ulink type="http" url="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html">OAuth 1.0</ulink> or
* <ulink type="http" url="https://developers.google.com/identity/protocols/OAuth2">OAuth 2.0</ulink> processes.
*
- * #GDataGoaAuthorizer natively supports authorization against multiple services (unlike #GDataClientLoginAuthorizer), depending entirely on which
+ * #GDataGoaAuthorizer natively supports authorization against multiple services, depending entirely on which
* services the user has enabled for their Google account in GOA. #GDataGoaAuthorizer cannot authenticate for more services than are enabled in GOA.
*
* <example>
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index 40fbaf84..8c62c367 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -51,7 +51,6 @@
#include "gdata-service.h"
#include "gdata-private.h"
-#include "gdata-client-login-authorizer.h"
#include "gdata-marshal.h"
#include "gdata-types.h"
diff --git a/gdata/gdata.h b/gdata/gdata.h
index 1650ae35..1901a0d6 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -37,7 +37,6 @@
#include <gdata/gdata-batch-operation.h>
#include <gdata/gdata-authorizer.h>
#include <gdata/gdata-authorization-domain.h>
-#include <gdata/gdata-client-login-authorizer.h>
#include <gdata/gdata-oauth2-authorizer.h>
#ifdef GOA_API_IS_SUBJECT_TO_CHANGE
/* You need to define GOA_API_IS_SUBJECT_TO_CHANGE in order to use the GOA authoriser. */
diff --git a/gdata/meson.build b/gdata/meson.build
index ff14f81f..728bfc1f 100644
--- a/gdata/meson.build
+++ b/gdata/meson.build
@@ -27,7 +27,6 @@ headers = files(
'gdata-authorizer.h',
'gdata-batch-operation.h',
'gdata-batchable.h',
- 'gdata-client-login-authorizer.h',
'gdata-comment.h',
'gdata-commentable.h',
'gdata-comparable.h',
@@ -51,7 +50,6 @@ sources += files(
'gdata-batch-operation.c',
'gdata-batchable.c',
'gdata-buffer.c',
- 'gdata-client-login-authorizer.c',
'gdata-comment.c',
'gdata-commentable.c',
'gdata-comparable.c',
diff --git a/gdata/services/picasaweb/gdata-picasaweb-service.c b/gdata/services/picasaweb/gdata-picasaweb-service.c
index 89dcd7ac..1a9dc8ce 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-service.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-service.c
@@ -33,14 +33,13 @@
* <example>
* <title>Authenticating and Creating a New Album</title>
* <programlisting>
- * GDataClientLoginAuthorizer *authorizer;
+ * GDataAuthorizer *authorizer;
* GDataPicasaWebService *service;
* GDataPicasaWebAlbum *album, *inserted_album;
*
* /<!-- -->* Create a service object and authorize against the PicasaWeb service *<!-- -->/
- * authorizer = gdata_client_login_authorizer_new ("companyName-applicationName-versionID", GDATA_TYPE_PICASAWEB_SERVICE);
- * gdata_client_login_authorizer_authenticate (authorizer, username, password, NULL, NULL);
- * service = gdata_picasaweb_service_new (GDATA_AUTHORIZER (authorizer));
+ * authorizer = create_authorizer (…, gdata_picasaweb_service_get_primary_authorization_domain ());
+ * service = gdata_picasaweb_service_new (authorizer);
*
* /<!-- -->* Create a GDataPicasaWebAlbum entry for the new album, setting some information about it *<!-- -->/
* album = gdata_picasaweb_album_new (NULL);
diff --git a/gdata/symbol.map b/gdata/symbol.map
index f4ffa762..e8fc7606 100644
--- a/gdata/symbol.map
+++ b/gdata/symbol.map
@@ -129,23 +129,6 @@ global:
gdata_category_set_label;
gdata_category_set_scheme;
gdata_category_set_term;
- gdata_client_login_authorizer_authenticate;
- gdata_client_login_authorizer_authenticate_async;
- gdata_client_login_authorizer_authenticate_finish;
- gdata_client_login_authorizer_error_get_type;
- gdata_client_login_authorizer_error_quark;
- gdata_client_login_authorizer_get_client_id;
- gdata_client_login_authorizer_get_password;
- gdata_client_login_authorizer_get_proxy_resolver;
- gdata_client_login_authorizer_get_proxy_uri;
- gdata_client_login_authorizer_get_timeout;
- gdata_client_login_authorizer_get_type;
- gdata_client_login_authorizer_get_username;
- gdata_client_login_authorizer_new;
- gdata_client_login_authorizer_new_for_authorization_domains;
- gdata_client_login_authorizer_set_proxy_resolver;
- gdata_client_login_authorizer_set_proxy_uri;
- gdata_client_login_authorizer_set_timeout;
gdata_color_from_hexadecimal;
gdata_color_get_type;
gdata_color_to_hexadecimal;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e42183c8..014dac90 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,7 +2,6 @@
# Please keep this file sorted alphabetically.
gdata/gdata-access-handler.c
gdata/gdata-batch-operation.c
-gdata/gdata-client-login-authorizer.c
gdata/gdata-commentable.c
gdata/gdata-download-stream.c
gdata/gdata-entry.c