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

relay: cleanup, consistently include the function in log.debug calls

This commit is contained in:
James Lu 2016-02-27 18:36:20 -08:00
parent a740163cbe
commit 34ca973047

View File

@ -30,12 +30,17 @@ dbname = utils.getDatabaseName('pylinkrelay')
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
# Wait for all IRC objects to be created first. This prevents
# relay servers from being spawned too early (before server authentication), # relay servers from being spawned too early (before server authentication),
# which would break connections. # which would break connections.
world.started.wait(2) world.started.wait(2)
for chanpair, entrydata in db.items(): for chanpair, entrydata in db.items():
# Iterate over all the channels stored in our relay links DB.
network, channel = chanpair network, channel = chanpair
# Initialize each relay channel on their home network, and on every linked one too.
initializeChannel(irc, channel) initializeChannel(irc, channel)
for link in entrydata['links']: for link in entrydata['links']:
network, channel = link network, channel = link
@ -90,7 +95,7 @@ def normalizeNick(irc, netname, nick, separator=None, uid=''):
"""Creates a normalized nickname for the given nick suitable for """Creates a normalized nickname for the given nick suitable for
introduction to a remote network (as a relay client).""" introduction to a remote network (as a relay client)."""
separator = separator or irc.serverdata.get('separator') or "/" separator = separator or irc.serverdata.get('separator') or "/"
log.debug('(%s) normalizeNick: using %r as separator.', irc.name, separator) log.debug('(%s) relay.normalizeNick: using %r as separator.', irc.name, separator)
orig_nick = nick orig_nick = nick
protoname = irc.protoname protoname = irc.protoname
maxnicklen = irc.maxnicklen maxnicklen = irc.maxnicklen
@ -129,7 +134,7 @@ def normalizeNick(irc, netname, nick, separator=None, uid=''):
# the same client. # the same client.
while irc.nickToUid(nick) and irc.nickToUid(nick) != uid: while irc.nickToUid(nick) and irc.nickToUid(nick) != uid:
new_sep = separator + separator[-1] new_sep = separator + separator[-1]
log.debug('(%s) normalizeNick: nick %r is in use; using %r as new_sep.', irc.name, nick, new_sep) log.debug('(%s) relay.normalizeNick: nick %r is in use; using %r as new_sep.', irc.name, nick, new_sep)
nick = normalizeNick(irc, netname, orig_nick, separator=new_sep) nick = normalizeNick(irc, netname, orig_nick, separator=new_sep)
finalLength = len(nick) finalLength = len(nick)
assert finalLength <= maxnicklen, "Normalized nick %r went over max " \ assert finalLength <= maxnicklen, "Normalized nick %r went over max " \
@ -189,11 +194,18 @@ def getPrefixModes(irc, remoteirc, channel, user, mlist=None):
modes are processed and added to the channel state. modes are processed and added to the channel state.
""" """
modes = '' modes = ''
# Get the mapping of all the channel's prefix modes
mlist = mlist or irc.channels[channel].prefixmodes mlist = mlist or irc.channels[channel].prefixmodes
# Iterate over the the supported prefix modes for relay
for pmode in ('owner', 'admin', 'op', 'halfop', 'voice'): for pmode in ('owner', 'admin', 'op', 'halfop', 'voice'):
if pmode in remoteirc.cmodes: # Mode supported by IRCd if pmode in remoteirc.cmodes: # If the mode supported by IRCd
# Check if the caller is in the prefix modes list for that
# prefix.prefixmodes mapping looks like:
# {'ops': ['user1', 'user2'], 'voices': ['user3', 'user4'], ...}
userlist = mlist[pmode+'s'] userlist = mlist[pmode+'s']
log.debug('(%s) getPrefixModes: checking if %r is in %s list: %r', log.debug('(%s) relay.getPrefixModes: checking if %r is in %s list: %r',
irc.name, user, pmode, userlist) irc.name, user, pmode, userlist)
if user in userlist: if user in userlist:
modes += remoteirc.cmodes[pmode] modes += remoteirc.cmodes[pmode]
@ -321,7 +333,7 @@ def getOrigUser(irc, user, targetirc=None):
remoteuser = irc.users[user].remote remoteuser = irc.users[user].remote
except (AttributeError, KeyError): except (AttributeError, KeyError):
remoteuser = None remoteuser = None
log.debug('(%s) getOrigUser: remoteuser set to %r (looking up %s/%s).', log.debug('(%s) relay.getOrigUser: remoteuser set to %r (looking up %s/%s).',
irc.name, remoteuser, user, irc.name) irc.name, remoteuser, user, irc.name)
if remoteuser: if remoteuser:
# If targetirc is given, we'll return simply the UID of the user on the # If targetirc is given, we'll return simply the UID of the user on the
@ -336,7 +348,7 @@ def getOrigUser(irc, user, targetirc=None):
# Otherwise, use getRemoteUser to find our UID. # Otherwise, use getRemoteUser to find our UID.
res = getRemoteUser(sourceobj, targetirc, remoteuser[1], res = getRemoteUser(sourceobj, targetirc, remoteuser[1],
spawnIfMissing=False) spawnIfMissing=False)
log.debug('(%s) getOrigUser: targetirc found, getting %r as ' log.debug('(%s) relay.getOrigUser: targetirc found, getting %r as '
'remoteuser for %r (looking up %s/%s).', irc.name, res, 'remoteuser for %r (looking up %s/%s).', irc.name, res,
remoteuser[1], user, irc.name) remoteuser[1], user, irc.name)
return res return res
@ -373,33 +385,41 @@ def initializeChannel(irc, channel):
# We're initializing a relay that already exists. This can be done at # We're initializing a relay that already exists. This can be done at
# ENDBURST, or on the LINK command. # ENDBURST, or on the LINK command.
relay = getRelay((irc.name, channel)) relay = getRelay((irc.name, channel))
log.debug('(%s) initializeChannel being called on %s', irc.name, channel) log.debug('(%s) relay.initializeChannel being called on %s', irc.name, channel)
log.debug('(%s) initializeChannel: relay pair found to be %s', irc.name, relay) log.debug('(%s) relay.initializeChannel: relay pair found to be %s', irc.name, relay)
queued_users = [] queued_users = []
if relay: if relay:
all_links = db[relay]['links'].copy() all_links = db[relay]['links'].copy()
all_links.update((relay,)) all_links.update((relay,))
log.debug('(%s) initializeChannel: all_links: %s', irc.name, all_links) log.debug('(%s) relay.initializeChannel: all_links: %s', irc.name, all_links)
# Iterate over all the remote channels linked in this relay. # Iterate over all the remote channels linked in this relay.
for link in all_links: for link in all_links:
remotenet, remotechan = link remotenet, remotechan = link
if remotenet == irc.name: if remotenet == irc.name: # If the network is us, skip.
continue continue
remoteirc = world.networkobjects.get(remotenet) remoteirc = world.networkobjects.get(remotenet)
if remoteirc is None: if remoteirc is None:
# Remote network doesn't have an IRC object; e.g. it was removed
# from the config. Skip this.
continue continue
rc = remoteirc.channels[remotechan] rc = remoteirc.channels[remotechan]
if not (remoteirc.connected.is_set() and getRemoteChan(remoteirc, irc, remotechan)): if not (remoteirc.connected.is_set() and getRemoteChan(remoteirc, irc, remotechan)):
continue # They aren't connected, don't bother! continue # Remote network isn't connected.
# Join their (remote) users and set their modes. # Join their (remote) users and set their modes.
relayJoins(remoteirc, remotechan, rc.users, rc.ts) relayJoins(remoteirc, remotechan, rc.users, rc.ts)
topic = remoteirc.channels[remotechan].topic topic = remoteirc.channels[remotechan].topic
# Only update the topic if it's different from what we already have, # Only update the topic if it's different from what we already have,
# and topic bursting is complete. # and topic bursting is complete.
if remoteirc.channels[remotechan].topicset and topic != irc.channels[channel].topic: if remoteirc.channels[remotechan].topicset and topic != irc.channels[channel].topic:
irc.proto.topicBurst(getRemoteSid(irc, remoteirc), channel, topic) irc.proto.topicBurst(getRemoteSid(irc, remoteirc), channel, topic)
# Send our users and channel modes to the other nets # Send our users and channel modes to the other nets
log.debug('(%s) initializeChannel: joining our (%s) users: %s', irc.name, remotenet, irc.channels[channel].users) log.debug('(%s) relay.initializeChannel: joining our (%s) users: %s', irc.name, remotenet, irc.channels[channel].users)
relayJoins(irc, channel, irc.channels[channel].users, irc.channels[channel].ts) relayJoins(irc, channel, irc.channels[channel].users, irc.channels[channel].ts)
if irc.pseudoclient.uid not in irc.channels[channel].users: if irc.pseudoclient.uid not in irc.channels[channel].users:
irc.proto.join(irc.pseudoclient.uid, channel) irc.proto.join(irc.pseudoclient.uid, channel)
@ -457,27 +477,42 @@ def getSupportedUmodes(irc, remoteirc, modes):
"""Given a list of user modes, filters out all of those not supported by the """Given a list of user modes, filters out all of those not supported by the
remote network.""" remote network."""
supported_modes = [] supported_modes = []
# Iterate over all mode pairs.
for modepair in modes: for modepair in modes:
try: try:
# Get the prefix and the actual mode character (the prefix being + or -, or
# whether we're setting or unsetting a mode)
prefix, modechar = modepair[0] prefix, modechar = modepair[0]
except ValueError: except ValueError:
# If the prefix is missing, assume we're adding a mode.
modechar = modepair[0] modechar = modepair[0]
prefix = '+' prefix = '+'
# Get the mode argument.
arg = modepair[1] arg = modepair[1]
# Iterate over all supported user modes for the current network.
for name, m in irc.umodes.items(): for name, m in irc.umodes.items():
supported_char = None supported_char = None
# Mode character matches one in our list, so set that named mode
# as the one we're trying to set. Then, look up that named mode
# in the supported modes list for the TARGET network, and set that
# mode character as the one we're setting, if it exists.
if modechar == m: if modechar == m:
if name not in whitelisted_umodes: if name not in whitelisted_umodes:
log.debug("(%s) getSupportedUmodes: skipping mode (%r, %r) because " log.debug("(%s) relay.getSupportedUmodes: skipping mode (%r, %r) because "
"it isn't a whitelisted (safe) mode for relay.", "it isn't a whitelisted (safe) mode for relay.",
irc.name, modechar, arg) irc.name, modechar, arg)
break break
supported_char = remoteirc.umodes.get(name) supported_char = remoteirc.umodes.get(name)
if supported_char: if supported_char:
supported_modes.append((prefix+supported_char, arg)) supported_modes.append((prefix+supported_char, arg))
break break
else: else:
log.debug("(%s) getSupportedUmodes: skipping mode (%r, %r) because " log.debug("(%s) relay.getSupportedUmodes: skipping mode (%r, %r) because "
"the remote network (%s)'s IRCd (%s) doesn't support it.", "the remote network (%s)'s IRCd (%s) doesn't support it.",
irc.name, modechar, arg, remoteirc.name, irc.name, modechar, arg, remoteirc.name,
remoteirc.protoname) remoteirc.protoname)
@ -499,59 +534,83 @@ def isRelayClient(irc, user):
### EVENT HANDLER INTERNALS ### EVENT HANDLER INTERNALS
def relayJoins(irc, channel, users, ts, burst=True): def relayJoins(irc, channel, users, ts, burst=True):
"""
Relays one or more users' joins from a channel to its relay links.
"""
for name, remoteirc in world.networkobjects.copy().items(): for name, remoteirc in world.networkobjects.copy().items():
queued_users = [] queued_users = []
if name == irc.name or not remoteirc.connected.is_set(): if name == irc.name or not remoteirc.connected.is_set():
# Don't relay things to their source network... # Don't relay things to their source network...
continue continue
remotechan = getRemoteChan(irc, remoteirc, channel) remotechan = getRemoteChan(irc, remoteirc, channel)
if remotechan is None: if remotechan is None:
# If there is no link on our network for the user, don't # If there is no link on the current network for the channel in question,
# bother spawning it. # just skip it
continue continue
log.debug('(%s) relayJoins: got %r for users', irc.name, users)
log.debug('(%s) relay.relayJoins: got %r for users', irc.name, users)
for user in users.copy(): for user in users.copy():
if isRelayClient(irc, user): if isRelayClient(irc, user):
# Don't clone relay clients; that'll cause some bad, bad # Don't clone relay clients; that'll cause bad infinite loops.
# things to happen.
continue continue
log.debug('Okay, spawning %s/%s everywhere', user, irc.name)
assert user in irc.users, "(%s) How is this possible? %r isn't in our user database." % (irc.name, user) assert user in irc.users, "(%s) relay.relayJoins: How is this possible? %r isn't in our user database." % (irc.name, user)
u = getRemoteUser(irc, remoteirc, user) u = getRemoteUser(irc, remoteirc, user)
# Only join users if they aren't already joined. This prevents op floods
# on charybdis from all the SJOINing.
if u not in remoteirc.channels[remotechan].users: if u not in remoteirc.channels[remotechan].users:
# Note: only join users if they aren't already joined. This prevents op floods
# on charybdis from repeated SJOINs sent for one user.
# Fetch the known channel TS and all the prefix modes for each user. This ensures
# the different sides of the relay are merged properly.
ts = irc.channels[channel].ts ts = irc.channels[channel].ts
prefixes = getPrefixModes(irc, remoteirc, channel, user) prefixes = getPrefixModes(irc, remoteirc, channel, user)
# proto.sjoin() takes its users as a list of (prefix mode characters, UID) pairs.
userpair = (prefixes, u) userpair = (prefixes, u)
queued_users.append(userpair) queued_users.append(userpair)
log.debug('(%s) relayJoins: joining %s to %s%s', irc.name, userpair, remoteirc.name, remotechan)
else:
log.debug('(%s) relayJoins: not joining %s to %s%s; they\'re already there!', irc.name,
u, remoteirc.name, remotechan)
if queued_users: if queued_users:
# Burst was explicitly given, or we're trying to join multiple # Look at whether we should relay this join as a regular JOIN, or a SJOIN.
# users/someone with a prefix. # SJOIN will be used if either the amount of users to join is > 1, or there are modes
# to be set on the joining user.
if burst or len(queued_users) > 1 or queued_users[0][0]: if burst or len(queued_users) > 1 or queued_users[0][0]:
# Send the SJOIN from the relay subserver on the target network.
rsid = getRemoteSid(remoteirc, irc) rsid = getRemoteSid(remoteirc, irc)
remoteirc.proto.sjoin(rsid, remotechan, queued_users, ts=ts) remoteirc.proto.sjoin(rsid, remotechan, queued_users, ts=ts)
relayModes(irc, remoteirc, getRemoteSid(irc, remoteirc), channel, irc.channels[channel].modes) relayModes(irc, remoteirc, getRemoteSid(irc, remoteirc), channel, irc.channels[channel].modes)
else: else:
# A regular JOIN only needs the user and the channel. TS, source SID, etc., can all be omitted.
remoteirc.proto.join(queued_users[0][1], remotechan) remoteirc.proto.join(queued_users[0][1], remotechan)
def relayPart(irc, channel, user): def relayPart(irc, channel, user):
"""
Relays a user part from a channel to its relay links, as part of a channel delink.
"""
for name, remoteirc in world.networkobjects.items(): for name, remoteirc in world.networkobjects.items():
if name == irc.name or not remoteirc.connected.is_set(): if name == irc.name or not remoteirc.connected.is_set():
# Don't relay things to their source network... # Don't relay things to their source network...
continue continue
remotechan = getRemoteChan(irc, remoteirc, channel) remotechan = getRemoteChan(irc, remoteirc, channel)
log.debug('(%s) relayPart: looking for %s/%s on %s', irc.name, user, irc.name, remoteirc.name) log.debug('(%s) relay.relayPart: looking for %s/%s on %s', irc.name, user, irc.name, remoteirc.name)
log.debug('(%s) relayPart: remotechan found as %s', irc.name, remotechan) log.debug('(%s) relay.relayPart: remotechan found as %s', irc.name, remotechan)
remoteuser = getRemoteUser(irc, remoteirc, user, spawnIfMissing=False) remoteuser = getRemoteUser(irc, remoteirc, user, spawnIfMissing=False)
log.debug('(%s) relayPart: remoteuser for %s/%s found as %s', irc.name, user, irc.name, remoteuser) log.debug('(%s) relay.relayPart: remoteuser for %s/%s found as %s', irc.name, user, irc.name, remoteuser)
if remotechan is None or remoteuser is None: 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 continue
# Part the relay client with the channel delinked message.
remoteirc.proto.part(remoteuser, remotechan, 'Channel delinked.') remoteirc.proto.part(remoteuser, remotechan, 'Channel delinked.')
# If the relay client no longer has any channels, quit them to prevent inflating /lusers.
if isRelayClient(remoteirc, remoteuser) and not remoteirc.users[remoteuser].channels: if isRelayClient(remoteirc, remoteuser) and not remoteirc.users[remoteuser].channels:
remoteirc.proto.quit(remoteuser, 'Left all shared channels.') remoteirc.proto.quit(remoteuser, 'Left all shared channels.')
del relayusers[(irc.name, user)][remoteirc.name] del relayusers[(irc.name, user)][remoteirc.name]
@ -568,15 +627,21 @@ whitelisted_umodes = {'bot', 'hidechans', 'hideoper', 'invisible', 'oper',
'regdeaf', 'u_stripcolor', 'u_noctcp', 'wallops', 'regdeaf', 'u_stripcolor', 'u_noctcp', 'wallops',
'hideidle'} 'hideidle'}
def relayModes(irc, remoteirc, sender, channel, modes=None): def relayModes(irc, remoteirc, sender, channel, modes=None):
"""
Relays a mode change on a channel to its relay links.
"""
remotechan = getRemoteChan(irc, remoteirc, channel) remotechan = getRemoteChan(irc, remoteirc, channel)
log.debug('(%s) Relay mode: remotechan for %s on %s is %s', irc.name, channel, irc.name, remotechan) log.debug('(%s) relay.relayModes: remotechan for %s on %s is %s', irc.name, channel, irc.name, remotechan)
if remotechan is None: if remotechan is None:
return return
if modes is None: if modes is None:
modes = irc.channels[channel].modes modes = irc.channels[channel].modes
log.debug('(%s) Relay mode: channel data for %s%s: %s', irc.name, remoteirc.name, remotechan, remoteirc.channels[remotechan]) log.debug('(%s) relay.relayModes: channel data for %s%s: %s', irc.name, remoteirc.name, remotechan, remoteirc.channels[remotechan])
supported_modes = [] supported_modes = []
log.debug('(%s) Relay mode: initial modelist for %s is %s', irc.name, channel, modes) log.debug('(%s) relay.relayModes: initial modelist for %s is %s', irc.name, channel, modes)
for modepair in modes: for modepair in modes:
try: try:
prefix, modechar = modepair[0] prefix, modechar = modepair[0]
@ -584,56 +649,72 @@ def relayModes(irc, remoteirc, sender, channel, modes=None):
modechar = modepair[0] modechar = modepair[0]
prefix = '+' prefix = '+'
arg = modepair[1] arg = modepair[1]
# Iterate over every mode see whether the remote IRCd supports # Iterate over every mode see whether the remote IRCd supports
# this mode, and what its mode char for it is (if it is different). # this mode, and what its mode char for it is (if it is different).
for name, m in irc.cmodes.items(): for name, m in irc.cmodes.items():
supported_char = None supported_char = None
if modechar == m: if modechar == m:
supported_char = remoteirc.cmodes.get(name) supported_char = remoteirc.cmodes.get(name)
if supported_char is None: if supported_char is None:
break break
if name not in whitelisted_cmodes: if name not in whitelisted_cmodes:
log.debug("(%s) Relay mode: skipping mode (%r, %r) because " log.debug("(%s) relay.relayModes: skipping mode (%r, %r) because "
"it isn't a whitelisted (safe) mode for relay.", "it isn't a whitelisted (safe) mode for relay.",
irc.name, modechar, arg) irc.name, modechar, arg)
break break
if modechar in irc.prefixmodes: if modechar in irc.prefixmodes:
# This is a prefix mode (e.g. +o). We must coerse the argument # This is a prefix mode (e.g. +o). We must coerse the argument
# so that the target exists on the remote relay network. # so that the target exists on the remote relay network.
log.debug("(%s) Relay mode: coersing argument of (%r, %r) " log.debug("(%s) relay.relayModes: coersing argument of (%r, %r) "
"for network %r.", "for network %r.",
irc.name, modechar, arg, remoteirc.name) irc.name, modechar, arg, remoteirc.name)
# If the target is a remote user, get the real target # If the target is a remote user, get the real target
# (original user). # (original user).
arg = getOrigUser(irc, arg, targetirc=remoteirc) or \ arg = getOrigUser(irc, arg, targetirc=remoteirc) or \
getRemoteUser(irc, remoteirc, arg, spawnIfMissing=False) getRemoteUser(irc, remoteirc, arg, spawnIfMissing=False)
log.debug("(%s) Relay mode: argument found as (%r, %r) "
log.debug("(%s) relay.relayModes: argument found as (%r, %r) "
"for network %r.", "for network %r.",
irc.name, modechar, arg, remoteirc.name) irc.name, modechar, arg, remoteirc.name)
oplist = remoteirc.channels[remotechan].prefixmodes[name+'s'] oplist = remoteirc.channels[remotechan].prefixmodes[name+'s']
log.debug("(%s) Relay mode: list of %ss on %r is: %s",
log.debug("(%s) relay.relayModes: list of %ss on %r is: %s",
irc.name, name, remotechan, oplist) irc.name, name, remotechan, oplist)
if prefix == '+' and arg in oplist: if prefix == '+' and arg in oplist:
# Don't set prefix modes that are already set. # Don't set prefix modes that are already set.
log.debug("(%s) Relay mode: skipping setting %s on %s/%s because it appears to be already set.", log.debug("(%s) relay.relayModes: skipping setting %s on %s/%s because it appears to be already set.",
irc.name, name, arg, remoteirc.name) irc.name, name, arg, remoteirc.name)
break break
supported_char = remoteirc.cmodes.get(name) supported_char = remoteirc.cmodes.get(name)
if supported_char: if supported_char:
final_modepair = (prefix+supported_char, arg) final_modepair = (prefix+supported_char, arg)
if name in ('ban', 'banexception', 'invex') and not utils.isHostmask(arg): if name in ('ban', 'banexception', 'invex') and not utils.isHostmask(arg):
# Don't add bans that don't match n!u@h syntax! # Don't add bans that don't match n!u@h syntax!
log.debug("(%s) Relay mode: skipping mode (%r, %r) because it doesn't match nick!user@host syntax.", log.debug("(%s) relay.relayModes: skipping mode (%r, %r) because it doesn't match nick!user@host syntax.",
irc.name, modechar, arg) irc.name, modechar, arg)
break break
# Don't set modes that are already set, to prevent floods on TS6 # Don't set modes that are already set, to prevent floods on TS6
# where the same mode can be set infinite times. # where the same mode can be set infinite times.
if prefix == '+' and final_modepair in remoteirc.channels[remotechan].modes: if prefix == '+' and final_modepair in remoteirc.channels[remotechan].modes:
log.debug("(%s) Relay mode: skipping setting mode (%r, %r) on %s%s because it appears to be already set.", log.debug("(%s) relay.relayModes: skipping setting mode (%r, %r) on %s%s because it appears to be already set.",
irc.name, supported_char, arg, remoteirc.name, remotechan) irc.name, supported_char, arg, remoteirc.name, remotechan)
break break
supported_modes.append(final_modepair) supported_modes.append(final_modepair)
log.debug('(%s) Relay mode: final modelist (sending to %s%s) is %s', irc.name, remoteirc.name, remotechan, supported_modes)
log.debug('(%s) relay.relayModes: final modelist (sending to %s%s) is %s', irc.name, remoteirc.name, remotechan, supported_modes)
# Don't send anything if there are no supported modes left after filtering. # Don't send anything if there are no supported modes left after filtering.
if supported_modes: if supported_modes:
# Check if the sender is a user; remember servers are allowed to set modes too. # Check if the sender is a user; remember servers are allowed to set modes too.
@ -645,6 +726,9 @@ def relayModes(irc, remoteirc, sender, channel, modes=None):
remoteirc.proto.mode(rsid, remotechan, supported_modes) remoteirc.proto.mode(rsid, remotechan, supported_modes)
def relayWhoisHandler(irc, target): def relayWhoisHandler(irc, target):
"""
WHOIS handler for the relay plugin.
"""
user = irc.users[target] user = irc.users[target]
orig = getOrigUser(irc, target) orig = getOrigUser(irc, target)
if orig: if orig:
@ -700,7 +784,7 @@ def handle_squit(irc, numeric, command, args):
# Some other netsplit happened on the network, we'll have to fake # Some other netsplit happened on the network, we'll have to fake
# some *.net *.split quits for that. # some *.net *.split quits for that.
for user in users: for user in users:
log.debug('(%s) relay handle_squit: sending handle_quit on %s', irc.name, user) log.debug('(%s) relay.handle_squit: sending handle_quit on %s', irc.name, user)
handle_quit(irc, user, command, {'text': '*.net *.split'}) handle_quit(irc, user, command, {'text': '*.net *.split'})
utils.add_hook(handle_squit, 'SQUIT') utils.add_hook(handle_squit, 'SQUIT')
@ -748,7 +832,7 @@ def handle_messages(irc, numeric, command, args):
elif numeric not in irc.users: elif numeric not in irc.users:
# Sender didn't pass the check above, AND isn't a user. # Sender didn't pass the check above, AND isn't a user.
log.debug('(%s) relay: Unknown message sender %s.', irc.name, numeric) log.debug('(%s) relay.handle_messages: Unknown message sender %s.', irc.name, numeric)
return return
relay = getRelay((irc.name, target)) relay = getRelay((irc.name, target))
@ -762,7 +846,7 @@ def handle_messages(irc, numeric, command, args):
else: else:
target = '#' + target target = '#' + target
log.debug('(%s) relay privmsg: prefix is %r, target is %r', irc.name, prefix, target) log.debug('(%s) relay.handle_messages: prefix is %r, target is %r', irc.name, prefix, target)
if utils.isChannel(target) and relay and numeric not in irc.channels[target].users: if utils.isChannel(target) and relay and numeric not in irc.channels[target].users:
# The sender must be in the target channel to send messages over the relay; # The sender must be in the target channel to send messages over the relay;
# it's the only way we can make sure they have a spawned client on ALL # it's the only way we can make sure they have a spawned client on ALL
@ -784,7 +868,7 @@ def handle_messages(irc, numeric, command, args):
user = getRemoteUser(irc, remoteirc, numeric, spawnIfMissing=False) user = getRemoteUser(irc, remoteirc, numeric, spawnIfMissing=False)
real_target = prefix + real_target real_target = prefix + real_target
log.debug('(%s) relay: sending message to %s from %s on behalf of %s', log.debug('(%s) relay.handle_messages: sending message to %s from %s on behalf of %s',
irc.name, real_target, user, numeric) irc.name, real_target, user, numeric)
if notice: if notice:
remoteirc.proto.notice(user, real_target, text) remoteirc.proto.notice(user, real_target, text)
@ -832,24 +916,24 @@ def handle_kick(irc, source, command, args):
if irc.name == name or not remoteirc.connected.is_set(): if irc.name == name or not remoteirc.connected.is_set():
continue continue
remotechan = getRemoteChan(irc, remoteirc, channel) remotechan = getRemoteChan(irc, remoteirc, channel)
log.debug('(%s) Relay kick: remotechan for %s on %s is %s', irc.name, channel, name, remotechan) log.debug('(%s) relay.handle_kick: remotechan for %s on %s is %s', irc.name, channel, name, remotechan)
if remotechan is None: if remotechan is None:
continue continue
real_kicker = getRemoteUser(irc, remoteirc, kicker, spawnIfMissing=False) real_kicker = getRemoteUser(irc, remoteirc, kicker, spawnIfMissing=False)
log.debug('(%s) Relay kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker) log.debug('(%s) relay.handle_kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker)
if not isRelayClient(irc, target): if not isRelayClient(irc, target):
log.debug('(%s) Relay kick: target %s is NOT an internal client', irc.name, 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. # Both the target and kicker are external clients; i.e.
# they originate from the same network. We won't have # they originate from the same network. We won't have
# to filter this; the uplink IRCd will handle it appropriately, # to filter this; the uplink IRCd will handle it appropriately,
# and we'll just follow. # and we'll just follow.
real_target = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False) real_target = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False)
log.debug('(%s) Relay kick: real target for %s is %s', irc.name, target, real_target) log.debug('(%s) relay.handle_kick: real target for %s is %s', irc.name, target, real_target)
else: else:
log.debug('(%s) Relay kick: target %s is an internal client, going to look up the real user', irc.name, target) log.debug('(%s) relay.handle_kick: target %s is an internal client, going to look up the real user', irc.name, target)
real_target = getOrigUser(irc, target, targetirc=remoteirc) real_target = getOrigUser(irc, target, targetirc=remoteirc)
if not checkClaim(irc, channel, kicker): if not checkClaim(irc, channel, kicker):
log.debug('(%s) Relay kick: kicker %s is not opped... We should rejoin the target user %s', irc.name, kicker, real_target) 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 # Home network is not in the channel's claim AND the kicker is not
# opped. We won't propograte the kick then. # opped. We won't propograte the kick then.
# TODO: make the check slightly more advanced: i.e. halfops can't # TODO: make the check slightly more advanced: i.e. halfops can't
@ -858,14 +942,14 @@ def handle_kick(irc, source, command, args):
# Join the kicked client back with its respective modes. # Join the kicked client back with its respective modes.
irc.proto.sjoin(irc.sid, channel, [(modes, target)]) irc.proto.sjoin(irc.sid, channel, [(modes, target)])
if kicker in irc.users: if kicker in irc.users:
log.info('(%s) Relay claim: Blocked KICK (reason %r) from %s to relay client %s on %s.', log.info('(%s) relay.handle_kick: Blocked KICK (reason %r) from %s to relay client %s on %s.',
irc.name, args['text'], irc.users[source].nick, irc.name, args['text'], irc.users[source].nick,
remoteirc.users[real_target].nick, channel) remoteirc.users[real_target].nick, channel)
irc.msg(kicker, "This channel is claimed; your kick to " irc.msg(kicker, "This channel is claimed; your kick to "
"%s has been blocked because you are not " "%s has been blocked because you are not "
"(half)opped." % channel, notice=True) "(half)opped." % channel, notice=True)
else: else:
log.info('(%s) Relay claim: Blocked KICK (reason %r) from server %s to relay client %s/%s on %s.', log.info('(%s) relay.handle_kick: Blocked KICK (reason %r) from server %s to relay client %s/%s on %s.',
irc.name, args['text'], irc.servers[source].name, irc.name, args['text'], irc.servers[source].name,
remoteirc.users[real_target].nick, remoteirc.name, channel) remoteirc.users[real_target].nick, remoteirc.name, channel)
return return
@ -874,13 +958,13 @@ def handle_kick(irc, source, command, args):
continue continue
# Propogate the kick! # Propogate the kick!
if real_kicker: if real_kicker:
log.debug('(%s) Relay kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan,real_kicker, kicker, irc.name) 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)
remoteirc.proto.kick(real_kicker, remotechan, real_target, args['text']) remoteirc.proto.kick(real_kicker, remotechan, real_target, args['text'])
else: else:
# Kick originated from a server, or the kicker isn't in any # Kick originated from a server, or the kicker isn't in any
# common channels with the target relay network. # common channels with the target relay network.
rsid = getRemoteSid(remoteirc, irc) rsid = getRemoteSid(remoteirc, irc)
log.debug('(%s) Relay kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan, rsid, kicker, irc.name) 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)
try: try:
if kicker in irc.servers: if kicker in irc.servers:
kname = irc.servers[kicker].name kname = irc.servers[kicker].name
@ -921,7 +1005,7 @@ def handle_chgclient(irc, source, command, args):
text = normalizeHost(remoteirc, text) text = normalizeHost(remoteirc, text)
remoteirc.proto.updateClient(user, field, text) remoteirc.proto.updateClient(user, field, text)
except NotImplementedError: # IRCd doesn't support changing the field we want except NotImplementedError: # IRCd doesn't support changing the field we want
log.debug('(%s) Ignoring changing field %r of %s on %s (for %s/%s);' log.debug('(%s) relay.handle_chgclient: Ignoring changing field %r of %s on %s (for %s/%s);'
' remote IRCd doesn\'t support it', irc.name, field, ' remote IRCd doesn\'t support it', irc.name, field,
user, netname, target, irc.name) user, netname, target, irc.name)
continue continue
@ -935,27 +1019,33 @@ def handle_mode(irc, numeric, command, args):
for name, remoteirc in world.networkobjects.items(): for name, remoteirc in world.networkobjects.items():
if irc.name == name or not remoteirc.connected.is_set(): if irc.name == name or not remoteirc.connected.is_set():
continue continue
if utils.isChannel(target): if utils.isChannel(target):
oldchan = args.get('oldchan') oldchan = args.get('oldchan')
if checkClaim(irc, target, numeric, chanobj=oldchan): if checkClaim(irc, target, numeric, chanobj=oldchan):
relayModes(irc, remoteirc, numeric, target, modes) relayModes(irc, remoteirc, numeric, target, modes)
else: # Mode change blocked by CLAIM. else: # Mode change blocked by CLAIM.
reversed_modes = utils.reverseModes(irc, target, modes, oldobj=oldchan) reversed_modes = utils.reverseModes(irc, target, modes, oldobj=oldchan)
log.debug('(%s) Reversing mode changes of %r with %r (CLAIM).', log.debug('(%s) relay.handle_mode: Reversing mode changes of %r with %r (CLAIM).',
irc.name, modes, reversed_modes) irc.name, modes, reversed_modes)
irc.proto.mode(irc.pseudoclient.uid, target, reversed_modes) irc.proto.mode(irc.pseudoclient.uid, target, reversed_modes)
break break
else: else:
# Set hideoper on remote opers, to prevent inflating # Set hideoper on remote opers, to prevent inflating
# /lusers and various /stats # /lusers and various /stats
hideoper_mode = remoteirc.umodes.get('hideoper') hideoper_mode = remoteirc.umodes.get('hideoper')
modes = getSupportedUmodes(irc, remoteirc, modes) modes = getSupportedUmodes(irc, remoteirc, modes)
if hideoper_mode: if hideoper_mode:
if ('+o', None) in modes: if ('+o', None) in modes:
modes.append(('+%s' % hideoper_mode, None)) modes.append(('+%s' % hideoper_mode, None))
elif ('-o', None) in modes: elif ('-o', None) in modes:
modes.append(('-%s' % hideoper_mode, None)) modes.append(('-%s' % hideoper_mode, None))
remoteuser = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False) remoteuser = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False)
if remoteuser and modes: if remoteuser and modes:
remoteirc.proto.mode(remoteuser, remoteuser, modes) remoteirc.proto.mode(remoteuser, remoteuser, modes)
@ -990,7 +1080,7 @@ def handle_kill(irc, numeric, command, args):
target = args['target'] target = args['target']
userdata = args['userdata'] userdata = args['userdata']
realuser = getOrigUser(irc, target) or userdata.__dict__.get('remote') realuser = getOrigUser(irc, target) or userdata.__dict__.get('remote')
log.debug('(%s) relay handle_kill: realuser is %r', irc.name, realuser) log.debug('(%s) relay.handle_kill: realuser is %r', irc.name, realuser)
# Target user was remote: # Target user was remote:
if realuser and realuser[0] != irc.name: if realuser and realuser[0] != irc.name:
# We don't allow killing over the relay, so we must respawn the affected # We don't allow killing over the relay, so we must respawn the affected
@ -1002,11 +1092,11 @@ def handle_kill(irc, numeric, command, args):
localchan = getRemoteChan(remoteirc, irc, remotechan) localchan = getRemoteChan(remoteirc, irc, remotechan)
if localchan: if localchan:
modes = getPrefixModes(remoteirc, irc, remotechan, realuser[1]) modes = getPrefixModes(remoteirc, irc, remotechan, realuser[1])
log.debug('(%s) relay handle_kill: userpair: %s, %s', irc.name, modes, realuser) log.debug('(%s) relay.handle_kill: userpair: %s, %s', irc.name, modes, realuser)
client = getRemoteUser(remoteirc, irc, realuser[1]) client = getRemoteUser(remoteirc, irc, realuser[1])
irc.proto.sjoin(getRemoteSid(irc, remoteirc), localchan, [(modes, client)]) irc.proto.sjoin(getRemoteSid(irc, remoteirc), localchan, [(modes, client)])
if userdata and numeric in irc.users: if userdata and numeric in irc.users:
log.info('(%s) Relay claim: Blocked KILL (reason %r) from %s to relay client %s/%s.', log.info('(%s) relay.handle_kill: Blocked KILL (reason %r) from %s to relay client %s/%s.',
irc.name, args['text'], irc.users[numeric].nick, irc.name, args['text'], irc.users[numeric].nick,
remoteirc.users[realuser[1]].nick, realuser[0]) remoteirc.users[realuser[1]].nick, realuser[0])
irc.msg(numeric, "Your kill to %s has been blocked " irc.msg(numeric, "Your kill to %s has been blocked "
@ -1014,11 +1104,11 @@ def handle_kill(irc, numeric, command, args):
" users over the relay at this time." % \ " users over the relay at this time." % \
userdata.nick, notice=True) userdata.nick, notice=True)
else: else:
log.info('(%s) Relay claim: Blocked KILL (reason %r) from server %s to relay client %s/%s.', log.info('(%s) relay.handle_kill: Blocked KILL (reason %r) from server %s to relay client %s/%s.',
irc.name, args['text'], irc.servers[numeric].name, irc.name, args['text'], irc.servers[numeric].name,
remoteirc.users[realuser[1]].nick, realuser[0]) remoteirc.users[realuser[1]].nick, realuser[0])
else: else:
log.error('(%s) Too many kills received for target %s, aborting!', log.error('(%s) relay.handle_kill: Too many kills received for target %s, aborting!',
irc.name, userdata.nick) irc.name, userdata.nick)
irc.disconnect() irc.disconnect()
killcache[irc.name] += 1 killcache[irc.name] += 1
@ -1108,7 +1198,7 @@ utils.add_hook(handle_disconnect, "PYLINK_DISCONNECT")
def handle_save(irc, numeric, command, args): def handle_save(irc, numeric, command, args):
target = args['target'] target = args['target']
realuser = getOrigUser(irc, target) realuser = getOrigUser(irc, target)
log.debug('(%s) relay handle_save: %r got in a nick collision! Real user: %r', log.debug('(%s) relay.handle_save: %r got in a nick collision! Real user: %r',
irc.name, target, realuser) irc.name, target, realuser)
if isRelayClient(irc, target) and realuser: if isRelayClient(irc, target) and realuser:
# Nick collision! # Nick collision!
@ -1121,11 +1211,11 @@ def handle_save(irc, numeric, command, args):
# floods and such. # floods and such.
if savecache.setdefault(irc.name, 0) <= 5: if savecache.setdefault(irc.name, 0) <= 5:
newnick = normalizeNick(irc, remotenet, nick) newnick = normalizeNick(irc, remotenet, nick)
log.info('(%s) SAVE received for relay client %r (%s), fixing nick to %s', log.info('(%s) relay.handle_save: SAVE received for relay client %r (%s), fixing nick to %s',
irc.name, target, nick, newnick) irc.name, target, nick, newnick)
irc.proto.nick(target, newnick) irc.proto.nick(target, newnick)
else: else:
log.warning('(%s) SAVE received for relay client %r (%s), not ' log.warning('(%s) relay.handle_save: SAVE received for relay client %r (%s), not '
'fixing nick again due to 5 failed attempts in ' 'fixing nick again due to 5 failed attempts in '
'the last 10 seconds!', irc.name, target, nick) 'the last 10 seconds!', irc.name, target, nick)
savecache[irc.name] += 1 savecache[irc.name] += 1