added revoke_mode_a setting
[srvx.git] / src / chanserv.c
index 0b1eb400924602de690951a828110365a6ae77df..e06cf468053de91c6afa5030f2f17ffed0203299 100644 (file)
@@ -22,7 +22,7 @@
 #include "conf.h"
 #include "global.h"
 #include "modcmd.h"
-#include "opserv.h" /* for opserv_bad_channel() */
+#include "opserv.h" /* for opserv_bad_channel() and devnull management */
 #include "nickserv.h" /* for oper_outranks() */
 #include "saxdb.h"
 #include "spamserv.h"
 #define KEY_MAX_USERINFO_LENGTH     "max_userinfo_length"
 #define KEY_GIVEOWNERSHIP_PERIOD    "giveownership_timeout"
 #define KEY_INVITED_INTERVAL           "invite_timeout"
+#define KEY_REVOKE_MODE_A           "revoke_mode_a"
+#define KEY_NEW_CHANNEL_AUTHED      "new_channel_authed_join"
+#define KEY_NEW_CHANNEL_UNAUTHED    "new_channel_unauthed_join"
+#define KEY_NEW_CHANNEL_MSG         "new_channel_message"
 
 /* ChanServ database */
 #define KEY_CHANNELS                "channels"
@@ -206,7 +210,7 @@ static const struct message_entry msgtab[] = {
 /* User management */
     { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
     { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
-    { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
+    { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
     { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
     { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
     { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
@@ -342,6 +346,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
     { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
     { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
+    { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
 
 /* Channel note list */
     { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
@@ -384,8 +389,9 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_UC_H_TITLE", "network helper" },
     { "CSMSG_LC_H_TITLE", "support helper" },
     { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
-    { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels." },
-    { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel." },
+    { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
+       { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
+
 
 /* Seen information */
     { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
@@ -563,6 +569,8 @@ static struct
     unsigned int    max_chan_users;
     unsigned int    max_chan_bans;
     unsigned int    max_userinfo_length;
+    
+    unsigned int    revoke_mode_a;
 
     struct string_list  *set_shows;
     struct string_list  *eightball;
@@ -574,6 +582,10 @@ static struct
     const char          *irc_operator_epithet;
     const char          *network_helper_epithet;
     const char          *support_helper_epithet;
+
+    const char          *new_channel_authed;
+    const char          *new_channel_unauthed;
+    const char          *new_channel_msg;
 } chanserv_conf;
 
 struct listData
@@ -1429,11 +1441,13 @@ unregister_channel(struct chanData *channel, const char *reason)
 
     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
 
-    if(off_channel > 0)
-    {
-      mod_chanmode_init(&change);
-      change.modes_clear |= MODE_REGISTERED;
-      mod_chanmode_announce(chanserv, channel->channel, &change);
+    if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
+        mod_chanmode_init(&change);
+        if(off_channel > 0)
+            change.modes_clear |= MODE_REGISTERED;
+        if(chanserv_conf.revoke_mode_a)
+            change.modes_clear |= MODE_ACCESS;
+        mod_chanmode_announce(chanserv, channel->channel, &change);
     }
 
     while(channel->users)
@@ -1489,7 +1503,7 @@ unregister_channel(struct chanData *channel, const char *reason)
 }
 
 static void
-expire_channels(UNUSED_ARG(void *data))
+expire_channels(void *data)
 {
     struct chanData *channel, *next;
     struct userData *user;
@@ -1520,7 +1534,7 @@ expire_channels(UNUSED_ARG(void *data))
         unregister_channel(channel, "registration expired.");
     }
 
-    if(chanserv_conf.channel_expire_frequency)
+    if(chanserv_conf.channel_expire_frequency && !data)
         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 }
 
@@ -2276,7 +2290,7 @@ static CHANSERV_FUNC(cmd_unregister)
         return 0;
     }
 
-    if(IsProtected(cData))
+    if(IsProtected(cData) && !IsOper(user))
     {
         reply("CSMSG_UNREG_NODELETE", channel->name);
         return 0;
@@ -2981,7 +2995,7 @@ cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short m
     unsigned int count;
     unsigned long limit;
 
-    actor = GetChannelAccess(channel->channel_info, user->handle_info);
+    actor = GetChannelUser(channel->channel_info, user->handle_info);
     if(min_access > max_access)
     {
         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
@@ -3227,6 +3241,44 @@ static CHANSERV_FUNC(cmd_devoice)
     return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
 }
 
+static CHANSERV_FUNC(cmd_opme)
+{
+    struct mod_chanmode change;
+    const char *errmsg;
+
+    mod_chanmode_init(&change);
+    change.argc = 1;
+    change.args[0].u.member = GetUserMode(channel, user);
+    if(!change.args[0].u.member)
+    {
+        if(argc)
+            reply("MSG_CHANNEL_ABSENT", channel->name);
+        return 0;
+    }
+
+    struct devnull_class *devnull;
+    if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
+    {
+        change.args[0].mode = MODE_CHANOP;
+        errmsg = "CSMSG_ALREADY_OPPED";
+    }
+    else
+    {
+        if(argc)
+            reply("CSMSG_NO_ACCESS");
+        return 0;
+    }
+    change.args[0].mode &= ~change.args[0].u.member->modes;
+    if(!change.args[0].mode)
+    {
+        if(argc)
+            reply(errmsg, channel->name);
+        return 0;
+    }
+    modcmd_chanmode_announce(&change);
+    return 1;
+}
+
 static int
 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
 {
@@ -3264,6 +3316,20 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
 
     offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
     REQUIRE_PARAMS(offset);
+    if(argc > offset && IsNetServ(user))
+    {
+        if(*argv[offset] == '$') {
+            struct userNode *hib;
+            const char *accountnameb = argv[offset] + 1;
+            if(!(hib = GetUserH(accountnameb)))
+            {
+                reply("MSG_HANDLE_UNKNOWN", accountnameb);
+                return 0;
+            }
+            user=hib;
+            offset++;
+        }
+    }
     if(argc > offset)
     {
         reason = unsplit_string(argv + offset, argc - offset, NULL);
@@ -3600,7 +3666,7 @@ static CHANSERV_FUNC(cmd_addtimedban)
     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
 }
 
-static struct mod_chanmode *
+struct mod_chanmode *
 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
 {
     struct mod_chanmode *change;
@@ -3798,6 +3864,7 @@ static CHANSERV_FUNC(cmd_myaccess)
     struct handle_info *target_handle;
     struct userData *uData;
     int ccount = 0;
+       int ocount = 0;
 
     if(argc < 2)
         target_handle = user->handle_info;
@@ -3823,17 +3890,21 @@ static CHANSERV_FUNC(cmd_myaccess)
     {
         struct chanData *cData = uData->channel;
         ccount++;
+        unsigned int base_len;
 
         if(uData->access > UL_OWNER)
             continue;
+        if(uData->access == UL_OWNER)
+            ocount++;
+
         if(IsProtected(cData)
            && (target_handle != user->handle_info)
-           && !GetTrueChannelAccess(cData, user->handle_info))
+           && !GetTrueChannelAccess(cData, user->handle_info)
+           && !IsNetworkHelper(user))
             continue;
         sbuf.used = 0;
-        string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
-        if(uData->flags != USER_AUTO_OP)
-            string_buffer_append(&sbuf, ',');
+        string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
+        base_len = sbuf.used;
         if(IsUserSuspended(uData))
             string_buffer_append(&sbuf, 's');
         if(IsUserAutoOp(uData))
@@ -3845,6 +3916,8 @@ static CHANSERV_FUNC(cmd_myaccess)
         }
         if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
             string_buffer_append(&sbuf, 'i');
+        if(sbuf.used==base_len)
+            sbuf.used--;
         if(uData->info)
             string_buffer_append_printf(&sbuf, ")] %s", uData->info);
         else
@@ -3854,9 +3927,9 @@ static CHANSERV_FUNC(cmd_myaccess)
     }
 
     if(ccount == 1) {
-        reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount);
+        reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
     } else {
-        reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount);
+        reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
     }
 
     return 1;
@@ -3984,56 +4057,6 @@ static CHANSERV_FUNC(cmd_access)
     return 1;
 }
 
-static void
-zoot_list(struct listData *list)
-{
-    struct userData *uData;
-    unsigned int start, curr, highest, lowest;
-    struct helpfile_table tmp_table;
-    const char **temp, *msg;
-
-    if(list->table.length == 1)
-    {
-        if(list->search)
-            send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
-        else
-            send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
-        msg = user_find_message(list->user, "MSG_NONE");
-        send_message_type(4, list->user, list->bot, "  %s", msg);
-    }
-    tmp_table.width = list->table.width;
-    tmp_table.flags = list->table.flags;
-    list->table.contents[0][0] = " ";
-    highest = list->highest;
-    if(list->lowest != 0)
-        lowest = list->lowest;
-    else if(highest < 100)
-        lowest = 1;
-    else
-        lowest = highest - 100;
-    for(start = curr = 1; curr < list->table.length; )
-    {
-        uData = list->users[curr-1];
-        list->table.contents[curr++][0] = " ";
-        if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
-        {
-            if(list->search)
-                send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
-            else
-                send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
-            temp = list->table.contents[--start];
-            list->table.contents[start] = list->table.contents[0];
-            tmp_table.contents = list->table.contents + start;
-            tmp_table.length = curr - start;
-            table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
-            list->table.contents[start] = temp;
-            start = curr;
-            highest = lowest - 1;
-            lowest = (highest < 100) ? 0 : (highest - 99);
-        }
-    }
-}
-
 static void
 def_list(struct listData *list)
 {
@@ -4079,7 +4102,6 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     lData.highest = highest;
     lData.search = (argc > 1) ? argv[1] : NULL;
     send_list = def_list;
-    (void)zoot_list; /* since it doesn't show user levels */
 
     if(user->handle_info)
     {
@@ -4145,6 +4167,7 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     }
     free(lData.table.contents[0]);
     free(lData.table.contents);
+    reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
     return 1;
 }
 
@@ -4399,7 +4422,7 @@ static CHANSERV_FUNC(cmd_mode)
         base_oplevel = 1;
     else
         base_oplevel = 1 + UL_OWNER - uData->access;
-    change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
+    change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
     if(!change)
     {
         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
@@ -4440,13 +4463,10 @@ chanserv_del_invite_mark(void *data)
 
 static CHANSERV_FUNC(cmd_invite)
 {
-    struct userData *uData;
     struct userNode *invite;
     struct ChanUser *chanuser;
     unsigned int i;
 
-    uData = GetChannelUser(channel->channel_info, user->handle_info);
-
     if(argc > 1)
     {
         if(!(invite = GetUserH(argv[1])))
@@ -4695,6 +4715,8 @@ send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
             continue;
         if(IsBot(user))
             continue;
+               if(IsInvi(user))
+                   continue;
         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
         if(IsAway(user))
         {
@@ -5194,7 +5216,7 @@ chanserv_support_channels(void)
 static CHANSERV_FUNC(cmd_expire)
 {
     int channel_count = registered_channels;
-    expire_channels(NULL);
+    expire_channels(chanserv);
     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
     return 1;
 }
@@ -5653,7 +5675,7 @@ static MODCMD_FUNC(chan_opt_modes)
         {
             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
         }
-        else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, 0)))
+        else if(!(new_modes = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS|(IsOper(user) && IsHelping(user) ? MCP_OPERMODE : 0), 0)))
         {
             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
             return 0;
@@ -7034,6 +7056,16 @@ handle_new_channel(struct chanNode *channel)
         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
 }
 
+void handle_new_channel_created(char *chan, struct userNode *user) {
+    if(user->handle_info && chanserv_conf.new_channel_authed) {
+        send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
+    } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
+        send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
+    }
+    if(chanserv_conf.new_channel_msg)
+        send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
+}
+
 /* Welcome to my worst nightmare. Warning: Read (or modify)
    the code below at your own risk. */
 static int
@@ -7632,6 +7664,8 @@ chanserv_conf_read(void)
     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
+    str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
+    chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
@@ -7659,12 +7693,18 @@ chanserv_conf_read(void)
     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
+    str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
+    chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
+    str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
+    chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
+    str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
+    chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
     if(!str)
         str = "+nt";
     safestrncpy(mode_line, str, sizeof(mode_line));
     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
-    if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
+    if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
        && (change->argc < 2))
     {
         chanserv_conf.default_modes = *change;
@@ -7850,7 +7890,6 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
 static void
 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
 {
-    struct banData *bData;
     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
     unsigned long set_time, triggered_time, expires_time;
 
@@ -7881,7 +7920,7 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
     if(!reason || (expires_time && (expires_time < now)))
         return;
 
-    bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
+    add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
 }
 
 static struct suspended *
@@ -8091,7 +8130,7 @@ chanserv_channel_read(const char *key, struct record_data *hir)
     if(!IsSuspended(cData)
        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
        && (argc = split_line(str, 0, ArrayLength(argv), argv))
-       && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
+       && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
         cData->modes = *modes;
         if(off_channel > 0)
           cData->modes.modes_set |= MODE_REGISTERED;
@@ -8603,19 +8642,21 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
-    DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
-    DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     
-    DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
-    DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
-
+    DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+    DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
+
+    DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
+    
     /* Channel options */
     DEFINE_CHANNEL_OPTION(defaulttopic);
     DEFINE_CHANNEL_OPTION(topicmask);