grim/purple-spasm

7388a4c9a1b3
Parents a1e6bcaf27c3
Children 60d133ef0a45
Move the parsing to regex and make real_send add the trailing \r\n
  • +155 -109
    src/spasm-chat.c
  • --- a/src/spasm-chat.c Sun Apr 19 13:55:45 2020 -0500
    +++ b/src/spasm-chat.c Sun Apr 19 17:22:00 2020 -0500
    @@ -39,17 +39,32 @@
    GHashTable *handlers;
    gint id; /* used to track ids for this service */
    +
    + GRegex *regex_message;
    + GRegex *regex_target;
    };
    -typedef struct {
    - gchar *name;
    - gint n_args;
    - void (*callback)(SpasmChatService *sa, const gchar *from, gchar **args);
    -} SpasmChatMessageHandler;
    +typedef void (*SpasmChatMessageHandler)(SpasmChatService *sa,
    + const gchar *prefix,
    + const gchar *middle,
    + const gchar *trailing);
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    +static void
    +spasm_chat_service_regex_init(SpasmChatService *chat) {
    + chat->regex_message = g_regex_new("(?::(?<prefix>[^ ]+) +)?"
    + "(?<command>[^ :]+)"
    + "(?:(?: +(?<middle>[^ :]+)))*"
    + "(?<coda> +:(?<trailing>.*)?)?",
    + 0, 0, NULL);
    + g_assert(chat->regex_message != NULL);
    +
    + chat->regex_target = g_regex_new("(?:#(?<target>[^\\s]+))", 0, 0, NULL);
    + g_assert(chat->regex_target != NULL);
    +}
    +
    static gchar *
    spasm_chat_service_nick_from_mask(const gchar *mask) {
    gchar *nick = NULL, *bang = NULL;
    @@ -65,68 +80,7 @@
    }
    /******************************************************************************
    - * Handlers
    - *****************************************************************************/
    -static void
    -spasm_chat_service_handle_join(SpasmChatService *chat, const gchar *from,
    - gchar **args)
    -{
    - PurpleConnection *connection = NULL;
    - const gchar *name = args[0] + 1; /* we want to ignore the leading # */
    -
    - connection = spasm_account_get_connection(chat->sa);
    - serv_got_joined_chat(connection, chat->id++, name);
    -}
    -
    -static void
    -spasm_chat_service_handle_privmsg(SpasmChatService *chat, const gchar *from,
    - gchar **args)
    -{
    - PurpleAccount *account = NULL;
    - PurpleConversation *conversation = NULL;
    - gchar *nick = spasm_chat_service_nick_from_mask(from);
    - gint id = 0;
    -
    - account = spasm_account_get_account(chat->sa);
    - conversation = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
    - args[0] + 1, account);
    - if(conversation != NULL) {
    - id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conversation));
    - serv_got_chat_in(spasm_account_get_connection(chat->sa), id, nick, 0,
    - args[1] + 1, time(NULL));
    - }
    -
    - g_free(nick);
    -}
    -
    -static GHashTable *
    -spasm_chat_service_init_handlers(void) {
    - static SpasmChatMessageHandler handlers[] = {
    - { "001", 0, NULL }, /* ignore RPL_WELCOME */
    - { "002", 0, NULL }, /* ignore RPL_YOURHOST */
    - { "003", 0, NULL }, /* ignore RPL_CREATED */
    - { "004", 0, NULL }, /* ignore RPL_MYINFO */
    - { "353", 0, NULL }, /* ignore RPL_NAMREPLY */
    - { "366", 0, NULL }, /* ignore RPL_ENDOFNAMES */
    - { "372", 0, NULL }, /* ignore RPL_MOTD */
    - { "375", 0, NULL }, /* ignore RPL_MOTDSTART */
    - { "376", 0, NULL }, /* ignore RPL_ENDOFMOTD */
    - { "JOIN", 1, spasm_chat_service_handle_join },
    - { "PRIVMSG", 2, spasm_chat_service_handle_privmsg },
    - };
    - GHashTable *ret = NULL;
    - gint i;
    -
    - ret = g_hash_table_new(g_str_hash, g_str_equal);
    - for(i = 0; i < G_N_ELEMENTS(handlers); i++) {
    - g_hash_table_insert(ret, handlers[i].name, &handlers[i]);
    - }
    -
    - return ret;
    -}
    -
    -/******************************************************************************
    - * sending
    + * Sending
    *****************************************************************************/
    static void
    spasm_chat_service_real_send(SpasmChatService *chat,
    @@ -151,6 +105,7 @@
    NULL,
    cancellable,
    &error,
    + "%s\r\n",
    buffer
    );
    @@ -173,62 +128,148 @@
    }
    /******************************************************************************
    - * read loop
    + * Handlers
    + *****************************************************************************/
    +static void
    +spasm_chat_service_handle_ping(SpasmChatService *chat, const gchar *prefix,
    + const gchar *middle, const gchar *trailing)
    +{
    + spasm_chat_service_real_send(chat, "PONG :%s", trailing);
    +}
    +
    +static void
    +spasm_chat_service_handle_join(SpasmChatService *chat, const gchar *prefix,
    + const gchar *middle, const gchar *trailing)
    +{
    + GMatchInfo *info = NULL;
    +
    + if(g_regex_match(chat->regex_target, middle, 0, &info)) {
    + gchar *target = g_match_info_fetch_named(info, "target");
    +
    + if(target != NULL) {
    + PurpleConnection *connection = NULL;
    +
    + connection = spasm_account_get_connection(chat->sa);
    + serv_got_joined_chat(connection, chat->id++, target);
    + }
    +
    + g_free(target);
    + }
    +
    + g_match_info_unref(info);
    +}
    +
    +static void
    +spasm_chat_service_handle_privmsg(SpasmChatService *chat, const gchar *prefix,
    + const gchar *middle, const gchar *trailing)
    +{
    + GMatchInfo *info = NULL;
    +
    + if(g_regex_match(chat->regex_target, middle, 0, &info)) {
    + PurpleAccount *account = NULL;
    + PurpleConversation *conversation = NULL;
    + gchar *target = NULL, *nick = NULL;
    +
    + target = g_match_info_fetch_named(info, "target");
    + nick = spasm_chat_service_nick_from_mask(prefix);
    +
    + account = spasm_account_get_account(chat->sa);
    + conversation =
    + purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
    + target, account);
    +
    + if(conversation != NULL) {
    + gint id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conversation));
    + serv_got_chat_in(spasm_account_get_connection(chat->sa), id,
    + nick, 0, trailing, time(NULL));
    + }
    +
    + g_free(target);
    + g_free(nick);
    + } else {
    + purple_debug_misc("spasm-chat",
    + "failed to to find a target in \"%s\"\n", middle);
    + }
    +
    + g_match_info_unref(info);
    +}
    +
    +static GHashTable *
    +spasm_chat_service_init_handlers(void) {
    + GHashTable *handlers = NULL;
    +
    + handlers = g_hash_table_new(g_str_hash, g_str_equal);
    +
    + g_hash_table_insert(handlers, "001", NULL); /* Ignore RPL_WELCOME */
    + g_hash_table_insert(handlers, "002", NULL); /* Ignore RPL_YOURHOST */
    + g_hash_table_insert(handlers, "003", NULL); /* Ignore RPL_CREATED */
    + g_hash_table_insert(handlers, "004", NULL); /* Ignore RPL_MYINFO */
    + g_hash_table_insert(handlers, "353", NULL); /* Ignore RPL_NAMREPLY */
    + g_hash_table_insert(handlers, "366", NULL); /* Ignore RPL_ENDOFNAMES */
    + g_hash_table_insert(handlers, "372", NULL); /* Ignore RPL_MOTD */
    + g_hash_table_insert(handlers, "375", NULL); /* Ignore RPL_MOTDSTART */
    + g_hash_table_insert(handlers, "376", NULL); /* Ignore RPL_ENDOFMOTD */
    +
    + g_hash_table_insert(handlers, "JOIN", spasm_chat_service_handle_join);
    + g_hash_table_insert(handlers, "PING", spasm_chat_service_handle_ping);
    + g_hash_table_insert(handlers, "PRIVMSG",
    + spasm_chat_service_handle_privmsg);
    +
    + return handlers;
    +}
    +
    +/******************************************************************************
    + * Read Loop
    *****************************************************************************/
    static void
    spasm_chat_service_parse(SpasmChatService *chat,
    const gchar *buffer)
    {
    - SpasmChatMessageHandler *handler = NULL;
    - gchar **parts = NULL;
    + GMatchInfo *info = NULL;
    + gchar *prefix = NULL, *command = NULL, *middle = NULL, *trailing = NULL;
    + gboolean matches = FALSE;
    + gpointer value;
    g_return_if_fail(buffer != NULL);
    - /* special case message parsing */
    - if(purple_str_has_prefix(buffer, "PING ")) {
    - purple_debug_misc("spasm-chat", "PING? PONG!\n");
    -
    - spasm_chat_service_real_send(chat, "PONG %s\r\n", buffer + 5);
    + matches = g_regex_match(chat->regex_message, buffer, 0, &info);
    + if(!matches) {
    + purple_debug_misc("spasm-chat", "failed to parse \"%s\"\n", buffer);
    - return;
    - }
    -
    - /* handle basic message parsing */
    - if(buffer[0] != ':') {
    - purple_debug_warning("spasm", "failed to parse message \"%s\"\n",
    - buffer);
    + g_match_info_unref(info);
    return;
    }
    - parts = g_strsplit(buffer, " ", 3);
    + prefix = g_match_info_fetch_named(info, "prefix");
    + command = g_match_info_fetch_named(info, "command");
    + middle = g_match_info_fetch_named(info, "middle");
    + trailing = g_match_info_fetch_named(info, "trailing");
    +
    + g_match_info_unref(info);
    - /* look up the command handler */
    - handler = g_hash_table_lookup(chat->handlers, parts[1]);
    - if(handler == NULL) {
    - purple_debug_misc("spasm-chat", "unknown message type %s\n", parts[1]);
    + /* Look up the command handler. */
    + if(g_hash_table_lookup_extended(chat->handlers, command, NULL, &value)) {
    + /* Ignored replies have a NULL handler. */
    + if(value != NULL) {
    + SpasmChatMessageHandler handler = (SpasmChatMessageHandler)value;
    - purple_debug_misc("spasm-chat", "from: %s\n", parts[0]);
    - purple_debug_misc("spasm-chat", "command: %s\n", parts[1]);
    - purple_debug_misc("spasm-chat", "args: %s\n", parts[2]);
    - purple_debug_misc("spasm-chat", "-----\n");
    + handler(chat, prefix, middle, trailing);
    + }
    } else {
    - if(handler->callback != NULL) {
    - gchar **args = g_strsplit(g_strstrip(parts[2]), " ",
    - handler->n_args);
    - gchar *from = parts[0];
    -
    - if(from && *from == ':') {
    - from++; /* increment past the : */
    - }
    -
    - handler->callback(chat, from, args);
    -
    - g_strfreev(args);
    - }
    + purple_debug_misc("spasm-chat", "no handler found for \"%s\"\n",
    + buffer);
    + purple_debug_misc("spasm-chat", "prefix: \"%s\"\n", prefix);
    + purple_debug_misc("spasm-chat", "command: \"%s\"\n", command);
    + purple_debug_misc("spasm-chat", "middle: \"%s\"\n", middle);
    + purple_debug_misc("spasm-chat", "trailing: \"%s\"\n", trailing);
    + purple_debug_misc("spasm-chat", "----\n");
    }
    - g_strfreev(parts);
    + g_free(prefix);
    + g_free(command);
    + g_free(middle);
    + g_free(trailing);
    }
    static void spasm_chat_read(SpasmChatService *chat);
    @@ -324,14 +365,14 @@
    /* now do the login */
    spasm_chat_service_real_send(
    chat,
    - "PASS oauth:%s\r\n",
    + "PASS oauth:%s",
    spasm_account_get_access_token(chat->sa)
    );
    /* now try to use our nick */
    spasm_chat_service_real_send(
    chat,
    - "NICK %s\r\n",
    + "NICK %s",
    spasm_account_get_name(chat->sa)
    );
    @@ -355,6 +396,8 @@
    chat->sa = sa;
    chat->handlers = spasm_chat_service_init_handlers();
    + spasm_chat_service_regex_init(chat);
    +
    return chat;
    }
    @@ -365,6 +408,9 @@
    g_object_unref(chat->socket_client);
    g_object_unref(chat->socket_connection);
    + g_regex_unref(chat->regex_message);
    + g_regex_unref(chat->regex_target);
    +
    g_slice_free(SpasmChatService, chat);
    }
    @@ -430,7 +476,7 @@
    sa = purple_connection_get_protocol_data(connection);
    chat = spasm_account_get_chat_service(sa);
    - spasm_chat_service_real_send(chat, "JOIN #%s\r\n", channel);
    + spasm_chat_service_real_send(chat, "JOIN #%s", channel);
    }
    gchar *
    @@ -459,7 +505,7 @@
    sa = purple_connection_get_protocol_data(connection);
    chat = spasm_account_get_chat_service(sa);
    - spasm_chat_service_real_send(chat, "PRIVMSG #%s :%s\r\n",
    + spasm_chat_service_real_send(chat, "PRIVMSG #%s :%s",
    purple_conversation_get_name(conversation),
    message);