Implement WHO query
This commit is contained in:
parent
5e2839eb39
commit
1e89db7d0f
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in New Issue