IRCv3: strip formatting from incoming messages
This covers everything in https://modern.ircdocs.horse/formatting.html.
This is basically just a stop gap for now as we figure out exactly how we can't to handle formatting between the core and ui.
Testing Done:
Ran the unit tests under valgrind.
Reviewed at https://reviews.imfreedom.org/r/2797/
--- a/libpurple/protocols/ircv3/meson.build Thu Nov 16 23:44:39 2023 -0600
+++ b/libpurple/protocols/ircv3/meson.build Thu Nov 16 23:50:35 2023 -0600
@@ -2,6 +2,7 @@
'purpleircv3capabilities.c',
'purpleircv3connection.c',
+ 'purpleircv3formatting.c', 'purpleircv3messagehandlers.c',
@@ -16,6 +17,7 @@
'purpleircv3connection.h',
'purpleircv3constants.h',
+ 'purpleircv3formatting.h', 'purpleircv3messagehandlers.h',
--- a/libpurple/protocols/ircv3/purpleircv3connection.c Thu Nov 16 23:44:39 2023 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3connection.c Thu Nov 16 23:50:35 2023 -0600
@@ -21,6 +21,7 @@
#include "purpleircv3connection.h"
#include "purpleircv3core.h"
+#include "purpleircv3formatting.h" #include "purpleircv3parser.h"
@@ -665,6 +666,7 @@
PurpleMessage *message = NULL;
const char *command = NULL;
g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
@@ -685,13 +687,15 @@
+ stripped = purple_ircv3_formatting_strip(str->str); + g_string_free(str, TRUE); "author", purple_ircv3_message_get_source(v3_message),
- g_string_free(str, TRUE);
purple_conversation_write_message(priv->status_conversation, message);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/ircv3/purpleircv3formatting.c Thu Nov 16 23:50:35 2023 -0600
@@ -0,0 +1,114 @@
+ * 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 "purpleircv3formatting.h" +#define IRCV3_FORMAT_BOLD (0x02) +#define IRCV3_FORMAT_COLOR (0x03) +#define IRCV3_FORMAT_HEX_COLOR (0x04) +#define IRCV3_FORMAT_ITALIC (0x1d) +#define IRCV3_FORMAT_MONOSPACE (0x11) +#define IRCV3_FORMAT_RESET (0x0f) +#define IRCV3_FORMAT_REVERSE (0x16) +#define IRCV3_FORMAT_STRIKETHROUGH (0x1e) +#define IRCV3_FORMAT_UNDERLINE (0x1f) +/****************************************************************************** + *****************************************************************************/ +purple_ircv3_formatting_is_hex_color(const char *text) { + for(int i = 0; i < 6; i++) { + if(!g_ascii_isxdigit(text[i])) { +/****************************************************************************** + *****************************************************************************/ +purple_ircv3_formatting_strip(const char *text) { + /* We don't use purple_strempty here because if we're passed an empty + * string, we should return a newly allocated empty string. + str = g_string_new(""); + for(int i = 0; text[i] != '\0'; i++) { + case IRCV3_FORMAT_BOLD: + case IRCV3_FORMAT_ITALIC: + case IRCV3_FORMAT_MONOSPACE: + case IRCV3_FORMAT_RESET: + case IRCV3_FORMAT_REVERSE: + case IRCV3_FORMAT_STRIKETHROUGH: + case IRCV3_FORMAT_UNDERLINE: + case IRCV3_FORMAT_COLOR: + if(g_ascii_isdigit(text[i + 1])) { + if(g_ascii_isdigit(text[i + 1])) { + if(text[i + 1] == ',' && g_ascii_isdigit(text[i + 2])) { + if(g_ascii_isdigit(text[i + 1])) { + case IRCV3_FORMAT_HEX_COLOR: + if(purple_ircv3_formatting_is_hex_color(&text[i + 1])) { + if(text[i + 1] == ',' && + purple_ircv3_formatting_is_hex_color(&text[i + 2])) + g_string_append_c(str, text[i]); + return g_string_free(str, FALSE); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/ircv3/purpleircv3formatting.h Thu Nov 16 23:50:35 2023 -0600
@@ -0,0 +1,47 @@
+ * 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/>. +#if !defined(PURPLE_IRCV3_GLOBAL_HEADER_INSIDE) && \ + !defined(PURPLE_IRCV3_COMPILATION) +# error "only <libpurple/protocols/ircv3.h> may be included directly" +#ifndef PURPLE_IRCV3_FORMATTING_H +#define PURPLE_IRCV3_FORMATTING_H +#include "purpleircv3version.h" + * purple_ircv3_formatting_strip: + * @text: (nullable): The text to strip. + * Removes all formatting from @text and returns the result. + * Returns: (transfer full) (nullable): The result of stripping @text. +PURPLE_IRCV3_AVAILABLE_IN_ALL +char *purple_ircv3_formatting_strip(const char *text); +#endif /* PURPLE_IRCV3_FORMATTING_H */ --- a/libpurple/protocols/ircv3/purpleircv3messagehandlers.c Thu Nov 16 23:44:39 2023 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3messagehandlers.c Thu Nov 16 23:50:35 2023 -0600
@@ -23,6 +23,7 @@
#include "purpleircv3connection.h"
#include "purpleircv3constants.h"
#include "purpleircv3core.h"
+#include "purpleircv3formatting.h" #include "purpleircv3source.h"
/******************************************************************************
@@ -134,6 +135,7 @@
gpointer raw_timestamp = NULL;
const char *command = NULL;
const char *source = NULL;
@@ -210,14 +212,16 @@
dt = g_date_time_new_now_local();
+ stripped = purple_ircv3_formatting_strip(params[1]);
--- a/libpurple/protocols/ircv3/tests/meson.build Thu Nov 16 23:44:39 2023 -0600
+++ b/libpurple/protocols/ircv3/tests/meson.build Thu Nov 16 23:50:35 2023 -0600
@@ -1,4 +1,5 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/ircv3/tests/test_ircv3_formatting.c Thu Nov 16 23:50:35 2023 -0600
@@ -0,0 +1,232 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This library 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <https://www.gnu.org/licenses/>. +#include "../purpleircv3formatting.h" +/****************************************************************************** + *****************************************************************************/ +test_ircv3_formatting_strip(const char *input, const char *expected) { + actual = purple_ircv3_formatting_strip(input); + g_assert_cmpstr(actual, ==, expected); + g_clear_pointer(&actual, g_free); +test_ircv3_formatting_strip_null(void) { + test_ircv3_formatting_strip(NULL, NULL); +test_ircv3_formatting_strip_empty(void) { + test_ircv3_formatting_strip("", ""); +test_ircv3_formatting_strip_color_comma(void) { + test_ircv3_formatting_strip("\003,", ","); +test_ircv3_formatting_strip_color_foreground_comma(void) { + test_ircv3_formatting_strip("\0033,", ","); +test_ircv3_formatting_strip_color_full(void) { + test_ircv3_formatting_strip("\0033,9wee", "wee"); +test_ircv3_formatting_strip_color_foreground_3_digit(void) { + test_ircv3_formatting_strip("\003314", "4"); +test_ircv3_formatting_strip_color_background_3_digit(void) { + test_ircv3_formatting_strip("\0031,234", "4"); +test_ircv3_formatting_strip_hex_color(void) { + test_ircv3_formatting_strip("\004FF00FFwoo!", "woo!"); +test_ircv3_formatting_strip_hex_color_full(void) { + test_ircv3_formatting_strip("\004FF00FF,00FF00woo!", "woo!"); +test_ircv3_formatting_strip_hex_color_comma(void) { + test_ircv3_formatting_strip("\004,", ","); +test_ircv3_formatting_strip_hex_color_foreground_comma(void) { + test_ircv3_formatting_strip("\004FEFEFE,", ","); +test_ircv3_formatting_strip_hex_color_foreground_7_characters(void) { + test_ircv3_formatting_strip("\004FEFEFEF", "F"); +test_ircv3_formatting_strip_hex_color_background_7_characters(void) { + test_ircv3_formatting_strip("\004FEFEFE,2222223", "3"); +test_ircv3_formatting_strip_bold(void) { + test_ircv3_formatting_strip("this is \002bold\002!", "this is bold!"); +test_ircv3_formatting_strip_italic(void) { + test_ircv3_formatting_strip("what do you \035mean\035?!", +test_ircv3_formatting_strip_monospace(void) { + test_ircv3_formatting_strip("\021i++;\021", "i++;"); +test_ircv3_formatting_strip_reset(void) { + test_ircv3_formatting_strip("end of formatting\017", "end of formatting"); +test_ircv3_formatting_strip_reverse(void) { + test_ircv3_formatting_strip("re\026ver\026se", "reverse"); +test_ircv3_formatting_strip_strikethrough(void) { + test_ircv3_formatting_strip("\036I could be wrong\036", +test_ircv3_formatting_strip_underline(void) { + test_ircv3_formatting_strip("You can't handle the \037truth\037!", + "You can't handle the truth!"); +test_ircv3_formatting_strip_spec_example1(void) { + test_ircv3_formatting_strip( + "I love \0033IRC! \003It is the \0037best protocol ever!", + "I love IRC! It is the best protocol ever!"); +test_ircv3_formatting_strip_spec_example2(void) { + test_ircv3_formatting_strip( + "This is a \035\00313,9cool \003message", + "This is a cool message"); +test_ircv3_formatting_strip_spec_example3(void) { + test_ircv3_formatting_strip( + "IRC \002is \0034,12so \003great\017!", +test_ircv3_formatting_strip_spec_example4(void) { + test_ircv3_formatting_strip( + "Rules: Don't spam 5\00313,8,6\003,7,8, and especially not \0029\002\035!", + "Rules: Don't spam 5,6,7,8, and especially not 9!"); +/****************************************************************************** + *****************************************************************************/ +main(int argc, char *argv[]) { + g_test_init(&argc, &argv, NULL); + g_test_add_func("/ircv3/formatting/strip/null", + test_ircv3_formatting_strip_null); + g_test_add_func("/ircv3/formatting/strip/empty", + test_ircv3_formatting_strip_empty); + g_test_add_func("/ircv3/formatting/strip/color-comma", + test_ircv3_formatting_strip_color_comma); + g_test_add_func("/ircv3/formatting/strip/color-full", + test_ircv3_formatting_strip_color_full); + g_test_add_func("/ircv3/formatting/strip/color-foreground-comma", + test_ircv3_formatting_strip_color_foreground_comma); + g_test_add_func("/ircv3/formatting/strip/color-foreground-3-digit", + test_ircv3_formatting_strip_color_foreground_3_digit); + g_test_add_func("/ircv3/formatting/strip/color-background-3-digit", + test_ircv3_formatting_strip_color_background_3_digit); + g_test_add_func("/ircv3/formatting/strip/hex-color", + test_ircv3_formatting_strip_hex_color); + g_test_add_func("/ircv3/formatting/strip/hex-color-full", + test_ircv3_formatting_strip_hex_color_full); + g_test_add_func("/ircv3/formatting/strip/hex-color-comma", + test_ircv3_formatting_strip_hex_color_comma); + g_test_add_func("/ircv3/formatting/strip/hex-color-foreground-comma", + test_ircv3_formatting_strip_hex_color_foreground_comma); + g_test_add_func("/ircv3/formatting/strip/hex-color-foreground-7-characters", + test_ircv3_formatting_strip_hex_color_foreground_7_characters); + g_test_add_func("/ircv3/formatting/strip/hex-color-background-7-characters", + test_ircv3_formatting_strip_hex_color_background_7_characters); + g_test_add_func("/ircv3/formatting/strip/bold", + test_ircv3_formatting_strip_bold); + g_test_add_func("/ircv3/formatting/strip/italic", + test_ircv3_formatting_strip_italic); + g_test_add_func("/ircv3/formatting/strip/monospace", + test_ircv3_formatting_strip_monospace); + g_test_add_func("/ircv3/formatting/strip/reset", + test_ircv3_formatting_strip_reset); + g_test_add_func("/ircv3/formatting/strip/reverse", + test_ircv3_formatting_strip_reverse); + g_test_add_func("/ircv3/formatting/strip/strikethrough", + test_ircv3_formatting_strip_strikethrough); + g_test_add_func("/ircv3/formatting/strip/underline", + test_ircv3_formatting_strip_underline); + /* These tests are based on the examples here + * https://modern.ircdocs.horse/formatting.html#examples + g_test_add_func("/ircv3/formatting/strip/spec-example1", + test_ircv3_formatting_strip_spec_example1); + g_test_add_func("/ircv3/formatting/strip/spec-example2", + test_ircv3_formatting_strip_spec_example2); + g_test_add_func("/ircv3/formatting/strip/spec-example3", + test_ircv3_formatting_strip_spec_example3); + g_test_add_func("/ircv3/formatting/strip/spec-example4", + test_ircv3_formatting_strip_spec_example4);