3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-25 12:12:53 +01:00

unreal: implement modes in SJOIN (SJ3), respect S2S message length limits

Closes #378. Ref #253
This commit is contained in:
James Lu 2017-01-01 00:00:01 -08:00
parent eafec9d4ad
commit f851dc8ac1

View File

@ -12,6 +12,14 @@ from pylinkirc.classes import *
from pylinkirc.log import log from pylinkirc.log import log
from pylinkirc.protocols.ts6_common import * from pylinkirc.protocols.ts6_common import *
SJOIN_PREFIXES = {'q': '*', 'a': '~', 'o': '@', 'h': '%', 'v': '+', 'b': '&', 'e': '"', 'I': "'"}
# I'm not sure what the real limit is, but the text posted at
# https://github.com/GLolol/PyLink/issues/378 suggests 427 characters.
# https://github.com/unrealircd/unrealircd/blob/4cad9cb/src/modules/m_server.c#L1260 may
# also help. (but why BUFSIZE-*80*?) -GL
S2S_BUFSIZE = 427
class UnrealProtocol(TS6BaseProtocol): class UnrealProtocol(TS6BaseProtocol):
def __init__(self, irc): def __init__(self, irc):
super().__init__(irc) super().__init__(irc)
@ -124,14 +132,8 @@ class UnrealProtocol(TS6BaseProtocol):
Example uses: Example uses:
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')]) sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)]) sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
Note that for UnrealIRCd, no mode data is sent in an SJOIN command, only
The channel name, TS, and user list.
""" """
# <- :001 SJOIN 1444361345 #test :001DJ1O02 # <- :001 SJOIN 1444361345 #test :*@+1JJAAAAAB %2JJAAAA4C 1JJAAAADS
# The nicklist consists of users joining the channel, with status prefixes for
# their status ('@+', '@', '+' or ''), for example:
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'.
channel = self.irc.toLower(channel) channel = self.irc.toLower(channel)
server = server or self.irc.sid server = server or self.irc.sid
assert users, "sjoin: No users sent?" assert users, "sjoin: No users sent?"
@ -142,7 +144,7 @@ class UnrealProtocol(TS6BaseProtocol):
orig_ts = self.irc.channels[channel].ts orig_ts = self.irc.channels[channel].ts
ts = ts or orig_ts ts = ts or orig_ts
uids = [] uids = []
namelist = [] itemlist = []
for userpair in users: for userpair in users:
assert len(userpair) == 2, "Incorrect format of userpair: %r" % userpair assert len(userpair) == 2, "Incorrect format of userpair: %r" % userpair
@ -151,13 +153,12 @@ class UnrealProtocol(TS6BaseProtocol):
# Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~, # Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~,
# and +a is ~ instead of &. # and +a is ~ instead of &.
# &, ", and ' are used for bursting bans. # &, ", and ' are used for bursting bans.
sjoin_prefixes = {'q': '*', 'a': '~', 'o': '@', 'h': '%', 'v': '+'} prefixchars = ''.join([SJOIN_PREFIXES.get(prefix, '') for prefix in prefixes])
prefixchars = ''.join([sjoin_prefixes.get(prefix, '') for prefix in prefixes])
if prefixchars: if prefixchars:
changedmodes |= {('+%s' % prefix, user) for prefix in prefixes} changedmodes |= {('+%s' % prefix, user) for prefix in prefixes}
namelist.append(prefixchars+user) itemlist.append(prefixchars+user)
uids.append(user) uids.append(user)
try: try:
@ -165,15 +166,35 @@ class UnrealProtocol(TS6BaseProtocol):
except KeyError: # Not initialized yet? except KeyError: # Not initialized yet?
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user) log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
namelist = ' '.join(namelist) # Track simple modes separately.
simplemodes = set()
for modepair in modes:
if modepair[0][-1] in self.irc.cmodes['*A']:
# Bans, exempts, invex get expanded to forms like "&*!*@some.host" in SJOIN.
self._send(server, "SJOIN {ts} {channel} :{users}".format( if (modepair[0][-1], modepair[1]) in self.irc.channels[channel].modes:
ts=ts, users=namelist, channel=channel)) # Mode is already set; skip it.
continue
# Burst modes separately. No really, this is what I see UnrealIRCd do! It sends sjoin_prefix = SJOIN_PREFIXES.get(modepair[0][-1])
# JOINs on burst and then MODE! if sjoin_prefix:
itemlist.append(sjoin_prefix+modepair[1])
else:
simplemodes.add(modepair)
# Store the part of the SJOIN that we may reuse due to line wrapping (i.e. the sjoin
# "prefix")
sjoin_prefix = "SJOIN {ts} {channel}".format(ts=ts, channel=channel)
# Modes are optional; add them if they exist
if modes: if modes:
self.mode(server, channel, modes, ts=ts) sjoin_prefix += " %s" % self.irc.joinModes(simplemodes)
sjoin_prefix += " :"
# Wrap arguments to the max supported S2S line length to prevent cutoff
# (https://github.com/GLolol/PyLink/issues/378)
for line in utils.wrapArguments(sjoin_prefix, itemlist, S2S_BUFSIZE):
self._send(server, line)
self.irc.channels[channel].users.update(uids) self.irc.channels[channel].users.update(uids)
@ -540,6 +561,7 @@ class UnrealProtocol(TS6BaseProtocol):
def handle_sjoin(self, numeric, command, args): def handle_sjoin(self, numeric, command, args):
"""Handles the UnrealIRCd SJOIN command.""" """Handles the UnrealIRCd SJOIN command."""
# <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC # <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC
# <- :001 SJOIN 1483250129 #services +nt :+001OR9V02 @*~001DH6901 &*!*@test "*!*@blah.blah '*!*@yes.no
channel = self.irc.toLower(args[1]) channel = self.irc.toLower(args[1])
chandata = self.irc.channels[channel].deepcopy() chandata = self.irc.channels[channel].deepcopy()
userlist = args[-1].split() userlist = args[-1].split()
@ -548,8 +570,10 @@ class UnrealProtocol(TS6BaseProtocol):
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel) log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
modestring = '' modestring = ''
# FIXME: Implement edge-case mode conflict handling as documented here: # FIXME: Implement edge-case mode conflict handling as documented here:
# https://www.unrealircd.org/files/docs/technical/serverprotocol.html#S5_1 # https://www.unrealircd.org/files/docs/technical/serverprotocol.html#S5_1
changedmodes = set() changedmodes = set()
try: try:
if args[2].startswith('+'): if args[2].startswith('+'):