From f60b3cc98867b04ea9de11858f27b9f0fad0723b Mon Sep 17 00:00:00 2001 From: Les De Ridder Date: Thu, 13 Apr 2017 22:20:49 +0200 Subject: [PATCH] Use case-insensitive comparisons for nicknames and channel names --- source/ircd/connection.d | 33 ++++++++++++++---------------- source/ircd/helpers.d | 26 +++++++++++++++++++++++- source/ircd/server.d | 44 +++++++++++++++++++++++++++++----------- 3 files changed, 72 insertions(+), 31 deletions(-) diff --git a/source/ircd/connection.d b/source/ircd/connection.d index 6825f31..fb3b97a 100644 --- a/source/ircd/connection.d +++ b/source/ircd/connection.d @@ -205,7 +205,7 @@ class Connection auto newNick = message.parameters[0]; - if(!_server.isNickAvailable(newNick)) + if(!_server.isNickAvailable(newNick) && newNick.toIRCLower != nick.toIRCLower) { send(Message(_server.name, "433", [nick, newNick, "Nickname already in use"])); return; @@ -311,7 +311,7 @@ class Connection { sendErrNoSuchChannel(channel); } - else if(!channels.canFind!(c => c.name == channel)) + else if(!_server.canFindChannelByName(channel)) { send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true)); } @@ -347,7 +347,7 @@ class Connection if(Server.isValidChannelName(target)) { - if(!_server.channels.canFind!(c => c.name == target)) + if(!_server.canFindChannelByName(target)) { sendErrNoSuchNick(target); } @@ -358,7 +358,7 @@ class Connection } else if(Server.isValidNick(target)) { - if(!_server.connections.canFind!(c => c.nick == target)) + if(!_server.canFindConnectionByNick(target)) { sendErrNoSuchNick(target); } @@ -366,7 +366,7 @@ class Connection { _server.privmsgToUser(this, target, text); - auto targetUser = _server.connections.find!(c => c.nick == target)[0]; + auto targetUser = _server.findConnectionByNick(target)[0]; if(targetUser.modes.canFind('a')) { sendRplAway(target, targetUser.awayMessage); @@ -393,14 +393,11 @@ class Connection return; } - if(Server.isValidChannelName(target)) + if(Server.isValidChannelName(target) && _server.canFindChannelByName(target)) { - if(_server.channels.canFind!(c => c.name == target)) - { - _server.noticeToChannel(this, target, text); - } + _server.noticeToChannel(this, target, text); } - else if(Server.isValidNick(target) && _server.connections.canFind!(c => c.nick == target)) + else if(Server.isValidNick(target) && _server.canFindConnectionByNick(target)) { _server.noticeToUser(this, target, text); } @@ -417,7 +414,7 @@ class Connection auto mask = message.parameters[0]; auto operatorsOnly = message.parameters.length > 1 && message.parameters[1] == "o"; - if(_server.isValidChannelName(mask) && _server.channels.canFind!(c => c.name == mask)) + if(_server.isValidChannelName(mask) && _server.canFindChannelByName(mask)) { _server.whoChannel(this, mask, operatorsOnly); } @@ -459,7 +456,7 @@ class Connection auto channelName = message.parameters[0]; if(message.parameters.length == 1) { - if(!_server.channels.canFind!(c => c.name == channelName && (!(c.modes.canFind('s') || c.modes.canFind('p')) || c.members.canFind(this)))) + if(!_server.channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower && (!(c.modes.canFind('s') || c.modes.canFind('p')) || c.members.canFind(this)))) { //NOTE: The RFCs don't allow ERR_NOSUCHCHANNEL as a response to TOPIC //TODO: If RFC-strictness is off, do send ERR_NOSUCHCHANNEL @@ -473,12 +470,12 @@ class Connection else { auto newTopic = message.parameters[1]; - if(!channels.canFind!(c => c.name == channelName)) + if(!channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower)) { sendErrNotOnChannel(channelName); } //TODO: Allow operators to set flags - else if(channels.find!(c => c.name == channelName).map!(c => c.modes.canFind('t') /* && this user isn't an operator */).array[0]) + else if(channels.find!(c => c.name.toIRCLower == channelName.toIRCLower).map!(c => c.modes.canFind('t') /* && this user isn't an operator */).array[0]) { sendErrChanopPrivsNeeded(channelName); } @@ -505,7 +502,7 @@ class Connection { foreach(channelName; message.parameters[0].split(',')) { - if(_server.channels.canFind!(c => c.name == channelName && c.visibleTo(this))) + if(_server.channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower && c.visibleTo(this))) { _server.sendChannelNames(this, channelName); } @@ -545,7 +542,7 @@ class Connection } auto targetNick = message.parameters[0]; - auto targetUserRange = _server.connections.find!(c => c.nick == targetNick); + auto targetUserRange = _server.findConnectionByNick(targetNick); if(targetUserRange.empty) { sendErrNoSuchNick(targetNick); @@ -554,7 +551,7 @@ class Connection auto targetUser = targetUserRange[0]; auto channelName = message.parameters[1]; - auto channelRange = _server.channels.find!(c => c.name == channelName); + auto channelRange = _server.findChannelByName(channelName); if(channelRange.empty) { _server.invite(this, targetUser.nick, channelName); diff --git a/source/ircd/helpers.d b/source/ircd/helpers.d index 3e5fdd1..c76307a 100644 --- a/source/ircd/helpers.d +++ b/source/ircd/helpers.d @@ -1,6 +1,7 @@ module ircd.helpers; -import std.range; +import std.range : array, empty, front, popFront, save; +import std.algorithm : map; //Based on std.path.globMatch (https://github.com/dlang/phobos/blob/v2.073.2/std/path.d#L3164) //License: Boost License 1.0 (http://www.boost.org/LICENSE_1_0.txt) @@ -46,3 +47,26 @@ bool wildcardMatch(string input, string pattern) return input.empty; } +@safe pure +dchar toIRCLower(dchar input) +{ + import std.uni : toLower; + switch(input) + { + case '[': + return '{'; + case ']': + return '}'; + case '\\': + return '|'; + default: + return input.toLower; + } +} + +@safe pure +string toIRCLower(string input) +{ + import std.utf : byChar; + return input.map!toIRCLower.byChar.array.idup; +} diff --git a/source/ircd/server.d b/source/ircd/server.d index e6f0b02..0f0272e 100644 --- a/source/ircd/server.d +++ b/source/ircd/server.d @@ -98,14 +98,34 @@ class Server return true; } + Connection[] findConnectionByNick(string nick) + { + return connections.find!(c => c.nick.toIRCLower == nick.toIRCLower); + } + + bool canFindConnectionByNick(string nick) + { + return !findConnectionByNick(nick).empty; + } + bool isNickAvailable(string nick) { - return !connections.canFind!(c => c.nick == nick); + return !canFindConnectionByNick(nick); + } + + Channel[] findChannelByName(string name) + { + return channels.find!(c => c.name.toIRCLower == name.toIRCLower); + } + + bool canFindChannelByName(string name) + { + return !findConnectionByNick(name).empty; } void join(Connection connection, string channelName) { - auto channelRange = channels.find!(c => c.name == channelName); + auto channelRange = findChannelByName(channelName); Channel channel; if(channelRange.empty) { @@ -133,7 +153,7 @@ class Server void part(Connection connection, string channelName, string partMessage) { - auto channel = connection.channels.array.find!(c => c.name == channelName)[0]; + auto channel = connection.channels.array.find!(c => c.name.toIRCLower == channelName.toIRCLower)[0]; foreach(member; channel.members) { @@ -186,7 +206,7 @@ class Server { //TODO: Check what RFCs say about secret/private channels - auto channel = channels.find!(c => c.name == channelName)[0]; + auto channel = findChannelByName(channelName)[0]; foreach(c; channel.members.filter!(c => !operatorsOnly || c.isOperator) .filter!(c => c.visibleTo(origin))) { @@ -210,43 +230,43 @@ class Server void privmsgToChannel(Connection sender, string target, string text) { - auto channel = channels.find!(c => c.name == target)[0]; + auto channel = findChannelByName(target)[0]; channel.sendPrivMsg(sender, text); } void privmsgToUser(Connection sender, string target, string text) { - auto user = connections.find!(c => c.nick == target)[0]; + auto user = findConnectionByNick(target)[0]; user.send(Message(sender.mask, "PRIVMSG", [target, text], true)); } void noticeToChannel(Connection sender, string target, string text) { - auto channel = channels.find!(c => c.name == target)[0]; + auto channel = findChannelByName(target)[0]; channel.sendNotice(sender, text); } void noticeToUser(Connection sender, string target, string text) { - auto user = connections.find!(c => c.nick == target)[0]; + auto user = findConnectionByNick(target)[0]; user.send(Message(sender.mask, "NOTICE", [target, text], true)); } void sendChannelTopic(Connection origin, string channelName) { - auto channel = channels.find!(c => c.name == channelName)[0]; + auto channel = findChannelByName(channelName)[0]; channel.sendTopic(origin); } void setChannelTopic(Connection origin, string channelName, string newTopic) { - auto channel = channels.find!(c => c.name == channelName)[0]; + auto channel = findChannelByName(channelName)[0]; channel.setTopic(origin, newTopic); } void sendChannelNames(Connection connection, string channelName) { - auto channel = channels.find!(c => c.name == channelName)[0]; + auto channel = findChannelByName(channelName)[0]; channel.sendNames(connection); } @@ -339,7 +359,7 @@ class Server void ison(Connection connection, string[] nicks) { - auto reply = nicks.filter!(n => connections.canFind!(u => u.nick == n)).join(' '); + auto reply = nicks.filter!(n => canFindConnectionByNick(n)).join(' '); connection.send(Message(name, "303", [connection.nick, reply], true)); }