From 871607614a6e7418655b53d9004487d437e43ef6 Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sat, 2 Aug 2014 12:30:24 +0200 Subject: [PATCH 1/8] Implement account-notify, extended-join and WHOX Also integrate it with NickAuth. Closes #778. --- plugins/NickAuth/plugin.py | 60 +++++++++++++++++++++++++++++ src/irclib.py | 79 +++++++++++++++++++++++--------------- src/ircmsgs.py | 4 +- 3 files changed, 111 insertions(+), 32 deletions(-) diff --git a/plugins/NickAuth/plugin.py b/plugins/NickAuth/plugin.py index 60ac7075b..2dcb56c48 100644 --- a/plugins/NickAuth/plugin.py +++ b/plugins/NickAuth/plugin.py @@ -167,6 +167,66 @@ class NickAuth(callbacks.Plugin): else: irc.error(_('No user has this nick on this network.')) + def doAccount(self, irc, msg): + account = msg.args[0] + user = ircdb.users.getUserFromNick(irc.network, account) + + if not user: + try: + user = ircdb.users.getUser(msg.prefix) + except KeyError: + user = None + + if user: + if account == '*': + user.clearAuth() + else: + user.addAuth(msg.prefix) + ircdb.users.setUser(user, flush=False) + + + def doJoin(self, irc, msg): + # extended-join is not supported + if len(msg.args) < 2: + return + + account = msg.args[1] + user = ircdb.users.getUserFromNick(irc.network, account) + + if not user: + try: + user = ircdb.users.getUser(msg.prefix) + except KeyError: + user = None + + if user: + if account == '*': + user.clearAuth() + else: + user.addAuth(msg.prefix) + ircdb.users.setUser(user, flush=False) + + def do354(self, irc, msg): + (_, ident, host, nick, account) = msg.args + prefix = '%s!%s@%s' % (nick, ident, host) + user = ircdb.users.getUserFromNick(irc.network, account) + + if not user: + try: + user = ircdb.users.getUser(prefix) + except KeyError: + user = None + + if user: + #if account == '0': + # user.clearAuth() + #else: + # user.addAuth(prefix) + # ircdb.users.setUser(user, flush=False) + if account != '0': + user.addAuth(prefix) + ircdb.users.setUser(user, flush=False) + Class = NickAuth diff --git a/src/irclib.py b/src/irclib.py index baeb93137..1b12d1c1a 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -481,9 +481,10 @@ class IrcState(IrcCommandDispatcher): else: self.supported[arg] = None - def do352(self, irc, msg): + def do354(self, irc, msg): # WHO reply. - (nick, user, host) = (msg.args[5], msg.args[2], msg.args[3]) + + (_, user, host, nick, __) = msg.args hostmask = '%s!%s@%s' % (nick, user, host) self.nicksToHostmasks[nick] = hostmask @@ -927,25 +928,39 @@ class Irc(IrcCommandDispatcher): if self.zombie: self.driver.die() self._reallyDie() - else: - if self.sasl_password: - if not self.sasl_username: - log.warning('%s: SASL username is not set, unable to ' - 'identify.', self.network) - else: - log.debug("%s: Requesting capability 'sasl'.", - self.network) - self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', 'sasl'))) - if self.password: - log.info('%s: Queuing PASS command, not logging the password.', - self.network) - self.queueMsg(ircmsgs.password(self.password)) - log.debug('%s: Queuing NICK command, nick is %s.', - self.network, self.nick) - self.queueMsg(ircmsgs.nick(self.nick)) - log.debug('%s: Queuing USER command, ident is %s, user is %s.', - self.network, self.ident, self.user) - self.queueMsg(ircmsgs.user(self.ident, self.user)) + + return + + caps = ['account-notify', 'extended-join'] + + if self.sasl_password: + if not self.sasl_username: + log.warning('%s: SASL username is not set, unable to ' + 'identify.', self.network) + else: + caps.append('sasl') + + log.debug('%s: Requesting capabilities: %s', + self.network, ' '.join(caps)) + + for cap in caps: + self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap))) + + if self.password: + log.info('%s: Queuing PASS command, not logging the password.', + self.network) + + self.queueMsg(ircmsgs.password(self.password)) + + log.debug('%s: Queuing NICK command, nick is %s.', + self.network, self.nick) + + self.queueMsg(ircmsgs.nick(self.nick)) + + log.debug('%s: Queuing USER command, ident is %s, user is %s.', + self.network, self.ident, self.user) + + self.queueMsg(ircmsgs.user(self.ident, self.user)) def doAuthenticate(self, msg): if msg.args[0] == '+': @@ -960,16 +975,20 @@ class Irc(IrcCommandDispatcher): self.queueMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=(authstring,))) def doCap(self, msg): - if msg.args[2] == 'sasl': + for cap in msg.args[2].split(' '): if msg.args[1] == 'ACK': - log.debug("%s: Server acknowledged 'sasl' capability", - self.network) - self.queueMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', - args=('PLAIN',))) + log.info('%s: Server acknowledged %r capability', + self.network, cap) + + if cap == 'sasl': + self.queueMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', + args=('PLAIN',))) elif msg.args[1] == 'NAK': - log.warning("%s: Server refused 'sasl' capability", - self.network) - self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('END',))) + log.warning('%s: Server refused %r capability', + self.network, cap) + + if cap == 'sasl': + self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('END',))) def do903(self, msg): log.info('%s: SASL authentication successful', self.network) @@ -1068,7 +1087,7 @@ class Irc(IrcCommandDispatcher): def doJoin(self, msg): if msg.nick == self.nick: channel = msg.args[0] - self.queueMsg(ircmsgs.who(channel)) # Ends with 315. + self.queueMsg(ircmsgs.who(channel, args=('%uhna',))) # Ends with 315. self.queueMsg(ircmsgs.mode(channel)) # Ends with 329. for channel in msg.args[0].split(','): self.queueMsg(ircmsgs.mode(channel, '+b')) diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 9df9576ae..54ab272b3 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -736,7 +736,7 @@ def user(ident, user, prefix='', msg=None): return IrcMsg(prefix=prefix, command='USER', args=(ident, '0', '*', user), msg=msg) -def who(hostmaskOrChannel, prefix='', msg=None): +def who(hostmaskOrChannel, prefix='', msg=None, args=()): """Returns a WHO for the hostmask or channel hostmaskOrChannel.""" if conf.supybot.protocols.irc.strictRfc(): assert isChannel(hostmaskOrChannel) or \ @@ -744,7 +744,7 @@ def who(hostmaskOrChannel, prefix='', msg=None): if msg and not prefix: prefix = msg.prefix return IrcMsg(prefix=prefix, command='WHO', - args=(hostmaskOrChannel,), msg=msg) + args=(hostmaskOrChannel,) + args, msg=msg) def _whois(COMMAND, nick, mask='', prefix='', msg=None): """Returns a WHOIS for nick.""" From e185416987621fb8212578691565f690d638e4d8 Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sat, 2 Aug 2014 13:45:41 +0200 Subject: [PATCH 2/8] Fix tests --- src/irclib.py | 33 ++++++++++++++++----------------- test/test_irclib.py | 6 +++++- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/irclib.py b/src/irclib.py index 1b12d1c1a..1d358a3d0 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -931,20 +931,7 @@ class Irc(IrcCommandDispatcher): return - caps = ['account-notify', 'extended-join'] - - if self.sasl_password: - if not self.sasl_username: - log.warning('%s: SASL username is not set, unable to ' - 'identify.', self.network) - else: - caps.append('sasl') - - log.debug('%s: Requesting capabilities: %s', - self.network, ' '.join(caps)) - - for cap in caps: - self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap))) + self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('LS',))) if self.password: log.info('%s: Queuing PASS command, not logging the password.', @@ -975,16 +962,28 @@ class Irc(IrcCommandDispatcher): self.queueMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=(authstring,))) def doCap(self, msg): + caps = ['account-notify', 'extended-join'] + + if self.sasl_password: + if self.sasl_username: + caps.append('sasl') + else: + log.warning('%s: SASL username is not set, unable to ' + 'identify.', self.network) + for cap in msg.args[2].split(' '): - if msg.args[1] == 'ACK': - log.info('%s: Server acknowledged %r capability', + if msg.args[1] == 'LS' and cap in caps: + log.debug('%s: Requesting capability %r', cap) + self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap))) + elif msg.args[1] == 'ACK': + log.info('%s: Server acknowledged capability %r', self.network, cap) if cap == 'sasl': self.queueMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('PLAIN',))) elif msg.args[1] == 'NAK': - log.warning('%s: Server refused %r capability', + log.warning('%s: Server refused capability %r', self.network, cap) if cap == 'sasl': diff --git a/test/test_irclib.py b/test/test_irclib.py index 190a46557..ebb9fd896 100644 --- a/test/test_irclib.py +++ b/test/test_irclib.py @@ -382,6 +382,8 @@ class IrcTestCase(SupyTestCase): m = self.irc.takeMsg() self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m) m = self.irc.takeMsg() + self.failUnless(m.command == 'CAP', 'Expected NICK, got %r.' % m) + m = self.irc.takeMsg() self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m) def testPingResponse(self): @@ -478,7 +480,9 @@ class IrcCallbackTestCase(SupyTestCase): conf.supybot.nick.setValue(nick) user = 'user any user' conf.supybot.user.setValue(user) - expected = [ircmsgs.nick(nick), ircmsgs.user('limnoria', user)] + expected = [ircmsgs.nick(nick), + ircmsgs.IrcMsg(command='CAP', args=('LS',)), + ircmsgs.user('limnoria', user)] irc = irclib.Irc('test') msgs = [irc.takeMsg()] while msgs[-1] != None: From 8729fee01596b072efb7496d28159e6eaaf851cd Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sat, 2 Aug 2014 13:47:19 +0200 Subject: [PATCH 3/8] Fix typo --- test/test_irclib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_irclib.py b/test/test_irclib.py index ebb9fd896..849cf9f32 100644 --- a/test/test_irclib.py +++ b/test/test_irclib.py @@ -382,7 +382,7 @@ class IrcTestCase(SupyTestCase): m = self.irc.takeMsg() self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m) m = self.irc.takeMsg() - self.failUnless(m.command == 'CAP', 'Expected NICK, got %r.' % m) + self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) m = self.irc.takeMsg() self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m) From dd5556ea9959d6235b0259c5441923a828ca6eca Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sat, 2 Aug 2014 14:11:49 +0200 Subject: [PATCH 4/8] Fix previous commit --- src/irclib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/irclib.py b/src/irclib.py index 1d358a3d0..0fbd29161 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -973,7 +973,7 @@ class Irc(IrcCommandDispatcher): for cap in msg.args[2].split(' '): if msg.args[1] == 'LS' and cap in caps: - log.debug('%s: Requesting capability %r', cap) + log.debug('%s: Requesting capability %r', self.network, cap) self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap))) elif msg.args[1] == 'ACK': log.info('%s: Server acknowledged capability %r', From ddbfad50862e4d6f9c539f2860578a8a90efac0f Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sun, 3 Aug 2014 13:58:49 +0200 Subject: [PATCH 5/8] Add do352 back for IRCds which do not support WHOX --- src/irclib.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/irclib.py b/src/irclib.py index 0fbd29161..b2c5378f6 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -481,16 +481,23 @@ class IrcState(IrcCommandDispatcher): else: self.supported[arg] = None - def do354(self, irc, msg): + def do352(self, irc, msg): # WHO reply. - (_, user, host, nick, __) = msg.args + (nick, user, host) = (msg.args[5], msg.args[2], msg.args[3]) + hostmask = '%s!%s@%s' % (nick, user, host) + self.nicksToHostmasks[nick] = hostmask + + def do354(self, irc, msg): + # WHOX reply. + + (__, user, host, nick, ___) = msg.args hostmask = '%s!%s@%s' % (nick, user, host) self.nicksToHostmasks[nick] = hostmask def do353(self, irc, msg): # NAMES reply. - (_, type, channel, names) = msg.args + (__, type, channel, names) = msg.args if channel not in self.channels: self.channels[channel] = ChannelState() c = self.channels[channel] From d1ecfe3153a6af9c97e39abb4130214c9813e21c Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sun, 3 Aug 2014 13:59:35 +0200 Subject: [PATCH 6/8] Fix code style --- plugins/NickAuth/plugin.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/NickAuth/plugin.py b/plugins/NickAuth/plugin.py index 2dcb56c48..a4a0e603b 100644 --- a/plugins/NickAuth/plugin.py +++ b/plugins/NickAuth/plugin.py @@ -186,7 +186,6 @@ class NickAuth(callbacks.Plugin): def doJoin(self, irc, msg): - # extended-join is not supported if len(msg.args) < 2: return @@ -207,7 +206,7 @@ class NickAuth(callbacks.Plugin): ircdb.users.setUser(user, flush=False) def do354(self, irc, msg): - (_, ident, host, nick, account) = msg.args + (__, ident, host, nick, account) = msg.args prefix = '%s!%s@%s' % (nick, ident, host) user = ircdb.users.getUserFromNick(irc.network, account) @@ -218,11 +217,6 @@ class NickAuth(callbacks.Plugin): user = None if user: - #if account == '0': - # user.clearAuth() - #else: - # user.addAuth(prefix) - # ircdb.users.setUser(user, flush=False) if account != '0': user.addAuth(prefix) ircdb.users.setUser(user, flush=False) From cb4f5a0a39651d58e5e673a10ea02099fc6bc55b Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sun, 3 Aug 2014 14:00:36 +0200 Subject: [PATCH 7/8] Re-add removed comment --- plugins/NickAuth/plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/NickAuth/plugin.py b/plugins/NickAuth/plugin.py index a4a0e603b..ed8e6408a 100644 --- a/plugins/NickAuth/plugin.py +++ b/plugins/NickAuth/plugin.py @@ -187,6 +187,7 @@ class NickAuth(callbacks.Plugin): def doJoin(self, irc, msg): if len(msg.args) < 2: + # extended-join is not supported return account = msg.args[1] From 7d09f501656865794ac835f87f34e8de9a0893d9 Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sun, 3 Aug 2014 14:35:53 +0200 Subject: [PATCH 8/8] NickAuth: Don't clear auth when joining unidentified --- plugins/NickAuth/plugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/NickAuth/plugin.py b/plugins/NickAuth/plugin.py index ed8e6408a..59eaff8d8 100644 --- a/plugins/NickAuth/plugin.py +++ b/plugins/NickAuth/plugin.py @@ -200,9 +200,7 @@ class NickAuth(callbacks.Plugin): user = None if user: - if account == '*': - user.clearAuth() - else: + if account != '*': user.addAuth(msg.prefix) ircdb.users.setUser(user, flush=False)