Use CAPAB IDENTIFY-MSG to validate bot operators
authorStefan Hajnoczi <stefanha@gmail.com>
Sun, 21 Jun 2009 16:25:40 +0000 (17:25 +0100)
committerStefan Hajnoczi <stefanha@gmail.com>
Sun, 21 Jun 2009 16:25:40 +0000 (17:25 +0100)
cmds.py
gpxebot.py
utils.py

diff --git a/cmds.py b/cmds.py
index 7d0d164..ac85da5 100644 (file)
--- a/cmds.py
+++ b/cmds.py
@@ -93,7 +93,7 @@ def help_command(who, _, replyto, msg):
                 output.append(('PRIVMSG', (replyto, '%s    %s' % (name.ljust(max_width), cmd.__doc__))))
         return output
     output = do_help(commands)
-    if utils.nick_from_mask(who) in op_members:
+    if who.is_op():
         output.extend(do_help(op_commands))
     return output
 commands['help'] = help_command
@@ -155,7 +155,7 @@ def privmsg_command(who, target, replyto, words):
     '''Send a chat message (privmsg #etherboot Hello all!)'''
     if len(words) < 3:
         return
-    return ('PRIVMSG', (words[1], ' '.join(words[2:])))
+    return 'PRIVMSG', (words[1], ' '.join(words[2:]))
 op_commands['privmsg'] = privmsg_command
 
 def restart_command(who, target, replyto, words):
@@ -168,7 +168,7 @@ def do_command(who, target, replyto, words):
         return
     command = words[0].lower()
 
-    if utils.nick_from_mask(who) in op_members and command in op_commands:
+    if who.is_op() and command in op_commands:
         return op_commands[command](who, target, replyto, words)
     if command in commands:
         return commands[command](who, target, replyto, words)
index 20098c3..2e90e33 100755 (executable)
@@ -27,47 +27,65 @@ import utils
 NO_ARGS = -1
 
 handlers = {}
-restart  = False
+
+class Who(object):
+    def __init__(self, mask, is_identified=False):
+        self.mask = mask
+        self.nick = mask.split('!', 1)[0]
+        self.is_identified = is_identified
+
+    def is_op(self):
+        return self.is_identified and self.nick in cmds.op_members
 
 def autojoin():
     del handlers['376']
-    do_response(cmds.readrc_command())
+
+    # Receive '+' or '-' indicator with every message showing whether the
+    # sender is identified to nick services.  Only identified users may be bot
+    # operators.
+    cmd('CAPAB IDENTIFY-MSG')
+
+    return do_response(cmds.readrc_command())
 
 def ping(_, arg):
     cmd('PONG %s' % arg)
+    return False
 
 def do_response(response):
-    global restart
-
     # it should be in the format (command, args) 
     # or [(command, args), (command, args)...]
     # args is a tuple
     if not response:
-        return
+        return False
     if isinstance(response, list):
-        [do_response(r) for r in response]
-        return
+        return any(do_response(r) for r in response)
+    print response
     (command, args) = response
     if command == 'PRIVMSG':
         pmsg(*args)
     elif command == 'CMD':
         cmd(*args)
-    elif command == 'RESTART':
-        restart = True
+    return command == 'RESTART'
 
 def privmsg(_, target, msg):
-    utils.do_log(cmds.logs, target, who, msg)
+    # Establish identify of sender from CAPAB IDENTIFY-MSG info
+    is_identified = msg.startswith('+')
+    if msg.startswith('-') or msg.startswith('+'):
+        msg = msg[1:]
+    who = Who(mask, is_identified)
+
+    utils.do_log(cmds.logs, target, who.nick, msg)
 
     words = msg.split()
     if target.startswith('#'):
         replyto = target
         if not config.NICK in words[0]:
-            return
+            return False
         words.pop(0)
     elif target == config.NICK:
-        replyto = utils.nick_from_mask(who)
+        replyto = who.nick
 
-    do_response(cmds.do_command(who, target, replyto, words))
+    return do_response(cmds.do_command(who, target, replyto, words))
 
 def add_handler(command, handler, nargs):
     handlers[command] = (handler, nargs)
@@ -77,24 +95,26 @@ def cmd(msg):
     sock.sendall('%s\r\n' % msg)
 
 def pmsg(target, msg):
-    utils.do_log(cmds.logs, target, config.NICK, msg)
-    cmd('PRIVMSG %s :%s' % (target, msg))
+    if target:
+        utils.do_log(cmds.logs, target, config.NICK, msg)
+        cmd('PRIVMSG %s :%s' % (target, msg))
 
 def dispatch(args):
     command = args[0]
     if command in handlers:
         h = handlers[command]
         if h[1] == NO_ARGS:
-            h[0]()
+            return h[0]()
         elif len(args) == h[1]:
-            h[0](*args)
+            return h[0](*args)
+    return False
 
 def parse(line):
     if line[0] == ':':
-        who, line = line.split(None, 1)
-        who = who[1:]
+        mask, line = line[1:].split(None, 1)
     else:
-        who = None
+        mask = None
+
     args = []
     while line and line[0] != ':':
         fields = line.split(None, 1)
@@ -103,11 +123,9 @@ def parse(line):
         arg, line = fields
         args.append(arg)
     if line:
-        if line[0] == ':':
-            args.append(line[1:])
-        else:
-            args.append(line)
-    return who, args
+        args.append(line[1:])
+
+    return mask, args
 
 add_handler('376', autojoin, NO_ARGS)
 add_handler('PING', ping, 2)
@@ -136,6 +154,7 @@ except OSError:
 # read/write will not block on Linux.
 cmdfifo = open(config.FIFO, 'r+')
 
+restart = False
 sockbuf = ''
 while not restart:
     rlist, _, xlist = select.select([sock, cmdfifo], [], [sock])
@@ -146,17 +165,17 @@ while not restart:
             break
         sockbuf += r
 
-        while sockbuf.find('\r\n') != -1:
+        while '\r\n' in sockbuf:
             line, sockbuf = sockbuf.split('\r\n', 1)
             if not line:
                 continue
             utils.dbg('READ ' + line)
-            who, args = parse(line)
-            dispatch(args)
+            mask, args = parse(line)
+            restart = dispatch(args)
 
     if cmdfifo in rlist:
         words = cmdfifo.readline().strip().split()
-        do_response(cmds.do_command(config.NICK, config.NICK, config.NICK, words))
+        restart = do_response(cmds.do_command(Who(config.NICK, True), config.NICK, config.NICK, words)) or restart
 
 cmdfifo.close()
 
index 2f83975..f25663b 100644 (file)
--- a/utils.py
+++ b/utils.py
@@ -9,9 +9,6 @@ def dbg(msg):
     if config.DEBUG:
         sys.stderr.write(msg + '\n')
 
-def nick_from_mask(mask):
-    return (mask.find('!') > -1 and mask.split('!', 1)[0]) or mask
-
 def email_log(to_address, channel, logfile, aliases):
     email_addresses = []
     unrecognized = []
@@ -49,4 +46,4 @@ def make_log(channel):
 def do_log(logs, target, nick, msg):
     if target in logs:
         (name, f) = logs[target]
-        f.write('%s <%s> %s\n' % (get_time(), nick_from_mask(nick), msg))
+        f.write('%s <%s> %s\n' % (get_time(), nick, msg))