# HG changeset patch
# Node ID 6c996b3928a6
# Parent  0472e404b129
diff --git a/libpurple/protocols/ircv3/purpleircv3parser.c b/libpurple/protocols/ircv3/purpleircv3parser.c
--- a/libpurple/protocols/ircv3/purpleircv3parser.c
+++ b/libpurple/protocols/ircv3/purpleircv3parser.c
@@ -36,6 +36,45 @@
 /******************************************************************************
  * Helpers
  *****************************************************************************/
+static char *
+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++) {
+		if(escaping) {
+			/* Set the replacement to the current character which will fall
+			 * through for everything, including '\\'
+			 */
+			char replacement = value[i];
+
+			if(value[i] == ':') {
+				replacement = ';';
+			} else if(value[i] == 's') {
+				replacement = ' ';
+			} else if(value[i] == 'r') {
+				replacement = '\r';
+			} else if(value[i] == 'n') {
+				replacement = '\n';
+			}
+
+			g_string_append_c(unescaped, replacement);
+			escaping = FALSE;
+		} else {
+			if(value[i] == '\\') {
+				escaping = TRUE;
+			} else {
+				g_string_append_c(unescaped, value[i]);
+			}
+		}
+	}
+
+	return g_string_free(unescaped, FALSE);
+}
+
 static GHashTable *
 purple_ircv3_parser_parse_tags(PurpleIRCv3Parser *parser,
                                const gchar *tags_string, GError **error)
@@ -76,16 +115,20 @@
 	}
 
 	while(g_match_info_matches(info)) {
-		gchar *key = NULL;
-		gchar *value = NULL;
+		char *key = NULL;
+		char *value = NULL;
+		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);
+		g_free(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) {
diff --git a/libpurple/protocols/ircv3/tests/test_ircv3_parser.c b/libpurple/protocols/ircv3/tests/test_ircv3_parser.c
--- a/libpurple/protocols/ircv3/tests/test_ircv3_parser.c
+++ b/libpurple/protocols/ircv3/tests/test_ircv3_parser.c
@@ -255,8 +255,6 @@
 
 static void
 test_purple_ircv3_parser_with_escaped_tags(void) {
-#if 0
-/* Escaped tags aren't implemented yet. */
 	TestPurpleIRCv3ParserData data = {
 		.command = "foo",
 	};
@@ -268,7 +266,6 @@
 
 	test_purple_ircv3_parser("@a=b\\\\and\\nk;c=72\\s45;d=gh\\:764 foo",
 	                         &data);
-#endif
 }
 
 static void
@@ -452,8 +449,6 @@
 
 static void
 test_purple_ircv3_slashes_are_fun(void) {
-#if 0
-/* Escaped tags aren't implemented yet. */
 	TestPurpleIRCv3ParserData data = {
 		.command = "COMMAND",
 	};
@@ -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);
-#endif
 }
 
 static void
@@ -508,8 +502,6 @@
 
 static void
 test_purple_ircv3_tag_escape_char_at_a_time(void) {
-#if 0
-/* Escaped tags aren't implemented yet. */
 	TestPurpleIRCv3ParserData data = {
 		.command = "COMMAND",
 	};
@@ -518,12 +510,10 @@
 	g_hash_table_insert(data.tags, "tag1", "value\\ntest");
 
 	test_purple_ircv3_parser("@tag1=value\\\\ntest COMMAND", &data);
-#endif
 }
 
 static void
 test_purple_ircv3_tag_drop_unnecessary_escapes(void) {
-#if 0
 	TestPurpleIRCv3ParserData data = {
 		.command = "COMMAND",
 	};
@@ -532,13 +522,10 @@
 	g_hash_table_insert(data.tags, "tag1", "value1");
 
 	test_purple_ircv3_parser("@tag1=value\\1 COMMAND", &data);
-#endif
 }
 
 static void
 test_purple_ircv3_tag_drop_trailing_slash(void) {
-#if 0
-/* Escaped tags aren't implemented yet. */
 	TestPurpleIRCv3ParserData data = {
 		.command = "COMMAND",
 	};
@@ -547,7 +534,6 @@
 	g_hash_table_insert(data.tags, "tag1", "value1");
 
 	test_purple_ircv3_parser("@tag1=value1\\ COMMAND", &data);
-#endif
 }
 
 static void
