diff options
| -rw-r--r-- | bouncer.py | 783 | ||||
| -rw-r--r-- | irc.py | 2680 | ||||
| -rw-r--r-- | logger.py | 308 | 
3 files changed, 2131 insertions, 1640 deletions
| @@ -9,16 +9,29 @@ import string  import hashlib  import traceback  import irc +import getpass  from threading import Thread, Lock  import Queue +def BouncerReload(BNC): +    if BNC.isAlive(): +        BNC.stop() +    newBNC = Bouncer(addr=BNC.addr, port=BNC.port, ssl=BNC.ssl, ipv6=BNC.ipv6, +                     certfile=BNC.certfile, keyfile=BNC.keyfile, timeout=BNC.timeout, autoaway=BNC.autoaway) +    for label, (IRC, passwd, hashtype) in BNC.servers.items(): +        IRC.rmAddon(BNC) +        IRC.addAddon(newBNC, label=label, passwd=passwd, hashtype=hashtype) +    return newBNC + +  class Bouncer (Thread): -    def __init__(self, addr="", port=16667, ssl=False, ipv6=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr, timeout=300, BNC=None, autoaway=None): + +    def __init__(self, addr="", port=16667, ssl=False, ipv6=False, certfile=None, keyfile=None, ignore=None, debug=False, timeout=300, autoaway=None):          self.__name__ = "Bouncer for pyIRC" -        self.__version__ = "1.1" +        self.__version__ = "1.2"          self.__author__ = "Brian Sherson" -        self.__date__ = "December 1, 2013" +        self.__date__ = "December 26, 2013"          self.addr = addr          self.port = port @@ -37,9 +50,10 @@ class Bouncer (Thread):          self.debug = debug          self.timeout = timeout          self.autoaway = autoaway +        self._stopexpected = False -        ### Keep track of what extensions/connections are requesting WHO, WHOIS, and LIST, because we don't want to spam every bouncer connection with the server's replies. -        ### In the future, MAY implement this idea in the irc module. +        # Keep track of what extensions/connections are requesting WHO, WHOIS, and LIST, because we don't want to spam every bouncer connection with the server's replies. +        # In the future, MAY implement this idea in the irc module.          self.whoexpected = {}          self.whoisexpected = {}          self.listexpected = {} @@ -59,13 +73,19 @@ class Bouncer (Thread):              try:                  (connection, addr) = self.socket.accept()                  if self.ssl: -                    connection = ssl.wrap_socket(connection, server_side=True, certfile=self.certfile, keyfile=self.keyfile, ssl_version=ssl.PROTOCOL_SSLv23) +                    connection = ssl.wrap_socket( +                        connection, server_side=True, certfile=self.certfile, keyfile=self.keyfile, ssl_version=ssl.PROTOCOL_SSLv23)                  #print ((self,"New client connecting from %s:%s"%addr))              except socket.error: -                #print "Shutting down Listener" +                # print "Shutting down Listener"                  self.socket.close() -                #raise +                if not self._stopexpected: +                    raise                  sys.exit() +            except: +                tb = traceback.format_exc() +                print >>sys.stderr, tb +                continue              connection.settimeout(self.timeout)              bouncer = BouncerConnection(                  self, connection, addr, debug=self.debug) @@ -75,15 +95,23 @@ class Bouncer (Thread):          except:              pass -    def onAddonAdd(self, IRC, label, passwd, hashtype="md5"): -        if IRC in [connection for (connection, passwd, hashtype) in self.servers.values()]: +    def onAddonAdd(self, IRC, label, passwd=None, hashtype="sha512"): +        if passwd == None: +            while True: +                passwd = getpass.getpass("Enter new password: ") +                if passwd == getpass.getpass("Confirm new password: "): +                    break +                print "Passwords do not match!" +            passwd = hashlib.new(hashtype, passwd).hexdigest() +        if IRC in [connection for (connection, p, h) in self.servers.values()]:              return  # Silently do nothing          if label in self.servers.keys():              return          self.servers[label] = (IRC, passwd, hashtype)          self.whoexpected[IRC] = []          if self.debug: -            IRC.logwrite("dbg [Bouncer.onAddonAdd] Clearing WHO expected list." % vars()) +            IRC.logwrite( +                "dbg [Bouncer.onAddonAdd] Clearing WHO expected list." % vars())          self.whoisexpected[IRC] = []          self.listexpected[IRC] = [] @@ -96,6 +124,7 @@ class Bouncer (Thread):                  del self.servers[label]      def stop(self): +        self._stopexpected = True          self.socket.shutdown(0)      def disconnectall(self, quitmsg="Disconnecting all sessions"): @@ -104,248 +133,259 @@ class Bouncer (Thread):      def onDisconnect(self, IRC, expected=False):          self.whoexpected[IRC] = [] -        if self.debug: -            IRC.logwrite("dbg [Bouncer.onDisconnect] Clearing WHO expected list.")          self.whoisexpected[IRC] = []          self.listexpected[IRC] = []          for bouncerconnection in self.connections:              if bouncerconnection.IRC == IRC: -                bouncerconnection.quit(quitmsg="IRC connection lost") +                #bouncerconnection.quit(quitmsg="IRC connection lost") +                for channel in IRC.identity.channels: +                    bouncerconnection.send(":%s!%s@%s PART %s :Bouncer Connection Lost\n" % ( +                        IRC.identity.nick, IRC.identity.username, IRC.identity.host, channel.name)) +                bouncerconnection.send( +                    ":%s!%s@%s QUIT :Bouncer Connection Lost\n" % +                    (IRC.identity.nick, IRC.identity.username, IRC.identity.host)) +                bouncerconnection.send( +                    ":*Bouncer* NOTICE %s :Connection to %s:%s has been lost.\n" % +                    (bouncerconnection.nick, IRC.server, IRC.port)) + +    def onQuit(self, IRC, user, quitmsg): +        # For some odd reason, certain networks (*cough*Freenode*cough*) will send a quit message for the user, causing IRC.identity.channels to be cleared +        # before onDisconnect can be executed. This is the remedy. +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                if quitmsg: +                    bouncerconnection.send(":%s!%s@%s QUIT :%s\n" % ( +                        user.nick, user.username, user.host, quitmsg)) +                else: +                    bouncerconnection.send( +                        ":%s!%s@%s QUIT\n" % (user.nick, user.username, user.host)) +                if user == IRC.identity: +                    for channel in IRC.identity.channels: +                        bouncerconnection.send(":%s!%s@%s PART %s :Bouncer Connection Lost\n" % ( +                            IRC.identity.nick, IRC.identity.username, IRC.identity.host, channel.name)) + +    def onConnectAttempt(self, IRC): +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                bouncerconnection.send( +                    ":*Bouncer* NOTICE %s :Attempting connection to %s:%s.\n" % +                    (bouncerconnection.nick, IRC.server, IRC.port)) + +    def onConnect(self, IRC): +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                bouncerconnection.send( +                    ":*Bouncer* NOTICE %s :Connection to %s:%s established.\n" % +                    (bouncerconnection.nick, IRC.server, IRC.port)) + +    def onMeNickChange(self, IRC, newnick): +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                bouncerconnection.send(":%s!%s@%s NICK %s\n" % +                                       (IRC.identity.nick, IRC.identity.username, IRC.identity.host, newnick)) +                bouncerconnection.nick = newnick + +    def onRegistered(self, IRC): +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                if bouncerconnection.nick != IRC.identity.nick: +                    bouncerconnection.send(":%s!%s@%s NICK %s\n" % ( +                        bouncerconnection.nick, bouncerconnection.username, bouncerconnection.host, IRC.identity.nick)) +                    bouncerconnection.nick = IRC.identity.nick + +    def onConnectFail(self, IRC, exc, excmsg, tb): +        for bouncerconnection in self.connections: +            if bouncerconnection.IRC == IRC: +                bouncerconnection.send( +                    ":*Bouncer* NOTICE %s :Connection to %s:%s failed: %s.\n" % +                    (bouncerconnection.nick, IRC.server, IRC.port, excmsg))      def onSendChanMsg(self, IRC, origin, channel, targetprefix, msg): -        ### Called when bot sends a PRIVMSG to channel. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. +        # Called when bot sends a PRIVMSG to channel. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent.          for bouncerconnection in self.connections:              if IRC == bouncerconnection.IRC and origin != bouncerconnection: -                bouncerconnection.send(":%s!%s@%s PRIVMSG %s%s :%s\n"%(IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)) +                bouncerconnection.send(":%s!%s@%s PRIVMSG %s%s :%s\n" % +                                       (IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg))      def onSendChanAction(self, IRC, origin, channel, targetprefix, action): -        self.onSendChanMsg(IRC, origin, channel, targetprefix, -                           "\x01ACTION %s\x01"%action) +        self.onSendChanMsg( +            IRC, origin, channel, targetprefix, "\x01ACTION %s\x01" % action)      def onSendChanNotice(self, IRC, origin, channel, targetprefix, msg): -        ### Called when bot sends a NOTICE to channel. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. +        # Called when bot sends a NOTICE to channel. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent.          for bouncerconnection in self.connections:              if IRC == bouncerconnection.IRC and origin != bouncerconnection: -                bouncerconnection.send(":%s!%s@%s NOTICE %s%s :%s\n"%(IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)) +                bouncerconnection.send(":%s!%s@%s NOTICE %s%s :%s\n" % ( +                    IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg))      def onSend(self, IRC, origin, line, cmd, target, params, extinfo):          if cmd.upper() == "WHO":              self.whoexpected[IRC].append(origin)              if self.debug: -                with IRC.loglock: -                    timestamp = irc.timestamp() -                    if issubclass(type(origin), Thread): -                        name = origin.name -                        print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] Adding %(origin)s (%(name)s) to WHO expected list." % vars() -                    else: -                        print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] Adding %(origin)s to WHO expected list." % vars() -                    for obj in self.whoexpected[IRC]: -                        if issubclass(type(obj), Thread): -                            name = obj.name -                            print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] WHO expected: %(obj)s (%(name)s)" % vars() -                        else: -                            print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] WHO expected: %(obj)s" % vars() -                    IRC.log.flush() +                if issubclass(type(origin), Thread): +                    name = origin.name +                    IRC.logwrite( +                        "dbg [Bouncer.onSend] Adding %(origin)s (%(name)s) to WHO expected list." % vars()) +                else: +                    IRC.logwrite( +                        "dbg [Bouncer.onSend] Adding %(origin)s to WHO expected list." % vars()) +                IRC.logwrite( +                    "dbg [Bouncer.onSend] WHO expected list size: %d" % +                    len(self.whoexpected[IRC]))          elif cmd.upper() == "WHOIS":              self.whoisexpected[IRC].append(origin)          elif cmd.upper() == "LIST":              self.listexpected[IRC].append(origin)      def onWhoEntry(self, IRC, origin, channel, user, channame, username, host, serv, nick, flags, hops, realname): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:              bncconnection = self.whoexpected[IRC][0] -            try: -                bncconnection.send(":%s 352 %s %s %s %s %s %s %s :%s %s\n"%(origin, IRC.identity.nick, channame, username, host, serv, nick, flags, hops, realname)) -            except socket.error: -                pass +            bncconnection.send(":%s 352 %s %s %s %s %s %s %s :%s %s\n" % +                               (origin, IRC.identity.nick, channame, username, host, serv, nick, flags, hops, realname))      def onWhoEnd(self, IRC, origin, param, endmsg): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:              bncconnection = self.whoexpected[IRC][0] -            try: -                bncconnection.send(":%s 315 %s %s :%s\n"%( -                    origin, IRC.identity.nick, param, endmsg)) -            except socket.error: -                pass -            finally: -                del self.whoexpected[IRC][0] - -    def onWho(self, IRC, params, wholist, endmsg): -        ### Called when a WHO list is received. -        if len(self.whoexpected[IRC]): -            try: -                if self.whoexpected[IRC][0] in self.connections: -                    bncconnection = self.whoexpected[IRC][0] -                    lines = [":%s 352 %s %s %s %s %s %s %s :%s %s\n"%(IRC.serv, IRC.identity.nick, channame, username, host, serv, nick, flags, hops, realname) for (channel, user, channame, username, host, serv, nick, flags, hops, realname) in wholist]+[":%s 315 %s %s :%s\n"%(IRC.serv, IRC.identity.nick, params, endmsg)] -                    bncconnection.send("".join(lines)) -            finally: -                del self.whoexpected[IRC][0] +            bncconnection.send(":%s 315 %s %s :%s\n" % +                               (origin, IRC.identity.nick, param, endmsg)) +        if self.debug: +            if issubclass(type(self.whoexpected[IRC][0]), Thread): +                name = self.whoexpected[IRC][0].name +                IRC.logwrite( +                    "dbg [Bouncer.onWhoEnd] Removing %s (%s) from WHO expected list." % +                    (self.whoexpected[IRC][0], name)) +            else: +                IRC.logwrite( +                    "dbg [Bouncer.onWhoEnd] Removing %s from WHO expected list." % self.whoexpected[IRC][0]) +        del self.whoexpected[IRC][0] +        if self.debug: +            IRC.logwrite("dbg [Bouncer.onWhoEnd] WHO expected list size: %d" % +                         len(self.whoexpected[IRC]))      def onListStart(self, IRC, origin, params, extinfo): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:              bncconnection = self.listexpected[IRC][0] -            try: -                bncconnection.send(":%s 321 %s %s :%s\n"%(origin, -                                                          IRC.identity.nick, params, extinfo)) -            except socket.error: -                pass +            bncconnection.send(":%s 321 %s %s :%s\n" % +                               (origin, IRC.identity.nick, params, extinfo))      def onListEntry(self, IRC, origin, channel, population, extinfo): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:              bncconnection = self.listexpected[IRC][0] -            try: -                bncconnection.send(":%s 322 %s %s %d :%s\n"%(origin, IRC.identity.nick, channel.name, population, extinfo)) -            except socket.error: -                pass +            bncconnection.send(":%s 322 %s %s %d :%s\n" % +                               (origin, IRC.identity.nick, channel.name, population, extinfo))      def onListEnd(self, IRC, origin, endmsg): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:              bncconnection = self.listexpected[IRC][0] -            try: -                bncconnection.send(":%s 323 %s :%s\n"%( -                    origin, IRC.identity.nick, endmsg)) -            except socket.error: -                pass -            finally: -                del self.listexpected[IRC][0] +            bncconnection.send(":%s 323 %s :%s\n" % +                               (origin, IRC.identity.nick, endmsg)) +        del self.listexpected[IRC][0]      def onWhoisStart(self, IRC, origin, user, nickname, username, host, realname): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]):              if self.whoisexpected[IRC][0] in self.connections:                  bncconnection = self.whoisexpected[IRC][0] -                try: -                    bncconnection.send(":%s 311 %s %s %s %s * :%s\n" % (origin, IRC.identity.nick, nickname, username, host, realname)) -                except socket.error: -                    pass +                bncconnection.send(":%s 311 %s %s %s %s * :%s\n" % +                                   (origin, IRC.identity.nick, nickname, username, host, realname))      def onWhoisRegisteredNick(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 307 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 307 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisConnectingFrom(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 378 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 378 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisChannels(self, IRC, origin, user, nickname, chanlist): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 319 %s %s :%s\n" % (origin, IRC.identity.nick, nickname, " ".join(chanlist))) -            except socket.error: -                pass +            bncconnection.send(":%s 319 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, " ".join(chanlist)))      def onWhoisAvailability(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 310 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 310 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisServer(self, IRC, origin, user, nickname, server, servername): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 312 %s %s %s :%s\n" % (origin, IRC.identity.nick, nickname, server, servername)) -            except socket.error: -                pass +            bncconnection.send(":%s 312 %s %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, server, servername))      def onWhoisOp(self, IRC, origin, user, nickname, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 313 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 313 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisAway(self, IRC, origin, user, nickname, awaymsg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 301 %s %s :%s\n" % (origin, -                                                            IRC.identity.nick, nickname, awaymsg)) -            except socket.error: -                pass +            bncconnection.send(":%s 301 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, awaymsg))      def onWhoisTimes(self, IRC, origin, user, nickname, idletime, signontime, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 317 %s %s %d %d :%s\n" % (origin, IRC.identity.nick, nickname, idletime, signontime, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 317 %s %s %d %d :%s\n" % +                               (origin, IRC.identity.nick, nickname, idletime, signontime, msg))      def onWhoisSSL(self, IRC, origin, user, nickname, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 671 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 671 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisModes(self, IRC, origin, user, nickname, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 339 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 339 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg))      def onWhoisLoggedInAs(self, IRC, origin, user, nickname, loggedinas, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 330 %s %s %s :%s\n" % (origin, IRC.identity.nick, nickname, loggedinas, msg)) -            except socket.error: -                pass +            bncconnection.send(":%s 330 %s %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, loggedinas, msg))      def onWhoisEnd(self, IRC, origin, user, nickname, msg):          if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:              bncconnection = self.whoisexpected[IRC][0] -            try: -                bncconnection.send(":%s 318 %s %s :%s\n" % ( -                    origin, IRC.identity.nick, nickname, msg)) -            except socket.error: -                pass -            finally: -                del self.whoisexpected[IRC][0] +            bncconnection.send(":%s 318 %s %s :%s\n" % +                               (origin, IRC.identity.nick, nickname, msg)) +        del self.whoisexpected[IRC][0]      def onUnhandled(self, IRC, line, origin, cmd, target, params, extinfo):          for bouncerconnection in self.connections:              if bouncerconnection.IRC == IRC: -                bouncerconnection.send("%s\n"%line) +                bouncerconnection.send("%s\n" % line)  class BouncerConnection (Thread): +      def __init__(self, bouncer, connection, addr, debug=False): -        #print "Initializing ListenThread..." +        # print "Initializing ListenThread..."          self.bouncer = bouncer          self.connection = connection          self.host, self.port = self.addr = addr[:2] @@ -371,7 +411,8 @@ class BouncerConnection (Thread):                  self.connection.send(data)          except socket.error:              exc, excmsg, tb = sys.exc_info() -            print >>self.IRC.logwrite(*["!!! [BouncerConnection.send] Exception in thread %(self)s" % vars()]+["!!! [BouncerConnection.send] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")]) +            print >>self.IRC.logwrite(*["!!! [BouncerConnection.send] Exception in thread %(self)s" % vars()] + [ +                                      "!!! [BouncerConnection.send] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])              self.quit(quitmsg=excmsg.message)      def __repr__(self): @@ -401,18 +442,19 @@ class BouncerConnection (Thread):              self.quitmsg = quitmsg              with self.lock:                  try: -                    self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick if self.IRC else "*", self.host, quitmsg)) +                    self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % ( +                        self.IRC.identity.nick if self.IRC else "*", self.host, quitmsg))                  except:                      pass                  try: -                    self.connection.shutdown(1) +                    self.connection.shutdown(socket.SHUT_WR)                      self.connection.close()                  except:                      pass                  self.quitting = True      def run(self): -        ### Name loopup should happen here instead +        # Name loopup should happen here instead          ipv4match = re.findall(              r"^::ffff:((\d+)\.(\d+)\.(\d+)\.(\d+))$", self.host)          if self.bouncer.ipv6 and ipv4match: @@ -428,7 +470,7 @@ class BouncerConnection (Thread):          except:              pass -        ### Add connection to connection list. +        # Add connection to connection list.          listnumerics = dict(b=(367, 368, "channel ban list"),                              e=(348, 349, "Channel Exception List"), @@ -447,7 +489,8 @@ class BouncerConnection (Thread):          try:              while True: -                ### Read data (appending) into readbuf, then break lines and append lines to linebuf +                # Read data (appending) into readbuf, then break lines and +                # append lines to linebuf                  while len(linebuf) == 0:                      timestamp = irc.timestamp()                      read = self.connection.recv(512) @@ -460,10 +503,11 @@ class BouncerConnection (Thread):                      if lastlf >= 0:                          linebuf.extend(string.split(readbuf[0:lastlf], "\n")) -                        readbuf = readbuf[lastlf+1:] +                        readbuf = readbuf[lastlf + 1:]                  line = string.rstrip(linebuf.pop(0)) -                match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I) +                match = re.findall( +                    "^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)                  if len(match) == 0:                      continue @@ -474,146 +518,165 @@ class BouncerConnection (Thread):                          passwd = target if target else extinfo                      else:                          self.quit("Access Denied") +                        print "*** [BouncerConnection] Incoming connection from %s failed: Expected PASS." % (self.host)                          break -                elif not nick:  # Bouncer expects a NICK command +                elif not self.nick:  # Bouncer expects a NICK command                      if cmd.upper() == "NICK": -                        nick = target if target else extinfo +                        self.nick = target if target else extinfo                      else:                          self.quit("Access Denied") +                        print "*** [BouncerConnection] Incoming connection from %s failed: Expected NICK." % (self.host)                          break                  elif not self.username:  # Bouncer expects a USER command to finish registration                      if cmd.upper() == "USER":                          self.username = target -                        #print self.username +                        # print self.username                          if self.username in self.bouncer.servers.keys(): -                            self.IRC, passwdhash, hashtype = self.bouncer.servers[self.username] -                            passmatch = hashlib.new(hashtype, passwd).hexdigest() == passwdhash +                            self.IRC, passwdhash, hashtype = self.bouncer.servers[ +                                self.username] +                            passmatch = hashlib.new( +                                hashtype, passwd).hexdigest() == passwdhash                              with self.IRC.lock: -                                self.IRC.logwrite("*** [BouncerConnection] Incoming connection from %s to %s." % (self.host, self.IRC)) +                                if not passmatch: +                                    self.quit("Access Denied") +                                    self.IRC.logwrite( +                                        "*** [BouncerConnection] Incoming connection from %s to %s denied: Invalid password." % (self.host, self.IRC)) +                                    for bouncerconnection in self.bouncer.connections: +                                        if bouncerconnection.IRC != self.IRC: +                                            continue +                                        if not bouncerconnection.quitting: +                                            bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s dened: Invalid password.\n" % ( +                                                bouncerconnection.IRC.identity.nick, self.host, self.IRC)) +                                    break + +                                self.IRC.logwrite( +                                    "*** [BouncerConnection] Incoming connection from %s to %s." % (self.host, self.IRC))                                  with self.bouncer.lock: -                                    ### Announce connection to all other bouncer connections. -                                    if self.debug: -                                        self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast incoming connection %(self)s." % vars()) +                                    # Announce connection to all other bouncer +                                    # connections.                                      for bouncerconnection in self.bouncer.connections:                                          if bouncerconnection.IRC != self.IRC:                                              continue -                                        if self.debug: -                                            self.IRC.logwrite("dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars())                                          if not bouncerconnection.quitting: -                                            bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC)) -                                            if self.debug: -                                                self.IRC.logwrite("dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars()) -                                    if len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and self.IRC.identity.away: -                                        ### Bouncer connection should automatically return from away status. +                                            bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % ( +                                                bouncerconnection.IRC.identity.nick, self.host, self.IRC)) +                                    if len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and self.IRC.registered and type(self.IRC.identity) == irc.User and self.IRC.identity.away: +                                        # Bouncer connection should +                                        # automatically return from away +                                        # status.                                          self.IRC.raw("AWAY")                                      self.bouncer.connections.append(self) -                                if not (self.IRC.connected and self.IRC.registered and type(self.IRC.supports) == dict and "CHANMODES" in self.IRC.supports.keys() and passmatch): -                                    self.quit("Access Denied") -                                    break - -                                ### If we have made it to this point, then access has been granted. -                                labels = [bouncerconnection.label for bouncerconnection in self.bouncer.connections if bouncerconnection.IRC == self.IRC and bouncerconnection.label] -                                n = 1 -                                while "*%s_%d"%(self.username, n) in labels: -                                    n += 1 -                                self.label = "*%s_%d"%(self.username, n) - -                                ### Request Version info. -                                #self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick)) - -                                ### Log incoming connection - -                                ### Send Greeting. -                                with self.lock: -                                    self.connection.send(":%s 001 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.welcome)) -                                    self.connection.send(":%s 002 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.hostinfo)) -                                    self.connection.send(":%s 003 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servcreated)) -                                    self.connection.send(":%s 004 %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servinfo)) - -                                    ### Send 005 response. -                                    supports = ["CHANMODES=%s"%(",".join(value)) if name == "CHANMODES" else "PREFIX=(%s)%s"%value if name == "PREFIX" else "%s=%s"%(name, value) if value else name for name, value in self.IRC.supports.items()] -                                    supports.sort() -                                    supportsreply = [] -                                    supportsstr = " ".join(supports) -                                    index = 0 -                                    while True: -                                        if len(supportsstr)-index > 196: -                                            nextindex = supportsstr.rfind(" ", index, index+196) -                                            supportsreply.append(supportsstr[index:nextindex]) -                                            index = nextindex+1 +                                if self.IRC.registered: +                                    # Send Greeting. +                                    with self.lock: +                                        if self.IRC.welcome: +                                            self.connection.send(":%s 001 %s :%s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.welcome)) +                                        if self.IRC.hostinfo: +                                            self.connection.send(":%s 002 %s :%s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.hostinfo)) +                                        if self.IRC.servcreated: +                                            self.connection.send(":%s 003 %s :%s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.servcreated)) +                                        if self.IRC.servinfo: +                                            self.connection.send(":%s 004 %s %s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.servinfo)) + +                                        # Send 005 response. +                                        if self.IRC.supports: +                                            supports = ["CHANMODES=%s" % (",".join(value)) if name == "CHANMODES" else "PREFIX=(%s)%s" % value if name == "PREFIX" else "%s=%s" % ( +                                                name, value) if value else name for name, value in self.IRC.supports.items()] +                                            supports.sort() +                                            supportsreply = [] +                                            supportsstr = " ".join(supports) +                                            index = 0 +                                            while True: +                                                if len(supportsstr) - index > 196: +                                                    nextindex = supportsstr.rfind( +                                                        " ", index, index + 196) +                                                    supportsreply.append( +                                                        supportsstr[index:nextindex]) +                                                    index = nextindex + 1 +                                                else: +                                                    supportsreply.append( +                                                        supportsstr[index:]) +                                                    break +                                            for support in supportsreply: +                                                self.connection.send(":%s 005 %s %s :are supported by this server\n" % ( +                                                    self.IRC.serv, self.IRC.identity.nick, support)) + +                                        # Send MOTD +                                        if self.IRC.motdgreet and self.IRC.motd and self.IRC.motdend: +                                            self.connection.send(":%s 375 %s :%s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.motdgreet)) +                                            for motdline in self.IRC.motd: +                                                self.connection.send(":%s 372 %s :%s\n" % ( +                                                    self.IRC.serv, self.IRC.identity.nick, motdline)) +                                            try: +                                                self.connection.send(":%s 376 %s :%s\n" % ( +                                                    self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend)) +                                            except AttributeError: +                                                self.connection.send( +                                                    ":%s 376 %s\n" % (self.IRC.serv, self.IRC.identity.nick))                                          else: -                                            supportsreply.append(supportsstr[index:]) -                                            break -                                    for support in supportsreply: -                                        self.connection.send(":%s 005 %s %s :are supported by this server\n" % (self.IRC.serv, self.IRC.identity.nick, support)) - -                                    ### Send MOTD -                                    if self.IRC.motdgreet and self.IRC.motd and self.IRC.motdend: -                                        self.connection.send(":%s 375 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdgreet)) -                                        for motdline in self.IRC.motd: -                                            self.connection.send(":%s 372 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, motdline)) -                                        try: -                                            self.connection.send(":%s 376 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend)) -                                        except AttributeError: -                                            self.connection.send(":%s 376 %s\n" % (self.IRC.serv, self.IRC.identity.nick)) -                                    else: -                                        self.connection.send(":%s 422 %s :MOTD File is missing\n" % (self.IRC.serv, self.IRC.identity.nick)) - -                                    ### Send user modes and snomasks. -                                    self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes)) -                                    if "s" in self.IRC.identity.modes and self.IRC.identity.snomask: -                                        self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.server, self.IRC.identity.nick, self.IRC.identity.snomask)) - -        #                                                       ### Join user to internal bouncer channel. -        #                                                       self.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host)) - -        #                                                       ### Set internal bouncer topic. -        #                                                       self.connection.send(":$bouncer 332 %s $bouncer :Bouncer internal channel. Enter bouncer commands here.\n" % (self.IRC.identity.nick)) -        #                                                       self.connection.send(":$bouncer 333 %s $bouncer $bouncer %s\n" % (self.IRC.identity.nick, self.bouncer.starttime)) - -        #                                                       ### Send NAMES for internal bouncer channel. -        #                                                       self.connection.send(":$bouncer 353 %s @ $bouncer :%s\n" % ( -        #                                                               self.IRC.identity.nick, -        #                                                               string.join(["@*Bouncer*"]+["@%s"%bouncerconnection.label for bouncerconnection in self.bouncer.connections])) -        #                                                               ) -        #                                                       self.connection.send(":$bouncer 366 %s $bouncer :End of /NAMES list.\n" % (self.IRC.identity.nick)) - -        #                                                       ### Give operator mode to user. -        #                                                       self.connection.send(":*Bouncer* MODE $bouncer +o %s\n" % (self.IRC.identity.nick)) - -                                    ### Join user to channels. -                                    for channel in self.IRC.identity.channels: -                                        ### JOIN command -                                        self.connection.send(":%s!%s@%s JOIN :%s\n" % (self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host, channel.name)) - -                                        ### Topic -                                        self.connection.send(":%s 332 %s %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topic)) -                                        self.connection.send(":%s 333 %s %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topicsetby, channel.topictime)) - -                                        ### Determine if +s or +p modes are set in channel -                                        secret = "s" in channel.modes.keys() and channel.modes["s"] -                                        private = "p" in channel.modes.keys() and channel.modes["p"] - -                                        ### Construct NAMES for channel. -                                        namesusers = [] -                                        modes, symbols = self.IRC.supports["PREFIX"] -                                        self.connection.send(":%s 353 %s %s %s :%s\n" % ( -                                                             self.IRC.serv, -                                                             self.IRC.identity.nick, -                                                             "@" if secret else ("*" if private else "="), -                                                             channel.name, -                                                             string.join([string.join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))], "")+user.nick for user in channel.users])) -                                                             ) -                                        self.connection.send(":%s 366 %s %s :End of /NAMES list.\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name)) - +                                            self.connection.send( +                                                ":%s 422 %s :MOTD File is missing\n" % (self.IRC.serv, self.IRC.identity.nick)) + +                                        # Send user modes and snomasks. +                                        self.connection.send(":%s 221 %s +%s\n" % ( +                                            self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes)) + +                                        if "s" in self.IRC.identity.modes and self.IRC.identity.snomask: +                                            self.connection.send(":%s 008 %s +%s :Server notice mask\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.snomask)) + +                                        # Join user to channels. +                                        for channel in self.IRC.identity.channels: +                                            # JOIN command +                                            self.connection.send(":%s!%s@%s JOIN :%s\n" % ( +                                                self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host, channel.name)) + +                                            # Topic +                                            self.connection.send(":%s 332 %s %s :%s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topic)) +                                            self.connection.send(":%s 333 %s %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topicsetby.nick if type( +                                                channel.topicsetby) == irc.User else channel.topicsetby, channel.topictime)) + +                                            # Determine if +s or +p modes are +                                            # set in channel +                                            secret = "s" in channel.modes.keys() and channel.modes[ +                                                "s"] +                                            private = "p" in channel.modes.keys( +                                            ) and channel.modes["p"] + +                                            # Construct NAMES for channel. +                                            namesusers = [] +                                            modes, symbols = self.IRC.supports[ +                                                "PREFIX"] +                                            self.connection.send(":%s 353 %s %s %s :%s\n" % ( +                                                self.IRC.serv, +                                                self.IRC.identity.nick, +                                                "@" if secret else ( +                                                    "*" if private else "="), +                                                channel.name, +                                                string.join([string.join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))], "") + user.nick for user in channel.users])) +                                            ) +                                            self.connection.send(":%s 366 %s %s :End of /NAMES list.\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, channel.name)) +                                else: +                                    self.send( +                                        ":*Bouncer* NOTICE %s :Not connected to server. Type /bncconnect to attempt connection.\n" % self.nick) +                                    self.send(":%s 001 %s :Welcome to the Bouncer IRC Network %s!%s@%s\n" % ( +                                        "*Bouncer*", self.nick, self.nick, self.username, self.host))                          else:  # User not found                              self.quit("Access Denied")                              break                      else:  # Client did not send USER command when expected                          self.quit("Access Denied") +                        print "*** [BouncerConnection] Incoming connection from %s failed: Expected USER." % (self.host)                          break                  elif cmd.upper() == "QUIT": @@ -621,60 +684,98 @@ class BouncerConnection (Thread):                      break                  elif cmd.upper() == "PING": -                    self.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick)) - -                elif cmd.upper() in ("PRIVMSG", "NOTICE"): -                    ### Check if CTCP -                    ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", -                                      extinfo) - -                    if ctcp:  # If CTCP, only want to -                        (ctcptype, ext) = ctcp[0]  # Unpack CTCP info - -                        if ctcptype == "LAGCHECK":  # Client is doing a lag check. No need to send to IRC network, just reply back. -                            self.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host, line)) +                    self.send(":%s PONG %s :%s\n" % +                              (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick if type(self.IRC.identity) == irc.User else "***")) + +                elif cmd.upper() == "BNCCONNECT": +                    with self.IRC.lock: +                        if self.IRC.isAlive() and self.IRC.connected: +                            self.send( +                                ":*Bouncer* NOTICE %s :Bouncer is already connected.\n" % self.nick)                          else: -                            self.IRC.raw(line, origin=self) -                    else: -                        self.IRC.raw(line, origin=self) - -                elif cmd.upper() == "MODE":  # Will want to determine is requesting modes, or attempting to modify modes. -                    if target and "CHANTYPES" in self.IRC.supports.keys() and target[0] in self.IRC.supports["CHANTYPES"]: -                        if params == "": -                            channel = self.IRC.channel(target) -                            modes = channel.modes.keys() -                            modestr = "".join([mode for mode in modes if mode not in self.IRC.supports["CHANMODES"][0]+self.IRC.supports["PREFIX"][0] and channel.modes[mode]]) -                            params = " ".join([channel.modes[mode] for mode in modes if mode in self.IRC.supports["CHANMODES"][1]+self.IRC.supports["CHANMODES"][2] and channel.modes[mode]]) -                            with self.lock: -                                if len(modestr): -                                    self.connection.send(":%s 324 %s %s +%s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params)) -                                if channel.created: -                                    self.connection.send(":%s 329 %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.created)) -                        elif re.match("^\\+?[%s]+$"%self.IRC.supports["CHANMODES"][0], params) and extinfo == "": -                            #print "ddd Mode List Request", params -                            channel = self.IRC.channel(target) -                            redundant = [] -                            for mode in params.lstrip("+"): -                                if mode in redundant or mode not in listnumerics.keys(): -                                    continue -                                i, e, l = listnumerics[mode] +                            self.IRC.start() + +                elif cmd.upper() == "BNCQUIT": +                    with self.IRC.lock: +                        if self.IRC.isAlive() and self.IRC.connected and self.IRC.registered: +                            quitmsg = " ".join( +                                [word for word in [target, params, extinfo] if word]) +                            self.IRC.quit(quitmsg) +                        else: +                            self.send( +                                ":*Bouncer* NOTICE %s :Bouncer is already disconnected.\n" % self.nick) + +                else: +                    with self.IRC.lock: +                        if not self.IRC.connected: +                            self.send( +                                ":*Bouncer* NOTICE %s :Not connected to server. Type /bncconnect to attempt connection.\n" % self.nick) +                            break +                        elif not self.IRC.registered: +                            self.send( +                                ":*Bouncer* NOTICE %s :Not registered.\n" % self.nick) +                            break +                        elif cmd.upper() in ("PRIVMSG", "NOTICE"): +                            # Check if CTCP +                            ctcp = re.findall( +                                "^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo) + +                            if ctcp:  # If CTCP, only want to +                                (ctcptype, ext) = ctcp[0]  # Unpack CTCP info + +                                if ctcptype == "LAGCHECK":  # Client is doing a lag check. No need to send to IRC network, just reply back. +                                    self.send(":%s!%s@%s %s\n" % ( +                                        self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host, line)) +                                else: +                                    self.IRC.raw(line, origin=self) +                            else: +                                self.IRC.raw(line, origin=self) + +                        elif cmd.upper() == "MODE":  # Will want to determine is requesting modes, or attempting to modify modes. +                            if target and "CHANTYPES" in self.IRC.supports.keys() and target[0] in self.IRC.supports["CHANTYPES"]: +                                if params == "": +                                    channel = self.IRC.channel(target) +                                    modes = channel.modes.keys() +                                    modestr = "".join([mode for mode in modes if mode not in self.IRC.supports[ +                                                      "CHANMODES"][0] + self.IRC.supports["PREFIX"][0] and channel.modes[mode]]) +                                    params = " ".join([channel.modes[mode] for mode in modes if mode in self.IRC.supports[ +                                                      "CHANMODES"][1] + self.IRC.supports["CHANMODES"][2] and channel.modes[mode]]) +                                    with self.lock: +                                        if len(modestr): +                                            self.connection.send(":%s 324 %s %s +%s %s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params)) +                                        if channel.created: +                                            self.connection.send(":%s 329 %s %s %s\n" % ( +                                                self.IRC.serv, self.IRC.identity.nick, channel.name, channel.created)) +                                elif re.match("^\\+?[%s]+$" % self.IRC.supports["CHANMODES"][0], params) and extinfo == "": +                                    # print "ddd Mode List Request", params +                                    channel = self.IRC.channel(target) +                                    redundant = [] +                                    for mode in params.lstrip("+"): +                                        if mode in redundant or mode not in listnumerics.keys(): +                                            continue +                                        i, e, l = listnumerics[mode] +                                        with self.lock: +                                            if mode in channel.modes.keys(): +                                                for (mask, setby, settime) in channel.modes[mode]: +                                                    self.connection.send(":%s %d %s %s %s %s %s\n" % ( +                                                        self.IRC.serv, i, channel.context.identity.nick, channel.name, mask, setby, settime)) +                                            self.connection.send(":%s %d %s %s :End of %s\n" % ( +                                                self.IRC.serv, e, channel.context.identity.nick, channel.name, l)) +                                        redundant.append(mode) +                                else: +                                    self.IRC.raw(line, origin=self) +                            elif params == "" and target.lower() == self.IRC.identity.nick.lower():                                  with self.lock: -                                    if mode in channel.modes.keys(): -                                        for (mask, setby, settime) in channel.modes[mode]: -                                            self.connection.send(":%s %d %s %s %s %s %s\n" % (self.IRC.serv, i, channel.context.identity.nick, channel.name, mask, setby, settime)) -                                    self.connection.send(":%s %d %s %s :End of %s\n" % (self.IRC.serv, e, channel.context.identity.nick, channel.name, l)) -                                redundant.append(mode) +                                    self.connection.send(":%s 221 %s +%s\n" % ( +                                        self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes)) +                                    if "s" in self.IRC.identity.modes and self.IRC.identity.snomask: +                                        self.connection.send(":%s 008 %s +%s :Server notice mask\n" % ( +                                            self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.snomask)) +                            else: +                                self.IRC.raw(line, origin=self)                          else:                              self.IRC.raw(line, origin=self) -                    elif params == "" and target.lower() == self.IRC.identity.nick.lower(): -                        with self.lock: -                            self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes)) -                            if "s" in self.IRC.identity.modes and self.IRC.identity.snomask: -                                self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.snomask)) -                    else: -                        self.IRC.raw(line, origin=self) -                else: -                    self.IRC.raw(line, origin=self)          except SystemExit:              pass  # No need to pass error message if break resulted from sys.exit() @@ -683,9 +784,10 @@ class BouncerConnection (Thread):              self.quitmsg = str(excmsg)              if self.IRC:                  exc, excmsg, tb = sys.exc_info() -                self.IRC.logwrite(*["!!! [BouncerConnection] Exception in thread %(self)s" % vars()]+["!!! [BouncerConnection] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")]) +                self.IRC.logwrite(*["!!! [BouncerConnection] Exception in thread %(self)s" % vars()] + [ +                                  "!!! [BouncerConnection] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])          finally: -            ### Juuuuuuust in case. +            # Juuuuuuust in case.              with self.lock:                  try:                      self.connection.shutdown(1) @@ -694,28 +796,33 @@ class BouncerConnection (Thread):                      pass              if self.IRC: -                self.IRC.logwrite("*** [BouncerConnection] Connection from %s terminated (%s)." % (self.host, self.quitmsg)) +                self.IRC.logwrite( +                    "*** [BouncerConnection] Connection from %s terminated (%s)." % (self.host, self.quitmsg))              if self in self.bouncer.connections:                  with self.bouncer.lock:                      self.bouncer.connections.remove(self) -                    if self.IRC.connected and self.IRC.identity and len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and not self.IRC.identity.away and self.bouncer.autoaway: -                        ### Bouncer automatically sets away status. -                        self.IRC.raw("AWAY :%s"%self.bouncer.autoaway) +                    if self.IRC.connected and self.IRC.identity and len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and self.IRC.registered and type(self.IRC.identity) == irc.User and not self.IRC.identity.away and self.bouncer.autoaway: +                        # Bouncer automatically sets away status. +                        self.IRC.raw("AWAY :%s" % self.bouncer.autoaway)                      if self.debug: -                        self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars()) +                        self.IRC.logwrite( +                            "dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars())                      for bouncerconnection in self.bouncer.connections:                          if bouncerconnection.IRC == self.IRC:                              if self.debug: -                                self.IRC.logwrite("dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars()) +                                self.IRC.logwrite( +                                    "dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars())                              if not bouncerconnection.quitting: -                                bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg)) +                                bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % ( +                                    bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg))                                  if self.debug: -                                    self.IRC.logwrite("dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars()) - -#                               ### Announce QUIT to other bouncer connections. -#                               for bouncerconnection in self.bouncer.connections: -#                                       try: -#                                               bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.username, self.host, self.quitmsg)) -#                                       except: -#                                               pass +                                    self.IRC.logwrite( +                                        "dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars()) + +# Announce QUIT to other bouncer connections. +#				for bouncerconnection in self.bouncer.connections: +#					try: +#						bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.username, self.host, self.quitmsg)) +#					except: +#						pass @@ -1,5 +1,5 @@  #!/usr/bin/python -from threading import Thread, Condition, Lock +from threading import Thread, Condition, Lock, currentThread  import re  import time  import sys @@ -10,6 +10,7 @@ import platform  import traceback  import ssl  import glob +from collections import deque  import iqueue as Queue @@ -105,6 +106,10 @@ class AlreadyJoined(BaseException):      pass +class AlreadyConnected(BaseException): +    pass + +  class RegistrationRequired(BaseException):      pass @@ -115,102 +120,164 @@ class RejoinDelay(BaseException):  _rfc1459casemapping = string.maketrans(string.ascii_uppercase + r'\[]~',                                         string.ascii_lowercase + r'|{}^') -exceptcodes = {489: SSLOnly, 384: Cbaned, 403: NoSuchChannel, 405: TooManyChannels, 442: NotOnChannel, 470: RedirectedJoin, 471: ChannelFull, 473: InviteOnly, 474: BannedFromChannel, 475: BadChannelKey, 476: BadChannelMask, 520: OpersOnly, 437: Unavailable, 477: RegistrationRequired, 495: RejoinDelay, 530: OperCreateOnly} +# The IRC RFC does not permit the first character in a nickname to be a +# numeral. However, this is not always adhered to. +_nickmatch = r"^[A-Za-z0-9\-\^\`\\\|\_\{\}\[\]]+$" + +_intmatch = r"^\d+$" +_chanmatch = r"^[%%s][^%s\s\n]*$" % re.escape("\x07,") +_targchanmatch = r"^([%%s]?)([%%s][^%s\s\n]*)$" % re.escape("\x07,") +_usermatch = r"^[A-Za-z0-9\-\^\`\\\|\_\{\}\[\]]+$" +_realnamematch = r"^[^\n]*$" +_ircrecvmatch = r"^:(.+?)(?:!(.+?)@(.+?))?\s+(.+?)(?:\s+(.+?)(?:\s+(.+?))??)??(?:\s+:(.*))?$" +_ircsendmatch = r"^(.+?)(?:\s+(.+?)(?:\s+(.+?))??)??(?:\s+:(.*))?$" +_ctcpmatch = "^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$" +_prefixmatch = r"\((.*)\)(.*)" + +_privmodeeventnames = dict(q=("Owner", "Deowner"), a=("Admin", "Deadmin"), o=( +    "Op", "Deop"), h=("Halfop", "Dehalfop"), v=("Voice", "Devoice")) +_maskmodeeventnames = dict(b=("Ban", "Unban"), e=( +    "BanExcept", "UnbanExcept"), I=("Invite", "Uninvite")) + +exceptcodes = {489: SSLOnly, 384: Cbaned, 403: NoSuchChannel, 405: TooManyChannels, 442: NotOnChannel, 470: RedirectedJoin, 471: ChannelFull, 473: InviteOnly, 474: +               BannedFromChannel, 475: BadChannelKey, 476: BadChannelMask, 520: OpersOnly, 437: Unavailable, 477: RegistrationRequired, 495: RejoinDelay, 530: OperCreateOnly}  def timestamp():      t = time.time() -    ms = 1000*t%1000 +    ms = 1000 * t % 1000      ymdhms = time.localtime(t)      tz = time.altzone if ymdhms.tm_isdst else time.timezone      sgn = "-" if tz >= 0 else "+" -    return "%04d-%02d-%02d %02d:%02d:%02d.%03d%s%02d:%02d"%(ymdhms[:6]+(1000*t%1000, sgn, abs(tz)/3600, abs(tz)/60%60)) +    return "%04d-%02d-%02d %02d:%02d:%02d.%03d%s%02d:%02d" % (ymdhms[:6] + (1000 * t % 1000, sgn, abs(tz) / 3600, abs(tz) / 60 % 60)) + +class Connection(object): -class Connection(Thread):      def __init__(self, server, nick="ircbot", username="python", realname="Python IRC Library", passwd=None, port=None, ipv6=False, ssl=False, autoreconnect=True, log=sys.stderr, timeout=300, retrysleep=5, maxretries=15, onlogin=None):          self.__name__ = "pyIRC" -        self.__version__ = "1.1" +        self.__version__ = "1.2"          self.__author__ = "Brian Sherson" -        self.__date__ = "December 1, 2013" +        self.__date__ = "December 28, 2013" -        if port is None: +        if port == None:              self.port = 6667 if not ssl else 6697          else:              self.port = port          if type(nick) in (str, unicode): -            self.nick = [nick] +            if re.match(_nickmatch, nick): +                self.nick = [nick] +            else: +                raise InvalidCharacter          elif type(nick) in (list, tuple): -            self.nick = nick +            if all([re.match(_nickmatch, n) for n in nick]): +                self.nick = nick +            else: +                raise InvalidCharacter + +        if re.match(_realnamematch, realname): +            self.realname = realname +        else: +            raise InvalidCharacter + +        if re.match(_usermatch, username): +            self.username = username +        else: +            raise InvalidCharacter + +        if passwd == None or "\n" not in passwd: +            self.passwd = passwd +        else: +            raise InvalidCharacter -        self.realname = realname -        self.username = username -        self.passwd = passwd          self.server = server          self.ssl = ssl          self.ipv6 = ipv6 -        self.connected = False -        self.registered = False -        self.connection = None -          self.autoreconnect = autoreconnect          self.maxretries = maxretries          self.timeout = timeout          self.retrysleep = retrysleep -        self.quitexpected = False +        self._quitexpected = False          self.log = log          self.addons = [] -        self.trusted = [] +        self.trusted = []  # To be implemented later + +        self.lock = Lock() + +        self._loglock = Lock() +        self._outlock = Lock() +        self._sendline = Condition(self._outlock) +        self._outgoing = deque() + +        self._sendhandlerthread = None +        self._recvhandlerthread = None + +        # Initialize IRC environment variables +        self._init() + +    def _init(self): +        self._connected = False +        self._registered = False +        self._connection = None +        self.trynick = 0 -        ### Initialize IRC environment variables -        self.motdgreet = None -        self.motd = None -        self.motdend = None          self.identity = None          self.users = []          self.channels = [] + +        self.motdgreet = None +        self.motd = None +        self.motdend = None + +        self.serv = None +        self.welcome = None +        self.hostinfo = None +        self.servcreated = None +        self.servinfo = None +        self.serv005 = None          self.supports = {} +        self.throttledata = [] +        self.throttled = False -        self.lock = Lock() -        self._linereceived = Condition(self.lock) -        self.loglock = Lock() -        self.sendlock = Lock() -        self.outgoing = Queue.Queue() -        self.outgoingthread = None +    @property +    def connected(self): +        return self._connected -        Thread.__init__(self) +    @property +    def registered(self): +        return self._registered      def logwrite(self, *lines): -        with self.loglock: +        with self._loglock:              ts = timestamp()              for line in lines: -                print >>self.log, "%s %s"%(ts, line) +                print >>self.log, "%s %s" % (ts, line)              self.log.flush()      def logopen(self, filename): -        with self.loglock: +        with self._loglock:              ts = timestamp()              newlog = open(filename, "a")              if type(self.log) == file and not self.log.closed: -                print >>self.log, "%s ### Log file closed" % (ts)                  if self.log not in (sys.stdout, sys.stderr): +                    print >>self.log, "%s ### Log file closed" % (ts)                      self.log.close()              self.log = newlog              print >>self.log, "%s ### Log file opened" % (ts)              self.log.flush() -    def event(self, method, modlist, exceptions=False, **params): -        timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, -                                                               "0") for t in time.localtime()[0:6]]) +    def _event(self, method, modlist, exceptions=False, **params): +        # Used to call event handlers on all attached addons, when applicable.          handled = []          unhandled = []          errors = []          for k, addon in enumerate(modlist):              if modlist.index(addon) < k: +                # Duplicate                  continue              if method in dir(addon) and callable(getattr(addon, method)):                  try: @@ -218,7 +285,10 @@ class Connection(Thread):                  except:                      exc, excmsg, tb = sys.exc_info()                      errors.append((addon, exc, excmsg, tb)) -                    self.logwrite(*["!!! Exception in addon %(addon)s" % vars()]+["!!! %s"%line for line in traceback.format_exc().split("\n")]) + +                    # Print to log AND stderr +                    self.logwrite(*["!!! Exception in addon %(addon)s" % vars()] + [ +                                  "!!! %s" % line for line in traceback.format_exc().split("\n")])                      print >>sys.stderr, "Exception in addon %(addon)s" % vars()                      print >>sys.stderr, traceback.format_exc()                      if exceptions:  # If set to true, we raise the exception. @@ -231,969 +301,1400 @@ class Connection(Thread):      def addAddon(self, addon, trusted=False, **params):          if addon in self.addons: -            raise BaseException("Addon already added.") +            raise BaseException, "Addon already added."          with self.lock: -            self.event("onAddonAdd", [addon], exceptions=True, **params) +            self._event("onAddonAdd", [addon], exceptions=True, **params)              self.addons.append(addon) +            self.logwrite("*** Addon %s added." % repr(addon))              if trusted:                  self.trusted.append(addon)      def insertAddon(self, index, addon, trusted=False, **params):          if addon in self.addons: -            raise BaseException("Addon already added.") +            raise BaseException, "Addon already added."          with self.lock: -            self.event("onAddonAdd", [addon], exceptions=True, **params) +            self._event("onAddonAdd", [addon], exceptions=True, **params)              self.addons.insert(index, addon) +            self.logwrite("*** Addon %s inserted into index %d." % +                          (repr(addon), index))              if trusted:                  self.trusted.append(addon)      def rmAddon(self, addon, **params):          with self.lock:              self.addons.remove(addon) -            self.event("onAddonRem", [addon], exceptions=True, **params) +            self.logwrite("*** Addon %s removed." % repr(addon)) +            self._event("onAddonRem", [addon], exceptions=True, **params)              if addon in self.trusted:                  self.trusted.remove(addon) -    def run(self): -        privmodeeventnames = dict(q=("Owner", "Deowner"), a=("Admin", "Deadmin"), o=("Op", "Deop"), h=("Halfop", "Dehalfop"), v=("Voice", "Devoice")) -        maskmodeeventnames = dict(b=("Ban", "Unban"), e=( -            "BanExcept", "UnbanExcept"), I=("Invite", "Uninvite")) -        self.quitexpected = False -        whoisstarted = False -        nameslist = [] -        wholist = [] -        lists = {} -        nameschan = None +    def connect(self): +        if self.isAlive(): +            raise AlreadyConnected +        with self.lock: +            self._recvhandlerthread = Thread(target=self._recvhandler) +            self._sendhandlerthread = Thread(target=self._sendhandler) +            self._recvhandlerthread.start() +            self._sendhandlerthread.start() + +    def _connect(self): +        with self.lock: +            if self._connected: +                raise AlreadyConnected          server = self.server          if self.ipv6 and ":" in server: -            server = "[%s]"%server +            server = "[%s]" % server          port = self.port + +        with self.lock: +            self.logwrite( +                "*** Attempting connection to %(server)s:%(port)s." % vars()) +            self._event("onConnectAttempt", self.addons + reduce( +                lambda x, y: x + y, [chan.addons for chan in self.channels], []))          try: +            if self.ssl: +                connection = socket.socket( +                    socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM) +                connection.settimeout(self.timeout) +                self._connection = ssl.wrap_socket( +                    connection, cert_reqs=ssl.CERT_NONE) +            else: +                self._connection = socket.socket( +                    socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM) +                self._connection.settimeout(self.timeout) +            self._connection.connect( +                (self.server, self.port, 0, 0) if self.ipv6 else (self.server, self.port)) +        except socket.error: +            exc, excmsg, tb = sys.exc_info()              with self.lock: -                self.event("onSessionOpen", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], [])) +                self.logwrite( +                    "*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()) +                self._event("onConnectFail", self.addons + reduce( +                    lambda x, y: x + y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb) +            raise -            self.logwrite("### Log session started") +        with self.lock: +            # Run onConnect on all addons to signal connection was established. +            self._event("onConnect", self.addons + reduce( +                lambda x, y: x + y, [chan.addons for chan in self.channels], [])) +            self.logwrite( +                "*** Connection to %(server)s:%(port)s established." % vars()) +            self._connected = True + +    def _procrecvline(self, line): +        # If received PING, then just pong back transparently, bypassing +        # _outgoingthread. +        ping = re.findall("^PING :?(.*)$", line) +        if len(ping): +            with self.lock: +                self._connection.send("PONG :%s\n" % ping[0]) +            return -            attempt = 1 -            while True:  # An entire connection lives within this while loop. When the connection fails, will try to reestablish, unless self.autoreconnect is set to False. -                while True:  # Enter retry loop -                    self.logwrite("*** Attempting connection to %(server)s:%(port)s." % vars()) +        self.logwrite("<<< %s" % line) -                    with self.lock: -                        self.event("onConnectAttempt", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], [])) +        # Attempts to match against pattern ":src cmd target params :extinfo" +        matches = re.findall(_ircrecvmatch, line) + +        # We have a match! +        if len(matches): +            parsed = (origin, username, host, cmd, +                      target, params, extinfo) = matches[0] +            unhandled = [] + +            if re.match(_intmatch, cmd): +                cmd = int(cmd)  # Code is a numerical response +            else: +                cmd = cmd.upper() -                        try: -                            if self.ssl: -                                s = socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM) -                                s.settimeout(self.timeout) -                                self.connection = ssl.wrap_socket(s, cert_reqs=ssl.CERT_NONE) +            with self.lock: +                if not self._registered: +                    if type(cmd) == int and cmd != 451 and target != "*":  # Registration complete! +                        self.identity = self.user(target) +                        self.serv = origin +                        self._event("onRegistered", self.addons + reduce( +                            lambda x, y: x + y, [chan.addons for chan in self.channels], [])) +                        self._registered = True + +                    elif cmd == 433 and target == "*":  # Server reports nick taken, so we need to try another. +                        self._trynick() +                    if not self._registered:  # Registration is not yet complete +                        return + +                if username and host: +                    nickname = origin +                    origin = self.user(origin) +                    if origin.nick != nickname: +                        # Origin nickname has changed +                        origin.user = nickname +                    if origin.username != username: +                        # Origin username has changed +                        origin.username = username +                    if origin.host != host: +                        # Origin host has changed +                        origin.host = host + +                chanmatch = re.findall( +                    _targchanmatch % (re.escape(self.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.supports.get("CHANTYPES", "#"))), target) +                if chanmatch: +                    targetprefix, channame = chanmatch[0] +                    target = self.channel(channame) +                    if target.name != channame: +                        # Target channel name has changed +                        target.name = channame +                elif re.match(_nickmatch, target) and cmd != "NICK": +                    targetprefix = "" +                    target = self.user(target) +                else: +                    targetprefix = "" + +                data = dict(origin=origin, cmd=cmd, target=target, +                            targetprefix=targetprefix, params=params, extinfo=extinfo) + +                # Major codeblock here! Track IRC state. +                # Send line to addons having onRecv method first +                self._event("onRecv", self.addons, line=line, **data) + +                # Support for further addon events is taken care of here. Each invocation of self._event will return (handled, unhandled, exceptions), +                # where handled is the list of addons that have an event handler, and was executed without error, unhandled gives the list of addons +                # not having the event handler, and exeptions giving the list of addons having an event handler, but an exception occurred. +                # WARNING: When writing an addon, never, EVER attempt to aquire self.lock (IRC.lock from inside the method), or you will have a +                # deadlock. + +                if cmd == 1: +                    (handled, unhandled, exceptions) = self._event("onWelcome", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) +                    self.welcome = extinfo  # Welcome message +                elif cmd == 2: +                    (handled, unhandled, exceptions) = self._event("onYourHost", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) +                    self.hostinfo = extinfo  # Your Host +                elif cmd == 3: +                    (handled, unhandled, exceptions) = self._event("onServerCreated", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) +                    self.servcreated = extinfo  # Server Created +                elif cmd == 4: +                    (handled, unhandled, exceptions) = self._event("onServInfo", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, servinfo=params) +                    self.servinfo = params  # What is this code? +                elif cmd == 5:  # Server Supports +                    support = dict( +                        re.findall("([A-Za-z0-9]+)(?:=(\\S*))?", params)) +                    if support.has_key("CHANMODES"): +                        support["CHANMODES"] = support["CHANMODES"].split(",") +                    if support.has_key("PREFIX"): +                        matches = re.findall(_prefixmatch, support["PREFIX"]) +                        if matches: +                            support["PREFIX"] = matches[0] +                        else: +                            del support[ +                                "PREFIX"]  # Might as well delete the info if it doesn't match expected pattern +                    (handled, unhandled, exceptions) = self._event("onSupports", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, supports=support) +                    self.supports.update(support) +                    if "serv005" in dir(self) and type(self.serv005) == list: +                        self.serv005.append(params) +                    else: +                        self.serv005 = [params] +                elif cmd == 8:  # Snomask +                    snomask = params.lstrip("+") +                    (handled, unhandled, exceptions) = self._event("onSnoMask", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, snomask=snomask) +                    self.identity.snomask = snomask +                    if "s" not in self.identity.modes: +                        self.snomask = "" +                elif cmd == 221:  # User Modes +                    modes = (params if params else extinfo).lstrip("+") +                    (handled, unhandled, exceptions) = self._event("onUserModes", self.addons + reduce( +                        lambda x, y: x + y, [chan.addons for chan in self.channels], []), origin=origin, snomask=modes) +                    self.identity.modes = modes +                    if "s" not in self.identity.modes: +                        self.snomask = "" +                elif cmd == 251:  # Net Stats +                    (handled, unhandled, exceptions) = self._event( +                        "onNetStats", self.addons, origin=origin, netstats=extinfo) +                    self.netstats = extinfo +                elif cmd == 252: +                    opcount = int(params) +                    (handled, unhandled, exceptions) = self._event( +                        "onOpCount", self.addons, origin=origin, opcount=opcount) +                    self.opcount = opcount +                elif cmd == 254: +                    chancount = int(params) +                    (handled, unhandled, exceptions) = self._event( +                        "onChanCount", self.addons, origin=origin, chancount=chancount) +                    self.chancount = chancount + +                elif cmd == 305:  # Returned from away status +                    (handled, unhandled, exceptions) = self._event( +                        "onReturn", self.addons, origin=origin, msg=extinfo) +                    self.identity.away = False + +                elif cmd == 306:  # Entered away status +                    (handled, unhandled, exceptions) = self._event( +                        "onAway", self.addons, origin=origin, msg=extinfo) +                    self.identity.away = True + +                elif cmd == 311:  # Start of WHOIS data +                    nickname, username, host, star = params.split() +                    user = self.user(nickname) +                    (handled, unhandled, exceptions) = self._event("onWhoisStart", self.addons, origin=origin, +                                                                   user=user, nickname=nickname, username=username, host=host, realname=extinfo) +                    user.nick = nickname +                    user.username = username +                    user.host = host + +                elif cmd == 301:  # Away Message +                    user = self.user(params) +                    (handled, unhandled, exceptions) = self._event( +                        "onWhoisAway", self.addons, origin=origin, user=user, nickname=params, awaymsg=extinfo) +                    user.away = True +                    user.awaymsg = extinfo + +                elif cmd == 303:  # ISON Reply +                    users = [self.user(user) for user in extinfo.split(" ")] +                    (handled, unhandled, exceptions) = self._event( +                        "onIsonReply", self.addons, origin=origin, isonusers=users) + +                elif cmd == 307:  # Is a registered nick +                    (handled, unhandled, exceptions) = self._event("onWhoisRegisteredNick", +                                                                   self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) +                elif cmd == 378:  # Connecting From +                    (handled, unhandled, exceptions) = self._event("onWhoisConnectingFrom", +                                                                   self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) +                elif cmd == 319:  # Channels +                    (handled, unhandled, exceptions) = self._event("onWhoisChannels", self.addons, +                                                                   origin=origin, user=self.user(params), nickname=params, chanlist=extinfo.split(" ")) +                elif cmd == 310:  # Availability +                    (handled, unhandled, exceptions) = self._event("onWhoisAvailability", +                                                                   self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) +                elif cmd == 312:  # Server +                    nickname, server = params.split(" ") +                    user = self.user(nickname) +                    (handled, unhandled, exceptions) = self._event("onWhoisServer", self.addons, +                                                                   origin=origin, user=user, nickname=nickname, server=server, servername=extinfo) +                    user.server = server +                elif cmd == 313:  # IRC Op +                    user = self.user(params) +                    (handled, unhandled, exceptions) = self._event( +                        "onWhoisOp", self.addons, origin=origin, user=user, nickname=params, msg=extinfo) +                    user.ircop = True +                    user.ircopmsg = extinfo +                elif cmd == 317:  # Idle and Signon times +                    nickname, idletime, signontime = params.split(" ") +                    user = self.user(nickname) +                    (handled, unhandled, exceptions) = self._event("onWhoisTimes", self.addons, origin=origin, +                                                                   user=user, nickname=nickname, idletime=int(idletime), signontime=int(signontime), msg=extinfo) +                    user.idlesince = int(time.time()) - int(idletime) +                    user.signontime = int(signontime) +                elif cmd == 671:  # SSL +                    user = self.user(params) +                    (handled, unhandled, exceptions) = self._event( +                        "onWhoisSSL", self.addons, origin=origin, user=user, nickname=params, msg=extinfo) +                    user.ssl = True +                elif cmd == 379:  # User modes +                    (handled, unhandled, exceptions) = self._event("onWhoisModes", self.addons, +                                                                   origin=origin, user=self.user(params), nickname=params, msg=extinfo) +                elif cmd == 330:  # Logged in as +                    nickname, loggedinas = params.split(" ") +                    user = self.user(nickname) +                    (handled, unhandled, exceptions) = self._event("onWhoisLoggedInAs", self.addons, +                                                                   origin=origin, user=user, nickname=nickname, loggedinas=loggedinas, msg=extinfo) +                    user.loggedinas = loggedinas +                elif cmd == 318:  # End of WHOIS +                    (handled, unhandled, exceptions) = self._event("onWhoisEnd", self.addons, +                                                                   origin=origin, user=self.user(params), nickname=params, msg=extinfo) + +                elif cmd == 321:  # Start LIST +                    (handled, unhandled, exceptions) = self._event( +                        "onListStart", self.addons, origin=origin, params=params, extinfo=extinfo) +                elif cmd == 322:  # LIST item +                    (chan, pop) = params.split(" ", 1) +                    (handled, unhandled, exceptions) = self._event("onListEntry", self.addons, +                                                                   origin=origin, channel=self.channel(chan), population=int(pop), extinfo=extinfo) +                elif cmd == 323:  # End of LIST +                    (handled, unhandled, exceptions) = self._event( +                        "onListEnd", self.addons, origin=origin, endmsg=extinfo) + +                elif cmd == 324:  # Channel Modes +                    modeparams = params.split() +                    channame = modeparams.pop(0) +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    if channel.name != channame: +                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name +                    setmodes = modeparams.pop(0) +                    modedelta = [] +                    for mode in setmodes: +                        if mode == "+": +                            continue +                        elif mode in self.supports["CHANMODES"][2]: +                            param = modeparams.pop(0) +                            modedelta.append(("+%s" % mode, param)) +                        elif mode in self.supports["CHANMODES"][3]: +                            modedelta.append(("+%s" % mode, None)) +                    (handled, unhandled, exceptions) = self._event( +                        "onChannelModes", self.addons + channel.addons, channel=channel, modedelta=modedelta) +                    for ((modeset, mode), param) in modedelta: +                        if mode in self.supports["CHANMODES"][2]: +                            channel.modes[mode] = param +                        elif mode in self.supports["CHANMODES"][3]: +                            channel.modes[mode] = True + +                elif cmd == 329:  # Channel created +                    channame, created = params.split() +                    created = int(created) +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onChanCreated", self.addons + channel.addons, channel=channel, created=created) +                    channel.created = int(created) + +                elif cmd == 332:  # Channel Topic +                    channame = params +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onTopic", self.addons + channel.addons, origin=origin, channel=channel, topic=extinfo) +                    channel.topic = extinfo + +                elif cmd == 333:  # Channel Topic info +                    (channame, nick, dt) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onTopicInfo", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, topicsetby=nick, topictime=int(dt)) +                    channel.topicsetby = nick +                    channel.topictime = int(dt) + +                elif cmd == 352:  # WHO reply +                    (channame, username, host, serv, +                     nick, flags) = params.split() +                    try: +                        (hops, realname) = extinfo.split(" ", 1) +                    except ValueError: +                        hops = extinfo +                        realname = None + +                    chantypes = self.supports.get("CHANTYPES", "&#+!") +                    if re.match(_chanmatch % re.escape(chantypes), channame): +                        channel = self.channel(channame) +                    else: +                        channel = None + +                    user = self.user(nick) + +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onWhoEntry", self.addons + channel.addons, origin=origin, channel=channel, +                                                                   user=user, channame=channame, username=username, host=host, serv=serv, nick=nick, flags=flags, hops=int(hops), realname=realname) +                    user.hops = hops +                    user.realname = realname +                    user.username = username +                    user.host = host +                    user.server = serv +                    user.away = "G" in flags +                    user.ircop = "*" in flags +                    if type(channel) == Channel: +                        if user not in channel.users: +                            channel.users.append(user) +                        if channel not in user.channels: +                            user.channels.append(channel) +                        for (mode, prefix) in zip(*self.supports["PREFIX"]): +                            if prefix in flags: +                                if mode in channel.modes.keys() and user not in channel.modes[mode]: +                                    channel.modes[mode].append(user) +                                elif mode not in channel.modes.keys(): +                                    channel.modes[mode] = [user] + +                elif cmd == 315:  # End of WHO reply +                    chantypes = self.supports.get("CHANTYPES", "&#+!") +                    if re.match(_chanmatch % re.escape(chantypes), params): +                        channel = self.channel(params) +                        (handled, unhandled, exceptions) = self._event( +                            "onWhoEnd", self.addons + channel.addons, origin=origin, param=params, endmsg=extinfo) +                    else: +                        (handled, unhandled, exceptions) = self._event( +                            "onWhoEnd", self.addons, origin=origin, param=params, endmsg=extinfo) + +                elif cmd == 353:  # NAMES reply +                    (flag, channame) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) + +                    if self.supports.has_key("PREFIX"): +                        names = re.findall( +                            r"([%s]*)([^@!\s]+)(?:!(\S+)@(\S+))?" % +                            re.escape(self.supports["PREFIX"][1]), extinfo) +                    else: +                        names = re.findall( +                            r"()([^@!\s]+)(?:!(\S+)@(\S+))?", extinfo) +                                           # Still put it into tuple form for +                                           # compatibility in the next +                                           # structure +                    (handled, unhandled, exceptions) = self._event("onNames", self.addons + channel.addons, +                                                                   origin=origin, channel=channel, flag=flag, channame=channame, nameslist=names) + +                    for (symbs, nick, username, host) in names: +                        user = self.user(nick) +                        if user.nick != nick: +                            user.nick = nick +                        if username and user.username != username: +                            user.username = username +                        if host and user.host != host: +                            user.host = host +                        with channel.lock: +                            if channel not in user.channels: +                                user.channels.append(channel) +                            if user not in channel.users: +                                channel.users.append(user) +                            if self.supports.has_key("PREFIX"): +                                for symb in symbs: +                                    mode = self.supports["PREFIX"][0][ +                                        self.supports["PREFIX"][1].index(symb)] +                                    if not channel.modes.has_key(mode): +                                        channel.modes[mode] = [user] +                                    elif user not in channel.modes[mode]: +                                        channel.modes[mode].append(user) + +                elif cmd == 366:  # End of NAMES reply +                    channel = self.channel(params) +                    (handled, unhandled, exceptions) = self._event("onNamesEnd", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, channame=params, endmsg=extinfo) + +                elif cmd == 372:  # MOTD line +                    (handled, unhandled, exceptions) = self._event( +                        "onMOTDLine", self.addons, origin=origin, motdline=extinfo) +                    self.motd.append(extinfo) +                elif cmd == 375:  # Begin MOTD +                    (handled, unhandled, exceptions) = self._event( +                        "onMOTDStart", self.addons, origin=origin, motdgreet=extinfo) +                    self.motdgreet = extinfo +                    self.motd = [] +                elif cmd == 376: +                    (handled, unhandled, exceptions) = self._event( +                        "onMOTDEnd", self.addons, origin=origin, motdend=extinfo) +                    self.motdend = extinfo  # End of MOTD + +                elif cmd == 386 and "q" in self.supports["PREFIX"][0]:  # Channel Owner (Unreal) +                    (channame, owner) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    if channel.name != channame: +                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name +                    user = self.user(owner) +                    if user.nick != owner: +                        user.nick = owner +                    if channel.modes.has_key("q"): +                        if user not in channel.modes["q"]: +                            channel.modes["q"].append(user) +                    else: +                        channel.modes["q"] = [user] + +                elif cmd == 388 and "a" in self.supports["PREFIX"][0]:  # Channel Admin (Unreal) +                    (channame, admin) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    if channel.name != channame: +                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name +                    user = self.user(admin) +                    if user.nick != admin: +                        user.nick = admin +                    if channel.modes.has_key("a"): +                        if user not in channel.modes["a"]: +                            channel.modes["a"].append(user) +                    else: +                        channel.modes["a"] = [user] + +                elif cmd == "NICK": +                    newnick = extinfo if len(extinfo) else target + +                    addons = reduce( +                        lambda x, y: x + y, [chan.addons for chan in origin.channels], []) +                    self._event("onRecv", addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onNickChange", self.addons + addons, user=origin, newnick=newnick) +                    if origin == self.identity: +                        (handled, unhandled, exceptions) = self._event( +                            "onMeNickChange", self.addons + addons, newnick=newnick) + +                    for u in self.users: +                        if u.nick.lower() == newnick.lower(): +                            self.users.remove( +                                u)  # Nick collision, safe to assume this orphaned user is offline, so we shall remove the old instance. +                            for channel in self.channels: +                                # If for some odd reason, the old user still +                                # appears common channels, then we will remove +                                # the user anyway. +                                if u in channel.users: +                                    channel.users.remove(u) +                    origin.nick = newnick + +                elif cmd == "JOIN": +                    channel = target if type( +                        target) == Channel else self.channel(extinfo) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onJoin", self.addons + channel.addons, user=origin, channel=channel) + +                    if origin == self.identity:  # This means the bot is entering the room, +                        # and will reset all the channel data, on the assumption that such data may have changed. +                        # Also, the bot must request modes +                        with channel._joining: +                            if channel._joinrequested: +                                channel._joinreply = cmd +                                channel._joining.notify() +                        channel.topic = "" +                        channel.topicmod = "" +                        channel.modes = {} +                        channel.users = [] +                        self._event( +                            "onMeJoin", self.addons + channel.addons, channel=channel) +                        self.raw("MODE %s" % channel.name) +                        self.raw("WHO %s" % channel.name) +                        if "CHANMODES" in self.supports.keys(): +                            self.raw( +                                "MODE %s :%s" % (channel.name, self.supports["CHANMODES"][0])) + +                    if channel not in origin.channels: +                        origin.channels.append(channel) +                    if origin not in channel.users: +                        channel.users.append(origin) + +                elif cmd == "KICK": +                    kicked = self.user(params) +                    if kicked.nick != params: +                        kicked.nick = params + +                    self._event("onRecv", target.addons, line=line, **data) +                    if origin == self.identity: +                        self._event( +                            "onMeKick", self.addons + target.addons, channel=target, kicked=kicked, kickmsg=extinfo) +                    if kicked == self.identity: +                        self._event("onMeKicked", self.addons + target.addons, +                                    kicker=origin, channel=target, kickmsg=extinfo) +                    (handled, unhandled, exceptions) = self._event("onKick", self.addons + +                                                                   target.addons, kicker=origin, channel=target, kicked=kicked, kickmsg=extinfo) + +                    if target in kicked.channels: +                        kicked.channels.remove(target) +                    if kicked in target.users: +                        target.users.remove(kicked) +                    if self.supports.has_key("PREFIX"): +                        for mode in self.supports["PREFIX"][0]: +                            if target.modes.has_key(mode) and kicked in target.modes[mode]: +                                target.modes[mode].remove(kicked) + +                elif cmd == "PART": +                    try: +                        self._event("onRecv", target.addons, line=line, **data) +                        if origin == self.identity: +                            with target	._parting: +                                if target._partrequested: +                                    target._partreply = cmd +                                    target._parting.notify() +                            self._event( +                                "onMePart", self.addons + target.addons, channel=target, partmsg=extinfo) +                        (handled, unhandled, exceptions) = self._event( +                            "onPart", self.addons + target.addons, user=origin, channel=target, partmsg=extinfo) + +                        if target in origin.channels: +                            origin.channels.remove(target) +                        if origin in target.users: +                            target.users.remove(origin) +                        if self.supports.has_key("PREFIX"): +                            for mode in self.supports["PREFIX"][0]: +                                if target.modes.has_key(mode) and origin in target.modes[mode]: +                                    target.modes[mode].remove(origin) +                    except: +                        print target +                        raise +                elif cmd == "QUIT": +                    channels = list(origin.channels) +                    addons = reduce( +                        lambda x, y: x + y, [chan.addons for chan in origin.channels], []) +                    self._event("onRecv", addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onQuit", self.addons + addons, user=origin, quitmsg=extinfo) +                    for channel in origin.channels: +                        with channel.lock: +                            if origin in channel.users: +                                channel.users.remove(origin) +                            if self.supports.has_key("PREFIX"): +                                for mode in self.supports["PREFIX"][0]: +                                    if channel.modes.has_key(mode) and origin in channel.modes[mode]: +                                        channel.modes[mode].remove(origin) +                    origin.channels = [] + +                elif cmd == "MODE": +                    if type(target) == Channel: +                        self._event("onRecv", target.addons, line=line, **data) +                        modedelta = [] +                        modeparams = params.split() +                        setmodes = modeparams.pop(0) +                        modeset = "+" +                        for mode in setmodes: +                            if mode in "+-": +                                modeset = mode                              else: -                                self.connection = socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM) -                            self.connection.connect((self.server, self.port, 0, 0) if self.ipv6 else (self.server, self.port)) -                        except socket.error: -                            exc, excmsg, tb = sys.exc_info() -                            self.event("onConnectFail", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb) -                            self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()) +                                if mode in self.supports["CHANMODES"][0] + self.supports["CHANMODES"][1]: +                                    param = modeparams.pop(0) +                                    modedelta.append( +                                        ("%s%s" % (modeset, mode), param)) +                                    if mode in _maskmodeeventnames.keys(): +                                        if modeset == "+": +                                            eventname = _maskmodeeventnames[ +                                                mode][0] +                                        if modeset == "-": +                                            eventname = _maskmodeeventnames[ +                                                mode][1] +                                        matchesbot = glob.fnmatch.fnmatch( +                                            "%s!%s@%s".lower() % (self.identity.nick, self.identity.username, self.identity.host), param.lower()) +                                        self._event( +                                            "on%s" % eventname, self.addons + target.addons, user=origin, channel=target, banmask=param) +                                        if matchesbot: +                                            self._event( +                                                "onMe%s" % eventname, self.addons + target.addons, user=origin, channel=target, banmask=param) +                                elif mode in self.supports["CHANMODES"][2]: +                                    if modeset == "+": +                                        param = modeparams.pop(0) +                                        modedelta.append( +                                            ("%s%s" % (modeset, mode), param)) +                                    else: +                                        modedelta.append( +                                            ("%s%s" % (modeset, mode), None)) +                                elif mode in self.supports["CHANMODES"][3]: +                                    modedelta.append( +                                        ("%s%s" % (modeset, mode), None)) +                                elif self.supports.has_key("PREFIX") and mode in self.supports["PREFIX"][0]: +                                    modenick = modeparams.pop(0) +                                    modeuser = self.user(modenick) +                                    if mode in _privmodeeventnames.keys(): +                                        if modeset == "+": +                                            eventname = _privmodeeventnames[ +                                                mode][0] +                                        if modeset == "-": +                                            eventname = _privmodeeventnames[ +                                                mode][1] +                                        self._event( +                                            "on%s" % eventname, self.addons + target.addons, user=origin, channel=target, modeuser=modeuser) +                                        if modeuser == self.identity: +                                            self._event( +                                                "onMe%s" % eventname, self.addons + target.addons, user=origin, channel=target) +                                    modedelta.append( +                                        ("%s%s" % (modeset, mode), modeuser)) +                        (handled, unhandled, exceptions) = self._event( +                            "onChanModeSet", self.addons + target.addons, user=origin, channel=target, modedelta=modedelta) +                        with target.lock: +                            for ((modeset, mode), param) in modedelta: +                                if mode in self.supports["CHANMODES"][0]: +                                    if modeset == "+": +                                        if target.modes.has_key(mode): +                                            if param.lower() not in [mask.lower() for (mask, setby, settime) in target.modes[mode]]: +                                                target.modes[mode].append( +                                                    (param, origin, int(time.time()))) +                                        else: +                                            target.modes[mode] = [ +                                                (param, origin, int(time.time()))] +                                    else: +                                        if mode in target.modes.keys(): +                                            if mode == "b":  # Inspircd mode is case insentive when unsetting the mode +                                                masks = [ +                                                    mask.lower() for (mask, setby, settime) in target.modes[mode]] +                                                if param.lower() in masks: +                                                    index = masks.index( +                                                        param.lower()) +                                                    # print "Index: %d"%index +                                                    del target.modes[ +                                                        mode][index] +                                            else: +                                                masks = [ +                                                    mask for (mask, setby, settime) in target.modes[mode]] +                                                if param in masks: +                                                    index = masks.index(param) +                                                    del target.modes[ +                                                        mode][index] +                                elif mode in self.supports["CHANMODES"][1]: +                                    if modeset == "+": +                                        target.modes[mode] = param +                                    else: +                                        target.modes[mode] = None +                                elif mode in self.supports["CHANMODES"][2]: +                                    if modeset == "+": +                                        target.modes[mode] = param +                                    else: +                                        target.modes[mode] = None +                                elif mode in self.supports["CHANMODES"][3]: +                                    if modeset == "+": +                                        target.modes[mode] = True +                                    else: +                                        target.modes[mode] = False +                                elif self.supports.has_key("PREFIX") and mode in self.supports["PREFIX"][0]: +                                    if modeset == "+": +                                        if target.modes.has_key(mode) and param not in target.modes[mode]: +                                            target.modes[mode].append(param) +                                        if not target.modes.has_key(mode): +                                            target.modes[mode] = [param] +                                    elif target.modes.has_key(mode) and param in target.modes[mode]: +                                        target.modes[mode].remove(param) +                    elif type(target) == User: +                        modeparams = (params if params else extinfo).split() +                        setmodes = modeparams.pop(0) +                        modeset = "+" +                        for mode in setmodes: +                            if mode in "+-": +                                modeset = mode +                                continue +                            if modeset == "+": +                                if mode not in target.modes: +                                    target.modes += mode +                                if mode == "s" and len(modeparams): +                                    snomask = modeparams.pop(0) +                                    for snomode in snomask: +                                        if snomode in "+-": +                                            snomodeset = snomode +                                            continue +                                        if snomodeset == "+" and snomode not in target.snomask: +                                            target.snomask += snomode +                                        if snomodeset == "-" and snomode in target.snomask: +                                            target.snomask = target.snomask.replace( +                                                snomode, "") +                            if modeset == "-": +                                if mode in target.modes: +                                    target.modes = target.modes.replace( +                                        mode, "") +                                if mode == "s": +                                    target.snomask = "" + +                elif cmd == "TOPIC": +                    self._event("onRecv", target.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onTopicSet", self.addons + target.addons, user=origin, channel=target, topic=extinfo) + +                    with target.lock: +                        target.topic = extinfo +                        target.topicsetby = origin +                        target.topictime = int(time.time()) + +                elif cmd == "INVITE": +                    channel = self.channel(extinfo if extinfo else params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onInvite", self.addons + channel.addons, user=origin, channel=channel) + +                elif cmd == "PRIVMSG": +                    if type(target) == Channel: +                        self._event("onRecv", target.addons, line=line, **data) + +                    # CTCP handling +                    ctcp = re.findall(_ctcpmatch, extinfo) +                    if ctcp: +                        (ctcptype, ext) = ctcp[0] +                        if ctcptype.upper() == "ACTION": +                            if type(target) == Channel: +                                (handled, unhandled, exceptions) = self._event( +                                    "onChanAction", self.addons + target.addons, user=origin, channel=target, targetprefix=targetprefix, action=ext) +                            elif target == self.identity: +                                (handled, unhandled, exceptions) = self._event( +                                    "onPrivAction", self.addons, user=origin, action=ext)                          else: -                            self.connected = True -                            self.connection.settimeout(self.timeout) +                            if type(target) == Channel: +                                (handled, unhandled, exceptions) = self._event("onChanCTCP", self.addons + target.addons, +                                                                               user=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext) +                            elif target == self.identity: +                                (handled, unhandled, exceptions) = self._event( +                                    "onPrivCTCP", self.addons, user=origin, ctcptype=ctcptype, params=ext) +                        if ctcptype.upper() == "VERSION": +                            origin.ctcpreply("VERSION", self.ctcpversion()) +                        if ctcptype.upper() == "TIME": +                            tformat = time.ctime() +                            tz = time.tzname[0] +                            origin.ctcpreply( +                                "TIME", "%(tformat)s %(tz)s" % vars()) +                        if ctcptype.upper() == "PING": +                            origin.ctcpreply("PING", "%(ext)s" % vars()) +                        if ctcptype.upper() == "FINGER": +                            origin.ctcpreply("FINGER", "%(ext)s" % vars()) +                    else: +                        if type(target) == Channel: +                            (handled, unhandled, exceptions) = self._event( +                                "onChanMsg", self.addons + target.addons, user=origin, channel=target, targetprefix=targetprefix, msg=extinfo) +                        elif target == self.identity: +                            (handled, unhandled, exceptions) = self._event( +                                "onPrivMsg", self.addons, user=origin, msg=extinfo) + +                elif cmd == "NOTICE": +                    if type(target) == Channel: +                        self._event("onRecv", target.addons, line=line, **data) + +                    # CTCP handling +                    ctcp = re.findall(_ctcpmatch, extinfo) +                    if ctcp and target == self.identity: +                        (ctcptype, ext) = ctcp[0] +                        (handled, unhandled, exceptions) = self._event( +                            "onCTCPReply", self.addons, origin=origin, ctcptype=ctcptype, params=ext) +                    else: +                        if type(target) == Channel: +                            (handled, unhandled, exceptions) = self._event( +                                "onChanNotice", self.addons + target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo) +                        elif target == self.identity: +                            (handled, unhandled, exceptions) = self._event( +                                "onPrivNotice", self.addons, origin=origin, msg=extinfo) + +                elif cmd == 367:  # Channel Ban list +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onBanListEntry", self.addons + channel.addons, +                                                                   origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "b" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]: +                            channel.modes["b"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["b"] = [(mask, setby, int(settime))] +                elif cmd == 368: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onBanListEnd", self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 346:  # Channel Invite list +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onInviteListEntry", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "I" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["I"]]: +                            channel.modes["I"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["I"] = [(mask, setby, int(settime))] +                elif cmd == 347: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onInviteListEnd", self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 348:  # Channel Ban Exception list +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onBanExceptListEntry", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "e" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["e"]]: +                            channel.modes["e"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["e"] = [(mask, setby, int(settime))] +                elif cmd == 349: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onBanExceptListEnd", +                                                                   self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 910:  # Channel Access List +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onAccessListEntry", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "w" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]: +                            channel.modes["w"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["w"] = [(mask, setby, int(settime))] +                elif cmd == 911: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onAccessListEnd", self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 941:  # Spam Filter list +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onSpamfilterListEntry", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "g" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["g"]]: +                            channel.modes["g"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["g"] = [(mask, setby, int(settime))] +                elif cmd == 940: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onSpamfilterListEnd", +                                                                   self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 954:  # Channel exemptchanops list +                    (channame, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onExemptChanOpsListEntry", self.addons + +                                                                   channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) +                    if "X" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["X"]]: +                            channel.modes["X"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["X"] = [(mask, setby, int(settime))] +                elif cmd == 953: +                    channel = self.channel(params) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onExemptChanOpsListEnd", +                                                                   self.addons + channel.addons, origin=origin, channel=channel, endmsg=extinfo) + +                elif cmd == 728:  # Channel quiet list +                    (channame, modechar, mask, setby, settime) = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event("onQuietListEntry", self.addons + channel.addons, +                                                                   origin=origin, channel=channel, modechar=modechar, mask=mask, setby=setby, settime=int(settime)) +                    if "q" in channel.modes.keys(): +                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["q"]]: +                            channel.modes["q"].append( +                                (mask, setby, int(settime))) +                    else: +                        channel.modes["q"] = [(mask, setby, int(settime))] +                elif cmd == 729: +                    channame, modechar = params.split() +                    channel = self.channel(channame) +                    self._event("onRecv", channel.addons, line=line, **data) +                    (handled, unhandled, exceptions) = self._event( +                        "onQuietListEnd", self.addons + channel.addons, channel=channel, endmsg=extinfo) + +                elif cmd in (495, 384, 385, 386, 468, 470, 366, 315, 482, 484, 953, 368, 482, 349, 940, 911, 489, 490, 492, 520, 530):  # Channels which appear in params +                    for param in params.split(): +                        if len(param) and param[0] in self.supports["CHANTYPES"]: +                            channel = self.channel(param) +                            self._event( +                                "onRecv", channel.addons, line=line, **data) + +                elif type(cmd) == int: +                    (handled, unhandled, exceptions) = self._event("on%03d" % +                                                                   cmd, self.addons, line=line, origin=origin, target=target, params=params, extinfo=extinfo) +                else: +                    (handled, unhandled, exceptions) = self._event("on%s" % +                                                                   cmd, self.addons, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo) -                            ### Setting up thread responsible for sending data back to IRC server. -                            self.outgoing._interrupted = False -                            self.outgoingthread = Outgoing(self) -                            self.outgoingthread.daemon = True -                            self.outgoingthread.start() +                if cmd in (384, 403, 405, 471, 473, 474, 475, 476, 520, 477, 489, 495):  # Channel Join denied +                    try: +                        channel = self.channel(params) +                    except InvalidName: +                        pass +                    else: +                        with channel._joining: +                            if channel._joinrequested: +                                channel._joinreply = (cmd, extinfo) +                                channel._joining.notify() -                            ### Run onConnect on all addons to signal connection was established. -                            self.event("onConnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], [])) -                            self.logwrite("*** Connection to %(server)s:%(port)s established." % vars()) -                            break +                elif cmd == 470:  # Channel Join denied due to redirect +                    channelname, redirect = params.split() +                    try: +                        channel = self.channel(channelname) +                    except InvalidName: +                        pass +                    else: +                        with channel._joining: +                            if channel._joinrequested: +                                channel._joinreply = ( +                                    cmd, "%s (%s)" % (extinfo, redirect)) +                                channel._joining.notify() + +                # Handle events that were not handled. +                self._event("onUnhandled", unhandled, line=line, origin=origin, +                            cmd=cmd, target=target, params=params, extinfo=extinfo) + +    def _trynick(self): +        (q, s) = divmod(self.trynick, len(self.nick)) +        nick = self.nick[s] +        if q > 0: +            nick = "%s%d" % (nick, q) +        self._send("NICK %s" % nick) +        self.trynick += 1 + +    def _recvhandler(self): +        # Enforce that this function must only be run from within +        # self._sendhandlerthread. +        if currentThread() != self._recvhandlerthread: +            raise RuntimeError, "This function is designed to run in its own thread." +        server = self.server +        if self.ipv6 and ":" in server: +            server = "[%s]" % server +        port = self.port -                    if self.quitexpected: -                        sys.exit() -                    if attempt < self.maxretries or self.maxretries == -1: -                        time.sleep(self.retrysleep) -                        if self.quitexpected: +        try: +            with self.lock: +                self._event("onSessionOpen", self.addons + reduce( +                    lambda x, y: x + y, [chan.addons for chan in self.channels], [])) + +            self.logwrite("### Log session started") +            while True:  # Autoreconnect loop +                attempt = 1 +                while True:  # Autoretry loop +                    try: +                        self._connect() +                        break +                    except socket.error: +                        if self._quitexpected: +                            sys.exit() +                        if attempt < self.maxretries or self.maxretries < 0: +                            if self.retrysleep > 0: +                                time.sleep(self.retrysleep) +                            if self._quitexpected: +                                sys.exit() +                            attempt += 1 +                        else: +                            self.logwrite( +                                "*** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars())                              sys.exit() -                        attempt += 1 -                    else: -                        self.logwrite("*** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars()) -                        sys.exit() -                ### Connection succeeded +                # Connection succeeded                  try: -                    ### Attempt initial registration. +                    with self._sendline: +                        self._sendline.notify() + +                    # Attempt initial registration.                      nick = self.nick[0] -                    trynick = 0                      if self.passwd: -                        self.raw("PASS :%s" % self.passwd.split( -                            "\n")[0].rstrip()) -                    self.raw("NICK :%s" % nick.split("\n")[0].rstrip()) -                    self.raw("USER %s * * :%s" % (self.username.split("\n")[0].rstrip(), self.realname.split("\n")[0].rstrip())) +                        self._send("PASS %s" % self.passwd) +                    self._trynick() +                    self._send("USER %s * * :%s" % +                               (self.username.split("\n")[0].rstrip(), self.realname.split("\n")[0].rstrip())) -                    ### Initialize buffers +                    # Initialize buffers                      linebuf = []                      readbuf = ""                      while True:  # Main loop of IRC connection.                          while len(linebuf) == 0:  # Need Moar Data -                            read = self.connection.recv(512) +                            read = self._connection.recv(512) -                            ### If read was empty, connection is terminated. +                            # If read was empty, connection is terminated.                              if read == "":                                  sys.exit() -                            ### If read was successful, parse away! +                            # If read was successful, parse away!                              readbuf += read                              lastlf = readbuf.rfind("\n")                              if lastlf >= 0: -                                linebuf.extend(string.split(readbuf[0:lastlf], -                                                            "\n")) -                                readbuf = readbuf[lastlf+1:] +                                linebuf.extend( +                                    string.split(readbuf[0:lastlf], "\n")) +                                readbuf = readbuf[lastlf + 1:]                          line = string.rstrip(linebuf.pop(0)) +                        self._procrecvline(line) -                        ### If received PING, then just pong back transparently. -                        ping = re.findall("^PING :?(.*)$", line) -                        if len(ping): -                            with self.lock: -                                self.connection.send("PONG :%s\n" % ping[0]) -                            continue - -                        self.logwrite("<<< %(line)s" % vars()) - -                        ### Attempts to match against pattern ":src cmd target params :extinfo" -                        matches = re.findall(r"^:(.+?)(?:!(.+?)@(.+?))?\s+(.+?)(?:\s+(.+?)(?:\s+(.+?))??)??(?:\s+:(.*))?$", line) - -                        ### We have a match! -                        if len(matches): -                            parsed = (origin, username, host, cmd, target, params, extinfo) = matches[0] -                            unhandled = [] - -                            if re.match("^\\d+$", cmd): -                                cmd = int(cmd)  # Code is a numerical response -                            else: -                                cmd = cmd.upper() - -                            with self._linereceived: -                                if not self.registered: -                                    if type(cmd) == int and cmd != 451 and target != "*":  # Registration complete! -                                        self.registered = True -                                        self.identity = self.user(target) -                                        self.serv = origin -                                        self.event("onRegistered", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], [])) - -                                    elif cmd == 433 and target == "*":  # Server reports nick taken, so we need to try another. -                                        trynick += 1 -                                        (q, s) = divmod(trynick, len(self.nick)) -                                        nick = self.nick[s] -                                        if q > 0: -                                            nick += str(q) -                                        self.raw("NICK :%s" % nick.split("\n")[0].rstrip()) -                                    if not self.registered:  # Registration is not yet complete -                                        continue - -                                if username and host: -                                    nickname = origin -                                    origin = self.user(origin) -                                    if origin.nick != nickname: -                                        ### Origin nickname has changed -                                        origin.user = nickname -                                    if origin.username != username: -                                        ### Origin username has changed -                                        origin.username = username -                                    if origin.host != host: -                                        ### Origin host has changed -                                        origin.host = host - -                                chanmatch = re.findall(r"([%s]?)([%s]\S*)"%(re.escape(self.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.supports.get("CHANTYPES", "#"))), target) -                                if chanmatch: -                                    targetprefix, channame = chanmatch[0] -                                    target = self.channel(channame) -                                    if target.name != channame: -                                        ### Target channel name has changed -                                        target.name = channame -                                elif len(target) and target[0] != "$" and cmd != "NICK": -                                    targetprefix = "" -                                    target = self.user(target) - -                                self.data = data = dict(origin=origin, cmd=cmd, target=target, targetprefix=targetprefix, params=params, extinfo=extinfo) -                                self._linereceived.notifyAll() - -                                ### Major codeblock here! Track IRC state. -                                ### Send line to addons first -                                self.event("onRecv", self.addons, line=line, -                                           **data) -                                if cmd == 1: -                                    (handled, unhandled, exceptions) = self.event("onWelcome", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) -                                    self.welcome = extinfo  # Welcome message -                                elif cmd == 2: -                                    (handled, unhandled, exceptions) = self.event("onYourHost", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) -                                    self.hostinfo = extinfo  # Your Host -                                elif cmd == 3: -                                    (handled, unhandled, exceptions) = self.event("onServerCreated", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo) -                                    self.servcreated = extinfo  # Server Created -                                elif cmd == 4: -                                    (handled, unhandled, exceptions) = self.event("onServInfo", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, servinfo=params) -                                    self.servinfo = params  # What is this code? -                                elif cmd == 5:  # Server Supports -                                    support = dict(re.findall("([A-Za-z0-9]+)(?:=(\\S*))?", params)) -                                    if "CHANMODES" in support: -                                        support["CHANMODES"] = support["CHANMODES"].split(",") -                                    if "PREFIX" in support: -                                        matches = re.findall("\\((.*)\\)(.*)", support["PREFIX"]) -                                        if matches: -                                            support["PREFIX"] = matches[0] -                                        else: -                                            del support["PREFIX"]  # Might as well delete the info if it doesn't match expected pattern -                                    (handled, unhandled, exceptions) = self.event("onSupports", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, supports=support) -                                    self.supports.update(support) -                                    if "serv005" in dir(self) and type(self.serv005) == list: -                                        self.serv005.append(params) -                                    else: -                                        self.serv005 = [params] -                                elif cmd == 8:  # Snomask -                                    snomask = params.lstrip("+") -                                    (handled, unhandled, exceptions) = self.event("onSnoMask", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=snomask) -                                    self.identity.snomask = snomask -                                    if "s" not in self.identity.modes: -                                        self.snomask = "" -                                elif cmd == 221:  # User Modes -                                    modes = (params if params else extinfo).lstrip("+") -                                    (handled, unhandled, exceptions) = self.event("onUserModes", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=modes) -                                    self.identity.modes = modes -                                    if "s" not in self.identity.modes: -                                        self.snomask = "" -                                elif cmd == 251:  # Net Stats -                                    (handled, unhandled, exceptions) = self.event("onNetStats", self.addons, origin=origin, netstats=extinfo) -                                    self.netstats = extinfo -                                elif cmd == 252: -                                    opcount = int(params) -                                    (handled, unhandled, exceptions) = self.event("onOpCount", self.addons, origin=origin, opcount=opcount) -                                    self.opcount = opcount -                                elif cmd == 254: -                                    chancount = int(params) -                                    (handled, unhandled, exceptions) = self.event("onChanCount", self.addons, origin=origin, chancount=chancount) -                                    self.chancount = chancount - -                                elif cmd == 305:  # Returned from away status -                                    (handled, unhandled, exceptions) = self.event("onReturn", self.addons, origin=origin, msg=extinfo) -                                    self.identity.away = False - -                                elif cmd == 306:  # Entered away status -                                    (handled, unhandled, exceptions) = self.event("onAway", self.addons, origin=origin, msg=extinfo) -                                    self.identity.away = True - -                                elif cmd == 311:  # Start of WHOIS data -                                    nickname, username, host, star = params.split() -                                    user = self.user(nickname) -                                    (handled, unhandled, exceptions) = self.event("onWhoisStart", self.addons, origin=origin, user=user, nickname=nickname, username=username, host=host, realname=extinfo) -                                    user.nick = nickname -                                    user.username = username -                                    user.host = host - -                                elif cmd == 301:  # Away Message -                                    user = self.user(params) -                                    (handled, unhandled, exceptions) = self.event("onWhoisAway", self.addons, origin=origin, user=user, nickname=params, awaymsg=extinfo) -                                    user.away = True -                                    user.awaymsg = extinfo - -                                elif cmd == 303:  # ISON Reply -                                    users = [self.user(user) for user in extinfo.split(" ")] -                                    (handled, unhandled, exceptions) = self.event("onIsonReply", self.addons, origin=origin, isonusers=users) - -                                elif cmd == 307:  # Is a registered nick -                                    (handled, unhandled, exceptions) = self.event("onWhoisRegisteredNick", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) -                                elif cmd == 378:  # Connecting From -                                    (handled, unhandled, exceptions) = self.event("onWhoisConnectingFrom", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) -                                elif cmd == 319:  # Channels -                                    (handled, unhandled, exceptions) = self.event("onWhoisChannels", self.addons, origin=origin, user=self.user(params), nickname=params, chanlist=extinfo.split(" ")) -                                elif cmd == 310:  # Availability -                                    (handled, unhandled, exceptions) = self.event("onWhoisAvailability", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) -                                elif cmd == 312:  # Server -                                    nickname, server = params.split(" ") -                                    user = self.user(nickname) -                                    (handled, unhandled, exceptions) = self.event("onWhoisServer", self.addons, origin=origin, user=user, nickname=nickname, server=server, servername=extinfo) -                                    user.server = server -                                elif cmd == 313:  # IRC Op -                                    user = self.user(params) -                                    (handled, unhandled, exceptions) = self.event("onWhoisOp", self.addons, origin=origin, user=user, nickname=params, msg=extinfo) -                                    user.ircop = True -                                    user.ircopmsg = extinfo -                                elif cmd == 317:  # Idle and Signon times -                                    nickname, idletime, signontime = params.split(" ") -                                    user = self.user(nickname) -                                    (handled, unhandled, exceptions) = self.event("onWhoisTimes", self.addons, origin=origin, user=user, nickname=nickname, idletime=int(idletime), signontime=int(signontime), msg=extinfo) -                                    user.idlesince = int(time.time())-int(idletime) -                                    user.signontime = int(signontime) -                                elif cmd == 671:  # SSL -                                    user = self.user(params) -                                    (handled, unhandled, exceptions) = self.event("onWhoisSSL", self.addons, origin=origin, user=user, nickname=params, msg=extinfo) -                                    user.ssl = True -                                elif cmd == 379:  # User modes -                                    (handled, unhandled, exceptions) = self.event("onWhoisModes", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) -                                elif cmd == 330:  # Logged in as -                                    nickname, loggedinas = params.split(" ") -                                    user = self.user(nickname) -                                    (handled, unhandled, exceptions) = self.event("onWhoisLoggedInAs", self.addons, origin=origin, user=user, nickname=nickname, loggedinas=loggedinas, msg=extinfo) -                                    user.loggedinas = loggedinas -                                elif cmd == 318:  # End of WHOIS -                                    (handled, unhandled, exceptions) = self.event("onWhoisEnd", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo) - -                                elif cmd == 321:  # Start LIST -                                    (handled, unhandled, exceptions) = self.event("onListStart", self.addons, origin=origin, params=params, extinfo=extinfo) -                                elif cmd == 322:  # LIST item -                                    (chan, pop) = params.split(" ", 1) -                                    (handled, unhandled, exceptions) = self.event("onListEntry", self.addons, origin=origin, channel=self.channel(chan), population=int(pop), extinfo=extinfo) -                                elif cmd == 323:  # End of LIST -                                    (handled, unhandled, exceptions) = self.event("onListEnd", self.addons, origin=origin, endmsg=extinfo) - -                                elif cmd == 324:  # Channel Modes -                                    modeparams = params.split() -                                    channame = modeparams.pop(0) -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    if channel.name != channame: -                                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name -                                    setmodes = modeparams.pop(0) -                                    modedelta = [] -                                    for mode in setmodes: -                                        if mode == "+": -                                            continue -                                        elif mode in self.supports["CHANMODES"][2]: -                                            param = modeparams.pop(0) -                                            modedelta.append(("+%s"%mode, param)) -                                        elif mode in self.supports["CHANMODES"][3]: -                                            modedelta.append(("+%s"%mode, None)) -                                    (handled, unhandled, exceptions) = self.event("onChannelModes", self.addons+channel.addons, channel=channel, modedelta=modedelta) -                                    for ((modeset, mode), param) in modedelta: -                                        if mode in self.supports["CHANMODES"][2]: -                                            channel.modes[mode] = param -                                        elif mode in self.supports["CHANMODES"][3]: -                                            channel.modes[mode] = True - -                                elif cmd == 329:  # Channel created -                                    channame, created = params.split() -                                    created = int(created) -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onChanCreated", self.addons+channel.addons, channel=channel, created=created) -                                    channel.created = int(created) - -                                elif cmd == 332:  # Channel Topic -                                    channame = params -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onTopic", self.addons+channel.addons, origin=origin, channel=channel, topic=extinfo) -                                    channel.topic = extinfo - -                                elif cmd == 333:  # Channel Topic info -                                    (channame, nick, dt) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onTopicInfo", self.addons+channel.addons, origin=origin, channel=channel, topicsetby=nick, topictime=int(dt)) -                                    channel.topicsetby = nick -                                    channel.topictime = int(dt) - -                                elif cmd == 352:  # WHO reply -                                    (channame, username, host, serv, nick, flags) = params.split() -                                    try: -                                        (hops, realname) = extinfo.split(" ", 1) -                                    except ValueError: -                                        hops = extinfo -                                        realname = None - -                                    if channame[0] in self.supports.get("CHANTYPES", "#"): -                                        channel = self.channel(channame) -                                    else: -                                        channel = None - -                                    user = self.user(nick) - -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onWhoEntry", self.addons+channel.addons, origin=origin, channel=channel, user=user, channame=channame, username=username, host=host, serv=serv, nick=nick, flags=flags, hops=int(hops), realname=realname) -                                    user.hops = hops -                                    user.realname = realname -                                    user.username = username -                                    user.host = host -                                    user.server = serv -                                    user.away = "G" in flags -                                    user.ircop = "*" in flags -                                    if type(channel) == Channel: -                                        if user not in channel.users: -                                            channel.users.append(user) -                                        if channel not in user.channels: -                                            user.channels.append(channel) -                                        for (mode, prefix) in zip(*self.supports["PREFIX"]): -                                            if prefix in flags: -                                                if mode in channel.modes.keys() and user not in channel.modes[mode]: -                                                    channel.modes[mode].append(user) -                                                elif mode not in channel.modes.keys(): -                                                    channel.modes[mode] = [user] - -                                elif cmd == 315:  # End of WHO reply -                                    (handled, unhandled, exceptions) = self.event("onWhoEnd", self.addons+channel.addons, origin=origin, param=params, endmsg=extinfo) - -                                elif cmd == 353:  # NAMES reply -                                    (flag, channame) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) - -                                    if "PREFIX" in self.supports: -                                        names = re.findall(r"([%s]*)([^@!\s]+)(?:!(\S+)@(\S+))?"%re.escape(self.supports["PREFIX"][1]), extinfo) -                                    else: -                                        names = re.findall(r"()([^@!\s]+)(?:!(\S+)@(\S+))?", extinfo)  # Still put it into tuple form for compatibility in the next structure -                                    (handled, unhandled, exceptions) = self.event("onNames", self.addons+channel.addons, origin=origin, channel=channel, flag=flag, channame=channame, nameslist=names) - -                                    for (symbs, nick, username, host) in names: -                                        user = self.user(nick) -                                        if user.nick != nick: -                                            user.nick = nick -                                        if username and user.username != username: -                                            user.username = username -                                        if host and user.host != host: -                                            user.host = host -                                        with channel.lock: -                                            if channel not in user.channels: -                                                user.channels.append(channel) -                                            if user not in channel.users: -                                                channel.users.append(user) -                                            if "PREFIX" in self.supports: -                                                for symb in symbs: -                                                    mode = self.supports["PREFIX"][0][self.supports["PREFIX"][1].index(symb)] -                                                    if mode not in channel.modes: -                                                        channel.modes[mode] = [user] -                                                    elif user not in channel.modes[mode]: -                                                        channel.modes[mode].append(user) - -                                elif cmd == 366:  # End of NAMES reply -                                    channel = self.channel(params) -                                    (handled, unhandled, exceptions) = self.event("onNamesEnd", self.addons+channel.addons, origin=origin, channel=channel, channame=params, endmsg=extinfo) - -                                elif cmd == 372:  # MOTD line -                                    (handled, unhandled, exceptions) = self.event("onMOTDLine", self.addons, origin=origin, motdline=extinfo) -                                    self.motd.append(extinfo) -                                elif cmd == 375:  # Begin MOTD -                                    (handled, unhandled, exceptions) = self.event("onMOTDStart", self.addons, origin=origin, motdgreet=extinfo) -                                    self.motdgreet = extinfo -                                    self.motd = [] -                                elif cmd == 376: -                                    (handled, unhandled, exceptions) = self.event("onMOTDEnd", self.addons, origin=origin, motdend=extinfo) -                                    self.motdend = extinfo  # End of MOTD - -                                elif cmd == 386 and "q" in self.supports["PREFIX"][0]:  # Channel Owner (Unreal) -                                    (channame, owner) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    if channel.name != channame: -                                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name -                                    user = self.user(owner) -                                    if user.nick != owner: -                                        user.nick = owner -                                    if "q" in channel.modes: -                                        if user not in channel.modes["q"]: -                                            channel.modes["q"].append(user) -                                    else: -                                        channel.modes["q"] = [user] - -                                elif cmd == 388 and "a" in self.supports["PREFIX"][0]:  # Channel Admin (Unreal) -                                    (channame, admin) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    if channel.name != channame: -                                        channel.name = channame  # Server seems to have changed the idea of the case of the channel name -                                    user = self.user(admin) -                                    if user.nick != admin: -                                        user.nick = admin -                                    if "a" in channel.modes: -                                        if user not in channel.modes["a"]: -                                            channel.modes["a"].append(user) -                                    else: -                                        channel.modes["a"] = [user] - -                                elif cmd == "NICK": -                                    newnick = extinfo if len(extinfo) else target - -                                    addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], []) -                                    self.event("onRecv", addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onNickChange", self.addons+addons, user=origin, newnick=newnick) -                                    if origin == self.identity: -                                        (handled, unhandled, exceptions) = self.event("onMeNickChange", self.addons+addons, newnick=newnick) - -                                    for u in self.users: -                                        if u.nick.lower() == newnick.lower(): -                                            self.users.remove(u)  # Nick collision, safe to assume this orphaned user is offline, so we shall remove the old instance. -                                            for channel in self.channels: -                                                ### If for some odd reason, the old user still appears common channels, then we will remove the user anyway. -                                                if u in channel.users: -                                                    channel.users.remove(u) -                                    origin.nick = newnick - -                                elif cmd == "JOIN": -                                    channel = target if type(target) == Channel else self.channel(extinfo) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onJoin", self.addons+channel.addons, user=origin, channel=channel) - -                                    if origin == self.identity:  # This means the bot is entering the room, -                                        # and will reset all the channel data, on the assumption that such data may have changed. -                                        # Also, the bot must request modes -                                        with channel._joining: -                                            if channel._joinrequested: -                                                channel._joinreply = cmd -                                                channel._joining.notify() -                                        channel.topic = "" -                                        channel.topicmod = "" -                                        channel.modes = {} -                                        channel.users = [] -                                        self.event("onMeJoin", self.addons+channel.addons, channel=channel) -                                        self.raw("MODE %s" % channel.name) -                                        self.raw("WHO %s" % channel.name) -                                        if "CHANMODES" in self.supports.keys(): -                                            self.raw("MODE %s :%s" % (channel.name, self.supports["CHANMODES"][0])) - -                                    if channel not in origin.channels: -                                        origin.channels.append(channel) -                                    if origin not in channel.users: -                                        channel.users.append(origin) - -                                elif cmd == "KICK": -                                    kicked = self.user(params) -                                    if kicked.nick != params: -                                        kicked.nick = params - -                                    self.event("onRecv", target.addons, line=line, **data) -                                    if origin == self.identity: -                                        self.event("onMeKick", self.addons+target.addons, channel=target, kicked=kicked, kickmsg=extinfo) -                                    if kicked == self.identity: -                                        self.event("onMeKicked", self.addons+target.addons, kicker=origin, channel=target, kickmsg=extinfo) -                                    (handled, unhandled, exceptions) = self.event("onKick", self.addons+target.addons, kicker=origin, channel=target, kicked=kicked, kickmsg=extinfo) - -                                    if target in kicked.channels: -                                        kicked.channels.remove(target) -                                    if kicked in target.users: -                                        target.users.remove(kicked) -                                    if "PREFIX" in self.supports: -                                        for mode in self.supports["PREFIX"][0]: -                                            if mode in target.modes and kicked in target.modes[mode]: -                                                target.modes[mode].remove(kicked) - -                                elif cmd == "PART": -                                    self.event("onRecv", target.addons, line=line, **data) -                                    if origin == self.identity: -                                        with target     ._parting: -                                            if target._partrequested: -                                                target._partreply = cmd -                                                target._parting.notify() -                                        self.event("onMePart", self.addons+target.addons, channel=target, partmsg=extinfo) -                                    (handled, unhandled, exceptions) = self.event("onPart", self.addons+target.addons, user=origin, channel=target, partmsg=extinfo) - -                                    if target in origin.channels: -                                        origin.channels.remove(target) -                                    if origin in target.users: -                                        target.users.remove(origin) -                                    if "PREFIX" in self.supports: -                                        for mode in self.supports["PREFIX"][0]: -                                            if mode in target.modes and origin in target.modes[mode]: -                                                target.modes[mode].remove(origin) - -                                elif cmd == "QUIT": -                                    channels = list(origin.channels) -                                    addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], []) -                                    self.event("onRecv", addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onQuit", self.addons+addons, user=origin, quitmsg=extinfo) -                                    for channel in origin.channels: -                                        with channel.lock: -                                            if origin in channel.users: -                                                channel.users.remove(origin) -                                            if "PREFIX" in self.supports: -                                                for mode in self.supports["PREFIX"][0]: -                                                    if mode in channel.modes and origin in channel.modes[mode]: -                                                        channel.modes[mode].remove(origin) -                                    origin.channels = [] - -                                elif cmd == "MODE": -                                    if type(target) == Channel: -                                        self.event("onRecv", target.addons, line=line, **data) -                                        modedelta = [] -                                        modeparams = params.split() -                                        setmodes = modeparams.pop(0) -                                        modeset = "+" -                                        for mode in setmodes: -                                            if mode in "+-": -                                                modeset = mode -                                            else: -                                                if mode in self.supports["CHANMODES"][0]+self.supports["CHANMODES"][1]: -                                                    param = modeparams.pop(0) -                                                    modedelta.append(("%s%s"%(modeset, mode), param)) -                                                    if mode in maskmodeeventnames.keys(): -                                                        if modeset == "+": -                                                            eventname = maskmodeeventnames[mode][0] -                                                        if modeset == "-": -                                                            eventname = maskmodeeventnames[mode][1] -                                                        matchesbot = glob.fnmatch.fnmatch("%s!%s@%s".lower()%(self.identity.nick, self.identity.username, self.identity.host), param.lower()) -                                                        self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param) -                                                        if matchesbot: -                                                            self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param) -                                                elif mode in self.supports["CHANMODES"][2]: -                                                    if modeset == "+": -                                                        param = modeparams.pop(0) -                                                        modedelta.append(("%s%s"%(modeset, mode), param)) -                                                    else: -                                                        modedelta.append(("%s%s"%(modeset, mode), None)) -                                                elif mode in self.supports["CHANMODES"][3]: -                                                    modedelta.append(("%s%s"%(modeset, mode), None)) -                                                elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]: -                                                    modenick = modeparams.pop(0) -                                                    modeuser = self.user(modenick) -                                                    if mode in privmodeeventnames.keys(): -                                                        if modeset == "+": -                                                            eventname = privmodeeventnames[mode][0] -                                                        if modeset == "-": -                                                            eventname = privmodeeventnames[mode][1] -                                                        self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, modeuser=modeuser) -                                                        if modeuser == self.identity: -                                                            self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target) -                                                    modedelta.append(("%s%s"%(modeset, mode), modeuser)) -                                        (handled, unhandled, exceptions) = self.event("onChanModeSet", self.addons+target.addons, user=origin, channel=target, modedelta=modedelta) -                                        with target.lock: -                                            for ((modeset, mode), param) in modedelta: -                                                if mode in self.supports["CHANMODES"][0]: -                                                    if modeset == "+": -                                                        if mode in target.modes: -                                                            if param.lower() not in [mask.lower() for (mask, setby, settime) in target.modes[mode]]: -                                                                target.modes[mode].append((param, origin, int(time.time()))) -                                                        else: -                                                            target.modes[mode] = [(param, origin, int(time.time()))] -                                                    else: -                                                        if mode in target.modes.keys(): -                                                            if mode == "b":  # Inspircd mode is case insentive when unsetting the mode -                                                                masks = [mask.lower() for (mask, setby, settime) in target.modes[mode]] -                                                                if param.lower() in masks: -                                                                    index = masks.index(param.lower()) -                                                                    #print "Index: %d"%index -                                                                    del target.modes[mode][index] -                                                            else: -                                                                masks = [mask for (mask, setby, settime) in target.modes[mode]] -                                                                if param in masks: -                                                                    index = masks.index(param) -                                                                    del target.modes[mode][index] -                                                elif mode in self.supports["CHANMODES"][1]: -                                                    if modeset == "+": -                                                        target.modes[mode] = param -                                                    else: -                                                        target.modes[mode] = None -                                                elif mode in self.supports["CHANMODES"][2]: -                                                    if modeset == "+": -                                                        target.modes[mode] = param -                                                    else: -                                                        target.modes[mode] = None -                                                elif mode in self.supports["CHANMODES"][3]: -                                                    if modeset == "+": -                                                        target.modes[mode] = True -                                                    else: -                                                        target.modes[mode] = False -                                                elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]: -                                                    if modeset == "+": -                                                        if mode in target.modes and param not in target.modes[mode]: -                                                            target.modes[mode].append(param) -                                                        if mode not in target.modes: -                                                            target.modes[mode] = [param] -                                                    elif mode in target.modes and param in target.modes[mode]: -                                                        target.modes[mode].remove(param) -                                    elif type(target) == User: -                                        modeparams = (params if params else extinfo).split() -                                        setmodes = modeparams.pop(0) -                                        modeset = "+" -                                        for mode in setmodes: -                                            if mode in "+-": -                                                modeset = mode -                                                continue -                                            if modeset == "+": -                                                if mode not in target.modes: -                                                    target.modes += mode -                                                if mode == "s" and len(modeparams): -                                                    snomask = modeparams.pop(0) -                                                    for snomode in snomask: -                                                        if snomode in "+-": -                                                            snomodeset = snomode -                                                            continue -                                                        if snomodeset == "+" and snomode not in target.snomask: -                                                            target.snomask += snomode -                                                        if snomodeset == "-" and snomode in target.snomask: -                                                            target.snomask = target.snomask.replace(snomode, "") -                                            if modeset == "-": -                                                if mode in target.modes: -                                                    target.modes = target.modes.replace(mode, "") -                                                if mode == "s": -                                                    target.snomask = "" - -                                elif cmd == "TOPIC": -                                    self.event("onRecv", target.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onTopicSet", self.addons+target.addons, user=origin, channel=target, topic=extinfo) - -                                    with target.lock: -                                        target.topic = extinfo -                                        target.topicsetby = origin -                                        target.topictime = int(time.time()) - -                                elif cmd == "INVITE": -                                    channel = self.channel(extinfo if extinfo else params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onInvite", self.addons+channel.addons, user=origin, channel=channel) - -                                elif cmd == "PRIVMSG": -                                    if type(target) == Channel: -                                        self.event("onRecv", target.addons, line=line, **data) - -                                    ### CTCP handling -                                    ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo) -                                    if ctcp: -                                        (ctcptype, ext) = ctcp[0] -                                        if ctcptype.upper() == "ACTION": -                                            if type(target) == Channel: -                                                (handled, unhandled, exceptions) = self.event("onChanAction", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, action=ext) -                                            elif target == self.identity: -                                                (handled, unhandled, exceptions) = self.event("onPrivAction", self.addons, user=origin, action=ext) -                                        else: -                                            if type(target) == Channel: -                                                (handled, unhandled, exceptions) = self.event("onChanCTCP", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext) -                                            elif target == self.identity: -                                                (handled, unhandled, exceptions) = self.event("onPrivCTCP", self.addons, user=origin, ctcptype=ctcptype, params=ext) -                                        if ctcptype.upper() == "VERSION": -                                            origin.ctcpreply("VERSION", self.ctcpversion()) -                                        if ctcptype.upper() == "TIME": -                                            tformat = time.ctime() -                                            tz = time.tzname[0] -                                            origin.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars()) -                                        if ctcptype.upper() == "PING": -                                            origin.ctcpreply("PING", "%(ext)s" % vars()) -                                        if ctcptype.upper() == "FINGER": -                                            origin.ctcpreply("FINGER", "%(ext)s" % vars()) -                                    else: -                                        if type(target) == Channel: -                                            (handled, unhandled, exceptions) = self.event("onChanMsg", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, msg=extinfo) -                                        elif target == self.identity: -                                            (handled, unhandled, exceptions) = self.event("onPrivMsg", self.addons, user=origin, msg=extinfo) - -                                elif cmd == "NOTICE": -                                    if type(target) == Channel: -                                        self.event("onRecv", target.addons, line=line, **data) - -                                    ### CTCP handling -                                    ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo) -                                    if ctcp and target == self.identity: -                                        (ctcptype, ext) = ctcp[0] -                                        (handled, unhandled, exceptions) = self.event("onCTCPReply", self.addons, origin=origin, ctcptype=ctcptype, params=ext) -                                    else: -                                        if type(target) == Channel: -                                            (handled, unhandled, exceptions) = self.event("onChanNotice", self.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo) -                                        elif target == self.identity: -                                            (handled, unhandled, exceptions) = self.event("onPrivNotice", self.addons, origin=origin, msg=extinfo) - -                                elif cmd == 367:  # Channel Ban list -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onBanListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "b" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]: -                                            channel.modes["b"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["b"] = [(mask, setby, int(settime))] -                                elif cmd == 368: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onBanListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 346:  # Channel Invite list -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onInviteListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "I" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["I"]]: -                                            channel.modes["I"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["I"] = [(mask, setby, int(settime))] -                                elif cmd == 347: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onInviteListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 348:  # Channel Ban Exception list -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onBanExceptListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "e" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["e"]]: -                                            channel.modes["e"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["e"] = [(mask, setby, int(settime))] -                                elif cmd == 349: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onBanExceptListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 910:  # Channel Access List -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onAccessListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "w" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]: -                                            channel.modes["w"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["w"] = [(mask, setby, int(settime))] -                                elif cmd == 911: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onAccessListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 941:  # Spam Filter list -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onSpamfilterListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "g" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["g"]]: -                                            channel.modes["g"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["g"] = [(mask, setby, int(settime))] -                                elif cmd == 940: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onSpamfilterListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 954:  # Channel exemptchanops list -                                    (channame, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime)) -                                    if "X" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["X"]]: -                                            channel.modes["X"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["X"] = [(mask, setby, int(settime))] -                                elif cmd == 953: -                                    channel = self.channel(params) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo) - -                                elif cmd == 728:  # Channel quiet list -                                    (channame, modechar, mask, setby, settime) = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onQuietListEntry", self.addons+channel.addons, origin=origin, channel=channel, modechar=modechar, mask=mask, setby=setby, settime=int(settime)) -                                    if "q" in channel.modes.keys(): -                                        if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["q"]]: -                                            channel.modes["q"].append((mask, setby, int(settime))) -                                    else: -                                        channel.modes["q"] = [(mask, setby, int(settime))] -                                elif cmd == 729: -                                    channame, modechar = params.split() -                                    channel = self.channel(channame) -                                    self.event("onRecv", channel.addons, line=line, **data) -                                    (handled, unhandled, exceptions) = self.event("onQuietListEnd", self.addons+channel.addons, channel=channel, endmsg=extinfo) - -                                elif cmd in (495, 384, 385, 386, 468, 470, 366, 315, 482, 484, 953, 368, 482, 349, 940, 911, 489, 490, 492, 520, 530):  # Channels which appear in params -                                    for param in params.split(): -                                        if len(param) and param[0] in self.supports["CHANTYPES"]: -                                            channel = self.channel(param) -                                            self.event("onRecv", channel.addons, line=line, **data) - -                                elif type(cmd) == int: -                                    (handled, unhandled, exceptions) = self.event("on%03d"%cmd, self.addons, line=line, origin=origin, target=target, params=params, extinfo=extinfo) -                                else: -                                    (handled, unhandled, exceptions) = self.event("on%s"%cmd, self.addons, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo) - -                                if cmd in (384, 403, 405, 471, 473, 474, 475, 476, 520, 477, 489, 495):  # Channel Join denied -                                    try: -                                        channel = self.channel(params) -                                    except InvalidName: -                                        pass -                                    else: -                                        with channel._joining: -                                            if channel._joinrequested: -                                                channel._joinreply = (cmd, extinfo) -                                                channel._joining.notify() - -                                elif cmd == 470:  # Channel Join denied due to redirect -                                    channelname, redirect = params.split() -                                    try: -                                        channel = self.channel(channelname) -                                    except InvalidName: -                                        pass -                                    else: -                                        with channel._joining: -                                            if channel._joinrequested: -                                                channel._joinreply = (cmd, "%s (%s)"%(extinfo, redirect)) -                                                channel._joining.notify() - -                                self.event("onUnhandled", unhandled, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo) - -                        else:  # Line does NOT match ":origin cmd target params :extinfo" -                            self.event("onRecv", self.addons, line=line)                  except SystemExit:  # Connection lost normally.                      pass +                  except socket.error:  # Connection lost due to either ping timeout or connection reset by peer. Not a fatal error.                      exc, excmsg, tb = sys.exc_info() -                    self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()) +                    with self.lock: +                        self.logwrite( +                            "*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()) +                        self._event("onConnectFail", self.addons + reduce( +                            lambda x, y: x + y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb) +                  except:  # Unknown exception, treated as FATAL. Try to quit IRC and terminate thread with exception. -                    ### Quit with a (hopefully) useful quit message, or die trying. -                    self.quitexpected = True +                    # Quit with a (hopefully) useful quit message, or die +                    # trying. +                    self._quitexpected = True                      try: -                        self.quit("%s" % traceback.format_exc() -                            .rstrip().split("\n")[-1]) +                        self.quit( +                            "%s" % traceback.format_exc().rstrip().split("\n")[-1])                      except:                          pass                      raise +                  finally:  # Post-connection operations after connection is lost, and must be executed, even if exception occurred. +                    with self._sendline: +                        self._outgoing.clear() +                        self._sendline.notify()                      with self.lock: -                        (handled, unhandled, exceptions) = self.event("onDisconnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), expected=self.quitexpected) -                        self.connected = False -                        self.registered = False -                        self.identity = None - -                    ### Tell outgoing thread to quit. -                    self.outgoing.interrupt() +                        (handled, unhandled, exceptions) = self._event("onDisconnect", self.addons + reduce( +                            lambda x, y: x + y, [chan.addons for chan in self.channels], []), expected=self._quitexpected) +                        self._init() -                    ### Wait until the outgoing thread dies. -                    if self.outgoingthread and self.outgoingthread.isAlive(): -                        self.outgoingthread.join() -                        self.outgoingthread = None +                    # Notify _outgoingthread that the connection has been +                    # terminated. +                    with self._sendline: +                        self._sendline.notify()                      try: -                        self.connection.close() +                        self._connection.close()                      except:                          pass                      self.logwrite("*** Connection Terminated.") -                if self.quitexpected or not self.autoreconnect: +                if self._quitexpected or not self.autoreconnect: +                    self._quitexpected = False                      sys.exit() -                ### If we make it to this point, then it is because connection was lost unexpectedly, and will attempt to reconnect if self.autoreconnect is True. -                time.sleep(self.retrysleep) -          except SystemExit:              pass          except:  # Print exception to log file -            self.logwrite(*["!!! FATAL Exception"]+["!!! %s"%line for line in traceback.format_exc().split("\n")]) -            print >>sys.stderr, "FATAL Exception" % vars() +            self.logwrite(*["!!! FATAL Exception"] + ["!!! %s" % +                          line for line in traceback.format_exc().split("\n")]) +            print >>sys.stderr, "FATAL Exception"              print >>sys.stderr, traceback.format_exc()              sys.exit()          finally:              self.logwrite("### Log session ended") -            (handled, unhandled, exceptions) = self.event("onSessionClose", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], [])) -            Thread.__init__(self)  # Makes thread restartable +            (handled, unhandled, exceptions) = self._event("onSessionClose", self.addons + +                                                           reduce(lambda x, y: x + y, [chan.addons for chan in self.channels], [])) + +            # Tell _sendhandler to quit +            with self._sendline: +                self._outgoing.append("quit") +                self._sendline.notify() + +    def _send(self, line, origin=None): +        if "\r" in line or "\n" in line: +            raise InvalidCharacter +        cmd = line.split(" ")[0].upper() + +        T = time.time() +        if cmd == "PRIVMSG": +            # Hard-coding a throttling mechanism for PRIVMSGs only here. Will later build support for custom throttlers. +            # The throttle will be triggered when it attempts to send a sixth PRIVMSG in a four-second interval. +            # When the throttle is active, PRIVMSGs will be sent in at least one-second intervals. +            # The throttle is deactivated when three seconds elapse without +            # sending a PRIVMSG. +            while len(self.throttledata) and self.throttledata[0] < T - 4: +                del self.throttledata[0] +            if not self.throttled: +                if len(self.throttledata) >= 5: +                    self.throttled = True +                    T = self.throttledata[-1] + 1 +            else: +                if len(self.throttledata) == 0 or self.throttledata[-1] < T - 2: +                    self.throttled = False +                else: +                    T = max(T, self.throttledata[-1] + 1) +            self.throttledata.append(T) +        with self._sendline: +            self._outgoing.append((T, line, origin)) +            self._sendline.notify() + +    def _procsendline(self, line, origin=None): +        match = re.findall(_ircsendmatch, line) +        if len(match) == 0: +            return +        (cmd, target, params, extinfo) = match[0] +        cmd = cmd.upper() +        with self.lock: +            if cmd == "QUIT": +                self._quitexpected = True +            self._connection.send("%s\n" % line) + +            # Modify line if it contains a password so that the password is not +            # logged or sent to any potentially untrustworthy addons +            if cmd == "PRIVMSG": +                if target.upper() == "NICKSERV": +                    nscmd = re.findall( +                        r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I) +                    if nscmd: +                        nscmd = nscmd[0] +                        if nscmd[0].upper() in ("IDENTIFY", "REGISTER"): +                            extinfo = "%s ********" % nscmd[0] +                            line = "%s %s :%s" % (cmd, target, extinfo) +                        elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): +                            extinfo = "%s %s ********" % nscmd[:2] +                            line = "%s %s :%s" % (cmd, target, extinfo) +                        elif nscmd[0].upper() == "SET": +                            if nscmd[1].upper() == "PASSWORD": +                                extinfo = "%s %s ********" % nscmd[:2] +                                line = "%s %s :%s" % (cmd, target, extinfo) +                        elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): +                            extinfo = "********" +                            line = "%s %s :%s" % (cmd, target, extinfo) +                if target.upper() == "CHANSERV": +                    cscmd = re.findall( +                        r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I) +                    if cscmd: +                        cscmd = cscmd[0] +                        if cscmd[0].upper() in ("IDENTIFY", "REGISTER"): +                            extinfo = "%s %s ********" % cscmd[:2] +                            line = "%s %s :%s" % (cmd, target, extinfo) +                        elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): +                            extinfo = "%s %s %s ********" % cscmd[:3] +                            line = "%s %s :%s" % (cmd, target, extinfo) +                        elif cscmd[0].upper() == "SET": +                            if cscmd[2].upper() == "PASSWORD": +                                extinfo = "%s %s %s ********" % cscmd[:3] +                                line = "%s %s :%s" % (cmd, target, extinfo) +                        elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): +                            extinfo = "********" +                            line = "%s %s :%s" % (cmd, target, extinfo) + +                chanmatch = re.findall( +                    _targchanmatch % (re.escape(self.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.supports.get("CHANTYPES", "#"))), target) +                if chanmatch: +                    targetprefix, channame = chanmatch[0] +                    target = self.channel(channame) +                    if target.name != channame: +                        # Target channel name has changed +                        target.name = channame +                elif re.match(_nickmatch, target) and cmd != "NICK": +                    targetprefix = "" +                    target = self.user(target) + +                ctcp = re.findall(_ctcpmatch, extinfo) +                if ctcp: +                    (ctcptype, ext) = ctcp[0] +                    if ctcptype.upper() == "ACTION": +                        if type(target) == Channel: +                            self._event( +                                "onSendChanAction", self.addons + +                                target.addons, +                                origin=origin, channel=target, targetprefix=targetprefix, action=ext) +                        elif type(target) == User: +                            self._event( +                                "onSendPrivAction", self.addons, origin=origin, user=target, action=ext) +                    else: +                        if type(target) == Channel: +                            self._event( +                                "onSendChanCTCP", self.addons + target.addons, origin=origin, +                                channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext) +                        elif type(target) == User: +                            self._event( +                                "onSendPrivCTCP", self.addons, origin=origin, user=target, ctcptype=ctcptype, params=ext) +                else: +                    if type(target) == Channel: +                        self._event( +                            "onSendChanMsg", self.addons + target.addons, origin=origin, +                            channel=target, targetprefix=targetprefix, msg=extinfo) +                    elif type(target) == User: +                        self._event( +                            "onSendPrivMsg", self.addons, origin=origin, user=target, msg=extinfo) + +                # elif target.upper()=="CHANSERV": +                    #msg=extinfo.split(" ") +                    # if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2: +                        # msg[2]="********" +                        #extinfo=" ".join(msg) +                        #line="%s %s :%s"%(cmd, target, extinfo) +            elif cmd.upper() in ("NS", "NICKSERV"): +                if target.upper() in ("IDENTIFY", "REGISTER"): +                    params = params.split(" ") +                    while "" in params: +                        params.remove("") +                    if len(params): +                        params[0] = "********" +                    params = " ".join(params) +                    line = "%s %s %s" % (cmd, target, params) +                elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): +                    params = params.split(" ") +                    while "" in params: +                        params.remove("") +                    if len(params) > 1: +                        params[1] = "********" +                    params = " ".join(params) +                    line = "%s %s %s" % (cmd, target, params) +                elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): +                    params = "" +                    target = "********" +                    line = "%s %s" % (cmd, target) +            elif cmd.upper() == "OPER": +                params = "********" +                line = "%s %s %s" % (cmd, target, params) +            elif cmd.upper() == "PASS": +                extinfo = "********" +                target = "" +                line = "%s :%s" % (cmd, extinfo) +            elif cmd.upper() == "IDENTIFY": +                target = "********" +                line = "%s %s" % (cmd, target) +            self._event("onSend", self.addons, origin=origin, line=line, +                        cmd=cmd, target=target, params=params, extinfo=extinfo) +        self.logwrite(">>> %s" % line) + +    def _sendhandler(self): +        # Enforce that this function must only be run from within +        # self._sendhandlerthread. +        if currentThread() != self._sendhandlerthread: +            raise RuntimeError, "This function is designed to run in its own thread." + +        try: +            while True: +                # Wait for one of the following: +                # (1) An item placed into _outgoing +                # (2) Connection is lost +                # (3) self._recvhandlerthread is set to None + +                with self._sendline: +                    if "quit" in self._outgoing: +                        sys.exit() +                    S = time.time() +                    if len(self._outgoing): +                        T, line, origin = min(self._outgoing) +                        if T > S: +                            self._sendline.wait(T - S) +                            continue +                        else: +                            self._outgoing.remove((T, line, origin)) +                    else: +                        self._sendline.wait() +                        continue + +                try: +                    self._procsendline(line, origin=origin) +                except socket.error: +                    exc, excmsg, tb = sys.exc_info() +                    with self.lock: +                        self.logwrite( +                            "*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()) +                        self._event("onConnectFail", self.addons + reduce( +                            lambda x, y: x + y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb) +                    with self._sendline: +                        self._outgoing.clear() +                    try: +                        self._connection.close() +                    except: +                        pass + +        except SystemExit: +            pass + +        except: +            tb = traceback.format_exc() +            self._quitexpected = True +            self.logwrite(*["!!! FATAL Exception"] + [ +                          "!!! %s" % line for line in tb.split("\n")]) +            print >>sys.stderr, "FATAL Exception" +            print >>sys.stderr, tb +            with self._sendline: +                try: +                    self._connection.send( +                        "QUIT :%s\n" % tb.rstrip().split("\n")[-1]) +                    self._connection.shutdown(socket.SHUT_WR) +                except: +                    pass +        finally: +            with self._sendline: +                self._outgoing.clear()  # Clear out _outgoing. + +    # For compatibility, when modules still expect irc.Connection to be a +    # subclass of threading.Thread +    def isAlive(self): +        return type(self._recvhandlerthread) == Thread and self._recvhandlerthread.isAlive() and type(self._sendhandlerthread) == Thread and self._sendhandlerthread.isAlive() + +    # For compatibility, when modules still expect irc.Connection to be a +    # subclass of threading.Thread +    def start(self): +        return self.connect()      def __repr__(self):          server = self.server          if self.ipv6 and ":" in server: -            server = "[%s]"%server +            server = "[%s]" % server          port = self.port          if self.identity:              nick = self.identity.nick @@ -1212,61 +1713,60 @@ class Connection(Thread):          else:              protocol = "irc"          return "<IRC Context: %(nick)s!%(user)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals() -        #else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals() +        # else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % +        # locals()      def oper(self, name, passwd, origin=None): -        self.raw("OPER %s %s" % (re.findall("^([^\r\n\\s]*)", name)[0], -                                 re.findall("^([^\r\n\\s]*)", passwd)[0]), origin=origin) +        self.raw("OPER %s %s" % +                 (re.findall("^([^\r\n\\s]*)", name)[0], re.findall("^([^\r\n\\s]*)", passwd)[0]), origin=origin)      def list(self, params="", origin=None):          if len(re.findall("^([^\r\n\\s]*)", params)[0]): -            self.raw("LIST %s" % (re.findall( -                "^([^\r\n\\s]*)", params)[0]), origin=origin) +            self.raw("LIST %s" % +                     (re.findall("^([^\r\n\\s]*)", params)[0]), origin=origin)          else:              self.raw("LIST", origin=origin)      def getmotd(self, target="", origin=None):          if len(re.findall("^([^\r\n\\s]*)", target)[0]): -            self.raw("MOTD %s" % (re.findall( -                "^([^\r\n\\s]*)", target)[0]), origin=origin) +            self.raw("MOTD %s" % +                     (re.findall("^([^\r\n\\s]*)", target)[0]), origin=origin)          else:              self.raw("MOTD", origin=origin)      def version(self, target="", origin=None):          if len(re.findall("^([^\r\n\\s]*)", target)[0]): -            self.raw("VERSION %s" % (re.findall( -                "^([^\r\n\\s]*)", target)[0]), origin=origin) +            self.raw("VERSION %s" % +                     (re.findall("^([^\r\n\\s]*)", target)[0]), origin=origin)          else:              self.raw("VERSION", origin=origin)      def stats(self, query, target="", origin=None):          if len(re.findall("^([^\r\n\\s]*)", target)[0]): -            self.raw("STATS %s %s" % (query, re.findall( -                "^([^\r\n\\s]*)", target)[0]), origin=origin) +            self.raw("STATS %s %s" % +                     (query, re.findall("^([^\r\n\\s]*)", target)[0]), origin=origin)          else: -            self.raw("STATS %s"%query, origin=origin) +            self.raw("STATS %s" % query, origin=origin)      def quit(self, msg="", origin=None): -        with self.lock: -            if self.connected: -                if len(re.findall("^([^\r\n]*)", msg)[0]): -                    self.raw("QUIT :%s" % re.findall("^([^\r\n]*)", -                                                     msg)[0], origin=origin) -                else: -                    self.raw("QUIT", origin=origin) +        if len(re.findall("^([^\r\n]*)", msg)[0]): +            self._send("QUIT :%s" % +                       re.findall("^([^\r\n]*)", msg)[0], origin=origin) +        else: +            self._send("QUIT", origin=origin)      def ctcpversion(self):          reply = [] -        ### Prepare reply for addon -        reply.append("%(__name__)s %(__version__)s, %(__author__)s" % -                     vars(self)) +        # Prepare reply for addon +        reply.append( +            "%(__name__)s %(__version__)s, %(__author__)s" % vars(self)) -        ### Prepare reply for Python and OS versions +        # Prepare reply for Python and OS versions          pyver = sys.version.split("\n") -        pyver[0] = "Python "+pyver[0] +        pyver[0] = "Python " + pyver[0]          reply.extend(pyver)          reply.extend(platform.platform().split("\n")) -        ### Prepare reply for extension addons +        # Prepare reply for extension addons          for addon in self.addons:              try:                  r = "%(__name__)s %(__version__)s" % vars(addon) @@ -1278,46 +1778,47 @@ class Connection(Thread):          return reduce(lambda x, y: "%s; %s" % (x, y), reply)      def raw(self, line, origin=None): -        if "\r" in line or "\n" in line: -            raise InvalidCharacter -        self.outgoing.put((line, origin)) +        self._send(line, origin=origin)      def user(self, nick):          if self.supports.get("CASEMAPPING", "rfc1459") == "ascii": -            users = [user for user in self.users if user.nick.lower( -            ) == nick.lower()] +            users = [ +                user for user in self.users if user.nick.lower() == nick.lower()]          else: -            users = [user for user in self.users if user.nick.translate(_rfc1459casemapping) == nick.translate(_rfc1459casemapping)] +            users = [user for user in self.users if user.nick.translate( +                _rfc1459casemapping) == nick.translate(_rfc1459casemapping)]          if len(users):              return users[0]          else:              user = User(nick, self)              self.users.append(user) -            timestamp = reduce(lambda x, y: x+":"+y, [str( -                t).rjust(2, "0") for t in time.localtime()[0:6]]) +            timestamp = reduce(lambda x, y: x + ":" + y, [ +                               str(t).rjust(2, "0") for t in time.localtime()[0:6]])              return user      def channel(self, name):          if self.supports.get("CASEMAPPING", "rfc1459") == "ascii": -            channels = [chan for chan in self.channels if chan.name.lower( -            ) == name.lower()] +            channels = [ +                chan for chan in self.channels if chan.name.lower() == name.lower()]          else: -            channels = [chan for chan in self.channels if chan.name.translate(_rfc1459casemapping) == name.translate(_rfc1459casemapping)] +            channels = [chan for chan in self.channels if chan.name.translate( +                _rfc1459casemapping) == name.translate(_rfc1459casemapping)]          if len(channels):              return channels[0]          else: -            timestamp = reduce(lambda x, y: x+":"+y, [str( -                t).rjust(2, "0") for t in time.localtime()[0:6]]) +            timestamp = reduce(lambda x, y: x + ":" + y, [ +                               str(t).rjust(2, "0") for t in time.localtime()[0:6]])              chan = Channel(name, self)              self.channels.append(chan)              return chan  class Channel(object): +      def __init__(self, name, context):          chantypes = context.supports.get("CHANTYPES", "&#+!") -        if not re.match(r"^[%s][^%s\s]*$" % (re.escape(chantypes), re.escape("\x07,")), name): -            raise InvalidName(repr(name)) +        if not re.match(_chanmatch % re.escape(chantypes), name): +            raise InvalidName, repr(name)          self.name = name          self.context = context          self.addons = [] @@ -1340,37 +1841,37 @@ class Channel(object):          if target and target not in self.context.supports.get("PREFIX", ("ohv", "@%+"))[1]:              raise InvalidPrefix          for line in re.findall("([^\r\n]+)", msg): -            self.context.raw("PRIVMSG %s%s :%s" % (target, -                                                   self.name, line), origin=origin) +            self.context._send("PRIVMSG %s%s :%s" % +                               (target, self.name, line), origin=origin)      def who(self, origin=None): -        self.context.raw("WHO %s" % (self.name), origin=origin) +        self.context._send("WHO %s" % (self.name), origin=origin)      def names(self, origin=None): -        self.context.raw("NAMES %s" % (self.name), origin=origin) +        self.context._send("NAMES %s" % (self.name), origin=origin)      def notice(self, msg, target="", origin=None):          if target and target not in self.context.supports.get("PREFIX", ("ohv", "@%+"))[1]:              raise InvalidPrefix          for line in re.findall("([^\r\n]+)", msg): -            self.context.raw("NOTICE %s%s :%s" % (target, -                                                  self.name, line), origin=origin) +            self.context._send("NOTICE %s%s :%s" % +                               (target, self.name, line), origin=origin)      def settopic(self, msg, origin=None): -        self.context.raw("TOPIC %s :%s" % (self.name, re.findall( -            "^([^\r\n]*)", msg)[0]), origin=origin) +        self.context._send("TOPIC %s :%s" % +                           (self.name, re.findall("^([^\r\n]*)", msg)[0]), origin=origin)      def ctcp(self, act, msg="", origin=None):          if len(re.findall("^([^\r\n]*)", msg)[0]): -            self.msg("\01%s %s\01" % (act.upper(), re.findall( -                "^([^\r\n]*)", msg)[0]), origin=origin) +            self.msg("\01%s %s\01" % +                     (act.upper(), re.findall("^([^\r\n]*)", msg)[0]), origin=origin)          else:              self.msg("\01%s\01" % act.upper())      def ctcpreply(self, act, msg="", origin=None):          if len(re.findall("^([^\r\n]*)", msg)[0]): -            self.notice("\01%s %(msg)s\01" % (act.upper(), -                                              re.findall("^([^\r\n]*)", msg)[0]), origin=origin) +            self.notice("\01%s %(msg)s\01" % +                        (act.upper(), re.findall("^([^\r\n]*)", msg)[0]), origin=origin)          else:              self.notice("\01%s\01" % act.upper(), origin=origin) @@ -1380,7 +1881,7 @@ class Channel(object):      def part(self, msg="", blocking=False, timeout=30, origin=None):          with self.context.lock:              if self.context.identity not in self.users: -                ### Bot is not on the channel +                # Bot is not on the channel                  raise NotOnChannel          with self._parting:              try: @@ -1388,19 +1889,20 @@ class Channel(object):                      raise ActionAlreadyRequested                  self._partrequested = True                  if len(re.findall("^([^\r\n]*)", msg)[0]): -                    self.context.raw("PART %s :%s" % (self.name, re.findall("^([^\r\n]*)", msg)[0]), origin=origin) +                    self.context._send( +                        "PART %s :%s" % (self.name, re.findall("^([^\r\n]*)", msg)[0]), origin=origin)                  else: -                    self.context.raw("PART %s" % self.name, origin=origin) +                    self.context._send("PART %s" % self.name, origin=origin) -                ### Anticipated Numeric Replies: +                # Anticipated Numeric Replies: -                ### ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL -                ### ERR_NOTONCHANNEL +                # ERR_NEEDMOREPARAMS ERR_NOSUCHCHANNEL +                # ERR_NOTONCHANNEL                  if blocking:                      endtime = time.time() + timeout                      while True: -                        self._parting.wait(max(0, endtime-time.time())) +                        self._parting.wait(max(0, endtime - time.time()))                          t = time.time()                          if not self.context.connected:                              raise NotConnected @@ -1408,7 +1910,7 @@ class Channel(object):                              return                          elif type(self._partreply) == tuple and len(self._partreply) == 2:                              cmd, extinfo = self._partreply -                            raise exceptcodes[cmd](extinfo) +                            raise exceptcodes[cmd], extinfo                          if t > endtime:                              raise RequestTimedOut              finally: @@ -1420,12 +1922,13 @@ class Channel(object):              user) == User else re.findall("^([^\r\n\\s]*)", user)[0]          if nickname == "":              raise InvalidName -        self.context.raw("INVITE %s %s" % (nickname, self.name), origin=origin) +        self.context._send("INVITE %s %s" % +                           (nickname, self.name), origin=origin)      def join(self, key="", blocking=False, timeout=30, origin=None):          with self.context.lock:              if self.context.identity in self.users: -                ### Bot is already on the channel +                # Bot is already on the channel                  raise AlreadyJoined          with self._joining:              try: @@ -1433,22 +1936,23 @@ class Channel(object):                      raise ActionAlreadyRequested                  self._joinrequested = True                  if len(re.findall("^([^\r\n\\s]*)", key)[0]): -                    self.context.raw("JOIN %s %s" % (self.name, re.findall("^([^\r\n\\s]*)", key)[0]), origin=origin) +                    self.context._send( +                        "JOIN %s %s" % (self.name, re.findall("^([^\r\n\\s]*)", key)[0]), origin=origin)                  else: -                    self.context.raw("JOIN %s" % self.name, origin=origin) +                    self.context._send("JOIN %s" % self.name, origin=origin) -                ### Anticipated Numeric Replies: +                # Anticipated Numeric Replies: -                ### ERR_NEEDMOREPARAMS ERR_BANNEDFROMCHAN -                ### ERR_INVITEONLYCHAN ERR_BADCHANNELKEY -                ### ERR_CHANNELISFULL ERR_BADCHANMASK -                ### ERR_NOSUCHCHANNEL ERR_TOOMANYCHANNELS -                ### ERR_TOOMANYTARGETS ERR_UNAVAILRESOURCE +                # ERR_NEEDMOREPARAMS ERR_BANNEDFROMCHAN +                # ERR_INVITEONLYCHAN ERR_BADCHANNELKEY +                # ERR_CHANNELISFULL ERR_BADCHANMASK +                # ERR_NOSUCHCHANNEL ERR_TOOMANYCHANNELS +                # ERR_TOOMANYTARGETS ERR_UNAVAILRESOURCE                  if blocking:                      endtime = time.time() + timeout                      while True: -                        self._joining.wait(max(0, endtime-time.time())) +                        self._joining.wait(max(0, endtime - time.time()))                          t = time.time()                          if not self.context.connected:                              raise NotConnected @@ -1456,7 +1960,7 @@ class Channel(object):                              return                          elif type(self._joinreply) == tuple and len(self._joinreply) == 2:                              cmd, extinfo = self._joinreply -                            raise exceptcodes[cmd](extinfo) +                            raise exceptcodes[cmd], extinfo                          if t > endtime:                              raise RequestTimedOut              finally: @@ -1469,19 +1973,20 @@ class Channel(object):          if nickname == "":              raise InvalidName          if len(re.findall("^([^\r\n]*)", msg)[0]): -            self.context.raw("KICK %s %s :%s" % (self.name, nickname, -                                                 re.findall("^([^\r\n]*)", msg)[0]), origin=origin) +            self.context._send("KICK %s %s :%s" % +                               (self.name, nickname, re.findall("^([^\r\n]*)", msg)[0]), origin=origin)          else: -            self.context.raw("KICK %s %s" % (self.name, -                                             nickname), origin=origin) +            self.context._send("KICK %s %s" % +                               (self.name, nickname), origin=origin)      def __repr__(self): -        return "<Channel: "+self.name+"@"+self.context.server+"/"+str(self.context.port)+">" +        return "<Channel: " + self.name + "@" + self.context.server + "/" + str(self.context.port) + ">"  class User(object): +      def __init__(self, nick, context): -        if not re.match(r"^[A-Za-z\^\`\\\|\_\{\}\[\]][A-Za-z0-9\-\^\`\\\|\_\{\}\[\]]*$", nick): +        if not re.match(_nickmatch, nick):              raise InvalidName          self.nick = nick          self.username = "" @@ -1504,202 +2009,27 @@ class User(object):      def msg(self, msg, origin=None):          for line in re.findall("([^\r\n]+)", msg): -            self.context.raw("PRIVMSG %s :%s" % (self.nick, -                                                 line), origin=origin) +            self.context._send("PRIVMSG %s :%s" % +                               (self.nick, line), origin=origin)      def notice(self, msg, origin=None):          for line in re.findall("([^\r\n]+)", msg): -            self.context.raw("NOTICE %s :%s" % (self.nick, -                                                line), origin=origin) +            self.context._send("NOTICE %s :%s" % +                               (self.nick, line), origin=origin)      def ctcp(self, act, msg="", origin=None):          if len(re.findall("^([^\r\n]*)", msg)[0]): -            self.msg("\01%s %s\01" % (act.upper(), re.findall( -                "^([^\r\n]*)", msg)[0]), origin=origin) +            self.msg("\01%s %s\01" % +                     (act.upper(), re.findall("^([^\r\n]*)", msg)[0]), origin=origin)          else:              self.msg("\01%s\01" % act.upper())      def ctcpreply(self, act, msg="", origin=None):          if len(re.findall("^([^\r\n]*)", msg)[0]): -            self.notice("\01%s %s\01" % (act.upper(), -                                         re.findall("^([^\r\n]*)", msg)[0]), origin=origin) +            self.notice("\01%s %s\01" % +                        (act.upper(), re.findall("^([^\r\n]*)", msg)[0]), origin=origin)          else:              self.notice("\01%s\01" % act.upper(), origin=origin)      def me(self, msg="", origin=None):          self.ctcp("ACTION", msg, origin=origin) - - -class Outgoing(Thread): -    def __init__(self, IRC, throttle=0.25, lines=10, t=5): -        self.IRC = IRC -        self.throttle = throttle -        self.lines = lines -        self.time = t -        #self.queue=Queue() -        Thread.__init__(self) - -    def run(self): -        try: -            throttled = False -            timestamps = [] -            while True: -                try: -                    q = self.IRC.outgoing.get() -                except Queue.Interrupted: -                    break -                if q == "quit" or not self.IRC.connected: -                    break -                line, origin = q -                match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I) -                (cmd, target, params, extinfo) = match[0] -                timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( -                    2, "0") for t in time.localtime()[0:6]]) -                with self.IRC.lock: -                    try: -                        self.IRC.connection.send("%(line)s\n" % vars()) -                    except socket.error: -                        try: -                            self.IRC.connection.shutdown(0) -                        except: -                            pass -                        raise - -                    ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy addons -                    #if re.match("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I): -                    if cmd.upper() == "PRIVMSG": -                        if target.upper() == "NICKSERV": -                            nscmd = re.findall(r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I) -                            if nscmd: -                                nscmd = nscmd[0] -                                if nscmd[0].upper() in ("IDENTIFY", "REGISTER"): -                                    extinfo = "%s ********"%nscmd[0] -                                    line = "%s %s :%s"%(cmd, target, extinfo) -                                elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): -                                    extinfo = "%s %s ********"%nscmd[:2] -                                    line = "%s %s :%s"%(cmd, target, extinfo) -                                elif nscmd[0].upper() == "SET": -                                    if nscmd[1].upper() == "PASSWORD": -                                        extinfo = "%s %s ********"%nscmd[:2] -                                        line = "%s %s :%s"%(cmd, target, extinfo) -                                elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): -                                    extinfo = "********" -                                    line = "%s %s :%s"%(cmd, target, extinfo) -                        if target.upper() == "CHANSERV": -                            cscmd = re.findall(r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I) -                            if cscmd: -                                cscmd = cscmd[0] -                                if cscmd[0].upper() in ("IDENTIFY", "REGISTER"): -                                    extinfo = "%s %s ********"%cscmd[:2] -                                    line = "%s %s :%s"%(cmd, target, extinfo) -                                elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): -                                    extinfo = "%s %s %s ********"%cscmd[:3] -                                    line = "%s %s :%s"%(cmd, target, extinfo) -                                elif cscmd[0].upper() == "SET": -                                    if cscmd[2].upper() == "PASSWORD": -                                        extinfo = "%s %s %s ********"%cscmd[:3] -                                        line = "%s %s :%s"%(cmd, target, extinfo) -                                elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): -                                    extinfo = "********" -                                    line = "%s %s :%s"%(cmd, target, extinfo) - -                        chanmatch = re.findall("([%s]?)([%s].+)"%(re.escape(self.IRC.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.IRC.supports.get("CHANTYPES", "#"))), target) -                        if chanmatch: -                            targetprefix, channame = chanmatch[0] -                            target = self.IRC.channel(channame) -                            if target.name != channame: -                                ### Target channel name has changed -                                target.name = channame -                        elif len(target) and target[0] != "$" and cmd != "NICK": -                            targetprefix = "" -                            target = self.IRC.user(target) - -                        ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", -                                          extinfo) -                        if ctcp: -                            (ctcptype, ext) = ctcp[0] -                            if ctcptype.upper() == "ACTION": -                                if type(target) == Channel: -                                    self.IRC.event("onSendChanAction", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, action=ext) -                                elif type(target) == User: -                                    self.IRC.event("onSendPrivAction", self.IRC.addons, origin=origin, user=target, action=ext) -                            else: -                                if type(target) == Channel: -                                    self.IRC.event("onSendChanCTCP", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext) -                                elif type(target) == User: -                                    self.IRC.event("onSendPrivCTCP", self.IRC.addons, origin=origin, user=target, ctcptype=ctcptype, params=ext) -                        else: -                            if type(target) == Channel: -                                self.IRC.event("onSendChanMsg", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo) -                            elif type(target) == User: -                                self.IRC.event("onSendPrivMsg", self.IRC.addons, origin=origin, user=target, msg=extinfo) - -                        #elif target.upper()=="CHANSERV": -                            #msg=extinfo.split(" ") -                            #if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2: -                                #msg[2]="********" -                                #extinfo=" ".join(msg) -                                #line="%s %s :%s"%(cmd, target, extinfo) -                    elif cmd.upper() == "NS": -                        if target.upper() in ("IDENTIFY", "REGISTER"): -                            params = params.split(" ") -                            while "" in params: -                                params.remove("") -                            if len(params): -                                params[0] = "********" -                            params = " ".join(params) -                            line = "%s %s %s"%(cmd, target, params) -                        elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"): -                            params = params.split(" ") -                            while "" in params: -                                params.remove("") -                            if len(params) > 1: -                                params[1] = "********" -                            params = " ".join(params) -                            line = "%s %s %s"%(cmd, target, params) -                        elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"): -                            params = "" -                            target = "********" -                            line = "%s %s"%(cmd, target) -                    elif cmd.upper() == "OPER": -                        params = "********" -                        line = "%s %s %s"%(cmd, target, params) -                    elif cmd.upper() == "PASS": -                        extinfo = "********" -                        target = "" -                        line = "%s :%s"%(cmd, extinfo) -                    elif cmd.upper() == "IDENTIFY": -                        target = "********" -                        line = "%s %s"%(cmd, target) -                    self.IRC.event("onSend", self.IRC.addons, origin=origin, line=line, cmd=cmd, target=target, params=params, extinfo=extinfo) -                self.IRC.logwrite(">>> %(line)s" % vars()) -                if cmd.upper() == "QUIT": -                    self.IRC.quitexpected = True -                timestamps.append(time.time()) -                while timestamps[0] < timestamps[-1]-self.time-0.1: -                    del timestamps[0] -                if throttled: -                    if len(timestamps) < 2: -                        throttled = False -                else: -                    if len(timestamps) >= self.lines: -                        throttled = True -                if throttled: -                    time.sleep(max(timestamps[-1] + -                                   self.throttle-time.time(), 0)) -        except: -            self.IRC.connection.send("QUIT :%s\n" % -                                     traceback.format_exc().rstrip().split("\n")[-1]) -            self.IRC.connection.close() -            self.IRC.connection.shutdown(0) - - -class Pinger(Thread): -    def __init__(self, connection, lock=None): -        self.connection = connection -        self.lock = lock -        self.daemon = True -        Thread.__init__(self) - -    def run(self): -        pass @@ -14,11 +14,20 @@ import ssl  import urllib2  import irc -modemapping = dict( -    Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice") +modemapping = dict(Y="ircop", q="owner", +                   a="admin", o="op", h="halfop", v="voice") + + +def LoggerReload(log): +    newlog = Logger(logroot=log.logroot) +    for IRC, label in log.labels.items(): +        IRC.rmAddon(log) +        IRC.addAddon(newlog, label=label) +    return newlog  class Logger(Thread): +      def __init__(self, logroot):          self.logroot = logroot          path = [logroot] @@ -31,7 +40,7 @@ class Logger(Thread):          while len(path) > 1:              path[0] = os.path.join(*path[:2])              del path[1] -            #print path +            # print path              os.mkdir(path[0])          self.logs = {} @@ -45,10 +54,10 @@ class Logger(Thread):      def run(self):          try:              Y, M, D, h, m, s, w, d, dst = time.localtime() -            nextrotate = int(time.mktime((Y, M, D+1, 0, 0, 0, 0, 0, -1))) +            nextrotate = int(time.mktime((Y, M, D + 1, 0, 0, 0, 0, 0, -1)))              while True:                  while nextrotate > time.time():  # May need to do this in a loop in case the following time.sleep command wakes up a second too early. -                    time.sleep(max(0.1, min((nextrotate-time.time(), 3600)))) +                    time.sleep(max(0.1, min((nextrotate - time.time(), 3600))))                  with self.rotatelock:                      if all([not log or log.closed for log in self.logs.values()]):                          break @@ -60,31 +69,36 @@ class Logger(Thread):                                  self.rotateLog(IRC)                              except:                                  exc, excmsg, tb = sys.exc_info() -                                IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")]) +                                IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()] + [ +                                             "!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")])                              if IRC.identity:                                  for channel in IRC.identity.channels:                                      try:                                          self.rotateLog(channel)                                      except:                                          exc, excmsg, tb = sys.exc_info() -                                        IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")]) +                                        IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()] + [ +                                                     "!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")])                                  for user in IRC.users:                                      if user in self.logs.keys():                                          try:                                              self.closeLog(user)                                          except:                                              exc, excmsg, tb = sys.exc_info() -                                            IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")]) -                            IRC.logopen(os.path.join(self.logroot, self.labels[IRC], "rawdata-%04d.%02d.%02d.log"%now[:3])) -                nextrotate = int(time.mktime((Y, M, D+1, 0, 0, 0, 0, 0, -1))) +                                            IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()] + [ +                                                         "!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")]) +                            IRC.logopen( +                                os.path.join(self.logroot, self.labels[IRC], "rawdata-%04d.%02d.%02d.log" % now[:3])) +                nextrotate = int(time.mktime((Y, M, D + 1, 0, 0, 0, 0, 0, -1)))          finally:              Thread.__init__(self) +            self.daemon = True      def onAddonAdd(self, IRC, label):          if label in self.labels.values(): -            raise BaseException("Label already exists") +            raise BaseException, "Label already exists"          if IRC in self.labels.keys(): -            raise BaseException("Network already exists") +            raise BaseException, "Network already exists"          if not os.path.isdir(os.path.join(self.logroot, label)):              os.mkdir(os.path.join(self.logroot, label))          self.labels[IRC] = label @@ -94,10 +108,10 @@ class Logger(Thread):                  for channel in IRC.identity.channels:                      self.openLog(channel)          now = time.localtime() -        timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, -                                                               "0") for t in now[0:6]]) -        IRC.logopen(os.path.join(self.logroot, self.labels[IRC], -                                 "rawdata-%04d.%02d.%02d.log"%now[:3])) +        timestamp = reduce(lambda x, y: x + ":" + y, [ +                           str(t).rjust(2, "0") for t in now[0:6]]) +        IRC.logopen( +            os.path.join(self.logroot, self.labels[IRC], "rawdata-%04d.%02d.%02d.log" % now[:3]))      def onAddonRem(self, IRC):          if IRC.connected: @@ -118,21 +132,25 @@ class Logger(Thread):              if not self.isAlive():                  self.start()          now = time.localtime() -        timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, -                                                               "0") for t in now[0:6]]) +        timestamp = reduce(lambda x, y: x + ":" + y, [ +                           str(t).rjust(2, "0") for t in now[0:6]])          if type(window) == irc.Connection: -            log = self.logs[window] = open(os.path.join(self.logroot, self.labels[window], "console-%04d.%02d.%02d.log"%now[:3]), "a") +            log = self.logs[window] = open( +                os.path.join(self.logroot, self.labels[window], "console-%04d.%02d.%02d.log" % now[:3]), "a")              print >>log, "%s ### Log file opened" % (irc.timestamp())          elif type(window) == irc.Channel:              label = self.labels[window.context] -            log = self.logs[window] = open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(window.name.lower()).replace("/", "%2f"),)+now[:3])), "a") +            log = self.logs[window] = open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log" % ( +                (urllib2.quote(window.name.lower()).replace("/", "%2f"),) + now[:3])), "a")              print >>log, "%s ### Log file opened" % (irc.timestamp())              self.logs[window].flush()              if window.context.identity in window.users:                  if window.topic: -                    print >>log, "%s <<< :%s 332 %s %s :%s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topic) +                    print >>log, "%s <<< :%s 332 %s %s :%s" % ( +                        irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topic)                  if window.topicsetby and window.topictime: -                    print >>log, "%s <<< :%s 333 %s %s %s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topicsetby, window.topictime) +                    print >>log, "%s <<< :%s 333 %s %s %s %s" % ( +                        irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topicsetby, window.topictime)                  if window.users:                      secret = "s" in window.modes.keys() and window.modes["s"]                      private = "p" in window.modes.keys() and window.modes["p"] @@ -141,18 +159,24 @@ class Logger(Thread):                      print >>log, "%s <<< :%s 353 %s %s %s :%s" % (irc.timestamp(),                                                                    window.context.serv,                                                                    window.context.identity.nick, -                                                                  "@" if secret else ("*" if private else "="), +                                                                  "@" if secret else ( +                                                                      "*" if private else "="),                                                                    window.name, -                                                                  " ".join(["".join([symbols[k] if modes[k] in window.modes.keys() and user in window.modes[modes[k]] else "" for k in xrange(len(modes))])+user.nick for user in window.users])) +                                                                  " ".join(["".join([symbols[k] if modes[k] in window.modes.keys() and user in window.modes[modes[k]] else "" for k in xrange(len(modes))]) + user.nick for user in window.users]))                  if window.modes:                      modes = window.modes.keys() -                    modestr = "".join([mode for mode in modes if mode not in window.context.supports["CHANMODES"][0]+window.context.supports["PREFIX"][0] and window.modes[mode]]) -                    params = " ".join([window.modes[mode] for mode in modes if mode in window.context.supports["CHANMODES"][1]+window.context.supports["CHANMODES"][2] and window.modes[mode]]) -                    print >>log, "%s <<< :%s 324 %s %s +%s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, modestr, params) +                    modestr = "".join([mode for mode in modes if mode not in window.context.supports[ +                                      "CHANMODES"][0] + window.context.supports["PREFIX"][0] and window.modes[mode]]) +                    params = " ".join([window.modes[mode] for mode in modes if mode in window.context.supports[ +                                      "CHANMODES"][1] + window.context.supports["CHANMODES"][2] and window.modes[mode]]) +                    print >>log, "%s <<< :%s 324 %s %s +%s %s" % ( +                        irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, modestr, params)                  if window.created: -                    print >>log, "%s <<< :%s 329 %s %s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.created) +                    print >>log, "%s <<< :%s 329 %s %s %s" % ( +                        irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.created)          if type(window) == irc.User: -            logname = os.path.join(self.logroot, self.labels[window.context], "query-%s-%04d.%02d.%02d.log"%((urllib2.quote(window.nick.lower()).replace("/", "%2f"),)+now[:3])) +            logname = os.path.join(self.logroot, self.labels[window.context], "query-%s-%04d.%02d.%02d.log" % ( +                (urllib2.quote(window.nick.lower()).replace("/", "%2f"),) + now[:3]))              for (other, log) in self.logs.items():                  if other == window:                      continue @@ -169,8 +193,8 @@ class Logger(Thread):      def closeLog(self, window):          if window in self.logs.keys() and type(self.logs[window]) == file and not self.logs[window].closed: -            print >>self.logs[window], "%s ### Log file closed" % ( -                irc.timestamp()) +            print >>self.logs[ +                window], "%s ### Log file closed" % (irc.timestamp())              self.logs[window].close()          if window in self.logs.keys():              del self.logs[window] @@ -194,7 +218,7 @@ class Logger(Thread):              ts, IRC.server, IRC.port)      def onConnectFail(self, IRC, exc, excmsg, tb): -        ### Called when a connection attempt fails. +        # Called when a connection attempt fails.          if IRC not in self.logs.keys() or (not self.logs[IRC]) or self.logs[IRC].closed:              self.openLog(IRC)          ts = irc.timestamp() @@ -205,7 +229,8 @@ class Logger(Thread):          ts = irc.timestamp()          for window in self.logs.keys():              if type(window) in (irc.Channel, irc.User) and window.context == IRC: -                print >>self.logs[window], "%s *** Connection to %s:%s terminated." % (ts, IRC.server, IRC.port) +                print >>self.logs[window], "%s *** Connection to %s:%s terminated." % ( +                    ts, IRC.server, IRC.port)                  self.logs[window].flush()                  self.closeLog(window)          print >>self.logs[IRC], "%s *** Connection %s:%s terminated." % ( @@ -214,7 +239,7 @@ class Logger(Thread):          self.closeLog(IRC)      def onJoin(self, IRC, user, channel): -        ### Called when somebody joins a channel, includes bot. +        # Called when somebody joins a channel, includes bot.          ts = irc.timestamp()          if user == IRC.identity:              self.openLog(channel) @@ -223,95 +248,111 @@ class Logger(Thread):          self.logs[channel].flush()      def onChanMsg(self, IRC, user, channel, targetprefix, msg): -        ### Called when someone sends a PRIVMSG to channel. +        # Called when someone sends a PRIVMSG to channel.          ts = irc.timestamp()          if type(user) == irc.User: -            classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]]) +            classes = " ".join([modemapping[mode] +                               for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])              if classes: -                print >>self.logs[channel], "%s %s <<< :%s!%s@%s PRIVMSG %s%s :%s" % (ts, classes, user.nick, user.username, user.host, targetprefix, channel.name, msg) +                print >>self.logs[channel], "%s %s <<< :%s!%s@%s PRIVMSG %s%s :%s" % ( +                    ts, classes, user.nick, user.username, user.host, targetprefix, channel.name, msg)              else: -                print >>self.logs[channel], "%s <<< :%s!%s@%s PRIVMSG %s%s :%s" % (ts, user.nick, user.username, user.host, targetprefix, channel.name, msg) +                print >>self.logs[channel], "%s <<< :%s!%s@%s PRIVMSG %s%s :%s" % ( +                    ts, user.nick, user.username, user.host, targetprefix, channel.name, msg)          elif type(user) in (str, unicode):              classes = "server" -            print >>self.logs[channel], "%s %s <<< :%s PRIVMSG %s%s :%s" % (ts, -                                                                            classes, user, targetprefix, channel.name, msg) +            print >>self.logs[channel], "%s %s <<< :%s PRIVMSG %s%s :%s" % ( +                ts, classes, user, targetprefix, channel.name, msg)          self.logs[channel].flush()      def onChanAction(self, IRC, user, channel, targetprefix, action): -        self.onChanMsg(IRC, user, channel, targetprefix, -                       "\x01ACTION %s\x01"%action) +        self.onChanMsg( +            IRC, user, channel, targetprefix, "\x01ACTION %s\x01" % action)      def onChanNotice(self, IRC, origin, channel, targetprefix, msg): -        ### Called when someone sends a NOTICE to channel. +        # Called when someone sends a NOTICE to channel.          ts = irc.timestamp()          if type(origin) == irc.User: -            classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and origin in channel.modes[mode]]) +            classes = " ".join([modemapping[mode] +                               for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and origin in channel.modes[mode]])              if classes: -                print >>self.logs[channel], "%s %s <<< :%s!%s@%s NOTICE %s%s :%s" % (ts, classes, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg) +                print >>self.logs[channel], "%s %s <<< :%s!%s@%s NOTICE %s%s :%s" % ( +                    ts, classes, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg)              else: -                print >>self.logs[channel], "%s <<< :%s!%s@%s NOTICE %s%s :%s" % (ts, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg) +                print >>self.logs[channel], "%s <<< :%s!%s@%s NOTICE %s%s :%s" % ( +                    ts, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg)          elif type(origin) in (str, unicode):              classes = "server" -            print >>self.logs[channel], "%s %s <<< :%s NOTICE %s%s :%s" % (ts, -                                                                           classes, origin, targetprefix, channel.name, msg) +            print >>self.logs[channel], "%s %s <<< :%s NOTICE %s%s :%s" % ( +                ts, classes, origin, targetprefix, channel.name, msg)          self.logs[channel].flush()      def onPart(self, IRC, user, channel, partmsg): -        ### Called when somebody parts the channel, includes bot. +        # Called when somebody parts the channel, includes bot.          ts = irc.timestamp()          if partmsg: -            print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s :%s" % (ts, user.nick, user.username, user.host, channel.name, partmsg) +            print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s :%s" % ( +                ts, user.nick, user.username, user.host, channel.name, partmsg)          else: -            print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s" % (ts, -                                                                      user.nick, user.username, user.host, channel.name) +            print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s" % ( +                ts, user.nick, user.username, user.host, channel.name)          self.logs[channel].flush()          if user == IRC.identity:              self.closeLog(channel)      def onKick(self, IRC, kicker, channel, kicked, kickmsg): -        ### Called when somebody is kicked from the channel, includes bot. +        # Called when somebody is kicked from the channel, includes bot.          ts = irc.timestamp()          if kickmsg: -            print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s :%s" % (ts, kicker.nick, kicker.username, kicker.host, channel.name, kicked.nick, kickmsg) +            print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s :%s" % ( +                ts, kicker.nick, kicker.username, kicker.host, channel.name, kicked.nick, kickmsg)          else: -            print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s" % (ts, user.nick, user.username, user.host, channel.name, kicked.nick) +            print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s" % ( +                ts, user.nick, user.username, user.host, channel.name, kicked.nick)          self.logs[channel].flush() -        if user == IRC.identity: +        if kicked == IRC.identity:              self.closeLog(channel)      def onSendChanMsg(self, IRC, origin, channel, targetprefix, msg): -        ### Called when bot sends a PRIVMSG to channel. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. +        # Called when bot sends a PRIVMSG to channel. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent.          ts = irc.timestamp() -        classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]]) +        classes = " ".join([modemapping[mode] +                           for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]])          if classes: -            print >>self.logs[channel], "%s %s >>> :%s!%s@%s PRIVMSG %s%s :%s" % (ts, classes, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg) +            print >>self.logs[channel], "%s %s >>> :%s!%s@%s PRIVMSG %s%s :%s" % ( +                ts, classes, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)          else: -            print >>self.logs[channel], "%s >>> :%s!%s@%s PRIVMSG %s%s :%s" % (ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg) +            print >>self.logs[channel], "%s >>> :%s!%s@%s PRIVMSG %s%s :%s" % ( +                ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)          self.logs[channel].flush()      def onSendChanAction(self, IRC, origin, channel, targetprefix, action): -        ### origin is the source of the channel message -        ### Called when bot sends an action (/me) to channel. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. -        self.onSendChanMsg(IRC, origin, channel, targetprefix, -                           "\x01ACTION %s\x01"%action) +        # origin is the source of the channel message +        # Called when bot sends an action (/me) to channel. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent. +        self.onSendChanMsg( +            IRC, origin, channel, targetprefix, "\x01ACTION %s\x01" % action)      def onPrivMsg(self, IRC, user, msg): -        ### Called when someone sends a PRIVMSG to the bot. +        # Called when someone sends a PRIVMSG to the bot.          if user not in self.logs.keys():              self.openLog(user)          ts = irc.timestamp() -        print >>self.logs[user], "%s <<< :%s!%s@%s PRIVMSG %s :%s" % (ts, user.nick, user.username, user.host, IRC.identity.nick, msg) +        print >>self.logs[user], "%s <<< :%s!%s@%s PRIVMSG %s :%s" % ( +            ts, user.nick, user.username, user.host, IRC.identity.nick, msg)          self.logs[user].flush()      def onPrivNotice(self, IRC, origin, msg): -        ### Called when someone sends a NOTICE to the bot. +        # Called when someone sends a NOTICE to the bot.          ts = irc.timestamp()          if type(origin) == irc.User:              if origin not in self.logs.keys():                  self.openLog(origin) -            print >>self.logs[origin], "%s <<< :%s!%s@%s NOTICE %s :%s" % (ts, origin.nick, origin.username, origin.host, IRC.identity.nick, msg) +            print >>self.logs[origin], "%s <<< :%s!%s@%s NOTICE %s :%s" % ( +                ts, origin.nick, origin.username, origin.host, IRC.identity.nick, msg)              self.logs[origin].flush()          else:              print >>self.logs[IRC], "%s <<< :%s NOTICE %s :%s" % ( @@ -319,53 +360,57 @@ class Logger(Thread):              self.logs[IRC].flush()      def onPrivAction(self, IRC, user, action): -        ### Called when someone sends an action (/me) to the bot. -        self.onPrivMsg(IRC, user, "\x01ACTION %s\x01"%action) +        # Called when someone sends an action (/me) to the bot. +        self.onPrivMsg(IRC, user, "\x01ACTION %s\x01" % action)      def onSendPrivMsg(self, IRC, origin, user, msg): -        ### Called when bot sends a PRIVMSG to a user. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. +        # Called when bot sends a PRIVMSG to a user. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent.          if user not in self.logs.keys():              self.openLog(user)          ts = irc.timestamp() -        print >>self.logs[user], "%s >>> :%s!%s@%s PRIVMSG %s :%s" % (ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, user.nick, msg) +        print >>self.logs[user], "%s >>> :%s!%s@%s PRIVMSG %s :%s" % ( +            ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, user.nick, msg)          self.logs[user].flush()      def onSendPrivAction(self, IRC, origin, user, action): -        ### Called when bot sends an action (/me) to a user. -        ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent. -        self.onSendPrivMsg(IRC, origin, user, "\x01ACTION %s\x01"%action) +        # Called when bot sends an action (/me) to a user. +        # The variable origin refers to a class instance voluntarily +        # identifying itself as that which requested data be sent. +        self.onSendPrivMsg(IRC, origin, user, "\x01ACTION %s\x01" % action)      def onNickChange(self, IRC, user, newnick): -        ### Called when somebody changes nickname. +        # Called when somebody changes nickname.          ts = irc.timestamp()          line = "%s <<< :%s!%s@%s NICK %s" % (              ts, user.nick, user.username, user.host, newnick) -        ### Print nick change in each channel the user is in. +        # Print nick change in each channel the user is in.          for channel in user.channels:              print >>self.logs[channel], line              self.logs[channel].flush() -        ### And in the query if open. +        # And in the query if open.          if user in self.logs.keys():              print >>self.logs[user], line              self.logs[user].flush()      def onMeNickChange(self, IRC, newnick): -        ### Called when the bot changes nickname. +        # Called when the bot changes nickname. -        ### Print nick change to all open queries, except for query with self (already done with onNickChange). +        # Print nick change to all open queries, except for query with self +        # (already done with onNickChange).          ts = irc.timestamp() -        line = "%s <<< :%s!%s@%s NICK %s" % (ts, IRC.identity.nick, -                                             IRC.identity.username, IRC.identity.host, newnick) +        line = "%s <<< :%s!%s@%s NICK %s" % ( +            ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, newnick)          for (window, log) in self.logs.items():              if type(window) == irc.User and window != IRC.identity:                  print >>log, line                  log.flush()      def onQuit(self, IRC, user, quitmsg): -        ### Called when somebody quits IRC. +        # Called when somebody quits IRC.          ts = irc.timestamp()          if quitmsg:              line = "%s <<< :%s!%s@%s QUIT :%s" % ( @@ -374,20 +419,20 @@ class Logger(Thread):              line = "%s <<< :%s!%s@%s QUIT" % (                  ts, user.nick, user.username, user.host) -        ### Print quit in each channel the user was in. +        # Print quit in each channel the user was in.          for channel in user.channels:              if channel in self.logs.keys() and not self.logs[channel].closed:                  print >>self.logs[channel], line                  self.logs[channel].flush() -        ### And in the query if open. +        # And in the query if open.          if user in self.logs.keys():              print >>self.logs[user], line              self.logs[user].flush()              self.closeLog(user)      def onNames(self, IRC, origin, channel, flag, channame, nameslist): -        ### Called when a NAMES list is received. +        # Called when a NAMES list is received.          if channel in self.logs.keys() and not self.logs[channel].closed:              log = self.logs[channel]          else: @@ -398,7 +443,7 @@ class Logger(Thread):          private = "p" in channel.modes.keys() and channel.modes["p"]          modes, symbols = channel.context.supports["PREFIX"]          print >>log, "%s <<< :%s 353 %s %s %s :%s" % (ts, origin, IRC.identity.nick, flag, channame, -                                                      " ".join(["%s%s!%s@%s"%(prefix, nick, username, host) if username and host else "%s%s"%(prefix, nick) for (prefix, nick, username, host) in nameslist])) +                                                      " ".join(["%s%s!%s@%s" % (prefix, nick, username, host) if username and host else "%s%s" % (prefix, nick) for (prefix, nick, username, host) in nameslist]))          log.flush()      def onNamesEnd(self, IRC, origin, channel, channame, endmsg): @@ -412,51 +457,53 @@ class Logger(Thread):          log.flush()      def onWhoisStart(self, IRC, origin, user, nickname, username, host, realname): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 311 %s %s %s %s * :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, username, host, realname) +        print >>self.logs[user], "%s <<< :%s 311 %s %s %s %s * :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, username, host, realname)      def onWhoisRegisteredNick(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user)          print >>self.logs[user], "%s <<< :%s 307 %s %s :%s" % (              irc.timestamp(), origin, IRC.identity.nick, nickname, msg)      def onWhoisAway(self, IRC, origin, user, nickname, awaymsg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 301 %s %s :%s" % (irc.timestamp( -        ), origin, IRC.identity.nick, nickname, awaymsg) +        print >>self.logs[user], "%s <<< :%s 301 %s %s :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, awaymsg)      def onWhoisConnectingFrom(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user)          print >>self.logs[user], "%s <<< :%s 378 %s %s :%s" % (              irc.timestamp(), origin, IRC.identity.nick, nickname, msg)      def onWhoisChannels(self, IRC, origin, user, nickname, chanlist): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 319 %s %s :%s" % (irc.timestamp(), -                                                               origin, IRC.identity.nick, nickname, " ".join(chanlist)) +        print >>self.logs[user], "%s <<< :%s 319 %s %s :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, " ".join(chanlist))      def onWhoisAvailability(self, IRC, origin, user, nickname, msg): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user)          print >>self.logs[user], "%s <<< :%s 310 %s %s :%s" % (              irc.timestamp(), origin, IRC.identity.nick, nickname, msg)      def onWhoisServer(self, IRC, origin, user, nickname, server, servername): -        ### Called when a WHOIS reply is received. +        # Called when a WHOIS reply is received.          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 312 %s %s %s :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, server, servername) +        print >>self.logs[user], "%s <<< :%s 312 %s %s %s :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, server, servername)      def onWhoisOp(self, IRC, origin, user, nickname, msg):          if user not in self.logs.keys(): @@ -467,7 +514,8 @@ class Logger(Thread):      def onWhoisTimes(self, IRC, origin, user, nickname, idletime, signontime, msg):          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 317 %s %s %d %d :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, idletime, signontime, msg) +        print >>self.logs[user], "%s <<< :%s 317 %s %s %d %d :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, idletime, signontime, msg)      def onWhoisSSL(self, IRC, origin, user, nickname, msg):          if user not in self.logs.keys(): @@ -484,7 +532,8 @@ class Logger(Thread):      def onWhoisLoggedInAs(self, IRC, origin, user, nickname, loggedinas, msg):          if user not in self.logs.keys():              self.openLog(user) -        print >>self.logs[user], "%s <<< :%s 330 %s %s %s :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, loggedinas, msg) +        print >>self.logs[user], "%s <<< :%s 330 %s %s %s :%s" % ( +            irc.timestamp(), origin, IRC.identity.nick, nickname, loggedinas, msg)      def onWhoisEnd(self, IRC, origin, user, nickname, msg):          if user not in self.logs.keys(): @@ -494,19 +543,19 @@ class Logger(Thread):          self.logs[user].flush()      def onWhoEntry(self, IRC, **kwargs): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          pass      def onWhoEnd(self, IRC, **kwargs): -        ### Called when a WHO list is received. +        # Called when a WHO list is received.          pass      def onList(self, IRC, chanlistbegin, chanlist, endmsg): -        ### Called when a channel list is received. +        # Called when a channel list is received.          pass      def onTopic(self, IRC, origin, channel, topic): -        ### Called when channel topic is received via 332 response. +        # Called when channel topic is received via 332 response.          ts = irc.timestamp()          if channel in self.logs.keys() and not self.logs[channel].closed:              log = self.logs[channel] @@ -517,26 +566,27 @@ class Logger(Thread):          log.flush()      def onTopicInfo(self, IRC, origin, channel, topicsetby, topictime): -        ### Called when channel topic info is received via 333 response. +        # Called when channel topic info is received via 333 response.          ts = irc.timestamp()          if channel in self.logs.keys() and not self.logs[channel].closed:              log = self.logs[channel]          else:              log = self.logs[IRC] -        print >>log, "%s <<< :%s 333 %s %s %s %d" % (ts, origin, -                                                     IRC.identity.nick, channel.name, topicsetby, topictime) +        print >>log, "%s <<< :%s 333 %s %s %s %d" % ( +            ts, origin, IRC.identity.nick, channel.name, topicsetby, topictime)          log.flush()      def onTopicSet(self, IRC, user, channel, topic): -        ### Called when channel topic is changed. +        # Called when channel topic is changed.          ts = irc.timestamp() -        print >>self.logs[channel], "%s <<< :%s!%s@%s TOPIC %s :%s" % (ts, -                                                                       user.nick, user.username, user.host, channel.name, topic) +        print >>self.logs[channel], "%s <<< :%s!%s@%s TOPIC %s :%s" % ( +            ts, user.nick, user.username, user.host, channel.name, topic)          self.logs[channel].flush()      def onChanModeSet(self, IRC, user, channel, modedelta): -        ### Called when channel modes are changed. -        ### modedelta is a list of tuples of the format ("+x", parameter), ("+x", None) if no parameter is provided. +        # Called when channel modes are changed. +        # modedelta is a list of tuples of the format ("+x", parameter), ("+x", +        # None) if no parameter is provided.          ts = irc.timestamp()          modestr = ""          params = [] @@ -546,23 +596,26 @@ class Logger(Thread):                  modestr += sgn                  sign = sgn              modestr += modechar -            if param is not None: +            if param != None:                  params.append(param.nick if type(param) == irc.User else param)          if len(params):              if type(user) == irc.User: -                print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s %s" % (ts, user.nick, user.username, user.host, channel.name, modestr, " ".join(params)) +                print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s %s" % ( +                    ts, user.nick, user.username, user.host, channel.name, modestr, " ".join(params))              else: -                print >>self.logs[channel], "%s <<< :%s MODE %s %s %s" % (ts, user, channel.name, modestr, " ".join(params)) +                print >>self.logs[channel], "%s <<< :%s MODE %s %s %s" % ( +                    ts, user, channel.name, modestr, " ".join(params))          else:              if type(user) == irc.User: -                print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s" % (ts, user.nick, user.username, user.host, channel.name, modestr) +                print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s" % ( +                    ts, user.nick, user.username, user.host, channel.name, modestr)              else:                  print >>self.logs[channel], "%s <<< :%s MODE %s %s" % (                      ts, user, channel.name, modestr)          self.logs[channel].flush()      def onChannelModes(self, IRC, channel, modedelta): -        ### Called when channel modes are received via 324 response. +        # Called when channel modes are received via 324 response.          ts = irc.timestamp()          if channel in self.logs.keys() and not self.logs[channel].closed:              log = self.logs[channel] @@ -576,17 +629,18 @@ class Logger(Thread):                  modestr += sgn                  sign = sgn              modestr += modechar -            if param is not None: +            if param != None:                  params.append(param)          if len(params): -            print >>log, "%s <<< :%s 324 %s %s %s %s" % (ts, IRC.serv, IRC.identity.nick, channel.name, modestr, " ".join(params)) +            print >>log, "%s <<< :%s 324 %s %s %s %s" % ( +                ts, IRC.serv, IRC.identity.nick, channel.name, modestr, " ".join(params))          else: -            print >>log, "%s <<< :%s 324 %s %s %s" % (ts, IRC.serv, -                                                      IRC.identity.nick, channel.name, modestr) +            print >>log, "%s <<< :%s 324 %s %s %s" % ( +                ts, IRC.serv, IRC.identity.nick, channel.name, modestr)          log.flush()      def onChanCreated(self, IRC, channel, created): -        ### Called when a 329 response is received. +        # Called when a 329 response is received.          ts = irc.timestamp()          if channel in self.logs.keys() and not self.logs[channel].closed:              log = self.logs[channel] | 
