libpurple/protocols/gg/oauth/oauth-purple.c

Wed, 16 Nov 2022 23:44:59 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Wed, 16 Nov 2022 23:44:59 -0600
changeset 41921
24e5305ebcb6
parent 41878
64e455cfb6b4
child 41979
31c32b8d4bef
permissions
-rw-r--r--

Remove soupcompat.h since we require libsoup3 now

Testing Done:
Compiled

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

/* 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.
 *
 * Rewritten from scratch during Google Summer of Code 2012
 * by Tomek Wasilczyk (http://www.wasilczyk.pl).
 *
 * Previously implemented by:
 *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
 *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
 *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
 *
 * 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-purple.h"
#include "gg.h"

#include "oauth.h"
#include "../utils.h"
#include "../xml.h"

#include <purple.h>

#define GGP_OAUTH_RESPONSE_MAX 10240

typedef struct
{
	SoupMessage *msg;
	PurpleConnection *gc;
	ggp_oauth_request_cb callback;
	gpointer user_data;
	gchar *token;
	gchar *token_secret;

	gchar *sign_method, *sign_url;
} ggp_oauth_data;

static void ggp_oauth_data_free(ggp_oauth_data *data)
{
	g_object_unref(data->msg);
	g_free(data->token);
	g_free(data->token_secret);
	g_free(data->sign_method);
	g_free(data->sign_url);
	g_free(data);
}

static void
ggp_oauth_access_token_got(GObject *source, GAsyncResult *result,
                           gpointer user_data)
{
	ggp_oauth_data *data = user_data;
	GBytes *response_body = NULL;
	const char *buffer = NULL;
	gsize size = 0;
	gchar *token = NULL, *token_secret = NULL;
	PurpleXmlNode *xml;
	gboolean succ = TRUE;
	GError *error = NULL;

	response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
	                                                  result, &error);
	if(response_body == NULL) {
		purple_debug_error("gg", "ggp_oauth_access_token_got: failed: %s",
		                   error->message);
		ggp_oauth_data_free(data);
		g_error_free(error);
		return;
	}

	buffer = g_bytes_get_data(response_body, &size);
	xml = purple_xmlnode_from_str(buffer, size);
	g_bytes_unref(response_body);

	if (xml == NULL) {
		purple_debug_error("gg", "ggp_oauth_access_token_got: invalid xml");
		ggp_oauth_data_free(data);
		return;
	}

	succ &= ggp_xml_get_string(xml, "oauth_token", &token);
	succ &= ggp_xml_get_string(xml, "oauth_token_secret", &token_secret);
	purple_xmlnode_free(xml);
	if (!succ || strlen(token) < 10) {
		purple_debug_error("gg", "ggp_oauth_access_token_got: invalid xml - "
		                         "token is not present");
		g_free(token);
		g_free(token_secret);
		ggp_oauth_data_free(data);
		return;
	}

	if (data->sign_url) {
		PurpleAccount *account;
		gchar *auth;

		purple_debug_misc("gg", "ggp_oauth_access_token_got: got access token, "
		                        "returning signed url");

		account = purple_connection_get_account(data->gc);
		auth = gg_oauth_generate_header(
		        data->sign_method, data->sign_url,
		        purple_account_get_username(account),
		        purple_connection_get_password(data->gc), token, token_secret);
		data->callback(data->gc, auth, data->user_data);
	} else {
		purple_debug_misc(
		        "gg",
		        "ggp_oauth_access_token_got: got access token, returning it");
		data->callback(data->gc, token, data->user_data);
	}

	g_free(token);
	g_free(token_secret);
	ggp_oauth_data_free(data);
}

static void
ggp_oauth_authorization_done(GObject *source, GAsyncResult *result,
                             gpointer user_data)
{
	ggp_oauth_data *data = user_data;
	PurpleAccount *account;
	SoupStatus status_code;
	char *auth;
	SoupMessage *msg = NULL;
	const char *method = "POST";
	const char *url = "http://api.gadu-gadu.pl/access_token";

	PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);

	account = purple_connection_get_account(data->gc);

	status_code = soup_message_get_status(data->msg);
	if (status_code != 302) {
		purple_debug_error("gg",
		                   "ggp_oauth_authorization_done: failed (code = %d)",
		                   status_code);
		ggp_oauth_data_free(data);
		return;
	}

	purple_debug_misc("gg", "ggp_oauth_authorization_done: authorization done, "
	                        "requesting access token...");

	auth = gg_oauth_generate_header(method, url,
	                                purple_account_get_username(account),
	                                purple_connection_get_password(data->gc),
	                                data->token, data->token_secret);

	g_clear_object(&data->msg);
	data->msg = msg = soup_message_new(method, url);
	// purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
	soup_message_headers_replace(soup_message_get_request_headers(msg),
	                             "Authorization", auth);
	soup_session_send_and_read_async(SOUP_SESSION(source), msg,
	                                 G_PRIORITY_DEFAULT, NULL,
	                                 ggp_oauth_access_token_got, data);

	g_free(auth);
}

static void
ggp_oauth_request_token_got(GObject *source, GAsyncResult *result,
                            gpointer user_data)
{
	SoupSession *session = SOUP_SESSION(source);
	ggp_oauth_data *data = user_data;
	GBytes *response_body = NULL;
	const char *buffer = NULL;
	gsize size = 0;
	PurpleAccount *account;
	PurpleXmlNode *xml;
	SoupMessage *msg = NULL;
	gchar *request_data;
	GBytes *body = NULL;
	gboolean succ = TRUE;
	GError *error = NULL;

	PURPLE_ASSERT_CONNECTION_IS_VALID(data->gc);

	account = purple_connection_get_account(data->gc);

	if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(data->msg))) {
		purple_debug_error("gg", "ggp_oauth_request_token_got: "
			"requested token not received\n");
		ggp_oauth_data_free(data);
		return;
	}

	response_body = soup_session_send_and_read_finish(session, result, &error);
	if(response_body == NULL) {
		purple_debug_error("gg", "ggp_oauth_access_token_got: failed: %s",
		                   error->message);
		ggp_oauth_data_free(data);
		g_error_free(error);
		return;
	}

	purple_debug_misc("gg", "ggp_oauth_request_token_got: "
		"got request token, doing authorization...\n");

	buffer = g_bytes_get_data(response_body, &size);
	xml = purple_xmlnode_from_str(buffer, size);
	g_bytes_unref(response_body);

	if (xml == NULL) {
		purple_debug_error("gg", "ggp_oauth_request_token_got: "
			"invalid xml\n");
		ggp_oauth_data_free(data);
		return;
	}

	succ &= ggp_xml_get_string(xml, "oauth_token", &data->token);
	succ &= ggp_xml_get_string(xml, "oauth_token_secret",
		&data->token_secret);
	purple_xmlnode_free(xml);
	if (!succ) {
		purple_debug_error("gg", "ggp_oauth_request_token_got: "
			"invalid xml - token is not present\n");
		ggp_oauth_data_free(data);
		return;
	}

	request_data = g_strdup_printf(
		"callback_url=http://www.mojageneracja.pl&request_token=%s&"
		"uin=%s&password=%s", data->token,
		purple_account_get_username(account),
		purple_connection_get_password(data->gc));

	g_clear_object(&data->msg);
	data->msg = msg = soup_message_new("POST",
	                                   "https://login.gadu-gadu.pl/authorize");
	// purple_http_request_set_max_len(msg, GGP_OAUTH_RESPONSE_MAX);
	/* we don't need any results, nor 302 redirection */
	soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT);
	body = g_bytes_new_take(request_data, strlen(request_data));
	soup_message_set_request_body_from_bytes(msg,
	                                         "application/x-www-form-urlencoded",
	                                         body);
	g_bytes_unref(body);
	soup_session_send_and_read_async(session, msg, G_PRIORITY_DEFAULT, NULL,
	                                 ggp_oauth_authorization_done, data);
}

void
ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
                  gpointer user_data, const gchar *sign_method,
                  const gchar *sign_url)
{
	GGPInfo *info = purple_connection_get_protocol_data(gc);
	PurpleAccount *account = purple_connection_get_account(gc);
	SoupMessage *msg;
	char *auth;
	const char *method = "POST";
	const char *url = "http://api.gadu-gadu.pl/request_token";
	ggp_oauth_data *data;

	purple_debug_misc("gg", "ggp_oauth_request: requesting token...\n");

	auth = gg_oauth_generate_header(
	        method, url, purple_account_get_username(account),
	        purple_connection_get_password(gc), NULL, NULL);

	data = g_new0(ggp_oauth_data, 1);
	data->gc = gc;
	data->callback = callback;
	data->user_data = user_data;
	data->sign_method = g_strdup(sign_method);
	data->sign_url = g_strdup(sign_url);

	data->msg = msg = soup_message_new(method, url);
	// purple_http_request_set_max_len(req, GGP_OAUTH_RESPONSE_MAX);
	soup_message_headers_replace(soup_message_get_request_headers(msg),
	                             "Authorization", auth);
	soup_session_send_and_read_async(info->http, msg, G_PRIORITY_DEFAULT, NULL,
	                                 ggp_oauth_request_token_got, data);

	g_free(auth);
}

mercurial