diff --git a/source/ircd/channel.d b/source/ircd/channel.d index 0afee77..929cabc 100644 --- a/source/ircd/channel.d +++ b/source/ircd/channel.d @@ -39,6 +39,10 @@ class Channel { memberModes[connection] ~= 'o'; } + else + { + memberModes[connection] = []; + } } void part(Connection connection, string partMessage) @@ -300,20 +304,22 @@ class Channel return true; } - string prefixedNick(Connection member) + string nickPrefix(Connection member) { if(memberModes[member].canFind('o')) { - return '@' ~ member.nick; + return "@"; } else if(memberModes[member].canFind('v')) { - return '+' ~ member.nick; + return "+"; } - return member.nick; + return ""; } + string prefixedNick(Connection member) { return nickPrefix(member) ~ member.nick; } + bool visibleTo(Connection connection) { return members.canFind(connection) || !modes.canFind('s') && !modes.canFind('p'); diff --git a/source/ircd/connection.d b/source/ircd/connection.d index df2a4df..b162282 100644 --- a/source/ircd/connection.d +++ b/source/ircd/connection.d @@ -41,6 +41,9 @@ class Connection @property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); } @property string servername() { return _server.name; } //TODO: Support server linking + //TODO: Maybe replace string's opEquals (or make a new string class/struct) to compare with toIRCLower + //TODO: Read errata + this(TCPConnection connection, Server server) { _connection = connection; @@ -263,7 +266,6 @@ class Connection auto wasRegistered = registered; - //TODO: Check validity etc. nick = newNick; if(!wasRegistered && registered) @@ -323,14 +325,18 @@ class Connection return; } - auto channel = message.parameters[0]; - if(!Server.isValidChannelName(channel)) + auto channelList = message.parameters[0].split(','); + foreach(channel; channelList) { - sendErrNoSuchChannel(channel); - } - else - { - _server.join(this, channel); + //TODO: Check if the user isn't already on the channel + if(!Server.isValidChannelName(channel)) + { + sendErrNoSuchChannel(channel); + } + else + { + _server.join(this, channel); + } } } @@ -342,26 +348,27 @@ class Connection return; } - //TODO: Support channel lists - //TODO: Check if user is member of channel(s) - auto channel = message.parameters[0]; - if(!Server.isValidChannelName(channel)) + auto channelList = message.parameters[0].split(','); + foreach(channel; channelList) { - sendErrNoSuchChannel(channel); - } - else if(!_server.canFindChannelByName(channel)) - { - send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true)); - } - else - { - if(message.parameters.length > 1) + if(!Server.isValidChannelName(channel)) { - _server.part(this, channel, message.parameters[1]); + sendErrNoSuchChannel(channel); + } + else if(!_server.canFindChannelByName(channel) || !channels.canFind!(c => c.name.toIRCLower == channel.toIRCLower)) + { + send(Message(_server.name, "442", [nick, channel, "You're not on that channel"], true)); } else { - _server.part(this, channel, null); + if(message.parameters.length > 1) + { + _server.part(this, channel, message.parameters[1]); + } + else + { + _server.part(this, channel, null); + } } } } @@ -1041,7 +1048,7 @@ Lforeach: { limit = limitString.to!uint; } - catch(Exception) + catch(Throwable) { break Lforeach; } @@ -1090,11 +1097,11 @@ Lforeach: } } - void sendWhoReply(string channel, Connection user, uint hopCount) + void sendWhoReply(string channel, Connection user, string nickPrefix, uint hopCount) { auto flags = user.modes.canFind('a') ? "G" : "H"; if(user.isOperator) flags ~= "*"; - //TODO: Add channel prefix + flags ~= nickPrefix; send(Message(_server.name, "352", [nick, channel, user.user, user.hostname, user.servername, user.nick, flags, hopCount.to!string ~ " " ~ user.realname], true)); } diff --git a/source/ircd/server.d b/source/ircd/server.d index 10804fa..2ec5189 100644 --- a/source/ircd/server.d +++ b/source/ircd/server.d @@ -208,7 +208,7 @@ class Server .filter!(c => c.visibleTo(origin))) { //TODO: Support hop count - origin.sendWhoReply(channelName, c, 0); + origin.sendWhoReply(channelName, c, channel.nickPrefix(c), 0); } } @@ -220,8 +220,9 @@ class Server { //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 nickPrefix = c.channels.empty ? "" : c.channels.array[0].nickPrefix(c); //TODO: Support hop count - origin.sendWhoReply(channelName, c, 0); + origin.sendWhoReply(channelName, c, nickPrefix, 0); } } @@ -374,8 +375,7 @@ class Server } 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(' '); + auto userChannels = user.channels.map!(c => c.nickPrefix(user) ~ c.name).join(' '); connection.send(Message(name, "319", [connection.nick, user.nick, userChannels], true)); } @@ -385,10 +385,8 @@ class Server user.send(Message(killer.prefix, "KILL", [nick, comment], true)); - //TODO: Find out if any RFC specifies a QUIT message quit(user, "Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"); - //TODO: Find out if what we have to send here user.send(Message(null, "ERROR", ["Closing Link: Killed by " ~ killer.nick ~ " (" ~ comment ~ ")"], true)); user.closeConnection(); }