diff options
-rw-r--r-- | autoexec.py | 139 | ||||
-rw-r--r-- | bouncer.py | 1007 | ||||
-rw-r--r-- | cannon.py | 62 | ||||
-rw-r--r-- | figlet.py | 39 | ||||
-rw-r--r-- | irc.py | 2227 | ||||
-rw-r--r-- | logger.py | 672 | ||||
-rw-r--r-- | sedbot.py | 136 | ||||
-rwxr-xr-x | startirc.py | 93 | ||||
-rw-r--r-- | wallet.py | 74 |
9 files changed, 2460 insertions, 1989 deletions
diff --git a/autoexec.py b/autoexec.py index 8a8b7e7..7953267 100644 --- a/autoexec.py +++ b/autoexec.py @@ -1,68 +1,83 @@ #!/usr/bin/python import re + class Autoexec(object): - def __init__(self): - self.networks={} - def onModuleAdd(self, IRC, label, onconnect=None, onregister=None, autojoin=None, usermodes=None, wallet=None, opername=None, opermodes=None, snomasks=None, operexec=None, operjoin=None): - labels=[v[0] for v in self.networks.values()] - if label in labels: - raise BaseException, "Label already exists" - if IRC in self.networks.keys(): - raise BaseException, "Network already exists" - self.networks[IRC]=(label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) - def onModuleRem(self, IRC): - del self.networks[IRC] - def onConnect(self, IRC): - (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC] - if onconnect: - for line in onconnect: - IRC.raw(line, origin=self) - def onRegistered(self, IRC): - (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC] - if onregister: - for line in onregister: - IRC.raw(line, origin=self) - if usermodes: - IRC.raw("MODE %s %s"%(IRC.identity.nick, usermodes), origin=self) - if opername and wallet and "%s/opers/%s"%(label, opername) in wallet.keys(): - IRC.raw("OPER %s %s"%(opername, wallet["%s/opers/%s"%(label, opername)]), origin=self) - if autojoin: - IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self) - def onRecv(self, IRC, line, data): - if data==None: - return - (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC] - (origin, ident, host, cmd, target, params, extinfo)=data - if cmd=="381" and opermodes: - if operexec: - for line in operexec: - IRC.raw(line, origin=self) - if opermodes: - IRC.raw("MODE %s %s"%(IRC.identity.nick, opermodes), origin=self) - if snomasks: - IRC.raw("MODE %s +s %s"%(IRC.identity.nick, snomasks), origin=self) - if operjoin: - IRC.raw("JOIN %s"%(",".join(operjoin)), origin=self) + def __init__(self): + self.networks = {} + + def onModuleAdd(self, IRC, label, onconnect=None, onregister=None, autojoin=None, usermodes=None, wallet=None, opername=None, opermodes=None, snomasks=None, operexec=None, operjoin=None): + labels = [v[0] for v in self.networks.values()] + if label in labels: + raise BaseException("Label already exists") + if IRC in self.networks.keys(): + raise BaseException("Network already exists") + self.networks[IRC] = (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) + + def onModuleRem(self, IRC): + del self.networks[IRC] + + def onConnect(self, IRC): + (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC] + if onconnect: + for line in onconnect: + IRC.raw(line, origin=self) + + def onRegistered(self, IRC): + (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC] + if onregister: + for line in onregister: + IRC.raw(line, origin=self) + if usermodes: + IRC.raw("MODE %s %s"%(IRC.identity.nick, usermodes), origin=self) + if opername and wallet and "%s/opers/%s"%(label, opername) in wallet.keys(): + IRC.raw("OPER %s %s"%(opername, wallet[ + "%s/opers/%s"%(label, opername)]), origin=self) + if autojoin: + IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self) + + def onRecv(self, IRC, line, data): + if data is None: + return + (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC] + (origin, ident, host, cmd, target, params, extinfo) = data + if cmd == "381" and opermodes: + if operexec: + for line in operexec: + IRC.raw(line, origin=self) + if opermodes: + IRC.raw("MODE %s %s"%(IRC.identity.nick, + opermodes), origin=self) + if snomasks: + IRC.raw("MODE %s +s %s"%( + IRC.identity.nick, snomasks), origin=self) + if operjoin: + IRC.raw("JOIN %s"%(",".join(operjoin)), origin=self) + class NickServ(object): - def __init__(self): - self.networks={} - def onModuleAdd(self, IRC, label, wallet=None, autojoin=None): - labels=[v[0] for v in self.networks.values()] - #print labels - if label in labels: - raise BaseException, "Label already exists" - if IRC in self.networks.keys(): - raise BaseException, "Network already exists" - self.networks[IRC]=(label, wallet, autojoin) - def onModuleRem(self, IRC): - del self.networks[IRC] - def onRecv(self, IRC, line, data): - if data==None: 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(): - IRC.user("NickServ").msg("identify %s" % wallet["%s/NickServ/%s"%(label, target.lower())]) - if cmd=="900" and autojoin: - IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self) + def __init__(self): + self.networks = {} + + def onModuleAdd(self, IRC, label, wallet=None, autojoin=None): + labels = [v[0] for v in self.networks.values()] + #print labels + if label in labels: + raise BaseException("Label already exists") + if IRC in self.networks.keys(): + raise BaseException("Network already exists") + self.networks[IRC] = (label, wallet, autojoin) + + def onModuleRem(self, IRC): + del self.networks[IRC] + + def onRecv(self, IRC, line, data): + if data is None: + 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(): + IRC.user("NickServ").msg("identify %s" % wallet[ + "%s/NickServ/%s"%(label, target.lower())]) + if cmd == "900" and autojoin: + IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self) @@ -1,452 +1,569 @@ #!/usr/bin/python -import socket, ssl, os, re, time, sys, string, hashlib, traceback +import socket +import ssl +import os +import re +import time +import sys +import string +import hashlib +import traceback from threading import Thread, Lock import Queue + class Bouncer (Thread): - def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None): - self.__name__="Bouncer for pyIRC" - self.__version__="1.0.0rc1" - self.__author__="Brian Sherson" - self.__date__="May 23, 2013" - #print "Initializing ListenThread..." - self.addr=addr - self.port=port - self.servers={} - self.passwd={} - self.socket=s=socket.socket() - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.ssl=ssl - self.certfile=certfile - self.keyfile=keyfile - s.bind((self.addr,self.port)) - self.connections=[] - self.ignore=ignore - - ### 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={} - #self.lock=Lock() - self.starttime=int(time.time()) - Thread.__init__ ( self ) - self.daemon=True - self.start() - def __repr__(self): - return "<Bouncer listening on port %(addr)s:%(port)s>" % vars(self) - - def run(self): - self.socket.listen(5) - #print ((self,"Now listening on port "+str(self.port))) - while True: - 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) - try: - hostname, aliaslist, addresslist = socket.gethostbyaddr(addr[0]) - addr = (hostname, addr[1]) - except: - pass - #print ((self,"New client connecting from %s:%s"%addr)) - except socket.error: - #print "Shutting down Listener" - self.socket.close() - #raise - sys.exit() - bouncer=BouncerConnection(self, connection, addr) - #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): ### WHO reply - if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections: - self.whoexpected[IRC][0].connection.send(line+"\n") - if cmd==315: ### End of WHO reply - del self.whoexpected[IRC][0] - 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") - 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") - 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.connection.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.connection.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)) - elif cmd.upper()=="WHO": - #print origin, line - self.whoexpected[IRC].append(origin) - elif cmd.upper()=="WHOIS": - #print origin, line - self.whoisexpected[IRC].append(origin) - elif cmd.upper()=="LIST": - #print origin, line - self.listexpected[IRC].append(origin) - def onModuleAdd(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(): return - self.servers[label]=(IRC, passwd, hashtype) - self.whoexpected[IRC]=[] - self.whoisexpected[IRC]=[] - self.listexpected[IRC]=[] - - def onModuleRem(self, IRC): - for bouncerconnection in self.connections: - if bouncerconnection.IRC==IRC: - bouncerconnection.stop(quitmsg="Bouncer extension removed") - for (label, (connection, passwd, hashtype)) in self.servers.items(): - if connection==IRC: - 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"): - for bouncerconnection in self.connections: - bouncerconnection.stop(quitmsg=quitmsg) - def onDisconnect(self, IRC): - self.whoexpected[IRC]=[] - self.whoisexpected[IRC]=[] - self.listexpected[IRC]=[] - for bouncerconnection in self.connections: - if bouncerconnection.IRC==IRC: - bouncerconnection.stop(quitmsg="IRC connection lost") + def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr): + self.__name__ = "Bouncer for pyIRC" + self.__version__ = "1.0.0rc2" + self.__author__ = "Brian Sherson" + self.__date__ = "August 26, 2013" + #print "Initializing ListenThread..." + self.addr = addr + self.port = port + self.servers = {} + self.passwd = {} + self.socket = s = socket.socket() + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.ssl = ssl + self.certfile = certfile + self.keyfile = keyfile + s.bind((self.addr, self.port)) + self.connections = [] + self.ignore = ignore + self.debug = debug + + ### 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 = {} + self.lock = Lock() + self.starttime = int(time.time()) + Thread.__init__(self) + self.daemon = True + self.start() + + def __repr__(self): + return "<Bouncer listening on port %(addr)s:%(port)s>" % vars(self) + + def run(self): + self.socket.listen(5) + #print ((self,"Now listening on port "+str(self.port))) + while True: + 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) + try: + hostname, aliaslist, addresslist = socket.gethostbyaddr( + addr[0]) + addr = (hostname, addr[1]) + except: + pass + #print ((self,"New client connecting from %s:%s"%addr)) + except socket.error: + #print "Shutting down Listener" + self.socket.close() + #raise + sys.exit() + bouncer = BouncerConnection(self, connection, addr) + #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].connection.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].connection.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") + 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: + 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() + + 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.connection.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)) + 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"): + if IRC in [connection for (connection, passwd, hashtype) 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: + 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() + self.whoisexpected[IRC] = [] + self.listexpected[IRC] = [] + + def onModuleRem(self, IRC): + for bouncerconnection in self.connections: + if bouncerconnection.IRC == IRC: + bouncerconnection.quit(quitmsg="Bouncer extension removed") + for (label, (connection, passwd, hashtype)) in self.servers.items(): + if connection == IRC: + 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"): + for bouncerconnection in self.connections: + bouncerconnection.stop(quitmsg=quitmsg) + + def onDisconnect(self, IRC): + 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() + self.whoisexpected[IRC] = [] + self.listexpected[IRC] = [] + for bouncerconnection in self.connections: + if bouncerconnection.IRC == IRC: + bouncerconnection.quit(quitmsg="IRC connection lost") + class BouncerConnection (Thread): - def __init__(self, bouncer, connection, addr): - #print "Initializing ListenThread..." - self.bouncer=bouncer - self.connection=connection - self.host, self.port=self.addr=addr - self.IRC=None - self.pwd=None - self.nick=None - self.label=None - self.idnt=None - self.realname=None - self.addr=addr - self.quitmsg="Connection Closed" - - Thread.__init__ ( self ) - self.daemon=True - self.start() - - def __repr__(self): - server=self.IRC.server if self.IRC else "*" - 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 "*" - host=self.IRC.identity.host if self.IRC.identity.host else "*" - else: - nick="*" - ident="*" - host="*" - protocol="ircs" if self.IRC.ssl else "irc" - addr=self.host - return "<Bouncer connection from %(addr)s to %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals() - - def stop(self, quitmsg="Disconnected"): - self.quitmsg=quitmsg - #self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick, self.host, self.quitmsg)) - self.connection.shutdown(0) - - 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")) - - passwd=None - nick=None - user=None - - readbuf="" - linebuf=[] - - try: - 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]]) - read=self.connection.recv(512) - if read=="" and len(linebuf)==0: ### No more data to process. - #self.quitmsg="Connection Closed" - sys.exit() - - readbuf+=read - lastlf=readbuf.rfind("\n") - - if lastlf>=0: - linebuf.extend(string.split(readbuf[0:lastlf], "\n")) - readbuf=readbuf[lastlf+1:] - - line=string.rstrip(linebuf.pop(0)) - match=re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I) - - if len(match)==0: continue - (cmd, target, params, extinfo)=match[0] - - if not passwd: ### Bouncer expects a password - if cmd.upper()=="PASS": - passwd=target if target else extinfo - else: - self.quitmsg="Access Denied" - break - - elif not nick: ### Bouncer expects a NICK command - if cmd.upper()=="NICK": - nick=target if target else extinfo - else: - self.quitmsg="Access Denied" - break - - elif not self.idnt: ### 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] - passmatch=hashlib.new(hashtype, passwd).hexdigest()==passwdhash - self.IRC.lock.acquire() - 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.quitmsg="Access Denied" - self.IRC.lock.release() - break - - ### If we have made it to this point, then access has been granted. - self.bouncer.connections.append(self) - 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: - n+=1 - self.label="*%s_%d"%(self.idnt, n) - - ### Request Version info. - self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick)) - - ### 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)) - - ### Announce connection to all other bouncer connections. - for bouncerconnection in self.bouncer.connections: - try: - bouncerconnection.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.label, self.idnt, self.addr[0])) - bouncerconnection.connection.send(":*Bouncer* MODE $bouncer +o %s\n" % (self.label)) - except: - pass - self.IRC.lock.release() - else: ### User not found - self.quitmsg="Access Denied" - break - else: ### Client did not send USER command when expected - self.quitmsg="Access Denied" - break - - elif cmd.upper()=="QUIT": - self.quitmsg=extinfo - break - - elif cmd.upper()=="PING": - 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)) - - 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 - 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 - 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)) - 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]]) - 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) - redundant=[] - for mode in params.lstrip("+"): - 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)) - 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)) - 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() - except: - exc,excmsg,tb=sys.exc_info() - self.quitmsg=str(excmsg) - finally: - if self.IRC and self.IRC.lock.locked(): self.IRC.lock.release() ### Release lock in case lock is locked. - try: - self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick if self.IRC else "*", self.host, self.quitmsg)) - self.connection.shutdown(1) - self.connection.close() - except: - pass - - if self in self.bouncer.connections: - self.bouncer.connections.remove(self) - - ### 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)) - except: - pass + def __init__(self, bouncer, connection, addr): + #print "Initializing ListenThread..." + self.bouncer = bouncer + self.connection = connection + self.host, self.port = self.addr = addr + self.IRC = None + self.pwd = None + self.nick = None + self.label = None + self.idnt = None + self.realname = None + self.addr = addr + self.quitmsg = "Connection Closed" + + Thread.__init__(self) + self.daemon = True + self.start() + + def __repr__(self): + server = self.IRC.server if self.IRC else "*" + 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 "*" + host = self.IRC.identity.host if self.IRC.identity.host else "*" + else: + nick = "*" + ident = "*" + host = "*" + protocol = "ircs" if self.IRC.ssl else "irc" + addr = self.host + return "<Bouncer connection from %(addr)s to %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % 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 + + 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")) + + passwd = None + nick = None + user = None + addr = self.host + + readbuf = "" + linebuf = [] + + try: + 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]]) + read = self.connection.recv(512) + if read == "" and len(linebuf) == 0: # No more data to process. + #self.quitmsg="Connection Closed" + sys.exit() + + readbuf += read + lastlf = readbuf.rfind("\n") + + if lastlf >= 0: + linebuf.extend(string.split(readbuf[0:lastlf], "\n")) + readbuf = readbuf[lastlf+1:] + + line = string.rstrip(linebuf.pop(0)) + match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I) + + if len(match) == 0: + continue + (cmd, target, params, extinfo) = match[0] + + if not passwd: # Bouncer expects a password + if cmd.upper() == "PASS": + passwd = target if target else extinfo + else: + self.quit("Access Denied") + break + + elif not nick: # Bouncer expects a NICK command + if cmd.upper() == "NICK": + nick = target if target else extinfo + else: + self.quit("Access Denied") + break + + elif not self.idnt: # 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] + 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() + with self.bouncer.lock: + self.bouncer.connections.append(self) + ### Announce connection to all other bouncer connections. + 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: + 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() + bouncerconnection.quitmsg = excmsg + try: + bouncerconnection.connection.shutdown(0) + except: + pass + + 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.idnt, n) in labels: + n += 1 + self.label = "*%s_%d"%(self.idnt, n) + + ### Request Version info. + #self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick)) + + ### 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)) + + else: # User not found + self.quit("Access Denied") + break + else: # Client did not send USER command when expected + self.quit("Access Denied") + break + + elif cmd.upper() == "QUIT": + self.quit(extinfo) + break + + elif cmd.upper() == "PING": + 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)) + + 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 + 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 + 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)) + 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]]) + 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) + redundant = [] + for mode in params.lstrip("+"): + 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)) + 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)) + 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() + except: + 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() + finally: + ### Juuuuuuust in case. + try: + self.connection.shutdown(1) + self.connection.close() + except: + 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() + + if self in self.bouncer.connections: + with self.bouncer.lock: + self.bouncer.connections.remove(self) + 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: + 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() + bouncerconnection.quitmsg = excmsg + try: + bouncerconnection.connection.shutdown(0) + except: + pass + +# ### 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)) +# except: +# pass @@ -1,30 +1,40 @@ #!/usr/bin/python -import re, os +import re +import os + class Cannon(object): - def __init__(self): - self.firecount={} - def onRecv(self, IRC, line, data): - if data==None: return - (origin, ident, host, cmd, target, params, extinfo)=data - if len(target) and target[0]=="#" and cmd=="PRIVMSG": - channel=IRC.channel(target) - matches=re.findall("^!fire\\s+(.*)$",extinfo) - if matches: - nickname=matches[0] - if any([nickname.lower()==user.nick.lower() for user in channel.users]): - user=IRC.user(nickname) - if user in self.firecount.keys(): - count=self.firecount[user]+1 - else: - count=1 - self.firecount[user]=count - if 10<=count%100<20: ordinal="th" - elif count%10==1: ordinal="st" - elif count%10==2: ordinal="nd" - elif count%10==3: ordinal="rd" - else: ordinal="th" - channel.me("fires %s out of a cannon for the %d%s time."%(user.nick, count, ordinal)) - else: - channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(origin, nickname)) + def __init__(self): + self.firecount = {} + + def onRecv(self, IRC, line, data): + if data is None: + return + (origin, ident, host, cmd, target, params, extinfo) = data + if len(target) and target[0] == "#" and cmd == "PRIVMSG": + channel = IRC.channel(target) + matches = re.findall("^!fire\\s+(.*)$", extinfo) + if matches: + nickname = matches[0] + if any([nickname.lower() == user.nick.lower() for user in channel.users]): + user = IRC.user(nickname) + if user in self.firecount.keys(): + count = self.firecount[user]+1 + else: + count = 1 + self.firecount[user] = count + if 10 <= count%100 < 20: + ordinal = "th" + elif count%10 == 1: + ordinal = "st" + elif count%10 == 2: + ordinal = "nd" + elif count%10 == 3: + ordinal = "rd" + else: + ordinal = "th" + channel.me("fires %s out of a cannon for the %d%s time." % + (user.nick, count, ordinal)) + else: + channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(origin, nickname)) @@ -1,20 +1,25 @@ #!/usr/bin/python -import re, os +import re +import os + class Figlet(object): - def onRecv(self, IRC, line, data): - if data==None: return - (origin, ident, host, cmd, target, params, extinfo)=data - if len(target) and target[0]=="#" and cmd=="PRIVMSG": - channel=IRC.channel(target) - matches=re.findall("^!figlet\\s+(.*)$",extinfo) - if matches: - gif,fig=os.popen2("figlet") - gif.write(matches[0]) - gif.close() - while True: - line=fig.readline() - if line=="": break - if re.match("^\\s+$", line.rstrip()): continue - channel.msg(line.rstrip()) - fig.close() + def onRecv(self, IRC, line, data): + if data is None: + return + (origin, ident, host, cmd, target, params, extinfo) = data + if len(target) and target[0] == "#" and cmd == "PRIVMSG": + channel = IRC.channel(target) + matches = re.findall("^!figlet\\s+(.*)$", extinfo) + if matches: + gif, fig = os.popen2("figlet") + gif.write(matches[0]) + gif.close() + while True: + line = fig.readline() + if line == "": + break + if re.match("^\\s+$", line.rstrip()): + continue + channel.msg(line.rstrip()) + fig.close() @@ -1,1021 +1,1234 @@ #!/usr/bin/python from threading import Thread, Event, Lock -import re, time, sys, string, socket, os, platform, traceback, Queue, ssl +import re +import time +import sys +import string +import socket +import os +import platform +import traceback +import Queue +import ssl -class Connection(Thread): - def __init__(self, server, nick="ircbot", ident="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.0.0rc2" - self.__author__="Brian Sherson" - self.__date__="August 26, 2013" - - if port==None: - self.port=6667 if not ssl else 6697 - else: - self.port=port - - if type(nick) in (str, unicode): self.nick=[nick] - elif type(nick) in (list, tuple): self.nick=nick - - self.realname=realname - self.idnt=ident - self.passwd=passwd - self.server=server - self.ssl=ssl - self.ipv6=ipv6 - self.identity=None - - 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.log=log - - self.modules=[] - self.trusted=[] - - - ### Initialize IRC environment variables - self.motdgreet="" - self.motd=[] - self.identity=None - self.users=[] - self.channels=[] - self.supports={} - - self.lock=Lock() - self.loglock=Lock() - self.sendlock=Lock() - self.outgoing=Queue.Queue() - - Thread.__init__(self) - - def event(self, method, modlist, exceptions=False, **params): - #print method, modlist - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - for module in modlist: - #print module, dir(module) - #if modlist is not self.modules and module in self.modules: continue - if method in dir(module) and callable(getattr(module, method)): - try: - getattr(module, method)(self, **params) - except: - with self.loglock: - exc,excmsg,tb=sys.exc_info() - print >>self.log, "%(timestamp)s !!! Exception in module %(module)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars() - self.log.flush() - if exceptions: ### If set to true, we raise the exception. - raise - - def addModule(self, module, trusted=False, **params): - if module in self.modules: - raise BaseException, "Module already added." - with self.lock: - self.event("onModuleAdd", [module], exceptions=True, **params) - self.modules.append(module) - if trusted: - self.trusted.append(module) - - def insertModule(self, index, module, trusted=False, **params): - if module in self.modules: - raise BaseException, "Module already added." - with self.lock: - self.event("onModuleAdd", [module], exceptions=True, **params) - self.modules.insert(index, module) - if trusted: - self.trusted.append(module) - - def rmModule(self, module, **params): - with self.lock: - self.modules.remove(module) - self.event("onModuleRem", [module], exceptions=True, **params) - if module in self.trusted: - self.trusted.remove(module) - - def run(self): - self.quitexpected=False - server=self.server - if self.ipv6 and ":" in server: - server="[%s]"%server - port=self.port - try: - modules=list(self.modules) - for channel in self.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - with self.lock: - self.event("onLogOpen", modules) - - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s ### Log session started" % vars() - self.log.flush() - - 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 - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars() - self.log.flush() - - with self.lock: - self.event("onConnectAttempt", self.modules) - - 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) - 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)) - self.connected=True - self.connection.settimeout(self.timeout) - - ### Setting up thread responsible for sending data back to IRC server. - outgoingthread=Outgoing(self) - outgoingthread.daemon=True - outgoingthread.start() - - ### Run onConnect on all modules to signal connection was established. - modules=list(self.modules) - for channel in self.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - self.event("onConnect", modules) - with self.loglock: - 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 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) - attempt+=1 - else: - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s *** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars() - self.log.flush() - break - - ### Connection succeeded - try: - ### Attempt initial registration. - nick=self.nick[0] - trynick=0 - if self.passwd: self.raw("PASS :%(passwd)s" % vars(self)) - self.raw("NICK :%(nick)s" % vars()) - self.raw("USER %(idnt)s * * :%(realname)s" % vars(self)) - - ### Initialize buffers - linebuf=[] - readbuf="" - - while True: ### Main loop of IRC connection. - while len(linebuf)==0: ### Need Moar Data - read=self.connection.recv(512) - - ### If read was empty, connection is terminated. - if read=="": sys.exit() - - ### 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:] - - line=string.rstrip(linebuf.pop(0)) - - ### 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 - - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >> self.log, "%(timestamp)s <<< %(line)s" % vars() - self.log.flush() - - ### Attempts to match against pattern ":src cmd target params :extinfo" - matches=re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$",line) - - ### We have a match! - if len(matches): - parsed=(origin, ident, host, cmd, target, params, extinfo)=matches[0] - if re.match("^\\d+$",cmd): cmd=int(cmd) ### Code is a numerical response - else: cmd=cmd.upper() - - if not self.registered: - if type(cmd)==int and target!="*": ### Registration complete! - with self.lock: - self.registered=True - self.identity=self.user(target) - self.serv=origin - - modules=list(self.modules) - for channel in self.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - self.event("onRegistered", modules) - - 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 :%(nick)s" % vars()) - if not self.registered: ### Registration is not yet complete - continue - - ### Major codeblock here! Track IRC state. - ### Send line to modules first - with self.lock: - self.event("onRecv", self.modules, line=line, data=parsed) - if cmd==1: self.welcome=extinfo # Welcome message - elif cmd==2: self.hostinfo=extinfo # Your Host - elif cmd==3: self.servinfo=extinfo # Server Created - elif cmd==4: self.serv004=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("\\((.*)\\)(.*)",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 - 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: # Channel Modes - self.identity.snomask=params.lstrip("+") - if "s" not in self.identity.modes: self.snomask="" - elif cmd==221: # Channel Modes - self.identity.modes=(params if params else extinfo).lstrip("+") - if "s" not in self.identity.modes: self.snomask="" - elif cmd==251: self.netstats=extinfo - elif cmd==252: self.opcount=int(params) - elif cmd==254: self.chancount=int(params) - elif cmd==311: # WHOIS data - pass - elif cmd==321: # Start LIST - self.chanlistbegin=(params,extinfo) - self.chanlist={} - elif cmd==322: # LIST item - (chan,pop)=params.split(" ",1) - self.chanlist[chan]=(pop,extinfo) - elif cmd==323: self.chanlistend=extinfo # End of LIST - elif cmd==324: # Channel Modes - modeparams=params.split() - channame=modeparams.pop(0) - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - 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) - for mode in setmodes: - if mode=="+": continue - elif mode in self.supports["CHANMODES"][2]: - param=modeparams.pop(0) - channel.modes[mode]=param - elif mode in self.supports["CHANMODES"][3]: channel.modes[mode]=True - elif cmd==329: # Channel created - channame, created=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - channel.created=int(created) - elif cmd==332: # Channel Topic - channame=params - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - channel.topic=extinfo - elif cmd==333: # Channel Topic info - (channame,nick,dt)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - channel.topicsetby=nick - channel.topictime=dt - elif cmd==346: # Invite - (channame,invite,nick,invtime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if channel.modes.has_key("I"): - if invite.lower() not in [m.lower() for (m, b, t) in channel.modes["I"]]: channel.modes["I"].append((invite,nick,int(invtime))) - else: channel.modes["I"]=[(invite,nick,int(invtime))] - elif cmd==348: # Ban Exception - (channame,exception,nick,exctime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if channel.modes.has_key("e"): - if exception.lower() not in [m.lower() for (m, b, t) in channel.modes["e"]]: channel.modes["e"].append((exception, nick, int(exctime))) - else: channel.modes["e"]=[(exception, nick, int(exctime))] - elif cmd==352: # WHO reply - (channame,ident,host,serv,nick,flags)=params.split() - (hops,realname)=extinfo.split(" ",1) - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - user=self.user(nick) - if user.nick!=nick: user.nick=nick - user.hops=hops - user.realname=realname - user.idnt=ident - user.host=host - user.server=serv - user.away="G" in flags - user.ircop="*" in flags - 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==353: # NAMES reply - (devnull,channame)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if self.supports.has_key("PREFIX"): - names=re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)",extinfo) - else: names=[("",name) for name in extinfo.split()] # Still put it into tuple form for compatibility in the next structure - for (symbs,nick) in names: - user=self.user(nick) - if user.nick!=nick: user.nick=nick - 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==367: # Channel Ban - (channame,ban,nick,bantime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if "b" in channel.modes.keys(): - if ban.lower() not in [m.lower() for (m, b, t) in channel.modes["b"]]: - channel.modes["b"].append((ban,nick,int(bantime))) - else: channel.modes["b"]=[(ban,nick,int(bantime))] - elif cmd==372: self.motd.append(extinfo) # MOTD item - elif cmd==375: # Begin MOTD - self.motdgreet=extinfo - self.motd=[] - elif cmd==376: self.motdend=extinfo # End of MOTD - elif cmd==386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal) - (channame,admin)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - 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.modules, line=line, data=parsed) - 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": - user=self.user(origin) - modules=[] - for channel in user.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - self.event(modules, line, parsed) - newnick=extinfo if len(extinfo) else target - #print user, newnick - ### Need to check if newnick is still listed - for u in self.users: - #print u - #print [u.nick, newnick, u.nick.lower(), newnick.lower()] - if u.nick.lower()==newnick.lower(): - with self.loglock: - print >>self.log, "%s *** Orphaned user %s!%s@%s was removed when %s!%s@%s changed his/her nick to %s."%(timestamp, u.nick, u.idnt, u.host, user.nick, user.idnt, user.host, newnick) - self.log.flush() - self.users.remove(u) - for channel in self.channels: - if u in channel.users: - channel.users.remove(u) - user.nick=newnick - elif cmd=="JOIN": - channame=target if len(target) else extinfo - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - - if user==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 - channel.topic="" - channel.topicmod="" - channel.modes={} - channel.users=[] - self.raw("MODE %(channame)s" % vars()) - self.raw("WHO %(channame)s" % vars()) - self.raw("MODE %s :%s" % (channame, self.supports["CHANMODES"][0])) - if channel not in user.channels: user.channels.append(channel) - if user not in channel.users: channel.users.append(user) - elif cmd=="KICK": - kicker=self.user(origin) - if kicker.nick!=origin: kicker.nick=origin - if kicker.idnt!=ident: kicker.idnt=ident - if kicker.host!=host: kicker.host=host - - kicked=self.user(params) - if kicked.nick!=params: kicked.nick=params - - channel=self.channel(target) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name - if channel in kicked.channels: kicked.channels.remove(channel) - if kicked in channel.users: channel.users.remove(kicked) - if self.supports.has_key("PREFIX"): - for mode in self.supports["PREFIX"][0]: - if channel.modes.has_key(mode) and kicked in channel.modes[mode]: channel.modes[mode].remove(kicked) - #if not len(chanobj.users): #Let's remove this channel - # del self.channels[target.lower()] - if all([kicked not in c.users for c in self.identity.channels]): - with self.loglock: - print >>self.log, "%s *** User %s!%s@%s was orphaned when being kicked from %s."%(timestamp, kicked.nick, kicked.idnt, kicked.host, channel.name) - self.log.flush() - elif cmd=="PART": - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - - channel=self.channel(target) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name - - if channel in user.channels: user.channels.remove(channel) - if user in channel.users: channel.users.remove(user) - if self.supports.has_key("PREFIX"): - for mode in self.supports["PREFIX"][0]: - if channel.modes.has_key(mode) and user in channel.modes[mode]: channel.modes[mode].remove(user) - if all([user not in c.users for c in self.identity.channels]): - with self.loglock: - print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name) - self.log.flush() - elif cmd=="QUIT": - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - channels=list(user.channels) - for channel in user.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - self.event(modules, line, parsed) - for channel in channels: - channel.lock.acquire(True) - for channel in user.channels: - if user in channel.users: channel.users.remove(user) - if self.supports.has_key("PREFIX"): - for mode in self.supports["PREFIX"][0]: - if channel.modes.has_key(mode) and user in channel.modes[mode]: channel.modes[mode].remove(user) - #if not len(chanobj.users): #Let's remove this channel - # del self.channels[chan] - # On second thought, no, since we may want to save certain info - user.channels=[] - for channel in channels: - channel.lock.release() - print >>self.log, "%s *** User %s!%s@%s was orphaned when quitting IRC."%(timestamp, user.nick, user.idnt, user.host) - elif cmd=="MODE": - if target[0] in self.supports["CHANTYPES"]: - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - - channel=self.channel(target) - self.event("onRecv", channel.modules, line=line, data=parsed) - with channel.lock: - if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name - - 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]: - param=modeparams.pop(0) - if modeset=="+": - if channel.modes.has_key(mode): - if param.lower() not in [mask.lower() for (mask, setby, settime) in channel.modes[mode]]: channel.modes[mode].append((param, origin, int(time.time()))) - else: channel.modes[mode]=[(param, origin, int(time.time()))] - else: - if mode in channel.modes.keys(): - if mode=="b": ### Inspircd mode is case insentive when unsetting the mode - masks=[mask.lower() for (mask, setby, settime) in channel.modes[mode]] - if param.lower() in masks: - index=masks.index(param.lower()) - #print "Index: %d"%index - del channel.modes[mode][index] - else: - masks=[mask for (mask, setby, settime) in channel.modes[mode]] - if param in masks: - index=masks.index(param) - del channel.modes[mode][index] - elif mode in self.supports["CHANMODES"][1]: - param=modeparams.pop(0) - if modeset=="+": channel.modes[mode]=param - else: channel.modes[mode]=None - elif mode in self.supports["CHANMODES"][2]: - if modeset=="+": - param=modeparams.pop(0) - channel.modes[mode]=param - else: channel.modes[mode]=None - elif mode in self.supports["CHANMODES"][3]: - if modeset=="+": channel.modes[mode]=True - else: channel.modes[mode]=False - elif self.supports.has_key("PREFIX") and mode in self.supports["PREFIX"][0]: - modenick=modeparams.pop(0) - modeuser=self.user(modenick) - if modeuser.nick!=modenick: modeuser.nick=modenick - if modeset=="+": - if channel.modes.has_key(mode) and modeuser not in channel.modes[mode]: channel.modes[mode].append(modeuser) - if not channel.modes.has_key(mode): channel.modes[mode]=[modeuser] - elif channel.modes.has_key(mode) and modeuser in channel.modes[mode]: channel.modes[mode].remove(modeuser) - else: - user=self.user(target) - 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 user.modes: user.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 user.snomask: user.snomask+=snomode - if snomodeset=="-" and snomode in user.snomask: user.snomask=user.snomask.replace(snomode,"") - if modeset=="-": - if mode in user.modes: user.modes=user.modes.replace(mode,"") - if mode=="s": user.snomask="" - elif cmd=="TOPIC": - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - - channel=self.channel(target) - self.event("onRecv", channel.modules, line=line, data=parsed) - - with channel.lock: - if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name - channel.topic=extinfo - elif cmd=="PRIVMSG": - user=self.user(origin) - if user.nick!=origin: user.nick=origin - if user.idnt!=ident: user.idnt=ident - if user.host!=host: user.host=host - - if target[0] in self.supports["CHANTYPES"]: - channel=self.channel(target) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name - elif target[0]=="$": - pass ### Server message -- Not implemented - else: - targetuser=self.user(target) - if targetuser.nick!=target: targetuser.nick=target ### Server seems to have changed the idea of the case of the nickname - - ### CTCP handling - ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo) - if ctcp: - (ctcptype,ext)=ctcp[0] - if ctcptype.upper()=="VERSION": - user.ctcpreply("VERSION",self.ctcpversion()) - if ctcptype.upper()=="TIME": - tformat=time.ctime() - tz=time.tzname[0] - user.ctcpreply("TIME","%(tformat)s %(tz)s" % vars()) - if ctcptype.upper()=="PING": user.ctcpreply("PING","%(ext)s" % vars()) - if ctcptype.upper()=="FINGER": user.ctcpreply("FINGER","%(ext)s" % vars()) - elif cmd==910: # Channel Access List - (channame,mask,setby,settime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if "w" in channel.modes.keys(): - if mask not in [m for (m, b, t) in channel.modes["w"]]: channel.modes["w"].append((mask, setby, int(settime))) - else: channel.modes["w"]=[(mask, setby, int(settime))] - elif cmd==941: # Channel spamfilter List - (channame,mask,setby,settime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if "g" in channel.modes.keys(): - if mask not in [m for (m, b, t) in channel.modes["g"]]: channel.modes["g"].append((mask, setby, int(settime))) - else: channel.modes["g"]=[(mask, setby, int(settime))] - elif cmd==954: # Channel spamfilter List - (channame,mask,setby,settime)=params.split() - channel=self.channel(channame) - self.event("onRecv", channel.modules, line=line, data=parsed) - if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name - if "X" in channel.modes.keys(): - if mask not in [m for (m, b, t) in channel.modes["X"]]: channel.modes["X"].append((mask, setby, int(settime))) - else: channel.modes["X"]=[(mask, setby, int(settime))] - 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.modules, line=line, data=parsed) - - else: ### Line does NOT match ":src cmd target params :extinfo" - self.event("onRecv", self.modules, line=line, data=None) - 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. - 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() - 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. - try: - 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.lock: - modules=list(self.modules) - for channel in self.channels: - for module in channel.modules: - if module not in modules: modules.append(module) - self.event("onDisconnect", self.modules) - - ### Tell outgoing thread to quit. - self.outgoing.put("quit") - - ### Wait until the outgoing thread dies. - outgoingthread.join() - - self.connected=False - self.registered=False - - try: self.connection.close() - except: pass - - with self.loglock: - 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 Terminated." % vars() - self.log.flush() - - if self.quitexpected or not self.autoreconnect: 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 - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars() - self.log.flush() - sys.exit() - - finally: - with self.loglock: - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]]) - print >>self.log, "%(timestamp)s ### Log session ended" % vars() - self.log.flush() - - Thread.__init__(self) ### Makes thread restartable - - def __repr__(self): - server=self.server - if self.ipv6 and ":" in server: - server="[%s]"%server - port=self.port - if self.identity: - nick=self.identity.nick - ident=self.identity.idnt if self.identity.idnt else "*" - host=self.identity.host if self.identity.host else "*" - else: - nick="*" - ident="*" - host="*" - if self.ssl and self.ipv6: - protocol="ircs6" - elif self.ssl: - protocol="ircs" - elif self.ipv6: - protocol="irc6" - else: - protocol="irc" - return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals() - #else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals() - def quit(self, msg="", origin=None): - with self.lock: - self.quitexpected=True - if self.connected: - if len(msg): self.raw("QUIT :%(msg)s" % vars(), origin=origin) - else: self.raw("QUIT", origin=origin) - def ctcpversion(self): - reply=[] - ### Prepare reply for module - reply.append("%(__name__)s %(__version__)s, %(__author__)s" % vars(self)) - - ### Prepare reply for Python and OS versions - pyver=sys.version.split("\n") - pyver[0]="Python "+pyver[0] - reply.extend(pyver) - reply.extend(platform.platform().split("\n")) - ### Prepare reply for extension modules - for module in self.modules: - try: - r="%(__name__)s %(__version__)s" % vars(module) - if "__extinfo__" in vars(module): r+=", %(__extinfo__)s" % vars() - reply.append(r) - except: - pass - return reduce(lambda x, y: "%s; %s" % (x,y),reply) - - def raw(self, line, origin=None): - self.outgoing.put((line, origin)) - - - def user(self,nick): - users=[user for user in self.users if user.nick.lower()==nick.lower()] - 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]]) - with self.loglock: - print >>self.log, "%s *** User %s created."%(timestamp, nick) - self.log.flush() - return user - def channel(self,name): - channels=[chan for chan in self.channels if name.lower()==chan.name.lower()] - 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]]) - chan=Channel(name,self) - self.channels.append(chan) - with self.loglock: - print >>self.log, "%s *** Channel %s created."%(timestamp, name) - self.log.flush() - return chan +class Connection(Thread): + def __init__(self, server, nick="ircbot", ident="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.0.0rc2" + self.__author__ = "Brian Sherson" + self.__date__ = "August 26, 2013" + + if port is None: + self.port = 6667 if not ssl else 6697 + else: + self.port = port + + if type(nick) in (str, unicode): + self.nick = [nick] + elif type(nick) in (list, tuple): + self.nick = nick + + self.realname = realname + self.idnt = ident + 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.log = log + + self.modules = [] + self.trusted = [] + + ### Initialize IRC environment variables + self.motdgreet = "" + self.motd = [] + self.identity = None + self.users = [] + self.channels = [] + self.supports = {} + + self.lock = Lock() + self.loglock = Lock() + self.sendlock = Lock() + self.outgoing = Queue.Queue() + + Thread.__init__(self) + + def event(self, method, modlist, exceptions=False, **params): + #print method, modlist + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in time.localtime()[0:6]]) + for module in modlist: + #print module, dir(module) + #if modlist is not self.modules and module in self.modules: continue + if method in dir(module) and callable(getattr(module, method)): + try: + getattr(module, method)(self, **params) + except: + with self.loglock: + exc, excmsg, tb = sys.exc_info() + print >>self.log, "%(timestamp)s !!! Exception in module %(module)s" % vars() + for tbline in traceback.format_exc().split("\n"): + print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars() + self.log.flush() + if exceptions: # If set to true, we raise the exception. + raise + + def addModule(self, module, trusted=False, **params): + if module in self.modules: + raise BaseException("Module already added.") + with self.lock: + self.event("onModuleAdd", [module], exceptions=True, **params) + self.modules.append(module) + if trusted: + self.trusted.append(module) + + def insertModule(self, index, module, trusted=False, **params): + if module in self.modules: + raise BaseException("Module already added.") + with self.lock: + self.event("onModuleAdd", [module], exceptions=True, **params) + self.modules.insert(index, module) + if trusted: + self.trusted.append(module) + + def rmModule(self, module, **params): + with self.lock: + self.modules.remove(module) + self.event("onModuleRem", [module], exceptions=True, **params) + if module in self.trusted: + self.trusted.remove(module) + + def run(self): + self.quitexpected = False + outgoingthread = None + server = self.server + if self.ipv6 and ":" in server: + server = "[%s]"%server + port = self.port + try: + modules = list(self.modules) + for channel in self.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + with self.lock: + self.event("onLogOpen", modules) + + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( + 2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s ### Log session started" % vars() + self.log.flush() + + 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 + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars() + self.log.flush() + + with self.lock: + self.event("onConnectAttempt", self.modules) + + 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) + 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)) + self.connected = True + self.connection.settimeout(self.timeout) + + ### Setting up thread responsible for sending data back to IRC server. + outgoingthread = Outgoing(self) + outgoingthread.daemon = True + outgoingthread.start() + + ### Run onConnect on all modules to signal connection was established. + modules = list(self.modules) + for channel in self.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + self.event("onConnect", modules) + with self.loglock: + 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 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) + attempt += 1 + else: + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s *** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars() + self.log.flush() + break + + ### Connection succeeded + try: + ### Attempt initial registration. + nick = self.nick[0] + trynick = 0 + if self.passwd: + self.raw("PASS :%(passwd)s" % vars(self)) + self.raw("NICK :%(nick)s" % vars()) + self.raw("USER %(idnt)s * * :%(realname)s" % vars(self)) + + ### Initialize buffers + linebuf = [] + readbuf = "" + + while True: # Main loop of IRC connection. + while len(linebuf) == 0: # Need Moar Data + read = self.connection.recv(512) + + ### If read was empty, connection is terminated. + if read == "": + sys.exit() + + ### 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:] + + line = string.rstrip(linebuf.pop(0)) + + ### 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 + + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]]) + print >> self.log, "%(timestamp)s <<< %(line)s" % vars() + self.log.flush() + + ### Attempts to match against pattern ":src cmd target params :extinfo" + matches = re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line) + + ### We have a match! + if len(matches): + parsed = (origin, ident, host, cmd, target, params, extinfo) = matches[0] + if re.match("^\\d+$", cmd): + cmd = int(cmd) # Code is a numerical response + else: + cmd = cmd.upper() + + if not self.registered: + if type(cmd) == int and target != "*": # Registration complete! + with self.lock: + self.registered = True + self.identity = self.user(target) + self.serv = origin + + modules = list(self.modules) + for channel in self.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + self.event("onRegistered", modules) + + 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 :%(nick)s" % vars()) + if not self.registered: # Registration is not yet complete + continue + + ### Major codeblock here! Track IRC state. + ### Send line to modules first + with self.lock: + self.event("onRecv", self.modules, line=line, data=parsed) + if cmd == 1: + self.welcome = extinfo # Welcome message + elif cmd == 2: + self.hostinfo = extinfo # Your Host + elif cmd == 3: + self.servinfo = extinfo # Server Created + elif cmd == 4: + self.serv004 = 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 + 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: # Channel Modes + self.identity.snomask = params.lstrip("+") + if "s" not in self.identity.modes: + self.snomask = "" + elif cmd == 221: # Channel Modes + self.identity.modes = (params if params else extinfo).lstrip("+") + if "s" not in self.identity.modes: + self.snomask = "" + elif cmd == 251: + self.netstats = extinfo + elif cmd == 252: + self.opcount = int(params) + elif cmd == 254: + self.chancount = int(params) + elif cmd == 311: # WHOIS data + pass + elif cmd == 321: # Start LIST + self.chanlistbegin = (params, extinfo) + self.chanlist = {} + elif cmd == 322: # LIST item + (chan, pop) = params.split(" ", 1) + self.chanlist[chan] = (pop, extinfo) + elif cmd == 323: + self.chanlistend = extinfo # End of LIST + elif cmd == 324: # Channel Modes + modeparams = params.split() + channame = modeparams.pop(0) + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + 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) + for mode in setmodes: + if mode == "+": + continue + elif mode in self.supports["CHANMODES"][2]: + param = modeparams.pop(0) + channel.modes[mode] = param + elif mode in self.supports["CHANMODES"][3]: + channel.modes[mode] = True + elif cmd == 329: # Channel created + channame, created = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + channel.created = int(created) + elif cmd == 332: # Channel Topic + channame = params + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + channel.topic = extinfo + elif cmd == 333: # Channel Topic info + (channame, nick, dt) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + channel.topicsetby = nick + channel.topictime = dt + elif cmd == 346: # Invite + (channame, invite, nick, invtime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "I" in channel.modes: + if invite.lower() not in [m.lower() for (m, b, t) in channel.modes["I"]]: + channel.modes["I"].append((invite, nick, int(invtime))) + else: + channel.modes["I"] = [(invite, nick, int(invtime))] + elif cmd == 348: # Ban Exception + (channame, exception, nick, exctime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "e" in channel.modes: + if exception.lower() not in [m.lower() for (m, b, t) in channel.modes["e"]]: + channel.modes["e"].append((exception, nick, int(exctime))) + else: + channel.modes["e"] = [(exception, nick, int(exctime))] + elif cmd == 352: # WHO reply + (channame, ident, host, serv, nick, flags) = params.split() + (hops, realname) = extinfo.split(" ", 1) + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + user = self.user(nick) + if user.nick != nick: + user.nick = nick + user.hops = hops + user.realname = realname + user.idnt = ident + user.host = host + user.server = serv + user.away = "G" in flags + user.ircop = "*" in flags + 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 == 353: # NAMES reply + (devnull, channame) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "PREFIX" in self.supports: + names = re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)", extinfo) + else: + names = [("", name) for name in extinfo.split()] # Still put it into tuple form for compatibility in the next structure + for (symbs, nick) in names: + user = self.user(nick) + if user.nick != nick: + user.nick = nick + 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 == 367: # Channel Ban + (channame, ban, nick, bantime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "b" in channel.modes.keys(): + if ban.lower() not in [m.lower() for (m, b, t) in channel.modes["b"]]: + channel.modes["b"].append((ban, nick, int(bantime))) + else: + channel.modes["b"] = [(ban, nick, int(bantime))] + elif cmd == 372: + self.motd.append(extinfo) # MOTD item + elif cmd == 375: # Begin MOTD + self.motdgreet = extinfo + self.motd = [] + elif cmd == 376: + self.motdend = extinfo # End of MOTD + elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal) + (channame, admin) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + 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.modules, line=line, data=parsed) + 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": + user = self.user(origin) + modules = [] + for channel in user.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + self.event(modules, line, parsed) + newnick = extinfo if len(extinfo) else target + #print user, newnick + ### Need to check if newnick is still listed + for u in self.users: + #print u + #print [u.nick, newnick, u.nick.lower(), newnick.lower()] + if u.nick.lower() == newnick.lower(): + with self.loglock: + print >>self.log, "%s *** Orphaned user %s!%s@%s was removed when %s!%s@%s changed his/her nick to %s."%(timestamp, u.nick, u.idnt, u.host, user.nick, user.idnt, user.host, newnick) + self.log.flush() + self.users.remove(u) + for channel in self.channels: + if u in channel.users: + channel.users.remove(u) + user.nick = newnick + elif cmd == "JOIN": + channame = target if len(target) else extinfo + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + + if user == 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 + channel.topic = "" + channel.topicmod = "" + channel.modes = {} + channel.users = [] + self.raw("MODE %(channame)s" % vars()) + self.raw("WHO %(channame)s" % vars()) + self.raw("MODE %s :%s" % (channame, self.supports["CHANMODES"][0])) + if channel not in user.channels: + user.channels.append(channel) + if user not in channel.users: + channel.users.append(user) + elif cmd == "KICK": + kicker = self.user(origin) + if kicker.nick != origin: + kicker.nick = origin + if kicker.idnt != ident: + kicker.idnt = ident + if kicker.host != host: + kicker.host = host + + kicked = self.user(params) + if kicked.nick != params: + kicked.nick = params + + channel = self.channel(target) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != target: + channel.name = target # Server seems to have changed the idea of the case of the channel name + if channel in kicked.channels: + kicked.channels.remove(channel) + if kicked in channel.users: + channel.users.remove(kicked) + if "PREFIX" in self.supports: + for mode in self.supports["PREFIX"][0]: + if mode in channel.modes and kicked in channel.modes[mode]: + channel.modes[mode].remove(kicked) + #if not len(chanobj.users): #Let's remove this channel + # del self.channels[target.lower()] + if all([kicked not in c.users for c in self.identity.channels]): + with self.loglock: + print >>self.log, "%s *** User %s!%s@%s was orphaned when being kicked from %s."%(timestamp, kicked.nick, kicked.idnt, kicked.host, channel.name) + self.log.flush() + elif cmd == "PART": + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + + channel = self.channel(target) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != target: + channel.name = target # Server seems to have changed the idea of the case of the channel name + + if channel in user.channels: + user.channels.remove(channel) + if user in channel.users: + channel.users.remove(user) + if "PREFIX" in self.supports: + for mode in self.supports["PREFIX"][0]: + if mode in channel.modes and user in channel.modes[mode]: + channel.modes[mode].remove(user) + if all([user not in c.users for c in self.identity.channels]): + with self.loglock: + print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name) + self.log.flush() + elif cmd == "QUIT": + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + channels = list(user.channels) + for channel in user.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + self.event(modules, line, parsed) + for channel in channels: + channel.lock.acquire(True) + for channel in user.channels: + if user in channel.users: + channel.users.remove(user) + if "PREFIX" in self.supports: + for mode in self.supports["PREFIX"][0]: + if mode in channel.modes and user in channel.modes[mode]: + channel.modes[mode].remove(user) + #if not len(chanobj.users): #Let's remove this channel + # del self.channels[chan] + # On second thought, no, since we may want to save certain info + user.channels = [] + for channel in channels: + channel.lock.release() + print >>self.log, "%s *** User %s!%s@%s was orphaned when quitting IRC."%(timestamp, user.nick, user.idnt, user.host) + elif cmd == "MODE": + if target[0] in self.supports["CHANTYPES"]: + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + + channel = self.channel(target) + self.event("onRecv", channel.modules, line=line, data=parsed) + with channel.lock: + if channel.name != target: + channel.name = target # Server seems to have changed the idea of the case of the channel name + + 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]: + param = modeparams.pop(0) + if modeset == "+": + if mode in channel.modes: + if param.lower() not in [mask.lower() for (mask, setby, settime) in channel.modes[mode]]: + channel.modes[mode].append((param, origin, int(time.time()))) + else: + channel.modes[mode] = [(param, origin, int(time.time()))] + else: + if mode in channel.modes.keys(): + if mode == "b": # Inspircd mode is case insentive when unsetting the mode + masks = [mask.lower() for (mask, setby, settime) in channel.modes[mode]] + if param.lower() in masks: + index = masks.index(param.lower()) + #print "Index: %d"%index + del channel.modes[mode][index] + else: + masks = [mask for (mask, setby, settime) in channel.modes[mode]] + if param in masks: + index = masks.index(param) + del channel.modes[mode][index] + elif mode in self.supports["CHANMODES"][1]: + param = modeparams.pop(0) + if modeset == "+": + channel.modes[mode] = param + else: + channel.modes[mode] = None + elif mode in self.supports["CHANMODES"][2]: + if modeset == "+": + param = modeparams.pop(0) + channel.modes[mode] = param + else: + channel.modes[mode] = None + elif mode in self.supports["CHANMODES"][3]: + if modeset == "+": + channel.modes[mode] = True + else: + channel.modes[mode] = False + elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]: + modenick = modeparams.pop(0) + modeuser = self.user(modenick) + if modeuser.nick != modenick: + modeuser.nick = modenick + if modeset == "+": + if mode in channel.modes and modeuser not in channel.modes[mode]: + channel.modes[mode].append(modeuser) + if mode not in channel.modes: + channel.modes[mode] = [modeuser] + elif mode in channel.modes and modeuser in channel.modes[mode]: + channel.modes[mode].remove(modeuser) + else: + user = self.user(target) + 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 user.modes: + user.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 user.snomask: + user.snomask += snomode + if snomodeset == "-" and snomode in user.snomask: + user.snomask = user.snomask.replace(snomode, "") + if modeset == "-": + if mode in user.modes: + user.modes = user.modes.replace(mode, "") + if mode == "s": + user.snomask = "" + elif cmd == "TOPIC": + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + + channel = self.channel(target) + self.event("onRecv", channel.modules, line=line, data=parsed) + + with channel.lock: + if channel.name != target: + channel.name = target # Server seems to have changed the idea of the case of the channel name + channel.topic = extinfo + elif cmd == "PRIVMSG": + user = self.user(origin) + if user.nick != origin: + user.nick = origin + if user.idnt != ident: + user.idnt = ident + if user.host != host: + user.host = host + + if target[0] in self.supports["CHANTYPES"]: + channel = self.channel(target) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != target: + channel.name = target # Server seems to have changed the idea of the case of the channel name + elif target[0] == "$": + pass # Server message -- Not implemented + else: + targetuser = self.user(target) + if targetuser.nick != target: + targetuser.nick = target # Server seems to have changed the idea of the case of the nickname + + ### CTCP handling + ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo) + if ctcp: + (ctcptype, ext) = ctcp[0] + if ctcptype.upper() == "VERSION": + user.ctcpreply("VERSION", self.ctcpversion()) + if ctcptype.upper() == "TIME": + tformat = time.ctime() + tz = time.tzname[0] + user.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars()) + if ctcptype.upper() == "PING": + user.ctcpreply("PING", "%(ext)s" % vars()) + if ctcptype.upper() == "FINGER": + user.ctcpreply("FINGER", "%(ext)s" % vars()) + elif cmd == 910: # Channel Access List + (channame, mask, setby, settime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "w" in channel.modes.keys(): + if mask not in [m for (m, b, t) in channel.modes["w"]]: + channel.modes["w"].append((mask, setby, int(settime))) + else: + channel.modes["w"] = [(mask, setby, int(settime))] + elif cmd == 941: # Channel spamfilter List + (channame, mask, setby, settime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "g" in channel.modes.keys(): + if mask not in [m for (m, b, t) in channel.modes["g"]]: + channel.modes["g"].append((mask, setby, int(settime))) + else: + channel.modes["g"] = [(mask, setby, int(settime))] + elif cmd == 954: # Channel spamfilter List + (channame, mask, setby, settime) = params.split() + channel = self.channel(channame) + self.event("onRecv", channel.modules, line=line, data=parsed) + if channel.name != channame: + channel.name = channame # Server seems to have changed the idea of the case of the channel name + if "X" in channel.modes.keys(): + if mask not in [m for (m, b, t) in channel.modes["X"]]: + channel.modes["X"].append((mask, setby, int(settime))) + else: + channel.modes["X"] = [(mask, setby, int(settime))] + 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.modules, line=line, data=parsed) + + else: # Line does NOT match ":src cmd target params :extinfo" + self.event("onRecv", self.modules, line=line, + data=None) + 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. + 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() + 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. + try: + 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.lock: + modules = list(self.modules) + for channel in self.channels: + for module in channel.modules: + if module not in modules: + modules.append(module) + self.event("onDisconnect", self.modules) + + ### Tell outgoing thread to quit. + self.outgoing.put("quit") + + ### Wait until the outgoing thread dies. + if outgoingthread: + outgoingthread.join() + outgoingthread = None + + self.connected = False + self.registered = False + self.identity = None + + try: + self.connection.close() + except: + pass + + with self.loglock: + 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 Terminated." % vars() + self.log.flush() + + if self.quitexpected or not self.autoreconnect: + 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 + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( + 2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars() + for tbline in traceback.format_exc().split("\n"): + print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars() + self.log.flush() + sys.exit() + + finally: + with self.loglock: + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust( + 2, "0") for t in time.localtime()[0:6]]) + print >>self.log, "%(timestamp)s ### Log session ended" % vars( + ) + self.log.flush() + + Thread.__init__(self) # Makes thread restartable + + def __repr__(self): + server = self.server + if self.ipv6 and ":" in server: + server = "[%s]"%server + port = self.port + if self.identity: + nick = self.identity.nick + ident = self.identity.idnt if self.identity.idnt else "*" + host = self.identity.host if self.identity.host else "*" + else: + nick = "*" + ident = "*" + host = "*" + if self.ssl and self.ipv6: + protocol = "ircs6" + elif self.ssl: + protocol = "ircs" + elif self.ipv6: + protocol = "irc6" + else: + protocol = "irc" + return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals() + #else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals() + + def quit(self, msg="", origin=None): + with self.lock: + self.quitexpected = True + if self.connected: + if len(msg): + self.raw("QUIT :%(msg)s" % vars(), origin=origin) + else: + self.raw("QUIT", origin=origin) + + def ctcpversion(self): + reply = [] + ### Prepare reply for module + reply.append("%(__name__)s %(__version__)s, %(__author__)s" % + vars(self)) + + ### Prepare reply for Python and OS versions + pyver = sys.version.split("\n") + pyver[0] = "Python "+pyver[0] + reply.extend(pyver) + reply.extend(platform.platform().split("\n")) + ### Prepare reply for extension modules + for module in self.modules: + try: + r = "%(__name__)s %(__version__)s" % vars(module) + if "__extinfo__" in vars(module): + r += ", %(__extinfo__)s" % vars() + reply.append(r) + except: + pass + return reduce(lambda x, y: "%s; %s" % (x, y), reply) + + def raw(self, line, origin=None): + self.outgoing.put((line, origin)) + + def user(self, nick): + users = [user for user in self.users if user.nick.lower( + ) == nick.lower()] + 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]]) + with self.loglock: + print >>self.log, "%s *** User %s created."%(timestamp, nick) + self.log.flush() + return user + + def channel(self, name): + channels = [chan for chan in self.channels if name.lower( + ) == chan.name.lower()] + 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]]) + chan = Channel(name, self) + self.channels.append(chan) + with self.loglock: + print >>self.log, "%s *** Channel %s created."%( + timestamp, name) + self.log.flush() + return chan class Channel(object): - def __init__(self,name,context): - self.name=name - self.context=context - self.modules=[] - self.topic="" - self.topicsetby="" - self.topictime=() - self.topicmod="" - self.modes={} - self.users=[] - self.created=None - self.lock=Lock() - def msg(self, msg, origin=None): - chan=self.name - self.context.raw("PRIVMSG %(chan)s :%(msg)s" % vars(), origin=origin) - def settopic(self, msg, origin=None): - chan=self.name - self.context.raw("TOPIC %(chan)s :%(msg)s" % vars(), origin=origin) - def notice(self, msg, target="", origin=None): - chan=self.name - self.context.raw("NOTICE %(target)s%(chan)s :%(msg)s" % vars(), origin=origin) - def ctcp(self, act, msg="", origin=None): - if len(msg): self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin) - else: self.msg("\01%(act)s\01" % vars()) - def ctcpreply(self, act, msg="", origin=None): - if len(msg): self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin) - else: self.notice("\01%(act)s\01" % vars(), origin=origin) - def me(self,msg="", origin=None): self.ctcp("ACTION", msg, origin=origin) - def part(self, msg="", origin=None): - chan=self.name - if msg: self.context.raw("PART %(chan)s :%(msg)s" % vars(), origin=origin) - else: self.context.raw("PART %(chan)s" % vars(), origin) - def join(self, key="", origin=None): - chan=self.name - if key: self.context.raw("JOIN :%(chan)s %(key)s" % vars(), origin=origin) - else: self.context.raw("JOIN :%(chan)s" % vars(), origin=origin) - def kick(self, nick, msg="", origin=None): - chan=self.name - if len(msg): self.context.raw("KICK %(chan)s %(nick)s :%(msg)s" % vars(), origin=origin) - else: self.context.raw("KICK %(chan)s %(nick)s" % vars(), origin=origin) - def __repr__(self): - return "<Channel: "+self.name+"@"+self.context.server+"/"+str(self.context.port)+">" + def __init__(self, name, context): + self.name = name + self.context = context + self.modules = [] + self.topic = "" + self.topicsetby = "" + self.topictime = () + self.topicmod = "" + self.modes = {} + self.users = [] + self.created = None + self.lock = Lock() + + def msg(self, msg, origin=None): + chan = self.name + self.context.raw("PRIVMSG %(chan)s :%(msg)s" % vars(), origin=origin) + + def settopic(self, msg, origin=None): + chan = self.name + self.context.raw("TOPIC %(chan)s :%(msg)s" % vars(), origin=origin) + + def notice(self, msg, target="", origin=None): + chan = self.name + self.context.raw("NOTICE %(target)s%(chan)s :%(msg)s" % + vars(), origin=origin) + + def ctcp(self, act, msg="", origin=None): + if len(msg): + self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin) + else: + self.msg("\01%(act)s\01" % vars()) + + def ctcpreply(self, act, msg="", origin=None): + if len(msg): + self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin) + else: + self.notice("\01%(act)s\01" % vars(), origin=origin) + + def me(self, msg="", origin=None): + self.ctcp("ACTION", msg, origin=origin) + + def part(self, msg="", origin=None): + chan = self.name + if msg: + self.context.raw("PART %(chan)s :%(msg)s" % vars(), origin=origin) + else: + self.context.raw("PART %(chan)s" % vars(), origin) + + def join(self, key="", origin=None): + chan = self.name + if key: + self.context.raw("JOIN :%(chan)s %(key)s" % vars(), origin=origin) + else: + self.context.raw("JOIN :%(chan)s" % vars(), origin=origin) + + def kick(self, nick, msg="", origin=None): + chan = self.name + if len(msg): + self.context.raw("KICK %(chan)s %(nick)s :%(msg)s" % + vars(), origin=origin) + else: + self.context.raw("KICK %(chan)s %(nick)s" % vars(), origin=origin) + + def __repr__(self): + return "<Channel: "+self.name+"@"+self.context.server+"/"+str(self.context.port)+">" + + class User(object): - def __init__(self,nick,context): - self.nick=nick - self.idnt="" - self.host="" - self.channels=[] - self.context=context - self.modes="" - self.snomask="" - self.server=None - self.hops=None - self.ircop=False - def __repr__(self): - return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self) - def msg(self, msg, origin=None): - nick=self.nick - self.context.raw("PRIVMSG %(nick)s :%(msg)s" % vars(), origin=origin) - def notice(self, msg, origin=None): - nick=self.nick - self.context.raw("NOTICE %(nick)s :%(msg)s" % vars(), origin=origin) - def ctcp(self, act, msg="", origin=None): - if len(msg): self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin) - else: self.msg("\01%(act)s\01" % vars(), origin=origin) - def ctcpreply(self, act, msg="", origin=None): - if len(msg): self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin) - else: self.notice("\01%(act)s\01" % vars(), origin=origin) - def me(self, msg, origin=None): self.ctcp("ACTION", msg, origin=origin) + def __init__(self, nick, context): + self.nick = nick + self.idnt = "" + self.host = "" + self.channels = [] + self.context = context + self.modes = "" + self.snomask = "" + self.server = None + self.hops = None + self.ircop = False + + def __repr__(self): + return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self) + + def msg(self, msg, origin=None): + nick = self.nick + self.context.raw("PRIVMSG %(nick)s :%(msg)s" % vars(), origin=origin) + + def notice(self, msg, origin=None): + nick = self.nick + self.context.raw("NOTICE %(nick)s :%(msg)s" % vars(), origin=origin) + + def ctcp(self, act, msg="", origin=None): + if len(msg): + self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin) + else: + self.msg("\01%(act)s\01" % vars(), origin=origin) + + def ctcpreply(self, act, msg="", origin=None): + if len(msg): + self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin) + else: + self.notice("\01%(act)s\01" % vars(), origin=origin) + + def me(self, msg, origin=None): + self.ctcp("ACTION", msg, origin=origin) + + class SendLines(Thread): - def __init__(self, connection, lines, origin=None): - self.connection=connection - self.lines=lines - self.origin=origin - Thread.__init__(self) - def run(self): - for line in self.lines: - self.connection.raw(reply, origin=self.origin) - self.connection.log.flush() - time.sleep(2) + def __init__(self, connection, lines, origin=None): + self.connection = connection + self.lines = lines + self.origin = origin + Thread.__init__(self) + + def run(self): + for line in self.lines: + self.connection.raw(reply, origin=self.origin) + self.connection.log.flush() + time.sleep(2) + class Outgoing(Thread): - def __init__(self, IRC, throttle=0.25, lines=40, t=5): - self.IRC=IRC - self.throttle=throttle - self.lines=lines - self.time=t - #self.queue=Queue() - Thread.__init__(self) - def run(self): - throttled=False - timestamps=[] - while True: - q=self.IRC.outgoing.get() - 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] - if cmd.upper()=="QUIT": self.IRC.quitexpected=True - 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 modules - #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) - #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.modules, line=line, data=(cmd, target, params, extinfo), origin=origin) - with self.IRC.loglock: - print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars() - self.IRC.log.flush() - 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)) + def __init__(self, IRC, throttle=0.25, lines=40, t=5): + self.IRC = IRC + self.throttle = throttle + self.lines = lines + self.time = t + #self.queue=Queue() + Thread.__init__(self) + + def run(self): + throttled = False + timestamps = [] + while True: + q = self.IRC.outgoing.get() + 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] + if cmd.upper() == "QUIT": + self.IRC.quitexpected = True + 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 modules + #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) + #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.modules, line=line, data=(cmd, target, params, extinfo), origin=origin) + with self.IRC.loglock: + print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars() + self.IRC.log.flush() + 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)) 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
\ No newline at end of file + def __init__(self, connection, lock=None): + self.connection = connection + self.lock = lock + self.daemon = True + Thread.__init__(self) + + def run(self): + pass @@ -1,310 +1,382 @@ #!/usr/bin/python from threading import Thread, Lock -import re, time, sys, string, socket, os, platform, traceback, Queue, ssl, urllib2 +import re +import time +import sys +import string +import socket +import os +import platform +import traceback +import Queue +import ssl +import urllib2 + class LogRotate(Thread): - def __init__(self, logger): - self.logger=logger - Thread.__init__(self) - self.daemon=True - self.start() - def run(self): - 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))) - #print time.time()-nextrotate - #print time.localtime(), time.localtime(nextrotate) - 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)))) - with self.logger.rotatelock: - if all([not log or log.closed for log in self.logger.consolelogs.values()+self.logger.channellogs.values()]): - ### If there are no logs to rotate, we are going to terminate this thread. Logger will spawn another LogRotate thread when needed. - self.logger.logrotate=None - break - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - for IRC in self.logger.labels.keys(): - if IRC.connected: - with IRC.lock: - try: - self.logger.rotateConsoleLog(IRC) - except: - with IRC.loglock: - exc,excmsg,tb=sys.exc_info() - print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars() - IRC.log.flush() - if IRC.identity: - for channel in IRC.identity.channels: - try: - self.logger.rotateChannelLog(channel) - except: - with IRC.loglock: - exc,excmsg,tb=sys.exc_info() - print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars() - for tbline in traceback.format_exc().split("\n"): - print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars() - IRC.log.flush() - nextrotate+=3600*24 + def __init__(self, logger): + self.logger = logger + Thread.__init__(self) + self.daemon = True + self.start() + + def run(self): + 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))) + #print time.time()-nextrotate + #print time.localtime(), time.localtime(nextrotate) + 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)))) + with self.logger.rotatelock: + if all([not log or log.closed for log in self.logger.consolelogs.values()+self.logger.channellogs.values()]): + ### If there are no logs to rotate, we are going to terminate this thread. Logger will spawn another LogRotate thread when needed. + self.logger.logrotate = None + break + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t) + .rjust(2, "0") for t in now[0:6]]) + for IRC in self.logger.labels.keys(): + if IRC.connected: + with IRC.lock: + try: + self.logger.rotateConsoleLog(IRC) + except: + with IRC.loglock: + exc, excmsg, tb = sys.exc_info() + print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars() + for tbline in traceback.format_exc().split("\n"): + print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars() + IRC.log.flush() + if IRC.identity: + for channel in IRC.identity.channels: + try: + self.logger.rotateChannelLog(channel) + except: + with IRC.loglock: + exc, excmsg, tb = sys.exc_info() + print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars() + for tbline in traceback.format_exc().split("\n"): + print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars() + IRC.log.flush() + nextrotate += 3600*24 + class Logger(object): - def __init__(self, logroot): - self.logroot=logroot - path=[logroot] - #print path - while not os.path.isdir(path[0]): - split=os.path.split(path[0]) - path.insert(1,split[1]) - path[0]=split[0] - #print path - while len(path)>1: - path[0]=os.path.join(*path[:2]) - del path[1] - #print path - os.mkdir(path[0]) - #return - self.consolelogs={} - self.channellogs={} - self.labels={} - self.rotatelock=Lock() - self.logrotate=None - def onModuleAdd(self, IRC, label): - if label in self.labels.values(): - raise BaseException, "Label already exists" - if IRC in self.labels.keys(): - 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 - if IRC.connected: - self.openConsoleLog(IRC) - if IRC.identity: - for channel in IRC.identity.channels: - self.openChannelLog(channel) + def __init__(self, logroot): + self.logroot = logroot + path = [logroot] + #print path + while not os.path.isdir(path[0]): + split = os.path.split(path[0]) + path.insert(1, split[1]) + path[0] = split[0] + #print path + while len(path) > 1: + path[0] = os.path.join(*path[:2]) + del path[1] + #print path + os.mkdir(path[0]) + #return + self.consolelogs = {} + self.channellogs = {} + self.labels = {} + self.rotatelock = Lock() + self.logrotate = None + + def onModuleAdd(self, IRC, label): + if label in self.labels.values(): + raise BaseException("Label already exists") + if IRC in self.labels.keys(): + 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 + if IRC.connected: + self.openConsoleLog(IRC) + if IRC.identity: + for channel in IRC.identity.channels: + self.openChannelLog(channel) + + def onModuleRem(self, IRC): + if IRC.connected: + for channel in self.channellogs.keys(): + if channel in IRC.channels: + if not self.channellogs[channel].closed: + self.closeChannelLog(channel) + del self.channellogs[channel] + if not self.consolelogs[IRC].closed: + self.closeConsoleLog(IRC) + del self.labels[IRC] + del self.consolelogs[IRC] + + def openConsoleLog(self, IRC): + with self.rotatelock: + if not self.logrotate or not self.logrotate.isAlive(): + self.logrotate = LogRotate(self) + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + self.consolelogs[IRC] = open(os.path.join(self.logroot, self.labels[ + IRC], "console-%04d.%02d.%02d.log"%now[:3]), "a") + print >>self.consolelogs[IRC], "%s %s ### Log session started" % ( + timestamp, time.tzname[now[-1]]) + self.consolelogs[IRC].flush() + + def closeConsoleLog(self, IRC): + if IRC in self.consolelogs.keys() and type(self.consolelogs[IRC]) == file and not self.consolelogs[IRC].closed: + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t) + .rjust(2, "0") for t in now[0:6]]) + print >>self.consolelogs[IRC], "%s %s ### Log session ended" % ( + timestamp, time.tzname[now[-1]]) + self.consolelogs[IRC].close() + + def rotateConsoleLog(self, IRC): + self.closeConsoleLog(IRC) + self.openConsoleLog(IRC) + + def openChannelLog(self, channel): + with self.rotatelock: + if not self.logrotate or not self.logrotate.isAlive(): + self.logrotate = LogRotate(self) + self.logrotate.daemon = True + self.logrotate.start() + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + label = self.labels[channel.context] + self.channellogs[channel] = open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(channel.name.lower()).replace("/", "%2f"),)+now[:3])), "a") + print >>self.channellogs[channel], "%s %s ### Log session started" % ( + timestamp, time.tzname[now[-1]]) + self.channellogs[channel].flush() + if channel.context.identity in channel.users: + if channel.topic: + print >>self.channellogs[channel], "%s %s <<< :%s 332 %s %s :%s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topic) + if channel.topicsetby and channel.topictime: + print >>self.channellogs[channel], "%s %s <<< :%s 333 %s %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topicsetby, channel.topictime) + if channel.users: + secret = "s" in channel.modes.keys() and channel.modes["s"] + private = "p" in channel.modes.keys() and channel.modes["p"] + namesusers = [] + modes, symbols = channel.context.supports["PREFIX"] + print >>self.channellogs[channel], "%s %s <<< :%s 353 %s %s %s :%s" % (timestamp, time.tzname[now[-1]], + channel.context.serv, + channel.context.identity.nick, + "@" if secret else ("*" if private else "="), + channel.name, + " ".join(["".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])) + if channel.modes: + modes = channel.modes.keys() + modestr = "".join([mode for mode in modes if mode not in channel.context.supports["CHANMODES"][0]+channel.context.supports["PREFIX"][0] and channel.modes[mode]]) + params = " ".join([channel.modes[mode] for mode in modes if mode in channel.context.supports["CHANMODES"][1]+channel.context.supports["CHANMODES"][2] and channel.modes[mode]]) + print >>self.channellogs[channel], "%s %s <<< :%s 324 %s %s +%s %s" % (timestamp, time.tzname[now[-1]], channel.context.server, channel.context.identity.nick, channel.name, modestr, params) + if channel.created: + print >>self.channellogs[channel], "%s %s <<< :%s 329 %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.created) + self.channellogs[channel].flush() + + def closeChannelLog(self, channel): + if channel in self.channellogs.keys() and type(self.channellogs[channel]) == file and not self.channellogs[channel].closed: + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t) + .rjust(2, "0") for t in now[0:6]]) + print >>self.channellogs[channel], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]]) + self.channellogs[channel].close() + + def rotateChannelLog(self, channel): + self.closeChannelLog(channel) + self.openChannelLog(channel) + + def onRecv(self, IRC, line, data): + modemapping = dict(Y="ircop", q="owner", a="admin", o="op", + h="halfop", v="voice") + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + if data is None: + print >>self.consolelogs[IRC], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.consolelogs[IRC].flush() + return + (origin, ident, host, cmd, target, params, extinfo) = data + if re.match("^\\d+$", cmd): + cmd = int(cmd) + if cmd in (324, 329): + modeparams = params.split() + channame = modeparams[0] + channel = IRC.channel(channame) + if channel in self.channellogs.keys() and not self.channellogs[channel].closed: + log = self.channellogs[channel] + else: + log = self.consolelogs[IRC] + print >>log, "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + log.flush() + elif cmd == 332: + channel = IRC.channel(params) + if channel in self.channellogs.keys() and not self.channellogs[channel].closed: + log = self.channellogs[channel] + else: + log = self.consolelogs[IRC] + print >>log, "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + log.flush() + elif cmd == 333: + (channame, nick, dt) = params.split() + channel = IRC.channel(channame) + if not self.channellogs[channel].closed: + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif cmd == 353: + (flag, channame) = params.split() + channel = IRC.channel(channame) + if not self.channellogs[channel].closed: + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif cmd == "JOIN": + user = IRC.user(origin) + channel = IRC.channel(target if len(target) else extinfo) + if user == IRC.identity: + self.openChannelLog(channel) + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif cmd == "PRIVMSG": + if target and target[0] in IRC.supports["CHANTYPES"]: + channel = IRC.channel(target) + if ident and host: + user = IRC.user(origin) + classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]]) + else: + classes = "server" + if classes: + print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line) + else: + print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif cmd == "NOTICE": + if target and (target[0] in IRC.supports["CHANTYPES"] or (len(target) > 1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])): + if target[0] in IRC.supports["PREFIX"][1]: + channel = IRC.channel(target[1:]) + else: + channel = IRC.channel(target) + if ident and host: + user = IRC.user(origin) + classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]]) + else: + classes = "server" + if classes: + print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line) + else: + print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif target.lower() == IRC.identity.nick.lower() and not ident and not host: + print >>self.consolelogs[IRC], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.consolelogs[IRC].flush() + elif cmd == "TOPIC": + channel = IRC.channel(target) + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + elif cmd == "PART": + user = IRC.user(origin) + channel = IRC.channel(target) + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + if user == IRC.identity: + self.closeChannelLog(channel) + elif cmd == "KICK": + kicked = IRC.user(params) + channel = IRC.channel(target) + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + if kicked == IRC.identity: + self.closeChannelLog(channel) + elif cmd == "MODE": + if target and target[0] in IRC.supports["CHANTYPES"]: + channel = IRC.channel(target) + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + else: + print >>self.consolelogs[IRC], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.consolelogs[IRC].flush() + elif cmd in ("NICK", "QUIT"): + user = IRC.user(origin) + for channel in user.channels: + print >>self.channellogs[channel], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.channellogs[channel].flush() + else: + print >>self.consolelogs[IRC], "%s %s <<< %s" % ( + timestamp, time.tzname[now[-1]], line) + self.consolelogs[IRC].flush() + + def onConnectAttempt(self, IRC): + if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed: + self.openConsoleLog(IRC) + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + print >>self.consolelogs[IRC], "%s %s *** Attempting connection to %s:%s." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - def onModuleRem(self, IRC): - if IRC.connected: - for channel in self.channellogs.keys(): - if channel in IRC.channels: - if not self.channellogs[channel].closed: self.closeChannelLog(channel) - del self.channellogs[channel] - if not self.consolelogs[IRC].closed: self.closeConsoleLog(IRC) - del self.labels[IRC] - del self.consolelogs[IRC] + def onConnect(self, IRC): + if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed: + self.openConsoleLog(IRC) + #self.openConsoleLog(IRC) + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + print >>self.consolelogs[IRC], "%s %s *** Connection to %s:%s established." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - def openConsoleLog(self, IRC): - with self.rotatelock: - if not self.logrotate or not self.logrotate.isAlive(): - self.logrotate=LogRotate(self) - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - self.consolelogs[IRC]=open(os.path.join(self.logroot, self.labels[IRC], "console-%04d.%02d.%02d.log"%now[:3]), "a") - print >>self.consolelogs[IRC], "%s %s ### Log session started" % (timestamp, time.tzname[now[-1]]) - self.consolelogs[IRC].flush() - def closeConsoleLog(self, IRC): - if IRC in self.consolelogs.keys() and type(self.consolelogs[IRC])==file and not self.consolelogs[IRC].closed: - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - print >>self.consolelogs[IRC], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]]) - self.consolelogs[IRC].close() - def rotateConsoleLog(self, IRC): - self.closeConsoleLog(IRC) - self.openConsoleLog(IRC) + def onDisconnect(self, IRC): + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + for channel in IRC.identity.channels: + print >>self.channellogs[channel], "%s %s *** Connection to %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) + self.channellogs[channel].flush() + self.closeChannelLog(channel) + print >>self.consolelogs[IRC], "%s %s *** Connection %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) + self.consolelogs[IRC].flush() + self.closeConsoleLog(IRC) - def openChannelLog(self, channel): - with self.rotatelock: - if not self.logrotate or not self.logrotate.isAlive(): - self.logrotate=LogRotate(self) - self.logrotate.daemon=True - self.logrotate.start() - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - label=self.labels[channel.context] - self.channellogs[channel]=open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(channel.name.lower()).replace("/","%2f"),)+now[:3])), "a") - print >>self.channellogs[channel], "%s %s ### Log session started" % (timestamp, time.tzname[now[-1]]) - self.channellogs[channel].flush() - if channel.context.identity in channel.users: - if channel.topic: print >>self.channellogs[channel], "%s %s <<< :%s 332 %s %s :%s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topic) - if channel.topicsetby and channel.topictime: print >>self.channellogs[channel], "%s %s <<< :%s 333 %s %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topicsetby, channel.topictime) - if channel.users: - secret="s" in channel.modes.keys() and channel.modes["s"] - private="p" in channel.modes.keys() and channel.modes["p"] - namesusers=[] - modes, symbols=channel.context.supports["PREFIX"] - print >>self.channellogs[channel], "%s %s <<< :%s 353 %s %s %s :%s" % (timestamp, time.tzname[now[-1]], - channel.context.serv, - channel.context.identity.nick, - "@" if secret else ("*" if private else "="), - channel.name, - " ".join(["".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])) - if channel.modes: - modes=channel.modes.keys() - modestr="".join([mode for mode in modes if mode not in channel.context.supports["CHANMODES"][0]+channel.context.supports["PREFIX"][0] and channel.modes[mode]]) - params=" ".join([channel.modes[mode] for mode in modes if mode in channel.context.supports["CHANMODES"][1]+channel.context.supports["CHANMODES"][2] and channel.modes[mode]]) - print >>self.channellogs[channel], "%s %s <<< :%s 324 %s %s +%s %s" % (timestamp, time.tzname[now[-1]], channel.context.server, channel.context.identity.nick, channel.name, modestr, params) - if channel.created: print >>self.channellogs[channel], "%s %s <<< :%s 329 %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.created) - self.channellogs[channel].flush() - def closeChannelLog(self, channel): - if channel in self.channellogs.keys() and type(self.channellogs[channel])==file and not self.channellogs[channel].closed: - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - print >>self.channellogs[channel], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]]) - self.channellogs[channel].close() - def rotateChannelLog(self, channel): - self.closeChannelLog(channel) - self.openChannelLog(channel) - def onRecv(self, IRC, line, data): - modemapping=dict(Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice") - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - if data==None: - print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.consolelogs[IRC].flush() - return - (origin, ident, host, cmd, target, params, extinfo)=data - if re.match("^\\d+$",cmd): cmd=int(cmd) - if cmd in (324, 329): - modeparams=params.split() - channame=modeparams[0] - channel=IRC.channel(channame) - if channel in self.channellogs.keys() and not self.channellogs[channel].closed: log=self.channellogs[channel] - else: log=self.consolelogs[IRC] - print >>log, "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - log.flush() - elif cmd == 332: - channel=IRC.channel(params) - if channel in self.channellogs.keys() and not self.channellogs[channel].closed: log=self.channellogs[channel] - else: log=self.consolelogs[IRC] - print >>log, "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - log.flush() - elif cmd == 333: - (channame,nick,dt)=params.split() - channel=IRC.channel(channame) - if not self.channellogs[channel].closed: - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif cmd == 353: - (flag, channame)=params.split() - channel=IRC.channel(channame) - if not self.channellogs[channel].closed: - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif cmd=="JOIN": - user=IRC.user(origin) - channel=IRC.channel(target if len(target) else extinfo) - if user==IRC.identity: - self.openChannelLog(channel) - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif cmd == "PRIVMSG": - if target and target[0] in IRC.supports["CHANTYPES"]: - channel=IRC.channel(target) - if ident and host: - user=IRC.user(origin) - classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]]) - else: - classes="server" - if classes: print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line) - else: print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif cmd == "NOTICE": - if target and (target[0] in IRC.supports["CHANTYPES"] or (len(target)>1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])): - if target[0] in IRC.supports["PREFIX"][1]: - channel=IRC.channel(target[1:]) - else: - channel=IRC.channel(target) - if ident and host: - user=IRC.user(origin) - classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]]) - else: - classes="server" - if classes: print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line) - else: print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif target.lower()==IRC.identity.nick.lower() and not ident and not host: - print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.consolelogs[IRC].flush() - elif cmd == "TOPIC": - channel=IRC.channel(target) - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - elif cmd == "PART": - user=IRC.user(origin) - channel=IRC.channel(target) - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - if user==IRC.identity: - self.closeChannelLog(channel) - elif cmd == "KICK": - kicked=IRC.user(params) - channel=IRC.channel(target) - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - if kicked==IRC.identity: - self.closeChannelLog(channel) - elif cmd == "MODE": - if target and target[0] in IRC.supports["CHANTYPES"]: - channel=IRC.channel(target) - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - else: - print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.consolelogs[IRC].flush() - elif cmd in ("NICK", "QUIT"): - user=IRC.user(origin) - for channel in user.channels: - print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.channellogs[channel].flush() - else: - print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line) - self.consolelogs[IRC].flush() - def onConnectAttempt(self, IRC): - if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed: - self.openConsoleLog(IRC) - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - print >>self.consolelogs[IRC], "%s %s *** Attempting connection to %s:%s." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - def onConnect(self, IRC): - if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed: - self.openConsoleLog(IRC) - #self.openConsoleLog(IRC) - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - print >>self.consolelogs[IRC], "%s %s *** Connection to %s:%s established." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - def onDisconnect(self, IRC): - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - for channel in IRC.identity.channels: - print >>self.channellogs[channel], "%s %s *** Connection to %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - self.channellogs[channel].flush() - self.closeChannelLog(channel) - print >>self.consolelogs[IRC], "%s %s *** Connection %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port) - self.consolelogs[IRC].flush() - self.closeConsoleLog(IRC) - def onSend(self, IRC, line, data, origin): - modemapping=dict(Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice") - now=time.localtime() - timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]]) - (cmd, target, params, extinfo)=data - if IRC.registered and cmd=="PRIVMSG" and "CHANTYPES" in IRC.supports.keys() and len(target) and target[0] in IRC.supports["CHANTYPES"]: - channel=IRC.channel(target) - if channel in IRC.identity.channels: - 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.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - else: print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - self.channellogs[channel].flush() - else: - print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - self.consolelogs[IRC].flush() - if IRC.registered and len(target) and (target[0] in IRC.supports["CHANTYPES"] or (len(target)>1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])) and cmd=="NOTICE": - channel=IRC.channel(target[1:] if target[0] in IRC.supports["PREFIX"][1] else target) - if channel in IRC.identity.channels: - 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.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - else: print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - self.channellogs[channel].flush() - else: - print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) - self.consolelogs[IRC].flush() + def onSend(self, IRC, line, data, origin): + modemapping = dict(Y="ircop", q="owner", a="admin", o="op", + h="halfop", v="voice") + now = time.localtime() + timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, + "0") for t in now[0:6]]) + (cmd, target, params, extinfo) = data + if IRC.registered and cmd == "PRIVMSG" and "CHANTYPES" in IRC.supports.keys() and len(target) and target[0] in IRC.supports["CHANTYPES"]: + channel = IRC.channel(target) + if channel in IRC.identity.channels: + 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.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + else: + print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + self.channellogs[channel].flush() + else: + print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + self.consolelogs[IRC].flush() + if IRC.registered and len(target) and (target[0] in IRC.supports["CHANTYPES"] or (len(target) > 1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])) and cmd == "NOTICE": + channel = IRC.channel(target[1:] if target[0] + in IRC.supports["PREFIX"][1] else target) + if channel in IRC.identity.channels: + 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.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + else: + print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + self.channellogs[channel].flush() + else: + print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line) + self.consolelogs[IRC].flush() @@ -1,64 +1,78 @@ #!/usr/bin/python -import os, re, time +import os +import re +import time + class SED(object): - def __init__(self, expiry=1800): - self.__name__="SED Bot" - self.__version__="0.0.1" - self.expiry=expiry - self.history=[] - self.pattern=r"^!?s([,/#])((?:.|\\\1)*)\1((?:.|\\\1)*)\1([ig]*)$" - def onRecv(self, IRC, line, data): - if data==None: return - self.replace(IRC, *data) - def onSend(self, IRC, line, data, origin): - if origin==self: return - #print data - (cmd, target, params, extinfo)=data - if IRC.identity: self.replace(IRC, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, *data) - def replace(self, IRC, origin, ident, host, cmd, target, params, extinfo): - ### Clear out old data that has expired. - while len(self.history) and self.history[0][0]<time.time()-self.expiry: - del self.history[0] - if len(target) and target[0]=="#" and cmd=="PRIVMSG": - target=IRC.channel(target) - matches=re.findall(self.pattern,extinfo) - if matches: - separator, find, replace, flags=matches[0] - #print matches - find=re.sub("\\\\([,/#\\\\])","\\1",find) - replace=re.sub("\\\\(,/#\\\\)","\\1",replace) - #print find, replace - match=False - #print self.history - #print find - #print replace - for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__(): - #print target, target2, origin2, extinfo2 - if target!=target2: continue - action=re.findall("^\x01ACTION\\s+(.*)\x01$", extinfo2) - #print action - if action: - try: - if re.findall(find, action[0]): - sub=re.sub(find, replace, action[0], flags=re.I if "i" in flags else 0) - target.msg("What %s really meant was: *%s %s" % (origin2, origin2, sub), origin=self) - match=True - break - except: - target.msg("%s: Invalid syntax" % (origin), origin=self) - raise - else: - try: - if re.findall(find, extinfo2): - sub=re.sub(find, replace, extinfo2, flags=re.I if "i" in flags else 0) - target.msg("What %s really meant to say was: %s" % (origin2, sub), origin=self) - match=True - break - except: - target.msg("%s: Invalid syntax" % (origin), origin=self) - raise - if not match: - target.msg("%s: I tried. I really tried! But I could not find the pattern: %s" % (origin, find), origin=self) - else: - self.history.append((time.time(), IRC, (origin, ident, host, cmd, target, params, extinfo))) + def __init__(self, expiry=1800): + self.__name__ = "SED Bot" + self.__version__ = "0.0.1" + self.expiry = expiry + self.history = [] + self.pattern = r"^!?s([,/#])((?:.|\\\1)*)\1((?:.|\\\1)*)\1([ig]*)$" + + def onRecv(self, IRC, line, data): + if data is None: + return + self.replace(IRC, *data) + + def onSend(self, IRC, line, data, origin): + if origin == self: + return + #print data + (cmd, target, params, extinfo) = data + if IRC.identity: + self.replace(IRC, IRC.identity.nick, + IRC.identity.idnt, IRC.identity.host, *data) + + def replace(self, IRC, origin, ident, host, cmd, target, params, extinfo): + ### Clear out old data that has expired. + while len(self.history) and self.history[0][0] < time.time()-self.expiry: + del self.history[0] + if len(target) and target[0] == "#" and cmd == "PRIVMSG": + target = IRC.channel(target) + matches = re.findall(self.pattern, extinfo) + if matches: + separator, find, replace, flags = matches[0] + #print matches + find = re.sub("\\\\([,/#\\\\])", "\\1", find) + replace = re.sub("\\\\(,/#\\\\)", "\\1", replace) + #print find, replace + match = False + #print self.history + #print find + #print replace + for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__(): + #print target, target2, origin2, extinfo2 + if target != target2: + continue + action = re.findall("^\x01ACTION\\s+(.*)\x01$", extinfo2) + #print action + if action: + try: + if re.findall(find, action[0]): + sub = re.sub(find, replace, action[0], flags=re.I if "i" in flags else 0) + target.msg("What %s really meant was: *%s %s" % (origin2, origin2, sub), origin=self) + match = True + break + except: + target.msg("%s: Invalid syntax" % (origin), + origin=self) + raise + else: + try: + if re.findall(find, extinfo2): + sub = re.sub(find, replace, extinfo2, flags=re.I if "i" in flags else 0) + target.msg("What %s really meant to say was: %s" % (origin2, sub), origin=self) + match = True + break + except: + target.msg("%s: Invalid syntax" % (origin), + origin=self) + raise + if not match: + target.msg("%s: I tried. I really tried! But I could not find the pattern: %s" % (origin, find), origin=self) + else: + self.history.append((time.time(), IRC, (origin, ident, + host, cmd, target, params, extinfo))) diff --git a/startirc.py b/startirc.py index 2acf1b9..6d0d8e7 100755 --- a/startirc.py +++ b/startirc.py @@ -1,55 +1,72 @@ #!/usr/bin/python -i -import os, re, time, logger, signal, figlet, cannon, wallet, autoexec, sys, irc, bouncer, readline +import os +import re +import time +import logger +import signal +import figlet +import cannon +import wallet +import autoexec +import sys +import irc +import bouncer +import readline readline.parse_and_bind("tab: complete") -networks={} +networks = {} + def quit(quitmsg="Goodbye!"): - global networks - modules=[] - for IRC in networks.values(): - if IRC.isAlive(): - IRC.quit(quitmsg) - while any([IRC.isAlive() for IRC in networks.values()]): - time.sleep(0.25) - for IRC in networks.values(): - for module in list(IRC.modules): - IRC.rmModule(module) - if module not in modules: - modules.append(module) - for module in modules: - if "stop" in dir(module) and callable(module.stop) and "isAlive" in dir(module) and callable(module.isAlive) and module.isAlive(): - try: - module.stop() - except: - pass - print "Goodbye!" - sys.exit() - -termcaught=False + global networks + modules = [] + for IRC in networks.values(): + if IRC.isAlive(): + IRC.quit(quitmsg) + while any([IRC.isAlive() for IRC in networks.values()]): + time.sleep(0.25) + for IRC in networks.values(): + for module in list(IRC.modules): + IRC.rmModule(module) + if module not in modules: + modules.append(module) + for module in modules: + if "stop" in dir(module) and callable(module.stop) and "isAlive" in dir(module) and callable(module.isAlive) and module.isAlive(): + try: + module.stop() + except: + pass + print "Goodbye!" + sys.exit() + +termcaught = False + + def sigterm(signum, frame): - global termcaught - if not termcaught: - termcaught=True - quit("Caught SIGTERM") + global termcaught + if not termcaught: + termcaught = True + quit("Caught SIGTERM") signal.signal(signal.SIGTERM, sigterm) -logroot=os.path.join(os.environ["HOME"], "IRC") +logroot = os.path.join(os.environ["HOME"], "IRC") -insomnialog=open(os.path.join(logroot, "insomnia.log"), "a") -InsomniaIRC=networks["InsomniaIRC"]=irc.Connection(server="irc.insomniairc.net", ipv6=False, ssl=True, log=insomnialog) +insomnialog = open(os.path.join(logroot, "insomnia.log"), "a") +InsomniaIRC = networks["InsomniaIRC"] = irc.Connection( + server="irc.insomniairc.net", ipv6=False, ssl=True, log=insomnialog) -ax=autoexec.Autoexec() -log=logger.Logger(logroot) -BNC=bouncer.Bouncer("", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem") +ax = autoexec.Autoexec() +log = logger.Logger(logroot) +BNC = bouncer.Bouncer( + "", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem") for (label, IRC) in networks.items(): - IRC.addModule(log, label=label) - ### The password is 'hunter2' - IRC.addModule(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512") + IRC.addModule(log, label=label) + ### The password is 'hunter2' + IRC.addModule(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512") InsomniaIRC.addModule(ax, label="InsomniaIRC", autojoin=["#chat"]) for (label, IRC) in networks.items(): - IRC.start() + IRC.start() @@ -1,37 +1,45 @@ #!/usr/bin/python -import pickle, Crypto.Cipher.Blowfish, os, getpass +import pickle +import Crypto.Cipher.Blowfish +import os +import getpass from threading import Lock + class Wallet(dict): - def __init__(self, filename): - self.lock=Lock() - if os.path.isfile(filename): - self.f=open(filename,"rb+") - self.passwd=getpass.getpass() - self.crypt=Crypto.Cipher.Blowfish.new(self.passwd) - contents_encrypted=self.f.read() - contents=self.crypt.decrypt(contents_encrypted+"\x00"*((-len(contents_encrypted))%8)) - if contents.startswith(self.passwd): - self.update(dict(pickle.loads(contents[len(self.passwd):]))) - else: - self.f.close() - raise BaseException, "Incorrect Password" - else: - self.f=open(filename,"wb+") - passwd=self.passwd=None - while passwd==None or passwd!=self.passwd: - passwd=getpass.getpass("Enter new password: ") - self.passwd=getpass.getpass("Confirm new password: ") - if passwd!=self.passwd: print "Passwords do not match!" - self.crypt=Crypto.Cipher.Blowfish.new(self.passwd) - self.flush() - def flush(self): - contents=self.passwd+pickle.dumps(self.items(), protocol=2) - self.lock.acquire() - try: - self.f.seek(0) - self.f.write(self.crypt.encrypt(contents+"\x00"*((-len(contents))%8))) - self.f.truncate() - self.f.flush() - finally: - self.lock.release() + def __init__(self, filename): + self.lock = Lock() + if os.path.isfile(filename): + self.f = open(filename, "rb+") + self.passwd = getpass.getpass() + self.crypt = Crypto.Cipher.Blowfish.new(self.passwd) + contents_encrypted = self.f.read() + contents = self.crypt.decrypt(contents_encrypted + + "\x00"*((-len(contents_encrypted))%8)) + if contents.startswith(self.passwd): + self.update(dict(pickle.loads(contents[len(self.passwd):]))) + else: + self.f.close() + raise BaseException("Incorrect Password") + else: + self.f = open(filename, "wb+") + passwd = self.passwd = None + while passwd is None or passwd != self.passwd: + passwd = getpass.getpass("Enter new password: ") + self.passwd = getpass.getpass("Confirm new password: ") + if passwd != self.passwd: + print "Passwords do not match!" + self.crypt = Crypto.Cipher.Blowfish.new(self.passwd) + self.flush() + + def flush(self): + contents = self.passwd+pickle.dumps(self.items(), protocol=2) + self.lock.acquire() + try: + self.f.seek(0) + self.f.write(self.crypt.encrypt( + contents+"\x00"*((-len(contents))%8))) + self.f.truncate() + self.f.flush() + finally: + self.lock.release() |