Implement WHO query

This commit is contained in:
Les De Ridder 2017-03-19 22:43:52 +01:00
parent 5e2839eb39
commit 1e89db7d0f
No known key found for this signature in database
GPG Key ID: 5EC132DFA85DB372
3 changed files with 121 additions and 6 deletions

View File

@ -13,6 +13,7 @@ import vibe.stream.operations;
import ircd.message; import ircd.message;
import ircd.server; import ircd.server;
import ircd.channel; import ircd.channel;
import ircd.helpers;
class Connection class Connection
{ {
@ -26,11 +27,12 @@ class Connection
string hostname; string hostname;
char[] modes; char[] modes;
@property string mask() { return nick ~ "!" ~ user ~ "@" ~ hostname; }
@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 mask() { return nick ~ "!" ~ user ~ "@" ~ hostname; }
@property bool registered() { return nick !is null && user !is null; } @property bool registered() { return nick !is null && user !is null; }
@property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); }
@property string servername() { return _server.name; } //TODO: Support server linking
bool connected; bool connected;
@ -52,6 +54,11 @@ class Connection
return 0; return 0;
} }
bool visibleTo(Connection other)
{
return !modes.canFind('i') || channels.any!(c => c.members.canFind(other));
}
void send(Message message) void send(Message message)
{ {
string messageString = message.toString; string messageString = message.toString;
@ -127,6 +134,10 @@ class Connection
if(!registered) sendErrNotRegistered(); if(!registered) sendErrNotRegistered();
else onPrivMsg(message); else onPrivMsg(message);
break; break;
case "WHO":
if(!registered) sendErrNotRegistered();
else onWho(message);
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"]));
@ -287,7 +298,7 @@ class Connection
} }
else else
{ {
_server.sendToChannel(this, target, text); _server.privmsgToChannel(this, target, text);
} }
} }
else if(Server.isValidNick(target)) else if(Server.isValidNick(target))
@ -298,7 +309,7 @@ class Connection
} }
else else
{ {
_server.sendToUser(this, target, text); _server.privmsgToUser(this, target, text);
} }
} }
else else
@ -308,6 +319,31 @@ class Connection
} }
} }
void onWho(Message message)
{
if(message.parameters.length == 0)
{
_server.whoGlobal(this, "*", false);
}
else
{
auto mask = message.parameters[0];
auto operatorsOnly = message.parameters.length > 1 && message.parameters[1] == "o";
if(_server.isValidChannelName(mask) && _server.channels.canFind!(c => c.name == mask))
{
_server.whoChannel(this, mask, operatorsOnly);
}
else
{
_server.whoGlobal(this, mask == "0" ? "*" : mask, operatorsOnly);
}
}
auto name = message.parameters.length == 0 ? "*" : message.parameters[0];
send(Message(_server.name, "315", [nick, name, "End of WHO list"], true));
}
void sendErrNoSuchNick(string name) void sendErrNoSuchNick(string name)
{ {
send(Message(_server.name, "401", [nick, name, "No such nick/channel"], true)); send(Message(_server.name, "401", [nick, name, "No such nick/channel"], true));
@ -333,6 +369,15 @@ class Connection
send(Message(_server.name, "461", [nick, command, "Not enough parameters"], true)); send(Message(_server.name, "461", [nick, command, "Not enough parameters"], true));
} }
void sendWhoReply(string channel, Connection user, uint hopCount)
{
auto flags = user.modes.canFind('a') ? "G" : "H";
if(user.isOperator) flags ~= "*";
//TODO: Add channel prefix
send(Message(_server.name, "352", [nick, channel, user.user, user.hostname, user.servername, user.nick, flags, hopCount.to!string ~ " " ~ user.realname], true));
}
void sendWelcome() void sendWelcome()
{ {
send(Message(_server.name, "001", [nick, "Welcome to the Internet Relay Network " ~ mask], true)); send(Message(_server.name, "001", [nick, "Welcome to the Internet Relay Network " ~ mask], true));

43
source/ircd/helpers.d Normal file
View File

@ -0,0 +1,43 @@
module ircd.helpers;
import std.range;
//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)
{
foreach (ref pi; 0 .. pattern.length)
{
const pc = pattern[pi];
switch (pc)
{
case '*':
if (pi + 1 == pattern.length)
return true;
for (; !input.empty; input.popFront())
{
auto p = input.save;
if (wildcardMatch(p, pattern[pi + 1 .. pattern.length]))
return true;
}
return false;
case '?':
if (input.empty)
return false;
input.popFront();
break;
default:
if (input.empty)
return false;
if (pc != input.front)
return false;
input.popFront();
break;
}
}
return input.empty;
}

View File

@ -14,6 +14,7 @@ import ircd.packageVersion;
import ircd.message; import ircd.message;
import ircd.connection; import ircd.connection;
import ircd.channel; import ircd.channel;
import ircd.helpers;
class Server class Server
{ {
@ -143,13 +144,39 @@ class Server
} }
} }
void sendToChannel(Connection sender, string target, string text) void whoChannel(Connection origin, string channelName, bool operatorsOnly)
{
//TODO: Check what RFCs say about secret/private channels
auto channel = channels.find!(c => c.name == channelName)[0];
foreach(c; channel.members.filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => c.visibleTo(origin)))
{
//TODO: Support hop count
origin.sendWhoReply(channelName, c, 0);
}
}
void whoGlobal(Connection origin, string mask, bool operatorsOnly)
{
foreach(c; connections.filter!(c => c.visibleTo(origin))
.filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => [c.hostname, c.servername, c.realname, c.nick].any!(n => wildcardMatch(n, mask))))
{
//TODO: Don't leak secret/private channels (even if the RFCs don't say anything about it?)
auto channelName = c.channels.empty ? "*" : c.channels.array[0].name;
//TODO: Support hop count
origin.sendWhoReply(channelName, c, 0);
}
}
void privmsgToChannel(Connection sender, string target, string text)
{ {
auto channel = channels.find!(c => c.name == target)[0]; auto channel = channels.find!(c => c.name == target)[0];
channel.sendPrivMsg(sender, text); channel.sendPrivMsg(sender, text);
} }
void sendToUser(Connection sender, string target, string text) void privmsgToUser(Connection sender, string target, string text)
{ {
auto user = connections.find!(c => c.nick == target)[0]; auto user = connections.find!(c => c.nick == target)[0];
user.send(Message(sender.mask, "PRIVMSG", [target, text], true)); user.send(Message(sender.mask, "PRIVMSG", [target, text], true));