Ticket #1335: twisted-words-ctcp-quoting.2.diff

File twisted-words-ctcp-quoting.2.diff, 8.0 KB (added by nullie, 9 years ago)

New view on this.

  • D:/work/eclipse-workspace/twisted/twisted/words/test/test_irc.py

     
    2020    "xargs%(NUL)smight%(NUL)slike%(NUL)sthis" % {'NUL': irc.NUL },
    2121    "embedded%(CR)snewline%(CR)s%(NL)sFUN%(NL)s" % {'CR': irc.CR,
    2222                                                    'NL': irc.NL},
    23     "escape!%(X)s escape!%(M)s %(X)s%(X)sa %(M)s0" % {'X': irc.X_QUOTE,
    24                                                       'M': irc.M_QUOTE}
     23    "escape!escape!%(M)s %(M)s0" % {'M': irc.M_QUOTE}
    2524    ]
    2625
    2726
     
    3130        for s in stringSubjects:
    3231            self.failUnlessEqual(s, irc.lowDequote(irc.lowQuote(s)))
    3332
    34     def test_ctcpquoteSanity(self):
    35         """Testing CTCP message level quote/dequote"""
    36         for s in stringSubjects:
    37             self.failUnlessEqual(s, irc.ctcpDequote(irc.ctcpQuote(s)))
    3833
    39 
    4034class IRCClientWithoutLogin(irc.IRCClient):
    4135    performLogin = 0
    4236
     
    297291                             user="sender!ident@host",
    298292                             channel="recipient",
    299293                             message=msg)
     294       
     295    def test_ACTION(self):
     296        """Testing CTCP ACTION.
     297       
     298        This imitates behavior of wide-spread IRC clients for ACTION CTCP
     299        query.
     300        """
     301       
     302        actionQuery = (r":nick!guy@over.there PRIVMSG #theChan :"
     303                       "%(X)cACTION \o/%(X)c%(EOL)s"
     304                       % {'X': irc.X_DELIM,
     305                       'EOL': irc.CR + irc.LF})
     306       
     307        self.client.dataReceived(actionQuery)
     308        self.assertEquals(self.client.calls,
     309                          [("action", dict(user="nick!guy@over.there",
     310                                           channel="#theChan",
     311                                           data=r"\o/"))])
    300312
    301313class BasicServerFunctionalityTestCase(unittest.TestCase):
    302314    def setUp(self):
  • D:/work/eclipse-workspace/twisted/twisted/words/protocols/irc.py

     
    10281028
    10291029        if not message: return # don't raise an exception if some idiot sends us a blank message
    10301030
     1031        # If message starts with X_DELIM, it's CTCP message.
    10311032        if message[0]==X_DELIM:
    1032             m = ctcpExtract(message)
    1033             if m['extended']:
    1034                 self.ctcpQuery(user, channel, m['extended'])
     1033            # Trailing X_DELIM is optional.
     1034            tag, data = ctcpParse(message)
     1035            self.ctcpQuery(user, channel, tag, data)
     1036            return
    10351037
    1036             if not m['normal']:
    1037                 return
    1038 
    1039             message = string.join(m['normal'], ' ')
    1040 
    10411038        self.privmsg(user, channel, message)
    10421039
    10431040    def irc_NOTICE(self, prefix, params):
     
    10451042        channel = params[0]
    10461043        message = params[-1]
    10471044
     1045        # If message starts with X_DELIM, it's CTCP message.
    10481046        if message[0]==X_DELIM:
    1049             m = ctcpExtract(message)
    1050             if m['extended']:
    1051                 self.ctcpReply(user, channel, m['extended'])
     1047            # Trailing X_DELIM is optional.
     1048            tag, data = ctcpParse(message)
     1049            self.ctcpReply(user, channel, tag, data)
     1050            return
    10521051
    1053             if not m['normal']:
    1054                 return
    1055 
    1056             message = string.join(m['normal'], ' ')
    1057 
    10581052        self.noticed(user, channel, message)
    10591053
    10601054    def irc_NICK(self, prefix, params):
     
    11551149    ### Receiving a CTCP query from another party
    11561150    ### It is safe to leave these alone.
    11571151
    1158     def ctcpQuery(self, user, channel, messages):
     1152    def ctcpQuery(self, user, channel, tag, data):
    11591153        """Dispatch method for any CTCP queries received.
    11601154        """
    1161         for m in messages:
    1162             method = getattr(self, "ctcpQuery_%s" % m[0], None)
    1163             if method:
    1164                 method(user, channel, m[1])
    1165             else:
    1166                 self.ctcpUnknownQuery(user, channel, m[0], m[1])
     1155        method = getattr(self, "ctcpQuery_%s" % tag, None)
     1156        if method:
     1157            method(user, channel, data)
     1158        else:
     1159            self.ctcpUnknownQuery(user, channel, tag, data)
    11671160
    11681161    def ctcpQuery_ACTION(self, user, channel, data):
    11691162        self.action(user, channel, data)
     
    14191412    ### Receiving a response to a CTCP query (presumably to one we made)
    14201413    ### You may want to add methods here, or override UnknownReply.
    14211414
    1422     def ctcpReply(self, user, channel, messages):
     1415    def ctcpReply(self, user, channel, tag, data):
    14231416        """Dispatch method for any CTCP replies received.
    14241417        """
    1425         for m in messages:
    1426             method = getattr(self, "ctcpReply_%s" % m[0], None)
    1427             if method:
    1428                 method(user, channel, m[1])
    1429             else:
    1430                 self.ctcpUnknownReply(user, channel, m[0], m[1])
    14311418
     1419        method = getattr(self, "ctcpReply_%s" % tag, None)
     1420        if method:
     1421            method(user, channel, data)
     1422        else:
     1423            self.ctcpUnknownReply(user, channel, tag, data)
     1424
    14321425    def ctcpReply_PING(self, user, channel, data):
    14331426        nick = user.split('!', 1)[0]
    14341427        if (not self._pings) or (not self._pings.has_key((nick, data))):
     
    19081901
    19091902X_DELIM = chr(001)
    19101903
    1911 def ctcpExtract(message):
    1912     """Extract CTCP data from a string.
     1904def ctcpParse(message):
     1905    """Basic CTCP message parsing, data decoding is done by handlers"""
    19131906
    1914     Returns a dictionary with two items:
     1907    # Trailing X_DELIM is optional.
     1908    if message[-1] == X_DELIM:
     1909        message = message[1:-1]
     1910    else:
     1911        message = message[1:]
     1912   
     1913    tag, data = message.split(" ", 1)
     1914    return (tag, data)
    19151915
    1916        - C{'extended'}: a list of CTCP (tag, data) tuples
    1917        - C{'normal'}: a list of strings which were not inside a CTCP delimeter
    1918     """
    1919 
    1920     extended_messages = []
    1921     normal_messages = []
    1922     retval = {'extended': extended_messages,
    1923               'normal': normal_messages }
    1924 
    1925     messages = string.split(message, X_DELIM)
    1926     odd = 0
    1927 
    1928     # X1 extended data X2 nomal data X3 extended data X4 normal...
    1929     while messages:
    1930         if odd:
    1931             extended_messages.append(messages.pop(0))
    1932         else:
    1933             normal_messages.append(messages.pop(0))
    1934         odd = not odd
    1935 
    1936     extended_messages[:] = filter(None, extended_messages)
    1937     normal_messages[:] = filter(None, normal_messages)
    1938 
    1939     extended_messages[:] = map(ctcpDequote, extended_messages)
    1940     for i in xrange(len(extended_messages)):
    1941         m = string.split(extended_messages[i], SPC, 1)
    1942         tag = m[0]
    1943         if len(m) > 1:
    1944             data = m[1]
    1945         else:
    1946             data = None
    1947 
    1948         extended_messages[i] = (tag, data)
    1949 
    1950     return retval
    1951 
    19521916# CTCP escaping
    19531917
    19541918M_QUOTE= chr(020)
     
    19831947
    19841948    return mEscape_re.sub(sub, s)
    19851949
    1986 X_QUOTE = '\\'
    1987 
    1988 xQuoteTable = {
    1989     X_DELIM: X_QUOTE + 'a',
    1990     X_QUOTE: X_QUOTE + X_QUOTE
    1991     }
    1992 
    1993 xDequoteTable = {}
    1994 
    1995 for k, v in xQuoteTable.items():
    1996     xDequoteTable[v[-1]] = k
    1997 
    1998 xEscape_re = re.compile('%s.' % (re.escape(X_QUOTE),), re.DOTALL)
    1999 
    2000 def ctcpQuote(s):
    2001     for c in (X_QUOTE, X_DELIM):
    2002         s = string.replace(s, c, xQuoteTable[c])
    2003     return s
    2004 
    2005 def ctcpDequote(s):
    2006     def sub(matchobj, xDequoteTable=xDequoteTable):
    2007         s = matchobj.group()[1]
    2008         try:
    2009             s = xDequoteTable[s]
    2010         except KeyError:
    2011             s = s
    2012         return s
    2013 
    2014     return xEscape_re.sub(sub, s)
    2015 
    20161950def ctcpStringify(messages):
    20171951    """
    20181952    @type messages: a list of extended messages.  An extended
     
    20341968            m = "%s %s" % (tag, data)
    20351969        else:
    20361970            m = str(tag)
    2037         m = ctcpQuote(m)
     1971
    20381972        m = "%s%s%s" % (X_DELIM, m, X_DELIM)
    20391973        coded_messages.append(m)
    20401974