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

@ -70,4 +70,3 @@ shared static this()
}
}
}

View File

@ -9,6 +9,7 @@ import ircd.server;
import ircd.message;
import ircd.helpers;
//TODO: Make this a struct?
class Channel
{
string name;
@ -57,7 +58,9 @@ class Channel
{
if (partMessage !is null)
{
member.send(Message(connection.prefix, "PART", [name, partMessage], true));
member.send(Message(connection.prefix, "PART", [
name, partMessage
], true));
}
else
{
@ -93,7 +96,12 @@ 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)
{
@ -121,11 +129,15 @@ class Channel
{
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));
}
}
@ -143,7 +155,9 @@ class Channel
{
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);
@ -169,7 +183,9 @@ 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)
@ -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;
@ -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;
@ -255,30 +273,42 @@ class Channel
{
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'])
{
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'])
{
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)
@ -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)
{
@ -348,7 +381,8 @@ class Channel
{
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;
}

View File

@ -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
@ -110,7 +129,9 @@ class Connection
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);
}
@ -148,7 +169,8 @@ class Connection
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;
@ -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;
}
@ -259,13 +285,17 @@ class Connection
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))
{
send(Message(_server.name, "432", [nick, newNick, "Erroneous nickname"]));
send(Message(_server.name, "432", [
nick, newNick, "Erroneous nickname"
]));
return;
}
@ -295,7 +325,9 @@ class Connection
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;
}
@ -325,7 +357,9 @@ class Connection
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;
}
@ -381,11 +415,17 @@ class Connection
auto channel = channelRange[0];
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
@ -413,9 +453,12 @@ class Connection
{
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
{
@ -439,7 +482,9 @@ class Connection
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)
@ -457,7 +502,9 @@ class Connection
}
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
{
@ -542,13 +589,17 @@ class Connection
{
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));
}
}
@ -563,11 +614,14 @@ class Connection
auto channelName = message.parameters[0];
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
{
@ -581,7 +635,9 @@ class Connection
{
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);
}
@ -608,7 +664,8 @@ class Connection
{
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);
}
@ -678,7 +735,10 @@ class Connection
}
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'))
{
@ -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);
}
@ -901,7 +962,9 @@ class Connection
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;
}
@ -934,21 +997,27 @@ class Connection
case 'i':
case 'w':
case 's':
if(add) modes ~= mode;
else removeMode(mode);
if (add)
modes ~= mode;
else
removeMode(mode);
break;
case 'o':
case 'O':
if(!add) removeMode(mode);
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;
}
}
@ -1014,8 +1083,7 @@ 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)
@ -1046,8 +1114,10 @@ Lforeach:
}
bool success;
if(add) success = channel.setMemberMode(member, mode);
else success = channel.unsetMemberMode(member, mode);
if (add)
success = channel.setMemberMode(member, mode);
else
success = channel.unsetMemberMode(member, mode);
if (success)
{
processedModes ~= mode;
@ -1071,8 +1141,10 @@ Lforeach:
}
bool success;
if(add) success = channel.addMaskListEntry(mask, mode);
else success = channel.removeMaskListEntry(mask, mode);
if (add)
success = channel.addMaskListEntry(mask, mode);
else
success = channel.removeMaskListEntry(mask, mode);
if (success)
{
processedModes ~= mode;
@ -1088,8 +1160,10 @@ Lforeach:
auto key = message.parameters[++i];
bool success;
if(add) success = channel.setKey(key);
else success = channel.unsetKey(key);
if (add)
success = channel.setKey(key);
else
success = channel.unsetKey(key);
if (success)
{
processedModes ~= mode;
@ -1136,15 +1210,20 @@ Lforeach:
case 's':
case 't':
bool success;
if(add) success = channel.setMode(mode);
else success = channel.unsetMode(mode);
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;
}
}
@ -1153,7 +1232,9 @@ Lforeach:
{
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));
}
}
}
@ -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
}
@ -1323,4 +1428,3 @@ Lforeach:
modes = modes.byCodeUnit.remove!(a => a == mode).array;
}
}

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)
//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,10 +46,10 @@ 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)
{
case '[':
@ -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;
}

View File

@ -6,7 +6,6 @@ import std.array;
import std.algorithm;
import std.conv;
//TODO: Make this a class
struct Message
{
string prefix;
@ -90,4 +89,3 @@ struct Message
return message;
}
}

View File

@ -19,6 +19,7 @@ import ircd.connection;
import ircd.channel;
import ircd.helpers;
//TODO: Make this a struct?
class Server
{
Connection[] connections;
@ -53,6 +54,7 @@ class Server
private void readMotd()
{
import std.file : exists, readText;
if (exists("motd"))
{
motd = readText("motd");
@ -171,7 +173,8 @@ 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);
@ -213,7 +216,8 @@ 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)
foreach (c; channel.members
.filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => c.visibleTo(origin)))
{
//TODO: Support hop count
@ -225,7 +229,8 @@ class Server
{
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))))
.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;
@ -284,10 +289,15 @@ class Server
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')
&& 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("*");
@ -297,23 +307,30 @@ class Server
{
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));
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))
{
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))
{
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)
{
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));
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();
}
@ -429,7 +482,10 @@ class Server
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));
}