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.message;
import ircd.helpers; import ircd.helpers;
//TODO: Make this a struct?
class Channel class Channel
{ {
string name; string name;
@ -57,7 +58,9 @@ class Channel
{ {
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
{ {
@ -93,7 +96,12 @@ 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)
{ {
@ -121,11 +129,15 @@ class Channel
{ {
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));
} }
} }
@ -143,7 +155,9 @@ class Channel
{ {
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);
@ -169,7 +183,9 @@ 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)
@ -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;
@ -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;
@ -255,30 +273,42 @@ class Channel
{ {
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)
@ -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)
{ {
@ -348,7 +381,8 @@ class Channel
{ {
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;
} }

View File

@ -18,6 +18,7 @@ import ircd.server;
import ircd.channel; import ircd.channel;
import ircd.helpers; import ircd.helpers;
//TODO: Make this a struct?
class Connection class Connection
{ {
private TCPConnection _connection; private TCPConnection _connection;
@ -37,12 +38,30 @@ class Connection
string pass = null; 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 string prefix()
@property bool registered() { return nick !is null && user !is null && _server.isPassCorrect(pass); } {
@property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); } return nick ~ "!" ~ user ~ "@" ~ hostname;
@property string servername() { return _server.name; } //TODO: Support server linking }
@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: Maybe replace string's opEquals (or make a new string class/struct) to compare with toIRCLower
//TODO: Read errata //TODO: Read errata
@ -110,7 +129,9 @@ class Connection
return; 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); connection.send(message);
} }
@ -148,7 +169,8 @@ class Connection
writeln("C> " ~ message.toString); 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(); sendErrNotRegistered();
continue; continue;
@ -167,7 +189,9 @@ class Connection
break; break;
case "PING": case "PING":
//TODO: Connection timeout when we don't get a PONG //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; break;
case "PONG": case "PONG":
//TODO: Handle pong //TODO: Handle pong
@ -237,7 +261,9 @@ class Connection
break; break;
default: default:
writeln("unknown command '", message.command, "'"); 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; continue;
} }
@ -259,13 +285,17 @@ class Connection
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; 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; return;
} }
@ -295,7 +325,9 @@ class Connection
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; return;
} }
@ -325,7 +357,9 @@ class Connection
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; return;
} }
@ -381,11 +415,17 @@ class Connection
auto channel = channelRange[0]; 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 limit
//TODO: Implement channel key //TODO: Implement channel key
@ -413,9 +453,12 @@ class Connection
{ {
sendErrNoSuchChannel(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 else
{ {
@ -439,7 +482,9 @@ class Connection
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; return;
} }
if (message.parameters.length == 1) if (message.parameters.length == 1)
@ -457,7 +502,9 @@ class Connection
} }
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 else
{ {
@ -542,13 +589,17 @@ class Connection
{ {
removeMode('a'); removeMode('a');
awayMessage = null; 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 else
{ {
modes ~= 'a'; modes ~= 'a';
awayMessage = message.parameters[0]; 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]; 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 //NOTE: The RFCs don't allow ERR_NOSUCHCHANNEL as a response to TOPIC
//TODO: If RFC-strictness is off, do send ERR_NOSUCHCHANNEL //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 else
{ {
@ -581,7 +635,9 @@ class Connection
{ {
sendErrNotOnChannel(channelName); 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); sendErrChanopPrivsNeeded(channelName);
} }
@ -608,7 +664,8 @@ class Connection
{ {
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); _server.sendChannelNames(this, channelName);
} }
@ -678,7 +735,10 @@ class Connection
} }
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'))
{ {
@ -779,7 +839,8 @@ class Connection
auto mask = message.parameters[0]; auto mask = message.parameters[0];
//TODO: Support user masks //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); sendErrNoSuchNick(mask);
} }
@ -901,7 +962,9 @@ class Connection
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 //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; return;
} }
@ -934,21 +997,27 @@ class Connection
case 'i': case 'i':
case 'w': case 'w':
case 's': case 's':
if(add) modes ~= mode; if (add)
else removeMode(mode); modes ~= mode;
else
removeMode(mode);
break; break;
case 'o': case 'o':
case 'O': case 'O':
if(!add) removeMode(mode); if (!add)
removeMode(mode);
break; break;
case 'r': case 'r':
if(add) modes ~= 'r'; if (add)
modes ~= 'r';
break; break;
case 'a': case 'a':
//ignore //ignore
break; break;
default: default:
send(Message(_server.name, "501", [nick, "Unknown MODE flag"], true)); send(Message(_server.name, "501", [
nick, "Unknown MODE flag"
], true));
break; break;
} }
} }
@ -1014,8 +1083,7 @@ class Connection
string[] processedParameters; string[] processedParameters;
auto changedModes = modeString[1 .. $]; auto changedModes = modeString[1 .. $];
Lforeach: Lforeach: foreach (mode; changedModes)
foreach(mode; changedModes)
{ {
//when RFC-strictness is off, maybe send an error when trying to do an illegal change //when RFC-strictness is off, maybe send an error when trying to do an illegal change
switch (mode) switch (mode)
@ -1046,8 +1114,10 @@ Lforeach:
} }
bool success; bool success;
if(add) success = channel.setMemberMode(member, mode); if (add)
else success = channel.unsetMemberMode(member, mode); success = channel.setMemberMode(member, mode);
else
success = channel.unsetMemberMode(member, mode);
if (success) if (success)
{ {
processedModes ~= mode; processedModes ~= mode;
@ -1071,8 +1141,10 @@ Lforeach:
} }
bool success; bool success;
if(add) success = channel.addMaskListEntry(mask, mode); if (add)
else success = channel.removeMaskListEntry(mask, mode); success = channel.addMaskListEntry(mask, mode);
else
success = channel.removeMaskListEntry(mask, mode);
if (success) if (success)
{ {
processedModes ~= mode; processedModes ~= mode;
@ -1088,8 +1160,10 @@ Lforeach:
auto key = message.parameters[++i]; auto key = message.parameters[++i];
bool success; bool success;
if(add) success = channel.setKey(key); if (add)
else success = channel.unsetKey(key); success = channel.setKey(key);
else
success = channel.unsetKey(key);
if (success) if (success)
{ {
processedModes ~= mode; processedModes ~= mode;
@ -1136,15 +1210,20 @@ Lforeach:
case 's': case 's':
case 't': case 't':
bool success; bool success;
if(add) success = channel.setMode(mode); if (add)
else success = channel.unsetMode(mode); success = channel.setMode(mode);
else
success = channel.unsetMode(mode);
if (success) if (success)
{ {
processedModes ~= mode; processedModes ~= mode;
} }
break; break;
default: 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; break;
} }
} }
@ -1153,7 +1232,9 @@ Lforeach:
{ {
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));
} }
} }
} }
@ -1188,16 +1269,22 @@ Lforeach:
break; 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) void sendWhoReply(string channel, Connection user, string nickPrefix, uint hopCount)
{ {
auto flags = user.modes.canFind('a') ? "G" : "H"; auto flags = user.modes.canFind('a') ? "G" : "H";
if(user.isOperator) flags ~= "*"; if (user.isOperator)
flags ~= "*";
flags ~= nickPrefix; 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) void sendRplAway(string target, string message)
@ -1207,7 +1294,9 @@ Lforeach:
void sendRplList(string channelName, ulong visibleCount, string topic) 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() void sendRplListEnd()
@ -1223,7 +1312,9 @@ Lforeach:
void sendRplEndOfNames(string channelName) 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) void sendErrNoSuchNick(string name)
@ -1243,12 +1334,16 @@ Lforeach:
void sendErrUserNotInChannel(string otherNick, string channel) 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) 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() void sendErrNotRegistered()
@ -1258,27 +1353,37 @@ Lforeach:
void sendErrNeedMoreParams(string command) 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() 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) 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) 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() 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 //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; 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) //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,10 +46,10 @@ 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 '[':
@ -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;
@ -90,4 +89,3 @@ struct Message
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,6 +54,7 @@ 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");
@ -171,7 +173,8 @@ 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);
@ -213,7 +216,8 @@ 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 => !operatorsOnly || c.isOperator)
.filter!(c => c.visibleTo(origin))) .filter!(c => c.visibleTo(origin)))
{ {
//TODO: Support hop count //TODO: Support hop count
@ -225,7 +229,8 @@ class Server
{ {
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;
@ -284,10 +289,15 @@ class Server
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')
&& c.channels.filter!(ch => !ch.modes.canFind('s')
&& !ch.modes.canFind('p')).empty);
if (!otherUsers.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("*");
@ -297,23 +307,30 @@ class Server
{ {
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", [
connection.nick, ":- " ~ name ~ " Message of the day - "
], true));
foreach (line; motd.splitLines) 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", [
connection.nick, user.nick, name, info
], true));
if (user.isOperator) 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();
} }
@ -429,7 +482,10 @@ class Server
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));
} }