3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-01 09:19:23 +01:00

relay/utils: make mode reversals work with mode changes that affect op statuses.

This commit is contained in:
James Lu 2015-09-13 13:48:14 -07:00
parent c82a0a771c
commit 9a139212dd
2 changed files with 43 additions and 16 deletions

View File

@ -116,14 +116,23 @@ def save(irc, source, args):
irc.msg(source, 'Error: You are not authenticated!') irc.msg(source, 'Error: You are not authenticated!')
return return
def getPrefixModes(irc, remoteirc, channel, user): def getPrefixModes(irc, remoteirc, channel, user, mlist=None):
"""
Fetches all prefix modes for a user in a channel that are supported by the
remote IRC network given.
Optionally, an mlist argument can be given to look at an earlier state of
the channel, e.g. for checking the op status of a mode setter before their
modes are processed and added to the channel state.
"""
modes = '' modes = ''
mlist = mlist or irc.channels[channel].prefixmodes
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: # Mode supported by IRCd
mlist = irc.channels[channel].prefixmodes[pmode+'s'] userlist = mlist[pmode+'s']
log.debug('(%s) getPrefixModes: checking if %r is in %s list: %r', log.debug('(%s) getPrefixModes: checking if %r is in %s list: %r',
irc.name, user, pmode, mlist) irc.name, user, pmode, userlist)
if user in mlist: if user in userlist:
modes += remoteirc.cmodes[pmode] modes += remoteirc.cmodes[pmode]
return modes return modes
@ -416,7 +425,7 @@ def handle_privmsg(irc, numeric, command, args):
utils.add_hook(handle_privmsg, 'PRIVMSG') utils.add_hook(handle_privmsg, 'PRIVMSG')
utils.add_hook(handle_privmsg, 'NOTICE') utils.add_hook(handle_privmsg, 'NOTICE')
def checkClaim(irc, channel, sender): def checkClaim(irc, channel, sender, chanobj=None):
""" """
Checks whether the sender of a kick/mode change passes CLAIM checks for Checks whether the sender of a kick/mode change passes CLAIM checks for
a given channel. This returns True if any of the following criteria are met: a given channel. This returns True if any of the following criteria are met:
@ -428,7 +437,13 @@ def checkClaim(irc, channel, sender):
5) The originating network is the one that created the relay. 5) The originating network is the one that created the relay.
""" """
relay = findRelay((irc.name, channel)) relay = findRelay((irc.name, channel))
sender_modes = getPrefixModes(irc, irc, channel, sender) try:
mlist = chanobj.prefixmodes
except AttributeError:
mlist = None
sender_modes = getPrefixModes(irc, irc, channel, sender, mlist=mlist)
log.debug('(%s) relay.checkClaim: sender modes (%s/%s) are %s (mlist=%s)', irc.name,
sender, channel, sender_modes, mlist)
return (not relay) or irc.name == relay[0] or irc.name in db[relay]['claim'] or \ return (not relay) or irc.name == relay[0] or irc.name in db[relay]['claim'] or \
any([mode in sender_modes for mode in ('y', 'q', 'a', 'o', 'h')]) \ any([mode in sender_modes for mode in ('y', 'q', 'a', 'o', 'h')]) \
or utils.isInternalClient(irc, sender) or \ or utils.isInternalClient(irc, sender) or \
@ -665,10 +680,11 @@ def handle_mode(irc, numeric, command, args):
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):
if checkClaim(irc, target, numeric): oldchan = args['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) reversed_modes = utils.reverseModes(irc, target, modes, oldobj=oldchan)
log.debug('(%s) Reversing mode changes of %r with %r (CLAIM).', log.debug('(%s) Reversing mode changes of %r with %r (CLAIM).',
irc.name, modes, reversed_modes) irc.name, modes, reversed_modes)
irc.proto.modeClient(irc.pseudoclient.uid, target, reversed_modes) irc.proto.modeClient(irc.pseudoclient.uid, target, reversed_modes)

View File

@ -338,14 +338,19 @@ def _flip(mode):
mode.insert(0, '-') mode.insert(0, '-')
return ''.join(mode) return ''.join(mode)
def reverseModes(irc, target, modes): def reverseModes(irc, target, modes, oldobj=None):
"""Reverses/Inverts the mode string or mode list given. """Reverses/Inverts the mode string or mode list given.
"+nt-lk" => "-nt+lk" Optionally, an oldobj argument can be given to look at an earlier state of
"nt-k" => "-nt+k" a channel/user object, e.g. for checking the op status of a mode setter
[('+m', None), ('+t', None), ('+l', '3'), ('-o', 'person')] => before their modes are processed and added to the channel state.
[('-m', None), ('-t', None), ('-l', '3'), ('+o', 'person')]
[('s', None), ('+n', None)] => [('-s', None), ('-n', None)] This function allows both mode strings or mode lists. Example uses:
"+mi-lk test => "-mi+lk test"
"mi-k test => "-mi+k test"
[('+m', None), ('+r', None), ('+l', '3'), ('-o', 'person')
=> {('-m', None), ('-r', None), ('-l', None), ('+o', 'person')})
{('s', None), ('+o', 'whoever') => {('-s', None), ('-o', 'whoever')})
""" """
origtype = type(modes) origtype = type(modes)
# If the query is a string, we have to parse it first. # If the query is a string, we have to parse it first.
@ -353,11 +358,12 @@ def reverseModes(irc, target, modes):
modes = parseModes(irc, target, modes.split(" ")) modes = parseModes(irc, target, modes.split(" "))
# Get the current mode list first. # Get the current mode list first.
if isChannel(target): if isChannel(target):
oldmodes = irc.channels[target].modes.copy() c = oldobj or irc.channels[target]
oldmodes = c.modes.copy()
possible_modes = irc.cmodes.copy() possible_modes = irc.cmodes.copy()
# For channels, this also includes the list of prefix modes. # For channels, this also includes the list of prefix modes.
possible_modes['*A'] += ''.join(irc.prefixmodes) possible_modes['*A'] += ''.join(irc.prefixmodes)
for name, userlist in irc.channels[target].prefixmodes.items(): for name, userlist in c.prefixmodes.items():
try: try:
oldmodes.update([(irc.cmodes[name[:-1]], u) for u in userlist]) oldmodes.update([(irc.cmodes[name[:-1]], u) for u in userlist])
except KeyError: except KeyError:
@ -366,6 +372,8 @@ def reverseModes(irc, target, modes):
oldmodes = irc.users[target].modes oldmodes = irc.users[target].modes
possible_modes = irc.umodes possible_modes = irc.umodes
newmodes = [] newmodes = []
log.debug('(%s) reverseModes: old/current mode list for %s is: %s', irc.name,
target, oldmodes)
for char, arg in modes: for char, arg in modes:
# Mode types: # Mode types:
# A = Mode that adds or removes a nick or address to a list. Always has a parameter. # A = Mode that adds or removes a nick or address to a list. Always has a parameter.
@ -390,9 +398,12 @@ def reverseModes(irc, target, modes):
mpair = (_flip(char), arg) mpair = (_flip(char), arg)
if char[0] != '-' and (mchar, arg) in oldmodes: if char[0] != '-' and (mchar, arg) in oldmodes:
# Mode is already set. # Mode is already set.
log.debug("(%s) reverseModes: skipping reversing '%s %s' with %s since we're "
"setting a mode that's already set.", irc.name, char, arg, mpair)
continue continue
newmodes.append(mpair) newmodes.append(mpair)
log.debug('(%s) reverseModes: new modes: %s', irc.name, newmodes)
if origtype == str: if origtype == str:
# If the original query is a string, send it back as a string. # If the original query is a string, send it back as a string.
return joinModes(newmodes) return joinModes(newmodes)