Merge pull request #1122 from nyuszika7h/fix-cap-neg

Fix capability negotiation
This commit is contained in:
Valentin Lorentz 2015-06-12 18:34:49 +02:00
commit c8ce6230ca
2 changed files with 45 additions and 43 deletions

View File

@ -961,6 +961,11 @@ class Irc(IrcCommandDispatcher):
return return
# Notes:
# * using sendMsg instead of queueMsg because these messages cannot
# be throttled.
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('LS', '302')))
if self.password: if self.password:
log.info('%s: Queuing PASS command, not logging the password.', log.info('%s: Queuing PASS command, not logging the password.',
self.network) self.network)
@ -987,12 +992,8 @@ class Irc(IrcCommandDispatcher):
elif self.sasl_username and self.sasl_password: elif self.sasl_username and self.sasl_password:
self.sasl = 'plain' self.sasl = 'plain'
# Notes: if self.sasl:
# * not sending caps at once, because the server has no granularity self.REQUEST_CAPABILITIES.add('sasl')
# in telling between ACK and NAK
# * using sendMsg instead of queueMsg because these messages cannot
# be throttled.
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('LS', '302')))
def doAuthenticate(self, msg): def doAuthenticate(self, msg):
if len(msg.args) == 1 and msg.args[0] == '+': if len(msg.args) == 1 and msg.args[0] == '+':
@ -1034,28 +1035,24 @@ class Irc(IrcCommandDispatcher):
if len(msg.args) != 3: if len(msg.args) != 3:
log.warning('Bad CAP ACK from server: %r', msg) log.warning('Bad CAP ACK from server: %r', msg)
return return
for cap in msg.args[2].split(' '): caps = msg.args[2]
log.info('%s: Server acknowledged capability %r', log.info('%s: Server acknowledged capabilities: %s',
self.network, cap) self.network, caps)
self.state.capabilities_ack.add(cap) self.state.capabilities_ack.update(caps)
if cap == 'sasl': if 'sasl' in caps:
self.sendMsg(ircmsgs.IrcMsg( self.sendMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=(self.sasl.upper(),)))
command='AUTHENTICATE', else:
args=(self.sasl.upper(),))) self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('END',)))
def doCapNak(self, msg): def doCapNak(self, msg):
if len(msg.args) != 3: if len(msg.args) != 3:
log.warning('Bad CAP NAK from server: %r', msg) log.warning('Bad CAP NAK from server: %r', msg)
return return
for cap in msg.args[2].split(' '): caps = msg.args[2]
self.state.capabilities_nak.add(cap) self.state.capabilities_nak.update(caps)
log.warning('%s: Server refused capability %r', log.warning('%s: Server refused capabilities: %s',
self.network, cap) self.network, caps)
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('END',)))
if cap == 'sasl':
self.sendMsg(ircmsgs.IrcMsg(
command='CAP',
args=('END',)))
def _addCapabilities(self, capstring): def _addCapabilities(self, capstring):
for item in capstring.split(): for item in capstring.split():
while item.startswith(('=', '~')): while item.startswith(('=', '~')):
@ -1076,14 +1073,11 @@ class Irc(IrcCommandDispatcher):
self._addCapabilities(msg.args[2]) self._addCapabilities(msg.args[2])
common_supported_capabilities = set(self.state.capabilities_ls) & \ common_supported_capabilities = set(self.state.capabilities_ls) & \
self.REQUEST_CAPABILITIES self.REQUEST_CAPABILITIES
for cap in common_supported_capabilities: # NOTE: Capabilities are requested in alphabetic order, because
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap))) # sets are unordered, and their "order" is nondeterministic.
# This is needed for the tests.
if 'sasl' in self.state.capabilities_ls and self.sasl: self.sendMsg(ircmsgs.IrcMsg(command='CAP',
# TODO: use the value of self.state.capabilities_ls['sasl'] args=('REQ', ' '.join(sorted(common_supported_capabilities)))))
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', 'sasl')))
else:
self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('END',)))
else: else:
log.warning('Bad CAP LS from server: %r', msg) log.warning('Bad CAP LS from server: %r', msg)
return return

View File

@ -378,32 +378,40 @@ class IrcStateTestCase(SupyTestCase):
class IrcTestCase(SupyTestCase): class IrcTestCase(SupyTestCase):
def setUp(self): def setUp(self):
self.irc = irclib.Irc('test') self.irc = irclib.Irc('test')
#m = self.irc.takeMsg() #m = self.irc.takeMsg()
#self.failUnless(m.command == 'PASS', 'Expected PASS, got %r.' % m) #self.failUnless(m.command == 'PASS', 'Expected PASS, got %r.' % m)
m = self.irc.takeMsg()
self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m)
m = self.irc.takeMsg()
self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m)
m = self.irc.takeMsg() m = self.irc.takeMsg()
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
self.failUnless(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m) self.failUnless(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m)
m = self.irc.takeMsg()
self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m)
m = self.irc.takeMsg()
self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m)
# TODO # TODO
self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
args=('*', 'LS', '*', 'account-tag multi-prefix'))) args=('*', 'LS', '*', 'account-tag multi-prefix')))
self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
args=('*', 'LS', 'extended-join'))) args=('*', 'LS', 'extended-join')))
m = self.irc.takeMsg() m = self.irc.takeMsg()
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
self.assertEqual(m.args[0], 'REQ', m) self.assertEqual(m.args[0], 'REQ', m)
m = self.irc.takeMsg() # NOTE: Capabilities are requested in alphabetic order, because
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) # sets are unordered, and their "order" is nondeterministic.
self.assertEqual(m.args[0], 'REQ', m) self.assertEqual(m.args[1], 'account-tag extended-join multi-prefix')
m = self.irc.takeMsg()
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
self.assertEqual(m.args[0], 'REQ', m) args=('*', 'ACK', 'account-tag multi-prefix extended-join')))
m = self.irc.takeMsg() m = self.irc.takeMsg()
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
self.assertEqual(m.args, ('END',), m) self.assertEqual(m.args, ('END',), m)
m = self.irc.takeMsg() m = self.irc.takeMsg()
self.failUnless(m is None, m) self.failUnless(m is None, m)
@ -501,9 +509,9 @@ class IrcCallbackTestCase(SupyTestCase):
user = 'user any user' user = 'user any user'
conf.supybot.user.setValue(user) conf.supybot.user.setValue(user)
expected = [ expected = [
ircmsgs.IrcMsg(command='CAP', args=('LS', '302')),
ircmsgs.nick(nick), ircmsgs.nick(nick),
ircmsgs.user('limnoria', user), ircmsgs.user('limnoria', user),
ircmsgs.IrcMsg(command='CAP', args=('LS', '302')),
] ]
irc = irclib.Irc('test') irc = irclib.Irc('test')
msgs = [irc.takeMsg()] msgs = [irc.takeMsg()]
@ -518,7 +526,7 @@ class IrcCallbackTestCase(SupyTestCase):
while msgs[-1] is not None: while msgs[-1] is not None:
msgs.append(irc.takeMsg()) msgs.append(irc.takeMsg())
msgs.pop() msgs.pop()
expected.insert(0, ircmsgs.password(password)) expected.insert(1, ircmsgs.password(password))
self.assertEqual(msgs, expected) self.assertEqual(msgs, expected)
finally: finally:
conf.supybot.nick.setValue(originalNick) conf.supybot.nick.setValue(originalNick)