3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-17 23:52:49 +01:00

relay: move iteration over all networks into a wrapper function

First part of #471.
This commit is contained in:
James Lu 2017-07-10 23:09:00 -07:00
parent a43076e815
commit f203abdeb0

View File

@ -611,9 +611,23 @@ def is_relay_client(irc, user):
return False
isRelayClient = is_relay_client
def iterate_all(origirc, func, extra_args=(), kwargs=None):
"""
Runs the given function 'func' on all connected networks. 'func' must take at least two arguments: the original IRCNetwork object and the remote IRCNetwork object.
"""
if kwargs is None:
kwargs = {}
for name, remoteirc in world.networkobjects.copy().items():
if name == origirc.name or not remoteirc.connected.is_set():
# Don't relay things to their source network...
continue
func(origirc, remoteirc, *extra_args, **kwargs)
### EVENT HANDLER INTERNALS
def relay_joins(irc, channel, users, ts, burst=True):
def relay_joins(irc, channel, users, ts, **kwargs):
"""
Relays one or more users' joins from a channel to its relay links.
"""
@ -623,17 +637,14 @@ def relay_joins(irc, channel, users, ts, burst=True):
log.debug('(%s) relay: resetting too low TS value of %s on %s to %s', irc.name, ts, users, current_ts)
ts = current_ts
for name, remoteirc in world.networkobjects.copy().items():
def _relay_joins_loop(irc, remoteirc, channel, users, ts, burst=True):
queued_users = []
if name == irc.name or not remoteirc.connected.is_set():
# Don't relay things to their source network...
continue
remotechan = get_remote_channel(irc, remoteirc, channel)
if remotechan is None:
# If there is no link on the current network for the channel in question,
# just skip it
continue
# just skip it.
return
for user in users.copy():
if is_relay_client(irc, user):
@ -680,15 +691,13 @@ def relay_joins(irc, channel, users, ts, burst=True):
remoteirc.call_hooks([rsid, 'PYLINK_RELAY_JOIN', {'channel': remotechan, 'users': [u[-1] for u in queued_users]}])
def relay_part(irc, channel, user):
iterate_all(irc, _relay_joins_loop, extra_args=(channel, users, ts), kwargs=kwargs)
def relay_part(irc, *args, **kwargs):
"""
Relays a user part from a channel to its relay links, as part of a channel delink.
"""
for name, remoteirc in world.networkobjects.copy().items():
if name == irc.name or not remoteirc.connected.is_set():
# Don't relay things to their source network...
continue
def _relay_part_loop(irc, remoteirc, channel, user):
remotechan = get_remote_channel(irc, remoteirc, channel)
log.debug('(%s) relay.relay_part: looking for %s/%s on %s', irc.name, user, irc.name, remoteirc.name)
log.debug('(%s) relay.relay_part: remotechan found as %s', irc.name, remotechan)
@ -699,7 +708,7 @@ def relay_part(irc, channel, user):
if remotechan is None or remoteuser is None:
# If there is no relay channel on the target network, or the relay
# user doesn't exist, just do nothing.
continue
return
# Part the relay client with the channel delinked message.
remoteirc.part(remoteuser, remotechan, 'Channel delinked.')
@ -709,6 +718,7 @@ def relay_part(irc, channel, user):
remoteirc.quit(remoteuser, 'Left all shared channels.')
del relayusers[(irc.name, user)][remoteirc.name]
iterate_all(irc, _relay_part_loop, extra_args=args, kwargs=kwargs)
whitelisted_cmodes = {'admin', 'allowinvite', 'autoop', 'ban', 'banexception',
'blockcolor', 'halfop', 'invex', 'inviteonly', 'key',
@ -1069,14 +1079,13 @@ def handle_messages(irc, numeric, command, args):
target = '#' + target
if utils.isChannel(target):
for name, remoteirc in world.networkobjects.copy().items():
def _handle_messages_loop(irc, remoteirc, numeric, command, args, notice, target, text):
real_target = get_remote_channel(irc, remoteirc, target)
# Don't relay anything back to the source net, or to disconnected networks
# and networks without a relay for this channel.
if irc.name == name or (not remoteirc.connected.is_set()) or (not real_target) \
or (not irc.connected.is_set()):
continue
if (not real_target) or (not irc.connected.is_set()):
return
user = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
@ -1085,7 +1094,7 @@ def handle_messages(irc, numeric, command, args):
conf.conf.get('relay', {}).get('accept_weird_senders', True))):
log.debug("(%s) Dropping message for %s from user-less sender %s", irc.name,
real_target, numeric)
continue
return
# No relay clone exists for the sender; route the message through our
# main client (or SID for notices).
@ -1104,14 +1113,14 @@ def handle_messages(irc, numeric, command, args):
user = get_remote_sid(remoteirc, irc, spawn_if_missing=False) \
if notice else remoteirc.pseudoclient.uid
if not user:
continue
return
except AttributeError:
# Remote main client hasn't spawned yet. Drop the message.
continue
return
else:
if remoteirc.pseudoclient.uid not in remoteirc.users:
# Remote UID is ghosted, drop message.
continue
return
else:
real_text = text
@ -1131,7 +1140,8 @@ def handle_messages(irc, numeric, command, args):
log.warning("(%s) relay: Relay client %s on %s was killed while "
"trying to send a message through it!", irc.name,
remoteirc.name, user)
continue
return
iterate_all(irc, _handle_messages_loop, extra_args=(numeric, command, args, notice, target, text))
else:
# Get the real user that the PM was meant for
@ -1196,17 +1206,21 @@ def handle_kick(irc, source, command, args):
return
origuser = get_orig_user(irc, target)
for name, remoteirc in world.networkobjects.copy().items():
if irc.name == name or not remoteirc.connected.is_set():
continue
def _handle_kick_loop(irc, remoteirc, source, command, args):
remotechan = get_remote_channel(irc, remoteirc, channel)
name = remoteirc.name
log.debug('(%s) relay.handle_kick: remotechan for %s on %s is %s', irc.name, channel, name, remotechan)
if remotechan is None:
continue
return
real_kicker = get_remote_user(irc, remoteirc, kicker, spawn_if_missing=False)
log.debug('(%s) relay.handle_kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker)
if not is_relay_client(irc, target):
log.debug('(%s) relay.handle_kick: target %s is NOT an internal client', irc.name, target)
# Both the target and kicker are external clients; i.e.
# they originate from the same network. We won't have
# to filter this; the uplink IRCd will handle it appropriately,
@ -1216,6 +1230,7 @@ def handle_kick(irc, source, command, args):
else:
log.debug('(%s) relay.handle_kick: target %s is an internal client, going to look up the real user', irc.name, target)
real_target = get_orig_user(irc, target, targetirc=remoteirc)
if not check_claim(irc, channel, kicker):
log.debug('(%s) relay.handle_kick: kicker %s is not opped... We should rejoin the target user %s', irc.name, kicker, real_target)
# Home network is not in the channel's claim AND the kicker is not
@ -1223,8 +1238,10 @@ def handle_kick(irc, source, command, args):
# TODO: make the check slightly more advanced: i.e. halfops can't
# kick ops, admins can't kick owners, etc.
modes = get_prefix_modes(remoteirc, irc, remotechan, real_target)
# Join the kicked client back with its respective modes.
irc.sjoin(irc.sid, channel, [(modes, target)])
if kicker in irc.users:
log.info('(%s) relay: Blocked KICK (reason %r) from %s/%s to relay client %s on %s.',
irc.name, args['text'], irc.users[source].nick, irc.name,
@ -1239,7 +1256,8 @@ def handle_kick(irc, source, command, args):
return
if not real_target:
continue
return
# Propogate the kick!
if real_kicker:
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan,real_kicker, kicker, irc.name)
@ -1249,6 +1267,7 @@ def handle_kick(irc, source, command, args):
# common channels with the target relay network.
rsid = get_remote_sid(remoteirc, irc)
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan, rsid, kicker, irc.name)
if not irc.has_cap('can-spawn-clients'):
# Special case for clientbot: no kick prefixes are needed.
text = args['text']
@ -1261,6 +1280,7 @@ def handle_kick(irc, source, command, args):
text = "(%s/%s) %s" % (kname, irc.name, args['text'])
except AttributeError:
text = "(<unknown kicker>@%s) %s" % (irc.name, args['text'])
rsid = rsid or remoteirc.sid # Fall back to the main PyLink SID if get_remote_sid() fails
remoteirc.kick(rsid, remotechan, real_target, text)
@ -1269,6 +1289,8 @@ def handle_kick(irc, source, command, args):
del relayusers[(irc.name, target)][remoteirc.name]
remoteirc.quit(real_target, 'Left all shared channels.')
iterate_all(irc, _handle_kick_loop, extra_args=(source, command, args))
if origuser and not irc.users[target].channels:
del relayusers[origuser][irc.name]
irc.quit(target, 'Left all shared channels.')
@ -1305,13 +1327,9 @@ for c in ('CHGHOST', 'CHGNAME', 'CHGIDENT'):
utils.add_hook(handle_chgclient, c)
def handle_mode(irc, numeric, command, args):
target = args['target']
modes = args['modes']
for name, remoteirc in world.networkobjects.copy().items():
if irc.name == name or not remoteirc.connected.is_set():
continue
def _handle_mode_loop(irc, remoteirc, numeric, command, args):
target = args['target']
modes = args['modes']
if utils.isChannel(target):
# Use the old state of the channel to check for CLAIM access.
oldchan = args.get('channeldata')
@ -1335,7 +1353,7 @@ def handle_mode(irc, numeric, command, args):
irc.name, modes, reversed_modes)
if reversed_modes:
irc.mode(irc.sid, target, reversed_modes)
break
return
else:
# Set hideoper on remote opers, to prevent inflating
@ -1353,6 +1371,7 @@ def handle_mode(irc, numeric, command, args):
if remoteuser and modes:
remoteirc.mode(remoteuser, remoteuser, modes)
iterate_all(irc, _handle_mode_loop, extra_args=(numeric, command, args))
utils.add_hook(handle_mode, 'MODE')
@ -1360,15 +1379,19 @@ def handle_topic(irc, numeric, command, args):
channel = args['channel']
oldtopic = args.get('oldtopic')
topic = args['text']
if check_claim(irc, channel, numeric):
for name, remoteirc in world.networkobjects.copy().items():
if irc.name == name or not remoteirc.connected.is_set():
continue
def _handle_topic_loop(irc, remoteirc, numeric, command, args):
channel = args['channel']
oldtopic = args.get('oldtopic')
topic = args['text']
remotechan = get_remote_channel(irc, remoteirc, channel)
# Don't send if the remote topic is the same as ours.
if remotechan is None or topic == remoteirc.channels[remotechan].topic:
continue
return
# This might originate from a server too.
remoteuser = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
if remoteuser:
@ -1376,6 +1399,8 @@ def handle_topic(irc, numeric, command, args):
else:
rsid = get_remote_sid(remoteirc, irc)
remoteirc.topic_burst(rsid, remotechan, topic)
iterate_all(irc, _handle_topic_loop, extra_args=(numeric, command, args))
elif oldtopic: # Topic change blocked by claim.
irc.topic_burst(irc.sid, channel, oldtopic)
@ -1493,18 +1518,22 @@ def handle_disconnect(irc, numeric, command, args):
log.debug('(%s) Grabbing spawnlocks_servers[%s] from thread %r in function %r', irc.name, irc.name,
threading.current_thread().name, inspect.currentframe().f_code.co_name)
if spawnlocks_servers[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
for name, ircobj in world.networkobjects.copy().items():
def _handle_disconnect_loop(irc, remoteirc):
name = remoteirc.name
if name != irc.name:
try:
rsid = relayservers[name][irc.name]
except KeyError:
continue
return
else:
ircobj.proto.squit(ircobj.sid, rsid, text='Relay network lost connection.')
remoteirc.proto.squit(remoteirc.sid, rsid, text='Relay network lost connection.')
if irc.name in relayservers[name]:
del relayservers[name][irc.name]
iterate_all(irc, _handle_disconnect_loop)
try:
del relayservers[irc.name]
except KeyError: # Already removed; ignore.