From da91a70f6eff3c71f55101ad1dd8e7f017cf425b Mon Sep 17 00:00:00 2001 From: PrincessNyanara Date: Tue, 18 Nov 2014 01:30:17 -0500 Subject: [PATCH] Proper kick/ban semantics --- script/main.lua | 136 ++++++++++++++++++++------------------------ src/channel.cpp | 63 +++++++++----------- src/channel.hpp | 20 ++++--- src/lua_channel.cpp | 54 ++++++++++++++++++ src/lua_channel.hpp | 2 + 5 files changed, 158 insertions(+), 117 deletions(-) diff --git a/script/main.lua b/script/main.lua index b3e288e..7972baa 100644 --- a/script/main.lua +++ b/script/main.lua @@ -74,6 +74,37 @@ function propagateIgnoreList(con, laction, lcharacter) end end +-- Checks to see if a kick/ban/timeout is possible +-- returns nil (no errors) if able to kick/ban, otherwise returns error +-- Syntax: canChannelKickBan +function canChannelKickBan(con, targetname, targetcon, chan) + -- Source connection must be a channel op or above. + if c.isMod(chan, con) ~= true then + return const.FERR_NOT_OP + end + + -- Prefer online connection to name if possible. + local target = targetname + if targetcon ~= nil then + target = targetcon + end + + if c.isBanned(chan, target) == true then + return const.FERR_ALREADY_CHANNEL_BANNED + end + + -- Channel owners or above cannot kick/ban/timeout other owners or above. + if c.isOwner(chan, con) and c.isOwner(chan, target) then + return const.FERR_DENIED_ON_OP + end + + if c.isOnlyMod(chan, con) and c.isOnlyMod(chan, target) then + return const.FERR_DENIED_ON_OP + end + + return nil +end + -- Bans a person by their account. -- Syntax: ACB event.ACB = @@ -200,7 +231,7 @@ function (con, args) end -- Bans a user from a channel. --- Syntax:: CKU +-- Syntax:: CBU event.CBU = function (con, args) if args.channel == nil or args.character == nil then @@ -212,41 +243,24 @@ function (con, args) return const.FERR_CHANNEL_NOT_FOUND end - if c.isMod(chan, con) ~= true then - return const.FERR_NOT_OP - end + local targetname = string.lower(args.character) + local targetonline, target = u.getConnection(lowertargetname) - if c.inChannel(chan, con) ~= true then - return const.FERR_USER_NOT_IN_CHANNEL + local canban = canChannelKickBan(con, targetname, target, chan) + if canban ~= nil then + return canban end - local targetonline, char = u.getConnection(string.lower(args.character)) - local chantype = c.getType(chan) if chantype == "public" then - if targetonline == true and c.isMod(chan, char) == true then - return const.FERR_DENIED_ON_OP - end s.logAction(con, "CBU", args) elseif chantype == "private" then - c.removeInvite(chan, string.lower(args.character)) + c.removeInvite(chan, targetname) end - if targetonline == false then - char = args.character - if c.isBanned(chan, string.lower(char)) == true then - return const.FERR_ALREADY_CHANNEL_BANNED - end - c.sendAll(chan, "CBU", {channel=args.channel, operator=u.getName(con), character=char}) - c.ban(chan, con, string.lower(char)) - else - if c.isBanned(chan, char) == true then - return const.FERR_ALREADY_CHANNEL_BANNED - end - c.sendAll(chan, "CBU", {channel=args.channel, operator=u.getName(con), character=u.getName(char)}) - c.ban(chan, con, char) - if c.inChannel(chan, char) == true then - partChannel(chan, char) - end + c.ban(chan, con, targetname) + c.sendAll(chan, "CBU", {channel=args.channel, operator=u.getName(con), character=args.character}) + if targetonline and c.inChannel(chan, target) then + partChannel(chan, target) end return const.FERR_OK @@ -355,31 +369,24 @@ function (con, args) return const.FERR_CHANNEL_NOT_FOUND end - local fchar, char = u.getConnection(string.lower(args.character)) - if fchar ~= true then - return const.FERR_USER_NOT_FOUND - end - - if c.isMod(chan, con) ~= true then - return const.FERR_NOT_OP - end + local targetname = string.lower(args.character) + local targetonline, target = u.getConnection(targetname) - if (c.inChannel(chan, con) ~= true) or (c.inChannel(chan, char) ~= true) then - return const.FERR_USER_NOT_IN_CHANNEL + local cankick = canChannelKickban(con, targetname, target, chan) + if cankick ~= nil then + return cankick end - local chantype = c.getType(chan) if chantype == "public" then - if c.isMod(chan, char) == true then - return const.FERR_DENIED_ON_OP - end - s.logAction(con, "CKU", args) + s.logAction(con, "CBU", args) elseif chantype == "private" then - c.removeInvite(chan, string.lower(args.character)) + c.removeInvite(chan, targetname) end - c.sendAll(chan, "CKU", {channel=args.channel, operator=u.getName(con), character=u.getName(char)}) - partChannel(chan, char) + c.sendAll(chan, "CKU", {channel=args.channel, operator=u.getName(con), character=args.character}) + if targetonline and c.inChannel(chan, target) then + partChannel(chan, target) + end return const.FERR_OK end @@ -578,41 +585,24 @@ function (con, args) return const.FERR_CHANNEL_NOT_FOUND end - if c.isMod(chan, con) ~= true then - return const.FERR_NOT_OP - end + local targetname = string.lower(args.character) + local targetonline, target = u.getConnection(lowertargetname) - if c.inChannel(chan, con) ~= true then - return const.FERR_USER_NOT_IN_CHANNEL + local cantimeout = canChannelKickban(con, targetname, target, chan) + if cantimeout ~= nil then + return cantimeout end - local targetonline, char = u.getConnection(string.lower(args.character)) - local chantype = c.getType(chan) if chantype == "public" then - if c.isMod(chan, char) == true then - return const.FERR_DENIED_ON_OP - end s.logAction(con, "CTU", args) elseif chantype == "private" then - c.removeInvite(chan, string.lower(args.character)) + c.removeInvite(chan, targetname) end - if targetonline == false then - char = args.character - if c.isBanned(chan, string.lower(char)) == true then - return const.FERR_ALREADY_CHANNEL_BANNED - end - c.sendAll(chan, "CTU", {channel=args.channel, operator=u.getName(con), character=char, length=tonumber(args.length)}) - c.timeout(chan, con, string.lower(char), length) - else - if c.isBanned(chan, char) == true then - return const.FERR_ALREADY_CHANNEL_BANNED - end - c.sendAll(chan, "CTU", {channel=args.channel, operator=u.getName(con), character=u.getName(char), length=tonumber(args.length)}) - c.timeout(chan, con, char, length) - if c.inChannel(chan, char) == true then - partChannel(chan, char) - end + c.sendAll(chan, "CTU", {channel=args.channel, operator=u.getName(con), character=args.character, length=tonumber(args.length)}) + c.timeout(chan, con, targetname, length) + if targetonline and c.inChannel(chan, target) then + partChannel(chan, target) end return const.FERR_OK diff --git a/src/channel.cpp b/src/channel.cpp index 4139f53..ee1bfc8 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -164,20 +164,12 @@ bool Channel::inChannel(ConnectionPtr con) { } bool Channel::isBanned(ConnectionPtr con) { - chbanmap_t::const_iterator itr = bans.find(con->characterNameLower); - if (itr != bans.end()) { - BanRecord b = itr->second; - if (b.timeout == 0 || b.timeout >= time(0)) { - return true; - } else { - bans.erase(con->characterNameLower); - } - } - - return false; + // no reason to rewrite the function: just pass it through + // to the other overload + return isBanned(con->characterNameLower); } -bool Channel::isBanned(string& name) { +bool Channel::isBanned(const string& name) { chbanmap_t::const_iterator itr = bans.find(name); if (itr != bans.end()) { BanRecord b = itr->second; @@ -192,18 +184,10 @@ bool Channel::isBanned(string& name) { } bool Channel::getBan(ConnectionPtr con, BanRecord& ban) { - chbanmap_t::const_iterator itr = bans.find(con->characterNameLower); - if (itr != bans.end()) { - BanRecord tmp = itr->second; - ban.banner = tmp.banner; - ban.time = tmp.time; - ban.timeout = tmp.timeout; - return true; - } - return false; + return getBan(con->characterNameLower, ban); } -bool Channel::getBan(string& name, BanRecord& ban) { +bool Channel::getBan(const string& name, BanRecord& ban) { chbanmap_t::const_iterator itr = bans.find(name); if (itr != bans.end()) { BanRecord tmp = itr->second; @@ -233,32 +217,39 @@ void Channel::remMod(string& dest) { moderators.erase(dest); } -bool Channel::isMod(ConnectionPtr con) { - if (con->globalModerator || con->admin || (owner == con->characterName) || moderators.find(con->characterName) != moderators.end()) - return true; - - return false; +bool Channel::isMod(ConnectionPtr con) const { + return con->globalModerator || con->admin || isOnlyOwner(con->characterName) || isOnlyMod(con->characterName); } -bool Channel::isMod(string& name) { +bool Channel::isMod(const string& name) const { if ((owner == name) || moderators.find(name) != moderators.end()) return true; return false; } -bool Channel::isOwner(ConnectionPtr con) { - if (con->globalModerator || con->admin || (owner == con->characterName)) - return true; +bool Channel::isOnlyMod(ConnectionPtr con) const { + return isOnlyMod(con->characterName); +} - return false; +bool Channel::isOnlyMod(const string& name) const { + return moderators.find(name) != moderators.end(); } -bool Channel::isOwner(string& name) { - if (owner == name) - return true; +bool Channel::isOwner(ConnectionPtr con) const { + return con->globalModerator || con->admin || isOwner(con->characterName); +} - return false; +bool Channel::isOwner(const string& name) const { + return owner == name; +} + +bool Channel::isOnlyOwner(ConnectionPtr con) const { + return isOnlyOwner(con->characterName); +} + +bool Channel::isOnlyOwner(const string& name) const { + return owner == name; } const double Channel::getTimerEntry(ConnectionPtr con) { diff --git a/src/channel.hpp b/src/channel.hpp index c858970..f44777c 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -92,9 +92,9 @@ class Channel : public LBase { void timeout(ConnectionPtr src, string dest, long length); void unban(string& dest); bool isBanned(ConnectionPtr con); - bool isBanned(string& name); + bool isBanned(const string& name); bool getBan(ConnectionPtr con, BanRecord& ban); - bool getBan(string& name, BanRecord& ban); + bool getBan(const string& name, BanRecord& ban); const chbanmap_t& getBanRecords() const { return bans; @@ -107,15 +107,19 @@ class Channel : public LBase { const chmodmap_t& getModRecords() const { return moderators; } - bool isMod(ConnectionPtr con); - bool isMod(string& name); - + bool isMod(ConnectionPtr con) const; + bool isMod(const string& name) const; + bool isOnlyMod(ConnectionPtr con) const; + bool isOnlyMod(const string& name) const; + const string& getOwner() const { return owner; } - bool isOwner(ConnectionPtr con); - bool isOwner(string& name); - + bool isOwner(ConnectionPtr con) const; + bool isOwner(const string& name) const; + bool isOnlyOwner(ConnectionPtr con) const; + bool isOnlyOwner(const string& name) const; + void setOwner(string& name) { owner = name; } diff --git a/src/lua_channel.cpp b/src/lua_channel.cpp index 2cae8c4..5b08fde 100644 --- a/src/lua_channel.cpp +++ b/src/lua_channel.cpp @@ -844,6 +844,60 @@ int LuaChannel::isMod(lua_State* L) { return 1; } +/** + * Returns a boolean that describes if a connection or name is a moderator in the channel. + * @param LUD channel + * @param LUD/string connection/name + * @returns true if the connection or name is in the channels moderator list, false otherwise. + */ +int LuaChannel::isOnlyMod(lua_State* L) { + bool ret = false; + luaL_checkany(L, 2); + + LBase* base = 0; + GETLCHAN(base, L, 1, chan); + int type = lua_type(L, 2); + if (type == LUA_TLIGHTUSERDATA) { + GETLCON(base, L, 2, con); + ret = chan->isOnlyMod(con); + } else if (type == LUA_TSTRING) { + string name = lua_tostring(L, 2); + ret = chan->isOnlyMod(name); + } else + return luaL_error(L, "isOnlyMod expects string or ConnectionPtr as argument 2."); + + lua_pop(L, 2); + lua_pushboolean(L, ret); + return 1; +} + +/** + * Returns a boolean that describes if a connection or name is a moderator in the channel. + * @param LUD channel + * @param LUD/string connection/name + * @returns true if the connection or name is an owner, false otherwise + */ +int LuaChannel::isOnlyOwner(lua_State* L) { + bool ret = false; + luaL_checkany(L, 2); + + LBase* base = 0; + GETLCHAN(base, L, 1, chan); + int type = lua_type(L, 2); + if (type == LUA_TLIGHTUSERDATA) { + GETLCON(base, L, 2, con); + ret = chan->isOnlyOwner(con); + } else if (type == LUA_TSTRING) { + string name = lua_tostring(L, 2); + ret = chan->isOnlyOwner(name); + } else + return luaL_error(L, "isOnlyOwner expects string or ConnectionPtr as argument 2."); + + lua_pop(L, 2); + lua_pushboolean(L, ret); + return 1; +} + /** * Returns a boolean that describes if a connection or name is the channel owner. * @param LUD channel diff --git a/src/lua_channel.hpp b/src/lua_channel.hpp index 889b112..b9ca834 100644 --- a/src/lua_channel.hpp +++ b/src/lua_channel.hpp @@ -71,7 +71,9 @@ class LuaChannel { static int addMod(lua_State* L); static int removeMod(lua_State* L); static int isMod(lua_State* L); + static int isOnlyMod(lua_State* L); static int isOwner(lua_State* L); + static int isOnlyOwner(lua_State* L); static int getModList(lua_State* L); static int checkUpdateTimer(lua_State* L);