mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-01 09:19:23 +01:00
Merge branch 'wip/irc-explosion-2' into devel (#475)
This brings in a major refactor of the IRC/protocol stack, to start off 2.0-dev.
This commit is contained in:
commit
f800c9f7c2
2225
classes.py
2225
classes.py
File diff suppressed because it is too large
Load Diff
@ -130,7 +130,11 @@ def _rehash():
|
|||||||
# Connect any new networks or disconnected networks if they aren't already.
|
# Connect any new networks or disconnected networks if they aren't already.
|
||||||
if (network not in world.networkobjects) or (not world.networkobjects[network].connection_thread.is_alive()):
|
if (network not in world.networkobjects) or (not world.networkobjects[network].connection_thread.is_alive()):
|
||||||
proto = utils.getProtocolModule(sdata['protocol'])
|
proto = utils.getProtocolModule(sdata['protocol'])
|
||||||
world.networkobjects[network] = classes.Irc(network, proto, new_conf)
|
|
||||||
|
# API note: 2.0.x style of starting network connections
|
||||||
|
world.networkobjects[network] = newirc = proto.Class(network)
|
||||||
|
newirc.connect()
|
||||||
|
|
||||||
log.info('Finished reloading PyLink configuration.')
|
log.info('Finished reloading PyLink configuration.')
|
||||||
|
|
||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
|
@ -18,12 +18,12 @@ def _login(irc, source, username):
|
|||||||
irc.users[source].account = username
|
irc.users[source].account = username
|
||||||
irc.reply('Successfully logged in as %s.' % username)
|
irc.reply('Successfully logged in as %s.' % username)
|
||||||
log.info("(%s) Successful login to %r by %s",
|
log.info("(%s) Successful login to %r by %s",
|
||||||
irc.name, username, irc.getHostmask(source))
|
irc.name, username, irc.get_hostmask(source))
|
||||||
|
|
||||||
def _loginfail(irc, source, username):
|
def _loginfail(irc, source, username):
|
||||||
"""Internal function to process login failures."""
|
"""Internal function to process login failures."""
|
||||||
irc.error('Incorrect credentials.')
|
irc.error('Incorrect credentials.')
|
||||||
log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.getHostmask(source))
|
log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.get_hostmask(source))
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
def identify(irc, source, args):
|
def identify(irc, source, args):
|
||||||
@ -86,7 +86,7 @@ def load(irc, source, args):
|
|||||||
if name in world.plugins:
|
if name in world.plugins:
|
||||||
irc.reply("Error: %r is already loaded." % name)
|
irc.reply("Error: %r is already loaded." % name)
|
||||||
return
|
return
|
||||||
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.getHostmask(source))
|
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
|
||||||
try:
|
try:
|
||||||
world.plugins[name] = pl = utils.loadPlugin(name)
|
world.plugins[name] = pl = utils.loadPlugin(name)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@ -119,7 +119,7 @@ def unload(irc, source, args):
|
|||||||
modulename = utils.PLUGIN_PREFIX + name
|
modulename = utils.PLUGIN_PREFIX + name
|
||||||
|
|
||||||
if name in world.plugins:
|
if name in world.plugins:
|
||||||
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.getHostmask(source))
|
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
|
||||||
pl = world.plugins[name]
|
pl = world.plugins[name]
|
||||||
log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))
|
log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ def account(irc, host, uid):
|
|||||||
homenet, realuid)
|
homenet, realuid)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
slogin = irc.toLower(userobj.services_account)
|
slogin = irc.to_lower(userobj.services_account)
|
||||||
|
|
||||||
# Split the given exttarget host into parts, so we know how many to look for.
|
# Split the given exttarget host into parts, so we know how many to look for.
|
||||||
groups = list(map(irc.toLower, host.split(':')))
|
groups = list(map(irc.to_lower, host.split(':')))
|
||||||
log.debug('(%s) exttargets.account: groups to match: %s', irc.name, groups)
|
log.debug('(%s) exttargets.account: groups to match: %s', irc.name, groups)
|
||||||
|
|
||||||
if len(groups) == 1:
|
if len(groups) == 1:
|
||||||
@ -74,10 +74,10 @@ def ircop(irc, host, uid):
|
|||||||
|
|
||||||
if len(groups) == 1:
|
if len(groups) == 1:
|
||||||
# 1st scenario.
|
# 1st scenario.
|
||||||
return irc.isOper(uid, allowAuthed=False)
|
return irc.is_oper(uid, allowAuthed=False)
|
||||||
else:
|
else:
|
||||||
# 2nd scenario. Use matchHost (ircmatch) to match the opertype glob to the opertype.
|
# 2nd scenario. Use match_host (ircmatch) to match the opertype glob to the opertype.
|
||||||
return irc.matchHost(groups[1], irc.users[uid].opertype)
|
return irc.match_host(groups[1], irc.users[uid].opertype)
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
def server(irc, host, uid):
|
def server(irc, host, uid):
|
||||||
@ -93,10 +93,10 @@ def server(irc, host, uid):
|
|||||||
log.debug('(%s) exttargets.server: groups to match: %s', irc.name, groups)
|
log.debug('(%s) exttargets.server: groups to match: %s', irc.name, groups)
|
||||||
|
|
||||||
if len(groups) >= 2:
|
if len(groups) >= 2:
|
||||||
sid = irc.getServer(uid)
|
sid = irc.get_server(uid)
|
||||||
query = groups[1]
|
query = groups[1]
|
||||||
# Return True if the SID matches the query or the server's name glob matches it.
|
# Return True if the SID matches the query or the server's name glob matches it.
|
||||||
return sid == query or irc.matchHost(query, irc.getFriendlyName(sid))
|
return sid == query or irc.match_host(query, irc.get_friendly_name(sid))
|
||||||
# $server alone is invalid. Don't match anything.
|
# $server alone is invalid. Don't match anything.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ def channel(irc, host, uid):
|
|||||||
return uid in irc.channels[channel].users
|
return uid in irc.channels[channel].users
|
||||||
elif len(groups) >= 3:
|
elif len(groups) >= 3:
|
||||||
# For things like #channel:op, check if the query is in the user's prefix modes.
|
# For things like #channel:op, check if the query is in the user's prefix modes.
|
||||||
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].getPrefixModes(uid))
|
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].get_prefix_modes(uid))
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
def pylinkacc(irc, host, uid):
|
def pylinkacc(irc, host, uid):
|
||||||
@ -134,8 +134,8 @@ def pylinkacc(irc, host, uid):
|
|||||||
$pylinkacc -> Returns True if the target is logged in to PyLink.
|
$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.
|
$pylinkacc:accountname -> Returns True if the target's PyLink login matches the one given.
|
||||||
"""
|
"""
|
||||||
login = irc.toLower(irc.users[uid].account)
|
login = irc.to_lower(irc.users[uid].account)
|
||||||
groups = list(map(irc.toLower, host.split(':')))
|
groups = list(map(irc.to_lower, host.split(':')))
|
||||||
log.debug('(%s) exttargets.pylinkacc: groups to match: %s', irc.name, groups)
|
log.debug('(%s) exttargets.pylinkacc: groups to match: %s', irc.name, groups)
|
||||||
|
|
||||||
if len(groups) == 1:
|
if len(groups) == 1:
|
||||||
@ -187,6 +187,6 @@ def exttarget_and(irc, host, uid):
|
|||||||
targets = targets[1:-1]
|
targets = targets[1:-1]
|
||||||
targets = list(filter(None, targets.split('+')))
|
targets = list(filter(None, targets.split('+')))
|
||||||
log.debug('exttargets_and: using raw subtargets list %r (original query=%r)', targets, host)
|
log.debug('exttargets_and: using raw subtargets list %r (original query=%r)', targets, host)
|
||||||
# Wrap every subtarget into irc.matchHost and return True if all subtargets return True.
|
# Wrap every subtarget into irc.match_host and return True if all subtargets return True.
|
||||||
return all(map(lambda sub_exttarget: irc.matchHost(sub_exttarget, uid), targets))
|
return all(map(lambda sub_exttarget: irc.match_host(sub_exttarget, uid), targets))
|
||||||
world.exttarget_handlers['and'] = exttarget_and
|
world.exttarget_handlers['and'] = exttarget_and
|
||||||
|
@ -11,10 +11,10 @@ def handle_whois(irc, source, command, args):
|
|||||||
target = args['target']
|
target = args['target']
|
||||||
user = irc.users.get(target)
|
user = irc.users.get(target)
|
||||||
|
|
||||||
f = lambda num, source, text: irc.proto.numeric(irc.sid, num, source, text)
|
f = lambda num, source, text: irc.numeric(irc.sid, num, source, text)
|
||||||
|
|
||||||
# Get the server that the target is on.
|
# Get the server that the target is on.
|
||||||
server = irc.getServer(target)
|
server = irc.get_server(target)
|
||||||
|
|
||||||
if user is None: # User doesn't exist
|
if user is None: # User doesn't exist
|
||||||
# <- :42X 401 7PYAAAAAB GL- :No such nick/channel
|
# <- :42X 401 7PYAAAAAB GL- :No such nick/channel
|
||||||
@ -22,7 +22,7 @@ def handle_whois(irc, source, command, args):
|
|||||||
f(401, source, "%s :No such nick/channel" % nick)
|
f(401, source, "%s :No such nick/channel" % nick)
|
||||||
else:
|
else:
|
||||||
nick = user.nick
|
nick = user.nick
|
||||||
sourceisOper = ('o', None) in irc.users[source].modes
|
sourceis_oper = ('o', None) in irc.users[source].modes
|
||||||
sourceisBot = (irc.umodes.get('bot'), None) in irc.users[source].modes
|
sourceisBot = (irc.umodes.get('bot'), None) in irc.users[source].modes
|
||||||
|
|
||||||
# Get the full network name.
|
# Get the full network name.
|
||||||
@ -35,7 +35,7 @@ def handle_whois(irc, source, command, args):
|
|||||||
# 319: RPL_WHOISCHANNELS; Show public channels of the target, respecting
|
# 319: RPL_WHOISCHANNELS; Show public channels of the target, respecting
|
||||||
# hidechans umodes for non-oper callers.
|
# hidechans umodes for non-oper callers.
|
||||||
isHideChans = (irc.umodes.get('hidechans'), None) in user.modes
|
isHideChans = (irc.umodes.get('hidechans'), None) in user.modes
|
||||||
if (not isHideChans) or (isHideChans and sourceisOper):
|
if (not isHideChans) or (isHideChans and sourceis_oper):
|
||||||
public_chans = []
|
public_chans = []
|
||||||
for chan in user.channels:
|
for chan in user.channels:
|
||||||
c = irc.channels[chan]
|
c = irc.channels[chan]
|
||||||
@ -44,11 +44,11 @@ def handle_whois(irc, source, command, args):
|
|||||||
|
|
||||||
if ((irc.cmodes.get('secret'), None) in c.modes or \
|
if ((irc.cmodes.get('secret'), None) in c.modes or \
|
||||||
(irc.cmodes.get('private'), None) in c.modes) \
|
(irc.cmodes.get('private'), None) in c.modes) \
|
||||||
and not (sourceisOper or source in c.users):
|
and not (sourceis_oper or source in c.users):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Show the highest prefix mode like a regular IRCd does, if there are any.
|
# Show the highest prefix mode like a regular IRCd does, if there are any.
|
||||||
prefixes = c.getPrefixModes(target)
|
prefixes = c.get_prefix_modes(target)
|
||||||
if prefixes:
|
if prefixes:
|
||||||
highest = prefixes[-1]
|
highest = prefixes[-1]
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ def handle_whois(irc, source, command, args):
|
|||||||
# 2) +H is set, but the caller is oper
|
# 2) +H is set, but the caller is oper
|
||||||
# 3) +H is set, but whois_use_hideoper is disabled in config
|
# 3) +H is set, but whois_use_hideoper is disabled in config
|
||||||
isHideOper = (irc.umodes.get('hideoper'), None) in user.modes
|
isHideOper = (irc.umodes.get('hideoper'), None) in user.modes
|
||||||
if (not isHideOper) or (isHideOper and sourceisOper) or \
|
if (not isHideOper) or (isHideOper and sourceis_oper) or \
|
||||||
(isHideOper and not conf.conf['bot'].get('whois_use_hideoper', True)):
|
(isHideOper and not conf.conf['bot'].get('whois_use_hideoper', True)):
|
||||||
# Let's be gramatically correct. (If the opertype starts with a vowel,
|
# Let's be gramatically correct. (If the opertype starts with a vowel,
|
||||||
# write "an Operator" instead of "a Operator")
|
# write "an Operator" instead of "a Operator")
|
||||||
@ -84,9 +84,9 @@ def handle_whois(irc, source, command, args):
|
|||||||
|
|
||||||
# 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd to show user modes.
|
# 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd to show user modes.
|
||||||
# Only show this to opers!
|
# Only show this to opers!
|
||||||
if sourceisOper:
|
if sourceis_oper:
|
||||||
f(378, source, "%s :is connecting from %s@%s %s" % (nick, user.ident, user.realhost, user.ip))
|
f(378, source, "%s :is connecting from %s@%s %s" % (nick, user.ident, user.realhost, user.ip))
|
||||||
f(379, source, '%s :is using modes %s' % (nick, irc.joinModes(user.modes, sort=True)))
|
f(379, source, '%s :is using modes %s' % (nick, irc.join_modes(user.modes, sort=True)))
|
||||||
|
|
||||||
# 301: used to show away information if present
|
# 301: used to show away information if present
|
||||||
away_text = user.away
|
away_text = user.away
|
||||||
@ -101,7 +101,7 @@ def handle_whois(irc, source, command, args):
|
|||||||
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook, unless the
|
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook, unless the
|
||||||
# caller is marked a bot and the whois_show_extensions_to_bots option is False
|
# caller is marked a bot and the whois_show_extensions_to_bots option is False
|
||||||
if (sourceisBot and conf.conf['bot'].get('whois_show_extensions_to_bots')) or (not sourceisBot):
|
if (sourceisBot and conf.conf['bot'].get('whois_show_extensions_to_bots')) or (not sourceisBot):
|
||||||
irc.callHooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
|
irc.call_hooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
|
||||||
else:
|
else:
|
||||||
log.debug('(%s) coremods.handlers.handle_whois: skipping custom whois handlers because '
|
log.debug('(%s) coremods.handlers.handle_whois: skipping custom whois handlers because '
|
||||||
'caller %s is marked as a bot', irc.name, source)
|
'caller %s is marked as a bot', irc.name, source)
|
||||||
@ -116,15 +116,15 @@ def handle_mode(irc, source, command, args):
|
|||||||
modes = args['modes']
|
modes = args['modes']
|
||||||
# If the sender is not a PyLink client, and the target IS a protected
|
# If the sender is not a PyLink client, and the target IS a protected
|
||||||
# client, revert any forced deoper attempts.
|
# client, revert any forced deoper attempts.
|
||||||
if irc.isInternalClient(target) and not irc.isInternalClient(source):
|
if irc.is_internal_client(target) and not irc.is_internal_client(source):
|
||||||
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.isManipulatableClient(target)):
|
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.is_manipulatable_client(target)):
|
||||||
irc.proto.mode(irc.sid, target, {('+o', None)})
|
irc.mode(irc.sid, target, {('+o', None)})
|
||||||
utils.add_hook(handle_mode, 'MODE')
|
utils.add_hook(handle_mode, 'MODE')
|
||||||
|
|
||||||
def handle_operup(irc, source, command, args):
|
def handle_operup(irc, source, command, args):
|
||||||
"""Logs successful oper-ups on networks."""
|
"""Logs successful oper-ups on networks."""
|
||||||
otype = args.get('text', 'IRC Operator')
|
otype = args.get('text', 'IRC Operator')
|
||||||
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.getHostmask(source))
|
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.get_hostmask(source))
|
||||||
irc.users[source].opertype = otype
|
irc.users[source].opertype = otype
|
||||||
|
|
||||||
utils.add_hook(handle_operup, 'CLIENT_OPERED')
|
utils.add_hook(handle_operup, 'CLIENT_OPERED')
|
||||||
@ -143,11 +143,11 @@ def handle_version(irc, source, command, args):
|
|||||||
"""Handles requests for the PyLink server version."""
|
"""Handles requests for the PyLink server version."""
|
||||||
# 351 syntax is usually "<server version>. <server hostname> :<anything else you want to add>
|
# 351 syntax is usually "<server version>. <server hostname> :<anything else you want to add>
|
||||||
fullversion = irc.version()
|
fullversion = irc.version()
|
||||||
irc.proto.numeric(irc.sid, 351, source, fullversion)
|
irc.numeric(irc.sid, 351, source, fullversion)
|
||||||
utils.add_hook(handle_version, 'VERSION')
|
utils.add_hook(handle_version, 'VERSION')
|
||||||
|
|
||||||
def handle_time(irc, source, command, args):
|
def handle_time(irc, source, command, args):
|
||||||
"""Handles requests for the PyLink server time."""
|
"""Handles requests for the PyLink server time."""
|
||||||
timestring = time.ctime()
|
timestring = time.ctime()
|
||||||
irc.proto.numeric(irc.sid, 391, source, '%s :%s' % (irc.hostname(), timestring))
|
irc.numeric(irc.sid, 391, source, '%s :%s' % (irc.hostname(), timestring))
|
||||||
utils.add_hook(handle_time, 'TIME')
|
utils.add_hook(handle_time, 'TIME')
|
||||||
|
@ -60,22 +60,22 @@ def checkPermissions(irc, uid, perms, also_show=[]):
|
|||||||
"""
|
"""
|
||||||
# For old (< 1.1 login blocks):
|
# For old (< 1.1 login blocks):
|
||||||
# If the user is logged in, they automatically have all permissions.
|
# If the user is logged in, they automatically have all permissions.
|
||||||
if irc.matchHost('$pylinkacc', uid) and conf.conf['login'].get('user'):
|
if irc.match_host('$pylinkacc', uid) and conf.conf['login'].get('user'):
|
||||||
log.debug('permissions: overriding permissions check for old-style admin user %s',
|
log.debug('permissions: overriding permissions check for old-style admin user %s',
|
||||||
irc.getHostmask(uid))
|
irc.get_hostmask(uid))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Iterate over all hostmask->permission list mappings.
|
# Iterate over all hostmask->permission list mappings.
|
||||||
for host, permlist in permissions.copy().items():
|
for host, permlist in permissions.copy().items():
|
||||||
log.debug('permissions: permlist for %s: %s', host, permlist)
|
log.debug('permissions: permlist for %s: %s', host, permlist)
|
||||||
if irc.matchHost(host, uid):
|
if irc.match_host(host, uid):
|
||||||
# Now, iterate over all the perms we are looking for.
|
# Now, iterate over all the perms we are looking for.
|
||||||
for perm in permlist:
|
for perm in permlist:
|
||||||
# Use irc.matchHost to expand globs in an IRC-case insensitive and wildcard
|
# Use irc.match_host to expand globs in an IRC-case insensitive and wildcard
|
||||||
# friendly way. e.g. 'xyz.*.#Channel\' will match 'xyz.manage.#channel|' on IRCds
|
# friendly way. e.g. 'xyz.*.#Channel\' will match 'xyz.manage.#channel|' on IRCds
|
||||||
# using the RFC1459 casemapping.
|
# using the RFC1459 casemapping.
|
||||||
log.debug('permissions: checking if %s glob matches anything in %s', perm, permlist)
|
log.debug('permissions: checking if %s glob matches anything in %s', perm, permlist)
|
||||||
if any(irc.matchHost(perm, p) for p in perms):
|
if any(irc.match_host(perm, p) for p in perms):
|
||||||
return True
|
return True
|
||||||
raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" %
|
raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" %
|
||||||
(', '.join(perms+also_show)))
|
(', '.join(perms+also_show)))
|
||||||
|
@ -14,7 +14,7 @@ def spawn_service(irc, source, command, args):
|
|||||||
# Service name
|
# Service name
|
||||||
name = args['name']
|
name = args['name']
|
||||||
|
|
||||||
if name != 'pylink' and not irc.proto.hasCap('can-spawn-clients'):
|
if name != 'pylink' and not irc.has_cap('can-spawn-clients'):
|
||||||
log.debug("(%s) Not spawning service %s because the server doesn't support spawning clients",
|
log.debug("(%s) Not spawning service %s because the server doesn't support spawning clients",
|
||||||
irc.name, name)
|
irc.name, name)
|
||||||
return
|
return
|
||||||
@ -49,16 +49,16 @@ def spawn_service(irc, source, command, args):
|
|||||||
|
|
||||||
# Track the service's UIDs on each network.
|
# Track the service's UIDs on each network.
|
||||||
log.debug('(%s) spawn_service: Using nick %s for service %s', irc.name, nick, name)
|
log.debug('(%s) spawn_service: Using nick %s for service %s', irc.name, nick, name)
|
||||||
u = irc.nickToUid(nick)
|
u = irc.nick_to_uid(nick)
|
||||||
if u and irc.isInternalClient(u): # If an internal client exists, reuse it.
|
if u and irc.is_internal_client(u): # If an internal client exists, reuse it.
|
||||||
log.debug('(%s) spawn_service: Using existing client %s/%s', irc.name, u, nick)
|
log.debug('(%s) spawn_service: Using existing client %s/%s', irc.name, u, nick)
|
||||||
userobj = irc.users[u]
|
userobj = irc.users[u]
|
||||||
else:
|
else:
|
||||||
log.debug('(%s) spawn_service: Spawning new client %s', irc.name, nick)
|
log.debug('(%s) spawn_service: Spawning new client %s', irc.name, nick)
|
||||||
userobj = irc.proto.spawnClient(nick, ident, host, modes=modes, opertype="PyLink Service",
|
userobj = irc.spawn_client(nick, ident, host, modes=modes, opertype="PyLink Service",
|
||||||
manipulatable=sbot.manipulatable)
|
manipulatable=sbot.manipulatable)
|
||||||
|
|
||||||
# Store the service name in the IrcUser object for easier access.
|
# Store the service name in the User object for easier access.
|
||||||
userobj.service = name
|
userobj.service = name
|
||||||
|
|
||||||
sbot.uids[irc.name] = u = userobj.uid
|
sbot.uids[irc.name] = u = userobj.uid
|
||||||
@ -101,7 +101,7 @@ def handle_kill(irc, source, command, args):
|
|||||||
"""Handle KILLs to PyLink service bots, respawning them as needed."""
|
"""Handle KILLs to PyLink service bots, respawning them as needed."""
|
||||||
target = args['target']
|
target = args['target']
|
||||||
userdata = args.get('userdata')
|
userdata = args.get('userdata')
|
||||||
sbot = irc.getServiceBot(target)
|
sbot = irc.get_service_bot(target)
|
||||||
servicename = None
|
servicename = None
|
||||||
|
|
||||||
if userdata and hasattr(userdata, 'service'): # Look for the target's service name attribute
|
if userdata and hasattr(userdata, 'service'): # Look for the target's service name attribute
|
||||||
@ -118,7 +118,7 @@ def handle_kick(irc, source, command, args):
|
|||||||
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
|
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
|
||||||
kicked = args['target']
|
kicked = args['target']
|
||||||
channel = args['channel']
|
channel = args['channel']
|
||||||
sbot = irc.getServiceBot(kicked)
|
sbot = irc.get_service_bot(kicked)
|
||||||
if sbot:
|
if sbot:
|
||||||
sbot.join(irc, channel)
|
sbot.join(irc, channel)
|
||||||
utils.add_hook(handle_kick, 'KICK')
|
utils.add_hook(handle_kick, 'KICK')
|
||||||
@ -128,7 +128,7 @@ def handle_commands(irc, source, command, args):
|
|||||||
target = args['target']
|
target = args['target']
|
||||||
text = args['text']
|
text = args['text']
|
||||||
|
|
||||||
sbot = irc.getServiceBot(target)
|
sbot = irc.get_service_bot(target)
|
||||||
if sbot:
|
if sbot:
|
||||||
sbot.call_cmd(irc, source, text)
|
sbot.call_cmd(irc, source, text)
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ Some hooks do not map directly to IRC commands, but to events that protocol modu
|
|||||||
|
|
||||||
- **PYLINK_CUSTOM_WHOIS**: `{'target': UID1, 'server': SID1}`
|
- **PYLINK_CUSTOM_WHOIS**: `{'target': UID1, 'server': SID1}`
|
||||||
- This hook is called by `coremods/handlers.py` during its WHOIS handling process, to allow plugins to provide custom WHOIS information. The `target` field represents the target UID, while the `server` field represents the SID that should be replying to the WHOIS request. The source of the payload is the user using `/whois`.
|
- This hook is called by `coremods/handlers.py` during its WHOIS handling process, to allow plugins to provide custom WHOIS information. The `target` field represents the target UID, while the `server` field represents the SID that should be replying to the WHOIS request. The source of the payload is the user using `/whois`.
|
||||||
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.proto.numeric()` to reply to the source from the given server.
|
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.numeric()` to reply to the source from the given server.
|
||||||
- This hook replaces the pre-0.8.x fashion of defining custom WHOIS handlers, which was non-standard and poorly documented.
|
- This hook replaces the pre-0.8.x fashion of defining custom WHOIS handlers, which was non-standard and poorly documented.
|
||||||
|
|
||||||
## Commands handled WITHOUT hooks
|
## Commands handled WITHOUT hooks
|
||||||
|
@ -77,7 +77,7 @@ internals](https://github.com/GLolol/PyLink/blob/1.0-beta1/classes.py#L474-L483)
|
|||||||
- **`sjoin`**`(self, server, channel, users, ts=None, modes=set())` - Sends an SJOIN for a group of users to a channel. The sender should always be a Server ID (SID). TS is
|
- **`sjoin`**`(self, server, channel, users, ts=None, modes=set())` - Sends an SJOIN for a group of users to a channel. The sender should always be a Server ID (SID). TS is
|
||||||
optional, and defaults to the one we've stored in the channel state if not given. `users` is a list of `(prefix mode, UID)` pairs. Example uses:
|
optional, and defaults to the one we've stored in the channel state if not given. `users` is a list of `(prefix mode, UID)` pairs. Example uses:
|
||||||
- `sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])`
|
- `sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])`
|
||||||
- `sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])`
|
- `sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])`
|
||||||
|
|
||||||
- **`spawnServer`**`(self, name, sid=None, uplink=None, desc=None)` - Spawns a server off another PyLink server. `desc` (server description) defaults to the one in the config. `uplink` defaults to the main PyLink server, and `sid` (the server ID) is automatically generated if not given. Sanity checks for server name and SID validity ARE done by the protocol module here.
|
- **`spawnServer`**`(self, name, sid=None, uplink=None, desc=None)` - Spawns a server off another PyLink server. `desc` (server description) defaults to the one in the config. `uplink` defaults to the main PyLink server, and `sid` (the server ID) is automatically generated if not given. Sanity checks for server name and SID validity ARE done by the protocol module here.
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ A protocol module should also set the following variables in their protocol clas
|
|||||||
|
|
||||||
A protocol module manipulates the following attributes in the IRC object it is attached to:
|
A protocol module manipulates the following attributes in the IRC object it is attached to:
|
||||||
|
|
||||||
- `self.irc.cmodes` / `self.irc.umodes`: These are mappings of named IRC modes (e.g. `inviteonly` or `moderated`) to a string list of mode letters, that should be either set during link negotiation or hardcoded into the protocol module. There are also special keys: `*A`, `*B`, `*C`, and `*D`, which **must** be set properly with a list of mode characters for that type of mode.
|
- `self.cmodes` / `self.umodes`: These are mappings of named IRC modes (e.g. `inviteonly` or `moderated`) to a string list of mode letters, that should be either set during link negotiation or hardcoded into the protocol module. There are also special keys: `*A`, `*B`, `*C`, and `*D`, which **must** be set properly with a list of mode characters for that type of mode.
|
||||||
- Types of modes are defined as follows (from http://www.irc.org/tech_docs/005.html):
|
- Types of modes are defined as follows (from http://www.irc.org/tech_docs/005.html):
|
||||||
- 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.
|
||||||
- B = Mode that changes a setting and always has a parameter.
|
- B = Mode that changes a setting and always has a parameter.
|
||||||
@ -114,8 +114,8 @@ A protocol module manipulates the following attributes in the IRC object it is a
|
|||||||
- If not defined, these will default to modes defined by RFC 1459: https://github.com/GLolol/PyLink/blob/1.0-beta1/classes.py#L127-L152
|
- If not defined, these will default to modes defined by RFC 1459: https://github.com/GLolol/PyLink/blob/1.0-beta1/classes.py#L127-L152
|
||||||
- An example of mode mapping hardcoding can be found here: https://github.com/GLolol/PyLink/blob/1.0-beta1/protocols/ts6.py#L259-L311
|
- An example of mode mapping hardcoding can be found here: https://github.com/GLolol/PyLink/blob/1.0-beta1/protocols/ts6.py#L259-L311
|
||||||
- You can find a list of supported (named) channel modes [here](channel-modes.csv), and a list of user modes [here](user-modes.csv).
|
- You can find a list of supported (named) channel modes [here](channel-modes.csv), and a list of user modes [here](user-modes.csv).
|
||||||
- `self.irc.prefixmodes`: This defines a mapping of prefix modes (+o, +v, etc.) to their respective mode prefix. This will default to `{'o': '@', 'v': '+'}` (the standard op and voice) if not defined.
|
- `self.prefixmodes`: This defines a mapping of prefix modes (+o, +v, etc.) to their respective mode prefix. This will default to `{'o': '@', 'v': '+'}` (the standard op and voice) if not defined.
|
||||||
- Example: `self.irc.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}`
|
- Example: `self.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}`
|
||||||
|
|
||||||
### Topics
|
### Topics
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ As an example, one protocol module that tweaks this is [`Clientbot`](https://git
|
|||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
* 2017-03-15 (1.2-dev)
|
* 2017-03-15 (1.2-dev)
|
||||||
- Corrected the location of `self.irc.cmodes/umodes/prefixmodes` attributes
|
- Corrected the location of `self.cmodes/umodes/prefixmodes` attributes
|
||||||
- Mention `self.conf_keys` as a special variable for completeness
|
- Mention `self.conf_keys` as a special variable for completeness
|
||||||
* 2017-01-29 (1.2-dev)
|
* 2017-01-29 (1.2-dev)
|
||||||
- NOTICE can now be sent from servers.
|
- NOTICE can now be sent from servers.
|
||||||
|
@ -46,7 +46,7 @@ Command handlers do not return anything and can raise exceptions, which are caug
|
|||||||
|
|
||||||
Plugins receive data from the underlying protocol module, and communicate back using outgoing [command functions](pmodule-spec.md) implemented by the protocol module. They should *never* send raw data directly back to IRC, because that wouldn't be portable across different IRCds.
|
Plugins receive data from the underlying protocol module, and communicate back using outgoing [command functions](pmodule-spec.md) implemented by the protocol module. They should *never* send raw data directly back to IRC, because that wouldn't be portable across different IRCds.
|
||||||
|
|
||||||
These functions are usually called in this fashion: `irc.proto.command(arg1, arg2, ...)`. For example, the command `irc.proto.join('10XAAAAAB', '#bots')` would join a PyLink client with UID `10XAAAAAB` to channel `#bots`.
|
These functions are usually called in this fashion: `irc.command(arg1, arg2, ...)`. For example, the command `irc.join('10XAAAAAB', '#bots')` would join a PyLink client with UID `10XAAAAAB` to channel `#bots`.
|
||||||
|
|
||||||
For sending messages (e.g. replies to commands), simpler forms of:
|
For sending messages (e.g. replies to commands), simpler forms of:
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ def checkAccess(irc, uid, channel, command):
|
|||||||
# - automode.<command>.#channel: ability to <command> automode on the given channel.
|
# - automode.<command>.#channel: ability to <command> automode on the given channel.
|
||||||
# - automode.savedb: ability to save the automode DB.
|
# - automode.savedb: ability to save the automode DB.
|
||||||
log.debug('(%s) Automode: checking access for %s/%s for %s capability on %s', irc.name, uid,
|
log.debug('(%s) Automode: checking access for %s/%s for %s capability on %s', irc.name, uid,
|
||||||
irc.getHostmask(uid), command, channel)
|
irc.get_hostmask(uid), command, channel)
|
||||||
|
|
||||||
baseperm = 'automode.%s' % command
|
baseperm = 'automode.%s' % command
|
||||||
try:
|
try:
|
||||||
@ -99,7 +99,7 @@ def match(irc, channel, uids=None):
|
|||||||
|
|
||||||
for mask, modes in dbentry.items():
|
for mask, modes in dbentry.items():
|
||||||
for uid in uids:
|
for uid in uids:
|
||||||
if irc.matchHost(mask, uid):
|
if irc.match_host(mask, uid):
|
||||||
# User matched a mask. Filter the mode list given to only those that are valid
|
# User matched a mask. Filter the mode list given to only those that are valid
|
||||||
# prefix mode characters.
|
# prefix mode characters.
|
||||||
outgoing_modes += [('+'+mode, uid) for mode in modes if mode in irc.prefixmodes]
|
outgoing_modes += [('+'+mode, uid) for mode in modes if mode in irc.prefixmodes]
|
||||||
@ -113,10 +113,10 @@ def match(irc, channel, uids=None):
|
|||||||
log.debug("(%s) automode: sending modes from modebot_uid %s",
|
log.debug("(%s) automode: sending modes from modebot_uid %s",
|
||||||
irc.name, modebot_uid)
|
irc.name, modebot_uid)
|
||||||
|
|
||||||
irc.proto.mode(modebot_uid, channel, outgoing_modes)
|
irc.mode(modebot_uid, channel, outgoing_modes)
|
||||||
|
|
||||||
# Create a hook payload to support plugins like relay.
|
# Create a hook payload to support plugins like relay.
|
||||||
irc.callHooks([modebot_uid, 'AUTOMODE_MODE',
|
irc.call_hooks([modebot_uid, 'AUTOMODE_MODE',
|
||||||
{'target': channel, 'modes': outgoing_modes, 'parse_as': 'MODE'}])
|
{'target': channel, 'modes': outgoing_modes, 'parse_as': 'MODE'}])
|
||||||
|
|
||||||
def handle_join(irc, source, command, args):
|
def handle_join(irc, source, command, args):
|
||||||
@ -124,7 +124,7 @@ def handle_join(irc, source, command, args):
|
|||||||
Automode JOIN listener. This sets modes accordingly if the person joining matches a mask in the
|
Automode JOIN listener. This sets modes accordingly if the person joining matches a mask in the
|
||||||
ACL.
|
ACL.
|
||||||
"""
|
"""
|
||||||
channel = irc.toLower(args['channel'])
|
channel = irc.to_lower(args['channel'])
|
||||||
match(irc, channel, args['users'])
|
match(irc, channel, args['users'])
|
||||||
|
|
||||||
utils.add_hook(handle_join, 'JOIN')
|
utils.add_hook(handle_join, 'JOIN')
|
||||||
@ -153,7 +153,7 @@ def getChannelPair(irc, source, chanpair, perm=None):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("Invalid channel pair %r" % chanpair)
|
raise ValueError("Invalid channel pair %r" % chanpair)
|
||||||
channel = '#' + channel
|
channel = '#' + channel
|
||||||
channel = irc.toLower(channel)
|
channel = irc.to_lower(channel)
|
||||||
|
|
||||||
assert utils.isChannel(channel), "Invalid channel name %s." % channel
|
assert utils.isChannel(channel), "Invalid channel name %s." % channel
|
||||||
|
|
||||||
@ -202,7 +202,7 @@ def setacc(irc, source, args):
|
|||||||
|
|
||||||
modes = modes.lstrip('+') # remove extraneous leading +'s
|
modes = modes.lstrip('+') # remove extraneous leading +'s
|
||||||
dbentry[mask] = modes
|
dbentry[mask] = modes
|
||||||
log.info('(%s) %s set modes +%s for %s on %s', ircobj.name, irc.getHostmask(source), modes, mask, channel)
|
log.info('(%s) %s set modes +%s for %s on %s', ircobj.name, irc.get_hostmask(source), modes, mask, channel)
|
||||||
reply(irc, "Done. \x02%s\x02 now has modes \x02+%s\x02 in \x02%s\x02." % (mask, modes, channel))
|
reply(irc, "Done. \x02%s\x02 now has modes \x02+%s\x02 in \x02%s\x02." % (mask, modes, channel))
|
||||||
|
|
||||||
# Join the Automode bot to the channel if not explicitly told to.
|
# Join the Automode bot to the channel if not explicitly told to.
|
||||||
@ -233,7 +233,7 @@ def delacc(irc, source, args):
|
|||||||
|
|
||||||
if mask in dbentry:
|
if mask in dbentry:
|
||||||
del dbentry[mask]
|
del dbentry[mask]
|
||||||
log.info('(%s) %s removed modes for %s on %s', ircobj.name, irc.getHostmask(source), mask, channel)
|
log.info('(%s) %s removed modes for %s on %s', ircobj.name, irc.get_hostmask(source), mask, channel)
|
||||||
reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
|
reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
|
||||||
else:
|
else:
|
||||||
error(irc, "No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))
|
error(irc, "No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))
|
||||||
@ -299,7 +299,7 @@ def syncacc(irc, source, args):
|
|||||||
else:
|
else:
|
||||||
ircobj, channel = getChannelPair(irc, source, chanpair, perm='sync')
|
ircobj, channel = getChannelPair(irc, source, chanpair, perm='sync')
|
||||||
|
|
||||||
log.info('(%s) %s synced modes on %s', ircobj.name, irc.getHostmask(source), channel)
|
log.info('(%s) %s synced modes on %s', ircobj.name, irc.get_hostmask(source), channel)
|
||||||
match(ircobj, channel)
|
match(ircobj, channel)
|
||||||
|
|
||||||
reply(irc, 'Done.')
|
reply(irc, 'Done.')
|
||||||
@ -324,7 +324,7 @@ def clearacc(irc, source, args):
|
|||||||
|
|
||||||
if db.get(ircobj.name+channel):
|
if db.get(ircobj.name+channel):
|
||||||
del db[ircobj.name+channel]
|
del db[ircobj.name+channel]
|
||||||
log.info('(%s) %s cleared modes on %s', ircobj.name, irc.getHostmask(source), channel)
|
log.info('(%s) %s cleared modes on %s', ircobj.name, irc.get_hostmask(source), channel)
|
||||||
reply(irc, "Done. Removed all Automode access entries for \x02%s\x02." % channel)
|
reply(irc, "Done. Removed all Automode access entries for \x02%s\x02." % channel)
|
||||||
else:
|
else:
|
||||||
error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
|
error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
|
||||||
|
@ -18,7 +18,7 @@ def spawnclient(irc, source, args):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
irc.error("Not enough arguments. Needs 3: nick, user, host.")
|
irc.error("Not enough arguments. Needs 3: nick, user, host.")
|
||||||
return
|
return
|
||||||
irc.proto.spawnClient(nick, ident, host, manipulatable=True)
|
irc.spawn_client(nick, ident, host, manipulatable=True)
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
@ -33,21 +33,21 @@ def quit(irc, source, args):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).")
|
irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).")
|
||||||
return
|
return
|
||||||
if irc.pseudoclient.uid == irc.nickToUid(nick):
|
if irc.pseudoclient.uid == irc.nick_to_uid(nick):
|
||||||
irc.error("Cannot quit the main PyLink client!")
|
irc.error("Cannot quit the main PyLink client!")
|
||||||
return
|
return
|
||||||
|
|
||||||
u = irc.nickToUid(nick)
|
u = irc.nick_to_uid(nick)
|
||||||
|
|
||||||
quitmsg = ' '.join(args[1:]) or 'Client Quit'
|
quitmsg = ' '.join(args[1:]) or 'Client Quit'
|
||||||
|
|
||||||
if not irc.isManipulatableClient(u):
|
if not irc.is_manipulatable_client(u):
|
||||||
irc.error("Cannot force quit a protected PyLink services client.")
|
irc.error("Cannot force quit a protected PyLink services client.")
|
||||||
return
|
return
|
||||||
|
|
||||||
irc.proto.quit(u, quitmsg)
|
irc.quit(u, quitmsg)
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
|
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
|
||||||
|
|
||||||
def joinclient(irc, source, args):
|
def joinclient(irc, source, args):
|
||||||
"""[<target>] <channel1>[,<channel2>,<channel3>,...]
|
"""[<target>] <channel1>[,<channel2>,<channel3>,...]
|
||||||
@ -63,9 +63,9 @@ def joinclient(irc, source, args):
|
|||||||
try:
|
try:
|
||||||
# Check if the first argument is an existing PyLink client. If it is not,
|
# Check if the first argument is an existing PyLink client. If it is not,
|
||||||
# then assume that the first argument was actually the channels being joined.
|
# then assume that the first argument was actually the channels being joined.
|
||||||
u = irc.nickToUid(args[0])
|
u = irc.nick_to_uid(args[0])
|
||||||
|
|
||||||
if not irc.isInternalClient(u): # First argument isn't one of our clients
|
if not irc.is_internal_client(u): # First argument isn't one of our clients
|
||||||
raise IndexError
|
raise IndexError
|
||||||
|
|
||||||
clist = args[1]
|
clist = args[1]
|
||||||
@ -82,7 +82,7 @@ def joinclient(irc, source, args):
|
|||||||
irc.error("No valid channels given.")
|
irc.error("No valid channels given.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (irc.isManipulatableClient(u) or irc.getServiceBot(u)):
|
if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||||
irc.error("Cannot force join a protected PyLink services client.")
|
irc.error("Cannot force join a protected PyLink services client.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -99,12 +99,12 @@ def joinclient(irc, source, args):
|
|||||||
|
|
||||||
# join() doesn't support prefixes.
|
# join() doesn't support prefixes.
|
||||||
if prefixes:
|
if prefixes:
|
||||||
irc.proto.sjoin(irc.sid, real_channel, [(joinmodes, u)])
|
irc.sjoin(irc.sid, real_channel, [(joinmodes, u)])
|
||||||
else:
|
else:
|
||||||
irc.proto.join(u, real_channel)
|
irc.join(u, real_channel)
|
||||||
|
|
||||||
# Call a join hook manually so other plugins like relay can understand it.
|
# Call a join hook manually so other plugins like relay can understand it.
|
||||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': real_channel, 'users': [u],
|
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': real_channel, 'users': [u],
|
||||||
'modes': irc.channels[real_channel].modes,
|
'modes': irc.channels[real_channel].modes,
|
||||||
'parse_as': 'JOIN'}])
|
'parse_as': 'JOIN'}])
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
@ -128,7 +128,7 @@ def nick(irc, source, args):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1-2: nick (optional), newnick.")
|
irc.error("Not enough arguments. Needs 1-2: nick (optional), newnick.")
|
||||||
return
|
return
|
||||||
u = irc.nickToUid(nick)
|
u = irc.nick_to_uid(nick)
|
||||||
|
|
||||||
if newnick in ('0', u): # Allow /nick 0 to work
|
if newnick in ('0', u): # Allow /nick 0 to work
|
||||||
newnick = u
|
newnick = u
|
||||||
@ -137,14 +137,14 @@ def nick(irc, source, args):
|
|||||||
irc.error('Invalid nickname %r.' % newnick)
|
irc.error('Invalid nickname %r.' % newnick)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif not (irc.isManipulatableClient(u) or irc.getServiceBot(u)):
|
elif not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||||
irc.error("Cannot force nick changes for a protected PyLink services client.")
|
irc.error("Cannot force nick changes for a protected PyLink services client.")
|
||||||
return
|
return
|
||||||
|
|
||||||
irc.proto.nick(u, newnick)
|
irc.nick(u, newnick)
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
# Ditto above: manually send a NICK change hook payload to other plugins.
|
# Ditto above: manually send a NICK change hook payload to other plugins.
|
||||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
|
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
def part(irc, source, args):
|
def part(irc, source, args):
|
||||||
@ -161,8 +161,8 @@ def part(irc, source, args):
|
|||||||
|
|
||||||
# First, check if the first argument is an existing PyLink client. If it is not,
|
# First, check if the first argument is an existing PyLink client. If it is not,
|
||||||
# then assume that the first argument was actually the channels being parted.
|
# then assume that the first argument was actually the channels being parted.
|
||||||
u = irc.nickToUid(nick)
|
u = irc.nick_to_uid(nick)
|
||||||
if not irc.isInternalClient(u): # First argument isn't one of our clients
|
if not irc.is_internal_client(u): # First argument isn't one of our clients
|
||||||
raise IndexError
|
raise IndexError
|
||||||
|
|
||||||
except IndexError: # No nick was given; shift arguments one to the left.
|
except IndexError: # No nick was given; shift arguments one to the left.
|
||||||
@ -180,7 +180,7 @@ def part(irc, source, args):
|
|||||||
irc.error("No valid channels given.")
|
irc.error("No valid channels given.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (irc.isManipulatableClient(u) or irc.getServiceBot(u)):
|
if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||||
irc.error("Cannot force part a protected PyLink services client.")
|
irc.error("Cannot force part a protected PyLink services client.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -188,10 +188,10 @@ def part(irc, source, args):
|
|||||||
if not utils.isChannel(channel):
|
if not utils.isChannel(channel):
|
||||||
irc.error("Invalid channel name %r." % channel)
|
irc.error("Invalid channel name %r." % channel)
|
||||||
return
|
return
|
||||||
irc.proto.part(u, channel, reason)
|
irc.part(u, channel, reason)
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}])
|
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}])
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
def msg(irc, source, args):
|
def msg(irc, source, args):
|
||||||
@ -208,8 +208,8 @@ def msg(irc, source, args):
|
|||||||
|
|
||||||
# First, check if the first argument is an existing PyLink client. If it is not,
|
# First, check if the first argument is an existing PyLink client. If it is not,
|
||||||
# then assume that the first argument was actually the message TARGET.
|
# then assume that the first argument was actually the message TARGET.
|
||||||
sourceuid = irc.nickToUid(msgsource)
|
sourceuid = irc.nick_to_uid(msgsource)
|
||||||
if not irc.isInternalClient(sourceuid): # First argument isn't one of our clients
|
if not irc.is_internal_client(sourceuid): # First argument isn't one of our clients
|
||||||
raise IndexError
|
raise IndexError
|
||||||
|
|
||||||
if not text:
|
if not text:
|
||||||
@ -229,14 +229,14 @@ def msg(irc, source, args):
|
|||||||
|
|
||||||
if not utils.isChannel(target):
|
if not utils.isChannel(target):
|
||||||
# Convert nick of the message target to a UID, if the target isn't a channel
|
# Convert nick of the message target to a UID, if the target isn't a channel
|
||||||
real_target = irc.nickToUid(target)
|
real_target = irc.nick_to_uid(target)
|
||||||
if real_target is None: # Unknown target user, if target isn't a valid channel name
|
if real_target is None: # Unknown target user, if target isn't a valid channel name
|
||||||
irc.error('Unknown user %r.' % target)
|
irc.error('Unknown user %r.' % target)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
real_target = target
|
real_target = target
|
||||||
|
|
||||||
irc.proto.message(sourceuid, real_target, text)
|
irc.message(sourceuid, real_target, text)
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
|
irc.call_hooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
|
||||||
utils.add_cmd(msg, 'say')
|
utils.add_cmd(msg, 'say')
|
||||||
|
@ -19,7 +19,7 @@ def _changehost(irc, target, args):
|
|||||||
|
|
||||||
if target not in irc.users:
|
if target not in irc.users:
|
||||||
return
|
return
|
||||||
elif irc.isInternalClient(target):
|
elif irc.is_internal_client(target):
|
||||||
log.debug('(%s) Skipping changehost on internal client %s', irc.name, target)
|
log.debug('(%s) Skipping changehost on internal client %s', irc.name, target)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ def _changehost(irc, target, args):
|
|||||||
|
|
||||||
for host_glob, host_template in changehost_hosts.items():
|
for host_glob, host_template in changehost_hosts.items():
|
||||||
log.debug('(%s) Changehost: checking mask %s', irc.name, host_glob)
|
log.debug('(%s) Changehost: checking mask %s', irc.name, host_glob)
|
||||||
if irc.matchHost(host_glob, target, ip=match_ip, realhost=match_realhosts):
|
if irc.match_host(host_glob, target, ip=match_ip, realhost=match_realhosts):
|
||||||
log.debug('(%s) Changehost matched mask %s', irc.name, host_glob)
|
log.debug('(%s) Changehost matched mask %s', irc.name, host_glob)
|
||||||
# This uses template strings for simple substitution:
|
# This uses template strings for simple substitution:
|
||||||
# https://docs.python.org/3/library/string.html#template-strings
|
# https://docs.python.org/3/library/string.html#template-strings
|
||||||
@ -78,7 +78,7 @@ def _changehost(irc, target, args):
|
|||||||
if char not in allowed_chars:
|
if char not in allowed_chars:
|
||||||
new_host = new_host.replace(char, '-')
|
new_host = new_host.replace(char, '-')
|
||||||
|
|
||||||
irc.proto.updateClient(target, 'HOST', new_host)
|
irc.update_client(target, 'HOST', new_host)
|
||||||
|
|
||||||
# Only operate on the first match.
|
# Only operate on the first match.
|
||||||
break
|
break
|
||||||
@ -103,13 +103,13 @@ def handle_chghost(irc, sender, command, args):
|
|||||||
|
|
||||||
target = args['target']
|
target = args['target']
|
||||||
|
|
||||||
if (not irc.isInternalClient(sender)) and (not irc.isInternalServer(sender)):
|
if (not irc.is_internal_client(sender)) and (not irc.is_internal_server(sender)):
|
||||||
if irc.name in changehost_conf.get('enforced_nets', []):
|
if irc.name in changehost_conf.get('enforced_nets', []):
|
||||||
log.debug('(%s) Enforce for network is on, re-checking host for target %s/%s',
|
log.debug('(%s) Enforce for network is on, re-checking host for target %s/%s',
|
||||||
irc.name, target, irc.getFriendlyName(target))
|
irc.name, target, irc.get_friendly_name(target))
|
||||||
|
|
||||||
for ex in changehost_conf.get("enforce_exceptions", []):
|
for ex in changehost_conf.get("enforce_exceptions", []):
|
||||||
if irc.matchHost(ex, target):
|
if irc.match_host(ex, target):
|
||||||
log.debug('(%s) Skipping host change for target %s; they are exempted by mask %s',
|
log.debug('(%s) Skipping host change for target %s; they are exempted by mask %s',
|
||||||
irc.name, target, ex)
|
irc.name, target, ex)
|
||||||
return
|
return
|
||||||
|
@ -29,7 +29,7 @@ def status(irc, source, args):
|
|||||||
irc.reply('You are identified as \x02%s\x02.' % identified)
|
irc.reply('You are identified as \x02%s\x02.' % identified)
|
||||||
else:
|
else:
|
||||||
irc.reply('You are not identified as anyone.')
|
irc.reply('You are not identified as anyone.')
|
||||||
irc.reply('Operator access: \x02%s\x02' % bool(irc.isOper(source)))
|
irc.reply('Operator access: \x02%s\x02' % bool(irc.is_oper(source)))
|
||||||
|
|
||||||
_none = '\x1D(none)\x1D'
|
_none = '\x1D(none)\x1D'
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
@ -43,10 +43,10 @@ def showuser(irc, source, args):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1: nick.")
|
irc.error("Not enough arguments. Needs 1: nick.")
|
||||||
return
|
return
|
||||||
u = irc.nickToUid(target) or target
|
u = irc.nick_to_uid(target) or target
|
||||||
# Only show private info if the person is calling 'showuser' on themselves,
|
# Only show private info if the person is calling 'showuser' on themselves,
|
||||||
# or is an oper.
|
# or is an oper.
|
||||||
verbose = irc.isOper(source) or u == source
|
verbose = irc.is_oper(source) or u == source
|
||||||
if u not in irc.users:
|
if u not in irc.users:
|
||||||
irc.error('Unknown user %r.' % target)
|
irc.error('Unknown user %r.' % target)
|
||||||
return
|
return
|
||||||
@ -57,7 +57,7 @@ def showuser(irc, source, args):
|
|||||||
f('Showing information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident,
|
f('Showing information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident,
|
||||||
userobj.host, userobj.realname))
|
userobj.host, userobj.realname))
|
||||||
|
|
||||||
sid = irc.getServer(u)
|
sid = irc.get_server(u)
|
||||||
serverobj = irc.servers[sid]
|
serverobj = irc.servers[sid]
|
||||||
ts = userobj.ts
|
ts = userobj.ts
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ def showuser(irc, source, args):
|
|||||||
|
|
||||||
if verbose: # Oper only data: user modes, channels on, account info, etc.
|
if verbose: # Oper only data: user modes, channels on, account info, etc.
|
||||||
|
|
||||||
f('\x02User modes\x02: %s' % irc.joinModes(userobj.modes, sort=True))
|
f('\x02User modes\x02: %s' % irc.join_modes(userobj.modes, sort=True))
|
||||||
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \
|
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \
|
||||||
(u, userobj.realhost, userobj.ip))
|
(u, userobj.realhost, userobj.ip))
|
||||||
channels = sorted(userobj.channels)
|
channels = sorted(userobj.channels)
|
||||||
@ -83,7 +83,7 @@ def showchan(irc, source, args):
|
|||||||
Shows information about <channel>."""
|
Shows information about <channel>."""
|
||||||
permissions.checkPermissions(irc, source, ['commands.showchan'])
|
permissions.checkPermissions(irc, source, ['commands.showchan'])
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1: channel.")
|
irc.error("Not enough arguments. Needs 1: channel.")
|
||||||
return
|
return
|
||||||
@ -95,7 +95,7 @@ def showchan(irc, source, args):
|
|||||||
|
|
||||||
c = irc.channels[channel]
|
c = irc.channels[channel]
|
||||||
# Only show verbose info if caller is oper or is in the target channel.
|
# Only show verbose info if caller is oper or is in the target channel.
|
||||||
verbose = source in c.users or irc.isOper(source)
|
verbose = source in c.users or irc.is_oper(source)
|
||||||
secret = ('s', None) in c.modes
|
secret = ('s', None) in c.modes
|
||||||
if secret and not verbose:
|
if secret and not verbose:
|
||||||
# Hide secret channels from normal users.
|
# Hide secret channels from normal users.
|
||||||
@ -110,17 +110,17 @@ def showchan(irc, source, args):
|
|||||||
|
|
||||||
# Mark TS values as untrusted on Clientbot and others (where TS is read-only or not trackable)
|
# Mark TS values as untrusted on Clientbot and others (where TS is read-only or not trackable)
|
||||||
f('\x02Channel creation time\x02: %s (%s)%s' % (ctime(c.ts), c.ts,
|
f('\x02Channel creation time\x02: %s (%s)%s' % (ctime(c.ts), c.ts,
|
||||||
' [UNTRUSTED]' if not irc.proto.hasCap('has-ts') else ''))
|
' [UNTRUSTED]' if not irc.has_cap('has-ts') else ''))
|
||||||
|
|
||||||
# Show only modes that aren't list-style modes.
|
# Show only modes that aren't list-style modes.
|
||||||
modes = irc.joinModes([m for m in c.modes if m[0] not in irc.cmodes['*A']], sort=True)
|
modes = irc.join_modes([m for m in c.modes if m[0] not in irc.cmodes['*A']], sort=True)
|
||||||
f('\x02Channel modes\x02: %s' % modes)
|
f('\x02Channel modes\x02: %s' % modes)
|
||||||
if verbose:
|
if verbose:
|
||||||
nicklist = []
|
nicklist = []
|
||||||
# Iterate over the user list, sorted by nick.
|
# Iterate over the user list, sorted by nick.
|
||||||
for user, nick in sorted(zip(c.users, nicks),
|
for user, nick in sorted(zip(c.users, nicks),
|
||||||
key=lambda userpair: userpair[1].lower()):
|
key=lambda userpair: userpair[1].lower()):
|
||||||
for pmode in c.getPrefixModes(user):
|
for pmode in c.get_prefix_modes(user):
|
||||||
# Show prefix modes in order from highest to lowest.
|
# Show prefix modes in order from highest to lowest.
|
||||||
nick = irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '') + nick
|
nick = irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '') + nick
|
||||||
nicklist.append(nick)
|
nicklist.append(nick)
|
||||||
@ -179,7 +179,7 @@ def logout(irc, source, args):
|
|||||||
irc.error("You are not logged in!")
|
irc.error("You are not logged in!")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
otheruid = irc.nickToUid(othernick)
|
otheruid = irc.nick_to_uid(othernick)
|
||||||
if not otheruid:
|
if not otheruid:
|
||||||
irc.error("Unknown user %s." % othernick)
|
irc.error("Unknown user %s." % othernick)
|
||||||
return
|
return
|
||||||
|
@ -18,7 +18,7 @@ def hook_privmsg(irc, source, command, args):
|
|||||||
channel = args['target']
|
channel = args['target']
|
||||||
text = args['text']
|
text = args['text']
|
||||||
|
|
||||||
# irc.pseudoclient stores the IrcUser object of the main PyLink client.
|
# irc.pseudoclient stores the User object of the main PyLink client.
|
||||||
# (i.e. the user defined in the bot: section of the config)
|
# (i.e. the user defined in the bot: section of the config)
|
||||||
if utils.isChannel(channel) and irc.pseudoclient.nick in text:
|
if utils.isChannel(channel) and irc.pseudoclient.nick in text:
|
||||||
irc.msg(channel, 'hi there!')
|
irc.msg(channel, 'hi there!')
|
||||||
|
@ -34,7 +34,7 @@ def _exec(irc, source, args, locals_dict=None):
|
|||||||
return
|
return
|
||||||
|
|
||||||
log.info('(%s) Executing %r for %s', irc.name, args,
|
log.info('(%s) Executing %r for %s', irc.name, args,
|
||||||
irc.getHostmask(source))
|
irc.get_hostmask(source))
|
||||||
if locals_dict is None:
|
if locals_dict is None:
|
||||||
locals_dict = locals()
|
locals_dict = locals()
|
||||||
else:
|
else:
|
||||||
@ -86,7 +86,7 @@ def _eval(irc, source, args, locals_dict=None, pretty_print=False):
|
|||||||
locals_dict['args'] = args
|
locals_dict['args'] = args
|
||||||
|
|
||||||
log.info('(%s) Evaluating %r for %s', irc.name, args,
|
log.info('(%s) Evaluating %r for %s', irc.name, args,
|
||||||
irc.getHostmask(source))
|
irc.get_hostmask(source))
|
||||||
|
|
||||||
result = eval(args, globals(), locals_dict)
|
result = eval(args, globals(), locals_dict)
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ def raw(irc, source, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
log.debug('(%s) Sending raw text %r to IRC for %s', irc.name, args,
|
log.debug('(%s) Sending raw text %r to IRC for %s', irc.name, args,
|
||||||
irc.getHostmask(source))
|
irc.get_hostmask(source))
|
||||||
irc.send(args)
|
irc.send(args)
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
@ -170,5 +170,5 @@ def inject(irc, source, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name,
|
log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name,
|
||||||
args, irc.getHostmask(source))
|
args, irc.get_hostmask(source))
|
||||||
irc.reply(irc.runline(args))
|
irc.reply(irc.runline(args))
|
||||||
|
@ -12,7 +12,7 @@ def handle_fantasy(irc, source, command, args):
|
|||||||
channel = args['target']
|
channel = args['target']
|
||||||
orig_text = args['text']
|
orig_text = args['text']
|
||||||
|
|
||||||
if utils.isChannel(channel) and not irc.isInternalClient(source):
|
if utils.isChannel(channel) and not irc.is_internal_client(source):
|
||||||
# The following conditions must be met for an incoming message for
|
# The following conditions must be met for an incoming message for
|
||||||
# fantasy to trigger:
|
# fantasy to trigger:
|
||||||
# 1) The message target is a channel.
|
# 1) The message target is a channel.
|
||||||
@ -42,7 +42,7 @@ def handle_fantasy(irc, source, command, args):
|
|||||||
|
|
||||||
# If responding to nick is enabled, add variations of the current nick
|
# If responding to nick is enabled, add variations of the current nick
|
||||||
# to the prefix list: "<nick>," and "<nick>:"
|
# to the prefix list: "<nick>," and "<nick>:"
|
||||||
nick = irc.toLower(irc.users[servuid].nick)
|
nick = irc.to_lower(irc.users[servuid].nick)
|
||||||
|
|
||||||
nick_prefixes = [nick+',', nick+':']
|
nick_prefixes = [nick+',', nick+':']
|
||||||
if respondtonick:
|
if respondtonick:
|
||||||
@ -52,7 +52,7 @@ def handle_fantasy(irc, source, command, args):
|
|||||||
# No prefixes were set, so skip.
|
# No prefixes were set, so skip.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
lowered_text = irc.toLower(orig_text)
|
lowered_text = irc.to_lower(orig_text)
|
||||||
for prefix in filter(None, prefixes): # Cycle through the prefixes list we finished with.
|
for prefix in filter(None, prefixes): # Cycle through the prefixes list we finished with.
|
||||||
if lowered_text.startswith(prefix):
|
if lowered_text.startswith(prefix):
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ def g(irc, source, args):
|
|||||||
for name, ircd in world.networkobjects.items():
|
for name, ircd in world.networkobjects.items():
|
||||||
if ircd.connected.is_set(): # Only attempt to send to connected networks
|
if ircd.connected.is_set(): # Only attempt to send to connected networks
|
||||||
for channel in ircd.pseudoclient.channels:
|
for channel in ircd.pseudoclient.channels:
|
||||||
subst = {'sender': irc.getFriendlyName(source),
|
subst = {'sender': irc.get_friendly_name(source),
|
||||||
'network': irc.name,
|
'network': irc.name,
|
||||||
'fullnetwork': irc.getFullNetworkName(),
|
'fullnetwork': irc.get_full_network_name(),
|
||||||
'current_channel': channel,
|
'current_channel': channel,
|
||||||
'current_network': ircd.name,
|
'current_network': ircd.name,
|
||||||
'current_fullnetwork': ircd.getFullNetworkName(),
|
'current_fullnetwork': ircd.get_full_network_name(),
|
||||||
'text': message}
|
'text': message}
|
||||||
|
|
||||||
# Disable relaying or other plugins handling the global message.
|
# Disable relaying or other plugins handling the global message.
|
||||||
|
@ -27,10 +27,10 @@ def checkban(irc, source, args):
|
|||||||
|
|
||||||
results = 0
|
results = 0
|
||||||
for uid, userobj in irc.users.copy().items():
|
for uid, userobj in irc.users.copy().items():
|
||||||
if irc.matchHost(banmask, uid):
|
if irc.match_host(banmask, uid):
|
||||||
if results < 50: # XXX rather arbitrary limit
|
if results < 50: # XXX rather arbitrary limit
|
||||||
s = "\x02%s\x02 (%s@%s) [%s] {\x02%s\x02}" % (userobj.nick, userobj.ident,
|
s = "\x02%s\x02 (%s@%s) [%s] {\x02%s\x02}" % (userobj.nick, userobj.ident,
|
||||||
userobj.host, userobj.realname, irc.getFriendlyName(irc.getServer(uid)))
|
userobj.host, userobj.realname, irc.get_friendly_name(irc.get_server(uid)))
|
||||||
|
|
||||||
# Always reply in private to prevent information leaks.
|
# Always reply in private to prevent information leaks.
|
||||||
irc.reply(s, private=True)
|
irc.reply(s, private=True)
|
||||||
@ -42,9 +42,9 @@ def checkban(irc, source, args):
|
|||||||
else:
|
else:
|
||||||
irc.msg(source, "No results found.", notice=True)
|
irc.msg(source, "No results found.", notice=True)
|
||||||
else:
|
else:
|
||||||
# Target can be both a nick (of an online user) or a hostmask. irc.matchHost() handles this
|
# Target can be both a nick (of an online user) or a hostmask. irc.match_host() handles this
|
||||||
# automatically.
|
# automatically.
|
||||||
if irc.matchHost(banmask, targetmask):
|
if irc.match_host(banmask, targetmask):
|
||||||
irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (targetmask, banmask))
|
irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (targetmask, banmask))
|
||||||
else:
|
else:
|
||||||
irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (targetmask, banmask))
|
irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (targetmask, banmask))
|
||||||
@ -61,7 +61,7 @@ def jupe(irc, source, args):
|
|||||||
try:
|
try:
|
||||||
servername = args[0]
|
servername = args[0]
|
||||||
reason = ' '.join(args[1:]) or "No reason given"
|
reason = ' '.join(args[1:]) or "No reason given"
|
||||||
desc = "Juped by %s: [%s]" % (irc.getHostmask(source), reason)
|
desc = "Juped by %s: [%s]" % (irc.get_hostmask(source), reason)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error('Not enough arguments. Needs 1-2: servername, reason (optional).')
|
irc.error('Not enough arguments. Needs 1-2: servername, reason (optional).')
|
||||||
return
|
return
|
||||||
@ -70,9 +70,9 @@ def jupe(irc, source, args):
|
|||||||
irc.error("Invalid server name '%s'." % servername)
|
irc.error("Invalid server name '%s'." % servername)
|
||||||
return
|
return
|
||||||
|
|
||||||
sid = irc.proto.spawnServer(servername, desc=desc)
|
sid = irc.spawn_server(servername, desc=desc)
|
||||||
|
|
||||||
irc.callHooks([irc.pseudoclient.uid, 'OPERCMDS_SPAWNSERVER',
|
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_SPAWNSERVER',
|
||||||
{'name': servername, 'sid': sid, 'text': desc}])
|
{'name': servername, 'sid': sid, 'text': desc}])
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
@ -85,14 +85,14 @@ def kick(irc, source, args):
|
|||||||
Admin only. Kicks <user> from the specified channel."""
|
Admin only. Kicks <user> from the specified channel."""
|
||||||
permissions.checkPermissions(irc, source, ['opercmds.kick'])
|
permissions.checkPermissions(irc, source, ['opercmds.kick'])
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
target = args[1]
|
target = args[1]
|
||||||
reason = ' '.join(args[2:])
|
reason = ' '.join(args[2:])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 2-3: channel, target, reason (optional).")
|
irc.error("Not enough arguments. Needs 2-3: channel, target, reason (optional).")
|
||||||
return
|
return
|
||||||
|
|
||||||
targetu = irc.nickToUid(target)
|
targetu = irc.nick_to_uid(target)
|
||||||
|
|
||||||
if channel not in irc.channels: # KICK only works on channels that exist.
|
if channel not in irc.channels: # KICK only works on channels that exist.
|
||||||
irc.error("Unknown channel %r." % channel)
|
irc.error("Unknown channel %r." % channel)
|
||||||
@ -104,9 +104,9 @@ def kick(irc, source, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
sender = irc.pseudoclient.uid
|
sender = irc.pseudoclient.uid
|
||||||
irc.proto.kick(sender, channel, targetu, reason)
|
irc.kick(sender, channel, targetu, reason)
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([sender, 'CHANCMDS_KICK', {'channel': channel, 'target': targetu,
|
irc.call_hooks([sender, 'CHANCMDS_KICK', {'channel': channel, 'target': targetu,
|
||||||
'text': reason, 'parse_as': 'KICK'}])
|
'text': reason, 'parse_as': 'KICK'}])
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
@ -124,7 +124,7 @@ def kill(irc, source, args):
|
|||||||
|
|
||||||
# Convert the source and target nicks to UIDs.
|
# Convert the source and target nicks to UIDs.
|
||||||
sender = irc.pseudoclient.uid
|
sender = irc.pseudoclient.uid
|
||||||
targetu = irc.nickToUid(target)
|
targetu = irc.nick_to_uid(target)
|
||||||
userdata = irc.users.get(targetu)
|
userdata = irc.users.get(targetu)
|
||||||
|
|
||||||
if targetu not in irc.users:
|
if targetu not in irc.users:
|
||||||
@ -132,13 +132,13 @@ def kill(irc, source, args):
|
|||||||
irc.error("No such nick '%s'." % target)
|
irc.error("No such nick '%s'." % target)
|
||||||
return
|
return
|
||||||
|
|
||||||
irc.proto.kill(sender, targetu, reason)
|
irc.kill(sender, targetu, reason)
|
||||||
|
|
||||||
# Format the kill reason properly in hooks.
|
# Format the kill reason properly in hooks.
|
||||||
reason = "Killed (%s (%s))" % (irc.getFriendlyName(sender), reason)
|
reason = "Killed (%s (%s))" % (irc.get_friendly_name(sender), reason)
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([sender, 'CHANCMDS_KILL', {'target': targetu, 'text': reason,
|
irc.call_hooks([sender, 'CHANCMDS_KILL', {'target': targetu, 'text': reason,
|
||||||
'userdata': userdata, 'parse_as': 'KILL'}])
|
'userdata': userdata, 'parse_as': 'KILL'}])
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
@ -164,7 +164,7 @@ def mode(irc, source, args):
|
|||||||
irc.error("No valid modes were given.")
|
irc.error("No valid modes were given.")
|
||||||
return
|
return
|
||||||
|
|
||||||
parsedmodes = irc.parseModes(target, modes)
|
parsedmodes = irc.parse_modes(target, modes)
|
||||||
|
|
||||||
if not parsedmodes:
|
if not parsedmodes:
|
||||||
# Modes were given but they failed to parse into anything meaningful.
|
# Modes were given but they failed to parse into anything meaningful.
|
||||||
@ -173,10 +173,10 @@ def mode(irc, source, args):
|
|||||||
irc.error("No valid modes were given.")
|
irc.error("No valid modes were given.")
|
||||||
return
|
return
|
||||||
|
|
||||||
irc.proto.mode(irc.pseudoclient.uid, target, parsedmodes)
|
irc.mode(irc.pseudoclient.uid, target, parsedmodes)
|
||||||
|
|
||||||
# Call the appropriate hooks for plugins like relay.
|
# Call the appropriate hooks for plugins like relay.
|
||||||
irc.callHooks([irc.pseudoclient.uid, 'OPERCMDS_MODEOVERRIDE',
|
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MODEOVERRIDE',
|
||||||
{'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}])
|
{'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}])
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
@ -198,9 +198,9 @@ def topic(irc, source, args):
|
|||||||
irc.error("Unknown channel %r." % channel)
|
irc.error("Unknown channel %r." % channel)
|
||||||
return
|
return
|
||||||
|
|
||||||
irc.proto.topic(irc.pseudoclient.uid, channel, topic)
|
irc.topic(irc.pseudoclient.uid, channel, topic)
|
||||||
|
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
irc.callHooks([irc.pseudoclient.uid, 'CHANCMDS_TOPIC',
|
irc.call_hooks([irc.pseudoclient.uid, 'CHANCMDS_TOPIC',
|
||||||
{'channel': channel, 'text': topic, 'setter': source,
|
{'channel': channel, 'text': topic, 'setter': source,
|
||||||
'parse_as': 'TOPIC'}])
|
'parse_as': 'TOPIC'}])
|
||||||
|
195
plugins/relay.py
195
plugins/relay.py
@ -106,7 +106,7 @@ def normalize_nick(irc, netname, nick, times_tagged=0, uid=''):
|
|||||||
forcetag_nicks = conf.conf.get('relay', {}).get('forcetag_nicks', [])
|
forcetag_nicks = conf.conf.get('relay', {}).get('forcetag_nicks', [])
|
||||||
log.debug('(%s) relay.normalize_nick: checking if globs %s match %s.', irc.name, forcetag_nicks, nick)
|
log.debug('(%s) relay.normalize_nick: checking if globs %s match %s.', irc.name, forcetag_nicks, nick)
|
||||||
for glob in forcetag_nicks:
|
for glob in forcetag_nicks:
|
||||||
if irc.matchHost(glob, nick):
|
if irc.match_host(glob, nick):
|
||||||
# User matched a nick to force tag nicks for. Tag them.
|
# User matched a nick to force tag nicks for. Tag them.
|
||||||
times_tagged = 1
|
times_tagged = 1
|
||||||
break
|
break
|
||||||
@ -118,7 +118,7 @@ def normalize_nick(irc, netname, nick, times_tagged=0, uid=''):
|
|||||||
# Charybdis, IRCu, etc. don't allow / in nicks, and will SQUIT with a protocol
|
# Charybdis, IRCu, etc. don't allow / in nicks, and will SQUIT with a protocol
|
||||||
# violation if it sees one. Or it might just ignore the client introduction and
|
# violation if it sees one. Or it might just ignore the client introduction and
|
||||||
# cause bad desyncs.
|
# cause bad desyncs.
|
||||||
protocol_allows_slashes = irc.proto.hasCap('slash-in-nicks') or \
|
protocol_allows_slashes = irc.has_cap('slash-in-nicks') or \
|
||||||
irc.serverdata.get('relay_force_slashes')
|
irc.serverdata.get('relay_force_slashes')
|
||||||
|
|
||||||
if '/' not in separator or not protocol_allows_slashes:
|
if '/' not in separator or not protocol_allows_slashes:
|
||||||
@ -154,7 +154,7 @@ def normalize_nick(irc, netname, nick, times_tagged=0, uid=''):
|
|||||||
if char not in allowed_chars:
|
if char not in allowed_chars:
|
||||||
nick = nick.replace(char, fallback_separator)
|
nick = nick.replace(char, fallback_separator)
|
||||||
|
|
||||||
while irc.nickToUid(nick) and irc.nickToUid(nick) != uid:
|
while irc.nick_to_uid(nick) and irc.nick_to_uid(nick) != uid:
|
||||||
# The nick we want exists: Increase the separator length by 1 if the user was already
|
# The nick we want exists: Increase the separator length by 1 if the user was already
|
||||||
# tagged, but couldn't be created due to a nick conflict. This can happen when someone
|
# tagged, but couldn't be created due to a nick conflict. This can happen when someone
|
||||||
# steals a relay user's nick.
|
# steals a relay user's nick.
|
||||||
@ -177,11 +177,11 @@ def normalize_host(irc, host):
|
|||||||
log.debug('(%s) relay.normalize_host: IRCd=%s, host=%s', irc.name, irc.protoname, host)
|
log.debug('(%s) relay.normalize_host: IRCd=%s, host=%s', irc.name, irc.protoname, host)
|
||||||
|
|
||||||
allowed_chars = string.ascii_letters + string.digits + '-.:'
|
allowed_chars = string.ascii_letters + string.digits + '-.:'
|
||||||
if irc.proto.hasCap('slash-in-hosts'):
|
if irc.has_cap('slash-in-hosts'):
|
||||||
# UnrealIRCd and IRCd-Hybrid don't allow slashes in hostnames
|
# UnrealIRCd and IRCd-Hybrid don't allow slashes in hostnames
|
||||||
allowed_chars += '/'
|
allowed_chars += '/'
|
||||||
|
|
||||||
if irc.proto.hasCap('underscore-in-hosts'):
|
if irc.has_cap('underscore-in-hosts'):
|
||||||
# Most IRCds allow _ in hostnames, but hybrid/charybdis/ratbox IRCds do not.
|
# Most IRCds allow _ in hostnames, but hybrid/charybdis/ratbox IRCds do not.
|
||||||
allowed_chars += '_'
|
allowed_chars += '_'
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ def get_prefix_modes(irc, remoteirc, channel, user, mlist=None):
|
|||||||
# Note: reverse the order so prefix modes are bursted in their traditional order
|
# Note: reverse the order so prefix modes are bursted in their traditional order
|
||||||
# (e.g. owner before op before halfop). TODO: SJOIN modes should probably be
|
# (e.g. owner before op before halfop). TODO: SJOIN modes should probably be
|
||||||
# consistently sorted IRCd-side.
|
# consistently sorted IRCd-side.
|
||||||
for pmode in reversed(irc.channels[channel].getPrefixModes(user, prefixmodes=mlist)):
|
for pmode in reversed(irc.channels[channel].get_prefix_modes(user, prefixmodes=mlist)):
|
||||||
if pmode in remoteirc.cmodes:
|
if pmode in remoteirc.cmodes:
|
||||||
modes += remoteirc.cmodes[pmode]
|
modes += remoteirc.cmodes[pmode]
|
||||||
return modes
|
return modes
|
||||||
@ -220,9 +220,9 @@ def spawn_relay_server(irc, remoteirc):
|
|||||||
suffix = conf.conf.get('relay', {}).get('server_suffix', 'relay')
|
suffix = conf.conf.get('relay', {}).get('server_suffix', 'relay')
|
||||||
# Strip any leading or trailing .'s
|
# Strip any leading or trailing .'s
|
||||||
suffix = suffix.strip('.')
|
suffix = suffix.strip('.')
|
||||||
sid = irc.proto.spawnServer('%s.%s' % (remoteirc.name, suffix),
|
sid = irc.spawn_server('%s.%s' % (remoteirc.name, suffix),
|
||||||
desc="PyLink Relay network - %s" %
|
desc="PyLink Relay network - %s" %
|
||||||
(remoteirc.getFullNetworkName()), endburst_delay=3)
|
(remoteirc.get_full_network_name()), endburst_delay=3)
|
||||||
except (RuntimeError, ValueError): # Network not initialized yet, or a server name conflict.
|
except (RuntimeError, ValueError): # Network not initialized yet, or a server name conflict.
|
||||||
log.exception('(%s) Failed to spawn server for %r (possible jupe?):',
|
log.exception('(%s) Failed to spawn server for %r (possible jupe?):',
|
||||||
irc.name, remoteirc.name)
|
irc.name, remoteirc.name)
|
||||||
@ -298,7 +298,7 @@ def spawn_relay_user(irc, remoteirc, user, times_tagged=0):
|
|||||||
else:
|
else:
|
||||||
opertype = 'IRC Operator'
|
opertype = 'IRC Operator'
|
||||||
|
|
||||||
opertype += ' (on %s)' % irc.getFullNetworkName()
|
opertype += ' (on %s)' % irc.get_full_network_name()
|
||||||
|
|
||||||
# Set hideoper on remote opers, to prevent inflating
|
# Set hideoper on remote opers, to prevent inflating
|
||||||
# /lusers and various /stats
|
# /lusers and various /stats
|
||||||
@ -328,14 +328,14 @@ def spawn_relay_user(irc, remoteirc, user, times_tagged=0):
|
|||||||
realhost = None
|
realhost = None
|
||||||
ip = '0.0.0.0'
|
ip = '0.0.0.0'
|
||||||
|
|
||||||
u = remoteirc.proto.spawnClient(nick, ident=ident, host=host, realname=realname, modes=modes,
|
u = remoteirc.spawn_client(nick, ident=ident, host=host, realname=realname, modes=modes,
|
||||||
opertype=opertype, server=rsid, ip=ip, realhost=realhost).uid
|
opertype=opertype, server=rsid, ip=ip, realhost=realhost).uid
|
||||||
try:
|
try:
|
||||||
remoteirc.users[u].remote = (irc.name, user)
|
remoteirc.users[u].remote = (irc.name, user)
|
||||||
remoteirc.users[u].opertype = opertype
|
remoteirc.users[u].opertype = opertype
|
||||||
away = userobj.away
|
away = userobj.away
|
||||||
if away:
|
if away:
|
||||||
remoteirc.proto.away(u, away)
|
remoteirc.away(u, away)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# User got killed somehow while we were setting options on it.
|
# User got killed somehow while we were setting options on it.
|
||||||
# This is probably being done by the uplink, due to something like an
|
# This is probably being done by the uplink, due to something like an
|
||||||
@ -353,7 +353,7 @@ def get_remote_user(irc, remoteirc, user, spawn_if_missing=True, times_tagged=0)
|
|||||||
# 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.wait(TCONDITION_TIMEOUT):
|
||||||
# Don't spawn clones for registered service bots.
|
# Don't spawn clones for registered service bots.
|
||||||
sbot = irc.getServiceBot(user)
|
sbot = irc.get_service_bot(user)
|
||||||
if sbot:
|
if sbot:
|
||||||
return sbot.uids.get(remoteirc.name)
|
return sbot.uids.get(remoteirc.name)
|
||||||
|
|
||||||
@ -480,10 +480,9 @@ def initialize_channel(irc, channel):
|
|||||||
# 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(irc.sid, channel, topic)
|
irc.topic_burst(irc.sid, 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) relay.initialize_channel: joining our (%s) users: %s', irc.name, remotenet, irc.channels[channel].users)
|
|
||||||
relay_joins(irc, channel, irc.channels[channel].users, irc.channels[channel].ts)
|
relay_joins(irc, channel, irc.channels[channel].users, irc.channels[channel].ts)
|
||||||
|
|
||||||
if 'pylink' in world.services:
|
if 'pylink' in world.services:
|
||||||
@ -497,7 +496,7 @@ def remove_channel(irc, channel):
|
|||||||
if channel not in map(str.lower, irc.serverdata.get('channels', [])):
|
if channel not in map(str.lower, irc.serverdata.get('channels', [])):
|
||||||
world.services['pylink'].extra_channels[irc.name].discard(channel)
|
world.services['pylink'].extra_channels[irc.name].discard(channel)
|
||||||
if irc.pseudoclient:
|
if irc.pseudoclient:
|
||||||
irc.proto.part(irc.pseudoclient.uid, channel, 'Channel delinked.')
|
irc.part(irc.pseudoclient.uid, channel, 'Channel delinked.')
|
||||||
|
|
||||||
relay = get_relay((irc.name, channel))
|
relay = get_relay((irc.name, channel))
|
||||||
if relay:
|
if relay:
|
||||||
@ -509,12 +508,12 @@ def remove_channel(irc, channel):
|
|||||||
if user == irc.pseudoclient.uid and channel in \
|
if user == irc.pseudoclient.uid and channel in \
|
||||||
irc.serverdata.get('channels', []):
|
irc.serverdata.get('channels', []):
|
||||||
continue
|
continue
|
||||||
irc.proto.part(user, channel, 'Channel delinked.')
|
irc.part(user, channel, 'Channel delinked.')
|
||||||
# Don't ever quit it either...
|
# Don't ever quit it either...
|
||||||
if user != irc.pseudoclient.uid and not irc.users[user].channels:
|
if user != irc.pseudoclient.uid and not irc.users[user].channels:
|
||||||
remoteuser = get_orig_user(irc, user)
|
remoteuser = get_orig_user(irc, user)
|
||||||
del relayusers[remoteuser][irc.name]
|
del relayusers[remoteuser][irc.name]
|
||||||
irc.proto.quit(user, 'Left all shared channels.')
|
irc.quit(user, 'Left all shared channels.')
|
||||||
|
|
||||||
def check_claim(irc, channel, sender, chanobj=None):
|
def check_claim(irc, channel, sender, chanobj=None):
|
||||||
"""
|
"""
|
||||||
@ -541,8 +540,8 @@ def check_claim(irc, channel, sender, chanobj=None):
|
|||||||
return (not relay) or irc.name == relay[0] or not db[relay]['claim'] or \
|
return (not relay) or irc.name == relay[0] or not db[relay]['claim'] or \
|
||||||
irc.name in db[relay]['claim'] 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 irc.isInternalClient(sender) or \
|
or irc.is_internal_client(sender) or \
|
||||||
irc.isInternalServer(sender)
|
irc.is_internal_server(sender)
|
||||||
|
|
||||||
def get_supported_umodes(irc, remoteirc, modes):
|
def get_supported_umodes(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
|
||||||
@ -651,7 +650,7 @@ def relay_joins(irc, channel, users, ts, burst=True):
|
|||||||
|
|
||||||
# Fetch the known channel TS and all the prefix modes for each user. This ensures
|
# Fetch the known channel TS and all the prefix modes for each user. This ensures
|
||||||
# the different sides of the relay are merged properly.
|
# the different sides of the relay are merged properly.
|
||||||
if not irc.proto.hasCap('has-ts'):
|
if not irc.has_cap('has-ts'):
|
||||||
# Special hack for clientbot: just use the remote's modes so mode changes
|
# Special hack for clientbot: just use the remote's modes so mode changes
|
||||||
# take precendence. (TS is always outside the clientbot's control)
|
# take precendence. (TS is always outside the clientbot's control)
|
||||||
ts = remoteirc.channels[remotechan].ts
|
ts = remoteirc.channels[remotechan].ts
|
||||||
@ -671,16 +670,16 @@ def relay_joins(irc, channel, users, ts, burst=True):
|
|||||||
modes = get_supported_cmodes(irc, remoteirc, channel, irc.channels[channel].modes)
|
modes = get_supported_cmodes(irc, remoteirc, channel, irc.channels[channel].modes)
|
||||||
rsid = get_remote_sid(remoteirc, irc)
|
rsid = get_remote_sid(remoteirc, irc)
|
||||||
if rsid:
|
if rsid:
|
||||||
remoteirc.proto.sjoin(rsid, remotechan, queued_users, ts=ts, modes=modes)
|
remoteirc.sjoin(rsid, remotechan, queued_users, ts=ts, modes=modes)
|
||||||
else:
|
else:
|
||||||
# A regular JOIN only needs the user and the channel. TS, source SID, etc., can all be omitted.
|
# 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.join(queued_users[0][1], remotechan)
|
||||||
|
|
||||||
joined_nets[remoteirc] = {'channel': remotechan, 'users': [u[-1] for u in queued_users]}
|
joined_nets[remoteirc] = {'channel': remotechan, 'users': [u[-1] for u in queued_users]}
|
||||||
|
|
||||||
for remoteirc, hookdata in joined_nets.items():
|
for remoteirc, hookdata in joined_nets.items():
|
||||||
# HACK: Announce this JOIN as a special hook on each network, for plugins like Automode.
|
# HACK: Announce this JOIN as a special hook on each network, for plugins like Automode.
|
||||||
remoteirc.callHooks([remoteirc.sid, 'PYLINK_RELAY_JOIN', hookdata])
|
remoteirc.call_hooks([remoteirc.sid, 'PYLINK_RELAY_JOIN', hookdata])
|
||||||
|
|
||||||
def relay_part(irc, channel, user):
|
def relay_part(irc, channel, user):
|
||||||
"""
|
"""
|
||||||
@ -704,11 +703,11 @@ def relay_part(irc, channel, user):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Part the relay client with the channel delinked message.
|
# Part the relay client with the channel delinked message.
|
||||||
remoteirc.proto.part(remoteuser, remotechan, 'Channel delinked.')
|
remoteirc.part(remoteuser, remotechan, 'Channel delinked.')
|
||||||
|
|
||||||
# If the relay client no longer has any channels, quit them to prevent inflating /lusers.
|
# 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.quit(remoteuser, 'Left all shared channels.')
|
||||||
del relayusers[(irc.name, user)][remoteirc.name]
|
del relayusers[(irc.name, user)][remoteirc.name]
|
||||||
|
|
||||||
|
|
||||||
@ -786,7 +785,7 @@ def get_supported_cmodes(irc, remoteirc, channel, modes):
|
|||||||
"for network %r.",
|
"for network %r.",
|
||||||
irc.name, modechar, arg, remoteirc.name)
|
irc.name, modechar, arg, remoteirc.name)
|
||||||
|
|
||||||
if (not irc.proto.hasCap('can-spawn-clients')) and irc.pseudoclient and arg == irc.pseudoclient.uid:
|
if (not irc.has_cap('can-spawn-clients')) and irc.pseudoclient and arg == irc.pseudoclient.uid:
|
||||||
# Skip modesync on the main PyLink client.
|
# Skip modesync on the main PyLink client.
|
||||||
log.debug("(%s) relay.get_supported_cmodes: filtering prefix change (%r, %r) on Clientbot relayer",
|
log.debug("(%s) relay.get_supported_cmodes: filtering prefix change (%r, %r) on Clientbot relayer",
|
||||||
irc.name, name, arg)
|
irc.name, name, arg)
|
||||||
@ -851,7 +850,7 @@ def handle_relay_whois(irc, source, command, args):
|
|||||||
"""Convenience wrapper to return WHOIS replies."""
|
"""Convenience wrapper to return WHOIS replies."""
|
||||||
# WHOIS replies are by convention prefixed with the target user's nick.
|
# WHOIS replies are by convention prefixed with the target user's nick.
|
||||||
text = '%s %s' % (targetuser.nick, text)
|
text = '%s %s' % (targetuser.nick, text)
|
||||||
irc.proto.numeric(server, num, source, text)
|
irc.numeric(server, num, source, text)
|
||||||
|
|
||||||
def checkSendKey(infoline):
|
def checkSendKey(infoline):
|
||||||
"""
|
"""
|
||||||
@ -860,7 +859,7 @@ def handle_relay_whois(irc, source, command, args):
|
|||||||
setting = conf.conf.get('relay', {}).get(infoline, '').lower()
|
setting = conf.conf.get('relay', {}).get(infoline, '').lower()
|
||||||
if setting == 'all':
|
if setting == 'all':
|
||||||
return True
|
return True
|
||||||
elif setting == 'opers' and irc.isOper(source, allowAuthed=False):
|
elif setting == 'opers' and irc.is_oper(source, allowAuthed=False):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -870,7 +869,7 @@ def handle_relay_whois(irc, source, command, args):
|
|||||||
homenet, uid = origuser
|
homenet, uid = origuser
|
||||||
realirc = world.networkobjects[homenet]
|
realirc = world.networkobjects[homenet]
|
||||||
realuser = realirc.users[uid]
|
realuser = realirc.users[uid]
|
||||||
netname = realirc.getFullNetworkName()
|
netname = realirc.get_full_network_name()
|
||||||
|
|
||||||
wreply(320, ":is a remote user connected via PyLink Relay. Home network: %s; "
|
wreply(320, ":is a remote user connected via PyLink Relay. Home network: %s; "
|
||||||
"Home nick: %s" % (netname, realuser.nick))
|
"Home nick: %s" % (netname, realuser.nick))
|
||||||
@ -879,9 +878,9 @@ def handle_relay_whois(irc, source, command, args):
|
|||||||
# Send account information if told to and the target is logged in.
|
# Send account information if told to and the target is logged in.
|
||||||
wreply(330, "%s :is logged in (on %s) as" % (realuser.services_account, netname))
|
wreply(330, "%s :is logged in (on %s) as" % (realuser.services_account, netname))
|
||||||
|
|
||||||
if checkSendKey('whois_show_server') and realirc.proto.hasCap('can-track-servers'):
|
if checkSendKey('whois_show_server') and realirc.has_cap('can-track-servers'):
|
||||||
wreply(320, ":is actually connected via the following server:")
|
wreply(320, ":is actually connected via the following server:")
|
||||||
realserver = realirc.getServer(uid)
|
realserver = realirc.get_server(uid)
|
||||||
realserver = realirc.servers[realserver]
|
realserver = realirc.servers[realserver]
|
||||||
wreply(312, "%s :%s" % (realserver.name, realserver.desc))
|
wreply(312, "%s :%s" % (realserver.name, realserver.desc))
|
||||||
|
|
||||||
@ -891,7 +890,7 @@ def handle_operup(irc, numeric, command, args):
|
|||||||
"""
|
"""
|
||||||
Handles setting oper types on relay clients during oper up.
|
Handles setting oper types on relay clients during oper up.
|
||||||
"""
|
"""
|
||||||
newtype = '%s (on %s)' % (args['text'], irc.getFullNetworkName())
|
newtype = '%s (on %s)' % (args['text'], irc.get_full_network_name())
|
||||||
for netname, user in relayusers[(irc.name, numeric)].items():
|
for netname, user in relayusers[(irc.name, numeric)].items():
|
||||||
log.debug('(%s) relay.handle_opertype: setting OPERTYPE of %s/%s to %s',
|
log.debug('(%s) relay.handle_opertype: setting OPERTYPE of %s/%s to %s',
|
||||||
irc.name, user, netname, newtype)
|
irc.name, user, netname, newtype)
|
||||||
@ -924,11 +923,11 @@ def handle_join(irc, numeric, command, args):
|
|||||||
# XXX: Find the diff of the new and old mode lists of the channel. Not pretty, but I'd
|
# XXX: Find the diff of the new and old mode lists of the channel. Not pretty, but I'd
|
||||||
# rather not change the 'users' format of SJOIN just for this. -GL
|
# rather not change the 'users' format of SJOIN just for this. -GL
|
||||||
try:
|
try:
|
||||||
oldmodes = set(chandata.getPrefixModes(user))
|
oldmodes = set(chandata.get_prefix_modes(user))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# User was never in channel. Treat their mode list as empty.
|
# User was never in channel. Treat their mode list as empty.
|
||||||
oldmodes = set()
|
oldmodes = set()
|
||||||
newmodes = set(current_chandata.getPrefixModes(user))
|
newmodes = set(current_chandata.get_prefix_modes(user))
|
||||||
modediff = newmodes - oldmodes
|
modediff = newmodes - oldmodes
|
||||||
log.debug('(%s) relay.handle_join: mode diff for %s on %s: %s oldmodes=%s newmodes=%s',
|
log.debug('(%s) relay.handle_join: mode diff for %s on %s: %s oldmodes=%s newmodes=%s',
|
||||||
irc.name, user, channel, modediff, oldmodes, newmodes)
|
irc.name, user, channel, modediff, oldmodes, newmodes)
|
||||||
@ -938,8 +937,8 @@ def handle_join(irc, numeric, command, args):
|
|||||||
modes.append(('-%s' % modechar, user))
|
modes.append(('-%s' % modechar, user))
|
||||||
|
|
||||||
if modes:
|
if modes:
|
||||||
log.debug('(%s) relay.handle_join: reverting modes on BURST: %s', irc.name, irc.joinModes(modes))
|
log.debug('(%s) relay.handle_join: reverting modes on BURST: %s', irc.name, irc.join_modes(modes))
|
||||||
irc.proto.mode(irc.sid, channel, modes)
|
irc.mode(irc.sid, channel, modes)
|
||||||
|
|
||||||
relay_joins(irc, channel, users, ts, burst=False)
|
relay_joins(irc, channel, users, ts, burst=False)
|
||||||
utils.add_hook(handle_join, 'JOIN')
|
utils.add_hook(handle_join, 'JOIN')
|
||||||
@ -954,7 +953,7 @@ def handle_quit(irc, numeric, command, args):
|
|||||||
for netname, user in relayusers[(irc.name, numeric)].copy().items():
|
for netname, user in relayusers[(irc.name, numeric)].copy().items():
|
||||||
remoteirc = world.networkobjects[netname]
|
remoteirc = world.networkobjects[netname]
|
||||||
try: # Try to quit the client. If this fails because they're missing, bail.
|
try: # Try to quit the client. If this fails because they're missing, bail.
|
||||||
remoteirc.proto.quit(user, args['text'])
|
remoteirc.quit(user, args['text'])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
pass
|
||||||
del relayusers[(irc.name, numeric)]
|
del relayusers[(irc.name, numeric)]
|
||||||
@ -1011,7 +1010,7 @@ def handle_nick(irc, numeric, command, args):
|
|||||||
remoteirc = world.networkobjects[netname]
|
remoteirc = world.networkobjects[netname]
|
||||||
newnick = normalize_nick(remoteirc, irc.name, args['newnick'], uid=user)
|
newnick = normalize_nick(remoteirc, irc.name, args['newnick'], uid=user)
|
||||||
if remoteirc.users[user].nick != newnick:
|
if remoteirc.users[user].nick != newnick:
|
||||||
remoteirc.proto.nick(user, newnick)
|
remoteirc.nick(user, newnick)
|
||||||
utils.add_hook(handle_nick, 'NICK')
|
utils.add_hook(handle_nick, 'NICK')
|
||||||
|
|
||||||
def handle_part(irc, numeric, command, args):
|
def handle_part(irc, numeric, command, args):
|
||||||
@ -1021,14 +1020,14 @@ def handle_part(irc, numeric, command, args):
|
|||||||
if numeric == irc.pseudoclient.uid:
|
if numeric == irc.pseudoclient.uid:
|
||||||
# For clientbot: treat forced parts to the bot as clearchan, and attempt to rejoin only
|
# For clientbot: treat forced parts to the bot as clearchan, and attempt to rejoin only
|
||||||
# if it affected a relay.
|
# if it affected a relay.
|
||||||
if not irc.proto.hasCap('can-spawn-clients'):
|
if not irc.has_cap('can-spawn-clients'):
|
||||||
for channel in [c for c in channels if get_relay((irc.name, c))]:
|
for channel in [c for c in channels if get_relay((irc.name, c))]:
|
||||||
for user in irc.channels[channel].users:
|
for user in irc.channels[channel].users:
|
||||||
if (not irc.isInternalClient(user)) and (not isRelayClient(irc, user)):
|
if (not irc.is_internal_client(user)) and (not isRelayClient(irc, user)):
|
||||||
irc.callHooks([irc.sid, 'CLIENTBOT_SERVICE_KICKED', {'channel': channel, 'target': user,
|
irc.call_hooks([irc.sid, 'CLIENTBOT_SERVICE_KICKED', {'channel': channel, 'target': user,
|
||||||
'text': 'Clientbot was force parted (Reason: %s)' % text or 'None',
|
'text': 'Clientbot was force parted (Reason: %s)' % text or 'None',
|
||||||
'parse_as': 'KICK'}])
|
'parse_as': 'KICK'}])
|
||||||
irc.proto.join(irc.pseudoclient.uid, channel)
|
irc.join(irc.pseudoclient.uid, channel)
|
||||||
|
|
||||||
return
|
return
|
||||||
return
|
return
|
||||||
@ -1039,9 +1038,9 @@ def handle_part(irc, numeric, command, args):
|
|||||||
remotechan = get_remote_channel(irc, remoteirc, channel)
|
remotechan = get_remote_channel(irc, remoteirc, channel)
|
||||||
if remotechan is None:
|
if remotechan is None:
|
||||||
continue
|
continue
|
||||||
remoteirc.proto.part(user, remotechan, text)
|
remoteirc.part(user, remotechan, text)
|
||||||
if not remoteirc.users[user].channels:
|
if not remoteirc.users[user].channels:
|
||||||
remoteirc.proto.quit(user, 'Left all shared channels.')
|
remoteirc.quit(user, 'Left all shared channels.')
|
||||||
del relayusers[(irc.name, numeric)][remoteirc.name]
|
del relayusers[(irc.name, numeric)][remoteirc.name]
|
||||||
utils.add_hook(handle_part, 'PART')
|
utils.add_hook(handle_part, 'PART')
|
||||||
|
|
||||||
@ -1049,7 +1048,7 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
notice = (command in ('NOTICE', 'PYLINK_SELF_NOTICE'))
|
notice = (command in ('NOTICE', 'PYLINK_SELF_NOTICE'))
|
||||||
target = args['target']
|
target = args['target']
|
||||||
text = args['text']
|
text = args['text']
|
||||||
if irc.isInternalClient(numeric) and irc.isInternalClient(target):
|
if irc.is_internal_client(numeric) and irc.is_internal_client(target):
|
||||||
# Drop attempted PMs between internal clients (this shouldn't happen,
|
# Drop attempted PMs between internal clients (this shouldn't happen,
|
||||||
# but whatever).
|
# but whatever).
|
||||||
return
|
return
|
||||||
@ -1093,8 +1092,8 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
|
|
||||||
# Skip "from:" formatting for servers; it's messy with longer hostnames.
|
# Skip "from:" formatting for servers; it's messy with longer hostnames.
|
||||||
# Also skip this formatting for servicebot relaying.
|
# Also skip this formatting for servicebot relaying.
|
||||||
if numeric not in irc.servers and not irc.getServiceBot(numeric):
|
if numeric not in irc.servers and not irc.get_service_bot(numeric):
|
||||||
displayedname = irc.getFriendlyName(numeric)
|
displayedname = irc.get_friendly_name(numeric)
|
||||||
real_text = '<%s/%s> %s' % (displayedname, irc.name, text)
|
real_text = '<%s/%s> %s' % (displayedname, irc.name, text)
|
||||||
else:
|
else:
|
||||||
real_text = text
|
real_text = text
|
||||||
@ -1124,9 +1123,9 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if notice:
|
if notice:
|
||||||
remoteirc.proto.notice(user, real_target, real_text)
|
remoteirc.notice(user, real_target, real_text)
|
||||||
else:
|
else:
|
||||||
remoteirc.proto.message(user, real_target, real_text)
|
remoteirc.message(user, real_target, real_text)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
# Our relay clone disappeared while we were trying to send the message.
|
# Our relay clone disappeared while we were trying to send the message.
|
||||||
# This is normally due to a nick conflict with the IRCd.
|
# This is normally due to a nick conflict with the IRCd.
|
||||||
@ -1153,7 +1152,7 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
return
|
return
|
||||||
remoteirc = world.networkobjects[homenet]
|
remoteirc = world.networkobjects[homenet]
|
||||||
|
|
||||||
if (not remoteirc.proto.hasCap('can-spawn-clients')) and not conf.conf.get('relay', {}).get('allow_clientbot_pms'):
|
if (not remoteirc.has_cap('can-spawn-clients')) and not conf.conf.get('relay', {}).get('allow_clientbot_pms'):
|
||||||
irc.msg(numeric, 'Private messages to users connected via Clientbot have '
|
irc.msg(numeric, 'Private messages to users connected via Clientbot have '
|
||||||
'been administratively disabled.', notice=True)
|
'been administratively disabled.', notice=True)
|
||||||
return
|
return
|
||||||
@ -1162,9 +1161,9 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if notice:
|
if notice:
|
||||||
remoteirc.proto.notice(user, real_target, text)
|
remoteirc.notice(user, real_target, text)
|
||||||
else:
|
else:
|
||||||
remoteirc.proto.message(user, real_target, text)
|
remoteirc.message(user, real_target, text)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
# Our relay clone disappeared while we were trying to send the message.
|
# Our relay clone disappeared while we were trying to send the message.
|
||||||
# This is normally due to a nick conflict with the IRCd.
|
# This is normally due to a nick conflict with the IRCd.
|
||||||
@ -1184,17 +1183,17 @@ def handle_kick(irc, source, command, args):
|
|||||||
relay = get_relay((irc.name, channel))
|
relay = get_relay((irc.name, channel))
|
||||||
|
|
||||||
# Special case for clientbot: treat kicks to the PyLink service bot as channel clear.
|
# Special case for clientbot: treat kicks to the PyLink service bot as channel clear.
|
||||||
if (not irc.proto.hasCap('can-spawn-clients')) and irc.pseudoclient and target == irc.pseudoclient.uid:
|
if (not irc.has_cap('can-spawn-clients')) and irc.pseudoclient and target == irc.pseudoclient.uid:
|
||||||
for user in irc.channels[channel].users:
|
for user in irc.channels[channel].users:
|
||||||
if (not irc.isInternalClient(user)) and (not isRelayClient(irc, user)):
|
if (not irc.is_internal_client(user)) and (not isRelayClient(irc, user)):
|
||||||
reason = "Clientbot kicked by %s (Reason: %s)" % (irc.getFriendlyName(source), text)
|
reason = "Clientbot kicked by %s (Reason: %s)" % (irc.get_friendly_name(source), text)
|
||||||
irc.callHooks([irc.sid, 'CLIENTBOT_SERVICE_KICKED', {'channel': channel, 'target': user,
|
irc.call_hooks([irc.sid, 'CLIENTBOT_SERVICE_KICKED', {'channel': channel, 'target': user,
|
||||||
'text': reason, 'parse_as': 'KICK'}])
|
'text': reason, 'parse_as': 'KICK'}])
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Don't relay kicks to protected service bots.
|
# Don't relay kicks to protected service bots.
|
||||||
if relay is None or irc.getServiceBot(target):
|
if relay is None or irc.get_service_bot(target):
|
||||||
return
|
return
|
||||||
|
|
||||||
origuser = get_orig_user(irc, target)
|
origuser = get_orig_user(irc, target)
|
||||||
@ -1226,7 +1225,7 @@ def handle_kick(irc, source, command, args):
|
|||||||
# kick ops, admins can't kick owners, etc.
|
# kick ops, admins can't kick owners, etc.
|
||||||
modes = get_prefix_modes(remoteirc, irc, remotechan, real_target)
|
modes = get_prefix_modes(remoteirc, irc, remotechan, real_target)
|
||||||
# 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.sjoin(irc.sid, channel, [(modes, target)])
|
||||||
if kicker in irc.users:
|
if kicker in irc.users:
|
||||||
log.info('(%s) relay: Blocked KICK (reason %r) from %s/%s to relay client %s on %s.',
|
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,
|
irc.name, args['text'], irc.users[source].nick, irc.name,
|
||||||
@ -1245,13 +1244,13 @@ def handle_kick(irc, source, command, args):
|
|||||||
# Propogate the kick!
|
# Propogate the kick!
|
||||||
if real_kicker:
|
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)
|
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.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 = get_remote_sid(remoteirc, irc)
|
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)
|
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.proto.hasCap('can-spawn-clients'):
|
if not irc.has_cap('can-spawn-clients'):
|
||||||
# Special case for clientbot: no kick prefixes are needed.
|
# Special case for clientbot: no kick prefixes are needed.
|
||||||
text = args['text']
|
text = args['text']
|
||||||
else:
|
else:
|
||||||
@ -1264,16 +1263,16 @@ def handle_kick(irc, source, command, args):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
text = "(<unknown kicker>@%s) %s" % (irc.name, args['text'])
|
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
|
rsid = rsid or remoteirc.sid # Fall back to the main PyLink SID if get_remote_sid() fails
|
||||||
remoteirc.proto.kick(rsid, remotechan, real_target, text)
|
remoteirc.kick(rsid, remotechan, real_target, text)
|
||||||
|
|
||||||
# If the target isn't on any channels, quit them.
|
# If the target isn't on any channels, quit them.
|
||||||
if remoteirc != irc and (not remoteirc.users[real_target].channels) and not origuser:
|
if remoteirc != irc and (not remoteirc.users[real_target].channels) and not origuser:
|
||||||
del relayusers[(irc.name, target)][remoteirc.name]
|
del relayusers[(irc.name, target)][remoteirc.name]
|
||||||
remoteirc.proto.quit(real_target, 'Left all shared channels.')
|
remoteirc.quit(real_target, 'Left all shared channels.')
|
||||||
|
|
||||||
if origuser and not irc.users[target].channels:
|
if origuser and not irc.users[target].channels:
|
||||||
del relayusers[origuser][irc.name]
|
del relayusers[origuser][irc.name]
|
||||||
irc.proto.quit(target, 'Left all shared channels.')
|
irc.quit(target, 'Left all shared channels.')
|
||||||
|
|
||||||
utils.add_hook(handle_kick, 'KICK')
|
utils.add_hook(handle_kick, 'KICK')
|
||||||
|
|
||||||
@ -1296,7 +1295,7 @@ def handle_chgclient(irc, source, command, args):
|
|||||||
newtext = normalize_host(remoteirc, text)
|
newtext = normalize_host(remoteirc, text)
|
||||||
else: # Don't overwrite the original text variable on every iteration.
|
else: # Don't overwrite the original text variable on every iteration.
|
||||||
newtext = text
|
newtext = text
|
||||||
remoteirc.proto.updateClient(user, field, newtext)
|
remoteirc.update_client(user, field, newtext)
|
||||||
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) relay.handle_chgclient: 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,
|
||||||
@ -1326,17 +1325,17 @@ def handle_mode(irc, numeric, command, args):
|
|||||||
# from the corresponding server.
|
# from the corresponding server.
|
||||||
u = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
u = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
||||||
if u:
|
if u:
|
||||||
remoteirc.proto.mode(u, remotechan, supported_modes)
|
remoteirc.mode(u, remotechan, supported_modes)
|
||||||
else:
|
else:
|
||||||
rsid = get_remote_sid(remoteirc, irc)
|
rsid = get_remote_sid(remoteirc, irc)
|
||||||
rsid = rsid or remoteirc.sid
|
rsid = rsid or remoteirc.sid
|
||||||
remoteirc.proto.mode(rsid, remotechan, supported_modes)
|
remoteirc.mode(rsid, remotechan, supported_modes)
|
||||||
else: # Mode change blocked by CLAIM.
|
else: # Mode change blocked by CLAIM.
|
||||||
reversed_modes = irc.reverseModes(target, modes, oldobj=oldchan)
|
reversed_modes = irc.reverse_modes(target, modes, oldobj=oldchan)
|
||||||
log.debug('(%s) relay.handle_mode: 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)
|
||||||
if reversed_modes:
|
if reversed_modes:
|
||||||
irc.proto.mode(irc.sid, target, reversed_modes)
|
irc.mode(irc.sid, target, reversed_modes)
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -1354,7 +1353,7 @@ def handle_mode(irc, numeric, command, args):
|
|||||||
remoteuser = get_remote_user(irc, remoteirc, target, spawn_if_missing=False)
|
remoteuser = get_remote_user(irc, remoteirc, target, spawn_if_missing=False)
|
||||||
|
|
||||||
if remoteuser and modes:
|
if remoteuser and modes:
|
||||||
remoteirc.proto.mode(remoteuser, remoteuser, modes)
|
remoteirc.mode(remoteuser, remoteuser, modes)
|
||||||
|
|
||||||
utils.add_hook(handle_mode, 'MODE')
|
utils.add_hook(handle_mode, 'MODE')
|
||||||
|
|
||||||
@ -1374,12 +1373,12 @@ def handle_topic(irc, numeric, command, args):
|
|||||||
# This might originate from a server too.
|
# This might originate from a server too.
|
||||||
remoteuser = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
remoteuser = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
||||||
if remoteuser:
|
if remoteuser:
|
||||||
remoteirc.proto.topic(remoteuser, remotechan, topic)
|
remoteirc.topic(remoteuser, remotechan, topic)
|
||||||
else:
|
else:
|
||||||
rsid = get_remote_sid(remoteirc, irc)
|
rsid = get_remote_sid(remoteirc, irc)
|
||||||
remoteirc.proto.topicBurst(rsid, remotechan, topic)
|
remoteirc.topic_burst(rsid, remotechan, topic)
|
||||||
elif oldtopic: # Topic change blocked by claim.
|
elif oldtopic: # Topic change blocked by claim.
|
||||||
irc.proto.topicBurst(irc.sid, channel, oldtopic)
|
irc.topic_burst(irc.sid, channel, oldtopic)
|
||||||
|
|
||||||
utils.add_hook(handle_topic, 'TOPIC')
|
utils.add_hook(handle_topic, 'TOPIC')
|
||||||
|
|
||||||
@ -1407,7 +1406,7 @@ def handle_kill(irc, numeric, command, args):
|
|||||||
modes = get_prefix_modes(remoteirc, irc, remotechan, realuser[1])
|
modes = get_prefix_modes(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 = get_remote_user(remoteirc, irc, realuser[1], times_tagged=1)
|
client = get_remote_user(remoteirc, irc, realuser[1], times_tagged=1)
|
||||||
irc.proto.sjoin(get_remote_sid(irc, remoteirc), localchan, [(modes, client)])
|
irc.sjoin(get_remote_sid(irc, remoteirc), localchan, [(modes, client)])
|
||||||
|
|
||||||
if userdata and numeric in irc.users:
|
if userdata and numeric in irc.users:
|
||||||
log.info('(%s) relay.handle_kill: 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.',
|
||||||
@ -1436,7 +1435,7 @@ utils.add_hook(handle_kill, 'KILL')
|
|||||||
def handle_away(irc, numeric, command, args):
|
def handle_away(irc, numeric, command, args):
|
||||||
for netname, user in relayusers[(irc.name, numeric)].items():
|
for netname, user in relayusers[(irc.name, numeric)].items():
|
||||||
remoteirc = world.networkobjects[netname]
|
remoteirc = world.networkobjects[netname]
|
||||||
remoteirc.proto.away(user, args['text'])
|
remoteirc.away(user, args['text'])
|
||||||
utils.add_hook(handle_away, 'AWAY')
|
utils.add_hook(handle_away, 'AWAY')
|
||||||
|
|
||||||
def handle_invite(irc, source, command, args):
|
def handle_invite(irc, source, command, args):
|
||||||
@ -1457,7 +1456,7 @@ def handle_invite(irc, source, command, args):
|
|||||||
'channel not on their network!',
|
'channel not on their network!',
|
||||||
notice=True)
|
notice=True)
|
||||||
else:
|
else:
|
||||||
remoteirc.proto.invite(remotesource, remoteuser,
|
remoteirc.invite(remotesource, remoteuser,
|
||||||
remotechan)
|
remotechan)
|
||||||
utils.add_hook(handle_invite, 'INVITE')
|
utils.add_hook(handle_invite, 'INVITE')
|
||||||
|
|
||||||
@ -1472,7 +1471,7 @@ def handle_services_login(irc, numeric, command, args):
|
|||||||
"""
|
"""
|
||||||
for netname, user in relayusers[(irc.name, numeric)].items():
|
for netname, user in relayusers[(irc.name, numeric)].items():
|
||||||
remoteirc = world.networkobjects[netname]
|
remoteirc = world.networkobjects[netname]
|
||||||
remoteirc.callHooks([user, 'PYLINK_RELAY_SERVICES_LOGIN', args])
|
remoteirc.call_hooks([user, 'PYLINK_RELAY_SERVICES_LOGIN', args])
|
||||||
utils.add_hook(handle_services_login, 'CLIENT_SERVICES_LOGIN')
|
utils.add_hook(handle_services_login, 'CLIENT_SERVICES_LOGIN')
|
||||||
|
|
||||||
def handle_disconnect(irc, numeric, command, args):
|
def handle_disconnect(irc, numeric, command, args):
|
||||||
@ -1546,7 +1545,7 @@ def nick_collide(irc, target):
|
|||||||
newnick = normalize_nick(irc, remotenet, nick, times_tagged=1)
|
newnick = normalize_nick(irc, remotenet, nick, times_tagged=1)
|
||||||
log.debug('(%s) relay.nick_collide: Fixing nick of relay client %r (%s) to %s',
|
log.debug('(%s) relay.nick_collide: Fixing nick of relay client %r (%s) to %s',
|
||||||
irc.name, target, nick, newnick)
|
irc.name, target, nick, newnick)
|
||||||
irc.proto.nick(target, newnick)
|
irc.nick(target, newnick)
|
||||||
|
|
||||||
def handle_save(irc, numeric, command, args):
|
def handle_save(irc, numeric, command, args):
|
||||||
target = args['target']
|
target = args['target']
|
||||||
@ -1581,14 +1580,14 @@ def create(irc, source, args):
|
|||||||
|
|
||||||
Opens up the given channel over PyLink Relay."""
|
Opens up the given channel over PyLink Relay."""
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1: channel.")
|
irc.error("Not enough arguments. Needs 1: channel.")
|
||||||
return
|
return
|
||||||
if not utils.isChannel(channel):
|
if not utils.isChannel(channel):
|
||||||
irc.error('Invalid channel %r.' % channel)
|
irc.error('Invalid channel %r.' % channel)
|
||||||
return
|
return
|
||||||
if not irc.proto.hasCap('can-host-relay'):
|
if not irc.has_cap('can-host-relay'):
|
||||||
irc.error('Clientbot networks cannot be used to host a relay.')
|
irc.error('Clientbot networks cannot be used to host a relay.')
|
||||||
return
|
return
|
||||||
if source not in irc.channels[channel].users:
|
if source not in irc.channels[channel].users:
|
||||||
@ -1604,7 +1603,7 @@ def create(irc, source, args):
|
|||||||
irc.error('Channel %r is already part of a relay.' % channel)
|
irc.error('Channel %r is already part of a relay.' % channel)
|
||||||
return
|
return
|
||||||
|
|
||||||
creator = irc.getHostmask(source)
|
creator = irc.get_hostmask(source)
|
||||||
# Create the relay database entry with the (network name, channel name)
|
# Create the relay database entry with the (network name, channel name)
|
||||||
# pair - this is just a dict with various keys.
|
# pair - this is just a dict with various keys.
|
||||||
db[(irc.name, channel)] = {'claim': [irc.name], 'links': set(),
|
db[(irc.name, channel)] = {'claim': [irc.name], 'links': set(),
|
||||||
@ -1628,11 +1627,11 @@ def destroy(irc, source, args):
|
|||||||
|
|
||||||
Removes the given channel from the PyLink Relay, delinking all networks linked to it. If the home network is given and you are logged in as admin, this can also remove relay channels from other networks."""
|
Removes the given channel from the PyLink Relay, delinking all networks linked to it. If the home network is given and you are logged in as admin, this can also remove relay channels from other networks."""
|
||||||
try: # Two args were given: first one is network name, second is channel.
|
try: # Two args were given: first one is network name, second is channel.
|
||||||
channel = irc.toLower(args[1])
|
channel = irc.to_lower(args[1])
|
||||||
network = args[0]
|
network = args[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
try: # One argument was given; assume it's just the channel.
|
try: # One argument was given; assume it's just the channel.
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
network = irc.name
|
network = irc.name
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1-2: channel, network (optional).")
|
irc.error("Not enough arguments. Needs 1-2: channel, network (optional).")
|
||||||
@ -1655,7 +1654,7 @@ def destroy(irc, source, args):
|
|||||||
del db[entry]
|
del db[entry]
|
||||||
|
|
||||||
log.info('(%s) relay: Channel %s destroyed by %s.', irc.name,
|
log.info('(%s) relay: Channel %s destroyed by %s.', irc.name,
|
||||||
channel, irc.getHostmask(source))
|
channel, irc.get_hostmask(source))
|
||||||
irc.reply('Done.')
|
irc.reply('Done.')
|
||||||
else:
|
else:
|
||||||
irc.error("No such channel %r exists. If you're trying to delink a channel from "
|
irc.error("No such channel %r exists. If you're trying to delink a channel from "
|
||||||
@ -1709,8 +1708,8 @@ def link(irc, source, args):
|
|||||||
args = link_parser.parse_args(args)
|
args = link_parser.parse_args(args)
|
||||||
|
|
||||||
# Normalize channel case
|
# Normalize channel case
|
||||||
channel = irc.toLower(args.channel)
|
channel = irc.to_lower(args.channel)
|
||||||
localchan = irc.toLower(args.localchannel or args.channel)
|
localchan = irc.to_lower(args.localchannel or args.channel)
|
||||||
remotenet = args.remotenet
|
remotenet = args.remotenet
|
||||||
|
|
||||||
for c in (channel, localchan):
|
for c in (channel, localchan):
|
||||||
@ -1729,7 +1728,7 @@ def link(irc, source, args):
|
|||||||
# Special case for Clientbot: join the requested channel first, then
|
# Special case for Clientbot: join the requested channel first, then
|
||||||
# require that the caller be opped.
|
# require that the caller be opped.
|
||||||
if localchan not in irc.pseudoclient.channels:
|
if localchan not in irc.pseudoclient.channels:
|
||||||
irc.proto.join(irc.pseudoclient.uid, localchan)
|
irc.join(irc.pseudoclient.uid, localchan)
|
||||||
irc.reply('Joining %r now to check for op status; please run this command again after I join.' % localchan)
|
irc.reply('Joining %r now to check for op status; please run this command again after I join.' % localchan)
|
||||||
return
|
return
|
||||||
elif not irc.channels[localchan].isOpPlus(source):
|
elif not irc.channels[localchan].isOpPlus(source):
|
||||||
@ -1780,7 +1779,7 @@ def link(irc, source, args):
|
|||||||
|
|
||||||
our_ts = irc.channels[localchan].ts
|
our_ts = irc.channels[localchan].ts
|
||||||
their_ts = world.networkobjects[remotenet].channels[channel].ts
|
their_ts = world.networkobjects[remotenet].channels[channel].ts
|
||||||
if (our_ts < their_ts) and irc.proto.hasCap('has-ts'):
|
if (our_ts < their_ts) and irc.has_cap('has-ts'):
|
||||||
log.debug('(%s) relay: Blocking link request %s%s -> %s%s due to bad TS (%s < %s)', irc.name,
|
log.debug('(%s) relay: Blocking link request %s%s -> %s%s due to bad TS (%s < %s)', irc.name,
|
||||||
irc.name, localchan, remotenet, args.channel, our_ts, their_ts)
|
irc.name, localchan, remotenet, args.channel, our_ts, their_ts)
|
||||||
irc.error("The channel creation date (TS) on %s (%s) is lower than the target "
|
irc.error("The channel creation date (TS) on %s (%s) is lower than the target "
|
||||||
@ -1791,7 +1790,7 @@ def link(irc, source, args):
|
|||||||
|
|
||||||
entry['links'].add((irc.name, localchan))
|
entry['links'].add((irc.name, localchan))
|
||||||
log.info('(%s) relay: Channel %s linked to %s%s by %s.', irc.name,
|
log.info('(%s) relay: Channel %s linked to %s%s by %s.', irc.name,
|
||||||
localchan, remotenet, args.channel, irc.getHostmask(source))
|
localchan, remotenet, args.channel, irc.get_hostmask(source))
|
||||||
initialize_channel(irc, localchan)
|
initialize_channel(irc, localchan)
|
||||||
irc.reply('Done.')
|
irc.reply('Done.')
|
||||||
link = utils.add_cmd(link, featured=True)
|
link = utils.add_cmd(link, featured=True)
|
||||||
@ -1802,7 +1801,7 @@ def delink(irc, source, args):
|
|||||||
Delinks the given channel from PyLink Relay. \x02network\x02 must and can only be specified if you are on the host network for the channel given, and allows you to pick which network to delink.
|
Delinks the given channel from PyLink Relay. \x02network\x02 must and can only be specified if you are on the host network for the channel given, and allows you to pick which network to delink.
|
||||||
To remove a relay channel entirely, use the 'destroy' command instead."""
|
To remove a relay channel entirely, use the 'destroy' command instead."""
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1-2: channel, remote netname (optional).")
|
irc.error("Not enough arguments. Needs 1-2: channel, remote netname (optional).")
|
||||||
return
|
return
|
||||||
@ -1835,7 +1834,7 @@ def delink(irc, source, args):
|
|||||||
db[entry]['links'].remove((irc.name, channel))
|
db[entry]['links'].remove((irc.name, channel))
|
||||||
irc.reply('Done.')
|
irc.reply('Done.')
|
||||||
log.info('(%s) relay: Channel %s delinked from %s%s by %s.', irc.name,
|
log.info('(%s) relay: Channel %s delinked from %s%s by %s.', irc.name,
|
||||||
channel, entry[0], entry[1], irc.getHostmask(source))
|
channel, entry[0], entry[1], irc.get_hostmask(source))
|
||||||
else:
|
else:
|
||||||
irc.error('No such relay %r.' % channel)
|
irc.error('No such relay %r.' % channel)
|
||||||
delink = utils.add_cmd(delink, featured=True)
|
delink = utils.add_cmd(delink, featured=True)
|
||||||
@ -1886,7 +1885,7 @@ def linked(irc, source, args):
|
|||||||
# Only show secret channels to opers or those in the channel, and tag them as
|
# Only show secret channels to opers or those in the channel, and tag them as
|
||||||
# [secret].
|
# [secret].
|
||||||
localchan = get_remote_channel(remoteirc, irc, channel)
|
localchan = get_remote_channel(remoteirc, irc, channel)
|
||||||
if irc.isOper(source) or (localchan and source in irc.channels[localchan].users):
|
if irc.is_oper(source) or (localchan and source in irc.channels[localchan].users):
|
||||||
s += '\x02[secret]\x02 '
|
s += '\x02[secret]\x02 '
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
@ -1902,7 +1901,7 @@ def linked(irc, source, args):
|
|||||||
|
|
||||||
irc.reply(s, private=True)
|
irc.reply(s, private=True)
|
||||||
|
|
||||||
if irc.isOper(source):
|
if irc.is_oper(source):
|
||||||
s = ''
|
s = ''
|
||||||
|
|
||||||
# If the caller is an oper, we can show the hostmasks of people
|
# If the caller is an oper, we can show the hostmasks of people
|
||||||
@ -1933,7 +1932,7 @@ def linkacl(irc, source, args):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
cmd = args[0].lower()
|
cmd = args[0].lower()
|
||||||
channel = irc.toLower(args[1])
|
channel = irc.to_lower(args[1])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error(missingargs)
|
irc.error(missingargs)
|
||||||
return
|
return
|
||||||
@ -1980,7 +1979,7 @@ def showuser(irc, source, args):
|
|||||||
# No errors here; showuser from the commands plugin already does this
|
# No errors here; showuser from the commands plugin already does this
|
||||||
# for us.
|
# for us.
|
||||||
return
|
return
|
||||||
u = irc.nickToUid(target)
|
u = irc.nick_to_uid(target)
|
||||||
if u:
|
if u:
|
||||||
irc.reply("Showing relay information on user \x02%s\x02:" % irc.users[u].nick, private=True)
|
irc.reply("Showing relay information on user \x02%s\x02:" % irc.users[u].nick, private=True)
|
||||||
try:
|
try:
|
||||||
@ -2003,7 +2002,7 @@ def showuser(irc, source, args):
|
|||||||
relay = get_relay((irc.name, ch))
|
relay = get_relay((irc.name, ch))
|
||||||
if relay:
|
if relay:
|
||||||
relaychannels.append(''.join(relay))
|
relaychannels.append(''.join(relay))
|
||||||
if relaychannels and (irc.isOper(source) or u == source):
|
if relaychannels and (irc.is_oper(source) or u == source):
|
||||||
irc.reply("\x02Relay channels\x02: %s" % ' '.join(relaychannels), private=True)
|
irc.reply("\x02Relay channels\x02: %s" % ' '.join(relaychannels), private=True)
|
||||||
|
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
@ -2012,7 +2011,7 @@ def showchan(irc, source, args):
|
|||||||
|
|
||||||
Shows relay data about the given channel. This supplements the 'showchan' command in the 'commands' plugin, which provides more general information."""
|
Shows relay data about the given channel. This supplements the 'showchan' command in the 'commands' plugin, which provides more general information."""
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return
|
return
|
||||||
if channel not in irc.channels:
|
if channel not in irc.channels:
|
||||||
@ -2023,7 +2022,7 @@ def showchan(irc, source, args):
|
|||||||
c = irc.channels[channel]
|
c = irc.channels[channel]
|
||||||
|
|
||||||
# Only show verbose info if caller is oper or is in the target channel.
|
# Only show verbose info if caller is oper or is in the target channel.
|
||||||
verbose = source in c.users or irc.isOper(source)
|
verbose = source in c.users or irc.is_oper(source)
|
||||||
secret = ('s', None) in c.modes
|
secret = ('s', None) in c.modes
|
||||||
if secret and not verbose:
|
if secret and not verbose:
|
||||||
# Hide secret channels from normal users.
|
# Hide secret channels from normal users.
|
||||||
@ -2061,7 +2060,7 @@ def claim(irc, source, args):
|
|||||||
as well).
|
as well).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
channel = irc.toLower(args[0])
|
channel = irc.to_lower(args[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
irc.error("Not enough arguments. Needs 1-2: channel, list of networks (optional).")
|
irc.error("Not enough arguments. Needs 1-2: channel, list of networks (optional).")
|
||||||
return
|
return
|
||||||
|
@ -42,7 +42,7 @@ def cb_relay_core(irc, source, command, args):
|
|||||||
|
|
||||||
if irc.pseudoclient and relay:
|
if irc.pseudoclient and relay:
|
||||||
try:
|
try:
|
||||||
sourcename = irc.getFriendlyName(source)
|
sourcename = irc.get_friendly_name(source)
|
||||||
except KeyError: # User has left due to /quit
|
except KeyError: # User has left due to /quit
|
||||||
sourcename = args['userdata'].nick
|
sourcename = args['userdata'].nick
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ def cb_relay_core(irc, source, command, args):
|
|||||||
text_template = string.Template(text_template)
|
text_template = string.Template(text_template)
|
||||||
|
|
||||||
if text_template:
|
if text_template:
|
||||||
if irc.getServiceBot(source):
|
if irc.get_service_bot(source):
|
||||||
# HACK: service bots are global and lack the relay state we look for.
|
# HACK: service bots are global and lack the relay state we look for.
|
||||||
# just pretend the message comes from the current network.
|
# just pretend the message comes from the current network.
|
||||||
log.debug('(%s) relay_cb_core: Overriding network origin to local (source=%s)', irc.name, source)
|
log.debug('(%s) relay_cb_core: Overriding network origin to local (source=%s)', irc.name, source)
|
||||||
@ -128,7 +128,7 @@ def cb_relay_core(irc, source, command, args):
|
|||||||
|
|
||||||
if source in irc.users:
|
if source in irc.users:
|
||||||
try:
|
try:
|
||||||
identhost = irc.getHostmask(source).split('!')[-1]
|
identhost = irc.get_hostmask(source).split('!')[-1]
|
||||||
except KeyError: # User got removed due to quit
|
except KeyError: # User got removed due to quit
|
||||||
identhost = '%s@%s' % (args['olduser'].ident, args['olduser'].host)
|
identhost = '%s@%s' % (args['olduser'].ident, args['olduser'].host)
|
||||||
# This is specifically spaced so that ident@host is only shown for users that have
|
# This is specifically spaced so that ident@host is only shown for users that have
|
||||||
@ -139,7 +139,7 @@ def cb_relay_core(irc, source, command, args):
|
|||||||
|
|
||||||
# $target_nick: Convert the target for kicks, etc. from a UID to a nick
|
# $target_nick: Convert the target for kicks, etc. from a UID to a nick
|
||||||
if args.get("target") in irc.users:
|
if args.get("target") in irc.users:
|
||||||
args["target_nick"] = irc.getFriendlyName(args['target'])
|
args["target_nick"] = irc.get_friendly_name(args['target'])
|
||||||
|
|
||||||
args.update({'netname': netname, 'sender': sourcename, 'sender_identhost': identhost,
|
args.update({'netname': netname, 'sender': sourcename, 'sender_identhost': identhost,
|
||||||
'colored_sender': color_text(sourcename), 'colored_netname': color_text(netname)})
|
'colored_sender': color_text(sourcename), 'colored_netname': color_text(netname)})
|
||||||
@ -201,7 +201,7 @@ def rpm(irc, source, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
relay = world.plugins.get('relay')
|
relay = world.plugins.get('relay')
|
||||||
if irc.proto.hasCap('can-spawn-clients'):
|
if irc.has_cap('can-spawn-clients'):
|
||||||
irc.error('This command is only supported on Clientbot networks. Try /msg %s <text>' % target)
|
irc.error('This command is only supported on Clientbot networks. Try /msg %s <text>' % target)
|
||||||
return
|
return
|
||||||
elif relay is None:
|
elif relay is None:
|
||||||
@ -215,7 +215,7 @@ def rpm(irc, source, args):
|
|||||||
'administratively disabled.')
|
'administratively disabled.')
|
||||||
return
|
return
|
||||||
|
|
||||||
uid = irc.nickToUid(target)
|
uid = irc.nick_to_uid(target)
|
||||||
if not uid:
|
if not uid:
|
||||||
irc.error('Unknown user %s.' % target)
|
irc.error('Unknown user %s.' % target)
|
||||||
return
|
return
|
||||||
@ -223,7 +223,7 @@ def rpm(irc, source, args):
|
|||||||
irc.error('%s is not a relay user.' % target)
|
irc.error('%s is not a relay user.' % target)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
assert not irc.isInternalClient(source), "rpm is not allowed from PyLink bots"
|
assert not irc.is_internal_client(source), "rpm is not allowed from PyLink bots"
|
||||||
# Send the message through relay by faking a hook for its handler.
|
# Send the message through relay by faking a hook for its handler.
|
||||||
relay.handle_messages(irc, source, 'RELAY_CLIENTBOT_PRIVMSG', {'target': uid, 'text': text})
|
relay.handle_messages(irc, source, 'RELAY_CLIENTBOT_PRIVMSG', {'target': uid, 'text': text})
|
||||||
irc.reply('Message sent.')
|
irc.reply('Message sent.')
|
||||||
|
@ -78,7 +78,7 @@ def _map(irc, source, args, show_relay=True):
|
|||||||
# This is a relay server - display the remote map of the network it represents
|
# This is a relay server - display the remote map of the network it represents
|
||||||
relay_server = serverlist[leaf].remote
|
relay_server = serverlist[leaf].remote
|
||||||
remoteirc = world.networkobjects[relay_server]
|
remoteirc = world.networkobjects[relay_server]
|
||||||
if remoteirc.proto.hasCap('can-track-servers'):
|
if remoteirc.has_cap('can-track-servers'):
|
||||||
# Only ever show relay subservers once - this prevents infinite loops.
|
# Only ever show relay subservers once - this prevents infinite loops.
|
||||||
showall(remoteirc, remoteirc.sid, hops=hops, is_relay_server=True)
|
showall(remoteirc, remoteirc.sid, hops=hops, is_relay_server=True)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ def handle_kill(irc, numeric, command, args):
|
|||||||
automatically disconnects from the network.
|
automatically disconnects from the network.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if (args['userdata'] and irc.isInternalServer(args['userdata'].server)) or irc.isInternalClient(args['target']):
|
if (args['userdata'] and irc.is_internal_server(args['userdata'].server)) or irc.is_internal_client(args['target']):
|
||||||
if killcache.setdefault(irc.name, 1) >= length:
|
if killcache.setdefault(irc.name, 1) >= length:
|
||||||
log.error('(%s) servprotect: Too many kills received, aborting!', irc.name)
|
log.error('(%s) servprotect: Too many kills received, aborting!', irc.name)
|
||||||
irc.disconnect()
|
irc.disconnect()
|
||||||
@ -33,7 +33,7 @@ def handle_save(irc, numeric, command, args):
|
|||||||
Tracks SAVEs (nick collision) against PyLink clients. If too many are received,
|
Tracks SAVEs (nick collision) against PyLink clients. If too many are received,
|
||||||
automatically disconnects from the network.
|
automatically disconnects from the network.
|
||||||
"""
|
"""
|
||||||
if irc.isInternalClient(args['target']):
|
if irc.is_internal_client(args['target']):
|
||||||
if savecache.setdefault(irc.name, 0) >= length:
|
if savecache.setdefault(irc.name, 0) >= length:
|
||||||
log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name)
|
log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name)
|
||||||
irc.disconnect()
|
irc.disconnect()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@ from pylinkirc.log import log
|
|||||||
from pylinkirc.classes import *
|
from pylinkirc.classes import *
|
||||||
from pylinkirc.protocols.ts6 import *
|
from pylinkirc.protocols.ts6 import *
|
||||||
|
|
||||||
|
# This protocol module inherits from the TS6 protocol.
|
||||||
class HybridProtocol(TS6Protocol):
|
class HybridProtocol(TS6Protocol):
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
# This protocol module inherits from the TS6 protocol.
|
super().__init__(*args, **kwargs)
|
||||||
super().__init__(irc)
|
|
||||||
|
|
||||||
self.casemapping = 'ascii'
|
self.casemapping = 'ascii'
|
||||||
self.caps = {}
|
self.caps = {}
|
||||||
@ -16,11 +16,11 @@ class HybridProtocol(TS6Protocol):
|
|||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
self.protocol_caps -= {'slash-in-hosts'}
|
self.protocol_caps -= {'slash-in-hosts'}
|
||||||
|
|
||||||
def connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
f = self.irc.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
|
||||||
cmodes = {
|
cmodes = {
|
||||||
@ -37,7 +37,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
'*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
|
'*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.irc.cmodes = cmodes
|
self.cmodes = cmodes
|
||||||
|
|
||||||
umodes = {
|
umodes = {
|
||||||
'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'locops': 'l',
|
'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'locops': 'l',
|
||||||
@ -52,13 +52,13 @@ class HybridProtocol(TS6Protocol):
|
|||||||
'*A': '', '*B': '', '*C': '', '*D': 'DFGHRSWabcdefgijklnopqrsuwxy'
|
'*A': '', '*B': '', '*C': '', '*D': 'DFGHRSWabcdefgijklnopqrsuwxy'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.irc.umodes = umodes
|
self.umodes = umodes
|
||||||
|
|
||||||
# halfops is mandatory on Hybrid
|
# halfops is mandatory on Hybrid
|
||||||
self.irc.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
||||||
f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))
|
f('PASS %s TS 6 %s' % (self.serverdata["sendpass"], self.sid))
|
||||||
|
|
||||||
# We request the following capabilities (for hybrid):
|
# We request the following capabilities (for hybrid):
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# CHW: Allow sending messages to @#channel and the like.
|
# CHW: Allow sending messages to @#channel and the like.
|
||||||
# KNOCK: Support for /knock
|
# KNOCK: Support for /knock
|
||||||
# SVS: Deal with extended NICK/UID messages that contain service IDs/stamps
|
# SVS: Deal with extended NICK/UID messages that contain service IDs/stamps
|
||||||
# TBURST: Topic Burst command; we send this in topicBurst
|
# TBURST: Topic Burst command; we send this in topic_burst
|
||||||
# DLN: DLINE command
|
# DLN: DLINE command
|
||||||
# UNDLN: UNDLINE command
|
# UNDLN: UNDLINE command
|
||||||
# KLN: KLINE command
|
# KLN: KLINE command
|
||||||
@ -79,13 +79,13 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# EOB: Supports EOB (end of burst) command
|
# EOB: Supports EOB (end of burst) command
|
||||||
f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')
|
f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')
|
||||||
|
|
||||||
f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
|
f('SERVER %s 0 :%s' % (self.serverdata["hostname"],
|
||||||
self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
# send endburst now
|
# send endburst now
|
||||||
self.irc.send(':%s EOB' % (self.irc.sid,))
|
self.send(':%s EOB' % (self.sid,))
|
||||||
|
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawn_client(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
||||||
manipulatable=False):
|
manipulatable=False):
|
||||||
"""
|
"""
|
||||||
@ -95,8 +95,8 @@ class HybridProtocol(TS6Protocol):
|
|||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -104,18 +104,18 @@ class HybridProtocol(TS6Protocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.join_modes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
||||||
self.irc.applyModes(uid, modes)
|
self.apply_modes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
self._send_with_prefix(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
"* :{realname}".format(ts=ts, host=host,
|
"* :{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
modes=raw_modes, ip=ip, realname=realname))
|
modes=raw_modes, ip=ip, realname=realname))
|
||||||
return u
|
return u
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def update_client(self, target, field, text):
|
||||||
"""Updates the ident, host, or realname of a PyLink client."""
|
"""Updates the ident, host, or realname of a PyLink client."""
|
||||||
# https://github.com/ircd-hybrid/ircd-hybrid/blob/58323b8/modules/m_svsmode.c#L40-L103
|
# https://github.com/ircd-hybrid/ircd-hybrid/blob/58323b8/modules/m_svsmode.c#L40-L103
|
||||||
# parv[0] = command
|
# parv[0] = command
|
||||||
@ -125,28 +125,28 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# parv[4] = optional argument (services account, vhost)
|
# parv[4] = optional argument (services account, vhost)
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
|
|
||||||
ts = self.irc.users[target].ts
|
ts = self.users[target].ts
|
||||||
|
|
||||||
if field == 'HOST':
|
if field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
# On Hybrid, it appears that host changing is actually just forcing umode
|
# On Hybrid, it appears that host changing is actually just forcing umode
|
||||||
# "+x <hostname>" on the target. -GLolol
|
# "+x <hostname>" on the target. -GLolol
|
||||||
self._send(self.irc.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
|
self._send_with_prefix(self.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)
|
raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topic_burst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.is_internal_server(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
|
|
||||||
ts = self.irc.channels[target].ts
|
ts = self.channels[target].ts
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
|
|
||||||
self._send(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
|
self._send_with_prefix(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
# command handlers
|
# command handlers
|
||||||
|
|
||||||
@ -154,13 +154,13 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# We only get a list of keywords here. Hybrid obviously assumes that
|
# We only get a list of keywords here. Hybrid obviously assumes that
|
||||||
# we know what modes it supports (indeed, this is a standard list).
|
# we know what modes it supports (indeed, this is a standard list).
|
||||||
# <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
|
# <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
|
||||||
self.irc.caps = caps = args[0].split()
|
self.caps = caps = args[0].split()
|
||||||
for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
|
for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
|
||||||
if required_cap not in caps:
|
if required_cap not in caps:
|
||||||
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
||||||
|
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
def handle_uid(self, numeric, command, args):
|
def handle_uid(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -174,39 +174,39 @@ class HybridProtocol(TS6Protocol):
|
|||||||
if account == '*':
|
if account == '*':
|
||||||
account = None
|
account = None
|
||||||
log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
|
log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
|
||||||
'host=%s realname=%s ip=%s', self.irc.name, nick, ts, uid,
|
'host=%s realname=%s ip=%s', self.name, nick, ts, uid,
|
||||||
ident, host, realname, ip)
|
ident, host, realname, ip)
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, host, ip)
|
self.users[uid] = User(nick, ts, uid, numeric, ident, host, realname, host, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [modes])
|
parsedmodes = self.parse_modes(uid, [modes])
|
||||||
log.debug('(%s) handle_uid: Applying modes %s for %s', self.irc.name, parsedmodes, uid)
|
log.debug('(%s) handle_uid: Applying modes %s for %s', self.name, parsedmodes, uid)
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.apply_modes(uid, parsedmodes)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Call the OPERED UP hook if +o is being added to the mode list.
|
# Call the OPERED UP hook if +o is being added to the mode list.
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])
|
self.call_hooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])
|
||||||
|
|
||||||
# Set the account name if present
|
# Set the account name if present
|
||||||
if account:
|
if account:
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_tburst(self, numeric, command, args):
|
def handle_tburst(self, numeric, command, args):
|
||||||
"""Handles incoming topic burst (TBURST) commands."""
|
"""Handles incoming topic burst (TBURST) commands."""
|
||||||
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
setter = args[3]
|
setter = args[3]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
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.irc.name)
|
log.debug('(%s) end of burst received', self.name)
|
||||||
if not self.has_eob: # Only call ENDBURST hooks if we haven't already.
|
if not self.has_eob: # Only call ENDBURST hooks if we haven't already.
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
target = args[0]
|
target = args[0]
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
parsedmodes = self.irc.parseModes(target, modes)
|
parsedmodes = self.parse_modes(target, modes)
|
||||||
|
|
||||||
for modepair in parsedmodes:
|
for modepair in parsedmodes:
|
||||||
if modepair[0] == '+d':
|
if modepair[0] == '+d':
|
||||||
@ -241,7 +241,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
|
|
||||||
# Send the login hook, and remove this mode from the mode
|
# Send the login hook, and remove this mode from the mode
|
||||||
# list, as it shouldn't be parsed literally.
|
# list, as it shouldn't be parsed literally.
|
||||||
self.irc.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.call_hooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
parsedmodes.remove(modepair)
|
parsedmodes.remove(modepair)
|
||||||
|
|
||||||
elif modepair[0] == '+x':
|
elif modepair[0] == '+x':
|
||||||
@ -250,16 +250,16 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# to some.host, for example.
|
# to some.host, for example.
|
||||||
host = args[-1]
|
host = args[-1]
|
||||||
|
|
||||||
self.irc.users[target].host = host
|
self.users[target].host = host
|
||||||
|
|
||||||
# Propagate the hostmask change as a hook.
|
# Propagate the hostmask change as a hook.
|
||||||
self.irc.callHooks([numeric, 'CHGHOST',
|
self.call_hooks([numeric, 'CHGHOST',
|
||||||
{'target': target, 'newhost': host}])
|
{'target': target, 'newhost': host}])
|
||||||
|
|
||||||
parsedmodes.remove(modepair)
|
parsedmodes.remove(modepair)
|
||||||
|
|
||||||
if parsedmodes:
|
if parsedmodes:
|
||||||
self.irc.applyModes(target, parsedmodes)
|
self.apply_modes(target, parsedmodes)
|
||||||
|
|
||||||
return {'target': target, 'modes': parsedmodes}
|
return {'target': target, 'modes': parsedmodes}
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ from pylinkirc.log import log
|
|||||||
from pylinkirc.protocols.ts6_common import *
|
from pylinkirc.protocols.ts6_common import *
|
||||||
|
|
||||||
class InspIRCdProtocol(TS6BaseProtocol):
|
class InspIRCdProtocol(TS6BaseProtocol):
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.protocol_caps |= {'slash-in-nicks', 'slash-in-hosts', 'underscore-in-hosts'}
|
self.protocol_caps |= {'slash-in-nicks', 'slash-in-hosts', 'underscore-in-hosts'}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
### Outgoing commands
|
### Outgoing commands
|
||||||
|
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawn_client(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
||||||
manipulatable=False):
|
manipulatable=False):
|
||||||
"""
|
"""
|
||||||
@ -42,9 +42,9 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
|
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -52,14 +52,14 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.join_modes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
|
|
||||||
self.irc.applyModes(uid, modes)
|
self.apply_modes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
self._send(server, "UID {uid} {ts} {nick} {realhost} {host} {ident} {ip}"
|
self._send_with_prefix(server, "UID {uid} {ts} {nick} {realhost} {host} {ident} {ip}"
|
||||||
" {ts} {modes} + :{realname}".format(ts=ts, host=host,
|
" {ts} {modes} + :{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
modes=raw_modes, ip=ip, realname=realname,
|
modes=raw_modes, ip=ip, realname=realname,
|
||||||
@ -73,20 +73,20 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# InspIRCd doesn't distinguish between burst joins and regular joins,
|
# InspIRCd doesn't distinguish between burst joins and regular joins,
|
||||||
# so what we're actually doing here is sending FJOIN from the server,
|
# so what we're actually doing here is sending FJOIN from the server,
|
||||||
# on behalf of the clients that are joining.
|
# on behalf of the clients that are joining.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
|
|
||||||
server = self.irc.getServer(client)
|
server = self.get_server(client)
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
||||||
modes = [m for m in self.irc.channels[channel].modes if m[0] not in self.irc.cmodes['*A']]
|
modes = [m for m in self.channels[channel].modes if m[0] not in self.cmodes['*A']]
|
||||||
self._send(server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
|
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
|
||||||
ts=self.irc.channels[channel].ts, uid=client, channel=channel,
|
ts=self.channels[channel].ts, uid=client, channel=channel,
|
||||||
modes=self.irc.joinModes(modes)))
|
modes=self.join_modes(modes)))
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -97,29 +97,29 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
log.debug('(%s) sjoin: got %r for users', self.irc.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
|
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Strip out list-modes, they shouldn't ever be sent in FJOIN (protocol rules).
|
# Strip out list-modes, they shouldn't ever be sent in FJOIN (protocol rules).
|
||||||
modes = modes or self.irc.channels[channel].modes
|
modes = modes or self.channels[channel].modes
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
|
|
||||||
banmodes = []
|
banmodes = []
|
||||||
regularmodes = []
|
regularmodes = []
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
modechar = mode[0][-1]
|
modechar = mode[0][-1]
|
||||||
if modechar in self.irc.cmodes['*A']:
|
if modechar in self.cmodes['*A']:
|
||||||
# Track bans separately (they are sent as a normal FMODE instead of in FJOIN.
|
# Track bans separately (they are sent as a normal FMODE instead of in FJOIN.
|
||||||
# However, don't reset bans that have already been set.
|
# However, don't reset bans that have already been set.
|
||||||
if (modechar, mode[1]) not in self.irc.channels[channel].modes:
|
if (modechar, mode[1]) not in self.channels[channel].modes:
|
||||||
banmodes.append(mode)
|
banmodes.append(mode)
|
||||||
else:
|
else:
|
||||||
regularmodes.append(mode)
|
regularmodes.append(mode)
|
||||||
@ -137,21 +137,21 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
for m in prefixes:
|
for m in prefixes:
|
||||||
changedmodes.add(('+%s' % m, user))
|
changedmodes.add(('+%s' % m, user))
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
|
|
||||||
namelist = ' '.join(namelist)
|
namelist = ' '.join(namelist)
|
||||||
self._send(server, "FJOIN {channel} {ts} {modes} :{users}".format(
|
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :{users}".format(
|
||||||
ts=ts, users=namelist, channel=channel,
|
ts=ts, users=namelist, channel=channel,
|
||||||
modes=self.irc.joinModes(modes)))
|
modes=self.join_modes(modes)))
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
if banmodes:
|
if banmodes:
|
||||||
# Burst ban modes if there are any.
|
# Burst ban modes if there are any.
|
||||||
# <- :1ML FMODE #test 1461201525 +bb *!*@bad.user *!*@rly.bad.user
|
# <- :1ML FMODE #test 1461201525 +bb *!*@bad.user *!*@rly.bad.user
|
||||||
self._send(server, "FMODE {channel} {ts} {modes} ".format(
|
self._send_with_prefix(server, "FMODE {channel} {ts} {modes} ".format(
|
||||||
ts=ts, channel=channel, modes=self.irc.joinModes(banmodes)))
|
ts=ts, channel=channel, modes=self.join_modes(banmodes)))
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
@ -163,19 +163,19 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
recognize ANY non-burst oper ups.
|
recognize ANY non-burst oper ups.
|
||||||
|
|
||||||
Plugins don't have to call this function themselves, but they can
|
Plugins don't have to call this function themselves, but they can
|
||||||
set the opertype attribute of an IrcUser object (in self.irc.users),
|
set the opertype attribute of an User object (in self.users),
|
||||||
and the change will be reflected here."""
|
and the change will be reflected here."""
|
||||||
userobj = self.irc.users[target]
|
userobj = self.users[target]
|
||||||
try:
|
try:
|
||||||
otype = opertype or userobj.opertype or 'IRC Operator'
|
otype = opertype or userobj.opertype or 'IRC Operator'
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log.debug('(%s) opertype field for %s (%s) isn\'t filled yet!',
|
log.debug('(%s) opertype field for %s (%s) isn\'t filled yet!',
|
||||||
self.irc.name, target, userobj.nick)
|
self.name, target, userobj.nick)
|
||||||
# whatever, this is non-standard anyways.
|
# whatever, this is non-standard anyways.
|
||||||
otype = 'IRC Operator'
|
otype = 'IRC Operator'
|
||||||
assert otype, "Tried to send an empty OPERTYPE!"
|
assert otype, "Tried to send an empty OPERTYPE!"
|
||||||
log.debug('(%s) Sending OPERTYPE from %s to oper them up.',
|
log.debug('(%s) Sending OPERTYPE from %s to oper them up.',
|
||||||
self.irc.name, target)
|
self.name, target)
|
||||||
userobj.opertype = otype
|
userobj.opertype = otype
|
||||||
|
|
||||||
# InspIRCd 2.x uses _ in OPERTYPE to denote spaces, while InspIRCd 3.x does not. This is not
|
# InspIRCd 2.x uses _ in OPERTYPE to denote spaces, while InspIRCd 3.x does not. This is not
|
||||||
@ -187,75 +187,75 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
else:
|
else:
|
||||||
otype = ':' + otype
|
otype = ':' + otype
|
||||||
|
|
||||||
self._send(target, 'OPERTYPE %s' % otype)
|
self._send_with_prefix(target, 'OPERTYPE %s' % otype)
|
||||||
|
|
||||||
def mode(self, numeric, target, modes, ts=None):
|
def mode(self, numeric, target, modes, ts=None):
|
||||||
"""Sends mode changes from a PyLink client/server."""
|
"""Sends mode changes from a PyLink client/server."""
|
||||||
# -> :9PYAAAAAA FMODE #pylink 1433653951 +os 9PYAAAAAA
|
# -> :9PYAAAAAA FMODE #pylink 1433653951 +os 9PYAAAAAA
|
||||||
# -> :9PYAAAAAA MODE 9PYAAAAAA -i+w
|
# -> :9PYAAAAAA MODE 9PYAAAAAA -i+w
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
log.debug('(%s) inspircd._sendModes: received %r for mode list', self.irc.name, modes)
|
log.debug('(%s) inspircd._send_with_prefixModes: received %r for mode list', self.name, modes)
|
||||||
if ('+o', None) in modes and not utils.isChannel(target):
|
if ('+o', None) in modes and not utils.isChannel(target):
|
||||||
# https://github.com/inspircd/inspircd/blob/master/src/modules/m_spanningtree/opertype.cpp#L26-L28
|
# https://github.com/inspircd/inspircd/blob/master/src/modules/m_spanningtree/opertype.cpp#L26-L28
|
||||||
# Servers need a special command to set umode +o on people.
|
# Servers need a special command to set umode +o on people.
|
||||||
self._operUp(target)
|
self._operUp(target)
|
||||||
self.irc.applyModes(target, modes)
|
self.apply_modes(target, modes)
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.join_modes(modes)
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.to_lower(target)].ts
|
||||||
self._send(numeric, 'FMODE %s %s %s' % (target, ts, joinedmodes))
|
self._send_with_prefix(numeric, 'FMODE %s %s %s' % (target, ts, joinedmodes))
|
||||||
else:
|
else:
|
||||||
self._send(numeric, 'MODE %s %s' % (target, joinedmodes))
|
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
||||||
|
|
||||||
def kill(self, numeric, target, reason):
|
def kill(self, numeric, target, reason):
|
||||||
"""Sends a kill from a PyLink client/server."""
|
"""Sends a kill from a PyLink client/server."""
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# InspIRCd will show the raw kill message sent from our server as the quit message.
|
# InspIRCd will show the raw kill message sent from our server as the quit message.
|
||||||
# So, make the kill look actually like a kill instead of someone quitting with
|
# So, make the kill look actually like a kill instead of someone quitting with
|
||||||
# an arbitrary message.
|
# an arbitrary message.
|
||||||
if numeric in self.irc.servers:
|
if numeric in self.servers:
|
||||||
sourcenick = self.irc.servers[numeric].name
|
sourcenick = self.servers[numeric].name
|
||||||
else:
|
else:
|
||||||
sourcenick = self.irc.users[numeric].nick
|
sourcenick = self.users[numeric].nick
|
||||||
|
|
||||||
self._send(numeric, 'KILL %s :Killed (%s (%s))' % (target, sourcenick, reason))
|
self._send_with_prefix(numeric, 'KILL %s :Killed (%s (%s))' % (target, sourcenick, reason))
|
||||||
|
|
||||||
# We only need to call removeClient here if the target is one of our
|
# We only need to call _remove_client here if the target is one of our
|
||||||
# clients, since any remote servers will send a QUIT from
|
# clients, since any remote servers will send a QUIT from
|
||||||
# their target if the command succeeds.
|
# their target if the command succeeds.
|
||||||
if self.irc.isInternalClient(target):
|
if self.is_internal_client(target):
|
||||||
self.removeClient(target)
|
self._remove_client(target)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topic_burst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.is_internal_server(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
ts = int(time.time())
|
ts = int(time.time())
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
self._send(numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
|
self._send_with_prefix(numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(numeric, 'INVITE %s %s' % (target, channel))
|
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
||||||
|
|
||||||
def knock(self, numeric, target, text):
|
def knock(self, numeric, target, text):
|
||||||
"""Sends a KNOCK from a PyLink client."""
|
"""Sends a KNOCK from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(numeric, 'ENCAP * KNOCK %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'ENCAP * KNOCK %s :%s' % (target, text))
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def update_client(self, target, field, text):
|
||||||
"""Updates the ident, host, or realname of any connected client."""
|
"""Updates the ident, host, or realname of any connected client."""
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
|
|
||||||
@ -263,58 +263,58 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
"unsupported by this protocol." % field)
|
"unsupported by this protocol." % field)
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.is_internal_client(target):
|
||||||
# It is one of our clients, use FIDENT/HOST/NAME.
|
# It is one of our clients, use FIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send(target, 'FIDENT %s' % text)
|
self._send_with_prefix(target, 'FIDENT %s' % text)
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send(target, 'FHOST %s' % text)
|
self._send_with_prefix(target, 'FHOST %s' % text)
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send(target, 'FNAME :%s' % text)
|
self._send_with_prefix(target, 'FNAME :%s' % text)
|
||||||
else:
|
else:
|
||||||
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
if 'm_chgident.so' not in self.modsupport:
|
if 'm_chgident.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change ident of %s to %r: load m_chgident.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change ident of %s to %r: load m_chgident.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send(self.irc.sid, 'CHGIDENT %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGIDENT %s %s' % (target, text))
|
||||||
|
|
||||||
# Send hook payloads for other plugins to listen to.
|
# Send hook payloads for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGIDENT',
|
self.call_hooks([self.sid, 'CHGIDENT',
|
||||||
{'target': target, 'newident': text}])
|
{'target': target, 'newident': text}])
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
if 'm_chghost.so' not in self.modsupport:
|
if 'm_chghost.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change host of %s to %r: load m_chghost.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change host of %s to %r: load m_chghost.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send(self.irc.sid, 'CHGHOST %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s %s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.call_hooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
|
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
if 'm_chgname.so' not in self.modsupport:
|
if 'm_chgname.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change real name of %s to %r: load m_chgname.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change real name of %s to %r: load m_chgname.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send(self.irc.sid, 'CHGNAME %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGNAME %s :%s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGNAME',
|
self.call_hooks([self.sid, 'CHGNAME',
|
||||||
{'target': target, 'newgecos': text}])
|
{'target': target, 'newgecos': text}])
|
||||||
|
|
||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
target = target or self.irc.uplink
|
target = target or self.uplink
|
||||||
if not (target is None or source is None):
|
if not (target is None or source is None):
|
||||||
self._send(source, 'PING %s %s' % (source, target))
|
self._send_with_prefix(source, 'PING %s %s' % (source, target))
|
||||||
|
|
||||||
def numeric(self, source, numeric, target, text):
|
def numeric(self, source, numeric, target, text):
|
||||||
"""Sends raw numerics from a server to a remote client."""
|
"""Sends raw numerics from a server to a remote client."""
|
||||||
@ -327,18 +327,18 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
|
# :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
|
||||||
# Take this into consideration if we ever target InspIRCd 2.2, even though m_spanningtree
|
# Take this into consideration if we ever target InspIRCd 2.2, even though m_spanningtree
|
||||||
# does provide backwards compatibility for commands like this. -GLolol
|
# does provide backwards compatibility for commands like this. -GLolol
|
||||||
self._send(self.irc.sid, 'PUSH %s ::%s %s %s %s' % (target, source, numeric, target, text))
|
self._send_with_prefix(self.sid, 'PUSH %s ::%s %s %s %s' % (target, source, numeric, target, text))
|
||||||
|
|
||||||
def away(self, source, text):
|
def away(self, source, text):
|
||||||
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
||||||
to unset AWAY status."""
|
to unset AWAY status."""
|
||||||
if text:
|
if text:
|
||||||
self._send(source, 'AWAY %s :%s' % (int(time.time()), text))
|
self._send_with_prefix(source, 'AWAY %s :%s' % (int(time.time()), text))
|
||||||
else:
|
else:
|
||||||
self._send(source, 'AWAY')
|
self._send_with_prefix(source, 'AWAY')
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
def spawn_server(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
||||||
"""
|
"""
|
||||||
Spawns a server off a PyLink server. desc (server description)
|
Spawns a server off a PyLink server. desc (server description)
|
||||||
defaults to the one in the config. uplink defaults to the main PyLink
|
defaults to the one in the config. uplink defaults to the main PyLink
|
||||||
@ -351,68 +351,68 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
and prevent connections from filling up the snomasks too much.
|
and prevent connections from filling up the snomasks too much.
|
||||||
"""
|
"""
|
||||||
# -> :0AL SERVER test.server * 1 0AM :some silly pseudoserver
|
# -> :0AL SERVER test.server * 1 0AM :some silly pseudoserver
|
||||||
uplink = uplink or self.irc.sid
|
uplink = uplink or self.sid
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
# "desc" defaults to the configured server description.
|
# "desc" defaults to the configured server description.
|
||||||
desc = desc or self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = desc or self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
if sid is None: # No sid given; generate one!
|
if sid is None: # No sid given; generate one!
|
||||||
sid = self.sidgen.next_sid()
|
sid = self.sidgen.next_sid()
|
||||||
assert len(sid) == 3, "Incorrect SID length"
|
assert len(sid) == 3, "Incorrect SID length"
|
||||||
if sid in self.irc.servers:
|
if sid in self.servers:
|
||||||
raise ValueError('A server with SID %r already exists!' % sid)
|
raise ValueError('A server with SID %r already exists!' % sid)
|
||||||
for server in self.irc.servers.values():
|
for server in self.servers.values():
|
||||||
if name == server.name:
|
if name == server.name:
|
||||||
raise ValueError('A server named %r already exists!' % name)
|
raise ValueError('A server named %r already exists!' % name)
|
||||||
if not self.irc.isInternalServer(uplink):
|
if not self.is_internal_server(uplink):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
||||||
if not utils.isServerName(name):
|
if not utils.isServerName(name):
|
||||||
raise ValueError('Invalid server name %r' % name)
|
raise ValueError('Invalid server name %r' % name)
|
||||||
self._send(uplink, 'SERVER %s * 1 %s :%s' % (name, sid, desc))
|
self._send_with_prefix(uplink, 'SERVER %s * 1 %s :%s' % (name, sid, desc))
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
self.servers[sid] = Server(uplink, name, internal=True, desc=desc)
|
||||||
|
|
||||||
def endburstf():
|
def endburstf():
|
||||||
# Delay ENDBURST by X seconds if requested.
|
# Delay ENDBURST by X seconds if requested.
|
||||||
if self.irc.aborted.wait(endburst_delay):
|
if self.aborted.wait(endburst_delay):
|
||||||
# We managed to catch the abort flag before sending ENDBURST, so break
|
# We managed to catch the abort flag before sending ENDBURST, so break
|
||||||
log.debug('(%s) stopping endburstf() for %s as aborted was set', self.irc.name, sid)
|
log.debug('(%s) stopping endburstf() for %s as aborted was set', self.name, sid)
|
||||||
return
|
return
|
||||||
self._send(sid, 'ENDBURST')
|
self._send_with_prefix(sid, 'ENDBURST')
|
||||||
|
|
||||||
if endburst_delay:
|
if endburst_delay:
|
||||||
threading.Thread(target=endburstf).start()
|
threading.Thread(target=endburstf).start()
|
||||||
else: # Else, send burst immediately
|
else: # Else, send burst immediately
|
||||||
self._send(sid, 'ENDBURST')
|
self._send_with_prefix(sid, 'ENDBURST')
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def squit(self, source, target, text='No reason given'):
|
def squit(self, source, target, text='No reason given'):
|
||||||
"""SQUITs a PyLink server."""
|
"""SQUITs a PyLink server."""
|
||||||
# -> :9PY SQUIT 9PZ :blah, blah
|
# -> :9PY SQUIT 9PZ :blah, blah
|
||||||
self._send(source, 'SQUIT %s :%s' % (target, text))
|
self._send_with_prefix(source, 'SQUIT %s :%s' % (target, text))
|
||||||
self.handle_squit(source, 'SQUIT', [target, text])
|
self.handle_squit(source, 'SQUIT', [target, text])
|
||||||
|
|
||||||
### Core / command handlers
|
### Core / command handlers
|
||||||
|
|
||||||
def connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
|
|
||||||
# Track the modules supported by the uplink.
|
# Track the modules supported by the uplink.
|
||||||
self.modsupport = []
|
self.modsupport = []
|
||||||
|
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
f('CAPAB START %s' % self.proto_ver)
|
f('CAPAB START %s' % self.proto_ver)
|
||||||
f('CAPAB CAPABILITIES :PROTOCOL=%s' % self.proto_ver)
|
f('CAPAB CAPABILITIES :PROTOCOL=%s' % self.proto_ver)
|
||||||
f('CAPAB END')
|
f('CAPAB END')
|
||||||
|
|
||||||
host = self.irc.serverdata["hostname"]
|
host = self.serverdata["hostname"]
|
||||||
f('SERVER {host} {Pass} 0 {sid} :{sdesc}'.format(host=host,
|
f('SERVER {host} {Pass} 0 {sid} :{sdesc}'.format(host=host,
|
||||||
Pass=self.irc.serverdata["sendpass"], sid=self.irc.sid,
|
Pass=self.serverdata["sendpass"], sid=self.sid,
|
||||||
sdesc=self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
sdesc=self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
self._send(self.irc.sid, 'BURST %s' % ts)
|
self._send_with_prefix(self.sid, 'BURST %s' % ts)
|
||||||
# InspIRCd sends VERSION data on link, instead of whenever requested by a client.
|
# InspIRCd sends VERSION data on link, instead of whenever requested by a client.
|
||||||
self._send(self.irc.sid, 'VERSION :%s' % self.irc.version())
|
self._send_with_prefix(self.sid, 'VERSION :%s' % self.version())
|
||||||
self._send(self.irc.sid, 'ENDBURST')
|
self._send_with_prefix(self.sid, 'ENDBURST')
|
||||||
|
|
||||||
def handle_capab(self, source, command, args):
|
def handle_capab(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
@ -453,7 +453,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
name = 'owner'
|
name = 'owner'
|
||||||
|
|
||||||
# We don't care about mode prefixes; just the mode char.
|
# We don't care about mode prefixes; just the mode char.
|
||||||
self.irc.cmodes[name] = char[-1]
|
self.cmodes[name] = char[-1]
|
||||||
|
|
||||||
|
|
||||||
elif args[0] == 'USERMODES':
|
elif args[0] == 'USERMODES':
|
||||||
@ -467,7 +467,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
name, char = modepair.split('=')
|
name, char = modepair.split('=')
|
||||||
# Strip u_ prefixes to be consistent with other protocols.
|
# Strip u_ prefixes to be consistent with other protocols.
|
||||||
name = name.lstrip('u_')
|
name = name.lstrip('u_')
|
||||||
self.irc.umodes[name] = char
|
self.umodes[name] = char
|
||||||
|
|
||||||
elif args[0] == 'CAPABILITIES':
|
elif args[0] == 'CAPABILITIES':
|
||||||
# <- CAPAB CAPABILITIES :NICKMAX=21 CHANMAX=64 MAXMODES=20
|
# <- CAPAB CAPABILITIES :NICKMAX=21 CHANMAX=64 MAXMODES=20
|
||||||
@ -477,8 +477,8 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# USERMODES=,,s,BHIRSWcghikorwx GLOBOPS=1 SVSPART=1
|
# USERMODES=,,s,BHIRSWcghikorwx GLOBOPS=1 SVSPART=1
|
||||||
|
|
||||||
# First, turn the arguments into a dict
|
# First, turn the arguments into a dict
|
||||||
caps = self.parseCapabilities(args[-1])
|
caps = self.parse_isupport(args[-1])
|
||||||
log.debug("(%s) capabilities list: %s", self.irc.name, caps)
|
log.debug("(%s) capabilities list: %s", self.name, caps)
|
||||||
|
|
||||||
# Check the protocol version
|
# Check the protocol version
|
||||||
self.remote_proto_ver = protocol_version = int(caps['PROTOCOL'])
|
self.remote_proto_ver = protocol_version = int(caps['PROTOCOL'])
|
||||||
@ -491,30 +491,30 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
elif protocol_version > self.max_proto_ver:
|
elif protocol_version > self.max_proto_ver:
|
||||||
log.warning("(%s) PyLink support for InspIRCd 2.2+ is experimental, "
|
log.warning("(%s) PyLink support for InspIRCd 2.2+ is experimental, "
|
||||||
"and should not be relied upon for anything important.",
|
"and should not be relied upon for anything important.",
|
||||||
self.irc.name)
|
self.name)
|
||||||
|
|
||||||
# Store the max nick and channel lengths
|
# Store the max nick and channel lengths
|
||||||
self.irc.maxnicklen = int(caps['NICKMAX'])
|
self.maxnicklen = int(caps['NICKMAX'])
|
||||||
self.irc.maxchanlen = int(caps['CHANMAX'])
|
self.maxchanlen = int(caps['CHANMAX'])
|
||||||
|
|
||||||
# Modes are divided into A, B, C, and D classes
|
# Modes are divided into A, B, C, and D classes
|
||||||
# See http://www.irc.org/tech_docs/005.html
|
# See http://www.irc.org/tech_docs/005.html
|
||||||
|
|
||||||
# FIXME: Find a neater way to assign/store this.
|
# FIXME: Find a neater way to assign/store this.
|
||||||
self.irc.cmodes['*A'], self.irc.cmodes['*B'], self.irc.cmodes['*C'], self.irc.cmodes['*D'] \
|
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] \
|
||||||
= caps['CHANMODES'].split(',')
|
= caps['CHANMODES'].split(',')
|
||||||
self.irc.umodes['*A'], self.irc.umodes['*B'], self.irc.umodes['*C'], self.irc.umodes['*D'] \
|
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] \
|
||||||
= caps['USERMODES'].split(',')
|
= caps['USERMODES'].split(',')
|
||||||
|
|
||||||
# Separate the prefixes field (e.g. "(Yqaohv)!~&@%+") into a
|
# Separate the prefixes field (e.g. "(Yqaohv)!~&@%+") into a
|
||||||
# dict mapping mode characters to mode prefixes.
|
# dict mapping mode characters to mode prefixes.
|
||||||
self.irc.prefixmodes = self.parsePrefixes(caps['PREFIX'])
|
self.prefixmodes = self.parse_isupport_prefixes(caps['PREFIX'])
|
||||||
log.debug('(%s) self.irc.prefixmodes set to %r', self.irc.name,
|
log.debug('(%s) self.prefixmodes set to %r', self.name,
|
||||||
self.irc.prefixmodes)
|
self.prefixmodes)
|
||||||
|
|
||||||
# Finally, set the irc.connected (protocol negotiation complete)
|
# Finally, set the irc.connected (protocol negotiation complete)
|
||||||
# state to True.
|
# state to True.
|
||||||
self.irc.connected.set()
|
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()
|
||||||
@ -523,19 +523,19 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
"""Handles incoming PING commands, so we don't time out."""
|
"""Handles incoming PING commands, so we don't time out."""
|
||||||
# <- :70M PING 70M 0AL
|
# <- :70M PING 70M 0AL
|
||||||
# -> :0AL PONG 0AL 70M
|
# -> :0AL PONG 0AL 70M
|
||||||
if self.irc.isInternalServer(args[1]):
|
if self.is_internal_server(args[1]):
|
||||||
self._send(args[1], 'PONG %s %s' % (args[1], source), queue=False)
|
self._send_with_prefix(args[1], 'PONG %s %s' % (args[1], source), queue=False)
|
||||||
|
|
||||||
def handle_fjoin(self, servernumeric, command, args):
|
def handle_fjoin(self, servernumeric, command, args):
|
||||||
"""Handles incoming FJOIN commands (InspIRCd equivalent of JOIN/SJOIN)."""
|
"""Handles incoming FJOIN commands (InspIRCd equivalent of JOIN/SJOIN)."""
|
||||||
# :70M FJOIN #chat 1423790411 +AFPfjnt 6:5 7:5 9:5 :o,1SRAABIT4 v,1IOAAF53R <...>
|
# :70M FJOIN #chat 1423790411 +AFPfjnt 6:5 7:5 9:5 :o,1SRAABIT4 v,1IOAAF53R <...>
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
# InspIRCd sends each channel's users in the form of 'modeprefix(es),UID'
|
# InspIRCd sends each channel's users in the form of 'modeprefix(es),UID'
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
modestring = args[2:-1] or args[2]
|
modestring = args[2:-1] or args[2]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parse_modes(channel, modestring)
|
||||||
namelist = []
|
namelist = []
|
||||||
|
|
||||||
# Keep track of other modes that are added due to prefix modes being joined too.
|
# Keep track of other modes that are added due to prefix modes being joined too.
|
||||||
@ -545,18 +545,18 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
modeprefix, user = user.split(',', 1)
|
modeprefix, user = user.split(',', 1)
|
||||||
|
|
||||||
# Don't crash when we get an invalid UID.
|
# Don't crash when we get an invalid UID.
|
||||||
if user not in self.irc.users:
|
if user not in self.users:
|
||||||
log.debug('(%s) handle_fjoin: tried to introduce user %s not in our user list, ignoring...',
|
log.debug('(%s) handle_fjoin: tried to introduce user %s not in our user list, ignoring...',
|
||||||
self.irc.name, user)
|
self.name, user)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only save mode changes if the remote has lower TS than us.
|
# Only save mode changes if the remote has lower TS than us.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in modeprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in modeprefix}
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
# Statekeeping with timestamps. Note: some service packages (Anope 1.8) send a trailing
|
# Statekeeping with timestamps. Note: some service packages (Anope 1.8) send a trailing
|
||||||
# 'd' after the timestamp, which we should strip out to prevent int() from erroring.
|
# 'd' after the timestamp, which we should strip out to prevent int() from erroring.
|
||||||
@ -565,7 +565,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK
|
# <- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK
|
||||||
their_ts = int(''.join(char for char in args[1] if char.isdigit()))
|
their_ts = int(''.join(char for char in args[1] if char.isdigit()))
|
||||||
|
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
||||||
@ -577,35 +577,35 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
uid, ts, nick, realhost, host, ident, ip = args[0:7]
|
uid, ts, nick, realhost, host, ident, ip = args[0:7]
|
||||||
self.check_nick_collision(nick)
|
self.check_nick_collision(nick)
|
||||||
realname = args[-1]
|
realname = args[-1]
|
||||||
self.irc.users[uid] = userobj = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = userobj = User(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [args[8], args[9]])
|
parsedmodes = self.parse_modes(uid, [args[8], args[9]])
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.apply_modes(uid, parsedmodes)
|
||||||
|
|
||||||
if (self.irc.umodes.get('servprotect'), None) in userobj.modes:
|
if (self.umodes.get('servprotect'), None) in userobj.modes:
|
||||||
# Services are usually given a "Network Service" WHOIS, so
|
# Services are usually given a "Network Service" WHOIS, so
|
||||||
# set that as the opertype.
|
# set that as the opertype.
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'Network Service'}])
|
self.call_hooks([uid, 'CLIENT_OPERED', {'text': 'Network Service'}])
|
||||||
|
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
"""Handles incoming SERVER commands (introduction of servers)."""
|
"""Handles incoming SERVER commands (introduction of servers)."""
|
||||||
|
|
||||||
# Initial SERVER command on connect.
|
# Initial SERVER command on connect.
|
||||||
if self.irc.uplink is None:
|
if self.uplink is None:
|
||||||
# <- SERVER whatever.net abcdefgh 0 10X :some server description
|
# <- SERVER whatever.net abcdefgh 0 10X :some server description
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
numeric = args[3]
|
numeric = args[3]
|
||||||
|
|
||||||
if args[1] != self.irc.serverdata['recvpass']:
|
if args[1] != self.serverdata['recvpass']:
|
||||||
# Check if recvpass is correct
|
# Check if recvpass is correct
|
||||||
raise ProtocolError('Error: recvpass from uplink server %s does not match configuration!' % servername)
|
raise ProtocolError('Error: recvpass from uplink server %s does not match configuration!' % servername)
|
||||||
|
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[numeric] = IrcServer(None, servername, desc=sdesc)
|
self.servers[numeric] = Server(None, servername, desc=sdesc)
|
||||||
self.irc.uplink = numeric
|
self.uplink = numeric
|
||||||
return
|
return
|
||||||
|
|
||||||
# Other server introductions.
|
# Other server introductions.
|
||||||
@ -613,18 +613,18 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sid = args[3]
|
sid = args[3]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[sid] = Server(numeric, servername, desc=sdesc)
|
||||||
|
|
||||||
return {'name': servername, 'sid': args[3], 'text': sdesc}
|
return {'name': servername, 'sid': args[3], 'text': sdesc}
|
||||||
|
|
||||||
def handle_fmode(self, numeric, command, args):
|
def handle_fmode(self, numeric, command, args):
|
||||||
"""Handles the FMODE command, used for channel mode changes."""
|
"""Handles the FMODE command, used for channel mode changes."""
|
||||||
# <- :70MAAAAAA FMODE #chat 1433653462 +hhT 70MAAAAAA 70MAAAAAD
|
# <- :70MAAAAAA FMODE #chat 1433653462 +hhT 70MAAAAAA 70MAAAAAD
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
changedmodes = self.irc.parseModes(channel, modes)
|
changedmodes = self.parse_modes(channel, modes)
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.apply_modes(channel, changedmodes)
|
||||||
ts = int(args[1])
|
ts = int(args[1])
|
||||||
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
||||||
'channeldata': oldobj}
|
'channeldata': oldobj}
|
||||||
@ -636,8 +636,8 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
||||||
target = args[0]
|
target = args[0]
|
||||||
modestrings = args[1:]
|
modestrings = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modestrings)
|
changedmodes = self.parse_modes(target, modestrings)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.apply_modes(target, changedmodes)
|
||||||
return {'target': target, 'modes': changedmodes}
|
return {'target': target, 'modes': changedmodes}
|
||||||
|
|
||||||
def handle_idle(self, numeric, command, args):
|
def handle_idle(self, numeric, command, args):
|
||||||
@ -662,29 +662,21 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
def handle_ftopic(self, numeric, command, args):
|
def handle_ftopic(self, numeric, command, args):
|
||||||
"""Handles incoming FTOPIC (sets topic on burst)."""
|
"""Handles incoming FTOPIC (sets topic on burst)."""
|
||||||
# <- :70M FTOPIC #channel 1434510754 GLo|o|!GLolol@escape.the.dreamland.ca :Some channel topic
|
# <- :70M FTOPIC #channel 1434510754 GLo|o|!GLolol@escape.the.dreamland.ca :Some channel topic
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
setter = args[2]
|
setter = args[2]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
# SVSTOPIC is used by InspIRCd module m_topiclock - its arguments are the same as FTOPIC
|
# SVSTOPIC is used by InspIRCd module m_topiclock - its arguments are the same as FTOPIC
|
||||||
handle_svstopic = handle_ftopic
|
handle_svstopic = handle_ftopic
|
||||||
|
|
||||||
def handle_invite(self, numeric, command, args):
|
|
||||||
"""Handles incoming INVITEs."""
|
|
||||||
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 0
|
|
||||||
target = args[0]
|
|
||||||
channel = self.irc.toLower(args[1])
|
|
||||||
# We don't actually need to process this; just send the hook so plugins can use it
|
|
||||||
return {'target': target, 'channel': channel}
|
|
||||||
|
|
||||||
def handle_knock(self, numeric, command, args):
|
def handle_knock(self, numeric, command, args):
|
||||||
"""Handles channel KNOCKs."""
|
"""Handles channel KNOCKs."""
|
||||||
# <- :70MAAAAAA ENCAP * KNOCK #blah :abcdefg
|
# <- :70MAAAAAA ENCAP * KNOCK #blah :abcdefg
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
text = args[1]
|
text = args[1]
|
||||||
return {'channel': channel, 'text': text}
|
return {'channel': channel, 'text': text}
|
||||||
|
|
||||||
@ -701,29 +693,29 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Set umode +o on the target.
|
# Set umode +o on the target.
|
||||||
omode = [('+o', None)]
|
omode = [('+o', None)]
|
||||||
self.irc.applyModes(target, omode)
|
self.apply_modes(target, omode)
|
||||||
|
|
||||||
# Call the CLIENT_OPERED hook that protocols use. The MODE hook
|
# Call the CLIENT_OPERED hook that protocols use. The MODE hook
|
||||||
# payload is returned below.
|
# payload is returned below.
|
||||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': opertype}])
|
self.call_hooks([target, 'CLIENT_OPERED', {'text': opertype}])
|
||||||
return {'target': target, 'modes': omode}
|
return {'target': target, 'modes': omode}
|
||||||
|
|
||||||
def handle_fident(self, numeric, command, args):
|
def handle_fident(self, numeric, command, args):
|
||||||
"""Handles FIDENT, used for denoting ident changes."""
|
"""Handles FIDENT, used for denoting ident changes."""
|
||||||
# <- :70MAAAAAB FIDENT test
|
# <- :70MAAAAAB FIDENT test
|
||||||
self.irc.users[numeric].ident = newident = args[0]
|
self.users[numeric].ident = newident = args[0]
|
||||||
return {'target': numeric, 'newident': newident}
|
return {'target': numeric, 'newident': newident}
|
||||||
|
|
||||||
def handle_fhost(self, numeric, command, args):
|
def handle_fhost(self, numeric, command, args):
|
||||||
"""Handles FHOST, used for denoting hostname changes."""
|
"""Handles FHOST, used for denoting hostname changes."""
|
||||||
# <- :70MAAAAAB FHOST some.host
|
# <- :70MAAAAAB FHOST some.host
|
||||||
self.irc.users[numeric].host = newhost = args[0]
|
self.users[numeric].host = newhost = args[0]
|
||||||
return {'target': numeric, 'newhost': newhost}
|
return {'target': numeric, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_fname(self, numeric, command, args):
|
def handle_fname(self, numeric, command, args):
|
||||||
"""Handles FNAME, used for denoting real name/gecos changes."""
|
"""Handles FNAME, used for denoting real name/gecos changes."""
|
||||||
# <- :70MAAAAAB FNAME :afdsafasf
|
# <- :70MAAAAAB FNAME :afdsafasf
|
||||||
self.irc.users[numeric].realname = newgecos = args[0]
|
self.users[numeric].realname = newgecos = args[0]
|
||||||
return {'target': numeric, 'newgecos': newgecos}
|
return {'target': numeric, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_endburst(self, numeric, command, args):
|
def handle_endburst(self, numeric, command, args):
|
||||||
@ -735,10 +727,10 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :1MLAAAAIG AWAY 1439371390 :Auto-away
|
# <- :1MLAAAAIG AWAY 1439371390 :Auto-away
|
||||||
try:
|
try:
|
||||||
ts = args[0]
|
ts = args[0]
|
||||||
self.irc.users[numeric].away = text = args[1]
|
self.users[numeric].away = text = args[1]
|
||||||
return {'text': text, 'ts': ts}
|
return {'text': text, 'ts': ts}
|
||||||
except IndexError: # User is unsetting away status
|
except IndexError: # User is unsetting away status
|
||||||
self.irc.users[numeric].away = ''
|
self.users[numeric].away = ''
|
||||||
return {'text': ''}
|
return {'text': ''}
|
||||||
|
|
||||||
def handle_rsquit(self, numeric, command, args):
|
def handle_rsquit(self, numeric, command, args):
|
||||||
@ -759,16 +751,16 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# If we receive such a remote SQUIT, just forward it as a regular
|
# If we receive such a remote SQUIT, just forward it as a regular
|
||||||
# SQUIT, in order to be consistent with other IRCds which make SQUITs
|
# SQUIT, in order to be consistent with other IRCds which make SQUITs
|
||||||
# implicit.
|
# implicit.
|
||||||
target = self._getSid(args[0])
|
target = self._get_SID(args[0])
|
||||||
if self.irc.isInternalServer(target):
|
if self.is_internal_server(target):
|
||||||
# The target has to be one of our servers in order to work...
|
# The target has to be one of our servers in order to work...
|
||||||
uplink = self.irc.servers[target].uplink
|
uplink = self.servers[target].uplink
|
||||||
reason = 'Requested by %s' % self.irc.getHostmask(numeric)
|
reason = 'Requested by %s' % self.get_hostmask(numeric)
|
||||||
self._send(uplink, 'SQUIT %s :%s' % (target, reason))
|
self._send_with_prefix(uplink, 'SQUIT %s :%s' % (target, reason))
|
||||||
return self.handle_squit(numeric, 'SQUIT', [target, reason])
|
return self.handle_squit(numeric, 'SQUIT', [target, reason])
|
||||||
else:
|
else:
|
||||||
log.debug("(%s) Got RSQUIT for '%s', which is either invalid or not "
|
log.debug("(%s) Got RSQUIT for '%s', which is either invalid or not "
|
||||||
"a server of ours!", self.irc.name, args[0])
|
"a server of ours!", self.name, args[0])
|
||||||
|
|
||||||
def handle_metadata(self, numeric, command, args):
|
def handle_metadata(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -777,12 +769,12 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
"""
|
"""
|
||||||
uid = args[0]
|
uid = args[0]
|
||||||
|
|
||||||
if args[1] == 'accountname' and uid in self.irc.users:
|
if args[1] == 'accountname' and uid in self.users:
|
||||||
# <- :00A METADATA 1MLAAAJET accountname :
|
# <- :00A METADATA 1MLAAAJET accountname :
|
||||||
# <- :00A METADATA 1MLAAAJET accountname :tester
|
# <- :00A METADATA 1MLAAAJET accountname :tester
|
||||||
# Sets the services login name of the client.
|
# Sets the services login name of the client.
|
||||||
|
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': args[-1]}])
|
self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': args[-1]}])
|
||||||
|
|
||||||
def handle_version(self, numeric, command, args):
|
def handle_version(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -797,9 +789,9 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# removed from our user list.
|
# removed from our user list.
|
||||||
# If not, we have to assume that KILL = QUIT and remove them
|
# If not, we have to assume that KILL = QUIT and remove them
|
||||||
# ourselves.
|
# ourselves.
|
||||||
data = self.irc.users.get(killed)
|
data = self.users.get(killed)
|
||||||
if data:
|
if data:
|
||||||
self.removeClient(killed)
|
self._remove_client(killed)
|
||||||
return {'target': killed, 'text': args[1], 'userdata': data}
|
return {'target': killed, 'text': args[1], 'userdata': data}
|
||||||
|
|
||||||
def handle_sakick(self, source, command, args):
|
def handle_sakick(self, source, command, args):
|
||||||
@ -808,20 +800,20 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# ENCAP -> SAKICK args: ['#test', '0ALAAAAAB', 'test']
|
# ENCAP -> SAKICK args: ['#test', '0ALAAAAAB', 'test']
|
||||||
|
|
||||||
target = args[1]
|
target = args[1]
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
try:
|
try:
|
||||||
reason = args[2]
|
reason = args[2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Kick reason is optional, strange...
|
# Kick reason is optional, strange...
|
||||||
reason = self.irc.getFriendlyName(source)
|
reason = self.get_friendly_name(source)
|
||||||
|
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.is_internal_client(target):
|
||||||
log.warning("(%s) Got SAKICK for client that not one of ours: %s", self.irc.name, target)
|
log.warning("(%s) Got SAKICK for client that not one of ours: %s", self.name, target)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Like RSQUIT, SAKICK requires that the receiving server acknowledge that a kick has
|
# Like RSQUIT, SAKICK requires that the receiving server acknowledge that a kick has
|
||||||
# happened. This comes from the server hosting the target client.
|
# happened. This comes from the server hosting the target client.
|
||||||
server = self.irc.getServer(target)
|
server = self.get_server(target)
|
||||||
|
|
||||||
self.kick(server, channel, target, reason)
|
self.kick(server, channel, target, reason)
|
||||||
return {'channel': channel, 'target': target, 'text': reason}
|
return {'channel': channel, 'target': target, 'text': reason}
|
||||||
@ -833,6 +825,6 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
||||||
timestring = '%s (%s)' % (time.strftime('%Y-%m-%d %H:%M:%S'), int(time.time()))
|
timestring = '%s (%s)' % (time.strftime('%Y-%m-%d %H:%M:%S'), int(time.time()))
|
||||||
self._send(self.irc.sid, 'NOTICE %s :System time is %s on %s' % (source, timestring, self.irc.hostname()))
|
self._send_with_prefix(self.sid, 'NOTICE %s :System time is %s on %s' % (source, timestring, self.hostname()))
|
||||||
|
|
||||||
Class = InspIRCdProtocol
|
Class = InspIRCdProtocol
|
||||||
|
@ -3,16 +3,147 @@ ircs2s_common.py: Common base protocol class with functions shared by TS6 and P1
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from pylinkirc.classes import Protocol
|
from pylinkirc.classes import IRCNetwork, ProtocolError
|
||||||
from pylinkirc.log import log
|
from pylinkirc.log import log
|
||||||
from pylinkirc import utils
|
from pylinkirc import utils
|
||||||
|
|
||||||
class IRCS2SProtocol(Protocol):
|
class IRCCommonProtocol(IRCNetwork):
|
||||||
|
def validate_server_conf(self):
|
||||||
|
"""Validates that the server block given contains the required keys."""
|
||||||
|
for k in self.conf_keys:
|
||||||
|
assert k in self.serverdata, "Missing option %r in server block for network %s." % (k, self.name)
|
||||||
|
|
||||||
|
port = self.serverdata['port']
|
||||||
|
assert type(port) == int and 0 < port < 65535, "Invalid port %r for network %s" % (port, self.name)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_args(args):
|
||||||
|
"""
|
||||||
|
Parses a string or list of of RFC1459-style arguments, where ":" may
|
||||||
|
be used for multi-word arguments that last until the end of a line.
|
||||||
|
"""
|
||||||
|
if isinstance(args, str):
|
||||||
|
args = args.split(' ')
|
||||||
|
|
||||||
|
real_args = []
|
||||||
|
for idx, arg in enumerate(args):
|
||||||
|
if arg.startswith(':') and idx != 0:
|
||||||
|
# ":" is used to begin multi-word arguments that last until the end of the message.
|
||||||
|
# Use list splicing here to join them into one argument, and then add it to our list of args.
|
||||||
|
joined_arg = ' '.join(args[idx:])[1:] # Cut off the leading : as well
|
||||||
|
real_args.append(joined_arg)
|
||||||
|
break
|
||||||
|
real_args.append(arg)
|
||||||
|
|
||||||
|
return real_args
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_prefixed_args(cls, args):
|
||||||
|
"""Similar to parse_args(), but stripping leading colons from the first argument
|
||||||
|
of a line (usually the sender field)."""
|
||||||
|
args = cls.parse_args(args)
|
||||||
|
args[0] = args[0].split(':', 1)[1]
|
||||||
|
return args
|
||||||
|
|
||||||
|
def _squit(self, numeric, command, args):
|
||||||
|
"""Handles incoming SQUITs."""
|
||||||
|
|
||||||
|
split_server = self._get_SID(args[0])
|
||||||
|
|
||||||
|
# Normally we'd only need to check for our SID as the SQUIT target, but Nefarious
|
||||||
|
# actually uses the uplink server as the SQUIT target.
|
||||||
|
# <- ABAAE SQ nefarious.midnight.vpn 0 :test
|
||||||
|
if split_server in (self.sid, self.uplink):
|
||||||
|
raise ProtocolError('SQUIT received: (reason: %s)' % args[-1])
|
||||||
|
|
||||||
|
affected_users = []
|
||||||
|
affected_nicks = defaultdict(list)
|
||||||
|
log.debug('(%s) Splitting server %s (reason: %s)', self.name, split_server, args[-1])
|
||||||
|
|
||||||
|
if split_server not in self.servers:
|
||||||
|
log.warning("(%s) Tried to split a server (%s) that didn't exist!", self.name, split_server)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prevent RuntimeError: dictionary changed size during iteration
|
||||||
|
old_servers = self.servers.copy()
|
||||||
|
old_channels = self.channels.copy()
|
||||||
|
|
||||||
|
# Cycle through our list of servers. If any server's uplink is the one that is being SQUIT,
|
||||||
|
# remove them and all their users too.
|
||||||
|
for sid, data in old_servers.items():
|
||||||
|
if data.uplink == split_server:
|
||||||
|
log.debug('Server %s also hosts server %s, removing those users too...', split_server, sid)
|
||||||
|
# Recursively run SQUIT on any other hubs this server may have been connected to.
|
||||||
|
args = self._squit(sid, 'SQUIT', [sid, "0",
|
||||||
|
"PyLink: Automatically splitting leaf servers of %s" % sid])
|
||||||
|
affected_users += args['users']
|
||||||
|
|
||||||
|
for user in self.servers[split_server].users.copy():
|
||||||
|
affected_users.append(user)
|
||||||
|
nick = self.users[user].nick
|
||||||
|
|
||||||
|
# Nicks affected is channel specific for SQUIT:. This makes Clientbot's SQUIT relaying
|
||||||
|
# much easier to implement.
|
||||||
|
for name, cdata in old_channels.items():
|
||||||
|
if user in cdata.users:
|
||||||
|
affected_nicks[name].append(nick)
|
||||||
|
|
||||||
|
log.debug('Removing client %s (%s)', user, nick)
|
||||||
|
self._remove_client(user)
|
||||||
|
|
||||||
|
serverdata = self.servers[split_server]
|
||||||
|
sname = serverdata.name
|
||||||
|
uplink = serverdata.uplink
|
||||||
|
|
||||||
|
del self.servers[split_server]
|
||||||
|
log.debug('(%s) Netsplit affected users: %s', self.name, affected_users)
|
||||||
|
|
||||||
|
return {'target': split_server, 'users': affected_users, 'name': sname,
|
||||||
|
'uplink': uplink, 'nicks': affected_nicks, 'serverdata': serverdata,
|
||||||
|
'channeldata': old_channels}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_isupport(args, fallback=''):
|
||||||
|
"""
|
||||||
|
Parses a string of capabilities in the 005 / RPL_ISUPPORT format.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type(args) == str:
|
||||||
|
args = args.split(' ')
|
||||||
|
|
||||||
|
caps = {}
|
||||||
|
for cap in args:
|
||||||
|
try:
|
||||||
|
# Try to split it as a KEY=VALUE pair.
|
||||||
|
key, value = cap.split('=', 1)
|
||||||
|
except ValueError:
|
||||||
|
key = cap
|
||||||
|
value = fallback
|
||||||
|
caps[key] = value
|
||||||
|
|
||||||
|
return caps
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_isupport_prefixes(args):
|
||||||
|
"""
|
||||||
|
Separates prefixes field like "(qaohv)~&@%+" into a dict mapping mode characters to mode
|
||||||
|
prefixes.
|
||||||
|
"""
|
||||||
|
prefixsearch = re.search(r'\(([A-Za-z]+)\)(.*)', args)
|
||||||
|
return dict(zip(prefixsearch.group(1), prefixsearch.group(2)))
|
||||||
|
|
||||||
|
def handle_error(self, numeric, command, args):
|
||||||
|
"""Handles ERROR messages - these mean that our uplink has disconnected us!"""
|
||||||
|
raise ProtocolError('Received an ERROR, disconnecting!')
|
||||||
|
|
||||||
|
class IRCS2SProtocol(IRCCommonProtocol):
|
||||||
COMMAND_TOKENS = {}
|
COMMAND_TOKENS = {}
|
||||||
|
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
self.protocol_caps = {'can-spawn-clients', 'has-ts', 'can-host-relay',
|
self.protocol_caps = {'can-spawn-clients', 'has-ts', 'can-host-relay',
|
||||||
'can-track-servers'}
|
'can-track-servers'}
|
||||||
|
|
||||||
@ -28,46 +159,46 @@ class IRCS2SProtocol(Protocol):
|
|||||||
the SID of the uplink server.
|
the SID of the uplink server.
|
||||||
"""
|
"""
|
||||||
data = data.split(" ")
|
data = data.split(" ")
|
||||||
args = self.parseArgs(data)
|
args = self.parse_args(data)
|
||||||
|
|
||||||
sender = args[0]
|
sender = args[0]
|
||||||
sender = sender.lstrip(':')
|
sender = sender.lstrip(':')
|
||||||
|
|
||||||
# If the sender isn't in numeric format, try to convert it automatically.
|
# If the sender isn't in numeric format, try to convert it automatically.
|
||||||
sender_sid = self._getSid(sender)
|
sender_sid = self._get_SID(sender)
|
||||||
sender_uid = self._getUid(sender)
|
sender_uid = self._get_UID(sender)
|
||||||
|
|
||||||
if sender_sid in self.irc.servers:
|
if sender_sid in self.servers:
|
||||||
# Sender is a server (converting from name to SID gave a valid result).
|
# Sender is a server (converting from name to SID gave a valid result).
|
||||||
sender = sender_sid
|
sender = sender_sid
|
||||||
elif sender_uid in self.irc.users:
|
elif sender_uid in self.users:
|
||||||
# Sender is a user (converting from name to UID gave a valid result).
|
# Sender is a user (converting from name to UID gave a valid result).
|
||||||
sender = sender_uid
|
sender = sender_uid
|
||||||
else:
|
else:
|
||||||
# No sender prefix; treat as coming from uplink IRCd.
|
# No sender prefix; treat as coming from uplink IRCd.
|
||||||
sender = self.irc.uplink
|
sender = self.uplink
|
||||||
args.insert(0, sender)
|
args.insert(0, sender)
|
||||||
|
|
||||||
if self.irc.isInternalClient(sender) or self.irc.isInternalServer(sender):
|
if self.is_internal_client(sender) or self.is_internal_server(sender):
|
||||||
log.warning("(%s) Received command %s being routed the wrong way!", self.irc.name, command)
|
log.warning("(%s) Received command %s being routed the wrong way!", self.name, command)
|
||||||
return
|
return
|
||||||
|
|
||||||
raw_command = args[1].upper()
|
raw_command = args[1].upper()
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
|
|
||||||
log.debug('(%s) Found message sender as %s', self.irc.name, sender)
|
log.debug('(%s) Found message sender as %s, raw_command=%r, args=%r', self.name, sender, raw_command, args)
|
||||||
|
|
||||||
# For P10, convert the command token into a regular command, if present.
|
# For P10, convert the command token into a regular command, if present.
|
||||||
command = self.COMMAND_TOKENS.get(raw_command, raw_command)
|
command = self.COMMAND_TOKENS.get(raw_command, raw_command)
|
||||||
if command != raw_command:
|
if command != raw_command:
|
||||||
log.debug('(%s) Translating token %s to command %s', self.irc.name, raw_command, command)
|
log.debug('(%s) Translating token %s to command %s', self.name, raw_command, command)
|
||||||
|
|
||||||
if command == 'ENCAP':
|
if command == 'ENCAP':
|
||||||
# Special case for TS6 encapsulated commands (ENCAP), in forms like this:
|
# Special case for TS6 encapsulated commands (ENCAP), in forms like this:
|
||||||
# <- :00A ENCAP * SU 42XAAAAAC :GLolol
|
# <- :00A ENCAP * SU 42XAAAAAC :GLolol
|
||||||
command = args[1]
|
command = args[1]
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.irc.name, command, args)
|
log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.name, command, args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
func = getattr(self, 'handle_'+command.lower())
|
func = getattr(self, 'handle_'+command.lower())
|
||||||
@ -78,46 +209,51 @@ class IRCS2SProtocol(Protocol):
|
|||||||
if parsed_args is not None:
|
if parsed_args is not None:
|
||||||
return [sender, command, parsed_args]
|
return [sender, command, parsed_args]
|
||||||
|
|
||||||
def handle_privmsg(self, source, command, args):
|
|
||||||
"""Handles incoming PRIVMSG/NOTICE."""
|
|
||||||
# TS6:
|
|
||||||
# <- :70MAAAAAA PRIVMSG #dev :afasfsa
|
|
||||||
# <- :70MAAAAAA NOTICE 0ALAAAAAA :afasfsa
|
|
||||||
# P10:
|
|
||||||
# <- ABAAA P AyAAA :privmsg text
|
|
||||||
# <- ABAAA O AyAAA :notice text
|
|
||||||
target = self._getUid(args[0])
|
|
||||||
|
|
||||||
# Coerse =#channel from Charybdis op moderated +z to @#channel.
|
|
||||||
if target.startswith('='):
|
|
||||||
target = '@' + target[1:]
|
|
||||||
|
|
||||||
# We use lowercase channels internally, but uppercase UIDs.
|
|
||||||
# Strip the target of leading prefix modes (for targets like @#channel)
|
|
||||||
# before checking whether it's actually a channel.
|
|
||||||
split_channel = target.split('#', 1)
|
|
||||||
if len(split_channel) >= 2 and utils.isChannel('#' + split_channel[1]):
|
|
||||||
# Note: don't mess with the case of the channel prefix, or ~#channel
|
|
||||||
# messages will break on RFC1459 casemapping networks (it becomes ^#channel
|
|
||||||
# instead).
|
|
||||||
target = '#'.join((split_channel[0], self.irc.toLower(split_channel[1])))
|
|
||||||
log.debug('(%s) Normalizing channel target %s to %s', self.irc.name, args[0], target)
|
|
||||||
|
|
||||||
return {'target': target, 'text': args[1]}
|
|
||||||
|
|
||||||
handle_notice = handle_privmsg
|
|
||||||
|
|
||||||
def check_nick_collision(self, nick):
|
def check_nick_collision(self, nick):
|
||||||
"""
|
"""
|
||||||
Nick collision checker.
|
Nick collision checker.
|
||||||
"""
|
"""
|
||||||
uid = self.irc.nickToUid(nick)
|
uid = self.nick_to_uid(nick)
|
||||||
# If there is a nick collision, we simply alert plugins. Relay will purposely try to
|
# If there is a nick collision, we simply alert plugins. Relay will purposely try to
|
||||||
# lose fights and tag nicks instead, while other plugins can choose how to handle this.
|
# lose fights and tag nicks instead, while other plugins can choose how to handle this.
|
||||||
if uid:
|
if uid:
|
||||||
log.info('(%s) Nick collision on %s/%s, forwarding this to plugins', self.irc.name,
|
log.info('(%s) Nick collision on %s/%s, forwarding this to plugins', self.name,
|
||||||
uid, nick)
|
uid, nick)
|
||||||
self.irc.callHooks([self.irc.sid, 'SAVE', {'target': uid}])
|
self.call_hooks([self.sid, 'SAVE', {'target': uid}])
|
||||||
|
|
||||||
|
def handle_away(self, numeric, command, args):
|
||||||
|
"""Handles incoming AWAY messages."""
|
||||||
|
# TS6:
|
||||||
|
# <- :6ELAAAAAB AWAY :Auto-away
|
||||||
|
# P10:
|
||||||
|
# <- ABAAA A :blah
|
||||||
|
# <- ABAAA A
|
||||||
|
try:
|
||||||
|
self.users[numeric].away = text = args[0]
|
||||||
|
except IndexError: # User is unsetting away status
|
||||||
|
self.users[numeric].away = text = ''
|
||||||
|
return {'text': text}
|
||||||
|
|
||||||
|
def handle_invite(self, numeric, command, args):
|
||||||
|
"""Handles incoming INVITEs."""
|
||||||
|
# TS6:
|
||||||
|
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 12345
|
||||||
|
# P10:
|
||||||
|
# <- ABAAA I PyLink-devel #services 1460948992
|
||||||
|
# Note that the target is a nickname, not a numeric.
|
||||||
|
|
||||||
|
target = self._get_UID(args[0])
|
||||||
|
channel = self.to_lower(args[1])
|
||||||
|
|
||||||
|
curtime = int(time.time())
|
||||||
|
try:
|
||||||
|
ts = int(args[2])
|
||||||
|
except IndexError:
|
||||||
|
ts = curtime
|
||||||
|
|
||||||
|
ts = ts or curtime # Treat 0 timestamps (e.g. inspircd) as the current time.
|
||||||
|
|
||||||
|
return {'target': target, 'channel': channel, 'ts': ts}
|
||||||
|
|
||||||
def handle_kill(self, source, command, args):
|
def handle_kill(self, source, command, args):
|
||||||
"""Handles incoming KILLs."""
|
"""Handles incoming KILLs."""
|
||||||
@ -127,9 +263,9 @@ class IRCS2SProtocol(Protocol):
|
|||||||
# removed from our user list.
|
# removed from our user list.
|
||||||
# If not, we have to assume that KILL = QUIT and remove them
|
# If not, we have to assume that KILL = QUIT and remove them
|
||||||
# ourselves.
|
# ourselves.
|
||||||
data = self.irc.users.get(killed)
|
data = self.users.get(killed)
|
||||||
if data:
|
if data:
|
||||||
self.removeClient(killed)
|
self._remove_client(killed)
|
||||||
|
|
||||||
# TS6-style kills look something like this:
|
# TS6-style kills look something like this:
|
||||||
# <- :GL KILL 38QAAAAAA :hidden-1C620195!GL (test)
|
# <- :GL KILL 38QAAAAAA :hidden-1C620195!GL (test)
|
||||||
@ -138,7 +274,7 @@ class IRCS2SProtocol(Protocol):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the nick or server name of the caller.
|
# Get the nick or server name of the caller.
|
||||||
killer = self.irc.getFriendlyName(source)
|
killer = self.get_friendly_name(source)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Killer was... neither? We must have aliens or something. Fallback
|
# Killer was... neither? We must have aliens or something. Fallback
|
||||||
# to the given "UID".
|
# to the given "UID".
|
||||||
@ -151,26 +287,79 @@ class IRCS2SProtocol(Protocol):
|
|||||||
|
|
||||||
return {'target': killed, 'text': killmsg, 'userdata': data}
|
return {'target': killed, 'text': killmsg, 'userdata': data}
|
||||||
|
|
||||||
|
def handle_part(self, source, command, args):
|
||||||
|
"""Handles incoming PART commands."""
|
||||||
|
channels = self.to_lower(args[0]).split(',')
|
||||||
|
|
||||||
|
for channel in channels:
|
||||||
|
self.channels[channel].remove_user(source)
|
||||||
|
try:
|
||||||
|
self.users[source].channels.discard(channel)
|
||||||
|
except KeyError:
|
||||||
|
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?", self.name, channel, source)
|
||||||
|
|
||||||
|
try:
|
||||||
|
reason = args[1]
|
||||||
|
except IndexError:
|
||||||
|
reason = ''
|
||||||
|
|
||||||
|
# Clear empty non-permanent channels.
|
||||||
|
if not (self.channels[channel].users or ((self.cmodes.get('permanent'), None) in self.channels[channel].modes)):
|
||||||
|
del self.channels[channel]
|
||||||
|
|
||||||
|
return {'channels': channels, 'text': reason}
|
||||||
|
|
||||||
|
def handle_pong(self, source, command, args):
|
||||||
|
"""Handles incoming PONG commands."""
|
||||||
|
if source == self.uplink:
|
||||||
|
self.lastping = time.time()
|
||||||
|
|
||||||
|
def handle_privmsg(self, source, command, args):
|
||||||
|
"""Handles incoming PRIVMSG/NOTICE."""
|
||||||
|
# TS6:
|
||||||
|
# <- :70MAAAAAA PRIVMSG #dev :afasfsa
|
||||||
|
# <- :70MAAAAAA NOTICE 0ALAAAAAA :afasfsa
|
||||||
|
# P10:
|
||||||
|
# <- ABAAA P AyAAA :privmsg text
|
||||||
|
# <- ABAAA O AyAAA :notice text
|
||||||
|
target = self._get_UID(args[0])
|
||||||
|
|
||||||
|
# Coerse =#channel from Charybdis op moderated +z to @#channel.
|
||||||
|
if target.startswith('='):
|
||||||
|
target = '@' + target[1:]
|
||||||
|
|
||||||
|
# We use lowercase channels internally, but uppercase UIDs.
|
||||||
|
# Strip the target of leading prefix modes (for targets like @#channel)
|
||||||
|
# before checking whether it's actually a channel.
|
||||||
|
|
||||||
|
split_channel = target.split('#', 1)
|
||||||
|
if len(split_channel) >= 2 and utils.isChannel('#' + split_channel[1]):
|
||||||
|
# Note: don't mess with the case of the channel prefix, or ~#channel
|
||||||
|
# messages will break on RFC1459 casemapping networks (it becomes ^#channel
|
||||||
|
# instead).
|
||||||
|
target = '#'.join((split_channel[0], self.to_lower(split_channel[1])))
|
||||||
|
log.debug('(%s) Normalizing channel target %s to %s', self.name, args[0], target)
|
||||||
|
|
||||||
|
return {'target': target, 'text': args[1]}
|
||||||
|
|
||||||
|
handle_notice = handle_privmsg
|
||||||
|
|
||||||
|
def handle_quit(self, numeric, command, args):
|
||||||
|
"""Handles incoming QUIT commands."""
|
||||||
|
# TS6:
|
||||||
|
# <- :1SRAAGB4T QUIT :Quit: quit message goes here
|
||||||
|
# P10:
|
||||||
|
# <- ABAAB Q :Killed (GL_ (bangbang))
|
||||||
|
self._remove_client(numeric)
|
||||||
|
return {'text': args[0]}
|
||||||
|
|
||||||
def handle_squit(self, numeric, command, args):
|
def handle_squit(self, numeric, command, args):
|
||||||
"""Handles incoming SQUITs."""
|
"""Handles incoming SQUITs."""
|
||||||
return self._squit(numeric, command, args)
|
return self._squit(numeric, command, args)
|
||||||
|
|
||||||
def handle_away(self, numeric, command, args):
|
def handle_time(self, numeric, command, args):
|
||||||
"""Handles incoming AWAY messages."""
|
"""Handles incoming /TIME requests."""
|
||||||
# TS6:
|
return {'target': args[0]}
|
||||||
# <- :6ELAAAAAB AWAY :Auto-away
|
|
||||||
# P10:
|
|
||||||
# <- ABAAA A :blah
|
|
||||||
# <- ABAAA A
|
|
||||||
try:
|
|
||||||
self.irc.users[numeric].away = text = args[0]
|
|
||||||
except IndexError: # User is unsetting away status
|
|
||||||
self.irc.users[numeric].away = text = ''
|
|
||||||
return {'text': text}
|
|
||||||
|
|
||||||
def handle_version(self, numeric, command, args):
|
|
||||||
"""Handles requests for the PyLink server version."""
|
|
||||||
return {} # See coremods/handlers.py for how this hook is used
|
|
||||||
|
|
||||||
def handle_whois(self, numeric, command, args):
|
def handle_whois(self, numeric, command, args):
|
||||||
"""Handles incoming WHOIS commands.."""
|
"""Handles incoming WHOIS commands.."""
|
||||||
@ -184,22 +373,8 @@ class IRCS2SProtocol(Protocol):
|
|||||||
# WHOIS commands received are for us, since we don't host any real servers
|
# WHOIS commands received are for us, since we don't host any real servers
|
||||||
# to route it to.
|
# to route it to.
|
||||||
|
|
||||||
return {'target': self._getUid(args[-1])}
|
return {'target': self._get_UID(args[-1])}
|
||||||
|
|
||||||
def handle_quit(self, numeric, command, args):
|
def handle_version(self, numeric, command, args):
|
||||||
"""Handles incoming QUIT commands."""
|
"""Handles requests for the PyLink server version."""
|
||||||
# TS6:
|
return {} # See coremods/handlers.py for how this hook is used
|
||||||
# <- :1SRAAGB4T QUIT :Quit: quit message goes here
|
|
||||||
# P10:
|
|
||||||
# <- ABAAB Q :Killed (GL_ (bangbang))
|
|
||||||
self.removeClient(numeric)
|
|
||||||
return {'text': args[0]}
|
|
||||||
|
|
||||||
def handle_time(self, numeric, command, args):
|
|
||||||
"""Handles incoming /TIME requests."""
|
|
||||||
return {'target': args[0]}
|
|
||||||
|
|
||||||
def handle_pong(self, source, command, args):
|
|
||||||
"""Handles incoming PONG commands."""
|
|
||||||
if source == self.irc.uplink:
|
|
||||||
self.irc.lastping = time.time()
|
|
||||||
|
@ -6,11 +6,11 @@ from pylinkirc.log import log
|
|||||||
from pylinkirc.protocols.p10 import *
|
from pylinkirc.protocols.p10 import *
|
||||||
|
|
||||||
class NefariousProtocol(P10Protocol):
|
class NefariousProtocol(P10Protocol):
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
log.warning("(%s) protocols/nefarious.py has been renamed to protocols/p10.py, which "
|
log.warning("(%s) protocols/nefarious.py has been renamed to protocols/p10.py, which "
|
||||||
"now also supports other IRCu variants. Please update your configuration, "
|
"now also supports other IRCu variants. Please update your configuration, "
|
||||||
"as this migration stub will be removed in a future version.",
|
"as this migration stub will be removed in a future version.",
|
||||||
self.irc.name)
|
self.name)
|
||||||
|
|
||||||
Class = NefariousProtocol
|
Class = NefariousProtocol
|
||||||
|
481
protocols/p10.py
481
protocols/p10.py
File diff suppressed because it is too large
Load Diff
@ -7,20 +7,21 @@ from pylinkirc.protocols.ts6 import *
|
|||||||
|
|
||||||
class RatboxProtocol(TS6Protocol):
|
class RatboxProtocol(TS6Protocol):
|
||||||
|
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
# Don't require EUID for Ratbox
|
# Don't require EUID for Ratbox
|
||||||
self.required_caps.discard('EUID')
|
self.required_caps.discard('EUID')
|
||||||
|
|
||||||
self.hook_map['LOGIN'] = 'CLIENT_SERVICES_LOGIN'
|
self.hook_map['LOGIN'] = 'CLIENT_SERVICES_LOGIN'
|
||||||
self.protocol_caps -= {'slash-in-hosts'}
|
self.protocol_caps -= {'slash-in-hosts'}
|
||||||
|
|
||||||
def connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
super().connect()
|
|
||||||
|
super().post_connect()
|
||||||
|
|
||||||
# Note: +r, +e, and +I support will be negotiated on link
|
# Note: +r, +e, and +I support will be negotiated on link
|
||||||
self.irc.cmodes = {'op': 'o', 'secret': 's', 'private': 'p', 'noextmsg': 'n', 'moderated': 'm',
|
self.cmodes = {'op': 'o', 'secret': 's', 'private': 'p', 'noextmsg': 'n', 'moderated': 'm',
|
||||||
'inviteonly': 'i', 'topiclock': 't', 'limit': 'l', 'ban': 'b', 'voice': 'v',
|
'inviteonly': 'i', 'topiclock': 't', 'limit': 'l', 'ban': 'b', 'voice': 'v',
|
||||||
'key': 'k', 'sslonly': 'S',
|
'key': 'k', 'sslonly': 'S',
|
||||||
'*A': 'beI',
|
'*A': 'beI',
|
||||||
@ -28,7 +29,7 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
'*C': 'l',
|
'*C': 'l',
|
||||||
'*D': 'imnpstrS'}
|
'*D': 'imnpstrS'}
|
||||||
|
|
||||||
self.irc.umodes = {
|
self.umodes = {
|
||||||
'invisible': 'i', 'callerid': 'g', 'oper': 'o', 'admin': 'a', 'sno_botfloods': 'b',
|
'invisible': 'i', 'callerid': 'g', 'oper': 'o', 'admin': 'a', 'sno_botfloods': 'b',
|
||||||
'sno_clientconnections': 'c', 'sno_extclientconnections': 'C', 'sno_debug': 'd',
|
'sno_clientconnections': 'c', 'sno_extclientconnections': 'C', 'sno_debug': 'd',
|
||||||
'sno_fullauthblock': 'f', 'sno_skill': 'k', 'locops': 'l',
|
'sno_fullauthblock': 'f', 'sno_skill': 'k', 'locops': 'l',
|
||||||
@ -39,7 +40,7 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
'*A': '', '*B': '', '*C': '', '*D': 'igoabcCdfklrsuwxyzZD'
|
'*A': '', '*B': '', '*C': '', '*D': 'igoabcCdfklrsuwxyzZD'
|
||||||
}
|
}
|
||||||
|
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawn_client(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
||||||
manipulatable=False):
|
manipulatable=False):
|
||||||
"""
|
"""
|
||||||
@ -52,46 +53,47 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
# parameters: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address,
|
# parameters: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address,
|
||||||
# UID, gecos
|
# UID, gecos
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
|
|
||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.join_modes(modes)
|
||||||
|
|
||||||
orig_realhost = realhost
|
orig_realhost = realhost
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
|
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
||||||
self.irc.applyModes(uid, modes)
|
self.apply_modes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
|
||||||
":{realname}".format(ts=ts, host=host,
|
self._send_with_prefix(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
nick=nick, ident=ident, uid=uid,
|
":{realname}".format(ts=ts, host=host,
|
||||||
modes=raw_modes, ip=ip, realname=realname))
|
nick=nick, ident=ident, uid=uid,
|
||||||
|
modes=raw_modes, ip=ip, realname=realname))
|
||||||
|
|
||||||
if orig_realhost:
|
if orig_realhost:
|
||||||
# If real host is specified, send it using ENCAP REALHOST
|
# If real host is specified, send it using ENCAP REALHOST
|
||||||
self._send(uid, "ENCAP * REALHOST %s" % orig_realhost)
|
self._send_with_prefix(uid, "ENCAP * REALHOST %s" % orig_realhost)
|
||||||
|
|
||||||
return u
|
return u
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def update_client(self, target, field, text):
|
||||||
"""updateClient() stub for ratbox."""
|
"""update_client() stub for ratbox."""
|
||||||
raise NotImplementedError("User data changing is not supported on ircd-ratbox.")
|
raise NotImplementedError("User data changing is not supported on ircd-ratbox.")
|
||||||
|
|
||||||
def handle_realhost(self, uid, command, args):
|
def handle_realhost(self, uid, command, args):
|
||||||
"""Handles real host propagation."""
|
"""Handles real host propagation."""
|
||||||
log.debug('(%s) Got REALHOST %s for %s', args[0], uid)
|
log.debug('(%s) Got REALHOST %s for %s', args[0], uid)
|
||||||
self.irc.users[uid].realhost = args[0]
|
self.users[uid].realhost = args[0]
|
||||||
|
|
||||||
def handle_login(self, uid, command, args):
|
def handle_login(self, uid, command, args):
|
||||||
"""Handles login propagation on burst."""
|
"""Handles login propagation on burst."""
|
||||||
self.irc.users[uid].services_account = args[0]
|
self.users[uid].services_account = args[0]
|
||||||
return {'text': args[0]}
|
return {'text': args[0]}
|
||||||
|
|
||||||
Class = RatboxProtocol
|
Class = RatboxProtocol
|
||||||
|
314
protocols/ts6.py
314
protocols/ts6.py
@ -13,8 +13,8 @@ from pylinkirc.protocols.ts6_common import *
|
|||||||
S2S_BUFSIZE = 510
|
S2S_BUFSIZE = 510
|
||||||
|
|
||||||
class TS6Protocol(TS6BaseProtocol):
|
class TS6Protocol(TS6BaseProtocol):
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
self.protocol_caps |= {'slash-in-hosts'}
|
self.protocol_caps |= {'slash-in-hosts'}
|
||||||
self.casemapping = 'rfc1459'
|
self.casemapping = 'rfc1459'
|
||||||
self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE',
|
self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE',
|
||||||
@ -27,7 +27,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
### OUTGOING COMMANDS
|
### OUTGOING COMMANDS
|
||||||
|
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawn_client(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
||||||
manipulatable=False):
|
manipulatable=False):
|
||||||
"""
|
"""
|
||||||
@ -36,9 +36,9 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
|
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -49,14 +49,14 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.join_modes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
|
|
||||||
self.irc.applyModes(uid, modes)
|
self.apply_modes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
self._send(server, "EUID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
self._send_with_prefix(server, "EUID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
"{realhost} * :{realname}".format(ts=ts, host=host,
|
"{realhost} * :{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
modes=raw_modes, ip=ip, realname=realname,
|
modes=raw_modes, ip=ip, realname=realname,
|
||||||
@ -66,15 +66,15 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""Joins a PyLink client to a channel."""
|
"""Joins a PyLink client to a channel."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
# JOIN:
|
# JOIN:
|
||||||
# parameters: channelTS, channel, '+' (a plus sign)
|
# parameters: channelTS, channel, '+' (a plus sign)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.is_internal_client(client):
|
||||||
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(client, "JOIN {ts} {channel} +".format(ts=self.irc.channels[channel].ts, channel=channel))
|
self._send_with_prefix(client, "JOIN {ts} {channel} +".format(ts=self.channels[channel].ts, channel=channel))
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -85,7 +85,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L821
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L821
|
||||||
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
||||||
@ -96,34 +96,34 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# their status ('@+', '@', '+' or ''), for example:
|
# their status ('@+', '@', '+' or ''), for example:
|
||||||
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'. All users must be behind the source server
|
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'. All users must be behind the source server
|
||||||
# so it is not possible to use this message to force users to join a channel.
|
# so it is not possible to use this message to force users to join a channel.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
log.debug('(%s) sjoin: got %r for users', self.irc.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
modes = set(modes or self.irc.channels[channel].modes)
|
modes = set(modes or self.channels[channel].modes)
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
|
|
||||||
# Get all the ban modes in a separate list. These are bursted using a separate BMASK
|
# Get all the ban modes in a separate list. These are bursted using a separate BMASK
|
||||||
# command.
|
# command.
|
||||||
banmodes = {k: [] for k in self.irc.cmodes['*A']}
|
banmodes = {k: [] for k in self.cmodes['*A']}
|
||||||
regularmodes = []
|
regularmodes = []
|
||||||
log.debug('(%s) Unfiltered SJOIN modes: %s', self.irc.name, modes)
|
log.debug('(%s) Unfiltered SJOIN modes: %s', self.name, modes)
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
modechar = mode[0][-1]
|
modechar = mode[0][-1]
|
||||||
if modechar in self.irc.cmodes['*A']:
|
if modechar in self.cmodes['*A']:
|
||||||
# Mode character is one of 'beIq'
|
# Mode character is one of 'beIq'
|
||||||
if (modechar, mode[1]) in self.irc.channels[channel].modes:
|
if (modechar, mode[1]) in self.channels[channel].modes:
|
||||||
# Don't reset modes that are already set.
|
# Don't reset modes that are already set.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
banmodes[modechar].append(mode[1])
|
banmodes[modechar].append(mode[1])
|
||||||
else:
|
else:
|
||||||
regularmodes.append(mode)
|
regularmodes.append(mode)
|
||||||
log.debug('(%s) Filtered SJOIN modes to be regular modes: %s, banmodes: %s', self.irc.name, regularmodes, banmodes)
|
log.debug('(%s) Filtered SJOIN modes to be regular modes: %s, banmodes: %s', self.name, regularmodes, banmodes)
|
||||||
|
|
||||||
changedmodes = modes
|
changedmodes = modes
|
||||||
while users[:12]:
|
while users[:12]:
|
||||||
@ -135,22 +135,22 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
prefixes, user = userpair
|
prefixes, user = userpair
|
||||||
prefixchars = ''
|
prefixchars = ''
|
||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
pr = self.irc.prefixmodes.get(prefix)
|
pr = self.prefixmodes.get(prefix)
|
||||||
if pr:
|
if pr:
|
||||||
prefixchars += pr
|
prefixchars += pr
|
||||||
changedmodes.add(('+%s' % prefix, user))
|
changedmodes.add(('+%s' % prefix, user))
|
||||||
namelist.append(prefixchars+user)
|
namelist.append(prefixchars+user)
|
||||||
uids.append(user)
|
uids.append(user)
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
users = users[12:]
|
users = users[12:]
|
||||||
namelist = ' '.join(namelist)
|
namelist = ' '.join(namelist)
|
||||||
self._send(server, "SJOIN {ts} {channel} {modes} :{users}".format(
|
self._send_with_prefix(server, "SJOIN {ts} {channel} {modes} :{users}".format(
|
||||||
ts=ts, users=namelist, channel=channel,
|
ts=ts, users=namelist, channel=channel,
|
||||||
modes=self.irc.joinModes(regularmodes)))
|
modes=self.join_modes(regularmodes)))
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
# Now, burst bans.
|
# Now, burst bans.
|
||||||
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
||||||
@ -158,12 +158,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# Max 15-3 = 12 bans per line to prevent cut off. (TS6 allows a max of 15 parameters per
|
# Max 15-3 = 12 bans per line to prevent cut off. (TS6 allows a max of 15 parameters per
|
||||||
# line)
|
# line)
|
||||||
if bans:
|
if bans:
|
||||||
log.debug('(%s) sjoin: bursting mode %s with bans %s, ts:%s', self.irc.name, bmode, bans, ts)
|
log.debug('(%s) sjoin: bursting mode %s with bans %s, ts:%s', self.name, bmode, bans, ts)
|
||||||
msgprefix = ':{sid} BMASK {ts} {channel} {bmode} :'.format(sid=server, ts=ts,
|
msgprefix = ':{sid} BMASK {ts} {channel} {bmode} :'.format(sid=server, ts=ts,
|
||||||
channel=channel, bmode=bmode)
|
channel=channel, bmode=bmode)
|
||||||
# Actually, we cut off at 17 arguments/line, since the prefix and command name don't count.
|
# Actually, we cut off at 17 arguments/line, since the prefix and command name don't count.
|
||||||
for msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE, max_args_per_line=17):
|
for msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE, max_args_per_line=17):
|
||||||
self.irc.send(msg)
|
self.send(msg)
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
@ -172,15 +172,15 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# c <- :0UYAAAAAA TMODE 0 #a +o 0T4AAAAAC
|
# c <- :0UYAAAAAA TMODE 0 #a +o 0T4AAAAAC
|
||||||
# u <- :0UYAAAAAA MODE 0UYAAAAAA :-Facdefklnou
|
# u <- :0UYAAAAAA MODE 0UYAAAAAA :-Facdefklnou
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self.irc.applyModes(target, modes)
|
self.apply_modes(target, modes)
|
||||||
modes = list(modes)
|
modes = list(modes)
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.to_lower(target)].ts
|
||||||
# TMODE:
|
# TMODE:
|
||||||
# parameters: channelTS, channel, cmode changes, opt. cmode parameters...
|
# parameters: channelTS, channel, cmode changes, opt. cmode parameters...
|
||||||
|
|
||||||
@ -189,54 +189,54 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
msgprefix = ':%s TMODE %s %s ' % (numeric, ts, target)
|
msgprefix = ':%s TMODE %s %s ' % (numeric, ts, target)
|
||||||
bufsize = S2S_BUFSIZE - len(msgprefix)
|
bufsize = S2S_BUFSIZE - len(msgprefix)
|
||||||
|
|
||||||
for modestr in self.irc.wrapModes(modes, bufsize, max_modes_per_msg=10):
|
for modestr in self.wrap_modes(modes, bufsize, max_modes_per_msg=10):
|
||||||
self.irc.send(msgprefix + modestr)
|
self.send(msgprefix + modestr)
|
||||||
else:
|
else:
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.join_modes(modes)
|
||||||
self._send(numeric, 'MODE %s %s' % (target, joinedmodes))
|
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topic_burst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.is_internal_server(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
# TB
|
# TB
|
||||||
# capab: TB
|
# capab: TB
|
||||||
# source: server
|
# source: server
|
||||||
# propagation: broadcast
|
# propagation: broadcast
|
||||||
# parameters: channel, topicTS, opt. topic setter, topic
|
# parameters: channel, topicTS, opt. topic setter, topic
|
||||||
ts = self.irc.channels[target].ts
|
ts = self.channels[target].ts
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
self._send(numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
|
self._send_with_prefix(numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(numeric, 'INVITE %s %s %s' % (target, channel, self.irc.channels[channel].ts))
|
self._send_with_prefix(numeric, 'INVITE %s %s %s' % (target, channel, self.channels[channel].ts))
|
||||||
|
|
||||||
def knock(self, numeric, target, text):
|
def knock(self, numeric, target, text):
|
||||||
"""Sends a KNOCK from a PyLink client."""
|
"""Sends a KNOCK from a PyLink client."""
|
||||||
if 'KNOCK' not in self.irc.caps:
|
if 'KNOCK' not in self.caps:
|
||||||
log.debug('(%s) knock: Dropping KNOCK to %r since the IRCd '
|
log.debug('(%s) knock: Dropping KNOCK to %r since the IRCd '
|
||||||
'doesn\'t support it.', self.irc.name, target)
|
'doesn\'t support it.', self.name, target)
|
||||||
return
|
return
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
# No text value is supported here; drop it.
|
# No text value is supported here; drop it.
|
||||||
self._send(numeric, 'KNOCK %s' % target)
|
self._send_with_prefix(numeric, 'KNOCK %s' % target)
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def update_client(self, target, field, text):
|
||||||
"""Updates the hostname of any connected client."""
|
"""Updates the hostname of any connected client."""
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
if field == 'HOST':
|
if field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send(self.irc.sid, 'CHGHOST %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s :%s' % (target, text))
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.is_internal_client(target):
|
||||||
# If the target isn't one of our clients, send hook payload
|
# If the target isn't one of our clients, send hook payload
|
||||||
# for other plugins to listen to.
|
# for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.call_hooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
@ -245,22 +245,22 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
if source is None:
|
if source is None:
|
||||||
return
|
return
|
||||||
if target is not None:
|
if target is not None:
|
||||||
self._send(source, 'PING %s %s' % (source, target))
|
self._send_with_prefix(source, 'PING %s %s' % (source, target))
|
||||||
else:
|
else:
|
||||||
self._send(source, 'PING %s' % source)
|
self._send_with_prefix(source, 'PING %s' % source)
|
||||||
|
|
||||||
### Core / handlers
|
### Core / handlers
|
||||||
|
|
||||||
def connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
|
|
||||||
f = self.irc.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
|
||||||
chary_cmodes = { # TS6 generic modes (note that +p is noknock instead of private):
|
chary_cmodes = { # TS6 generic modes (note that +p is noknock instead of private):
|
||||||
@ -278,17 +278,17 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# Now, map all the ABCD type modes:
|
# Now, map all the ABCD type modes:
|
||||||
'*A': 'beIq', '*B': 'k', '*C': 'lfj', '*D': 'mnprstFLPQcgzCOAST'}
|
'*A': 'beIq', '*B': 'k', '*C': 'lfj', '*D': 'mnprstFLPQcgzCOAST'}
|
||||||
|
|
||||||
if self.irc.serverdata.get('use_owner'):
|
if self.serverdata.get('use_owner'):
|
||||||
chary_cmodes['owner'] = 'y'
|
chary_cmodes['owner'] = 'y'
|
||||||
self.irc.prefixmodes['y'] = '~'
|
self.prefixmodes['y'] = '~'
|
||||||
if self.irc.serverdata.get('use_admin'):
|
if self.serverdata.get('use_admin'):
|
||||||
chary_cmodes['admin'] = 'a'
|
chary_cmodes['admin'] = 'a'
|
||||||
self.irc.prefixmodes['a'] = '!'
|
self.prefixmodes['a'] = '!'
|
||||||
if self.irc.serverdata.get('use_halfop'):
|
if self.serverdata.get('use_halfop'):
|
||||||
chary_cmodes['halfop'] = 'h'
|
chary_cmodes['halfop'] = 'h'
|
||||||
self.irc.prefixmodes['h'] = '%'
|
self.prefixmodes['h'] = '%'
|
||||||
|
|
||||||
self.irc.cmodes = chary_cmodes
|
self.cmodes = chary_cmodes
|
||||||
|
|
||||||
# Define supported user modes
|
# Define supported user modes
|
||||||
chary_umodes = {'deaf': 'D', 'servprotect': 'S', 'admin': 'a',
|
chary_umodes = {'deaf': 'D', 'servprotect': 'S', 'admin': 'a',
|
||||||
@ -298,26 +298,26 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
'cloak': 'x', 'override': 'p',
|
'cloak': 'x', 'override': 'p',
|
||||||
# Now, map all the ABCD type modes:
|
# Now, map all the ABCD type modes:
|
||||||
'*A': '', '*B': '', '*C': '', '*D': 'DSaiowsQRgzlxp'}
|
'*A': '', '*B': '', '*C': '', '*D': 'DSaiowsQRgzlxp'}
|
||||||
self.irc.umodes = chary_umodes
|
self.umodes = chary_umodes
|
||||||
|
|
||||||
# Toggles support of shadowircd/elemental-ircd specific channel modes:
|
# Toggles support of shadowircd/elemental-ircd specific channel modes:
|
||||||
# +T (no notice), +u (hidden ban list), +E (no kicks), +J (blocks kickrejoin),
|
# +T (no notice), +u (hidden ban list), +E (no kicks), +J (blocks kickrejoin),
|
||||||
# +K (no repeat messages), +d (no nick changes), and user modes:
|
# +K (no repeat messages), +d (no nick changes), and user modes:
|
||||||
# +B (bot), +C (blocks CTCP), +D (deaf), +V (no invites), +I (hides channel list)
|
# +B (bot), +C (blocks CTCP), +D (deaf), +V (no invites), +I (hides channel list)
|
||||||
if self.irc.serverdata.get('use_elemental_modes'):
|
if self.serverdata.get('use_elemental_modes'):
|
||||||
elemental_cmodes = {'hiddenbans': 'u', 'nokick': 'E',
|
elemental_cmodes = {'hiddenbans': 'u', 'nokick': 'E',
|
||||||
'kicknorejoin': 'J', 'repeat': 'K', 'nonick': 'd',
|
'kicknorejoin': 'J', 'repeat': 'K', 'nonick': 'd',
|
||||||
'blockcaps': 'G'}
|
'blockcaps': 'G'}
|
||||||
self.irc.cmodes.update(elemental_cmodes)
|
self.cmodes.update(elemental_cmodes)
|
||||||
self.irc.cmodes['*D'] += ''.join(elemental_cmodes.values())
|
self.cmodes['*D'] += ''.join(elemental_cmodes.values())
|
||||||
|
|
||||||
elemental_umodes = {'noctcp': 'C', 'deaf': 'D', 'bot': 'B', 'noinvite': 'V',
|
elemental_umodes = {'noctcp': 'C', 'deaf': 'D', 'bot': 'B', 'noinvite': 'V',
|
||||||
'hidechans': 'I'}
|
'hidechans': 'I'}
|
||||||
self.irc.umodes.update(elemental_umodes)
|
self.umodes.update(elemental_umodes)
|
||||||
self.irc.umodes['*D'] += ''.join(elemental_umodes.values())
|
self.umodes['*D'] += ''.join(elemental_umodes.values())
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
||||||
f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))
|
f('PASS %s TS 6 %s' % (self.serverdata["sendpass"], self.sid))
|
||||||
|
|
||||||
# We request the following capabilities (for charybdis):
|
# We request the following capabilities (for charybdis):
|
||||||
|
|
||||||
@ -330,7 +330,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# KNOCK: support for /knock
|
# KNOCK: support for /knock
|
||||||
# SAVE: support for SAVE (forces user to UID in nick collision)
|
# SAVE: support for SAVE (forces user to UID in nick collision)
|
||||||
# SERVICES: adds mode +r (only registered users can join a channel)
|
# SERVICES: adds mode +r (only registered users can join a channel)
|
||||||
# TB: topic burst command; we send this in topicBurst
|
# TB: topic burst command; we send this in topic_burst
|
||||||
# EUID: extended UID command, which includes real hostname + account data info,
|
# EUID: extended UID command, which includes real hostname + account data info,
|
||||||
# and allows sending CHGHOST without ENCAP.
|
# and allows sending CHGHOST without ENCAP.
|
||||||
# RSFNC: states that we support RSFNC (forced nick changed attempts). XXX: With atheme services,
|
# RSFNC: states that we support RSFNC (forced nick changed attempts). XXX: With atheme services,
|
||||||
@ -338,8 +338,8 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# EOPMOD: supports ETB (extended TOPIC burst) and =#channel messages for opmoderated +z
|
# EOPMOD: supports ETB (extended TOPIC burst) and =#channel messages for opmoderated +z
|
||||||
f('CAPAB :QS ENCAP EX CHW IE KNOCK SAVE SERVICES TB EUID RSFNC EOPMOD SAVETS_100')
|
f('CAPAB :QS ENCAP EX CHW IE KNOCK SAVE SERVICES TB EUID RSFNC EOPMOD SAVETS_100')
|
||||||
|
|
||||||
f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
|
f('SERVER %s 0 :%s' % (self.serverdata["hostname"],
|
||||||
self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
# Finally, end all the initialization with a PING - that's Charybdis'
|
# Finally, end all the initialization with a PING - that's Charybdis'
|
||||||
# way of saying end-of-burst :)
|
# way of saying end-of-burst :)
|
||||||
@ -352,7 +352,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
"""
|
"""
|
||||||
# <- PASS $somepassword TS 6 :42X
|
# <- PASS $somepassword TS 6 :42X
|
||||||
|
|
||||||
if args[0] != self.irc.serverdata['recvpass']:
|
if args[0] != self.serverdata['recvpass']:
|
||||||
# Check if recvpass is correct
|
# Check if recvpass is correct
|
||||||
raise ProtocolError('Recvpass from uplink server %s does not match configuration!' % servername)
|
raise ProtocolError('Recvpass from uplink server %s does not match configuration!' % servername)
|
||||||
|
|
||||||
@ -360,12 +360,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
raise ProtocolError("Remote protocol version is too old! Is this even TS6?")
|
raise ProtocolError("Remote protocol version is too old! Is this even TS6?")
|
||||||
|
|
||||||
numeric = args[-1]
|
numeric = args[-1]
|
||||||
log.debug('(%s) Found uplink SID as %r', self.irc.name, numeric)
|
log.debug('(%s) Found uplink SID as %r', self.name, numeric)
|
||||||
|
|
||||||
# Server name and SID are sent in different messages, so we fill this
|
# Server name and SID are sent in different messages, so we fill this
|
||||||
# with dummy information until we get the actual sid.
|
# with dummy information until we get the actual sid.
|
||||||
self.irc.servers[numeric] = IrcServer(None, '')
|
self.servers[numeric] = Server(None, '')
|
||||||
self.irc.uplink = numeric
|
self.uplink = numeric
|
||||||
|
|
||||||
def handle_capab(self, numeric, command, args):
|
def handle_capab(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -374,21 +374,21 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# We only get a list of keywords here. Charybdis obviously assumes that
|
# We only get a list of keywords here. Charybdis obviously assumes that
|
||||||
# we know what modes it supports (indeed, this is a standard list).
|
# we know what modes it supports (indeed, this is a standard list).
|
||||||
# <- CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SAVE SERVICES TB UNKLN
|
# <- CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SAVE SERVICES TB UNKLN
|
||||||
self.irc.caps = caps = args[0].split()
|
self.caps = caps = args[0].split()
|
||||||
|
|
||||||
for required_cap in self.required_caps:
|
for required_cap in self.required_caps:
|
||||||
if required_cap not in caps:
|
if required_cap not in caps:
|
||||||
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
||||||
|
|
||||||
if 'EX' in caps:
|
if 'EX' in caps:
|
||||||
self.irc.cmodes['banexception'] = 'e'
|
self.cmodes['banexception'] = 'e'
|
||||||
if 'IE' in caps:
|
if 'IE' in caps:
|
||||||
self.irc.cmodes['invex'] = 'I'
|
self.cmodes['invex'] = 'I'
|
||||||
if 'SERVICES' in caps:
|
if 'SERVICES' in caps:
|
||||||
self.irc.cmodes['regonly'] = 'r'
|
self.cmodes['regonly'] = 'r'
|
||||||
|
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
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."""
|
||||||
@ -405,11 +405,11 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
try:
|
try:
|
||||||
destination = args[1]
|
destination = args[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
destination = self.irc.sid
|
destination = self.sid
|
||||||
if self.irc.isInternalServer(destination):
|
if self.is_internal_server(destination):
|
||||||
self._send(destination, 'PONG %s %s' % (destination, source), queue=False)
|
self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False)
|
||||||
|
|
||||||
if destination == self.irc.sid and not self.has_eob:
|
if destination == self.sid and not self.has_eob:
|
||||||
# Charybdis' idea of endburst is just sending a PING. No, really!
|
# Charybdis' idea of endburst is just sending a PING. No, really!
|
||||||
# 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.has_eob = True
|
||||||
@ -421,18 +421,18 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
"""Handles incoming SJOIN commands."""
|
"""Handles incoming SJOIN commands."""
|
||||||
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
||||||
# <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB
|
# <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
modestring = args[2:-1] or args[2]
|
modestring = args[2:-1] or args[2]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parse_modes(channel, modestring)
|
||||||
namelist = []
|
namelist = []
|
||||||
|
|
||||||
# Keep track of other modes that are added due to prefix modes being joined too.
|
# Keep track of other modes that are added due to prefix modes being joined too.
|
||||||
changedmodes = set(parsedmodes)
|
changedmodes = set(parsedmodes)
|
||||||
|
|
||||||
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.name, userlist, channel)
|
||||||
for userpair in userlist:
|
for userpair in userlist:
|
||||||
# charybdis sends this in the form "@+UID1, +UID2, UID3, @UID4"
|
# charybdis sends this in the form "@+UID1, +UID2, UID3, @UID4"
|
||||||
r = re.search(r'([^\d]*)(.*)', userpair)
|
r = re.search(r'([^\d]*)(.*)', userpair)
|
||||||
@ -440,30 +440,30 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
modeprefix = r.group(1) or ''
|
modeprefix = r.group(1) or ''
|
||||||
finalprefix = ''
|
finalprefix = ''
|
||||||
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
|
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
|
||||||
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)
|
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.name, modeprefix, user)
|
||||||
|
|
||||||
# Don't crash when we get an invalid UID.
|
# Don't crash when we get an invalid UID.
|
||||||
if user not in self.irc.users:
|
if user not in self.users:
|
||||||
log.debug('(%s) handle_sjoin: tried to introduce user %s not in our user list, ignoring...',
|
log.debug('(%s) handle_sjoin: tried to introduce user %s not in our user list, ignoring...',
|
||||||
self.irc.name, user)
|
self.name, user)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for m in modeprefix:
|
for m in modeprefix:
|
||||||
# Iterate over the mapping of prefix chars to prefixes, and
|
# Iterate over the mapping of prefix chars to prefixes, and
|
||||||
# find the characters that match.
|
# find the characters that match.
|
||||||
for char, prefix in self.irc.prefixmodes.items():
|
for char, prefix in self.prefixmodes.items():
|
||||||
if m == prefix:
|
if m == prefix:
|
||||||
finalprefix += char
|
finalprefix += char
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only save mode changes if the remote has lower TS than us.
|
# Only save mode changes if the remote has lower TS than us.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
# Statekeeping with timestamps
|
# Statekeeping with timestamps
|
||||||
their_ts = int(args[0])
|
their_ts = int(args[0])
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
||||||
@ -476,24 +476,24 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
if args[0] == '0':
|
if args[0] == '0':
|
||||||
# /join 0; part the user from all channels
|
# /join 0; part the user from all channels
|
||||||
oldchans = self.irc.users[numeric].channels.copy()
|
oldchans = self.users[numeric].channels.copy()
|
||||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||||
self.irc.name, numeric, oldchans)
|
self.name, numeric, oldchans)
|
||||||
for channel in oldchans:
|
for channel in oldchans:
|
||||||
self.irc.channels[channel].users.discard(numeric)
|
self.channels[channel].users.discard(numeric)
|
||||||
self.irc.users[numeric].channels.discard(channel)
|
self.users[numeric].channels.discard(channel)
|
||||||
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||||
else:
|
else:
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
self.updateTS(numeric, channel, ts)
|
self.updateTS(numeric, channel, ts)
|
||||||
|
|
||||||
self.irc.users[numeric].channels.add(channel)
|
self.users[numeric].channels.add(channel)
|
||||||
self.irc.channels[channel].users.add(numeric)
|
self.channels[channel].users.add(numeric)
|
||||||
|
|
||||||
# We send users and modes here because SJOIN and JOIN both use one hook,
|
# We send users and modes here because SJOIN and JOIN both use one hook,
|
||||||
# for simplicity's sake (with plugins).
|
# for simplicity's sake (with plugins).
|
||||||
return {'channel': channel, 'users': [numeric], 'modes':
|
return {'channel': channel, 'users': [numeric], 'modes':
|
||||||
self.irc.channels[channel].modes, 'ts': ts}
|
self.channels[channel].modes, 'ts': ts}
|
||||||
|
|
||||||
def handle_euid(self, numeric, command, args):
|
def handle_euid(self, numeric, command, args):
|
||||||
"""Handles incoming EUID commands (user introduction)."""
|
"""Handles incoming EUID commands (user introduction)."""
|
||||||
@ -505,28 +505,28 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
realhost = None
|
realhost = None
|
||||||
|
|
||||||
log.debug('(%s) handle_euid got args: nick=%s ts=%s uid=%s ident=%s '
|
log.debug('(%s) handle_euid got args: nick=%s ts=%s uid=%s ident=%s '
|
||||||
'host=%s realname=%s realhost=%s ip=%s', self.irc.name, nick, ts, uid,
|
'host=%s realname=%s realhost=%s ip=%s', self.name, nick, ts, uid,
|
||||||
ident, host, realname, realhost, ip)
|
ident, host, realname, realhost, ip)
|
||||||
assert ts != 0, "Bad TS 0 for user %s" % uid
|
assert ts != 0, "Bad TS 0 for user %s" % uid
|
||||||
|
|
||||||
if ip == '0': # IP was invalid; something used for services.
|
if ip == '0': # IP was invalid; something used for services.
|
||||||
ip = '0.0.0.0'
|
ip = '0.0.0.0'
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = User(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [modes])
|
parsedmodes = self.parse_modes(uid, [modes])
|
||||||
log.debug('Applying modes %s for %s', parsedmodes, uid)
|
log.debug('Applying modes %s for %s', parsedmodes, uid)
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.apply_modes(uid, parsedmodes)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Call the OPERED UP hook if +o is being added to the mode list.
|
# Call the OPERED UP hook if +o is being added to the mode list.
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
otype = 'Server Administrator' if ('+a', None) in parsedmodes else 'IRC Operator'
|
otype = 'Server Administrator' if ('+a', None) in parsedmodes else 'IRC Operator'
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': otype}])
|
self.call_hooks([uid, 'CLIENT_OPERED', {'text': otype}])
|
||||||
|
|
||||||
# Set the accountname if present
|
# Set the accountname if present
|
||||||
if accountname != "*":
|
if accountname != "*":
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
@ -555,7 +555,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sid = args[2]
|
sid = args[2]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[sid] = Server(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': sid, 'text': sdesc}
|
return {'name': servername, 'sid': sid, 'text': sdesc}
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
@ -563,35 +563,35 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
Handles 1) incoming legacy (no SID) server introductions,
|
Handles 1) incoming legacy (no SID) server introductions,
|
||||||
2) Sending server data in initial connection.
|
2) Sending server data in initial connection.
|
||||||
"""
|
"""
|
||||||
if numeric == self.irc.uplink and not self.irc.servers[numeric].name:
|
if numeric == self.uplink and not self.servers[numeric].name:
|
||||||
# <- SERVER charybdis.midnight.vpn 1 :charybdis test server
|
# <- SERVER charybdis.midnight.vpn 1 :charybdis test server
|
||||||
sname = args[0].lower()
|
sname = args[0].lower()
|
||||||
|
|
||||||
log.debug('(%s) Found uplink server name as %r', self.irc.name, sname)
|
log.debug('(%s) Found uplink server name as %r', self.name, sname)
|
||||||
self.irc.servers[numeric].name = sname
|
self.servers[numeric].name = sname
|
||||||
self.irc.servers[numeric].desc = args[-1]
|
self.servers[numeric].desc = args[-1]
|
||||||
|
|
||||||
# According to the TS6 protocol documentation, we should send SVINFO
|
# According to the TS6 protocol documentation, we should send SVINFO
|
||||||
# when we get our uplink's SERVER command.
|
# when we get our uplink's SERVER command.
|
||||||
self.irc.send('SVINFO 6 6 0 :%s' % int(time.time()))
|
self.send('SVINFO 6 6 0 :%s' % int(time.time()))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[servername] = Server(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': None, 'text': sdesc}
|
return {'name': servername, 'sid': None, 'text': sdesc}
|
||||||
|
|
||||||
def handle_tmode(self, numeric, command, args):
|
def handle_tmode(self, numeric, command, args):
|
||||||
"""Handles incoming TMODE commands (channel mode change)."""
|
"""Handles incoming TMODE commands (channel mode change)."""
|
||||||
# <- :42XAAAAAB TMODE 1437450768 #test -c+lkC 3 agte4
|
# <- :42XAAAAAB TMODE 1437450768 #test -c+lkC 3 agte4
|
||||||
# <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD
|
# <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
changedmodes = self.irc.parseModes(channel, modes)
|
changedmodes = self.parse_modes(channel, modes)
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.apply_modes(channel, changedmodes)
|
||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
||||||
'channeldata': oldobj}
|
'channeldata': oldobj}
|
||||||
@ -601,66 +601,54 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
||||||
target = args[0]
|
target = args[0]
|
||||||
modestrings = args[1:]
|
modestrings = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modestrings)
|
changedmodes = self.parse_modes(target, modestrings)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.apply_modes(target, changedmodes)
|
||||||
# Call the OPERED UP hook if +o is being set.
|
# Call the OPERED UP hook if +o is being set.
|
||||||
if ('+o', None) in changedmodes:
|
if ('+o', None) in changedmodes:
|
||||||
otype = 'Server Administrator' if ('a', None) in self.irc.users[target].modes else 'IRC Operator'
|
otype = 'Server Administrator' if ('a', None) in self.users[target].modes else 'IRC Operator'
|
||||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': otype}])
|
self.call_hooks([target, 'CLIENT_OPERED', {'text': otype}])
|
||||||
return {'target': target, 'modes': changedmodes}
|
return {'target': target, 'modes': changedmodes}
|
||||||
|
|
||||||
def handle_tb(self, numeric, command, args):
|
def handle_tb(self, numeric, command, args):
|
||||||
"""Handles incoming topic burst (TB) commands."""
|
"""Handles incoming topic burst (TB) commands."""
|
||||||
# <- :42X TB #chat 1467427448 GL!~gl@127.0.0.1 :test
|
# <- :42X TB #chat 1467427448 GL!~gl@127.0.0.1 :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
setter = args[2]
|
setter = args[2]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_etb(self, numeric, command, args):
|
def handle_etb(self, numeric, command, args):
|
||||||
"""Handles extended topic burst (ETB)."""
|
"""Handles extended topic burst (ETB)."""
|
||||||
# <- :00AAAAAAC ETB 0 #test 1470021157 GL :test | abcd
|
# <- :00AAAAAAC ETB 0 #test 1470021157 GL :test | abcd
|
||||||
# Same as TB, with extra TS and extensions arguments.
|
# Same as TB, with extra TS and extensions arguments.
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
setter = args[3]
|
setter = args[3]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_invite(self, numeric, command, args):
|
|
||||||
"""Handles incoming INVITEs."""
|
|
||||||
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 12345
|
|
||||||
target = args[0]
|
|
||||||
channel = self.irc.toLower(args[1])
|
|
||||||
try:
|
|
||||||
ts = args[3]
|
|
||||||
except IndexError:
|
|
||||||
ts = int(time.time())
|
|
||||||
# We don't actually need to process this; it's just something plugins/hooks can use
|
|
||||||
return {'target': target, 'channel': channel, 'ts': ts}
|
|
||||||
|
|
||||||
def handle_chghost(self, numeric, command, args):
|
def handle_chghost(self, numeric, command, args):
|
||||||
"""Handles incoming CHGHOST commands."""
|
"""Handles incoming CHGHOST commands."""
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].host = newhost = args[1]
|
self.users[target].host = newhost = args[1]
|
||||||
return {'target': target, 'newhost': newhost}
|
return {'target': target, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_bmask(self, numeric, command, args):
|
def handle_bmask(self, numeric, command, args):
|
||||||
"""Handles incoming BMASK commands (ban propagation on burst)."""
|
"""Handles incoming BMASK commands (ban propagation on burst)."""
|
||||||
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
||||||
# This is used for propagating bans, not TMODE!
|
# This is used for propagating bans, not TMODE!
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
mode = args[2]
|
mode = args[2]
|
||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
modes = []
|
modes = []
|
||||||
for ban in args[-1].split():
|
for ban in args[-1].split():
|
||||||
modes.append(('+%s' % mode, ban))
|
modes.append(('+%s' % mode, ban))
|
||||||
self.irc.applyModes(channel, modes)
|
self.apply_modes(channel, modes)
|
||||||
return {'target': channel, 'modes': modes, 'ts': ts}
|
return {'target': channel, 'modes': modes, 'ts': ts}
|
||||||
|
|
||||||
def handle_472(self, numeric, command, args):
|
def handle_472(self, numeric, command, args):
|
||||||
@ -681,7 +669,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
log.warning('(%s) User %r attempted to set channel mode %r, but the '
|
log.warning('(%s) User %r attempted to set channel mode %r, but the '
|
||||||
'extension providing it isn\'t loaded! To prevent possible'
|
'extension providing it isn\'t loaded! To prevent possible'
|
||||||
' desyncs, try adding the line "loadmodule "extensions/%s.so";" to '
|
' desyncs, try adding the line "loadmodule "extensions/%s.so";" to '
|
||||||
'your IRCd configuration.', self.irc.name, setter, badmode,
|
'your IRCd configuration.', self.name, setter, badmode,
|
||||||
charlist[badmode])
|
charlist[badmode])
|
||||||
|
|
||||||
def handle_su(self, numeric, command, args):
|
def handle_su(self, numeric, command, args):
|
||||||
@ -696,7 +684,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
account = '' # No account name means a logout
|
account = '' # No account name means a logout
|
||||||
|
|
||||||
uid = args[0]
|
uid = args[0]
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
def handle_rsfnc(self, numeric, command, args):
|
def handle_rsfnc(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
|
@ -99,19 +99,18 @@ class TS6UIDGenerator(utils.IncrementalUIDGenerator):
|
|||||||
super().__init__(sid)
|
super().__init__(sid)
|
||||||
|
|
||||||
class TS6BaseProtocol(IRCS2SProtocol):
|
class TS6BaseProtocol(IRCS2SProtocol):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
def __init__(self, irc):
|
super().__init__(*args, **kwargs)
|
||||||
super().__init__(irc)
|
|
||||||
|
|
||||||
# Dictionary of UID generators (one for each server).
|
# Dictionary of UID generators (one for each server).
|
||||||
self.uidgen = structures.KeyedDefaultdict(TS6UIDGenerator)
|
self.uidgen = structures.KeyedDefaultdict(TS6UIDGenerator)
|
||||||
|
|
||||||
# SID generator for TS6.
|
# SID generator for TS6.
|
||||||
self.sidgen = TS6SIDGenerator(irc)
|
self.sidgen = TS6SIDGenerator(self)
|
||||||
|
|
||||||
def _send(self, source, msg, **kwargs):
|
def _send_with_prefix(self, source, msg, **kwargs):
|
||||||
"""Sends a TS6-style raw command from a source numeric to the self.irc connection given."""
|
"""Sends a TS6-style raw command from a source numeric to the self.irc connection given."""
|
||||||
self.irc.send(':%s %s' % (source, msg), **kwargs)
|
self.send(':%s %s' % (source, msg), **kwargs)
|
||||||
|
|
||||||
def _expandPUID(self, uid):
|
def _expandPUID(self, uid):
|
||||||
"""
|
"""
|
||||||
@ -130,23 +129,23 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
# Mangle the target for IRCds that require it.
|
# Mangle the target for IRCds that require it.
|
||||||
target = self._expandPUID(target)
|
target = self._expandPUID(target)
|
||||||
|
|
||||||
self._send(source, '%s %s %s' % (numeric, target, text))
|
self._send_with_prefix(source, '%s %s %s' % (numeric, target, text))
|
||||||
|
|
||||||
def kick(self, numeric, channel, target, reason=None):
|
def kick(self, numeric, channel, target, reason=None):
|
||||||
"""Sends kicks from a PyLink client/server."""
|
"""Sends kicks from a PyLink client/server."""
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
if not reason:
|
if not reason:
|
||||||
reason = 'No reason given'
|
reason = 'No reason given'
|
||||||
|
|
||||||
# Mangle kick targets for IRCds that require it.
|
# Mangle kick targets for IRCds that require it.
|
||||||
real_target = self._expandPUID(target)
|
real_target = self._expandPUID(target)
|
||||||
|
|
||||||
self._send(numeric, 'KICK %s %s :%s' % (channel, real_target, reason))
|
self._send_with_prefix(numeric, 'KICK %s %s :%s' % (channel, real_target, reason))
|
||||||
|
|
||||||
# We can pretend the target left by its own will; all we really care about
|
# We can pretend the target left by its own will; all we really care about
|
||||||
# is that the target gets removed from the channel userlist, and calling
|
# is that the target gets removed from the channel userlist, and calling
|
||||||
@ -156,8 +155,8 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
def kill(self, numeric, target, reason):
|
def kill(self, numeric, target, reason):
|
||||||
"""Sends a kill from a PyLink client/server."""
|
"""Sends a kill from a PyLink client/server."""
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# From TS6 docs:
|
# From TS6 docs:
|
||||||
@ -168,86 +167,86 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
# the kill followed by a space and a parenthesized reason. To avoid overflow,
|
# the kill followed by a space and a parenthesized reason. To avoid overflow,
|
||||||
# it is recommended not to add anything to the path.
|
# it is recommended not to add anything to the path.
|
||||||
|
|
||||||
assert target in self.irc.users, "Unknown target %r for kill()!" % target
|
assert target in self.users, "Unknown target %r for kill()!" % target
|
||||||
|
|
||||||
if numeric in self.irc.users:
|
if numeric in self.users:
|
||||||
# Killer was an user. Follow examples of setting the path to be "killer.host!killer.nick".
|
# Killer was an user. Follow examples of setting the path to be "killer.host!killer.nick".
|
||||||
userobj = self.irc.users[numeric]
|
userobj = self.users[numeric]
|
||||||
killpath = '%s!%s' % (userobj.host, userobj.nick)
|
killpath = '%s!%s' % (userobj.host, userobj.nick)
|
||||||
elif numeric in self.irc.servers:
|
elif numeric in self.servers:
|
||||||
# Sender was a server; killpath is just its name.
|
# Sender was a server; killpath is just its name.
|
||||||
killpath = self.irc.servers[numeric].name
|
killpath = self.servers[numeric].name
|
||||||
else:
|
else:
|
||||||
# Invalid sender?! This shouldn't happen, but make the killpath our server name anyways.
|
# Invalid sender?! This shouldn't happen, but make the killpath our server name anyways.
|
||||||
log.warning('(%s) Invalid sender %s for kill(); using our server name instead.',
|
log.warning('(%s) Invalid sender %s for kill(); using our server name instead.',
|
||||||
self.irc.name, numeric)
|
self.name, numeric)
|
||||||
killpath = self.irc.servers[self.irc.sid].name
|
killpath = self.servers[self.sid].name
|
||||||
|
|
||||||
self._send(numeric, 'KILL %s :%s (%s)' % (target, killpath, reason))
|
self._send_with_prefix(numeric, 'KILL %s :%s (%s)' % (target, killpath, reason))
|
||||||
self.removeClient(target)
|
self._remove_client(target)
|
||||||
|
|
||||||
def nick(self, numeric, newnick):
|
def nick(self, numeric, newnick):
|
||||||
"""Changes the nick of a PyLink client."""
|
"""Changes the nick of a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
self._send(numeric, 'NICK %s %s' % (newnick, int(time.time())))
|
self._send_with_prefix(numeric, 'NICK %s %s' % (newnick, int(time.time())))
|
||||||
|
|
||||||
self.irc.users[numeric].nick = newnick
|
self.users[numeric].nick = newnick
|
||||||
|
|
||||||
# Update the NICK TS.
|
# Update the NICK TS.
|
||||||
self.irc.users[numeric].ts = int(time.time())
|
self.users[numeric].ts = int(time.time())
|
||||||
|
|
||||||
def part(self, client, channel, reason=None):
|
def part(self, client, channel, reason=None):
|
||||||
"""Sends a part from a PyLink client."""
|
"""Sends a part from a PyLink client."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.is_internal_client(client):
|
||||||
log.error('(%s) Error trying to part %r from %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to part %r from %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
msg = "PART %s" % channel
|
msg = "PART %s" % channel
|
||||||
if reason:
|
if reason:
|
||||||
msg += " :%s" % reason
|
msg += " :%s" % reason
|
||||||
self._send(client, msg)
|
self._send_with_prefix(client, msg)
|
||||||
self.handle_part(client, 'PART', [channel])
|
self.handle_part(client, 'PART', [channel])
|
||||||
|
|
||||||
def quit(self, numeric, reason):
|
def quit(self, numeric, reason):
|
||||||
"""Quits a PyLink client."""
|
"""Quits a PyLink client."""
|
||||||
if self.irc.isInternalClient(numeric):
|
if self.is_internal_client(numeric):
|
||||||
self._send(numeric, "QUIT :%s" % reason)
|
self._send_with_prefix(numeric, "QUIT :%s" % reason)
|
||||||
self.removeClient(numeric)
|
self._remove_client(numeric)
|
||||||
else:
|
else:
|
||||||
raise LookupError("No such PyLink client exists.")
|
raise LookupError("No such PyLink client exists.")
|
||||||
|
|
||||||
def message(self, numeric, target, text):
|
def message(self, numeric, target, text):
|
||||||
"""Sends a PRIVMSG from a PyLink client."""
|
"""Sends a PRIVMSG from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Mangle message targets for IRCds that require it.
|
# Mangle message targets for IRCds that require it.
|
||||||
target = self._expandPUID(target)
|
target = self._expandPUID(target)
|
||||||
|
|
||||||
self._send(numeric, 'PRIVMSG %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'PRIVMSG %s :%s' % (target, text))
|
||||||
|
|
||||||
def notice(self, numeric, target, text):
|
def notice(self, numeric, target, text):
|
||||||
"""Sends a NOTICE from a PyLink client or server."""
|
"""Sends a NOTICE from a PyLink client or server."""
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# Mangle message targets for IRCds that require it.
|
# Mangle message targets for IRCds that require it.
|
||||||
target = self._expandPUID(target)
|
target = self._expandPUID(target)
|
||||||
|
|
||||||
self._send(numeric, 'NOTICE %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'NOTICE %s :%s' % (target, text))
|
||||||
|
|
||||||
def topic(self, numeric, target, text):
|
def topic(self, numeric, target, text):
|
||||||
"""Sends a TOPIC change from a PyLink client."""
|
"""Sends a TOPIC change from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(numeric, 'TOPIC %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
def spawn_server(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
||||||
"""
|
"""
|
||||||
Spawns a server off a PyLink server. desc (server description)
|
Spawns a server off a PyLink server. desc (server description)
|
||||||
defaults to the one in the config. uplink defaults to the main PyLink
|
defaults to the one in the config. uplink defaults to the main PyLink
|
||||||
@ -258,65 +257,65 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
option will be ignored if given.
|
option will be ignored if given.
|
||||||
"""
|
"""
|
||||||
# -> :0AL SID test.server 1 0XY :some silly pseudoserver
|
# -> :0AL SID test.server 1 0XY :some silly pseudoserver
|
||||||
uplink = uplink or self.irc.sid
|
uplink = uplink or self.sid
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
desc = desc or self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = desc or self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
if sid is None: # No sid given; generate one!
|
if sid is None: # No sid given; generate one!
|
||||||
sid = self.sidgen.next_sid()
|
sid = self.sidgen.next_sid()
|
||||||
assert len(sid) == 3, "Incorrect SID length"
|
assert len(sid) == 3, "Incorrect SID length"
|
||||||
if sid in self.irc.servers:
|
if sid in self.servers:
|
||||||
raise ValueError('A server with SID %r already exists!' % sid)
|
raise ValueError('A server with SID %r already exists!' % sid)
|
||||||
for server in self.irc.servers.values():
|
for server in self.servers.values():
|
||||||
if name == server.name:
|
if name == server.name:
|
||||||
raise ValueError('A server named %r already exists!' % name)
|
raise ValueError('A server named %r already exists!' % name)
|
||||||
if not self.irc.isInternalServer(uplink):
|
if not self.is_internal_server(uplink):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
||||||
if not utils.isServerName(name):
|
if not utils.isServerName(name):
|
||||||
raise ValueError('Invalid server name %r' % name)
|
raise ValueError('Invalid server name %r' % name)
|
||||||
self._send(uplink, 'SID %s 1 %s :%s' % (name, sid, desc))
|
self._send_with_prefix(uplink, 'SID %s 1 %s :%s' % (name, sid, desc))
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
self.servers[sid] = Server(uplink, name, internal=True, desc=desc)
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def squit(self, source, target, text='No reason given'):
|
def squit(self, source, target, text='No reason given'):
|
||||||
"""SQUITs a PyLink server."""
|
"""SQUITs a PyLink server."""
|
||||||
# -> SQUIT 9PZ :blah, blah
|
# -> SQUIT 9PZ :blah, blah
|
||||||
log.debug('source=%s, target=%s', source, target)
|
log.debug('source=%s, target=%s', source, target)
|
||||||
self._send(source, 'SQUIT %s :%s' % (target, text))
|
self._send_with_prefix(source, 'SQUIT %s :%s' % (target, text))
|
||||||
self.handle_squit(source, 'SQUIT', [target, text])
|
self.handle_squit(source, 'SQUIT', [target, text])
|
||||||
|
|
||||||
def away(self, source, text):
|
def away(self, source, text):
|
||||||
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
||||||
to unset AWAY status."""
|
to unset AWAY status."""
|
||||||
if text:
|
if text:
|
||||||
self._send(source, 'AWAY :%s' % text)
|
self._send_with_prefix(source, 'AWAY :%s' % text)
|
||||||
else:
|
else:
|
||||||
self._send(source, 'AWAY')
|
self._send_with_prefix(source, 'AWAY')
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
### HANDLERS
|
### HANDLERS
|
||||||
def handle_kick(self, source, command, args):
|
def handle_kick(self, source, command, args):
|
||||||
"""Handles incoming KICKs."""
|
"""Handles incoming KICKs."""
|
||||||
# :70MAAAAAA KICK #test 70MAAAAAA :some reason
|
# :70MAAAAAA KICK #test 70MAAAAAA :some reason
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
kicked = self._getUid(args[1])
|
kicked = self._get_UID(args[1])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reason = args[2]
|
reason = args[2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = ''
|
reason = ''
|
||||||
|
|
||||||
log.debug('(%s) Removing kick target %s from %s', self.irc.name, kicked, channel)
|
log.debug('(%s) Removing kick target %s from %s', self.name, kicked, channel)
|
||||||
self.handle_part(kicked, 'KICK', [channel, reason])
|
self.handle_part(kicked, 'KICK', [channel, reason])
|
||||||
return {'channel': channel, 'target': kicked, 'text': reason}
|
return {'channel': channel, 'target': kicked, 'text': reason}
|
||||||
|
|
||||||
def handle_nick(self, numeric, command, args):
|
def handle_nick(self, numeric, command, args):
|
||||||
"""Handles incoming NICK changes."""
|
"""Handles incoming NICK changes."""
|
||||||
# <- :70MAAAAAA NICK GL-devel 1434744242
|
# <- :70MAAAAAA NICK GL-devel 1434744242
|
||||||
oldnick = self.irc.users[numeric].nick
|
oldnick = self.users[numeric].nick
|
||||||
newnick = self.irc.users[numeric].nick = args[0]
|
newnick = self.users[numeric].nick = args[0]
|
||||||
|
|
||||||
# Update the nick TS.
|
# Update the nick TS.
|
||||||
self.irc.users[numeric].ts = ts = int(args[1])
|
self.users[numeric].ts = ts = int(args[1])
|
||||||
|
|
||||||
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
||||||
|
|
||||||
@ -331,12 +330,12 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
# -> :0AL000001 NICK Derp_ 1433728673
|
# -> :0AL000001 NICK Derp_ 1433728673
|
||||||
# <- :70M SAVE 0AL000001 1433728673
|
# <- :70M SAVE 0AL000001 1433728673
|
||||||
user = args[0]
|
user = args[0]
|
||||||
oldnick = self.irc.users[user].nick
|
oldnick = self.users[user].nick
|
||||||
self.irc.users[user].nick = user
|
self.users[user].nick = user
|
||||||
|
|
||||||
# TS6 SAVE sets nick TS to 100. This is hardcoded in InspIRCd and
|
# TS6 SAVE sets nick TS to 100. This is hardcoded in InspIRCd and
|
||||||
# charybdis.
|
# charybdis.
|
||||||
self.irc.users[user].ts = 100
|
self.users[user].ts = 100
|
||||||
|
|
||||||
return {'target': user, 'ts': 100, 'oldnick': oldnick}
|
return {'target': user, 'ts': 100, 'oldnick': oldnick}
|
||||||
|
|
||||||
@ -344,35 +343,16 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
"""Handles incoming TOPIC changes from clients. For topic bursts,
|
"""Handles incoming TOPIC changes from clients. For topic bursts,
|
||||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead."""
|
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead."""
|
||||||
# <- :70MAAAAAA TOPIC #test :test
|
# <- :70MAAAAAA TOPIC #test :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
topic = args[1]
|
topic = args[1]
|
||||||
|
|
||||||
oldtopic = self.irc.channels[channel].topic
|
oldtopic = self.channels[channel].topic
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
|
|
||||||
return {'channel': channel, 'setter': numeric, 'text': topic,
|
return {'channel': channel, 'setter': numeric, 'text': topic,
|
||||||
'oldtopic': oldtopic}
|
'oldtopic': oldtopic}
|
||||||
|
|
||||||
def handle_part(self, source, command, args):
|
|
||||||
"""Handles incoming PART commands."""
|
|
||||||
channels = self.irc.toLower(args[0]).split(',')
|
|
||||||
for channel in channels:
|
|
||||||
# We should only get PART commands for channels that exist, right??
|
|
||||||
self.irc.channels[channel].removeuser(source)
|
|
||||||
try:
|
|
||||||
self.irc.users[source].channels.discard(channel)
|
|
||||||
except KeyError:
|
|
||||||
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?", self.irc.name, channel, source)
|
|
||||||
try:
|
|
||||||
reason = args[1]
|
|
||||||
except IndexError:
|
|
||||||
reason = ''
|
|
||||||
# Clear empty non-permanent channels.
|
|
||||||
if not (self.irc.channels[channel].users or ((self.irc.cmodes.get('permanent'), None) in self.irc.channels[channel].modes)):
|
|
||||||
del self.irc.channels[channel]
|
|
||||||
return {'channels': channels, 'text': reason}
|
|
||||||
|
|
||||||
def handle_svsnick(self, source, command, args):
|
def handle_svsnick(self, source, command, args):
|
||||||
"""Handles SVSNICK (forced nickname change attempts)."""
|
"""Handles SVSNICK (forced nickname change attempts)."""
|
||||||
# InspIRCd:
|
# InspIRCd:
|
||||||
@ -381,4 +361,4 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
# UnrealIRCd:
|
# UnrealIRCd:
|
||||||
# <- :services.midnight.vpn SVSNICK GL Guest87795 1468303726
|
# <- :services.midnight.vpn SVSNICK GL Guest87795 1468303726
|
||||||
return {'target': self._getUid(args[0]), 'newnick': args[1]}
|
return {'target': self._get_UID(args[0]), 'newnick': args[1]}
|
||||||
|
@ -21,8 +21,8 @@ SJOIN_PREFIXES = {'q': '*', 'a': '~', 'o': '@', 'h': '%', 'v': '+', 'b': '&', 'e
|
|||||||
S2S_BUFSIZE = 427
|
S2S_BUFSIZE = 427
|
||||||
|
|
||||||
class UnrealProtocol(TS6BaseProtocol):
|
class UnrealProtocol(TS6BaseProtocol):
|
||||||
def __init__(self, irc):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(irc)
|
super().__init__(*args, **kwargs)
|
||||||
self.protocol_caps |= {'slash-in-nicks', 'underscore-in-hosts'}
|
self.protocol_caps |= {'slash-in-nicks', 'underscore-in-hosts'}
|
||||||
# Set our case mapping (rfc1459 maps "\" and "|" together, for example)
|
# Set our case mapping (rfc1459 maps "\" and "|" together, for example)
|
||||||
self.casemapping = 'ascii'
|
self.casemapping = 'ascii'
|
||||||
@ -34,7 +34,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
'EOS': 'ENDBURST'}
|
'EOS': 'ENDBURST'}
|
||||||
|
|
||||||
self.caps = []
|
self.caps = []
|
||||||
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
self.needed_caps = ["VL", "SID", "CHANMODES", "NOQUIT", "SJ3", "NICKIP", "UMODE2", "SJOIN"]
|
self.needed_caps = ["VL", "SID", "CHANMODES", "NOQUIT", "SJ3", "NICKIP", "UMODE2", "SJOIN"]
|
||||||
|
|
||||||
@ -47,16 +47,16 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
3.2 users), this will change the PUID given to the actual user's nick,
|
3.2 users), this will change the PUID given to the actual user's nick,
|
||||||
so that that the older IRCds can understand it.
|
so that that the older IRCds can understand it.
|
||||||
"""
|
"""
|
||||||
if uid in self.irc.users and '@' in uid:
|
if uid in self.users and '@' in uid:
|
||||||
# UID exists and has a @ in it, meaning it's a PUID (orignick@counter style).
|
# UID exists and has a @ in it, meaning it's a PUID (orignick@counter style).
|
||||||
# Return this user's nick accordingly.
|
# Return this user's nick accordingly.
|
||||||
nick = self.irc.users[uid].nick
|
nick = self.users[uid].nick
|
||||||
log.debug('(%s) Mangling target PUID %s to nick %s', self.irc.name, uid, nick)
|
log.debug('(%s) Mangling target PUID %s to nick %s', self.name, uid, nick)
|
||||||
return nick
|
return nick
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
### OUTGOING COMMAND FUNCTIONS
|
### OUTGOING COMMAND FUNCTIONS
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawn_client(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator',
|
||||||
manipulatable=False):
|
manipulatable=False):
|
||||||
"""
|
"""
|
||||||
@ -65,8 +65,8 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.is_internal_server(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
# Unreal 4.0 uses TS6-style UIDs. They don't start from AAAAAA like other IRCd's
|
# Unreal 4.0 uses TS6-style UIDs. They don't start from AAAAAA like other IRCd's
|
||||||
@ -81,11 +81,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
modes = set(modes) # Ensure type safety
|
modes = set(modes) # Ensure type safety
|
||||||
modes |= {('+x', None), ('+t', None)}
|
modes |= {('+x', None), ('+t', None)}
|
||||||
|
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.join_modes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
self.irc.applyModes(uid, modes)
|
self.apply_modes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
# UnrealIRCd requires encoding the IP by first packing it into a binary format,
|
# UnrealIRCd requires encoding the IP by first packing it into a binary format,
|
||||||
# and then encoding the binary with Base64.
|
# and then encoding the binary with Base64.
|
||||||
@ -106,7 +106,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
encoded_ip = encoded_ip.strip().decode()
|
encoded_ip = encoded_ip.strip().decode()
|
||||||
|
|
||||||
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * midnight-1C620195 fwAAAQ== :realname
|
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * midnight-1C620195 fwAAAQ== :realname
|
||||||
self._send(server, "UID {nick} 0 {ts} {ident} {realhost} {uid} 0 {modes} "
|
self._send_with_prefix(server, "UID {nick} 0 {ts} {ident} {realhost} {uid} 0 {modes} "
|
||||||
"{host} * {ip} :{realname}".format(ts=ts, host=host,
|
"{host} * {ip} :{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
modes=raw_modes, realname=realname,
|
modes=raw_modes, realname=realname,
|
||||||
@ -116,12 +116,12 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""Joins a PyLink client to a channel."""
|
"""Joins a PyLink client to a channel."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.is_internal_client(client):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(client, "JOIN %s" % channel)
|
self._send_with_prefix(client, "JOIN %s" % channel)
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -132,17 +132,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
# <- :001 SJOIN 1444361345 #test :*@+1JJAAAAAB %2JJAAAA4C 1JJAAAADS
|
# <- :001 SJOIN 1444361345 #test :*@+1JJAAAAAB %2JJAAAA4C 1JJAAAADS
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
|
|
||||||
changedmodes = set(modes or self.irc.channels[channel].modes)
|
changedmodes = set(modes or self.channels[channel].modes)
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
uids = []
|
uids = []
|
||||||
itemlist = []
|
itemlist = []
|
||||||
@ -163,17 +163,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
uids.append(user)
|
uids.append(user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
|
|
||||||
# Track simple modes separately.
|
# Track simple modes separately.
|
||||||
simplemodes = set()
|
simplemodes = set()
|
||||||
for modepair in modes:
|
for modepair in modes:
|
||||||
if modepair[0][-1] in self.irc.cmodes['*A']:
|
if modepair[0][-1] in self.cmodes['*A']:
|
||||||
# Bans, exempts, invex get expanded to forms like "&*!*@some.host" in SJOIN.
|
# Bans, exempts, invex get expanded to forms like "&*!*@some.host" in SJOIN.
|
||||||
|
|
||||||
if (modepair[0][-1], modepair[1]) in self.irc.channels[channel].modes:
|
if (modepair[0][-1], modepair[1]) in self.channels[channel].modes:
|
||||||
# Mode is already set; skip it.
|
# Mode is already set; skip it.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -189,49 +189,49 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Modes are optional; add them if they exist
|
# Modes are optional; add them if they exist
|
||||||
if modes:
|
if modes:
|
||||||
sjoin_prefix += " %s" % self.irc.joinModes(simplemodes)
|
sjoin_prefix += " %s" % self.join_modes(simplemodes)
|
||||||
|
|
||||||
sjoin_prefix += " :"
|
sjoin_prefix += " :"
|
||||||
# Wrap arguments to the max supported S2S line length to prevent cutoff
|
# Wrap arguments to the max supported S2S line length to prevent cutoff
|
||||||
# (https://github.com/GLolol/PyLink/issues/378)
|
# (https://github.com/GLolol/PyLink/issues/378)
|
||||||
for line in utils.wrapArguments(sjoin_prefix, itemlist, S2S_BUFSIZE):
|
for line in utils.wrapArguments(sjoin_prefix, itemlist, S2S_BUFSIZE):
|
||||||
self.irc.send(line)
|
self.send(line)
|
||||||
|
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
target = target or self.irc.uplink
|
target = target or self.uplink
|
||||||
if not (target is None or source is None):
|
if not (target is None or source is None):
|
||||||
self._send(source, 'PING %s %s' % (self.irc.servers[source].name, self.irc.servers[target].name))
|
self._send_with_prefix(source, 'PING %s %s' % (self.servers[source].name, self.servers[target].name))
|
||||||
|
|
||||||
def mode(self, numeric, target, modes, ts=None):
|
def mode(self, numeric, target, modes, ts=None):
|
||||||
"""
|
"""
|
||||||
Sends mode changes from a PyLink client/server. The mode list should be
|
Sends mode changes from a PyLink client/server. The mode list should be
|
||||||
a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output.
|
a list of (mode, arg) tuples, i.e. the format of utils.parse_modes() output.
|
||||||
"""
|
"""
|
||||||
# <- :unreal.midnight.vpn MODE #test +ntCo GL 1444361345
|
# <- :unreal.midnight.vpn MODE #test +ntCo GL 1444361345
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.is_internal_client(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.is_internal_server(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self.irc.applyModes(target, modes)
|
self.apply_modes(target, modes)
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
|
|
||||||
# Make sure we expand any PUIDs when sending outgoing modes...
|
# Make sure we expand any PUIDs when sending outgoing modes...
|
||||||
for idx, mode in enumerate(modes):
|
for idx, mode in enumerate(modes):
|
||||||
if mode[0][-1] in self.irc.prefixmodes:
|
if mode[0][-1] in self.prefixmodes:
|
||||||
log.debug('(%s) mode: expanding PUID of mode %s', self.irc.name, str(mode))
|
log.debug('(%s) mode: expanding PUID of mode %s', self.name, str(mode))
|
||||||
modes[idx] = (mode[0], self._expandPUID(mode[1]))
|
modes[idx] = (mode[0], self._expandPUID(mode[1]))
|
||||||
|
|
||||||
# The MODE command is used for channel mode changes only
|
# The MODE command is used for channel mode changes only
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.to_lower(target)].ts
|
||||||
|
|
||||||
# 7 characters for "MODE", the space between MODE and the target, the space between the
|
# 7 characters for "MODE", the space between MODE and the target, the space between the
|
||||||
# target and mode list, and the space between the mode list and TS.
|
# target and mode list, and the space between the mode list and TS.
|
||||||
@ -242,7 +242,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
bufsize -= len(target)
|
bufsize -= len(target)
|
||||||
|
|
||||||
# Subtract the prefix (":SID " for servers or ":SIDAAAAAA " for servers)
|
# Subtract the prefix (":SID " for servers or ":SIDAAAAAA " for servers)
|
||||||
bufsize -= (5 if self.irc.isInternalServer(numeric) else 11)
|
bufsize -= (5 if self.is_internal_server(numeric) else 11)
|
||||||
|
|
||||||
# There is also an (undocumented) 15 args per line limit for MODE. The target, mode
|
# There is also an (undocumented) 15 args per line limit for MODE. The target, mode
|
||||||
# characters, and TS take up three args, so we're left with 12 spaces for parameters.
|
# characters, and TS take up three args, so we're left with 12 spaces for parameters.
|
||||||
@ -251,30 +251,30 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# * *** Warning! Possible desynch: MODE for channel #test ('+bbbbbbbbbbbb *!*@0.1 *!*@1.1 *!*@2.1 *!*@3.1 *!*@4.1 *!*@5.1 *!*@6.1 *!*@7.1 *!*@8.1 *!*@9.1 *!*@10.1 *!*@11.1') has fishy timestamp (12) (from pylink.local/pylink.local)
|
# * *** Warning! Possible desynch: MODE for channel #test ('+bbbbbbbbbbbb *!*@0.1 *!*@1.1 *!*@2.1 *!*@3.1 *!*@4.1 *!*@5.1 *!*@6.1 *!*@7.1 *!*@8.1 *!*@9.1 *!*@10.1 *!*@11.1') has fishy timestamp (12) (from pylink.local/pylink.local)
|
||||||
|
|
||||||
# Thanks to kevin and Jobe for helping me debug this!
|
# Thanks to kevin and Jobe for helping me debug this!
|
||||||
for modestring in self.irc.wrapModes(modes, bufsize, max_modes_per_msg=12):
|
for modestring in self.wrap_modes(modes, bufsize, max_modes_per_msg=12):
|
||||||
self._send(numeric, 'MODE %s %s %s' % (target, modestring, ts))
|
self._send_with_prefix(numeric, 'MODE %s %s %s' % (target, modestring, ts))
|
||||||
else:
|
else:
|
||||||
# For user modes, the only way to set modes (for non-U:Lined servers)
|
# For user modes, the only way to set modes (for non-U:Lined servers)
|
||||||
# is through UMODE2, which sets the modes on the caller.
|
# is through UMODE2, which sets the modes on the caller.
|
||||||
# U:Lines can use SVSMODE/SVS2MODE, but I won't expect people to
|
# U:Lines can use SVSMODE/SVS2MODE, but I won't expect people to
|
||||||
# U:Line a PyLink daemon...
|
# U:Line a PyLink daemon...
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.is_internal_client(target):
|
||||||
raise ProtocolError('Cannot force mode change on external clients!')
|
raise ProtocolError('Cannot force mode change on external clients!')
|
||||||
|
|
||||||
# XXX: I don't expect usermode changes to ever get cut off, but length
|
# XXX: I don't expect usermode changes to ever get cut off, but length
|
||||||
# checks could be added just to be safe...
|
# checks could be added just to be safe...
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.join_modes(modes)
|
||||||
self._send(target, 'UMODE2 %s' % joinedmodes)
|
self._send_with_prefix(target, 'UMODE2 %s' % joinedmodes)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topic_burst(self, numeric, target, text):
|
||||||
"""Sends a TOPIC change from a PyLink server."""
|
"""Sends a TOPIC change from a PyLink server."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.is_internal_server(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
self._send(numeric, 'TOPIC %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def update_client(self, target, field, text):
|
||||||
"""Updates the ident, host, or realname of any connected client."""
|
"""Updates the ident, host, or realname of any connected client."""
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
|
|
||||||
@ -282,46 +282,46 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
"unsupported by this protocol." % field)
|
"unsupported by this protocol." % field)
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.is_internal_client(target):
|
||||||
# It is one of our clients, use SETIDENT/HOST/NAME.
|
# It is one of our clients, use SETIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send(target, 'SETIDENT %s' % text)
|
self._send_with_prefix(target, 'SETIDENT %s' % text)
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send(target, 'SETHOST %s' % text)
|
self._send_with_prefix(target, 'SETHOST %s' % text)
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send(target, 'SETNAME :%s' % text)
|
self._send_with_prefix(target, 'SETNAME :%s' % text)
|
||||||
else:
|
else:
|
||||||
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send(self.irc.sid, 'CHGIDENT %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGIDENT %s %s' % (target, text))
|
||||||
|
|
||||||
# Send hook payloads for other plugins to listen to.
|
# Send hook payloads for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGIDENT',
|
self.call_hooks([self.sid, 'CHGIDENT',
|
||||||
{'target': target, 'newident': text}])
|
{'target': target, 'newident': text}])
|
||||||
|
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send(self.irc.sid, 'CHGHOST %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s %s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.call_hooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
|
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send(self.irc.sid, 'CHGNAME %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGNAME %s :%s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGNAME',
|
self.call_hooks([self.sid, 'CHGNAME',
|
||||||
{'target': target, 'newgecos': text}])
|
{'target': target, 'newgecos': text}])
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.is_internal_client(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send(numeric, 'INVITE %s %s' % (target, channel))
|
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
||||||
|
|
||||||
def knock(self, numeric, target, text):
|
def knock(self, numeric, target, text):
|
||||||
"""Sends a KNOCK from a PyLink client."""
|
"""Sends a KNOCK from a PyLink client."""
|
||||||
@ -329,21 +329,21 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# sent to all ops in a channel.
|
# sent to all ops in a channel.
|
||||||
# <- :unreal.midnight.vpn NOTICE @#test :[Knock] by GL|!gl@hidden-1C620195 (test)
|
# <- :unreal.midnight.vpn NOTICE @#test :[Knock] by GL|!gl@hidden-1C620195 (test)
|
||||||
assert utils.isChannel(target), "Can only knock on channels!"
|
assert utils.isChannel(target), "Can only knock on channels!"
|
||||||
sender = self.irc.getServer(numeric)
|
sender = self.get_server(numeric)
|
||||||
s = '[Knock] by %s (%s)' % (self.irc.getHostmask(numeric), text)
|
s = '[Knock] by %s (%s)' % (self.get_hostmask(numeric), text)
|
||||||
self._send(sender, 'NOTICE @%s :%s' % (target, s))
|
self._send_with_prefix(sender, 'NOTICE @%s :%s' % (target, s))
|
||||||
|
|
||||||
### HANDLERS
|
### HANDLERS
|
||||||
|
|
||||||
def connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
# Track usages of legacy (Unreal 3.2) nicks.
|
# Track usages of legacy (Unreal 3.2) nicks.
|
||||||
self.legacy_uidgen = utils.PUIDGenerator('U32user')
|
self.legacy_uidgen = utils.PUIDGenerator('U32user')
|
||||||
|
|
||||||
self.irc.umodes.update({'deaf': 'd', 'invisible': 'i', 'hidechans': 'p',
|
self.umodes.update({'deaf': 'd', 'invisible': 'i', 'hidechans': 'p',
|
||||||
'protected': 'q', 'registered': 'r',
|
'protected': 'q', 'registered': 'r',
|
||||||
'snomask': 's', 'vhost': 't', 'wallops': 'w',
|
'snomask': 's', 'vhost': 't', 'wallops': 'w',
|
||||||
'bot': 'B', 'cloak': 'x', 'ssl': 'z',
|
'bot': 'B', 'cloak': 'x', 'ssl': 'z',
|
||||||
@ -352,10 +352,10 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
'noctcp': 'T', 'showwhois': 'W',
|
'noctcp': 'T', 'showwhois': 'W',
|
||||||
'*A': '', '*B': '', '*C': '', '*D': 'dipqrstwBxzGHIRSTW'})
|
'*A': '', '*B': '', '*C': '', '*D': 'dipqrstwBxzGHIRSTW'})
|
||||||
|
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
host = self.irc.serverdata["hostname"]
|
host = self.serverdata["hostname"]
|
||||||
|
|
||||||
f('PASS :%s' % self.irc.serverdata["sendpass"])
|
f('PASS :%s' % self.serverdata["sendpass"])
|
||||||
# https://github.com/unrealircd/unrealircd/blob/2f8cb55e/doc/technical/protoctl.txt
|
# https://github.com/unrealircd/unrealircd/blob/2f8cb55e/doc/technical/protoctl.txt
|
||||||
# We support the following protocol features:
|
# We support the following protocol features:
|
||||||
# SJOIN - supports SJOIN for user introduction
|
# SJOIN - supports SJOIN for user introduction
|
||||||
@ -374,11 +374,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# not work for any UnrealIRCd 3.2 users.
|
# not work for any UnrealIRCd 3.2 users.
|
||||||
# ESVID - Supports account names in services stamps instead of just the signon time.
|
# ESVID - Supports account names in services stamps instead of just the signon time.
|
||||||
# AFAIK this doesn't actually affect services' behaviour?
|
# AFAIK this doesn't actually affect services' behaviour?
|
||||||
f('PROTOCTL SJOIN SJ3 NOQUIT NICKv2 VL UMODE2 PROTOCTL NICKIP EAUTH=%s SID=%s VHP ESVID' % (self.irc.serverdata["hostname"], self.irc.sid))
|
f('PROTOCTL SJOIN SJ3 NOQUIT NICKv2 VL UMODE2 PROTOCTL NICKIP EAUTH=%s SID=%s VHP ESVID' % (self.serverdata["hostname"], self.sid))
|
||||||
sdesc = self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
sdesc = self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
f('SERVER %s 1 U%s-h6e-%s :%s' % (host, self.proto_ver, self.irc.sid, sdesc))
|
f('SERVER %s 1 U%s-h6e-%s :%s' % (host, self.proto_ver, self.sid, sdesc))
|
||||||
f('NETINFO 1 %s %s * 0 0 0 :%s' % (self.irc.start_ts, self.proto_ver, self.irc.serverdata.get("netname", self.irc.name)))
|
f('NETINFO 1 %s %s * 0 0 0 :%s' % (self.start_ts, self.proto_ver, self.serverdata.get("netname", self.name)))
|
||||||
self._send(self.irc.sid, 'EOS')
|
self._send_with_prefix(self.sid, 'EOS')
|
||||||
|
|
||||||
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."""
|
||||||
@ -418,50 +418,50 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
realname = args[-1]
|
realname = args[-1]
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = User(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Handle user modes
|
# Handle user modes
|
||||||
parsedmodes = self.irc.parseModes(uid, [modestring])
|
parsedmodes = self.parse_modes(uid, [modestring])
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.apply_modes(uid, parsedmodes)
|
||||||
|
|
||||||
# The cloaked (+x) host is completely separate from the displayed host
|
# The cloaked (+x) host is completely separate from the displayed host
|
||||||
# and real host in that it is ONLY shown if the user is +x (cloak mode
|
# and real host in that it is ONLY shown if the user is +x (cloak mode
|
||||||
# enabled) but NOT +t (vHost set).
|
# enabled) but NOT +t (vHost set).
|
||||||
self.irc.users[uid].cloaked_host = args[9]
|
self.users[uid].cloaked_host = args[9]
|
||||||
|
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
# If +o being set, call the CLIENT_OPERED internal hook.
|
# If +o being set, call the CLIENT_OPERED internal hook.
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.call_hooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
if ('+x', None) not in parsedmodes:
|
if ('+x', None) not in parsedmodes:
|
||||||
# If +x is not set, update to use the person's real host.
|
# If +x is not set, update to use the person's real host.
|
||||||
self.irc.users[uid].host = realhost
|
self.users[uid].host = realhost
|
||||||
|
|
||||||
# Set the account name if present: if this is a number, set it to the user nick.
|
# Set the account name if present: if this is a number, set it to the user nick.
|
||||||
if ('+r', None) in parsedmodes and accountname.isdigit():
|
if ('+r', None) in parsedmodes and accountname.isdigit():
|
||||||
accountname = nick
|
accountname = nick
|
||||||
|
|
||||||
if not accountname.isdigit():
|
if not accountname.isdigit():
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_pass(self, numeric, command, args):
|
def handle_pass(self, numeric, command, args):
|
||||||
# <- PASS :abcdefg
|
# <- PASS :abcdefg
|
||||||
if args[0] != self.irc.serverdata['recvpass']:
|
if args[0] != self.serverdata['recvpass']:
|
||||||
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
||||||
|
|
||||||
def handle_ping(self, numeric, command, args):
|
def handle_ping(self, numeric, command, args):
|
||||||
if numeric == self.irc.uplink:
|
if numeric == self.uplink:
|
||||||
self.irc.send('PONG %s :%s' % (self.irc.serverdata['hostname'], args[-1]), queue=False)
|
self.send('PONG %s :%s' % (self.serverdata['hostname'], args[-1]), queue=False)
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
"""Handles the SERVER command, which is used for both authentication and
|
"""Handles the SERVER command, which is used for both authentication and
|
||||||
introducing legacy (non-SID) servers."""
|
introducing legacy (non-SID) servers."""
|
||||||
# <- SERVER unreal.midnight.vpn 1 :U3999-Fhin6OoEM UnrealIRCd test server
|
# <- SERVER unreal.midnight.vpn 1 :U3999-Fhin6OoEM UnrealIRCd test server
|
||||||
sname = args[0]
|
sname = args[0]
|
||||||
if numeric == self.irc.uplink and not self.irc.connected.is_set(): # We're doing authentication
|
if numeric == self.uplink and not self.connected.is_set(): # We're doing authentication
|
||||||
for cap in self.needed_caps:
|
for cap in self.needed_caps:
|
||||||
if cap not in self.caps:
|
if cap not in self.caps:
|
||||||
raise ProtocolError("Not all required capabilities were met "
|
raise ProtocolError("Not all required capabilities were met "
|
||||||
@ -485,17 +485,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
if protover < self.min_proto_ver:
|
if protover < self.min_proto_ver:
|
||||||
raise ProtocolError("Protocol version too old! (needs at least %s "
|
raise ProtocolError("Protocol version too old! (needs at least %s "
|
||||||
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
||||||
self.irc.servers[numeric] = IrcServer(None, sname, desc=sdesc)
|
self.servers[numeric] = Server(None, sname, desc=sdesc)
|
||||||
|
|
||||||
# Set irc.connected to True, meaning that protocol negotiation passed.
|
# Set irc.connected to True, meaning that protocol negotiation passed.
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
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
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[servername] = Server(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': None, 'text': sdesc}
|
return {'name': servername, 'sid': None, 'text': sdesc}
|
||||||
|
|
||||||
def handle_sid(self, numeric, command, args):
|
def handle_sid(self, numeric, command, args):
|
||||||
@ -504,14 +504,14 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
sname = args[0].lower()
|
sname = args[0].lower()
|
||||||
sid = args[2]
|
sid = args[2]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, sname, desc=sdesc)
|
self.servers[sid] = Server(numeric, sname, desc=sdesc)
|
||||||
return {'name': sname, 'sid': sid, 'text': sdesc}
|
return {'name': sname, 'sid': sid, 'text': sdesc}
|
||||||
|
|
||||||
def handle_squit(self, numeric, command, args):
|
def handle_squit(self, numeric, command, args):
|
||||||
"""Handles the SQUIT command."""
|
"""Handles the SQUIT command."""
|
||||||
# <- SQUIT services.int :Read error
|
# <- SQUIT services.int :Read error
|
||||||
# Convert the server name to a SID...
|
# Convert the server name to a SID...
|
||||||
args[0] = self._getSid(args[0])
|
args[0] = self._get_SID(args[0])
|
||||||
# Then, use the SQUIT handler in TS6BaseProtocol as usual.
|
# Then, use the SQUIT handler in TS6BaseProtocol as usual.
|
||||||
return super().handle_squit(numeric, 'SQUIT', args)
|
return super().handle_squit(numeric, 'SQUIT', args)
|
||||||
|
|
||||||
@ -534,18 +534,18 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- PROTOCTL CHANMODES=beI,k,l,psmntirzMQNRTOVKDdGPZSCc NICKCHARS= SID=001 MLOCK TS=1441314501 EXTSWHOIS
|
# <- PROTOCTL CHANMODES=beI,k,l,psmntirzMQNRTOVKDdGPZSCc NICKCHARS= SID=001 MLOCK TS=1441314501 EXTSWHOIS
|
||||||
for cap in args:
|
for cap in args:
|
||||||
if cap.startswith('SID'):
|
if cap.startswith('SID'):
|
||||||
self.irc.uplink = cap.split('=', 1)[1]
|
self.uplink = cap.split('=', 1)[1]
|
||||||
elif cap.startswith('CHANMODES'):
|
elif cap.startswith('CHANMODES'):
|
||||||
# Parse all the supported channel modes.
|
# Parse all the supported channel modes.
|
||||||
supported_cmodes = cap.split('=', 1)[1]
|
supported_cmodes = cap.split('=', 1)[1]
|
||||||
self.irc.cmodes['*A'], self.irc.cmodes['*B'], self.irc.cmodes['*C'], self.irc.cmodes['*D'] = supported_cmodes.split(',')
|
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = supported_cmodes.split(',')
|
||||||
for namedmode, modechar in cmodes.items():
|
for namedmode, modechar in cmodes.items():
|
||||||
if modechar in supported_cmodes:
|
if modechar in supported_cmodes:
|
||||||
self.irc.cmodes[namedmode] = modechar
|
self.cmodes[namedmode] = modechar
|
||||||
self.irc.cmodes['*B'] += 'f' # Add +f to the list too, dunno why it isn't there.
|
self.cmodes['*B'] += 'f' # Add +f to the list too, dunno why it isn't there.
|
||||||
|
|
||||||
# Add in the supported prefix modes.
|
# Add in the supported prefix modes.
|
||||||
self.irc.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
|
self.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
|
||||||
'op': 'o', 'voice': 'v'})
|
'op': 'o', 'voice': 'v'})
|
||||||
|
|
||||||
def handle_join(self, numeric, command, args):
|
def handle_join(self, numeric, command, args):
|
||||||
@ -553,38 +553,38 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- :GL JOIN #pylink,#test
|
# <- :GL JOIN #pylink,#test
|
||||||
if args[0] == '0':
|
if args[0] == '0':
|
||||||
# /join 0; part the user from all channels
|
# /join 0; part the user from all channels
|
||||||
oldchans = self.irc.users[numeric].channels.copy()
|
oldchans = self.users[numeric].channels.copy()
|
||||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||||
self.irc.name, numeric, oldchans)
|
self.name, numeric, oldchans)
|
||||||
for ch in oldchans:
|
for ch in oldchans:
|
||||||
self.irc.channels[ch].users.discard(numeric)
|
self.channels[ch].users.discard(numeric)
|
||||||
self.irc.users[numeric].channels.discard(ch)
|
self.users[numeric].channels.discard(ch)
|
||||||
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for channel in args[0].split(','):
|
for channel in args[0].split(','):
|
||||||
# Normalize channel case.
|
# Normalize channel case.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.to_lower(channel)
|
||||||
|
|
||||||
c = self.irc.channels[channel]
|
c = self.channels[channel]
|
||||||
|
|
||||||
self.irc.users[numeric].channels.add(channel)
|
self.users[numeric].channels.add(channel)
|
||||||
self.irc.channels[channel].users.add(numeric)
|
self.channels[channel].users.add(numeric)
|
||||||
# Call hooks manually, because one JOIN command in UnrealIRCd can
|
# Call hooks manually, because one JOIN command in UnrealIRCd can
|
||||||
# have multiple channels...
|
# have multiple channels...
|
||||||
self.irc.callHooks([numeric, command, {'channel': channel, 'users': [numeric], 'modes':
|
self.call_hooks([numeric, command, {'channel': channel, 'users': [numeric], 'modes':
|
||||||
c.modes, 'ts': c.ts}])
|
c.modes, 'ts': c.ts}])
|
||||||
|
|
||||||
def handle_sjoin(self, numeric, command, args):
|
def handle_sjoin(self, numeric, command, args):
|
||||||
"""Handles the UnrealIRCd SJOIN command."""
|
"""Handles the UnrealIRCd SJOIN command."""
|
||||||
# <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC
|
# <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC
|
||||||
# <- :001 SJOIN 1483250129 #services +nt :+001OR9V02 @*~001DH6901 &*!*@test "*!*@blah.blah '*!*@yes.no
|
# <- :001 SJOIN 1483250129 #services +nt :+001OR9V02 @*~001DH6901 &*!*@test "*!*@blah.blah '*!*@yes.no
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.to_lower(args[1])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
namelist = []
|
namelist = []
|
||||||
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.name, userlist, channel)
|
||||||
|
|
||||||
modestring = ''
|
modestring = ''
|
||||||
|
|
||||||
@ -599,7 +599,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# Strip extra spaces between the mode argument and the user list, if
|
# Strip extra spaces between the mode argument and the user list, if
|
||||||
# there are any. XXX: report this as a bug in unreal's s2s protocol?
|
# there are any. XXX: report this as a bug in unreal's s2s protocol?
|
||||||
modestring = [m for m in modestring if m]
|
modestring = [m for m in modestring if m]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parse_modes(channel, modestring)
|
||||||
changedmodes = set(parsedmodes)
|
changedmodes = set(parsedmodes)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
@ -624,28 +624,28 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- :002 SJOIN 1486361658 #idlerpg :@
|
# <- :002 SJOIN 1486361658 #idlerpg :@
|
||||||
continue
|
continue
|
||||||
|
|
||||||
user = self._getUid(user) # Normalize nicks to UIDs for Unreal 3.2 links
|
user = self._get_UID(user) # Normalize nicks to UIDs for Unreal 3.2 links
|
||||||
# Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~,
|
# Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~,
|
||||||
# and +a is ~ instead of &.
|
# and +a is ~ instead of &.
|
||||||
modeprefix = (r.group(1) or '').replace("~", "&").replace("*", "~")
|
modeprefix = (r.group(1) or '').replace("~", "&").replace("*", "~")
|
||||||
finalprefix = ''
|
finalprefix = ''
|
||||||
|
|
||||||
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)
|
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.name, modeprefix, user)
|
||||||
for m in modeprefix:
|
for m in modeprefix:
|
||||||
# Iterate over the mapping of prefix chars to prefixes, and
|
# Iterate over the mapping of prefix chars to prefixes, and
|
||||||
# find the characters that match.
|
# find the characters that match.
|
||||||
for char, prefix in self.irc.prefixmodes.items():
|
for char, prefix in self.prefixmodes.items():
|
||||||
if m == prefix:
|
if m == prefix:
|
||||||
finalprefix += char
|
finalprefix += char
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only merge the remote's prefix modes if their TS is smaller or equal to ours.
|
# Only merge the remote's prefix modes if their TS is smaller or equal to ours.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
their_ts = int(args[0])
|
their_ts = int(args[0])
|
||||||
self.updateTS(numeric, channel, their_ts, changedmodes)
|
self.updateTS(numeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- NICK GL32 2 1470699865 gl localhost unreal32.midnight.vpn GL +iowx hidden-1C620195 AAAAAAAAAAAAAAAAAAAAAQ== :realname
|
# <- NICK GL32 2 1470699865 gl localhost unreal32.midnight.vpn GL +iowx hidden-1C620195 AAAAAAAAAAAAAAAAAAAAAQ== :realname
|
||||||
# to this:
|
# to this:
|
||||||
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * hidden-1C620195 fwAAAQ== :realname
|
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * hidden-1C620195 fwAAAQ== :realname
|
||||||
log.debug('(%s) got legacy NICK args: %s', self.irc.name, ' '.join(args))
|
log.debug('(%s) got legacy NICK args: %s', self.name, ' '.join(args))
|
||||||
|
|
||||||
new_args = args[:] # Clone the old args list
|
new_args = args[:] # Clone the old args list
|
||||||
servername = new_args[5].lower() # Get the name of the users' server.
|
servername = new_args[5].lower() # Get the name of the users' server.
|
||||||
@ -682,7 +682,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# hosts from UnrealIRCd 3.2 users. Otherwise, +x host cloaking won't work!
|
# hosts from UnrealIRCd 3.2 users. Otherwise, +x host cloaking won't work!
|
||||||
new_args.insert(-2, args[4])
|
new_args.insert(-2, args[4])
|
||||||
|
|
||||||
log.debug('(%s) translating legacy NICK args to: %s', self.irc.name, ' '.join(new_args))
|
log.debug('(%s) translating legacy NICK args to: %s', self.name, ' '.join(new_args))
|
||||||
|
|
||||||
return self.handle_uid(servername, 'UID_LEGACY', new_args)
|
return self.handle_uid(servername, 'UID_LEGACY', new_args)
|
||||||
else:
|
else:
|
||||||
@ -705,11 +705,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Also, we need to get rid of that extra space following the +f argument. :|
|
# Also, we need to get rid of that extra space following the +f argument. :|
|
||||||
if utils.isChannel(args[0]):
|
if utils.isChannel(args[0]):
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
|
|
||||||
modes = [arg for arg in args[1:] if arg] # normalize whitespace
|
modes = [arg for arg in args[1:] if arg] # normalize whitespace
|
||||||
parsedmodes = self.irc.parseModes(channel, modes)
|
parsedmodes = self.parse_modes(channel, modes)
|
||||||
|
|
||||||
if parsedmodes:
|
if parsedmodes:
|
||||||
if parsedmodes[0][0] == '+&':
|
if parsedmodes[0][0] == '+&':
|
||||||
@ -717,12 +717,12 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# attempt to set modes by us was rejected for some reason (usually due to
|
# attempt to set modes by us was rejected for some reason (usually due to
|
||||||
# timestamps). Drop the mode change to prevent mode floods.
|
# timestamps). Drop the mode change to prevent mode floods.
|
||||||
log.debug("(%s) Received mode bounce %s in channel %s! Our TS: %s",
|
log.debug("(%s) Received mode bounce %s in channel %s! Our TS: %s",
|
||||||
self.irc.name, modes, channel, self.irc.channels[channel].ts)
|
self.name, modes, channel, self.channels[channel].ts)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.applyModes(channel, parsedmodes)
|
self.apply_modes(channel, parsedmodes)
|
||||||
|
|
||||||
if numeric in self.irc.servers and args[-1].isdigit():
|
if numeric in self.servers and args[-1].isdigit():
|
||||||
# Sender is a server AND last arg is number. Perform TS updates.
|
# Sender is a server AND last arg is number. Perform TS updates.
|
||||||
their_ts = int(args[-1])
|
their_ts = int(args[-1])
|
||||||
if their_ts > 0:
|
if their_ts > 0:
|
||||||
@ -732,13 +732,13 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# User mode change: pass those on to handle_umode2()
|
# User mode change: pass those on to handle_umode2()
|
||||||
self.handle_umode2(numeric, 'MODE', args[1:])
|
self.handle_umode2(numeric, 'MODE', args[1:])
|
||||||
|
|
||||||
def checkCloakChange(self, uid, parsedmodes):
|
def _check_cloak_change(self, uid, parsedmodes):
|
||||||
"""
|
"""
|
||||||
Checks whether +x/-x was set in the mode query, and changes the
|
Checks whether +x/-x was set in the mode query, and changes the
|
||||||
hostname of the user given to or from their cloaked host if True.
|
hostname of the user given to or from their cloaked host if True.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
userobj = self.irc.users[uid]
|
userobj = self.users[uid]
|
||||||
final_modes = userobj.modes
|
final_modes = userobj.modes
|
||||||
oldhost = userobj.host
|
oldhost = userobj.host
|
||||||
|
|
||||||
@ -763,20 +763,20 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
if newhost != oldhost:
|
if newhost != oldhost:
|
||||||
# Only send a payload if the old and new hosts are different.
|
# Only send a payload if the old and new hosts are different.
|
||||||
self.irc.callHooks([uid, 'SETHOST',
|
self.call_hooks([uid, 'SETHOST',
|
||||||
{'target': uid, 'newhost': newhost}])
|
{'target': uid, 'newhost': newhost}])
|
||||||
|
|
||||||
def handle_svsmode(self, numeric, command, args):
|
def handle_svsmode(self, numeric, command, args):
|
||||||
"""Handles SVSMODE, used by services for setting user modes on others."""
|
"""Handles SVSMODE, used by services for setting user modes on others."""
|
||||||
# <- :source SVSMODE target +usermodes
|
# <- :source SVSMODE target +usermodes
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
modes = args[1:]
|
modes = args[1:]
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(target, modes)
|
parsedmodes = self.parse_modes(target, modes)
|
||||||
self.irc.applyModes(target, parsedmodes)
|
self.apply_modes(target, parsedmodes)
|
||||||
|
|
||||||
# If +x/-x is being set, update cloaked host info.
|
# If +x/-x is being set, update cloaked host info.
|
||||||
self.checkCloakChange(target, parsedmodes)
|
self._check_cloak_change(target, parsedmodes)
|
||||||
|
|
||||||
return {'target': target, 'modes': parsedmodes}
|
return {'target': target, 'modes': parsedmodes}
|
||||||
|
|
||||||
@ -817,8 +817,8 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- :NickServ SVS2MODE 001SALZ01 +d GL
|
# <- :NickServ SVS2MODE 001SALZ01 +d GL
|
||||||
# <- :NickServ SVS2MODE 001SALZ01 +r
|
# <- :NickServ SVS2MODE 001SALZ01 +r
|
||||||
|
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
parsedmodes = self.irc.parseModes(target, args[1:])
|
parsedmodes = self.parse_modes(target, args[1:])
|
||||||
|
|
||||||
if ('+r', None) in parsedmodes:
|
if ('+r', None) in parsedmodes:
|
||||||
# Umode +r is being set (log in)
|
# Umode +r is being set (log in)
|
||||||
@ -828,19 +828,19 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
# If one doesn't exist, make it the same as the nick, but only if the account name
|
# If one doesn't exist, make it the same as the nick, but only if the account name
|
||||||
# wasn't set already.
|
# wasn't set already.
|
||||||
if not self.irc.users[target].services_account:
|
if not self.users[target].services_account:
|
||||||
account = self.irc.getFriendlyName(target)
|
account = self.get_friendly_name(target)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if account.isdigit():
|
if account.isdigit():
|
||||||
# If the +d argument is a number, ignore it and set the account name to the nick.
|
# If the +d argument is a number, ignore it and set the account name to the nick.
|
||||||
account = self.irc.getFriendlyName(target)
|
account = self.get_friendly_name(target)
|
||||||
|
|
||||||
elif ('-r', None) in parsedmodes:
|
elif ('-r', None) in parsedmodes:
|
||||||
# Umode -r being set.
|
# Umode -r being set.
|
||||||
|
|
||||||
if not self.irc.users[target].services_account:
|
if not self.users[target].services_account:
|
||||||
# User already has no account; ignore.
|
# User already has no account; ignore.
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -853,19 +853,19 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.call_hooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
def handle_umode2(self, numeric, command, args):
|
def handle_umode2(self, numeric, command, args):
|
||||||
"""Handles UMODE2, used to set user modes on oneself."""
|
"""Handles UMODE2, used to set user modes on oneself."""
|
||||||
# <- :GL UMODE2 +W
|
# <- :GL UMODE2 +W
|
||||||
parsedmodes = self.irc.parseModes(numeric, args)
|
parsedmodes = self.parse_modes(numeric, args)
|
||||||
self.irc.applyModes(numeric, parsedmodes)
|
self.apply_modes(numeric, parsedmodes)
|
||||||
|
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
# If +o being set, call the CLIENT_OPERED internal hook.
|
# If +o being set, call the CLIENT_OPERED internal hook.
|
||||||
self.irc.callHooks([numeric, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.call_hooks([numeric, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
self.checkCloakChange(numeric, parsedmodes)
|
self._check_cloak_change(numeric, parsedmodes)
|
||||||
|
|
||||||
return {'target': numeric, 'modes': parsedmodes}
|
return {'target': numeric, 'modes': parsedmodes}
|
||||||
|
|
||||||
@ -873,14 +873,14 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
"""Handles the TOPIC command."""
|
"""Handles the TOPIC command."""
|
||||||
# <- GL TOPIC #services GL 1444699395 :weeee
|
# <- GL TOPIC #services GL 1444699395 :weeee
|
||||||
# <- TOPIC #services devel.relay 1452399682 :test
|
# <- TOPIC #services devel.relay 1452399682 :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.to_lower(args[0])
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
setter = args[1]
|
setter = args[1]
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
|
|
||||||
oldtopic = self.irc.channels[channel].topic
|
oldtopic = self.channels[channel].topic
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
|
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic,
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic,
|
||||||
'oldtopic': oldtopic}
|
'oldtopic': oldtopic}
|
||||||
@ -888,66 +888,58 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
def handle_setident(self, numeric, command, args):
|
def handle_setident(self, numeric, command, args):
|
||||||
"""Handles SETIDENT, used for self ident changes."""
|
"""Handles SETIDENT, used for self ident changes."""
|
||||||
# <- :70MAAAAAB SETIDENT test
|
# <- :70MAAAAAB SETIDENT test
|
||||||
self.irc.users[numeric].ident = newident = args[0]
|
self.users[numeric].ident = newident = args[0]
|
||||||
return {'target': numeric, 'newident': newident}
|
return {'target': numeric, 'newident': newident}
|
||||||
|
|
||||||
def handle_sethost(self, numeric, command, args):
|
def handle_sethost(self, numeric, command, args):
|
||||||
"""Handles CHGHOST, used for self hostname changes."""
|
"""Handles CHGHOST, used for self hostname changes."""
|
||||||
# <- :70MAAAAAB SETIDENT some.host
|
# <- :70MAAAAAB SETIDENT some.host
|
||||||
self.irc.users[numeric].host = newhost = args[0]
|
self.users[numeric].host = newhost = args[0]
|
||||||
|
|
||||||
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
||||||
# target.
|
# target.
|
||||||
self.irc.applyModes(numeric, [('+x', None), ('+t', None)])
|
self.apply_modes(numeric, [('+x', None), ('+t', None)])
|
||||||
|
|
||||||
return {'target': numeric, 'newhost': newhost}
|
return {'target': numeric, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_setname(self, numeric, command, args):
|
def handle_setname(self, numeric, command, args):
|
||||||
"""Handles SETNAME, used for self real name/gecos changes."""
|
"""Handles SETNAME, used for self real name/gecos changes."""
|
||||||
# <- :70MAAAAAB SETNAME :afdsafasf
|
# <- :70MAAAAAB SETNAME :afdsafasf
|
||||||
self.irc.users[numeric].realname = newgecos = args[0]
|
self.users[numeric].realname = newgecos = args[0]
|
||||||
return {'target': numeric, 'newgecos': newgecos}
|
return {'target': numeric, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_chgident(self, numeric, command, args):
|
def handle_chgident(self, numeric, command, args):
|
||||||
"""Handles CHGIDENT, used for denoting ident changes."""
|
"""Handles CHGIDENT, used for denoting ident changes."""
|
||||||
# <- :GL CHGIDENT GL test
|
# <- :GL CHGIDENT GL test
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].ident = newident = args[1]
|
self.users[target].ident = newident = args[1]
|
||||||
return {'target': target, 'newident': newident}
|
return {'target': target, 'newident': newident}
|
||||||
|
|
||||||
def handle_chghost(self, numeric, command, args):
|
def handle_chghost(self, numeric, command, args):
|
||||||
"""Handles CHGHOST, used for denoting hostname changes."""
|
"""Handles CHGHOST, used for denoting hostname changes."""
|
||||||
# <- :GL CHGHOST GL some.host
|
# <- :GL CHGHOST GL some.host
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].host = newhost = args[1]
|
self.users[target].host = newhost = args[1]
|
||||||
|
|
||||||
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
||||||
# target.
|
# target.
|
||||||
self.irc.applyModes(target, [('+x', None), ('+t', None)])
|
self.apply_modes(target, [('+x', None), ('+t', None)])
|
||||||
|
|
||||||
return {'target': target, 'newhost': newhost}
|
return {'target': target, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_chgname(self, numeric, command, args):
|
def handle_chgname(self, numeric, command, args):
|
||||||
"""Handles CHGNAME, used for denoting real name/gecos changes."""
|
"""Handles CHGNAME, used for denoting real name/gecos changes."""
|
||||||
# <- :GL CHGNAME GL :afdsafasf
|
# <- :GL CHGNAME GL :afdsafasf
|
||||||
target = self._getUid(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].realname = newgecos = args[1]
|
self.users[target].realname = newgecos = args[1]
|
||||||
return {'target': target, 'newgecos': newgecos}
|
return {'target': target, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_invite(self, numeric, command, args):
|
|
||||||
"""Handles incoming INVITEs."""
|
|
||||||
# <- :GL INVITE PyLink-devel :#a
|
|
||||||
target = self._getUid(args[0])
|
|
||||||
channel = self.irc.toLower(args[1])
|
|
||||||
# We don't actually need to process this; it's just something plugins/hooks can use
|
|
||||||
return {'target': target, 'channel': channel}
|
|
||||||
|
|
||||||
def handle_kill(self, numeric, command, args):
|
def handle_kill(self, numeric, command, args):
|
||||||
"""Handles incoming KILLs."""
|
"""Handles incoming KILLs."""
|
||||||
# <- :GL| KILL GLolol :hidden-1C620195!GL| (test)
|
# <- :GL| KILL GLolol :hidden-1C620195!GL| (test)
|
||||||
# Use ts6_common's handle_kill, but coerse UIDs to nicks first.
|
# Use ts6_common's handle_kill, but coerse UIDs to nicks first.
|
||||||
|
|
||||||
new_args = [self._getUid(args[0])]
|
new_args = [self._get_UID(args[0])]
|
||||||
new_args.extend(args[1:])
|
new_args.extend(args[1:])
|
||||||
|
|
||||||
return super().handle_kill(numeric, command, new_args)
|
return super().handle_kill(numeric, command, new_args)
|
||||||
@ -958,6 +950,6 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
if args[0] == 'alltime':
|
if args[0] == 'alltime':
|
||||||
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
||||||
self._send(self.irc.sid, 'NOTICE %s :*** Server=%s time()=%d' % (source, self.irc.hostname(), time.time()))
|
self._send_with_prefix(self.sid, 'NOTICE %s :*** Server=%s time()=%d' % (source, self.hostname(), time.time()))
|
||||||
|
|
||||||
Class = UnrealProtocol
|
Class = UnrealProtocol
|
||||||
|
8
pylink
8
pylink
@ -83,9 +83,13 @@ if __name__ == '__main__':
|
|||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
log.error("(%s) Configuration error: No protocol module specified, aborting.", network)
|
log.error("(%s) Configuration error: No protocol module specified, aborting.", network)
|
||||||
else:
|
else:
|
||||||
# Fetch the correct protocol module
|
# Fetch the correct protocol module.
|
||||||
proto = utils.getProtocolModule(protoname)
|
proto = utils.getProtocolModule(protoname)
|
||||||
world.networkobjects[network] = irc = classes.Irc(network, proto, conf.conf)
|
|
||||||
|
# Create and connect the network.
|
||||||
|
world.networkobjects[network] = irc = proto.Class(network)
|
||||||
|
log.debug('Connecting to network %r', network)
|
||||||
|
irc.connect()
|
||||||
|
|
||||||
world.started.set()
|
world.started.set()
|
||||||
log.info("Loaded plugins: %s", ', '.join(sorted(world.plugins.keys())))
|
log.info("Loaded plugins: %s", ', '.join(sorted(world.plugins.keys())))
|
||||||
|
57
utils.py
57
utils.py
@ -129,25 +129,6 @@ def isHostmask(text):
|
|||||||
# Band-aid patch here to prevent bad bans set by Janus forwarding people into invalid channels.
|
# Band-aid patch here to prevent bad bans set by Janus forwarding people into invalid channels.
|
||||||
return hostmaskRe.match(text) and '#' not in text
|
return hostmaskRe.match(text) and '#' not in text
|
||||||
|
|
||||||
def parseModes(irc, target, args):
|
|
||||||
"""Parses a modestring list into a list of (mode, argument) tuples.
|
|
||||||
['+mitl-o', '3', 'person'] => [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')]
|
|
||||||
|
|
||||||
This method is deprecated. Use irc.parseModes() instead.
|
|
||||||
"""
|
|
||||||
log.warning("(%s) utils.parseModes is deprecated. Use irc.parseModes() instead!", irc.name)
|
|
||||||
return irc.parseModes(target, args)
|
|
||||||
|
|
||||||
def applyModes(irc, target, changedmodes):
|
|
||||||
"""Takes a list of parsed IRC modes, and applies them on the given target.
|
|
||||||
|
|
||||||
The target can be either a channel or a user; this is handled automatically.
|
|
||||||
|
|
||||||
This method is deprecated. Use irc.applyModes() instead.
|
|
||||||
"""
|
|
||||||
log.warning("(%s) utils.applyModes is deprecated. Use irc.applyModes() instead!", irc.name)
|
|
||||||
return irc.applyModes(target, changedmodes)
|
|
||||||
|
|
||||||
def expandpath(path):
|
def expandpath(path):
|
||||||
"""
|
"""
|
||||||
Returns a path expanded with environment variables and home folders (~) expanded, in that order."""
|
Returns a path expanded with environment variables and home folders (~) expanded, in that order."""
|
||||||
@ -250,7 +231,7 @@ class ServiceBot():
|
|||||||
# which is handled by coreplugin.
|
# which is handled by coreplugin.
|
||||||
if irc is None:
|
if irc is None:
|
||||||
for irc in world.networkobjects.values():
|
for irc in world.networkobjects.values():
|
||||||
irc.callHooks([None, 'PYLINK_NEW_SERVICE', {'name': self.name}])
|
irc.call_hooks([None, 'PYLINK_NEW_SERVICE', {'name': self.name}])
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Network specific plugins not supported yet.")
|
raise NotImplementedError("Network specific plugins not supported yet.")
|
||||||
|
|
||||||
@ -301,7 +282,7 @@ class ServiceBot():
|
|||||||
else:
|
else:
|
||||||
irc.proto.join(u, chan)
|
irc.proto.join(u, chan)
|
||||||
|
|
||||||
irc.callHooks([irc.sid, 'PYLINK_SERVICE_JOIN', {'channel': chan, 'users': [u]}])
|
irc.call_hooks([irc.sid, 'PYLINK_SERVICE_JOIN', {'channel': chan, 'users': [u]}])
|
||||||
else:
|
else:
|
||||||
log.warning('(%s) Ignoring invalid autojoin channel %r.', irc.name, chan)
|
log.warning('(%s) Ignoring invalid autojoin channel %r.', irc.name, chan)
|
||||||
|
|
||||||
@ -343,10 +324,10 @@ class ServiceBot():
|
|||||||
if cmd and show_unknown_cmds and not cmd.startswith('\x01'):
|
if cmd and show_unknown_cmds and not cmd.startswith('\x01'):
|
||||||
# Ignore empty commands and invalid command errors from CTCPs.
|
# Ignore empty commands and invalid command errors from CTCPs.
|
||||||
self.reply(irc, 'Error: Unknown command %r.' % cmd)
|
self.reply(irc, 'Error: Unknown command %r.' % cmd)
|
||||||
log.info('(%s/%s) Received unknown command %r from %s', irc.name, self.name, cmd, irc.getHostmask(source))
|
log.info('(%s/%s) Received unknown command %r from %s', irc.name, self.name, cmd, irc.get_hostmask(source))
|
||||||
return
|
return
|
||||||
|
|
||||||
log.info('(%s/%s) Calling command %r for %s', irc.name, self.name, cmd, irc.getHostmask(source))
|
log.info('(%s/%s) Calling command %r for %s', irc.name, self.name, cmd, irc.get_hostmask(source))
|
||||||
for func in self.commands[cmd]:
|
for func in self.commands[cmd]:
|
||||||
try:
|
try:
|
||||||
func(irc, source, cmd_args)
|
func(irc, source, cmd_args)
|
||||||
@ -601,8 +582,36 @@ class DeprecatedAttributesObject():
|
|||||||
def __getattribute__(self, attr):
|
def __getattribute__(self, attr):
|
||||||
# Note: "self.deprecated_attributes" calls this too, so the != check is
|
# Note: "self.deprecated_attributes" calls this too, so the != check is
|
||||||
# needed to prevent a recursive loop!
|
# needed to prevent a recursive loop!
|
||||||
if attr != 'deprecated_attributes' and attr in self.deprecated_attributes:
|
# Also ignore reserved names beginning with "__".
|
||||||
|
if attr != 'deprecated_attributes' and not attr.startswith('__') and attr in self.deprecated_attributes:
|
||||||
log.warning('Attribute %s.%s is deprecated: %s' % (self.__class__.__name__, attr,
|
log.warning('Attribute %s.%s is deprecated: %s' % (self.__class__.__name__, attr,
|
||||||
self.deprecated_attributes.get(attr)))
|
self.deprecated_attributes.get(attr)))
|
||||||
|
|
||||||
return object.__getattribute__(self, attr)
|
return object.__getattribute__(self, attr)
|
||||||
|
|
||||||
|
class CamelCaseToSnakeCase():
|
||||||
|
"""
|
||||||
|
Class which automatically converts missing attributes from camel case to snake case.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
"""
|
||||||
|
Attribute fetching fallback function which normalizes camel case attributes to snake case.
|
||||||
|
"""
|
||||||
|
assert isinstance(attr, str), "Requested attribute %r is not a string!" % attr
|
||||||
|
|
||||||
|
normalized_attr = '' # Start off with the first letter, which is ignored when processing
|
||||||
|
for char in attr:
|
||||||
|
if char in string.ascii_uppercase:
|
||||||
|
char = '_' + char.lower()
|
||||||
|
normalized_attr += char
|
||||||
|
|
||||||
|
classname = self.__class__.__name__
|
||||||
|
if normalized_attr == attr:
|
||||||
|
# __getattr__ only fires if normal attribute fetching fails, so we can assume that
|
||||||
|
# the attribute was tried already and failed.
|
||||||
|
raise AttributeError('%s object has no attribute with normalized name %r' % (classname, attr))
|
||||||
|
|
||||||
|
target = getattr(self, normalized_attr)
|
||||||
|
log.warning('%s.%s is deprecated, considering migrating to %s.%s!', classname, attr, classname, normalized_attr)
|
||||||
|
return target
|
||||||
|
Loading…
Reference in New Issue
Block a user