grim/purple-spasm

Move the parsing to regex and make real_send add the trailing \r\n
/*
* Spasm - A Twitch Protocol Plugin
* Copyright (C) 2017-2019 Gary Kramlich <grim@reaperworld.com>
*
* 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 02110-1301, USA.
*/
#include <purple.h>
#include "spasm.h"
#include "spasm-account.h"
#include "spasm-chat.h"
#include "spasm-const.h"
#include "spasm-user.h"
/******************************************************************************
* Structs
*****************************************************************************/
struct _SpasmAccount {
PurpleAccount *account;
PurpleConnection *connection;
GCancellable *cancellable;
SoupSession *session;
gchar *access_token;
gchar *display_name;
gchar *id;
gchar *name;
gchar *type;
gchar *bio;
gchar *logo;
gchar *email;
gboolean email_verified;
gboolean partnered;
gboolean twitter_connected;
SpasmChatService *chat;
};
/******************************************************************************
* Helpers
*****************************************************************************/
static SpasmAccount *
spasm_account_new(PurpleAccount *account,
PurpleConnection *connection)
{
SpasmAccount *sa = NULL;
sa = g_new0(SpasmAccount, 1);
sa->account = account;
sa->connection = connection;
sa->session = soup_session_new();
SoupLogger *logger = soup_logger_new(SOUP_LOGGER_LOG_BODY, -1);
soup_session_add_feature(sa->session, SOUP_SESSION_FEATURE(logger));
return sa;
}
static void
spasm_account_free(SpasmAccount *sa) {
g_return_if_fail(sa);
g_free(sa->access_token);
spasm_chat_service_free(sa->chat);
g_free(sa);
}
static void
spasm_account_set_access_token(SpasmAccount *sa,
const gchar *access_token)
{
g_return_if_fail(sa);
sa->access_token = g_strdup(access_token);
}
static GError *
spasm_account_update_profile(SpasmAccount *sa, JsonParser *parser) {
JsonNode *root = NULL;
JsonObject *obj = NULL;
const gchar *str_attr = NULL;
if(sa == NULL) {
return g_error_new(
SPASM_DOMAIN,
0,
"invalid spasm account"
);
}
root = json_parser_get_root(parser);
obj = json_node_get_object(root);
str_attr = json_object_get_string_member(obj, "display_name");
sa->display_name = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "_id");
sa->id = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "name");
sa->name = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "type");
sa->type = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "bio");
sa->bio = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "logo");
sa->logo = g_strdup(str_attr);
str_attr = json_object_get_string_member(obj, "email");
sa->email = g_strdup(str_attr);
sa->email_verified = json_object_get_boolean_member(obj, "email_verified");
sa->partnered = json_object_get_boolean_member(obj, "partnered");
sa->twitter_connected = json_object_get_boolean_member(obj, "twitter_connected");
return NULL;
}
/******************************************************************************
* Auth
*****************************************************************************/
static void spasm_oauth_login(SpasmAccount *sa);
static void
spasm_login_test_cb(SpasmAccount *sa, JsonParser *parser,
GError *error, gpointer data)
{
PurpleAccount *account = NULL;
PurpleConnection *connection = NULL;
if(error != NULL) {
purple_debug_info("spasm", "oauth token invalid, re-authenticating : %s\n", error->message);
spasm_oauth_login(sa);
return;
}
account = spasm_account_get_account(sa);
connection = purple_account_get_connection(account);
error = spasm_account_update_profile(sa, parser);
if(error != NULL) {
gchar *err_msg = NULL;
err_msg = g_strdup_printf(
"failed to update profile : %s",
error->message
);
g_error_free(error);
purple_connection_error(connection, err_msg);
g_free(err_msg);
return;
}
sa->chat = spasm_chat_service_new(sa);
spasm_chat_service_connect(sa->chat);
}
static void
spasm_access_token_input_cb(gpointer data, const gchar *access_token) {
PurpleAccount *account = NULL;
SpasmAccount *sa = SPASM_ACCOUNT(data);
account = spasm_account_get_account(sa);
purple_account_set_remember_password(account, TRUE);
spasm_account_set_access_token(sa, access_token);
purple_account_set_password(account, access_token);
spasm_get_user(sa, spasm_login_test_cb, NULL);
}
static void
spasm_access_token_cancel_cb(gpointer data) {
SpasmAccount *sa = SPASM_ACCOUNT(data);
purple_connection_error(
spasm_account_get_connection(sa),
"User cancelled authorization"
);
spasm_account_free(sa);
}
static void
spasm_oauth_login(SpasmAccount *sa) {
PurpleAccount *account = NULL;
PurpleConnection *connection = NULL;
gchar *state = NULL, *uri = NULL;
const gchar *username = NULL;
account = spasm_account_get_account(sa);
connection = purple_account_get_connection(account);
username = purple_account_get_username(account);
state = g_strdup_printf("%s,%s", SPASM_PLUGIN_ID, username);
uri = g_strdup_printf(
SPASM_OAUTH2_URI,
SPASM_OAUTH2_CLIENT_ID,
SPASM_OAUTH2_REDIRECT_URI,
SPASM_OAUTH2_SCOPES,
state
);
g_free(state);
/* send off the oauth implicit request */
purple_notify_uri(connection, uri);
g_free(uri);
purple_request_input(
connection,
"Access Token",
"Enter the access token from https://pidgin.im/oauth.html which should have opened in your web browser",
NULL,
"access token",
FALSE,
TRUE,
NULL,
"OK", G_CALLBACK(spasm_access_token_input_cb),
"Cancel", G_CALLBACK(spasm_access_token_cancel_cb),
account,
NULL,
NULL,
sa
);
}
/******************************************************************************
* API
*****************************************************************************/
PurpleAccount *
spasm_account_get_account(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->account;
}
PurpleConnection *
spasm_account_get_connection(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->connection;
}
GCancellable *
spasm_account_get_cancellable(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->cancellable;
}
SoupSession *
spasm_account_get_session(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
if(sa->session) {
return SOUP_SESSION(g_object_ref(sa->session));
}
return NULL;
}
const gchar *
spasm_account_get_access_token(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->access_token;
}
const gchar *
spasm_account_get_display_name(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->display_name;
}
const gchar *
spasm_account_get_id(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->id;
}
const gchar *
spasm_account_get_name(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->name;
}
const gchar *
spasm_account_get_type(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->type;
}
const gchar *
spasm_account_get_bio(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->bio;
}
const gchar *
spasm_account_get_logo(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->logo;
}
const gchar *
spasm_account_get_email(const SpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->email;
}
gboolean
spasm_account_get_email_verified(const SpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->email_verified;
}
gboolean
spasm_account_get_partnered(const SpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->partnered;
}
gboolean
spasm_account_get_twitter_connected(const SpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->twitter_connected;
}
void
spasm_account_login(PurpleAccount *account) {
PurpleConnection *pc = NULL;
SpasmAccount *sa = NULL;
const gchar *password = NULL;
pc = purple_account_get_connection(account);
sa = spasm_account_new(account, pc);
purple_connection_set_protocol_data(pc, sa);
purple_connection_set_state(pc, PURPLE_CONNECTING);
/* try to load the password */
password = purple_account_get_password(account);
if(password != NULL) {
spasm_account_set_access_token(sa, password);
spasm_get_user(sa, spasm_login_test_cb, NULL);
} else {
spasm_oauth_login(sa);
}
}
SpasmChatService *
spasm_account_get_chat_service(SpasmAccount *sa) {
g_return_val_if_fail(sa != NULL, NULL);
return sa->chat;
}