From f7adc0b35b50f06706872a161f1c7476e6e6981e Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 1 Aug 2011 22:37:27 -0400 Subject: Rewrote the access systems and added a flags access system --- data/commands.example.conf | 7 +- data/example.conf | 98 +++++++- data/mysql/tables.sql | 6 +- include/access.h | 91 +++++++ include/extern.h | 10 - include/modules.h | 20 +- include/regchannel.h | 69 +----- include/services.h | 82 ++---- modules/core/bs_assign.cpp | 8 +- modules/core/bs_badwords.cpp | 12 +- modules/core/bs_control.cpp | 4 +- modules/core/bs_info.cpp | 2 +- modules/core/bs_kick.cpp | 8 +- modules/core/bs_main.cpp | 4 +- modules/core/bs_set.cpp | 4 +- modules/core/cs_access.cpp | 408 ++++++++++++++++++++---------- modules/core/cs_akick.cpp | 31 ++- modules/core/cs_ban.cpp | 9 +- modules/core/cs_clearusers.cpp | 5 +- modules/core/cs_clone.cpp | 6 +- modules/core/cs_drop.cpp | 4 +- modules/core/cs_flags.cpp | 455 ++++++++++++++++++++++++++++++++++ modules/core/cs_getkey.cpp | 4 +- modules/core/cs_info.cpp | 3 +- modules/core/cs_invite.cpp | 2 +- modules/core/cs_kick.cpp | 9 +- modules/core/cs_main.cpp | 23 +- modules/core/cs_mode.cpp | 25 +- modules/core/cs_modes.cpp | 58 +++-- modules/core/cs_saset_noexpire.cpp | 2 +- modules/core/cs_set_bantype.cpp | 2 +- modules/core/cs_set_description.cpp | 2 +- modules/core/cs_set_founder.cpp | 4 +- modules/core/cs_set_keeptopic.cpp | 2 +- modules/core/cs_set_opnotice.cpp | 2 +- modules/core/cs_set_peace.cpp | 2 +- modules/core/cs_set_persist.cpp | 2 +- modules/core/cs_set_private.cpp | 2 +- modules/core/cs_set_restricted.cpp | 6 +- modules/core/cs_set_secure.cpp | 2 +- modules/core/cs_set_securefounder.cpp | 2 +- modules/core/cs_set_secureops.cpp | 2 +- modules/core/cs_set_signkick.cpp | 2 +- modules/core/cs_set_successor.cpp | 4 +- modules/core/cs_set_topiclock.cpp | 2 +- modules/core/cs_set_xop.cpp | 146 ----------- modules/core/cs_status.cpp | 75 ------ modules/core/cs_topic.cpp | 4 +- modules/core/cs_unban.cpp | 2 +- modules/core/cs_xop.cpp | 417 ++++++++++++++++++++++--------- modules/core/db_mysql.cpp | 51 ++-- modules/core/db_plain.cpp | 102 ++++---- modules/core/ms_del.cpp | 2 +- modules/core/ms_ignore.cpp | 2 +- modules/core/ms_info.cpp | 2 +- modules/core/ms_list.cpp | 2 +- modules/core/ms_main.cpp | 4 +- modules/core/ms_read.cpp | 2 +- modules/core/ms_set.cpp | 2 +- modules/core/ns_ajoin.cpp | 4 +- modules/core/ns_alist.cpp | 123 ++------- modules/extra/cs_appendtopic.cpp | 4 +- modules/extra/cs_enforce.cpp | 7 +- modules/extra/cs_tban.cpp | 2 +- modules/extra/m_helpchan.cpp | 2 +- modules/extra/m_statusupdate.cpp | 52 ++-- src/access.cpp | 140 +++++++++++ src/botserv.cpp | 14 +- src/channels.cpp | 38 +-- src/chanserv.cpp | 189 +------------- src/config.cpp | 2 - src/regchannel.cpp | 195 ++++----------- 72 files changed, 1773 insertions(+), 1319 deletions(-) create mode 100644 include/access.h create mode 100644 modules/core/cs_flags.cpp delete mode 100644 modules/core/cs_set_xop.cpp delete mode 100644 modules/core/cs_status.cpp create mode 100644 src/access.cpp diff --git a/data/commands.example.conf b/data/commands.example.conf index 0fd81e49c..ff7cbb581 100644 --- a/data/commands.example.conf +++ b/data/commands.example.conf @@ -50,6 +50,8 @@ module { name = "cs_clone" } command { service = "ChanServ"; name = "CLONE"; command = "chanserv/clone"; } module { name = "cs_drop" } command { service = "ChanServ"; name = "DROP"; command = "chanserv/drop"; } +module { name = "cs_flags" } +command { service = "ChanServ"; name = "FLAGS"; command = "chanserv/flags"; } module { name = "cs_getkey" } command { service = "ChanServ"; name = "GETKEY"; command = "chanserv/getkey"; } module { name = "cs_info" } @@ -127,13 +129,8 @@ command { service = "ChanServ"; name = "SASET SUCCESSOR"; command = "chanserv/sa module { name = "cs_set_topiclock" } command { service = "ChanServ"; name = "SET TOPICLOCK"; command = "chanserv/set/topiclock"; } command { service = "ChanServ"; name = "SASET TOPICLOCK"; command = "chanserv/saset/topiclock"; } -module { name = "cs_set_xop" } -command { service = "ChanServ"; name = "SET XOP"; command = "chanserv/set/xop"; } -command { service = "ChanServ"; name = "SASET XOP"; command = "chanserv/saset/xop"; } module { name = "cs_saset_noexpire" } command { service = "ChanServ"; name = "SASET NOEXPIRE"; command = "chanserv/saset/noexpire"; } -module { name = "cs_status" } -command { service = "ChanServ"; name = "STATUS"; command = "chanserv/status"; } module { name = "cs_suspend" } command { service = "ChanServ"; name = "SUSPEND"; command = "chanserv/suspend"; } command { service = "ChanServ"; name = "UNSUSPEND"; command = "chanserv/unsuspend"; } diff --git a/data/example.conf b/data/example.conf index 3479db966..22a5ee201 100644 --- a/data/example.conf +++ b/data/example.conf @@ -439,7 +439,7 @@ options /* * Modes that will not be allowed to be locked. Oper only modes such as +O * are always restricted from regular users and are not affected by this. - * Leave blank for no restrictions. + * Comment out for no restrictions. */ nomlock = "z" @@ -646,14 +646,13 @@ chanserv * - signkicklevel: Same as above, but the kick will not be signed if the user is at the same access * level or superior to the target * - topiclock: Disallow the topic to be changed except with ChanServ's TOPIC command - * - xop: Enable use of the xOP system * - persist: Keep the channel open at all times * - none: No defaults * * This directive is optional, if left blank, the options will default to keetopic, secure, securefounder, * and signkick. If you really want no defaults, use "none" by itself as the option. */ - defaults="keeptopic peace secure securefounder signkick xop" + defaults="keeptopic peace secure securefounder signkick" /* * The maximum number of channels which may be registered to a single nickname. @@ -738,6 +737,95 @@ chanserv * This directive is optional. */ #opersonly = yes + + /* + * ChanServ levels configuration, for use with cs_access. + * + * These levels are used by the chanserv/access access system. + * The levels configured below will be used as a default by newly registered channels. + * + * The level "founder" is a special level that means anyone with the level_founder permission + * can use (which of course defaults to "founder"). Anyone with the founder level permission in + * a channel can do anything in the channel. + */ + level_change = 10 + level_list = 1 + level_akick = 10 + level_assign = "founder" + level_autohalfop = 4 + level_autoop = 5 + level_autoowner = 9999 + level_autoprotect = 10 + level_autovoice = 3 + level_badwords = 10 + level_ban = 4 + level_fantasia = 3 + level_founder = "founder" + level_getkey = 5 + level_greet = 5 + level_halfop = 5 + level_halfopme = 4 + level_info = 9999 + level_invite = 5 + level_kick = 4 + level_memo = 10 + level_mode = 5 + level_nokick = 1 + level_opdeop = 5 + level_opdeopme = 5 + level_owner = "founder" + level_ownerme = 9999 + level_protect = 9999 + level_protectme = 10 + level_say = 5 + level_signkick = 9999 + level_set = 9999 + level_topic = 5 + level_unban = 4 + level_voice = 4 + level_voiceme = 3 + + /* + * ChanServ flags configuration, for use with cs_flags. + * + * These flags are used by the chanserv/flags access system. + */ + flag_change = "f" + flag_list = "l" + flag_akick = "K" + flag_assign = "s" + flag_autohalfop = "H" + flag_autoop = "O" + flag_autoowner = "Q" + flag_autoprotect = "A" + flag_autovoice = "V" + flag_badwords = "k" + flag_ban = "b" + flag_fantasia = "c" + flag_founder = "F" + flag_getkey = "G" + flag_greet = "g" + flag_halfop = "h" + flag_halfopme = "h" + flag_info = "I" + flag_invite = "i" + flag_kick = "k" + flag_memo = "m" + flag_mode = "s" + flag_nokick = "N" + flag_opdeop = "o" + flag_opdeopme = "o" + flag_owner = "q" + flag_ownerme = "q" + flag_protect = "a" + flag_protectme = "a" + flag_say = "B" + flag_set = "s" + flag_signkick = "K" + flag_topic = "t" + flag_unban = "u" + flag_voice = "v" + flag_voiceme = "v" } module { name = "gl_main" } @@ -1065,7 +1153,7 @@ operserv /* * If set, this option will make Services send an (SVS)KILL command immediately after SNLINE ADD. - * This eliminates the need for killingthe user after the SNLINE has been added. + * This eliminates the need for killing the user after the SNLINE has been added. * *This directive is optional. */ @@ -1073,7 +1161,7 @@ operserv /* * If set, this option will make Services send an (SVS)KILL command immediately after SQLINE ADD. - * This eliminates the need for killingthe user after the SQLINE has been added. + * This eliminates the need for killing the user after the SQLINE has been added. * * This directive is optional. */ diff --git a/data/mysql/tables.sql b/data/mysql/tables.sql index 32bfcde35..4f9665869 100644 --- a/data/mysql/tables.sql +++ b/data/mysql/tables.sql @@ -63,11 +63,13 @@ CREATE TABLE IF NOT EXISTS `anope_bs_info_metadata` ( -- CREATE TABLE IF NOT EXISTS `anope_cs_access` ( - `level` int(11) NOT NULL DEFAULT '0', - `display` varchar(255) NOT NULL DEFAULT '', + `provider` varchar(255) NOT NULL DEFAULT '', + `data` varchar(255) NOT NULL DEFAULT '', + `mask` varchar(255) NOT NULL DEFAULT '', `channel` varchar(255) NOT NULL DEFAULT '', `last_seen` int(10) unsigned NOT NULL DEFAULT '0', `creator` varchar(255) NOT NULL DEFAULT '', + `created` int(11) unsigned NOT NULL DEFAULT '0', UNIQUE KEY `channel` (`channel`,`display`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/include/access.h b/include/access.h new file mode 100644 index 000000000..69f8f4bc7 --- /dev/null +++ b/include/access.h @@ -0,0 +1,91 @@ +#ifndef ACCESS_H +#define ACCESS_H + +enum ChannelAccess +{ + CA_ACCESS_LIST, + CA_NOKICK, + CA_FANTASIA, + CA_GREET, + CA_AUTOVOICE, + CA_VOICEME, + CA_VOICE, + CA_INFO, + CA_SAY, + CA_AUTOHALFOP, + CA_HALFOPME, + CA_HALFOP, + CA_KICK, + CA_SIGNKICK, + CA_BAN, + CA_TOPIC, + CA_MODE, + CA_GETKEY, + CA_INVITE, + CA_UNBAN, + CA_AUTOOP, + CA_OPDEOPME, + CA_OPDEOP, + CA_AUTOPROTECT, + CA_AKICK, + CA_BADWORDS, + CA_ASSIGN, + CA_MEMO, + CA_ACCESS_CHANGE, + CA_PROTECTME, + CA_PROTECT, + CA_SET, + CA_AUTOOWNER, + CA_OWNERME, + CA_OWNER, + CA_FOUNDER, + CA_SIZE +}; + +class ChanAccess; + +class AccessProvider : public Service +{ + public: + AccessProvider(Module *o, const Anope::string &n); + virtual ~AccessProvider(); + virtual ChanAccess *Create() = 0; +}; + +class CoreExport ChanAccess +{ + public: + AccessProvider *provider; + ChannelInfo *ci; + Anope::string mask; + Anope::string creator; + time_t last_seen; + time_t created; + + ChanAccess(AccessProvider *p); + virtual ~ChanAccess(); + virtual bool Matches(User *u, NickCore *nc) = 0; + virtual bool HasPriv(ChannelAccess priv) = 0; + virtual Anope::string Serialize() = 0; + virtual void Unserialize(const Anope::string &data) = 0; + + bool operator>(ChanAccess &other); + bool operator<(ChanAccess &other); + bool operator>=(ChanAccess &other); + bool operator<=(ChanAccess &other); +}; + +class AccessGroup : public std::vector +{ + public: + AccessGroup(); + bool HasPriv(ChannelAccess priv) const; + ChanAccess *Highest() const; + bool operator>(const AccessGroup &other) const; + bool operator<(const AccessGroup &other) const; + bool operator>=(const AccessGroup &other) const; + bool operator<=(const AccessGroup &other) const; +}; + +#endif + diff --git a/include/extern.h b/include/extern.h index 18df923a8..0b90c6e14 100644 --- a/include/extern.h +++ b/include/extern.h @@ -42,8 +42,6 @@ E Channel *findchan(const Anope::string &chan); E User *nc_on_chan(Channel *c, const NickCore *nc); -E Anope::string get_xop_level(int level); - E void do_cmode(const Anope::string &source, const Anope::string &channel, const Anope::string &modes, const Anope::string &ts); E void do_join(const Anope::string &source, const Anope::string &channels, const Anope::string &ts); E void do_kick(const Anope::string &source, const Anope::string &channel, const Anope::string &users, const Anope::string &reason); @@ -53,21 +51,13 @@ E void chan_set_correct_modes(User *user, Channel *c, int give_modes); /**** chanserv.c ****/ -E LevelInfo levelinfo[]; - - -E void reset_levels(ChannelInfo *ci); - E void check_modes(Channel *c); E ChannelInfo *cs_findchan(const Anope::string &chan); -E int check_access(User *user, ChannelInfo *ci, int what); E bool IsFounder(User *user, ChannelInfo *ci); E void update_cs_lastseen(User *user, ChannelInfo *ci); E int get_idealban(ChannelInfo *ci, User *u, Anope::string &ret); -E int levelinfo_maxwidth; - /**** config.c ****/ E ConfigurationFile services_conf; diff --git a/include/modules.h b/include/modules.h index 28627f506..baf437ff8 100644 --- a/include/modules.h +++ b/include/modules.h @@ -646,13 +646,6 @@ class CoreExport Module : public Extensible */ virtual void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) { } - /** Called when access is changed - * @param ci The channel - * @param u The user who changed the access - * @param u access The access changed - */ - virtual void OnAccessChange(ChannelInfo *ci, User *u, ChanAccess *access) { } - /** Called when access is added * @param ci The channel * @param u The user who added the access @@ -1029,7 +1022,7 @@ enum Implementation I_OnNickUpdate, /* ChanServ */ - I_OnChanForbidden, I_OnChanSuspend, I_OnChanDrop, I_OnPreChanExpire, I_OnChanExpire, I_OnAccessAdd, I_OnAccessChange, + I_OnChanForbidden, I_OnChanSuspend, I_OnChanDrop, I_OnPreChanExpire, I_OnChanExpire, I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, I_OnChanRegistered, I_OnChanUnsuspend, I_OnDelChan, I_OnChannelCreate, I_OnChannelDelete, I_OnAkickAdd, I_OnAkickDel, I_OnCheckKick, I_OnChanInfo, I_OnFindChan, @@ -1241,17 +1234,6 @@ class CallBack : public Timer } }; -class CoreExport Service : public Base -{ - public: - Module *owner; - Anope::string name; - - Service(Module *o, const Anope::string &n); - - virtual ~Service(); -}; - template class service_reference : public dynamic_reference { diff --git a/include/regchannel.h b/include/regchannel.h index 9c3d1c9b9..0a4a7827a 100644 --- a/include/regchannel.h +++ b/include/regchannel.h @@ -44,8 +44,6 @@ enum ChannelInfoFlag CI_SIGNKICK, /* Sign kicks if level is < than the one defined by the SIGNKIGK level */ CI_SIGNKICK_LEVEL, - /* Uses XOP */ - CI_XOP, /* Channel is suspended */ CI_SUSPENDED, /* Channel still exists when emptied, this can be caused by setting a perm @@ -61,20 +59,7 @@ enum ChannelInfoFlag const Anope::string ChannelInfoFlagStrings[] = { "BEGIN", "KEEPTOPIC", "SECUREOPS", "PRIVATE", "TOPICLOCK", "RESTRICTED", "PEACE", "SECURE", "NO_EXPIRE", "MEMO_HARDMAX", "OPNOTICE", "SECUREFOUNDER", - "SIGNKICK", "SIGNKICK_LEVEL", "XOP", "SUSPENDED", "PERSIST", "" -}; - -class CoreExport ChanAccess -{ - Anope::string mask; /* Mask of the access entry */ - public: - int16 level; - NickCore *nc; /* NC of the entry, if the entry is a valid nickcore */ - time_t last_seen; - Anope::string creator; - - ChanAccess(const Anope::string &umask); - const Anope::string &GetMask(); + "SIGNKICK", "SIGNKICK_LEVEL", "SUSPENDED", "PERSIST", "" }; /** Flags for auto kick @@ -117,9 +102,9 @@ class CoreExport ChannelInfo : public Extensible, public Flags ModeList; - private: + NickCore *founder; /* Channel founder */ - std::vector access; /* List of authorized users */ + std::vector access; /* List of authorized users */ std::vector akick; /* List of users to kickban */ std::vector badwords; /* List of badwords */ ModeList mode_locks; @@ -151,17 +136,16 @@ class CoreExport ChannelInfo : public Extensible, public Flags botflags; - int16 *ttb; /* Times to ban for each kicker */ + int16 ttb[TTB_SIZE]; /* Times to ban for each kicker */ int16 capsmin, capspercent; /* For CAPS kicker */ int16 floodlines, floodsecs; /* For FLOOD kicker */ @@ -183,16 +167,9 @@ class CoreExport ChannelInfo : public Extensible, public Flags class Flags } }; +class Module; + +class CoreExport Service : public Base +{ + public: + Module *owner; + Anope::string name; + + Service(Module *o, const Anope::string &n); + + virtual ~Service(); +}; + + #include "sockets.h" #include "socketengine.h" #include "extensible.h" @@ -562,28 +576,6 @@ class CoreExport HostInfo const time_t GetTime() const; }; -enum AccessLevel -{ - /* Note that these two levels also serve as exclusive boundaries for valid - * access levels. ACCESS_FOUNDER may be assumed to be strictly greater - * than any valid access level, and ACCESS_INVALID may be assumed to be - * strictly less than any valid access level. Also read below. - */ - ACCESS_FOUNDER = 10001, /* Numeric level indicating founder access */ - ACCESS_INVALID = -10000, /* Used in levels[] for disabled settings */ - /* There is one exception to the above access levels: SuperAdmins will have - * access level 10001. This level is never stored, however; it is only used - * in comparison and to let SuperAdmins win from founders where needed - */ - ACCESS_SUPERADMIN = 10002, - /* Levels for xOP */ - ACCESS_VOP = 3, - ACCESS_HOP = 4, - ACCESS_AOP = 5, - ACCESS_SOP = 10, - ACCESS_QOP = 10000 -}; - /** Flags for badwords */ enum BadWordType @@ -605,51 +597,6 @@ struct BadWord BadWordType type; }; -/* Indices for cmd_access[]: */ -enum ChannelAccess -{ - CA_INVITE, - CA_AKICK, - CA_SET, /* but not FOUNDER or PASSWORD */ - CA_UNBAN, - CA_AUTOOP, - CA_AUTOVOICE, - CA_OPDEOP, /* ChanServ commands OP and DEOP */ - CA_ACCESS_LIST, - CA_NOJOIN, /* Maximum */ - CA_ACCESS_CHANGE, - CA_MEMO, - CA_ASSIGN, /* BotServ ASSIGN command */ - CA_BADWORDS, /* BotServ BADWORDS command */ - CA_NOKICK, /* Not kicked by the bot */ - CA_FANTASIA, - CA_SAY, - CA_GREET, - CA_VOICEME, - CA_VOICE, - CA_GETKEY, - CA_AUTOHALFOP, - CA_AUTOPROTECT, - CA_OPDEOPME, - CA_HALFOPME, - CA_HALFOP, - CA_PROTECTME, - CA_PROTECT, - CA_KICKME, - CA_KICK, - CA_SIGNKICK, - CA_BANME, - CA_BAN, - CA_TOPIC, - CA_MODE, - CA_INFO, - CA_AUTOOWNER, - CA_OWNER, - CA_OWNERME, - CA_FOUNDER, - CA_SIZE -}; - /* BotServ SET flags */ enum BotServFlag { @@ -716,6 +663,7 @@ enum TTB_SIZE }; +#include "access.h" #include "regchannel.h" /*************************************************************************/ diff --git a/modules/core/bs_assign.cpp b/modules/core/bs_assign.cpp index 0a08eec3b..803368994 100644 --- a/modules/core/bs_assign.cpp +++ b/modules/core/bs_assign.cpp @@ -49,7 +49,7 @@ class CommandBSAssign : public Command return; } - if (ci->botflags.HasFlag(BS_NOBOT) || (!check_access(u, ci, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) + if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->HasPriv(u, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) { source.Reply(ACCESS_DENIED); return; @@ -67,7 +67,7 @@ class CommandBSAssign : public Command return; } - bool override = !check_access(u, ci, CA_ASSIGN); + bool override = !ci->HasPriv(u, CA_ASSIGN); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << bi->nick; bi->Assign(u, ci); @@ -111,7 +111,7 @@ class CommandBSUnassign : public Command return; } - if (!u->HasPriv("botserv/administration") && !check_access(u, ci, CA_ASSIGN)) + if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_ASSIGN)) { source.Reply(ACCESS_DENIED); return; @@ -129,7 +129,7 @@ class CommandBSUnassign : public Command return; } - bool override = !check_access(u, ci, CA_ASSIGN); + bool override = !ci->HasPriv(u, CA_ASSIGN); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << ci->bi->nick; ci->bi->UnAssign(u, ci); diff --git a/modules/core/bs_badwords.cpp b/modules/core/bs_badwords.cpp index a272bdff9..a27dca2d4 100644 --- a/modules/core/bs_badwords.cpp +++ b/modules/core/bs_badwords.cpp @@ -60,7 +60,7 @@ class BadwordsDelCallback : public NumberList public: BadwordsDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0), override(false) { - if (!check_access(source.u, ci, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) + if (!ci->HasPriv(source.u, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) this->override = true; } @@ -90,7 +90,7 @@ class CommandBSBadwords : public Command private: void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word) { - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "LIST"; if (!ci->GetBadWordCount()) @@ -167,7 +167,7 @@ class CommandBSBadwords : public Command } } - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "ADD " << realword; ci->AddBadWord(realword, type); @@ -203,7 +203,7 @@ class CommandBSBadwords : public Command return; } - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "DEL " << badword->word; source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); @@ -216,7 +216,7 @@ class CommandBSBadwords : public Command void DoClear(CommandSource &source, ChannelInfo *ci) { - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "CLEAR"; ci->ClearBadWords(); @@ -254,7 +254,7 @@ class CommandBSBadwords : public Command } - if (!check_access(u, ci, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) + if (!ci->HasPriv(u, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_control.cpp b/modules/core/bs_control.cpp index 431739693..16a5b372a 100644 --- a/modules/core/bs_control.cpp +++ b/modules/core/bs_control.cpp @@ -35,7 +35,7 @@ class CommandBSSay : public Command return; } - if (!check_access(u, ci, CA_SAY)) + if (!ci->HasPriv(u, CA_SAY)) { source.Reply(ACCESS_DENIED); return; @@ -98,7 +98,7 @@ class CommandBSAct : public Command return; } - if (!check_access(u, ci, CA_SAY)) + if (!ci->HasPriv(u, CA_SAY)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_info.cpp b/modules/core/bs_info.cpp index 38178a07b..b48a0b296 100644 --- a/modules/core/bs_info.cpp +++ b/modules/core/bs_info.cpp @@ -70,7 +70,7 @@ class CommandBSInfo : public Command } else if ((ci = cs_findchan(query))) { - if (!check_access(u, ci, CA_FOUNDER) && !u->HasPriv("botserv/administration")) + if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("botserv/administration")) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_kick.cpp b/modules/core/bs_kick.cpp index a8c34e5cd..3f433f773 100644 --- a/modules/core/bs_kick.cpp +++ b/modules/core/bs_kick.cpp @@ -41,13 +41,13 @@ class CommandBSKick : public Command this->OnSyntaxError(source, ""); else if (!value.equals_ci("ON") && !value.equals_ci("OFF")) this->OnSyntaxError(source, ""); - else if (!check_access(u, ci, CA_SET) && !u->HasPriv("botserv/administration")) + else if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) source.Reply(ACCESS_DENIED); else if (!ci->bi) source.Reply(BOT_NOT_ASSIGNED); else { - bool override = !check_access(u, ci, CA_SET); + bool override = !ci->HasPriv(u, CA_SET); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << option << " " << value; if (option.equals_ci("BADWORDS")) @@ -768,7 +768,7 @@ class BSKick : public Module return; bool Allow = true; - if (check_access(u, ci, CA_NOKICK)) + if (ci->HasPriv(u, CA_NOKICK)) Allow = false; else if (ci->botflags.HasFlag(BS_DONTKICKOPS) && (c->HasUserStatus(u, CMODE_HALFOP) || c->HasUserStatus(u, CMODE_OP) || c->HasUserStatus(u, CMODE_PROTECT) || c->HasUserStatus(u, CMODE_OWNER))) Allow = false; @@ -993,7 +993,7 @@ class BSKick : public Module Channel *chan = (*it)->chan; ++it; - if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !check_access(u, chan->ci, CA_NOKICK)) + if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->HasPriv(u, CA_NOKICK)) { check_ban(chan->ci, u, TTB_AMSGS); bot_kick(chan->ci, u, _("Don't use AMSGs!")); diff --git a/modules/core/bs_main.cpp b/modules/core/bs_main.cpp index 515b99ae0..69eab6040 100644 --- a/modules/core/bs_main.cpp +++ b/modules/core/bs_main.cpp @@ -81,7 +81,7 @@ class BotServCore : public Module if (bi == NULL || !bi->commands.count(command)) return; - if (check_access(u, c->ci, CA_FANTASIA)) + if (c->ci->HasPriv(u, CA_FANTASIA)) { this->fantasy_channel = c; @@ -125,7 +125,7 @@ class BotServCore : public Module * to has synced, or we'll get greet-floods when the net * recovers from a netsplit. -GD */ - if (c->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && check_access(user, c->ci, CA_GREET) && user->server->IsSynced()) + if (c->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && c->ci->HasPriv(user, CA_GREET) && user->server->IsSynced()) { ircdproto->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), user->Account()->greet.c_str()); c->ci->bi->lastmsg = Anope::CurTime; diff --git a/modules/core/bs_set.cpp b/modules/core/bs_set.cpp index 0fbba874e..ccd1bb615 100644 --- a/modules/core/bs_set.cpp +++ b/modules/core/bs_set.cpp @@ -59,11 +59,11 @@ class CommandBSSet : public Command } else if (!(ci = cs_findchan(chan))) source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - else if (!u->HasPriv("botserv/administration") && !check_access(u, ci, CA_SET)) + else if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_SET)) source.Reply(ACCESS_DENIED); else { - bool override = !check_access(u, ci, CA_SET); + bool override = !ci->HasPriv(u, CA_SET); Log(override ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << option << " " << value; if (option.equals_ci("DONTKICKOPS")) diff --git a/modules/core/cs_access.cpp b/modules/core/cs_access.cpp index 643292c38..777b0c176 100644 --- a/modules/core/cs_access.cpp +++ b/modules/core/cs_access.cpp @@ -13,6 +13,132 @@ #include "module.h" +enum +{ + ACCESS_INVALID = -10000, + ACCESS_FOUNDER = 10001 +}; + +static struct AccessLevels +{ + ChannelAccess priv; + int default_level; + Anope::string config_name; + Anope::string name; + Anope::string desc; +} defaultLevels[] = { + { CA_ACCESS_CHANGE, 10, "level_change", "ACC-CHANGE", _("Allowed to modify the access list") }, + { CA_ACCESS_LIST, 1, "level_list", "ACC-LIST", _("Allowed to view the access list") }, + { CA_AKICK, 10, "level_akick", "AKICK", _("Allowed to use AKICK command") }, + { CA_ASSIGN, ACCESS_FOUNDER, "level_assign", "ASSIGN", _("Allowed to assign/unassign a bot") }, + { CA_AUTOHALFOP, 4, "level_autohalfop", "AUTOHALFOP", _("Automatic mode +h") }, + { CA_AUTOOP, 5, "level_autoop", "AUTOOP", _("Automatic channel operator status") }, + { CA_AUTOOWNER, 10000, "level_autoowner", "AUTOOWNER", _("Automatic mode +q") }, + { CA_AUTOPROTECT, 10, "level_autoprotect", "AUTOPROTECT", _("Automatic mode +a") }, + { CA_AUTOVOICE, 3, "level_autovoice", "AUTOVOICE", _("Automatic mode +v") }, + { CA_BADWORDS, 10, "level_badwords", "BADWORDS", _("Allowed to modify channel badwords list") }, + { CA_BAN, 4, "level_ban", "BAN", _("Allowed to use ban users") }, + { CA_FANTASIA, 3, "level_fantasia", "FANTASIA", _("Allowed to use fantaisist commands") }, + { CA_FOUNDER, ACCESS_FOUNDER, "level_founder", "FOUNDER", _("Allowed to issue commands restricted to channel founders") }, + { CA_GETKEY, 5, "level_getkey", "GETKEY", _("Allowed to use GETKEY command") }, + { CA_GREET, 5, "level_greet", "GREET", _("Greet message displayed") }, + { CA_HALFOP, 5, "level_halfop", "HALFOP", _("Allowed to (de)halfop users") }, + { CA_HALFOPME, 4, "level_halfopme", "HALFOPME", _("Allowed to (de)halfop him/herself") }, + { CA_INFO, 10000, "level_info", "INFO", _("Allowed to use INFO command with ALL option") }, + { CA_INVITE, 5, "level_invite", "INVITE", _("Allowed to use the INVITE command") }, + { CA_KICK, 4, "level_kick", "KICK", _("Allowed to use the KICK command") }, + { CA_MEMO, 10, "level_memo", "MEMO", _("Allowed to read channel memos") }, + { CA_MODE, 5, "level_mode", "MODE", _("Allowed to change channel modes") }, + { CA_NOKICK, 1, "level_nokick", "NOKICK", _("Never kicked by the bot's kickers") }, + { CA_OPDEOP, 5, "level_opdeop", "OPDEOP", _("Allowed to (de)op users") }, + { CA_OPDEOPME, 5, "level_opdeopme", "OPDEOPME", _("Allowed to (de)op him/herself") }, + { CA_OWNER, ACCESS_FOUNDER, "level_owner", "OWNER", _("Allowed to use (de)owner users") }, + { CA_OWNERME, 10000, "level_ownerme", "OWNERME", _("Allowed to (de)owner him/herself") }, + { CA_PROTECT, 10000, "level_protect", "PROTECT", _("Allowed to (de)protect users") }, + { CA_PROTECTME, 10, "level_protectme", "PROTECTME", _("Allowed to (de)protect him/herself"), }, + { CA_SAY, 5, "level_say", "SAY", _("Allowed to use SAY and ACT commands") }, + { CA_SIGNKICK, ACCESS_FOUNDER, "level_signkick", "SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used") }, + { CA_SET, 10000, "level_set", "SET", _("Allowed to set channel settings") }, + { CA_TOPIC, 5, "level_topic", "TOPIC", _("Allowed to change channel topics") }, + { CA_UNBAN, 4, "level_unban", "UNBAN", _("Allowed to unban users") }, + { CA_VOICE, 4, "level_voice", "VOICE", _("Allowed to (de)voice users") }, + { CA_VOICEME, 3, "level_voiceme", "VOICEME", _("Allowed to (de)voice him/herself") }, + { CA_SIZE, -1, "", "", "" } +}; + +static void reset_levels(ChannelInfo *ci) +{ + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + ci->levels[defaultLevels[i].priv] = defaultLevels[i].default_level; +} + +class AccessChanAccess : public ChanAccess +{ + public: + int level; + + AccessChanAccess(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; defaultLevels[i].priv != CA_SIZE; ++i) + if (defaultLevels[i].priv == priv) + return DetermineLevel(this) >= this->ci->levels[priv]; + return false; + } + + Anope::string Serialize() + { + return stringify(this->level); + } + + void Unserialize(const Anope::string &data) + { + this->level = convertTo(data); + } + + static int DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/access") + { + AccessChanAccess *aaccess = debug_cast(access); + return aaccess->level; + } + else + { + int highest = 1; + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + if (access->ci->levels[defaultLevels[i].priv] > highest && access->HasPriv(defaultLevels[i].priv)) + highest = access->ci->levels[defaultLevels[i].priv]; + return highest; + } + } +}; + +class AccessAccessProvider : public AccessProvider +{ + public: + AccessAccessProvider(Module *o) : AccessProvider(o, "access/access") + { + } + + ChanAccess *Create() + { + return new AccessChanAccess(this); + } +}; + class AccessListCallback : public NumberList { protected: @@ -27,7 +153,7 @@ class AccessListCallback : public NumberList ~AccessListCallback() { if (SentHeader) - source.Reply(_("End of access list."), ci->name.c_str()); + source.Reply(_("End of access list.")); else source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); } @@ -48,13 +174,7 @@ class AccessListCallback : public NumberList static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) { - if (ci->HasFlag(CI_XOP)) - { - Anope::string xop = get_xop_level(access->level); - source.Reply(_(" %3d %s %s"), Number + 1, xop.c_str(), access->GetMask().c_str()); - } - else - source.Reply(_(" %3d %4d %s"), Number + 1, access->level, access->GetMask().c_str()); + source.Reply(_(" %3d %4d %s"), Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str()); } }; @@ -82,20 +202,19 @@ class AccessViewCallback : public AccessListCallback static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) { Anope::string timebuf; - if (ci->c && nc_on_chan(ci->c, access->nc)) - timebuf = "Now"; - else if (access->last_seen == 0) - timebuf = "Never"; - else - timebuf = do_strftime(access->last_seen); - - if (ci->HasFlag(CI_XOP)) + if (ci->c) + for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) + if (access->Matches((*cit)->user, (*cit)->user->Account())) + timebuf = "Now"; + if (timebuf.empty()) { - Anope::string xop = get_xop_level(access->level); - source.Reply(CHAN_ACCESS_VIEW_XOP_FORMAT, Number + 1, xop.c_str(), access->GetMask().c_str(), access->creator.c_str(), timebuf.c_str()); + if (access->last_seen == 0) + timebuf = "Never"; + else + timebuf = do_strftime(access->last_seen); } - else - source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, access->level, access->GetMask().c_str(), access->creator.c_str(), timebuf.c_str()); + + source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str(), access->creator.c_str(), timebuf.c_str()); } }; @@ -111,7 +230,7 @@ class AccessDelCallback : public NumberList public: AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), Denied(false) { - if (!check_access(source.u, ci, CA_ACCESS_CHANGE) && source.u->HasPriv("chanserv/access/modify")) + if (!ci->HasPriv(source.u, CA_ACCESS_CHANGE) && source.u->HasPriv("chanserv/access/modify")) this->override = true; } @@ -141,9 +260,10 @@ class AccessDelCallback : public NumberList ChanAccess *access = ci->GetAccess(Number - 1); - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (u_level <= access->level && !u->HasPriv("chanserv/access/modify")) + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *u_highest = u_access.Highest(); + + if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u->HasPriv("chanserv/access/modify")) { Denied = true; return; @@ -151,9 +271,9 @@ class AccessDelCallback : public NumberList ++Deleted; if (!Nicks.empty()) - Nicks += ", " + access->GetMask(); + Nicks += ", " + access->mask; else - Nicks = access->GetMask(); + Nicks = access->mask; FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); @@ -176,17 +296,18 @@ class CommandCSAccess : public Command } catch (const ConvertException &) { } - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (level >= u_level && !u->HasPriv("chanserv/access/modify")) + if (!level) { - source.Reply(ACCESS_DENIED); + source.Reply(_("Access level must be non-zero.")); return; } - if (!level) + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *highest = u_access.Highest(); + int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); + if (!ci->HasPriv(u, CA_FOUNDER) && level >= u_level && !u->HasPriv("chanserv/access/modify")) { - source.Reply(_("Access level must be non-zero.")); + source.Reply(ACCESS_DENIED); return; } else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) @@ -195,29 +316,22 @@ class CommandCSAccess : public Command return; } - bool override = !check_access(u, ci, CA_ACCESS_CHANGE) || level >= u_level; + bool override = !ci->HasPriv(u, CA_ACCESS_CHANGE) || level >= u_level; - ChanAccess *access = ci->GetAccess(mask, 0, false); - if (access) + for (unsigned i = ci->GetAccessCount(); i > 0; --i) { - /* Don't allow lowering from a level >= u_level */ - if (access->level >= u_level && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - if (access->level == level) + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) { - source.Reply(_("Access level for \002%s\002 on %s unchanged from \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); - return; + /* Don't allow lowering from a level >= u_level */ + if (AccessChanAccess::DetermineLevel(access) >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + ci->EraseAccess(i - 1); + break; } - access->level = level; - - FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, access)); - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; - source.Reply(_("Access level for \002%s\002 on %s changed to \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); - return; } if (ci->GetAccessCount() >= Config->CSAccessMax) @@ -226,12 +340,25 @@ class CommandCSAccess : public Command return; } - access = ci->AddAccess(mask, level, u->nick); + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + service_reference provider("access/access"); + if (!provider) + return; + AccessChanAccess *access = debug_cast(provider->Create()); + access->ci = ci; + access->mask = mask; + access->creator = u->nick; + access->level = level; + access->last_seen = 0; + access->created = Anope::CurTime; + ci->AddAccess(access); FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; - source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); + source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->mask.c_str(), ci->name.c_str(), level); return; } @@ -251,23 +378,32 @@ class CommandCSAccess : public Command } else { - ChanAccess *access = ci->GetAccess(mask, 0, false); - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (!access) - source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); - else if (access->nc != u->Account() && check_access(u, ci, CA_NOJOIN) && u_level <= access->level && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else - { - source.Reply(_("\002%s\002 deleted from %s access list."), access->GetMask().c_str(), ci->name.c_str()); - bool override = !check_access(u, ci, CA_ACCESS_CHANGE) && access->nc != u->Account(); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << access->GetMask() << " from level " << access->level; - - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *highest = u_access.Highest(); + int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); - ci->EraseAccess(access); + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) + { + int access_level = AccessChanAccess::DetermineLevel(access); + if (!access->mask.equals_ci(u->Account()->display) && u_level <= access_level && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + else + { + source.Reply(_("\002%s\002 deleted from %s access list."), access->mask.c_str(), ci->name.c_str()); + bool override = !ci->HasPriv(u, CA_ACCESS_CHANGE) && !access->mask.equals_ci(u->Account()->display); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << access->mask; + + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); + ci->EraseAccess(access); + } + return; + } } + + source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); } return; @@ -292,7 +428,7 @@ class CommandCSAccess : public Command { ChanAccess *access = ci->GetAccess(i); - if (!nick.empty() && !Anope::Match(access->GetMask(), nick)) + if (!nick.empty() && !Anope::Match(access->mask, nick)) continue; if (!SentHeader) @@ -305,7 +441,7 @@ class CommandCSAccess : public Command } if (SentHeader) - source.Reply(_("End of access list."), ci->name.c_str()); + source.Reply(_("End of access list.")); else source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); } @@ -332,7 +468,7 @@ class CommandCSAccess : public Command { ChanAccess *access = ci->GetAccess(i); - if (!nick.empty() && !Anope::Match(access->GetMask(), nick)) + if (!nick.empty() && !Anope::Match(access->mask, nick)) continue; if (!SentHeader) @@ -345,7 +481,7 @@ class CommandCSAccess : public Command } if (SentHeader) - source.Reply(_("End of access list."), ci->name.c_str()); + source.Reply(_("End of access list.")); else source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); } @@ -406,9 +542,9 @@ class CommandCSAccess : public Command bool has_access = false; if (u->HasPriv("chanserv/access/modify")) has_access = true; - else if (is_list && check_access(u, ci, CA_ACCESS_LIST)) + else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) has_access = true; - else if (check_access(u, ci, CA_ACCESS_CHANGE)) + else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) has_access = true; else if (is_del) { @@ -424,18 +560,6 @@ class CommandCSAccess : public Command this->OnSyntaxError(source, cmd); else if (!has_access) source.Reply(ACCESS_DENIED); - /* We still allow LIST and CLEAR in xOP mode, but not others */ - else if (ci->HasFlag(CI_XOP) && !is_list && !is_clear) - { - if (ModeManager::FindChannelModeByName(CMODE_HALFOP)) - source.Reply(_("You can't use this command. \n" - "Use the AOP, SOP, HOP and VOP commands instead.\n" - "Type \002%s%s HELP \037command\037\002 for more information."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - else - source.Reply(_("You can't use this command. \n" - "Use the AOP, SOP and VOP commands instead.\n" - "Type \002%s%s HELP \037command\037\002 for more information."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - } else if (readonly && !is_list) source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); else if (cmd.equals_ci("ADD")) @@ -520,6 +644,8 @@ class CommandCSAccess : public Command class CommandCSLevels : public Command { + int levelinfo_maxwidth; + void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) { User *u = source.u; @@ -533,7 +659,6 @@ class CommandCSLevels : public Command level = ACCESS_FOUNDER; else { - level = 1; try { level = convertTo(lev); @@ -549,20 +674,22 @@ class CommandCSLevels : public Command source.Reply(_("Level must be between %d and %d inclusive."), ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); else { - for (int i = 0; levelinfo[i].what >= 0; ++i) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) { - if (what.equals_ci(levelinfo[i].name)) + AccessLevels &l = defaultLevels[i]; + + if (what.equals_ci(l.name)) { - ci->levels[levelinfo[i].what] = level; + ci->levels[l.priv] = level; FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, level)); - bool override = !check_access(u, ci, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "SET " << levelinfo[i].name << " to " << level; + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "SET " << l.name << " to " << level; if (level == ACCESS_FOUNDER) - source.Reply(_("Level for %s on channel %s changed to founder only."), levelinfo[i].name.c_str(), ci->name.c_str()); + source.Reply(_("Level for %s on channel %s changed to founder only."), l.name.c_str(), ci->name.c_str()); else - source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), levelinfo[i].name.c_str(), ci->name.c_str(), level); + source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), l.name.c_str(), ci->name.c_str(), level); return; } } @@ -581,17 +708,19 @@ class CommandCSLevels : public Command /* Don't allow disabling of the founder level. It would be hard to change it back if you dont have access to use this command */ if (!what.equals_ci("FOUNDER")) - for (int i = 0; levelinfo[i].what >= 0; ++i) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) { - if (what.equals_ci(levelinfo[i].name)) + AccessLevels &l = defaultLevels[i]; + + if (what.equals_ci(l.name)) { - ci->levels[levelinfo[i].what] = ACCESS_INVALID; - FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, levelinfo[i].what)); + ci->levels[l.priv] = ACCESS_INVALID; + FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, l.priv)); - bool override = !check_access(u, ci, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DISABLE " << levelinfo[i].name; + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DISABLE " << l.name; - source.Reply(_("\002%s\002 disabled on channel %s."), levelinfo[i].name.c_str(), ci->name.c_str()); + source.Reply(_("\002%s\002 disabled on channel %s."), l.name.c_str(), ci->name.c_str()); return; } } @@ -606,27 +735,26 @@ class CommandCSLevels : public Command source.Reply(_("Access level settings for channel %s:"), ci->name.c_str()); if (!levelinfo_maxwidth) - for (int i = 0; levelinfo[i].what >= 0; ++i) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) { - int len = levelinfo[i].name.length(); + AccessLevels &l = defaultLevels[i]; + + int len = l.name.length(); if (len > levelinfo_maxwidth) levelinfo_maxwidth = len; } - for (int i = 0; levelinfo[i].what >= 0; ++i) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) { - int j = ci->levels[levelinfo[i].what]; + AccessLevels &l = defaultLevels[i]; + int j = ci->levels[l.priv]; if (j == ACCESS_INVALID) - { - j = levelinfo[i].what; - - source.Reply(_(" %-*s (disabled)"), levelinfo_maxwidth, levelinfo[i].name.c_str()); - } + source.Reply(_(" %-*s (disabled)"), levelinfo_maxwidth, l.name.c_str()); else if (j == ACCESS_FOUNDER) - source.Reply(_(" %-*s (founder only)"), levelinfo_maxwidth, levelinfo[i].name.c_str()); + source.Reply(_(" %-*s (founder only)"), levelinfo_maxwidth, l.name.c_str()); else - source.Reply(_(" %-*s %d"), levelinfo_maxwidth, levelinfo[i].name.c_str(), j); + source.Reply(_(" %-*s %d"), levelinfo_maxwidth, l.name.c_str(), j); } return; @@ -639,7 +767,7 @@ class CommandCSLevels : public Command reset_levels(ci); FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, -1, 0)); - bool override = !check_access(u, ci, CA_FOUNDER); + bool override = !ci->HasPriv(u, CA_FOUNDER); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "RESET"; source.Reply(_("Access levels for \002%s\002 reset to defaults."), ci->name.c_str()); @@ -647,7 +775,7 @@ class CommandCSLevels : public Command } public: - CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4) + CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4), levelinfo_maxwidth(0) { this->SetDesc(_("Redefine the meanings of access levels")); this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037")); @@ -676,9 +804,7 @@ class CommandCSLevels : public Command */ if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty())) this->OnSyntaxError(source, cmd); - else if (ci->HasFlag(CI_XOP)) - source.Reply(_("Levels are not available as xOP is enabled on this channel.")); - else if (!check_access(u, ci, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) + else if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) source.Reply(ACCESS_DENIED); else if (cmd.equals_ci("SET")) this->DoSet(source, ci, params); @@ -698,19 +824,21 @@ class CommandCSLevels : public Command { if (subcommand.equals_ci("DESC")) { - int i; - source.Reply(_("The following feature/function names are understood. Note\n" - "that the leves for NOJOIN is the maximum level,\n" - "while all others are minimum levels.")); + source.Reply(_("The following feature/function names are understood.")); if (!levelinfo_maxwidth) - for (i = 0; levelinfo[i].what >= 0; ++i) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) { - int len = levelinfo[i].name.length(); + AccessLevels &l = defaultLevels[i]; + + int len = l.name.length(); if (len > levelinfo_maxwidth) levelinfo_maxwidth = len; } - for (i = 0; levelinfo[i].what >= 0; ++i) - source.Reply(_(" %-*s %s"), levelinfo_maxwidth, levelinfo[i].name.c_str(), translate(source.u, levelinfo[i].desc)); + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + source.Reply(_(" %-*s %s"), levelinfo_maxwidth, l.name.c_str(), translate(source.u, l.desc.c_str())); + } } else { @@ -742,17 +870,45 @@ class CommandCSLevels : public Command class CSAccess : public Module { + AccessAccessProvider accessprovider; CommandCSAccess commandcsaccess; CommandCSLevels commandcslevels; public: CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsaccess(this), commandcslevels(this) + accessprovider(this), commandcsaccess(this), commandcslevels(this) { this->SetAuthor("Anope"); + ModuleManager::RegisterService(&accessprovider); ModuleManager::RegisterService(&commandcsaccess); ModuleManager::RegisterService(&commandcslevels); + + Implementation i[] = { I_OnReload, I_OnChanRegistered }; + ModuleManager::Attach(i, this, 2); + + this->OnReload(); + } + + void OnReload() + { + ConfigReader config; + + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); + if (value.equals_ci("founder")) + l.default_level = ACCESS_FOUNDER; + else + l.default_level = config.ReadInteger("chanserv", l.config_name, 0, false); + } + } + + void OnChanRegistered(ChannelInfo *ci) + { + reset_levels(ci); } }; diff --git a/modules/core/cs_akick.cpp b/modules/core/cs_akick.cpp index 2a2ca8fa2..437bf5572 100644 --- a/modules/core/cs_akick.cpp +++ b/modules/core/cs_akick.cpp @@ -134,7 +134,7 @@ class AkickDelCallback : public NumberList ~AkickDelCallback() { User *u = source.u; - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, c, ci) << "DEL on " << Deleted << " users"; if (!Deleted) @@ -195,9 +195,8 @@ class CommandCSAKick : public Command * or whether the mask matches a user with higher/equal access - Viper */ if (ci->HasFlag(CI_PEACE) && nc) { - ChanAccess *nc_access = ci->GetAccess(nc), *u_access = ci->GetAccess(u); - int16 nc_level = nc_access ? nc_access->level : 0, u_level = u_access ? u_access->level : 0; - if (nc == ci->GetFounder() || nc_level >= u_level) + AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); + if (nc == ci->GetFounder() || nc_access >= u_access) { source.Reply(ACCESS_DENIED); return; @@ -211,11 +210,10 @@ class CommandCSAKick : public Command { User *u2 = it->second; - ChanAccess *u2_access = ci->GetAccess(nc), *u_access = ci->GetAccess(u); - int16 u2_level = u2_access ? u2_access->level : 0, u_level = u_access ? u_access->level : 0; + AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); Entry entry_mask(CMODE_BEGIN, mask); - if ((check_access(u2, ci, CA_FOUNDER) || u2_level >= u_level) && entry_mask.Matches(u2)) + if ((ci->HasPriv(u2, CA_FOUNDER) || nc_access >= u_access) && entry_mask.Matches(u2)) { source.Reply(ACCESS_DENIED); return; @@ -228,9 +226,8 @@ class CommandCSAKick : public Command { na = it->second; - ChanAccess *na_access = ci->GetAccess(na->nc), *u_access = ci->GetAccess(u); - int16 na_level = na_access ? na_access->level : 0, u_level = u_access ? u_access->level : 0; - if (na->nc && (na->nc == ci->GetFounder() || na_level >= u_level)) + AccessGroup nc_access = ci->AccessFor(na->nc), u_access = ci->AccessFor(u); + if (na->nc && (na->nc == ci->GetFounder() || nc_access >= u_access)) { Anope::string buf = na->nick + "!" + na->last_usermask; if (Anope::Match(buf, mask)) @@ -263,7 +260,7 @@ class CommandCSAKick : public Command else akick = ci->AddAkick(u->nick, mask, reason); - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << ": " << reason; FOREACH_MOD(I_OnAkickAdd, OnAkickAdd(u, ci, akick)); @@ -312,7 +309,7 @@ class CommandCSAKick : public Command return; } - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << mask; ci->EraseAkick(i); @@ -327,7 +324,7 @@ class CommandCSAKick : public Command const Anope::string &mask = params.size() > 2 ? params[2] : ""; - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "LIST"; if (!ci->GetAkickCount()) @@ -377,7 +374,7 @@ class CommandCSAKick : public Command const Anope::string &mask = params.size() > 2 ? params[2] : ""; - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "VIEW"; if (!ci->GetAkickCount()) @@ -441,7 +438,7 @@ class CommandCSAKick : public Command ++count; } - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ENFORCE, affects " << count << " users"; source.Reply(_("AKICK ENFORCE for \002%s\002 complete; \002%d\002 users were affected."), ci->name.c_str(), count); @@ -450,7 +447,7 @@ class CommandCSAKick : public Command void DoClear(CommandSource &source, ChannelInfo *ci) { User *u = source.u; - bool override = !check_access(u, ci, CA_AKICK); + bool override = !ci->HasPriv(u, CA_AKICK); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; ci->ClearAkick(); @@ -486,7 +483,7 @@ class CommandCSAKick : public Command if (mask.empty() && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL"))) this->OnSyntaxError(source, cmd); - else if (!check_access(u, ci, CA_AKICK) && !u->HasPriv("chanserv/access/modify")) + else if (!ci->HasPriv(u, CA_AKICK) && !u->HasPriv("chanserv/access/modify")) source.Reply(ACCESS_DENIED); else if (!cmd.equals_ci("LIST") && !cmd.equals_ci("VIEW") && !cmd.equals_ci("ENFORCE") && readonly) source.Reply(_("Sorry, channel autokick list modification is temporarily disabled.")); diff --git a/modules/core/cs_ban.cpp b/modules/core/cs_ban.cpp index 599bab969..759649662 100644 --- a/modules/core/cs_ban.cpp +++ b/modules/core/cs_ban.cpp @@ -40,16 +40,15 @@ class CommandCSBan : public Command bool is_same = target.equals_ci(u->nick); User *u2 = is_same ? u : finduser(target); - ChanAccess *u_access = ci->GetAccess(u), *u2_access = ci->GetAccess(u2); - uint16 u_level = u_access ? u_access->level : 0, u2_level = u2_access ? u2_access->level : 0; + AccessGroup u_access = ci->AccessFor(u), u2_access = ci->AccessFor(u2); if (!c) source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); else if (!u2) source.Reply(NICK_X_NOT_IN_USE, target.c_str()); - else if (!is_same ? !check_access(u, ci, CA_BAN) : !check_access(u, ci, CA_BANME)) + else if (!ci->HasPriv(u, CA_BAN)) source.Reply(ACCESS_DENIED); - else if (!is_same && ci->HasFlag(CI_PEACE) && u2_level >= u_level) + else if (!is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) source.Reply(ACCESS_DENIED); /* * Dont ban/kick the user on channels where he is excepted @@ -73,7 +72,7 @@ class CommandCSBan : public Command if (!c->FindUser(u2)) return; - if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(u, ci, CA_SIGNKICK))) + if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !ci->HasPriv(u, CA_SIGNKICK))) c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); else c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); diff --git a/modules/core/cs_clearusers.cpp b/modules/core/cs_clearusers.cpp index 52fb3b990..18dc75479 100644 --- a/modules/core/cs_clearusers.cpp +++ b/modules/core/cs_clearusers.cpp @@ -34,8 +34,11 @@ class CommandCSClearUsers : public Command source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); else if (!c->ci) source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!check_access(u, c->ci, CA_FOUNDER)) + else if (!c->ci->HasPriv(u, CA_FOUNDER)) + { source.Reply(ACCESS_DENIED); + return; + } Anope::string buf = "CLEARUSERS command from " + u->nick + " (" + u->Account()->display + ")"; diff --git a/modules/core/cs_clone.cpp b/modules/core/cs_clone.cpp index 9ce877825..b7f8763f7 100644 --- a/modules/core/cs_clone.cpp +++ b/modules/core/cs_clone.cpp @@ -36,7 +36,7 @@ public: return; } - if (!check_access(u, ci, CA_SET)) + if (!ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; @@ -111,7 +111,7 @@ public: } else if (what.equals_ci("ACCESS")) { - target_ci->ClearAccess(); + /*target_ci->ClearAccess(); for (unsigned i = 0; i < ci->GetAccessCount(); ++i) { ChanAccess *access = ci->GetAccess(i); @@ -119,6 +119,8 @@ public: } source.Reply(_("All access entries from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); + XXX + */ } else if (what.equals_ci("AKICK")) { diff --git a/modules/core/cs_drop.cpp b/modules/core/cs_drop.cpp index d206e35d2..eb01875db 100644 --- a/modules/core/cs_drop.cpp +++ b/modules/core/cs_drop.cpp @@ -49,7 +49,7 @@ class CommandCSDrop : public Command return; } - if ((ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)) && !u->HasCommand("chanserv/chanserv/drop")) + if ((ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) && !u->HasCommand("chanserv/chanserv/drop")) { source.Reply(ACCESS_DENIED); return; @@ -58,7 +58,7 @@ class CommandCSDrop : public Command if (ci->c && ModeManager::FindChannelModeByName(CMOD