--- a/libpurple/protocols/ircv3/purpleircv3parser.c Sat Nov 19 16:49:14 2022 -0600
+++ b/libpurple/protocols/ircv3/purpleircv3parser.c Tue Nov 22 22:06:24 2022 -0600
@@ -36,6 +36,45 @@
/******************************************************************************
*****************************************************************************/
+purple_ircv3_parser_unescape_tag_value(const char *value) { + GString *unescaped = g_string_new(""); + gboolean escaping = FALSE; + /* Walk the string and replace escaped values according to + * https://ircv3.net/specs/extensions/message-tags.html#escaping-values + for(int i = 0; value[i] != '\0'; i++) { + /* Set the replacement to the current character which will fall + * through for everything, including '\\' + char replacement = value[i]; + } else if(value[i] == 's') { + } else if(value[i] == 'r') { + } else if(value[i] == 'n') { + g_string_append_c(unescaped, replacement); + g_string_append_c(unescaped, value[i]); + return g_string_free(unescaped, FALSE); purple_ircv3_parser_parse_tags(PurpleIRCv3Parser *parser,
const gchar *tags_string, GError **error)
@@ -76,16 +115,20 @@
while(g_match_info_matches(info)) {
+ char *unescaped = NULL; key = g_match_info_fetch_named(info, "key");
value = g_match_info_fetch_named(info, "value");
+ unescaped = purple_ircv3_parser_unescape_tag_value(value); /* the hash table is created with destroy notifies for both key and
* value, so there's no need to free the allocated memory right now.
- g_hash_table_insert(tags, key, value);
+ g_hash_table_insert(tags, key, unescaped); g_match_info_next(info, &local_error);
if(local_error != NULL) {
--- a/libpurple/protocols/ircv3/tests/test_ircv3_parser.c Sat Nov 19 16:49:14 2022 -0600
+++ b/libpurple/protocols/ircv3/tests/test_ircv3_parser.c Tue Nov 22 22:06:24 2022 -0600
@@ -255,8 +255,6 @@
test_purple_ircv3_parser_with_escaped_tags(void) {
-/* Escaped tags aren't implemented yet. */
TestPurpleIRCv3ParserData data = {
@@ -268,7 +266,6 @@
test_purple_ircv3_parser("@a=b\\\\and\\nk;c=72\\s45;d=gh\\:764 foo",
@@ -452,8 +449,6 @@
test_purple_ircv3_slashes_are_fun(void) {
-/* Escaped tags aren't implemented yet. */
TestPurpleIRCv3ParserData data = {
@@ -462,7 +457,6 @@
g_hash_table_insert(data.tags, "foo", "\\\\;\\s \r\n");
test_purple_ircv3_parser("@foo=\\\\\\\\\\:\\\\s\\s\\r\\n COMMAND", &data);
@@ -508,8 +502,6 @@
test_purple_ircv3_tag_escape_char_at_a_time(void) {
-/* Escaped tags aren't implemented yet. */
TestPurpleIRCv3ParserData data = {
@@ -518,12 +510,10 @@
g_hash_table_insert(data.tags, "tag1", "value\\ntest");
test_purple_ircv3_parser("@tag1=value\\\\ntest COMMAND", &data);
test_purple_ircv3_tag_drop_unnecessary_escapes(void) {
TestPurpleIRCv3ParserData data = {
@@ -532,13 +522,10 @@
g_hash_table_insert(data.tags, "tag1", "value1");
test_purple_ircv3_parser("@tag1=value\\1 COMMAND", &data);
test_purple_ircv3_tag_drop_trailing_slash(void) {
-/* Escaped tags aren't implemented yet. */
TestPurpleIRCv3ParserData data = {
@@ -547,7 +534,6 @@
g_hash_table_insert(data.tags, "tag1", "value1");
test_purple_ircv3_parser("@tag1=value1\\ COMMAND", &data);