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

protocols: move 005 handling code to IRCCommonProtocol

Also enable extended server negotiation for ngIRCd, which really just passes 005 between servers (nifty!)
This commit is contained in:
James Lu 2017-07-03 12:42:51 -07:00
parent e9d7ac39ea
commit ec308acfcb
3 changed files with 73 additions and 39 deletions

View File

@ -8,7 +8,6 @@ from pylinkirc.protocols.ircs2s_common import *
from pylinkirc.classes import * from pylinkirc.classes import *
FALLBACK_REALNAME = 'PyLink Relay Mirror Client' FALLBACK_REALNAME = 'PyLink Relay Mirror Client'
COMMON_PREFIXMODES = [('h', 'halfop'), ('a', 'admin'), ('q', 'owner'), ('y', 'owner')]
IRCV3_CAPABILITIES = {'multi-prefix', 'sasl'} IRCV3_CAPABILITIES = {'multi-prefix', 'sasl'}
class ClientbotWrapperProtocol(IRCCommonProtocol): class ClientbotWrapperProtocol(IRCCommonProtocol):
@ -25,7 +24,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
# This is just a fallback. Actual casemapping is fetched by handle_005() # This is just a fallback. Actual casemapping is fetched by handle_005()
self.casemapping = 'ascii' self.casemapping = 'ascii'
self.caps = {} self._caps = {}
self.ircv3_caps = set() self.ircv3_caps = set()
self.ircv3_caps_available = {} self.ircv3_caps_available = {}
@ -42,9 +41,12 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
# are essentially all fatal errors for connections. # are essentially all fatal errors for connections.
self.handle_463 = self.handle_464 = self.handle_465 = self.handle_error self.handle_463 = self.handle_464 = self.handle_465 = self.handle_error
self._use_builtin_005_handling = True
def post_connect(self): def post_connect(self):
"""Initializes a connection to a server.""" """Initializes a connection to a server."""
# (Re)initialize counter-based pseudo UID generators # (Re)initialize counter-based pseudo UID generators
super().post_connect()
self.uidgen = utils.PUIDGenerator('PUID') self.uidgen = utils.PUIDGenerator('PUID')
self.sidgen = utils.PUIDGenerator('ClientbotInternalSID') self.sidgen = utils.PUIDGenerator('ClientbotInternalSID')
@ -58,7 +60,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
# Clear states from last connect # Clear states from last connect
self.who_received.clear() self.who_received.clear()
self.kick_queue.clear() self.kick_queue.clear()
self.caps.clear() self._caps.clear()
self.ircv3_caps.clear() self.ircv3_caps.clear()
self.ircv3_caps_available.clear() self.ircv3_caps_available.clear()
@ -576,40 +578,6 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
# enumerate our uplink # enumerate our uplink
self.uplink = source self.uplink = source
def handle_005(self, source, command, args):
"""
Handles 005 / RPL_ISUPPORT.
"""
self.caps.update(self.parse_isupport(args[1:-1]))
log.debug('(%s) handle_005: self.caps is %s', self.name, self.caps)
if 'CHANMODES' in self.caps:
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = \
self.caps['CHANMODES'].split(',')
log.debug('(%s) handle_005: cmodes: %s', self.name, self.cmodes)
if 'USERMODES' in self.caps:
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] = \
self.caps['USERMODES'].split(',')
log.debug('(%s) handle_005: umodes: %s', self.name, self.umodes)
self.casemapping = self.caps.get('CASEMAPPING', self.casemapping)
log.debug('(%s) handle_005: casemapping set to %s', self.name, self.casemapping)
if 'PREFIX' in self.caps:
self.prefixmodes = prefixmodes = self.parse_isupport_prefixes(self.caps['PREFIX'])
log.debug('(%s) handle_005: prefix modes set to %s', self.name, self.prefixmodes)
# Autodetect common prefix mode names.
for char, modename in COMMON_PREFIXMODES:
# Don't overwrite existing named mode definitions.
if char in self.prefixmodes and modename not in self.cmodes:
self.cmodes[modename] = char
log.debug('(%s) handle_005: autodetecting mode %s (%s) as %s', self.name,
char, self.prefixmodes[char], modename)
self.connected.set()
def handle_376(self, source, command, args): def handle_376(self, source, command, args):
""" """
Handles end of MOTD numerics, used to start things like autoperform. Handles end of MOTD numerics, used to start things like autoperform.
@ -623,6 +591,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
if not self.has_eob: if not self.has_eob:
self.has_eob = True self.has_eob = True
return {'parse_as': 'ENDBURST'} return {'parse_as': 'ENDBURST'}
self.connected.set()
handle_422 = handle_376 handle_422 = handle_376
def handle_353(self, source, command, args): def handle_353(self, source, command, args):

View File

@ -11,6 +11,18 @@ from pylinkirc.log import log
from pylinkirc import utils from pylinkirc import utils
class IRCCommonProtocol(IRCNetwork): class IRCCommonProtocol(IRCNetwork):
COMMON_PREFIXMODES = [('h', 'halfop'), ('a', 'admin'), ('q', 'owner'), ('y', 'owner')]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._caps = {}
self._use_builtin_005_handling = False # Disabled by default for greater security
def post_connect(self):
self._caps.clear()
def validate_server_conf(self): def validate_server_conf(self):
"""Validates that the server block given contains the required keys.""" """Validates that the server block given contains the required keys."""
for k in self.conf_keys: for k in self.conf_keys:
@ -163,6 +175,54 @@ class IRCCommonProtocol(IRCNetwork):
"""Handles ERROR messages - these mean that our uplink has disconnected us!""" """Handles ERROR messages - these mean that our uplink has disconnected us!"""
raise ProtocolError('Received an ERROR, disconnecting!') raise ProtocolError('Received an ERROR, disconnecting!')
def handle_005(self, source, command, args):
"""
Handles 005 / RPL_ISUPPORT. This is used by at least Clientbot and ngIRCd (for server negotiation).
"""
# ngIRCd:
# <- :ngircd.midnight.local 005 pylink-devel.int NETWORK=ngircd-test :is my network name
# <- :ngircd.midnight.local 005 pylink-devel.int RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(qaohv)~&@%+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPQRstVz CHANLIMIT=#&+:10 :are supported on this server
# <- :ngircd.midnight.local 005 pylink-devel.int CHANNELLEN=50 NICKLEN=21 TOPICLEN=490 AWAYLEN=127 KICKLEN=400 MODES=5 MAXLIST=beI:50 EXCEPTS=e INVEX=I PENALTY :are supported on this server
# Regular clientbot, connecting to InspIRCd:
# <- :millennium.overdrivenetworks.com 005 ice AWAYLEN=200 CALLERID=g CASEMAPPING=rfc1459 CHANMODES=IXbegw,k,FJLfjl,ACKMNOPQRSTUcimnprstz CHANNELLEN=64 CHANTYPES=# CHARSET=ascii ELIST=MU ESILENCE EXCEPTS=e EXTBAN=,ACNOQRSTUcmprsuz FNC INVEX=I :are supported by this server
# <- :millennium.overdrivenetworks.com 005 ice KICKLEN=255 MAP MAXBANS=60 MAXCHANNELS=30 MAXPARA=32 MAXTARGETS=20 MODES=20 NAMESX NETWORK=OVERdrive-IRC NICKLEN=21 OVERRIDE PREFIX=(Yqaohv)*~&@%+ SILENCE=32 :are supported by this server
# <- :millennium.overdrivenetworks.com 005 ice SSL=[::]:6697 STARTTLS STATUSMSG=*~&@%+ TOPICLEN=307 UHNAMES USERIP VBANLIST WALLCHOPS WALLVOICES WATCH=32 :are supported by this server
if not self._use_builtin_005_handling:
log.warning("(%s) Got spurious 005 message from %s: %r", self.name, source, args)
return
self._caps.update(self.parse_isupport(args[1:-1]))
log.debug('(%s) handle_005: self._caps is %s', self.name, self._caps)
if 'CHANMODES' in self._caps:
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = \
self._caps['CHANMODES'].split(',')
log.debug('(%s) handle_005: cmodes: %s', self.name, self.cmodes)
if 'USERMODES' in self._caps:
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] = \
self._caps['USERMODES'].split(',')
log.debug('(%s) handle_005: umodes: %s', self.name, self.umodes)
self.casemapping = self._caps.get('CASEMAPPING', self.casemapping)
log.debug('(%s) handle_005: casemapping set to %s', self.name, self.casemapping)
if 'PREFIX' in self._caps:
self.prefixmodes = prefixmodes = self.parse_isupport_prefixes(self._caps['PREFIX'])
log.debug('(%s) handle_005: prefix modes set to %s', self.name, self.prefixmodes)
# Autodetect common prefix mode names.
for char, modename in self.COMMON_PREFIXMODES:
# Don't overwrite existing named mode definitions.
if char in self.prefixmodes and modename not in self.cmodes:
self.cmodes[modename] = char
log.debug('(%s) handle_005: autodetecting mode %s (%s) as %s', self.name,
char, self.prefixmodes[char], modename)
self.connected.set()
def _send_with_prefix(self, source, msg, **kwargs): def _send_with_prefix(self, source, msg, **kwargs):
"""Sends a RFC 459-style raw command from the given sender.""" """Sends a RFC 459-style raw command from the given sender."""
self.send(':%s %s' % (self._expandPUID(source), msg), **kwargs) self.send(':%s %s' % (self._expandPUID(source), msg), **kwargs)

View File

@ -22,21 +22,25 @@ class NgIRCdProtocol(IRCS2SProtocol):
super().__init__(irc) super().__init__(irc)
self.conf_keys -= {'sid', 'sidrange'} self.conf_keys -= {'sid', 'sidrange'}
self.casemapping = 'rfc1459' self.casemapping = 'ascii' # This is the default; it's actually set on server negotiation
# Track whether we've received end-of-burst from the uplink. # Track whether we've received end-of-burst from the uplink.
self.has_eob = False self.has_eob = False
self.uidgen = utils.PUIDGenerator("PUID") self.uidgen = utils.PUIDGenerator("PUID")
self._caps = {}
self._use_builtin_005_handling = True
### Commands ### Commands
def post_connect(self): def post_connect(self):
self.send('PASS %s 0210-IRC+ PyLink|%s:LMoX' % (self.serverdata['sendpass'], __version__)) self.send('PASS %s 0210-IRC+ PyLink|%s:HLMoX' % (self.serverdata['sendpass'], __version__))
self.send("SERVER %s 1 :%s" % (self.serverdata['hostname'], self.send("SERVER %s 1 :%s" % (self.serverdata['hostname'],
self.serverdata.get('serverdesc') or conf.conf['pylink']['serverdesc'])); self.serverdata.get('serverdesc') or conf.conf['pylink']['serverdesc']));
self.sid = self.serverdata['hostname'] self.sid = self.serverdata['hostname']
self._caps.clear()
def spawn_client(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):