Fri, 20 Oct 2023 02:15:01 -0500
IRCv3: Implement the channel joining functions in PurpleProtocolConversation
I tested this on libera and twitch and was able to join channels and send
messages on both.
Testing Done:
As mentioned earlier tested on libera and twitch. I ran into some additional issues with twitch which these changes exposed but aren't caused by these changes.
Reviewed at https://reviews.imfreedom.org/r/2678/
/* * Purple - Internet Messaging Library * Copyright (C) Pidgin Developers <devel@pidgin.im> * * 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, see <https://www.gnu.org/licenses/>. */ #include <glib/gi18n-lib.h> #include "purpleircv3messagehandlers.h" #include "purpleircv3connection.h" #include "purpleircv3core.h" #include "purpleircv3source.h" /****************************************************************************** * Fallback *****************************************************************************/ gboolean purple_ircv3_message_handler_fallback(PurpleIRCv3Message *message, G_GNUC_UNUSED GError **error, gpointer data) { PurpleIRCv3Connection *connection = data; char *new_command = NULL; const char *command = NULL; command = purple_ircv3_message_get_command(message); new_command = g_strdup_printf(_("unknown command '%s'"), command); purple_ircv3_message_set_command(message, new_command); purple_ircv3_connection_add_status_message(connection, message); g_clear_pointer(&new_command, g_free); return TRUE; } /****************************************************************************** * Status Messages *****************************************************************************/ gboolean purple_ircv3_message_handler_status(PurpleIRCv3Message *message, G_GNUC_UNUSED GError **error, gpointer data) { purple_ircv3_connection_add_status_message(data, message); return TRUE; } gboolean purple_ircv3_message_handler_status_ignore_param0(PurpleIRCv3Message *message, GError **error, gpointer data) { GStrv params = NULL; GStrv new_params = NULL; guint n_params = 0; params = purple_ircv3_message_get_params(message); if(params != NULL) { n_params = g_strv_length(params); } if(n_params <= 1) { g_set_error(error, PURPLE_IRCV3_DOMAIN, 0, "expected n_params > 1, got %u", n_params); return FALSE; } /* We need to make a copy because otherwise we'd get a use after free in * set_params. */ new_params = g_strdupv(params + 1); purple_ircv3_message_set_params(message, new_params); g_clear_pointer(&new_params, g_strfreev); purple_ircv3_connection_add_status_message(data, message); return TRUE; } /****************************************************************************** * General Commands *****************************************************************************/ gboolean purple_ircv3_message_handler_ping(PurpleIRCv3Message *message, G_GNUC_UNUSED GError **error, gpointer data) { PurpleIRCv3Connection *connection = data; GStrv params = NULL; params = purple_ircv3_message_get_params(message); if(params != NULL && g_strv_length(params) == 1) { purple_ircv3_connection_writef(connection, "PONG %s", params[0]); } else { purple_ircv3_connection_writef(connection, "PONG"); } return TRUE; } gboolean purple_ircv3_message_handler_privmsg(PurpleIRCv3Message *v3_message, G_GNUC_UNUSED GError **error, gpointer data) { PurpleIRCv3Connection *connection = data; PurpleAccount *account = NULL; PurpleContact *contact = NULL; PurpleContactManager *contact_manager = NULL; PurpleConversation *conversation = NULL; PurpleConversationManager *conversation_manager = NULL; PurpleMessage *message = NULL; PurpleMessageFlags flags = PURPLE_MESSAGE_RECV; GDateTime *dt = NULL; GHashTable *tags = NULL; GStrv params = NULL; gpointer raw_id = NULL; gpointer raw_timestamp = NULL; char *nick = NULL; const char *command = NULL; const char *id = NULL; const char *source = NULL; const char *target = NULL; command = purple_ircv3_message_get_command(v3_message); params = purple_ircv3_message_get_params(v3_message); source = purple_ircv3_message_get_source(v3_message); tags = purple_ircv3_message_get_tags(v3_message); if(params != NULL && g_strv_length(params) != 2) { char *body = g_strjoinv(" ", params); g_warning("unknown privmsg message format: '%s'", body); g_free(body); return FALSE; } purple_ircv3_source_parse(source, &nick, NULL, NULL); account = purple_connection_get_account(PURPLE_CONNECTION(connection)); contact_manager = purple_contact_manager_get_default(); contact = purple_contact_manager_find_with_username(contact_manager, account, nick); if(!PURPLE_IS_CONTACT(contact)) { contact = purple_contact_new(account, NULL); purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), nick); purple_contact_info_set_sid(PURPLE_CONTACT_INFO(contact), source); purple_contact_manager_add(contact_manager, contact); } g_clear_object(&contact); conversation_manager = purple_conversation_manager_get_default(); target = params[0]; if(target[0] == '#') { conversation = purple_conversation_manager_find(conversation_manager, account, target); } else { conversation = purple_conversation_manager_find(conversation_manager, account, nick); } if(!PURPLE_IS_CONVERSATION(conversation)) { if(target[0] == '#') { conversation = g_object_new( PURPLE_TYPE_CONVERSATION, "account", account, "name", target, "type", PurpleConversationTypeChannel, "id", target, NULL); } else { conversation = g_object_new( PURPLE_TYPE_CONVERSATION, "account", account, "name", nick, "type", PurpleConversationTypeDM, "id", nick, NULL); } purple_conversation_manager_register(conversation_manager, conversation); /* The manager creates its own reference on our new conversation, so we * borrow it like we do above if it already exists. */ g_object_unref(conversation); } /* Grab the msgid if one was provided. */ if(g_hash_table_lookup_extended(tags, "msgid", NULL, &raw_id)) { if(!purple_strempty(raw_id)) { id = raw_id; } } if(purple_strequal(command, "NOTICE")) { flags |= PURPLE_MESSAGE_NOTIFY; } /* Determine the timestamp of the message. */ if(g_hash_table_lookup_extended(tags, "time", NULL, &raw_timestamp)) { const char *timestamp = raw_timestamp; if(!purple_strempty(timestamp)) { GTimeZone *tz = g_time_zone_new_utc(); dt = g_date_time_new_from_iso8601(timestamp, tz); g_time_zone_unref(tz); } } /* If the server didn't provide a time, use the current local time. */ if(dt == NULL) { dt = g_date_time_new_now_local(); } message = g_object_new( PURPLE_TYPE_MESSAGE, "author", source, "contents", params[1], "flags", flags, "id", id, "timestamp", dt, NULL); g_date_time_unref(dt); purple_conversation_write_message(conversation, message); g_clear_pointer(&nick, g_free); g_clear_object(&message); return TRUE; }