<commit_before>/*
 * e-gdata-goa-authorizer.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 */

#include "e-gdata-goa-authorizer.h"

#include <string.h>
#include <oauth.h>

#define E_GDATA_GOA_AUTHORIZER_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE \
	((obj), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizerPrivate))

struct _EGDataGoaAuthorizerPrivate {

	/* GoaObject is already thread-safe. */
	GoaObject *goa_object;

	/* These members are protected by the global mutex. */
	gchar *access_token;
	gchar *access_token_secret;
	GHashTable *authorization_domains;
};

enum {
	PROP_0,
	PROP_GOA_OBJECT
};

/* GDataAuthorizer methods must be thread-safe. */
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

/* Forward Declarations */
static void	e_gdata_goa_authorizer_interface_init
					(GDataAuthorizerInterface *interface);

G_DEFINE_TYPE_WITH_CODE (
	EGDataGoaAuthorizer,
	e_gdata_goa_authorizer,
	G_TYPE_OBJECT,
	G_IMPLEMENT_INTERFACE (
		GDATA_TYPE_AUTHORIZER,
		e_gdata_goa_authorizer_interface_init))

static GHashTable *
gdata_goa_authorizer_get_parameters (SoupMessage *message,
                                     const gchar *consumer_key,
                                     const gchar *consumer_secret,
                                     const gchar *access_token,
                                     const gchar *access_token_secret)
{
	GString *query;
	GString *base_string;
	GString *signing_key;
	GHashTable *parameters;
	GHashTableIter iter;
	SoupURI *soup_uri;
	GList *keys, *i;
	gchar *string;
	gchar *request_uri;
	gpointer key;

	parameters = g_hash_table_new_full (
		(GHashFunc) g_str_hash,
		(GEqualFunc) g_str_equal,
		(GDestroyNotify) NULL,
		(GDestroyNotify) g_free);

	/* soup_form_decode() uses an awkward allocation style for
	 * its hash table entries, so it's easier to copy its content
	 * into our own hash table than try to use the returned hash
	 * table directly. */

	soup_uri = soup_message_get_uri (message);
	if (soup_uri->query != NULL) {
		GHashTable *hash_table;
		gpointer value;

		hash_table = soup_form_decode (soup_uri->query);
		g_hash_table_iter_init (&iter, hash_table);
		while (g_hash_table_iter_next (&iter, &key, &value)) {
			key = (gpointer) g_intern_string (key);
			value = g_strdup (value);
			g_hash_table_insert (parameters, key, value);
		}
		g_hash_table_destroy (hash_table);
	}

	/* Add OAuth parameters. */

	key = (gpointer) "oauth_version";
	g_hash_table_insert (parameters, key, g_strdup ("1.0"));

	string = oauth_gen_nonce ();
	key = (gpointer) "oauth_nonce";
	g_hash_table_insert (parameters, key, g_strdup (string));
	free (string);  /* do not use g_free () */

	key = (gpointer) "oauth_timestamp";
	string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
	g_hash_table_insert (parameters, key, string); /* takes ownership */

	key = (gpointer) "oauth_consumer_key";
	g_hash_table_insert (parameters, key, g_strdup (consumer_key));

	key = (gpointer) "oauth_token";
	g_hash_table_insert (parameters, key, g_strdup (access_token));

	key = (gpointer) "oauth_signature_method";
	g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));

	/* Build the query part of the signature base string. */

	query = g_string_sized_new (512);
	keys = g_hash_table_get_keys (parameters);
	keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
	for (i = keys; i != NULL; i = g_list_next (i)) {
		const gchar *_key = i->data;
		const gchar *val;

		val = g_hash_table_lookup (parameters, _key);

		if (i != keys)
			g_string_append_c (query, '&');

		g_string_append_uri_escaped (query, _key, NULL, FALSE);
		g_string_append_c (query, '=');
		g_string_append_uri_escaped (query, val, NULL, FALSE);
	}
	g_list_free (keys);

	/* Build the signature base string. */

	soup_uri = soup_uri_copy (soup_uri);
	soup_uri_set_query (soup_uri, NULL);
	soup_uri_set_fragment (soup_uri, NULL);
	request_uri = soup_uri_to_string (soup_uri, FALSE);
	soup_uri_free (soup_uri);

	base_string = g_string_sized_new (512);
	g_string_append_uri_escaped (base_string, message->method, NULL, FALSE);
	g_string_append_c (base_string, '&');
	g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
	g_string_append_c (base_string, '&');
	g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);

	/* Build the HMAC-SHA1 signing key. */

	signing_key = g_string_sized_new (512);
	g_string_append_uri_escaped (
		signing_key, consumer_secret, NULL, FALSE);
	g_string_append_c (signing_key, '&');
	g_string_append_uri_escaped (
		signing_key, access_token_secret, NULL, FALSE);

	/* Sign the request. */

	key = (gpointer) "oauth_signature";
	string = oauth_sign_hmac_sha1 (base_string->str, signing_key->str);
	g_hash_table_insert (parameters, key, g_strdup (string));
	free (string);  /* do not use g_free () */

	g_free (request_uri);

	g_string_free (query, TRUE);
	g_string_free (base_string, TRUE);
	g_string_free (signing_key, TRUE);

	return parameters;
}

static void
gdata_goa_authorizer_add_authorization (GDataAuthorizer *authorizer,
                                        SoupMessage *message)
{
	EGDataGoaAuthorizerPrivate *priv;
	GoaOAuthBased *goa_oauth_based;
	GHashTable *parameters;
	GString *authorization;
	const gchar *consumer_key;
	const gchar *consumer_secret;
	guint ii;

	const gchar *oauth_keys[] = {
		"oauth_version",
		"oauth_nonce",
		"oauth_timestamp",
		"oauth_consumer_key",
		"oauth_token",
		"oauth_signature_method",
		"oauth_signature"<commit_msg>EGDataGoaAuthorizer: Use GHmac to sign the OAuth parameters.<commit_after>/*
 * e-gdata-goa-authorizer.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 */

#include "e-gdata-goa-authorizer.h"

#include <string.h>
#include <oauth.h>

#define E_GDATA_GOA_AUTHORIZER_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE \
	((obj), E_TYPE_GDATA_GOA_AUTHORIZER, EGDataGoaAuthorizerPrivate))

#define HMAC_SHA1_LEN 20 /* bytes, raw */

struct _EGDataGoaAuthorizerPrivate {

	/* GoaObject is already thread-safe. */
	GoaObject *goa_object;

	/* These members are protected by the global mutex. */
	gchar *access_token;
	gchar *access_token_secret;
	GHashTable *authorization_domains;
};

enum {
	PROP_0,
	PROP_GOA_OBJECT
};

/* GDataAuthorizer methods must be thread-safe. */
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

/* Forward Declarations */
static void	e_gdata_goa_authorizer_interface_init
					(GDataAuthorizerInterface *interface);

G_DEFINE_TYPE_WITH_CODE (
	EGDataGoaAuthorizer,
	e_gdata_goa_authorizer,
	G_TYPE_OBJECT,
	G_IMPLEMENT_INTERFACE (
		GDATA_TYPE_AUTHORIZER,
		e_gdata_goa_authorizer_interface_init))

static GHashTable *
gdata_goa_authorizer_get_parameters (SoupMessage *message,
                                     const gchar *consumer_key,
                                     const gchar *consumer_secret,
                                     const gchar *access_token,
                                     const gchar *access_token_secret)
{
	GString *query;
	GString *base_string;
	GString *signing_key;
	GHashTable *parameters;
	GHashTableIter iter;
	SoupURI *soup_uri;
	GList *keys, *i;
	GHmac *signature_hmac;
	guchar signature_digest[HMAC_SHA1_LEN];
	gsize signature_digest_len;
	gchar *string;
	gchar *request_uri;
	gpointer key;

	parameters = g_hash_table_new_full (
		(GHashFunc) g_str_hash,
		(GEqualFunc) g_str_equal,
		(GDestroyNotify) NULL,
		(GDestroyNotify) g_free);

	/* soup_form_decode() uses an awkward allocation style for
	 * its hash table entries, so it's easier to copy its content
	 * into our own hash table than try to use the returned hash
	 * table directly. */

	soup_uri = soup_message_get_uri (message);
	if (soup_uri->query != NULL) {
		GHashTable *hash_table;
		gpointer value;

		hash_table = soup_form_decode (soup_uri->query);
		g_hash_table_iter_init (&iter, hash_table);
		while (g_hash_table_iter_next (&iter, &key, &value)) {
			key = (gpointer) g_intern_string (key);
			value = g_strdup (value);
			g_hash_table_insert (parameters, key, value);
		}
		g_hash_table_destroy (hash_table);
	}

	/* Add OAuth parameters. */

	key = (gpointer) "oauth_version";
	g_hash_table_insert (parameters, key, g_strdup ("1.0"));

	string = oauth_gen_nonce ();
	key = (gpointer) "oauth_nonce";
	g_hash_table_insert (parameters, key, g_strdup (string));
	free (string);  /* do not use g_free () */

	key = (gpointer) "oauth_timestamp";
	string = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) time (NULL));
	g_hash_table_insert (parameters, key, string); /* takes ownership */

	key = (gpointer) "oauth_consumer_key";
	g_hash_table_insert (parameters, key, g_strdup (consumer_key));

	key = (gpointer) "oauth_token";
	g_hash_table_insert (parameters, key, g_strdup (access_token));

	key = (gpointer) "oauth_signature_method";
	g_hash_table_insert (parameters, key, g_strdup ("HMAC-SHA1"));

	/* Build the query part of the signature base string. */

	query = g_string_sized_new (512);
	keys = g_hash_table_get_keys (parameters);
	keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
	for (i = keys; i != NULL; i = g_list_next (i)) {
		const gchar *_key = i->data;
		const gchar *val;

		val = g_hash_table_lookup (parameters, _key);

		if (i != keys)
			g_string_append_c (query, '&');

		g_string_append_uri_escaped (query, _key, NULL, FALSE);
		g_string_append_c (query, '=');
		g_string_append_uri_escaped (query, val, NULL, FALSE);
	}
	g_list_free (keys);

	/* Build the signature base string. */

	soup_uri = soup_uri_copy (soup_uri);
	soup_uri_set_query (soup_uri, NULL);
	soup_uri_set_fragment (soup_uri, NULL);
	request_uri = soup_uri_to_string (soup_uri, FALSE);
	soup_uri_free (soup_uri);

	base_string = g_string_sized_new (512);
	g_string_append_uri_escaped (base_string, message->method, NULL, FALSE);
	g_string_append_c (base_string, '&');
	g_string_append_uri_escaped (base_string, request_uri, NULL, FALSE);
	g_string_append_c (base_string, '&');
	g_string_append_uri_escaped (base_string, query->str, NULL, FALSE);

	/* Build the HMAC-SHA1 signing key. */

	signing_key = g_string_sized_new (512);
	g_string_append_uri_escaped (
		signing_key, consumer_secret, NULL, FALSE);
	g_string_append_c (signing_key, '&');
	g_string_append_uri_escaped (
		signing_key, access_token_secret, NULL, FALSE);

	/* Sign the request. */

	signature_digest_len = sizeof (signature_digest);

	signature_hmac = g_hmac_new (
		G_CHECKSUM_SHA1,
		(guchar *) signing_key->str,
		signing_key->len);
	g_hmac_update (
		signature_hmac,
		(guchar *) base_string->str,
		base_string->len);
	g_hmac_get_digest (
		signature_hmac,
		signature_digest,
		&signature_digest_len);
	g_hmac_unref (signature_hmac);

	key = (gpointer) "oauth_signature";
	string = g_base64_encode (signature_digest, signature_digest_len);
	g_hash_table_insert (parameters, key, string);  /* takes ownership */

	g_free (request_uri);

	g_string_free (query, TRUE);
	g_string_free (base_string, TRUE);
	g_string_free (signing_key, TRUE);

	return parameters;
}

static void
gdata_goa_authorizer_add_authorization (GDataAuthorizer *authorizer,
                                        SoupMessage *message)
{
	EGDataGoaAuthorizerPrivate *priv;
	GoaOAuthBased *goa_oauth_based;
	GHashTable *parameters;
	GString *authorization;
	const gchar *consumer_key;
	const gchar *consumer_secret;
	guint ii;

	const gchar *oauth_keys[] = {
		"oauth_version",
		"oauth_nonce",
		"oauth_timestamp",
		"oauth_consumer_key",
		"oauth_token",
		"oauth_signature_method",
		"oauth_signature"
{'fraction_non_alphanumeric': 0.10171761739678538, 'fraction_numerical': 0.003939489442168295, 'mean_word_length': 4.478204574881312, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2885997', 'n_tokens_mistral': 4723, 'n_tokens_neox': 4481, 'n_words': 1092}