Blame gdata/gdata-service.c

Packit 4b6dd7
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit 4b6dd7
/*
Packit 4b6dd7
 * GData Client
Packit 4b6dd7
 * Copyright (C) Philip Withnall 2008, 2009, 2010, 2014 <philip@tecnocode.co.uk>
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is free software; you can redistribute it and/or
Packit 4b6dd7
 * modify it under the terms of the GNU Lesser General Public
Packit 4b6dd7
 * License as published by the Free Software Foundation; either
Packit 4b6dd7
 * version 2.1 of the License, or (at your option) any later version.
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is distributed in the hope that it will be useful,
Packit 4b6dd7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4b6dd7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 4b6dd7
 * Lesser General Public License for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * You should have received a copy of the GNU Lesser General Public
Packit 4b6dd7
 * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * SECTION:gdata-service
Packit 4b6dd7
 * @short_description: GData service object
Packit 4b6dd7
 * @stability: Stable
Packit 4b6dd7
 * @include: gdata/gdata-service.h
Packit 4b6dd7
 *
Packit 4b6dd7
 * #GDataService represents a GData API service, typically a website using the GData API, such as YouTube or Google Calendar. One
Packit 4b6dd7
 * #GDataService instance is required to issue queries to the service, handle insertions, updates and deletions, and generally
Packit 4b6dd7
 * communicate with the online service.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If operations performed on a #GDataService need authorization (such as uploading a video to YouTube or querying the user's personal calendar on
Packit 4b6dd7
 * Google Calendar), the service needs a #GDataAuthorizer instance set as #GDataService:authorizer. Once the user is appropriately authenticated and
Packit 4b6dd7
 * authorized by the #GDataAuthorizer implementation (see the documentation for #GDataAuthorizer for details on how this is achieved for specific
Packit 4b6dd7
 * implementations), all operations will be automatically authorized.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that it's not always necessary to supply a #GDataAuthorizer instance to a #GDataService. If the only operations to be performed on the
Packit 4b6dd7
 * #GDataService don't need authorization (e.g. they only query public information), setting up a #GDataAuthorizer is just extra overhead. See the
Packit 4b6dd7
 * documentation for the operations on individual #GDataService subclasses to see which need authorization and which don't.
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
#include <config.h>
Packit 4b6dd7
#include <glib.h>
Packit 4b6dd7
#include <glib/gi18n-lib.h>
Packit 4b6dd7
#include <libsoup/soup.h>
Packit 4b6dd7
#include <string.h>
Packit 4b6dd7
#include <stdarg.h>
Packit 4b6dd7
Packit 4b6dd7
#ifdef HAVE_GNOME
Packit 4b6dd7
#define GCR_API_SUBJECT_TO_CHANGE
Packit 4b6dd7
#include <gcr/gcr-base.h>
Packit 4b6dd7
#endif /* HAVE_GNOME */
Packit 4b6dd7
Packit 4b6dd7
#include "gdata-service.h"
Packit 4b6dd7
#include "gdata-private.h"
Packit 4b6dd7
#include "gdata-client-login-authorizer.h"
Packit 4b6dd7
#include "gdata-marshal.h"
Packit 4b6dd7
#include "gdata-types.h"
Packit 4b6dd7
Packit 4b6dd7
GQuark
Packit 4b6dd7
gdata_service_error_quark (void)
Packit 4b6dd7
{
Packit 4b6dd7
	return g_quark_from_static_string ("gdata-service-error-quark");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void gdata_service_dispose (GObject *object);
Packit 4b6dd7
static void gdata_service_finalize (GObject *object);
Packit 4b6dd7
static void gdata_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
Packit 4b6dd7
static void gdata_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
Packit 4b6dd7
static void real_append_query_headers (GDataService *self, GDataAuthorizationDomain *domain, SoupMessage *message);
Packit 4b6dd7
static void real_parse_error_response (GDataService *self, GDataOperationType operation_type, guint status, const gchar *reason_phrase,
Packit 4b6dd7
                                       const gchar *response_body, gint length, GError **error);
Packit 4b6dd7
static GDataFeed *
Packit 4b6dd7
real_parse_feed (GDataService *self,
Packit 4b6dd7
                 GDataAuthorizationDomain *domain,
Packit 4b6dd7
                 GDataQuery *query,
Packit 4b6dd7
                 GType entry_type,
Packit 4b6dd7
                 SoupMessage *message,
Packit 4b6dd7
                 GCancellable *cancellable,
Packit 4b6dd7
                 GDataQueryProgressCallback progress_callback,
Packit 4b6dd7
                 gpointer progress_user_data,
Packit 4b6dd7
                 GError **error);
Packit 4b6dd7
static void notify_proxy_uri_cb (GObject *gobject, GParamSpec *pspec, GObject *self);
Packit 4b6dd7
static void notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self);
Packit 4b6dd7
static void debug_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data);
Packit 4b6dd7
static void soup_log_printer (SoupLogger *logger, SoupLoggerLogLevel level, char direction, const char *data, gpointer user_data);
Packit 4b6dd7
Packit 4b6dd7
static GDataFeed *__gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query,
Packit 4b6dd7
                                         GType entry_type, GCancellable *cancellable, GDataQueryProgressCallback progress_callback,
Packit 4b6dd7
                                         gpointer progress_user_data, GError **error);
Packit 4b6dd7
Packit 4b6dd7
static SoupURI *_get_proxy_uri (GDataService *self);
Packit 4b6dd7
static void _set_proxy_uri (GDataService *self, SoupURI *proxy_uri);
Packit 4b6dd7
Packit 4b6dd7
struct _GDataServicePrivate {
Packit 4b6dd7
	SoupSession *session;
Packit 4b6dd7
	gchar *locale;
Packit 4b6dd7
	GDataAuthorizer *authorizer;
Packit 4b6dd7
	GProxyResolver *proxy_resolver;
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
enum {
Packit 4b6dd7
	PROP_PROXY_URI = 1,
Packit 4b6dd7
	PROP_TIMEOUT,
Packit 4b6dd7
	PROP_LOCALE,
Packit 4b6dd7
	PROP_AUTHORIZER,
Packit 4b6dd7
	PROP_PROXY_RESOLVER,
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
G_DEFINE_TYPE (GDataService, gdata_service, G_TYPE_OBJECT)
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_class_init (GDataServiceClass *klass)
Packit 4b6dd7
{
Packit 4b6dd7
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit 4b6dd7
Packit 4b6dd7
	g_type_class_add_private (klass, sizeof (GDataServicePrivate));
Packit 4b6dd7
Packit 4b6dd7
	gobject_class->set_property = gdata_service_set_property;
Packit 4b6dd7
	gobject_class->get_property = gdata_service_get_property;
Packit 4b6dd7
	gobject_class->dispose = gdata_service_dispose;
Packit 4b6dd7
	gobject_class->finalize = gdata_service_finalize;
Packit 4b6dd7
Packit 4b6dd7
	klass->api_version = "2";
Packit 4b6dd7
	klass->feed_type = GDATA_TYPE_FEED;
Packit 4b6dd7
	klass->append_query_headers = real_append_query_headers;
Packit 4b6dd7
	klass->parse_error_response = real_parse_error_response;
Packit 4b6dd7
	klass->parse_feed = real_parse_feed;
Packit 4b6dd7
	klass->get_authorization_domains = NULL; /* equivalent to returning an empty list of domains */
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataService:proxy-uri:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The proxy URI used internally for all network requests.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its proxy URI setting.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.2.0
Packit 4b6dd7
	 * Deprecated: 0.15.0: Use #GDataService:proxy-resolver instead, which gives more flexibility over the proxy used.
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_PROXY_URI,
Packit 4b6dd7
	                                 g_param_spec_boxed ("proxy-uri",
Packit 4b6dd7
	                                                     "Proxy URI", "The proxy URI used internally for all network requests.",
Packit 4b6dd7
	                                                     SOUP_TYPE_URI,
Packit 4b6dd7
	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataService:timeout:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * A timeout, in seconds, for network operations. If the timeout is exceeded, the operation will be cancelled and
Packit 4b6dd7
	 * %GDATA_SERVICE_ERROR_NETWORK_ERROR will be returned.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * If the timeout is 0, operations will never time out.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its timeout setting.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.7.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_TIMEOUT,
Packit 4b6dd7
	                                 g_param_spec_uint ("timeout",
Packit 4b6dd7
	                                                    "Timeout", "A timeout, in seconds, for network operations.",
Packit 4b6dd7
	                                                    0, G_MAXUINT, 0,
Packit 4b6dd7
	                                                    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataService:locale:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The locale to use for network requests, in Unix locale format. (e.g. "en_GB", "cs", "de_DE".) Use %NULL for the default "C" locale
Packit 4b6dd7
	 * (typically "en_US").
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Typically, this locale will be used by the server-side software to localise results, such as by translating category names, or by choosing
Packit 4b6dd7
	 * geographically relevant search results. This will vary from service to service.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The server-side behaviour is undefined if it doesn't support a given locale.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.7.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_LOCALE,
Packit 4b6dd7
	                                 g_param_spec_string ("locale",
Packit 4b6dd7
	                                                      "Locale", "The locale to use for network requests, in Unix locale format.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataService:authorizer:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * An object which implements #GDataAuthorizer. This should have previously been authenticated authorized against this service type (and
Packit 4b6dd7
	 * potentially other service types). The service will use the authorizer to add an authorization token to each request it performs.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Your application should call methods on the #GDataAuthorizer object itself in order to authenticate with the Google accounts service and
Packit 4b6dd7
	 * authorize against this service type. See the documentation for the particular #GDataAuthorizer implementation being used for more details.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The authorizer for a service can be changed at runtime for a different #GDataAuthorizer object or %NULL without affecting ongoing requests
Packit 4b6dd7
	 * and operations.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * 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
Packit 4b6dd7
	 * require authorization. For example, listing the current most popular videos on YouTube does not require authorization, but uploading a
Packit 4b6dd7
	 * video to YouTube does. It's an unnecessary overhead to require the user to authorize against a service when not strictly required.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.9.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_AUTHORIZER,
Packit 4b6dd7
	                                 g_param_spec_object ("authorizer",
Packit 4b6dd7
	                                                      "Authorizer", "An authorizer object to provide an authorization token for each request.",
Packit 4b6dd7
	                                                      GDATA_TYPE_AUTHORIZER,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataService:proxy-resolver:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The #GProxyResolver used to determine a proxy URI.  Setting this will clear the #GDataService:proxy-uri property.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.15.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
Packit 4b6dd7
	                                 g_param_spec_object ("proxy-resolver",
Packit 4b6dd7
	                                                      "Proxy Resolver", "A GProxyResolver used to determine a proxy URI.",
Packit 4b6dd7
	                                                      G_TYPE_PROXY_RESOLVER,
Packit 4b6dd7
	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_init (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_SERVICE, GDataServicePrivate);
Packit 4b6dd7
	self->priv->session = _gdata_service_build_session ();
Packit 4b6dd7
Packit 4b6dd7
	/* Log handling for all message types except debug */
Packit 4b6dd7
	g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_INFO | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING, (GLogFunc) debug_handler, self);
Packit 4b6dd7
Packit 4b6dd7
	/* Proxy the SoupSession's proxy-uri and timeout properties */
Packit 4b6dd7
	g_signal_connect (self->priv->session, "notify::proxy-uri", (GCallback) notify_proxy_uri_cb, self);
Packit 4b6dd7
	g_signal_connect (self->priv->session, "notify::timeout", (GCallback) notify_timeout_cb, self);
Packit 4b6dd7
Packit 4b6dd7
	/* Keep our GProxyResolver synchronized with SoupSession's. */
Packit 4b6dd7
	g_object_bind_property (self->priv->session, "proxy-resolver", self, "proxy-resolver", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_dispose (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	if (priv->authorizer != NULL)
Packit 4b6dd7
		g_object_unref (priv->authorizer);
Packit 4b6dd7
	priv->authorizer = NULL;
Packit 4b6dd7
Packit 4b6dd7
	if (priv->session != NULL)
Packit 4b6dd7
		g_object_unref (priv->session);
Packit 4b6dd7
	priv->session = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_clear_object (&priv->proxy_resolver);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_service_parent_class)->dispose (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_finalize (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->locale);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_service_parent_class)->finalize (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServicePrivate *priv = GDATA_SERVICE (object)->priv;
Packit 4b6dd7
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_PROXY_URI:
Packit 4b6dd7
			g_value_set_boxed (value, _get_proxy_uri (GDATA_SERVICE (object)));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_TIMEOUT:
Packit 4b6dd7
			g_value_set_uint (value, gdata_service_get_timeout (GDATA_SERVICE (object)));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_LOCALE:
Packit 4b6dd7
			g_value_set_string (value, priv->locale);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_AUTHORIZER:
Packit 4b6dd7
			g_value_set_object (value, priv->authorizer);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_PROXY_RESOLVER:
Packit 4b6dd7
			g_value_set_object (value, priv->proxy_resolver);
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We don't have any other property... */
Packit 4b6dd7
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
			break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_PROXY_URI:
Packit 4b6dd7
			_set_proxy_uri (GDATA_SERVICE (object), g_value_get_boxed (value));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_TIMEOUT:
Packit 4b6dd7
			gdata_service_set_timeout (GDATA_SERVICE (object), g_value_get_uint (value));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_LOCALE:
Packit 4b6dd7
			gdata_service_set_locale (GDATA_SERVICE (object), g_value_get_string (value));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_AUTHORIZER:
Packit 4b6dd7
			gdata_service_set_authorizer (GDATA_SERVICE (object), g_value_get_object (value));
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_PROXY_RESOLVER:
Packit 4b6dd7
			gdata_service_set_proxy_resolver (GDATA_SERVICE (object), g_value_get_object (value));
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We don't have any other property... */
Packit 4b6dd7
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
			break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
real_append_query_headers (GDataService *self, GDataAuthorizationDomain *domain, SoupMessage *message)
Packit 4b6dd7
{
Packit 4b6dd7
	g_assert (message != NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Set the authorisation header */
Packit 4b6dd7
	if (self->priv->authorizer != NULL) {
Packit 4b6dd7
		gdata_authorizer_process_request (self->priv->authorizer, domain, message);
Packit 4b6dd7
Packit 4b6dd7
		if (domain != NULL) {
Packit 4b6dd7
			/* Store the authorisation domain on the message so that we can access it again after refreshing authorisation if necessary.
Packit 4b6dd7
			 * See _gdata_service_send_message(). */
Packit 4b6dd7
			g_object_set_data_full (G_OBJECT (message), "gdata-authorization-domain", g_object_ref (domain),
Packit 4b6dd7
			                        (GDestroyNotify) g_object_unref);
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Set the GData-Version header to tell it we want to use the v2 API */
Packit 4b6dd7
	soup_message_headers_append (message->request_headers, "GData-Version", GDATA_SERVICE_GET_CLASS (self)->api_version);
Packit 4b6dd7
Packit 4b6dd7
	/* Set the locale, if it's been set for the service */
Packit 4b6dd7
	if (self->priv->locale != NULL)
Packit 4b6dd7
		soup_message_headers_append (message->request_headers, "Accept-Language", self->priv->locale);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
real_parse_error_response (GDataService *self, GDataOperationType operation_type, guint status, const gchar *reason_phrase,
Packit 4b6dd7
                           const gchar *response_body, gint length, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	/* We prefer to include the @response_body in the error message, but if it's empty, fall back to the @reason_phrase */
Packit 4b6dd7
	if (response_body == NULL || *response_body == '\0')
Packit 4b6dd7
		response_body = reason_phrase;
Packit 4b6dd7
Packit 4b6dd7
	/* See: http://code.google.com/apis/gdata/docs/2.0/reference.html#HTTPStatusCodes */
Packit 4b6dd7
	switch (status) {
Packit 4b6dd7
		case SOUP_STATUS_CANT_RESOLVE:
Packit 4b6dd7
		case SOUP_STATUS_CANT_CONNECT:
Packit 4b6dd7
		case SOUP_STATUS_SSL_FAILED:
Packit 4b6dd7
		case SOUP_STATUS_IO_ERROR:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_NETWORK_ERROR,
Packit 4b6dd7
			             _("Cannot connect to the service’s server."));
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_CANT_RESOLVE_PROXY:
Packit 4b6dd7
		case SOUP_STATUS_CANT_CONNECT_PROXY:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROXY_ERROR,
Packit 4b6dd7
			             _("Cannot connect to the proxy server."));
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_MALFORMED:
Packit 4b6dd7
		case SOUP_STATUS_BAD_REQUEST: /* 400 */
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the parameter is an error message returned by the server. */
Packit 4b6dd7
			             _("Invalid request URI or header, or unsupported nonstandard parameter: %s"), response_body);
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_UNAUTHORIZED: /* 401 */
Packit 4b6dd7
		case SOUP_STATUS_FORBIDDEN: /* 403 */
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
Packit 4b6dd7
			             /* Translators: the parameter is an error message returned by the server. */
Packit 4b6dd7
			             _("Authentication required: %s"), response_body);
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_NOT_FOUND: /* 404 */
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_NOT_FOUND,
Packit 4b6dd7
			             /* Translators: the parameter is an error message returned by the server. */
Packit 4b6dd7
			             _("The requested resource was not found: %s"), response_body);
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_CONFLICT: /* 409 */
Packit 4b6dd7
		case SOUP_STATUS_PRECONDITION_FAILED: /* 412 */
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_CONFLICT,
Packit 4b6dd7
			             /* Translators: the parameter is an error message returned by the server. */
Packit 4b6dd7
			             _("The entry has been modified since it was downloaded: %s"), response_body);
Packit 4b6dd7
			return;
Packit 4b6dd7
		case SOUP_STATUS_INTERNAL_SERVER_ERROR: /* 500 */
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We'll fall back to generic errors, below */
Packit 4b6dd7
			break;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* If the error hasn't been handled already, throw a generic error */
Packit 4b6dd7
	switch (operation_type) {
Packit 4b6dd7
		case GDATA_OPERATION_AUTHENTICATION:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when authenticating: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_QUERY:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when querying: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_INSERTION:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when inserting an entry: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_UPDATE:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when updating an entry: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_DELETION:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when deleting an entry: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_DOWNLOAD:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when downloading: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_UPLOAD:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the first parameter is an HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when uploading: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case GDATA_OPERATION_BATCH:
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_WITH_BATCH_OPERATION,
Packit 4b6dd7
			             /* Translators: the first parameter is a HTTP status,
Packit 4b6dd7
			              * and the second is an error message returned by the server. */
Packit 4b6dd7
			             _("Error code %u when running a batch operation: %s"), status, response_body);
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We should not be called with anything other than the above operation types */
Packit 4b6dd7
			g_assert_not_reached ();
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_is_authorized:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Determines whether the service is authorized for all the #GDataAuthorizationDomains it belongs to (as returned by
Packit 4b6dd7
 * gdata_service_get_authorization_domains()). If the service's #GDataService:authorizer is %NULL, %FALSE is always returned.
Packit 4b6dd7
 *
Packit 4b6dd7
 * This is basically a convenience method for checking that the service's #GDataAuthorizer is authorized for all the service's
Packit 4b6dd7
 * #GDataAuthorizationDomains.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE if the service is authorized for all its domains, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_service_is_authorized (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	GList *domains, *i;
Packit 4b6dd7
	gboolean authorised = TRUE;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* If we don't have an authoriser set, we can't be authorised */
Packit 4b6dd7
	if (self->priv->authorizer == NULL) {
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	domains = gdata_service_get_authorization_domains (G_OBJECT_TYPE (self));
Packit 4b6dd7
Packit 4b6dd7
	/* Find any domains which we're not authorised for */
Packit 4b6dd7
	for (i = domains; i != NULL; i = i->next) {
Packit 4b6dd7
		if (gdata_authorizer_is_authorized_for_domain (self->priv->authorizer, GDATA_AUTHORIZATION_DOMAIN (i->data)) == FALSE) {
Packit 4b6dd7
			authorised = FALSE;
Packit 4b6dd7
			break;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_list_free (domains);
Packit 4b6dd7
Packit 4b6dd7
	return authorised;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_authorizer:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataAuthorizer object currently in use by the service. See the documentation for #GDataService:authorizer for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none): the authorizer object for this service, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataAuthorizer *
Packit 4b6dd7
gdata_service_get_authorizer (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return self->priv->authorizer;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_set_authorizer:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @authorizer: a new authorizer object for the service, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets #GDataService:authorizer to @authorizer. This may be %NULL if the service will only make requests in future which don't require authorization.
Packit 4b6dd7
 * See the documentation for #GDataService:authorizer for more information.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_set_authorizer (GDataService *self, GDataAuthorizer *authorizer)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServicePrivate *priv = self->priv;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (authorizer == NULL || GDATA_IS_AUTHORIZER (authorizer));
Packit 4b6dd7
Packit 4b6dd7
	if (priv->authorizer != NULL) {
Packit 4b6dd7
		g_object_unref (priv->authorizer);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	priv->authorizer = authorizer;
Packit 4b6dd7
Packit 4b6dd7
	if (priv->authorizer != NULL) {
Packit 4b6dd7
		g_object_ref (priv->authorizer);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "authorizer");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_authorization_domains:
Packit 4b6dd7
 * @service_type: the #GType of the #GDataService subclass to retrieve the authorization domains for
Packit 4b6dd7
 *
Packit 4b6dd7
 * Retrieves the full list of #GDataAuthorizationDomains which relate to the specified @service_type. All the
Packit 4b6dd7
 * #GDataAuthorizationDomains are unique and interned, so can be compared with other domains by simple pointer comparison.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that in addition to this method, #GDataService subclasses may expose some or all of their authorization domains individually by means of
Packit 4b6dd7
 * individual accessor functions.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer container) (element-type GDataAuthorizationDomain): an unordered list of #GDataAuthorizationDomains; free with
Packit 4b6dd7
 * g_list_free()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GList *
Packit 4b6dd7
gdata_service_get_authorization_domains (GType service_type)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServiceClass *klass;
Packit 4b6dd7
	GList *domains = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE), NULL);
Packit 4b6dd7
Packit 4b6dd7
	klass = GDATA_SERVICE_CLASS (g_type_class_ref (service_type));
Packit 4b6dd7
	if (klass->get_authorization_domains != NULL) {
Packit 4b6dd7
		domains = klass->get_authorization_domains ();
Packit 4b6dd7
	}
Packit 4b6dd7
	g_type_class_unref (klass);
Packit 4b6dd7
Packit 4b6dd7
	return domains;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
SoupMessage *
Packit 4b6dd7
_gdata_service_build_message (GDataService *self, GDataAuthorizationDomain *domain, const gchar *method, const gchar *uri,
Packit 4b6dd7
                              const gchar *etag, gboolean etag_if_match)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	GDataServiceClass *klass;
Packit 4b6dd7
	SoupURI *_uri;
Packit 4b6dd7
Packit 4b6dd7
	/* Create the message. Allow changing the HTTPS port just for testing,
Packit 4b6dd7
	 * but require that the URI is always HTTPS for privacy. */
Packit 4b6dd7
	_uri = soup_uri_new (uri);
Packit 4b6dd7
	soup_uri_set_port (_uri, _gdata_service_get_https_port ());
Packit 4b6dd7
	g_assert_cmpstr (soup_uri_get_scheme (_uri), ==, SOUP_URI_SCHEME_HTTPS);
Packit 4b6dd7
	message = soup_message_new_from_uri (method, _uri);
Packit 4b6dd7
	soup_uri_free (_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Make sure subclasses set their headers */
Packit 4b6dd7
	klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
	if (klass->append_query_headers != NULL)
Packit 4b6dd7
		klass->append_query_headers (self, domain, message);
Packit 4b6dd7
Packit 4b6dd7
	/* Append the ETag header if possible */
Packit 4b6dd7
	if (etag != NULL)
Packit 4b6dd7
		soup_message_headers_append (message->request_headers, (etag_if_match == TRUE) ? "If-Match" : "If-None-Match", etag);
Packit 4b6dd7
Packit 4b6dd7
	return message;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	GMutex mutex; /* mutex to prevent cancellation before the message has been added to the session's message queue */
Packit 4b6dd7
	SoupSession *session;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
} MessageData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
message_cancel_cb (GCancellable *cancellable, MessageData *data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_mutex_lock (&(data->mutex));
Packit 4b6dd7
	soup_session_cancel_message (data->session, data->message, SOUP_STATUS_CANCELLED);
Packit 4b6dd7
	g_mutex_unlock (&(data->mutex));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
message_request_queued_cb (SoupSession *session, SoupMessage *message, MessageData *data)
Packit 4b6dd7
{
Packit 4b6dd7
	if (message == data->message) {
Packit 4b6dd7
		g_mutex_unlock (&(data->mutex));
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Synchronously send @message via @service, handling asynchronous cancellation as best we can. If @cancellable has been cancelled before we start
Packit 4b6dd7
 * network activity, return without doing any network activity. Otherwise, if @cancellable is cancelled (from another thread) after network activity
Packit 4b6dd7
 * has started, we wait until the message has been queued by the session, then cancel the network activity and return as soon as possible.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If cancellation has been handled, @error is guaranteed to be set to %G_IO_ERROR_CANCELLED. Otherwise, @error is guaranteed to be unset. */
Packit 4b6dd7
void
Packit 4b6dd7
_gdata_service_actually_send_message (SoupSession *session, SoupMessage *message, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	MessageData data;
Packit 4b6dd7
	gulong cancel_signal = 0, request_queued_signal = 0;
Packit 4b6dd7
Packit 4b6dd7
	/* 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
Packit 4b6dd7
	 * thread while we were making a request, the request would be unexpectedly cancelled. See bgo#650835 for an example of this breaking things.
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_ref (session);
Packit 4b6dd7
	g_object_ref (message);
Packit 4b6dd7
Packit 4b6dd7
	/* Listen for cancellation */
Packit 4b6dd7
	if (cancellable != NULL) {
Packit 4b6dd7
		g_mutex_init (&(data.mutex));
Packit 4b6dd7
		data.session = session;
Packit 4b6dd7
		data.message = message;
Packit 4b6dd7
Packit 4b6dd7
		cancel_signal = g_cancellable_connect (cancellable, (GCallback) message_cancel_cb, &data, NULL);
Packit 4b6dd7
		request_queued_signal = g_signal_connect (session, "request-queued", (GCallback) message_request_queued_cb, &data);
Packit 4b6dd7
Packit 4b6dd7
		/* We lock this mutex until the message has been queued by the session (i.e. it's unlocked in the request-queued callback), and require
Packit 4b6dd7
		 * the mutex to be held to cancel the message. Consequently, if the message is cancelled (in another thread) any time between this lock
Packit 4b6dd7
		 * and the request being queued, the cancellation will wait until the request has been queued before taking effect.
Packit 4b6dd7
		 * This is a little ugly, but is the only way I can think of to avoid a race condition between calling soup_session_cancel_message()
Packit 4b6dd7
		 * and soup_session_send_message(), as the former doesn't have any effect until the request has been queued, and once the latter has
Packit 4b6dd7
		 * returned, all network activity has been finished so cancellation is pointless. */
Packit 4b6dd7
		g_mutex_lock (&(data.mutex));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Only send the message if it hasn't already been cancelled. There is no race condition here for the above reasons: if the cancellable has
Packit 4b6dd7
	 * been cancelled, it's because it was cancelled before we called g_cancellable_connect().
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Otherwise, manually set the message's status code to SOUP_STATUS_CANCELLED, as the message was cancelled before even being queued to be
Packit 4b6dd7
	 * sent. */
Packit 4b6dd7
	if (cancellable == NULL || g_cancellable_is_cancelled (cancellable) == FALSE)
Packit 4b6dd7
		soup_session_send_message (session, message);
Packit 4b6dd7
	else {
Packit 4b6dd7
		if (cancellable != NULL) {
Packit 4b6dd7
			g_mutex_unlock (&data.mutex);
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		soup_message_set_status (message, SOUP_STATUS_CANCELLED);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Clean up the cancellation code */
Packit 4b6dd7
	if (cancellable != NULL) {
Packit 4b6dd7
		g_signal_handler_disconnect (session, request_queued_signal);
Packit 4b6dd7
Packit 4b6dd7
		if (cancel_signal != 0)
Packit 4b6dd7
			g_cancellable_disconnect (cancellable, cancel_signal);
Packit 4b6dd7
Packit 4b6dd7
		g_mutex_clear (&(data.mutex));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Set the cancellation error if applicable. We can't assume that our GCancellable has been cancelled just because the message has;
Packit 4b6dd7
	 * libsoup may internally cancel messages if, for example, the proxy URI of the SoupSession is changed.
Packit 4b6dd7
	 * libsoup also sometimes seems to return a SOUP_STATUS_IO_ERROR when we cancel a message, even though we've specified SOUP_STATUS_CANCELLED
Packit 4b6dd7
	 * at cancellation time. Ho Hum. */
Packit 4b6dd7
	g_assert (message->status_code != SOUP_STATUS_NONE);
Packit 4b6dd7
Packit 4b6dd7
	if (message->status_code == SOUP_STATUS_CANCELLED ||
Packit 4b6dd7
	    ((message->status_code == SOUP_STATUS_IO_ERROR || message->status_code == SOUP_STATUS_SSL_FAILED ||
Packit 4b6dd7
	      message->status_code == SOUP_STATUS_CANT_CONNECT || message->status_code == SOUP_STATUS_CANT_RESOLVE) &&
Packit 4b6dd7
	     cancellable != NULL && g_cancellable_is_cancelled (cancellable) == TRUE)) {
Packit 4b6dd7
		/* We hackily create and cancel a new GCancellable so that we can set the error using it and therefore save ourselves a translatable
Packit 4b6dd7
		 * string and the associated maintenance. */
Packit 4b6dd7
		GCancellable *error_cancellable = g_cancellable_new ();
Packit 4b6dd7
		g_cancellable_cancel (error_cancellable);
Packit 4b6dd7
		g_assert (g_cancellable_set_error_if_cancelled (error_cancellable, error) == TRUE);
Packit 4b6dd7
		g_object_unref (error_cancellable);
Packit 4b6dd7
Packit 4b6dd7
		/* As per the above comment, force the status to be SOUP_STATUS_CANCELLED. */
Packit 4b6dd7
		soup_message_set_status (message, SOUP_STATUS_CANCELLED);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Free things */
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
	g_object_unref (session);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
guint
Packit 4b6dd7
_gdata_service_send_message (GDataService *self, SoupMessage *message, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	/* Based on code from evolution-data-server's libgdata:
Packit 4b6dd7
	 *  Ebby Wiselyn <ebbywiselyn@gmail.com>
Packit 4b6dd7
	 *  Jason Willis <zenbrother@gmail.com>
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
Packit 4b6dd7
	 */
Packit 4b6dd7
Packit 4b6dd7
	soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);
Packit 4b6dd7
	_gdata_service_actually_send_message (self->priv->session, message, cancellable, error);
Packit 4b6dd7
	soup_message_set_flags (message, 0);
Packit 4b6dd7
Packit 4b6dd7
	/* Handle redirections specially so we don't lose our custom headers when making the second request */
Packit 4b6dd7
	if (SOUP_STATUS_IS_REDIRECTION (message->status_code)) {
Packit 4b6dd7
		SoupURI *new_uri;
Packit 4b6dd7
		const gchar *new_location;
Packit 4b6dd7
Packit 4b6dd7
		new_location = soup_message_headers_get_one (message->response_headers, "Location");
Packit 4b6dd7
		g_return_val_if_fail (new_location != NULL, SOUP_STATUS_NONE);
Packit 4b6dd7
Packit 4b6dd7
		new_uri = soup_uri_new_with_base (soup_message_get_uri (message), new_location);
Packit 4b6dd7
		if (new_uri == NULL) {
Packit 4b6dd7
			gchar *uri_string = soup_uri_to_string (new_uri, FALSE);
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
Packit 4b6dd7
			             /* Translators: the parameter is the URI which is invalid. */
Packit 4b6dd7
			             _("Invalid redirect URI: %s"), uri_string);
Packit 4b6dd7
			g_free (uri_string);
Packit 4b6dd7
			return SOUP_STATUS_NONE;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		/* Allow overriding the URI for testing. */
Packit 4b6dd7
		soup_uri_set_port (new_uri, _gdata_service_get_https_port ());
Packit 4b6dd7
Packit 4b6dd7
		soup_message_set_uri (message, new_uri);
Packit 4b6dd7
		soup_uri_free (new_uri);
Packit 4b6dd7
Packit 4b6dd7
		/* Send the message again */
Packit 4b6dd7
		_gdata_service_actually_send_message (self->priv->session, message, cancellable, error);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Not authorised, or authorisation has expired. If we were authorised in the first place, attempt to refresh the authorisation and
Packit 4b6dd7
	 * try sending the message again (but only once, so we don't get caught in an infinite loop of denied authorisation errors).
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Note that we have to re-process the message with the authoriser so that its authorisation headers get updated after the refresh
Packit 4b6dd7
	 * (bgo#653535). */
Packit 4b6dd7
	if (message->status_code == SOUP_STATUS_UNAUTHORIZED ||
Packit 4b6dd7
	    message->status_code == SOUP_STATUS_FORBIDDEN ||
Packit 4b6dd7
	    message->status_code == SOUP_STATUS_NOT_FOUND) {
Packit 4b6dd7
		GDataAuthorizer *authorizer = self->priv->authorizer;
Packit 4b6dd7
Packit 4b6dd7
		if (authorizer != NULL && gdata_authorizer_refresh_authorization (authorizer, cancellable, NULL) == TRUE) {
Packit 4b6dd7
			GDataAuthorizationDomain *domain;
Packit 4b6dd7
Packit 4b6dd7
			/* Re-process the request */
Packit 4b6dd7
			domain = g_object_get_data (G_OBJECT (message), "gdata-authorization-domain");
Packit 4b6dd7
			g_assert (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
Packit 4b6dd7
			gdata_authorizer_process_request (authorizer, domain, message);
Packit 4b6dd7
Packit 4b6dd7
			/* Send the message again */
Packit 4b6dd7
			g_clear_error (error);
Packit 4b6dd7
			_gdata_service_actually_send_message (self->priv->session, message, cancellable, error);
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return message->status_code;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	/* Input */
Packit 4b6dd7
	GDataAuthorizationDomain *domain;
Packit 4b6dd7
	gchar *feed_uri;
Packit 4b6dd7
	GDataQuery *query;
Packit 4b6dd7
	GType entry_type;
Packit 4b6dd7
Packit 4b6dd7
	/* Output */
Packit 4b6dd7
	GDataQueryProgressCallback progress_callback;
Packit 4b6dd7
	gpointer progress_user_data;
Packit 4b6dd7
	GDestroyNotify destroy_progress_user_data;
Packit 4b6dd7
} QueryAsyncData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
query_async_data_free (QueryAsyncData *self)
Packit 4b6dd7
{
Packit 4b6dd7
	if (self->domain != NULL)
Packit 4b6dd7
		g_object_unref (self->domain);
Packit 4b6dd7
Packit 4b6dd7
	g_free (self->feed_uri);
Packit 4b6dd7
	if (self->query)
Packit 4b6dd7
		g_object_unref (self->query);
Packit 4b6dd7
Packit 4b6dd7
	g_slice_free (QueryAsyncData, self);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
query_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataService *service = GDATA_SERVICE (source_object);
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	QueryAsyncData *data = task_data;
Packit 4b6dd7
	g_autoptr(GDataFeed) feed = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Execute the query and return */
Packit 4b6dd7
	feed = __gdata_service_query (service, data->domain, data->feed_uri, data->query, data->entry_type, cancellable,
Packit 4b6dd7
	                              data->progress_callback, data->progress_user_data, &error);
Packit 4b6dd7
	if (feed == NULL && error != NULL)
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_pointer (task, g_steal_pointer (&feed), g_object_unref);
Packit 4b6dd7
Packit 4b6dd7
	if (data->destroy_progress_user_data != NULL) {
Packit 4b6dd7
		data->destroy_progress_user_data (data->progress_user_data);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query_async:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
Packit 4b6dd7
 * @feed_uri: the feed URI to query, including the host name and protocol
Packit 4b6dd7
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
Packit 4b6dd7
 * @entry_type: a #GType for the #GDataEntrys to build from the XML
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @progress_callback: (allow-none) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
Packit 4b6dd7
 * @progress_user_data: (closure): data to pass to the @progress_callback function
Packit 4b6dd7
 * @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
Packit 4b6dd7
 * called with @progress_user_data as a parameter and can be used to free any memory allocated for it.
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when the query is finished
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Queries the service's @feed_uri feed to build a #GDataFeed. @self, @feed_uri and
Packit 4b6dd7
 * @query are all reffed/copied when this function is called, so can safely be freed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_service_query(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the operation is finished, @callback will be called. You can then call gdata_service_query_finish()
Packit 4b6dd7
 * to get the results of the operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.1
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_query_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
Packit 4b6dd7
                           GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
Packit 4b6dd7
                           GDestroyNotify destroy_progress_user_data, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
	QueryAsyncData *data;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
	g_return_if_fail (feed_uri != NULL);
Packit 4b6dd7
	g_return_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY));
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
	g_return_if_fail (callback != NULL);
Packit 4b6dd7
Packit 4b6dd7
	data = g_slice_new (QueryAsyncData);
Packit 4b6dd7
	data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
Packit 4b6dd7
	data->feed_uri = g_strdup (feed_uri);
Packit 4b6dd7
	data->query = (query != NULL) ? g_object_ref (query) : NULL;
Packit 4b6dd7
	data->entry_type = entry_type;
Packit 4b6dd7
	data->progress_callback = progress_callback;
Packit 4b6dd7
	data->progress_user_data = progress_user_data;
Packit 4b6dd7
	data->destroy_progress_user_data = destroy_progress_user_data;
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_service_query_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) query_async_data_free);
Packit 4b6dd7
	g_task_run_in_thread (task, query_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query_finish:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous query operation started with gdata_service_query_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a #GDataFeed of query results, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 */
Packit 4b6dd7
GDataFeed *
Packit 4b6dd7
gdata_service_query_finish (GDataService *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_service_query_async), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_pointer (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Does the bulk of the work of gdata_service_query. Split out because certain queries (such as that done by
Packit 4b6dd7
 * gdata_service_query_single_entry()) only return a single entry, and thus need special parsing code. */
Packit 4b6dd7
SoupMessage *
Packit 4b6dd7
_gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query,
Packit 4b6dd7
                      GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	const gchar *etag = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Append the ETag header if possible */
Packit 4b6dd7
	if (query != NULL)
Packit 4b6dd7
		etag = gdata_query_get_etag (query);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the message */
Packit 4b6dd7
	if (query != NULL) {
Packit 4b6dd7
		gchar *query_uri = gdata_query_get_query_uri (query, feed_uri);
Packit 4b6dd7
		message = _gdata_service_build_message (self, domain, SOUP_METHOD_GET, query_uri, etag, FALSE);
Packit 4b6dd7
		g_free (query_uri);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		message = _gdata_service_build_message (self, domain, SOUP_METHOD_GET, feed_uri, etag, FALSE);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Note that cancellation only applies to network activity; not to the processing done afterwards */
Packit 4b6dd7
	status = _gdata_service_send_message (self, message, cancellable, error);
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_NOT_MODIFIED || status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Not modified (ETag has worked), or cancelled (in which case the error has been set) */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_OK) {
Packit 4b6dd7
		/* Error */
Packit 4b6dd7
		GDataServiceClass *klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
		g_assert (klass->parse_error_response != NULL);
Packit 4b6dd7
		klass->parse_error_response (self, GDATA_OPERATION_QUERY, status, message->reason_phrase, message->response_body->data,
Packit 4b6dd7
		                             message->response_body->length, error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return message;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static GDataFeed *
Packit 4b6dd7
__gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
Packit 4b6dd7
                       GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServiceClass *klass;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	GDataFeed *feed;
Packit 4b6dd7
Packit 4b6dd7
	klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
Packit 4b6dd7
	/* Are we off the end of the final page? */
Packit 4b6dd7
	if (query != NULL && _gdata_query_is_finished (query)) {
Packit 4b6dd7
		GTimeVal updated;
Packit 4b6dd7
Packit 4b6dd7
		/* Build an empty dummy feed to signify the end of the list. */
Packit 4b6dd7
		g_get_current_time (&updated);
Packit 4b6dd7
		return _gdata_feed_new (klass->feed_type, "Empty feed", "feed1",
Packit 4b6dd7
		                        updated.tv_sec);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Send the request. */
Packit 4b6dd7
	message = _gdata_service_query (self, domain, feed_uri, query, cancellable, error);
Packit 4b6dd7
	if (message == NULL)
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
	g_assert (klass->parse_feed != NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the response. */
Packit 4b6dd7
	feed = klass->parse_feed (self, domain, query, entry_type,
Packit 4b6dd7
	                          message, cancellable, progress_callback,
Packit 4b6dd7
	                          progress_user_data, error);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	return feed;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static GDataFeed *
Packit 4b6dd7
real_parse_feed (GDataService *self,
Packit 4b6dd7
                 GDataAuthorizationDomain *domain,
Packit 4b6dd7
                 GDataQuery *query,
Packit 4b6dd7
                 GType entry_type,
Packit 4b6dd7
                 SoupMessage *message,
Packit 4b6dd7
                 GCancellable *cancellable,
Packit 4b6dd7
                 GDataQueryProgressCallback progress_callback,
Packit 4b6dd7
                 gpointer progress_user_data,
Packit 4b6dd7
                 GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataServiceClass *klass;
Packit 4b6dd7
	GDataFeed *feed = NULL;
Packit 4b6dd7
	SoupMessageHeaders *headers;
Packit 4b6dd7
	const gchar *content_type;
Packit 4b6dd7
Packit 4b6dd7
	klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
	headers = message->response_headers;
Packit 4b6dd7
	content_type = soup_message_headers_get_content_type (headers, NULL);
Packit 4b6dd7
Packit 4b6dd7
	if (content_type != NULL && strcmp (content_type, "application/json") == 0) {
Packit 4b6dd7
		/* Definitely JSON. */
Packit 4b6dd7
		g_debug("JSON content type detected.");
Packit 4b6dd7
		feed = _gdata_feed_new_from_json (klass->feed_type, message->response_body->data, message->response_body->length, entry_type,
Packit 4b6dd7
		                                  progress_callback, progress_user_data, error);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		/* Potentially XML. Don't bother checking the Content-Type, since the parser
Packit 4b6dd7
		 * will fail gracefully if the response body is not valid XML. */
Packit 4b6dd7
		g_debug("XML content type detected.");
Packit 4b6dd7
		feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, message->response_body->length, entry_type,
Packit 4b6dd7
		                                 progress_callback, progress_user_data, error);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Update the query with the feed's ETag */
Packit 4b6dd7
	if (query != NULL && feed != NULL && gdata_feed_get_etag (feed) != NULL)
Packit 4b6dd7
		gdata_query_set_etag (query, gdata_feed_get_etag (feed));
Packit 4b6dd7
Packit 4b6dd7
	/* Update the query with the next and previous URIs from the feed */
Packit 4b6dd7
	if (query != NULL && feed != NULL) {
Packit 4b6dd7
		GDataLink *_link;
Packit 4b6dd7
		const gchar *token;
Packit 4b6dd7
Packit 4b6dd7
		_gdata_query_clear_pagination (query);
Packit 4b6dd7
Packit 4b6dd7
		/* Atom-style next and previous page links. */
Packit 4b6dd7
		_link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/next");
Packit 4b6dd7
		if (_link != NULL)
Packit 4b6dd7
			_gdata_query_set_next_uri (query, gdata_link_get_uri (_link));
Packit 4b6dd7
		_link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/previous");
Packit 4b6dd7
		if (_link != NULL)
Packit 4b6dd7
			_gdata_query_set_previous_uri (query, gdata_link_get_uri (_link));
Packit 4b6dd7
Packit 4b6dd7
		/* JSON-style next page token. (There is no previous page
Packit 4b6dd7
		 * token.) */
Packit 4b6dd7
		token = gdata_feed_get_next_page_token (feed);
Packit 4b6dd7
		if (token != NULL)
Packit 4b6dd7
			_gdata_query_set_next_page_token (query, token);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return feed;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
Packit 4b6dd7
 * @feed_uri: the feed URI to query, including the host name and protocol
Packit 4b6dd7
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
Packit 4b6dd7
 * @entry_type: a #GType for the #GDataEntrys to build from the XML
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @progress_callback: (allow-none) (scope call) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
Packit 4b6dd7
 * @progress_user_data: (closure): data to pass to the @progress_callback function
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Queries the service's @feed_uri feed to build a #GDataFeed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
Packit 4b6dd7
 * If the operation was cancelled before or during network activity, the error %G_IO_ERROR_CANCELLED will be returned. Cancellation has no effect
Packit 4b6dd7
 * after network activity has finished, however, and the query will return successfully (or return an error sent by the server) if it is first
Packit 4b6dd7
 * cancelled after network activity has finished. See the <link linkend="cancellable-support">overview of cancellation</link> for
Packit 4b6dd7
 * more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * A %GDATA_SERVICE_ERROR_PROTOCOL_ERROR will be returned if the server indicates there is a problem with the query, but subclasses may override
Packit 4b6dd7
 * this and return their own errors. See their documentation for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For each entry in the response feed, @progress_callback will be called in the main thread. If there was an error parsing the XML response,
Packit 4b6dd7
 * a #GDataParserError will be returned.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If the query is successful and the feed supports pagination, @query will be updated with the pagination URIs, and the next or previous page
Packit 4b6dd7
 * can then be loaded by calling gdata_query_next_page() or gdata_query_previous_page() before running the query again.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If the #GDataQuery's ETag is set and it finds a match on the server, %NULL will be returned, but @error will remain unset. Otherwise,
Packit 4b6dd7
 * @query's ETag will be updated with the ETag from the returned feed, if available.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a #GDataFeed of query results, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataFeed *
Packit 4b6dd7
gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, const gchar *feed_uri, GDataQuery *query, GType entry_type,
Packit 4b6dd7
                     GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
Packit 4b6dd7
	g_return_val_if_fail (feed_uri != NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY), NULL);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	return __gdata_service_query (self, domain, feed_uri, query, entry_type, cancellable, progress_callback, progress_user_data, error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query_single_entry:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
Packit 4b6dd7
 * @entry_id: the entry ID of the desired entry
Packit 4b6dd7
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
Packit 4b6dd7
 * @entry_type: a #GType for the #GDataEntry to build from the XML
Packit 4b6dd7
 * @cancellable: (allow-none): a #GCancellable, or %NULL
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Retrieves information about the single entry with the given @entry_id. @entry_id should be as returned by
Packit 4b6dd7
 * gdata_entry_get_id().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Parameters and errors are as for gdata_service_query(). Most of the properties of @query aren't relevant, and
Packit 4b6dd7
 * will cause a server-side error if used. The most useful property to use is #GDataQuery:etag, which will cause the
Packit 4b6dd7
 * server to not return anything if the entry hasn't been modified since it was given the specified ETag; thus saving
Packit 4b6dd7
 * bandwidth. If the server does not return anything for this reason, gdata_service_query_single_entry() will return
Packit 4b6dd7
 * %NULL, but will not set an error in @error.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_query_single_entry (GDataService *self, GDataAuthorizationDomain *domain, const gchar *entry_id, GDataQuery *query, GType entry_type,
Packit 4b6dd7
                                  GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataEntryClass *klass;
Packit 4b6dd7
	GDataEntry *entry;
Packit 4b6dd7
	gchar *entry_uri;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	SoupMessageHeaders *headers;
Packit 4b6dd7
	const gchar *content_type;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
Packit 4b6dd7
	g_return_val_if_fail (entry_id != NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY) == TRUE, NULL);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Query for just the specified entry */
Packit 4b6dd7
	klass = GDATA_ENTRY_CLASS (g_type_class_ref (entry_type));
Packit 4b6dd7
	g_assert (klass->get_entry_uri != NULL);
Packit 4b6dd7
Packit 4b6dd7
	entry_uri = klass->get_entry_uri (entry_id);
Packit 4b6dd7
	message = _gdata_service_query (GDATA_SERVICE (self), domain, entry_uri, query, cancellable, error);
Packit 4b6dd7
	g_free (entry_uri);
Packit 4b6dd7
Packit 4b6dd7
	if (message == NULL) {
Packit 4b6dd7
		g_type_class_unref (klass);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
Packit 4b6dd7
	headers = message->response_headers;
Packit 4b6dd7
	content_type = soup_message_headers_get_content_type (headers, NULL);
Packit 4b6dd7
Packit 4b6dd7
	if (g_strcmp0 (content_type, "application/json") == 0) {
Packit 4b6dd7
		entry = GDATA_ENTRY (gdata_parsable_new_from_json (entry_type, message->response_body->data, message->response_body->length, error));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		entry = GDATA_ENTRY (gdata_parsable_new_from_xml (entry_type, message->response_body->data, message->response_body->length, error));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
	g_type_class_unref (klass);
Packit 4b6dd7
Packit 4b6dd7
	return entry;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	GDataAuthorizationDomain *domain;
Packit 4b6dd7
	gchar *entry_id;
Packit 4b6dd7
	GDataQuery *query;
Packit 4b6dd7
	GType entry_type;
Packit 4b6dd7
} QuerySingleEntryAsyncData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
query_single_entry_async_data_free (QuerySingleEntryAsyncData *data)
Packit 4b6dd7
{
Packit 4b6dd7
	if (data->domain != NULL)
Packit 4b6dd7
		g_object_unref (data->domain);
Packit 4b6dd7
Packit 4b6dd7
	g_free (data->entry_id);
Packit 4b6dd7
	if (data->query != NULL)
Packit 4b6dd7
		g_object_unref (data->query);
Packit 4b6dd7
	g_slice_free (QuerySingleEntryAsyncData, data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
query_single_entry_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataService *service = GDATA_SERVICE (source_object);
Packit 4b6dd7
	g_autoptr(GDataEntry) entry = NULL;
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	QuerySingleEntryAsyncData *data = task_data;
Packit 4b6dd7
Packit 4b6dd7
	/* Execute the query and return */
Packit 4b6dd7
	entry = gdata_service_query_single_entry (service, data->domain, data->entry_id, data->query, data->entry_type, cancellable, &error);
Packit 4b6dd7
	if (entry == NULL && error != NULL)
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_pointer (task, g_steal_pointer (&entry), g_object_unref);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query_single_entry_async:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the query falls under, or %NULL
Packit 4b6dd7
 * @entry_id: the entry ID of the desired entry
Packit 4b6dd7
 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
Packit 4b6dd7
 * @entry_type: a #GType for the #GDataEntry to build from the XML
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when the query is finished
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Retrieves information about the single entry with the given @entry_id. @entry_id should be as returned by
Packit 4b6dd7
 * gdata_entry_get_id(). @self, @query and @entry_id are reffed/copied when this
Packit 4b6dd7
 * function is called, so can safely be freed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_service_query_single_entry(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the operation is finished, @callback will be called. You can then call gdata_service_query_single_entry_finish()
Packit 4b6dd7
 * to get the results of the operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_query_single_entry_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *entry_id, GDataQuery *query,
Packit 4b6dd7
                                        GType entry_type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
	QuerySingleEntryAsyncData *data;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
	g_return_if_fail (entry_id != NULL);
Packit 4b6dd7
	g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
Packit 4b6dd7
	g_return_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY) == TRUE);
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
	g_return_if_fail (callback != NULL);
Packit 4b6dd7
Packit 4b6dd7
	data = g_slice_new (QuerySingleEntryAsyncData);
Packit 4b6dd7
	data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
Packit 4b6dd7
	data->query = (query != NULL) ? g_object_ref (query) : NULL;
Packit 4b6dd7
	data->entry_id = g_strdup (entry_id);
Packit 4b6dd7
	data->entry_type = entry_type;
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_service_query_single_entry_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) query_single_entry_async_data_free);
Packit 4b6dd7
	g_task_run_in_thread (task, query_single_entry_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_query_single_entry_finish:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous query operation for a single entry, as started with gdata_service_query_single_entry_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): a #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_query_single_entry_finish (GDataService *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_service_query_single_entry_async), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_pointer (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	GDataAuthorizationDomain *domain;
Packit 4b6dd7
	gchar *upload_uri;
Packit 4b6dd7
	GDataEntry *entry;
Packit 4b6dd7
} InsertEntryAsyncData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
insert_entry_async_data_free (InsertEntryAsyncData *self)
Packit 4b6dd7
{
Packit 4b6dd7
	if (self->domain != NULL)
Packit 4b6dd7
		g_object_unref (self->domain);
Packit 4b6dd7
Packit 4b6dd7
	g_free (self->upload_uri);
Packit 4b6dd7
	if (self->entry)
Packit 4b6dd7
		g_object_unref (self->entry);
Packit 4b6dd7
Packit 4b6dd7
	g_slice_free (InsertEntryAsyncData, self);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
insert_entry_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataService *service = GDATA_SERVICE (source_object);
Packit 4b6dd7
	g_autoptr(GDataEntry) updated_entry = NULL;
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	InsertEntryAsyncData *data = task_data;
Packit 4b6dd7
Packit 4b6dd7
	/* Insert the entry and return */
Packit 4b6dd7
	updated_entry = gdata_service_insert_entry (service, data->domain, data->upload_uri, data->entry, cancellable, &error);
Packit 4b6dd7
	if (updated_entry == NULL)
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_pointer (task, g_steal_pointer (&updated_entry), g_object_unref);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_insert_entry_async:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the insertion operation falls under, or %NULL
Packit 4b6dd7
 * @upload_uri: the URI to which the upload should be sent
Packit 4b6dd7
 * @entry: the #GDataEntry to insert
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when insertion is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Inserts @entry by uploading it to the online service at @upload_uri. @self, @upload_uri and
Packit 4b6dd7
 * @entry are all reffed/copied when this function is called, so can safely be freed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_service_insert_entry(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the operation is finished, @callback will be called. You can then call gdata_service_insert_entry_finish()
Packit 4b6dd7
 * to get the results of the operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_insert_entry_async (GDataService *self, GDataAuthorizationDomain *domain, const gchar *upload_uri, GDataEntry *entry,
Packit 4b6dd7
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
	InsertEntryAsyncData *data;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
	g_return_if_fail (upload_uri != NULL);
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_ENTRY (entry));
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
Packit 4b6dd7
	data = g_slice_new (InsertEntryAsyncData);
Packit 4b6dd7
	data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
Packit 4b6dd7
	data->upload_uri = g_strdup (upload_uri);
Packit 4b6dd7
	data->entry = g_object_ref (entry);
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_service_insert_entry_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) insert_entry_async_data_free);
Packit 4b6dd7
	g_task_run_in_thread (task, insert_entry_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_insert_entry_finish:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous entry insertion operation started with gdata_service_insert_entry_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.3.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_insert_entry_finish (GDataService *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_service_insert_entry_async), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_pointer (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_insert_entry:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the insertion operation falls under, or %NULL
Packit 4b6dd7
 * @upload_uri: the URI to which the upload should be sent
Packit 4b6dd7
 * @entry: the #GDataEntry to insert
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Inserts @entry by uploading it to the online service at @upload_uri. For more information about the concept of inserting entries, see
Packit 4b6dd7
 * the <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/basics.html#InsertingEntry">online documentation</ulink> for the GData
Packit 4b6dd7
 * protocol.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The service will return an updated version of the entry, which is the return value of this function on success.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
Packit 4b6dd7
 * If the operation was cancelled before or during network activity, the error %G_IO_ERROR_CANCELLED will be returned. Cancellation has no effect
Packit 4b6dd7
 * after network activity has finished, however, and the insertion will return successfully (or return an error sent by the server) if it is first
Packit 4b6dd7
 * cancelled after network activity has finished. See the <link linkend="cancellable-support">overview of cancellation</link> for
Packit 4b6dd7
 * more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If the entry is marked as already having been inserted a %GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED error will be returned immediately
Packit 4b6dd7
 * (there will be no network requests).
Packit 4b6dd7
 *
Packit 4b6dd7
 * If there is an error inserting the entry, a %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error will be returned. Currently, subclasses
Packit 4b6dd7
 * <emphasis>cannot</emphasis> cannot override this or provide more specific errors.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_insert_entry (GDataService *self, GDataAuthorizationDomain *domain, const gchar *upload_uri, GDataEntry *entry,
Packit 4b6dd7
                            GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataEntry *updated_entry;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	gchar *upload_data;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	GDataParsableClass *klass;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
Packit 4b6dd7
	g_return_val_if_fail (upload_uri != NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), NULL);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	if (gdata_entry_is_inserted (entry) == TRUE) {
Packit 4b6dd7
		g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
Packit 4b6dd7
		                     _("The entry has already been inserted."));
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	message = _gdata_service_build_message (self, domain, SOUP_METHOD_POST, upload_uri, NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Append the data */
Packit 4b6dd7
	klass = GDATA_PARSABLE_GET_CLASS (entry);
Packit 4b6dd7
	g_assert (klass->get_content_type != NULL);
Packit 4b6dd7
	if (g_strcmp0 (klass->get_content_type (), "application/json") == 0) {
Packit 4b6dd7
		upload_data = gdata_parsable_get_json (GDATA_PARSABLE (entry));
Packit 4b6dd7
		soup_message_set_request (message, "application/json", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
Packit 4b6dd7
		soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	status = _gdata_service_send_message (self, message, cancellable, error);
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_NONE || status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Redirect error or cancelled */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_CREATED && status != SOUP_STATUS_OK) {
Packit 4b6dd7
		/* Error: for XML APIs Google returns CREATED and for JSON it returns OK. */
Packit 4b6dd7
		GDataServiceClass *service_klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
		g_assert (service_klass->parse_error_response != NULL);
Packit 4b6dd7
		service_klass->parse_error_response (self, GDATA_OPERATION_INSERTION, status, message->reason_phrase, message->response_body->data,
Packit 4b6dd7
		                                     message->response_body->length, error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the XML or JSON according to GDataEntry type; create and return a new GDataEntry of the same type as @entry */
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
	if (g_strcmp0 (klass->get_content_type (), "application/json") == 0) {
Packit 4b6dd7
		updated_entry = GDATA_ENTRY (gdata_parsable_new_from_json (G_OBJECT_TYPE (entry), message->response_body->data,
Packit 4b6dd7
		                             message->response_body->length, error));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		updated_entry = GDATA_ENTRY (gdata_parsable_new_from_xml (G_OBJECT_TYPE (entry), message->response_body->data,
Packit 4b6dd7
		                             message->response_body->length, error));
Packit 4b6dd7
	}
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	return updated_entry;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	GDataAuthorizationDomain *domain;
Packit 4b6dd7
	GDataEntry *entry;
Packit 4b6dd7
} UpdateEntryAsyncData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
update_entry_async_data_free (UpdateEntryAsyncData *data)
Packit 4b6dd7
{
Packit 4b6dd7
	if (data->domain != NULL)
Packit 4b6dd7
		g_object_unref (data->domain);
Packit 4b6dd7
Packit 4b6dd7
	if (data->entry != NULL)
Packit 4b6dd7
		g_object_unref (data->entry);
Packit 4b6dd7
Packit 4b6dd7
	g_slice_free (UpdateEntryAsyncData, data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
update_entry_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataService *service = GDATA_SERVICE (source_object);
Packit 4b6dd7
	g_autoptr(GDataEntry) updated_entry = NULL;
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	UpdateEntryAsyncData *data = task_data;
Packit 4b6dd7
Packit 4b6dd7
	/* Update the entry and return */
Packit 4b6dd7
	updated_entry = gdata_service_update_entry (service, data->domain, data->entry, cancellable, &error);
Packit 4b6dd7
	if (updated_entry == NULL)
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_pointer (task, g_steal_pointer (&updated_entry), g_object_unref);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_update_entry_async:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the update operation falls under, or %NULL
Packit 4b6dd7
 * @entry: the #GDataEntry to update
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when the update is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Updates @entry by PUTting it to its <literal>edit</literal> link's URI. @self and
Packit 4b6dd7
 * @entry are both reffed when this function is called, so can safely be unreffed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_service_update_entry(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the operation is finished, @callback will be called. You can then call gdata_service_update_entry_finish()
Packit 4b6dd7
 * to get the results of the operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_update_entry_async (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry,
Packit 4b6dd7
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
	UpdateEntryAsyncData *data;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_ENTRY (entry));
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
Packit 4b6dd7
	data = g_slice_new (UpdateEntryAsyncData);
Packit 4b6dd7
	data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
Packit 4b6dd7
	data->entry = g_object_ref (entry);
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (task, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_service_update_entry_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) update_entry_async_data_free);
Packit 4b6dd7
	g_task_run_in_thread (task, update_entry_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_update_entry_finish:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous entry update operation started with gdata_service_update_entry_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.3.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_update_entry_finish (GDataService *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_service_update_entry_async), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_pointer (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_update_entry:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the update operation falls under, or %NULL
Packit 4b6dd7
 * @entry: the #GDataEntry to update
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Updates @entry by PUTting it to its <literal>edit</literal> link's URI. For more information about the concept of updating entries, see
Packit 4b6dd7
 * the <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/basics.html#UpdatingEntry">online documentation</ulink> for the GData
Packit 4b6dd7
 * protocol.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The service will return an updated version of the entry, which is the return value of this function on success.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
Packit 4b6dd7
 * If the operation was cancelled before or during network activity, the error %G_IO_ERROR_CANCELLED will be returned. Cancellation has no effect
Packit 4b6dd7
 * after network activity has finished, however, and the update will return successfully (or return an error sent by the server) if it is first
Packit 4b6dd7
 * cancelled after network activity has finished. See the <link linkend="cancellable-support">overview of cancellation</link> for
Packit 4b6dd7
 * more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If there is an error updating the entry, a %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error will be returned. Currently, subclasses
Packit 4b6dd7
 * <emphasis>cannot</emphasis> cannot override this or provide more specific errors.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): an updated #GDataEntry, or %NULL; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataEntry *
Packit 4b6dd7
gdata_service_update_entry (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataEntry *updated_entry;
Packit 4b6dd7
	GDataLink *_link;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	gchar *upload_data;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	GDataParsableClass *klass;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), NULL);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), NULL);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Append the data */
Packit 4b6dd7
	klass = GDATA_PARSABLE_GET_CLASS (entry);
Packit 4b6dd7
	g_assert (klass->get_content_type != NULL);
Packit 4b6dd7
	if (g_strcmp0 (klass->get_content_type (), "application/json") == 0) {
Packit 4b6dd7
		/* Get the edit URI */
Packit 4b6dd7
		_link = gdata_entry_look_up_link (entry, GDATA_LINK_SELF);
Packit 4b6dd7
		g_assert (_link != NULL);
Packit 4b6dd7
		message = _gdata_service_build_message (self, domain, SOUP_METHOD_PUT, gdata_link_get_uri (_link), gdata_entry_get_etag (entry), TRUE);
Packit 4b6dd7
		upload_data = gdata_parsable_get_json (GDATA_PARSABLE (entry));
Packit 4b6dd7
		soup_message_set_request (message, "application/json", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		/* Get the edit URI */
Packit 4b6dd7
		_link = gdata_entry_look_up_link (entry, GDATA_LINK_EDIT);
Packit 4b6dd7
		g_assert (_link != NULL);
Packit 4b6dd7
		message = _gdata_service_build_message (self, domain, SOUP_METHOD_PUT, gdata_link_get_uri (_link), gdata_entry_get_etag (entry), TRUE);
Packit 4b6dd7
		upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
Packit 4b6dd7
		soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	status = _gdata_service_send_message (self, message, cancellable, error);
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_NONE || status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Redirect error or cancelled */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_OK) {
Packit 4b6dd7
		/* Error */
Packit 4b6dd7
		GDataServiceClass *service_klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
		g_assert (service_klass->parse_error_response != NULL);
Packit 4b6dd7
		service_klass->parse_error_response (self, GDATA_OPERATION_UPDATE, status, message->reason_phrase, message->response_body->data,
Packit 4b6dd7
		                                     message->response_body->length, error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the XML; create and return a new GDataEntry of the same type as @entry */
Packit 4b6dd7
	if (g_strcmp0 (klass->get_content_type (), "application/json") == 0) {
Packit 4b6dd7
		updated_entry = GDATA_ENTRY (gdata_parsable_new_from_json (G_OBJECT_TYPE (entry), message->response_body->data,
Packit 4b6dd7
		                         message->response_body->length, error));
Packit 4b6dd7
	} else {
Packit 4b6dd7
		updated_entry = GDATA_ENTRY (gdata_parsable_new_from_xml (G_OBJECT_TYPE (entry), message->response_body->data,
Packit 4b6dd7
		                             message->response_body->length, error));
Packit 4b6dd7
	}
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	return updated_entry;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	GDataAuthorizationDomain *domain;
Packit 4b6dd7
	GDataEntry *entry;
Packit 4b6dd7
} DeleteEntryAsyncData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
delete_entry_async_data_free (DeleteEntryAsyncData *data)
Packit 4b6dd7
{
Packit 4b6dd7
	if (data->domain != NULL)
Packit 4b6dd7
		g_object_unref (data->domain);
Packit 4b6dd7
Packit 4b6dd7
	if (data->entry != NULL)
Packit 4b6dd7
		g_object_unref (data->entry);
Packit 4b6dd7
Packit 4b6dd7
	g_slice_free (DeleteEntryAsyncData, data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
delete_entry_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataService *service = GDATA_SERVICE (source_object);
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
	DeleteEntryAsyncData *data = task_data;
Packit 4b6dd7
Packit 4b6dd7
	/* Delete the entry and return */
Packit 4b6dd7
	if (!gdata_service_delete_entry (service, data->domain, data->entry, cancellable, &error))
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_boolean (task, TRUE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_delete_entry_async:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the deletion falls under, or %NULL
Packit 4b6dd7
 * @entry: the #GDataEntry to delete
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when deletion is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Deletes @entry from the server. @self and @entry are both reffed when this function is called,
Packit 4b6dd7
 * so can safely be unreffed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_service_delete_entry(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the operation is finished, @callback will be called. You can then call gdata_service_delete_entry_finish()
Packit 4b6dd7
 * to get the results of the operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_delete_entry_async (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry,
Packit 4b6dd7
                                  GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
	DeleteEntryAsyncData *data;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain));
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_ENTRY (entry));
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
Packit 4b6dd7
	data = g_slice_new (DeleteEntryAsyncData);
Packit 4b6dd7
	data->domain = (domain != NULL) ? g_object_ref (domain) : NULL;
Packit 4b6dd7
	data->entry = g_object_ref (entry);
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_service_delete_entry_async);
Packit 4b6dd7
	g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) delete_entry_async_data_free);
Packit 4b6dd7
	g_task_run_in_thread (task, delete_entry_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_delete_entry_finish:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous entry deletion operation started with gdata_service_delete_entry_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE on success, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.3.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_service_delete_entry_finish (GDataService *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_service_delete_entry_async), FALSE);
Packit 4b6dd7
Packit 4b6dd7
	return g_task_propagate_boolean (G_TASK (async_result), error);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_delete_entry:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @domain: (allow-none): the #GDataAuthorizationDomain the deletion falls under, or %NULL
Packit 4b6dd7
 * @entry: the #GDataEntry to delete
Packit 4b6dd7
 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Deletes @entry from the server. For more information about the concept of deleting entries, see the
Packit 4b6dd7
 * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/basics.html#DeletingEntry">online documentation</ulink> for the GData
Packit 4b6dd7
 * protocol.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
Packit 4b6dd7
 * If the operation was cancelled before or during network activity, the error %G_IO_ERROR_CANCELLED will be returned. Cancellation has no effect
Packit 4b6dd7
 * after network activity has finished, however, and the deletion will return successfully (or return an error sent by the server) if it is first
Packit 4b6dd7
 * cancelled after network activity has finished. See the <link linkend="cancellable-support">overview of cancellation</link> for
Packit 4b6dd7
 * more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If there is an error deleting the entry, a %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error will be returned. Currently, subclasses
Packit 4b6dd7
 * <emphasis>cannot</emphasis> cannot override this or provide more specific errors.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE on success, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_service_delete_entry (GDataService *self, GDataAuthorizationDomain *domain, GDataEntry *entry, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataLink *_link;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	gchar *fixed_uri;
Packit 4b6dd7
	GDataParsableClass *klass;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (domain == NULL || GDATA_IS_AUTHORIZATION_DOMAIN (domain), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* 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. */
Packit 4b6dd7
	klass = GDATA_PARSABLE_GET_CLASS (entry);
Packit 4b6dd7
	g_assert (klass->get_content_type != NULL);
Packit 4b6dd7
	if (g_strcmp0 (klass->get_content_type (), "application/json") == 0) {
Packit 4b6dd7
		_link = gdata_entry_look_up_link (entry, GDATA_LINK_SELF);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		_link = gdata_entry_look_up_link (entry, GDATA_LINK_EDIT);
Packit 4b6dd7
	}
Packit 4b6dd7
	g_assert (_link != NULL);
Packit 4b6dd7
Packit 4b6dd7
	fixed_uri = _gdata_service_fix_uri_scheme (gdata_link_get_uri (_link));
Packit 4b6dd7
	message = _gdata_service_build_message (self, domain, SOUP_METHOD_DELETE, fixed_uri, gdata_entry_get_etag (entry), TRUE);
Packit 4b6dd7
	g_free (fixed_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	status = _gdata_service_send_message (self, message, cancellable, error);
Packit 4b6dd7
Packit 4b6dd7
	if (status == SOUP_STATUS_NONE || status == SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
		/* Redirect error or cancelled */
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	} else if (status != SOUP_STATUS_OK && status != SOUP_STATUS_NO_CONTENT) {
Packit 4b6dd7
		/* Error */
Packit 4b6dd7
		GDataServiceClass *service_klass = GDATA_SERVICE_GET_CLASS (self);
Packit 4b6dd7
		g_assert (service_klass->parse_error_response != NULL);
Packit 4b6dd7
		service_klass->parse_error_response (self, GDATA_OPERATION_DELETION, status, message->reason_phrase, message->response_body->data,
Packit 4b6dd7
		                                     message->response_body->length, error);
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	return TRUE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
notify_proxy_uri_cb (GObject *gobject, GParamSpec *pspec, GObject *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_object_notify (self, "proxy-uri");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Static function which isn't deprecated so we can continue using it internally. */
Packit 4b6dd7
static SoupURI *
Packit 4b6dd7
_get_proxy_uri (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupURI *proxy_uri;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	g_object_get (self->priv->session, SOUP_SESSION_PROXY_URI, &proxy_uri, NULL);
Packit 4b6dd7
	g_object_unref (proxy_uri); /* remove the ref added by g_object_get */
Packit 4b6dd7
Packit 4b6dd7
	return proxy_uri;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_proxy_uri:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the proxy URI on the #GDataService's #SoupSession.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none): the proxy URI, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.2.0
Packit 4b6dd7
 * Deprecated: 0.15.0: Use gdata_service_get_proxy_resolver() instead, which gives more flexibility over the proxy used.
Packit 4b6dd7
 */
Packit 4b6dd7
SoupURI *
Packit 4b6dd7
gdata_service_get_proxy_uri (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	return _get_proxy_uri (self);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Static function which isn't deprecated so we can continue using it internally. */
Packit 4b6dd7
static void
Packit 4b6dd7
_set_proxy_uri (GDataService *self, SoupURI *proxy_uri)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_object_set (self->priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "proxy-uri");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_set_proxy_uri:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @proxy_uri: (allow-none): the proxy URI, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the proxy URI on the #SoupSession used internally by the given #GDataService.
Packit 4b6dd7
 * This forces all requests through the given proxy.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @proxy_uri is %NULL, no proxy will be used.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its proxy URI setting.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.2.0
Packit 4b6dd7
 * Deprecated: 0.15.0: Use gdata_service_set_proxy_resolver() instead, which gives more flexibility over the proxy used.
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_set_proxy_uri (GDataService *self, SoupURI *proxy_uri)
Packit 4b6dd7
{
Packit 4b6dd7
	_set_proxy_uri (self, proxy_uri);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_proxy_resolver:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GProxyResolver on the #GDataService's #SoupSession.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none) (allow-none): a #GProxyResolver, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.15.0
Packit 4b6dd7
 */
Packit 4b6dd7
GProxyResolver *
Packit 4b6dd7
gdata_service_get_proxy_resolver (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return self->priv->proxy_resolver;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_set_proxy_resolver:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the #GProxyResolver on the #SoupSession used internally by the given #GDataService.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Setting this will clear the #GDataService:proxy-uri property.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.15.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_set_proxy_resolver (GDataService *self, GProxyResolver *proxy_resolver)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_return_if_fail (proxy_resolver == NULL || G_IS_PROXY_RESOLVER (proxy_resolver));
Packit 4b6dd7
Packit 4b6dd7
	if (proxy_resolver != NULL) {
Packit 4b6dd7
		g_object_ref (proxy_resolver);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_clear_object (&self->priv->proxy_resolver);
Packit 4b6dd7
	self->priv->proxy_resolver = proxy_resolver;
Packit 4b6dd7
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "proxy-resolver");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
notify_timeout_cb (GObject *gobject, GParamSpec *pspec, GObject *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_object_notify (self, "timeout");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_timeout:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataService:timeout property; the network timeout, in seconds.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the timeout, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_service_get_timeout (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	guint timeout;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), 0);
Packit 4b6dd7
Packit 4b6dd7
	g_object_get (self->priv->session, SOUP_SESSION_TIMEOUT, &timeout, NULL);
Packit 4b6dd7
Packit 4b6dd7
	return timeout;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_set_timeout:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @timeout: the timeout, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Sets the #GDataService:timeout property; the network timeout, in seconds.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If @timeout is 0, network operations will never time out.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that if a #GDataAuthorizer is being used with this #GDataService, the authorizer might also need its timeout setting.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_set_timeout (GDataService *self, guint timeout)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
	g_object_set (self->priv->session, SOUP_SESSION_TIMEOUT, timeout, NULL);
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "timeout");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
SoupSession *
Packit 4b6dd7
_gdata_service_get_session (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	return self->priv->session;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * _gdata_service_get_scheme:
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the name of the scheme to use, which will always be https. The return type used to vary according to the
Packit 4b6dd7
 * environment variable LIBGDATA_FORCE_HTTP, but Google has since switched to using HTTPS exclusively.
Packit 4b6dd7
 *
Packit 4b6dd7
 * See <ulink type="http" url="http://googlecode.blogspot.com/2011/03/improving-security-of-google-apis-with.html">Improving the security of Google
Packit 4b6dd7
 * APIs with SSL</ulink>.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the scheme to use (https)
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.6.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
_gdata_service_get_scheme (void)
Packit 4b6dd7
{
Packit 4b6dd7
	return "https";
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * _gdata_service_build_uri:
Packit 4b6dd7
 * @format: a standard printf() format string
Packit 4b6dd7
 * @...: the arguments to insert in the output
Packit 4b6dd7
 *
Packit 4b6dd7
 * Builds a URI from the given @format string, replacing each %%s format placeholder with a URI-escaped version of the
Packit 4b6dd7
 * corresponding argument, and each %%p format placeholder with a non-escaped version of the corresponding argument. No
Packit 4b6dd7
 * other printf() format placeholders are supported at the moment except %%d, which prints a signed integer; and
Packit 4b6dd7
 * %%, which prints a literal percent symbol.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The returned URI is guaranteed to use the scheme returned by _gdata_service_get_scheme(). The format string, once all the arguments have been
Packit 4b6dd7
 * inserted into it, must include a scheme, but it doesn't matter which one.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: a newly allocated URI string; free with g_free()
Packit 4b6dd7
 */
Packit 4b6dd7
gchar *
Packit 4b6dd7
_gdata_service_build_uri (const gchar *format, ...)
Packit 4b6dd7
{
Packit 4b6dd7
	const gchar *p;
Packit 4b6dd7
	gchar *fixed_uri;
Packit 4b6dd7
	GString *uri;
Packit 4b6dd7
	va_list args;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (format != NULL, NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Allocate a GString to build the URI in with at least as much space as the format string */
Packit 4b6dd7
	uri = g_string_sized_new (strlen (format));
Packit 4b6dd7
Packit 4b6dd7
	/* Build the URI */
Packit 4b6dd7
	va_start (args, format);
Packit 4b6dd7
Packit 4b6dd7
	for (p = format; *p != '\0'; p++) {
Packit 4b6dd7
		if (*p != '%') {
Packit 4b6dd7
			g_string_append_c (uri, *p);
Packit 4b6dd7
			continue;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		switch(*++p) {
Packit 4b6dd7
			case 's':
Packit 4b6dd7
				g_string_append_uri_escaped (uri, va_arg (args, gchar*), NULL, TRUE);
Packit 4b6dd7
				break;
Packit 4b6dd7
			case 'p':
Packit 4b6dd7
				g_string_append (uri, va_arg (args, gchar*));
Packit 4b6dd7
				break;
Packit 4b6dd7
			case 'd':
Packit 4b6dd7
				g_string_append_printf (uri, "%d", va_arg (args, gint));
Packit 4b6dd7
				break;
Packit 4b6dd7
			case '%':
Packit 4b6dd7
				g_string_append_c (uri, '%');
Packit 4b6dd7
				break;
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_error ("Unrecognized format placeholder '%%%c' in format '%s'. This is a programmer error.", *p, format);
Packit 4b6dd7
				break;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	va_end (args);
Packit 4b6dd7
Packit 4b6dd7
	/* Fix the scheme to always be HTTPS */
Packit 4b6dd7
	fixed_uri = _gdata_service_fix_uri_scheme (uri->str);
Packit 4b6dd7
	g_string_free (uri, TRUE);
Packit 4b6dd7
Packit 4b6dd7
	return fixed_uri;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_service_fix_uri_scheme:
Packit 4b6dd7
 * @uri: an URI with either HTTP or HTTPS as the scheme
Packit 4b6dd7
 *
Packit 4b6dd7
 * Fixes the given URI to always have HTTPS as its scheme.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer full): the URI with HTTPS as its scheme
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
gchar *
Packit 4b6dd7
_gdata_service_fix_uri_scheme (const gchar *uri)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Ensure we're using the correct scheme (HTTP or HTTPS) */
Packit 4b6dd7
	if (g_str_has_prefix (uri, "https") == FALSE) {
Packit 4b6dd7
		gchar *fixed_uri, **pieces;
Packit 4b6dd7
Packit 4b6dd7
		pieces = g_strsplit (uri, ":", 2);
Packit 4b6dd7
		g_assert (pieces[0] != NULL && pieces[1] != NULL && pieces[2] == NULL);
Packit 4b6dd7
Packit 4b6dd7
		fixed_uri = g_strconcat ("https:", pieces[1], NULL);
Packit 4b6dd7
Packit 4b6dd7
		g_strfreev (pieces);
Packit 4b6dd7
Packit 4b6dd7
		return fixed_uri;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return g_strdup (uri);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_service_get_https_port:
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the destination TCP/IP port number which libgdata should use for all outbound HTTPS traffic.
Packit 4b6dd7
 * This defaults to 443, but may be overridden using the LIBGDATA_HTTPS_PORT
Packit 4b6dd7
 * environment variable. This is intended to allow network traffic to be redirected to a local server for
Packit 4b6dd7
 * unit testing, with a listening port above 1024 so the tests don't need root privileges.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The value returned by this function may change at any time (e.g. between unit tests), so callers must not cache the result.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: port number to use for HTTPS traffic
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
_gdata_service_get_https_port (void)
Packit 4b6dd7
{
Packit 4b6dd7
	const gchar *port_string;
Packit 4b6dd7
Packit 4b6dd7
	/* Allow changing the HTTPS port just for testing. */
Packit 4b6dd7
	port_string = g_getenv ("LIBGDATA_HTTPS_PORT");
Packit 4b6dd7
	if (port_string != NULL) {
Packit 4b6dd7
		const gchar *end;
Packit 4b6dd7
Packit 4b6dd7
		guint64 port = g_ascii_strtoull (port_string, (gchar **) &end, 10);
Packit 4b6dd7
Packit 4b6dd7
		if (port != 0 && *end == '\0') {
Packit 4b6dd7
			g_debug ("Overriding message port to %" G_GUINT64_FORMAT ".", port);
Packit 4b6dd7
			return port;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Return the default. */
Packit 4b6dd7
	return 443;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * debug_handler:
Packit 4b6dd7
 *
Packit 4b6dd7
 * GLib debug message handler, which is passed all messages from g_debug() calls, and decides whether to print them.
Packit 4b6dd7
 */
Packit 4b6dd7
static void
Packit 4b6dd7
debug_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	if (_gdata_service_get_log_level () != GDATA_LOG_NONE)
Packit 4b6dd7
		g_log_default_handler (log_domain, log_level, message, NULL);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * soup_log_printer:
Packit 4b6dd7
 *
Packit 4b6dd7
 * Log printer for the libsoup logging functionality, which just marshals all soup log output to the standard GLib logging framework
Packit 4b6dd7
 * (and thus to debug_handler(), above).
Packit 4b6dd7
 */
Packit 4b6dd7
static void
Packit 4b6dd7
soup_log_printer (SoupLogger *logger, SoupLoggerLogLevel level, char direction, const char *data, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	gboolean filter_data;
Packit 4b6dd7
	gchar *_data = NULL;
Packit 4b6dd7
Packit 4b6dd7
	filter_data = (_gdata_service_get_log_level () > GDATA_LOG_NONE && _gdata_service_get_log_level () < GDATA_LOG_FULL_UNREDACTED) ? TRUE : FALSE;
Packit 4b6dd7
Packit 4b6dd7
	if (filter_data == TRUE) {
Packit 4b6dd7
		/* Filter out lines which look like they might contain usernames, passwords or auth. tokens. */
Packit 4b6dd7
		if (direction == '>' && g_str_has_prefix (data, "Authorization: GoogleLogin ") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("Authorization: GoogleLogin <redacted>");
Packit 4b6dd7
		} else if (direction == '>' && g_str_has_prefix (data, "Authorization: OAuth ") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("Authorization: OAuth <redacted>");
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "Set-Cookie: ") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("Set-Cookie: <redacted>");
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "Location: ") == TRUE) {
Packit 4b6dd7
			/* Looks like:
Packit 4b6dd7
			 * "Location: https://www.google.com/calendar/feeds/default/owncalendars/full?gsessionid=sBjmp05m5i67exYA51XjDA". */
Packit 4b6dd7
			SoupURI *uri;
Packit 4b6dd7
			gchar *_uri;
Packit 4b6dd7
			GHashTable *params;
Packit 4b6dd7
Packit 4b6dd7
			uri = soup_uri_new (data + strlen ("Location: "));
Packit 4b6dd7
Packit 4b6dd7
			if (uri->query != NULL) {
Packit 4b6dd7
				params = soup_form_decode (uri->query);
Packit 4b6dd7
Packit 4b6dd7
				/* strdup()s are necessary because the hash table's set up to free keys. */
Packit 4b6dd7
				if (g_hash_table_lookup (params, "gsessionid") != NULL) {
Packit 4b6dd7
					g_hash_table_insert (params, (gpointer) g_strdup ("gsessionid"), (gpointer) "<redacted>");
Packit 4b6dd7
				}
Packit 4b6dd7
Packit 4b6dd7
				soup_uri_set_query_from_form (uri, params);
Packit 4b6dd7
				g_hash_table_destroy (params);
Packit 4b6dd7
			}
Packit 4b6dd7
Packit 4b6dd7
			_uri = soup_uri_to_string (uri, FALSE);
Packit 4b6dd7
			_data = g_strconcat ("Location: ", _uri, NULL);
Packit 4b6dd7
			g_free (_uri);
Packit 4b6dd7
Packit 4b6dd7
			soup_uri_free (uri);
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "SID=") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("SID=<redacted>");
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "LSID=") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("LSID=<redacted>");
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "Auth=") == TRUE) {
Packit 4b6dd7
			_data = g_strdup ("Auth=<redacted>");
Packit 4b6dd7
		} else if (direction == '>' && g_str_has_prefix (data, "accountType=") == TRUE) {
Packit 4b6dd7
			/* Looks like: "> accountType=HOSTED%5FOR%5FGOOGLE&Email=[e-mail address]&Passwd=[plaintex password]"
Packit 4b6dd7
			               "&service=[service name]&source=ytapi%2DGNOME%2Dlibgdata%2D444fubtt%2D0". */
Packit 4b6dd7
			GHashTable *params = soup_form_decode (data);
Packit 4b6dd7
Packit 4b6dd7
			/* strdup()s are necessary because the hash table's set up to free keys. */
Packit 4b6dd7
			if (g_hash_table_lookup (params, "Email") != NULL) {
Packit 4b6dd7
				g_hash_table_insert (params, (gpointer) g_strdup ("Email"), (gpointer) "<redacted>");
Packit 4b6dd7
			}
Packit 4b6dd7
			if (g_hash_table_lookup (params, "Passwd") != NULL) {
Packit 4b6dd7
				g_hash_table_insert (params, (gpointer) g_strdup ("Passwd"), (gpointer) "<redacted>");
Packit 4b6dd7
			}
Packit 4b6dd7
Packit 4b6dd7
			_data = soup_form_encode_hash (params);
Packit 4b6dd7
Packit 4b6dd7
			g_hash_table_destroy (params);
Packit 4b6dd7
		} else if (direction == '<' && g_str_has_prefix (data, "oauth_token=") == TRUE) {
Packit 4b6dd7
			/* Looks like: "< oauth_token=4%2FI-WU7sBzKk5GhGlQUF8a_TCZRnb7&oauth_token_secret=qTTTJg3no25auiiWFerzjW4I"
Packit 4b6dd7
			               "&oauth_callback_confirmed=true". */
Packit 4b6dd7
			GHashTable *params = soup_form_decode (data);
Packit 4b6dd7
Packit 4b6dd7
			/* strdup()s are necessary because the hash table's set up to free keys. */
Packit 4b6dd7
			if (g_hash_table_lookup (params, "oauth_token") != NULL) {
Packit 4b6dd7
				g_hash_table_insert (params, (gpointer) g_strdup ("oauth_token"), (gpointer) "<redacted>");
Packit 4b6dd7
			}
Packit 4b6dd7
			if (g_hash_table_lookup (params, "oauth_token_secret") != NULL) {
Packit 4b6dd7
				g_hash_table_insert (params, (gpointer) g_strdup ("oauth_token_secret"), (gpointer) "<redacted>");
Packit 4b6dd7
			}
Packit 4b6dd7
Packit 4b6dd7
			_data = soup_form_encode_hash (params);
Packit 4b6dd7
Packit 4b6dd7
			g_hash_table_destroy (params);
Packit 4b6dd7
		} else if (direction == '>' && g_str_has_prefix (data, "X-GData-Key: key=") == TRUE) {
Packit 4b6dd7
			/* Looks like: "> X-GData-Key: key=[dev key in hex]". */
Packit 4b6dd7
			_data = g_strdup ("X-GData-Key: key=<redacted>");
Packit 4b6dd7
		} else {
Packit 4b6dd7
			/* Nothing to redact. */
Packit 4b6dd7
			_data = g_strdup (data);
Packit 4b6dd7
		}
Packit 4b6dd7
	} else {
Packit 4b6dd7
		/* Don't dupe the string. */
Packit 4b6dd7
		_data = (gchar*) data;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Log the data. */
Packit 4b6dd7
	g_debug ("%c %s", direction, _data);
Packit 4b6dd7
Packit 4b6dd7
	if (filter_data == TRUE) {
Packit 4b6dd7
		g_free (_data);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_service_get_log_level:
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the logging level for the library, currently set by an environment variable.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the log level
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataLogLevel
Packit 4b6dd7
_gdata_service_get_log_level (void)
Packit 4b6dd7
{
Packit 4b6dd7
	static int level = -1;
Packit 4b6dd7
Packit 4b6dd7
	if (level < 0) {
Packit 4b6dd7
		const gchar *envvar = g_getenv ("LIBGDATA_DEBUG");
Packit 4b6dd7
		if (envvar != NULL)
Packit 4b6dd7
			level = atoi (envvar);
Packit 4b6dd7
		level = MIN (MAX (level, 0), GDATA_LOG_FULL_UNREDACTED);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return level;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Build a User-Agent value to send to the server.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If we support gzip, we can request gzip from the server by both including
Packit 4b6dd7
 * the appropriate Accept-Encoding header and putting 'gzip' in the User-Agent
Packit 4b6dd7
 * header:
Packit 4b6dd7
 *  - https://developers.google.com/drive/web/performance#gzip
Packit 4b6dd7
 *  - http://googleappsdeveloper.blogspot.co.uk/2011/12/optimizing-bandwidth-usage-with-gzip.html
Packit 4b6dd7
 */
Packit 4b6dd7
static gchar *
Packit 4b6dd7
build_user_agent (gboolean supports_gzip)
Packit 4b6dd7
{
Packit 4b6dd7
	if (supports_gzip) {
Packit 4b6dd7
		return g_strdup_printf ("libgdata/%s - gzip", VERSION);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		return g_strdup_printf ("libgdata/%s", VERSION);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_service_build_session:
Packit 4b6dd7
 *
Packit 4b6dd7
 * Build a new #SoupSession, enabling GNOME features if support has been compiled for them, and adding a log printer which is hooked into
Packit 4b6dd7
 * libgdata's logging functionality.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: a new #SoupSession; unref with g_object_unref()
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
SoupSession *
Packit 4b6dd7
_gdata_service_build_session (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupSession *session;
Packit 4b6dd7
	gboolean ssl_strict = TRUE;
Packit 4b6dd7
	gchar *user_agent;
Packit 4b6dd7
Packit 4b6dd7
	/* Iff LIBGDATA_LAX_SSL_CERTIFICATES=1, relax SSL certificate validation to allow using invalid/unsigned certificates for testing. */
Packit 4b6dd7
	if (g_strcmp0 (g_getenv ("LIBGDATA_LAX_SSL_CERTIFICATES"), "1") == 0) {
Packit 4b6dd7
		ssl_strict = FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	session = soup_session_new_with_options ("ssl-strict", ssl_strict,
Packit 4b6dd7
	                                         "timeout", 0,
Packit 4b6dd7
	                                         NULL);
Packit 4b6dd7
Packit 4b6dd7
	user_agent = build_user_agent (soup_session_has_feature (session, SOUP_TYPE_CONTENT_DECODER));
Packit 4b6dd7
	g_object_set (session, "user-agent", user_agent, NULL);
Packit 4b6dd7
	g_free (user_agent);
Packit 4b6dd7
Packit 4b6dd7
	soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
Packit 4b6dd7
Packit 4b6dd7
	/* Log all libsoup traffic if debugging's turned on */
Packit 4b6dd7
	if (_gdata_service_get_log_level () > GDATA_LOG_MESSAGES) {
Packit 4b6dd7
		SoupLoggerLogLevel level;
Packit 4b6dd7
		SoupLogger *logger;
Packit 4b6dd7
Packit 4b6dd7
		switch (_gdata_service_get_log_level ()) {
Packit 4b6dd7
			case GDATA_LOG_FULL_UNREDACTED:
Packit 4b6dd7
			case GDATA_LOG_FULL:
Packit 4b6dd7
				level = SOUP_LOGGER_LOG_BODY;
Packit 4b6dd7
				break;
Packit 4b6dd7
			case GDATA_LOG_HEADERS:
Packit 4b6dd7
				level = SOUP_LOGGER_LOG_HEADERS;
Packit 4b6dd7
				break;
Packit 4b6dd7
			case GDATA_LOG_MESSAGES:
Packit 4b6dd7
			case GDATA_LOG_NONE:
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_assert_not_reached ();
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		logger = soup_logger_new (level, -1);
Packit 4b6dd7
		soup_logger_set_printer (logger, (SoupLoggerPrinter) soup_log_printer, NULL, NULL);
Packit 4b6dd7
Packit 4b6dd7
		soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
Packit 4b6dd7
Packit 4b6dd7
		g_object_unref (logger);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return session;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_get_locale:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 *
Packit 4b6dd7
 * Returns the locale currently being used for network requests, or %NULL if the locale is the default.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the current locale
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_service_get_locale (GDataService *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
Packit 4b6dd7
	return self->priv->locale;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_service_set_locale:
Packit 4b6dd7
 * @self: a #GDataService
Packit 4b6dd7
 * @locale: (allow-none): the new locale in Unix locale format, or %NULL for the default locale
Packit 4b6dd7
 *
Packit 4b6dd7
 * Set the locale used for network requests to @locale, given in standard Unix locale format. See #GDataService:locale for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that while it's possible to change the locale after sending network requests, it is unsupported, as the server-side software may behave
Packit 4b6dd7
 * unexpectedly. The only supported use of this function is after creation of a service, but before any network requests are made.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_service_set_locale (GDataService *self, const gchar *locale)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_SERVICE (self));
Packit 4b6dd7
Packit 4b6dd7
	g_free (self->priv->locale);
Packit 4b6dd7
	self->priv->locale = g_strdup (locale);
Packit 4b6dd7
	g_object_notify (G_OBJECT (self), "locale");
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * _gdata_service_secure_strdup:
Packit 4b6dd7
 * @str: string (which may be in pageable memory) to be duplicated, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Duplicate a string into non-pageable memory (if libgdata has been compiled with HAVE_GNOME) or just fall back to g_strdup() (if libgdata hasn't).
Packit 4b6dd7
 * Passing %NULL to this function will cause %NULL to be returned.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Strings allocated using this function must be freed using _gdata_service_secure_strfree().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: non-pageable copy of @str, or %NULL
Packit 4b6dd7
 * Since: 0.11.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataSecureString
Packit 4b6dd7
_gdata_service_secure_strdup (const gchar *str)
Packit 4b6dd7
{
Packit 4b6dd7
#ifdef HAVE_GNOME
Packit 4b6dd7
	return gcr_secure_memory_strdup (str);
Packit 4b6dd7
#else /* if !HAVE_GNOME */
Packit 4b6dd7
	return g_strdup (str);
Packit 4b6dd7
#endif /* !HAVE_GNOME */
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * _gdata_service_secure_strndup:
Packit 4b6dd7
 * @str: string (which may be in pageable memory) to be duplicated, or %NULL
Packit 4b6dd7
 * @n_bytes: maximum number of bytes to copy from @str
Packit 4b6dd7
 *
Packit 4b6dd7
 * Duplicate at most @n_bytes bytes from @str into non-pageable memory. See _gdata_service_secure_strdup() for more information; this function is just
Packit 4b6dd7
 * a version of that with the same semantics as strndup().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: non-pageable copy of at most the first @n_bytes bytes of @str, or %NULL
Packit 4b6dd7
 * Since: 0.11.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataSecureString
Packit 4b6dd7
_gdata_service_secure_strndup (const gchar *str, gsize n_bytes)
Packit 4b6dd7
{
Packit 4b6dd7
#ifdef HAVE_GNOME
Packit 4b6dd7
	gsize str_len;
Packit 4b6dd7
	GDataSecureString duped_str;
Packit 4b6dd7
Packit 4b6dd7
	if (str == NULL) {
Packit 4b6dd7
		return NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	str_len = MIN (strlen (str), n_bytes);
Packit 4b6dd7
	duped_str = (GDataSecureString) gcr_secure_memory_alloc (str_len + 1);
Packit 4b6dd7
	strncpy (duped_str, str, str_len);
Packit 4b6dd7
	*(duped_str + str_len) = '\0';
Packit 4b6dd7
Packit 4b6dd7
	return duped_str;
Packit 4b6dd7
#else /* if !HAVE_GNOME */
Packit 4b6dd7
	return g_strndup (str, n_bytes);
Packit 4b6dd7
#endif /* !HAVE_GNOME */
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/*
Packit 4b6dd7
 * _gdata_service_secure_strfree:
Packit 4b6dd7
 * @str: a string to free, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Free a string which was allocated securely using _gdata_service_secure_strdup().
Packit 4b6dd7
 * Passing %NULL to this function is safe.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.11.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
_gdata_service_secure_strfree (GDataSecureString str)
Packit 4b6dd7
{
Packit 4b6dd7
#ifdef HAVE_GNOME
Packit 4b6dd7
	gcr_secure_memory_free (str);
Packit 4b6dd7
#else /* if !HAVE_GNOME */
Packit 4b6dd7
	/* Poor man's approximation to non-pageable memory: the best we can do is ensure that we don't leak it in free memory.
Packit 4b6dd7
	 * This can't guarantee that it hasn't hit disk at some point, but does mean it can't hit disk in future. */
Packit 4b6dd7
	if (str != NULL) {
Packit 4b6dd7
		memset (str, 0, strlen (str));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (str);
Packit 4b6dd7
#endif /* !HAVE_GNOME */
Packit 4b6dd7
}