forked from lesderid/salty-ircd
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.server;
|
||||
import ircd.channel;
|
||||
import ircd.helpers;
|
||||
|
||||
class Connection
|
||||
{
|
||||
|
@ -26,11 +27,12 @@ class Connection
|
|||
string hostname;
|
||||
char[] modes;
|
||||
|
||||
@property string mask() { return nick ~ "!" ~ user ~ "@" ~ hostname; }
|
||||
|
||||
@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 isOperator() { return modes.canFind('o') || modes.canFind('O'); }
|
||||
@property string servername() { return _server.name; } //TODO: Support server linking
|
||||
|
||||
bool connected;
|
||||
|
||||
|
@ -52,6 +54,11 @@ class Connection
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool visibleTo(Connection other)
|
||||
{
|
||||
return !modes.canFind('i') || channels.any!(c => c.members.canFind(other));
|
||||
}
|
||||
|
||||
void send(Message message)
|
||||
{
|
||||
string messageString = message.toString;
|
||||
|
@ -127,6 +134,10 @@ class Connection
|
|||
if(!registered) sendErrNotRegistered();
|
||||
else onPrivMsg(message);
|
||||
break;
|
||||
case "WHO":
|
||||
if(!registered) sendErrNotRegistered();
|
||||
else onWho(message);
|
||||
break;
|
||||
default:
|
||||
writeln("unknown command '", message.command, "'");
|
||||
send(Message(_server.name, "421", [nick, message.command, "Unknown command"]));
|
||||
|
@ -287,7 +298,7 @@ class Connection
|
|||
}
|
||||
else
|
||||
{
|
||||
_server.sendToChannel(this, target, text);
|
||||
_server.privmsgToChannel(this, target, text);
|
||||
}
|
||||
}
|
||||
else if(Server.isValidNick(target))
|
||||
|
@ -298,7 +309,7 @@ class Connection
|
|||
}
|
||||
else
|
||||
{
|
||||
_server.sendToUser(this, target, text);
|
||||
_server.privmsgToUser(this, target, text);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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.connection;
|
||||
import ircd.channel;
|
||||
import ircd.helpers;
|
||||
|
||||
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];
|
||||
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];
|
||||
user.send(Message(sender.mask, "PRIVMSG", [target, text], true));
|
||||
|
|
Loading…
Reference in New Issue