libpurple/protocols/gg/oauth/oauth.c

Fri, 26 Feb 2021 00:12:08 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 26 Feb 2021 00:12:08 -0600
changeset 40782
1ea760e56414
parent 40439
e9838d634d5e
permissions
-rw-r--r--

Remove gg_oauth_parameter_t

Use variadic function gg_oauth_generate_request instead.

This is a repost of https://reviews.imfreedom.org/r/516/ but reviewboard wouldn't let me update it directly.

Original patch was from Arkadiy Illarionov with updates from Gary Kramlich.

Testing Done:
Compile only.

Reviewed at https://reviews.imfreedom.org/r/525/

/* purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
 * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
 * by Tomek Wasilczyk (http://www.wasilczyk.pl).
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */

#include "oauth.h"

#include <time.h>

#include <glib.h>

char *gg_oauth_static_nonce;		/* dla unit testów */
char *gg_oauth_static_timestamp;	/* dla unit testów */

static char *gg_oauth_generate_request(gboolean header, ...) G_GNUC_NULL_TERMINATED;

static void gg_oauth_generate_nonce(char *buf, int len)
{
	const char charset[] = "0123456789";

	if (buf == NULL || len < 1)
		return;

	while (len > 1) {
		*buf++ = charset[(unsigned) (((float) sizeof(charset) - 1.0) * g_random_int() / (RAND_MAX + 1.0))];
		len--;
	}

	*buf = 0;
}

/* Returns a comma separated header value if header is true,
 * or a url-encoded request otherwise
 */
static char *
gg_oauth_generate_request(gboolean header, ...)
{
	GString *res = g_string_new(NULL);
	va_list params;
	const gchar *key;
	gboolean truncate = FALSE;

	if(header) {
		res = g_string_append(res, "OAuth ");
	}

	va_start(params, header);
	while((key = va_arg(params, const gchar *))) {
		const gchar *value = va_arg(params, const gchar *);
		gchar *escaped = g_uri_escape_string(value, NULL, FALSE);

		if(header) {
			g_string_append_printf(res, "%s=\"%s\",", key, escaped);
		} else {
			g_string_append_printf(res, "%s=%s&", key, escaped);
		}

		g_free(escaped);

		truncate = TRUE;
	}
	va_end(params);

	if(truncate) {
		/* remove trailing separator */
		res = g_string_truncate(res, res->len - 1);
	}

	return g_string_free(res, FALSE);
}

static gchar *gg_hmac_sha1(const char *key, const char *message)
{
	GHmac *hmac;
	guchar digest[20];
	gsize digest_len = 20;

	hmac = g_hmac_new(G_CHECKSUM_SHA1, (guchar *)key, strlen(key));
	g_hmac_update(hmac, (guchar *)message, -1);
	g_hmac_get_digest(hmac, digest, &digest_len);
	g_hmac_unref(hmac);

	return g_base64_encode(digest, sizeof(digest));
}

static char *
gg_oauth_generate_signature(const char *method, const char *url,
	const char *request, const char *consumer_secret,
	const char *token_secret)
{
	char *text, *key, *res;
	gchar *url_e, *request_e, *consumer_secret_e, *token_secret_e;

	url_e = g_uri_escape_string(url, "?", FALSE);
	g_strdelimit(url_e, "?", '\0');
	request_e = g_uri_escape_string(request, NULL, FALSE);
	text = g_strdup_printf("%s&%s&%s", method, url_e, request_e);
	g_free(url_e);
	g_free(request_e);

	consumer_secret_e = g_uri_escape_string(consumer_secret, NULL, FALSE);
	token_secret_e = token_secret ? g_uri_escape_string(token_secret, NULL, FALSE) : NULL;
	key = g_strdup_printf("%s&%s", consumer_secret_e, token_secret ? token_secret_e : "");
	g_free(consumer_secret_e);
	g_free(token_secret_e);

	res = gg_hmac_sha1(key, text);

	g_free(key);
	g_free(text);

	return res;
}

char *
gg_oauth_generate_header(const char *method, const char *url,
	const char *consumer_key, const char *consumer_secret,
	const char *token, const char *token_secret)
{
	char *request, *signature, *res;
	char nonce[80], timestamp[16];

	if (gg_oauth_static_nonce == NULL)
		gg_oauth_generate_nonce(nonce, sizeof(nonce));
	else {
		strncpy(nonce, gg_oauth_static_nonce, sizeof(nonce) - 1);
		nonce[sizeof(nonce) - 1] = 0;
	}

	if (gg_oauth_static_timestamp == NULL) {
		g_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
	} else {
		strncpy(timestamp, gg_oauth_static_timestamp, sizeof(timestamp) - 1);
		timestamp[sizeof(timestamp) - 1] = 0;
	}

	request = gg_oauth_generate_request(FALSE,
	                                    "oauth_consumer_key", consumer_key,
	                                    "oauth_nonce", nonce,
	                                    "oauth_signature_method", "HMAC-SHA1",
	                                    "oauth_timestamp", timestamp,
	                                    "oauth_token", token,
	                                    "oauth_version", "1.0",
	                                    NULL);

	signature = gg_oauth_generate_signature(method, url, request, consumer_secret, token_secret);

	g_free(request);

	if (signature == NULL)
		return NULL;

	res = gg_oauth_generate_request(TRUE,
	                                "oauth_version", "1.0",
	                                "oauth_nonce", nonce,
	                                "oauth_timestamp", timestamp,
	                                "oauth_consumer_key", consumer_key,
	                                "oauth_token", token,
	                                "oauth_signature_method", "HMAC-SHA1",
	                                "oauth_signature", signature,
	                                NULL);
	g_free(signature);

	return res;
}

mercurial