mirror of
https://github.com/jlu5/PyLink.git
synced 2024-12-25 20:22:45 +01:00
Merge branch 'wip/rework-endburst' into devel
This commit is contained in:
commit
19bd3ec0b2
15
classes.py
15
classes.py
@ -468,16 +468,13 @@ class PyLinkNetworkCore(structures.DeprecatedAttributesObject, structures.CamelC
|
|||||||
if not userobj:
|
if not userobj:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Look for the "service" attribute in the User object, if one exists.
|
# Look for the "service" attribute in the User object,sname = userobj.service
|
||||||
try:
|
|
||||||
sname = userobj.service
|
|
||||||
# Warn if the service name we fetched isn't a registered service.
|
# Warn if the service name we fetched isn't a registered service.
|
||||||
if sname not in world.services.keys():
|
sname = userobj.service
|
||||||
|
if sname is not None and sname not in world.services.keys():
|
||||||
log.warning("(%s) User %s / %s had a service bot record to a service that doesn't "
|
log.warning("(%s) User %s / %s had a service bot record to a service that doesn't "
|
||||||
"exist (%s)!", self.name, uid, userobj.nick, sname)
|
"exist (%s)!", self.name, uid, userobj.nick, sname)
|
||||||
return world.services.get(sname)
|
return world.services.get(sname)
|
||||||
except AttributeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
structures._BLACKLISTED_COPY_TYPES.append(PyLinkNetworkCore)
|
structures._BLACKLISTED_COPY_TYPES.append(PyLinkNetworkCore)
|
||||||
|
|
||||||
@ -1596,6 +1593,9 @@ class User():
|
|||||||
# Cloaked host for IRCds that use it
|
# Cloaked host for IRCds that use it
|
||||||
self.cloaked_host = None
|
self.cloaked_host = None
|
||||||
|
|
||||||
|
# Stores service bot name if applicable
|
||||||
|
self.service = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'User(%s/%s)' % (self.uid, self.nick)
|
return 'User(%s/%s)' % (self.uid, self.nick)
|
||||||
IrcUser = User
|
IrcUser = User
|
||||||
@ -1619,6 +1619,9 @@ class Server():
|
|||||||
self.desc = desc
|
self.desc = desc
|
||||||
self._irc = irc
|
self._irc = irc
|
||||||
|
|
||||||
|
# Has the server finished bursting yet?
|
||||||
|
self.has_eob = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Server(%s)' % self.name
|
return 'Server(%s)' % self.name
|
||||||
|
|
||||||
|
@ -22,8 +22,17 @@ def spawn_service(irc, source, command, args):
|
|||||||
# Get the ServiceBot object.
|
# Get the ServiceBot object.
|
||||||
sbot = world.services[name]
|
sbot = world.services[name]
|
||||||
|
|
||||||
|
old_userobj = irc.users.get(sbot.uids.get(irc.name))
|
||||||
|
if old_userobj and old_userobj.service:
|
||||||
|
# A client already exists, so don't respawn it.
|
||||||
|
log.debug('(%s) spawn_service: Not respawning service %r as service client %r already exists.', irc.name, name,
|
||||||
|
irc.pseudoclient.nick)
|
||||||
|
return
|
||||||
|
|
||||||
if name == 'pylink' and irc.pseudoclient:
|
if name == 'pylink' and irc.pseudoclient:
|
||||||
# irc.pseudoclient already exists, for protocols like clientbot
|
# irc.pseudoclient already exists, reuse values from it but
|
||||||
|
# spawn a new client. This is used for protocols like Clientbot,
|
||||||
|
# so that they can override the main service nick, among other things.
|
||||||
log.debug('(%s) spawn_service: Using existing nick %r for service %r', irc.name, irc.pseudoclient.nick, name)
|
log.debug('(%s) spawn_service: Using existing nick %r for service %r', irc.name, irc.pseudoclient.nick, name)
|
||||||
userobj = irc.pseudoclient
|
userobj = irc.pseudoclient
|
||||||
userobj.opertype = "PyLink Service"
|
userobj.opertype = "PyLink Service"
|
||||||
|
@ -217,7 +217,7 @@ def get_prefix_modes(irc, remoteirc, channel, user, mlist=None):
|
|||||||
return modes
|
return modes
|
||||||
|
|
||||||
def spawn_relay_server(irc, remoteirc):
|
def spawn_relay_server(irc, remoteirc):
|
||||||
if irc.connected.wait(TCONDITION_TIMEOUT):
|
if irc.connected.is_set():
|
||||||
try:
|
try:
|
||||||
# ENDBURST is delayed by 3 secs on supported IRCds to prevent
|
# ENDBURST is delayed by 3 secs on supported IRCds to prevent
|
||||||
# triggering join-flood protection and the like.
|
# triggering join-flood protection and the like.
|
||||||
@ -358,7 +358,7 @@ def get_remote_user(irc, remoteirc, user, spawn_if_missing=True, times_tagged=0)
|
|||||||
spawning one if it doesn't exist and spawn_if_missing is True."""
|
spawning one if it doesn't exist and spawn_if_missing is True."""
|
||||||
|
|
||||||
# Wait until the network is working before trying to spawn anything.
|
# Wait until the network is working before trying to spawn anything.
|
||||||
if irc.connected.wait(TCONDITION_TIMEOUT):
|
if irc.connected.is_set():
|
||||||
# Don't spawn clones for registered service bots.
|
# Don't spawn clones for registered service bots.
|
||||||
sbot = irc.get_service_bot(user)
|
sbot = irc.get_service_bot(user)
|
||||||
if sbot:
|
if sbot:
|
||||||
|
@ -633,9 +633,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
self.send(line)
|
self.send(line)
|
||||||
|
|
||||||
# Virtual endburst hook.
|
# Virtual endburst hook.
|
||||||
self.connected.set() # Note, this should always be set before sending ENDBURST
|
self.connected.set() # Note, this should always be set before the actual ENDBURST hook
|
||||||
if not self.has_eob:
|
self.servers[self.uplink].has_eob = True
|
||||||
self.has_eob = True
|
|
||||||
return {'parse_as': 'ENDBURST'}
|
return {'parse_as': 'ENDBURST'}
|
||||||
|
|
||||||
handle_422 = handle_376
|
handle_422 = handle_376
|
||||||
|
@ -17,13 +17,11 @@ class HybridProtocol(TS6Protocol):
|
|||||||
self.casemapping = 'ascii'
|
self.casemapping = 'ascii'
|
||||||
self.caps = {}
|
self.caps = {}
|
||||||
self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC', 'SJOIN': 'JOIN'}
|
self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC', 'SJOIN': 'JOIN'}
|
||||||
self.has_eob = False
|
|
||||||
self.protocol_caps -= {'slash-in-hosts'}
|
self.protocol_caps -= {'slash-in-hosts'}
|
||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
|
||||||
f = self.send
|
f = self.send
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
||||||
@ -227,11 +225,15 @@ class HybridProtocol(TS6Protocol):
|
|||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_eob(self, numeric, command, args):
|
def handle_eob(self, numeric, command, args):
|
||||||
log.debug('(%s) end of burst received', self.name)
|
"""EOB (end-of-burst) handler."""
|
||||||
if not self.has_eob: # Only call ENDBURST hooks if we haven't already.
|
log.debug('(%s) end of burst received from %s', self.name, numeric)
|
||||||
return {}
|
if not self.servers[numeric].has_eob:
|
||||||
|
# Don't fight with TS6's generic PING-as-EOB
|
||||||
|
self.servers[numeric].has_eob = True
|
||||||
|
|
||||||
self.has_eob = True
|
if numeric == self.uplink:
|
||||||
|
self.connected.set()
|
||||||
|
return {}
|
||||||
|
|
||||||
def handle_svsmode(self, numeric, command, args):
|
def handle_svsmode(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
|
@ -520,9 +520,6 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
log.debug('(%s) self.prefixmodes set to %r', self.name,
|
log.debug('(%s) self.prefixmodes set to %r', self.name,
|
||||||
self.prefixmodes)
|
self.prefixmodes)
|
||||||
|
|
||||||
# Finally, set the irc.connected (protocol negotiation complete)
|
|
||||||
# state to True.
|
|
||||||
self.connected.set()
|
|
||||||
elif args[0] == 'MODSUPPORT':
|
elif args[0] == 'MODSUPPORT':
|
||||||
# <- CAPAB MODSUPPORT :m_alltime.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_fullversion.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_opmoderated.so m_sajoin.so m_sanick.so m_sapart.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
|
# <- CAPAB MODSUPPORT :m_alltime.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_fullversion.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_opmoderated.so m_sajoin.so m_sanick.so m_sapart.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
|
||||||
self.modsupport = args[-1].split()
|
self.modsupport = args[-1].split()
|
||||||
@ -716,6 +713,9 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def handle_endburst(self, numeric, command, args):
|
def handle_endburst(self, numeric, command, args):
|
||||||
"""ENDBURST handler; sends a hook with empty contents."""
|
"""ENDBURST handler; sends a hook with empty contents."""
|
||||||
|
self.servers[numeric].has_eob = True
|
||||||
|
if numeric == self.uplink:
|
||||||
|
self.connected.set()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_away(self, numeric, command, args):
|
def handle_away(self, numeric, command, args):
|
||||||
|
@ -229,26 +229,32 @@ class NgIRCdProtocol(IRCS2SProtocol):
|
|||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
|
|
||||||
njoin_prefix = ':%s NJOIN %s :' % (self._expandPUID(self.sid), channel)
|
njoin_prefix = ':%s NJOIN %s :' % (self._expandPUID(server), channel)
|
||||||
# Format the user list into strings such as @user1, +user2, user3, etc.
|
# Format the user list into strings such as @user1, +user2, user3, etc.
|
||||||
nicks_to_send = ['%s%s' % (''.join(self.prefixmodes[modechar] for modechar in userpair[0] if modechar in self.prefixmodes),
|
nicks_to_send = []
|
||||||
self._expandPUID(userpair[1])) for userpair in users]
|
for userpair in users:
|
||||||
|
prefixes, uid = userpair
|
||||||
|
|
||||||
|
if uid not in self.users:
|
||||||
|
log.warning('(%s) Trying to NJOIN missing user %s?', self.name, uid)
|
||||||
|
continue
|
||||||
|
elif uid in self._channels[channel].users:
|
||||||
|
# Don't rejoin users already in the channel, this causes errors with ngIRCd.
|
||||||
|
continue
|
||||||
|
|
||||||
|
self._channels[channel].users.add(uid)
|
||||||
|
self.users[uid].channels.add(channel)
|
||||||
|
|
||||||
|
self.apply_modes(channel, (('+%s' % prefix, uid) for prefix in userpair[0]))
|
||||||
|
|
||||||
|
nicks_to_send.append(''.join(self.prefixmodes[modechar] for modechar in userpair[0]) + \
|
||||||
|
self._expandPUID(userpair[1]))
|
||||||
|
|
||||||
|
if nicks_to_send:
|
||||||
# Use 13 args max per line: this is equal to the max of 15 minus the command name and target channel.
|
# Use 13 args max per line: this is equal to the max of 15 minus the command name and target channel.
|
||||||
for message in utils.wrapArguments(njoin_prefix, nicks_to_send, self.S2S_BUFSIZE, separator=',', max_args_per_line=13):
|
for message in utils.wrapArguments(njoin_prefix, nicks_to_send, self.S2S_BUFSIZE, separator=',', max_args_per_line=13):
|
||||||
self.send(message)
|
self.send(message)
|
||||||
|
|
||||||
# Add the affected users to our state.
|
|
||||||
for userpair in users:
|
|
||||||
uid = userpair[1]
|
|
||||||
self._channels[channel].users.add(uid)
|
|
||||||
try:
|
|
||||||
self.users[uid].channels.add(channel)
|
|
||||||
except KeyError: # Not initialized yet?
|
|
||||||
log.warning("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, uid)
|
|
||||||
|
|
||||||
self.apply_modes(channel, (('+%s' % prefix, uid) for prefix in userpair[0]))
|
|
||||||
|
|
||||||
if modes:
|
if modes:
|
||||||
# Burst modes separately if there are any.
|
# Burst modes separately if there are any.
|
||||||
log.debug("(%s) sjoin: bursting modes %r for channel %r now", self.name, modes, channel)
|
log.debug("(%s) sjoin: bursting modes %r for channel %r now", self.name, modes, channel)
|
||||||
@ -504,12 +510,16 @@ class NgIRCdProtocol(IRCS2SProtocol):
|
|||||||
assert 'IRC+' in args[1], "Linking to non-ngIRCd server using this protocol module is not supported"
|
assert 'IRC+' in args[1], "Linking to non-ngIRCd server using this protocol module is not supported"
|
||||||
|
|
||||||
def handle_ping(self, source, command, args):
|
def handle_ping(self, source, command, args):
|
||||||
if source == self.uplink:
|
"""
|
||||||
|
Handles incoming PINGs (and implicit end of burst).
|
||||||
|
"""
|
||||||
self._send_with_prefix(self.sid, 'PONG %s :%s' % (self._expandPUID(self.sid), args[-1]), queue=False)
|
self._send_with_prefix(self.sid, 'PONG %s :%s' % (self._expandPUID(self.sid), args[-1]), queue=False)
|
||||||
|
|
||||||
if not self.has_eob:
|
if not self.servers[source].has_eob:
|
||||||
# Treat the first PING we receive as end of burst.
|
# Treat the first PING we receive as end of burst.
|
||||||
self.has_eob = True
|
self.servers[source].has_eob = True
|
||||||
|
|
||||||
|
if source == self.uplink:
|
||||||
self.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
# Return the endburst hook.
|
# Return the endburst hook.
|
||||||
|
@ -824,7 +824,6 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
self.send('SERVER %s 1 %s %s J10 %s]]] +s6 :%s' % (name, ts, ts, sid, desc))
|
self.send('SERVER %s 1 %s %s J10 %s]]] +s6 :%s' % (name, ts, ts, sid, desc))
|
||||||
self._send_with_prefix(sid, "EB")
|
self._send_with_prefix(sid, "EB")
|
||||||
self.connected.set()
|
|
||||||
|
|
||||||
def handle_server(self, source, command, args):
|
def handle_server(self, source, command, args):
|
||||||
"""Handles incoming server introductions."""
|
"""Handles incoming server introductions."""
|
||||||
@ -1110,14 +1109,18 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
return {'channel': channel, 'users': [source], 'modes':
|
return {'channel': channel, 'users': [source], 'modes':
|
||||||
self._channels[channel].modes, 'ts': ts or int(time.time())}
|
self._channels[channel].modes, 'ts': ts or int(time.time())}
|
||||||
|
|
||||||
handle_create = handle_join
|
handle_create = handle_join
|
||||||
|
|
||||||
def handle_end_of_burst(self, source, command, args):
|
def handle_end_of_burst(self, source, command, args):
|
||||||
"""Handles end of burst from our uplink."""
|
"""Handles end of burst from servers."""
|
||||||
|
|
||||||
# Send EOB acknowledgement; this is required by the P10 specification,
|
# Send EOB acknowledgement; this is required by the P10 specification,
|
||||||
# and needed if we want to be able to receive channel messages, etc.
|
# and needed if we want to be able to receive channel messages, etc.
|
||||||
if source == self.uplink:
|
if source == self.uplink:
|
||||||
self._send_with_prefix(self.sid, 'EA')
|
self._send_with_prefix(self.sid, 'EA')
|
||||||
|
self.connected.set()
|
||||||
|
|
||||||
|
self.servers[source].has_eob = True
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_kick(self, source, command, args):
|
def handle_kick(self, source, command, args):
|
||||||
|
@ -20,9 +20,6 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE',
|
self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE',
|
||||||
'EUID': 'UID', 'RSFNC': 'SVSNICK', 'ETB': 'TOPIC', 'USERMODE': 'MODE'}
|
'EUID': 'UID', 'RSFNC': 'SVSNICK', 'ETB': 'TOPIC', 'USERMODE': 'MODE'}
|
||||||
|
|
||||||
# Track whether we've received end-of-burst from the uplink.
|
|
||||||
self.has_eob = False
|
|
||||||
|
|
||||||
self.required_caps = {'EUID', 'SAVE', 'TB', 'ENCAP', 'QS', 'CHW'}
|
self.required_caps = {'EUID', 'SAVE', 'TB', 'ENCAP', 'QS', 'CHW'}
|
||||||
|
|
||||||
# From ChatIRCd: https://github.com/ChatLounge/ChatIRCd/blob/master/doc/technical/ChatIRCd-extra.txt
|
# From ChatIRCd: https://github.com/ChatLounge/ChatIRCd/blob/master/doc/technical/ChatIRCd-extra.txt
|
||||||
@ -264,7 +261,6 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
|
||||||
|
|
||||||
f = self.send
|
f = self.send
|
||||||
|
|
||||||
@ -423,9 +419,6 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
if 'SERVICES' in caps:
|
if 'SERVICES' in caps:
|
||||||
self.cmodes['regonly'] = 'r'
|
self.cmodes['regonly'] = 'r'
|
||||||
|
|
||||||
log.debug('(%s) self.connected set!', self.name)
|
|
||||||
self.connected.set()
|
|
||||||
|
|
||||||
def handle_ping(self, source, command, args):
|
def handle_ping(self, source, command, args):
|
||||||
"""Handles incoming PING commands."""
|
"""Handles incoming PING commands."""
|
||||||
# PING:
|
# PING:
|
||||||
@ -445,10 +438,14 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
if self.is_internal_server(destination):
|
if self.is_internal_server(destination):
|
||||||
self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False)
|
self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False)
|
||||||
|
|
||||||
if destination == self.sid and not self.has_eob:
|
if not self.servers[source].has_eob:
|
||||||
# Charybdis' endburst is just sending a PING to the other server.
|
# TS6 endburst is just sending a PING to the other server.
|
||||||
# https://github.com/charybdis-ircd/charybdis/blob/dc336d1/modules/core/m_server.c#L484-L485
|
# https://github.com/charybdis-ircd/charybdis/blob/dc336d1/modules/core/m_server.c#L484-L485
|
||||||
self.has_eob = True
|
self.servers[source].has_eob = True
|
||||||
|
|
||||||
|
if source == self.uplink:
|
||||||
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
|
self.connected.set()
|
||||||
|
|
||||||
# Return the endburst hook.
|
# Return the endburst hook.
|
||||||
return {'parse_as': 'ENDBURST'}
|
return {'parse_as': 'ENDBURST'}
|
||||||
|
@ -379,6 +379,9 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def handle_eos(self, numeric, command, args):
|
def handle_eos(self, numeric, command, args):
|
||||||
"""EOS is used to denote end of burst."""
|
"""EOS is used to denote end of burst."""
|
||||||
|
self.servers[numeric].has_eob = True
|
||||||
|
if numeric == self.uplink:
|
||||||
|
self.connected.set()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_uid(self, numeric, command, args):
|
def handle_uid(self, numeric, command, args):
|
||||||
@ -482,9 +485,6 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
||||||
self.servers[numeric] = Server(self, None, sname, desc=sdesc)
|
self.servers[numeric] = Server(self, None, sname, desc=sdesc)
|
||||||
|
|
||||||
# Set irc.connected to True, meaning that protocol negotiation passed.
|
|
||||||
log.debug('(%s) self.connected set!', self.name)
|
|
||||||
self.connected.set()
|
|
||||||
else:
|
else:
|
||||||
# Legacy (non-SID) servers can still be introduced using the SERVER command.
|
# Legacy (non-SID) servers can still be introduced using the SERVER command.
|
||||||
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
||||||
|
Loading…
Reference in New Issue
Block a user