diff --git a/source/ircd/connection.d b/source/ircd/connection.d index 90c1192..9b6cfc1 100644 --- a/source/ircd/connection.d +++ b/source/ircd/connection.d @@ -226,11 +226,16 @@ class Connection case "MODE": onMode(message); break; + case "STATS": + onStats(message); + break; default: writeln("unknown command '", message.command, "'"); send(Message(_server.name, "421", [nick, message.command, "Unknown command"])); - break; + continue; } + + _server.updateCommandStatistics(message); } onDisconnect(); @@ -1097,6 +1102,37 @@ Lforeach: } } + void onStats(Message message) + { + if(message.parameters.length > 1) + { + notImplemented("forwarding STATS to another other server"); + return; + } + + char statsLetter = message.parameters.length > 0 ? message.parameters[0][0] : '*'; + + switch(statsLetter) + { + case 'l': + notImplemented("STATS server link information"); + break; + case 'm': + _server.sendCommandUsage(this); + break; + case 'o': + notImplemented("O-lines and O-line querying"); + break; + case 'u': + _server.sendUptime(this); + break; + default: + break; + } + + send(Message(_server.name, "219", [nick, [statsLetter].idup, "End of STATS report"], true)); + } + void sendWhoReply(string channel, Connection user, string nickPrefix, uint hopCount) { auto flags = user.modes.canFind('a') ? "G" : "H"; diff --git a/source/ircd/message.d b/source/ircd/message.d index d12ca69..6575629 100644 --- a/source/ircd/message.d +++ b/source/ircd/message.d @@ -14,6 +14,9 @@ struct Message string[] parameters; bool prefixedParameter; + //NOTE: The RFCs don't state what this is exactly, but common implementations use the byte count of the message parameters + ulong bytes; + static Message fromString(string line) { string prefix = null; @@ -32,6 +35,7 @@ struct Message auto command = line[0 .. line.indexOf(' ')]; line = line[command.length + 1 .. $]; + auto bytes = line.length; string[] params = []; bool prefixedParam; while(true) @@ -55,7 +59,7 @@ struct Message } } - return Message(prefix, command, params, prefixedParam); + return Message(prefix, command, params, prefixedParam, bytes); } string toString() diff --git a/source/ircd/server.d b/source/ircd/server.d index 2ec5189..7ef8a95 100644 --- a/source/ircd/server.d +++ b/source/ircd/server.d @@ -32,12 +32,19 @@ class Server Channel[] channels; + private uint[string] _commandUsage; + private ulong[string] _commandBytes; + + private SysTime _startTime; + this() { name = Socket.hostName; readMotd(); + _startTime = Clock.currTime; + runTask(&pingLoop); } @@ -399,6 +406,37 @@ class Server channel.kick(kicker, user, comment); } + void updateCommandStatistics(Message message) + { + auto command = message.command.toUpper; + if(command !in _commandUsage) + { + _commandUsage[command] = 0; + _commandBytes[command] = 0; + } + _commandUsage[command]++; + _commandBytes[command] += message.bytes; + } + + void sendCommandUsage(Connection connection) + { + foreach(command, count; _commandUsage) + { + //TODO: Implement remote count + connection.send(Message(name, "212", [connection.nick, command, count.to!string, _commandBytes[command].to!string, "0"], false)); + } + } + + void sendUptime(Connection connection) + { + import std.format : format; + + 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); + connection.send(Message(name, "242", [connection.nick, uptimeString], true)); + } + void listen(ushort port = 6667) { listenTCP(port, &acceptConnection);