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 "spasm-chat.h"
#include "spasm-const.h"
#include <stdarg.h>
#include "debug.h"
#include "util.h"
/******************************************************************************
* Structs
*****************************************************************************/
struct _PurpleSpasmChatService {
PurpleSpasmAccount *sa;
GSocketClient *socket_client;
GSocketConnection *socket_connection;
GOutputStream *output_stream;
GDataInputStream *input_stream;
};
/******************************************************************************
* sending
*****************************************************************************/
static void
_purple_spasm_chat_service_send(PurpleSpasmChatService *chat,
const gchar *format, ...)
{
GCancellable *cancellable = NULL;
GError *error = NULL;
gchar *buffer = NULL;
gboolean success;
va_list vargs;
cancellable = purple_spasm_account_get_cancellable(chat->sa);
va_start(vargs, format);
buffer = g_strdup_vprintf(format, vargs);
va_end(vargs);
success = g_output_stream_printf(
chat->output_stream,
NULL,
cancellable,
&error,
buffer
);
g_free(buffer);
if(!success) {
PurpleConnection *purple_connection = purple_spasm_account_get_connection(chat->sa);
if(error) {
purple_connection_error(purple_connection, error->message);
g_error_free(error);
} else {
purple_connection_error(purple_connection, "unknown error");
}
return;
}
g_output_stream_flush(chat->output_stream, NULL, NULL);
}
/******************************************************************************
* read loop
*****************************************************************************/
static void
_purple_spasm_chat_service_parse(PurpleSpasmChatService *chat,
const gchar *buffer)
{
if(purple_str_has_prefix(buffer, "PING ")) {
purple_debug_misc("spasm", "PING? PONG!\n");
_purple_spasm_chat_service_send(chat, "PONG %s\r\n", buffer + 5);
}
}
static void _purple_spasm_chat_read(PurpleSpasmChatService *chat);
static void
_purple_spasm_chat_read_cb(GObject *obj, GAsyncResult *res, gpointer data) {
GError *error = NULL;
gchar *buffer = NULL;
gsize buffer_len;
PurpleSpasmChatService *chat = (PurpleSpasmChatService *)data;
buffer = g_data_input_stream_read_line_finish(
G_DATA_INPUT_STREAM(obj),
res,
&buffer_len,
&error
);
/* g_data_input_stream_read_line_finish, will return null with error set
* on connection error. It will also return null with error not set if
* there is no content to read.
*/
if(buffer == NULL) {
gchar *error_msg = NULL;
if(error != NULL) {
error_msg = g_strdup_printf(
"spasm lost connection with server : %s",
error->message
);
} else {
error_msg = g_strdup_printf("spasm server closed connection");
}
purple_connection_error(
purple_spasm_account_get_connection(chat->sa),
error_msg
);
g_free(error_msg);
return;
}
purple_debug_info("spasm", "chat buffer: %s\n", buffer);
_purple_spasm_chat_service_parse(chat, buffer);
g_free(buffer);
_purple_spasm_chat_read(chat);
}
static void
_purple_spasm_chat_read(PurpleSpasmChatService *chat) {
g_data_input_stream_read_line_async(
chat->input_stream,
G_PRIORITY_DEFAULT,
purple_spasm_account_get_cancellable(chat->sa),
_purple_spasm_chat_read_cb,
chat
);
}
/******************************************************************************
* chat login flow
*****************************************************************************/
static void
_purple_spasm_chat_login_cb(GObject *obj, GAsyncResult *res, gpointer data) {
GError *error = NULL;
PurpleConnection *purple_connection = NULL;
PurpleSpasmChatService *chat = (PurpleSpasmChatService *)data;
purple_connection = purple_spasm_account_get_connection(chat->sa);
chat->socket_connection = g_socket_client_connect_to_host_finish(
G_SOCKET_CLIENT(obj),
res,
&error
);
if(chat->socket_connection == NULL) {
if(error) {
g_prefix_error(&error, "failed to connect: ");
purple_connection_error(purple_connection, error->message);
g_error_free(error);
} else {
purple_connection_error(purple_connection, "unknown error");
}
return;
}
chat->output_stream = g_io_stream_get_output_stream(G_IO_STREAM(chat->socket_connection));
/* now do the login */
_purple_spasm_chat_service_send(
chat,
"PASS oauth:%s\r\n",
purple_spasm_account_get_access_token(chat->sa)
);
/* now try to use our nick */
_purple_spasm_chat_service_send(
chat,
"NICK %s\r\n",
purple_spasm_account_get_name(chat->sa)
);
chat->input_stream = g_data_input_stream_new(
g_io_stream_get_input_stream(G_IO_STREAM(chat->socket_connection))
);
_purple_spasm_chat_read(chat);
}
PurpleSpasmChatService *
purple_spasm_chat_service_new(PurpleSpasmAccount *sa) {
PurpleSpasmChatService *chat = NULL;
g_return_val_if_fail(sa, NULL);
chat = g_slice_new0(PurpleSpasmChatService);
chat->sa = sa;
return chat;
}
void
purple_spasm_chat_service_free(PurpleSpasmChatService *chat) {
g_object_unref(chat->input_stream);
g_object_unref(chat->output_stream);
g_object_unref(chat->socket_client);
g_object_unref(chat->socket_connection);
g_slice_free(PurpleSpasmChatService, chat);
}
void
purple_spasm_chat_service_connect(PurpleSpasmChatService *chat) {
g_return_if_fail(chat);
chat->socket_client = g_socket_client_new();
g_socket_client_set_tls(chat->socket_client, TRUE);
g_socket_client_connect_to_host_async(
chat->socket_client,
PURPLE_SPASM_CHAT_HOSTNAME,
PURPLE_SPASM_CHAT_PORT,
purple_spasm_account_get_cancellable(chat->sa),
_purple_spasm_chat_login_cb,
chat
);
}