3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-26 04:32:51 +01:00

Merge branch 'devel' into wip/unrealircd

This commit is contained in:
James Lu 2015-10-12 17:50:35 -07:00
commit 07fce69c5e
4 changed files with 43 additions and 19 deletions

View File

@ -1,6 +1,6 @@
# PyLink # PyLink
PyLink is an extensible, plugin-based IRC Services framework written in Python. It aims to be a replacement for the now-defunct Janus. PyLink is an extensible, plugin-based IRC Services framework written in Python. It aims to be 1) a replacement for the now-defunct Janus 2) a versatile framework and gateway to IRC.
## Usage ## Usage

View File

@ -32,6 +32,10 @@ relay_started = True
def initializeAll(irc): def initializeAll(irc):
"""Initializes all relay channels for the given IRC object.""" """Initializes all relay channels for the given IRC object."""
# Wait for all IRC objects to initialize first. This prevents
# relay servers from being spawned too early (before server authentication),
# which would break connections.
world.started.wait(2)
for chanpair, entrydata in db.items(): for chanpair, entrydata in db.items():
network, channel = chanpair network, channel = chanpair
initializeChannel(irc, channel) initializeChannel(irc, channel)
@ -164,6 +168,8 @@ def getPrefixModes(irc, remoteirc, channel, user, mlist=None):
def getRemoteSid(irc, remoteirc): def getRemoteSid(irc, remoteirc):
"""Gets the remote server SID representing remoteirc on irc, spawning """Gets the remote server SID representing remoteirc on irc, spawning
it if it doesn't exist.""" it if it doesn't exist."""
# Don't spawn servers too early.
irc.connected.wait(2)
try: try:
spawnservers = irc.conf['relay']['spawn_servers'] spawnservers = irc.conf['relay']['spawn_servers']
except KeyError: except KeyError:
@ -183,6 +189,7 @@ def getRemoteSid(irc, remoteirc):
log.exception('(%s) Failed to spawn server for %r:', log.exception('(%s) Failed to spawn server for %r:',
irc.name, remoteirc.name) irc.name, remoteirc.name)
irc.aborted.set() irc.aborted.set()
return
else: else:
irc.servers[sid].remote = remoteirc.name irc.servers[sid].remote = remoteirc.name
relayservers[irc.name][remoteirc.name] = sid relayservers[irc.name][remoteirc.name] = sid
@ -996,6 +1003,9 @@ def handle_endburst(irc, numeric, command, args):
utils.add_hook(handle_endburst, "ENDBURST") utils.add_hook(handle_endburst, "ENDBURST")
def handle_disconnect(irc, numeric, command, args): def handle_disconnect(irc, numeric, command, args):
"""Handles IRC network disconnections (internal hook)."""
# Quit all of our users' representations on other nets, and remove
# them from our relay clients index.
for k, v in relayusers.copy().items(): for k, v in relayusers.copy().items():
if irc.name in v: if irc.name in v:
del relayusers[k][irc.name] del relayusers[k][irc.name]
@ -1005,11 +1015,15 @@ def handle_disconnect(irc, numeric, command, args):
del relayusers[k] del relayusers[k]
except KeyError: except KeyError:
pass pass
for name, ircobj in world.networkobjects.items(): # SQUIT all relay pseudoservers spawned for us, and remove them
if name != irc.name: # from our relay subservers index.
rsid = getRemoteSid(ircobj, irc) for name, ircobj in world.networkobjects.copy().items():
# Let's be super extra careful here... if name != irc.name and ircobj.connected.is_set():
if rsid and name in relayservers and irc.name in relayservers[name]: try:
rsid = relayservers[ircobj.name][irc.name]
except KeyError:
continue
else:
ircobj.proto.squitServer(ircobj.sid, rsid, text='Home network lost connection.') ircobj.proto.squitServer(ircobj.sid, rsid, text='Home network lost connection.')
del relayservers[name][irc.name] del relayservers[name][irc.name]
try: try:

View File

@ -288,7 +288,7 @@ class TS6Protocol(TS6BaseProtocol):
def squitServer(self, source, target, text='No reason given'): def squitServer(self, source, target, text='No reason given'):
"""SQUITs a PyLink server.""" """SQUITs a PyLink server."""
# -> SQUIT 9PZ :blah, blah # -> SQUIT 9PZ :blah, blah
self.irc.send('SQUIT %s :%s' % (target, text)) self._send(source, 'SQUIT %s :%s' % (target, text))
self.handle_squit(source, 'SQUIT', [target, text]) self.handle_squit(source, 'SQUIT', [target, text])
def connect(self): def connect(self):
@ -588,22 +588,23 @@ class TS6Protocol(TS6BaseProtocol):
raise ProtocolError("Servers should use EUID instead of UID to send users! " raise ProtocolError("Servers should use EUID instead of UID to send users! "
"This IS a required capability after all...") "This IS a required capability after all...")
def handle_server(self, numeric, command, args): def handle_sid(self, numeric, command, args):
"""Handles incoming SERVER introductions.""" """Handles incoming server introductions."""
# parameters: server name, hopcount, sid, server description # parameters: server name, hopcount, sid, server description
servername = args[0].lower() servername = args[0].lower()
try: sid = args[2]
sid = args[2]
except IndexError:
# It is allowed to send JUPEd servers that exist without a SID.
# That's not very fun to handle, though.
# XXX: don't just save these by their server names; that's ugly!
sid = servername
sdesc = args[-1] sdesc = args[-1]
self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc) self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
return {'name': servername, 'sid': sid, 'text': sdesc} return {'name': servername, 'sid': sid, 'text': sdesc}
handle_sid = handle_server def handle_server(self, sender, command, args):
"""Handles incoming legacy (no SID) server introductions."""
# <- :services.int SERVER a.bc 2 :(H) [GL] a
numeric = self._getSid(sender) # Convert the server name prefix to a SID.
servername = args[0].lower()
sdesc = args[-1]
self.irc.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
return {'name': servername, 'sid': None, 'text': sdesc}
def handle_tmode(self, numeric, command, args): def handle_tmode(self, numeric, command, args):
"""Handles incoming TMODE commands (channel mode change).""" """Handles incoming TMODE commands (channel mode change)."""

View File

@ -19,6 +19,13 @@ class TS6BaseProtocol(Protocol):
args[0] = args[0].split(':', 1)[1] args[0] = args[0].split(':', 1)[1]
return args return args
def _getSid(self, sname):
"""Returns the SID of a server with the given name, if present."""
nick = sname.lower()
for k, v in self.irc.servers.items():
if v.name.lower() == nick:
return k
### OUTGOING COMMANDS ### OUTGOING COMMANDS
def _sendKick(self, numeric, channel, target, reason=None): def _sendKick(self, numeric, channel, target, reason=None):
@ -162,8 +169,10 @@ class TS6BaseProtocol(Protocol):
# :70M SQUIT 1ML :Server quit by GL!gl@0::1 # :70M SQUIT 1ML :Server quit by GL!gl@0::1
split_server = args[0] split_server = args[0]
affected_users = [] affected_users = []
log.info('(%s) Netsplit on server %s', self.irc.name, split_server) log.debug('(%s) Splitting server %s (reason: %s)', self.irc.name, split_server, args[-1])
assert split_server in self.irc.servers, "Tried to split a server (%s) that didn't exist!" % split_server if split_server not in self.irc.servers:
log.warning("(%s) Tried to split a server (%s) that didn't exist!", self.irc.name, split_server)
return
# Prevent RuntimeError: dictionary changed size during iteration # Prevent RuntimeError: dictionary changed size during iteration
old_servers = self.irc.servers.copy() old_servers = self.irc.servers.copy()
for sid, data in old_servers.items(): for sid, data in old_servers.items():