diff options
author | Adam <Adam@anope.org> | 2011-08-01 22:37:27 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2011-08-01 22:37:27 -0400 |
commit | f7adc0b35b50f06706872a161f1c7476e6e6981e (patch) | |
tree | 91fbe281f2772136a327fd4fc4c64740d1ab65ab /modules/core/cs_flags.cpp | |
parent | 710c02f3bd3581b7c5b3d48e7604756975219fc8 (diff) |
Rewrote the access systems and added a flags access system
Diffstat (limited to 'modules/core/cs_flags.cpp')
-rw-r--r-- | modules/core/cs_flags.cpp | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/modules/core/cs_flags.cpp b/modules/core/cs_flags.cpp new file mode 100644 index 000000000..48f1a22f1 --- /dev/null +++ b/modules/core/cs_flags.cpp @@ -0,0 +1,455 @@ +/* ChanServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +/*************************************************************************/ + +#include "module.h" + +static struct FlagLevels +{ + ChannelAccess priv; + char default_char; + Anope::string config_name; + Anope::string desc; +} flagLevels[] = { + { CA_ACCESS_CHANGE, 'f', "flag_change", _("Allowed to modify the access list") }, + { CA_ACCESS_LIST, 'l', "flag_list", _("Allowed to view the access list") }, + { CA_AKICK, 'K', "flag_akick", _("Allowed to use AKICK command") }, + { CA_ASSIGN, 's', "flag_assign", _("Allowed to assign/unassign a bot") }, + { CA_AUTOHALFOP, 'H', "flag_autohalfop", _("Automatic mode +h") }, + { CA_AUTOOP, 'O', "flag_autoop", _("Automatic channel operator status") }, + { CA_AUTOOWNER, 'Q', "flag_autoowner", _("Automatic mode +q") }, + { CA_AUTOPROTECT, 'A', "flag_autoprotect", _("Automatic mode +a") }, + { CA_AUTOVOICE, 'V', "flag_autovoice", _("Automatic mode +v") }, + { CA_BADWORDS, 'k', "flag_badwords", _("Allowed to modify channel badwords list") }, + { CA_BAN, 'b', "flag_ban", _("Allowed to use ban users") }, + { CA_FANTASIA, 'c', "flag_fantasia", _("Allowed to use fantasy commands") }, + { CA_FOUNDER, 'F', "flag_founder", _("Allowed to issue commands restricted to channel founders") }, + { CA_GETKEY, 'G', "flag_getkey", _("Allowed to use GETKEY command") }, + { CA_GREET, 'g', "flag_greet", _("Greet message displayed") }, + { CA_HALFOP, 'h', "flag_halfop", _("Allowed to (de)halfop users") }, + { CA_HALFOPME, 'h', "flag_halfopme", _("Allowed to (de)halfop him/herself") }, + { CA_INFO, 'I', "flag_info", _("Allowed to use INFO command with ALL option") }, + { CA_INVITE, 'i', "flag_invite", _("Allowed to use the INVITE command") }, + { CA_KICK, 'k', "flag_kick", _("Allowed to use the KICK command") }, + { CA_MEMO, 'm', "flag_memo", _("Allowed to read channel memos") }, + { CA_MODE, 's', "flag_mode", _("Allowed to change channel modes") }, + { CA_NOKICK, 'N', "flag_nokick", _("Prevents users being kicked by Services") }, + { CA_OPDEOP, 'o', "flag_opdeop", _("Allowed to (de)op users") }, + { CA_OPDEOPME, 'o', "flag_opdeopme", _("Allowed to (de)op him/herself") }, + { CA_OWNER, 'q', "flag_owner", _("Allowed to use (de)owner users") }, + { CA_OWNERME, 'q', "flag_ownerme", _("Allowed to (de)owner him/herself") }, + { CA_PROTECT, 'a', "flag_protect", _("Allowed to (de)protect users") }, + { CA_PROTECTME, 'a', "flag_protectme", _("Allowed to (de)protect him/herself"), }, + { CA_SAY, 'B', "flag_say", _("Allowed to use SAY and ACT commands") }, + { CA_SET, 's', "flag_set", _("Allowed to set channel settings") }, + { CA_SIGNKICK, 'K', "flag_signkick", _("Prevents kicks from being signed") }, + { CA_TOPIC, 't', "flag_topic", _("Allowed to change channel topics") }, + { CA_UNBAN, 'u', "flag_unban", _("Allowed to unban users") }, + { CA_VOICE, 'v', "flag_voice", _("Allowed to (de)voice users") }, + { CA_VOICEME, 'v', "flag_voiceme", _("Allowed to (de)voice him/herself") }, + { CA_SIZE, -1, "", "" } +}; + +class FlagsChanAccess : public ChanAccess +{ + public: + std::set<char> flags; + + FlagsChanAccess(AccessProvider *p) : ChanAccess(p) + { + } + + bool Matches(User *u, NickCore *nc) + { + if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) + return true; + else if (nc && Anope::Match(nc->display, this->mask)) + return true; + return false; + } + + bool HasPriv(ChannelAccess priv) + { + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + if (flagLevels[i].priv == priv) + return this->flags.count(flagLevels[i].default_char); + + return false; + } + + Anope::string Serialize() + { + return Anope::string(this->flags.begin(), this->flags.end()); + } + + void Unserialize(const Anope::string &data) + { + for (unsigned i = data.length(); i > 0; --i) + this->flags.insert(data[i - 1]); + } + + static Anope::string DetermineFlags(ChanAccess *access) + { + if (access->provider->name == "access/flags") + return access->Serialize(); + + std::set<char> buffer; + + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + { + FlagLevels &l = flagLevels[i]; + + if (access->HasPriv(l.priv)) + buffer.insert(l.default_char); + } + + return Anope::string(buffer.begin(), buffer.end()); + } +}; + +class FlagsAccessProvider : public AccessProvider +{ + public: + FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags") + { + } + + ChanAccess *Create() + { + return new FlagsChanAccess(this); + } +}; + +class CommandCSFlags : public Command +{ + void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) + { + User *u = source.u; + + Anope::string mask = params.size() > 2 ? params[2] : ""; + Anope::string flags = params.size() > 3 ? params[3] : ""; + + if (flags.empty()) + { + this->OnSyntaxError(source, ""); + return; + } + + AccessGroup u_access = ci->AccessFor(u); + + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + ChanAccess *current = NULL; + std::set<char> current_flags; + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) + { + current = access; + Anope::string cur_flags = FlagsChanAccess::DetermineFlags(access); + for (unsigned j = cur_flags.length(); j > 0; --j) + current_flags.insert(cur_flags[j - 1]); + break; + } + } + + if (ci->GetAccessCount() >= Config->CSAccessMax) + { + source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax); + return; + } + + bool override = false; + int add = -1; + for (size_t i = 0; i < flags.length(); ++i) + { + char f = flags[i]; + switch (f) + { + case '+': + add = 1; + break; + case '-': + add = 0; + break; + case '*': + if (add == -1) + break; + for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) + { + FlagLevels &l = flagLevels[j]; + if (!u_access.HasPriv(l.priv)) + { + if (u->HasPriv("chanserv/access/modify")) + override = true; + else + continue; + } + if (add == 1) + current_flags.insert(f); + else if (add == 0) + current_flags.erase(f); + } + break; + default: + if (add == -1) + break; + for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) + { + FlagLevels &l = flagLevels[j]; + if (f != l.default_char) + continue; + else if (!u_access.HasPriv(l.priv)) + { + if (u->HasPriv("chanserv/access/modify")) + override = true; + else + { + source.Reply(_("You can not set the \002%c\002 flag."), f); + continue; + } + } + if (add == 1) + current_flags.insert(f); + else if (add == 0) + current_flags.erase(f); + break; + } + } + } + if (current_flags.empty()) + { + if (current != NULL) + { + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, current)); + ci->EraseAccess(current); + source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str()); + } + else + { + source.Reply(_("Insufficient flags given")); + } + return; + } + + service_reference<AccessProvider> provider("access/flags"); + if (!provider) + return; + FlagsChanAccess *access = debug_cast<FlagsChanAccess *>(provider->Create()); + access->ci = ci; + access->mask = mask; + access->creator = u->nick; + access->last_seen = current ? current->last_seen : 0; + access->created = Anope::CurTime; + access->flags = current_flags; + + if (current != NULL) + ci->EraseAccess(current); + + ci->AddAccess(access); + + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "MODIFY " << mask << " with flags " << access->Serialize(); + source.Reply(_("Access for \002%s\002 on %s set to +\002%s\002"), access->mask.c_str(), ci->name.c_str(), access->Serialize().c_str()); + + return; + } + + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) + { + const Anope::string &arg = params.size() > 2 ? params[2] : ""; + + if (!ci->GetAccessCount()) + source.Reply(_("%s access list is empty."), ci->name.c_str()); + else + { + unsigned total = 0; + + for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) + { + ChanAccess *access = ci->GetAccess(i); + const Anope::string &flags = FlagsChanAccess::DetermineFlags(access); + + if (!arg.empty()) + { + if (arg[0] == '+') + { + bool pass = true; + for (size_t j = 1; j < arg.length(); ++j) + if (flags.find(arg[j]) == Anope::string::npos) + pass = false; + if (pass == false) + continue; + } + else if (!Anope::Match(access->mask, arg)) + continue; + } + + if (++total == 1) + { + source.Reply(_("Flags list for %s"), ci->name.c_str()); + } + + source.Reply(_(" %3d %-10s +%-10s [last modified on %s by %s]"), i + 1, access->mask.c_str(), FlagsChanAccess::DetermineFlags(access).c_str(), do_strftime(access->created, source.u->Account(), true).c_str(), access->creator.c_str()); + } + + if (total == 0) + source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); + else if (total == ci->GetAccessCount()) + source.Reply(_("End of access list.")); + else + source.Reply(_("End of access list - %d/%d entries shown."), total, ci->GetAccessCount()); + } + } + + void DoClear(CommandSource &source, ChannelInfo *ci) + { + User *u = source.u; + + if (!IsFounder(u, ci) && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + else + { + ci->ClearAccess(); + + FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); + + source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); + + bool override = !IsFounder(u, ci); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; + } + + return; + } + + public: + CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 2, 4) + { + this->SetDesc(_("Modify the list of privileged users")); + this->SetSyntax(_("\037channel\037 MODIFY \037mask\037 \037changes\037")); + this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]")); + this->SetSyntax(_("\037channel\037 CLEAR\002")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &cmd = params[1]; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(chan); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + + bool is_list = cmd.equals_ci("LIST"); + bool has_access = false; + if (u->HasPriv("chanserv/access/modify")) + has_access = true; + else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) + has_access = true; + else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) + has_access = true; + + if (!has_access) + source.Reply(ACCESS_DENIED); + else if (readonly && !is_list) + source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); + else if (cmd.equals_ci("MODIFY")) + this->DoModify(source, ci, params); + else if (cmd.equals_ci("LIST")) + this->DoList(source, ci, params); + else if (cmd.equals_ci("CLEAR")) + this->DoClear(source, ci); + else + this->OnSyntaxError(source, cmd); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("%s is another way to modify the channel access list, similar to\n" + "the XOP and ACCESS methods."), source.command.c_str()); + source.Reply(" "); + source.Reply(_("The MODIFY command allows you to modify the access list. If mask is\n" + "not already on the access list is it added, then the changes are applied.\n" + "If the mask has no more flags, then the mask is removed from the access list.\n" + "Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n" + "only able to modify the access list if you have the proper permission on the channel,\n" + "and even then you can only give other people access to up what you already have.")); + source.Reply(" "); + source.Reply(_("The LIST command allows you to list existing entries on the channel access list.\n" + "If a mask is given, the mask is wildcard matched against all existing entries on the\n" + "access list, and only those entries are returned. If a set of flags is given, only those\n" + "on the access list with the specified flags are returned.")); + source.Reply(" "); + source.Reply(_("The CLEAR command clears the channel access list, which requires channel founder.")); + source.Reply(" "); + source.Reply(_("The available flags are:")); + + std::multimap<char, FlagLevels *, std::less<ci::string> > levels; + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + levels.insert(std::make_pair(flagLevels[i].default_char, &flagLevels[i])); + for (std::multimap<char, FlagLevels *, std::less<ci::string> >::iterator it = levels.begin(), it_end = levels.end(); it != it_end; ++it) + { + FlagLevels *l = it->second; + source.Reply(" %c - %s", l->default_char, translate(source.u->Account(), l->desc.c_str())); + } + + return true; + } +}; + +class CSFlags : public Module +{ + FlagsAccessProvider accessprovider; + CommandCSFlags commandcsflags; + + public: + CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + accessprovider(this), commandcsflags(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&accessprovider); + ModuleManager::RegisterService(&commandcsflags); + + Implementation i[] = { I_OnReload }; + ModuleManager::Attach(i, this, 1); + + this->OnReload(); + } + + void OnReload() + { + ConfigReader config; + + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + { + FlagLevels &l = flagLevels[i]; + + const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); + if (value.empty()) + continue; + l.default_char = value[0]; + } + } +}; + +MODULE_INIT(CSFlags) |