grim/purple-spasm

3c2a35b27899
Add some basic send message support and replies for pings
/*
* PurpleSpasm - A Twitch Protocol Plugin
* Copyright (C) 2017 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 "debug.h"
#include "request.h"
#include "spasm.h"
#include "spasm-account.h"
#include "spasm-chat.h"
#include "spasm-const.h"
#include "spasm-user.h"
/******************************************************************************
* Structs
*****************************************************************************/
struct _PurpleSpasmAccount {
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;
PurpleSpasmChatService *chat;
};
/******************************************************************************
* Helpers
*****************************************************************************/
static PurpleSpasmAccount *
purple_spasm_account_new(PurpleAccount *account, PurpleConnection *connection) {
PurpleSpasmAccount *sa = NULL;
sa = g_new0(PurpleSpasmAccount, 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
purple_spasm_account_free(PurpleSpasmAccount *sa) {
g_return_if_fail(sa);
g_free(sa->access_token);
purple_spasm_chat_service_free(sa->chat);
g_free(sa);
}
static void
_purple_spasm_account_set_access_token(PurpleSpasmAccount *sa,
const gchar *access_token)
{
g_return_if_fail(sa);
sa->access_token = g_strdup(access_token);
}
static GError *
_purple_spasm_account_update_profile(PurpleSpasmAccount *sa, JsonParser *parser) {
JsonNode *root = NULL;
JsonObject *obj = NULL;
const gchar *str_attr = NULL;
if(sa == NULL) {
return g_error_new(
PURPLE_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 _purple_spasm_oauth_login(PurpleSpasmAccount *sa);
static void
_purple_spasm_login_test_cb(PurpleSpasmAccount *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);
_purple_spasm_oauth_login(sa);
return;
}
account = purple_spasm_account_get_account(sa);
connection = purple_account_get_connection(account);
error = _purple_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 = purple_spasm_chat_service_new(sa);
purple_spasm_chat_service_connect(sa->chat);
}
static void
_purple_spasm_access_token_input_cb(gpointer data, const gchar *access_token) {
PurpleAccount *account = NULL;
PurpleConnection *connection = NULL;
PurpleSpasmAccount *sa = PURPLE_SPASM_ACCOUNT(data);
account = purple_spasm_account_get_account(sa);
purple_account_set_remember_password(account, TRUE);
_purple_spasm_account_set_access_token(sa, access_token);
purple_account_set_password(account, access_token);
connection = purple_spasm_account_get_connection(sa);
purple_connection_update_progress(connection, "Verifying", 2, 3);
purple_spasm_get_user(sa, _purple_spasm_login_test_cb, NULL);
}
static void
_purple_spasm_access_token_cancel_cb(gpointer data) {
PurpleSpasmAccount *sa = PURPLE_SPASM_ACCOUNT(data);
purple_connection_error(
purple_spasm_account_get_connection(sa),
"User cancelled authorization"
);
purple_spasm_account_free(sa);
}
static void
_purple_spasm_oauth_login(PurpleSpasmAccount *sa) {
PurpleAccount *account = NULL;
PurpleConnection *connection = NULL;
gchar *state = NULL, *uri = NULL;
const gchar *username = NULL;
account = purple_spasm_account_get_account(sa);
connection = purple_account_get_connection(account);
purple_connection_update_progress(
connection,
"Authenticating",
1,
3
);
username = purple_account_get_username(account);
state = g_strdup_printf("%s,%s", PURPLE_SPASM_PLUGIN_ID, username);
uri = g_strdup_printf(
PURPLE_SPASM_OAUTH2_URI,
PURPLE_SPASM_OAUTH2_CLIENT_ID,
PURPLE_SPASM_OAUTH2_REDIRECT_URI,
PURPLE_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(_purple_spasm_access_token_input_cb),
"Cancel", G_CALLBACK(_purple_spasm_access_token_cancel_cb),
account,
NULL,
NULL,
sa
);
}
/******************************************************************************
* API
*****************************************************************************/
PurpleAccount *
purple_spasm_account_get_account(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->account;
}
PurpleConnection *
purple_spasm_account_get_connection(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->connection;
}
GCancellable *
purple_spasm_account_get_cancellable(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->cancellable;
}
SoupSession *
purple_spasm_account_get_session(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
if(sa->session) {
return g_object_ref(G_OBJECT(sa->session));
}
return NULL;
}
const gchar *
purple_spasm_account_get_access_token(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->access_token;
}
const gchar *
purple_spasm_account_get_display_name(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->display_name;
}
const gchar *
purple_spasm_account_get_id(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->id;
}
const gchar *
purple_spasm_account_get_name(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->name;
}
const gchar *
purple_spasm_account_get_type(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->type;
}
const gchar *
purple_spasm_account_get_bio(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->bio;
}
const gchar *
purple_spasm_account_get_logo(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->logo;
}
const gchar *
purple_spasm_account_get_email(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, NULL);
return sa->email;
}
gboolean
purple_spasm_account_get_email_verified(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->email_verified;
}
gboolean
purple_spasm_account_get_partnered(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->partnered;
}
gboolean
purple_spasm_account_get_twitter_connected(const PurpleSpasmAccount *sa) {
g_return_val_if_fail(sa, FALSE);
return sa->twitter_connected;
}
void
purple_spasm_account_login(PurpleAccount *account) {
PurpleConnection *pc = NULL;
PurpleSpasmAccount *sa = NULL;
const gchar *password = NULL;
pc = purple_account_get_connection(account);
sa = purple_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) {
_purple_spasm_account_set_access_token(sa, password);
purple_spasm_get_user(sa, _purple_spasm_login_test_cb, NULL);
} else {
_purple_spasm_oauth_login(sa);
}
}