Implement WHOIS

This commit is contained in:
Les De Ridder 2017-04-24 06:20:18 +02:00
parent 132c0229a7
commit 0ac5cc07ba
No known key found for this signature in database
GPG Key ID: 5EC132DFA85DB372
2 changed files with 64 additions and 4 deletions

View File

@ -7,6 +7,7 @@ import std.range;
import std.conv;
import std.socket;
import std.utf;
import std.datetime;
import vibe.core.core;
import vibe.stream.operations;
@ -28,6 +29,12 @@ class Connection
string hostname;
char[] modes;
SysTime lastMessageTime;
string awayMessage;
bool connected;
@property auto channels() { return _server.channels.filter!(c => c.members.canFind(this)); }
@property string mask() { return nick ~ "!" ~ user ~ "@" ~ hostname; }
@ -35,10 +42,6 @@ class Connection
@property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); }
@property string servername() { return _server.name; } //TODO: Support server linking
string awayMessage;
bool connected;
this(TCPConnection connection, Server server)
{
_connection = connection;
@ -104,6 +107,13 @@ class Connection
continue;
}
//NOTE: The RFCs don't specify what 'being idle' means
// We assume that it's sending any message that isn't a PING/PONG.
if(message.command != "PING" && message.command != "PONG")
{
lastMessageTime = Clock.currTime;
}
writeln("C> " ~ message.toString);
//TODO: If RFC-strictness is off, ignore case
@ -185,6 +195,10 @@ class Connection
if(!registered) sendErrNotRegistered();
else onIson(message);
break;
case "WHOIS":
if(!registered) sendErrNotRegistered();
else onWhois(message);
break;
default:
writeln("unknown command '", message.command, "'");
send(Message(_server.name, "421", [nick, message.command, "Unknown command"]));
@ -658,6 +672,33 @@ class Connection
_server.ison(this, message.parameters[0].split);
}
void onWhois(Message message)
{
if(message.parameters.length < 1)
{
sendErrNoNickGiven();
return;
}
else if(message.parameters.length > 1)
{
notImplemented("forwarding WHOIS to another server");
return;
}
auto mask = message.parameters[0];
//TODO: Support user masks
if(!_server.canFindConnectionByNick(mask) || !_server.findConnectionByNick(mask)[0].visibleTo(this))
{
sendErrNoSuchNick(mask);
}
else
{
_server.whois(this, mask);
}
send(Message(_server.name, "318", [nick, mask, "End of WHOIS list"], true));
}
void sendWhoReply(string channel, Connection user, uint hopCount)
{
auto flags = user.modes.canFind('a') ? "G" : "H";

View File

@ -26,6 +26,7 @@ class Server
enum versionString = "salty-ircd-" ~ packageVersion;
string name;
enum string info = "A salty-ircd server"; //TODO: Make server info configurable
string motd;
@ -364,6 +365,24 @@ class Server
connection.send(Message(name, "303", [connection.nick, reply], true));
}
void whois(Connection connection, string mask)
{
auto user = findConnectionByNick(mask)[0];
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));
if(user.isOperator)
{
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));
//TODO: Prepend nick prefix (i.e. '@' or '+') when applicable
auto userChannels = user.channels.map!(c => c.name).join(' ');
connection.send(Message(name, "319", [connection.nick, user.nick, userChannels], true));
}
void listen(ushort port = 6667)
{
listenTCP(port, &acceptConnection);