Partially implement channel mode messasge

This commit is contained in:
Les De Ridder 2017-05-09 06:55:53 +02:00
parent d4aaea4f99
commit e507a38e0d
No known key found for this signature in database
GPG Key ID: 5EC132DFA85DB372
2 changed files with 185 additions and 5 deletions

View File

@ -16,6 +16,9 @@ class Channel
char[] modes;
char[][Connection] memberModes;
string key; //TODO: Fully implement key
//TODO: Implement member limit (+l)
private Server _server;
this(string name, Server server)
@ -130,12 +133,81 @@ class Channel
memberModes.remove(user);
}
void sendModes(Connection user)
{
if(members.canFind(user) && key !is null)
{
user.send(Message(_server.name, "324", [user.nick, name, "+" ~ modes.idup ~ "k", key]));
}
else
{
user.send(Message(_server.name, "324", [user.nick, name, "+" ~ modes.idup]));
}
}
bool setMemberMode(Connection setter, Connection target, char mode)
{
if(memberModes[target].canFind(mode))
{
return false;
}
memberModes[target] ~= mode;
return true;
}
bool unsetMemberMode(Connection setter, Connection target, char mode)
{
if(!memberModes[target].canFind(mode))
{
return false;
}
//NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges)
import std.utf : byCodeUnit;
import std.range : array;
memberModes[target] = memberModes[target].byCodeUnit.remove!(m => m == mode).array;
return true;
}
bool setMode(Connection setter, char mode)
{
if(modes.canFind(mode))
{
return false;
}
modes ~= mode;
return true;
}
bool unsetMode(Connection setter, char mode)
{
if(!modes.canFind(mode))
{
return false;
}
//NOTE: byCodeUnit is necessary due to auto-decoding (https://wiki.dlang.org/Language_issues#Unicode_and_ranges)
import std.utf : byCodeUnit;
import std.range : array;
modes = modes.byCodeUnit.remove!(m => m == mode).array;
return true;
}
string prefixedNick(Connection member)
{
if(memberModes[member].canFind('o'))
{
return '@' ~ member.nick;
}
else if(memberModes[member].canFind('v'))
{
return '+' ~ member.nick;
}
return member.nick;
}

View File

@ -507,8 +507,7 @@ class Connection
{
sendErrNotOnChannel(channelName);
}
//TODO: Allow operators to set flags
else if(channels.find!(c => c.name.toIRCLower == channelName.toIRCLower).map!(c => c.modes.canFind('t') /* && this user isn't an operator */).array[0])
else if(channels.find!(c => c.name.toIRCLower == channelName.toIRCLower).map!(c => c.modes.canFind('t') && !c.memberModes[this].canFind('o')).array[0])
{
sendErrChanopPrivsNeeded(channelName);
}
@ -607,7 +606,7 @@ class Connection
{
send(Message(_server.name, "443", [nick, targetUser.nick, channel.name, "is already on channel"], true));
}
else if(channel.modes.canFind('i') /* TODO: and this connection isn't a chanop */)
else if(channel.modes.canFind('i') && !channel.memberModes[this].canFind('o'))
{
sendErrChanopPrivsNeeded(channel.name);
}
@ -832,7 +831,7 @@ class Connection
return;
}
if(message.parameters.count == 1)
if(message.parameters.length == 1)
{
send(Message(_server.name, "221", [nick, "+" ~ modes.idup]));
}
@ -885,7 +884,116 @@ class Connection
void onChannelMode(Message message)
{
notImplemented("channel mode message");
auto channelRange = _server.findChannelByName(message.parameters[0]);
if(channelRange.empty)
{
//TODO: If RFC-strictness is off, send an error message when the channel doesn't exist
return;
}
auto channel = channelRange[0];
if(message.parameters.length == 1)
{
channel.sendModes(this);
}
else if(message.parameters.length == 2 && ["+b", "e", "I"].canFind(message.parameters[1]))
{
notImplemented("querying ban/exception/invite lists");
}
else
{
if(!channel.memberModes[this].canFind('o'))
{
sendErrChanopPrivsNeeded(channel.name);
return;
}
for(auto i = 1; i < message.parameters.length; i++)
{
auto modeString = message.parameters[i];
auto add = modeString[0] == '+';
if(!add && modeString[0] != '-')
{
//TODO: If RFC-strictness is off, send a malformed message error
return;
}
if(modeString.length == 1)
{
continue;
}
char[] processedModes;
string[] processedParameters;
auto changedModes = modeString[1 .. $];
Lforeach:
foreach(mode; changedModes)
{
//when RFC-strictness is off, maybe send an error when trying to do an illegal change
switch(mode)
{
case 'o':
case 'v':
if(i + 1 == message.parameters.length)
{
//TODO: Figure out what to do when we need more mode parameters
break Lforeach;
}
auto memberNick = message.parameters[++i];
auto memberRange = _server.findConnectionByNick(memberNick);
if(memberRange.empty)
{
sendErrNoSuchNick(memberNick);
break Lforeach;
}
auto member = memberRange[0];
if(!channel.members.canFind(member))
{
sendErrUserNotInChannel(memberNick, channel.name);
break Lforeach;
}
auto success = false;
if(add) success = channel.setMemberMode(this, member, mode);
else success = channel.unsetMemberMode(this, member, mode);
if(success)
{
processedModes ~= mode;
processedParameters ~= memberNick;
}
break;
case 'i': //TODO: Implement invite-only channels
case 'm': //TODO: Implement channel moderation
case 'n': //TODO: Implement the no messages from clients on the outside flag
case 'p':
case 's':
case 't':
auto success = false;
if(add) channel.setMode(this, mode);
else channel.unsetMode(this, mode);
if(success)
{
processedModes ~= mode;
}
break;
default:
send(Message(_server.name, "472", [nick, [mode], "is unknown mode char to me for " ~ channel.name], true));
break;
}
}
if(!processedModes.empty)
{
foreach(member; channel.members)
{
member.send(Message(mask, "MODE", [channel.name, (add ? '+' : '-') ~ processedModes.idup] ~ processedParameters, false));
}
}
}
}
}
void sendWhoReply(string channel, Connection user, uint hopCount)