Implement TOPIC

This commit is contained in:
Les De Ridder 2017-03-21 01:28:39 +01:00
parent 4f8aedddc9
commit 8a94d845e8
No known key found for this signature in database
GPG Key ID: 5EC132DFA85DB372
3 changed files with 101 additions and 9 deletions

View File

@ -9,27 +9,25 @@ import ircd.message;
class Channel class Channel
{ {
private string _name; string name;
Connection[] members; Connection[] members;
Connection owner; Connection owner;
string topic = "";
char[] modes;
private Server _server; private Server _server;
this(string name, Connection owner, Server server) this(string name, Connection owner, Server server)
{ {
this._name = name; this.name = name;
this.owner = owner; this.owner = owner;
this.members = [owner]; this.members = [owner];
this._server = server; this._server = server;
} }
@property
string name()
{
return _name;
}
void sendNames(Connection connection) void sendNames(Connection connection)
{ {
enum channelType = "="; //TODO: Support secret and private channels enum channelType = "="; //TODO: Support secret and private channels
@ -53,4 +51,26 @@ class Channel
member.send(Message(sender.mask, "NOTICE", [name, text], true)); member.send(Message(sender.mask, "NOTICE", [name, text], true));
} }
} }
void sendTopic(Connection connection)
{
if(topic.empty)
{
connection.send(Message(_server.name, "331", [connection.nick, name, "No topic is set"]));
}
else
{
connection.send(Message(_server.name, "332", [connection.nick, name, topic], true));
}
}
void setTopic(Connection connection, string newTopic)
{
topic = newTopic;
foreach(member; members)
{
member.send(Message(connection.mask, "TOPIC", [name, newTopic], true));
}
}
} }

View File

@ -149,6 +149,10 @@ class Connection
if(!registered) sendErrNotRegistered(); if(!registered) sendErrNotRegistered();
else onAway(message); else onAway(message);
break; break;
case "TOPIC":
if(!registered) sendErrNotRegistered();
else onTopic(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"]));
@ -404,6 +408,47 @@ class Connection
} }
} }
void onTopic(Message message)
{
if(message.parameters.length == 0)
{
sendErrNeedMoreParams(message.command);
return;
}
auto channelName = message.parameters[0];
if(message.parameters.length == 1)
{
if(!_server.channels.canFind!(c => c.name == channelName && (!(c.modes.canFind('s') || c.modes.canFind('p')) || c.members.canFind(this))))
{
//NOTE: The RFCs don't allow ERR_NOSUCHCHANNEL as a response to TOPIC
//TODO: If RFC-strictness is off, do send ERR_NOSUCHCHANNEL
send(Message(_server.name, "331", [nick, channelName, "No topic is set"], true));
}
else
{
_server.sendChannelTopic(this, channelName);
}
}
else
{
auto newTopic = message.parameters[1];
if(!channels.canFind!(c => c.name == channelName))
{
sendErrNotOnChannel(channelName);
}
//TODO: Allow operators to set flags
else if(channels.find!(c => c.name == channelName).map!(c => c.modes.canFind('t') /* && this user isn't an operator */).array[0])
{
sendErrChanopPrivsNeeded(channelName);
}
else
{
_server.setChannelTopic(this, channelName, newTopic);
}
}
}
void sendWhoReply(string channel, Connection user, uint hopCount) void sendWhoReply(string channel, Connection user, uint hopCount)
{ {
auto flags = user.modes.canFind('a') ? "G" : "H"; auto flags = user.modes.canFind('a') ? "G" : "H";
@ -433,6 +478,11 @@ class Connection
send(Message(_server.name, "431", [nick, "No nickname given"], true)); send(Message(_server.name, "431", [nick, "No nickname given"], true));
} }
void sendErrNotOnChannel(string channel)
{
send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true));
}
void sendErrNotRegistered() void sendErrNotRegistered()
{ {
send(Message(_server.name, "451", ["(You)", "You have not registered"], true)); send(Message(_server.name, "451", ["(You)", "You have not registered"], true));
@ -443,6 +493,11 @@ 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 sendErrChanopPrivsNeeded(string channel)
{
send(Message(_server.name, "482", [nick, channel, "You're not channel operator"], 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));

View File

@ -91,6 +91,11 @@ class Server
} }
channel.sendNames(connection); channel.sendNames(connection);
if(!channel.topic.empty)
{
channel.sendTopic(connection);
}
} }
void part(Connection connection, string channelName, string partMessage) void part(Connection connection, string channelName, string partMessage)
@ -163,7 +168,7 @@ class Server
.filter!(c => !operatorsOnly || c.isOperator) .filter!(c => !operatorsOnly || c.isOperator)
.filter!(c => [c.hostname, c.servername, c.realname, c.nick].any!(n => wildcardMatch(n, mask)))) .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?) //TODO: Don't leak secret/private channels if RFC-strictness is off (the RFCs don't seem to say anything about it?)
auto channelName = c.channels.empty ? "*" : c.channels.array[0].name; auto channelName = c.channels.empty ? "*" : c.channels.array[0].name;
//TODO: Support hop count //TODO: Support hop count
origin.sendWhoReply(channelName, c, 0); origin.sendWhoReply(channelName, c, 0);
@ -194,6 +199,18 @@ class Server
user.send(Message(sender.mask, "NOTICE", [target, text], true)); user.send(Message(sender.mask, "NOTICE", [target, text], true));
} }
void sendChannelTopic(Connection origin, string channelName)
{
auto channel = channels.find!(c => c.name == channelName)[0];
channel.sendTopic(origin);
}
void setChannelTopic(Connection origin, string channelName, string newTopic)
{
auto channel = channels.find!(c => c.name == channelName)[0];
channel.setTopic(origin, newTopic);
}
void listen(ushort port = 6667) void listen(ushort port = 6667)
{ {
listenTCP(port, &acceptConnection); listenTCP(port, &acceptConnection);