From 97d3076e651c36dbcfe4fe478762e63e83a3848b Mon Sep 17 00:00:00 2001 From: Brian Sherson Date: Sun, 1 Sep 2013 22:12:55 -0700 Subject: Just another update --- autoexec.py | 2 +- bouncer.py | 329 ++++++++++++++++++++++++++++++++---------------------------- irc.py | 15 +-- 3 files changed, 187 insertions(+), 159 deletions(-) diff --git a/autoexec.py b/autoexec.py index 7953267..a1fa52b 100644 --- a/autoexec.py +++ b/autoexec.py @@ -76,7 +76,7 @@ class NickServ(object): return (origin, ident, host, cmd, target, params, extinfo) = data label, wallet, autojoin = self.networks[IRC] - if target == IRC.identity.nick and origin == "NickServ" and re.match("This nickname is registered and protected.", extinfo) and wallet and "%s/NickServ/%s"%(label, target.lower()) in wallet.keys(): + if target == IRC.identity.nick and origin == "NickServ" and re.match("This nickname is registered( and protected)?.", extinfo) and wallet and "%s/NickServ/%s"%(label, target.lower()) in wallet.keys(): IRC.user("NickServ").msg("identify %s" % wallet[ "%s/NickServ/%s"%(label, target.lower())]) if cmd == "900" and autojoin: diff --git a/bouncer.py b/bouncer.py index db54644..c367dfd 100644 --- a/bouncer.py +++ b/bouncer.py @@ -13,7 +13,7 @@ import Queue class Bouncer (Thread): - def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr): + def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr, timeout=300): self.__name__ = "Bouncer for pyIRC" self.__version__ = "1.0.0rc2" self.__author__ = "Brian Sherson" @@ -32,6 +32,7 @@ class Bouncer (Thread): self.connections = [] self.ignore = ignore self.debug = debug + self.timeout = timeout ### 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. @@ -67,7 +68,9 @@ class Bouncer (Thread): self.socket.close() #raise sys.exit() - bouncer = BouncerConnection(self, connection, addr) + connection.settimeout(self.timeout) + bouncer = BouncerConnection( + self, connection, addr, debug=self.debug) #bouncer.daemon=True #self.connections.append(bouncer) #bouncer.start() @@ -87,7 +90,7 @@ class Bouncer (Thread): 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].connection.send(line+"\n") + 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] @@ -108,12 +111,12 @@ class Bouncer (Thread): 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].connection.send(line+"\n") + 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].connection.send(line+"\n") + self.listexpected[IRC][0].send(line+"\n") if cmd == 323: # End of LIST reply del self.listexpected[IRC][0] else: @@ -122,15 +125,7 @@ class Bouncer (Thread): #print IRC #print line if bouncer.IRC == IRC: - try: - bouncer.connection.send(line+"\n") - except socket.error: - with IRC.loglock: - exc, excmsg, tb = sys.exc_info() - print >>IRC.log, "%(timestamp)s !!! [Bouncer.onRecv] Exception in module %(module)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>IRC.log, "%(timestamp)s !!! [Bouncer.onRecv] %(tbline)s" % vars() - IRC.log.flush() + 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]): @@ -145,10 +140,10 @@ class Bouncer (Thread): if ctcp: (ctcptype, ext) = ctcp[0] if ctcptype == "ACTION": - bouncerconnection.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line)) + 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.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line)) + 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: @@ -196,8 +191,6 @@ class Bouncer (Thread): del self.servers[label] def stop(self): - #self.quitmsg=quitmsg - #self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick, self.addr[0], self.quitmsg)) self.socket.shutdown(0) def disconnectall(self, quitmsg="Disconnecting all sessions"): @@ -220,7 +213,7 @@ class Bouncer (Thread): class BouncerConnection (Thread): - def __init__(self, bouncer, connection, addr): + def __init__(self, bouncer, connection, addr, debug=False): #print "Initializing ListenThread..." self.bouncer = bouncer self.connection = connection @@ -232,12 +225,30 @@ class BouncerConnection (Thread): self.idnt = None self.realname = None self.addr = addr + self.debug = debug + self.lock = Lock() self.quitmsg = "Connection Closed" + self.quitting = False Thread.__init__(self) self.daemon = True self.start() + def send(self, data, flags=0): + try: + with self.lock: + self.connection.send(data, flags=flags) + 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() + self.quit(quitmsg=excmsg.message) + def __repr__(self): server = self.IRC.server if self.IRC else "*" port = self.IRC.port if self.IRC else "*" @@ -254,16 +265,19 @@ class BouncerConnection (Thread): return "" % locals() def quit(self, quitmsg="Disconnected"): - self.quitmsg = quitmsg - try: - 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.close() - except: - pass + if not self.quitting: + 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)) + except: + pass + try: + self.connection.shutdown(1) + self.connection.close() + except: + pass + self.quitting = True def run(self): ### Add connection to connection list. @@ -329,23 +343,26 @@ class BouncerConnection (Thread): print >>self.IRC.log, "%s *** [BouncerConnection] Incoming connection from %s to %s." % (timestamp, self.host, self.IRC) self.IRC.log.flush() with self.bouncer.lock: - self.bouncer.connections.append(self) ### 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() for bouncerconnection in self.bouncer.connections: - try: - bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC)) - except: + if self.debug: 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() + 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() - bouncerconnection.quitmsg = excmsg - try: - bouncerconnection.connection.shutdown(0) - except: - pass + 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.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") @@ -364,80 +381,81 @@ class BouncerConnection (Thread): ### Log incoming connection ### Send Greeting. - 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)) - - ### 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 - 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 - 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)) - - ### 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.idnt, 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.idnt, 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)) + 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)) + + ### 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 + 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 + 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)) + + ### 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.idnt, 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.idnt, 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)) else: # User not found self.quit("Access Denied") @@ -451,12 +469,14 @@ class BouncerConnection (Thread): break elif cmd.upper() == "PING": - self.connection.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick)) + 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": - 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)) + 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)) elif cmd.upper() in ("PRIVMSG", "NOTICE"): ### Check if CTCP @@ -467,17 +487,15 @@ class BouncerConnection (Thread): 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: - reply = ":%s!%s@%s PRIVMSG $bouncer :Version reply: %s\n" % (self.label, self.idnt, self.addr[0], ext) - try: - bouncerconnection.connection.send(reply) - except: - pass + bouncerconnection.connection.send(reply) elif 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.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line)) + with self.lock: + self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line)) else: self.IRC.raw(line, origin=self) else: @@ -490,8 +508,9 @@ class BouncerConnection (Thread): 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]]) - 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)) + 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)) elif re.match("^\\+?[%s]+$"%self.IRC.supports["CHANMODES"][0], params) and extinfo == "": #print "ddd Mode List Request", params channel = self.IRC.channel(target) @@ -500,17 +519,19 @@ class BouncerConnection (Thread): if mode in redundant or mode not in listnumerics.keys(): continue i, e, l = listnumerics[mode] - 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)) + 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(): - 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)) + 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: @@ -530,11 +551,12 @@ class BouncerConnection (Thread): self.IRC.log.flush() finally: ### Juuuuuuust in case. - try: - self.connection.shutdown(1) - self.connection.close() - except: - pass + with self.lock: + try: + self.connection.shutdown(1) + self.connection.close() + except: + pass if self.IRC: with self.IRC.loglock: @@ -545,21 +567,24 @@ class BouncerConnection (Thread): if self in self.bouncer.connections: with self.bouncer.lock: self.bouncer.connections.remove(self) + 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() for bouncerconnection in self.bouncer.connections: - try: - bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg)) - except: + if self.debug: 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() + 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() - bouncerconnection.quitmsg = excmsg - try: - bouncerconnection.connection.shutdown(0) - except: - pass + 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: + 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() # ### Announce QUIT to other bouncer connections. # for bouncerconnection in self.bouncer.connections: diff --git a/irc.py b/irc.py index d8d564e..4718187 100644 --- a/irc.py +++ b/irc.py @@ -152,6 +152,13 @@ class Connection(Thread): 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: + with self.loglock: + exc, excmsg, tb = sys.exc_info() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars() + self.log.flush() + else: self.connected = True self.connection.settimeout(self.timeout) @@ -172,17 +179,13 @@ class Connection(Thread): print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s established." % vars() self.log.flush() break - except socket.error: - with self.loglock: - exc, excmsg, tb = sys.exc_info() - timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars() - self.log.flush() if self.quitexpected: sys.exit() if attempt < self.maxretries or self.maxretries == -1: time.sleep(self.retrysleep) + if self.quitexpected: + sys.exit() attempt += 1 else: with self.loglock: -- cgit v1.2.3