3
0
mirror of https://github.com/jlu5/PyLink.git synced 2025-01-11 20:52:42 +01:00

Merge branch 'wip/protocol-caps' into devel

This commit is contained in:
James Lu 2017-03-23 23:54:46 -07:00
commit c894beed2e
11 changed files with 40 additions and 23 deletions

View File

@ -1409,6 +1409,12 @@ class Protocol():
return real_args
def hasCap(self, capab):
"""
Returns whether this protocol module instance has the requested capability.
"""
return capab.lower() in self.protocol_caps
def removeClient(self, numeric):
"""Internal function to remove a client from our internal state."""
for c, v in self.irc.channels.copy().items():

View File

@ -14,6 +14,11 @@ def spawn_service(irc, source, command, args):
# Service name
name = args['name']
if name != 'pylink' and not irc.proto.hasCap('can-spawn-clients'):
log.debug("(%s) Not spawning service %s because the server doesn't support spawning clients",
irc.name, name)
return
# Get the ServiceBot object.
sbot = world.services[name]
@ -26,10 +31,6 @@ def spawn_service(irc, source, command, args):
nick = irc.serverdata.get("%s_nick" % name) or conf.conf.get(name, {}).get('nick') or sbot.nick or name
ident = irc.serverdata.get("%s_ident" % name) or conf.conf.get(name, {}).get('ident') or sbot.ident or name
if name != 'pylink' and irc.protoname == 'clientbot':
# Prefix service bots spawned on Clientbot to prevent possible nick collisions.
nick = 'PyLinkService@' + nick
# TODO: make this configurable?
host = irc.hostname()

View File

@ -108,7 +108,7 @@ def showchan(irc, source, args):
if c.topic:
f('\x02Channel topic\x02: %s' % c.topic)
if irc.protoname != 'clientbot':
if irc.proto.hasCap('has-ts'):
# Clientbot-specific hack: don't show channel TS because it's not properly tracked.
f('\x02Channel creation time\x02: %s (%s)' % (ctime(c.ts), c.ts))

View File

@ -112,13 +112,12 @@ def normalize_nick(irc, netname, nick, times_tagged=0, uid=''):
log.debug('(%s) relay.normalize_nick: using %r as separator.', irc.name, separator)
orig_nick = nick
protoname = irc.protoname
maxnicklen = irc.maxnicklen
# 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
# cause bad desyncs.
protocol_allows_slashes = protoname.startswith(('insp', 'unreal', 'clientbot')) or \
protocol_allows_slashes = irc.proto.hasCap('slash-in-nicks') or \
irc.serverdata.get('relay_force_slashes')
if '/' not in separator or not protocol_allows_slashes:
@ -177,12 +176,12 @@ def normalize_host(irc, host):
log.debug('(%s) relay.normalize_host: IRCd=%s, host=%s', irc.name, irc.protoname, host)
allowed_chars = string.ascii_letters + string.digits + '-.:'
if irc.protoname in ('inspircd', 'ts6', 'clientbot', 'nefarious'):
if irc.proto.hasCap('slash-in-hosts'):
# UnrealIRCd and IRCd-Hybrid don't allow slashes in hostnames
allowed_chars += '/'
if irc.protoname in ('inspircd', 'clientbot', 'nefarious', 'unreal'):
# The above IRCds allow _ in hostnames, while TS6-like IRCds do not.
if irc.proto.hasCap('underscore-in-hosts'):
# Most IRCds allow _ in hostnames, but hybrid/charybdis/ratbox IRCds do not.
allowed_chars += '_'
for char in host:
@ -648,7 +647,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
# the different sides of the relay are merged properly.
if irc.protoname == 'clientbot':
if not irc.proto.hasCap('has-ts'):
# Special hack for clientbot: just use the remote's modes so mode changes
# take precendence. protocols/clientbot does not track channel TS.
ts = remoteirc.channels[remotechan].ts
@ -783,7 +782,7 @@ def get_supported_cmodes(irc, remoteirc, channel, modes):
"for network %r.",
irc.name, modechar, arg, remoteirc.name)
if irc.protoname == 'clientbot' and irc.pseudoclient and arg == irc.pseudoclient.uid:
if (not irc.proto.hasCap('can-spawn-clients')) and irc.pseudoclient and arg == irc.pseudoclient.uid:
# Skip modesync on the main PyLink client.
log.debug("(%s) relay.get_supported_cmodes: filtering prefix change (%r, %r) on Clientbot relayer",
irc.name, name, arg)
@ -876,7 +875,7 @@ def handle_relay_whois(irc, source, command, args):
# 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))
if checkSendKey('whois_show_server') and realirc.protoname != 'clientbot':
if checkSendKey('whois_show_server') and realirc.proto.hasCap('can-track-servers'):
wreply(320, ":is actually connected via the following server:")
realserver = realirc.getServer(uid)
realserver = realirc.servers[realserver]
@ -1017,7 +1016,7 @@ def handle_part(irc, numeric, command, args):
if numeric == irc.pseudoclient.uid:
# For clientbot: treat forced parts to the bot as clearchan, and attempt to rejoin only
# if it affected a relay.
if irc.protoname == 'clientbot':
if not irc.proto.hasCap('can-spawn-clients'):
for channel in [c for c in channels if get_relay((irc.name, c))]:
for user in irc.channels[channel].users:
if (not irc.isInternalClient(user)) and (not isRelayClient(irc, user)):
@ -1087,8 +1086,9 @@ def handle_messages(irc, numeric, command, args):
# No relay clone exists for the sender; route the message through our
# main client (or SID for notices).
# Skip "from:" formatting for servers; it's messy with longer hostnames
if numeric not in irc.servers:
# Skip "from:" formatting for servers; it's messy with longer hostnames.
# Also skip this formatting for servicebot relaying.
if numeric not in irc.servers and not irc.getServiceBot(numeric):
displayedname = irc.getFriendlyName(numeric)
real_text = '<%s/%s> %s' % (displayedname, irc.name, text)
else:
@ -1148,7 +1148,7 @@ def handle_messages(irc, numeric, command, args):
return
remoteirc = world.networkobjects[homenet]
if remoteirc.protoname == 'clientbot' and not conf.conf.get('relay', {}).get('allow_clientbot_pms'):
if (not remoteirc.proto.hasCap('can-spawn-clients')) and not conf.conf.get('relay', {}).get('allow_clientbot_pms'):
irc.msg(numeric, 'Private messages to users connected via Clientbot have '
'been administratively disabled.', notice=True)
return
@ -1179,7 +1179,7 @@ def handle_kick(irc, source, command, args):
relay = get_relay((irc.name, channel))
# Special case for clientbot: treat kicks to the PyLink service bot as channel clear.
if irc.protoname == 'clientbot' and irc.pseudoclient and target == irc.pseudoclient.uid:
if (not irc.proto.hasCap('can-spawn-clients')) and irc.pseudoclient and target == irc.pseudoclient.uid:
for user in irc.channels[channel].users:
if (not irc.isInternalClient(user)) and (not isRelayClient(irc, user)):
reason = "Clientbot kicked by %s (Reason: %s)" % (irc.getFriendlyName(source), text)
@ -1246,7 +1246,7 @@ def handle_kick(irc, source, command, args):
# common channels with the target relay network.
rsid = get_remote_sid(remoteirc, irc)
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan, rsid, kicker, irc.name)
if irc.protoname == 'clientbot':
if not irc.proto.hasCap('can-spawn-clients'):
# Special case for clientbot: no kick prefixes are needed.
text = args['text']
else:
@ -1582,7 +1582,7 @@ def create(irc, source, args):
if not utils.isChannel(channel):
irc.error('Invalid channel %r.' % channel)
return
if irc.protoname == 'clientbot':
if not irc.proto.hasCap('can-host-relay'):
irc.error('Clientbot networks cannot be used to host a relay.')
return
if source not in irc.channels[channel].users:
@ -1780,7 +1780,7 @@ def link(irc, source, args):
our_ts = irc.channels[localchan].ts
their_ts = world.networkobjects[remotenet].channels[args.channel].ts
if (our_ts < their_ts) and irc.protoname != 'clientbot':
if (our_ts < their_ts) and irc.proto.hasCap('has-ts'):
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.error("The channel creation date (TS) on %s (%s) is lower than the target "

View File

@ -204,7 +204,7 @@ def rpm(irc, source, args):
return
relay = world.plugins.get('relay')
if irc.protoname != 'clientbot':
if irc.proto.hasCap('can-spawn-clients'):
irc.error('This command is only supported on Clientbot networks. Try /msg %s <text>' % target)
return
elif relay is None:

View File

@ -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
relay_server = serverlist[leaf].remote
remoteirc = world.networkobjects[relay_server]
if remoteirc.protoname != 'clientbot':
if remoteirc.proto.hasCap('can-track-servers'):
# Only ever show relay subservers once - this prevents infinite loops.
showall(remoteirc, remoteirc.sid, hops=hops, is_relay_server=True)

View File

@ -14,6 +14,8 @@ class ClientbotWrapperProtocol(Protocol):
def __init__(self, irc):
super().__init__(irc)
self.protocol_caps = {'clear-channels-on-leave', 'slash-in-nicks', 'slash-in-hosts', 'underscore-in-hosts'}
self.has_eob = False
# Remove conf key checks for those not needed for Clientbot.

View File

@ -13,6 +13,9 @@ from pylinkirc.protocols.ts6_common import *
class InspIRCdProtocol(TS6BaseProtocol):
def __init__(self, irc):
super().__init__(irc)
self.protocol_caps |= {'slash-in-nicks', 'slash-in-hosts', 'underscore-in-hosts'}
# Set our case mapping (rfc1459 maps "\" and "|" together, for example).
self.casemapping = 'rfc1459'

View File

@ -35,6 +35,7 @@ def p10b64encode(num, length=2):
class P10SIDGenerator():
def __init__(self, irc):
self.irc = irc
try:
query = irc.serverdata["sidrange"]
except (KeyError, ValueError):
@ -74,6 +75,8 @@ class P10Protocol(IRCS2SProtocol):
self.hook_map = {'END_OF_BURST': 'ENDBURST', 'OPMODE': 'MODE', 'CLEARMODE': 'MODE', 'BURST': 'JOIN'}
self.protocol_caps |= {'slash-in-hosts', 'underscore-in-hosts'}
def _send(self, source, text):
self.irc.send("%s %s" % (source, text))

View File

@ -15,6 +15,7 @@ S2S_BUFSIZE = 510
class TS6Protocol(TS6BaseProtocol):
def __init__(self, irc):
super().__init__(irc)
self.protocol_caps |= {'slash-in-hosts'}
self.casemapping = 'rfc1459'
self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE',
'EUID': 'UID', 'RSFNC': 'SVSNICK', 'ETB': 'TOPIC'}

View File

@ -23,6 +23,7 @@ S2S_BUFSIZE = 427
class UnrealProtocol(TS6BaseProtocol):
def __init__(self, irc):
super().__init__(irc)
self.protocol_caps |= {'slash-in-nicks', 'underscore-in-hosts'}
# Set our case mapping (rfc1459 maps "\" and "|" together, for example)
self.casemapping = 'ascii'
self.proto_ver = 4000