fixed small memory leak (free old devnull names when setting new ones)
[srvx.git] / src / chanserv.c
index 900bf854fb812503736b65de34e9daaa527dd1d0..75122799b0d28a6e729cea46cbe3d51f9eb3652a 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"
@@ -58,6 +58,9 @@
 #define KEY_MAX_USERINFO_LENGTH     "max_userinfo_length"
 #define KEY_GIVEOWNERSHIP_PERIOD    "giveownership_timeout"
 #define KEY_INVITED_INTERVAL           "invite_timeout"
+#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 +209,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 +345,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,6 +388,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 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." },
@@ -572,6 +579,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
@@ -711,6 +722,7 @@ static unsigned int userCount;
 
 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
+static void unregister_channel(struct chanData *channel, const char *reason);
 
 unsigned short
 user_level_from_name(const char *name, unsigned short clamp_level)
@@ -1486,7 +1498,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;
@@ -1517,7 +1529,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);
 }
 
@@ -2273,7 +2285,7 @@ static CHANSERV_FUNC(cmd_unregister)
         return 0;
     }
 
-    if(IsProtected(cData))
+    if(IsProtected(cData) && !IsOper(user))
     {
         reply("CSMSG_UNREG_NODELETE", channel->name);
         return 0;
@@ -2978,7 +2990,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);
@@ -3224,6 +3236,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)
 {
@@ -3261,6 +3311,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);
@@ -3597,7 +3661,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;
@@ -3794,6 +3858,8 @@ static CHANSERV_FUNC(cmd_myaccess)
     static struct string_buffer sbuf;
     struct handle_info *target_handle;
     struct userData *uData;
+    int ccount = 0;
+       int ocount = 0;
 
     if(argc < 2)
         target_handle = user->handle_info;
@@ -3818,17 +3884,22 @@ static CHANSERV_FUNC(cmd_myaccess)
     for(uData = target_handle->channels; uData; uData = uData->u_next)
     {
         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))
@@ -3840,6 +3911,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
@@ -3848,6 +3921,12 @@ static CHANSERV_FUNC(cmd_myaccess)
         send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
     }
 
+    if(ccount == 1) {
+        reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
+    } else {
+        reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
+    }
+
     return 1;
 }
 
@@ -3973,56 +4052,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)
 {
@@ -4068,7 +4097,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)
     {
@@ -4134,6 +4162,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;
 }
 
@@ -4388,7 +4417,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_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));
@@ -4429,13 +4458,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])))
@@ -4501,6 +4527,28 @@ static CHANSERV_FUNC(cmd_inviteme)
     return 1;
 }
 
+static CHANSERV_FUNC(cmd_invitemeall)
+{
+    struct handle_info *target = user->handle_info;
+    struct userData *uData;
+
+    if(!target->channels)
+    {
+        reply("CSMSG_SQUAT_ACCESS", target->handle);
+        return 1;
+    }
+       
+    for(uData = target->channels; uData; uData = uData->u_next)
+    {
+        struct chanData *cData = uData->channel;
+        if(uData->access >= cData->lvlOpts[lvlInviteMe])
+               {
+            irc_invite(cmd->parent->bot, user, cData->channel);
+        }
+    }
+    return 1;
+}
+
 static void
 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
 {
@@ -4662,6 +4710,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))
         {
@@ -4797,8 +4847,16 @@ static CHANSERV_FUNC(cmd_resync)
         {
             if(!(mn->modes & MODE_CHANOP))
             {
-                changes->args[used].mode = MODE_CHANOP;
-                changes->args[used++].u.member = mn;
+                if(!uData || IsUserAutoOp(uData)) 
+                {
+                    changes->args[used].mode = MODE_CHANOP;
+                    changes->args[used++].u.member = mn;
+                    if(!(mn->modes & MODE_VOICE))
+                    {
+                        changes->args[used].mode = MODE_VOICE;
+                        changes->args[used++].u.member = mn;
+                    }
+                }
             }
         }
         else if(!cData->lvlOpts[lvlGiveVoice]
@@ -4809,7 +4867,7 @@ static CHANSERV_FUNC(cmd_resync)
                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
                 changes->args[used++].u.member = mn;
             }
-            if(!(mn->modes & MODE_VOICE))
+            if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
             {
                 changes->args[used].mode = MODE_VOICE;
                 changes->args[used++].u.member = mn;
@@ -5153,7 +5211,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;
 }
@@ -5612,7 +5670,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_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;
@@ -6611,7 +6669,7 @@ static CHANSERV_FUNC(cmd_vote)
     struct userData *target;
     struct handle_info *hi;
     unsigned int votedfor = 0;
-    char *votedfor_str;
+    char *votedfor_str = NULL;
     
     if (!cData || !cData->vote) {
         reply("CSMSG_NO_VOTE");
@@ -6716,6 +6774,7 @@ static CHANSERV_FUNC(cmd_startvote)
     irc_privmsg(cmd->parent->bot, channel->name, response);
     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
     irc_privmsg(cmd->parent->bot, channel->name, response);
+    return 1;
 }
 
 static CHANSERV_FUNC(cmd_endvote)
@@ -6992,6 +7051,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
@@ -7006,6 +7075,7 @@ handle_join(struct modeNode *mNode)
     struct handle_info *handle;
     unsigned int modes = 0, info = 0;
     char *greeting;
+    unsigned int i = 0;
 
     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
         return 0;
@@ -7616,12 +7686,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;
@@ -7807,7 +7883,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;
 
@@ -7838,7 +7913,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 *
@@ -8048,7 +8123,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;
@@ -8516,6 +8591,7 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
+    DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
@@ -8559,19 +8635,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);