Respect RFC1459 casemapping rules

This commit is contained in:
jesopo 2018-09-11 08:52:12 +01:00
parent 7530bb7cbd
commit 6c8399fa0f
6 changed files with 45 additions and 17 deletions

View file

@ -1,4 +1,5 @@
import re import re
import Utils
class BufferLine(object): class BufferLine(object):
def __init__(self, sender, message, action, from_self): def __init__(self, sender, message, action, from_self):
@ -28,7 +29,7 @@ class Buffer(object):
def find(self, pattern, **kwargs): def find(self, pattern, **kwargs):
from_self = kwargs.get("from_self", True) from_self = kwargs.get("from_self", True)
for_user = kwargs.get("for_user", "") for_user = kwargs.get("for_user", "")
for_user = for_user.lower() if for_user else None for_user = Utils.irc_lower(for_user) if for_user else None
not_pattern = kwargs.get("not_pattern", None) not_pattern = kwargs.get("not_pattern", None)
for line in self.lines: for line in self.lines:
if line.from_self and not from_self: if line.from_self and not from_self:
@ -36,7 +37,7 @@ class Buffer(object):
elif re.search(pattern, line.message): elif re.search(pattern, line.message):
if not_pattern and re.search(not_pattern, line.message): if not_pattern and re.search(not_pattern, line.message):
continue continue
if for_user and not line.sender.lower() == for_user: if for_user and not Utils.irc_lower(line.sender) == for_user:
continue continue
return line return line
def skip_next(self): def skip_next(self):

View file

@ -1,9 +1,9 @@
import uuid import uuid
import IRCBuffer import IRCBuffer, Utils
class Channel(object): class Channel(object):
def __init__(self, name, id, server, bot): def __init__(self, name, id, server, bot):
self.name = name.lower() self.name = Utils.irc_lower(name)
self.id = id self.id = id
self.server = server self.server = server
self.bot = bot self.bot = bot

View file

@ -5,6 +5,7 @@ RE_PREFIXES = re.compile(r"\bPREFIX=\((\w+)\)(\W+)(?:\b|$)")
RE_CHANMODES = re.compile( RE_CHANMODES = re.compile(
r"\bCHANMODES=(\w*),(\w*),(\w*),(\w*)(?:\b|$)") r"\bCHANMODES=(\w*),(\w*),(\w*),(\w*)(?:\b|$)")
RE_CHANTYPES = re.compile(r"\bCHANTYPES=(\W+)(?:\b|$)") RE_CHANTYPES = re.compile(r"\bCHANTYPES=(\W+)(?:\b|$)")
RE_CASEMAPPING = re.compile(r"\bCASEMAPPING=(\S+)")
RE_MODES = re.compile(r"[-+]\w+") RE_MODES = re.compile(r"[-+]\w+")
CAPABILITIES = {"multi-prefix", "chghost", "invite-notify", "account-tag", CAPABILITIES = {"multi-prefix", "chghost", "invite-notify", "account-tag",
@ -136,6 +137,11 @@ class LineHandler(object):
match = re.search(RE_CHANTYPES, isupport_line) match = re.search(RE_CHANTYPES, isupport_line)
if match: if match:
event["server"].channel_types = list(match.group(1)) event["server"].channel_types = list(match.group(1))
match = re.search(RE_CASEMAPPING, isupport_line)
if match:
event["server"].case_mapping = match.group(1)
self.events.on("received.numeric.005").call( self.events.on("received.numeric.005").call(
isupport=isupport_line, server=event["server"]) isupport=isupport_line, server=event["server"])
@ -554,10 +560,11 @@ class LineHandler(object):
# we need a registered nickname for this channel # we need a registered nickname for this channel
def handle_477(self, event): def handle_477(self, event):
if event["args"][1].lower() in event["server"].attempted_join: channel_name = Utils.irc_lower(event["args"][1])
if channel_name in event["server"].attempted_join:
self.bot.add_timer("rejoin", 5, self.bot.add_timer("rejoin", 5,
channel_name=event["args"][1], channel_name=event["args"][1],
key=event["server"].attempted_join[event["args"][1].lower()], key=event["server"].attempted_join[channel_name],
server_id=event["server"].id) server_id=event["server"].id)
# someone's been kicked from a channel # someone's been kicked from a channel

View file

@ -47,6 +47,7 @@ class Server(object):
{"@": "o", "+": "v"}) {"@": "o", "+": "v"})
self.channel_modes = [] self.channel_modes = []
self.channel_types = [] self.channel_types = []
self.case_mapping = "rfc1459"
self.last_read = time.monotonic() self.last_read = time.monotonic()
self.last_send = None self.last_send = None
@ -130,9 +131,9 @@ class Server(object):
def set_own_nickname(self, nickname): def set_own_nickname(self, nickname):
self.nickname = nickname self.nickname = nickname
self.nickname_lower = nickname.lower() self.nickname_lower = Utils.irc_lower(nickname)
def is_own_nickname(self, nickname): def is_own_nickname(self, nickname):
return nickname.lower() == self.nickname_lower return Utils.irc_equals(nickname, self.nickname)
def add_own_mode(self, mode, arg=None): def add_own_mode(self, mode, arg=None):
self.own_modes[mode] = arg self.own_modes[mode] = arg
@ -145,7 +146,7 @@ class Server(object):
self.add_own_mode(mode, arg) self.add_own_mode(mode, arg)
def has_user(self, nickname): def has_user(self, nickname):
return nickname.lower() in self.users return Utils.irc_lower(nickname) in self.users
def get_user(self, nickname): def get_user(self, nickname):
if not self.has_user(nickname): if not self.has_user(nickname):
user_id = self.get_user_id(nickname) user_id = self.get_user_id(nickname)
@ -154,7 +155,7 @@ class Server(object):
user=new_user, server=self) user=new_user, server=self)
self.users[new_user.nickname_lower] = new_user self.users[new_user.nickname_lower] = new_user
self.new_users.add(new_user) self.new_users.add(new_user)
return self.users[nickname.lower()] return self.users[Utils.irc_lower(nickname)]
def get_user_id(self, nickname): def get_user_id(self, nickname):
self.bot.database.users.add(self.id, nickname) self.bot.database.users.add(self.id, nickname)
return self.bot.database.users.get_id(self.id, nickname) return self.bot.database.users.get_id(self.id, nickname)
@ -164,12 +165,12 @@ class Server(object):
channel.remove_user(user) channel.remove_user(user)
def change_user_nickname(self, old_nickname, new_nickname): def change_user_nickname(self, old_nickname, new_nickname):
user = self.users.pop(old_nickname.lower()) user = self.users.pop(Utils.irc_lower(old_nickname))
user._id = self.get_user_id(new_nickname) user._id = self.get_user_id(new_nickname)
self.users[new_nickname.lower()] = user self.users[Utils.irc_lower(new_nickname)] = user
def has_channel(self, channel_name): def has_channel(self, channel_name):
return channel_name[0] in self.channel_types and channel_name.lower( return channel_name[0] in self.channel_types and Utils.irc_lower(
) in self.channels channel_name) in self.channels
def get_channel(self, channel_name): def get_channel(self, channel_name):
if not self.has_channel(channel_name): if not self.has_channel(channel_name):
channel_id = self.get_channel_id(channel_name) channel_id = self.get_channel_id(channel_name)
@ -178,7 +179,7 @@ class Server(object):
self.events.on("new").on("channel").call( self.events.on("new").on("channel").call(
channel=new_channel, server=self) channel=new_channel, server=self)
self.channels[new_channel.name] = new_channel self.channels[new_channel.name] = new_channel
return self.channels[channel_name.lower()] return self.channels[Utils.irc_lower(channel_name)]
def get_channel_id(self, channel_name): def get_channel_id(self, channel_name):
self.bot.database.channels.add(self.id, channel_name) self.bot.database.channels.add(self.id, channel_name)
return self.bot.database.channels.get_id(self.id, channel_name) return self.bot.database.channels.get_id(self.id, channel_name)

View file

@ -1,5 +1,5 @@
import uuid import uuid
import IRCBuffer import IRCBuffer, Utils
class User(object): class User(object):
def __init__(self, nickname, id, server, bot): def __init__(self, nickname, id, server, bot):
@ -31,7 +31,7 @@ class User(object):
def set_nickname(self, nickname): def set_nickname(self, nickname):
self.nickname = nickname self.nickname = nickname
self.nickname_lower = nickname.lower() self.nickname_lower = Utils.irc_lower(nickname)
self.name = self.nickname_lower self.name = self.nickname_lower
def join_channel(self, channel): def join_channel(self, channel):
self.channels.add(channel) self.channels.add(channel)

View file

@ -1,10 +1,14 @@
import json, re, traceback, urllib.request, urllib.parse, urllib.error, ssl import json, re, traceback, urllib.request, urllib.parse, urllib.error, ssl
import string
import bs4 import bs4
USER_AGENT = ("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 " USER_AGENT = ("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36") "(KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36")
REGEX_HTTP = re.compile("https?://", re.I) REGEX_HTTP = re.compile("https?://", re.I)
RFC1459_UPPER = r'\[]~'
RFC1459_UPPER = r'|{}^'
def remove_colon(s): def remove_colon(s):
if s.startswith(":"): if s.startswith(":"):
s = s[1:] s = s[1:]
@ -13,6 +17,21 @@ def remove_colon(s):
def arbitrary(s, n): def arbitrary(s, n):
return remove_colon(" ".join(s[n:])) return remove_colon(" ".join(s[n:]))
def _rfc1459_lower(s):
for upper, lower in zip(RFC1459_UPPER, RFC1459_LOWER):
s = s.replace(upper, lower)
return s.lower()
def irc_lower(server, s):
if server.case_mapping == "ascii":
return s.lower()
elif server.case_mapping == "rfc1459":
return _rfc1459_lower(s)
else:
raise ValueError("unknown casemapping '%s'" % server.case_mapping)
def irc_equals(server, s1, s2):
return irc_lower(server, s1) == irc_lower(server, s2)
class IRCHostmask(object): class IRCHostmask(object):
def __init__(self, nickname, username, hostname, hostmask): def __init__(self, nickname, username, hostname, hostmask):
self.nickname = nickname self.nickname = nickname