From fd9bd2f409191f429c62b04555eb49eb2061f9aa Mon Sep 17 00:00:00 2001
From: FurryHead <furryhead14@yahoo.com>
Date: Sat, 18 Jun 2011 20:47:14 +0000
Subject: [PATCH] Misc fixes to core; Auth plugin working (testing)

---
 guppy.py                |  24 +--
 irc.py                  | 361 ++++++++++++++++++----------------------
 plugins/auth.py         | 103 ++++++------
 plugins/channeltools.py |   1 -
 plugins/ddg.py          |   1 -
 plugins/die.py          |   1 -
 plugins/dns.py          |   1 -
 plugins/dynacode.py     |   5 +-
 plugins/games.py        |   3 +-
 plugins/info.py         |   2 +-
 plugins/ping.py         |   1 -
 plugins/printer.py      |  20 ++-
 plugins/rpn.py          |   1 -
 plugins/smack.py        |   1 -
 plugins/wikipedia.py    |  10 +-
 15 files changed, 252 insertions(+), 283 deletions(-)

diff --git a/guppy.py b/guppy.py
index 72e0f50..f750b10 100644
--- a/guppy.py
+++ b/guppy.py
@@ -20,13 +20,13 @@ import argparse
 import sys
 import os
 import ConfigParser
+import asyncore
 import irc
-import threading, time
 
 class main(object):
     def __init__(self):
         # Constant.
-        self.version = "0.4.0"
+        self.version = "0.4.1"
         print("  __ _ _   _ _ __  _ __  _   _ ")
         print(" / _` | | | | '_ \| '_ \| | | | http://repo.or.cz/w/guppy.git/summary")
         print("| (_| | |_| | |_) | |_) | |_| |")
@@ -66,6 +66,7 @@ class main(object):
         self.config.read(self.configpath)
         
     def start(self):
+        self.clients = []
         for section in self.config.sections():
             server_config = {}
             server_config["network"] = section
@@ -73,21 +74,14 @@ class main(object):
             for item,value in self.config.items(section):
                 server_config[item] = value
             
-            # since IRC is a subclass of threading.Thread, using 
-            # .start() on it will automatically run it in a new thread.
-            conn = irc.IRC(server_config)
-            conn.daemon = True
-            conn.start()
+            self.clients.append(irc.IRC(server_config))
         
-        while threading.activeCount() > 1:
-            time.sleep(15)
+        asyncore.loop()
     
     def stop(self):
-        for client in threading.enumerate():
-            if client.__class__.__name__ != "_MainThread":
-                client.doQuit("Keyboard interrupt.")
-                time.sleep(1)
-                client.pluginManager.unloadAllPlugins()
+        for client in self.clients:
+            client.doQuit("Keyboard interrupt.")
+            client.pluginManager.unloadAllPlugins()
             
     def my_raw(self,prompt="", default=""):
         ret = raw_input("%s [%s]: "%(prompt,default))
@@ -127,6 +121,7 @@ class main(object):
             self.set(currentNetwork,'ns_name',"NickServ username (if there is none, press ENTER)")
             self.set(currentNetwork,'ns_pwd',"NickServ password (if there is none, press ENTER)")
             self.set(currentNetwork,'port',"Port", "6667")
+            self.set(currentNetwork,'use_ssl','Use ssl (yes/no)', 'no')
             self.set(currentNetwork,"comchar","My command char", "-")
             self.set(currentNetwork,"plugins","Plugins to load on startup (comma-separated, no spaces)", "auth,printer,pluginloader")
             
@@ -157,4 +152,3 @@ if __name__ == "__main__":
         print("Shutting down all connections...")
         c.stop()
         print("Quitting!")
-        time.sleep(2) # to let connections close
diff --git a/irc.py b/irc.py
index abd701a..778932c 100644
--- a/irc.py
+++ b/irc.py
@@ -1,4 +1,4 @@
-import socket, os, threading, re
+import socket, os, threading, re, asynchat, traceback, ssl
 import plugins
 
 class User(str):
@@ -33,6 +33,9 @@ class PluginManager(object):
                 for command in handler_commands:
                     self.handlers[event][command.lower()] = handler_func
         else:
+            if self.handlers.get(event, None) is None:
+                self.handlers[event] = []
+                
             self.handlers[event].append(handler_func)
     
     def unhandle(self, event, handler_func, handler_commands=None):
@@ -54,23 +57,17 @@ class PluginManager(object):
         
     def event(self, eventName, *args):
         eventName = eventName.lower()
+        if self.handlers.get(eventName, None) is None: return
+        
         if eventName == "command":
             handler = self.handlers[eventName].get(args[2].lower(), None)
             if handler is None:
                 return #no such command
                 
-            try:
-                handler(*args)
-            except Exception, e:
-                s = handler.__self__
-                self.prnt("Plugin "+s.__class__.__name__+" "+e.__class__.__name__+": "+e.__str__())
+            handler(*args)
         else:
             for handler in self.handlers[eventName]:
-                try:
-                    handler(*args)
-                except Exception, e:
-                    s = handler.__self__
-                    self.prnt("Plugin "+s.__class__.__name__+" "+e.__class__.__name__+": "+e.__str__())
+                handler(*args)
     
     def loadedPlugin(self, pluginName):
         return pluginName.lower() in self.plugins
@@ -82,27 +79,11 @@ class PluginManager(object):
         plugins.refresh()
         if not self.loadedPlugin(pluginName.lower()):
             if not self.pluginExists(pluginName.lower()):
-                err = "No such plugin."
-                return err
+                return
             
-            pClass = None
-            try:
-                pClass = plugins.getPlugin(pluginName.lower())
-            except Exception, e:
-                err = "Cannot load plugin "+pluginName+" ("+e.__class__.__name__+": "+e.__str__()+")"
-                self.prnt(err)
-                return err
+            pClass = plugins.getPlugin(pluginName.lower())
             
-            try:
-                self.plugins[pluginName.lower()] = pClass(self.server)
-            except Exception, e:
-                err = "Could not initialize "+pluginName+" ("+e.__class__.__name__+": "+e.__str__()+")"
-                self.prnt(err)
-                return err
-                
-        else:
-            err = "Module has already been loaded."
-            return err
+            self.plugins[pluginName.lower()] = pClass(self.server)
     
     def getPlugin(self, pluginName): 
         try:
@@ -131,7 +112,7 @@ class PluginManager(object):
                     newList = {}
                     for cmd,func in eList.items():
                         if func.__self__ == inst:
-                            destructor = getattr(inst, "__del__", None)
+                            destructor = getattr(inst, "destroy", None)
                             if destructor is not None:
                                 destructor()
                         else:
@@ -141,7 +122,7 @@ class PluginManager(object):
                     newList = []
                     for func in eList:
                         if func.__self__ == inst:
-                            destructor = getattr(inst, "__del__", None)
+                            destructor = getattr(inst, "destroy", None)
                             if destructor is not None:
                                 destructor()
                         else:
@@ -172,9 +153,9 @@ class PluginManager(object):
             
         return errs
 
-class IRC(threading.Thread):
+class IRC(asynchat.async_chat):
     def __init__(self, config):
-        super(IRC, self).__init__()
+        asynchat.async_chat.__init__(self)
         self.pluginManager = PluginManager(self)
         self.config = config
         self.handle = self.pluginManager.handle
@@ -183,186 +164,176 @@ class IRC(threading.Thread):
         self.plugins = self.pluginManager.plugins
         self.getPlugin = self.pluginManager.getPlugin
         errs = self.pluginManager.loadAllPlugins()
-        if errs:
-            for k,v in errs.items():
-                self.prnt("Plugin "+k+" error loading: "+v)
                 
-        self.running = False
         self.userlist = { }
-        
-    def _readline(self):
-        ret = []
-        while True:
-            c = self.conn.recv(1)
-            if c == '\n':
-                break
-            else:
-                if c != '\r':
-                    ret.append(c)
-        return "".join(ret)
+        self.data = ""
+        self.set_terminator("\r\n")
            
-    def run(self):
-        self.conn = socket.socket()
-        self.conn.settimeout(60)
-        try:
-            self.conn.connect((self.config["host"], int(self.config["port"])))
-            self.goodstart = True
-        except socket.error:
-            self.prnt("Cannot connect to server.")
-            self.goodstart = False
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        if self.config["use_ssl"].lower().startswith("y"):
+            try:
+                self.ssl_sock = ssl.wrap_socket(self.sock)
+                self.ssl_sock.connect((self.config["host"], int(self.config["port"])))
+                self.set_socket(self.ssl_sock)
+            except ssl.SSLError, error:
+                self.prnt('SSL Error connecting to server. (Are you using the right port?)')
+                raise ssl.SSLError(error)
+            except socket.error, error:
+                print('There was an error connecting to %s' % address)
+                return
+        else:
+            try:
+                self.sock.connect((self.config["host"], int(self.config["port"])))
+            except socket.error, error:
+                print('There was an error connecting to %s' % address)
+                return
+            self.set_socket(self.sock)
+
+    def _getData(self):
+        ret = self.data
+        self.data = ''
+        return ret
+    
+    def found_terminator(self):
+        data = self._getData()
         
-        if self.goodstart:
-            self.config["ident"] = ident = self.config["ident"] if self.config["ident"] != "" else self.config["nickname"]
-            self.sendLine("USER "+ident+" * * *")
-            self.sendLine("NICK "+self.config["nickname"])
+        words = data.split(" ")
         
-        self.running = self.goodstart
-        while self.running:
-            try:
-                data = self._readline()
-            except socket.timeout:
-                self.sendLine("PING BACK")
-                try:
-                    data = self._readline()
-                    continue
-                except socket.timeout:
-                    raise IOError("Connection was broken.")
-            
-            if not self.running: continue
+        if words[0] == "PING":
+            # The server pinged us, we need to pong or we'll be disconnected
+            # (make sure to send whatever follows the PING, in case they send a random hash)
+            self.sendLine("PONG " + data[5:])
+            return 
+        
+        # Takes the ':Nick!Ident@Host' chunk and assigns (nick,ident,host) to user
+        user = None
+        if words[0].find("!") != -1:
+            user = User(words[0][words[0].find(":") + 1:words[0].find('!')])
+            user.ident = words[0][words[0].find('!') + 1:words[0].find('@')]
+            user.host = words[0][words[0].find('@') + 1:]
+        else:
+            #it's our nick, in the format ":NickName"
+            user = User(words[0][words[0].find(":") + 1:])
             
-            if data == "":
-                self.prnt("Connection lost to server.")
-                self.running = False
-                continue
+        if words[1] == "433":
+            #There was a nick collision
+            self.config["nickname"] = self.config["nickname"] + "_"
+            self.sendLine("NICK "+self.config["nickname"])
+        
+        elif words[1] == "422" or words[1] == "376":
+            # We successfully logged in, do post-login procedures
+            if self.config["ns_name"] != "" and self.config["ns_pass"] != "":
+                self.doMessage("NickServ", "IDENTIFY "+self.config["ns_name"]+" "+self.config["ns_pass"])
             
-            words = data.split(" ")
+            self.pluginManager.event("connect", self.config["network"])
             
-            if words[0] == "PING":
-                # The server pinged us, we need to pong or we'll be disconnected
-                # (make sure to send whatever follows the PING, in case they send a random hash)
-                self.sendLine("PONG " + data[5:])
-                continue
-            elif words[0] == "ERROR":
-                #there was an error, we probably had a KeyboardInterrupt
-                self.prnt("Connection lost to server.")
-                self.running = False
-                continue
+            for chan in [k for k in self.config["channels"].split(",") if k != '']:
+                self.doJoin(chan)
+        
+        elif words[1] == "353":
+            #user list, if it's large, we only got part of it.
+            words = [k for k in data.replace(" =", "").split(" ") if k != '']
+            if self.userlist.get(words[3], None) is None:
+                self.userlist[words[3]] = []
             
-            # Takes the ':Nick!Ident@Host' chunk and assigns (nick,ident,host) to user
-            user = None
-            if words[0].find("!") != -1:
-                user = User(words[0][words[0].find(":") + 1:words[0].find('!')])
-                user.ident = words[0][words[0].find('!') + 1:words[0].find('@')]
-                user.host = words[0][words[0].find('@') + 1:]
+            self.userlist[words[3]] += [u.strip(":") for u in words[3:]]
+        
+        elif words[1] == "PRIVMSG":
+            # We got a message
+            channel = (words[2] if words[2] != self.config["nickname"] else user)
+            message = data[data.find(":", data.find(channel[(channel.find("-") == -1 and 1 or channel.find("-")):]))+1:]
+            if message.find("\x01ACTION") == 0:
+                # String was found, it's an action
+                self.pluginManager.event("action", channel, user, message[8:-1])
+            elif message.find(self.config["comchar"]) == 0:
+                # String was found, it's a command!
+                args = message.split(" ")
+                cmd = args[0][len(self.config["comchar"]):]
+                args.pop(0)
+                self.pluginManager.event("message", channel, user, message)
+                self.pluginManager.event("command", channel, user, cmd.lower(), args)
             else:
-                #it's our nick, in the format ":NickName"
-                user = User(words[0][words[0].find(":") + 1:])
-                
-            if words[1] == "433":
-                #There was a nick collision
-                self.config["nickname"] = self.config["nickname"] + "_"
-                self.sendLine("NICK "+self.config["nickname"])
-            
-            elif words[1] == "422" or words[1] == "376":
-                # We successfully logged in, do post-login procedures
-                if self.config["ns_name"] != "" and self.config["ns_pass"] != "":
-                    self.doMessage("NickServ", "IDENTIFY "+self.config["ns_name"]+" "+self.config["ns_pass"])
-                
-                self.pluginManager.event("connect", self.config["host"])
-                
-                for chan in [k for k in self.config["channels"].split(",") if k != '']:
-                    self.doJoin(chan)
-            
-            elif words[1] == "353":
-                #user list, if it's large, we only got part of it.
-                words = [k for k in data.replace(" =", "").split(" ") if k != '']
-                if self.userlist.get(words[3], None) is None:
-                    self.userlist[words[3]] = []
-                
-                userlist = [u.strip(":") for u in words[3:]]
-                self.userlist[words[3]] += userlist
-            
-            elif words[1] == "PRIVMSG":
-                # We got a message
-                channel = (words[2] if words[2] != self.config["nickname"] else user)
-                message = data[data.find(":", data.find(channel[(channel.find("-") == -1 and 1 or channel.find("-")):]))+1:]
-                if message.find("\x01ACTION") == 0:
-                    # String was found, it's an action
-                    self.pluginManager.event("action", channel, user, message[8:-1])
-                elif message.find(self.config["comchar"]) == 0:
-                    # String was found, it's a command!
+                if re.match("^"+self.config["nickname"]+"[^A-Za-z0-9] .+$", message, flags=re.IGNORECASE):
+                    #Someone addressed us, it's a command (probably)
                     args = message.split(" ")
-                    cmd = args[0][len(self.config["comchar"]):]
-                    args.pop(0)
+                    cmd = args[1]
+                    args = args[2:]
                     self.pluginManager.event("message", channel, user, message)
                     self.pluginManager.event("command", channel, user, cmd.lower(), args)
                 else:
-                    if re.match("^"+self.config["nickname"]+"[^A-Za-z0-9] .+$", message):
-                        #Someone addressed us, it's a command (probably)
-                        args = message.split(" ")
-                        cmd = args[1]
-                        args = args[2:]
-                        self.pluginManager.event("message", channel, user, message)
-                        self.pluginManager.event("command", channel, user, cmd.lower(), args)
-                    else:
-                        #Nothing was found, it has to be a message
-                        self.pluginManager.event("message", channel, user, message)
-            
-            elif words[1] == "JOIN":
-                # Someone joined a channel that we're in
-                if user != self.config["nickname"]:
-                    self.pluginManager.event("join", words[2].strip(":"), user)
-                    self.userlist[words[2].strip(":")].append(user)
-            
-            elif words[1] == "PART":
-                if user != self.config["nickname"]:
-                    # Someone parted a channel we're in
-                    if user in self.userlist[words[2].strip(":")]:
-                        self.userlist[words[2]].remove(user)
-                    self.pluginManager.event("part", words[2].strip(":"), user, " ".join(words[3:]))
-            
-            elif words[1] == "QUIT":
-                # Someone quit the server
-                for k in self.userlist.keys():
-                    if user in self.userlist[k]:
-                        self.userlist[k].remove(user)
-                self.pluginManager.event("quit", user, " ".join(words[2:])[1:]) 
-            
-            elif words[1] == "NICK":
-                # Someone changed their nickname 
-                for k in self.userlist.keys():
-                    if user in self.userlist[k]:
-                        self.userlist[k].remove(user)
-                        self.userlist[k].append(words[2].strip(":"))
-                self.pluginManager.event("nick", user, words[2].strip(":"))
+                    #Nothing was found, it has to be a message
+                    self.pluginManager.event("message", channel, user, message)
+        
+        elif words[1] == "JOIN":
+            # Someone joined a channel that we're in
+            if user != self.config["nickname"]:
+                self.pluginManager.event("join", words[2].strip(":"), user)
+                self.userlist[words[2].strip(":")].append(user)
+        
+        elif words[1] == "PART":
+            if user != self.config["nickname"]:
+                # Someone parted a channel we're in
+                if user in self.userlist[words[2].strip(":")]:
+                    self.userlist[words[2]].remove(user)
+                self.pluginManager.event("part", words[2].strip(":"), user, " ".join(words[3:]))
+        
+        elif words[1] == "QUIT":
+            # Someone quit the server
+            for k in self.userlist.keys():
+                if user in self.userlist[k]:
+                    self.userlist[k].remove(user)
+            self.pluginManager.event("quit", user, " ".join(words[2:])[1:]) 
+        
+        elif words[1] == "NICK":
+            # Someone changed their nickname 
+            for k in self.userlist.keys():
+                if user in self.userlist[k]:
+                    self.userlist[k].remove(user)
+                    self.userlist[k].append(words[2].strip(":"))
+            self.pluginManager.event("nick", user, words[2].strip(":"))
+        
+        elif words[1] == "MODE":
+            # Someone set a mode
+            try:
+                self.pluginManager.event("mode", words[2], user, words[3], words[4])
+            except IndexError: # words[4] is not valid, it wasn't set on a user
+                self.pluginManager.event("mode", words[2], user, words[3], "")
+        
+        elif words[1] == "KICK":
+            #someone kicked someone
+            if self.userlist.get(words[2].lower(), None) is not None:
+                self.userlist[words[2].lower()].remove(words[3])
+                
+            self.pluginManager.event("kick", words[2], user, words[3], words[4][1:])
+        
+        elif words[1] == "NOTICE":
+            self.pluginManager.event("notice", user, words[2], " ".join(words[3:]).strip(":"))
             
-            elif words[1] == "MODE":
-                # Someone set a mode
-                try:
-                    self.pluginManager.event("mode", words[2], user, words[3], words[4])
-                except IndexError: # words[4] is not valid, it wasn't set on a user
-                    self.pluginManager.event("mode", words[2], user, words[3], "")
+        else:
+            self.pluginManager.event(words[1], data)
             
-            elif words[1] == "KICK":
-                #someone kicked someone
-                if self.userlist.get(words[2].lower(), None) is not None:
-                    self.userlist[words[2].lower()].remove(words[3])
-                    
-                self.pluginManager.event("kick", words[2], user, words[3], words[4][1:])
-        
-        #end of loop
-        if self.goodstart:
-            self.pluginManager.event("disconnect", self.config["host"])
+    def collect_incoming_data(self, data):
+        self.data += data
         
+    def handle_connect(self):
+        self.config["ident"] = ident = self.config["ident"] if self.config["ident"] != "" else self.config["nickname"]
+        self.sendLine("USER "+ident+" * * *")
+        self.sendLine("NICK "+self.config["nickname"])
+    
+    def handle_disconnect(self):
+        self.pluginManager.event("disconnect", self.config["network"])
+    
+    def handle_error(self):
+        traceback.print_exc()
+    
     def prnt(self, line):
-        print("["+self.config["host"]+"] "+line)
+        print("["+self.config["network"]+"] "+line)
     
     def sendLine(self, line):
-        try:
-            self.conn.send(line+"\r\n")
-        except socket.error:
-            self.prnt("Connection lost to server.")
-            exit()
+        if line.endswith("\r\n"):
+            self.push(line)
+        else:
+            self.push(line+"\r\n")
     
     def doMessage(self, channel, message):
         self.sendLine("PRIVMSG "+channel+" :"+message)
@@ -375,7 +346,7 @@ class IRC(threading.Thread):
     def doQuit(self, message=""):
         self.sendLine("QUIT :" + message)
         self.pluginManager.event("quit", User(self.config["nickname"]), message)
-        self.running = False
+        self.close_when_done()
     
     def doNotice(self, user, message):
         self.sendLine("NOTICE "+user+" :"+message)
diff --git a/plugins/auth.py b/plugins/auth.py
index 8bb45a6..1f16f23 100644
--- a/plugins/auth.py
+++ b/plugins/auth.py
@@ -4,23 +4,18 @@ import os, ConfigParser
 class Auth(object):
     def __init__(self, server):
         self.server = server
-        self.commands = [ "addadmin", "addmod", "deladmin", "delmod", "admins", "mods", "owners", "passwd", "identify" ]
+        self.commands = [ "addowner", "delowner", "addadmin", "addmod", "deladmin", "delmod", "admins", "mods", "owners", "passwd", "identify" ]
         self.owners = {}
-        self.authed_owner_hosts = []
         self.admins = {}
-        self.authed_admin_hosts = []
         self.mods = {}
-        self.authed_mod_hosts = []
         self.server.handle("command", self.handle_command, self.commands)
         self.server.handle("part", self.handle_part)
         self.server.handle("quit", self.handle_quit)
         self.server.handle("nick", self.handle_nick)
-        self.authdir = self.server.config["confdir"] + "network-auth-users/"
-        self.authfile = self.authdir + self.server.config["network"] + "-auth.cfg"
+        self.authfile = self.server.config["confdir"] + "auth.cfg"
         self._loadusers()
     
     def _checkAuth(self, user):
-        user = user[0]
         stillOn = False
         for ch in self.server.userlist.keys():
             if user in self.server.userlist[ch]:
@@ -42,7 +37,6 @@ class Auth(object):
     
     def handle_nick(self, oldnick, newnick):
         if self.isOwner(oldnick):
-            print self.owners
             self.owners[newnick.lower()] = self.owners[oldnick.lower()]
             del self.owners[oldnick.lower()]
         elif self.isAdmin(oldnick):
@@ -66,7 +60,7 @@ class Auth(object):
                         if len(args) < 1:
                             self.server.doMessage(channel, user+": Not enough arguments.")
                             return
-                        
+                            
                         if len(args) >= 2:
                             self.owners[user.lower()][0] = args[1]
                             self.server.doMessage(channel, user+": Password successfully changed.")
@@ -144,68 +138,75 @@ class Auth(object):
                 self.server.doMessage(channel, user+": Not enough arguments.")
                 return
             
-            if cmd == "addadmin":
-                if args[0] in self.admins.keys(): return
-                self.admins[args[0]] = ['', False]
+            if cmd == "addowner":
+                if args[0].lower() in self.owners.keys(): return
+                self.owners[args[0].lower()] = ['', True]
+            elif cmd == "addadmin":
+                if args[0].lower() in self.admins.keys(): return
+                self.admins[args[0].lower()] = ['', True]
             elif cmd == "addmod":
-                if args[0] in self.mods.keys(): return
-                self.mods[args[0]] = ['', False]
+                if args[0].lower() in self.mods.keys(): return
+                self.mods[args[0].lower()] = ['', True]
+            elif cmd == "delowner":
+                if args[0].lower() in self.owners.keys():
+                    del self.admins[args[0].lower()]
             elif cmd == "deladmin":
-                if args[0] in self.admins.keys():
-                    del self.admins[args[0]]
+                if args[0].lower() in self.admins.keys():
+                    del self.admins[args[0].lower()]
             elif cmd == "delmod":
-                if args[0] in self.mods.keys():
-                    del self.mods[args[0]]
+                if args[0].lower() in self.mods.keys():
+                    del self.mods[args[0].lower()]
     
     #save users
-    def __del__(self):
-        if not os.path.isdir(self.authdir):
-            os.mkdir(self.authdir)
+    def destroy(self):
+        self.configParser = ConfigParser.RawConfigParser()
+        
+        if os.path.isfile(self.authfile):
+            self.configParser.read(self.authfile)
             
         fh = open(self.authfile, "w")
-        if not self.configParser.has_section("owners"):
-            self.configParser.add_section("owners")
-        if not self.configParser.has_section("admins"):
-            self.configParser.add_section("admins")
-        if not self.configParser.has_section("mods"):
-            self.configParser.add_section("mods")
+        network = self.server.config["network"]
+        if not self.configParser.has_section(network):
+            self.configParser.add_section(network)
         
-        for user in self.owners.keys():
-            self.configParser.set("owners",user,self.owners[user][0])
-        for user in self.admins.keys():
-            self.configParser.set("admins",user,self.admins[user][0])
-        for user in self.mods.keys():
-            self.configParser.set("mods",user,self.mods[user][0])
+        self.configParser.set(self.server.config["network"],"owners",",".join( [k+":"+v[0] for k,v in self.owners.items()] ))
+        self.configParser.set(self.server.config["network"],"admins",",".join( [k+":"+v[0] for k,v in self.admins.items()] ))
+        self.configParser.set(self.server.config["network"],"mods",",".join( [k+":"+v[0] for k,v in self.mods.items()] ))
         
         self.configParser.write(fh)
         fh.close()
         
-    
     def _loadusers(self):
+        network = self.server.config["network"]
+        
         if os.path.isfile(self.authfile):
             self.configParser = ConfigParser.RawConfigParser()
             self.configParser.read(self.authfile)
+            
+            if self.configParser.has_section(network):
+                if self.configParser.has_option(network, "owners"):
+                    olist = self.configParser.get(network, "owners").lower().split(",")
+                    olist = [o for o in olist if o != '']
+                    for user in olist:
+                        nick, pw = user.split(":")
+                        self.owners[nick] = [pw, pw == '']
                 
-            for section in self.configParser.sections():
-                if section == "owners":
-                    for user,pswd in self.configParser.items(section):
-                        self.owners[user.lower()] = [pswd, pswd == '']
-                elif section == "admins":
-                    for user,pswd in self.configParser.items(section):
-                        self.admins[user.lower()] = [pswd, pswd == '']
-                elif section == "mods":
-                    for user,pswd in self.configParser.items(section):
-                        self.mods[user.lower()] = [pswd, pswd == '']
+                if self.configParser.has_option(network, "admins"):
+                    alist = self.configParser.get(network, "admins").lower().split(",")
+                    alist = [a for a in alist if a != '']
+                    for user in alist:
+                        nick, pw = user.split(":")
+                        self.admins[nick] = [pw, pw == '']
+                
+                if self.configParser.has_option(network, "mods"):
+                    mlist = self.configParser.get(network, "mods").lower().split(",")
+                    mlist = [m for m in mlist if m != '']
+                    for user in mlist:
+                        nick, pw = user.split(":")
+                        self.mods[nick] = [pw, pw == '']
             
         onick = self.server.config["owner_nick"].lower()
         if not onick in self.owners.keys():
-            if getattr(self, "configParser", None) is None:
-                self.configParser = ConfigParser.RawConfigParser()
-                self.configParser.add_section("owners")
-                self.configParser.add_section("admins")
-                self.configParser.add_section("mods")
-            
-            self.configParser.set("owners", onick, '')
             self.owners[onick] = ['', True]
     
     def isOwner(self, user):
diff --git a/plugins/channeltools.py b/plugins/channeltools.py
index f19288f..92f3e6b 100644
--- a/plugins/channeltools.py
+++ b/plugins/channeltools.py
@@ -7,7 +7,6 @@ class ChannelTools(object):
         self.server.handle("command", self.handle_command, self.commands)
         
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if self.server.getPlugin("auth").isAdmin(user):
             if len(args) < 1:
                 self.server.doMessage(channel, user+": Not enough arguments.")
diff --git a/plugins/ddg.py b/plugins/ddg.py
index 85ad9b6..86ad9c4 100644
--- a/plugins/ddg.py
+++ b/plugins/ddg.py
@@ -13,7 +13,6 @@ class ddg(object):
         self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if cmd == "ddg":
             if len(args) < 1:
                 self.server.doMessage(channel, user+": DuckDuckGo.com Zero-Click infoboxes search. Syntax: ddg <query>.")
diff --git a/plugins/die.py b/plugins/die.py
index 2d6c0a1..e5cffc1 100644
--- a/plugins/die.py
+++ b/plugins/die.py
@@ -8,7 +8,6 @@ class Die(object):
         self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if self.server.getPlugin("auth").isOwner(user):
             self.server.doMessage(channel, user + " wants me to leave, but I'll be back!")
             self.server.doQuit()
diff --git a/plugins/dns.py b/plugins/dns.py
index 06e91d7..5967502 100644
--- a/plugins/dns.py
+++ b/plugins/dns.py
@@ -12,7 +12,6 @@ class dns(object):
         self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if cmd == "dns":
             if len(args) < 1:
                 self.server.doMessage(channel, user+": Return a fully qualified domain name for a list of space-separated IPs or hostnames.")
diff --git a/plugins/dynacode.py b/plugins/dynacode.py
index c2fb71c..ecc80ba 100644
--- a/plugins/dynacode.py
+++ b/plugins/dynacode.py
@@ -1,15 +1,12 @@
 import sys
 
-@plugin
 class DynaCode(object):
     def __init__(self, server):
         self.server = server
         self.commands = [ "py" ]
-        self.server.handle("command", getattr(self, "handle_command"), self.commands)
-        self.env = { }
+        self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if self.server.getPlugin("auth").isOwner(user) and cmd == "py":
             if len(args) < 1:
                 self.server.doMessage(channel, user+": Not enough arguments.")
diff --git a/plugins/games.py b/plugins/games.py
index a9bf939..0955a4d 100644
--- a/plugins/games.py
+++ b/plugins/games.py
@@ -2,7 +2,8 @@ import random
 
 UNODECK = ['b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'g0', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7', 'g8', 'g9', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7', 'g8', 'g9', 'y0', 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7', 'y8', 'y9', 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7', 'y8', 'y9', 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'rS', 'gS', 'bS', 'yS', 'rD2', 'gD2', 'bD2', 'yD2', 'rR', 'gR', 'bR', 'yR', 'wW', 'wW', 'wW', 'wW', 'wWD4', 'wWD4', 'wWD4', 'wWD4', 'rS', 'gS', 'bS', 'yS', 'rD2', 'gD2', 'bD2', 'yD2', 'rR', 'gR', 'bR', 'yR']
 
-@plugin 
+#commented out the decorator, as the plugin is not yet made
+#@plugin 
 class Uno(object):
     def __init__(self, server):
         self.server = server
diff --git a/plugins/info.py b/plugins/info.py
index 2dafe56..b32631c 100644
--- a/plugins/info.py
+++ b/plugins/info.py
@@ -17,7 +17,7 @@ class Info(object):
                 k,v = line.split("\t")
                 self.infodb[k] = v
     
-    def __del__(self):
+    def destroy(self):
         fh = open(self.dbfile+".tmp", "w")
         for k,v in self.infodb.items():
             fh.write("%s\t%s\n" % (k,v))
diff --git a/plugins/ping.py b/plugins/ping.py
index 1f5bd36..ce16deb 100644
--- a/plugins/ping.py
+++ b/plugins/ping.py
@@ -7,7 +7,6 @@ class Ping(object):
         self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if cmd == "ping":
             if len(args) > 0:
                 self.server.doMessage(channel, user+": Pong "+" ".join(args))
diff --git a/plugins/printer.py b/plugins/printer.py
index 2647c66..6708bb8 100644
--- a/plugins/printer.py
+++ b/plugins/printer.py
@@ -5,11 +5,17 @@ class Printer(object):
         self.server = server
         self.prnt = server.prnt
         
-        #this is just so that we don't have to hard-code every event
-        for h in self.server.handlers:
-            func = getattr(self, "handle_"+h, None)
-            if func is not None:
-                self.server.handle(h, func)
+        self.server.handle("message", self.handle_message)
+        self.server.handle("kick", self.handle_kick)
+        self.server.handle("part", self.handle_part)
+        self.server.handle("join", self.handle_join)
+        self.server.handle("quit", self.handle_quit)
+        self.server.handle("mode", self.handle_mode)
+        self.server.handle("notice", self.handle_notice)
+        self.server.handle("connect", self.handle_connect)
+        self.server.handle("disconnect", self.handle_disconnect)
+        self.server.handle("action", self.handle_action)
+        self.server.handle("nick", self.handle_nick)
     
     def handle_connect(self, server):
         self.prnt("I have connected to %s" % server)
@@ -24,7 +30,6 @@ class Printer(object):
         self.prnt("<%s> * %s %s" % (channel, user, action))
     
     def handle_join(self, channel, user):
-        user = user[0]
         self.prnt("%s has joined %s" % (user, channel))
         
     def handle_part(self, channel, user, message):
@@ -39,5 +44,8 @@ class Printer(object):
     def handle_kick(self, channel, user, userkicked, message):
         self.prnt("%s has kicked %s from %s (%s)" % (user, userkicked, channel, message))
     
+    def handle_notice(self, user, dest, message):
+        self.prnt("(%s) -%s- %s" % (dest, user, message))
+    
     def handle_mode(self, channel, user, mode, otheruser):
         self.prnt("%s set mode %s on %s" % (user, mode, otheruser if otheruser != "" else channel))
diff --git a/plugins/rpn.py b/plugins/rpn.py
index 79dab01..16147fc 100644
--- a/plugins/rpn.py
+++ b/plugins/rpn.py
@@ -10,7 +10,6 @@ class rpn(object):
         self.server.handle("command", self.handle_command, self.commands)
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if cmd == "rpn":
             if len(args) < 1:
                 self.server.doMessage(channel, user+": reverse-polish-notation calculator. syntax: rpn 2 4 1 + / gives 2/5.")
diff --git a/plugins/smack.py b/plugins/smack.py
index 27a6311..f4b33b7 100644
--- a/plugins/smack.py
+++ b/plugins/smack.py
@@ -15,7 +15,6 @@ class Smack(object):
                                   ]
     
     def handle_command(self, channel, user, cmd, args):
-        user = user[0]
         if cmd == "smack" and len(args) > 0:
             self.server.doAction(channel, "smacks "+args[0]+" with a "+self.objects[random.Random().randint(0, len(self.objects)-1)]+"!")
     
diff --git a/plugins/wikipedia.py b/plugins/wikipedia.py
index c1db907..2186fe8 100644
--- a/plugins/wikipedia.py
+++ b/plugins/wikipedia.py
@@ -12,7 +12,7 @@ class Wikipedia(object):
         user = user[0]
         if cmd == "wiki":
             if len(args) < 1: 
-                self.server.doMessage(channel, user+": What should I search for on Wikipedia?")
+                self.server.doMessage(channel, user+": Not enough arguments.")
                 return
             
             url = "http://en.wikipedia.org/w/api.php?action=opensearch&limit=3&namespace=0&format=xml&search="
@@ -25,8 +25,12 @@ class Wikipedia(object):
                 self.server.doMessage(channel, user+": I/O Error retrieving the results: "+ioe)
                 return
             
-            print data[0]
-            xmldoc = minidom.parseString(data[0]) 
+            try:
+                xmldoc = minidom.parseString(data[0]) 
+            except xml.parsers.expat.ExpatError:
+                self.server.doMessage(channel, user+": Error parsing the xml search results.")
+                return
+                
             searchResults = xmldoc.childNodes[0].childNodes[1].childNodes
             for result in searchResults:
                 resultStr = ""
-- 
GitLab