diff --git a/classes.py b/classes.py index cab1e6c..72c564a 100644 --- a/classes.py +++ b/classes.py @@ -499,6 +499,16 @@ class Irc(): """Replies to the last caller in the right context (channel or PM).""" self.msg(self.called_by, text, notice=notice, source=source) + def toLower(self, text): + """Returns a lowercase representation of text based on the IRC object's + casemapping (rfc1459 or ascii).""" + if self.proto.casemapping == 'rfc1459': + text = text.replace('{', '[') + text = text.replace('}', ']') + text = text.replace('|', '\\') + text = text.replace('~', '^') + return text.lower() + def parseModes(self, 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')] @@ -792,9 +802,9 @@ class Irc(): ### State checking functions def nickToUid(self, nick): """Looks up the UID of a user with the given nick, if one is present.""" - nick = utils.toLower(self, nick) + nick = self.toLower(nick) for k, v in self.users.copy().items(): - if utils.toLower(self, v.nick) == nick: + if self.toLower(v.nick) == nick: return k def isInternalClient(self, numeric): diff --git a/plugins/commands.py b/plugins/commands.py index 17b26ba..5b378cc 100644 --- a/plugins/commands.py +++ b/plugins/commands.py @@ -116,7 +116,7 @@ def showchan(irc, source, args): Shows information about .""" try: - channel = utils.toLower(irc, args[0]) + channel = irc.toLower(args[0]) except IndexError: irc.reply("Error: Not enough arguments. Needs 1: channel.") return diff --git a/plugins/relay.py b/plugins/relay.py index 19b1a13..ab4635e 100644 --- a/plugins/relay.py +++ b/plugins/relay.py @@ -1225,7 +1225,7 @@ def create(irc, source, args): Creates the channel over the relay.""" try: - channel = utils.toLower(irc, args[0]) + channel = irc.toLower(args[0]) except IndexError: irc.reply("Error: Not enough arguments. Needs 1: channel.") return @@ -1260,11 +1260,11 @@ def destroy(irc, source, args): Removes from the relay, delinking all networks linked to it. If 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. - channel = utils.toLower(irc, args[1]) + channel = irc.toLower(args[1]) network = args[0] except IndexError: try: # One argument was given; assume it's just the channel. - channel = utils.toLower(irc, args[0]) + channel = irc.toLower(args[0]) network = irc.name except IndexError: irc.reply("Error: Not enough arguments. Needs 1-2: channel, network (optional).") @@ -1304,13 +1304,13 @@ def link(irc, source, args): Links channel on over the relay to . If is not specified, it defaults to the same name as .""" try: - channel = utils.toLower(irc, args[1]) + channel = irc.toLower(args[1]) remotenet = args[0] except IndexError: irc.reply("Error: Not enough arguments. Needs 2-3: remote netname, channel, local channel name (optional).") return try: - localchan = utils.toLower(irc, args[2]) + localchan = irc.toLower(args[2]) except IndexError: localchan = channel for c in (channel, localchan): @@ -1355,7 +1355,7 @@ def delink(irc, source, args): Delinks channel . must and can only be specified if you are on the host network for , and allows you to pick which network to delink. To remove a relay entirely, use the 'destroy' command instead.""" try: - channel = utils.toLower(irc, args[0]) + channel = irc.toLower(args[0]) except IndexError: irc.reply("Error: Not enough arguments. Needs 1-2: channel, remote netname (optional).") return @@ -1463,7 +1463,7 @@ def linkacl(irc, source, args): irc.checkAuthenticated(source) try: cmd = args[0].lower() - channel = utils.toLower(irc, args[1]) + channel = irc.toLower(args[1]) except IndexError: irc.reply(missingargs) return @@ -1551,7 +1551,7 @@ def claim(irc, source, args): CLAIM is a way of enforcing network ownership for a channel, similarly to Janus. Unless the list is empty, only networks on the CLAIM list for a channel (plus the creating network) are allowed to override kicks, mode changes, and topic changes in it - attempts from other networks' opers to do this are simply blocked or reverted.""" irc.checkAuthenticated(source) try: - channel = utils.toLower(irc, args[0]) + channel = irc.toLower(args[0]) except IndexError: irc.reply("Error: Not enough arguments. Needs 1-2: channel, list of networks (optional).") return diff --git a/protocols/inspircd.py b/protocols/inspircd.py index c788559..9100409 100644 --- a/protocols/inspircd.py +++ b/protocols/inspircd.py @@ -76,7 +76,7 @@ class InspIRCdProtocol(TS6BaseProtocol): # InspIRCd doesn't distinguish between burst joins and regular joins, # so what we're actually doing here is sending FJOIN from the server, # on behalf of the clients that are joining. - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) server = self.irc.isInternalClient(client) if not server: log.error('(%s) Error trying to join %r to %r (no such client exists)', self.irc.name, client, channel) @@ -100,7 +100,7 @@ class InspIRCdProtocol(TS6BaseProtocol): sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')]) sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)]) """ - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) server = server or self.irc.sid assert users, "sjoin: No users sent?" log.debug('(%s) sjoin: got %r for users', self.irc.name, users) @@ -180,7 +180,7 @@ class InspIRCdProtocol(TS6BaseProtocol): self.irc.applyModes(target, modes) joinedmodes = self.irc.joinModes(modes) if utils.isChannel(target): - ts = ts or self.irc.channels[utils.toLower(self.irc, target)].ts + ts = ts or self.irc.channels[self.irc.toLower(target)].ts self._send(numeric, 'FMODE %s %s %s' % (target, ts, joinedmodes)) else: self._send(numeric, 'MODE %s %s' % (target, joinedmodes)) @@ -482,7 +482,7 @@ class InspIRCdProtocol(TS6BaseProtocol): def handle_fjoin(self, servernumeric, command, args): """Handles incoming FJOIN commands (InspIRCd equivalent of JOIN/SJOIN).""" # :70M FJOIN #chat 1423790411 +AFPfjnt 6:5 7:5 9:5 :o,1SRAABIT4 v,1IOAAF53R <...> - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) # InspIRCd sends each channel's users in the form of 'modeprefix(es),UID' userlist = args[-1].split() @@ -552,7 +552,7 @@ class InspIRCdProtocol(TS6BaseProtocol): def handle_fmode(self, numeric, command, args): """Handles the FMODE command, used for channel mode changes.""" # <- :70MAAAAAA FMODE #chat 1433653462 +hhT 70MAAAAAA 70MAAAAAD - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) oldobj = self.irc.channels[channel].deepcopy() modes = args[2:] changedmodes = self.irc.parseModes(channel, modes) @@ -583,7 +583,7 @@ class InspIRCdProtocol(TS6BaseProtocol): def handle_ftopic(self, numeric, command, args): """Handles incoming FTOPIC (sets topic on burst).""" # <- :70M FTOPIC #channel 1434510754 GLo|o|!GLolol@escape.the.dreamland.ca :Some channel topic - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) ts = args[1] setter = args[2] topic = args[-1] @@ -598,7 +598,7 @@ class InspIRCdProtocol(TS6BaseProtocol): """Handles incoming INVITEs.""" # <- :70MAAAAAC INVITE 0ALAAAAAA #blah 0 target = args[0] - channel = utils.toLower(self.irc, args[1]) + 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} @@ -621,7 +621,7 @@ class InspIRCdProtocol(TS6BaseProtocol): targetmask = args[0] real_command = args[1] if targetmask == '*' and real_command == 'KNOCK': - channel = utils.toLower(self.irc, args[2]) + channel = self.irc.toLower(args[2]) text = args[3] return {'parse_as': real_command, 'channel': channel, 'text': text} diff --git a/protocols/nefarious.py b/protocols/nefarious.py index c62a1ca..26b773e 100644 --- a/protocols/nefarious.py +++ b/protocols/nefarious.py @@ -314,7 +314,7 @@ class P10Protocol(Protocol): def join(self, client, channel): """Joins a PyLink client to a channel.""" # <- ABAAB J #test3 1460744371 - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) ts = self.irc.channels[channel].ts if not self.irc.isInternalClient(client): @@ -336,7 +336,7 @@ class P10Protocol(Protocol): (not self.irc.isInternalServer(numeric)): raise LookupError('No such PyLink client/server exists.') - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) if not reason: reason = 'No reason given' @@ -386,7 +386,7 @@ class P10Protocol(Protocol): # modestring, this means we can send a max of 13 modes with arguments per line. if utils.isChannel(target): # Channel mode changes have a trailing TS. User mode changes do not. - cobj = self.irc.channels[utils.toLower(self.irc, target)] + cobj = self.irc.channels[self.irc.toLower(target)] ts = ts or cobj.ts send_ts = True else: @@ -421,7 +421,7 @@ class P10Protocol(Protocol): def part(self, client, channel, reason=None): """Sends a part from a PyLink client.""" - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) if not self.irc.isInternalClient(client): raise LookupError('No such PyLink client exists.') @@ -463,7 +463,7 @@ class P10Protocol(Protocol): sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)]) """ # <- AB B #test 1460742014 +tnl 10 ABAAB,ABAAA:o :%*!*@other.bad.host ~ *!*@bad.host - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) server = server or self.irc.sid assert users, "sjoin: No users sent?" @@ -918,7 +918,7 @@ class P10Protocol(Protocol): # No useful data was sent, ignore. return - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) userlist = args[-1].split() their_ts = int(args[1]) our_ts = self.irc.channels[channel].ts @@ -1016,7 +1016,7 @@ class P10Protocol(Protocol): self.irc.users[source].channels.discard(channel) return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'} else: - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) if ts: # Only update TS if one was sent. self.updateTS(channel, ts) @@ -1036,7 +1036,7 @@ class P10Protocol(Protocol): # We use lowercase channels internally, but uppercase UIDs. if utils.isChannel(target): - target = utils.toLower(self.irc, target) + target = self.irc.toLower(target) return {'target': target, 'text': args[1]} handle_notice = handle_privmsg @@ -1055,7 +1055,7 @@ class P10Protocol(Protocol): # <- ABAAA OM #test +h ABAAA target = self._getUid(args[0]) if utils.isChannel(target): - target = utils.toLower(self.irc, target) + target = self.irc.toLower(target) modestrings = args[1:] changedmodes = self.irc.parseModes(target, modestrings) @@ -1078,7 +1078,7 @@ class P10Protocol(Protocol): # <- ABAAA L #test,#test2 # <- ABAAA L #test :test - channels = utils.toLower(self.irc, args[0]).split(',') + 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) @@ -1102,7 +1102,7 @@ class P10Protocol(Protocol): def handle_kick(self, source, command, args): """Handles incoming KICKs.""" # <- ABAAA K #TEST AyAAA :PyLink-devel - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) kicked = args[1] self.handle_part(kicked, 'KICK', [channel, args[2]]) @@ -1165,7 +1165,7 @@ class P10Protocol(Protocol): def handle_topic(self, source, command, args): """Handles TOPIC changes.""" # <- ABAAA T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) topic = args[-1] oldtopic = self.irc.channels[channel].topic @@ -1183,7 +1183,7 @@ class P10Protocol(Protocol): # - note that the target is a nickname, not a numeric. # <- ABAAA I PyLink-devel #services 1460948992 target = self._getUid(args[0]) - channel = utils.toLower(self.irc, args[1]) + channel = self.irc.toLower(args[1]) return {'target': target, 'channel': channel} @@ -1206,7 +1206,7 @@ class P10Protocol(Protocol): def handle_clearmode(self, numeric, command, args): """Handles CLEARMODE, which is used to clear a channel's modes.""" # <- ABAAA CM #test ovpsmikbl - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) modes = args[1] # Enumerate a list of our existing modes, including prefix modes. diff --git a/protocols/ts6.py b/protocols/ts6.py index fbe5cb0..e480e66 100644 --- a/protocols/ts6.py +++ b/protocols/ts6.py @@ -67,7 +67,7 @@ class TS6Protocol(TS6BaseProtocol): def join(self, client, channel): """Joins a PyLink client to a channel.""" - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) # JOIN: # parameters: channelTS, channel, '+' (a plus sign) if not self.irc.isInternalClient(client): @@ -97,7 +97,7 @@ class TS6Protocol(TS6BaseProtocol): # their status ('@+', '@', '+' or ''), for example: # '@+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. - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) server = server or self.irc.sid assert users, "sjoin: No users sent?" log.debug('(%s) sjoin: got %r for users', self.irc.name, users) @@ -154,7 +154,7 @@ class TS6Protocol(TS6BaseProtocol): modes = list(modes) if utils.isChannel(target): - ts = ts or self.irc.channels[utils.toLower(self.irc, target)].ts + ts = ts or self.irc.channels[self.irc.toLower(target)].ts # TMODE: # parameters: channelTS, channel, cmode changes, opt. cmode parameters... @@ -419,7 +419,7 @@ class TS6Protocol(TS6BaseProtocol): """Handles incoming SJOIN commands.""" # parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist # <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB - channel = utils.toLower(self.irc, args[1]) + channel = self.irc.toLower(args[1]) userlist = args[-1].split() their_ts = int(args[0]) our_ts = self.irc.channels[channel].ts @@ -474,7 +474,7 @@ class TS6Protocol(TS6BaseProtocol): self.irc.users[numeric].channels.discard(channel) return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'} else: - channel = utils.toLower(self.irc, args[1]) + channel = self.irc.toLower(args[1]) self.updateTS(channel, ts) self.irc.users[numeric].channels.add(channel) @@ -560,7 +560,7 @@ class TS6Protocol(TS6BaseProtocol): """Handles incoming TMODE commands (channel mode change).""" # <- :42XAAAAAB TMODE 1437450768 #test -c+lkC 3 agte4 # <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD - channel = utils.toLower(self.irc, args[1]) + channel = self.irc.toLower(args[1]) oldobj = self.irc.channels[channel].deepcopy() modes = args[2:] changedmodes = self.irc.parseModes(channel, modes) diff --git a/protocols/ts6_common.py b/protocols/ts6_common.py index e9d7507..bbfaa4c 100644 --- a/protocols/ts6_common.py +++ b/protocols/ts6_common.py @@ -151,7 +151,7 @@ class TS6BaseProtocol(Protocol): (not self.irc.isInternalServer(numeric)): raise LookupError('No such PyLink client/server exists.') - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) if not reason: reason = 'No reason given' @@ -174,7 +174,7 @@ class TS6BaseProtocol(Protocol): def part(self, client, channel, reason=None): """Sends a part from a PyLink client.""" - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) if not self.irc.isInternalClient(client): log.error('(%s) Error trying to part %r from %r (no such client exists)', self.irc.name, client, channel) raise LookupError('No such PyLink client exists.') @@ -319,7 +319,7 @@ class TS6BaseProtocol(Protocol): target = args[0] # We use lowercase channels internally, but uppercase UIDs. if utils.isChannel(target): - target = utils.toLower(self.irc, target) + target = self.irc.toLower(target) return {'target': target, 'text': args[1]} handle_notice = handle_privmsg @@ -340,7 +340,7 @@ class TS6BaseProtocol(Protocol): def handle_kick(self, source, command, args): """Handles incoming KICKs.""" # :70MAAAAAA KICK #test 70MAAAAAA :some reason - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) kicked = self._getUid(args[1]) self.handle_part(kicked, 'KICK', [channel, args[2]]) return {'channel': channel, 'target': kicked, 'text': args[2]} @@ -408,7 +408,7 @@ class TS6BaseProtocol(Protocol): """Handles incoming TOPIC changes from clients. For topic bursts, TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.""" # <- :70MAAAAAA TOPIC #test :test - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) topic = args[1] oldtopic = self.irc.channels[channel].topic @@ -420,7 +420,7 @@ class TS6BaseProtocol(Protocol): def handle_part(self, source, command, args): """Handles incoming PART commands.""" - channels = utils.toLower(self.irc, args[0]).split(',') + 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) diff --git a/protocols/unreal.py b/protocols/unreal.py index f561ebf..fc0efd1 100644 --- a/protocols/unreal.py +++ b/protocols/unreal.py @@ -120,7 +120,7 @@ class UnrealProtocol(TS6BaseProtocol): def join(self, client, channel): """Joins a PyLink client to a channel.""" - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) if not self.irc.isInternalClient(client): raise LookupError('No such PyLink client exists.') self._send(client, "JOIN %s" % channel) @@ -145,7 +145,7 @@ class UnrealProtocol(TS6BaseProtocol): # The nicklist consists of users joining the channel, with status prefixes for # their status ('@+', '@', '+' or ''), for example: # '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'. - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) server = server or self.irc.sid assert users, "sjoin: No users sent?" if not server: @@ -219,7 +219,7 @@ class UnrealProtocol(TS6BaseProtocol): joinedmodes = self.irc.joinModes(modes) if utils.isChannel(target): # The MODE command is used for channel mode changes only - ts = ts or self.irc.channels[utils.toLower(self.irc, target)].ts + ts = ts or self.irc.channels[self.irc.toLower(target)].ts self._send(numeric, 'MODE %s %s %s' % (target, joinedmodes, ts)) else: # For user modes, the only way to set modes (for non-U:Lined servers) @@ -510,7 +510,7 @@ class UnrealProtocol(TS6BaseProtocol): target = self._getUid(args[0]) # We use lowercase channels internally, but uppercase UIDs. if utils.isChannel(target): - target = utils.toLower(self.irc, target) + target = self.irc.toLower(target) return {'target': target, 'text': args[1]} handle_notice = handle_privmsg @@ -530,7 +530,7 @@ class UnrealProtocol(TS6BaseProtocol): else: for channel in args[0].split(','): # Normalize channel case. - channel = utils.toLower(self.irc, channel) + channel = self.irc.toLower(channel) c = self.irc.channels[channel] @@ -547,7 +547,7 @@ class UnrealProtocol(TS6BaseProtocol): # memberlist should be a list of UIDs with their channel status prefixes, as # in ":001AAAAAA @001AAAAAB +001AAAAAC". # Interestingly, no modes are ever sent in this command as far as I've seen. - channel = utils.toLower(self.irc, args[1]) + channel = self.irc.toLower(args[1]) userlist = args[-1].split() our_ts = self.irc.channels[channel].ts @@ -638,7 +638,7 @@ class UnrealProtocol(TS6BaseProtocol): # Also, we need to get rid of that extra space following the +f argument. :| if utils.isChannel(args[0]): - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) oldobj = self.irc.channels[channel].deepcopy() modes = list(filter(None, args[1:])) # normalize whitespace parsedmodes = self.irc.parseModes(channel, modes) @@ -752,7 +752,7 @@ class UnrealProtocol(TS6BaseProtocol): """Handles the TOPIC command.""" # <- GL TOPIC #services GL 1444699395 :weeee # <- TOPIC #services devel.relay 1452399682 :test - channel = utils.toLower(self.irc, args[0]) + channel = self.irc.toLower(args[0]) topic = args[-1] setter = args[1] ts = args[2] diff --git a/utils.py b/utils.py index 80e492f..0f0d6a3 100644 --- a/utils.py +++ b/utils.py @@ -69,16 +69,6 @@ def add_hook(func, command): world.hooks[command].append(func) return func -def toLower(irc, text): - """Returns a lowercase representation of text based on the IRC object's - casemapping (rfc1459 or ascii).""" - if irc.proto.casemapping == 'rfc1459': - text = text.replace('{', '[') - text = text.replace('}', ']') - text = text.replace('|', '\\') - text = text.replace('~', '^') - return text.lower() - _nickregex = r'^[A-Za-z\|\\_\[\]\{\}\^\`][A-Z0-9a-z\-\|\\_\[\]\{\}\^\`]*$' def isNick(s, nicklen=None): """Returns whether the string given is a valid nick."""