qulogic/pidgin

cc095f9ce1f3
Parents 02c38caf387b
Children bf4beafd15a3
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',
'purpleircv3core.c',
+ 'purpleircv3formatting.c',
'purpleircv3message.c',
'purpleircv3messagehandlers.c',
'purpleircv3parser.c',
@@ -16,6 +17,7 @@
'purpleircv3connection.h',
'purpleircv3constants.h',
'purpleircv3core.h',
+ 'purpleircv3formatting.h',
'purpleircv3message.h',
'purpleircv3messagehandlers.h',
'purpleircv3parser.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"
enum {
@@ -665,6 +666,7 @@
PurpleMessage *message = NULL;
GString *str = NULL;
GStrv params = NULL;
+ char *stripped = NULL;
const char *command = NULL;
g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
@@ -685,13 +687,15 @@
g_free(joined);
}
+ stripped = purple_ircv3_formatting_strip(str->str);
+ g_string_free(str, TRUE);
+
message = g_object_new(
PURPLE_TYPE_MESSAGE,
"author", purple_ircv3_message_get_source(v3_message),
- "contents", str->str,
+ "contents", stripped,
NULL);
-
- g_string_free(str, TRUE);
+ g_free(stripped);
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)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static inline gboolean
+purple_ircv3_formatting_is_hex_color(const char *text) {
+ for(int i = 0; i < 6; i++) {
+ if(text[i] == '\0') {
+ return FALSE;
+ }
+
+ if(!g_ascii_isxdigit(text[i])) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+char *
+purple_ircv3_formatting_strip(const char *text) {
+ GString *str = NULL;
+
+ /* We don't use purple_strempty here because if we're passed an empty
+ * string, we should return a newly allocated empty string.
+ */
+ if(text == NULL) {
+ return NULL;
+ }
+
+ str = g_string_new("");
+
+ for(int i = 0; text[i] != '\0'; i++) {
+ switch(text[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:
+ continue;
+ break;
+ case IRCV3_FORMAT_COLOR:
+ if(g_ascii_isdigit(text[i + 1])) {
+ i += 1;
+
+ if(g_ascii_isdigit(text[i + 1])) {
+ i += 1;
+ }
+
+ if(text[i + 1] == ',' && g_ascii_isdigit(text[i + 2])) {
+ i += 2;
+
+ if(g_ascii_isdigit(text[i + 1])) {
+ i += 1;
+ }
+ }
+ }
+
+ break;
+ case IRCV3_FORMAT_HEX_COLOR:
+ if(purple_ircv3_formatting_is_hex_color(&text[i + 1])) {
+ i += 6;
+ }
+
+ if(text[i + 1] == ',' &&
+ purple_ircv3_formatting_is_hex_color(&text[i + 2]))
+ {
+ i += 7;
+ }
+
+ break;
+ default:
+ g_string_append_c(str, text[i]);
+ break;
+ }
+
+ }
+
+ 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"
+#endif
+
+#ifndef PURPLE_IRCV3_FORMATTING_H
+#define PURPLE_IRCV3_FORMATTING_H
+
+#include <glib.h>
+
+#include "purpleircv3version.h"
+
+G_BEGIN_DECLS
+
+/**
+ * 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.
+ *
+ * Since: 3.0.0
+ */
+PURPLE_IRCV3_AVAILABLE_IN_ALL
+char *purple_ircv3_formatting_strip(const char *text);
+
+G_END_DECLS
+
+#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_id = NULL;
gpointer raw_timestamp = NULL;
char *nick = NULL;
+ char *stripped = NULL;
const char *command = NULL;
const char *id = NULL;
const char *source = NULL;
@@ -210,14 +212,16 @@
dt = g_date_time_new_now_local();
}
+ stripped = purple_ircv3_formatting_strip(params[1]);
message = g_object_new(
PURPLE_TYPE_MESSAGE,
"author", source,
- "contents", params[1],
+ "contents", stripped,
"flags", flags,
"id", id,
"timestamp", dt,
NULL);
+ g_free(stripped);
g_date_time_unref(dt);
--- 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 @@
TESTS = [
+ 'formatting',
'parser',
'source',
]
--- /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 <glib.h>
+
+#include <purple.h>
+
+#include "../purpleircv3formatting.h"
+
+/******************************************************************************
+ * Strip Tests
+ *****************************************************************************/
+static void
+test_ircv3_formatting_strip(const char *input, const char *expected) {
+ char *actual = NULL;
+
+ actual = purple_ircv3_formatting_strip(input);
+ g_assert_cmpstr(actual, ==, expected);
+ g_clear_pointer(&actual, g_free);
+}
+
+static void
+test_ircv3_formatting_strip_null(void) {
+ test_ircv3_formatting_strip(NULL, NULL);
+}
+
+static void
+test_ircv3_formatting_strip_empty(void) {
+ test_ircv3_formatting_strip("", "");
+}
+
+static void
+test_ircv3_formatting_strip_color_comma(void) {
+ test_ircv3_formatting_strip("\003,", ",");
+}
+
+static void
+test_ircv3_formatting_strip_color_foreground_comma(void) {
+ test_ircv3_formatting_strip("\0033,", ",");
+}
+
+static void
+test_ircv3_formatting_strip_color_full(void) {
+ test_ircv3_formatting_strip("\0033,9wee", "wee");
+}
+
+static void
+test_ircv3_formatting_strip_color_foreground_3_digit(void) {
+ test_ircv3_formatting_strip("\003314", "4");
+}
+
+static void
+test_ircv3_formatting_strip_color_background_3_digit(void) {
+ test_ircv3_formatting_strip("\0031,234", "4");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color(void) {
+ test_ircv3_formatting_strip("\004FF00FFwoo!", "woo!");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color_full(void) {
+ test_ircv3_formatting_strip("\004FF00FF,00FF00woo!", "woo!");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color_comma(void) {
+ test_ircv3_formatting_strip("\004,", ",");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color_foreground_comma(void) {
+ test_ircv3_formatting_strip("\004FEFEFE,", ",");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color_foreground_7_characters(void) {
+ test_ircv3_formatting_strip("\004FEFEFEF", "F");
+}
+
+static void
+test_ircv3_formatting_strip_hex_color_background_7_characters(void) {
+ test_ircv3_formatting_strip("\004FEFEFE,2222223", "3");
+}
+
+static void
+test_ircv3_formatting_strip_bold(void) {
+ test_ircv3_formatting_strip("this is \002bold\002!", "this is bold!");
+}
+
+static void
+test_ircv3_formatting_strip_italic(void) {
+ test_ircv3_formatting_strip("what do you \035mean\035?!",
+ "what do you mean?!");
+}
+
+static void
+test_ircv3_formatting_strip_monospace(void) {
+ test_ircv3_formatting_strip("\021i++;\021", "i++;");
+}
+
+static void
+test_ircv3_formatting_strip_reset(void) {
+ test_ircv3_formatting_strip("end of formatting\017", "end of formatting");
+}
+
+static void
+test_ircv3_formatting_strip_reverse(void) {
+ test_ircv3_formatting_strip("re\026ver\026se", "reverse");
+}
+
+static void
+test_ircv3_formatting_strip_strikethrough(void) {
+ test_ircv3_formatting_strip("\036I could be wrong\036",
+ "I could be wrong");
+}
+
+static void
+test_ircv3_formatting_strip_underline(void) {
+ test_ircv3_formatting_strip("You can't handle the \037truth\037!",
+ "You can't handle the truth!");
+}
+
+static void
+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!");
+}
+
+static void
+test_ircv3_formatting_strip_spec_example2(void) {
+ test_ircv3_formatting_strip(
+ "This is a \035\00313,9cool \003message",
+ "This is a cool message");
+}
+
+static void
+test_ircv3_formatting_strip_spec_example3(void) {
+ test_ircv3_formatting_strip(
+ "IRC \002is \0034,12so \003great\017!",
+ "IRC is so great!");
+}
+
+static void
+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
+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);
+
+ return g_test_run();
+}