From 0d802dab5b586d511a6291248702c910502acaf7 Mon Sep 17 00:00:00 2001 From: Brian Sherson Date: Wed, 4 Dec 2013 01:35:44 -0800 Subject: Significant changes --- bouncer.py | 513 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 314 insertions(+), 199 deletions(-) (limited to 'bouncer.py') diff --git a/bouncer.py b/bouncer.py index c367dfd..9cfc786 100644 --- a/bouncer.py +++ b/bouncer.py @@ -8,31 +8,35 @@ import sys import string import hashlib import traceback +import irc from threading import Thread, Lock import Queue class Bouncer (Thread): - def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr, timeout=300): + 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): self.__name__ = "Bouncer for pyIRC" - self.__version__ = "1.0.0rc2" + self.__version__ = "1.1" self.__author__ = "Brian Sherson" - self.__date__ = "August 26, 2013" - #print "Initializing ListenThread..." + self.__date__ = "December 1, 2013" + self.addr = addr self.port = port self.servers = {} self.passwd = {} - self.socket = s = socket.socket() + self.socket = socket.socket( + socket.AF_INET6 if ipv6 else socket.AF_INET) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ssl = ssl + self.ipv6 = ipv6 self.certfile = certfile self.keyfile = keyfile - s.bind((self.addr, self.port)) + self.socket.bind((self.addr, self.port)) self.connections = [] self.ignore = ignore self.debug = debug self.timeout = timeout + self.autoaway = autoaway ### 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. @@ -71,102 +75,13 @@ class Bouncer (Thread): connection.settimeout(self.timeout) bouncer = BouncerConnection( self, connection, addr, debug=self.debug) - #bouncer.daemon=True - #self.connections.append(bouncer) - #bouncer.start() - #ccrecv.start() time.sleep(0.5) try: self.socket.close() except: pass - def onRecv(self, IRC, line, data): - if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]): - if data: - (origin, ident, host, cmd, target, params, extinfo) = data - #print data - if re.match("^\\d+$", cmd): - cmd = int(cmd) # Code is a numerical response - if cmd in (352, 315, 304) or (cmd == 461 and params.upper() == "WHO"): # WHO reply - if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections: - self.whoexpected[IRC][0].send(line+"\n") - if cmd == 315 or (cmd == 461 and params.upper() == "WHO"): # End of WHO reply - origin = self.whoexpected[IRC][0] - del self.whoexpected[IRC][0] - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - if self.debug: - with IRC.loglock: - if issubclass(type(origin), Thread): - name = origin.name - print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] Removing %(origin)s (%(name)s) from WHO expected list." % vars() - else: - print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] Removing %(origin)s from 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.onRecv] WHO expected: %(obj)s (%(name)s)" % vars() - else: - print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] WHO expected: %(obj)s" % vars() - IRC.log.flush() - elif cmd in (307, 311, 312, 313, 317, 318, 319, 330, 335, 336, 378, 379): # WHO reply - if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections: - self.whoisexpected[IRC][0].send(line+"\n") - if cmd == 318: # End of WHOIS reply - del self.whoisexpected[IRC][0] - elif cmd in (321, 322, 323): # LIST reply - if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections: - self.listexpected[IRC][0].send(line+"\n") - if cmd == 323: # End of LIST reply - del self.listexpected[IRC][0] - else: - for bouncer in self.connections: - #print bouncer.IRC - #print IRC - #print line - if bouncer.IRC == IRC: - bouncer.send(line+"\n") - - def onSend(self, IRC, line, data, origin): - if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]): - (cmd, target, params, extinfo) = data - if cmd.upper() in ("PRIVMSG", "NOTICE"): - for bouncerconnection in self.connections: - if bouncerconnection == origin: # Do NOT send the message back to the originating client. - continue - if bouncerconnection.IRC == IRC: # Send the message to the other clients connected to the bouncer. - ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", - extinfo) - if ctcp: - (ctcptype, ext) = ctcp[0] - if ctcptype == "ACTION": - bouncerconnection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line)) - ### Unless the message is a CTCP that is not ACTION. - else: - bouncerconnection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line)) - elif cmd.upper() == "WHO": - self.whoexpected[IRC].append(origin) - if self.debug: - with IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - 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() - elif cmd.upper() == "WHOIS": - self.whoisexpected[IRC].append(origin) - elif cmd.upper() == "LIST": - self.listexpected[IRC].append(origin) - - def onModuleAdd(self, IRC, label, passwd, hashtype="md5"): + def onAddonAdd(self, IRC, label, passwd, hashtype="md5"): if IRC in [connection for (connection, passwd, hashtype) in self.servers.values()]: return # Silently do nothing if label in self.servers.keys(): @@ -174,15 +89,11 @@ class Bouncer (Thread): self.servers[label] = (IRC, passwd, hashtype) self.whoexpected[IRC] = [] if self.debug: - with IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( - 2, "0") for t in time.localtime()[0:6]]) - print >>IRC.log, "%(timestamp)s dbg [Bouncer.onModuleAdd] Clearing WHO expected list." % vars() - IRC.log.flush() + IRC.logwrite("dbg [Bouncer.onAddonAdd] Clearing WHO expected list." % vars()) self.whoisexpected[IRC] = [] self.listexpected[IRC] = [] - def onModuleRem(self, IRC): + def onAddonRem(self, IRC): for bouncerconnection in self.connections: if bouncerconnection.IRC == IRC: bouncerconnection.quit(quitmsg="Bouncer extension removed") @@ -197,32 +108,263 @@ class Bouncer (Thread): for bouncerconnection in self.connections: bouncerconnection.stop(quitmsg=quitmsg) - def onDisconnect(self, IRC): + def onDisconnect(self, IRC, expected=False): self.whoexpected[IRC] = [] if self.debug: - with IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( - 2, "0") for t in time.localtime()[0:6]]) - print >>IRC.log, "%(timestamp)s dbg [Bouncer.onDisconnect] Clearing WHO expected list." % vars() - IRC.log.flush() + 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") + 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. + 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)) + + def onSendChanAction(self, IRC, origin, channel, targetprefix, 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. + 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)) + + 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() + 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. + 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 + + def onWhoEnd(self, IRC, origin, param, endmsg): + ### 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] + + def onListStart(self, IRC, origin, params, extinfo): + ### 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 + + def onListEntry(self, IRC, origin, channel, population, extinfo): + ### 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 + + def onListEnd(self, IRC, origin, endmsg): + ### 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] + + def onWhoisStart(self, IRC, origin, user, nickname, username, host, realname): + ### 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 + + def onWhoisRegisteredNick(self, IRC, origin, user, nickname, msg): + ### 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 + + def onWhoisConnectingFrom(self, IRC, origin, user, nickname, msg): + ### 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 + + def onWhoisChannels(self, IRC, origin, user, nickname, chanlist): + ### 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 + + def onWhoisAvailability(self, IRC, origin, user, nickname, msg): + ### 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 + + def onWhoisServer(self, IRC, origin, user, nickname, server, servername): + ### 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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] + + def onUnhandled(self, IRC, line, origin, cmd, target, params, extinfo): + for bouncerconnection in self.connections: + if bouncerconnection.IRC == IRC: + bouncerconnection.send("%s\n"%line) + class BouncerConnection (Thread): def __init__(self, bouncer, connection, addr, debug=False): #print "Initializing ListenThread..." self.bouncer = bouncer self.connection = connection - self.host, self.port = self.addr = addr + if len(addr) == 4: + self.host, self.port = self.addr = addr[:2] + self.ipv6 = True + else: + self.host, self.port = self.addr = addr + self.ipv6 = False self.IRC = None self.pwd = None self.nick = None self.label = None - self.idnt = None + self.username = None self.realname = None self.addr = addr self.debug = debug @@ -237,16 +379,10 @@ class BouncerConnection (Thread): def send(self, data, flags=0): try: with self.lock: - self.connection.send(data, flags=flags) + self.connection.send(data) except socket.error: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( - 2, "0") for t in time.localtime()[0:6]]) - exc, excmsg, tb = sys.exc_info() - print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection.send] Exception in module %(module)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection.send] %(tbline)s" % vars() - self.IRC.log.flush() + 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")]) self.quit(quitmsg=excmsg.message) def __repr__(self): @@ -254,7 +390,7 @@ class BouncerConnection (Thread): port = self.IRC.port if self.IRC else "*" if self.IRC and self.IRC.identity: nick = self.IRC.identity.nick - ident = self.IRC.identity.idnt if self.IRC.identity.idnt else "*" + ident = self.IRC.identity.username if self.IRC.identity.username else "*" host = self.IRC.identity.host if self.IRC.identity.host else "*" else: nick = "*" @@ -282,7 +418,12 @@ class BouncerConnection (Thread): def run(self): ### Add connection to connection list. - listnumerics = dict(b=(367, 368, "channel ban list"), e=(348, 349, "Channel Exception List"), I=(346, 347, "Channel Invite Exception List"), w=(910, 911, "Channel Access List"), g=(941, 940, "chanel spamfilter list"), X=(954, 953, "channel exemptchanops list")) + listnumerics = dict(b=(367, 368, "channel ban list"), + e=(348, 349, "Channel Exception List"), + I=(346, 347, "Channel Invite Exception List"), + w=(910, 911, "Channel Access List"), + g=(941, 940, "chanel spamfilter list"), + X=(954, 953, "channel exemptchanops list")) passwd = None nick = None @@ -296,7 +437,7 @@ class BouncerConnection (Thread): while True: ### Read data (appending) into readbuf, then break lines and append lines to linebuf while len(linebuf) == 0: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) + timestamp = irc.timestamp() read = self.connection.recv(512) if read == "" and len(linebuf) == 0: # No more data to process. #self.quitmsg="Connection Closed" @@ -330,38 +471,31 @@ class BouncerConnection (Thread): self.quit("Access Denied") break - elif not self.idnt: # Bouncer expects a USER command to finish registration + elif not self.username: # Bouncer expects a USER command to finish registration if cmd.upper() == "USER": - self.idnt = target - #print self.idnt - if self.idnt in self.bouncer.servers.keys(): - self.IRC, passwdhash, hashtype = self.bouncer.servers[self.idnt] + self.username = target + #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 with self.IRC.lock: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%s *** [BouncerConnection] Incoming connection from %s to %s." % (timestamp, self.host, self.IRC) - self.IRC.log.flush() + 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: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Attempting to broadcast incoming connection %(self)s." % vars() - self.IRC.log.flush() + self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast incoming connection %(self)s." % vars()) for bouncerconnection in self.bouncer.connections: + if bouncerconnection.IRC != self.IRC: + continue if self.debug: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars() - self.IRC.log.flush() + 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: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars() - self.IRC.log.flush() + 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. + 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): @@ -371,9 +505,9 @@ class BouncerConnection (Thread): ### 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.idnt, n) in labels: + while "*%s_%d"%(self.username, n) in labels: n += 1 - self.label = "*%s_%d"%(self.idnt, n) + self.label = "*%s_%d"%(self.username, n) ### Request Version info. #self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick)) @@ -384,8 +518,8 @@ class BouncerConnection (Thread): 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.servinfo)) - self.connection.send(":%s 004 %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.serv004)) + 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()] @@ -405,10 +539,16 @@ class BouncerConnection (Thread): self.connection.send(":%s 005 %s %s :are supported by this server\n" % (self.IRC.serv, self.IRC.identity.nick, support)) ### Send MOTD - 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)) - self.connection.send(":%s 376 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend)) + 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)) @@ -416,7 +556,7 @@ class BouncerConnection (Thread): 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.idnt, self.IRC.identity.host)) + # 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)) @@ -435,7 +575,7 @@ class BouncerConnection (Thread): ### 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.idnt, self.IRC.identity.host, channel.name)) + 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)) @@ -469,33 +609,18 @@ class BouncerConnection (Thread): break elif cmd.upper() == "PING": - with self.lock: - self.connection.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick)) - - elif cmd.upper() == "WHO" and target.lower() == "$bouncer": - with self.lock: - for bouncerconnection in self.bouncer.connections: - self.connection.send(":$bouncer 352 %s $bouncer %s %s $bouncer %s H@ :0 %s\n" % (self.IRC.identity.nick, bouncerconnection.idnt, bouncerconnection.host, bouncerconnection.label, bouncerconnection.IRC)) - self.connection.send(":$bouncer 315 %s $bouncer :End if /WHO list.\n" % (self.IRC.identity.nick)) + 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 target.lower() == "$bouncer": # Message to internal bouncer control channel - if ctcp and cmd.upper() == "NOTICE": - (ctcptype, ext) = ctcp[0] # Unpack CTCP info - if ctcptype == "VERSION": # Client is sending back version reply - reply = ":%s!%s@%s PRIVMSG $bouncer :Version reply: %s\n" % (self.label, self.idnt, self.addr[0], ext) - for bouncerconnection in self.bouncer.connections: - bouncerconnection.connection.send(reply) - elif ctcp: # If CTCP, only want to + 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. - with self.lock: - self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line)) + 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: @@ -509,8 +634,10 @@ class BouncerConnection (Thread): 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: - self.connection.send(":%s 324 %s %s +%s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params)) - self.connection.send(":%s 329 %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.created)) + 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) @@ -543,12 +670,8 @@ class BouncerConnection (Thread): exc, excmsg, tb = sys.exc_info() self.quitmsg = str(excmsg) if self.IRC: - with self.IRC.loglock: - exc, excmsg, tb = sys.exc_info() - print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] Exception in module %(self)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] %(tbline)s" % vars() - self.IRC.log.flush() + 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")]) finally: ### Juuuuuuust in case. with self.lock: @@ -559,36 +682,28 @@ class BouncerConnection (Thread): pass if self.IRC: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%s *** [BouncerConnection] Connection from %s terminated (%s)." % (timestamp, self.host, self.quitmsg) - self.IRC.log.flush() + 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.debug: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars() - self.IRC.log.flush() + self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars()) for bouncerconnection in self.bouncer.connections: - if self.debug: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars() - self.IRC.log.flush() - 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)) + if bouncerconnection.IRC == self.IRC: if self.debug: - with self.IRC.loglock: - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.IRC.log, "%(timestamp)s dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars() - self.IRC.log.flush() + 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)) + 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.idnt, self.host, self.quitmsg)) +# bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.username, self.host, self.quitmsg)) # except: # pass -- cgit v1.2.3