summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2011-08-01 22:37:27 -0400
committerAdam <Adam@anope.org>2011-08-01 22:37:27 -0400
commitf7adc0b35b50f06706872a161f1c7476e6e6981e (patch)
tree91fbe281f2772136a327fd4fc4c64740d1ab65ab
parent710c02f3bd3581b7c5b3d48e7604756975219fc8 (diff)
Rewrote the access systems and added a flags access system
-rw-r--r--data/commands.example.conf7
-rw-r--r--data/example.conf98
-rw-r--r--data/mysql/tables.sql6
-rw-r--r--include/access.h91
-rw-r--r--include/extern.h10
-rw-r--r--include/modules.h20
-rw-r--r--include/regchannel.h69
-rw-r--r--include/services.h82
-rw-r--r--modules/core/bs_assign.cpp8
-rw-r--r--modules/core/bs_badwords.cpp12
-rw-r--r--modules/core/bs_control.cpp4
-rw-r--r--modules/core/bs_info.cpp2
-rw-r--r--modules/core/bs_kick.cpp8
-rw-r--r--modules/core/bs_main.cpp4
-rw-r--r--modules/core/bs_set.cpp4
-rw-r--r--modules/core/cs_access.cpp408
-rw-r--r--modules/core/cs_akick.cpp31
-rw-r--r--modules/core/cs_ban.cpp9
-rw-r--r--modules/core/cs_clearusers.cpp5
-rw-r--r--modules/core/cs_clone.cpp6
-rw-r--r--modules/core/cs_drop.cpp4
-rw-r--r--modules/core/cs_flags.cpp455
-rw-r--r--modules/core/cs_getkey.cpp4
-rw-r--r--modules/core/cs_info.cpp3
-rw-r--r--modules/core/cs_invite.cpp2
-rw-r--r--modules/core/cs_kick.cpp9
-rw-r--r--modules/core/cs_main.cpp23
-rw-r--r--modules/core/cs_mode.cpp25
-rw-r--r--modules/core/cs_modes.cpp58
-rw-r--r--modules/core/cs_saset_noexpire.cpp2
-rw-r--r--modules/core/cs_set_bantype.cpp2
-rw-r--r--modules/core/cs_set_description.cpp2
-rw-r--r--modules/core/cs_set_founder.cpp4
-rw-r--r--modules/core/cs_set_keeptopic.cpp2
-rw-r--r--modules/core/cs_set_opnotice.cpp2
-rw-r--r--modules/core/cs_set_peace.cpp2
-rw-r--r--modules/core/cs_set_persist.cpp2
-rw-r--r--modules/core/cs_set_private.cpp2
-rw-r--r--modules/core/cs_set_restricted.cpp6
-rw-r--r--modules/core/cs_set_secure.cpp2
-rw-r--r--modules/core/cs_set_securefounder.cpp2
-rw-r--r--modules/core/cs_set_secureops.cpp2
-rw-r--r--modules/core/cs_set_signkick.cpp2
-rw-r--r--modules/core/cs_set_successor.cpp4
-rw-r--r--modules/core/cs_set_topiclock.cpp2
-rw-r--r--modules/core/cs_set_xop.cpp146
-rw-r--r--modules/core/cs_status.cpp75
-rw-r--r--modules/core/cs_topic.cpp4
-rw-r--r--modules/core/cs_unban.cpp2
-rw-r--r--modules/core/cs_xop.cpp417
-rw-r--r--modules/core/db_mysql.cpp51
-rw-r--r--modules/core/db_plain.cpp102
-rw-r--r--modules/core/ms_del.cpp2
-rw-r--r--modules/core/ms_ignore.cpp2
-rw-r--r--modules/core/ms_info.cpp2
-rw-r--r--modules/core/ms_list.cpp2
-rw-r--r--modules/core/ms_main.cpp4
-rw-r--r--modules/core/ms_read.cpp2
-rw-r--r--modules/core/ms_set.cpp2
-rw-r--r--modules/core/ns_ajoin.cpp4
-rw-r--r--modules/core/ns_alist.cpp123
-rw-r--r--modules/extra/cs_appendtopic.cpp4
-rw-r--r--modules/extra/cs_enforce.cpp7
-rw-r--r--modules/extra/cs_tban.cpp2
-rw-r--r--modules/extra/m_helpchan.cpp2
-rw-r--r--modules/extra/m_statusupdate.cpp52
-rw-r--r--src/access.cpp140
-rw-r--r--src/botserv.cpp14
-rw-r--r--src/channels.cpp38
-rw-r--r--src/chanserv.cpp189
-rw-r--r--src/config.cpp2
-rw-r--r--src/regchannel.cpp195
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> &params)
{
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> &params)
+ {
+ 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> &params)
+ {
+ 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> &params)
+ {
+ 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> &params)
- {
- 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> &params)
- {
- 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> &params, int level)
+ void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, 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> &params, int level)
+ void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, 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> &params, int level)
+ void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, 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> &params, int level)
+ void DoXop(CommandSource &source, const std::vector<Anope::string> &params, 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> &params)
{
- 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> &params)
{
- 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> &params)
{
- 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> &params)
{
- 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> &params)
{
- 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> &params)
{
- /*
- * 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;