Format code (with dfmt --align_switch_statements false)

This commit is contained in:
Les De Ridder 2020-02-12 13:59:41 +01:00
parent 3b93ecc60e
commit f80070aa92
6 changed files with 504 additions and 313 deletions

View File

@ -10,7 +10,7 @@ import ircd.server;
static T tagValueOrNull(T)(Tag tag, string childName) static T tagValueOrNull(T)(Tag tag, string childName)
{ {
if(childName !in tag.tags) if (childName !in tag.tags)
{ {
return null; return null;
} }
@ -22,7 +22,7 @@ static T tagValueOrNull(T)(Tag tag, string childName)
static T tagValue(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[]) template U(T : T[])
{ {
@ -31,16 +31,16 @@ static T tagValue(T)(Tag tag, string childName)
T array = []; T array = [];
foreach(value; tag.tags[childName][0].values) foreach (value; tag.tags[childName][0].values)
{ {
array ~= value.get!(U!T); array ~= value.get!(U!T);
} }
return array; 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 else
{ {
@ -57,17 +57,16 @@ shared static this()
auto pass = config.tagValue!string("pass"); auto pass = config.tagValue!string("pass");
server.setPass(pass.empty ? null : 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"); assert(listenBlock.tagValue!string("type") == "plaintext");
auto addresses = listenBlock.tagValue!(string[])("address"); auto addresses = listenBlock.tagValue!(string[])("address");
auto port = listenBlock.tagValue!ushort("port"); auto port = listenBlock.tagValue!ushort("port");
foreach(address; addresses) foreach (address; addresses)
{ {
server.listen(port, address); server.listen(port, address);
} }
} }
} }

View File

@ -9,6 +9,7 @@ import ircd.server;
import ircd.message; import ircd.message;
import ircd.helpers; import ircd.helpers;
//TODO: Make this a struct?
class Channel class Channel
{ {
string name; string name;
@ -29,14 +30,14 @@ class Channel
{ {
this.name = name; this.name = name;
this._server = server; this._server = server;
this.maskLists = ['b' : [], 'e' : [], 'I' : []]; this.maskLists = ['b': [], 'e': [], 'I': []];
} }
void join(Connection connection) void join(Connection connection)
{ {
members ~= connection; members ~= connection;
if(members.length == 1) if (members.length == 1)
{ {
memberModes[connection] ~= 'o'; memberModes[connection] ~= 'o';
} }
@ -45,7 +46,7 @@ class Channel
memberModes[connection] = []; memberModes[connection] = [];
} }
if(inviteHolders.canFind(connection)) if (inviteHolders.canFind(connection))
{ {
inviteHolders = inviteHolders.remove!(c => c == connection); inviteHolders = inviteHolders.remove!(c => c == connection);
} }
@ -53,11 +54,13 @@ class Channel
void part(Connection connection, string partMessage) 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 else
{ {
@ -78,11 +81,11 @@ class Channel
{ {
string channelType; string channelType;
if(modes.canFind('s')) if (modes.canFind('s'))
{ {
channelType = "@"; channelType = "@";
} }
else if(modes.canFind('p')) else if (modes.canFind('p'))
{ {
channelType = "*"; channelType = "*";
} }
@ -93,9 +96,14 @@ class Channel
auto onChannel = members.canFind(connection); 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); connection.sendRplEndOfNames(name);
} }
@ -103,7 +111,7 @@ class Channel
void sendPrivMsg(Connection sender, string text) 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)); member.send(Message(sender.prefix, "PRIVMSG", [name, text], true));
} }
@ -111,7 +119,7 @@ class Channel
void sendNotice(Connection sender, string text) 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)); member.send(Message(sender.prefix, "NOTICE", [name, text], true));
} }
@ -119,13 +127,17 @@ class Channel
void sendTopic(Connection connection) 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 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; topic = newTopic;
foreach(member; members) foreach (member; members)
{ {
member.send(Message(connection.prefix, "TOPIC", [name, newTopic], true)); member.send(Message(connection.prefix, "TOPIC", [name, newTopic], true));
} }
@ -141,9 +153,11 @@ class Channel
void kick(Connection kicker, Connection user, string comment) 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); members = members.remove!(m => m == user);
@ -155,13 +169,13 @@ class Channel
auto specialModes = ""; auto specialModes = "";
string[] specialModeParameters; string[] specialModeParameters;
if(members.canFind(user) && key !is null) if (members.canFind(user) && key !is null)
{ {
specialModes ~= "k"; specialModes ~= "k";
specialModeParameters ~= key; specialModeParameters ~= key;
} }
if(members.canFind(user) && !userLimit.isNull) if (members.canFind(user) && !userLimit.isNull)
{ {
import std.conv : to; import std.conv : to;
@ -169,12 +183,14 @@ class Channel
specialModeParameters ~= userLimit.to!string; 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) bool setMemberMode(Connection target, char mode)
{ {
if(memberModes[target].canFind(mode)) if (memberModes[target].canFind(mode))
{ {
return false; return false;
} }
@ -185,7 +201,7 @@ class Channel
bool unsetMemberMode(Connection target, char mode) bool unsetMemberMode(Connection target, char mode)
{ {
if(!memberModes[target].canFind(mode)) if (!memberModes[target].canFind(mode))
{ {
return false; 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) //NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges)
import std.utf : byCodeUnit; import std.utf : byCodeUnit;
import std.range : array; import std.range : array;
memberModes[target] = memberModes[target].byCodeUnit.remove!(m => m == mode).array; memberModes[target] = memberModes[target].byCodeUnit.remove!(m => m == mode).array;
return true; return true;
@ -200,7 +217,7 @@ class Channel
bool setMode(char mode) bool setMode(char mode)
{ {
if(modes.canFind(mode)) if (modes.canFind(mode))
{ {
return false; return false;
} }
@ -214,7 +231,7 @@ class Channel
bool unsetMode(char mode) bool unsetMode(char mode)
{ {
if(!modes.canFind(mode)) if (!modes.canFind(mode))
{ {
return false; 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) //NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges)
import std.utf : byCodeUnit; import std.utf : byCodeUnit;
import std.range : array; import std.range : array;
modes = modes.byCodeUnit.remove!(m => m == mode).array; modes = modes.byCodeUnit.remove!(m => m == mode).array;
return true; return true;
@ -229,7 +247,7 @@ class Channel
bool addMaskListEntry(string mask, char mode) 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; return false;
} }
@ -241,7 +259,7 @@ class Channel
bool removeMaskListEntry(string mask, char mode) 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; return false;
} }
@ -253,32 +271,44 @@ class Channel
void sendBanList(Connection connection) 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) 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) 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) bool setKey(string key)
@ -290,7 +320,7 @@ class Channel
bool unsetKey(string key) bool unsetKey(string key)
{ {
if(this.key != key) if (this.key != key)
{ {
return false; return false;
} }
@ -307,7 +337,7 @@ class Channel
bool unsetUserLimit() bool unsetUserLimit()
{ {
if(userLimit.isNull) if (userLimit.isNull)
{ {
return false; return false;
} }
@ -319,11 +349,11 @@ class Channel
string nickPrefix(Connection member) string nickPrefix(Connection member)
{ {
if(memberModes[member].canFind('o')) if (memberModes[member].canFind('o'))
{ {
return "@"; return "@";
} }
else if(memberModes[member].canFind('v')) else if (memberModes[member].canFind('v'))
{ {
return "+"; return "+";
} }
@ -331,7 +361,10 @@ class Channel
return ""; return "";
} }
string prefixedNick(Connection member) { return nickPrefix(member) ~ member.nick; } string prefixedNick(Connection member)
{
return nickPrefix(member) ~ member.nick;
}
bool visibleTo(Connection connection) bool visibleTo(Connection connection)
{ {
@ -340,15 +373,16 @@ class Channel
bool canReceiveMessagesFromUser(Connection connection) bool canReceiveMessagesFromUser(Connection connection)
{ {
if(modes.canFind('n') && !members.canFind(connection)) if (modes.canFind('n') && !members.canFind(connection))
{ {
return false; return false;
} }
else if(modes.canFind('m') && nickPrefix(connection).empty) else if (modes.canFind('m') && nickPrefix(connection).empty)
{ {
return false; 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; return false;
} }

File diff suppressed because it is too large Load Diff

View File

@ -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) //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) //License: Boost License 1.0 (http://www.boost.org/LICENSE_1_0.txt)
//Copyright (c) Lars T. Kyllingstad, Walter Bright //Copyright (c) Lars T. Kyllingstad, Walter Bright
@safe pure @safe pure bool wildcardMatch(string input, string pattern)
bool wildcardMatch(string input, string pattern)
{ {
foreach (ref pi; 0 .. pattern.length) foreach (ref pi; 0 .. pattern.length)
{ {
@ -47,11 +46,11 @@ bool wildcardMatch(string input, string pattern)
return input.empty; return input.empty;
} }
@safe pure @safe pure dchar toIRCLower(dchar input)
dchar toIRCLower(dchar input)
{ {
import std.uni : toLower; import std.uni : toLower;
switch(input)
switch (input)
{ {
case '[': case '[':
return '{'; return '{';
@ -64,9 +63,9 @@ dchar toIRCLower(dchar input)
} }
} }
@safe pure @safe pure string toIRCLower(string input)
string toIRCLower(string input)
{ {
import std.utf : byChar; import std.utf : byChar;
return input.map!toIRCLower.byChar.array.idup; return input.map!toIRCLower.byChar.array.idup;
} }

View File

@ -6,7 +6,6 @@ import std.array;
import std.algorithm; import std.algorithm;
import std.conv; import std.conv;
//TODO: Make this a class
struct Message struct Message
{ {
string prefix; string prefix;
@ -20,7 +19,7 @@ struct Message
static Message fromString(string line) static Message fromString(string line)
{ {
string prefix = null; string prefix = null;
if(line.startsWith(':')) if (line.startsWith(':'))
{ {
line = line[1 .. $]; line = line[1 .. $];
prefix = line[0 .. line.indexOf(' ')]; prefix = line[0 .. line.indexOf(' ')];
@ -28,7 +27,7 @@ struct Message
} }
//stop early when no space character can be found (message without parameters) //stop early when no space character can be found (message without parameters)
if(!line.canFind(' ')) if (!line.canFind(' '))
{ {
return Message(prefix, line, [], false); return Message(prefix, line, [], false);
} }
@ -38,15 +37,15 @@ struct Message
auto bytes = line.length; auto bytes = line.length;
string[] params = []; string[] params = [];
bool prefixedParam; bool prefixedParam;
while(true) while (true)
{ {
if(line.startsWith(':')) if (line.startsWith(':'))
{ {
params ~= line[1 .. $]; params ~= line[1 .. $];
prefixedParam = true; prefixedParam = true;
break; break;
} }
else if(line.canFind(' ')) else if (line.canFind(' '))
{ {
auto param = line[0 .. line.indexOf(' ')]; auto param = line[0 .. line.indexOf(' ')];
line = line[param.length + 1 .. $]; line = line[param.length + 1 .. $];
@ -65,29 +64,28 @@ struct Message
string toString() string toString()
{ {
auto message = ""; auto message = "";
if(prefix != null) if (prefix != null)
{ {
message = ":" ~ prefix ~ " "; message = ":" ~ prefix ~ " ";
} }
if(parameters.length == 0) if (parameters.length == 0)
{ {
return message ~ command; return message ~ command;
} }
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 ~= ":";
} }
message ~= parameters[$-1]; message ~= parameters[$ - 1];
return message; return message;
} }
} }

View File

@ -19,6 +19,7 @@ import ircd.connection;
import ircd.channel; import ircd.channel;
import ircd.helpers; import ircd.helpers;
//TODO: Make this a struct?
class Server class Server
{ {
Connection[] connections; Connection[] connections;
@ -53,7 +54,8 @@ class Server
private void readMotd() private void readMotd()
{ {
import std.file : exists, readText; import std.file : exists, readText;
if(exists("motd"))
if (exists("motd"))
{ {
motd = readText("motd"); motd = readText("motd");
} }
@ -61,9 +63,9 @@ class Server
private void pingLoop() private void pingLoop()
{ {
while(true) while (true)
{ {
foreach(connection; connections) foreach (connection; connections)
{ {
connection.send(Message(null, "PING", [name], true)); connection.send(Message(null, "PING", [name], true));
} }
@ -88,14 +90,14 @@ class Server
{ {
import std.ascii : digits, letters; import std.ascii : digits, letters;
if(name.length > 9) if (name.length > 9)
{ {
return false; return false;
} }
foreach(i, c; name) foreach (i, c; name)
{ {
auto allowed = letters ~ "[]\\`_^{|}"; auto allowed = letters ~ "[]\\`_^{|}";
if(i > 0) if (i > 0)
{ {
allowed ~= digits ~ "-"; allowed ~= digits ~ "-";
} }
@ -145,7 +147,7 @@ class Server
{ {
auto channelRange = findChannelByName(channelName); auto channelRange = findChannelByName(channelName);
Channel channel; Channel channel;
if(channelRange.empty) if (channelRange.empty)
{ {
channel = new Channel(channelName, this); channel = new Channel(channelName, this);
channels ~= channel; channels ~= channel;
@ -156,14 +158,14 @@ class Server
} }
channel.join(connection); channel.join(connection);
foreach(member; channel.members) foreach (member; channel.members)
{ {
member.send(Message(connection.prefix, "JOIN", [channelName])); member.send(Message(connection.prefix, "JOIN", [channelName]));
} }
channel.sendNames(connection); channel.sendNames(connection);
if(!channel.topic.empty) if (!channel.topic.empty)
{ {
channel.sendTopic(connection); channel.sendTopic(connection);
} }
@ -171,11 +173,12 @@ class Server
void part(Connection connection, string channelName, string partMessage) 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); channel.part(connection, partMessage);
if(channel.members.empty) if (channel.members.empty)
{ {
channels = channels.remove!(c => c == channel); channels = channels.remove!(c => c == channel);
} }
@ -184,20 +187,20 @@ class Server
void quit(Connection connection, string quitMessage) void quit(Connection connection, string quitMessage)
{ {
Connection[] peers; Connection[] peers;
foreach(channel; connection.channels) foreach (channel; connection.channels)
{ {
peers ~= channel.members; peers ~= channel.members;
channel.members = channel.members.remove!(m => m == connection); channel.members = channel.members.remove!(m => m == connection);
if(channel.members.empty) if (channel.members.empty)
{ {
channels = channels.remove!(c => c == channel); channels = channels.remove!(c => c == channel);
} }
} }
peers = peers.sort().uniq.filter!(c => c != connection).array; 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)); peer.send(Message(connection.prefix, "QUIT", [quitMessage], true));
} }
@ -213,8 +216,9 @@ class Server
//TODO: Check what RFCs say about secret/private channels //TODO: Check what RFCs say about secret/private channels
auto channel = findChannelByName(channelName)[0]; auto channel = findChannelByName(channelName)[0];
foreach(c; channel.members.filter!(c => !operatorsOnly || c.isOperator) foreach (c; channel.members
.filter!(c => c.visibleTo(origin))) .filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => c.visibleTo(origin)))
{ {
//TODO: Support hop count //TODO: Support hop count
origin.sendWhoReply(channelName, c, channel.nickPrefix(c), 0); origin.sendWhoReply(channelName, c, channel.nickPrefix(c), 0);
@ -223,9 +227,10 @@ class Server
void whoGlobal(Connection origin, string mask, bool operatorsOnly) void whoGlobal(Connection origin, string mask, bool operatorsOnly)
{ {
foreach(c; connections.filter!(c => c.visibleTo(origin)) foreach (c; connections.filter!(c => c.visibleTo(origin))
.filter!(c => !operatorsOnly || c.isOperator) .filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => [c.hostname, c.servername, c.realname, c.nick].any!(n => wildcardMatch(n, mask)))) .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?) //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; auto channelName = c.channels.empty ? "*" : c.channels.array[0].name;
@ -279,15 +284,20 @@ class Server
void sendGlobalNames(Connection connection) void sendGlobalNames(Connection connection)
{ {
foreach(channel; channels.filter!(c => c.visibleTo(connection))) foreach (channel; channels.filter!(c => c.visibleTo(connection)))
{ {
channel.sendNames(connection, false); 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); auto otherUsers = connections.filter!(c => !c.modes.canFind('i')
if(!otherUsers.empty) && 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("*"); connection.sendRplEndOfNames("*");
@ -295,25 +305,32 @@ class Server
void sendFullList(Connection connection) 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(); connection.sendRplListEnd();
} }
void sendPartialList(Connection connection, string[] channelNames) 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(); connection.sendRplListEnd();
} }
void sendVersion(Connection connection) 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) void sendTime(Connection connection)
@ -334,13 +351,17 @@ class Server
void sendMotd(Connection connection) void sendMotd(Connection connection)
{ {
connection.send(Message(name, "375", [connection.nick, ":- " ~ name ~ " Message of the day - "], true)); connection.send(Message(name, "375", [
foreach(line; motd.splitLines) connection.nick, ":- " ~ name ~ " Message of the day - "
], true));
foreach (line; motd.splitLines)
{ {
//TODO: Implement line wrapping //TODO: Implement line wrapping
connection.send(Message(name, "372", [connection.nick, ":- " ~ line], true)); 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) 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: 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 //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) void ison(Connection connection, string[] nicks)
@ -379,17 +419,29 @@ class Server
{ {
auto user = findConnectionByNick(mask)[0]; 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) //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)); connection.send(Message(name, "312", [
if(user.isOperator) 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"; 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(' '); 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) void kill(Connection killer, string nick, string comment)
@ -400,7 +452,8 @@ class Server
quit(user, "Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"); 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(); user.closeConnection();
} }
@ -415,7 +468,7 @@ class Server
void updateCommandStatistics(Message message) void updateCommandStatistics(Message message)
{ {
auto command = message.command.toUpper; auto command = message.command.toUpper;
if(command !in _commandUsage) if (command !in _commandUsage)
{ {
_commandUsage[command] = 0; _commandUsage[command] = 0;
_commandBytes[command] = 0; _commandBytes[command] = 0;
@ -426,10 +479,13 @@ class Server
void sendCommandUsage(Connection connection) void sendCommandUsage(Connection connection)
{ {
foreach(command, count; _commandUsage) foreach (command, count; _commandUsage)
{ {
//TODO: Implement remote count //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 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)); connection.send(Message(name, "242", [connection.nick, uptimeString], true));
} }