2016-07-07 09:25:50 +02:00
|
|
|
"""
|
|
|
|
exttargets.py - Implements extended targets like $account:xyz, $oper, etc.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from pylinkirc import world
|
|
|
|
from pylinkirc.log import log
|
|
|
|
|
2019-07-15 00:12:29 +02:00
|
|
|
|
2016-07-07 09:25:50 +02:00
|
|
|
def bind(func):
|
|
|
|
"""
|
|
|
|
Binds an exttarget with the given name.
|
|
|
|
"""
|
|
|
|
world.exttarget_handlers[func.__name__] = func
|
|
|
|
return func
|
|
|
|
|
|
|
|
@bind
|
|
|
|
def account(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$account exttarget handler. The following forms are supported, with groups separated by a
|
2016-07-07 20:11:13 +02:00
|
|
|
literal colon. Account matching is case insensitive, while network name matching IS case
|
|
|
|
sensitive.
|
2016-07-07 09:25:50 +02:00
|
|
|
|
|
|
|
$account -> Returns True (a match) if the target is registered.
|
|
|
|
$account:accountname -> Returns True if the target's account name matches the one given, and the
|
2017-05-05 02:59:50 +02:00
|
|
|
target is connected to the local network.
|
2016-07-07 09:25:50 +02:00
|
|
|
$account:accountname:netname -> Returns True if both the target's account name and origin
|
|
|
|
network name match the ones given.
|
|
|
|
$account:*:netname -> Matches all logged in users on the given network.
|
|
|
|
"""
|
|
|
|
userobj = irc.users[uid]
|
|
|
|
homenet = irc.name
|
|
|
|
if hasattr(userobj, 'remote'):
|
|
|
|
# User is a PyLink Relay pseudoclient. Use their real services account on their
|
|
|
|
# origin network.
|
|
|
|
homenet, realuid = userobj.remote
|
|
|
|
log.debug('(%s) exttargets.account: Changing UID of relay client %s to %s/%s', irc.name,
|
|
|
|
uid, homenet, realuid)
|
|
|
|
try:
|
|
|
|
userobj = world.networkobjects[homenet].users[realuid]
|
|
|
|
except KeyError: # User lookup failed. Bail and return False.
|
|
|
|
log.exception('(%s) exttargets.account: KeyError finding %s/%s:', irc.name,
|
|
|
|
homenet, realuid)
|
|
|
|
return False
|
|
|
|
|
2019-06-26 21:29:22 +02:00
|
|
|
slogin = irc.to_lower(str(userobj.services_account))
|
2016-07-07 09:25:50 +02:00
|
|
|
|
|
|
|
# Split the given exttarget host into parts, so we know how many to look for.
|
2017-06-30 08:01:39 +02:00
|
|
|
groups = list(map(irc.to_lower, host.split(':')))
|
2016-07-07 09:25:50 +02:00
|
|
|
log.debug('(%s) exttargets.account: groups to match: %s', irc.name, groups)
|
|
|
|
|
|
|
|
if len(groups) == 1:
|
|
|
|
# First scenario. Return True if user is logged in.
|
|
|
|
return bool(slogin)
|
|
|
|
elif len(groups) == 2:
|
|
|
|
# Second scenario. Return True if the user's account matches the one given.
|
|
|
|
return slogin == groups[1] and homenet == irc.name
|
|
|
|
else:
|
|
|
|
# Third or fourth scenario. If there are more than 3 groups, the rest are ignored.
|
2016-07-07 09:29:52 +02:00
|
|
|
# In other words: Return True if the user is logged in, the query matches either '*' or the
|
2016-07-07 09:41:31 +02:00
|
|
|
# user's login, and the user is connected on the network requested.
|
2016-07-07 09:29:52 +02:00
|
|
|
return slogin and (groups[1] in ('*', slogin)) and (homenet == groups[2])
|
2016-07-07 20:11:31 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def ircop(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$ircop exttarget handler. The following forms are supported, with groups separated by a
|
|
|
|
literal colon. Oper types are matched case insensitively.
|
|
|
|
|
|
|
|
$ircop -> Returns True (a match) if the target is opered.
|
|
|
|
$ircop:*admin* -> Returns True if the target's is opered and their opertype matches the glob
|
|
|
|
given.
|
|
|
|
"""
|
|
|
|
groups = host.split(':')
|
|
|
|
log.debug('(%s) exttargets.ircop: groups to match: %s', irc.name, groups)
|
|
|
|
|
|
|
|
if len(groups) == 1:
|
|
|
|
# 1st scenario.
|
2018-06-12 08:56:44 +02:00
|
|
|
return irc.is_oper(uid)
|
2016-07-07 20:11:31 +02:00
|
|
|
else:
|
2017-06-30 08:01:39 +02:00
|
|
|
# 2nd scenario. Use match_host (ircmatch) to match the opertype glob to the opertype.
|
|
|
|
return irc.match_host(groups[1], irc.users[uid].opertype)
|
2016-07-07 20:26:11 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def server(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$server exttarget handler. The following forms are supported, with groups separated by a
|
|
|
|
literal colon. Server names are matched case insensitively, but SIDs ARE case sensitive.
|
|
|
|
|
|
|
|
$server:server.name -> Returns True (a match) if the target is connected on the given server.
|
|
|
|
$server:server.glob -> Returns True (a match) if the target is connected on a server matching the glob.
|
|
|
|
$server:1XY -> Returns True if the target's is connected on the server with the given SID.
|
|
|
|
"""
|
|
|
|
groups = host.split(':')
|
|
|
|
log.debug('(%s) exttargets.server: groups to match: %s', irc.name, groups)
|
|
|
|
|
|
|
|
if len(groups) >= 2:
|
2017-06-30 08:01:39 +02:00
|
|
|
sid = irc.get_server(uid)
|
2016-07-07 20:26:11 +02:00
|
|
|
query = groups[1]
|
|
|
|
# Return True if the SID matches the query or the server's name glob matches it.
|
2017-06-30 08:01:39 +02:00
|
|
|
return sid == query or irc.match_host(query, irc.get_friendly_name(sid))
|
2016-07-07 20:26:11 +02:00
|
|
|
# $server alone is invalid. Don't match anything.
|
|
|
|
return False
|
2016-07-07 21:00:23 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def channel(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$channel exttarget handler. The following forms are supported, with groups separated by a
|
|
|
|
literal colon. Channel names are matched case insensitively.
|
|
|
|
|
|
|
|
$channel:#channel -> Returns True if the target is in the given channel.
|
|
|
|
$channel:#channel:op -> Returns True if the target is in the given channel, and is opped.
|
|
|
|
Any other supported prefix (owner, admin, op, halfop, voice) can be given, but only one at a
|
|
|
|
time.
|
|
|
|
"""
|
|
|
|
groups = host.split(':')
|
|
|
|
log.debug('(%s) exttargets.channel: groups to match: %s', irc.name, groups)
|
|
|
|
try:
|
|
|
|
channel = groups[1]
|
|
|
|
except IndexError: # No channel given, abort.
|
|
|
|
return False
|
|
|
|
|
2017-08-25 11:31:26 +02:00
|
|
|
if channel not in irc.channels:
|
|
|
|
# Channel doesn't even exist...
|
|
|
|
return False
|
|
|
|
|
2016-07-07 21:00:23 +02:00
|
|
|
if len(groups) == 2:
|
|
|
|
# Just #channel was given as query
|
|
|
|
return uid in irc.channels[channel].users
|
|
|
|
elif len(groups) >= 3:
|
|
|
|
# For things like #channel:op, check if the query is in the user's prefix modes.
|
2017-07-01 06:40:05 +02:00
|
|
|
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].get_prefix_modes(uid))
|
2016-07-07 21:10:09 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def pylinkacc(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$pylinkacc (PyLink account) exttarget handler. The following forms are supported, with groups
|
|
|
|
separated by a literal colon. Account matching is case insensitive.
|
|
|
|
|
|
|
|
$pylinkacc -> Returns True if the target is logged in to PyLink.
|
|
|
|
$pylinkacc:accountname -> Returns True if the target's PyLink login matches the one given.
|
|
|
|
"""
|
2017-06-30 08:01:39 +02:00
|
|
|
login = irc.to_lower(irc.users[uid].account)
|
|
|
|
groups = list(map(irc.to_lower, host.split(':')))
|
2016-07-07 21:10:09 +02:00
|
|
|
log.debug('(%s) exttargets.pylinkacc: groups to match: %s', irc.name, groups)
|
|
|
|
|
|
|
|
if len(groups) == 1:
|
|
|
|
# First scenario. Return True if user is logged in.
|
|
|
|
return bool(login)
|
|
|
|
elif len(groups) == 2:
|
|
|
|
# Second scenario. Return True if the user's login matches the one given.
|
|
|
|
return login == groups[1]
|
2017-05-05 02:16:50 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def network(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$network exttarget handler. This exttarget takes one argument: a network name, and returns
|
|
|
|
a match for all users on that network.
|
|
|
|
|
|
|
|
Note: network names are case sensitive.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
targetnet = host.split(':')[1]
|
|
|
|
except IndexError: # No network arg given, bail.
|
|
|
|
return False
|
|
|
|
|
|
|
|
userobj = irc.users[uid]
|
|
|
|
if hasattr(userobj, 'remote'):
|
|
|
|
# User is a PyLink Relay client; set the correct network name.
|
|
|
|
homenet = userobj.remote[0]
|
|
|
|
else:
|
|
|
|
homenet = irc.name
|
|
|
|
|
|
|
|
return homenet == targetnet
|
2017-05-05 02:59:50 +02:00
|
|
|
|
|
|
|
# Note: "and" can't be a function name so we use this.
|
|
|
|
def exttarget_and(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$and exttarget handler. This exttarget takes a series of exttargets (or hostmasks) joined with
|
|
|
|
a "+", and returns True if all sub exttargets match.
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
$and:($ircop:*admin*+$network:ovd) -> Matches all opers on the network ovd.
|
|
|
|
$and:($account+$pylinkirc) -> Matches all users logged in to both services and PyLink.
|
|
|
|
$and:(*!*@localhost+$ircop) -> Matches all opers with the host `localhost`.
|
|
|
|
$and:(*!*@*.mibbit.com+!$ircop+!$account) -> Matches all mibbit users that aren't opered or logged in to services.
|
|
|
|
"""
|
|
|
|
targets = host.split(':', 1)[-1]
|
|
|
|
# For readability, this requires that the exttarget list be wrapped in brackets.
|
|
|
|
if not (targets.startswith('(') and targets.endswith(')')):
|
|
|
|
return False
|
|
|
|
|
|
|
|
targets = targets[1:-1]
|
|
|
|
targets = list(filter(None, targets.split('+')))
|
|
|
|
log.debug('exttargets_and: using raw subtargets list %r (original query=%r)', targets, host)
|
2017-06-30 08:01:39 +02:00
|
|
|
# Wrap every subtarget into irc.match_host and return True if all subtargets return True.
|
|
|
|
return all(map(lambda sub_exttarget: irc.match_host(sub_exttarget, uid), targets))
|
2017-05-05 02:59:50 +02:00
|
|
|
world.exttarget_handlers['and'] = exttarget_and
|
2017-08-07 06:41:44 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def realname(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$realname exttarget handler. This takes one argument: a glob, which is compared case-insensitively to the user's real name.
|
|
|
|
|
|
|
|
Examples:
|
2017-09-06 04:19:44 +02:00
|
|
|
$realname:*James* -> matches anyone with "James" in their real name.
|
2017-08-07 06:41:44 +02:00
|
|
|
"""
|
|
|
|
groups = host.split(':')
|
|
|
|
if len(groups) >= 2:
|
|
|
|
return irc.match_host(groups[1], irc.users[uid].realname)
|
2017-09-06 04:19:01 +02:00
|
|
|
|
|
|
|
@bind
|
|
|
|
def service(irc, host, uid):
|
|
|
|
"""
|
|
|
|
$service exttarget handler. This takes one optional argument: a glob, which is compared case-insensitively to the target user's service name (if present).
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
$service -> Matches any PyLink service bot.
|
|
|
|
$service:automode -> Matches the Automode service bot.
|
|
|
|
"""
|
|
|
|
if not irc.users[uid].service:
|
|
|
|
return False
|
|
|
|
|
|
|
|
groups = host.split(':')
|
|
|
|
|
|
|
|
if len(groups) >= 2:
|
|
|
|
return irc.match_host(groups[1], irc.users[uid].service)
|
|
|
|
return True # It *is* a service bot because of the check at the top.
|