diff --git a/protocols/ircs2s_common.py b/protocols/ircs2s_common.py index 16edb50..d50c959 100644 --- a/protocols/ircs2s_common.py +++ b/protocols/ircs2s_common.py @@ -6,14 +6,103 @@ import time from pylinkirc.classes import Protocol from pylinkirc.log import log +from pylinkirc import utils class IRCS2SProtocol(Protocol): + COMMAND_TOKENS = {} def __init__(self, irc): super().__init__(irc) self.protocol_caps = {'can-spawn-clients', 'has-ts', 'can-host-relay', 'can-track-servers'} + def handle_events(self, data): + """Event handler for RFC1459-like protocols. + + This passes most commands to the various handle_ABCD() functions + elsewhere defined protocol modules, coersing various sender prefixes + from nicks and server names to UIDs and SIDs respectively, + whenever possible. + + Commands sent without an explicit sender prefix will have them set to + the SID of the uplink server. + """ + data = data.split(" ") + args = self.parseArgs(data) + + sender = args[0] + sender = sender.lstrip(':') + + # If the sender isn't in numeric format, try to convert it automatically. + sender_sid = self._getSid(sender) + sender_uid = self._getUid(sender) + + if sender_sid in self.irc.servers: + # Sender is a server (converting from name to SID gave a valid result). + sender = sender_sid + elif sender_uid in self.irc.users: + # Sender is a user (converting from name to UID gave a valid result). + sender = sender_uid + else: + # No sender prefix; treat as coming from uplink IRCd. + sender = self.irc.uplink + args.insert(0, sender) + + if self.irc.isInternalClient(sender) or self.irc.isInternalServer(sender): + log.warning("(%s) Received command %s being routed the wrong way!", self.irc.name, command) + return + + raw_command = args[1].upper() + args = args[2:] + + log.debug('(%s) Found message sender as %s', self.irc.name, sender) + + # For P10, convert the command token into a regular command, if present. + command = self.COMMAND_TOKENS.get(raw_command, raw_command) + if command != raw_command: + log.debug('(%s) Translating token %s to command %s', self.irc.name, raw_command, command) + + if command == 'ENCAP': + # Special case for TS6 encapsulated commands (ENCAP), in forms like this: + # <- :00A ENCAP * SU 42XAAAAAC :GLolol + command = args[1] + args = args[2:] + log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.irc.name, command, args) + + try: + func = getattr(self, 'handle_'+command.lower()) + except AttributeError: # Unhandled command + pass + else: + parsed_args = func(sender, command, args) + if parsed_args is not None: + 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 = 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. + stripped_target = target.lstrip(''.join(self.irc.prefixmodes.values())) + if utils.isChannel(stripped_target): + target = self.irc.toLower(target) + + return {'target': target, 'text': args[1]} + + handle_notice = handle_privmsg + def check_nick_collision(self, nick): """ Nick collision checker. diff --git a/protocols/p10.py b/protocols/p10.py index b9139c4..b622bd6 100644 --- a/protocols/p10.py +++ b/protocols/p10.py @@ -824,63 +824,6 @@ class P10Protocol(IRCS2SProtocol): self._send(sid, "EB") self.irc.connected.set() - def handle_events(self, data): - """ - Event handler for the P10 protocol. - - This passes most commands to the various handle_ABCD() functions defined elsewhere in the - protocol modules, coersing various sender prefixes from nicks and server names to P10 - "numeric nicks", whenever possible. - - Commands sent without an explicit sender prefix are treated as originating from the uplink - server. - """ - data = data.split(" ") - args = self.parseArgs(data) - - sender = args[0] - if sender.startswith(':'): - # From https://github.com/evilnet/nefarious2/blob/a29b63144/doc/p10.txt#L140: - # if source begins with a colon, it (except for the colon) is the name. otherwise, it is - # a numeric. a P10 implementation must only send lines with a numeric source prefix. - sender = sender[1:] - - # If the sender isn't in numeric format, try to convert it automatically. - sender_sid = self._getSid(sender) - sender_uid = self._getUid(sender) - if sender_sid in self.irc.servers: - # Sender is a server (converting from name to SID gave a valid result). - sender = sender_sid - elif sender_uid in self.irc.users: - # Sender is a user (converting from name to UID gave a valid result). - sender = sender_uid - else: - # No sender prefix; treat as coming from uplink IRCd. - sender = self.irc.uplink - args.insert(0, sender) - - command_token = args[1].upper() - args = args[2:] - - log.debug('(%s) Found message sender as %s', self.irc.name, sender) - # Convert the token given into a regular command, if present. - command = self.COMMAND_TOKENS.get(command_token, command_token) - log.debug('(%s) Translating token %s to command %s', self.irc.name, command_token, command) - - try: - func = getattr(self, 'handle_'+command.lower()) - except AttributeError: # Unhandled command, ignore - return - - else: # Send a hook with the hook arguments given by the handler function. - if self.irc.isInternalClient(sender) or self.irc.isInternalServer(sender): - log.warning("(%s) Received command %s being routed the wrong way!", self.irc.name, command) - return - - parsed_args = func(sender, command, args) - if parsed_args is not None: - return [sender, command, parsed_args] - def handle_server(self, source, command, args): """Handles incoming server introductions.""" # <- SERVER nefarious.midnight.vpn 1 1460673022 1460673239 J10 ABP]] +h6 :Nefarious2 test server @@ -1168,22 +1111,6 @@ class P10Protocol(IRCS2SProtocol): self.irc.channels[channel].modes, 'ts': ts or int(time.time())} handle_create = handle_join - - def handle_privmsg(self, source, command, args): - """Handles incoming PRIVMSG/NOTICE.""" - # <- ABAAA P AyAAA :privmsg text - # <- ABAAA O AyAAA :notice text - target = args[0] - - # We use lower case channels internally, but mixed case UIDs. - stripped_target = target.lstrip(''.join(self.irc.prefixmodes.values())) - if utils.isChannel(stripped_target): - target = self.irc.toLower(target) - - return {'target': target, 'text': args[1]} - - handle_notice = handle_privmsg - def handle_end_of_burst(self, source, command, args): """Handles end of burst from our uplink.""" # Send EOB acknowledgement; this is required by the P10 specification, diff --git a/protocols/ts6_common.py b/protocols/ts6_common.py index ea5a79b..3a88630 100644 --- a/protocols/ts6_common.py +++ b/protocols/ts6_common.py @@ -294,83 +294,6 @@ class TS6BaseProtocol(IRCS2SProtocol): self.irc.users[source].away = text ### HANDLERS - - def handle_events(self, data): - """Event handler for TS6 protocols. - - This passes most commands to the various handle_ABCD() functions - elsewhere defined protocol modules, coersing various sender prefixes - from nicks and server names to UIDs and SIDs respectively, - whenever possible. - - Commands sent without an explicit sender prefix will have them set to - the SID of the uplink server. - """ - data = data.split(" ") - try: # Message starts with a SID/UID prefix. - args = self.parsePrefixedArgs(data) - sender = args[0] - command = args[1] - args = args[2:] - # If the sender isn't in UID format, try to convert it automatically. - # Unreal's protocol, for example, isn't quite consistent with this yet! - sender_server = self._getSid(sender) - if sender_server in self.irc.servers: - # Sender is a server when converted from name to SID. - numeric = sender_server - else: - # Sender is a user. - numeric = self._getUid(sender) - - # parsePrefixedArgs() will raise IndexError if the TS6 sender prefix is missing. - except IndexError: - # Raw command without an explicit sender; assume it's being sent by our uplink. - args = self.parseArgs(data) - numeric = self.irc.uplink - command = args[0] - args = args[1:] - - if self.irc.isInternalClient(numeric) or self.irc.isInternalServer(numeric): - log.warning("(%s) Received command %s being routed the wrong way!", self.irc.name, command) - return - - if command == 'ENCAP': - # Special case for encapsulated commands (ENCAP), in forms like this: - # <- :00A ENCAP * SU 42XAAAAAC :GLolol - command = args[1] - args = args[2:] - log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.irc.name, command, args) - - try: - func = getattr(self, 'handle_'+command.lower()) - except AttributeError: # unhandled command - pass - else: - parsed_args = func(numeric, command, args) - if parsed_args is not None: - return [numeric, command, parsed_args] - - def handle_privmsg(self, source, command, args): - """Handles incoming PRIVMSG/NOTICE.""" - # <- :70MAAAAAA PRIVMSG #dev :afasfsa - # <- :70MAAAAAA NOTICE 0ALAAAAAA :afasfsa - target = 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. - stripped_target = target.lstrip(''.join(self.irc.prefixmodes.values())) - if utils.isChannel(stripped_target): - target = self.irc.toLower(target) - - return {'target': target, 'text': args[1]} - - handle_notice = handle_privmsg - def handle_kick(self, source, command, args): """Handles incoming KICKs.""" # :70MAAAAAA KICK #test 70MAAAAAA :some reason