grim/purple-spasm

Refactored a bit cleaned up more

2017-05-18, Gary Kramlich
8151bd6aae79
Parents 99c8b92f6aaf
Children 511b1f3fb898
Refactored a bit cleaned up more
  • +0 -2
    Makefile
  • +209 -100
    spasm-account.c
  • +5 -10
    spasm-account.h
  • +0 -183
    spasm-auth.c
  • +0 -33
    spasm-auth.h
  • +63 -37
    spasm-chat.c
  • +6 -1
    spasm-chat.h
  • +1 -2
    spasm.c
  • --- a/Makefile Tue May 16 22:54:51 2017 -0500
    +++ b/Makefile Thu May 18 19:38:28 2017 -0500
    @@ -9,7 +9,6 @@
    PLUGIN_SOURCES := \
    spasm.c \
    spasm-account.c \
    - spasm-auth.c \
    spasm-chat.c \
    spasm-rest.c \
    spasm-user.c
    @@ -17,7 +16,6 @@
    PLUGIN_HEADERS := \
    spasm.h \
    spasm-account.h \
    - spasm-auth.h \
    spasm-chat.h \
    spasm-const.h \
    spasm-rest.h \
    --- a/spasm-account.c Tue May 16 22:54:51 2017 -0500
    +++ b/spasm-account.c Thu May 18 19:38:28 2017 -0500
    @@ -18,19 +18,22 @@
    */
    #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;
    - GSocketClient *socket_client;
    - GSocketConnection *socket_connection;
    - GOutputStream *output_stream;
    - GDataInputStream *input_stream;
    SoupSession *session;
    @@ -46,12 +49,14 @@
    gboolean email_verified;
    gboolean partnered;
    gboolean twitter_connected;
    +
    + PurpleSpasmChatService *chat;
    };
    /******************************************************************************
    - * API
    + * Helpers
    *****************************************************************************/
    -PurpleSpasmAccount *
    +static PurpleSpasmAccount *
    purple_spasm_account_new(PurpleAccount *account, PurpleConnection *connection) {
    PurpleSpasmAccount *sa = NULL;
    @@ -68,100 +73,19 @@
    return sa;
    }
    -void
    +static void
    purple_spasm_account_free(PurpleSpasmAccount *sa) {
    g_return_if_fail(sa);
    g_free(sa->access_token);
    - g_object_unref(G_OBJECT(sa->socket_connection));
    - g_object_unref(G_OBJECT(sa->output_stream));
    - g_object_unref(G_OBJECT(sa->input_stream));
    - g_object_unref(G_OBJECT(sa->session));
    + purple_spasm_chat_service_free(sa->chat);
    g_free(sa);
    }
    -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;
    -}
    -
    -void
    -purple_spasm_account_set_socket_connection(
    - PurpleSpasmAccount *sa,
    - GSocketConnection *socket_connection)
    -{
    - g_return_if_fail(sa);
    -
    - if(sa->socket_connection)
    - g_object_unref(G_OBJECT(sa->socket_connection));
    -
    - sa->socket_connection = socket_connection;
    -}
    -
    -void
    -purple_spasm_account_set_output_stream(
    - PurpleSpasmAccount *sa,
    - GOutputStream *output_stream)
    -{
    - g_return_if_fail(sa);
    -
    - if(sa->output_stream)
    - g_object_unref(G_OBJECT(sa->output_stream));
    -
    - sa->output_stream = output_stream;
    -}
    -
    -void
    -purple_spasm_account_set_input_stream(
    - PurpleSpasmAccount *sa,
    - GDataInputStream *input_stream)
    -{
    - g_return_if_fail(sa);
    -
    - if(sa->input_stream)
    - g_object_unref(G_OBJECT(sa->input_stream));
    -
    - sa->input_stream = input_stream;
    -}
    -
    -GDataInputStream *
    -purple_spasm_account_get_input_stream(const PurpleSpasmAccount *sa) {
    - g_return_val_if_fail(sa, NULL);
    -
    - return g_object_ref(sa->input_stream);
    -}
    -
    -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;
    -}
    -
    -void
    -purple_spasm_account_set_access_token(PurpleSpasmAccount *sa,
    +static void
    +_purple_spasm_account_set_access_token(PurpleSpasmAccount *sa,
    const gchar *access_token)
    {
    g_return_if_fail(sa);
    @@ -169,15 +93,8 @@
    sa->access_token = g_strdup(access_token);
    }
    -const gchar *
    -purple_spasm_account_get_access_token(const PurpleSpasmAccount *sa) {
    - g_return_val_if_fail(sa, NULL);
    -
    - return sa->access_token;
    -}
    -
    -GError *
    -purple_spasm_account_update_profile(PurpleSpasmAccount *sa, JsonParser *parser) {
    +static GError *
    +_purple_spasm_account_update_profile(PurpleSpasmAccount *sa, JsonParser *parser) {
    JsonNode *root = NULL;
    JsonObject *obj = NULL;
    const gchar *str_attr = NULL;
    @@ -221,6 +138,174 @@
    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);
    @@ -290,3 +375,27 @@
    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);
    + }
    +}
    +
    --- a/spasm-account.h Tue May 16 22:54:51 2017 -0500
    +++ b/spasm-account.h Thu May 18 19:38:28 2017 -0500
    @@ -33,29 +33,24 @@
    typedef struct _PurpleSpasmAccount PurpleSpasmAccount;
    +#include "spasm-chat.h"
    +
    G_BEGIN_DECLS
    -PurpleSpasmAccount *purple_spasm_account_new(PurpleAccount *account, PurpleConnection *connection);
    -void purple_spasm_account_free(PurpleSpasmAccount *sa);
    +// PurpleSpasmAccount *purple_spasm_account_new(PurpleAccount *account, PurpleConnection *connection);
    +// void purple_spasm_account_free(PurpleSpasmAccount *sa);
    PurpleAccount *purple_spasm_account_get_account(const PurpleSpasmAccount *sa);
    PurpleConnection *purple_spasm_account_get_connection(const PurpleSpasmAccount *sa);
    -void purple_spasm_account_set_socket_connection(PurpleSpasmAccount *sa, GSocketConnection *socket_connection);
    -void purple_spasm_account_set_output_stream(PurpleSpasmAccount *sa, GOutputStream *output_stream);
    -
    -void purple_spasm_account_set_input_stream(PurpleSpasmAccount *sa, GDataInputStream *input_stream);
    -GDataInputStream *purple_spasm_account_get_input_stream(const PurpleSpasmAccount *sa);
    +void purple_spasm_account_login(PurpleAccount *account);
    GCancellable *purple_spasm_account_get_cancellable(const PurpleSpasmAccount *sa);
    SoupSession *purple_spasm_account_get_session(const PurpleSpasmAccount *sa);
    -void purple_spasm_account_set_access_token(PurpleSpasmAccount *sa, const gchar *access_token);
    const gchar * purple_spasm_account_get_access_token(const PurpleSpasmAccount *sa);
    -GError *purple_spasm_account_update_profile(PurpleSpasmAccount *sa, JsonParser *parser);
    -
    const gchar *purple_spasm_account_get_display_name(const PurpleSpasmAccount *sa);
    const gchar *purple_spasm_account_get_id(const PurpleSpasmAccount *sa);
    const gchar *purple_spasm_account_get_name(const PurpleSpasmAccount *sa);
    --- a/spasm-auth.c Tue May 16 22:54:51 2017 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,183 +0,0 @@
    -/*
    - * 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 <libsoup/soup.h>
    -
    -#include "debug.h"
    -#include "request.h"
    -
    -#include "spasm-account.h"
    -#include "spasm-auth.h"
    -#include "spasm-chat.h"
    -#include "spasm-const.h"
    -#include "spasm-user.h"
    -
    -/******************************************************************************
    - * Prototypes
    - *****************************************************************************/
    -static void _purple_spasm_oauth_login(PurpleSpasmAccount *sa);
    -
    -/******************************************************************************
    - * Helpers
    - *****************************************************************************/
    -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;
    - }
    -
    - purple_spasm_chat_login(sa);
    -}
    -
    -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
    - *****************************************************************************/
    -void
    -purple_spasm_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);
    - }
    -}
    --- a/spasm-auth.h Tue May 16 22:54:51 2017 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,33 +0,0 @@
    -/*
    - * 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.
    - */
    -
    -#ifndef PURPLE_SPASM_AUTH_H
    -#define PURPLE_SPASM_AUTH_H
    -
    -#include <glib.h>
    -
    -#include "account.h"
    -
    -G_BEGIN_DECLS
    -
    -void purple_spasm_login(PurpleAccount *account);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_SPASM_AUTH_H */
    --- a/spasm-chat.c Tue May 16 22:54:51 2017 -0500
    +++ b/spasm-chat.c Thu May 18 19:38:28 2017 -0500
    @@ -23,16 +23,28 @@
    #include "debug.h"
    /******************************************************************************
    + * Structs
    + *****************************************************************************/
    +struct _PurpleSpasmChatService {
    + PurpleSpasmAccount *sa;
    +
    + GSocketClient *socket_client;
    + GSocketConnection *socket_connection;
    + GOutputStream *output_stream;
    + GDataInputStream *input_stream;
    +};
    +
    +/******************************************************************************
    * read loop
    *****************************************************************************/
    -static void _purple_spasm_chat_read(PurpleSpasmAccount *sa);
    +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;
    - PurpleSpasmAccount *sa = (PurpleSpasmAccount *)data;
    + PurpleSpasmChatService *chat = (PurpleSpasmChatService *)data;
    buffer = g_data_input_stream_read_line_finish(
    G_DATA_INPUT_STREAM(obj),
    @@ -58,7 +70,7 @@
    }
    purple_connection_error(
    - purple_spasm_account_get_connection(sa),
    + purple_spasm_account_get_connection(chat->sa),
    error_msg
    );
    @@ -71,17 +83,17 @@
    g_free(buffer);
    - _purple_spasm_chat_read(sa);
    + _purple_spasm_chat_read(chat);
    }
    static void
    -_purple_spasm_chat_read(PurpleSpasmAccount *sa) {
    +_purple_spasm_chat_read(PurpleSpasmChatService *chat) {
    g_data_input_stream_read_line_async(
    - purple_spasm_account_get_input_stream(sa),
    + chat->input_stream,
    G_PRIORITY_DEFAULT,
    - purple_spasm_account_get_cancellable(sa),
    + purple_spasm_account_get_cancellable(chat->sa),
    _purple_spasm_chat_read_cb,
    - sa
    + chat
    );
    }
    @@ -92,22 +104,20 @@
    _purple_spasm_chat_login_cb(GObject *obj, GAsyncResult *res, gpointer data) {
    GCancellable *cancellable = NULL;
    GError *error = NULL;
    - GOutputStream *output = NULL;
    - GSocketConnection *socket_connection = NULL;
    PurpleConnection *purple_connection = NULL;
    - PurpleSpasmAccount *sa = (PurpleSpasmAccount *)data;
    + PurpleSpasmChatService *chat = (PurpleSpasmChatService *)data;
    gboolean success = FALSE;
    - cancellable = purple_spasm_account_get_cancellable(sa);
    - purple_connection = purple_spasm_account_get_connection(sa);
    + cancellable = purple_spasm_account_get_cancellable(chat->sa);
    + purple_connection = purple_spasm_account_get_connection(chat->sa);
    - socket_connection = g_socket_client_connect_to_host_finish(
    + chat->socket_connection = g_socket_client_connect_to_host_finish(
    G_SOCKET_CLIENT(obj),
    res,
    &error
    );
    - if(socket_connection == NULL) {
    + if(chat->socket_connection == NULL) {
    if(error) {
    g_prefix_error(&error, "failed to connect: ");
    @@ -121,18 +131,16 @@
    return;
    }
    - purple_spasm_account_set_socket_connection(sa, socket_connection);
    -
    - output = g_io_stream_get_output_stream(G_IO_STREAM(socket_connection));
    + chat->output_stream = g_io_stream_get_output_stream(G_IO_STREAM(chat->socket_connection));
    /* now do the login */
    success = g_output_stream_printf(
    - output,
    + chat->output_stream,
    NULL,
    cancellable,
    &error,
    "PASS oauth:%s\r\n",
    - purple_spasm_account_get_access_token(sa)
    + purple_spasm_account_get_access_token(chat->sa)
    );
    if(!success) {
    if(error) {
    @@ -147,16 +155,16 @@
    return;
    }
    - g_output_stream_flush(output, NULL, NULL);
    + g_output_stream_flush(chat->output_stream, NULL, NULL);
    /* now try to use our nick */
    success = g_output_stream_printf(
    - output,
    + chat->output_stream,
    NULL,
    cancellable,
    &error,
    "NICK %s\r\n",
    - purple_spasm_account_get_name(sa)
    + purple_spasm_account_get_name(chat->sa)
    );
    if(!success) {
    if(error) {
    @@ -172,31 +180,49 @@
    return;
    }
    - g_output_stream_flush(output, NULL, NULL);
    + g_output_stream_flush(chat->output_stream, NULL, NULL);
    - purple_spasm_account_set_input_stream(
    - sa,
    - g_data_input_stream_new(
    - g_io_stream_get_input_stream(G_IO_STREAM(socket_connection))
    - )
    + chat->input_stream = g_data_input_stream_new(
    + g_io_stream_get_input_stream(G_IO_STREAM(chat->socket_connection))
    );
    - _purple_spasm_chat_read(sa);
    + _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_login(PurpleSpasmAccount *sa) {
    - GSocketClient *socket_client = NULL;
    +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);
    - socket_client = g_socket_client_new();
    + 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_connect_to_host_async(
    - socket_client,
    + chat->socket_client,
    PURPLE_SPASM_CHAT_HOSTNAME,
    PURPLE_SPASM_CHAT_PORT,
    - purple_spasm_account_get_cancellable(sa),
    + purple_spasm_account_get_cancellable(chat->sa),
    _purple_spasm_chat_login_cb,
    - sa
    + chat
    );
    }
    -
    --- a/spasm-chat.h Tue May 16 22:54:51 2017 -0500
    +++ b/spasm-chat.h Thu May 18 19:38:28 2017 -0500
    @@ -22,11 +22,16 @@
    #include <glib.h>
    +typedef struct _PurpleSpasmChatService PurpleSpasmChatService;
    +
    #include "spasm-account.h"
    G_BEGIN_DECLS
    -void purple_spasm_chat_login(PurpleSpasmAccount *sa);
    +PurpleSpasmChatService *purple_spasm_chat_service_new(PurpleSpasmAccount *sa);
    +void purple_spasm_chat_service_free(PurpleSpasmChatService *chat);
    +
    +void purple_spasm_chat_service_connect(PurpleSpasmChatService *chat);
    G_END_DECLS
    --- a/spasm.c Tue May 16 22:54:51 2017 -0500
    +++ b/spasm.c Thu May 18 19:38:28 2017 -0500
    @@ -33,7 +33,6 @@
    #include "version.h"
    #include "spasm-account.h"
    -#include "spasm-auth.h"
    #include "spasm-const.h"
    /******************************************************************************
    @@ -79,7 +78,7 @@
    .options = OPT_PROTO_NO_PASSWORD,
    - .login = purple_spasm_login,
    + .login = purple_spasm_account_login,
    .close = _purple_spasm_close,
    .list_icon = _purple_spasm_list_icon,