diff --git a/source/ircd/app.d b/source/ircd/app.d index 5862b6b..dc730c0 100644 --- a/source/ircd/app.d +++ b/source/ircd/app.d @@ -10,7 +10,7 @@ import ircd.server; static T tagValueOrNull(T)(Tag tag, string childName) { - if(childName !in tag.tags) + if (childName !in tag.tags) { return null; } @@ -22,7 +22,7 @@ static T tagValueOrNull(T)(Tag tag, string childName) static T tagValue(T)(Tag tag, string childName) { - static if(isArray!T && !isSomeString!T) + static if (isArray!T && !isSomeString!T) { template U(T : T[]) { @@ -31,16 +31,16 @@ static T tagValue(T)(Tag tag, string childName) T array = []; - foreach(value; tag.tags[childName][0].values) + foreach (value; tag.tags[childName][0].values) { array ~= value.get!(U!T); } return array; } - else static if(isIntegral!T && !is(T == int)) + else static if (isIntegral!T && !is(T == int)) { - return cast(T)tagValue!int(tag, childName); + return cast(T) tagValue!int(tag, childName); } else { @@ -57,17 +57,16 @@ shared static this() auto pass = config.tagValue!string("pass"); server.setPass(pass.empty ? null : pass); - foreach(listenBlock; config.tags.filter!(t => t.getFullName.toString == "listen")) + foreach (listenBlock; config.tags.filter!(t => t.getFullName.toString == "listen")) { assert(listenBlock.tagValue!string("type") == "plaintext"); auto addresses = listenBlock.tagValue!(string[])("address"); auto port = listenBlock.tagValue!ushort("port"); - foreach(address; addresses) + foreach (address; addresses) { server.listen(port, address); } } } - diff --git a/source/ircd/channel.d b/source/ircd/channel.d index c0c1c9d..a5f75ae 100644 --- a/source/ircd/channel.d +++ b/source/ircd/channel.d @@ -9,6 +9,7 @@ import ircd.server; import ircd.message; import ircd.helpers; +//TODO: Make this a struct? class Channel { string name; @@ -29,14 +30,14 @@ class Channel { this.name = name; this._server = server; - this.maskLists = ['b' : [], 'e' : [], 'I' : []]; + this.maskLists = ['b': [], 'e': [], 'I': []]; } void join(Connection connection) { members ~= connection; - if(members.length == 1) + if (members.length == 1) { memberModes[connection] ~= 'o'; } @@ -45,7 +46,7 @@ class Channel memberModes[connection] = []; } - if(inviteHolders.canFind(connection)) + if (inviteHolders.canFind(connection)) { inviteHolders = inviteHolders.remove!(c => c == connection); } @@ -53,11 +54,13 @@ class Channel void part(Connection connection, string partMessage) { - foreach(member; members) + foreach (member; members) { - if(partMessage !is null) + if (partMessage !is null) { - member.send(Message(connection.prefix, "PART", [name, partMessage], true)); + member.send(Message(connection.prefix, "PART", [ + name, partMessage + ], true)); } else { @@ -78,11 +81,11 @@ class Channel { string channelType; - if(modes.canFind('s')) + if (modes.canFind('s')) { channelType = "@"; } - else if(modes.canFind('p')) + else if (modes.canFind('p')) { channelType = "*"; } @@ -93,9 +96,14 @@ class Channel auto onChannel = members.canFind(connection); - connection.send(Message(_server.name, "353", [connection.nick, channelType, name, members.filter!(m => onChannel || !m.modes.canFind('i')).map!(m => prefixedNick(m)).join(' ')], true)); + connection.send(Message(_server.name, "353", [ + connection.nick, channelType, name, + members.filter!(m => onChannel || !m.modes.canFind('i')) + .map!(m => prefixedNick(m)) + .join(' ') + ], true)); - if(sendRplEndOfNames) + if (sendRplEndOfNames) { connection.sendRplEndOfNames(name); } @@ -103,7 +111,7 @@ class Channel void sendPrivMsg(Connection sender, string text) { - foreach(member; members.filter!(m => m.nick != sender.nick)) + foreach (member; members.filter!(m => m.nick != sender.nick)) { member.send(Message(sender.prefix, "PRIVMSG", [name, text], true)); } @@ -111,7 +119,7 @@ class Channel void sendNotice(Connection sender, string text) { - foreach(member; members.filter!(m => m.nick != sender.nick)) + foreach (member; members.filter!(m => m.nick != sender.nick)) { member.send(Message(sender.prefix, "NOTICE", [name, text], true)); } @@ -119,13 +127,17 @@ class Channel void sendTopic(Connection connection) { - if(topic.empty) + if (topic.empty) { - connection.send(Message(_server.name, "331", [connection.nick, name, "No topic is set"])); + connection.send(Message(_server.name, "331", [ + connection.nick, name, "No topic is set" + ])); } else { - connection.send(Message(_server.name, "332", [connection.nick, name, topic], true)); + connection.send(Message(_server.name, "332", [ + connection.nick, name, topic + ], true)); } } @@ -133,7 +145,7 @@ class Channel { topic = newTopic; - foreach(member; members) + foreach (member; members) { member.send(Message(connection.prefix, "TOPIC", [name, newTopic], true)); } @@ -141,9 +153,11 @@ class Channel void kick(Connection kicker, Connection user, string comment) { - foreach(member; members) + foreach (member; members) { - member.send(Message(kicker.prefix, "KICK", [name, user.nick, comment], true)); + member.send(Message(kicker.prefix, "KICK", [ + name, user.nick, comment + ], true)); } members = members.remove!(m => m == user); @@ -155,13 +169,13 @@ class Channel auto specialModes = ""; string[] specialModeParameters; - if(members.canFind(user) && key !is null) + if (members.canFind(user) && key !is null) { specialModes ~= "k"; specialModeParameters ~= key; } - if(members.canFind(user) && !userLimit.isNull) + if (members.canFind(user) && !userLimit.isNull) { import std.conv : to; @@ -169,12 +183,14 @@ class Channel specialModeParameters ~= userLimit.to!string; } - user.send(Message(_server.name, "324", [user.nick, name, "+" ~ modes.idup ~ specialModes] ~ specialModeParameters)); + user.send(Message(_server.name, "324", [ + user.nick, name, "+" ~ modes.idup ~ specialModes + ] ~ specialModeParameters)); } bool setMemberMode(Connection target, char mode) { - if(memberModes[target].canFind(mode)) + if (memberModes[target].canFind(mode)) { return false; } @@ -185,7 +201,7 @@ class Channel bool unsetMemberMode(Connection target, char mode) { - if(!memberModes[target].canFind(mode)) + if (!memberModes[target].canFind(mode)) { return false; } @@ -193,6 +209,7 @@ class Channel //NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges) import std.utf : byCodeUnit; import std.range : array; + memberModes[target] = memberModes[target].byCodeUnit.remove!(m => m == mode).array; return true; @@ -200,7 +217,7 @@ class Channel bool setMode(char mode) { - if(modes.canFind(mode)) + if (modes.canFind(mode)) { return false; } @@ -214,7 +231,7 @@ class Channel bool unsetMode(char mode) { - if(!modes.canFind(mode)) + if (!modes.canFind(mode)) { return false; } @@ -222,6 +239,7 @@ class Channel //NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges) import std.utf : byCodeUnit; import std.range : array; + modes = modes.byCodeUnit.remove!(m => m == mode).array; return true; @@ -229,7 +247,7 @@ class Channel bool addMaskListEntry(string mask, char mode) { - if(maskLists[mode].canFind!(m => m.toIRCLower == mask.toIRCLower)) + if (maskLists[mode].canFind!(m => m.toIRCLower == mask.toIRCLower)) { return false; } @@ -241,7 +259,7 @@ class Channel bool removeMaskListEntry(string mask, char mode) { - if(!maskLists[mode].canFind!(m => m.toIRCLower == mask.toIRCLower)) + if (!maskLists[mode].canFind!(m => m.toIRCLower == mask.toIRCLower)) { return false; } @@ -253,32 +271,44 @@ class Channel void sendBanList(Connection connection) { - foreach(entry; maskLists['b']) + foreach (entry; maskLists['b']) { - connection.send(Message(_server.name, "367", [connection.nick, name, entry], false)); + connection.send(Message(_server.name, "367", [ + connection.nick, name, entry + ], false)); } - connection.send(Message(_server.name, "368", [connection.nick, name, "End of channel ban list"], true)); + connection.send(Message(_server.name, "368", [ + connection.nick, name, "End of channel ban list" + ], true)); } void sendExceptList(Connection connection) { - foreach(entry; maskLists['e']) + foreach (entry; maskLists['e']) { - connection.send(Message(_server.name, "348", [connection.nick, name, entry], false)); + connection.send(Message(_server.name, "348", [ + connection.nick, name, entry + ], false)); } - connection.send(Message(_server.name, "349", [connection.nick, name, "End of channel exception list"], true)); + connection.send(Message(_server.name, "349", [ + connection.nick, name, "End of channel exception list" + ], true)); } void sendInviteList(Connection connection) { - foreach(entry; maskLists['I']) + foreach (entry; maskLists['I']) { - connection.send(Message(_server.name, "346", [connection.nick, name, entry], false)); + connection.send(Message(_server.name, "346", [ + connection.nick, name, entry + ], false)); } - connection.send(Message(_server.name, "347", [connection.nick, name, "End of channel invite list"], true)); + connection.send(Message(_server.name, "347", [ + connection.nick, name, "End of channel invite list" + ], true)); } bool setKey(string key) @@ -290,7 +320,7 @@ class Channel bool unsetKey(string key) { - if(this.key != key) + if (this.key != key) { return false; } @@ -307,7 +337,7 @@ class Channel bool unsetUserLimit() { - if(userLimit.isNull) + if (userLimit.isNull) { return false; } @@ -319,11 +349,11 @@ class Channel string nickPrefix(Connection member) { - if(memberModes[member].canFind('o')) + if (memberModes[member].canFind('o')) { return "@"; } - else if(memberModes[member].canFind('v')) + else if (memberModes[member].canFind('v')) { return "+"; } @@ -331,7 +361,10 @@ class Channel return ""; } - string prefixedNick(Connection member) { return nickPrefix(member) ~ member.nick; } + string prefixedNick(Connection member) + { + return nickPrefix(member) ~ member.nick; + } bool visibleTo(Connection connection) { @@ -340,15 +373,16 @@ class Channel bool canReceiveMessagesFromUser(Connection connection) { - if(modes.canFind('n') && !members.canFind(connection)) + if (modes.canFind('n') && !members.canFind(connection)) { return false; } - else if(modes.canFind('m') && nickPrefix(connection).empty) + else if (modes.canFind('m') && nickPrefix(connection).empty) { return false; } - else if(maskLists['b'].any!(m => connection.matchesMask(m)) && !maskLists['e'].any!(m => connection.matchesMask(m))) + else if (maskLists['b'].any!(m => connection.matchesMask(m)) + && !maskLists['e'].any!(m => connection.matchesMask(m))) { return false; } diff --git a/source/ircd/connection.d b/source/ircd/connection.d index a9c4ed3..1cdfa2e 100644 --- a/source/ircd/connection.d +++ b/source/ircd/connection.d @@ -18,6 +18,7 @@ import ircd.server; import ircd.channel; import ircd.helpers; +//TODO: Make this a struct? class Connection { private TCPConnection _connection; @@ -37,12 +38,30 @@ class Connection string pass = null; - @property auto channels() { return _server.channels.filter!(c => c.members.canFind(this)); } + @property auto channels() + { + return _server.channels.filter!(c => c.members.canFind(this)); + } - @property string prefix() { return nick ~ "!" ~ user ~ "@" ~ hostname; } - @property bool registered() { return nick !is null && user !is null && _server.isPassCorrect(pass); } - @property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); } - @property string servername() { return _server.name; } //TODO: Support server linking + @property string prefix() + { + return nick ~ "!" ~ user ~ "@" ~ hostname; + } + + @property bool registered() + { + return nick !is null && user !is null && _server.isPassCorrect(pass); + } + + @property bool isOperator() + { + return modes.canFind('o') || modes.canFind('O'); + } + + @property string servername() + { + return _server.name; + } //TODO: Support server linking //TODO: Maybe replace string's opEquals (or make a new string class/struct) to compare with toIRCLower //TODO: Read errata @@ -58,7 +77,7 @@ class Connection override int opCmp(Object o) { Connection other; - if((other = cast(Connection)o) !is null) + if ((other = cast(Connection) o) !is null) { return cmp(nick, other.nick); } @@ -68,7 +87,7 @@ class Connection override bool opEquals(Object o) { Connection other; - if((other = cast(Connection)o) !is null) + if ((other = cast(Connection) o) !is null) { return nick == other.nick; } @@ -105,12 +124,14 @@ class Connection //sends the message to all clients who have a channel in common with this client void sendToPeers(Message message) { - if(channels.empty) + if (channels.empty) { return; } - foreach(connection; channels.map!(c => c.members).fold!((a, b) => a ~ b).sort().uniq.filter!(c => c != this)) + foreach (connection; channels.map!(c => c.members) + .fold!((a, b) => a ~ b) + .sort().uniq.filter!(c => c != this)) { connection.send(message); } @@ -123,14 +144,14 @@ class Connection void handle() { - while(connected) + while (connected) { Message message = void; try { - message = Message.fromString((cast(string)_connection.readLine()).chomp); + message = Message.fromString((cast(string) _connection.readLine()).chomp); } - catch(Throwable) + catch (Throwable) { //TODO: The actual Throwable could be useful? connected = _connection.connected; @@ -141,20 +162,21 @@ class Connection //NOTE: The RFCs don't specify what 'being idle' means // We assume that it's sending any message that isn't a PING/PONG. - if(message.command != "PING" && message.command != "PONG") + if (message.command != "PING" && message.command != "PONG") { lastMessageTime = Clock.currTime; } writeln("C> " ~ message.toString); - if(!registered && !["NICK", "USER", "PASS", "PING", "PONG", "QUIT"].canFind(message.command)) + if (!registered && !["NICK", "USER", "PASS", "PING", "PONG", + "QUIT"].canFind(message.command)) { sendErrNotRegistered(); continue; } - switch(message.command) + switch (message.command) { case "NICK": onNick(message); @@ -167,7 +189,9 @@ class Connection break; case "PING": //TODO: Connection timeout when we don't get a PONG - send(Message(_server.name, "PONG", [_server.name, message.parameters[0]], true)); + send(Message(_server.name, "PONG", [ + _server.name, message.parameters[0] + ], true)); break; case "PONG": //TODO: Handle pong @@ -237,7 +261,9 @@ class Connection break; default: writeln("unknown command '", message.command, "'"); - send(Message(_server.name, "421", [nick, message.command, "Unknown command"])); + send(Message(_server.name, "421", [ + nick, message.command, "Unknown command" + ])); continue; } @@ -249,7 +275,7 @@ class Connection void onNick(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { sendErrNoNickGiven(); return; @@ -257,19 +283,23 @@ class Connection auto newNick = message.parameters[0]; - if(!_server.isNickAvailable(newNick) && newNick.toIRCLower != nick.toIRCLower) + if (!_server.isNickAvailable(newNick) && newNick.toIRCLower != nick.toIRCLower) { - send(Message(_server.name, "433", [nick, newNick, "Nickname already in use"])); + send(Message(_server.name, "433", [ + nick, newNick, "Nickname already in use" + ])); return; } - if(!_server.isValidNick(newNick)) + if (!_server.isValidNick(newNick)) { - send(Message(_server.name, "432", [nick, newNick, "Erroneous nickname"])); + send(Message(_server.name, "432", [ + nick, newNick, "Erroneous nickname" + ])); return; } - if(nick !is null) + if (nick !is null) { sendToPeers(Message(nick, "NICK", [newNick])); send(Message(nick, "NICK", [newNick])); @@ -279,7 +309,7 @@ class Connection nick = newNick; - if(!wasRegistered && registered) + if (!wasRegistered && registered) { sendWelcome(); } @@ -287,15 +317,17 @@ class Connection void onUser(Message message) { - if(message.parameters.length < 4) + if (message.parameters.length < 4) { sendErrNeedMoreParams(message.command); return; } - if(user !is null) + if (user !is null) { - send(Message(_server.name, "462", [nick, "Unauthorized command (already registered)"], true)); + send(Message(_server.name, "462", [ + nick, "Unauthorized command (already registered)" + ], true)); return; } @@ -307,7 +339,7 @@ class Connection realname = message.parameters[3]; hostname = getHost(); - if(!wasRegistered && registered) + if (!wasRegistered && registered) { sendWelcome(); } @@ -317,21 +349,23 @@ class Connection { //TODO: Make sure PASS is sent before the NICK/USER combination - if(message.parameters.length < 1) + if (message.parameters.length < 1) { sendErrNeedMoreParams(message.command); return; } - if(registered) + if (registered) { - send(Message(_server.name, "462", [nick, "Unauthorized command (already registered)"], true)); + send(Message(_server.name, "462", [ + nick, "Unauthorized command (already registered)" + ], true)); return; } pass = message.parameters[0]; - if(!_server.isPassCorrect(pass)) + if (!_server.isPassCorrect(pass)) { //NOTE: The RFCs don't allow ERR_PASSWDMISMATCH as a response to PASS //TODO: If RFC-strictness is off, do send ERR_PASSWDMISMATCH @@ -343,7 +377,7 @@ class Connection connected = false; send(Message(_server.name, "ERROR", ["Bye!"])); - if(message.parameters.length > 0) + if (message.parameters.length > 0) { _server.quit(this, message.parameters[0]); } @@ -355,41 +389,47 @@ class Connection void onJoin(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { sendErrNeedMoreParams(message.command); return; } auto channelList = message.parameters[0].split(','); - foreach(channelName; channelList) + foreach (channelName; channelList) { //TODO: Check if the user isn't already on the channel - if(!Server.isValidChannelName(channelName)) + if (!Server.isValidChannelName(channelName)) { sendErrNoSuchChannel(channelName); } else { auto channelRange = _server.findChannelByName(channelName); - if(channelRange.empty) + if (channelRange.empty) { _server.join(this, channelName); } else { auto channel = channelRange[0]; - if(channel.maskLists['b'].any!(m => matchesMask(m))) + if (channel.maskLists['b'].any!(m => matchesMask(m))) { - send(Message(_server.name, "474", [nick, channelName, "Cannot join channel (+b)"], true)); + send(Message(_server.name, "474", [ + nick, channelName, "Cannot join channel (+b)" + ], true)); } - else if(channel.modes.canFind('i') && !(channel.maskLists['I'].any!(m => matchesMask(m)) || channel.inviteHolders.canFind(this))) + else if (channel.modes.canFind('i') + && !(channel.maskLists['I'].any!(m => matchesMask(m)) + || channel.inviteHolders.canFind(this))) { - send(Message(_server.name, "473", [nick, channelName, "Cannot join channel (+i)"], true)); + send(Message(_server.name, "473", [ + nick, channelName, "Cannot join channel (+i)" + ], true)); } //TODO: Implement channel limit //TODO: Implement channel key - else + else { _server.join(this, channelName); } @@ -400,26 +440,29 @@ class Connection void onPart(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { sendErrNeedMoreParams(message.command); return; } auto channelList = message.parameters[0].split(','); - foreach(channel; channelList) + foreach (channel; channelList) { - if(!Server.isValidChannelName(channel)) + if (!Server.isValidChannelName(channel)) { sendErrNoSuchChannel(channel); } - else if(!_server.canFindChannelByName(channel) || !channels.canFind!(c => c.name.toIRCLower == channel.toIRCLower)) + else if (!_server.canFindChannelByName(channel) + || !channels.canFind!(c => c.name.toIRCLower == channel.toIRCLower)) { - send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true)); + send(Message(_server.name, "442", [ + nick, channel, "You're not on that channel" + ], true)); } else { - if(message.parameters.length > 1) + if (message.parameters.length > 1) { _server.part(this, channel, message.parameters[1]); } @@ -437,36 +480,40 @@ class Connection auto target = message.parameters[0]; auto text = message.parameters[1]; - if(message.parameters.length == 0) + if (message.parameters.length == 0) { - send(Message(_server.name, "411", [nick, "No recipient given (PRIVMSG)"], true)); + send(Message(_server.name, "411", [ + nick, "No recipient given (PRIVMSG)" + ], true)); return; } - if(message.parameters.length == 1) + if (message.parameters.length == 1) { send(Message(_server.name, "412", [nick, "No text to send"], true)); return; } - if(Server.isValidChannelName(target)) + if (Server.isValidChannelName(target)) { auto channelRange = _server.findChannelByName(target); - if(channelRange.empty) + if (channelRange.empty) { sendErrNoSuchNick(target); } - else if(!channelRange[0].canReceiveMessagesFromUser(this)) + else if (!channelRange[0].canReceiveMessagesFromUser(this)) { - send(Message(_server.name, "404", [nick, target, "Cannot send to channel"], true)); + send(Message(_server.name, "404", [ + nick, target, "Cannot send to channel" + ], true)); } else { _server.privmsgToChannel(this, target, text); } } - else if(Server.isValidNick(target)) + else if (Server.isValidNick(target)) { - if(!_server.canFindConnectionByNick(target)) + if (!_server.canFindConnectionByNick(target)) { sendErrNoSuchNick(target); } @@ -475,7 +522,7 @@ class Connection _server.privmsgToUser(this, target, text); auto targetUser = _server.findConnectionByNick(target)[0]; - if(targetUser.modes.canFind('a')) + if (targetUser.modes.canFind('a')) { sendRplAway(target, targetUser.awayMessage); } @@ -496,16 +543,16 @@ class Connection //TODO: Figure out what we are allowed to send exactly - if(message.parameters.length < 2) + if (message.parameters.length < 2) { return; } - if(Server.isValidChannelName(target) && _server.canFindChannelByName(target)) + if (Server.isValidChannelName(target) && _server.canFindChannelByName(target)) { _server.noticeToChannel(this, target, text); } - else if(Server.isValidNick(target) && _server.canFindConnectionByNick(target)) + else if (Server.isValidNick(target) && _server.canFindConnectionByNick(target)) { _server.noticeToUser(this, target, text); } @@ -513,7 +560,7 @@ class Connection void onWho(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { _server.whoGlobal(this, "*", false); } @@ -522,7 +569,7 @@ class Connection auto mask = message.parameters[0]; auto operatorsOnly = message.parameters.length > 1 && message.parameters[1] == "o"; - if(_server.isValidChannelName(mask) && _server.canFindChannelByName(mask)) + if (_server.isValidChannelName(mask) && _server.canFindChannelByName(mask)) { _server.whoChannel(this, mask, operatorsOnly); } @@ -538,36 +585,43 @@ class Connection void onAway(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { removeMode('a'); awayMessage = null; - send(Message(_server.name, "305", [nick, "You are no longer marked as being away"], true)); + send(Message(_server.name, "305", [ + nick, "You are no longer marked as being away" + ], true)); } else { modes ~= 'a'; awayMessage = message.parameters[0]; - send(Message(_server.name, "306", [nick, "You have been marked as being away"], true)); + send(Message(_server.name, "306", [ + nick, "You have been marked as being away" + ], true)); } } void onTopic(Message message) { - if(message.parameters.length == 0) + if (message.parameters.length == 0) { sendErrNeedMoreParams(message.command); return; } auto channelName = message.parameters[0]; - if(message.parameters.length == 1) + if (message.parameters.length == 1) { - if(!_server.channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower && (!(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 - send(Message(_server.name, "331", [nick, channelName, "No topic is set"], true)); + send(Message(_server.name, "331", [ + nick, channelName, "No topic is set" + ], true)); } else { @@ -577,11 +631,13 @@ class Connection else { auto newTopic = message.parameters[1]; - if(!channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower)) + if (!channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower)) { sendErrNotOnChannel(channelName); } - else if(channels.find!(c => c.name.toIRCLower == channelName.toIRCLower).map!(c => c.modes.canFind('t') && !c.memberModes[this].canFind('o')).array[0]) + else if (channels.find!(c => c.name.toIRCLower == channelName.toIRCLower) + .map!(c => c.modes.canFind('t') && !c.memberModes[this].canFind('o')) + .array[0]) { sendErrChanopPrivsNeeded(channelName); } @@ -594,21 +650,22 @@ class Connection void onNames(Message message) { - if(message.parameters.length > 1) + if (message.parameters.length > 1) { notImplemented("forwarding NAMES to another server"); return; } - if(message.parameters.length == 0) + if (message.parameters.length == 0) { _server.sendGlobalNames(this); } else { - foreach(channelName; message.parameters[0].split(',')) + foreach (channelName; message.parameters[0].split(',')) { - if(_server.channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower && c.visibleTo(this))) + if (_server.channels.canFind!(c => c.name.toIRCLower == channelName.toIRCLower + && c.visibleTo(this))) { _server.sendChannelNames(this, channelName); } @@ -622,13 +679,13 @@ class Connection void onList(Message message) { - if(message.parameters.length > 1) + if (message.parameters.length > 1) { notImplemented("forwarding LIST to another server"); return; } - if(message.parameters.length == 0) + if (message.parameters.length == 0) { _server.sendFullList(this); } @@ -641,7 +698,7 @@ class Connection void onInvite(Message message) { - if(message.parameters.length < 2) + if (message.parameters.length < 2) { sendErrNeedMoreParams(message.command); return; @@ -649,7 +706,7 @@ class Connection auto targetNick = message.parameters[0]; auto targetUserRange = _server.findConnectionByNick(targetNick); - if(targetUserRange.empty) + if (targetUserRange.empty) { sendErrNoSuchNick(targetNick); return; @@ -658,13 +715,13 @@ class Connection auto channelName = message.parameters[1]; auto channelRange = _server.findChannelByName(channelName); - if(channelRange.empty) + if (channelRange.empty) { _server.invite(this, targetUser.nick, channelName); sendRplInviting(channelName, targetUser.nick); - if(targetUser.modes.canFind('a')) + if (targetUser.modes.canFind('a')) { sendRplAway(targetUser.nick, targetUser.awayMessage); } @@ -672,15 +729,18 @@ class Connection else { auto channel = channelRange[0]; - if(!channel.members.canFind(this)) + if (!channel.members.canFind(this)) { sendErrNotOnChannel(channel.name); } - else if(channel.members.canFind(targetUser)) + else if (channel.members.canFind(targetUser)) { - send(Message(_server.name, "443", [nick, targetUser.nick, channel.name, "is already on channel"], true)); + send(Message(_server.name, "443", [ + nick, targetUser.nick, channel.name, + "is already on channel" + ], true)); } - else if(channel.modes.canFind('i') && !channel.memberModes[this].canFind('o')) + else if (channel.modes.canFind('i') && !channel.memberModes[this].canFind('o')) { sendErrChanopPrivsNeeded(channel.name); } @@ -690,7 +750,7 @@ class Connection sendRplInviting(channel.name, targetUser.nick); - if(targetUser.modes.canFind('a')) + if (targetUser.modes.canFind('a')) { sendRplAway(targetUser.nick, targetUser.awayMessage); } @@ -700,7 +760,7 @@ class Connection void onVersion(Message message) { - if(message.parameters.length > 0) + if (message.parameters.length > 0) { notImplemented("querying the version of another server"); return; @@ -710,7 +770,7 @@ class Connection void onTime(Message message) { - if(message.parameters.length > 0) + if (message.parameters.length > 0) { notImplemented("querying the time of another server"); return; @@ -720,12 +780,12 @@ class Connection void onMotd(Message message) { - if(message.parameters.length > 0) + if (message.parameters.length > 0) { notImplemented("querying the motd of another server"); return; } - else if(_server.motd is null) + else if (_server.motd is null) { send(Message(_server.name, "422", [nick, "MOTD File is missing"], true)); return; @@ -735,12 +795,12 @@ class Connection void onLusers(Message message) { - if(message.parameters.length == 1) + if (message.parameters.length == 1) { notImplemented("querying the size of a part of the network"); return; } - else if(message.parameters.length > 1) + else if (message.parameters.length > 1) { notImplemented("forwarding LUSERS to another server"); return; @@ -750,7 +810,7 @@ class Connection void onIson(Message message) { - if(message.parameters.length < 1) + if (message.parameters.length < 1) { sendErrNeedMoreParams(message.command); return; @@ -766,12 +826,12 @@ class Connection void onWhois(Message message) { - if(message.parameters.length < 1) + if (message.parameters.length < 1) { sendErrNoNickGiven(); return; } - else if(message.parameters.length > 1) + else if (message.parameters.length > 1) { notImplemented("forwarding WHOIS to another server"); return; @@ -779,7 +839,8 @@ class Connection auto mask = message.parameters[0]; //TODO: Support user masks - if(!_server.canFindConnectionByNick(mask) || !_server.findConnectionByNick(mask)[0].visibleTo(this)) + if (!_server.canFindConnectionByNick(mask) + || !_server.findConnectionByNick(mask)[0].visibleTo(this)) { sendErrNoSuchNick(mask); } @@ -793,20 +854,20 @@ class Connection void onKill(Message message) { - if(!isOperator) + if (!isOperator) { sendErrNoPrivileges(); return; } - if(message.parameters.length < 2) + if (message.parameters.length < 2) { sendErrNeedMoreParams(message.command); return; } auto nick = message.parameters[0]; - if(!_server.canFindConnectionByNick(nick)) + if (!_server.canFindConnectionByNick(nick)) { sendErrNoSuchNick(nick); return; @@ -819,7 +880,7 @@ class Connection void onKick(Message message) { - if(message.parameters.length < 2) + if (message.parameters.length < 2) { sendErrNeedMoreParams(message.command); return; @@ -829,37 +890,37 @@ class Connection auto userList = message.parameters[1].split(','); auto comment = message.parameters.length > 2 ? message.parameters[2] : nick; - if(channelList.length != 1 && channelList.length != userList.length) + if (channelList.length != 1 && channelList.length != userList.length) { //TODO: Figure out what the right error is here sendErrNeedMoreParams(message.command); return; } - foreach(i, nick; userList) + foreach (i, nick; userList) { auto channelName = channelList[0]; - if(channelList.length != 1) + if (channelList.length != 1) { channelName = channelList[i]; } - if(!_server.canFindChannelByName(channelName)) + if (!_server.canFindChannelByName(channelName)) { sendErrNoSuchChannel(channelName); } else { auto channel = _server.findChannelByName(channelName)[0]; - if(!channel.members.canFind(this)) + if (!channel.members.canFind(this)) { sendErrNotOnChannel(channelName); } - else if(!channel.memberModes[this].canFind('o')) + else if (!channel.memberModes[this].canFind('o')) { sendErrChanopPrivsNeeded(channelName); } - else if(!channel.members.canFind!(m => m.nick.toIRCLower == nick.toIRCLower)) + else if (!channel.members.canFind!(m => m.nick.toIRCLower == nick.toIRCLower)) { sendErrUserNotInChannel(nick, channelName); } @@ -873,18 +934,18 @@ class Connection void onMode(Message message) { - if(message.parameters.empty) + if (message.parameters.empty) { sendErrNeedMoreParams(message.command); return; } auto target = message.parameters[0]; - if(Server.isValidNick(target)) + if (Server.isValidNick(target)) { onUserMode(message); } - else if(Server.isValidChannelName(target)) + else if (Server.isValidChannelName(target)) { onChannelMode(message); } @@ -898,57 +959,65 @@ class Connection { auto target = message.parameters[0]; - if(target.toIRCLower != nick.toIRCLower) + if (target.toIRCLower != nick.toIRCLower) { //TODO: If RFC-strictness is off, use a different error message when viewing modes and when changing modes - send(Message(_server.name, "502", [nick, "Cannot change mode for other users"], true)); + send(Message(_server.name, "502", [ + nick, "Cannot change mode for other users" + ], true)); return; } - if(message.parameters.length == 1) + if (message.parameters.length == 1) { send(Message(_server.name, "221", [nick, "+" ~ modes.idup])); } else { - foreach(modeString; message.parameters[1 .. $]) + foreach (modeString; message.parameters[1 .. $]) { auto add = modeString[0] == '+'; - if(!add && modeString[0] != '-') + if (!add && modeString[0] != '-') { //TODO: If RFC-strictness is off, send a malformed message error continue; } - if(modeString.length == 1) + if (modeString.length == 1) { continue; } auto changedModes = modeString[1 .. $]; - foreach(mode; changedModes) + foreach (mode; changedModes) { //when RFC-strictness is off, maybe send an error when trying to do an illegal change - switch(mode) + switch (mode) { case 'i': - case 'w': - case 's': - if(add) modes ~= mode; - else removeMode(mode); + case 'w': + case 's': + if (add) + modes ~= mode; + else + removeMode(mode); break; case 'o': - case 'O': - if(!add) removeMode(mode); + case 'O': + if (!add) + removeMode(mode); break; case 'r': - if(add) modes ~= 'r'; + if (add) + modes ~= 'r'; break; case 'a': //ignore break; default: - send(Message(_server.name, "501", [nick, "Unknown MODE flag"], true)); + send(Message(_server.name, "501", [ + nick, "Unknown MODE flag" + ], true)); break; } } @@ -959,22 +1028,22 @@ class Connection void onChannelMode(Message message) { auto channelRange = _server.findChannelByName(message.parameters[0]); - if(channelRange.empty) + if (channelRange.empty) { //TODO: If RFC-strictness is off, send an error message when the channel doesn't exist return; } auto channel = channelRange[0]; - if(message.parameters.length == 1) + if (message.parameters.length == 1) { channel.sendModes(this); } //TODO: If RFC-strictness is off, also allow '+e' and '+I' (?) - else if(message.parameters.length == 2 && ["+b", "e", "I"].canFind(message.parameters[1])) + else if (message.parameters.length == 2 && ["+b", "e", "I"].canFind(message.parameters[1])) { auto listChar = message.parameters[1][$ - 1]; - final switch(listChar) + final switch (listChar) { case 'b': channel.sendBanList(this); @@ -989,23 +1058,23 @@ class Connection } else { - if(!channel.memberModes[this].canFind('o')) + if (!channel.memberModes[this].canFind('o')) { sendErrChanopPrivsNeeded(channel.name); return; } - for(auto i = 1; i < message.parameters.length; i++) + for (auto i = 1; i < message.parameters.length; i++) { auto modeString = message.parameters[i]; auto add = modeString[0] == '+'; - if(!add && modeString[0] != '-') + if (!add && modeString[0] != '-') { //TODO: If RFC-strictness is off, send a malformed message error return; } - if(modeString.length == 1) + if (modeString.length == 1) { continue; } @@ -1014,17 +1083,16 @@ class Connection string[] processedParameters; auto changedModes = modeString[1 .. $]; -Lforeach: - foreach(mode; changedModes) + Lforeach: foreach (mode; changedModes) { //when RFC-strictness is off, maybe send an error when trying to do an illegal change - switch(mode) + switch (mode) { //TODO: If RFC-strictness is on, limit mode changes with parameter to 3 per command case 'o': - case 'v': - if(i + 1 == message.parameters.length) + case 'v': + if (i + 1 == message.parameters.length) { //TODO: Figure out what to do when we need more mode parameters break Lforeach; @@ -1032,55 +1100,59 @@ Lforeach: auto memberNick = message.parameters[++i]; auto memberRange = _server.findConnectionByNick(memberNick); - if(memberRange.empty) + if (memberRange.empty) { sendErrNoSuchNick(memberNick); break Lforeach; } auto member = memberRange[0]; - if(!channel.members.canFind(member)) + if (!channel.members.canFind(member)) { sendErrUserNotInChannel(memberNick, channel.name); break Lforeach; } bool success; - if(add) success = channel.setMemberMode(member, mode); - else success = channel.unsetMemberMode(member, mode); - if(success) + if (add) + success = channel.setMemberMode(member, mode); + else + success = channel.unsetMemberMode(member, mode); + if (success) { processedModes ~= mode; processedParameters ~= memberNick; } break; case 'b': //TODO: Implement bans - case 'e': //TODO: Implement ban exceptions - case 'I': //TODO: Implement invite lists - if(i + 1 == message.parameters.length) + case 'e': //TODO: Implement ban exceptions + case 'I': //TODO: Implement invite lists + if (i + 1 == message.parameters.length) { //TODO: Figure out what to do when we need more mode parameters break Lforeach; } auto mask = message.parameters[++i]; //TODO: If RFC-strictness is off, interpret '' as '!*@*' - if(!Server.isValidUserMask(mask)) + if (!Server.isValidUserMask(mask)) { //TODO: If RFC-strictness is off, send an error break Lforeach; } bool success; - if(add) success = channel.addMaskListEntry(mask, mode); - else success = channel.removeMaskListEntry(mask, mode); - if(success) + if (add) + success = channel.addMaskListEntry(mask, mode); + else + success = channel.removeMaskListEntry(mask, mode); + if (success) { processedModes ~= mode; processedParameters ~= mask; } break; case 'k': //TODO: Implement channel key - if(i + 1 == message.parameters.length) + if (i + 1 == message.parameters.length) { //TODO: Figure out what to do when we need more mode parameters break Lforeach; @@ -1088,18 +1160,20 @@ Lforeach: auto key = message.parameters[++i]; bool success; - if(add) success = channel.setKey(key); - else success = channel.unsetKey(key); - if(success) + if (add) + success = channel.setKey(key); + else + success = channel.unsetKey(key); + if (success) { processedModes ~= mode; processedParameters ~= key; } break; case 'l': //TODO: Implement user limit - if(add) + if (add) { - if(i + 1 == message.parameters.length) + if (i + 1 == message.parameters.length) { //TODO: Figure out what to do when we need more mode parameters break Lforeach; @@ -1111,7 +1185,7 @@ Lforeach: { limit = limitString.to!uint; } - catch(Throwable) + catch (Throwable) { break Lforeach; } @@ -1123,37 +1197,44 @@ Lforeach: } else { - if(channel.unsetUserLimit()) + if (channel.unsetUserLimit()) { processedModes ~= mode; } } break; case 'i': //TODO: Implement invite-only channels - case 'm': //TODO: Implement channel moderation - case 'n': //TODO: Implement the no messages from clients on the outside flag - case 'p': - case 's': - case 't': + case 'm': //TODO: Implement channel moderation + case 'n': //TODO: Implement the no messages from clients on the outside flag + case 'p': + case 's': + case 't': bool success; - if(add) success = channel.setMode(mode); - else success = channel.unsetMode(mode); - if(success) + if (add) + success = channel.setMode(mode); + else + success = channel.unsetMode(mode); + if (success) { processedModes ~= mode; } break; default: - send(Message(_server.name, "472", [nick, [mode], "is unknown mode char to me for " ~ channel.name], true)); + send(Message(_server.name, "472", [ + nick, [mode], + "is unknown mode char to me for " ~ channel.name + ], true)); break; } } - if(!processedModes.empty) + if (!processedModes.empty) { - foreach(member; channel.members) + foreach (member; channel.members) { - member.send(Message(prefix, "MODE", [channel.name, (add ? '+' : '-') ~ processedModes.idup] ~ processedParameters, false)); + member.send(Message(prefix, "MODE", [ + channel.name, (add ? '+' : '-') ~ processedModes.idup + ] ~ processedParameters, false)); } } } @@ -1162,7 +1243,7 @@ Lforeach: void onStats(Message message) { - if(message.parameters.length > 1) + if (message.parameters.length > 1) { notImplemented("forwarding STATS to another other server"); return; @@ -1170,7 +1251,7 @@ Lforeach: char statsLetter = message.parameters.length > 0 ? message.parameters[0][0] : '*'; - switch(statsLetter) + switch (statsLetter) { case 'l': notImplemented("STATS server link information"); @@ -1188,16 +1269,22 @@ Lforeach: break; } - send(Message(_server.name, "219", [nick, [statsLetter].idup, "End of STATS report"], true)); + send(Message(_server.name, "219", [ + nick, [statsLetter].idup, "End of STATS report" + ], true)); } void sendWhoReply(string channel, Connection user, string nickPrefix, uint hopCount) { auto flags = user.modes.canFind('a') ? "G" : "H"; - if(user.isOperator) flags ~= "*"; + if (user.isOperator) + flags ~= "*"; flags ~= nickPrefix; - send(Message(_server.name, "352", [nick, channel, user.user, user.hostname, user.servername, user.nick, flags, hopCount.to!string ~ " " ~ user.realname], true)); + send(Message(_server.name, "352", [ + nick, channel, user.user, user.hostname, user.servername, + user.nick, flags, hopCount.to!string ~ " " ~ user.realname + ], true)); } void sendRplAway(string target, string message) @@ -1207,7 +1294,9 @@ Lforeach: void sendRplList(string channelName, ulong visibleCount, string topic) { - send(Message(_server.name, "322", [nick, channelName, visibleCount.to!string, topic], true)); + send(Message(_server.name, "322", [ + nick, channelName, visibleCount.to!string, topic + ], true)); } void sendRplListEnd() @@ -1223,7 +1312,9 @@ Lforeach: void sendRplEndOfNames(string channelName) { - send(Message(_server.name, "366", [nick, channelName, "End of NAMES list"], true)); + send(Message(_server.name, "366", [ + nick, channelName, "End of NAMES list" + ], true)); } void sendErrNoSuchNick(string name) @@ -1243,12 +1334,16 @@ Lforeach: void sendErrUserNotInChannel(string otherNick, string channel) { - send(Message(_server.name, "441", [nick, otherNick, channel, "They aren't on that channel"], true)); + send(Message(_server.name, "441", [ + nick, otherNick, channel, "They aren't on that channel" + ], true)); } void sendErrNotOnChannel(string channel) { - send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true)); + send(Message(_server.name, "442", [ + nick, channel, "You're not on that channel" + ], true)); } void sendErrNotRegistered() @@ -1258,27 +1353,37 @@ Lforeach: void sendErrNeedMoreParams(string command) { - send(Message(_server.name, "461", [nick, command, "Not enough parameters"], true)); + send(Message(_server.name, "461", [ + nick, command, "Not enough parameters" + ], true)); } void sendErrNoPrivileges() { - send(Message(_server.name, "481", [nick, "Permission Denied- You're not an IRC operator"], true)); + send(Message(_server.name, "481", [ + nick, "Permission Denied- You're not an IRC operator" + ], true)); } void sendErrChanopPrivsNeeded(string channel) { - send(Message(_server.name, "482", [nick, channel, "You're not channel operator"], true)); + send(Message(_server.name, "482", [ + nick, channel, "You're not channel operator" + ], true)); } void notImplemented(string description) { - send(Message(_server.name, "ERROR", ["Not implemented yet (" ~ description ~ ")"], true)); + send(Message(_server.name, "ERROR", [ + "Not implemented yet (" ~ description ~ ")" + ], true)); } void sendWelcome() { - send(Message(_server.name, "001", [nick, "Welcome to the Internet Relay Network " ~ prefix], true)); + send(Message(_server.name, "001", [ + nick, "Welcome to the Internet Relay Network " ~ prefix + ], true)); //TODO: If RFC-strictness is off, also send 002, 003, and 004 } @@ -1287,7 +1392,7 @@ Lforeach: { auto address = parseAddress(_connection.remoteAddress.toAddressString); auto hostname = address.toHostNameString; - if(hostname is null) + if (hostname is null) { hostname = address.toAddrString; @@ -1305,11 +1410,11 @@ Lforeach: char[] modes; - if(mask & 0b100) + if (mask & 0b100) { modes ~= 'w'; } - if(mask & 0b1000) + if (mask & 0b1000) { modes ~= 'i'; } @@ -1323,4 +1428,3 @@ Lforeach: modes = modes.byCodeUnit.remove!(a => a == mode).array; } } - diff --git a/source/ircd/helpers.d b/source/ircd/helpers.d index 83b2998..7eca66b 100644 --- a/source/ircd/helpers.d +++ b/source/ircd/helpers.d @@ -6,8 +6,7 @@ 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) //Copyright (c) Lars T. Kyllingstad, Walter Bright -@safe pure -bool wildcardMatch(string input, string pattern) +@safe pure bool wildcardMatch(string input, string pattern) { foreach (ref pi; 0 .. pattern.length) { @@ -47,11 +46,11 @@ bool wildcardMatch(string input, string pattern) return input.empty; } -@safe pure -dchar toIRCLower(dchar input) +@safe pure dchar toIRCLower(dchar input) { import std.uni : toLower; - switch(input) + + switch (input) { case '[': return '{'; @@ -64,9 +63,9 @@ dchar toIRCLower(dchar input) } } -@safe pure -string toIRCLower(string input) +@safe pure string toIRCLower(string input) { import std.utf : byChar; + return input.map!toIRCLower.byChar.array.idup; } diff --git a/source/ircd/message.d b/source/ircd/message.d index bad391a..4ef6348 100644 --- a/source/ircd/message.d +++ b/source/ircd/message.d @@ -6,7 +6,6 @@ import std.array; import std.algorithm; import std.conv; -//TODO: Make this a class struct Message { string prefix; @@ -20,7 +19,7 @@ struct Message static Message fromString(string line) { string prefix = null; - if(line.startsWith(':')) + if (line.startsWith(':')) { line = line[1 .. $]; prefix = line[0 .. line.indexOf(' ')]; @@ -28,7 +27,7 @@ struct Message } //stop early when no space character can be found (message without parameters) - if(!line.canFind(' ')) + if (!line.canFind(' ')) { return Message(prefix, line, [], false); } @@ -38,15 +37,15 @@ struct Message auto bytes = line.length; string[] params = []; bool prefixedParam; - while(true) + while (true) { - if(line.startsWith(':')) + if (line.startsWith(':')) { params ~= line[1 .. $]; prefixedParam = true; break; } - else if(line.canFind(' ')) + else if (line.canFind(' ')) { auto param = line[0 .. line.indexOf(' ')]; line = line[param.length + 1 .. $]; @@ -65,29 +64,28 @@ struct Message string toString() { auto message = ""; - if(prefix != null) + if (prefix != null) { message = ":" ~ prefix ~ " "; } - if(parameters.length == 0) + if (parameters.length == 0) { return message ~ command; } message ~= command ~ " "; - if(parameters.length > 1) + if (parameters.length > 1) { - message ~= parameters[0 .. $-1].join(' ') ~ " "; + message ~= parameters[0 .. $ - 1].join(' ') ~ " "; } - if(parameters[$-1].canFind(' ') || prefixedParameter) + if (parameters[$ - 1].canFind(' ') || prefixedParameter) { message ~= ":"; } - message ~= parameters[$-1]; + message ~= parameters[$ - 1]; return message; } } - diff --git a/source/ircd/server.d b/source/ircd/server.d index 39fc46b..42aa76a 100644 --- a/source/ircd/server.d +++ b/source/ircd/server.d @@ -19,6 +19,7 @@ import ircd.connection; import ircd.channel; import ircd.helpers; +//TODO: Make this a struct? class Server { Connection[] connections; @@ -53,7 +54,8 @@ class Server private void readMotd() { import std.file : exists, readText; - if(exists("motd")) + + if (exists("motd")) { motd = readText("motd"); } @@ -61,9 +63,9 @@ class Server private void pingLoop() { - while(true) + while (true) { - foreach(connection; connections) + foreach (connection; connections) { connection.send(Message(null, "PING", [name], true)); } @@ -88,14 +90,14 @@ class Server { import std.ascii : digits, letters; - if(name.length > 9) + if (name.length > 9) { return false; } - foreach(i, c; name) + foreach (i, c; name) { auto allowed = letters ~ "[]\\`_^{|}"; - if(i > 0) + if (i > 0) { allowed ~= digits ~ "-"; } @@ -145,7 +147,7 @@ class Server { auto channelRange = findChannelByName(channelName); Channel channel; - if(channelRange.empty) + if (channelRange.empty) { channel = new Channel(channelName, this); channels ~= channel; @@ -156,14 +158,14 @@ class Server } channel.join(connection); - foreach(member; channel.members) + foreach (member; channel.members) { member.send(Message(connection.prefix, "JOIN", [channelName])); } channel.sendNames(connection); - if(!channel.topic.empty) + if (!channel.topic.empty) { channel.sendTopic(connection); } @@ -171,11 +173,12 @@ class Server void part(Connection connection, string channelName, string partMessage) { - auto channel = connection.channels.array.find!(c => c.name.toIRCLower == channelName.toIRCLower)[0]; + auto channel = connection.channels.array.find!( + c => c.name.toIRCLower == channelName.toIRCLower)[0]; channel.part(connection, partMessage); - if(channel.members.empty) + if (channel.members.empty) { channels = channels.remove!(c => c == channel); } @@ -184,20 +187,20 @@ class Server void quit(Connection connection, string quitMessage) { Connection[] peers; - foreach(channel; connection.channels) + foreach (channel; connection.channels) { peers ~= channel.members; channel.members = channel.members.remove!(m => m == connection); - if(channel.members.empty) + if (channel.members.empty) { channels = channels.remove!(c => c == channel); } } peers = peers.sort().uniq.filter!(c => c != connection).array; - foreach(peer; peers) + foreach (peer; peers) { - if(quitMessage !is null) + if (quitMessage !is null) { peer.send(Message(connection.prefix, "QUIT", [quitMessage], true)); } @@ -213,8 +216,9 @@ class Server //TODO: Check what RFCs say about secret/private channels auto channel = findChannelByName(channelName)[0]; - foreach(c; channel.members.filter!(c => !operatorsOnly || c.isOperator) - .filter!(c => c.visibleTo(origin))) + foreach (c; channel.members + .filter!(c => !operatorsOnly || c.isOperator) + .filter!(c => c.visibleTo(origin))) { //TODO: Support hop count origin.sendWhoReply(channelName, c, channel.nickPrefix(c), 0); @@ -223,9 +227,10 @@ class Server void whoGlobal(Connection origin, string mask, bool operatorsOnly) { - foreach(c; connections.filter!(c => c.visibleTo(origin)) - .filter!(c => !operatorsOnly || c.isOperator) - .filter!(c => [c.hostname, c.servername, c.realname, c.nick].any!(n => wildcardMatch(n, mask)))) + foreach (c; connections.filter!(c => c.visibleTo(origin)) + .filter!(c => !operatorsOnly || c.isOperator) + .filter!(c => [c.hostname, c.servername, c.realname, + c.nick].any!(n => wildcardMatch(n, mask)))) { //TODO: Don't leak secret/private channels if RFC-strictness is off (the RFCs don't seem to say anything about it?) auto channelName = c.channels.empty ? "*" : c.channels.array[0].name; @@ -279,15 +284,20 @@ class Server void sendGlobalNames(Connection connection) { - foreach(channel; channels.filter!(c => c.visibleTo(connection))) + foreach (channel; channels.filter!(c => c.visibleTo(connection))) { channel.sendNames(connection, false); } - auto otherUsers = connections.filter!(c => !c.modes.canFind('i') && c.channels.filter!(ch => !ch.modes.canFind('s') && !ch.modes.canFind('p')).empty); - if(!otherUsers.empty) + auto otherUsers = connections.filter!(c => !c.modes.canFind('i') + && c.channels.filter!(ch => !ch.modes.canFind('s') + && !ch.modes.canFind('p')).empty); + if (!otherUsers.empty) { - connection.send(Message(name, "353", [connection.nick, "=", "*", otherUsers.map!(m => m.nick).join(' ')], true)); + connection.send(Message(name, "353", [ + connection.nick, "=", "*", + otherUsers.map!(m => m.nick).join(' ') + ], true)); } connection.sendRplEndOfNames("*"); @@ -295,25 +305,32 @@ class Server void sendFullList(Connection connection) { - foreach(channel; channels.filter!(c => c.visibleTo(connection))) + foreach (channel; channels.filter!(c => c.visibleTo(connection))) { - connection.sendRplList(channel.name, channel.members.filter!(m => m.visibleTo(connection)).array.length, channel.topic); + connection.sendRplList(channel.name, + channel.members.filter!(m => m.visibleTo(connection)) + .array.length, channel.topic); } connection.sendRplListEnd(); } void sendPartialList(Connection connection, string[] channelNames) { - foreach(channel; channels.filter!(c => channelNames.canFind(c.name) && c.visibleTo(connection))) + foreach (channel; channels.filter!(c => channelNames.canFind(c.name) + && c.visibleTo(connection))) { - connection.sendRplList(channel.name, channel.members.filter!(m => m.visibleTo(connection)).array.length, channel.topic); + connection.sendRplList(channel.name, + channel.members.filter!(m => m.visibleTo(connection)) + .array.length, channel.topic); } connection.sendRplListEnd(); } void sendVersion(Connection connection) { - connection.send(Message(name, "351", [connection.nick, versionString ~ ".", name, ""], true)); + connection.send(Message(name, "351", [ + connection.nick, versionString ~ ".", name, "" + ], true)); } void sendTime(Connection connection) @@ -334,13 +351,17 @@ class Server void sendMotd(Connection connection) { - connection.send(Message(name, "375", [connection.nick, ":- " ~ name ~ " Message of the day - "], true)); - foreach(line; motd.splitLines) + connection.send(Message(name, "375", [ + connection.nick, ":- " ~ name ~ " Message of the day - " + ], true)); + foreach (line; motd.splitLines) { //TODO: Implement line wrapping connection.send(Message(name, "372", [connection.nick, ":- " ~ line], true)); } - connection.send(Message(name, "376", [connection.nick, "End of MOTD command"], true)); + connection.send(Message(name, "376", [ + connection.nick, "End of MOTD command" + ], true)); } void sendLusers(Connection connection) @@ -348,24 +369,43 @@ class Server //TODO: If RFC-strictness is off, use '1 server' instead of '1 servers' if the network (or the part of the network of the query) has only one server //TODO: Support services and multiple servers - connection.send(Message(name, "251", [connection.nick, "There are " ~ connections.filter!(c => c.registered).count.to!string ~ " users and 0 services on 1 servers"], true)); + connection.send(Message(name, "251", [ + connection.nick, + "There are " ~ connections.filter!(c => c.registered) + .count + .to!string ~ " users and 0 services on 1 servers" + ], true)); - if(connections.any!(c => c.isOperator)) + if (connections.any!(c => c.isOperator)) { - connection.send(Message(name, "252", [connection.nick, connections.count!(c => c.isOperator).to!string, "operator(s) online"], true)); + connection.send(Message(name, "252", [ + connection.nick, + connections.count!(c => c.isOperator) + .to!string, "operator(s) online" + ], true)); } - if(connections.any!(c => !c.registered)) + if (connections.any!(c => !c.registered)) { - connection.send(Message(name, "253", [connection.nick, connections.count!(c => !c.registered).to!string, "unknown connection(s)"], true)); + connection.send(Message(name, "253", [ + connection.nick, + connections.count!(c => !c.registered) + .to!string, "unknown connection(s)" + ], true)); } - if(channels.length > 0) + if (channels.length > 0) { - connection.send(Message(name, "254", [connection.nick, channels.length.to!string, "channels formed"], true)); + connection.send(Message(name, "254", [ + connection.nick, channels.length.to!string, + "channels formed" + ], true)); } - connection.send(Message(name, "255", [connection.nick, "I have " ~ connections.length.to!string ~ " clients and 1 servers"], true)); + connection.send(Message(name, "255", [ + connection.nick, + "I have " ~ connections.length.to!string ~ " clients and 1 servers" + ], true)); } void ison(Connection connection, string[] nicks) @@ -379,17 +419,29 @@ class Server { auto user = findConnectionByNick(mask)[0]; - connection.send(Message(name, "311", [connection.nick, user.nick, user.user, user.hostname, "*", user.hostname], true)); + connection.send(Message(name, "311", [ + connection.nick, user.nick, user.user, user.hostname, "*", + user.hostname + ], true)); //TODO: Send information about the user's actual server (which is not necessarily this one) - connection.send(Message(name, "312", [connection.nick, user.nick, name, info], true)); - if(user.isOperator) + connection.send(Message(name, "312", [ + connection.nick, user.nick, name, info + ], true)); + if (user.isOperator) { - connection.send(Message(name, "313", [connection.nick, user.nick, "is an IRC operator"], true)); + connection.send(Message(name, "313", [ + connection.nick, user.nick, "is an IRC operator" + ], true)); } auto idleSeconds = (Clock.currTime - user.lastMessageTime).total!"seconds"; - connection.send(Message(name, "317", [connection.nick, user.nick, idleSeconds.to!string, "seconds idle"], true)); + connection.send(Message(name, "317", [ + connection.nick, user.nick, idleSeconds.to!string, + "seconds idle" + ], true)); auto userChannels = user.channels.map!(c => c.nickPrefix(user) ~ c.name).join(' '); - connection.send(Message(name, "319", [connection.nick, user.nick, userChannels], true)); + connection.send(Message(name, "319", [ + connection.nick, user.nick, userChannels + ], true)); } void kill(Connection killer, string nick, string comment) @@ -400,7 +452,8 @@ class Server quit(user, "Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"); - user.send(Message(null, "ERROR", ["Closing Link: Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"], true)); + user.send(Message(null, "ERROR", + ["Closing Link: Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"], true)); user.closeConnection(); } @@ -415,7 +468,7 @@ class Server void updateCommandStatistics(Message message) { auto command = message.command.toUpper; - if(command !in _commandUsage) + if (command !in _commandUsage) { _commandUsage[command] = 0; _commandBytes[command] = 0; @@ -426,10 +479,13 @@ class Server void sendCommandUsage(Connection connection) { - foreach(command, count; _commandUsage) + foreach (command, count; _commandUsage) { //TODO: Implement remote count - connection.send(Message(name, "212", [connection.nick, command, count.to!string, _commandBytes[command].to!string, "0"], false)); + connection.send(Message(name, "212", [ + connection.nick, command, count.to!string, + _commandBytes[command].to!string, "0" + ], false)); } } @@ -439,7 +495,8 @@ class Server auto uptime = (Clock.currTime - _startTime).split!("days", "hours", "minutes", "seconds"); - auto uptimeString = format!"Server Up %d days %d:%02d:%02d"(uptime.days, uptime.hours, uptime.minutes, uptime.seconds); + auto uptimeString = format!"Server Up %d days %d:%02d:%02d"(uptime.days, + uptime.hours, uptime.minutes, uptime.seconds); connection.send(Message(name, "242", [connection.nick, uptimeString], true)); }