diff options
72 files changed, 1773 insertions, 1319 deletions
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<ChanAccess *> +{ + 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<typename T> class service_reference : public dynamic_reference<T> { 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<ChannelInfoFlag, { private: typedef std::multimap<ChannelModeName, ModeLock> ModeList; - private: + NickCore *founder; /* Channel founder */ - std::vector<ChanAccess *> access; /* List of authorized users */ + std::vector<ChanAccess *> access; /* List of authorized users */ std::vector<AutoKick *> akick; /* List of users to kickban */ std::vector<BadWord *> badwords; /* List of badwords */ ModeList mode_locks; @@ -151,17 +136,16 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag, time_t last_topic_time; /* Time */ int16 bantype; - int16 *levels; /* Access levels for commands */ + int16 levels[CA_SIZE]; MemoInfo memos; Channel *c; /* Pointer to channel record (if channel is currently in use) */ /* For BotServ */ - BotInfo *bi; /* Bot used on this channel */ Flags<BotServFlag> 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<ChannelInfoFlag, BotInfo *WhoSends(); /** Add an entry to the channel access list - * - * @param mask The mask of the access entry - * @param level The channel access level the user has on the channel - * @param creator The user who added the access - * @param last_seen When the user was last seen within the channel - * @return The new access class - * - * Creates a new access list entry and inserts it into the access list. + * @param access The entry */ - ChanAccess *AddAccess(const Anope::string &mask, int16 level, const Anope::string &creator, int32 last_seen = 0); + void AddAccess(ChanAccess *access); /** Get an entry from the channel access list by index * @@ -203,36 +180,16 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag, */ ChanAccess *GetAccess(unsigned index); - /** Get an entry from the channel access list by User + /** Check if a user has a privilege on a channel * * @param u The User to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @return A ChanAccess struct corresponding to the User, or NULL if not found - * - * Retrieves an entry from the access list that matches the given User, optionally also matching a certain level. + * @param priv The privilege to check for. + * @return true if the user has the privilege */ - ChanAccess *GetAccess(User *u, int16 level = 0); + bool HasPriv(User *u, ChannelAccess priv); - /** Get an entry from the channel access list by NickCore - * - * @param u The NickCore to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @return A ChanAccess struct corresponding to the NickCore, or NULL if not found - * - * Retrieves an entry from the access list that matches the given NickCore, optionally also matching a certain level. - */ - ChanAccess *GetAccess(NickCore *nc, int16 level = 0); - - /** Get an entry from the channel access list by mask - * - * @param u The mask to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @param wildcard True to match using wildcards - * @return A ChanAccess struct corresponding to the mask, or NULL if not found - * - * Retrieves an entry from the access list that matches the given mask, optionally also matching a certain level. - */ - ChanAccess *GetAccess(const Anope::string &mask, int16 level = 0, bool wildcard = true); + AccessGroup AccessFor(User *u); + AccessGroup AccessFor(NickCore *nc); /** Get the size of the accss vector for this channel * @return The access vector size diff --git a/include/services.h b/include/services.h index 556e1d715..0eeafc0e9 100644 --- a/include/services.h +++ b/include/services.h @@ -345,6 +345,20 @@ template<typename T, size_t Size = 32> 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<int>(data); + } + + static int DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/access") + { + AccessChanAccess *aaccess = debug_cast<AccessChanAccess *>(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<AccessProvider> provider("access/access"); + if (!provider) + return; + AccessChanAccess *access = debug_cast<AccessChanAccess *>(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<Anope::string> ¶ms) { User *u = source.u; @@ -533,7 +659,6 @@ class CommandCSLevels : public Command level = ACCESS_FOUNDER; else { - level = 1; try { level = convertTo<int>(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(CMODE_REGISTERED)) ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false); - bool override = (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)); + bool override = (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "none"); delete ci; 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) diff --git a/modules/core/cs_getkey.cpp b/modules/core/cs_getkey.cpp index 289110f14..4a6d62442 100644 --- a/modules/core/cs_getkey.cpp +++ b/modules/core/cs_getkey.cpp @@ -35,7 +35,7 @@ class CommandCSGetKey : public Command } - if (!check_access(u, ci, CA_GETKEY) && !u->HasCommand("chanserv/chanserv/getkey")) + if (!ci->HasPriv(u, CA_GETKEY) && !u->HasCommand("chanserv/chanserv/getkey")) { source.Reply(ACCESS_DENIED); return; @@ -48,7 +48,7 @@ class CommandCSGetKey : public Command return; } - bool override = !check_access(u, ci, CA_GETKEY); + bool override = !ci->HasPriv(u, CA_GETKEY); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); source.Reply(_("Key for channel \002%s\002 is \002%s\002."), chan.c_str(), key.c_str()); diff --git a/modules/core/cs_info.cpp b/modules/core/cs_info.cpp index 23303224f..ef026563e 100644 --- a/modules/core/cs_info.cpp +++ b/modules/core/cs_info.cpp @@ -50,7 +50,7 @@ class CommandCSInfo : public Command bool show_all = false; /* Should we show all fields? Only for sadmins and identified users */ - if (has_auspex || check_access(u, ci, CA_INFO)) + if (has_auspex || ci->HasPriv(u, CA_INFO)) show_all = true; source.Reply(CHAN_INFO_HEADER, chan.c_str()); @@ -90,7 +90,6 @@ class CommandCSInfo : public Command else CheckOptStr(optbuf, CI_SIGNKICK_LEVEL, _("Signed kicks"), ci, u->Account()); CheckOptStr(optbuf, CI_TOPICLOCK, _("Topic Lock"), ci, u->Account()); - CheckOptStr(optbuf, CI_XOP, _("xOP lists system"), ci, u->Account()); CheckOptStr(optbuf, CI_PERSIST, _("Persistant"), ci, u->Account()); CheckOptStr(optbuf, CI_NO_EXPIRE, _("No expire"), ci, u->Account()); diff --git a/modules/core/cs_invite.cpp b/modules/core/cs_invite.cpp index f3305a01d..27b4b03a3 100644 --- a/modules/core/cs_invite.cpp +++ b/modules/core/cs_invite.cpp @@ -42,7 +42,7 @@ class CommandCSInvite : public Command return; } - if (!check_access(u, ci, CA_INVITE)) + if (!ci->HasPriv(u, CA_INVITE)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_kick.cpp b/modules/core/cs_kick.cpp index bead90487..b98d0953d 100644 --- a/modules/core/cs_kick.cpp +++ b/modules/core/cs_kick.cpp @@ -34,8 +34,7 @@ class CommandCSKick : 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()); @@ -43,9 +42,9 @@ class CommandCSKick : public Command source.Reply(CHAN_X_NOT_REGISTERED, 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_KICK) : !check_access(u, ci, CA_KICKME)) + else if (!ci->HasPriv(u, CA_KICK)) 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); else if (u2->IsProtected()) source.Reply(ACCESS_DENIED); @@ -56,7 +55,7 @@ class CommandCSKick : public Command // XXX Log(LOG_COMMAND, u, this, ci) << "for " << u2->nick; - 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))) ci->c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); else ci->c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); diff --git a/modules/core/cs_main.cpp b/modules/core/cs_main.cpp index 7093ab3ca..5c490e4ff 100644 --- a/modules/core/cs_main.cpp +++ b/modules/core/cs_main.cpp @@ -58,14 +58,15 @@ class ChanServCore : public Module for (unsigned j = 0; j < ci->GetAccessCount(); ++j) { ChanAccess *ca = ci->GetAccess(j); - - if (!ca->nc || (!ca->nc->IsServicesOper() && Config->CSMaxReg && ca->nc->channelcount >= Config->CSMaxReg) || (ca->nc == nc)) + NickCore *anc = findcore(ca->mask); + + if (!anc || (!anc->IsServicesOper() && Config->CSMaxReg && anc->channelcount >= Config->CSMaxReg) || (anc == nc)) continue; - if (!highest || ca->level > highest->level) + if (!highest || *ca > *highest) highest = ca; } if (highest) - newowner = highest->nc; + newowner = findcore(highest->mask); } if (newowner) @@ -86,9 +87,17 @@ class ChanServCore : public Module if (ci->successor == nc) ci->successor = NULL; - ChanAccess *access = ci->GetAccess(nc); - if (access) - ci->EraseAccess(access); + for (unsigned j = 0; j < ci->GetAccessCount(); ++j) + { + ChanAccess *ca = ci->GetAccess(j); + NickCore *anc = findcore(ca->mask); + + if (anc && anc == nc) + { + ci->EraseAccess(j); + break; + } + } for (unsigned j = ci->GetAkickCount(); j > 0; --j) { diff --git a/modules/core/cs_mode.cpp b/modules/core/cs_mode.cpp index 23bd9e577..e67abd85a 100644 --- a/modules/core/cs_mode.cpp +++ b/modules/core/cs_mode.cpp @@ -20,15 +20,15 @@ class CommandCSMode : public Command switch (mode) { case CMODE_OWNER: - return check_access(u, ci, CA_OWNER); + return ci->HasPriv(u, CA_OWNER); case CMODE_PROTECT: - return check_access(u, ci, CA_PROTECT); + return ci->HasPriv(u, CA_PROTECT); case CMODE_OP: - return check_access(u, ci, CA_OPDEOP); + return ci->HasPriv(u, CA_OPDEOP); case CMODE_HALFOP: - return check_access(u, ci, CA_HALFOP); + return ci->HasPriv(u, CA_HALFOP); case CMODE_VOICE: - return check_access(u, ci, CA_VOICE); + return ci->HasPriv(u, CA_VOICE); default: break; } @@ -228,8 +228,7 @@ class CommandCSMode : public Command break; } - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; + AccessGroup u_access = ci->AccessFor(u); if (str_is_wildcard(param)) { @@ -237,10 +236,9 @@ class CommandCSMode : public Command { UserContainer *uc = *it; - ChanAccess *targ_access = ci->GetAccess(uc->user); - int16 t_level = targ_access ? targ_access->level : 0; + AccessGroup targ_access = ci->AccessFor(uc->user); - if (t_level > u_level) + if (targ_access > u_access) { source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str()); continue; @@ -260,9 +258,8 @@ class CommandCSMode : public Command User *target = finduser(param); if (target != NULL) { - ChanAccess *targ_access = ci->GetAccess(target); - int16 t_level = targ_access ? targ_access->level : 0; - if (t_level > u_level) + AccessGroup targ_access = ci->AccessFor(target); + if (targ_access > u_access) { source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str()); break; @@ -314,7 +311,7 @@ class CommandCSMode : public Command if (!ci || !ci->c) source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - else if (!check_access(u, ci, CA_MODE) && !u->HasCommand("chanserv/chanserv/mode")) + else if (!ci->HasPriv(u, CA_MODE) && !u->HasCommand("chanserv/chanserv/mode")) source.Reply(ACCESS_DENIED); else if (subcommand.equals_ci("LOCK")) this->DoLock(source, ci, params); diff --git a/modules/core/cs_modes.cpp b/modules/core/cs_modes.cpp index 46bb72782..cee2f9460 100644 --- a/modules/core/cs_modes.cpp +++ b/modules/core/cs_modes.cpp @@ -15,32 +15,7 @@ class CommandModeBase : public Command { - protected: - /** do_util: not a command, but does the job of others - * @param source The source of the command - * @param com The command calling this function - * @param cm A channel mode class - * @param chan The channel its being set on - * @param nick The nick the modes being set on - * @param set Is the mode being set or removed - * @param level The acecss level required to set this mode on someone else - * @param levelself The access level required to set this mode on yourself - * @param notice Flag required on a channel to send a notice - */ - void do_util(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, int level, int levelself, ChannelInfoFlag notice) - { - User *u = source.u; - - if (chan.empty()) - for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end(); ++it) - do_mode(source, com, cm, (*it)->chan->name, u->nick, set, level, levelself, notice); - else - do_mode(source, com, cm, chan, !nick.empty() ? nick : u->nick, set, level, levelself, notice); - - return; - } - - void do_mode(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, int level, int levelself, ChannelInfoFlag notice) + void do_mode(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) { User *u = source.u; User *u2 = finduser(nick); @@ -49,8 +24,7 @@ class CommandModeBase : public Command bool is_same = u == u2; - ChanAccess *u_access = ci ? ci->GetAccess(u) : NULL, *u2_access = ci && u2 ? ci->GetAccess(u2) : NULL; - uint16 u_level = u_access ? u_access->level : 0, u2_level = u2_access ? u2_access->level : 0; + AccessGroup u_access = ci ? ci->AccessFor(u) : AccessGroup(), u2_access = ci && u2 ? ci->AccessFor(u2) : AccessGroup(); if (!c) source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); @@ -58,9 +32,9 @@ class CommandModeBase : public Command source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); else if (!u2) source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (is_same ? !check_access(u, ci, levelself) : !check_access(u, ci, level)) + else if (is_same ? !ci->HasPriv(u, levelself) : !ci->HasPriv(u, level)) source.Reply(ACCESS_DENIED); - else if (!set && !is_same && ci->HasFlag(CI_PEACE) && u2_level >= u_level) + else if (!set && !is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) source.Reply(ACCESS_DENIED); else if (!set && u2->IsProtected() && !is_same) source.Reply(ACCESS_DENIED); @@ -79,6 +53,30 @@ class CommandModeBase : public Command } } + protected: + /** do_util: not a command, but does the job of others + * @param source The source of the command + * @param com The command calling this function + * @param cm A channel mode class + * @param chan The channel its being set on + * @param nick The nick the modes being set on + * @param set Is the mode being set or removed + * @param level The acecss level required to set this mode on someone else + * @param levelself The access level required to set this mode on yourself + * @param notice Flag required on a channel to send a notice + */ + void do_util(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) + { + User *u = source.u; + + if (chan.empty()) + for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end(); ++it) + do_mode(source, com, cm, (*it)->chan->name, u->nick, set, level, levelself, notice); + else + do_mode(source, com, cm, chan, !nick.empty() ? nick : u->nick, set, level, levelself, notice); + + return; + } public: CommandModeBase(Module *creator, const Anope::string &cname) : Command(creator, cname, 0, 2) diff --git a/modules/core/cs_saset_noexpire.cpp b/modules/core/cs_saset_noexpire.cpp index fa944f86a..4a1caa449 100644 --- a/modules/core/cs_saset_noexpire.cpp +++ b/modules/core/cs_saset_noexpire.cpp @@ -32,7 +32,7 @@ class CommandCSSASetNoexpire : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_bantype.cpp b/modules/core/cs_set_bantype.cpp index e126ae275..14b13a77b 100644 --- a/modules/core/cs_set_bantype.cpp +++ b/modules/core/cs_set_bantype.cpp @@ -32,7 +32,7 @@ class CommandCSSetBanType : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_description.cpp b/modules/core/cs_set_description.cpp index b6e65ca5a..3282f8144 100644 --- a/modules/core/cs_set_description.cpp +++ b/modules/core/cs_set_description.cpp @@ -32,7 +32,7 @@ class CommandCSSetDescription : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_founder.cpp b/modules/core/cs_set_founder.cpp index aeb29cada..e71fd4d3e 100644 --- a/modules/core/cs_set_founder.cpp +++ b/modules/core/cs_set_founder.cpp @@ -32,13 +32,13 @@ class CommandCSSetFounder : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; } - if (this->permission.empty() && (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER))) + if (this->permission.empty() && (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER))) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_keeptopic.cpp b/modules/core/cs_set_keeptopic.cpp index 4ff3b6f92..0322987e4 100644 --- a/modules/core/cs_set_keeptopic.cpp +++ b/modules/core/cs_set_keeptopic.cpp @@ -32,7 +32,7 @@ class CommandCSSetKeepTopic : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_opnotice.cpp b/modules/core/cs_set_opnotice.cpp index e73bd21ce..cfcaf358c 100644 --- a/modules/core/cs_set_opnotice.cpp +++ b/modules/core/cs_set_opnotice.cpp @@ -32,7 +32,7 @@ class CommandCSSetOpNotice : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_peace.cpp b/modules/core/cs_set_peace.cpp index 76e95e55d..cb208e540 100644 --- a/modules/core/cs_set_peace.cpp +++ b/modules/core/cs_set_peace.cpp @@ -32,7 +32,7 @@ class CommandCSSetPeace : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_persist.cpp b/modules/core/cs_set_persist.cpp index 5625c6c76..2402a3370 100644 --- a/modules/core/cs_set_persist.cpp +++ b/modules/core/cs_set_persist.cpp @@ -32,7 +32,7 @@ class CommandCSSetPersist : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_private.cpp b/modules/core/cs_set_private.cpp index 358a98718..85880d5da 100644 --- a/modules/core/cs_set_private.cpp +++ b/modules/core/cs_set_private.cpp @@ -32,7 +32,7 @@ class CommandCSSetPrivate : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_restricted.cpp b/modules/core/cs_set_restricted.cpp index 4178af110..9796c97da 100644 --- a/modules/core/cs_set_restricted.cpp +++ b/modules/core/cs_set_restricted.cpp @@ -31,7 +31,7 @@ class CommandCSSetRestricted : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; @@ -40,15 +40,11 @@ class CommandCSSetRestricted : public Command if (params[1].equals_ci("ON")) { ci->SetFlag(CI_RESTRICTED); - if (ci->levels[CA_NOJOIN] < 0) - ci->levels[CA_NOJOIN] = 0; source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { ci->UnsetFlag(CI_RESTRICTED); - if (ci->levels[CA_NOJOIN] >= 0) - ci->levels[CA_NOJOIN] = -2; source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str()); } else diff --git a/modules/core/cs_set_secure.cpp b/modules/core/cs_set_secure.cpp index 99085611e..2042ed7b9 100644 --- a/modules/core/cs_set_secure.cpp +++ b/modules/core/cs_set_secure.cpp @@ -32,7 +32,7 @@ class CommandCSSetSecure : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_securefounder.cpp b/modules/core/cs_set_securefounder.cpp index 3a35ccca0..6e11bbce4 100644 --- a/modules/core/cs_set_securefounder.cpp +++ b/modules/core/cs_set_securefounder.cpp @@ -33,7 +33,7 @@ class CommandCSSetSecureFounder : public Command } - if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)) + if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_secureops.cpp b/modules/core/cs_set_secureops.cpp index f816dc0f8..ed15f7cb5 100644 --- a/modules/core/cs_set_secureops.cpp +++ b/modules/core/cs_set_secureops.cpp @@ -32,7 +32,7 @@ class CommandCSSetSecureOps : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_signkick.cpp b/modules/core/cs_set_signkick.cpp index 59961b5b4..38894b62b 100644 --- a/modules/core/cs_set_signkick.cpp +++ b/modules/core/cs_set_signkick.cpp @@ -32,7 +32,7 @@ class CommandCSSetSignKick : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_successor.cpp b/modules/core/cs_set_successor.cpp index 9807b44d3..a1c756b5d 100644 --- a/modules/core/cs_set_successor.cpp +++ b/modules/core/cs_set_successor.cpp @@ -32,13 +32,13 @@ class CommandCSSetSuccessor : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; } - if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)) + if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_topiclock.cpp b/modules/core/cs_set_topiclock.cpp index e03999df0..3dea981f6 100644 --- a/modules/core/cs_set_topiclock.cpp +++ b/modules/core/cs_set_topiclock.cpp @@ -32,7 +32,7 @@ class CommandCSSetTopicLock : public Command return; } - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) + if (!this->permission.empty() && !ci->HasPriv(u, CA_SET)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_set_xop.cpp b/modules/core/cs_set_xop.cpp deleted file mode 100644 index 00b945fe2..000000000 --- a/modules/core/cs_set_xop.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* 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" - -#define CHECKLEV(lev) (ci->levels[(lev)] != ACCESS_INVALID && access->level >= ci->levels[(lev)]) - -class CommandCSSetXOP : public Command -{ - public: - CommandCSSetXOP(Module *creator, const Anope::string &cname = "chanserv/set/xop", const Anope::string &cpermission = "") : Command(creator, cname, 2, 2, cpermission) - { - this->SetDesc(_("Toggle the user privilege system")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) - { - User *u = source.u; - - if (!ModuleManager::FindModule("cs_xop")) - { - source.Reply(_("xOP system is not available.")); - return; - } - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!this->permission.empty() && !check_access(u, ci, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - if (!ci->HasFlag(CI_XOP)) - { - ChanAccess *access; - - for (unsigned i = ci->GetAccessCount(); i > 0; --i) - { - access = ci->GetAccess(i - 1); - - /* This will probably cause wrong levels to be set, but hey, - * it's better than losing it altogether. - */ - if (access->level == ACCESS_QOP) - access->level = ACCESS_QOP; - else if (CHECKLEV(CA_AKICK) || CHECKLEV(CA_SET)) - access->level = ACCESS_SOP; - else if (CHECKLEV(CA_AUTOOP) || CHECKLEV(CA_OPDEOP) || CHECKLEV(CA_OPDEOPME)) - access->level = ACCESS_AOP; - else if (ModeManager::FindChannelModeByName(CMODE_HALFOP) && (CHECKLEV(CA_AUTOHALFOP) || CHECKLEV(CA_HALFOP) || CHECKLEV(CA_HALFOPME))) - access->level = ACCESS_HOP; - else if (CHECKLEV(CA_AUTOVOICE) || CHECKLEV(CA_VOICE) || CHECKLEV(CA_VOICEME)) - access->level = ACCESS_VOP; - else - ci->EraseAccess(i - 1); - } - - reset_levels(ci); - ci->SetFlag(CI_XOP); - } - - Log(LOG_COMMAND, u, this, ci) << "to enable XOP"; - source.Reply(_("xOP lists system for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_XOP); - - Log(LOG_COMMAND, u, this, ci) << "to disable XOP"; - source.Reply(_("xOP lists system for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "XOP"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the xOP lists system for a channel.\n" - "When \002XOP\002 is set, you have to use the \002AOP\002/\002SOP\002/\002VOP\002\n" - "commands in order to give channel privileges to\n" - "users, else you have to use the \002ACCESS\002 command.\n" - " \n" - "\002Technical Note\002: when you switch from access list to xOP \n" - "lists system, your level definitions and user levels will be\n" - "changed, so you won't find the same values if you\n" - "switch back to access system! \n" - " \n" - "You should also check that your users are in the good xOP \n" - "list after the switch from access to xOP lists, because the \n" - "guess is not always perfect... in fact, it is not recommended \n" - "to use the xOP lists if you changed level definitions with \n" - "the \002LEVELS\002 command.\n" - " \n" - "Switching from xOP lists system to access list system\n" - "causes no problem though.")); - return true; - } -}; - -class CommandCSSASetXOP : public CommandCSSetXOP -{ - public: - CommandCSSASetXOP(Module *creator) : CommandCSSetXOP(creator, "chanserv/saset/xop", "chanserv/saset/xop") - { - } -}; - -class CSSetXOP : public Module -{ - CommandCSSetXOP commandcssetxop; - CommandCSSASetXOP commandcssasetxop; - - public: - CSSetXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetxop(this), commandcssasetxop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetxop); - ModuleManager::RegisterService(&commandcssasetxop); - } -}; - -MODULE_INIT(CSSetXOP) diff --git a/modules/core/cs_status.cpp b/modules/core/cs_status.cpp deleted file mode 100644 index 586a3b2ce..000000000 --- a/modules/core/cs_status.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* 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" - -class CommandCSStatus : public Command -{ - public: - CommandCSStatus(Module *creator) : Command(creator, "chanserv/status", 2, 2, "chanserv/status") - { - this->SetDesc(_("Returns the current access level of a user on a channel")); - this->SetSyntax(_("\037channel\037 \037item\037")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) - { - const Anope::string &nick = params[1]; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - User *u2 = finduser(nick); - ChanAccess *u2_access = ci->GetAccess(u2); - if (u2) - source.Reply(_("STATUS %s %s %d"), ci->name.c_str(), u2->nick.c_str(), u2_access ? u2_access->level : 0); - else /* !u2 */ - source.Reply(_("STATUS ERROR Nick %s not online"), nick.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Returns the current access level of the given nick on the\n" - "given channel. The reply is of the form:\n" - " \n" - " STATUS \037channel\037 \037nickname\037 \037access-level\037\n" - " \n" - "If an error occurs, the reply will be in the form:\n" - " \n" - " STATUS ERROR \037error-message\037")); - return true; - } -}; - -class CSStatus : public Module -{ - CommandCSStatus commandcsstatus; - - public: - CSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsstatus(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsstatus); - } -}; - -MODULE_INIT(CSStatus) diff --git a/modules/core/cs_topic.cpp b/modules/core/cs_topic.cpp index 2cb821662..1b5de11c3 100644 --- a/modules/core/cs_topic.cpp +++ b/modules/core/cs_topic.cpp @@ -37,7 +37,7 @@ class CommandCSTopic : public Command if (!ci->c) source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - else if (!check_access(u, ci, CA_TOPIC) && !u->HasCommand("chanserv/chanserv/topic")) + else if (!ci->HasPriv(u, CA_TOPIC) && !u->HasCommand("chanserv/chanserv/topic")) source.Reply(ACCESS_DENIED); else { @@ -47,7 +47,7 @@ class CommandCSTopic : public Command if (has_topiclock) ci->SetFlag(CI_TOPICLOCK); - bool override = !check_access(u, ci, CA_TOPIC); + bool override = !ci->HasPriv(u, CA_TOPIC); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "to change the topic to " << (!topic.empty() ? topic : "No topic"); } return; diff --git a/modules/core/cs_unban.cpp b/modules/core/cs_unban.cpp index dbd0d9ae3..cc3c68e91 100644 --- a/modules/core/cs_unban.cpp +++ b/modules/core/cs_unban.cpp @@ -39,7 +39,7 @@ class CommandCSUnban : public Command return; } - if (!check_access(u, ci, CA_UNBAN)) + if (!ci->HasPriv(u, CA_UNBAN)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/cs_xop.cpp b/modules/core/cs_xop.cpp index 1b9fae93e..1b9348026 100644 --- a/modules/core/cs_xop.cpp +++ b/modules/core/cs_xop.cpp @@ -13,25 +13,205 @@ #include "module.h" -enum +enum XOPType { - XOP_AOP, + XOP_QOP, XOP_SOP, - XOP_VOP, + XOP_AOP, XOP_HOP, - XOP_QOP, - XOP_TYPES + XOP_VOP, + XOP_UNKNOWN +}; + +static struct XOPAccess +{ + XOPType type; + Anope::string name; + ChannelAccess access[CA_SIZE]; +} xopAccess[] = { + { XOP_QOP, "QOP", + { + CA_SIGNKICK, + CA_SET, + CA_AUTOOWNER, + CA_OWNERME, + CA_PROTECT, + CA_INFO, + CA_SIZE + } + }, + { XOP_SOP, "SOP", + { + CA_AUTOPROTECT, + CA_AKICK, + CA_BADWORDS, + CA_ASSIGN, + CA_MEMO, + CA_ACCESS_CHANGE, + CA_PROTECTME, + CA_OPDEOP, + CA_SIZE + } + }, + { XOP_AOP, "AOP", + { + CA_TOPIC, + CA_MODE, + CA_GETKEY, + CA_INVITE, + CA_UNBAN, + CA_AUTOOP, + CA_OPDEOPME, + CA_HALFOP, + CA_SAY, + CA_NOKICK, + CA_SIZE + } + }, + { XOP_HOP, "HOP", + { + CA_AUTOHALFOP, + CA_HALFOPME, + CA_KICK, + CA_BAN, + CA_FANTASIA, + CA_SIZE + } + }, + { XOP_VOP, "VOP", + { + CA_AUTOVOICE, + CA_VOICEME, + CA_ACCESS_LIST, + CA_SIZE + } + }, + { XOP_UNKNOWN, "", { } + } +}; + +class XOPChanAccess : public ChanAccess +{ + public: + XOPType type; + + XOPChanAccess(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; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (this->type > x.type) + continue; + + for (int j = 0; x.access[j] != CA_SIZE; ++j) + if (x.access[j] == priv) + return true; + } + + return false; + } + + Anope::string Serialize() + { + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (this->type == x.type) + return x.name; + } + + return ""; + } + + void Unserialize(const Anope::string &data) + { + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (data == x.name) + { + this->type = x.type; + return; + } + } + + this->type = XOP_UNKNOWN; + } + + static XOPType DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/xop") + { + XOPChanAccess *xaccess = debug_cast<XOPChanAccess *>(access); + return xaccess->type; + } + else + { + int count[XOP_UNKNOWN]; + for (int i = 0; i < XOP_UNKNOWN; ++i) + count[i] = 0; + + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + for (int j = 0; x.access[j] != CA_SIZE; ++j) + if (access->HasPriv(x.access[j])) + ++count[x.type]; + } + + XOPType max = XOP_UNKNOWN; + int maxn = 0; + for (int i = 0; i < XOP_UNKNOWN; ++i) + if (count[i] > maxn) + { + max = static_cast<XOPType>(i); + maxn = count[i]; + } + + return max; + } + } +}; + +class XOPAccessProvider : public AccessProvider +{ + public: + XOPAccessProvider(Module *o) : AccessProvider(o, "access/xop") + { + } + + ChanAccess *Create() + { + return new XOPChanAccess(this); + } }; class XOPListCallback : public NumberList { CommandSource &source; ChannelInfo *ci; - int level; + XOPType type; Command *c; bool SentHeader; public: - XOPListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist, int _level, Command *_c) : NumberList(numlist, false), source(_source), ci(_ci), level(_level), c(_c), SentHeader(false) + XOPListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist, XOPType _type, Command *_c) : NumberList(numlist, false), source(_source), ci(_ci), type(_type), c(_c), SentHeader(false) { } @@ -42,21 +222,21 @@ class XOPListCallback : public NumberList ChanAccess *access = ci->GetAccess(Number - 1); - if (level != access->level) + if (this->type != XOPChanAccess::DetermineLevel(access)) return; if (!SentHeader) { SentHeader = true; - source.Reply(_("%s list for %s:\n Num Nick"), this->c->name.c_str(), ci->name.c_str()); + source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); } - DoList(source, access, Number - 1, level); + DoList(source, access, Number - 1, this->type); } - static void DoList(CommandSource &source, ChanAccess *access, unsigned index, int level) + static void DoList(CommandSource &source, ChanAccess *access, unsigned index, XOPType type) { - source.Reply(_(" %3d %s"), index, access->GetMask().c_str()); + source.Reply(_(" %3d %s"), index, access->mask.c_str()); } }; @@ -68,23 +248,24 @@ class XOPDelCallback : public NumberList unsigned Deleted; Anope::string Nicks; bool override; + XOPType type; public: - XOPDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, bool _override, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), override(_override) + XOPDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, bool _override, XOPType _type, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), override(_override), type(_type) { } ~XOPDelCallback() { if (!Deleted) - source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), this->c->name.c_str()); + source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str()); else { Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "deleted access of users " << Nicks; if (Deleted == 1) - source.Reply(_("Deleted one entry from %s %s list."), ci->name.c_str(), this->c->name.c_str()); + source.Reply(_("Deleted one entry from %s %s list."), ci->name.c_str(), source.command.c_str()); else - source.Reply(_("Deleted %d entries from %s %s list."), Deleted, ci->name.c_str(), this->c->name.c_str()); + source.Reply(_("Deleted %d entries from %s %s list."), Deleted, ci->name.c_str(), source.command.c_str()); } } @@ -95,11 +276,14 @@ class XOPDelCallback : public NumberList ChanAccess *access = ci->GetAccess(Number - 1); + if (this->type != XOPChanAccess::DetermineLevel(access)) + return; + ++Deleted; if (!Nicks.empty()) - Nicks += ", " + access->GetMask(); + Nicks += ", " + access->mask; else - Nicks = access->GetMask(); + Nicks = access->mask; FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source.u, access)); @@ -110,12 +294,11 @@ class XOPDelCallback : public NumberList class XOPBase : public Command { private: - void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, int level) + void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, XOPType level) { User *u = source.u; Anope::string mask = params.size() > 2 ? params[2] : ""; - int change = 0; if (mask.empty()) { @@ -125,66 +308,66 @@ class XOPBase : public Command if (readonly) { - source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), this->name.c_str()); + source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str()); return; } - ChanAccess *access = ci->GetAccess(u); - uint16 ulev = access ? access->level : 0; + AccessGroup access = ci->AccessFor(u); + ChanAccess *highest = access.Highest(); + int u_level = (highest ? XOPChanAccess::DetermineLevel(highest) : 0); - if ((level >= ulev || ulev < ACCESS_AOP) && !u->HasPriv("chanserv/access/modify")) + if (((access.HasPriv(CA_FOUNDER) || level >= u_level) || !access.HasPriv(CA_ACCESS_CHANGE)) && !u->HasPriv("chanserv/access/modify")) { source.Reply(ACCESS_DENIED); return; } - access = ci->GetAccess(mask, 0, false); - if (access) + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) { - /** - * Patch provided by PopCorn to prevert AOP's reducing SOP's levels - **/ - if (access->level >= ulev && !u->HasPriv("chanserv/access/modify")) + ChanAccess *a = ci->GetAccess(i); + + if (a->mask.equals_ci(mask)) { - source.Reply(ACCESS_DENIED); - return; + if (XOPChanAccess::DetermineLevel(a) >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + + ci->EraseAccess(i); + break; } - ++change; } - if (!change && ci->GetAccessCount() >= Config->CSAccessMax) + if (ci->GetAccessCount() >= Config->CSAccessMax) { - source.Reply(_("Sorry, you can only have %d %s entries on a channel."), Config->CSAccessMax, this->name.c_str()); + source.Reply(_("Sorry, you can only have %d %s entries on a channel."), Config->CSAccessMax, source.command.c_str()); return; } - if (!change) - access = ci->AddAccess(mask, level, u->nick); - else - { - access->level = level; - access->last_seen = 0; - access->creator = u->nick; - } + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; - bool override = (level >= ulev || ulev < ACCESS_AOP || (access && access->level > ulev)); + service_reference<AccessProvider> provider("access/xop"); + if (!provider) + return; + XOPChanAccess *acc = debug_cast<XOPChanAccess *>(provider->Create()); + acc->ci = ci; + acc->mask = mask; + acc->creator = u->nick; + acc->type = level; + acc->last_seen = 0; + acc->created = Anope::CurTime; + ci->AddAccess(acc); + + bool override = level >= u_level || !access.HasPriv(CA_ACCESS_CHANGE); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " as level " << level; - if (!change) - { - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); - source.Reply(("\002%s\002 added to %s %s list."), access->GetMask().c_str(), ci->name.c_str(), this->name.c_str()); - } - else - { - FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, access)); - source.Reply(_("\002%s\002 moved to %s %s list."), access->GetMask().c_str(), ci->name.c_str(), this->name.c_str()); - } - - return; + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, acc)); + source.Reply(("\002%s\002 added to %s %s list."), acc->mask.c_str(), ci->name.c_str(), source.command.c_str()); } - void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, int level) + void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, XOPType level) { User *u = source.u; @@ -198,80 +381,84 @@ class XOPBase : public Command if (readonly) { - source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), this->name.c_str()); + source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str()); return; } if (!ci->GetAccessCount()) { - source.Reply(_("%s %s list is empty."), ci->name.c_str(), this->name.c_str()); + source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str()); return; } - ChanAccess *access = ci->GetAccess(u); - uint16 ulev = access ? access->level : 0; - - if ((!access || access->nc != u->Account()) && (level >= ulev || ulev < ACCESS_AOP) && !u->HasPriv("chanserv/access/modify")) + AccessGroup access = ci->AccessFor(u); + ChanAccess *highest = access.Highest(); + bool override = false; + if (!mask.equals_ci(u->Account()->display) && !access.HasPriv(CA_ACCESS_CHANGE) && (!highest || level >= XOPChanAccess::DetermineLevel(highest))) { - source.Reply(ACCESS_DENIED); - return; + if (u->HasPriv("chanserv/access/modify")) + override = true; + else + { + source.Reply(ACCESS_DENIED); + return; + } } - access = ci->GetAccess(mask, 0, false); - /* Special case: is it a number/list? Only do search if it isn't. */ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) { - bool override = level >= ulev || ulev < ACCESS_AOP; - XOPDelCallback list(source, ci, this, override, mask); + XOPDelCallback list(source, ci, this, override, level, mask); list.Process(); } - else if (!access || access->level != level) - { - source.Reply(_("\002%s\002 not found on %s %s list."), mask.c_str(), ci->name.c_str(), this->name.c_str()); - return; - } else { - if (access->nc != u->Account() && ulev <= access->level && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) { - bool override = ulev <= access->level; - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << access->GetMask(); + ChanAccess *a = ci->GetAccess(i); + + if (a->mask.equals_ci(mask) && XOPChanAccess::DetermineLevel(a) == level) + { + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << a->mask; - source.Reply(_("\002%s\002 deleted from %s %s list."), access->GetMask().c_str(), ci->name.c_str(), this->name.c_str()); + source.Reply(_("\002%s\002 deleted from %s %s list."), a->mask.c_str(), ci->name.c_str(), source.command.c_str()); - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, a)); + ci->EraseAccess(a); - ci->EraseAccess(access); + return; + } } + + source.Reply(_("\002%s\002 not found on %s %s list."), mask.c_str(), ci->name.c_str(), source.command.c_str()); } - - return; } - void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, int level) + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, XOPType level) { User *u = source.u; const Anope::string &nick = params.size() > 2 ? params[2] : ""; - ChanAccess *access = ci->GetAccess(u); - uint16 ulev = access ? access->level : 0; + AccessGroup access = ci->AccessFor(u); + bool override = false; - if (!ulev && !u->HasCommand("chanserv/chanserv/access/list")) + if (!access.HasPriv(CA_ACCESS_LIST)) { - source.Reply(ACCESS_DENIED); - return; + if (u->HasCommand("chanserv/access/list")) + override = true; + else + { + source.Reply(ACCESS_DENIED); + return; + } } - bool override = !ulev; Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); if (!ci->GetAccessCount()) { - source.Reply(_("%s %s list is empty."), ci->name.c_str(), this->name.c_str()); + source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str()); return; } @@ -286,70 +473,70 @@ class XOPBase : public Command for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) { - access = ci->GetAccess(i); + ChanAccess *a = ci->GetAccess(i); - if (access->level != level) + if (XOPChanAccess::DetermineLevel(a) != level) continue; - else if (!nick.empty() && !Anope::Match(access->GetMask(), nick)) + else if (!nick.empty() && !Anope::Match(a->mask, nick)) continue; if (!SentHeader) { SentHeader = true; - source.Reply(_("%s list for %s:\n Num Nick"), this->name.c_str(), ci->name.c_str()); + source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); } - XOPListCallback::DoList(source, access, i + 1, level); + XOPListCallback::DoList(source, a, i + 1, level); } if (!SentHeader) - source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), this->name.c_str()); + source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str()); } return; } - void DoClear(CommandSource &source, ChannelInfo *ci, int level) + void DoClear(CommandSource &source, ChannelInfo *ci, XOPType level) { User *u = source.u; if (readonly) { - source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), this->name.c_str()); + source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str()); return; } if (!ci->GetAccessCount()) { - source.Reply(_("%s %s list is empty."), ci->name.c_str(), this->name.c_str()); + source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str()); return; } - if (!check_access(u, ci, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) + if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) { source.Reply(ACCESS_DENIED); return; } - bool override = !check_access(u, ci, CA_FOUNDER); + bool override = !ci->HasPriv(u, CA_FOUNDER); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR level " << level; for (unsigned i = ci->GetAccessCount(); i > 0; --i) { ChanAccess *access = ci->GetAccess(i - 1); - if (access->level == level) + if (XOPChanAccess::DetermineLevel(access) == level) ci->EraseAccess(i - 1); } FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); - source.Reply(_("Channel %s %s list has been cleared."), ci->name.c_str(), this->name.c_str()); + source.Reply(_("Channel %s %s list has been cleared."), ci->name.c_str(), source.command.c_str()); return; } protected: - void DoXop(CommandSource &source, const std::vector<Anope::string> ¶ms, int level) + void DoXop(CommandSource &source, const std::vector<Anope::string> ¶ms, XOPType level) { ChannelInfo *ci = cs_findchan(params[0]); if (ci == NULL) @@ -360,9 +547,7 @@ class XOPBase : public Command const Anope::string &cmd = params[1]; - if (!ci->HasFlag(CI_XOP)) - source.Reply(_("You can't use this command. Use the ACCESS command instead.")); - else if (cmd.equals_ci("ADD")) + if (cmd.equals_ci("ADD")) return this->DoAdd(source, ci, params, level); else if (cmd.equals_ci("DEL")) return this->DoDel(source, ci, params, level); @@ -403,7 +588,7 @@ class CommandCSQOP : public XOPBase void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - return this->DoXop(source, params, ACCESS_QOP); + return this->DoXop(source, params, XOP_QOP); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) @@ -457,7 +642,7 @@ class CommandCSAOP : public XOPBase void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - return this->DoXop(source, params, ACCESS_AOP); + return this->DoXop(source, params, XOP_AOP); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) @@ -513,7 +698,7 @@ class CommandCSHOP : public XOPBase void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - return this->DoXop(source, params, ACCESS_HOP); + return this->DoXop(source, params, XOP_HOP); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) @@ -567,7 +752,7 @@ class CommandCSSOP : public XOPBase void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - return this->DoXop(source, params, ACCESS_SOP); + return this->DoXop(source, params, XOP_SOP); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) @@ -623,7 +808,7 @@ class CommandCSVOP : public XOPBase void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - return this->DoXop(source, params, ACCESS_VOP); + return this->DoXop(source, params, XOP_VOP); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) @@ -670,6 +855,7 @@ class CommandCSVOP : public XOPBase class CSXOP : public Module { + XOPAccessProvider accessprovider; CommandCSQOP commandcsqop; CommandCSSOP commandcssop; CommandCSAOP commandcsaop; @@ -678,10 +864,11 @@ class CSXOP : public Module public: CSXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsqop(this), commandcssop(this), commandcsaop(this), commandcshop(this), commandcsvop(this) + accessprovider(this), commandcsqop(this), commandcssop(this), commandcsaop(this), commandcshop(this), commandcsvop(this) { this->SetAuthor("Anope"); + ModuleManager::RegisterService(&accessprovider); ModuleManager::RegisterService(&commandcssop); ModuleManager::RegisterService(&commandcsaop); ModuleManager::RegisterService(&commandcsqop); diff --git a/modules/core/db_mysql.cpp b/modules/core/db_mysql.cpp index d19485e09..3f18230fe 100644 --- a/modules/core/db_mysql.cpp +++ b/modules/core/db_mysql.cpp @@ -151,7 +151,7 @@ class DBMySQL : public Module I_OnNickRegister, I_OnChangeCoreDisplay, I_OnNickSuspended, I_OnDelNick, /* ChanServ */ - I_OnAccessAdd, I_OnAccessDel, I_OnAccessChange, I_OnAccessClear, I_OnLevelChange, + I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, I_OnChanForbidden, I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend, I_OnAkickAdd, I_OnAkickDel, I_OnMLock, I_OnUnMLock, /* BotServ */ @@ -166,7 +166,7 @@ class DBMySQL : public Module /* HostServ */ I_OnSetVhost, I_OnDeleteVhost }; - ModuleManager::Attach(i, this, 41); + ModuleManager::Attach(i, this, 40); } EventReturn OnLoadDatabase() @@ -425,7 +425,22 @@ class DBMySQL : public Module continue; } - ci->AddAccess(r.Get(i, "display"), atoi(r.Get(i, "level").c_str()), r.Get(i, "creator"), (r.Get(i, "last_seen").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "last_seen")) : Anope::CurTime)); + const Anope::string &provider = r.Get(i, "provider"), &data = r.Get(i, "data"); + service_reference<AccessProvider> ap(provider); + if (!ap) + { + Log() << "MySQL: Access entry for " << ci->name << " using nonexistant provider " << provider; + continue; + } + + ChanAccess *access = ap->Create(); + access->ci = ci; + access->mask = r.Get(i, "mask"); + access->creator = r.Get(i, "creator"); + access->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "last_seen")) : Anope::CurTime; + access->created = r.Get(i, "created").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "created")) : Anope::CurTime; + access->Unserialize(data); + ci->AddAccess(access); } query = "SELECT * FROM `anope_cs_akick`"; @@ -773,7 +788,7 @@ class DBMySQL : public Module ChannelInfo *ci = cs_findchan(params[0]); if (!ci) return; - if (!check_access(u, ci, CA_SET) && !u->HasPriv("botserv/administration")) + if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) return; if (params[1].equals_ci("BADWORDS") || params[1].equals_ci("BOLDS") || params[1].equals_ci("CAPS") || params[1].equals_ci("COLORS") || params[1].equals_ci("FLOOD") || params[1].equals_ci("REPEAT") || params[1].equals_ci("REVERSES") || params[1].equals_ci("UNDERLINES")) { @@ -824,7 +839,7 @@ class DBMySQL : public Module else if (command->name == "botserv/set" && params.size() > 1) { ChannelInfo *ci = cs_findchan(params[0]); - if (ci && !check_access(u, ci, CA_SET) && !u->HasPriv("botserv/administration")) + if (ci && !ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) return; BotInfo *bi = NULL; if (!ci) @@ -861,7 +876,7 @@ class DBMySQL : public Module else { ci = cs_findchan(target); - if (!ci || !check_access(u, ci, CA_MEMO)) + if (!ci || !ci->HasPriv(u, CA_MEMO)) return; } @@ -984,9 +999,10 @@ class DBMySQL : public Module void OnAccessAdd(ChannelInfo *ci, User *, ChanAccess *access) { - SQLQuery query("INSERT INTO `anope_cs_access` (level, display, channel, last_seen, creator) VALUES (@level, @display, @channel, @last_seen, @creator)"); - query.setValue("level", access->level); - query.setValue("display", access->GetMask()); + SQLQuery query("INSERT INTO `anope_cs_access` (provider, data, mask, channel, last_seen, creator) VALUES (@provider, @data, @mask, @channel, @last_seen, @creator) ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)"); + query.setValue("provider", access->provider->name); + query.setValue("data", access->Serialize()); + query.setValue("mask", access->mask); query.setValue("channel", ci->name); query.setValue("last_seen", access->last_seen); query.setValue("creator", access->creator); @@ -995,23 +1011,12 @@ class DBMySQL : public Module void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) { - SQLQuery query("DELETE FROM `anope_cs_access` WHERE `display` = @display AND `channel` = @channel"); - query.setValue("display", access->GetMask()); + SQLQuery query("DELETE FROM `anope_cs_access` WHERE `mask` = @mask AND `channel` = @channel"); + query.setValue("mask", access->mask); query.setValue("channel", ci->name); this->RunQuery(query); } - void OnAccessChange(ChannelInfo *ci, User *, ChanAccess *access) - { - SQLQuery query("INSERT INTO `anope_cs_access` (level, display, channel, last_seen, creator) VALUES (@level, @display, @channel, @last_seen, @creator) ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)"); - query.setValue("level", access->level); - query.setValue("display", access->GetMask()); - query.setValue("channel", ci->name); - query.setValue("last_seen", access->last_seen); - query.setValue("creator", access->creator); - this->RunQuery(query); - } - void OnAccessClear(ChannelInfo *ci, User *u) { SQLQuery query("DELETE FROM `anope_cs_access` WHERE `channel` = @channel"); @@ -1520,7 +1525,7 @@ static void SaveDatabases() { ChanAccess *access = ci->GetAccess(j); - me->OnAccessChange(ci, NULL, access); + me->OnAccessAdd(ci, NULL, access); } for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j) diff --git a/modules/core/db_plain.cpp b/modules/core/db_plain.cpp index da0f46053..c41cea2d2 100644 --- a/modules/core/db_plain.cpp +++ b/modules/core/db_plain.cpp @@ -181,49 +181,48 @@ static void ReadDatabase(Module *m = NULL) struct ChannelLevel { + ChannelAccess Level; Anope::string Name; - int Level; }; ChannelLevel ChannelLevels[] = { - {"INVITE", CA_INVITE}, - {"AKICK", CA_AKICK}, - {"SET", CA_SET}, - {"UNBAN", CA_UNBAN}, - {"AUTOOP", CA_AUTOOP}, - {"AUTOVOICE", CA_AUTOVOICE}, - {"OPDEOP", CA_OPDEOP}, - {"ACCESS_LIST", CA_ACCESS_LIST}, - {"NOJOIN", CA_NOJOIN}, - {"ACCESS_CHANGE", CA_ACCESS_CHANGE}, - {"MEMO", CA_MEMO}, - {"ASSIGN", CA_ASSIGN}, - {"BADWORDS", CA_BADWORDS}, - {"NOKICK", CA_NOKICK}, - {"FANTASIA", CA_FANTASIA}, - {"SAY", CA_SAY}, - {"GREET", CA_GREET}, - {"VOICEME", CA_VOICEME}, - {"VOICE", CA_VOICE}, - {"GETKEY", CA_GETKEY}, - {"AUTOHALFOP", CA_AUTOHALFOP}, - {"AUTOPROTECT", CA_AUTOPROTECT}, - {"OPDEOPME", CA_OPDEOPME}, - {"HALFOPME", CA_HALFOPME}, - {"HALFOP", CA_HALFOP}, - {"PROTECTME", CA_PROTECTME}, - {"PROTECT", CA_PROTECT}, - {"KICKME", CA_KICKME}, - {"KICK", CA_KICK}, - {"SIGNKICK", CA_SIGNKICK}, - {"BANME", CA_BANME}, - {"BAN", CA_BAN}, - {"TOPIC", CA_TOPIC}, - {"INFO", CA_INFO}, - {"AUTOOWNER", CA_AUTOOWNER}, - {"OWNER", CA_OWNER}, - {"OWNERME", CA_OWNERME}, - {"", -1} + { CA_ACCESS_LIST, "ACCESS_LIST" }, + { CA_NOKICK, "NOKICK" }, + { CA_FANTASIA, "FANTASIA" }, + { CA_GREET, "GREET" }, + { CA_AUTOVOICE, "AUTOVOICE" }, + { CA_VOICEME, "VOICEME" }, + { CA_VOICE, "VOICE" }, + { CA_INFO, "INFO" }, + { CA_SAY, "SAY" }, + { CA_AUTOHALFOP, "AUTOHALFOP" }, + { CA_HALFOPME, "HALFOPME" }, + { CA_HALFOP, "HALFOP" }, + { CA_KICK, "KICK" }, + { CA_SIGNKICK, "SIGNKICK" }, + { CA_BAN, "BAN" }, + { CA_TOPIC, "TOPIC" }, + { CA_MODE, "MODE" }, + { CA_GETKEY, "GETKEY" }, + { CA_INVITE, "INVITE" }, + { CA_UNBAN, "UNBAN" }, + { CA_AUTOOP, "AUTOOP" }, + { CA_OPDEOPME, "OPDEOPME" }, + { CA_OPDEOP, "OPDEOP" }, + { CA_AUTOPROTECT, "AUTOPROTECT" }, + { CA_AKICK, "AKICK" }, + { CA_BADWORDS, "BADWORDS" }, + { CA_ASSIGN, "ASSIGN" }, + { CA_MEMO, "MEMO" }, + { CA_ACCESS_CHANGE, "ACCESS_CHANGE" }, + { CA_PROTECTME, "PROTECTME" }, + { CA_PROTECT, "PROTECT" }, + { CA_SET, "SET" }, + { CA_AUTOOWNER, "AUTOPROTECT" }, + { CA_OWNERME, "OWNERME" }, + { CA_OWNER, "OWNER" }, + { CA_FOUNDER, "FOUNDER" }, + { CA_SIZE, "" } }; static Anope::string ToString(const std::vector<Anope::string> &strings) @@ -531,7 +530,7 @@ class DBPlain : public Module else if (key.equals_ci("LEVELS")) { for (unsigned j = 0, end = params.size(); j < end; j += 2) - for (int i = 0; ChannelLevels[i].Level != -1; ++i) + for (int i = 0; ChannelLevels[i].Level != CA_SIZE; ++i) if (params[j].equals_ci(ChannelLevels[i].Name)) ci->levels[ChannelLevels[i].Level] = params[j + 1].is_number_only() ? convertTo<int16>(params[j + 1]) : 0; } @@ -552,9 +551,19 @@ class DBPlain : public Module } else if (key.equals_ci("ACCESS")) { - int level = params[1].is_number_only() ? convertTo<int>(params[1]) : 0; - time_t last_seen = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0; - ci->AddAccess(params[0], level, params[3], last_seen); + service_reference<AccessProvider> provider(params[0]); + if (!provider) + throw DatabaseException("Access entry for nonexistant provider " + params[0]); + + ChanAccess *access = provider->Create(); + access->ci = ci; + access->mask = params[1]; + access->Unserialize(params[2]); + access->last_seen = params[3].is_pos_number_only() ? convertTo<time_t>(params[3]) : 0; + access->creator = params[4]; + access->created = params.size() > 5 && params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : Anope::CurTime; + + ci->AddAccess(access); } else if (key.equals_ci("AKICK")) { @@ -766,7 +775,7 @@ class DBPlain : public Module if (!ci->last_topic.empty()) db_buffer << "MD TOPIC " << ci->last_topic_setter << " " << ci->last_topic_time << " :" << ci->last_topic << endl; db_buffer << "MD LEVELS"; - for (int j = 0; ChannelLevels[j].Level != -1; ++j) + for (int j = 0; ChannelLevels[j].Level != CA_SIZE; ++j) db_buffer << " " << ChannelLevels[j].Name << " " << ci->levels[ChannelLevels[j].Level]; db_buffer << endl; if (ci->FlagCount()) @@ -779,7 +788,10 @@ class DBPlain : public Module db_buffer << "MD SUSPEND " << by << " :" << reason << endl; } for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k) - db_buffer << "MD ACCESS " << ci->GetAccess(k)->GetMask() << " " << ci->GetAccess(k)->level << " " << ci->GetAccess(k)->last_seen << " " << ci->GetAccess(k)->creator << endl; + { + ChanAccess *access = ci->GetAccess(k); + db_buffer << "MD ACCESS " << access->provider->name << " " << access->mask << " " << access->Serialize() << " " << access->last_seen << " " << access->creator << " " << access->created << endl; + } for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k) { db_buffer << "MD AKICK 0 " << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? "NICK " : "MASK ") << diff --git a/modules/core/ms_del.cpp b/modules/core/ms_del.cpp index c1d1e09c6..991adff2b 100644 --- a/modules/core/ms_del.cpp +++ b/modules/core/ms_del.cpp @@ -70,7 +70,7 @@ class CommandMSDel : public Command source.Reply(READ_ONLY_MODE); return; } - else if (!check_access(u, ci, CA_MEMO)) + else if (!ci->HasPriv(u, CA_MEMO)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/ms_ignore.cpp b/modules/core/ms_ignore.cpp index 250b1c171..c8b9c19e6 100644 --- a/modules/core/ms_ignore.cpp +++ b/modules/core/ms_ignore.cpp @@ -42,7 +42,7 @@ class CommandMSIgnore : public Command MemoInfo *mi = memoserv->GetMemoInfo(channel, ischan); if (!mi) source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), channel.c_str()); - else if (ischan && !check_access(u, cs_findchan(channel), CA_MEMO)) + else if (ischan && !cs_findchan(channel)->HasPriv(u, CA_MEMO)) source.Reply(ACCESS_DENIED); else if (command.equals_ci("ADD") && !param.empty()) { diff --git a/modules/core/ms_info.cpp b/modules/core/ms_info.cpp index ae532fb9f..9fdb467c9 100644 --- a/modules/core/ms_info.cpp +++ b/modules/core/ms_info.cpp @@ -50,7 +50,7 @@ class CommandMSInfo : public Command source.Reply(CHAN_X_NOT_REGISTERED, nname.c_str()); return; } - else if (!check_access(u, ci, CA_MEMO)) + else if (!ci->HasPriv(u, CA_MEMO)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/ms_list.cpp b/modules/core/ms_list.cpp index 0e86de54d..26821a413 100644 --- a/modules/core/ms_list.cpp +++ b/modules/core/ms_list.cpp @@ -74,7 +74,7 @@ class CommandMSList : public Command source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); return; } - else if (!check_access(u, ci, CA_MEMO)) + else if (!ci->HasPriv(u, CA_MEMO)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/ms_main.cpp b/modules/core/ms_main.cpp index 4a4531a3f..bb6d7847e 100644 --- a/modules/core/ms_main.cpp +++ b/modules/core/ms_main.cpp @@ -98,7 +98,7 @@ class MyMemoServService : public MemoServService { UserContainer *cu = *it; - if (check_access(cu->user, ci, CA_MEMO)) + if (ci->HasPriv(cu->user, CA_MEMO)) { if (cu->user->Account() && cu->user->Account()->HasFlag(NI_MEMO_RECEIVE)) cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), mi->memos.size()); @@ -179,7 +179,7 @@ class MemoServCore : public Module void OnJoinChannel(User *u, Channel *c) { - if (c->ci && check_access(u, c->ci, CA_MEMO) && c->ci->memos.memos.size() > 0) + if (c->ci && c->ci->HasPriv(u, CA_MEMO) && c->ci->memos.memos.size() > 0) { if (c->ci->memos.memos.size() == 1) u->SendMessage(MemoServ, _("There is \002%d\002 memo on channel %s."), c->ci->memos.memos.size(), c->ci->name.c_str()); diff --git a/modules/core/ms_read.cpp b/modules/core/ms_read.cpp index dba38f5b8..f275f8d7d 100644 --- a/modules/core/ms_read.cpp +++ b/modules/core/ms_read.cpp @@ -108,7 +108,7 @@ class CommandMSRead : public Command source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); return; } - else if (!check_access(u, ci, CA_MEMO)) + else if (!ci->HasPriv(u, CA_MEMO)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/ms_set.cpp b/modules/core/ms_set.cpp index 37ad0d7f8..1aa3f469d 100644 --- a/modules/core/ms_set.cpp +++ b/modules/core/ms_set.cpp @@ -91,7 +91,7 @@ class CommandMSSet : public Command source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); return; } - else if (!is_servadmin && !check_access(u, ci, CA_MEMO)) + else if (!is_servadmin && !ci->HasPriv(u, CA_MEMO)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/ns_ajoin.cpp b/modules/core/ns_ajoin.cpp index 056b22e08..4f35f28b8 100644 --- a/modules/core/ns_ajoin.cpp +++ b/modules/core/ns_ajoin.cpp @@ -159,7 +159,7 @@ class NSAJoin : public Module Anope::string k; if (c->GetParam(CMODE_KEY, k)) { - if (check_access(u, ci, CA_GETKEY)) + if (ci->HasPriv(u, CA_GETKEY)) key = k; else if (key != k) need_invite = true; @@ -184,7 +184,7 @@ class NSAJoin : public Module if (need_invite) { BotInfo *bi = findbot(Config->NickServ); - if (!bi || !check_access(u, ci, CA_INVITE)) + if (!bi || !ci->HasPriv(u, CA_INVITE)) continue; ircdproto->SendInvite(bi, channels[i].first, u->nick); } diff --git a/modules/core/ns_alist.cpp b/modules/core/ns_alist.cpp index ec4686261..f502b4344 100644 --- a/modules/core/ns_alist.cpp +++ b/modules/core/ns_alist.cpp @@ -19,137 +19,58 @@ class CommandNSAList : public Command CommandNSAList(Module *creator) : Command(creator, "nickserv/alist", 0, 2) { this->SetDesc(_("List channels you have access on")); - this->SetSyntax(_("[\037nickname\037] [\037level\037]")); + this->SetSyntax(_("[\037nickname\037]")); } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) { - /* - * List the channels that the given nickname has access on - * - * /ns ALIST [level] - * /ns ALIST [nickname] [level] - * - * -jester - */ - User *u = source.u; - Anope::string nick; - NickAlias *na; - - int is_servadmin = u->IsServicesOper(); - unsigned lev_param = 0; + Anope::string nick = u->Account()->display; - if (!is_servadmin) - /* Non service admins can only see their own levels */ - na = findnick(u->Account()->display); - else - { - /* Services admins can request ALIST on nicks. - * The first argument for service admins must - * always be a nickname. - */ - nick = !params.empty() ? params[0] : ""; - lev_param = 1; - - /* If an argument was passed, use it as the nick to see levels - * for, else check levels for the user calling the command */ - na = findnick(!nick.empty() ? nick : u->nick); - } + if (params.size() && u->IsServicesOper()) + nick = params[0]; - /* If available, get level from arguments */ - Anope::string lev = params.size() > lev_param ? params[lev_param] : ""; - - /* if a level was given, make sure it's an int for later */ - int min_level = 0; - if (!lev.empty()) - { - if (lev.equals_ci("FOUNDER")) - min_level = ACCESS_FOUNDER; - else if (lev.equals_ci("SOP")) - min_level = ACCESS_SOP; - else if (lev.equals_ci("AOP")) - min_level = ACCESS_AOP; - else if (lev.equals_ci("HOP")) - min_level = ACCESS_HOP; - else if (lev.equals_ci("VOP")) - min_level = ACCESS_VOP; - else - { - try - { - min_level = convertTo<int>(lev); - } - catch (const ConvertException &) - { - min_level = ACCESS_INVALID; - } - } - } + NickAlias *na = findnick(nick); if (!na) source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (min_level <= ACCESS_INVALID || min_level > ACCESS_FOUNDER) - source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); else { int chan_count = 0; - int match_count = 0; - if (is_servadmin) - source.Reply(_("Channels that \002%s\002 has access on:\n" - " Num Channel Level Description"), na->nick.c_str()); - else - source.Reply(_("Channels that you have access on:\n" - " Num Channel Level Description")); + source.Reply(_("Channels that \002%s\002 has access on:\n" + " Num Channel Level"), na->nick.c_str()); for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) { ChannelInfo *ci = it->second; - ChanAccess *access = ci->GetAccess(na->nc); - if (access) - { - ++chan_count; + AccessGroup access = ci->AccessFor(na->nc); + if (access.empty()) + continue; + + ++chan_count; - if (min_level > access->level) - continue; - - ++match_count; - - if (ci->HasFlag(CI_XOP) || access->level == ACCESS_FOUNDER) - { - Anope::string xop = get_xop_level(access->level); - - source.Reply(_(" %3d %c%-20s %-8s %s"), match_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), xop.c_str(), !ci->desc.empty() ? ci->desc.c_str() : ""); - } - else - source.Reply(_(" %3d %c%-20s %-8d %s"), match_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), access->level, !ci->desc.empty() ? ci->desc.c_str() : ""); + if (access.size() > 1) + { + source.Reply(_(" %3d You match %d access entries on %c%s, they are"), chan_count, access.size(), ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str()); + for (unsigned i = 0; i < access.size(); ++i) + source.Reply(_(" %3d %-8s"), i + 1, access[i]->Serialize().c_str()); } + else + source.Reply(_(" %3d %c%-20s %-8s"), chan_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), access[0]->Serialize().c_str()); } - source.Reply(_("End of list - %d/%d channels shown."), match_count, chan_count); + source.Reply(_("End of list - %d channels shown."), chan_count); } - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) { this->SendSyntax(source); source.Reply(" "); - source.Reply(_("Lists all channels you have access on. Optionally, you can specify\n" - "a level in XOP or ACCESS format. The resulting list will only\n" - "include channels where you have the given level of access.\n" - "Examples:\n" - " \002ALIST Founder\002\n" - " Lists all channels where you have Founder\n" - " access.\n" - " \002ALIST AOP\002\n" - " Lists all channels where you have AOP\n" - " access or greater.\n" - " \002ALIST 10\002\n" - " Lists all channels where you have level 10\n" - " access or greater.\n" + source.Reply(_("Lists all channels you have access on.\n" + " \n" "Channels that have the \037NOEXPIRE\037 option set will be\n" "prefixed by an exclamation mark. The nickname parameter is\n" "limited to Services Operators")); diff --git a/modules/extra/cs_appendtopic.cpp b/modules/extra/cs_appendtopic.cpp index 5b39b8c8c..c9af50613 100644 --- a/modules/extra/cs_appendtopic.cpp +++ b/modules/extra/cs_appendtopic.cpp @@ -58,7 +58,7 @@ class CommandCSAppendTopic : public Command source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); else if (!c->ci) source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!check_access(u, c->ci, CA_TOPIC)) + else if (!c->ci->HasPriv(u, CA_TOPIC)) source.Reply(ACCESS_DENIED); else { @@ -77,7 +77,7 @@ class CommandCSAppendTopic : public Command if (has_topiclock) c->ci->SetFlag(CI_TOPICLOCK); - bool override = !check_access(u, c->ci, CA_TOPIC); + bool override = c->ci->HasPriv(u, CA_TOPIC); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, c->ci) << "changed topic to " << topic; } return; diff --git a/modules/extra/cs_enforce.cpp b/modules/extra/cs_enforce.cpp index 4ba813ffd..5f59a4b31 100644 --- a/modules/extra/cs_enforce.cpp +++ b/modules/extra/cs_enforce.cpp @@ -69,7 +69,7 @@ class CommandCSEnforce : public Command void DoRestricted(Channel *c) { - ChannelInfo *ci; + /*ChannelInfo *ci; int16 old_nojoin_level; Anope::string mask; @@ -94,6 +94,9 @@ class CommandCSEnforce : public Command } ci->levels[CA_NOJOIN] = old_nojoin_level; + XXX + */ + } void DoCModeR(Channel *c) @@ -136,7 +139,7 @@ class CommandCSEnforce : public Command source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); else if (!c->ci) source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!check_access(u, c->ci, CA_AKICK)) + else if (!c->ci->HasPriv(u, CA_AKICK)) source.Reply(ACCESS_DENIED); else { diff --git a/modules/extra/cs_tban.cpp b/modules/extra/cs_tban.cpp index a0bb6a626..404279bef 100644 --- a/modules/extra/cs_tban.cpp +++ b/modules/extra/cs_tban.cpp @@ -40,7 +40,7 @@ static bool CanBanUser(CommandSource &source, Channel *c, User *u2) User *u = source.u; ChannelInfo *ci = c->ci; bool ok = false; - if (!check_access(u, ci, CA_BAN)) + if (!ci->HasPriv(u, CA_BAN)) source.Reply(ACCESS_DENIED); else if (matches_list(c, u2, CMODE_EXCEPT)) source.Reply(CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str()); diff --git a/modules/extra/m_helpchan.cpp b/modules/extra/m_helpchan.cpp index 71e1d3355..f5c63941a 100644 --- a/modules/extra/m_helpchan.cpp +++ b/modules/extra/m_helpchan.cpp @@ -28,7 +28,7 @@ class HelpChannel : public Module { User *u = finduser(param); - if (u && check_access(u, c->ci, CA_OPDEOPME)) + if (u && c->ci->HasPriv(u, CA_OPDEOPME)) u->SetMode(findbot(Config->OperServ), UMODE_HELPOP); } diff --git a/modules/extra/m_statusupdate.cpp b/modules/extra/m_statusupdate.cpp index f04ab4fa5..2b23492ec 100644 --- a/modules/extra/m_statusupdate.cpp +++ b/modules/extra/m_statusupdate.cpp @@ -7,6 +7,19 @@ #include "module.h" +static struct ModeInfo +{ + ChannelAccess priv; + ChannelModeName name; +} modeInfo[] = { + { CA_AUTOOWNER, CMODE_OWNER }, + { CA_AUTOPROTECT, CMODE_PROTECT }, + { CA_AUTOOP, CMODE_OP }, + { CA_AUTOHALFOP, CMODE_HALFOP }, + { CA_AUTOVOICE, CMODE_VOICE }, + { CA_SIZE, CMODE_END } +}; + class StatusUpdate : public Module { public: @@ -14,41 +27,42 @@ class StatusUpdate : public Module { this->SetAuthor("Anope"); - Implementation i[] = { I_OnAccessAdd, I_OnAccessChange, I_OnAccessDel, I_OnAccessClear }; + Implementation i[] = { I_OnAccessAdd, I_OnAccessDel }; ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); } void OnAccessAdd(ChannelInfo *ci, User *u, ChanAccess *access) { - this->OnAccessChange(ci, u, access); - } - - void OnAccessChange(ChannelInfo *ci, User *, ChanAccess *access) - { if (ci->c) for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) { User *user = (*it)->user; - if (user == ci->bi) - continue; - - ChanAccess *highest = ci->GetAccess(user); - - if (!access || (highest == access)) - chan_set_correct_modes(user, ci->c, 1); + if (access->Matches(user, user->Account())) + { + for (int i = 0; modeInfo[i].priv != CA_SIZE; ++i) + if (access->HasPriv(modeInfo[i].priv)) + ci->c->SetMode(NULL, modeInfo[i].name, user->nick); + else + ci->c->RemoveMode(NULL, modeInfo[i].name, user->nick); + } } } void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) { - access->level = 0; - this->OnAccessChange(ci, u, access); - } + if (ci->c) + for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) + { + User *user = (*it)->user; - void OnAccessClear(ChannelInfo *ci, User *u) - { - this->OnAccessChange(ci, u, NULL); + if (access->Matches(user, user->Account())) + { + for (int i = 0; modeInfo[i].priv != CA_SIZE; ++i) + if (access->HasPriv(modeInfo[i].priv)) + ci->c->RemoveMode(NULL, modeInfo[i].name, user->nick); + } + } } }; diff --git a/src/access.cpp b/src/access.cpp new file mode 100644 index 000000000..5fba02c98 --- /dev/null +++ b/src/access.cpp @@ -0,0 +1,140 @@ +/* + * + * (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 "services.h" +#include "modules.h" +#include "access.h" + +AccessProvider::AccessProvider(Module *o, const Anope::string &n) : Service(o, n) +{ +} + +AccessProvider::~AccessProvider() +{ +} + +ChanAccess::ChanAccess(AccessProvider *p) : provider(p) +{ +} + +ChanAccess::~ChanAccess() +{ +} + +bool ChanAccess::operator>(ChanAccess &other) +{ + for (size_t i = CA_SIZE; i > 0; --i) + if (this->HasPriv(static_cast<ChannelAccess>(i - 1)) && !other.HasPriv(static_cast<ChannelAccess>(i - 1))) + return true; + return false; +} + +bool ChanAccess::operator<(ChanAccess &other) +{ + for (size_t i = CA_SIZE; i > 0; --i) + if (!this->HasPriv(static_cast<ChannelAccess>(i - 1)) && other.HasPriv(static_cast<ChannelAccess>(i - 1))) + return true; + return false; +} + +bool ChanAccess::operator>=(ChanAccess &other) +{ + for (size_t i = CA_SIZE; i > 0; --i) + { + bool this_p = this->HasPriv(static_cast<ChannelAccess>(i - 1)), + other_p = other.HasPriv(static_cast<ChannelAccess>(i - 1)); + + if ((this_p && !other_p) || (this_p && other_p)) + return true; + } + + return false; +} + +bool ChanAccess::operator<=(ChanAccess &other) +{ + for (size_t i = CA_SIZE; i > 0; --i) + { + bool this_p = this->HasPriv(static_cast<ChannelAccess>(i - 1)), + other_p = other.HasPriv(static_cast<ChannelAccess>(i - 1)); + + if ((!this_p && other_p) || (this_p && other_p)) + return true; + } + + return false; +} + +AccessGroup::AccessGroup() : std::vector<ChanAccess *>() +{ +} + +bool AccessGroup::HasPriv(ChannelAccess priv) const +{ + for (unsigned i = this->size(); i > 0; --i) + if (this->at(i - 1)->HasPriv(priv)) + return true; + return false; +} + +ChanAccess *AccessGroup::Highest() const +{ + for (size_t i = CA_SIZE; i > 0; --i) + for (unsigned j = this->size(); j > 0; --j) + if (this->at(j - 1)->HasPriv(static_cast<ChannelAccess>(i - 1))) + return this->at(j - 1); + return NULL; +} + +bool AccessGroup::operator>(const AccessGroup &other) const +{ + for (size_t i = CA_SIZE; i > 0; --i) + if (this->HasPriv(static_cast<ChannelAccess>(i - 1)) && !other.HasPriv(static_cast<ChannelAccess>(i - 1))) + return true; + return true; +} + +bool AccessGroup::operator<(const AccessGroup &other) const +{ + for (size_t i = CA_SIZE; i > 0; --i) + if (!this->HasPriv(static_cast<ChannelAccess>(i - 1)) && other.HasPriv(static_cast<ChannelAccess>(i - 1))) + return true; + return false; +} + +bool AccessGroup::operator>=(const AccessGroup &other) const +{ + for (size_t i = CA_SIZE; i > 0; --i) + { + bool this_p = this->HasPriv(static_cast<ChannelAccess>(i - 1)), + other_p = other.HasPriv(static_cast<ChannelAccess>(i - 1)); + + if ((this_p && !other_p) || (this_p && other_p)) + return true; + } + + return false; +} + +bool AccessGroup::operator<=(const AccessGroup &other) const +{ + for (size_t i = CA_SIZE; i > 0; --i) + { + bool this_p = this->HasPriv(static_cast<ChannelAccess>(i - 1)), + other_p = other.HasPriv(static_cast<ChannelAccess>(i - 1)); + + if ((!this_p && other_p) || (this_p && other_p)) + return true; + } + + return false; +} + diff --git a/src/botserv.cpp b/src/botserv.cpp index 9d7c38817..1670283be 100644 --- a/src/botserv.cpp +++ b/src/botserv.cpp @@ -55,9 +55,8 @@ void bot_raw_ban(User *requester, ChannelInfo *ci, User *u, const Anope::string return; } - ChanAccess *u_access = ci->GetAccess(u), *req_access = ci->GetAccess(requester); - int16 u_level = u_access ? u_access->level : 0, req_level = req_access ? req_access->level : 0; - if (ci->HasFlag(CI_PEACE) && !requester->nick.equals_ci(u->nick) && u_level >= req_level) + AccessGroup u_access = ci->AccessFor(u), req_access = ci->AccessFor(requester); + if (ci->HasFlag(CI_PEACE) && u != requester && u_access >= req_access) return; if (matches_list(ci->c, u, CMODE_EXCEPT)) @@ -72,7 +71,7 @@ void bot_raw_ban(User *requester, ChannelInfo *ci, User *u, const Anope::string ci->c->SetMode(NULL, CMODE_BAN, mask); /* Check if we need to do a signkick or not -GD */ - if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(requester, ci, CA_SIGNKICK))) + if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !req_access.HasPriv(CA_SIGNKICK))) ci->c->Kick(ci->bi, u, "%s (%s)", !reason.empty() ? reason.c_str() : ci->bi->nick.c_str(), requester->nick.c_str()); else ci->c->Kick(ci->bi, u, "%s", !reason.empty() ? reason.c_str() : ci->bi->nick.c_str()); @@ -97,12 +96,11 @@ void bot_raw_kick(User *requester, ChannelInfo *ci, User *u, const Anope::string return; } - ChanAccess *u_access = ci->GetAccess(u), *req_access = ci->GetAccess(requester); - int16 u_level = u_access ? u_access->level : 0, req_level = req_access ? req_access->level : 0; - if (ci->HasFlag(CI_PEACE) && !requester->nick.equals_ci(u->nick) && u_level >= req_level) + AccessGroup u_access = ci->AccessFor(u), req_access = ci->AccessFor(requester); + if (ci->HasFlag(CI_PEACE) && requester != u && u_access >= req_access) return; - if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(requester, ci, CA_SIGNKICK))) + if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !req_access.HasPriv(CA_SIGNKICK))) ci->c->Kick(ci->bi, u, "%s (%s)", !reason.empty() ? reason.c_str() : ci->bi->nick.c_str(), requester->nick.c_str()); else ci->c->Kick(ci->bi, u, "%s", !reason.empty() ? reason.c_str() : ci->bi->nick.c_str()); diff --git a/src/channels.cpp b/src/channels.cpp index fe97c5b46..17ed8a629 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -1071,47 +1071,51 @@ void do_cmode(const Anope::string &source, const Anope::string &channel, const A **/ void chan_set_correct_modes(User *user, Channel *c, int give_modes) { - ChannelInfo *ci; - ChannelMode *owner, *admin, *op, *halfop, *voice; + ChannelMode *owner = ModeManager::FindChannelModeByName(CMODE_OWNER), + *admin = ModeManager::FindChannelModeByName(CMODE_PROTECT), + *op = ModeManager::FindChannelModeByName(CMODE_OP), + *halfop = ModeManager::FindChannelModeByName(CMODE_HALFOP), + *voice = ModeManager::FindChannelModeByName(CMODE_VOICE); - owner = ModeManager::FindChannelModeByName(CMODE_OWNER); - admin = ModeManager::FindChannelModeByName(CMODE_PROTECT); - op = ModeManager::FindChannelModeByName(CMODE_OP); - halfop = ModeManager::FindChannelModeByName(CMODE_HALFOP); - voice = ModeManager::FindChannelModeByName(CMODE_VOICE); + if (user == NULL || c == NULL) + return; + + ChannelInfo *ci = c->ci; - if (!c || !(ci = c->ci)) + if (ci == NULL) return; Log(LOG_DEBUG) << "Setting correct user modes for " << user->nick << " on " << c->name << " (" << (give_modes ? "" : "not ") << "giving modes)"; + AccessGroup u_access = ci->AccessFor(user); + if (give_modes && (!user->Account() || user->Account()->HasFlag(NI_AUTOOP))) { - if (owner && check_access(user, ci, CA_AUTOOWNER)) + if (owner && u_access.HasPriv(CA_AUTOOWNER)) c->SetMode(NULL, CMODE_OWNER, user->nick); - else if (admin && check_access(user, ci, CA_AUTOPROTECT)) + else if (admin && u_access.HasPriv(CA_AUTOPROTECT)) c->SetMode(NULL, CMODE_PROTECT, user->nick); - if (op && check_access(user, ci, CA_AUTOOP)) + if (op && u_access.HasPriv(CA_AUTOOP)) c->SetMode(NULL, CMODE_OP, user->nick); - else if (halfop && check_access(user, ci, CA_AUTOHALFOP)) + else if (halfop && u_access.HasPriv(CA_AUTOHALFOP)) c->SetMode(NULL, CMODE_HALFOP, user->nick); - else if (voice && check_access(user, ci, CA_AUTOVOICE)) + else if (voice && u_access.HasPriv(CA_AUTOVOICE)) c->SetMode(NULL, CMODE_VOICE, user->nick); } /* If this channel has secureops or the channel is syncing and they are not ulined, check to remove modes */ if ((ci->HasFlag(CI_SECUREOPS) || (c->HasFlag(CH_SYNCING) && user->server->IsSynced())) && !user->server->IsULined()) { - if (owner && c->HasUserStatus(user, CMODE_OWNER) && !check_access(user, ci, CA_FOUNDER)) + if (owner && c->HasUserStatus(user, CMODE_OWNER) && !u_access.HasPriv(CA_AUTOOWNER) && !u_access.HasPriv(CA_PROTECTME)) c->RemoveMode(NULL, CMODE_OWNER, user->nick); - if (admin && c->HasUserStatus(user, CMODE_PROTECT) && !check_access(user, ci, CA_AUTOPROTECT) && !check_access(user, ci, CA_PROTECTME)) + if (admin && c->HasUserStatus(user, CMODE_PROTECT) && !u_access.HasPriv(CA_AUTOPROTECT) && !u_access.HasPriv(CA_PROTECTME)) c->RemoveMode(NULL, CMODE_PROTECT, user->nick); - if (op && c->HasUserStatus(user, CMODE_OP) && !check_access(user, ci, CA_AUTOOP) && !check_access(user, ci, CA_OPDEOPME)) + if (op && c->HasUserStatus(user, CMODE_OP) && !u_access.HasPriv(CA_AUTOOP) && !u_access.HasPriv(CA_OPDEOPME)) c->RemoveMode(NULL, CMODE_OP, user->nick); - if (halfop && c->HasUserStatus(user, CMODE_HALFOP) && !check_access(user, ci, CA_AUTOHALFOP) && !check_access(user, ci, CA_HALFOPME)) + if (halfop && c->HasUserStatus(user, CMODE_HALFOP) && !u_access.HasPriv(CA_AUTOHALFOP) && !u_access.HasPriv(CA_HALFOPME)) c->RemoveMode(NULL, CMODE_HALFOP, user->nick); } diff --git a/src/chanserv.cpp b/src/chanserv.cpp index 6a39340ae..c7bbe11f8 100644 --- a/src/chanserv.cpp +++ b/src/chanserv.cpp @@ -16,93 +16,6 @@ registered_channel_map RegisteredChannelList; -static int def_levels[][2] = { - { CA_AUTOOP, 5 }, - { CA_AUTOVOICE, 3 }, - { CA_NOJOIN, -2 }, - { CA_INVITE, 5 }, - { CA_AKICK, 10 }, - { CA_SET, ACCESS_QOP }, - { CA_UNBAN, 5 }, - { CA_OPDEOP, 5 }, - { CA_ACCESS_LIST, 1 }, - { CA_ACCESS_CHANGE, 10 }, - { CA_MEMO, 10 }, - { CA_ASSIGN, ACCESS_FOUNDER }, - { CA_BADWORDS, 10 }, - { CA_NOKICK, 1 }, - { CA_FANTASIA, 3 }, - { CA_SAY, 5 }, - { CA_GREET, 5 }, - { CA_VOICEME, 3 }, - { CA_VOICE, 5 }, - { CA_GETKEY, 5 }, - { CA_AUTOHALFOP, 4 }, - { CA_AUTOPROTECT, 10 }, - { CA_OPDEOPME, 5 }, - { CA_HALFOPME, 4 }, - { CA_HALFOP, 5 }, - { CA_PROTECTME, 10 }, - { CA_PROTECT, ACCESS_QOP }, - { CA_KICKME, 5 }, - { CA_KICK, 5 }, - { CA_SIGNKICK, ACCESS_FOUNDER }, - { CA_BANME, 5 }, - { CA_BAN, 5 }, - { CA_TOPIC, ACCESS_FOUNDER }, - { CA_MODE, ACCESS_FOUNDER }, - { CA_INFO, ACCESS_QOP }, - { CA_AUTOOWNER, ACCESS_QOP }, - { CA_OWNER, ACCESS_FOUNDER }, - { CA_OWNERME, ACCESS_QOP }, - { CA_FOUNDER, ACCESS_QOP }, - { -1 } -}; - -LevelInfo levelinfo[] = { - { CA_AUTOHALFOP, "AUTOHALFOP", _("Automatic mode +h") }, - { CA_AUTOOP, "AUTOOP", _("Automatic channel operator status") }, - { CA_AUTOPROTECT, "AUTOPROTECT", _("Automatic mode +a") }, - { CA_AUTOVOICE, "AUTOVOICE", _("Automatic mode +v") }, - { CA_NOJOIN, "NOJOIN", _("Not allowed to join channel") }, - { CA_SIGNKICK, "SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used") }, - { CA_ACCESS_LIST, "ACC-LIST", _("Allowed to view the access list") }, - { CA_ACCESS_CHANGE, "ACC-CHANGE", _("Allowed to modify the access list") }, - { CA_AKICK, "AKICK", _("Allowed to use AKICK command") }, - { CA_SET, "SET", _("Allowed to use SET command (not FOUNDER/PASSWORD)"), }, - { CA_BAN, "BAN", _("Allowed to use BAN command") }, - { CA_BANME, "BANME", _("Allowed to ban him/herself") }, - { CA_GETKEY, "GETKEY", _("Allowed to use GETKEY command") }, - { CA_HALFOP, "HALFOP", _("Allowed to (de)halfop him/herself") }, - { CA_HALFOPME, "HALFOPME", _("Allowed to (de)halfop him/herself") }, - { CA_INFO, "INFO", _("Allowed to use INFO command with ALL option") }, - { CA_KICK, "KICK", _("Allowed to use KICK command") }, - { CA_KICKME, "KICKME", _("Allowed to kick him/herself") }, - { CA_INVITE, "INVITE", _("Allowed to use INVITE command") }, - { CA_OPDEOP, "OPDEOP", _("Allowed to use OP/DEOP commands") }, - { CA_OPDEOPME, "OPDEOPME", _("Allowed to (de)op him/herself") }, - { CA_PROTECT, "PROTECT", _("Allowed to use PROTECT/DEPROTECT commands") }, - { CA_PROTECTME, "PROTECTME", _("Allowed to (de)protect him/herself"), }, - { CA_TOPIC, "TOPIC", _("Allowed to use TOPIC command") }, - { CA_MODE, "MODE", _("Allowed to use MODE command") }, - { CA_UNBAN, "UNBAN", _("Allowed to use UNBAN command") }, - { CA_VOICE, "VOICE", _("Allowed to use VOICE/DEVOICE commands") }, - { CA_VOICEME, "VOICEME", _("Allowed to (de)voice him/herself") }, - { CA_MEMO, "MEMO", _("Allowed to list/read channel memos") }, - { CA_ASSIGN, "ASSIGN", _("Allowed to assign/unassign a bot") }, - { CA_BADWORDS, "BADWORDS", _("Allowed to use BADWORDS command") }, - { CA_FANTASIA, "FANTASIA", _("Allowed to use fantaisist commands") }, - { CA_GREET, "GREET", _("Greet message displayed") }, - { CA_NOKICK, "NOKICK", _("Never kicked by the bot's kickers") }, - { CA_SAY, "SAY", _("Allowed to use SAY and ACT commands") }, - { CA_AUTOOWNER, "AUTOOWNER", _("Automatic mode +q") }, - { CA_OWNER, "OWNER", _("Allowed to use OWNER command") }, - { CA_OWNERME, "OWNERME", _("Allowed to (de)owner him/herself") }, - { CA_FOUNDER, "FOUNDER", _("Allowed to issue commands restricted to channel founders") }, - { -1 } -}; -int levelinfo_maxwidth = 0; - /*************************************************************************/ /* Check the current modes on a channel; if they conflict with a mode lock, @@ -201,72 +114,6 @@ ChannelInfo *cs_findchan(const Anope::string &chan) /*************************************************************************/ -/* Return 1 if the user's access level on the given channel falls into the - * given category, 0 otherwise. Note that this may seem slightly confusing - * in some cases: for example, check_access(..., CA_NOJOIN) returns true if - * the user does _not_ have access to the channel (i.e. matches the NOJOIN - * criterion). */ - -int check_access(User *user, ChannelInfo *ci, int what) -{ - int level, limit; - - if (!user || !ci) - return 0; - - ChanAccess *u_access = ci->GetAccess(user); - level = u_access ? u_access->level : 0; - limit = ci->levels[what]; - - // Set should never be disabled, if it is it is db-converter screwup - // This all needs rewritten anyway... - if (what == CA_SET && limit == ACCESS_INVALID) - { - ci->levels[what] = ACCESS_FOUNDER; - limit = ACCESS_FOUNDER; - } - - /* Resetting the last used time */ - if (level > 0) - ci->last_used = Anope::CurTime; - - /* Superadmin always wins. Always. */ - if (user->isSuperAdmin) - return what == CA_NOJOIN ? 0 : 1; - /* If the access of the level we are checking is disabled, they *always* get denied */ - if (limit == ACCESS_INVALID) - return 0; - /* If the level of the user is >= the level for "founder" of this channel and "founder" isn't disabled, they can do anything */ - if (ci->levels[CA_FOUNDER] != ACCESS_INVALID && level >= ci->levels[CA_FOUNDER]) - return what == CA_NOJOIN ? 0 : 1; - - if (what == CA_NOJOIN) - return level <= ci->levels[what]; - else - return level >= ci->levels[what]; -} - -/* Reset channel access level values to their default state. */ - -void reset_levels(ChannelInfo *ci) -{ - int i; - - if (!ci) - { - Log() << "reset_levels() called with NULL values"; - return; - } - - if (ci->levels) - delete [] ci->levels; - ci->levels = new int16[CA_SIZE]; - for (i = 0; def_levels[i][0] >= 0; ++i) - ci->levels[def_levels[i][0]] = def_levels[i][1]; -} - -/*************************************************************************/ - /** Is the user the real founder? * @param user The user * @param ci The channel @@ -290,14 +137,12 @@ bool IsFounder(User *user, ChannelInfo *ci) void update_cs_lastseen(User *user, ChannelInfo *ci) { - ChanAccess *access; - - if (!ci || !user || !user->Account()) + if (!ci || !user) return; - - if (IsFounder(user, ci) || user->IsIdentified() || (user->IsRecognized() && !ci->HasFlag(CI_SECURE))) - if ((access = ci->GetAccess(user))) - access->last_seen = Anope::CurTime; + + AccessGroup u_access = ci->AccessFor(user); + for (unsigned i = u_access.size(); i > 0; --i) + u_access[i - 1]->last_seen = Anope::CurTime; } /*************************************************************************/ @@ -339,30 +184,6 @@ int get_idealban(ChannelInfo *ci, User *u, Anope::string &ret) } } -/*************************************************************************/ - -Anope::string get_xop_level(int level) -{ - ChannelMode *halfop = ModeManager::FindChannelModeByName(CMODE_HALFOP); - - if (level < ACCESS_VOP) - return "Err"; - else if (halfop && level < ACCESS_HOP) - return "VOP"; - else if (!halfop && level < ACCESS_AOP) - return "VOP"; - else if (halfop && level < ACCESS_AOP) - return "HOP"; - else if (level < ACCESS_SOP) - return "AOP"; - else if (level < ACCESS_QOP) - return "SOP"; - else if (level < ACCESS_FOUNDER) - return "QOP"; - else - return "Founder"; -} - ChanServTimer::ChanServTimer(Channel *chan) : Timer(Config->CSInhabit), c(chan) { BotInfo *bi = findbot(Config->ChanServ); diff --git a/src/config.cpp b/src/config.cpp index 5938d3f67..4790cd34d 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -110,8 +110,6 @@ ServerConfig::ServerConfig() : config_data(), NSDefFlags(NickCoreFlagStrings), C this->CSDefFlags.SetFlag(CI_SIGNKICK_LEVEL); else if (option.equals_ci("opnotice")) this->CSDefFlags.SetFlag(CI_OPNOTICE); - else if (option.equals_ci("xop")) - this->CSDefFlags.SetFlag(CI_XOP); else if (option.equals_ci("peace")) this->CSDefFlags.SetFlag(CI_PEACE); else if (option.equals_ci("persist")) diff --git a/src/regchannel.cpp b/src/regchannel.cpp index 65613097b..1220669e0 100644 --- a/src/regchannel.cpp +++ b/src/regchannel.cpp @@ -12,25 +12,6 @@ #include "services.h" #include "modules.h" -ChanAccess::ChanAccess(const Anope::string &umask) -{ - NickAlias *na = findnick(umask); - if (na != NULL) - this->nc = na->nc; - else - { - this->nc = NULL; - this->mask = umask; - } -} - -const Anope::string &ChanAccess::GetMask() -{ - if (this->nc != NULL) - return this->nc->display; - return this->mask; -} - /** Default constructor * @param chname The channel name */ @@ -41,7 +22,6 @@ ChannelInfo::ChannelInfo(const Anope::string &chname) : Flags<ChannelInfoFlag, C this->founder = this->successor = NULL; this->last_topic_time = 0; - this->levels = NULL; this->c = findchan(chname); if (this->c) this->c->ci = this; @@ -69,12 +49,12 @@ ChannelInfo::ChannelInfo(const Anope::string &chname) : Flags<ChannelInfoFlag, C this->memos.memomax = Config->MSMaxMemos; this->last_used = this->time_registered = Anope::CurTime; - this->ttb = new int16[TTB_SIZE]; + for (int i = 0; i < CA_SIZE; ++i) + this->levels[i] = 0; + for (int i = 0; i < TTB_SIZE; ++i) this->ttb[i] = 0; - reset_levels(this); - RegisteredChannelList[this->name] = this; } @@ -95,19 +75,11 @@ ChannelInfo::ChannelInfo(ChannelInfo *ci) : Flags<ChannelInfoFlag, CI_END>(Chann if (this->bi) ++this->bi->chancount; - this->ttb = new int16[TTB_SIZE]; for (int i = 0; i < TTB_SIZE; ++i) this->ttb[i] = ci->ttb[i]; + + // XXX access - this->levels = new int16[CA_SIZE]; - for (int i = 0; i < CA_SIZE; ++i) - this->levels[i] = ci->levels[i]; - - for (unsigned i = 0; i < ci->GetAccessCount(); ++i) - { - ChanAccess *taccess = ci->GetAccess(i); - this->AddAccess(taccess->GetMask(), taccess->level, taccess->creator, taccess->last_seen); - } for (unsigned i = 0; i < ci->GetAkickCount(); ++i) { AutoKick *takick = ci->GetAkick(i); @@ -145,8 +117,6 @@ ChannelInfo::~ChannelInfo() this->ClearAccess(); this->ClearAkick(); this->ClearBadWords(); - if (this->levels) - delete [] this->levels; if (!this->memos.memos.empty()) { @@ -155,9 +125,6 @@ ChannelInfo::~ChannelInfo() this->memos.memos.clear(); } - if (this->ttb) - delete [] this->ttb; - if (this->founder) --this->founder->channelcount; } @@ -200,29 +167,11 @@ BotInfo *ChannelInfo::WhoSends() } /** Add an entry to the channel access list - * - * @param mask The mask of the access entry - * @param level The channel access level the user has on the channel - * @param creator The user who added the access - * @param last_seen When the user was last seen within the channel - * @return The new access class - * - * Creates a new access list entry and inserts it into the access list. + * @param access The entry */ - -ChanAccess *ChannelInfo::AddAccess(const Anope::string &mask, int16 level, const Anope::string &creator, int32 last_seen) +void ChannelInfo::AddAccess(ChanAccess *access) { - ChanAccess *new_access = new ChanAccess(mask); - new_access->level = level; - new_access->last_seen = last_seen; - if (!creator.empty()) - new_access->creator = creator; - else - new_access->creator = "Unknown"; - - this->access.push_back(new_access); - - return new_access; + this->access.push_back(access); } /** Get an entry from the channel access list by index @@ -240,104 +189,68 @@ ChanAccess *ChannelInfo::GetAccess(unsigned index) return this->access[index]; } -/** Get an entry from the channel access list by NickCore +/** Check if a user has a privilege on a channel * * @param u The User to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @return A ChanAccess struct corresponding to the NickCore, or NULL if not found + * @param priv The privilege to check for. + * @return true if the user has the privilege * - * Retrieves an entry from the access list that matches the given NickCore, optionally also matching a certain level. + * Retrieves an entry from the access list that matches the given User and NickCore. */ - -ChanAccess *ChannelInfo::GetAccess(User *u, int16 level) +bool ChannelInfo::HasPriv(User *u, ChannelAccess priv) { - if (!u) - return NULL; + AccessGroup group = this->AccessFor(u); - if (u->isSuperAdmin || IsFounder(u, this)) + if (this->founder && u->Account() == this->founder) { - static ChanAccess dummy_access(""); - new(&dummy_access) ChanAccess(u->nick + "!*@*"); - dummy_access.level = u->isSuperAdmin ? ACCESS_SUPERADMIN : ACCESS_FOUNDER; - dummy_access.last_seen = Anope::CurTime; - return &dummy_access; + switch (priv) + { + case CA_AUTOOWNER: + case CA_AUTOPROTECT: + case CA_AUTOOP: + case CA_AUTOHALFOP: + case CA_AUTOVOICE: + break; + default: + return true; + } } - - if (this->access.empty()) - return NULL; - - ChanAccess *highest = NULL; - for (unsigned i = 0, end = this->access.size(); i < end; ++i) - { - ChanAccess *taccess = this->access[i]; - if (level && level != taccess->level) - continue; - /* Access entry is a mask and we match it */ - else if (!taccess->nc && (Anope::Match(u->nick, taccess->GetMask()) || Anope::Match(u->GetDisplayedMask(), taccess->GetMask()))) - ; - /* Access entry is a nick core and we are identified for that account */ - else if (taccess->nc && (u->IsIdentified() || (u->IsRecognized() && !this->HasFlag(CI_SECURE))) && u->Account() == taccess->nc) - ; - else - continue; - /* Use the highest level access available */ - if (!highest || taccess->level > highest->level) - highest = taccess; - } + if (group.HasPriv(CA_FOUNDER) || group.HasPriv(priv)) + return true; - return highest; + return false; } -/** Get an entry from the channel access list by NickCore - * - * @param u The NickCore to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @return A ChanAccess struct corresponding to the NickCore, or NULL if not found - * - * Retrieves an entry from the access list that matches the given NickCore, optionally also matching a certain level. - */ -ChanAccess *ChannelInfo::GetAccess(NickCore *nc, int16 level) +AccessGroup ChannelInfo::AccessFor(User *u) { - if (nc == this->founder) + NickCore *nc = u->Account(); + if (nc == NULL && u->IsRecognized()) { - static ChanAccess dummy_access(""); - new(&dummy_access) ChanAccess(nc->display); - dummy_access.level = ACCESS_FOUNDER; - dummy_access.last_seen = Anope::CurTime; - return &dummy_access; + NickAlias *na = findnick(u->nick); + if (na != NULL) + nc = na->nc; } + + AccessGroup group; + for (unsigned i = 0, end = this->access.size(); i < end; ++i) - { - if (level && this->access[i]->level != level) - continue; - if (this->access[i]->nc && this->access[i]->nc == nc) - return this->access[i]; - } - return NULL; + if (this->access[i]->Matches(u, nc)) + group.push_back(this->access[i]); + + return group; } -/** Get an entry from the channel access list by mask - * - * @param u The mask to find within the access list vector - * @param level Optional channel access level to compare the access entries to - * @param wildcard True to match using wildcards - * @return A ChanAccess struct corresponding to the mask, or NULL if not found - * - * Retrieves an entry from the access list that matches the given mask, optionally also matching a certain level. - */ -ChanAccess *ChannelInfo::GetAccess(const Anope::string &mask, int16 level, bool wildcard) +AccessGroup ChannelInfo::AccessFor(NickCore *nc) { + AccessGroup group; + for (unsigned i = 0, end = this->access.size(); i < end; ++i) - if (this->access[i]->nc != NULL) - { - if (wildcard ? Anope::Match(this->access[i]->nc->display, mask) : this->access[i]->nc->display.equals_ci(mask)) - return this->access[i]; - } - else if (wildcard ? Anope::Match(this->access[i]->GetMask(), mask) : this->access[i]->GetMask().equals_ci(mask)) - return this->access[i]; - return NULL; + if (this->access[i]->Matches(NULL, nc)) + group.push_back(this->access[i]); + + return group; } /** Get the size of the accss vector for this channel @@ -345,7 +258,7 @@ ChanAccess *ChannelInfo::GetAccess(const Anope::string &mask, int16 level, bool */ unsigned ChannelInfo::GetAccessCount() const { - return this->access.empty() ? 0 : this->access.size(); + return this->access.size(); } /** Erase an entry from the channel access list @@ -375,6 +288,7 @@ void ChannelInfo::EraseAccess(ChanAccess *taccess) { if (this->access[i] == taccess) { + delete this->access[i]; this->access.erase(this->access.begin() + i); break; } @@ -838,13 +752,6 @@ bool ChannelInfo::CheckKick(User *user) } } - if (!do_kick && check_access(user, this, CA_NOJOIN)) - { - get_idealban(this, user, mask); - reason = translate(user, CHAN_NOT_ALLOWED_TO_JOIN); - do_kick = true; - } - if (!do_kick) return false; |