~ubuntu-branches/ubuntu/oneiric/libgdata/oneiric-backports

« back to all changes in this revision

Viewing changes to gdata/gdata-service.c

  • Committer: Package Import Robot
  • Author(s): Evan Broder
  • Date: 2011-11-15 21:59:48 UTC
  • mfrom: (4.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20111115215948-e17s889ocgu5fv4f
Tags: 0.10.1-1~oneiric1
Automated backport upload; no source changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
 * #GDataService instance is required to issue queries to the service, handle insertions, updates and deletions, and generally
28
28
 * communicate with the online service.
29
29
 *
30
 
 * <example>
31
 
 *      <title>Authenticating Asynchronously</title>
32
 
 *      <programlisting>
33
 
 *      GDataSomeService *service;
34
 
 *
35
 
 *      /<!-- -->* Create a service object and authenticate with the service's server asynchronously *<!-- -->/
36
 
 *      service = gdata_some_service_new ("companyName-applicationName-versionID");
37
 
 *      gdata_service_authenticate_async (GDATA_SERVICE (service), username, password, cancellable, (GAsyncReadyCallback) authenticate_cb, user_data);
38
 
 *
39
 
 *      static void
40
 
 *      authenticate_cb (GDataSomeService *service, GAsyncResult *async_result, gpointer user_data)
41
 
 *      {
42
 
 *              GError *error = NULL;
43
 
 *
44
 
 *              if (gdata_service_authenticate_finish (GDATA_SERVICE (service), async_result, &error) == FALSE) {
45
 
 *                      /<!-- -->* Notify the user of all errors except cancellation errors *<!-- -->/
46
 
 *                      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
47
 
 *                              g_error ("Authentication failed: %s", error->message);
48
 
 *                      g_error_free (error);
49
 
 *                      return;
50
 
 *              }
51
 
 *
52
 
 *              /<!-- -->* (The client is now authenticated. It can now proceed to execute queries on the service object which require the user
53
 
 *               * to be authenticated. *<!-- -->/
54
 
 *      }
55
 
 *
56
 
 *      g_object_unref (service);
57
 
 *      </programlisting>
58
 
 * </example>
59
 
 **/
 
30
 * If operations performed on a #GDataService need authorization (such as uploading a video to YouTube or querying the user's personal calendar on
 
31
 * Google Calendar), the service needs a #GDataAuthorizer instance set as #GDataService:authorizer. Once the user is appropriately authenticated and
 
32
 * authorized by the #GDataAuthorizer implementation (see the documentation for #GDataAuthorizer for details on how this is achieved for specific
 
33
 * implementations), all operations will be automatically authorized.
 
34
 *
 
35
 * Note that it's not always necessary to supply a #GDataAuthorizer instance to a #GDataService. If the only operations to be performed on the
 
36
 * #GDataService don't need authorization (e.g. they only query public information), setting up a #GDataAuthorizer is just extra overhead. See the
 
37
 * documentation for the operations on individual #GDataService subclasses to see which need authorization and which don't.
 
38
 */
60
39
 
61
40
#include <config.h>
62
41
#include <glib.h>
71
50
 
72
51
#include "gdata-service.h"
73
52
#include "gdata-private.h"
 
53
#include "gdata-client-login-authorizer.h"
74
54
#include "gdata-marshal.h"
75
55
#include "gdata-types.h"
76
56
 
77
 
/* The default e-mail domain to use for usernames */
78
 
#define EMAIL_DOMAIN "gmail.com"
79
 
 
80
57
GQuark
81
58
gdata_service_error_quark (void)
82
59
{
83
60
        return g_quark_from_static_string ("gdata-service-error-quark");
84
61
}
85
62
 
86
 
GQuark
87
 
gdata_authentication_error_quark (void)
88
 
{
89
 
        return g_quark_from_static_string ("gdata-authentication-error-quark");
90
 
}
91
 
 
92
63
static void gdata_service_dispose (GObject *object);
93
64
static void gdata_service_finalize (GObject *object);
94
65
static void gdata_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
95
66
static void gdata_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
96
 
static gboolean real_parse_authentication_response (GDataService *self, guint status, const gchar *response_body, gint length, GError **error);
97
 
static void real_append_query_headers (GDataService *self, SoupMessage *message);
 
67
static void real_append_query_headers (GDataService *self, GDataAuthorizationDomain *domain, SoupMessage *message);
98
68
static void real_parse_error_response (GDataService *self, GDataOperationType operation_type, guint status, const gchar *reason_phrase,
99
69
                                       const gchar *response_body, gint length, GError **error);
100
70
static void notify_proxy_uri_cb (GObject *gobject, GParamSpec *pspec, GObject *self);
102
72
static void debug_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data);
103
73
static void soup_log_printer (SoupLogger *logger, SoupLoggerLogLevel level, char direction, const char *data, gpointer user_data);
104
74
 
 
75
static GDataFeed *__gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query,
 
76
                                         GType entry_type, GCancellable *cancellable, GDataQueryProgressCallback progress_callback,
 
77
                                         gpointer progress_user_data, GError **error, gboolean is_async);
 
78
 
105
79
struct _GDataServicePrivate {
106
80
        SoupSession *session;
107
 
 
108
 
        GStaticMutex authentication_mutex; /* mutex for username, password, auth_token and authenticated */
109
 
        gchar *username;
110
 
        gchar *password;
111
 
        gchar *auth_token;
112
 
        gchar *client_id;
113
 
        gboolean authenticated;
114
81
        gchar *locale;
 
82
        GDataAuthorizer *authorizer;
115
83
};
116
84
 
117
85
enum {
118
 
        PROP_CLIENT_ID = 1,
119
 
        PROP_USERNAME,
120
 
        PROP_PASSWORD,
121
 
        PROP_AUTHENTICATED,
122
 
        PROP_PROXY_URI,
 
86
        PROP_PROXY_URI = 1,
123
87
        PROP_TIMEOUT,
124
 
        PROP_LOCALE
125
 
};
126
 
 
127
 
enum {
128
 
        SIGNAL_CAPTCHA_CHALLENGE,
129
 
        LAST_SIGNAL
130
 
};
131
 
 
132
 
static guint service_signals[LAST_SIGNAL] = { 0, };
 
88
        PROP_LOCALE,
 
89
        PROP_AUTHORIZER,
 
90
};
133
91
 
134
92
G_DEFINE_TYPE (GDataService, gdata_service, G_TYPE_OBJECT)
135
93
 
145
103
        gobject_class->dispose = gdata_service_dispose;
146
104
        gobject_class->finalize = gdata_service_finalize;
147
105
 
148
 
        klass->service_name = "xapi";
149
 
        klass->authentication_uri = "https://www.google.com/accounts/ClientLogin";
150
106
        klass->api_version = "2";
151
107
        klass->feed_type = GDATA_TYPE_FEED;
152
 
        klass->parse_authentication_response = real_parse_authentication_response;
153
108
        klass->append_query_headers = real_append_query_headers;
154
109
        klass->parse_error_response = real_parse_error_response;
155
 
 
156
 
        /**
157
 
         * GDataService:client-id:
158
 
         *
159
 
         * A client ID for your application (see the
160
 
         * <ulink url="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request" type="http">reference documentation</ulink>).
161
 
         *
162
 
         * It is recommended that the ID is of the form <literal><replaceable>company name</replaceable>-<replaceable>application name</replaceable>-
163
 
         * <replaceable>version ID</replaceable></literal>.
164
 
         **/
165
 
        g_object_class_install_property (gobject_class, PROP_CLIENT_ID,
166
 
                                         g_param_spec_string ("client-id",
167
 
                                                              "Client ID", "A client ID for your application.",
168
 
                                                              NULL,
169
 
                                                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170
 
 
171
 
        /**
172
 
         * GDataService:username:
173
 
         *
174
 
         * The user's Google username for authentication. This will always be a full e-mail address.
175
 
         **/
176
 
        g_object_class_install_property (gobject_class, PROP_USERNAME,
177
 
                                         g_param_spec_string ("username",
178
 
                                                              "Username", "The user's Google username for authentication.",
179
 
                                                              NULL,
180
 
                                                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
181
 
 
182
 
        /**
183
 
         * GDataService:password:
184
 
         *
185
 
         * The user's account password for authentication.
186
 
         **/
187
 
        g_object_class_install_property (gobject_class, PROP_PASSWORD,
188
 
                                         g_param_spec_string ("password",
189
 
                                                              "Password", "The user's account password for authentication.",
190
 
                                                              NULL,
191
 
                                                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
192
 
 
193
 
        /**
194
 
         * GDataService:authenticated:
195
 
         *
196
 
         * Whether the user is authenticated (logged in) with the service.
197
 
         **/
198
 
        g_object_class_install_property (gobject_class, PROP_AUTHENTICATED,
199
 
                                         g_param_spec_boolean ("authenticated",
200
 
                                                               "Authenticated", "Whether the user is authenticated (logged in) with the service.",
201
 
                                                               FALSE,
202
 
                                                               G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
110
        klass->get_authorization_domains = NULL; /* equivalent to returning an empty list of domains */
203
111
 
204
112
        /**
205
113
         * GDataService:proxy-uri:
206
114
         *
207
115
         * The proxy URI used internally for all network requests.
208
116
         *
 
117
         * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its proxy URI setting.
 
118
         *
209
119
         * Since: 0.2.0
210
120
         **/
211
121
        g_object_class_install_property (gobject_class, PROP_PROXY_URI,
222
132
         *
223
133
         * If the timeout is <code class="literal">0</code>, operations will never time out.
224
134
         *
 
135
         * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its timeout setting.
 
136
         *
225
137
         * Since: 0.7.0
226
138
         **/
227
139
        g_object_class_install_property (gobject_class, PROP_TIMEOUT,
250
162
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
251
163
 
252
164
        /**
253
 
         * GDataService::captcha-challenge:
254
 
         * @service: the #GDataService which received the challenge
255
 
         * @uri: the URI of the CAPTCHA image to be used
256
 
         *
257
 
         * The #GDataService::captcha-challenge signal is emitted during the authentication process if
258
 
         * the service requires a CAPTCHA to be completed. The URI of a CAPTCHA image is given, and the
259
 
         * program should display this to the user, and return their response (the text displayed in the
260
 
         * image). There is no timeout imposed by the library for the response.
261
 
         *
262
 
         * Return value: a newly allocated string containing the text in the CAPTCHA image
 
165
         * GDataService:authorizer:
 
166
         *
 
167
         * An object which implements #GDataAuthorizer. This should have previously been authenticated authorized against this service type (and
 
168
         * potentially other service types). The service will use the authorizer to add an authorization token to each request it performs.
 
169
         *
 
170
         * Your application should call methods on the #GDataAuthorizer object itself in order to authenticate with the Google accounts service and
 
171
         * authorize against this service type. See the documentation for the particular #GDataAuthorizer implementation being used for more details.
 
172
         *
 
173
         * The authorizer for a service can be changed at runtime for a different #GDataAuthorizer object or %NULL without affecting ongoing requests
 
174
         * and operations.
 
175
         *
 
176
         * Note that it's only necessary to set an authorizer on the service if your application is going to make requests of the service which
 
177
         * require authorization. For example, listing the current most popular videos on YouTube does not require authorization, but uploading a
 
178
         * video to YouTube does. It's an unnecessary overhead to require the user to authorize against a service when not strictly required.
 
179
         *
 
180
         * Since: 0.9.0
263
181
         **/
264
 
        service_signals[SIGNAL_CAPTCHA_CHALLENGE] = g_signal_new ("captcha-challenge",
265
 
                                                                  G_TYPE_FROM_CLASS (klass),
266
 
                                                                  G_SIGNAL_RUN_LAST,
267
 
                                                                  0, NULL, NULL,
268
 
                                                                  gdata_marshal_STRING__OBJECT_STRING,
269
 
                                                                  G_TYPE_STRING, 1, G_TYPE_STRING);
 
182
        g_object_class_install_property (gobject_class, PROP_AUTHORIZER,
 
183
                                         g_param_spec_object ("authorizer",
 
184
                                                              "Authorizer", "An authorizer object to provide an authorization token for each request.",
 
185
                                                              GDATA_TYPE_AUTHORIZER,
 
186
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
270
187
}
271
188
 
272
189
static void
273
190
gdata_service_init (GDataService *self)
274
191
{
275
192
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_SERVICE, GDataServicePrivate);
276
 
        self->priv->session = soup_session_sync_new ();
277
 
 
278
 
#ifdef HAVE_GNOME
279
 
        soup_session_add_feature_by_type (self->priv->session, SOUP_TYPE_GNOME_FEATURES_2_26);
280
 
#endif /* HAVE_GNOME */
 
193
        self->priv->session = _gdata_service_build_session ();
281
194
 
282
195
        /* Debug log handling */
283
196
        g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, (GLogFunc) debug_handler, self);
284
197
 
285
 
        /* Set up the authentication mutex */
286
 
        g_static_mutex_init (&(self->priv->authentication_mutex));
287
 
 
288
 
        /* Log all libsoup traffic if debugging's turned on */
289
 
        if (_gdata_service_get_log_level () > GDATA_LOG_MESSAGES) {
290
 
                SoupLoggerLogLevel level;
291
 
                SoupLogger *logger;
292
 
 
293
 
                switch (_gdata_service_get_log_level ()) {
294
 
                        case GDATA_LOG_FULL:
295
 
                                level = SOUP_LOGGER_LOG_BODY;
296
 
                                break;
297
 
                        case GDATA_LOG_HEADERS:
298
 
                                level = SOUP_LOGGER_LOG_HEADERS;
299
 
                                break;
300
 
                        case GDATA_LOG_MESSAGES:
301
 
                        case GDATA_LOG_NONE:
302
 
                        default:
303
 
                                g_assert_not_reached ();
304
 
                }
305
 
 
306
 
                logger = soup_logger_new (level, -1);
307
 
                soup_logger_set_printer (logger, (SoupLoggerPrinter) soup_log_printer, self, NULL);
308
 
 
309
 
                soup_session_add_feature (self->priv->session, SOUP_SESSION_FEATURE (logger));
310
 
 
311
 
                g_object_unref (logger);
312
 
        }
313
 
 
314
198
        /* Proxy the SoupSession's proxy-uri and timeout properties */
315
199
        g_signal_connect (self->priv->session, "notify::proxy-uri", (GCallback) notify_proxy_uri_cb, self);
316
200
        g_signal_connect (self->priv->session, "notify::timeout", (GCallback) notify_timeout_cb, self);
321
205
{
322
206
        GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
323
207
 
 
208
        if (priv->authorizer != NULL)
 
209
                g_object_unref (priv->authorizer);
 
210
        priv->authorizer = NULL;
 
211
 
324
212
        if (priv->session != NULL)
325
213
                g_object_unref (priv->session);
326
214
        priv->session = NULL;
334
222
{
335
223
        GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
336
224
 
337
 
        g_free (priv->username);
338
 
        g_free (priv->password);
339
 
        g_free (priv->auth_token);
340
 
        g_free (priv->client_id);
341
225
        g_free (priv->locale);
342
 
        g_static_mutex_free (&(priv->authentication_mutex));
343
226
 
344
227
        /* Chain up to the parent class */
345
228
        G_OBJECT_CLASS (gdata_service_parent_class)->finalize (object);
351
234
        GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
352
235
 
353
236
        switch (property_id) {
354
 
                case PROP_CLIENT_ID:
355
 
                        g_value_set_string (value, priv->client_id);
356
 
                        break;
357
 
                case PROP_USERNAME:
358
 
                        g_value_set_string (value, priv->username);
359
 
                        break;
360
 
                case PROP_PASSWORD:
361
 
                        g_value_set_string (value, priv->password);
362
 
                        break;
363
 
                case PROP_AUTHENTICATED:
364
 
                        g_value_set_boolean (value, priv->authenticated);
365
 
                        break;
366
237
                case PROP_PROXY_URI:
367
238
                        g_value_set_boxed (value, gdata_service_get_proxy_uri (GDATA_SERVICE (object)));
368
239
                        break;
372
243
                case PROP_LOCALE:
373
244
                        g_value_set_string (value, priv->locale);
374
245
                        break;
 
246
                case PROP_AUTHORIZER:
 
247
                        g_value_set_object (value, priv->authorizer);
 
248
                        break;
375
249
                default:
376
250
                        /* We don't have any other property... */
377
251
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
382
256
static void
383
257
gdata_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
384
258
{
385
 
        GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
386
 
 
387
259
        switch (property_id) {
388
 
                case PROP_CLIENT_ID:
389
 
                        priv->client_id = g_value_dup_string (value);
390
 
                        break;
391
260
                case PROP_PROXY_URI:
392
261
                        gdata_service_set_proxy_uri (GDATA_SERVICE (object), g_value_get_boxed (value));
393
262
                        break;
397
266
                case PROP_LOCALE:
398
267
                        gdata_service_set_locale (GDATA_SERVICE (object), g_value_get_string (value));
399
268
                        break;
 
269
                case PROP_AUTHORIZER:
 
270
                        gdata_service_set_authorizer (GDATA_SERVICE (object), g_value_get_object (value));
 
271
                        break;
400
272
                default:
401
273
                        /* We don't have any other property... */
402
274
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
404
276
        }
405
277
}
406
278
 
407
 
static gboolean
408
 
real_parse_authentication_response (GDataService *self, guint status, const gchar *response_body, gint length, GError **error)
409
 
{
410
 
        gchar *auth_start, *auth_end;
411
 
 
412
 
        /* Parse the response */
413
 
        auth_start = strstr (response_body, "Auth=");
414
 
        if (auth_start == NULL)
415
 
                goto protocol_error;
416
 
        auth_start += strlen ("Auth=");
417
 
 
418
 
        auth_end = strstr (auth_start, "\n");
419
 
        if (auth_end == NULL)
420
 
                goto protocol_error;
421
 
 
422
 
        self->priv->auth_token = g_strndup (auth_start, auth_end - auth_start);
423
 
        if (self->priv->auth_token == NULL || strlen (self->priv->auth_token) == 0)
424
 
                goto protocol_error;
425
 
 
426
 
        return TRUE;
427
 
 
428
 
protocol_error:
429
 
        g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
430
 
                             _("The server returned a malformed response."));
431
 
        return FALSE;
432
 
}
433
 
 
434
279
static void
435
 
real_append_query_headers (GDataService *self, SoupMessage *message)
 
280
real_append_query_headers (GDataService *self, GDataAuthorizationDomain *domain, SoupMessage *message)
436
281
{
437
 
        gchar *authorisation_header;
438
 
 
439
282
        g_assert (message != NULL);
440
283
 
441
284
        /* Set the authorisation header */
442
 
        g_static_mutex_lock (&(self->priv->authentication_mutex));
443
 
        if (self->priv->auth_token != NULL) {
444
 
                authorisation_header = g_strdup_printf ("GoogleLogin auth=%s", self->priv->auth_token);
445
 
                soup_message_headers_append (message->request_headers, "Authorization", authorisation_header);
446
 
                g_free (authorisation_header);
 
285
        if (self->priv->authorizer != NULL) {
 
286
                gdata_authorizer_process_request (self->priv->authorizer, domain, message);
 
287
 
 
288
                if (domain != NULL) {
 
289
                        /* Store the authorisation domain on the message so that we can access it again after refreshing authorisation if necessary.
 
290
                         * See _gdata_service_send_message(). */
 
291
                        g_object_set_data_full (G_OBJECT (message), "gdata-authorization-domain", g_object_ref (domain),
 
292
                                                (GDestroyNotify) g_object_unref);
 
293
                }
447
294
        }
448
 
        g_static_mutex_unlock (&(self->priv->authentication_mutex));
449
295
 
450
296
        /* Set the GData-Version header to tell it we want to use the v2 API */
451
297
        soup_message_headers_append (message->request_headers, "GData-Version", GDATA_SERVICE_GET_CLASS (self)->api_version);
562
408
        }
563
409
}
564
410
 
565
 
static void
566
 
set_authentication_details (GDataService *self, const gchar *username, const gchar *password, gboolean authenticated)
567
 
{
568
 
        GObject *service = G_OBJECT (self);
569
 
        GDataServicePrivate *priv = self->priv;
570
 
 
571
 
        g_static_mutex_lock (&(priv->authentication_mutex));
572
 
 
573
 
        g_object_freeze_notify (service);
574
 
 
575
 
        if (authenticated == TRUE) {
576
 
                /* Update several properties the service holds */
577
 
                g_free (priv->username);
578
 
 
579
 
                /* Ensure the username is always a full e-mail address */
580
 
                if (strchr (username, '@') == NULL)
581
 
                        priv->username = g_strdup_printf ("%s@" EMAIL_DOMAIN, username);
582
 
                else
583
 
                        priv->username = g_strdup (username);
584
 
 
585
 
                g_free (priv->password);
586
 
                priv->password = g_strdup (password);
587
 
 
588
 
                g_object_notify (service, "username");
589
 
                g_object_notify (service, "password");
590
 
        }
591
 
 
592
 
        if (priv->authenticated != authenticated) {
593
 
                priv->authenticated = authenticated;
594
 
                g_object_notify (service, "authenticated");
595
 
        }
596
 
 
597
 
        g_object_thaw_notify (service);
598
 
 
599
 
        g_static_mutex_unlock (&(priv->authentication_mutex));
600
 
}
601
 
 
602
 
static gboolean
603
 
authenticate (GDataService *self, const gchar *username, const gchar *password, gchar *captcha_token, gchar *captcha_answer,
604
 
              GCancellable *cancellable, GError **error)
605
 
{
606
 
        GDataServicePrivate *priv = self->priv;
607
 
        GDataServiceClass *klass;
608
 
        SoupMessage *message;
609
 
        gchar *request_body;
610
 
        guint status;
611
 
        gboolean retval;
612
 
 
613
 
        /* Prepare the request */
614
 
        klass = GDATA_SERVICE_GET_CLASS (self);
615
 
        request_body = soup_form_encode ("accountType", "HOSTED_OR_GOOGLE",
616
 
                                         "Email", username,
617
 
                                         "Passwd", password,
618
 
                                         "service", klass->service_name,
619
 
                                         "source", priv->client_id,
620
 
                                         (captcha_token == NULL) ? NULL : "logintoken", captcha_token,
621
 
                                         "loginanswer", captcha_answer,
622
 
                                         NULL);
623
 
 
624
 
        /* Free the CAPTCHA token and answer if necessary */
625
 
        g_free (captcha_token);
626
 
        g_free (captcha_answer);
627
 
 
628
 
        /* Build the message */
629
 
        message = soup_message_new (SOUP_METHOD_POST, klass->authentication_uri);
630
 
        soup_message_set_request (message, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, request_body, strlen (request_body));
631
 
 
632
 
        /* Send the message */
633
 
        status = _gdata_service_send_message (self, message, cancellable, error);
634
 
 
635
 
        if (status == SOUP_STATUS_CANCELLED) {
636
 
                /* Cancelled (the error has already been set) */
637
 
                g_object_unref (message);
638
 
                return FALSE;
639
 
        } else if (status != SOUP_STATUS_OK) {
640
 
                const gchar *response_body = message->response_body->data;
641
 
                gchar *error_start, *error_end, *uri_start, *uri_end, *uri = NULL;
642
 
 
643
 
                /* Parse the error response; see: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Errors */
644
 
                if (response_body == NULL)
645
 
                        goto protocol_error;
646
 
 
647
 
                /* Error */
648
 
                error_start = strstr (response_body, "Error=");
649
 
                if (error_start == NULL)
650
 
                        goto protocol_error;
651
 
                error_start += strlen ("Error=");
652
 
 
653
 
                error_end = strstr (error_start, "\n");
654
 
                if (error_end == NULL)
655
 
                        goto protocol_error;
656
 
 
657
 
                if (strncmp (error_start, "CaptchaRequired", error_end - error_start) == 0) {
658
 
                        const gchar *captcha_base_uri = "http://www.google.com/accounts/";
659
 
                        gchar *captcha_start, *captcha_end, *captcha_uri, *new_captcha_answer;
660
 
                        guint captcha_base_uri_length;
661
 
 
662
 
                        /* CAPTCHA required to log in */
663
 
                        captcha_start = strstr (response_body, "CaptchaUrl=");
664
 
                        if (captcha_start == NULL)
665
 
                                goto protocol_error;
666
 
                        captcha_start += strlen ("CaptchaUrl=");
667
 
 
668
 
                        captcha_end = strstr (captcha_start, "\n");
669
 
                        if (captcha_end == NULL)
670
 
                                goto protocol_error;
671
 
 
672
 
                        /* Do some fancy memory stuff to save ourselves another alloc */
673
 
                        captcha_base_uri_length = strlen (captcha_base_uri);
674
 
                        captcha_uri = g_malloc (captcha_base_uri_length + (captcha_end - captcha_start) + 1);
675
 
                        memcpy (captcha_uri, captcha_base_uri, captcha_base_uri_length);
676
 
                        memcpy (captcha_uri + captcha_base_uri_length, captcha_start, (captcha_end - captcha_start));
677
 
                        captcha_uri[captcha_base_uri_length + (captcha_end - captcha_start)] = '\0';
678
 
 
679
 
                        /* Request a CAPTCHA answer from the application */
680
 
                        g_signal_emit (self, service_signals[SIGNAL_CAPTCHA_CHALLENGE], 0, captcha_uri, &new_captcha_answer);
681
 
                        g_free (captcha_uri);
682
 
 
683
 
                        if (new_captcha_answer == NULL || *new_captcha_answer == '\0') {
684
 
                                /* Translators: see http://en.wikipedia.org/wiki/CAPTCHA for information about CAPTCHAs */
685
 
                                g_set_error_literal (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_CAPTCHA_REQUIRED,
686
 
                                                     _("A CAPTCHA must be filled out to log in."));
687
 
                                goto login_error;
688
 
                        }
689
 
 
690
 
                        /* Get the CAPTCHA token */
691
 
                        captcha_start = strstr (response_body, "CaptchaToken=");
692
 
                        if (captcha_start == NULL)
693
 
                                goto protocol_error;
694
 
                        captcha_start += strlen ("CaptchaToken=");
695
 
 
696
 
                        captcha_end = strstr (captcha_start, "\n");
697
 
                        if (captcha_end == NULL)
698
 
                                goto protocol_error;
699
 
 
700
 
                        /* Save the CAPTCHA token and answer, and attempt to log in with them */
701
 
                        g_object_unref (message);
702
 
 
703
 
                        return authenticate (self, username, password, g_strndup (captcha_start, captcha_end - captcha_start), new_captcha_answer,
704
 
                                             cancellable, error);
705
 
                } else if (strncmp (error_start, "Unknown", error_end - error_start) == 0) {
706
 
                        goto protocol_error;
707
 
                } else if (strncmp (error_start, "BadAuthentication", error_end - error_start) == 0) {
708
 
                        /* Looks like Error=BadAuthentication errors don't return a URI */
709
 
                        g_set_error_literal (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_BAD_AUTHENTICATION,
710
 
                                             _("Your username or password were incorrect."));
711
 
                        goto login_error;
712
 
                }
713
 
 
714
 
                /* Get the information URI */
715
 
                uri_start = strstr (response_body, "Url=");
716
 
                if (uri_start == NULL)
717
 
                        goto protocol_error;
718
 
                uri_start += strlen ("Url=");
719
 
 
720
 
                uri_end = strstr (uri_start, "\n");
721
 
                if (uri_end == NULL)
722
 
                        goto protocol_error;
723
 
 
724
 
                uri = g_strndup (uri_start, uri_end - uri_start);
725
 
 
726
 
                if (strncmp (error_start, "NotVerified", error_end - error_start) == 0) {
727
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_NOT_VERIFIED,
728
 
                                     /* Translators: the parameter is a URI for further information. */
729
 
                                     _("Your account's e-mail address has not been verified. (%s)"), uri);
730
 
                        goto login_error;
731
 
                } else if (strncmp (error_start, "TermsNotAgreed", error_end - error_start) == 0) {
732
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_TERMS_NOT_AGREED,
733
 
                                     /* Translators: the parameter is a URI for further information. */
734
 
                                     _("You have not agreed to the service's terms and conditions. (%s)"), uri);
735
 
                        goto login_error;
736
 
                } else if (strncmp (error_start, "AccountMigrated", error_end - error_start) == 0) {
737
 
                        /* This is non-standard, and used by YouTube since it's got messed-up accounts */
738
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_MIGRATED,
739
 
                                     /* Translators: the parameter is a URI for further information. */
740
 
                                     _("This account has been migrated. Please log in online to receive your new username and password. (%s)"), uri);
741
 
                        goto login_error;
742
 
                } else if (strncmp (error_start, "AccountDeleted", error_end - error_start) == 0) {
743
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_DELETED,
744
 
                                     /* Translators: the parameter is a URI for further information. */
745
 
                                     _("This account has been deleted. (%s)"), uri);
746
 
                        goto login_error;
747
 
                } else if (strncmp (error_start, "AccountDisabled", error_end - error_start) == 0) {
748
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_DISABLED,
749
 
                                     /* Translators: the parameter is a URI for further information. */
750
 
                                     _("This account has been disabled. (%s)"), uri);
751
 
                        goto login_error;
752
 
                } else if (strncmp (error_start, "ServiceDisabled", error_end - error_start) == 0) {
753
 
                        g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_SERVICE_DISABLED,
754
 
                                     /* Translators: the parameter is a URI for further information. */
755
 
                                     _("This account's access to this service has been disabled. (%s)"), uri);
756
 
                        goto login_error;
757
 
                } else if (strncmp (error_start, "ServiceUnavailable", error_end - error_start) == 0) {
758
 
                        g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_UNAVAILABLE,
759
 
                                     /* Translators: the parameter is a URI for further information. */
760
 
                                     _("This service is not available at the moment. (%s)"), uri);
761
 
                        goto login_error;
762
 
                }
763
 
 
764
 
                /* Unknown error type! */
765
 
                goto protocol_error;
766
 
 
767
 
login_error:
768
 
                g_free (uri);
769
 
                g_object_unref (message);
770
 
 
771
 
                return FALSE;
772
 
        }
773
 
 
774
 
        g_assert (message->response_body->data != NULL);
775
 
 
776
 
        g_static_mutex_lock (&(priv->authentication_mutex));
777
 
        retval = klass->parse_authentication_response (self, status, message->response_body->data, message->response_body->length, error);
778
 
        g_static_mutex_unlock (&(priv->authentication_mutex));
779
 
 
780
 
        g_object_unref (message);
781
 
 
782
 
        return retval;
783
 
 
784
 
protocol_error:
785
 
        g_assert (klass->parse_error_response != NULL);
786
 
        klass->parse_error_response (self, GDATA_OPERATION_AUTHENTICATION, status, message->reason_phrase, message->response_body->data,
787
 
                                     message->response_body->length, error);
788
 
 
789
 
        g_object_unref (message);
790
 
 
791
 
        return FALSE;
792
 
}
793
 
 
794
 
typedef struct {
795
 
        gchar *username;
796
 
        gchar *password;
797
 
} AuthenticateAsyncData;
798
 
 
799
 
static void
800
 
authenticate_async_data_free (AuthenticateAsyncData *self)
801
 
{
802
 
        g_free (self->username);
803
 
        g_free (self->password);
804
 
 
805
 
        g_slice_free (AuthenticateAsyncData, self);
806
 
}
807
 
 
808
 
static void
809
 
authenticate_thread (GSimpleAsyncResult *result, GDataService *service, GCancellable *cancellable)
810
 
{
811
 
        GError *error = NULL;
812
 
        gboolean success;
813
 
        AuthenticateAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
814
 
 
815
 
        /* Authenticate and return */
816
 
        success = authenticate (service, data->username, data->password, NULL, NULL, cancellable, &error);
817
 
        set_authentication_details (service, data->username, data->password, success);
818
 
        g_simple_async_result_set_op_res_gboolean (result, success);
819
 
 
820
 
        if (success == FALSE) {
821
 
                g_simple_async_result_set_from_error (result, error);
822
 
                g_error_free (error);
823
 
        }
824
 
}
825
 
 
826
 
/**
827
 
 * gdata_service_authenticate_async:
828
 
 * @self: a #GDataService
829
 
 * @username: the user's username
830
 
 * @password: the user's password
831
 
 * @cancellable: optional #GCancellable object, or %NULL
832
 
 * @callback: a #GAsyncReadyCallback to call when authentication is finished
833
 
 * @user_data: (closure): data to pass to the @callback function
834
 
 *
835
 
 * Authenticates the #GDataService with the online service using the given @username and @password. @self, @username and
836
 
 * @password are all reffed/copied when this function is called, so can safely be freed after this function returns.
837
 
 *
838
 
 * For more details, see gdata_service_authenticate(), which is the synchronous version of this function.
839
 
 *
840
 
 * When the operation is finished, @callback will be called. You can then call gdata_service_authenticate_finish()
841
 
 * to get the results of the operation.
842
 
 **/
 
411
/**
 
412
 * gdata_service_is_authorized:
 
413
 * @self: a #GDataService
 
414
 *
 
415
 * Determines whether the service is authorized for all the #GDataAuthorizationDomain<!-- -->s it belongs to (as returned by
 
416
 * gdata_service_get_authorization_domains()). If the service's #GDataService:authorizer is %NULL, %FALSE is always returned.
 
417
 *
 
418
 * This is basically a convenience method for checking that the service's #GDataAuthorizer is authorized for all the service's
 
419
 * #GDataAuthorizationDomain<!-- -->s.
 
420
 *
 
421
 * Return value: %TRUE if the service is authorized for all its domains, %FALSE otherwise
 
422
 *
 
423
 * Since: 0.9.0
 
424
 */
 
425
gboolean
 
426
gdata_service_is_authorized (GDataService *self)
 
427
{
 
428
        GList *domains, *i;
 
429
        gboolean authorised = TRUE;
 
430
 
 
431
        g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
 
432
 
 
433
        /* If we don't have an authoriser set, we can't be authorised */
 
434
        if (self->priv->authorizer == NULL) {
 
435
                return FALSE;
 
436
        }
 
437
 
 
438
        domains = gdata_service_get_authorization_domains (G_OBJECT_TYPE (self));
 
439
 
 
440
        /* Find any domains which we're not authorised for */
 
441
        for (i = domains; i != NULL; i = i->next) {
 
442
                if (gdata_authorizer_is_authorized_for_domain (self->priv->authorizer, GDATA_AUTHORIZATION_DOMAIN (i->data)) == FALSE) {
 
443
                        authorised = FALSE;
 
444
                        break;
 
445
                }
 
446
        }
 
447
 
 
448
        g_list_free (domains);
 
449
 
 
450
        return authorised;
 
451
}
 
452
 
 
453
/**
 
454
 * gdata_service_get_authorizer:
 
455
 * @self: a #GDataService
 
456
 *
 
457
 * Gets the #GDataAuthorizer object currently in use by the service. See the documentation for #GDataService:authorizer for more details.
 
458
 *
 
459
 * Return value: (transfer none): the authorizer object for this service, or %NULL
 
460
 *
 
461
 * Since: 0.9.0
 
462
 */
 
463
GDataAuthorizer *
 
464
gdata_service_get_authorizer (GDataService *self)
 
465
{
 
466
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
 
467
 
 
468
        return self->priv->authorizer;
 
469
}
 
470
 
 
471
/**
 
472
 * gdata_service_set_authorizer:
 
473
 * @self: a #GDataService
 
474
 * @authorizer: a new authorizer object for the service, or %NULL
 
475
 *
 
476
 * Sets #GDataService:authorizer to @authorizer. This may be %NULL if the service will only make requests in future which don't require authorization.
 
477
 * See the documentation for #GDataService:authorizer for more information.
 
478
 *
 
479
 * Since: 0.9.0
 
480
 */
843
481
void
844
 
gdata_service_authenticate_async (GDataService *self, const gchar *username, const gchar *password,
845
 
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
482
gdata_service_set_authorizer (GDataService *self, GDataAuthorizer *authorizer)
846
483
{
847
 
        GSimpleAsyncResult *result;
848
 
        AuthenticateAsyncData *data;
 
484
        GDataServicePrivate *priv = self->priv;
849
485
 
850
486
        g_return_if_fail (GDATA_IS_SERVICE (self));
851
 
        g_return_if_fail (username != NULL);
852
 
        g_return_if_fail (password != NULL);
853
 
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
854
 
 
855
 
        data = g_slice_new (AuthenticateAsyncData);
856
 
        data->username = g_strdup (username);
857
 
        data->password = g_strdup (password);
858
 
 
859
 
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_authenticate_async);
860
 
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) authenticate_async_data_free);
861
 
        g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) authenticate_thread, G_PRIORITY_DEFAULT, cancellable);
862
 
        g_object_unref (result);
863
 
}
864
 
 
865
 
/**
866
 
 * gdata_service_authenticate_finish:
867
 
 * @self: a #GDataService
868
 
 * @async_result: a #GAsyncResult
869
 
 * @error: a #GError, or %NULL
870
 
 *
871
 
 * Finishes an asynchronous authentication operation started with gdata_service_authenticate_async().
872
 
 *
873
 
 * Return value: %TRUE if authentication was successful, %FALSE otherwise
874
 
 **/
875
 
gboolean
876
 
gdata_service_authenticate_finish (GDataService *self, GAsyncResult *async_result, GError **error)
877
 
{
878
 
        GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
879
 
        gboolean success;
880
 
 
881
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
882
 
        g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
883
 
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
884
 
 
885
 
        g_warn_if_fail (g_simple_async_result_get_source_tag (result) == gdata_service_authenticate_async);
886
 
 
887
 
        if (g_simple_async_result_propagate_error (result, error) == TRUE)
888
 
                return FALSE;
889
 
 
890
 
        success = g_simple_async_result_get_op_res_gboolean (result);
891
 
        g_assert (success == TRUE);
892
 
 
893
 
        return success;
894
 
}
895
 
 
896
 
/**
897
 
 * gdata_service_authenticate:
898
 
 * @self: a #GDataService
899
 
 * @username: the user's username
900
 
 * @password: the user's password
901
 
 * @cancellable: optional #GCancellable object, or %NULL
902
 
 * @error: a #GError, or %NULL
903
 
 *
904
 
 * Authenticates the #GDataService with the online service using @username and @password; i.e. logs into the service with the given
905
 
 * 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
906
 
 * not given, @username will have <literal>\@gmail.com</literal> appended to create an e-mail address
907
 
 *
908
 
 * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
909
 
 * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
910
 
 *
911
 
 * A %GDATA_AUTHENTICATION_ERROR_BAD_AUTHENTICATION will be returned if authentication failed due to an incorrect username or password.
912
 
 * Other #GDataAuthenticationError errors can be returned for other conditions.
913
 
 *
914
 
 * If the service requires a CAPTCHA to be completed, the #GDataService::captcha-challenge signal will be emitted. The return value from
915
 
 * 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,
916
 
 * authentication will fail with a %GDATA_AUTHENTICATION_ERROR_CAPTCHA_REQUIRED error. Otherwise, authentication will be automatically and
917
 
 * transparently restarted with the new CAPTCHA details.
918
 
 *
919
 
 * A %GDATA_SERVICE_ERROR_PROTOCOL_ERROR will be returned if the server's responses were invalid. Subclasses of #GDataService can override
920
 
 * parsing the authentication response, and may return their own error codes. See their documentation for more details.
921
 
 *
922
 
 * Return value: %TRUE if authentication was successful, %FALSE otherwise
923
 
 **/
924
 
gboolean
925
 
gdata_service_authenticate (GDataService *self, const gchar *username, const gchar *password, GCancellable *cancellable, GError **error)
926
 
{
927
 
        gboolean retval;
928
 
 
929
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
930
 
        g_return_val_if_fail (username != NULL, FALSE);
931
 
        g_return_val_if_fail (password != NULL, FALSE);
932
 
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
933
 
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
934
 
 
935
 
        retval = authenticate (self, username, password, NULL, NULL, cancellable, error);
936
 
        set_authentication_details (self, username, password, retval);
937
 
 
938
 
        return retval;
 
487
        g_return_if_fail (authorizer == NULL || GDATA_IS_AUTHORIZER (authorizer));
 
488
 
 
489
        if (priv->authorizer != NULL) {
 
490
                g_object_unref (priv->authorizer);
 
491
        }
 
492
 
 
493
        priv->authorizer = authorizer;
 
494
 
 
495
        if (priv->authorizer != NULL) {
 
496
                g_object_ref (priv->authorizer);
 
497
        }
 
498
 
 
499
        g_object_notify (G_OBJECT (self), "authorizer");
 
500
}
 
501
 
 
502
/**
 
503
 * gdata_service_get_authorization_domains:
 
504
 * @service_type: the #GType of the #GDataService subclass to retrieve the authorization domains for
 
505
 *
 
506
 * Retrieves the full list of #GDataAuthorizationDomain<!-- -->s which relate to the specified @service_type. All the
 
507
 * #GDataAuthorizationDomain<!-- -->s are unique and interned, so can be compared with other domains by simple pointer comparison.
 
508
 *
 
509
 * Note that in addition to this method, #GDataService subclasses may expose some or all of their authorization domains individually by means of
 
510
 * individual accessor functions.
 
511
 *
 
512
 * Return value: (transfer container) (element-type GDataAuthorizationDomain): an unordered list of #GDataAuthorizationDomain<!-- -->s; free with
 
513
 * g_list_free()
 
514
 *
 
515
 * Since: 0.9.0
 
516
 */
 
517
GList *
 
518
gdata_service_get_authorization_domains (GType service_type)
 
519
{
 
520
        GDataServiceClass *klass;
 
521
        GList *domains = NULL;
 
522
 
 
523
        g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE), NULL);
 
524
 
 
525
        klass = GDATA_SERVICE_CLASS (g_type_class_ref (service_type));
 
526
        if (klass->get_authorization_domains != NULL) {
 
527
                domains = klass->get_authorization_domains ();
 
528
        }
 
529
        g_type_class_unref (klass);
 
530
 
 
531
        return domains;
939
532
}
940
533
 
941
534
SoupMessage *
942
 
_gdata_service_build_message (GDataService *self, const gchar *method, const gchar *uri, const gchar *etag, gboolean etag_if_match)
 
535
_gdata_service_build_message (GDataService *self, GDataAuthorizationDomain *domain, const gchar *method, const gchar *uri,
 
536
                              const gchar *etag, gboolean etag_if_match)
943
537
{
944
538
        SoupMessage *message;
945
539
        GDataServiceClass *klass;
950
544
        /* Make sure subclasses set their headers */
951
545
        klass = GDATA_SERVICE_GET_CLASS (self);
952
546
        if (klass->append_query_headers != NULL)
953
 
                klass->append_query_headers (self, message);
 
547
                klass->append_query_headers (self, domain, message);
954
548
 
955
549
        /* Append the ETag header if possible */
956
550
        if (etag != NULL)
991
585
        MessageData data;
992
586
        gulong cancel_signal = 0, request_queued_signal = 0;
993
587
 
 
588
        /* Hold references to the session and message so they can't be freed by other threads. For example, if the SoupSession was freed by another
 
589
         * thread while we were making a request, the request would be unexpectedly cancelled. See bgo#650835 for an example of this breaking things.
 
590
         */
 
591
        g_object_ref (session);
 
592
        g_object_ref (message);
 
593
 
994
594
        /* Listen for cancellation */
995
595
        if (cancellable != NULL) {
996
596
                g_static_mutex_init (&(data.mutex));
1029
629
                g_static_mutex_free (&(data.mutex));
1030
630
        }
1031
631
 
1032
 
        /* Set the cancellation error if applicable */
 
632
        /* Set the cancellation error if applicable. We can't assume that our GCancellable has been cancelled just because the message has;
 
633
         * libsoup may internally cancel messages if, for example, the proxy URI of the SoupSession is changed.
 
634
         * libsoup also sometimes seems to return a SOUP_STATUS_IO_ERROR when we cancel a message, even though we've specified SOUP_STATUS_CANCELLED
 
635
         * at cancellation time. Ho Hum. */
1033
636
        g_assert (message->status_code != SOUP_STATUS_NONE);
1034
 
        if (message->status_code == SOUP_STATUS_CANCELLED)
1035
 
                g_assert (cancellable != NULL && g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE);
 
637
 
 
638
        if (message->status_code == SOUP_STATUS_CANCELLED ||
 
639
            (message->status_code == SOUP_STATUS_IO_ERROR && cancellable != NULL && g_cancellable_is_cancelled (cancellable) == TRUE)) {
 
640
                /* We hackily create and cancel a new GCancellable so that we can set the error using it and therefore save ourselves a translatable
 
641
                 * string and the associated maintenance. */
 
642
                GCancellable *error_cancellable = g_cancellable_new ();
 
643
                g_cancellable_cancel (error_cancellable);
 
644
                g_assert (g_cancellable_set_error_if_cancelled (error_cancellable, error) == TRUE);
 
645
                g_object_unref (error_cancellable);
 
646
 
 
647
                /* As per the above comment, force the status to be SOUP_STATUS_CANCELLED. */
 
648
                soup_message_set_status (message, SOUP_STATUS_CANCELLED);
 
649
        }
 
650
 
 
651
        /* Free things */
 
652
        g_object_unref (message);
 
653
        g_object_unref (session);
1036
654
}
1037
655
 
1038
656
guint
1074
692
                _gdata_service_actually_send_message (self->priv->session, message, cancellable, error);
1075
693
        }
1076
694
 
 
695
        /* Not authorised, or authorisation has expired. If we were authorised in the first place, attempt to refresh the authorisation and
 
696
         * try sending the message again (but only once, so we don't get caught in an infinite loop of denied authorisation errors).
 
697
         *
 
698
         * Note that we have to re-process the message with the authoriser so that its authorisation headers get updated after the refresh
 
699
         * (bgo#653535). */
 
700
        if (message->status_code == SOUP_STATUS_UNAUTHORIZED) {
 
701
                GDataAuthorizer *authorizer = self->priv->authorizer;
 
702
 
 
703
                if (authorizer != NULL && gdata_authorizer_refresh_authorization (authorizer, cancellable, NULL) == TRUE) {
 
704
                        GDataAuthorizationDomain *domain;
 
705
 
 
706
                        /* Re-process the request */
 
707
                        domain = g_object_get_data (G_OBJECT (message), "gdata-authorization-domain");
 
708
                        g_assert (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
 
709
 
 
710
                        gdata_authorizer_process_request (authorizer, domain, message);
 
711
 
 
712
                        /* Send the message again */
 
713
                        g_clear_error (error);
 
714
                        _gdata_service_actually_send_message (self->priv->session, message, cancellable, error);
 
715
                }
 
716
        }
 
717
 
1077
718
        return message->status_code;
1078
719
}
1079
720
 
1080
721
typedef struct {
1081
722
        /* Input */
 
723
        GDataAuthorizationDomain *domain;
1082
724
        gchar *feed_uri;
1083
725
        GDataQuery *query;
1084
726
        GType entry_type;
1087
729
        GDataFeed *feed;
1088
730
        GDataQueryProgressCallback progress_callback;
1089
731
        gpointer progress_user_data;
 
732
        GDestroyNotify destroy_progress_user_data;
1090
733
} QueryAsyncData;
1091
734
 
1092
735
static void
1093
736
query_async_data_free (QueryAsyncData *self)
1094
737
{
 
738
        if (self->domain != NULL)
 
739
                g_object_unref (self->domain);
 
740
 
1095
741
        g_free (self->feed_uri);
1096
742
        if (self->query)
1097
743
                g_object_unref (self->query);
1108
754
        QueryAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
1109
755
 
1110
756
        /* Execute the query and return */
1111
 
        data->feed = gdata_service_query (service, data->feed_uri, data->query, data->entry_type, cancellable,
1112
 
                                          data->progress_callback, data->progress_user_data, &error);
 
757
        data->feed = __gdata_service_query (service, data->domain, data->feed_uri, data->query, data->entry_type, cancellable,
 
758
                                            data->progress_callback, data->progress_user_data, &error, TRUE);
1113
759
        if (data->feed == NULL && error != NULL) {
1114
760
                g_simple_async_result_set_from_error (result, error);
1115
761
                g_error_free (error);
1116
762
        }
 
763
 
 
764
        if (data->destroy_progress_user_data != NULL) {
 
765
                data->destroy_progress_user_data (data->progress_user_data);
 
766
        }
1117
767
}
1118
768
 
1119
769
/**
1120
 
 * gdata_service_query_async: (skip)
 
770
 * gdata_service_query_async:
1121
771
 * @self: a #GDataService
 
772
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
1122
773
 * @feed_uri: the feed URI to query, including the host name and protocol
1123
774
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
1124
775
 * @entry_type: a #GType for the #GDataEntry<!-- -->s to build from the XML
1125
 
 * @cancellable: optional #GCancellable object, or %NULL
1126
 
 * @progress_callback: a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
 
776
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
 
777
 * @progress_callback: (allow-none) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
1127
778
 * @progress_user_data: (closure): data to pass to the @progress_callback function
 
779
 * @destroy_progress_user_data: (allow-none): the function to call when @progress_callback will not be called any more, or %NULL. This function will be
 
780
 * called with @progress_user_data as a parameter and can be used to free any memory allocated for it.
1128
781
 * @callback: a #GAsyncReadyCallback to call when the query is finished
1129
782
 * @user_data: (closure): data to pass to the @callback function
1130
783
 *
1135
788
 *
1136
789
 * When the operation is finished, @callback will be called. You can then call gdata_service_query_finish()
1137
790
 * to get the results of the operation.
 
791
 *
 
792
 * Since: 0.9.1
1138
793
 **/
1139
794
void
1140
 
gdata_service_query_async (GDataService *self, const gchar *feed_uri, GDataQuery *query, GType entry_type, GCancellable *cancellable,
1141
 
                           GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
1142
 
                           GAsyncReadyCallback callback, gpointer user_data)
 
795
gdata_service_query_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
 
796
                           GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
 
797
                           GDestroyNotify destroy_progress_user_data, GAsyncReadyCallback callback, gpointer user_data)
1143
798
{
1144
799
        GSimpleAsyncResult *result;
1145
800
        QueryAsyncData *data;
1146
801
 
1147
802
        g_return_if_fail (GDATA_IS_SERVICE (self));
 
803
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
1148
804
        g_return_if_fail (feed_uri != NULL);
1149
805
        g_return_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY));
1150
806
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1151
807
        g_return_if_fail (callback != NULL);
1152
808
 
1153
809
        data = g_slice_new (QueryAsyncData);
 
810
        data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
1154
811
        data->feed_uri = g_strdup (feed_uri);
1155
812
        data->query = (query != NULL) ? g_object_ref (query) : NULL;
1156
813
        data->entry_type = entry_type;
 
814
        data->feed = NULL;
1157
815
        data->progress_callback = progress_callback;
1158
816
        data->progress_user_data = progress_user_data;
 
817
        data->destroy_progress_user_data = destroy_progress_user_data;
1159
818
 
1160
819
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_query_async);
1161
820
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) query_async_data_free);
1197
856
/* Does the bulk of the work of gdata_service_query. Split out because certain queries (such as that done by
1198
857
 * gdata_service_query_single_entry()) only return a single entry, and thus need special parsing code. */
1199
858
SoupMessage *
1200
 
_gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GCancellable *cancellable, GError **error)
 
859
_gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query,
 
860
                      GCancellable *cancellable, GError **error)
1201
861
{
1202
862
        SoupMessage *message;
1203
863
        guint status;
1210
870
        /* Build the message */
1211
871
        if (query != NULL) {
1212
872
                gchar *query_uri = gdata_query_get_query_uri (query, feed_uri);
1213
 
                message = _gdata_service_build_message (self, SOUP_METHOD_GET, query_uri, etag, FALSE);
 
873
                message = _gdata_service_build_message (self, domain, SOUP_METHOD_GET, query_uri, etag, FALSE);
1214
874
                g_free (query_uri);
1215
875
        } else {
1216
 
                message = _gdata_service_build_message (self, SOUP_METHOD_GET, feed_uri, etag, FALSE);
 
876
                message = _gdata_service_build_message (self, domain, SOUP_METHOD_GET, feed_uri, etag, FALSE);
1217
877
        }
1218
878
 
1219
879
        /* Note that cancellation only applies to network activity; not to the processing done afterwards */
1236
896
        return message;
1237
897
}
1238
898
 
 
899
static GDataFeed *
 
900
__gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
 
901
                       GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error,
 
902
                       gboolean is_async)
 
903
{
 
904
        GDataServiceClass *klass;
 
905
        GDataFeed *feed;
 
906
        SoupMessage *message;
 
907
 
 
908
        message = _gdata_service_query (self, domain, feed_uri, query, cancellable, error);
 
909
        if (message == NULL)
 
910
                return NULL;
 
911
 
 
912
        g_assert (message->response_body->data != NULL);
 
913
        klass = GDATA_SERVICE_GET_CLASS (self);
 
914
        feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, message->response_body->length, entry_type,
 
915
                                         progress_callback, progress_user_data, is_async, error);
 
916
        g_object_unref (message);
 
917
 
 
918
        if (feed == NULL)
 
919
                return NULL;
 
920
 
 
921
        /* Update the query with the feed's ETag */
 
922
        if (query != NULL && feed != NULL && gdata_feed_get_etag (feed) != NULL)
 
923
                gdata_query_set_etag (query, gdata_feed_get_etag (feed));
 
924
 
 
925
        /* Update the query with the next and previous URIs from the feed */
 
926
        if (query != NULL && feed != NULL) {
 
927
                GDataLink *_link;
 
928
 
 
929
                _link = gdata_feed_look_up_link (feed, "next");
 
930
                if (_link != NULL)
 
931
                        _gdata_query_set_next_uri (query, gdata_link_get_uri (_link));
 
932
                _link = gdata_feed_look_up_link (feed, "previous");
 
933
                if (_link != NULL)
 
934
                        _gdata_query_set_previous_uri (query, gdata_link_get_uri (_link));
 
935
        }
 
936
 
 
937
        return feed;
 
938
}
 
939
 
1239
940
/**
1240
941
 * gdata_service_query:
1241
942
 * @self: a #GDataService
 
943
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
1242
944
 * @feed_uri: the feed URI to query, including the host name and protocol
1243
945
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
1244
946
 * @entry_type: a #GType for the #GDataEntry<!-- -->s to build from the XML
1245
 
 * @cancellable: optional #GCancellable object, or %NULL
1246
 
 * @progress_callback: (scope call): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
 
947
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
 
948
 * @progress_callback: (allow-none) (scope call) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
1247
949
 * @progress_user_data: (closure): data to pass to the @progress_callback function
1248
950
 * @error: a #GError, or %NULL
1249
951
 *
1268
970
 * @query's ETag will be updated with the ETag from the returned feed, if available.
1269
971
 *
1270
972
 * Return value: (transfer full): a #GDataFeed of query results, or %NULL; unref with g_object_unref()
 
973
 *
 
974
 * Since: 0.9.0
1271
975
 **/
1272
976
GDataFeed *
1273
 
gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GType entry_type,
 
977
gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
1274
978
                     GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
1275
979
{
1276
 
        GDataServiceClass *klass;
1277
 
        GDataFeed *feed;
1278
 
        SoupMessage *message;
1279
 
 
1280
980
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
 
981
        g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
1281
982
        g_return_val_if_fail (feed_uri != NULL, NULL);
1282
983
        g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY), NULL);
1283
984
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1284
985
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1285
986
 
1286
 
        message = _gdata_service_query (self, feed_uri, query, cancellable, error);
1287
 
        if (message == NULL)
1288
 
                return NULL;
1289
 
 
1290
 
        g_assert (message->response_body->data != NULL);
1291
 
        klass = GDATA_SERVICE_GET_CLASS (self);
1292
 
        feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, message->response_body->length, entry_type,
1293
 
                                         progress_callback, progress_user_data, error);
1294
 
        g_object_unref (message);
1295
 
 
1296
 
        if (feed == NULL)
1297
 
                return NULL;
1298
 
 
1299
 
        /* Update the query with the feed's ETag */
1300
 
        if (query != NULL && feed != NULL && gdata_feed_get_etag (feed) != NULL)
1301
 
                gdata_query_set_etag (query, gdata_feed_get_etag (feed));
1302
 
 
1303
 
        /* Update the query with the next and previous URIs from the feed */
1304
 
        if (query != NULL && feed != NULL) {
1305
 
                GDataLink *_link;
1306
 
 
1307
 
                _link = gdata_feed_look_up_link (feed, "next");
1308
 
                if (_link != NULL)
1309
 
                        _gdata_query_set_next_uri (query, gdata_link_get_uri (_link));
1310
 
                _link = gdata_feed_look_up_link (feed, "previous");
1311
 
                if (_link != NULL)
1312
 
                        _gdata_query_set_previous_uri (query, gdata_link_get_uri (_link));
1313
 
        }
1314
 
 
1315
 
        return feed;
 
987
        return __gdata_service_query (self, domain, feed_uri, query, entry_type, cancellable, progress_callback, progress_user_data, error, FALSE);
1316
988
}
1317
989
 
1318
990
/**
1319
991
 * gdata_service_query_single_entry:
1320
992
 * @self: a #GDataService
 
993
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
1321
994
 * @entry_id: the entry ID of the desired entry
1322
995
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
1323
996
 * @entry_type: a #GType for the #GDataEntry to build from the XML
1324
 
 * @cancellable: a #GCancellable, or %NULL
 
997
 * @cancellable: (allow-none): a #GCancellable, or %NULL
1325
998
 * @error: a #GError, or %NULL
1326
999
 *
1327
1000
 * Retrieves information about the single entry with the given @entry_id. @entry_id should be as returned by
1335
1008
 *
1336
1009
 * Return value: (transfer full): a #GDataEntry, or %NULL; unref with g_object_unref()
1337
1010
 *
1338
 
 * Since: 0.7.0
 
1011
 * Since: 0.9.0
1339
1012
 **/
1340
1013
GDataEntry *
1341
 
gdata_service_query_single_entry (GDataService *self, const gchar *entry_id, GDataQuery *query, GType entry_type,
 
1014
gdata_service_query_single_entry (GDataService *self, GDataAuthorizationDomain *domain, const gchar *entry_id, GDataQuery *query, GType entry_type,
1342
1015
                                  GCancellable *cancellable, GError **error)
1343
1016
{
1344
1017
        GDataEntryClass *klass;
1347
1020
        SoupMessage *message;
1348
1021
 
1349
1022
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
 
1023
        g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
1350
1024
        g_return_val_if_fail (entry_id != NULL, NULL);
1351
1025
        g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
1352
1026
        g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY) == TRUE, NULL);
1358
1032
        g_assert (klass->get_entry_uri != NULL);
1359
1033
 
1360
1034
        entry_uri = klass->get_entry_uri (entry_id);
1361
 
        message = _gdata_service_query (GDATA_SERVICE (self), entry_uri, query, cancellable, error);
 
1035
        message = _gdata_service_query (GDATA_SERVICE (self), domain, entry_uri, query, cancellable, error);
1362
1036
        g_free (entry_uri);
1363
1037
 
1364
1038
        if (message == NULL) {
1375
1049
}
1376
1050
 
1377
1051
typedef struct {
 
1052
        GDataAuthorizationDomain *domain;
1378
1053
        gchar *entry_id;
1379
1054
        GDataQuery *query;
1380
1055
        GType entry_type;
1383
1058
static void
1384
1059
query_single_entry_async_data_free (QuerySingleEntryAsyncData *data)
1385
1060
{
 
1061
        if (data->domain != NULL)
 
1062
                g_object_unref (data->domain);
 
1063
 
1386
1064
        g_free (data->entry_id);
1387
1065
        if (data->query != NULL)
1388
1066
                g_object_unref (data->query);
1397
1075
        QuerySingleEntryAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
1398
1076
 
1399
1077
        /* Execute the query and return */
1400
 
        entry = gdata_service_query_single_entry (service, data->entry_id, data->query, data->entry_type, cancellable, &error);
 
1078
        entry = gdata_service_query_single_entry (service, data->domain, data->entry_id, data->query, data->entry_type, cancellable, &error);
1401
1079
        if (entry == NULL && error != NULL) {
1402
1080
                g_simple_async_result_set_from_error (result, error);
1403
1081
                g_error_free (error);
 
1082
                return;
1404
1083
        }
1405
1084
 
1406
1085
        g_simple_async_result_set_op_res_gpointer (result, entry, (GDestroyNotify) g_object_unref);
1409
1088
/**
1410
1089
 * gdata_service_query_single_entry_async:
1411
1090
 * @self: a #GDataService
 
1091
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
1412
1092
 * @entry_id: the entry ID of the desired entry
1413
1093
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
1414
1094
 * @entry_type: a #GType for the #GDataEntry to build from the XML
1415
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1095
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1416
1096
 * @callback: a #GAsyncReadyCallback to call when the query is finished
1417
1097
 * @user_data: (closure): data to pass to the @callback function
1418
1098
 *
1425
1105
 * When the operation is finished, @callback will be called. You can then call gdata_service_query_single_entry_finish()
1426
1106
 * to get the results of the operation.
1427
1107
 *
1428
 
 * Since: 0.7.0
 
1108
 * Since: 0.9.0
1429
1109
 **/
1430
1110
void
1431
 
gdata_service_query_single_entry_async (GDataService *self, const gchar *entry_id, GDataQuery *query, GType entry_type,
1432
 
                                        GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
1111
gdata_service_query_single_entry_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *entry_id, GDataQuery *query,
 
1112
                                        GType entry_type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
1433
1113
{
1434
1114
        GSimpleAsyncResult *result;
1435
1115
        QuerySingleEntryAsyncData *data;
1436
1116
 
1437
1117
        g_return_if_fail (GDATA_IS_SERVICE (self));
 
1118
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
1438
1119
        g_return_if_fail (entry_id != NULL);
1439
1120
        g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
1440
1121
        g_return_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY) == TRUE);
1442
1123
        g_return_if_fail (callback != NULL);
1443
1124
 
1444
1125
        data = g_slice_new (QuerySingleEntryAsyncData);
 
1126
        data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
1445
1127
        data->query = (query != NULL) ? g_object_ref (query) : NULL;
1446
1128
        data->entry_id = g_strdup (entry_id);
1447
1129
        data->entry_type = entry_type;
1486
1168
}
1487
1169
 
1488
1170
typedef struct {
 
1171
        GDataAuthorizationDomain *domain;
1489
1172
        gchar *upload_uri;
1490
1173
        GDataEntry *entry;
1491
1174
} InsertEntryAsyncData;
1493
1176
static void
1494
1177
insert_entry_async_data_free (InsertEntryAsyncData *self)
1495
1178
{
 
1179
        if (self->domain != NULL)
 
1180
                g_object_unref (self->domain);
 
1181
 
1496
1182
        g_free (self->upload_uri);
1497
1183
        if (self->entry)
1498
1184
                g_object_unref (self->entry);
1508
1194
        InsertEntryAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
1509
1195
 
1510
1196
        /* Insert the entry and return */
1511
 
        updated_entry = gdata_service_insert_entry (service, data->upload_uri, data->entry, cancellable, &error);
 
1197
        updated_entry = gdata_service_insert_entry (service, data->domain, data->upload_uri, data->entry, cancellable, &error);
1512
1198
        if (updated_entry == NULL) {
1513
1199
                g_simple_async_result_set_from_error (result, error);
1514
1200
                g_error_free (error);
1522
1208
/**
1523
1209
 * gdata_service_insert_entry_async:
1524
1210
 * @self: a #GDataService
 
1211
 * @domain: (allow-none): the #GDataAuthorizationDomain the insertion operation falls under, or %NULL
1525
1212
 * @upload_uri: the URI to which the upload should be sent
1526
1213
 * @entry: the #GDataEntry to insert
1527
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1214
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1528
1215
 * @callback: a #GAsyncReadyCallback to call when insertion is finished, or %NULL
1529
1216
 * @user_data: (closure): data to pass to the @callback function
1530
1217
 *
1536
1223
 * When the operation is finished, @callback will be called. You can then call gdata_service_insert_entry_finish()
1537
1224
 * to get the results of the operation.
1538
1225
 *
1539
 
 * Since: 0.3.0
 
1226
 * Since: 0.9.0
1540
1227
 **/
1541
1228
void
1542
 
gdata_service_insert_entry_async (GDataService *self, const gchar *upload_uri, GDataEntry *entry, GCancellable *cancellable,
1543
 
                                  GAsyncReadyCallback callback, gpointer user_data)
 
1229
gdata_service_insert_entry_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *upload_uri, GDataEntry *entry,
 
1230
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
1544
1231
{
1545
1232
        GSimpleAsyncResult *result;
1546
1233
        InsertEntryAsyncData *data;
1547
1234
 
1548
1235
        g_return_if_fail (GDATA_IS_SERVICE (self));
 
1236
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
1549
1237
        g_return_if_fail (upload_uri != NULL);
1550
1238
        g_return_if_fail (GDATA_IS_ENTRY (entry));
1551
1239
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1552
1240
 
1553
1241
        data = g_slice_new (InsertEntryAsyncData);
 
1242
        data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
1554
1243
        data->upload_uri = g_strdup (upload_uri);
1555
1244
        data->entry = g_object_ref (entry);
1556
1245
 
1597
1286
/**
1598
1287
 * gdata_service_insert_entry:
1599
1288
 * @self: a #GDataService
 
1289
 * @domain: (allow-none): the #GDataAuthorizationDomain the insertion operation falls under, or %NULL
1600
1290
 * @upload_uri: the URI to which the upload should be sent
1601
1291
 * @entry: the #GDataEntry to insert
1602
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1292
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1603
1293
 * @error: a #GError, or %NULL
1604
1294
 *
1605
1295
 * Inserts @entry by uploading it to the online service at @upload_uri. For more information about the concept of inserting entries, see
1621
1311
 * <emphasis>cannot</emphasis> cannot override this or provide more specific errors.
1622
1312
 *
1623
1313
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
1624
 
 **/
 
1314
 *
 
1315
 * Since: 0.9.0
 
1316
 */
1625
1317
GDataEntry *
1626
 
gdata_service_insert_entry (GDataService *self, const gchar *upload_uri, GDataEntry *entry, GCancellable *cancellable, GError **error)
 
1318
gdata_service_insert_entry (GDataService *self, GDataAuthorizationDomain *domain, const gchar *upload_uri, GDataEntry *entry,
 
1319
                            GCancellable *cancellable, GError **error)
1627
1320
{
1628
1321
        GDataEntry *updated_entry;
1629
1322
        SoupMessage *message;
1631
1324
        guint status;
1632
1325
 
1633
1326
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
 
1327
        g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
1634
1328
        g_return_val_if_fail (upload_uri != NULL, NULL);
1635
1329
        g_return_val_if_fail (GDATA_IS_ENTRY (entry), NULL);
1636
1330
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1642
1336
                return NULL;
1643
1337
        }
1644
1338
 
1645
 
        message = _gdata_service_build_message (self, SOUP_METHOD_POST, upload_uri, NULL, FALSE);
 
1339
        message = _gdata_service_build_message (self, domain, SOUP_METHOD_POST, upload_uri, NULL, FALSE);
1646
1340
 
1647
1341
        /* Append the data */
1648
1342
        upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
1674
1368
        return updated_entry;
1675
1369
}
1676
1370
 
 
1371
typedef struct {
 
1372
        GDataAuthorizationDomain *domain;
 
1373
        GDataEntry *entry;
 
1374
} UpdateEntryAsyncData;
 
1375
 
 
1376
static void
 
1377
update_entry_async_data_free (UpdateEntryAsyncData *data)
 
1378
{
 
1379
        if (data->domain != NULL)
 
1380
                g_object_unref (data->domain);
 
1381
 
 
1382
        if (data->entry != NULL)
 
1383
                g_object_unref (data->entry);
 
1384
 
 
1385
        g_slice_free (UpdateEntryAsyncData, data);
 
1386
}
 
1387
 
1677
1388
static void
1678
1389
update_entry_thread (GSimpleAsyncResult *result, GDataService *service, GCancellable *cancellable)
1679
1390
{
1680
1391
        GDataEntry *updated_entry;
1681
1392
        GError *error = NULL;
 
1393
        UpdateEntryAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
1682
1394
 
1683
1395
        /* Update the entry and return */
1684
 
        updated_entry = gdata_service_update_entry (service, g_simple_async_result_get_op_res_gpointer (result), cancellable, &error);
 
1396
        updated_entry = gdata_service_update_entry (service, data->domain, data->entry, cancellable, &error);
1685
1397
        if (updated_entry == NULL) {
1686
1398
                g_simple_async_result_set_from_error (result, error);
1687
1399
                g_error_free (error);
1695
1407
/**
1696
1408
 * gdata_service_update_entry_async:
1697
1409
 * @self: a #GDataService
 
1410
 * @domain: (allow-none): the #GDataAuthorizationDomain the update operation falls under, or %NULL
1698
1411
 * @entry: the #GDataEntry to update
1699
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1412
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1700
1413
 * @callback: a #GAsyncReadyCallback to call when the update is finished, or %NULL
1701
1414
 * @user_data: (closure): data to pass to the @callback function
1702
1415
 *
1708
1421
 * When the operation is finished, @callback will be called. You can then call gdata_service_update_entry_finish()
1709
1422
 * to get the results of the operation.
1710
1423
 *
1711
 
 * Since: 0.3.0
 
1424
 * Since: 0.9.0
1712
1425
 **/
1713
1426
void
1714
 
gdata_service_update_entry_async (GDataService *self, GDataEntry *entry, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
1427
gdata_service_update_entry_async (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry,
 
1428
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
1715
1429
{
1716
1430
        GSimpleAsyncResult *result;
 
1431
        UpdateEntryAsyncData *data;
1717
1432
 
1718
1433
        g_return_if_fail (GDATA_IS_SERVICE (self));
 
1434
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
1719
1435
        g_return_if_fail (GDATA_IS_ENTRY (entry));
1720
1436
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1721
1437
 
 
1438
        data = g_slice_new (UpdateEntryAsyncData);
 
1439
        data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
 
1440
        data->entry = g_object_ref (entry);
 
1441
 
1722
1442
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_update_entry_async);
1723
 
        g_simple_async_result_set_op_res_gpointer (result, g_object_ref (entry), (GDestroyNotify) g_object_unref);
 
1443
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) update_entry_async_data_free);
1724
1444
        g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) update_entry_thread, G_PRIORITY_DEFAULT, cancellable);
1725
1445
        g_object_unref (result);
1726
1446
}
1762
1482
/**
1763
1483
 * gdata_service_update_entry:
1764
1484
 * @self: a #GDataService
 
1485
 * @domain: (allow-none): the #GDataAuthorizationDomain the update operation falls under, or %NULL
1765
1486
 * @entry: the #GDataEntry to update
1766
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1487
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1767
1488
 * @error: a #GError, or %NULL
1768
1489
 *
1769
1490
 * Updates @entry by PUTting it to its <literal>edit</literal> link's URI. For more information about the concept of updating entries, see
1783
1504
 *
1784
1505
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
1785
1506
 *
1786
 
 * Since: 0.2.0
 
1507
 * Since: 0.9.0
1787
1508
 **/
1788
1509
GDataEntry *
1789
 
gdata_service_update_entry (GDataService *self, GDataEntry *entry, GCancellable *cancellable, GError **error)
 
1510
gdata_service_update_entry (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry, GCancellable *cancellable, GError **error)
1790
1511
{
1791
1512
        GDataEntry *updated_entry;
1792
1513
        GDataLink *_link;
1795
1516
        guint status;
1796
1517
 
1797
1518
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
 
1519
        g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
1798
1520
        g_return_val_if_fail (GDATA_IS_ENTRY (entry), NULL);
1799
1521
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1800
1522
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1802
1524
        /* Get the edit URI */
1803
1525
        _link = gdata_entry_look_up_link (entry, GDATA_LINK_EDIT);
1804
1526
        g_assert (_link != NULL);
1805
 
        message = _gdata_service_build_message (self, SOUP_METHOD_PUT, gdata_link_get_uri (_link), gdata_entry_get_etag (entry), TRUE);
 
1527
        message = _gdata_service_build_message (self, domain, SOUP_METHOD_PUT, gdata_link_get_uri (_link), gdata_entry_get_etag (entry), TRUE);
1806
1528
 
1807
1529
        /* Append the data */
1808
1530
        upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
1834
1556
        return updated_entry;
1835
1557
}
1836
1558
 
 
1559
typedef struct {
 
1560
        GDataAuthorizationDomain *domain;
 
1561
        GDataEntry *entry;
 
1562
} DeleteEntryAsyncData;
 
1563
 
 
1564
static void
 
1565
delete_entry_async_data_free (DeleteEntryAsyncData *data)
 
1566
{
 
1567
        if (data->domain != NULL)
 
1568
                g_object_unref (data->domain);
 
1569
 
 
1570
        if (data->entry != NULL)
 
1571
                g_object_unref (data->entry);
 
1572
 
 
1573
        g_slice_free (DeleteEntryAsyncData, data);
 
1574
}
 
1575
 
1837
1576
static void
1838
1577
delete_entry_thread (GSimpleAsyncResult *result, GDataService *service, GCancellable *cancellable)
1839
1578
{
1840
1579
        gboolean success;
1841
1580
        GError *error = NULL;
 
1581
        DeleteEntryAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
1842
1582
 
1843
1583
        /* Delete the entry and return */
1844
 
        success = gdata_service_delete_entry (service, g_simple_async_result_get_op_res_gpointer (result), cancellable, &error);
 
1584
        success = gdata_service_delete_entry (service, data->domain, data->entry, cancellable, &error);
1845
1585
        if (success == FALSE) {
1846
1586
                g_simple_async_result_set_from_error (result, error);
1847
1587
                g_error_free (error);
1855
1595
/**
1856
1596
 * gdata_service_delete_entry_async:
1857
1597
 * @self: a #GDataService
 
1598
 * @domain: (allow-none): the #GDataAuthorizationDomain the deletion falls under, or %NULL
1858
1599
 * @entry: the #GDataEntry to delete
1859
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1600
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1860
1601
 * @callback: a #GAsyncReadyCallback to call when deletion is finished, or %NULL
1861
1602
 * @user_data: (closure): data to pass to the @callback function
1862
1603
 *
1868
1609
 * When the operation is finished, @callback will be called. You can then call gdata_service_delete_entry_finish()
1869
1610
 * to get the results of the operation.
1870
1611
 *
1871
 
 * Since: 0.3.0
 
1612
 * Since: 0.9.0
1872
1613
 **/
1873
1614
void
1874
 
gdata_service_delete_entry_async (GDataService *self, GDataEntry *entry, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
1615
gdata_service_delete_entry_async (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry,
 
1616
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
1875
1617
{
1876
1618
        GSimpleAsyncResult *result;
 
1619
        DeleteEntryAsyncData *data;
1877
1620
 
1878
1621
        g_return_if_fail (GDATA_IS_SERVICE (self));
 
1622
        g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
1879
1623
        g_return_if_fail (GDATA_IS_ENTRY (entry));
1880
1624
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1881
1625
 
 
1626
        data = g_slice_new (DeleteEntryAsyncData);
 
1627
        data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
 
1628
        data->entry = g_object_ref (entry);
 
1629
 
1882
1630
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_delete_entry_async);
1883
 
        g_simple_async_result_set_op_res_gpointer (result, g_object_ref (entry), (GDestroyNotify) g_object_unref);
 
1631
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) delete_entry_async_data_free);
1884
1632
        g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) delete_entry_thread, G_PRIORITY_DEFAULT, cancellable);
1885
1633
        g_object_unref (result);
1886
1634
}
1917
1665
/**
1918
1666
 * gdata_service_delete_entry:
1919
1667
 * @self: a #GDataService
 
1668
 * @domain: (allow-none): the #GDataAuthorizationDomain the deletion falls under, or %NULL
1920
1669
 * @entry: the #GDataEntry to delete
1921
 
 * @cancellable: optional #GCancellable object, or %NULL
 
1670
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1922
1671
 * @error: a #GError, or %NULL
1923
1672
 *
1924
1673
 * Deletes @entry from the server. For more information about the concept of deleting entries, see the
1936
1685
 *
1937
1686
 * Return value: %TRUE on success, %FALSE otherwise
1938
1687
 *
1939
 
 * Since: 0.2.0
 
1688
 * Since: 0.9.0
1940
1689
 **/
1941
1690
gboolean
1942
 
gdata_service_delete_entry (GDataService *self, GDataEntry *entry, GCancellable *cancellable, GError **error)
 
1691
gdata_service_delete_entry (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry, GCancellable *cancellable, GError **error)
1943
1692
{
1944
1693
        GDataLink *_link;
1945
1694
        SoupMessage *message;
1946
1695
        guint status;
 
1696
        gchar *fixed_uri;
1947
1697
 
1948
1698
        g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
 
1699
        g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), FALSE);
1949
1700
        g_return_val_if_fail (GDATA_IS_ENTRY (entry), FALSE);
1950
1701
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1951
1702
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1952
1703
 
1953
 
        /* Get the edit URI */
 
1704
        /* Get the edit URI. We have to fix it to always use HTTPS as YouTube videos appear to incorrectly return a HTTP URI as their edit URI. */
1954
1705
        _link = gdata_entry_look_up_link (entry, GDATA_LINK_EDIT);
1955
1706
        g_assert (_link != NULL);
1956
 
        message = _gdata_service_build_message (self, SOUP_METHOD_DELETE, gdata_link_get_uri (_link), gdata_entry_get_etag (entry), TRUE);
 
1707
 
 
1708
        fixed_uri = _gdata_service_fix_uri_scheme (gdata_link_get_uri (_link));
 
1709
        message = _gdata_service_build_message (self, domain, SOUP_METHOD_DELETE, fixed_uri, gdata_entry_get_etag (entry), TRUE);
 
1710
        g_free (fixed_uri);
1957
1711
 
1958
1712
        /* Send the message */
1959
1713
        status = _gdata_service_send_message (self, message, cancellable, error);
2016
1770
 *
2017
1771
 * If @proxy_uri is %NULL, no proxy will be used.
2018
1772
 *
 
1773
 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its proxy URI setting.
 
1774
 *
2019
1775
 * Since: 0.2.0
2020
1776
 **/
2021
1777
void
2063
1819
 *
2064
1820
 * If @timeout is <code class="literal">0</code>, network operations will never time out.
2065
1821
 *
 
1822
 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its timeout setting.
 
1823
 *
2066
1824
 * Since: 0.7.0
2067
1825
 **/
2068
1826
void
2073
1831
        g_object_notify (G_OBJECT (self), "timeout");
2074
1832
}
2075
1833
 
2076
 
/**
2077
 
 * gdata_service_is_authenticated:
2078
 
 * @self: a #GDataService
2079
 
 *
2080
 
 * Returns whether a user is authenticated with the online service through @self.
2081
 
 * Authentication is performed by calling gdata_service_authenticate() or gdata_service_authenticate_async().
2082
 
 *
2083
 
 * Return value: %TRUE if a user is authenticated, %FALSE otherwise
2084
 
 **/
2085
 
gboolean
2086
 
gdata_service_is_authenticated (GDataService *self)
2087
 
{
2088
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
2089
 
        return self->priv->authenticated;
2090
 
}
2091
 
 
2092
 
/* This should only ever be called in the main thread */
2093
 
void
2094
 
_gdata_service_set_authenticated (GDataService *self, gboolean authenticated)
2095
 
{
2096
 
        g_return_if_fail (GDATA_IS_SERVICE (self));
2097
 
        self->priv->authenticated = authenticated;
2098
 
        g_object_notify (G_OBJECT (self), "authenticated");
2099
 
}
2100
 
 
2101
 
/**
2102
 
 * gdata_service_get_client_id:
2103
 
 * @self: a #GDataService
2104
 
 *
2105
 
 * Returns the service's client ID, as specified on constructing the #GDataService.
2106
 
 *
2107
 
 * Return value: the service's client ID
2108
 
 **/
2109
 
const gchar *
2110
 
gdata_service_get_client_id (GDataService *self)
2111
 
{
2112
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
2113
 
        return self->priv->client_id;
2114
 
}
2115
 
 
2116
 
/**
2117
 
 * gdata_service_get_username:
2118
 
 * @self: a #GDataService
2119
 
 *
2120
 
 * Returns the username of the currently-authenticated user, or %NULL if nobody is authenticated.
2121
 
 *
2122
 
 * It is not safe to call this while an authentication operation is ongoing.
2123
 
 *
2124
 
 * Return value: the username of the currently-authenticated user, or %NULL
2125
 
 **/
2126
 
const gchar *
2127
 
gdata_service_get_username (GDataService *self)
2128
 
{
2129
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
2130
 
 
2131
 
        /* There's little point protecting this with authentication_mutex, as the data's meaningless if accessed during an authentication operation,
2132
 
         * and not being accessed concurrently otherwise. */
2133
 
        return self->priv->username;
2134
 
}
2135
 
 
2136
 
/**
2137
 
 * gdata_service_get_password:
2138
 
 * @self: a #GDataService
2139
 
 *
2140
 
 * Returns the password of the currently-authenticated user, or %NULL if nobody is authenticated.
2141
 
 *
2142
 
 * It is not safe to call this while an authentication operation is ongoing.
2143
 
 *
2144
 
 * Return value: the password of the currently-authenticated user, or %NULL
2145
 
 **/
2146
 
const gchar *
2147
 
gdata_service_get_password (GDataService *self)
2148
 
{
2149
 
        g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
2150
 
 
2151
 
        /* There's little point protecting this with authentication_mutex, as the data's meaningless if accessed during an authentication operation,
2152
 
         * and not being accessed concurrently otherwise. */
2153
 
        return self->priv->password;
2154
 
}
2155
 
 
2156
1834
SoupSession *
2157
1835
_gdata_service_get_session (GDataService *self)
2158
1836
{
2163
1841
/*
2164
1842
 * _gdata_service_get_scheme:
2165
1843
 *
2166
 
 * Returns the name of the scheme to use (either "http" or "https") for network operations if a request should use HTTPS. This allows
2167
 
 * requests to normally use HTTPS, but have the option of using HTTP for debugging purposes. If a request should normally use HTTP, that
2168
 
 * should be hard-coded in the relevant code, and this function needn't be called.
2169
 
 *
2170
 
 * Return value: the scheme to use
 
1844
 * Returns the name of the scheme to use, which will always be <code class="literal">https</code>. The return type used to vary according to the
 
1845
 * environment variable <code class="literal">LIBGDATA_FORCE_HTTP</code>, but Google has since switched to using HTTPS exclusively.
 
1846
 *
 
1847
 * See <ulink type="http" url="http://googlecode.blogspot.com/2011/03/improving-security-of-google-apis-with.html">Improving the security of Google
 
1848
 * APIs with SSL</ulink>.
 
1849
 *
 
1850
 * Return value: the scheme to use (<code class="literal">https</code>)
2171
1851
 *
2172
1852
 * Since: 0.6.0
2173
1853
 */
2174
1854
const gchar *
2175
1855
_gdata_service_get_scheme (void)
2176
1856
{
2177
 
        static gint force_http = -1;
2178
 
 
2179
 
        if (force_http == -1)
2180
 
                force_http = (g_getenv ("LIBGDATA_FORCE_HTTP") != NULL);
2181
 
 
2182
 
        if (force_http)
2183
 
                return "http";
2184
1857
        return "https";
2185
1858
}
2186
1859
 
2187
1860
/*
2188
1861
 * _gdata_service_build_uri:
2189
 
 * @force_http: %TRUE to force the outputted URI to use HTTP, %FALSE to use the scheme returned by _gdata_service_get_scheme()
2190
1862
 * @format: a standard printf() format string
2191
1863
 * @...: the arguments to insert in the output
2192
1864
 *
2195
1867
 * other printf() format placeholders are supported at the moment except <code class="literal">%%d</code>, which prints a signed integer; and
2196
1868
 * <code class="literal">%%</code>, which prints a literal percent symbol.
2197
1869
 *
2198
 
 * The returned URI is guaranteed to use the scheme returned by _gdata_service_get_scheme(), unless the @force_http argument is %TRUE; in that case,
2199
 
 * the returned URI is guaranteed to use HTTP. The format string, once all the arguments have been inserted into it, must include a service, but it
2200
 
 * doesn't matter which one.
 
1870
 * The returned URI is guaranteed to use the scheme returned by _gdata_service_get_scheme(). The format string, once all the arguments have been
 
1871
 * inserted into it, must include a scheme, but it doesn't matter which one.
2201
1872
 *
2202
1873
 * Return value: a newly allocated URI string; free with g_free()
2203
1874
 */
2204
1875
gchar *
2205
 
_gdata_service_build_uri (gboolean force_http, const gchar *format, ...)
 
1876
_gdata_service_build_uri (const gchar *format, ...)
2206
1877
{
2207
 
        const gchar *p, *scheme;
2208
 
        gchar *built_uri;
 
1878
        const gchar *p;
 
1879
        gchar *fixed_uri;
2209
1880
        GString *uri;
2210
1881
        va_list args;
2211
1882
 
2244
1915
 
2245
1916
        va_end (args);
2246
1917
 
2247
 
        built_uri = g_string_free (uri, FALSE);
2248
 
        scheme = (force_http == FALSE) ? _gdata_service_get_scheme () : "http";
 
1918
        /* Fix the scheme to always be HTTPS */
 
1919
        fixed_uri = _gdata_service_fix_uri_scheme (uri->str);
 
1920
        g_string_free (uri, TRUE);
 
1921
 
 
1922
        return fixed_uri;
 
1923
}
 
1924
 
 
1925
/**
 
1926
 * _gdata_service_fix_uri_scheme:
 
1927
 * @uri: an URI with either HTTP or HTTPS as the scheme
 
1928
 *
 
1929
 * Fixes the given URI to always have HTTPS as its scheme.
 
1930
 *
 
1931
 * Return value: (transfer full): the URI with HTTPS as its scheme
 
1932
 *
 
1933
 * Since: 0.9.0
 
1934
 */
 
1935
gchar *
 
1936
_gdata_service_fix_uri_scheme (const gchar *uri)
 
1937
{
 
1938
        g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
2249
1939
 
2250
1940
        /* Ensure we're using the correct scheme (HTTP or HTTPS) */
2251
 
        if (g_str_has_prefix (built_uri, scheme) == FALSE) {
 
1941
        if (g_str_has_prefix (uri, "https") == FALSE) {
2252
1942
                gchar *fixed_uri, **pieces;
2253
1943
 
2254
 
                pieces = g_strsplit (built_uri, ":", 2);
 
1944
                pieces = g_strsplit (uri, ":", 2);
2255
1945
                g_assert (pieces[0] != NULL && pieces[1] != NULL && pieces[2] == NULL);
2256
1946
 
2257
 
                fixed_uri = g_strdup_printf ("%s:%s", scheme, pieces[1]);
 
1947
                fixed_uri = g_strconcat ("https:", pieces[1], NULL);
2258
1948
 
2259
1949
                g_strfreev (pieces);
2260
 
                g_free (built_uri);
2261
1950
 
2262
1951
                return fixed_uri;
2263
1952
        }
2264
1953
 
2265
 
        return built_uri;
 
1954
        return g_strdup (uri);
2266
1955
}
2267
1956
 
2268
1957
/*
2314
2003
}
2315
2004
 
2316
2005
/**
 
2006
 * _gdata_service_build_session:
 
2007
 *
 
2008
 * Build a new #SoupSession, enabling GNOME features if support has been compiled for them, and adding a log printer which is hooked into
 
2009
 * libgdata's logging functionality.
 
2010
 *
 
2011
 * Return value: a new #SoupSession; unref with g_object_unref()
 
2012
 *
 
2013
 * Since: 0.9.0
 
2014
 */
 
2015
SoupSession *
 
2016
_gdata_service_build_session (void)
 
2017
{
 
2018
        SoupSession *session = soup_session_sync_new ();
 
2019
 
 
2020
#ifdef HAVE_GNOME
 
2021
        soup_session_add_feature_by_type (session, SOUP_TYPE_GNOME_FEATURES_2_26);
 
2022
#endif /* HAVE_GNOME */
 
2023
 
 
2024
        /* Log all libsoup traffic if debugging's turned on */
 
2025
        if (_gdata_service_get_log_level () > GDATA_LOG_MESSAGES) {
 
2026
                SoupLoggerLogLevel level;
 
2027
                SoupLogger *logger;
 
2028
 
 
2029
                switch (_gdata_service_get_log_level ()) {
 
2030
                        case GDATA_LOG_FULL:
 
2031
                                level = SOUP_LOGGER_LOG_BODY;
 
2032
                                break;
 
2033
                        case GDATA_LOG_HEADERS:
 
2034
                                level = SOUP_LOGGER_LOG_HEADERS;
 
2035
                                break;
 
2036
                        case GDATA_LOG_MESSAGES:
 
2037
                        case GDATA_LOG_NONE:
 
2038
                        default:
 
2039
                                g_assert_not_reached ();
 
2040
                }
 
2041
 
 
2042
                logger = soup_logger_new (level, -1);
 
2043
                soup_logger_set_printer (logger, (SoupLoggerPrinter) soup_log_printer, NULL, NULL);
 
2044
 
 
2045
                soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
 
2046
 
 
2047
                g_object_unref (logger);
 
2048
        }
 
2049
 
 
2050
        return session;
 
2051
}
 
2052
 
 
2053
/**
2317
2054
 * gdata_service_get_locale:
2318
2055
 * @self: a #GDataService
2319
2056
 *