summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2011-08-28 15:46:15 -0400
committerAdam <Adam@anope.org>2011-09-10 02:05:00 -0400
commit700a585b1bb38a9dc0ac3e749083250d405488f8 (patch)
tree8af306bd60778815fe5a137590d3b888213ad231
parent62752db4c49a8679b51d5996003fd3a23c2a3f2d (diff)
Allow modules to add their own channel levels
-rw-r--r--data/chanserv.example.conf8
-rw-r--r--data/tables.sql4
-rw-r--r--include/access.h59
-rw-r--r--include/anope.h22
-rw-r--r--include/extern.h2
-rw-r--r--include/modules.h8
-rw-r--r--include/regchannel.h24
-rw-r--r--include/services.h2
-rw-r--r--modules/commands/bs_assign.cpp8
-rw-r--r--modules/commands/bs_badwords.cpp12
-rw-r--r--modules/commands/bs_control.cpp4
-rw-r--r--modules/commands/bs_info.cpp2
-rw-r--r--modules/commands/bs_kick.cpp8
-rw-r--r--modules/commands/bs_set.cpp4
-rw-r--r--modules/commands/cs_access.cpp213
-rw-r--r--modules/commands/cs_akick.cpp18
-rw-r--r--modules/commands/cs_appendtopic.cpp4
-rw-r--r--modules/commands/cs_ban.cpp4
-rw-r--r--modules/commands/cs_clearusers.cpp2
-rw-r--r--modules/commands/cs_clone.cpp2
-rw-r--r--modules/commands/cs_drop.cpp4
-rw-r--r--modules/commands/cs_enforce.cpp2
-rw-r--r--modules/commands/cs_flags.cpp111
-rw-r--r--modules/commands/cs_getkey.cpp4
-rw-r--r--modules/commands/cs_info.cpp2
-rw-r--r--modules/commands/cs_invite.cpp2
-rw-r--r--modules/commands/cs_kick.cpp4
-rw-r--r--modules/commands/cs_mode.cpp6
-rw-r--r--modules/commands/cs_modes.cpp24
-rw-r--r--modules/commands/cs_saset_noexpire.cpp2
-rw-r--r--modules/commands/cs_set_bantype.cpp2
-rw-r--r--modules/commands/cs_set_description.cpp2
-rw-r--r--modules/commands/cs_set_founder.cpp4
-rw-r--r--modules/commands/cs_set_keeptopic.cpp2
-rw-r--r--modules/commands/cs_set_misc.cpp2
-rw-r--r--modules/commands/cs_set_peace.cpp2
-rw-r--r--modules/commands/cs_set_persist.cpp2
-rw-r--r--modules/commands/cs_set_private.cpp2
-rw-r--r--modules/commands/cs_set_restricted.cpp2
-rw-r--r--modules/commands/cs_set_secure.cpp2
-rw-r--r--modules/commands/cs_set_securefounder.cpp2
-rw-r--r--modules/commands/cs_set_secureops.cpp2
-rw-r--r--modules/commands/cs_set_signkick.cpp2
-rw-r--r--modules/commands/cs_set_successor.cpp4
-rw-r--r--modules/commands/cs_set_topiclock.cpp2
-rw-r--r--modules/commands/cs_tban.cpp2
-rw-r--r--modules/commands/cs_topic.cpp4
-rw-r--r--modules/commands/cs_unban.cpp2
-rw-r--r--modules/commands/cs_xop.cpp94
-rw-r--r--modules/commands/ms_del.cpp2
-rw-r--r--modules/commands/ms_ignore.cpp2
-rw-r--r--modules/commands/ms_info.cpp2
-rw-r--r--modules/commands/ms_list.cpp2
-rw-r--r--modules/commands/ms_read.cpp2
-rw-r--r--modules/commands/ms_set.cpp2
-rw-r--r--modules/commands/ns_ajoin.cpp4
-rw-r--r--modules/database/db_mysql.cpp42
-rw-r--r--modules/database/db_plain.cpp65
-rw-r--r--modules/extra/m_helpchan.cpp2
-rw-r--r--modules/extra/m_statusupdate.cpp18
-rw-r--r--modules/pseudoclients/botserv.cpp4
-rw-r--r--modules/pseudoclients/memoserv.cpp4
-rw-r--r--src/access.cpp141
-rw-r--r--src/botserv.cpp4
-rw-r--r--src/channels.cpp18
-rw-r--r--src/init.cpp1
-rw-r--r--src/regchannel.cpp28
67 files changed, 530 insertions, 520 deletions
diff --git a/data/chanserv.example.conf b/data/chanserv.example.conf
index cc2bbaef6..d9bd6eb0c 100644
--- a/data/chanserv.example.conf
+++ b/data/chanserv.example.conf
@@ -184,8 +184,8 @@ chanserv
* The level "disabled" is a special level that means the level is disabled, even from people
* with founder level access.
*/
- level_change = 10
- level_list = 1
+ level_access_change = 10
+ level_access_list = 1
level_akick = 10
level_assign = "founder"
level_autohalfop = 4
@@ -226,8 +226,8 @@ chanserv
*
* These flags are used by the chanserv/flags access system.
*/
- flag_change = "f"
- flag_list = "l"
+ flag_access_change = "f"
+ flag_access_list = "l"
flag_akick = "K"
flag_assign = "s"
flag_autohalfop = "H"
diff --git a/data/tables.sql b/data/tables.sql
index 69e694a32..ec99b7479 100644
--- a/data/tables.sql
+++ b/data/tables.sql
@@ -140,9 +140,9 @@ CREATE TABLE IF NOT EXISTS `anope_cs_info_metadata` (
CREATE TABLE IF NOT EXISTS `anope_cs_levels` (
`channel` varchar(255) NOT NULL DEFAULT '',
- `position` int(11) NOT NULL DEFAULT '0',
+ `name` varchar(255) NOT NULL DEFAULT '',
`level` int(11) NOT NULL DEFAULT '0',
- UNIQUE KEY `channel` (`channel`,`position`)
+ UNIQUE KEY `channel` (`channel`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
diff --git a/include/access.h b/include/access.h
index 0c730c9ff..6557d40c6 100644
--- a/include/access.h
+++ b/include/access.h
@@ -1,45 +1,24 @@
#ifndef ACCESS_H
#define ACCESS_H
-enum ChannelAccess
+struct Privilege
{
- 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
+ Anope::string name;
+ Anope::string desc;
+
+ Privilege(const Anope::string &n, const Anope::string &d);
+ bool operator==(const Privilege &other);
+};
+
+class PrivilegeManager
+{
+ static std::vector<Privilege> privs;
+ public:
+ static void AddPrivilege(Privilege p, int pos = -1, int def = 0);
+ static void RemovePrivilege(Privilege &p);
+ static Privilege *FindPrivilege(const Anope::string &name);
+ static void Init();
+ static std::vector<Privilege> &GetPrivileges();
};
class ChanAccess;
@@ -65,7 +44,7 @@ class CoreExport ChanAccess
ChanAccess(AccessProvider *p);
virtual ~ChanAccess();
virtual bool Matches(User *u, NickCore *nc) = 0;
- virtual bool HasPriv(ChannelAccess priv) = 0;
+ virtual bool HasPriv(const Anope::string &name) = 0;
virtual Anope::string Serialize() = 0;
virtual void Unserialize(const Anope::string &data) = 0;
@@ -82,7 +61,7 @@ class CoreExport AccessGroup : public std::vector<ChanAccess *>
NickCore *nc;
bool SuperAdmin, Founder;
AccessGroup();
- bool HasPriv(ChannelAccess priv) const;
+ bool HasPriv(const Anope::string &priv) const;
ChanAccess *Highest() const;
bool operator>(const AccessGroup &other) const;
bool operator<(const AccessGroup &other) const;
diff --git a/include/anope.h b/include/anope.h
index 5eeee0a04..3eec3d4e4 100644
--- a/include/anope.h
+++ b/include/anope.h
@@ -259,6 +259,28 @@ namespace Anope
}
/**
+ * Get the string in lowercase.
+ */
+ inline string lower()
+ {
+ Anope::string new_string = *this;
+ for (size_type i = 0; i < new_string.length(); ++i)
+ new_string[i] = tolower(new_string[i]);
+ return new_string;
+ }
+
+ /**
+ * Get the string in uppercase.
+ */
+ inline string upper()
+ {
+ Anope::string new_string = *this;
+ for (size_type i = 0; i < new_string.length(); ++i)
+ new_string[i] = toupper(new_string[i]);
+ return new_string;
+ }
+
+ /**
* Get a substring of the string.
*/
inline string substr(size_type pos = 0, size_type n = npos) const { return string(this->_string.substr(pos, n)); }
diff --git a/include/extern.h b/include/extern.h
index b62997aad..4b15d81ac 100644
--- a/include/extern.h
+++ b/include/extern.h
@@ -152,8 +152,6 @@ E bool OnError(const Anope::string &, const std::vector<Anope::string> &);
/**** misc.c ****/
E bool IsFile(const Anope::string &filename);
-E int toupper(char);
-E int tolower(char);
E time_t dotime(const Anope::string &s);
E Anope::string duration(const time_t &seconds, NickCore *nc = NULL);
diff --git a/include/modules.h b/include/modules.h
index a34b7f64d..f76a1f58a 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -662,10 +662,10 @@ class CoreExport Module : public Extensible
/** Called when a level for a channel is changed
* @param u The user changing the level
* @param ci The channel the level was changed on
- * @param pos The level position, can be -1 for resetting levels
+ * @param priv The privilege changed
* @param what The new level
*/
- virtual void OnLevelChange(User *u, ChannelInfo *ci, int pos, int what) { }
+ virtual void OnLevelChange(User *u, ChannelInfo *ci, const Anope::string &priv, int16 what) { }
/** Called when a channel is dropped
* @param chname The channel name
@@ -751,14 +751,14 @@ class CoreExport Module : public Extensible
* @param priv The privilege being checked for
* @return EVENT_ALLOW for yes, EVENT_STOP to stop all processing
*/
- virtual EventReturn OnCheckPriv(ChanAccess *access, ChannelAccess priv) { return EVENT_CONTINUE; }
+ virtual EventReturn OnCheckPriv(ChanAccess *access, const Anope::string &priv) { return EVENT_CONTINUE; }
/** Check whether an access group has a privilege
* @param group The group
* @param priv The privilege
* @return MOD_ALLOW to allow, MOD_STOP to stop
*/
- virtual EventReturn OnGroupCheckPriv(const AccessGroup *group, ChannelAccess priv) { return EVENT_CONTINUE; }
+ virtual EventReturn OnGroupCheckPriv(const AccessGroup *group, const Anope::string &priv) { return EVENT_CONTINUE; }
/** Called when a nick is dropped
* @param u The user dropping the nick
diff --git a/include/regchannel.h b/include/regchannel.h
index 71136a73b..1d071fb0f 100644
--- a/include/regchannel.h
+++ b/include/regchannel.h
@@ -103,6 +103,7 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag,
std::vector<ChanAccess *> access; /* List of authorized users */
std::vector<AutoKick *> akick; /* List of users to kickban */
std::vector<BadWord *> badwords; /* List of badwords */
+ std::map<Anope::string, int16> levels;
public:
typedef std::multimap<ChannelModeName, ModeLock> ModeList;
@@ -134,7 +135,6 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag,
time_t last_topic_time; /* Time */
int16 bantype;
- int16 levels[CA_SIZE];
MemoInfo memos;
@@ -345,6 +345,28 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag,
* and after uplink sync
*/
void RestoreTopic();
+
+ /** Get the level for a privilege
+ * @param priv The privilege name
+ * @return the level
+ * @throws CoreException if priv is not a valid privilege
+ */
+ int16 GetLevel(const Anope::string &priv);
+
+ /** Set the level for a privilege
+ * @param priv The privilege priv
+ * @param level The new level
+ */
+ void SetLevel(const Anope::string &priv, int16 level);
+
+ /** Remove a privilege from the channel
+ * @param priv The privilege
+ */
+ void RemoveLevel(const Anope::string &priv);
+
+ /** Clear all privileges from the channel
+ */
+ void ClearLevels();
};
/** A timer used to keep the BotServ bot/ChanServ in the channel
diff --git a/include/services.h b/include/services.h
index d6db4e9ed..3a7ff65c2 100644
--- a/include/services.h
+++ b/include/services.h
@@ -98,6 +98,8 @@ extern int strncasecmp(const char *, const char *, size_t);
#undef toupper
#define tolower tolower_
#define toupper toupper_
+extern int toupper(char);
+extern int tolower(char);
/** This definition is used as shorthand for the various classes
* and functions needed to make a module loadable by the OS.
diff --git a/modules/commands/bs_assign.cpp b/modules/commands/bs_assign.cpp
index 13f3e4403..64ae9064d 100644
--- a/modules/commands/bs_assign.cpp
+++ b/modules/commands/bs_assign.cpp
@@ -49,7 +49,7 @@ class CommandBSAssign : public Command
return;
}
- if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->AccessFor(u).HasPriv(CA_ASSIGN) && !u->HasPriv("botserv/administration")))
+ if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->AccessFor(u).HasPriv("ASSIGN") && !u->HasPriv("botserv/administration")))
{
source.Reply(ACCESS_DENIED);
return;
@@ -67,7 +67,7 @@ class CommandBSAssign : public Command
return;
}
- bool override = !ci->AccessFor(u).HasPriv(CA_ASSIGN);
+ bool override = !ci->AccessFor(u).HasPriv("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") && !ci->AccessFor(u).HasPriv(CA_ASSIGN))
+ if (!u->HasPriv("botserv/administration") && !ci->AccessFor(u).HasPriv("ASSIGN"))
{
source.Reply(ACCESS_DENIED);
return;
@@ -129,7 +129,7 @@ class CommandBSUnassign : public Command
return;
}
- bool override = !ci->AccessFor(u).HasPriv(CA_ASSIGN);
+ bool override = !ci->AccessFor(u).HasPriv("ASSIGN");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << ci->bi->nick;
ci->bi->UnAssign(u, ci);
diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp
index 15298d5eb..db3843cf4 100644
--- a/modules/commands/bs_badwords.cpp
+++ b/modules/commands/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 (!ci->AccessFor(source.u).HasPriv(CA_BADWORDS) && source.u->HasPriv("botserv/administration"))
+ if (!ci->AccessFor(source.u).HasPriv("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 = !ci->AccessFor(source.u).HasPriv(CA_BADWORDS);
+ bool override = !ci->AccessFor(source.u).HasPriv("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 = !ci->AccessFor(source.u).HasPriv(CA_BADWORDS);
+ bool override = !ci->AccessFor(source.u).HasPriv("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 = !ci->AccessFor(source.u).HasPriv(CA_BADWORDS);
+ bool override = !ci->AccessFor(source.u).HasPriv("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 = !ci->AccessFor(source.u).HasPriv(CA_BADWORDS);
+ bool override = !ci->AccessFor(source.u).HasPriv("BADWORDS");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "CLEAR";
ci->ClearBadWords();
@@ -254,7 +254,7 @@ class CommandBSBadwords : public Command
}
- if (!ci->AccessFor(u).HasPriv(CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration")))
+ if (!ci->AccessFor(u).HasPriv("BADWORDS") && (!need_args || !u->HasPriv("botserv/administration")))
{
source.Reply(ACCESS_DENIED);
return;
diff --git a/modules/commands/bs_control.cpp b/modules/commands/bs_control.cpp
index b464a4520..164542daf 100644
--- a/modules/commands/bs_control.cpp
+++ b/modules/commands/bs_control.cpp
@@ -35,7 +35,7 @@ class CommandBSSay : public Command
return;
}
- if (!ci->AccessFor(u).HasPriv(CA_SAY))
+ if (!ci->AccessFor(u).HasPriv("SAY"))
{
source.Reply(ACCESS_DENIED);
return;
@@ -98,7 +98,7 @@ class CommandBSAct : public Command
return;
}
- if (!ci->AccessFor(u).HasPriv(CA_SAY))
+ if (!ci->AccessFor(u).HasPriv("SAY"))
{
source.Reply(ACCESS_DENIED);
return;
diff --git a/modules/commands/bs_info.cpp b/modules/commands/bs_info.cpp
index d6668184d..0dbec9e29 100644
--- a/modules/commands/bs_info.cpp
+++ b/modules/commands/bs_info.cpp
@@ -70,7 +70,7 @@ class CommandBSInfo : public Command
}
else if ((ci = cs_findchan(query)))
{
- if (!ci->AccessFor(u).HasPriv(CA_FOUNDER) && !u->HasPriv("botserv/administration"))
+ if (!ci->AccessFor(u).HasPriv("FOUNDER") && !u->HasPriv("botserv/administration"))
{
source.Reply(ACCESS_DENIED);
return;
diff --git a/modules/commands/bs_kick.cpp b/modules/commands/bs_kick.cpp
index 107e78d64..74ccaf05c 100644
--- a/modules/commands/bs_kick.cpp
+++ b/modules/commands/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 (!ci->AccessFor(u).HasPriv(CA_SET) && !u->HasPriv("botserv/administration"))
+ else if (!ci->AccessFor(u).HasPriv("SET") && !u->HasPriv("botserv/administration"))
source.Reply(ACCESS_DENIED);
else if (!ci->bi)
source.Reply(BOT_NOT_ASSIGNED);
else
{
- bool override = !ci->AccessFor(u).HasPriv(CA_SET);
+ bool override = !ci->AccessFor(u).HasPriv("SET");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << option << " " << value;
if (option.equals_ci("BADWORDS"))
@@ -767,7 +767,7 @@ class BSKick : public Module
return;
bool Allow = true;
- if (ci->AccessFor(u).HasPriv(CA_NOKICK))
+ if (ci->AccessFor(u).HasPriv("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;
@@ -992,7 +992,7 @@ class BSKick : public Module
Channel *chan = (*it)->chan;
++it;
- if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->AccessFor(u).HasPriv(CA_NOKICK))
+ if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->AccessFor(u).HasPriv("NOKICK"))
{
check_ban(chan->ci, u, TTB_AMSGS);
bot_kick(chan->ci, u, _("Don't use AMSGs!"));
diff --git a/modules/commands/bs_set.cpp b/modules/commands/bs_set.cpp
index be699c3e8..b38c05d60 100644
--- a/modules/commands/bs_set.cpp
+++ b/modules/commands/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") && !ci->AccessFor(u).HasPriv(CA_SET))
+ else if (!u->HasPriv("botserv/administration") && !ci->AccessFor(u).HasPriv("SET"))
source.Reply(ACCESS_DENIED);
else
{
- bool override = !ci->AccessFor(u).HasPriv(CA_SET);
+ bool override = !ci->AccessFor(u).HasPriv("SET");
Log(override ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << option << " " << value;
if (option.equals_ci("DONTKICKOPS"))
diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp
index 2dd7da51c..8c3e1a1ca 100644
--- a/modules/commands/cs_access.cpp
+++ b/modules/commands/cs_access.cpp
@@ -19,57 +19,13 @@ enum
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") },
- {