Skip to content

Commit

Permalink
Fix RFC-compliance of privmsgs/notices/kicks/whois/... with list of n…
Browse files Browse the repository at this point in the history
…icks/channels are argument. Closes GH-462.
  • Loading branch information
progval committed Mar 23, 2013
1 parent 5209cbc commit 574d73c
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 7 deletions.
19 changes: 12 additions & 7 deletions src/ircmsgs.py
Expand Up @@ -337,7 +337,10 @@ def nick():
###

isNick = ircutils.isNick
areNicks = ircutils.areNicks
isChannel = ircutils.isChannel
areChannels = ircutils.areChannels
areReceivers = ircutils.areReceivers
isUserHostmask = ircutils.isUserHostmask

def pong(payload, prefix='', msg=None):
Expand Down Expand Up @@ -545,12 +548,14 @@ def kick(channel, nick, s='', prefix='', msg=None):
return IrcMsg(prefix=prefix, command='KICK',
args=(channel, nick), msg=msg)

def kicks(channel, nicks, s='', prefix='', msg=None):
def kicks(channels, nicks, s='', prefix='', msg=None):
"""Returns a KICK to kick each of nicks from channel with the message msg.
"""
if isinstance(channels, str): # Backward compatibility
channels = [channels]
if conf.supybot.protocols.irc.strictRfc():
assert isChannel(channel), repr(channel)
assert all(isNick, nicks), nicks
assert areChannels(channels), repr(channel)
assert areNicks(nicks), repr(nicks)
if msg and not prefix:
prefix = msg.prefix
if sys.version_info[0] < 3 and isinstance(s, unicode):
Expand All @@ -566,7 +571,7 @@ def kicks(channel, nicks, s='', prefix='', msg=None):
def privmsg(recipient, s, prefix='', msg=None):
"""Returns a PRIVMSG to recipient with the message msg."""
if conf.supybot.protocols.irc.strictRfc():
assert (isChannel(recipient) or isNick(recipient)), repr(recipient)
assert (areReceivers(recipient)), repr(recipient)
assert s, 's must not be empty.'
if sys.version_info[0] < 3 and isinstance(s, unicode):
s = s.encode('utf8')
Expand Down Expand Up @@ -598,7 +603,7 @@ def action(recipient, s, prefix='', msg=None):
def notice(recipient, s, prefix='', msg=None):
"""Returns a NOTICE to recipient with the message msg."""
if conf.supybot.protocols.irc.strictRfc():
assert (isChannel(recipient) or isNick(recipient)), repr(recipient)
assert areReceivers(recipient), repr(recipient)
assert s, 'msg must not be empty.'
if sys.version_info[0] < 3 and isinstance(s, unicode):
s = s.encode('utf8')
Expand Down Expand Up @@ -735,7 +740,7 @@ def who(hostmaskOrChannel, prefix='', msg=None):
def whois(nick, mask='', prefix='', msg=None):
"""Returns a WHOIS for nick."""
if conf.supybot.protocols.irc.strictRfc():
assert isNick(nick), repr(nick)
assert areNicks(nick), repr(nick)
if msg and not prefix:
prefix = msg.prefix
args = (nick,)
Expand All @@ -745,7 +750,7 @@ def whois(nick, mask='', prefix='', msg=None):

def names(channel=None, prefix='', msg=None):
if conf.supybot.protocols.irc.strictRfc():
assert isChannel(channel)
assert areChannels(channel)
if msg and not prefix:
prefix = msg.prefix
if channel is not None:
Expand Down
20 changes: 20 additions & 0 deletions src/ircutils.py
Expand Up @@ -43,6 +43,7 @@
import random
import string
import textwrap
import functools
from cStringIO import StringIO as sio

import supybot.utils as utils
Expand Down Expand Up @@ -132,6 +133,11 @@ def isNick(s, strictRfc=True, nicklen=None):
not isUserHostmask(s) and \
not ' ' in s and not '!' in s

def areNicks(s, strictRfc=True, nicklen=None):
"""Like 'isNick(x)' but for comma-separated list."""
nick = functools.partial(isNick, strictRfc=strictRfc, nicklen=nicklen)
return all(map(nick, s.split(',')))

def isChannel(s, chantypes='#&+!', channellen=50):
"""s => bool
Returns True if s is a valid IRC channel name."""
Expand All @@ -142,6 +148,20 @@ def isChannel(s, chantypes='#&+!', channellen=50):
len(s) <= channellen and \
len(s.split(None, 1)) == 1

def areChannels(s, chantypes='#&+!',channellen=50):
"""Like 'isChannel(x)' but for comma-separated list."""
chan = functools.partial(isChannel, chantypes=chantypes,
channellen=channellen)
return all(map(chan, s.split(',')))

def areReceivers(s, strictRfc=True, nicklen=None, chantypes='#&+!',
channellen=50):
"""Like 'isNick(x) or isChannel(x)' but for comma-separated list."""
nick = functools.partial(isNick, strictRfc=strictRfc, nicklen=nicklen)
chan = functools.partial(isChannel, chantypes=chantypes,
channellen=channellen)
return all(map(lambda x:nick(x) or chan(x), s.split(',')))

_patternCache = utils.structures.CacheDict(1000)
def _hostmaskPatternEqual(pattern, hostmask):
try:
Expand Down
12 changes: 12 additions & 0 deletions test/test_ircmsgs.py
Expand Up @@ -161,6 +161,18 @@ def testUnAction(self):
msg = ircmsgs.action('#foo', s)
self.assertEqual(ircmsgs.unAction(msg), s)

def testPrivmsg(self):
self.assertEqual(str(ircmsgs.privmsg('foo', 'bar')),
'PRIVMSG foo :bar\r\n')
self.assertEqual(str(ircmsgs.privmsg('foo,bar', 'baz')),
'PRIVMSG foo,bar :baz\r\n')

def testWhois(self):
self.assertEqual(str(ircmsgs.whois('foo')), 'WHOIS :foo\r\n')
self.assertEqual(str(ircmsgs.whois('foo,bar')), 'WHOIS :foo,bar\r\n')
self.assertRaises(AssertionError, ircmsgs.whois, '#foo')
self.assertRaises(AssertionError, ircmsgs.whois, 'foo,#foo')

def testBan(self):
channel = '#osu'
ban = '*!*@*.edu'
Expand Down

0 comments on commit 574d73c

Please sign in to comment.