summaryrefslogtreecommitdiff
path: root/bouncer.py
diff options
context:
space:
mode:
Diffstat (limited to 'bouncer.py')
-rw-r--r--bouncer.py1435
1 files changed, 758 insertions, 677 deletions
diff --git a/bouncer.py b/bouncer.py
index 6333e2d..45d4b6b 100644
--- a/bouncer.py
+++ b/bouncer.py
@@ -10,19 +10,27 @@ import hashlib
import traceback
import irc
import getpass
-from threading import Thread, Lock
+from threading import Thread
+from threading import RLock as Lock
import Queue
import chardet
# TODO: Rewrite this *entire* module and make more efficient.
+_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"))
+
def BouncerReload(BNC):
if BNC.isAlive():
BNC.stop()
- if BNC.__version__ == "1.2":
+ if BNC.__version__ == "1.3":
newBNC = Bouncer(
- addr=BNC.addr, port=BNC.port, ssl=BNC.ssl, ipv6=BNC.ipv6,
+ addr=BNC.addr, port=BNC.port, secure=BNC.ssl, ipv6=BNC.ipv6,
certfile=BNC.certfile, keyfile=BNC.keyfile, timeout=BNC.timeout, autoaway=BNC.autoaway)
for label, (context, passwd, hashtype) in BNC.servers.items():
context.rmAddon(BNC)
@@ -37,36 +45,30 @@ def BouncerReload(BNC):
class Bouncer (Thread):
+ __name__ = "Bouncer for pyIRC"
+ __version__ = "2.0"
+ __author__ = "Brian Sherson"
+ __date__ = "February 21, 2014"
- def __init__(self, addr="", port=16667, ssl=False, ipv6=False, certfile=None, keyfile=None, ignore=None, debug=False, timeout=300, autoaway=None):
- self.__name__ = "Bouncer for pyIRC"
- self.__version__ = "1.3"
- self.__author__ = "Brian Sherson"
- self.__date__ = "February 9, 2014"
- self.__options__ = dict(
- addr=addr, port=port, ssl=ssl, ipv6=ipv6, certfile=certfile,
- keyfile=keyfile, ignore=ignore, debug=debug, timeout=timeout, autoaway=autoaway)
-
+ def __init__(self, addr="", port=16667, secure=False, ipv6=False, certfile=None, keyfile=None, ignore=None, debug=False, timeout=300, autoaway=None, servname="bouncer.site"):
self.addr = addr
self.port = port
self.conf = {}
self.passwd = {}
- self.socket = socket.socket(
- socket.AF_INET6 if ipv6 else socket.AF_INET)
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.socket = None
self.ssl = ssl
self.ipv6 = ipv6
self.certfile = certfile
self.keyfile = keyfile
- self.socket.bind((self.addr, self.port))
- self.connections = []
+ self.clients = []
self.ignore = ignore
self.debug = debug
self.timeout = timeout
self.autoaway = autoaway
+ self.servname = servname
self._stopexpected = False
- # Keep track of what extensions/connections are requesting WHO, WHOIS, and LIST, because we don't want to spam every bouncer connection with the server's replies.
+ # Keep track of what extensions/clients 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 = {}
@@ -78,15 +80,20 @@ class Bouncer (Thread):
self.start()
def __repr__(self):
- return "<Bouncer listening on port %(addr)s:%(port)s>" % vars(self)
+ h = hash(self)
+ return "<Bouncer listening on {self.addr}:{self.port} at 0x{h:x}0>".format(**vars())
def run(self):
+ self.socket = socket.socket(
+ socket.AF_INET6 if self.ipv6 else socket.AF_INET)
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.socket.bind((self.addr, self.port))
self.socket.listen(5)
#print ((self,"Now listening on port "+str(self.port)))
while True:
try:
(connection, addr) = self.socket.accept()
- if self.ssl:
+ if self.secure:
connection = ssl.wrap_socket(
connection, server_side=True, certfile=self.certfile, keyfile=self.keyfile, ssl_version=ssl.PROTOCOL_SSLv23)
#print ((self,"New client connecting from %s:%s"%addr))
@@ -108,6 +115,9 @@ class Bouncer (Thread):
self.socket.close()
except:
pass
+ self.socket = None
+ Thread.__init__(self)
+ self.daemon = True
def onAddonAdd(self, context, label, passwd=None, hashtype="sha512", ignore=None, autoaway=None, translations=None, hidden=None):
for (context2, conf2) in self.conf.items():
@@ -123,7 +133,7 @@ class Bouncer (Thread):
print "Passwords do not match!"
passwd = hashlib.new(hashtype, passwd).hexdigest()
conf = irc.Config(
- label=label, passwd=passwd, hashtype=hashtype, ignore=ignore, autoaway=autoaway,
+ self, label=label, passwd=passwd, hashtype=hashtype, ignore=ignore, autoaway=autoaway,
translations={} if translations == None else translations, hidden=irc.ChanList(hidden, context=context))
self.conf[context] = conf
self._whoexpected[context] = []
@@ -132,139 +142,114 @@ class Bouncer (Thread):
"dbg [Bouncer.onAddonAdd] Clearing WHO expected list." % vars())
self._whoisexpected[context] = []
self._listexpected[context] = []
+ return conf
def onAddonRem(self, context):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.quit(quitmsg="Bouncer extension removed")
+ for client in self.clients:
+ if client.context == context:
+ client.quit(quitmsg="Bouncer extension removed")
del self.conf[context]
del self._whoexpected[context], self._whoisexpected[
context], self._listexpected[context]
- def stop(self):
+ def stop(self, disconnectall=False):
self._stopexpected = True
self.socket.shutdown(0)
+ if disconnectall:
+ self.disconnectall()
def disconnectall(self, quitmsg="Disconnecting all sessions"):
- for bouncerconnection in self.connections:
- bouncerconnection.quit(quitmsg=quitmsg)
+ for client in self.clients:
+ client.quit(quitmsg=quitmsg)
def onDisconnect(self, context, expected=False):
self._whoexpected[context] = []
self._whoisexpected[context] = []
self._listexpected[context] = []
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- #bouncerconnection.quit(quitmsg="context connection lost")
- if context.identity:
- for channel in context.identity.channels:
- bouncerconnection.send(":%s!%s@%s PART %s :Bouncer Connection Lost\n" % (
- context.identity.nick, context.identity.username, context.identity.host, channel.name))
- bouncerconnection.send(":%s!%s@%s QUIT :Bouncer Connection Lost\n" % (
- context.identity.nick, context.identity.username, context.identity.host))
- bouncerconnection.send(
- ":*Bouncer* NOTICE %s :Connection to %s:%s has been lost.\n" %
- (bouncerconnection.nick, context.server, context.port))
+ if context.identity:
+ for channel in context.identity.channels:
+ self.broadcast(context, origin=context.identity, cmd="PART", target=channel, extinfo="Bouncer Connection Lost", clients=[
+ client for client in self.clients if channel not in client.hidden])
+ self.broadcast(context, origin=context.identity,
+ cmd="QUIT", extinfo="Bouncer Connection Lost")
+ self.broadcast(
+ context, origin=self.servname, cmd="NOTICE", target=context.identity,
+ extinfo=":Connection to %s:%s has been lost." % (context.server, context.port))
def onQuit(self, context, user, quitmsg):
# For some odd reason, certain networks (*cough*Freenode*cough*) will send a quit message for the user, causing context.identity.channels to be cleared
# before onDisconnect can be executed. This is the remedy.
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- if quitmsg:
- bouncerconnection.send(":%s!%s@%s QUIT :%s\n" % (
- user.nick, user.username, user.host, quitmsg))
- else:
- bouncerconnection.send(
- ":%s!%s@%s QUIT\n" % (user.nick, user.username, user.host))
- if user == context.identity:
- for channel in context.identity.channels:
- bouncerconnection.send(":%s!%s@%s PART %s :Bouncer Connection Lost\n" % (
- context.identity.nick, context.identity.username, context.identity.host, channel.name))
+ if user == context.identity:
+ for channel in context.identity.channels:
+ self.broadcast(context, origin=user, cmd="PART", target=channel, extinfo="Bouncer Connection Lost", clients=[
+ client for client in self.clients if channel not in client.hidden])
+ self.broadcast(context, origin=user, cmd="QUIT", extinfo=quitmsg, clients=[
+ client for client in self.clients if any([user in channel for channel in context.channels if channel not in client.hidden])])
def onConnectAttempt(self, context):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.send(
- ":*Bouncer* NOTICE %s :Attempting connection to %s:%s.\n" %
- (bouncerconnection.nick, context.server, context.port))
+ self.broadcast(
+ context, origin=self.servname, cmd="NOTICE", target=context.identity,
+ extinfo="Attempting connection to %s:%s." % (context.server, context.port))
def onConnect(self, context):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.send(
- ":*Bouncer* NOTICE %s :Connection to %s:%s established.\n" %
- (bouncerconnection.nick, context.server, context.port))
+ self.broadcast(
+ context, origin=self.servname, cmd="NOTICE", target=context.identity,
+ extinfo="Connection to %s:%s established." % (context.server, context.port))
def onMeNickChange(self, context, newnick):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.send(":%s!%s@%s NICK %s\n" %
- (context.identity.nick, context.identity.username, context.identity.host, newnick))
- bouncerconnection.nick = newnick
+ for client in self.clients:
+ if client.context == context:
+ client.send(
+ origin=context.identity, cmd="NICK", target=newnick)
+ client.nick = newnick
def onNickChange(self, context, user, newnick):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.send(":%s!%s@%s NICK %s\n" %
- (user.nick, user.username, user.host, newnick))
- bouncerconnection.nick = newnick
+ self.broadcast(context, origin=user, cmd="NICK", target=newnick, clients=[
+ client for client in self.clients if any([user in channel for channel in context.channels if channel not in client.hidden])])
def onRegistered(self, context):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- if bouncerconnection.nick != context.identity.nick:
- bouncerconnection.send(":%s!%s@%s NICK %s\n" % (
- bouncerconnection.nick, bouncerconnection.username, bouncerconnection.host, context.identity.nick))
- bouncerconnection.nick = context.identity.nick
+ for client in self.clients:
+ if client.context == context:
+ if client.nick != context.identity.nick:
+ client.send(origin="%s!%s@%s" %
+ (client.nick, client.username, client.host), cmd="NICK", target=context.identity.nick)
+ client.nick = context.identity.nick
def onConnectFail(self, context, exc, excmsg, tb):
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context:
- bouncerconnection.send(
- ":*Bouncer* NOTICE %s :Connection to %s:%s failed: %s.\n" %
- (bouncerconnection.nick, context.server, context.port, excmsg))
+ for client in self.clients:
+ if client.context == context:
+ client.send(
+ origin=self.servname, cmd="NOTICE", target=client.nick,
+ extinfo="Connection to %s:%s failed: %s." % (context.server, context.port, excmsg))
def onSendChanMsg(self, context, origin, channel, targetprefix, msg):
# Called when bot sends a PRIVMSG to channel.
# The variable origin refers to a class instance voluntarily
# identifying itself as that which requested data be sent.
- conf = self.conf[context]
- if channel in conf.translations.keys():
- channame = conf.translations[channel]
- else:
- channame = channel.name
- for bouncerconnection in self.connections:
- if context == bouncerconnection.context and origin != bouncerconnection and channel not in bouncerconnection.hidden:
- bouncerconnection.send(":%s!%s@%s PRIVMSG %s%s :%s\n" % (
- context.identity.nick, context.identity.username, context.identity.host, targetprefix, channame, msg))
+ self.broadcast(
+ context, origin=context.identity, cmd="PRIVMSG", targetprefix=targetprefix,
+ target=channel, extinfo=msg, clients=[client for client in self.clients if client != origin])
def onSendChanAction(self, context, origin, channel, targetprefix, action):
self.onSendChanMsg(
- context, origin, channel, targetprefix, "\x01ACTION %s\x01" % action)
+ context, origin, channel, targetprefix, u"\x01ACTION {action}\x01".format(**vars()))
def onSendChanNotice(self, context, origin, channel, targetprefix, msg):
# Called when bot sends a NOTICE to channel.
# The variable origin refers to a class instance voluntarily
# identifying itself as that which requested data be sent.
- conf = self.conf[context]
- if channel in conf.translations.keys():
- channame = conf.translations[channel]
- else:
- channame = channel.name
- for bouncerconnection in self.connections:
- if context == bouncerconnection.context and origin != bouncerconnection:
- bouncerconnection.send(":%s!%s@%s NOTICE %s%s :%s\n" %
- (context.identity.nick, context.identity.username, context.identity.host, targetprefix, channame, msg))
+ self.broadcast(
+ context, origin=context.identity, cmd="NOTICE", targetprefix=targetprefix,
+ target=channel, extinfo=msg, clients=[client for client in self.clients if client != origin])
- def onSend(self, context, origin, line, cmd, target, params, extinfo):
+ def onSend(self, context, origin, line, cmd, target, targetprefix, params, extinfo):
if cmd.upper() == "WHO":
self._whoexpected[context].append(origin)
if self.debug:
if issubclass(type(origin), Thread):
name = origin.name
context.logwrite(
- "dbg [Bouncer.onSend] Adding %(origin)s (%(name)s) to WHO expected list." % vars())
+ "dbg [Bouncer.onSend] Adding {origin} ({name}) to WHO expected list.".format(**vars()))
else:
context.logwrite(
"dbg [Bouncer.onSend] Adding %(origin)s to WHO expected list." % vars())
@@ -278,26 +263,19 @@ class Bouncer (Thread):
def onWhoEntry(self, context, origin, channel, user, channame, username, host, serv, nick, flags, hops, realname):
# Called when a WHO list is received.
conf = self.conf[context]
- if context.channel(channame) in conf.translations.keys():
- channame = conf.translations[context.channel(channame)]
- if len(self._whoexpected[context]) and self._whoexpected[context][0] in self.connections:
- bncconnection = self._whoexpected[context][0]
- bncconnection.send(":%s 352 %s %s %s %s %s %s %s :%s %s\n" %
- (origin, context.identity.nick, channame, username, host, serv, nick, flags, hops, realname))
+ if len(self._whoexpected[context]) and self._whoexpected[context][0] in self.clients:
+ client = self._whoexpected[context][0]
+ client.send(origin=origin, cmd=352, target=context.identity, params=u"{channame} {username} {host} {serv} {nick} {flags}".format(
+ **vars()), extinfo=u"{hops} {realname}".format(**vars()))
+ # client.send(":%s 352 %s %s %s %s %s %s %s :%s %s\n"%(origin, context.identity.nick, channame, username, host, serv, nick, flags, hops, realname))
def onWhoEnd(self, context, origin, param, endmsg):
# Called when a WHO list is received.
- try:
- conf = self.conf[context]
- chantypes = context.supports.get("CHANTYPES", "&#+!")
- if re.match(irc._chanmatch % re.escape(chantypes), param) and context[param] in conf.translations.keys():
- param = conf.translations[context.channel(param)]
- except:
- pass
- if len(self._whoexpected[context]) and self._whoexpected[context][0] in self.connections:
- bncconnection = self._whoexpected[context][0]
- bncconnection.send(":%s 315 %s %s :%s\n" %
- (origin, context.identity.nick, param, endmsg))
+ if len(self._whoexpected[context]) and self._whoexpected[context][0] in self.clients:
+ client = self._whoexpected[context][0]
+ client.send(
+ origin=origin, cmd=315, target=context.identity, params=param, extinfo=endmsg)
+ #client.send(":%s 315 %s %s :%s\n"%(origin, context.identity.nick, param, endmsg))
if self.debug:
if issubclass(type(self._whoexpected[context][0]), Thread):
name = self._whoexpected[context][0].name
@@ -315,10 +293,11 @@ class Bouncer (Thread):
def onListStart(self, context, origin, params, extinfo):
# Called when a WHO list is received.
- if len(self._listexpected[context]) and self._listexpected[context][0] in self.connections:
- bncconnection = self._listexpected[context][0]
- bncconnection.send(":%s 321 %s %s :%s\n" %
- (origin, context.identity.nick, params, extinfo))
+ if len(self._listexpected[context]) and self._listexpected[context][0] in self.clients:
+ client = self._listexpected[context][0]
+ client.send(origin=origin, cmd=321,
+ target=context.identity, params=params, extinfo=extinfo)
+ #client.send(":%s 321 %s %s :%s\n"%(origin, context.identity.nick, params, extinfo))
def onListEntry(self, context, origin, channel, population, extinfo):
# Called when a WHO list is received.
@@ -327,161 +306,138 @@ class Bouncer (Thread):
channame = conf.translations[channel]
else:
channame = channel.name
- if len(self._listexpected[context]) and self._listexpected[context][0] in self.connections:
- bncconnection = self._listexpected[context][0]
- bncconnection.send(":%s 322 %s %s %d :%s\n" %
- (origin, context.identity.nick, channame, population, extinfo))
+ if len(self._listexpected[context]) and self._listexpected[context][0] in self.clients:
+ client = self._listexpected[context][0]
+ client.send(origin=origin, cmd=322, target=context.identity,
+ params=u"{channame} {population}".format(**vars()), extinfo=extinfo)
+ # client.send(":%s 322 %s %s %d :%s\n"%(origin, context.identity.nick, channame, population, extinfo))
def onListEnd(self, context, origin, endmsg):
# Called when a WHO list is received.
- if len(self._listexpected[context]) and self._listexpected[context][0] in self.connections:
- bncconnection = self._listexpected[context][0]
- bncconnection.send(":%s 323 %s :%s\n" %
- (origin, context.identity.nick, endmsg))
+ if len(self._listexpected[context]) and self._listexpected[context][0] in self.clients:
+ client = self._listexpected[context][0]
+ client.send(
+ origin=origin, cmd=323, target=context.identity, extinfo=endmsg)
+ # client.send(":%s 323 %s :%s\n"%(origin, context.identity.nick, endmsg))
del self._listexpected[context][0]
def onWhoisStart(self, context, origin, user, nickname, username, host, realname):
# Called when a WHOIS reply is received.
- if len(self._whoisexpected[context]):
- if self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 311 %s %s %s %s * :%s\n" %
- (origin, context.identity.nick, nickname, username, host, realname))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=311, target=context.identity,
+ params=u"{nickname} {username} {host} *".format(**vars()), extinfo=realname)
+ # client.send(":%s 311 %s %s %s %s * :%s\n" % (origin, context.identity.nick, nickname, username, host, realname))
def onWhoisRegisteredNick(self, context, origin, user, nickname, msg):
# Called when a WHOIS reply is received.
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 307 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(
+ origin=origin, cmd=307, target=context.identity, params=nickname, extinfo=msg)
+ # client.send(":%s 307 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisConnectingFrom(self, context, origin, user, nickname, msg):
# Called when a WHOIS reply is received.
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 378 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=378,
+ target=context.identity, params=nickname, extinfo=msg)
+ # client.send(":%s 378 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisChannels(self, context, origin, user, nickname, chanlist):
# Called when a WHOIS reply is received.
# TODO: Translations implementation
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 319 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, " ".join(chanlist)))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=319, target=context.identity,
+ params=nickname, extinfo=" ".join(chanlist))
+ # client.send(":%s 319 %s %s :%s\n" % (origin, context.identity.nick, nickname, " ".join(chanlist)))
def onWhoisAvailability(self, context, origin, user, nickname, msg):
# Called when a WHOIS reply is received.
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 310 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(
+ origin=origin, cmd=310, target=context.identity, params=nickname, extinfo=msg)
+ # client.send(":%s 310 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisServer(self, context, origin, user, nickname, server, servername):
# Called when a WHOIS reply is received.
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 312 %s %s %s :%s\n" %
- (origin, context.identity.nick, nickname, server, servername))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=312, target=context.identity,
+ params=u"{nickname} {server}".format(**vars()), extinfo=servername)
+ # client.send(":%s 312 %s %s %s :%s\n" % (origin, context.identity.nick, nickname, server, servername))
def onWhoisOp(self, context, origin, user, nickname, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 313 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(
+ origin=origin, cmd=313, target=context.identity, params=nickname, extinfo=msg)
+ # client.send(":%s 313 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisAway(self, context, origin, user, nickname, awaymsg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 301 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, awaymsg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=301, target=context.identity,
+ params=u"{nickname} {idletime} {signontime}".format(**vars()), extinfo=awaymsg)
+ # client.send(":%s 301 %s %s :%s\n" % (origin, context.identity.nick, nickname, awaymsg))
def onWhoisTimes(self, context, origin, user, nickname, idletime, signontime, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 317 %s %s %d %d :%s\n" %
- (origin, context.identity.nick, nickname, idletime, signontime, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=317, target=context.identity,
+ params=u"{nickname} {idletime} {signontime}".format(**vars()), extinfo=msg)
+ # client.send(":%s 317 %s %s %d %d :%s\n" % (origin, context.identity.nick, nickname, idletime, signontime, msg))
def onWhoisSSL(self, context, origin, user, nickname, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 671 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=671,
+ target=context.identity, params=nickname, extinfo=msg)
+ # client.send(":%s 671 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisModes(self, context, origin, user, nickname, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 339 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(
+ origin=origin, cmd=339, target=context.identity, params=nickname, extinfo=msg)
+ # ":%s 339 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg))
def onWhoisLoggedInAs(self, context, origin, user, nickname, loggedinas, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 330 %s %s %s :%s\n" %
- (origin, context.identity.nick, nickname, loggedinas, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=330, target=context.identity,
+ params=" ".join((nickname, loggedinas)), extinfo=msg)
+ # ":%s 330 %s %s %s :%s\n" % (origin, context.identity.nick, nickname, loggedinas, msg))
def onWhoisEnd(self, context, origin, user, nickname, msg):
- if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.connections:
- bncconnection = self._whoisexpected[context][0]
- bncconnection.send(":%s 318 %s %s :%s\n" %
- (origin, context.identity.nick, nickname, msg))
+ if len(self._whoisexpected[context]) and self._whoisexpected[context][0] in self.clients:
+ client = self._whoisexpected[context][0]
+ client.send(origin=origin, cmd=318,
+ target=context.identity, params=nickname, extinfo=msg)
+ # ":%s 318 %s %s :%s\n" % (origin, context.identity.nick, nickname, msg)
del self._whoisexpected[context][0]
def onJoin(self, context, user, channel):
- conf = self.conf[context]
- if channel in conf.translations.keys():
- channame = conf.translations[channel]
- else:
- channame = channel.name
- line = ":%s!%s@%s JOIN %s" % (
- user.nick, user.username, user.host, channame)
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context and channel not in bouncerconnection.hidden:
- bouncerconnection.send("%s\n" % line)
+ self.broadcast(context, origin=user, cmd="JOIN", target=channel, clients=[
+ client for client in self.clients if channel not in client.hidden])
def onUnhandled(self, context, line, origin, cmd, target, params, extinfo, targetprefix):
conf = self.conf[context]
+ self.broadcast(
+ context, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo,
+ targetprefix=targetprefix, clients=[client for client in self.clients if target not in client.hidden])
- if type(origin) == irc.User:
- origin = "%s!%s@%s" % (origin.nick, origin.username, origin.host)
-
- if target in conf.translations.keys():
- target = conf.translations[target]
-
- chantarg = None
-
- if type(target) == irc.User:
- target = target.nick
- elif type(target) == irc.Channel:
- chantarg = target
- target = target.name
-
- if params: # Channels which appear in params
- oldparams = params
- params = []
- for param in oldparams.split():
- chantypes = context.supports.get("CHANTYPES", "&#+!")
- if re.match(irc._chanmatch % re.escape(chantypes), param) and context[param] in conf.translations.keys():
- params.append(conf.translations[context[param]])
- else:
- params.append(param)
-
- if target:
- if type(cmd) == int:
- cmd = "%03d" % cmd
- if params and extinfo:
- line = ":%s %s %s %s :%s" % (
- origin, cmd, target, " ".join(params), extinfo)
- elif params:
- line = ":%s %s %s %s" % (origin, cmd, target, " ".join(params))
- elif extinfo:
- line = ":%s %s %s :%s" % (origin, cmd, target, extinfo)
- else:
- line = ":%s %s %s" % (origin, cmd, target)
-
- for bouncerconnection in self.connections:
- if bouncerconnection.context == context and chantarg not in bouncerconnection.hidden:
- bouncerconnection.send("%s\n" % line)
+ def broadcast(self, context, origin=None, cmd=None, target=None, params=None, extinfo=None, targetprefix=None, clients=None):
+ if clients == None:
+ clients = self.clients
+ for client in clients:
+ with client.lock:
+ if client.context == context and not client.quitting:
+ client.send(
+ origin, cmd, target, params, extinfo, targetprefix)
class BouncerConnection (Thread):
@@ -503,49 +459,94 @@ class BouncerConnection (Thread):
self.quitmsg = "Connection Closed"
self.quitting = False
self.hidden = irc.ChanList()
+ self.translations = {}
+ self.namesx = False
+ self.uhnames = False
Thread.__init__(self)
self.daemon = True
self.start()
- def send(self, data, flags=0):
- try:
- with self.lock:
+ def sendstr(self, data, flags=0):
+ with self.lock:
+ try:
self.connection.send(data.encode("utf8"))
- except socket.error:
- exc, excmsg, tb = sys.exc_info()
- print >>self.context.logwrite(*["!!! [BouncerConnection.send] Exception in thread %(self)s" % vars()] + [
- "!!! [BouncerConnection.send] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
- self.quit(quitmsg=excmsg.message)
+ except socket.error:
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.context.logwrite(*["!!! [BouncerConnection.send] Exception in thread %(self)s" % vars()] + [
+ "!!! [BouncerConnection.send] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
+ self.quit(quitmsg=excmsg.message)
+
+ # Format and send a string to the client
+ def send(self, origin=None, cmd=None, target=None, params=None, extinfo=None, targetprefix=None, flags=0):
+ if type(target) == irc.Channel:
+ if targetprefix == None:
+ targetprefix = ""
+ if target in self.translations.keys():
+ target = targetprefix + self.translations[target]
+ else:
+ target = targetprefix + target.name
+ elif type(target) == irc.User:
+ target = target.nick
- def __repr__(self):
- server = self.context.server if self.context else "*"
- port = self.context.port if self.context else "*"
- if self.context and self.context.identity:
- nick = self.context.identity.nick
- ident = self.context.identity.username if self.context.identity.username else "*"
- host = self.context.identity.host if self.context.identity.host else "*"
- else:
- nick = "*"
- ident = "*"
- host = "*"
- if self.context.ssl and self.context.ipv6:
- protocol = "ircs6"
- elif self.context.ssl:
- protocol = "ircs"
- elif self.context.ipv6:
- protocol = "irc6"
+ if type(cmd) == int:
+ cmd = "%03d" % cmd
+
+ translated = []
+ if params:
+ for param in params.split(" "):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ if re.match(irc._chanmatch % re.escape(chantypes), param) and self.context[param] in self.translations.keys():
+ translated.append(self.translations[self.context[param]])
+ else:
+ translated.append(param)
+ params = " ".join(translated)
+
+ if params:
+ line = u"{cmd} {target} {params}".format(**vars())
+ elif target:
+ line = u"{cmd} {target}".format(**vars())
else:
- protocol = "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()
+ line = cmd
+
+ if extinfo != None:
+ line = u"{line} :{extinfo}".format(**vars())
+
+ if type(origin) == irc.User:
+ line = u":{origin:full} {line}".format(**vars())
+ elif origin:
+ line = u":{origin} {line}".format(**vars())
+ self.sendstr(u"{line}\n".format(**vars()))
+
+ #server=self.context.server if self.context else "*"
+ #port=self.context.port if self.context else "*"
+ # if self.context and self.context.identity:
+ # nick=self.context.identity.nick
+ #ident=self.context.identity.username if self.context.identity.username else "*"
+ #host=self.context.identity.host if self.context.identity.host else "*"
+ # else:
+ # nick="*"
+ # ident="*"
+ # host="*"
+ # if self.context.ssl and self.context.ipv6:
+ # protocol="ircs6"
+ # elif self.context.ssl:
+ # protocol="ircs"
+ # elif self.context.ipv6:
+ # protocol="irc6"
+ # else:
+ # protocol="irc"
+ # addr=self.host
+ def __repr__(self):
+ return "<Bouncer connection from {self.host} to {self.context.identity} on {self.context:uri}>".format(**vars())
def quit(self, quitmsg="Disconnected"):
- if not self.quitting:
- self.quitmsg = quitmsg
- with self.lock:
+ with self.lock:
+ if not self.quitting:
+ self.quitmsg = quitmsg
try:
- self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (
+ self.send(cmd="ERROR", extinfo="Closing link: (%s@%s) [%s]\n" % (
self.context.identity.nick if self.context else "*", self.host, quitmsg))
except:
pass
@@ -556,6 +557,185 @@ class BouncerConnection (Thread):
pass
self.quitting = True
+ def showchannel(self, channel):
+ with self.context.lock, self.lock:
+ if channel in self.hidden:
+ self.hidden.remove(channel)
+ if self.context.identity in channel.users:
+ self.send(
+ origin=self.context.identity, cmd="JOIN", target=channel)
+ self.sendchanneltopic(channel)
+ self.sendchannelnames(channel)
+
+ def sendchanneltopic(self, channel):
+ with self.context.lock, self.lock:
+ if channel.topic and channel.topictime:
+ self.send(origin=self.bouncer.servname, cmd=332,
+ target=self.context.identity, params=channel.name, extinfo=channel.topic)
+ # u":{self.context.serv} 332 {self.context.identity.nick} {self.name} :{self.topic}".format(**vars())
+ self.send(
+ origin=self.bouncer.servname, cmd=333, target=self.context.identity,
+ params="{channel.name} {channel.topicsetby} {channel.topictime}".format(**vars()))
+ # u":{self.context.serv} 333 {self.context.identity.nick} {self.name} {self.topicsetby.nick} {self.topictime}".format(**vars())
+ else:
+ self.send(origin=self.bouncer.servname, cmd=331,
+ target=self.context.identity, params=channel.name, extinfo="No topic is set")
+ # u":{self.context.serv} 331 {self.context.identity.nick}
+ # {self.name} :No topic is set".format(**vars())]
+
+ def sendchannelnames(self, channel):
+ with self.context.lock, self.lock:
+ secret = "s" in channel.modes.keys() and channel.modes["s"]
+ private = "p" in channel.modes.keys() and channel.modes["p"]
+ flag = "@" if secret else ("*" if private else "=")
+
+ modes, symbols = supports = self.context.supports.get(
+ "PREFIX", irc._defaultprefix)
+ users = list(channel.users)
+ users.sort(key=lambda user: ([user not in channel.modes.get(mode, [])
+ for mode, char in zip(*supports)], user.nick.lower()))
+ if self.uhnames:
+ template = u"{prefixes}{user:full}"
+ else:
+ template = u"{prefixes}{user}"
+
+ nameslist = []
+ for user in users:
+ prefixes = u"".join(
+ [prefix if mode in channel.modes.keys() and user in channel.modes[mode] else "" for prefix, mode in zip(symbols, modes)])
+ if not self.namesx:
+ prefixes = prefixes[:1]
+ nameslist.append(template.format(**vars()))
+ names = " ".join(nameslist)
+
+ lines = []
+ while len(names) > 196:
+ index = names.rfind(" ", 0, 196)
+ slice = names[:index]
+ self.send(
+ origin=self.bouncer.servname, cmd=353, target=self.context.identity,
+ params="{flag} {channel.name}".format(**vars()), extinfo=slice)
+ #u":{channel.context.serv} 353 {channel.context.identity.nick} {flag} {channel.name} :{slice}".format(**vars())
+ names = names[index + 1:]
+ if len(names):
+ self.send(
+ origin=self.bouncer.servname, cmd=353, target=self.context.identity,
+ params="{flag} {channel.name}".format(**vars()), extinfo=names)
+ #u":{channel.context.serv} 353 {channel.context.identity.nick} {flag} {channel.name} :{names}".format(**vars())
+
+ self.send(
+ origin=self.bouncer.servname, cmd=366, target=self.context.identity,
+ params=channel.name, extinfo="End of /NAMES list.")
+ # u":{channel.context.serv} 366 {channel.context.identity.nick} {channel.name} :End of /NAMES list.".format(**vars())
+
+ def sendchannelmodes(self, channel, modechars=None):
+ with self.context.lock, self.lock:
+ if modechars:
+ for mode in modechars:
+ if 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.send(
+ origin=self.bouncer.servname, cmd=i, target=self.context.identity,
+ params=u"{channel.name} {mask} {setby} {settime}".format(**vars()))
+ self.send(origin=self.bouncer.servname, cmd=e,
+ target=self.context.identity, params=u"{channel.name} {l}".format(**vars()))
+ else:
+ items = channel.modes.items()
+ chanmodes = self.context.supports.get(
+ "CHANMODES", irc._defaultchanmodes)
+ prefix = self.context.supports.get(
+ "PREFIX", irc._defaultprefix)
+ modes = "".join(
+ [mode for (mode, val) in items if mode not in chanmodes[0] + prefix[0] and val])
+ params = " ".join(
+ [val for (mode, val) in items if mode in chanmodes[1] + chanmodes[2] and val])
+ if modes and params:
+ self.send(
+ origin=self.bouncer.servname, cmd=324, target=self.context.identity,
+ params="{channel.name} +{modes} {params}".format(**vars()))
+ # u":{channel.context.identity.server} 324 {channel.context.identity.nick} {channel.name} +{modes} {params}".format(**vars())
+ elif modes:
+ self.send(
+ origin=self.bouncer.servname, cmd=324, target=self.context.identity,
+ params="{channel.name} +{modes}".format(**vars()))
+ # u":{channel.context.identity.server} 324 {channel.context.identity.nick} {channel.name} +{modes}".format(**vars())
+
+ def sendsupports(self):
+ with self.context.lock, self.lock:
+ 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.context.supports.items()]
+ if "UHNAMES" not in supports:
+ supports.append("UHNAMES")
+ if "NAMESX" not in supports:
+ supports.append("NAMESX")
+ supports.sort()
+ supports = " ".join(supports)
+ lines = []
+ while len(supports) > 196:
+ index = supports.rfind(" ", 0, 196)
+ slice = supports[:index]
+ self.send(
+ origin=self.bouncer.servname, cmd=5, target=self.context.identity,
+ params=slice, extinfo="are supported by this server")
+ # u":{self.context.serv} 005 {self.context.identity.nick} {slice} :are supported by this server".format(**vars())
+ supports = supports[index + 1:]
+ if supports:
+ self.send(
+ origin=self.bouncer.servname, cmd=5, target=self.context.identity,
+ params=supports, extinfo="are supported by this server")
+ # u":{self.context.serv} 005 {self.context.identity.nick} {supports} :are supported by this server".format(**vars())
+
+ def sendgreeting(self):
+ with self.context.lock, self.lock:
+ if self.context.welcome:
+ self.send(origin=self.bouncer.servname, cmd=1,
+ target=self.context.identity, extinfo=self.context.welcome)
+ # u":{self.context.serv} 001 {self.context.identity.nick} :{self.context.welcome}".format(**vars())
+ if self.context.hostinfo:
+ self.send(origin=self.bouncer.servname, cmd=2,
+ target=self.context.identity, extinfo=self.context.hostinfo)
+ # u":{self.context.serv} 002 {self.context.identity.nick} :{self.context.hostinfo}".format(**vars())
+ if self.context.servcreated:
+ self.send(origin=self.bouncer.servname, cmd=3,
+ target=self.context.identity, extinfo=self.context.servcreated)
+ # u":{self.context.serv} 003 {self.context.identity.nick} :{self.context.servcreated}".format(**vars())
+ if self.context.servinfo:
+ self.send(origin=self.bouncer.servname, cmd=4,
+ target=self.context.identity, params=self.context.servinfo)
+ # u":{self.context.serv} 004 {self.context.identity.nick} {self.context.servinfo}".format(**vars())
+
+ def sendmotd(self):
+ with self.context.lock, self.lock:
+ if self.context.motdgreet and self.context.motd and self.context.motdend:
+ self.send(origin=self.bouncer.servname, cmd=375,
+ target=self.context.identity, extinfo=self.context.motdgreet)
+ # u":{server} 375 {self.identity.nick} :{self.motdgreet}".format(**vars())
+ for motdline in self.context.motd:
+ self.send(origin=self.bouncer.servname, cmd=372,
+ target=self.context.identity, extinfo=motdline)
+ # u":{server} 372 {self.identity.nick} :{motdline}".format(**vars())
+ self.send(origin=self.bouncer.servname, cmd=376,
+ target=self.context.identity, extinfo=self.context.motdend)
+ # u":{server} 376 {self.identity.nick} :{self.motdend}".format(**vars())
+ else:
+ self.send(origin=self.bouncer.servname, cmd=422,
+ target=self.context.identity, extinfo="MOTD File is missing")
+ # u":{server} 422 {self.identity.nick} :MOTD File is missing".format(**vars())
+
+ def sendusermodes(self):
+ with self.context.lock, self.lock:
+ self.send(
+ origin=self.bouncer.servname, cmd=221, target=self.context.identity,
+ params="+{self.context.identity.modes}".format(**vars()))
+ if "s" in self.context.identity.modes:
+ self.send(
+ origin=self.bouncer.servname, cmd=8, target=self.context.identity,
+ params="+{self.context.identity.snomask}".format(**vars()), extinfo="Server notice mask")
+
def run(self):
# Name loopup should happen here instead
ipv4match = re.findall(
@@ -575,13 +755,6 @@ class BouncerConnection (Thread):
# 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
@@ -596,7 +769,14 @@ class BouncerConnection (Thread):
# append lines to linebuf
while len(linebuf) == 0:
timestamp = irc.timestamp()
- read = self.connection.recv(512)
+ try:
+ read = self.connection.recv(512)
+ except socket.error, msg:
+ self.quit(msg)
+ sys.exit()
+ except ssl.SSLError, msg:
+ self.quit(msg)
+ sys.exit()
if read == "" and len(linebuf) == 0: # No more data to process.
#self.quitmsg="Connection Closed"
sys.exit()
@@ -649,17 +829,10 @@ class BouncerConnection (Thread):
contextfound = True
break
if not contextfound:
- with self.context.lock:
- self.quit("Access Denied")
- self.context.logwrite(
- "*** [BouncerConnection] Incoming connection from %s to %s denied: Invalid password." % (self.host, self.context))
- for bouncerconnection in self.bouncer.connections:
- if bouncerconnection.context != self.context:
- continue
- if not bouncerconnection.quitting:
- bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s dened: Invalid password.\n" % (
- bouncerconnection.context.identity.nick, self.host, self.context))
- break
+ self.quit("Access Denied")
+ print >>sys.stderr, "*** [BouncerConnection] Incoming connection from %s denied: Context not found." % (
+ self.host)
+ break
passmatch = hashlib.new(
conf.hashtype, passwd).hexdigest() == conf.passwd
with self.context.lock:
@@ -667,396 +840,130 @@ class BouncerConnection (Thread):
self.quit("Access Denied")
self.context.logwrite(
"*** [BouncerConnection] Incoming connection from %s to %s denied: Invalid password." % (self.host, self.context))
- for bouncerconnection in self.bouncer.connections:
- if bouncerconnection.context != self.context:
- continue
- if not bouncerconnection.quitting:
- bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s dened: Invalid password.\n" % (
- bouncerconnection.context.identity.nick, self.host, self.context))
+ self.bouncer.broadcast(
+ self.context, origin=self.bouncer.servname, cmd="NOTICE", target=client.context.identity,
+ extinfo="Incoming connection from %s to %s denied: Invalid password." % (self.host, self.context))
+ # for client in self.bouncer.clients:
+ # if client.context!=self.context:
+ # continue
+ # if not client.quitting:
+ #client.send(origin=self.bouncer.servname, cmd="NOTICE", target=client.context.identity, extinfo="Incoming connection from %s to %s dened: Invalid password.\n" % (self.host, self.context))
break
self.context.logwrite(
- "*** [BouncerConnection] Incoming connection from %s to %s." % (self.host, self.context))
+ "*** [BouncerConnection] Incoming connection from %s to %s established." % (self.host, self.context))
with self.bouncer.lock:
+ self.translations = dict(
+ self.bouncer.conf[self.context].translations)
# Announce connection to all other bouncer
- # connections.
- for bouncerconnection in self.bouncer.connections:
- if bouncerconnection.context != self.context:
- continue
- if not bouncerconnection.quitting:
- bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (
- bouncerconnection.context.identity.nick, self.host, self.context))
- if len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.context == self.context]) == 0 and self.context.registered and type(self.context.identity) == irc.User and self.context.identity.away:
- # Bouncer connection should automatically
- # return from away status.
+ # clients.
+ self.bouncer.broadcast(
+ self.context, origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="Incoming connection from %s to %s established." % (self.host, self.context))
+ # for client in self.bouncer.clients:
+ # if client.context!=self.context:
+ # continue
+ # if not client.quitting:
+ #client.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (client.context.identity.nick, self.host, self.context))
+ if len([client for client in self.bouncer.clients if client.context == self.context]) == 0 and self.context.registered and type(self.context.identity) == irc.User and self.context.identity.away:
+ # Bouncer connection should
+ # automatically return from away
+ # status.
self.context.raw("AWAY")
self.hidden = irc.ChanList(
self.bouncer.conf[self.context].hidden, context=self.context)
- self.bouncer.connections.append(self)
+ self.bouncer.clients.append(self)
if self.context.registered:
# Send Greeting.
with self.lock:
- if self.context.welcome:
- self.connection.send(
- (u":%s 001 %s :%s\n" % (self.context.serv, self.context.identity.nick, self.context.welcome)).encode("utf8"))
- if self.context.hostinfo:
- self.connection.send(
- (u":%s 002 %s :%s\n" % (self.context.serv, self.context.identity.nick, self.context.hostinfo)).encode("utf8"))
- if self.context.servcreated:
- self.connection.send(
- (u":%s 003 %s :%s\n" % (self.context.serv, self.context.identity.nick, self.context.servcreated)).encode("utf8"))
- if self.context.servinfo:
- self.connection.send(
- (u":%s 004 %s %s\n" % (self.context.serv, self.context.identity.nick, self.context.servinfo)).encode("utf8"))
-
- # Send 005 response.
- if self.context.supports:
- supports = ["CHANMODES=%s" % (",".join(value)) if name == "CHANMODES" else "PREFIX=(%s)%s" % value if name == "PREFIX" else "%s=%s" % (
- name, value) if value else name for name, value in self.context.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((u":%s 005 %s %s :are supported by this server\n" % (
- self.context.serv, self.context.identity.nick, support)).encode("utf8"))
-
- # Send MOTD
- if self.context.motdgreet and self.context.motd and self.context.motdend:
- self.connection.send(
- (u":%s 375 %s :%s\n" % (self.context.serv, self.context.identity.nick, self.context.motdgreet)).encode("utf8"))
- for motdline in self.context.motd:
- self.connection.send(
- (u":%s 372 %s :%s\n" % (self.context.serv, self.context.identity.nick, motdline)).encode("utf8"))
- try:
- self.connection.send(
- (u":%s 376 %s :%s\n" % (self.context.serv, self.context.identity.nick, self.context.motdend)).encode("utf8"))
- except AttributeError:
- self.connection.send(
- (u":%s 376 %s\n" % (self.context.serv, self.context.identity.nick)).encode("utf8"))
- else:
- self.connection.send((u":%s 422 %s :MOTD File is missing\n" % (
- self.context.serv, self.context.identity.nick)).encode("utf8"))
-
- # Send user modes and snomasks.
- self.connection.send(
- (u":%s 221 %s +%s\n" % (self.context.serv, self.context.identity.nick, self.context.identity.modes)).encode("utf8"))
-
- if "s" in self.context.identity.modes and self.context.identity.snomask:
- self.connection.send((u":%s 008 %s +%s :Server notice mask\n" % (
- self.context.serv, self.context.identity.nick, self.context.identity.snomask)).encode("utf8"))
+ self.sendgreeting()
+ self.sendsupports()
+ self.sendmotd()
+ self.sendusermodes()
# Join user to channels.
for channel in self.context.identity.channels:
- if channel in self.hidden:
- continue
-
- if channel in conf.translations.keys():
- channame = conf.translations[
- channel]
- else:
- channame = channel.name
- # JOIN command
- self.connection.send(
- (u":%s!%s@%s JOIN :%s\n" % (self.context.identity.nick, self.context.identity.username, self.context.identity.host, channame)).encode("utf8"))
-
- # Topic
- self.connection.send(
- (u":%s 332 %s %s :%s\n" % (self.context.serv, self.context.identity.nick, channame, channel.topic)).encode("utf8"))
- self.connection.send((u":%s 333 %s %s %s %s\n" % (self.context.serv, self.context.identity.nick, channame, channel.topicsetby.nick if type(
- channel.topicsetby) == irc.User else channel.topicsetby, channel.topictime)).encode("utf8"))
-
- # 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.context.supports[
- "PREFIX"]
- self.connection.send((u":%s 353 %s %s %s :%s\n" % (
- self.context.serv,
- self.context.identity.nick,
- "@" if secret else (
- "*" if private else "="),
- channame,
- u" ".join([u"".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]))
- ).encode("utf8"))
- self.connection.send((u":%s 366 %s %s :End of /NAMES list.\n" % (
- self.context.serv, self.context.identity.nick, channame)).encode("utf8"))
+ if channel not in self.hidden:
+ self.showchannel(channel)
else:
self.send(
- u":*Bouncer* NOTICE %s :Not connected to server. Type /bncconnect to attempt connection.\n" % self.nick)
- self.send(
- u":%s 001 %s :Welcome to the Bouncer context Network %s!%s@%s\n" %
- ("*Bouncer*", self.nick, self.nick, self.username, self.host))
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.nick,
+ extinfo="Not connected to server. Type /bncconnect to attempt connection.")
+ #self.send(u":%s 001 %s :Welcome to the Bouncer context Network %s!%s@%s\n" % ("*Bouncer*", self.nick, self.nick, self.username, self.host))
else: # Client did not send USER command when expected
self.quit("Access Denied")
print "*** [BouncerConnection] Incoming connection from %s failed: Expected USER." % (self.host)
break
- elif cmd.upper() == "QUIT":
- self.quit(extinfo)
- break
-
- elif cmd.upper() == "SHOW":
- if target:
- for chan in target.split(","):
- chantypes = self.context.supports.get(
- "CHANTYPES", "&#+!")
- if re.match(irc._chanmatch % re.escape(chantypes), target):
- translationfound = False
- for channel, channame in conf.translations.items():
- if translation.lower() == chan.lower():
- translationfound = True
- if not translationfound:
- channel = self.context[chan]
- channame = channel.name
-
- if channel in self.hidden:
- with self.lock:
- self.hidden.remove(channel)
-
- if channel in self.context.identity.channels:
- # JOIN command
- self.connection.send(
- (u":%s!%s@%s JOIN :%s\n" % (self.context.identity.nick, self.context.identity.username, self.context.identity.host, channame)).encode("utf8"))
-
- # Topic
- self.connection.send(
- (u":%s 332 %s %s :%s\n" % (self.context.serv, self.context.identity.nick, channame, channel.topic)).encode("utf8"))
- self.connection.send((u":%s 333 %s %s %s %s\n" % (self.context.serv, self.context.identity.nick, channame, channel.topicsetby.nick if type(
- channel.topicsetby) == irc.User else channel.topicsetby, channel.topictime)).encode("utf8"))
-
- # 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.context.supports[
- "PREFIX"]
- self.connection.send((u":%s 353 %s %s %s :%s\n" % (
- self.context.serv,
- self.context.identity.nick,
- "@" if secret else (
- "*" if private else "="),
- channame,
- u" ".join([u"".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]))
- ).encode("utf8"))
- self.connection.send((u":%s 366 %s %s :End of /NAMES list.\n" % (
- self.context.serv, self.context.identity.nick, channame)).encode("utf8"))
- else:
- self.connection.send((u":%s 442 %s %s :You are not on that channel.\n" % (
- self.context.serv, self.context.identity.nick, channame)).encode("utf8"))
- else:
- self.connection.send((u":%s 403 %s %s :Invalid channel name.\n" % (
- self.context.serv, self.context.identity.nick, chan)).encode("utf8"))
- else:
- self.connection.send((u":%s 461 %s SHOW :Not enough parameters.\n" % (
- self.context.serv, self.context.identity.nick)).encode("utf8"))
- self.connection.send((u":%s 304 %s :SYNTAX SHOW <channel>{,<channel>}\n" % (
- self.context.serv, self.context.identity.nick)).encode("utf8"))
- elif cmd.upper() == "HIDE":
- if target:
- for chan in target.split(","):
- chantypes = self.context.supports.get(
- "CHANTYPES", "&#+!")
- if re.match(irc._chanmatch % re.escape(chantypes), target):
- translationfound = False
- for channel, channame in conf.translations.items():
- if translation.lower() == chan.lower():
- translationfound = True
- if not translationfound:
- channel = self.context[chan]
- channame = channel.name
-
- if channel not in self.hidden:
- with self.lock:
- self.hidden.append(channel)
-
- if channel in self.context.identity.channels:
- # PART command
- self.connection.send((u":%s!%s@%s PART %s :Hiding channel\n" % (
- self.context.identity.nick, self.context.identity.username, self.context.identity.host, channame)).encode("utf8"))
- else:
- self.connection.send((u":%s 442 %s %s :You are not on that channel.\n" % (
- self.context.serv, self.context.identity.nick, channame)).encode("utf8"))
- else:
- self.connection.send((u":%s 403 %s %s :Invalid channel name.\n" % (
- self.context.serv, self.context.identity.nick, chan)).encode("utf8"))
- else:
- self.connection.send((u":%s 461 %s HIDE :Not enough parameters.\n" % (
- self.context.serv, self.context.identity.nick)).encode("utf8"))
- self.connection.send((u":%s 304 %s :SYNTAX HIDE <channel>{,<channel>}\n" % (
- self.context.serv, self.context.identity.nick)).encode("utf8"))
-
- elif cmd.upper() == "PING":
- self.send(":%s PONG %s :%s\n" %
- (self.context.serv, self.context.serv, self.context.identity.nick if type(self.context.identity) == irc.User else "***"))
-
- elif cmd.upper() == "BNCCONNECT":
- with self.context.lock:
- if self.context.isAlive() and self.context.connected:
- self.send(
- ":*Bouncer* NOTICE %s :Bouncer is already connected.\n" % self.nick)
- self.context.start()
-
- elif cmd.upper() == "BNCQUIT":
- with self.context.lock:
- if self.context.isAlive() and self.context.connected and self.context.registered:
- quitmsg = " ".join(
- [word for word in [target, params, extinfo] if word])
- self.context.quit(quitmsg)
- else:
- self.send(
- ":*Bouncer* NOTICE %s :Bouncer is already disconnected.\n" % self.nick)
-
else:
- if target:
- targetlist = []
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ if cmd.upper() not in ("SETTRANSLATE", "RMTRANSLATE"):
+ translated = []
for targ in target.split(","):
- translationfound = False
- for (channel, translation) in conf.translations.items():
- if translation.lower() == targ.lower():
- # print channel
- targetlist.append(channel.name)
- translationfound = True
- break
- if not translationfound:
- targetlist.append(targ)
- target = ",".join(targetlist)
-
- oldparams = params
- params = []
- for param in oldparams.split():
- translationfound = False
- for (channel, translation) in conf.translations.items():
- # print target, (channel, translation)
- if translation.lower() == param.lower():
- # print channel
- params.append(channel.name)
- translationfound = True
- break
- if not translationfound:
- params.append(param)
- params = " ".join(params)
-
- #print (cmd, target, params, extinfo)
-
- if params and extinfo:
- line = "%s %s %s :%s" % (
- cmd, target, params, extinfo)
- elif params:
- line = "%s %s %s" % (cmd, target, params)
- elif extinfo:
- line = "%s %s :%s" % (cmd, target, extinfo)
- else:
- line = "%s %s" % (cmd, target)
-
- with self.context.lock:
- # print "Locked"
- # print self.context.connected,
- # self.context.registered, cmd.upper()
- if not self.context.connected:
- self.send(
- ":*Bouncer* NOTICE %s :Not connected to server. Type /bncconnect to attempt connection.\n" % self.nick)
- break
-
- elif not self.context.registered:
- self.send(
- ":*Bouncer* NOTICE %s :Not registered.\n" % self.nick)
- break
-
- elif cmd.upper() in ("PRIVMSG", "NOTICE"):
- # Check if CTCP
- ctcp = re.findall(
- "^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
-
- if ctcp: # If CTCP, only want to
- (ctcptype, ext) = ctcp[0] # Unpack CTCP info
-
- if ctcptype == "LAGCHECK": # Client is doing a lag check. No need to send to context network, just reply back.
- self.send(":%s!%s@%s %s\n" % (
- self.context.identity.nick, self.context.identity.username, self.context.identity.host, line))
- else:
- self.context.raw(line, origin=self)
- else:
- self.context.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.context.supports.keys() and target[0] in
- # self.context.supports["CHANTYPES"]:
- chantypes = self.context.supports.get(
- "CHANTYPES", "&#+!")
- if re.match(irc._chanmatch % re.escape(chantypes), target):
- channel = self.context[target]
- if channel in conf.translations.keys():
- channame = conf.translations[channel]
- else:
- channame = channel.name
-
- if params == "":
- modes = channel.modes.keys()
- modestr = "".join([mode for mode in modes if mode not in self.context.supports[
- "CHANMODES"][0] + self.context.supports["PREFIX"][0] and channel.modes[mode]])
- params = " ".join([channel.modes[mode] for mode in modes if mode in self.context.supports[
- "CHANMODES"][1] + self.context.supports["CHANMODES"][2] and channel.modes[mode]])
- with self.lock:
- if len(modestr):
- self.connection.send(
- (u":%s 324 %s %s +%s %s\n" % (self.context.serv, self.context.identity.nick, channame, modestr, params)).encode("utf8"))
- if channel.created:
- self.connection.send(
- (u":%s 329 %s %s %s\n" % (self.context.serv, self.context.identity.nick, channame, channel.created)).encode("utf8"))
- elif re.match("^\\+?[%s]+$" % self.context.supports["CHANMODES"][0], params) and extinfo == "":
- # print "ddd Mode List Request", params
- redundant = []
- for mode in params.lstrip("+"):
- if mode in redundant or mode not in listnumerics.keys():
- continue
- i, e, l = listnumerics[mode]
- with self.lock:
- if mode in channel.modes.keys():
- for (mask, setby, settime) in channel.modes[mode]:
- self.connection.send(
- (u":%s %d %s %s %s %s %s\n" % (self.context.serv, i, channel.context.identity.nick, channame, mask, setby, settime)).encode("utf8"))
- self.connection.send(
- (u":%s %d %s %s :End of %s\n" % (self.context.serv, e, channel.context.identity.nick, channame, l)).encode("utf8"))
- redundant.append(mode)
- else:
- self.context.raw(line, origin=self)
- elif params == "" and target.lower() == self.context.identity.nick.lower():
- with self.lock:
- self.connection.send(
- (u":%s 221 %s +%s\n" % (self.context.serv, self.context.identity.nick, self.context.identity.modes)).encode("utf8"))
- if "s" in self.context.identity.modes and self.context.identity.snomask:
- self.connection.send((u":%s 008 %s +%s :Server notice mask\n" % (
- self.context.serv, self.context.identity.nick, self.context.identity.snomask)).encode("utf8"))
- else:
- self.context.raw(line, origin=self)
+ translatefound = False
+ if re.match(irc._chanmatch % re.escape(chantypes), targ):
+ for channel, translate in self.translations.items():
+ if targ.lower() == translate.lower():
+ translated.append(channel.name)
+ translatefound = True
+ break
+ if not translatefound:
+ translated.append(targ)
+ target = ",".join(translated)
+
+ translated = []
+ for param in params.split(" "):
+ translatefound = False
+ if re.match(irc._chanmatch % re.escape(chantypes), param):
+ for channel, translate in self.translations.items():
+ if param.lower() == translate.lower():
+ translated.append(channel.name)
+ translatefound = True
+ break
+ if not translatefound:
+ translated.append(param)
+ params = " ".join(translated)
+
+ if params:
+ line = u"{cmd} {target} {params}".format(**vars())
+ elif target:
+ line = u"{cmd} {target}".format(**vars())
else:
- self.context.raw(line, origin=self)
+ line = cmd
+
+ if extinfo:
+ line = u"{line} :{extinfo}".format(**vars())
+
+ cmdmethod = "cmd%s" % cmd.upper()
+ if hasattr(self, cmdmethod):
+ method = getattr(self, cmdmethod)
+ try:
+ method(line, target, params, extinfo)
+ except SystemExit:
+ sys.exit()
+ except:
+ if self.context:
+ exc, excmsg, tb = sys.exc_info()
+ self.context.logwrite(*[u"!!! [BouncerConnection] Exception in thread %(self)s" % vars()] + [
+ u"!!! [BouncerConnection] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
+ print >>sys.stderr, "Exception in thread %(self)s" % vars(
+ )
+ print >>sys.stderr, traceback.format_exc()
+ elif not self.context.connected:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.nick,
+ extinfo="Not connected to server. Type /bncconnect to attempt connection.")
+ continue
+
+ elif not self.context.registered:
+ self.send(origin=self.bouncer.servname, cmd="NOTICE",
+ target=self.nick, extinfo="Not registered.")
+ continue
+ else:
+ self.context.raw(line, origin=self)
except SystemExit:
pass # No need to pass error message if break resulted from sys.exit()
except:
@@ -1066,8 +973,8 @@ class BouncerConnection (Thread):
exc, excmsg, tb = sys.exc_info()
self.context.logwrite(*["!!! [BouncerConnection] Exception in thread %(self)s" % vars()] + [
"!!! [BouncerConnection] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
- print >>sys.stderr, "Exception in thread %(self)s" % vars()
- print >>sys.stderr, traceback.format_exc()
+ print >>sys.stderr, "Exception in thread %(self)s" % vars()
+ print >>sys.stderr, traceback.format_exc()
finally:
# Juuuuuuust in case.
with self.lock:
@@ -1081,30 +988,204 @@ class BouncerConnection (Thread):
self.context.logwrite(
"*** [BouncerConnection] Connection from %s terminated (%s)." % (self.host, self.quitmsg))
- if self in self.bouncer.connections:
- with self.bouncer.lock:
- self.bouncer.connections.remove(self)
- if self.context.connected and self.context.identity and len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.context == self.context]) == 0 and self.context.registered and type(self.context.identity) == irc.User and not self.context.identity.away and self.bouncer.autoaway:
+ with self.bouncer.lock:
+ if self in self.bouncer.clients:
+ self.bouncer.clients.remove(self)
+ if self.context.connected and self.context.identity and len([client for client in self.bouncer.clients if client.context == self.context]) == 0 and self.context.registered and type(self.context.identity) == irc.User and not self.context.identity.away and self.bouncer.autoaway:
# Bouncer automatically sets away status.
self.context.raw("AWAY :%s" % self.bouncer.autoaway)
- if self.debug:
- self.context.logwrite(
- "dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars())
- for bouncerconnection in self.bouncer.connections:
- if bouncerconnection.context == self.context:
- if self.debug:
- self.context.logwrite(
- "dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars())
- if not bouncerconnection.quitting:
- bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (
- bouncerconnection.context.identity.nick, self.host, self.context, self.quitmsg))
- if self.debug:
- self.context.logwrite(
- "dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars())
-
-# Announce QUIT to other bouncer connections.
-# for bouncerconnection in self.bouncer.connections:
-# try:
-# bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.username, self.host, self.quitmsg))
-# except:
-# pass
+ self.bouncer.broadcast(
+ self.context, origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="Connection from %s to %s terminated (%s)\n" % (self.host, self.context, self.quitmsg))
+ # ":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (client.context.identity.nick, self.host, self.context, self.quitmsg))
+
+ def cmdQUIT(self, line, target, params, extinfo):
+ self.quit(extinfo)
+ sys.exit()
+
+ def cmdPROTOCTL(self, line, target, params, extinfo):
+ protoparams = [target.upper()] + params.upper().split()
+ if "NAMESX" in protoparams:
+ self.namesx = True
+ if "UHNAMES" in protoparams:
+ self.uhnames = True
+
+ def cmdPING(self, line, target, params, extinfo):
+ with self.context.lock:
+ if True or (self.context.identity and type(self.context.identity) == irc.User):
+ self.send(origin=self.bouncer.servname,
+ cmd="PONG", target=params, extinfo=target)
+ # u":{self.context.identity.server} PONG {params}
+ # :{target}\n".format(**vars()).encode("utf8"))
+ else:
+ self.send(origin=self.bouncer.servname,
+ cmd="PONG", params=params, extinfo=target)
+ self.send(
+ u":{self.context.server} PONG {params} :{target}\n".format(**vars()).encode("utf8"))
+
+ def cmdPRIVMSG(self, line, target, params, extinfo):
+ # Check if CTCP
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
+
+ if ctcp:
+ (ctcptype, ext) = ctcp[0] # Unpack CTCP info
+
+ if ctcptype == "LAGCHECK": # Client is doing a lag check. No need to send to context network, just reply back.
+ self.send(
+ u":{self.context.identity:full} {line}\n".format(**vars()).encode("utf8"))
+ else:
+ self.context.raw(line, origin=self)
+ else:
+ self.context.raw(line, origin=self)
+
+ def cmdMODE(self, line, target, params, extinfo): # Will want to determine is requesting modes, or attempting to modify modes.
+ # if target and "CHANTYPES" in self.context.supports.keys() and
+ # target[0] in self.context.supports["CHANTYPES"]:
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ chanmodes = self.context.supports.get(
+ "CHANMODES", irc._defaultchanmodes)
+ prefix = self.context.supports.get("PREFIX", irc._defaultprefix)
+ if re.match(irc._chanmatch % re.escape(chantypes), target):
+ channel = self.context[target]
+
+ if params == "":
+ # We are requesting the modes for the channel
+ if self.context.identity in channel.users:
+ # We are in the channel, and we know the channel modes
+ self.sendchannelmodes(channel)
+ else:
+ # We are NOT in the channel, so we will forward the request
+ # to the server.
+ self.context.raw(
+ u"MODE {channel.name}".format(**vars()), origin=self)
+
+ elif re.match("^\\+?[%s]+$" % chanmodes[0], params) and extinfo == "":
+ # We are requesting one or more mode lists.
+ modechars = ""
+ for mode in params.lstrip("+"):
+ if mode not in modechars:
+ modechars += mode
+ if self.context.identity in channel.users:
+ self.sendchannelmodes(channel, modechars)
+ else:
+ self.context.raw(
+ u"MODE {channel.name} {params}".format(**vars()), origin=self)
+ else:
+ self.context.raw(line, origin=self)
+ elif params == "" and target.lower() == self.context.identity.nick.lower():
+ self.sendusermodes()
+ else:
+ self.context.raw(
+ u"MODE {target} {params}".format(**vars()), origin=self)
+
+ def cmdNAMES(self, line, target, params, extinfo):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ chanmodes = self.context.supports.get(
+ "CHANMODES", irc._defaultchanmodes)
+ prefix = self.context.supports.get("PREFIX", irc._defaultprefix)
+ fallback = []
+ with self.lock:
+ for channame in target.split():
+ if re.match(irc._chanmatch % re.escape(chantypes), channame):
+ channel = self.context[channame]
+ with self.lock:
+ if self.context.identity in channel:
+ self.sendchannelnames(channel)
+ else:
+ fallback.append(channame)
+ else:
+ fallback.append(channame)
+ if fallback:
+ self.context.raw("NAMES %s" %
+ (",".join(fallback)), origin=self)
+
+ def cmdSHOW(self, line, target, params, extinfo):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ with self.lock:
+ for channame in target.split():
+ if re.match(irc._chanmatch % re.escape(chantypes), channame):
+ channel = self.context[channame]
+ if channel in self.hidden:
+ if self.context.identity in channel:
+ self.showchannel(channel)
+ else:
+ self.hidden.remove(channel)
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channel.name} removed from hidden list, but not joined.".format(**vars()))
+ else:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channel.name} not in hidden list.".format(**vars()))
+ else:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channame}: invalid channel name.".format(**vars()))
+
+ def cmdHIDE(self, line, target, params, extinfo):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ with self.lock:
+ for channame in target.split():
+ if re.match(irc._chanmatch % re.escape(chantypes), channame):
+ channel = self.context[channame]
+ if channel not in self.hidden:
+ if self.context.identity in channel:
+ self.send(
+ origin=self.context.identity, cmd="PART", target=channel, extinfo="Hiding channel")
+ else:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channel.name} added to the hidden list, but not joined.".format(**vars()))
+ self.hidden.append(channel)
+ else:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channel.name} already in hidden list.".format(**vars()))
+ else:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="{channame}: invalid channel name.".format(**vars()))
+
+ def cmdSETTRANSLATE(self, line, target, params, extinfo):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ with self.lock:
+ if re.match(irc._chanmatch % re.escape(chantypes), target) and re.match(irc._chanmatch % re.escape(chantypes), target):
+ channel = self.context[target]
+ if self.context.supports.get("CASEMAPPING", "rfc1459") == "ascii":
+ translations_lower = [translation.translate(irc._rfc1459casemapping)
+ for translation in self.translations.values()]
+ params_lower = params.translate(irc._rfc1459casemapping)
+ else:
+ translations_lower = [translation.lower()
+ for translation in self.translations.values()]
+ params_lower = params.lower()
+ if params_lower in translations_lower:
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="Cannot set translation for {channel.name} to {param}.".format(**vars()))
+ else:
+ self.send(origin=self.context.identity, cmd="PART",
+ target=channel, extinfo="Translating...")
+ self.translations[channel] = params
+ self.showchannel(channel)
+
+ def cmdRMTRANSLATE(self, line, target, params, extinfo):
+ chantypes = self.context.supports.get(
+ "CHANTYPES", irc._defaultchantypes)
+ with self.lock:
+ if re.match(irc._chanmatch % re.escape(chantypes), target):
+ channel = self.context[target]
+ if channel not in self.translations.keys():
+ self.send(
+ origin=self.bouncer.servname, cmd="NOTICE", target=self.context.identity,
+ extinfo="Cannot remove translation for {channel.name}.".format(**vars()))
+ else:
+ self.send(origin=self.context.identity, cmd="PART",
+ target=channel, extinfo="Translating...")
+ del self.translations[channel]
+ self.showchannel(channel)