diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 64b82f5b6..c687e8cc7 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -63,8 +63,19 @@ SERVER_TAG_ESCAPE = [ ] escape_server_tag_value = utils.str.MultipleReplacer( dict(SERVER_TAG_ESCAPE)) -unescape_server_tag_value = utils.str.MultipleReplacer( - dict(map(lambda x:(x[1],x[0]), SERVER_TAG_ESCAPE))) + +_server_tag_unescape = {k: v for (v, k) in SERVER_TAG_ESCAPE} +_escape_sequence_pattern = re.compile(r'\\.?') +def _unescape_replacer(m): + escape_sequence = m.group(0) + unescaped = _server_tag_unescape.get(escape_sequence) + if unescaped is None: + # Matches both a lone \ at the end and a \ followed by an "invalid" + # character. In both cases, the \ must be dropped. + return escape_sequence[1:] + return unescaped +def unescape_server_tag_value(s): + return _escape_sequence_pattern.sub(_unescape_replacer, s) def parse_server_tags(s): server_tags = {} diff --git a/test/test_ircmsgs.py b/test/test_ircmsgs.py index 515f2e562..4b51ec9d0 100644 --- a/test/test_ircmsgs.py +++ b/test/test_ircmsgs.py @@ -163,6 +163,24 @@ class IrcMsgTestCase(SupyTestCase): m._str = None # Clear the cache (set before parsing) self.assertEqual(str(m), s + '\r\n') + # bar\1 is equivalent to baz1 + s = r'@foo=;bar=baz\1;qux= ' \ + r':nick!ident@host.com PRIVMSG me :Hello' + m = ircmsgs.IrcMsg(s) + self.assertEqual(m.server_tags, { + 'foo': None, + 'bar': 'baz1', + 'qux': None}) + + # bar\ is equivalent to baz + s = r'@foo=;bar=baz\;qux= ' \ + r':nick!ident@host.com PRIVMSG me :Hello' + m = ircmsgs.IrcMsg(s) + self.assertEqual(m.server_tags, { + 'foo': None, + 'bar': 'baz', + 'qux': None}) + s = r'@foo=;bar=baz;qux= ' \ r':nick!ident@host.com PRIVMSG me :Hello' m = ircmsgs.IrcMsg(s)