]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/chanserv.c
fbsd 7 warning fixes, this needs further testing *hint hint*
[irc/evilnet/x3.git] / src / chanserv.c
index 74f98985fe7230a6b5c43f5a414b005141cb3dfc..b39dd9b90cfb69ed069e13d78a9a092b1e79f2d4 100644 (file)
 #include "chanserv.h"
 #include "conf.h"
 #include "global.h"
+#include "gline.h"
+#include "ioset.h"
 #include "modcmd.h"
 #include "opserv.h" /* for opserv_bad_channel() */
 #include "saxdb.h"
+#include "shun.h"
 #include "spamserv.h"
 #include "timeq.h"
 
@@ -39,7 +42,6 @@
 #define KEY_ADJUST_DELAY       "adjust_delay"
 #define KEY_CHAN_EXPIRE_FREQ   "chan_expire_freq"
 #define KEY_CHAN_EXPIRE_DELAY  "chan_expire_delay"
-#define KEY_DNR_EXPIRE_FREQ     "dnr_expire_freq"
 #define KEY_BAN_TIMEOUT_FREQ    "ban_timeout_freq"
 #define KEY_MAX_CHAN_USERS             "max_chan_users"
 #define KEY_MAX_CHAN_BANS      "max_chan_bans"
 #define KEY_NOTES               "notes"
 #define KEY_TOPIC_MASK          "topic_mask"
 #define KEY_OWNER_TRANSFER      "owner_transfer"
+#define KEY_MAXSETINFO          "maxsetinfo"
 
 /* User data */
 #define KEY_LEVEL              "level"
 
 #define KEY_GOD_TIMEOUT         "god_timeout"
 
-#define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
-#define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
+#define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL)
 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
 
 /* Administrative messages */
@@ -154,7 +156,6 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
     { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
     { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
-    { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
     { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
     { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
     { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
@@ -162,8 +163,6 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
     { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
     { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
-    { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
-    { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
 
 /* Channel unregistration */
     { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
@@ -200,6 +199,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
     { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
     { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
+    { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." },
 
 /* Removing yourself from a channel. */
     { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
@@ -231,7 +231,6 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
     { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
     { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
-    { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
     { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
     { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
 
@@ -280,7 +279,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
     { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
     { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
-    { "CSMSG_INVALID_NUMERIC",   "$b%d$b is not a valid choice.  Choose one:" },
+    { "CSMSG_INVALID_NUMERIC",   "$b%s$b is not a valid choice.  Choose one:" },
     { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
     { "CSMSG_SET_TOPICMASK",     "$bTopicMask   $b %s" },
     { "CSMSG_SET_GREETING",      "$bGreeting    $b %s" },
@@ -303,9 +302,9 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_SET_TOYS",          "$bToys        $b %d - %s" },
     { "CSMSG_SET_CTCPREACTION",  "$bCTCPReaction$b %d - %s" },
     { "CSMSG_SET_TOPICREFRESH",  "$bTopicRefresh$b %d - %s" },
-    { "CSMSG_SET_UNREVIEWED",    "$bUnreviewed  $b %s" },
     { "CSMSG_SET_RESYNC",        "$bResync      $b %d - %s" },
     { "CSMSG_SET_BANTIMEOUT",    "$bBanTimeout  $b %d - %s" },
+    { "CSMSG_SET_MAXSETINFO",    "$bMaxSetInfo  $b %d - maximum characters in a setinfo line." },
 
     { "CSMSG_USET_AUTOOP",       "$bAutoOp      $b %s" },
     { "CSMSG_USET_AUTOVOICE",    "$bAutoVoice   $b %s" },
@@ -326,7 +325,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
 
     { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
-    { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
+    { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
     { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
     { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
     { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
@@ -432,9 +431,6 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
     { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
     { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
-    { "CSMSG_OPERATOR_TITLE", "IRC operator" },
-    { "CSMSG_UC_H_TITLE", "network helper" },
-    { "CSMSG_LC_H_TITLE", "support helper" },
     { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
 
 /* Seen information */
@@ -509,7 +505,6 @@ static const struct message_entry msgtab[] = {
 
 /* Channel configuration */
     { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
-    { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
     { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
     { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
     { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
@@ -529,6 +524,30 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
     { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
     { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
+    { "CSMSG_ROULETTE_LOADS",  "\001ACTION loads the gun and sets it on the table\001" },
+    { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
+    { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
+    { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
+    { "CSMSG_ROULETTE_CLICK", "Click" } ,
+
+    { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
+    { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
+    { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
+
+    { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
+    { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
+    { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
+    { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
+    { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
+    { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
+    { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
+    { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
+    { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
+    { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
+    { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
+    { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
+    { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
+    { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
 
 /* Other things */
     { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
@@ -582,7 +601,6 @@ static struct
 
     unsigned long      db_backup_frequency;
     unsigned long      channel_expire_frequency;
-    unsigned long      dnr_expire_frequency;
     unsigned long       ban_timeout_frequency;
 
     long               info_delay;
@@ -605,6 +623,7 @@ static struct
     struct string_list  *set_shows;
     struct string_list  *eightball;
     struct string_list  *old_ban_names;
+    struct string_list  *wheel;
 
     const char          *ctcp_short_ban_duration;
     const char          *ctcp_long_ban_duration;
@@ -640,6 +659,9 @@ enum note_visible_type
     NOTE_VIS_PRIVILEGED
 };
 
+struct io_fd *socket_io_fd;
+extern struct cManagerNode cManager;
+
 struct note_type
 {
     enum note_access_type set_access_type;
@@ -669,6 +691,7 @@ static const struct {
     unsigned short level;
     char ch;
 } accessLevels[] = { /* MUST be orderd less to most! */
+    { "pal", "Pal", UL_PEON, '+' },
     { "peon", "Peon", UL_PEON, '+' },
     { "halfop", "HalfOp", UL_HALFOP, '%' },
     { "op", "Op", UL_OP, '@' },
@@ -774,6 +797,28 @@ unsigned int chanserv_read_version = 0; /* db version control */
 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
 
+void sputsock(const char *text, ...) PRINTF_LIKE(1, 2);
+
+void
+sputsock(const char *text, ...)
+{
+    va_list arg_list;
+    char buffer[MAXLEN];
+    int pos;
+
+    if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
+    buffer[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
+    buffer[pos] = 0;
+    log_replay(MAIN_LOG, true, buffer);
+    buffer[pos++] = '\n';
+    buffer[pos] = 0;
+    ioset_write(socket_io_fd, buffer, pos);
+}
+
 unsigned short
 user_level_from_name(const char *name, unsigned short clamp_level)
 {
@@ -796,7 +841,7 @@ user_level_name_from_level(int level)
 
     highest = "None";
     if(level >= 1)
-        highest = "Peon";
+        highest = "Pal";
     for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
         if(level >= accessLevels[ii].level)
             highest = accessLevels[ii].title;
@@ -930,7 +975,7 @@ scan_user_presence(struct userData *uData, struct userNode *user)
 }
 
 static void
-chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
+chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
 {
     unsigned int eflags, argc;
     char *argv[4];
@@ -953,7 +998,7 @@ chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char
            return;
     /* We need to enforce against them; do so. */
     eflags = 0;
-    argv[0] = (char*)text;
+    argv[0] = text;
     argv[1] = user->nick;
     argc = 2;
     if(GetUserMode(channel, user))
@@ -1765,38 +1810,6 @@ expire_channels(UNUSED_ARG(void *data))
        timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 }
 
-static void
-expire_dnrs(UNUSED_ARG(void *data))
-{
-    dict_iterator_t it;
-    struct do_not_register *dnr;
-
-    for(it = dict_first(handle_dnrs); it; it = iter_next(it))
-    {
-        dnr = iter_data(it);
-        if(!dnr->expires || dnr->expires > now)
-            continue;
-        dict_remove(handle_dnrs, dnr->chan_name + 1);
-    }
-    for(it = dict_first(plain_dnrs); it; it = iter_next(it))
-    {
-        dnr = iter_data(it);
-        if(!dnr->expires || dnr->expires > now)
-            continue;
-        dict_remove(plain_dnrs, dnr->chan_name);
-    }
-    for(it = dict_first(mask_dnrs); it; it = iter_next(it))
-    {
-        dnr = iter_data(it);
-        if(!dnr->expires || dnr->expires > now)
-            continue;
-        dict_remove(mask_dnrs, dnr->chan_name);
-    }
-
-    if(chanserv_conf.dnr_expire_frequency)
-        timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
-}
-
 static int
 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel, int protect_invitables)
 {
@@ -1934,14 +1947,13 @@ validate_dehop(struct svccmd *cmd, struct userNode *user, struct chanNode *chann
 }
 
 static struct do_not_register *
-chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
+chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
 {
     struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
     safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
     safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
     strcpy(dnr->reason, reason);
     dnr->set = now;
-    dnr->expires = expires;
     if(dnr->chan_name[0] == '*')
         dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
     else if(strpbrk(dnr->chan_name, "*?"))
@@ -1952,81 +1964,44 @@ chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, cons
 }
 
 static struct dnrList
-chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
+chanserv_find_dnrs(const char *chan_name, const char *handle)
 {
     struct dnrList list;
-    dict_iterator_t it, next;
+    dict_iterator_t it;
     struct do_not_register *dnr;
 
     dnrList_init(&list);
-
     if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
-    {
-        if(dnr->expires && dnr->expires <= now)
-            dict_remove(handle_dnrs, handle);
-        else if(list.used < max)
         dnrList_append(&list, dnr);
-    }
-
     if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
-    {
-        if(dnr->expires && dnr->expires <= now)
-            dict_remove(plain_dnrs, chan_name);
-        else if(list.used < max)
         dnrList_append(&list, dnr);
-    }
-
     if(chan_name)
-    {
-        for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
-        {
-            next = iter_next(it);
-            if(!match_ircglob(chan_name, iter_key(it)))
-                continue;
-            dnr = iter_data(it);
-            if(dnr->expires && dnr->expires <= now)
-                dict_remove(mask_dnrs, iter_key(it));
-            else
-                dnrList_append(&list, dnr);
-        }
-    }
-
+        for(it = dict_first(mask_dnrs); it; it = iter_next(it))
+            if(match_ircglob(chan_name, iter_key(it)))
+                dnrList_append(&list, iter_data(it));
     return list;
 }
 
-static int dnr_print_func(struct do_not_register *dnr, void *extra)
-{
-    struct userNode *user;
-    char buf1[INTERVALLEN];
-    char buf2[INTERVALLEN];
-
-    user = extra;
-    if(dnr->set)
-        strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
-    if(dnr->expires)
-    {
-        strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
-        send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
-    }
-    else if(dnr->set)
-    {
-        send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
-    }
-    else
-        send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
-    return 0;
-}
-
 static unsigned int
 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
 {
     struct dnrList list;
+    struct do_not_register *dnr;
     unsigned int ii;
+    char buf[INTERVALLEN];
 
-    list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
+    list = chanserv_find_dnrs(chan_name, handle);
     for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
-        dnr_print_func(list.list[ii], user);
-
+    {
+        dnr = list.list[ii];
+        if(dnr->set)
+        {
+            strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
+            reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
+        }
+        else
+            reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
+    }
     if(ii < list.used)
         reply("CSMSG_MORE_DNRS", list.used - ii);
     free(list.list);
@@ -2035,51 +2010,66 @@ chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_n
 
 struct do_not_register *
 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
-{
-    struct dnrList list;
-    struct do_not_register *dnr;
-
-    list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
-    dnr = list.used ? list.list[0] : NULL;
-    free(list.list);
-
-    return dnr;
-}
-
-static unsigned int send_dnrs(struct userNode *user, dict_t dict)
 {
     struct do_not_register *dnr;
-    dict_iterator_t it, next;
-    unsigned int matches = 0;
+    dict_iterator_t it;
 
-    for(it = dict_first(dict); it; it = next)
+    if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
+        return dnr;
+    if(chan_name)
     {
-        dnr = iter_data(it);
-        next = iter_next(it);
-        if(dnr->expires && dnr->expires <= now)
-        {
-            dict_remove(dict, iter_key(it));
-            continue;
-        }
-        dnr_print_func(dnr, user);
-        matches++;
+        if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
+            return dnr;
+        for(it = dict_first(mask_dnrs); it; it = iter_next(it))
+            if(match_ircglob(chan_name, iter_key(it)))
+                return iter_data(it);
     }
-
-    return matches;
+    return NULL;
 }
 
 static CHANSERV_FUNC(cmd_noregister)
 {
     const char *target;
-    time_t expiry, duration;
+    struct do_not_register *dnr;
+    char buf[INTERVALLEN];
     unsigned int matches;
 
     if(argc < 2)
     {
+        dict_iterator_t it;
+
         reply("CSMSG_DNR_SEARCH_RESULTS");
-        matches = send_dnrs(user, handle_dnrs);
-        matches += send_dnrs(user, plain_dnrs);
-        matches += send_dnrs(user, mask_dnrs);
+        if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+            reply("CSMSG_BAR");
+        matches = 0;
+        for(it = dict_first(handle_dnrs); it; it = iter_next(it))
+        {
+            dnr = iter_data(it);
+            if(dnr->set)
+                reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
+            else
+                reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
+            matches++;
+        }
+        for(it = dict_first(plain_dnrs); it; it = iter_next(it))
+        {
+            dnr = iter_data(it);
+            if(dnr->set)
+                reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
+            else
+                reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
+            matches++;
+        }
+        for(it = dict_first(mask_dnrs); it; it = iter_next(it))
+        {
+            dnr = iter_data(it);
+            if(dnr->set)
+                reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
+            else
+                reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
+            matches++;
+        }
+
         if(matches)
             reply("MSG_MATCH_COUNT", matches);
         else
@@ -2097,34 +2087,20 @@ static CHANSERV_FUNC(cmd_noregister)
 
     if(argc > 2)
     {
-        if(argc == 3)
-        {
-            reply("MSG_INVALID_DURATION", argv[2]);
-            return 0;
-        }
-
-        if(!strcmp(argv[2], "0"))
-            expiry = 0;
-        else if((duration = ParseInterval(argv[2])))
-            expiry = now + duration;
-        else
-        {
-            reply("MSG_INVALID_DURATION", argv[2]);
-            return 0;
-        }
-
-        const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
+        const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
         if((*target == '*') && !get_handle_info(target + 1))
         {
             reply("MSG_HANDLE_UNKNOWN", target + 1);
             return 0;
         }
-        chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
+        chanserv_add_dnr(target, user->handle_info->handle, reason);
         reply("CSMSG_NOREGISTER_CHANNEL", target);
         return 1;
     }
 
     reply("CSMSG_DNR_SEARCH_RESULTS");
+    if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+        reply("CSMSG_BAR");
     if(*target == '*')
         matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
     else
@@ -2138,262 +2114,26 @@ static CHANSERV_FUNC(cmd_allowregister)
 {
     const char *chan_name = argv[1];
 
-    if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
-       || dict_remove(plain_dnrs, chan_name)
-       || dict_remove(mask_dnrs, chan_name))
+    if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
     {
+        dict_remove(handle_dnrs, chan_name+1);
         reply("CSMSG_DNR_REMOVED", chan_name);
-        return 1;
     }
-    reply("CSMSG_NO_SUCH_DNR", chan_name);
-    return 0;
-}
-
-struct dnr_search {
-    struct userNode *source;
-    char *chan_mask;
-    char *setter_mask;
-    char *reason_mask;
-    time_t min_set, max_set;
-    time_t min_expires, max_expires;
-    unsigned int limit;
-};
-
-static int
-dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
-{
-    return !((dnr->set < search->min_set)
-             || (dnr->set > search->max_set)
-             || (dnr->expires && ((dnr->expires < search->min_expires)
-                                  || (dnr->expires > search->max_expires)))
-             || (search->chan_mask
-                 && !match_ircglob(dnr->chan_name, search->chan_mask))
-             || (search->setter_mask
-                 && !match_ircglob(dnr->setter, search->setter_mask))
-             || (search->reason_mask
-                 && !match_ircglob(dnr->reason, search->reason_mask)));
-}
-
-static struct dnr_search *
-dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
-{
-    struct dnr_search *discrim;
-    unsigned int ii;
-
-    discrim = calloc(1, sizeof(*discrim));
-    discrim->source = user;
-    discrim->chan_mask = NULL;
-    discrim->setter_mask = NULL;
-    discrim->reason_mask = NULL;
-    discrim->max_set = INT_MAX;
-    discrim->max_expires = INT_MAX;
-    discrim->limit = 50;
-
-    for(ii=0; ii<argc; ++ii)
-    {
-        if(ii == argc - 1)
-        {
-            reply("MSG_MISSING_PARAMS", argv[ii]);
-            goto fail;
-        }
-        else if(0 == irccasecmp(argv[ii], "channel"))
-        {
-            discrim->chan_mask = argv[++ii];
-        }
-        else if(0 == irccasecmp(argv[ii], "setter"))
-        {
-            discrim->setter_mask = argv[++ii];
-        }
-        else if(0 == irccasecmp(argv[ii], "reason"))
-        {
-            discrim->reason_mask = argv[++ii];
-        }
-        else if(0 == irccasecmp(argv[ii], "limit"))
-        {
-            discrim->limit = strtoul(argv[++ii], NULL, 0);
-        }
-        else if(0 == irccasecmp(argv[ii], "set"))
-        {
-            const char *cmp = argv[++ii];
-            if(cmp[0] == '<') {
-                if(cmp[1] == '=')
-                    discrim->min_set = now - ParseInterval(cmp + 2);
-                else
-                    discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
-            } else if(cmp[0] == '=') {
-                discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
-            } else if(cmp[0] == '>') {
-                if(cmp[1] == '=')
-                    discrim->max_set = now - ParseInterval(cmp + 2);
-                else
-                    discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
-            } else {
-                discrim->max_set = now - (ParseInterval(cmp) - 1);
-            }
-        }
-        else if(0 == irccasecmp(argv[ii], "expires"))
-        {
-            const char *cmp = argv[++ii];
-            if(cmp[0] == '<') {
-                if(cmp[1] == '=')
-                    discrim->max_expires = now + ParseInterval(cmp + 2);
-                else
-                    discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
-            } else if(cmp[0] == '=') {
-                discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
-            } else if(cmp[0] == '>') {
-                if(cmp[1] == '=')
-                    discrim->min_expires = now + ParseInterval(cmp + 2);
-                else
-                    discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
-            } else {
-                discrim->min_expires = now + (ParseInterval(cmp) - 1);
-            }
-        }
-        else
-        {
-            reply("MSG_INVALID_CRITERIA", argv[ii]);
-            goto fail;
-        }
-    }
-    return discrim;
-
-  fail:
-    free(discrim);
-    return NULL;
-}
-
-typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
-
-static unsigned int
-dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
-{
-    struct do_not_register *dnr;
-    dict_iterator_t next;
-    dict_iterator_t it;
-    unsigned int count;
-    int target_fixed;
-
-    /* Initialize local variables. */
-    count = 0;
-    target_fixed = 0;
-    if(discrim->chan_mask)
+    else if(dict_find(plain_dnrs, chan_name, NULL))
     {
-        int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
-        if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
-            target_fixed = 1;
-    }
-
-    if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
-    {
-        /* Check against account-based DNRs. */
-        dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
-        if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
-            dsf(dnr, data);
-    }
-    else if(target_fixed)
-    {
-        /* Check against channel-based DNRs. */
-        dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
-        if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
-            dsf(dnr, data);
-    }
-    else
-    {
-        /* Exhaustively search account DNRs. */
-        for(it = dict_first(handle_dnrs); it; it = next)
-        {
-            next = iter_next(it);
-            dnr = iter_data(it);
-            if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
-                break;
-        }
-        /* Do the same for channel DNRs. */
-        for(it = dict_first(plain_dnrs); it; it = next)
-        {
-            next = iter_next(it);
-            dnr = iter_data(it);
-            if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
-                break;
-        }
-
-        /* Do the same for wildcarded channel DNRs. */
-        for(it = dict_first(mask_dnrs); it; it = next)
-        {
-            next = iter_next(it);
-            dnr = iter_data(it);
-            if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
-                break;
-        }
-    }
-    return count;
-}
-
-static int
-dnr_remove_func(struct do_not_register *match, void *extra)
-{
-    struct userNode *user;
-    char *chan_name;
-
-    chan_name = alloca(strlen(match->chan_name) + 1);
-    strcpy(chan_name, match->chan_name);
-    user = extra;
-    if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
-       || dict_remove(plain_dnrs, chan_name)
-       || dict_remove(mask_dnrs, chan_name))
-    {
-        send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
+        dict_remove(plain_dnrs, chan_name);
+        reply("CSMSG_DNR_REMOVED", chan_name);
     }
-    return 0;
-}
-
-static int
-dnr_count_func(struct do_not_register *match, void *extra)
-{
-    return 0; (void)match; (void)extra;
-}
-
-static MODCMD_FUNC(cmd_dnrsearch)
-{
-    struct dnr_search *discrim;
-    dnr_search_func action;
-    struct svccmd *subcmd;
-    unsigned int matches;
-    char buf[MAXLEN];
-
-    sprintf(buf, "dnrsearch %s", argv[1]);
-    subcmd = dict_find(cmd->parent->commands, buf, NULL);
-    if(!subcmd)
+    else if(dict_find(mask_dnrs, chan_name, NULL))
     {
-        reply("CSMSG_DNR_BAD_ACTION", argv[1]);
-        return 0;
+        dict_remove(mask_dnrs, chan_name);
+        reply("CSMSG_DNR_REMOVED", chan_name);
     }
-    if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
-        return 0;
-    if(!irccasecmp(argv[1], "print"))
-        action = dnr_print_func;
-    else if(!irccasecmp(argv[1], "remove"))
-        action = dnr_remove_func;
-    else if(!irccasecmp(argv[1], "count"))
-        action = dnr_count_func;
     else
     {
-        reply("CSMSG_DNR_BAD_ACTION", argv[1]);
+        reply("CSMSG_NO_SUCH_DNR", chan_name);
         return 0;
     }
-
-    discrim = dnr_search_create(user, cmd, argc-2, argv+2);
-    if(!discrim)
-        return 0;
-
-    if(action == dnr_print_func)
-        reply("CSMSG_DNR_SEARCH_RESULTS");
-    matches = dnr_search(discrim, action, user);
-    if(matches)
-        reply("MSG_MATCH_COUNT", matches);
-    else
-        reply("MSG_NO_MATCHES");
-    free(discrim);
     return 1;
 }
 
@@ -2556,6 +2296,7 @@ static CHANSERV_FUNC(cmd_register)
 
     /* Initialize the channel's max user record. */
     cData->max = channel->members.used;
+    cData->maxsetinfo = chanserv_conf.max_userinfo_length;
 
     if(handle != user->handle_info)
         reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
@@ -3012,6 +2753,10 @@ static CHANSERV_FUNC(cmd_opchan)
         reply("CSMSG_ALREADY_OPCHANNED", channel->name);
         return 0;
     }
+    if(!IsInChannel(channel,chanserv)) {
+       reply("CSMSG_NOT_IN_CHANNEL", channel->name);
+       return 0;
+    }
     channel->channel_info->may_opchan = 0;
     mod_chanmode_init(&change);
     change.argc = 1;
@@ -3025,9 +2770,9 @@ static CHANSERV_FUNC(cmd_opchan)
 static CHANSERV_FUNC(cmd_adduser)
 {
     struct userData *actee;
-    struct userData *actor, *real_actor;;
+    struct userData *actor;
     struct handle_info *handle;
-    unsigned short access, override = 0;
+    unsigned short access;
 
     REQUIRE_PARAMS(3);
 
@@ -3045,18 +2790,12 @@ static CHANSERV_FUNC(cmd_adduser)
     }
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
-
     if(actor->access <= access)
     {
        reply("CSMSG_NO_BUMP_ACCESS");
        return 0;
     }
 
-    /* Trying to add someone with equal/more access? */
-    if (!real_actor || real_actor->access <= access)
-        override = CMD_LOG_OVERRIDE;
-
     if(!(handle = modcmd_get_handle_info(user, argv[1])))
     {
         // 'kevin must first authenticate with AuthServ.' is sent to user
@@ -3103,21 +2842,20 @@ static CHANSERV_FUNC(cmd_adduser)
         timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
 
     reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
-    return 1 | override;
+    return 1;
 }
 
 static CHANSERV_FUNC(cmd_clvl)
 {
     struct handle_info *handle;
     struct userData *victim;
-    struct userData *actor, *real_actor;
-    unsigned short new_access, override = 0;
+    struct userData *actor;
+    unsigned short new_access;
     int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
 
     REQUIRE_PARAMS(3);
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
 
     if(!(handle = modcmd_get_handle_info(user, argv[1])))
         return 0;
@@ -3172,34 +2910,22 @@ static CHANSERV_FUNC(cmd_clvl)
         timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim);
     }
 
-    /* Trying to clvl a equal/higher user? */
-    if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
-        override = CMD_LOG_OVERRIDE;
-    /* Trying to clvl someone to equal/higher access? */
-    if(!real_actor || new_access >= real_actor->access)
-        override = CMD_LOG_OVERRIDE;
-    /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
-     * If they lower their own access it's not a big problem.
-     */
-
     victim->access = new_access;
     reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
-
-    return 1 | override;
+    return 1;
 }
 
 static CHANSERV_FUNC(cmd_deluser)
 {
     struct handle_info *handle;
     struct userData *victim;
-    struct userData *actor, *real_actor;
-    unsigned short access, override = 0;
+    struct userData *actor;
+    unsigned short access;
     char *chan_name;
 
     REQUIRE_PARAMS(2);
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
 
     if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
         return 0;
@@ -3236,28 +2962,19 @@ static CHANSERV_FUNC(cmd_deluser)
        return 0;
     }
 
-    /* If people delete themselves it is an override, but they
-     * could've used deleteme so we don't log it as an override
-     */
-    if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
-        override = CMD_LOG_OVERRIDE;
-
     chan_name = strdup(channel->name);
     del_channel_user(victim, 1);
     reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
     free(chan_name);
-
-    return 1 | override;
+    return 1;
 }
 
 static int
 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
 {
-    struct userData *actor, *real_actor, *uData, *next;
-    unsigned int override = 0;
+    struct userData *actor, *uData, *next;
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
 
     if(min_access > max_access)
     {
@@ -3271,9 +2988,6 @@ cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short mi
        return 0;
     }
 
-    if(!real_actor || real_actor->access <= max_access)
-        override = CMD_LOG_OVERRIDE;
-
     for(uData = channel->channel_info->users; uData; uData = next)
     {
        next = uData->next;
@@ -3285,8 +2999,7 @@ cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short mi
     }
 
     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
-
-    return 1 | override;
+    return 1;
 }
 
 static CHANSERV_FUNC(cmd_mdelowner)
@@ -3319,6 +3032,10 @@ static CHANSERV_FUNC(cmd_mdelpeon)
     return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
 }
 
+static CHANSERV_FUNC(cmd_mdelpal)
+{
+    return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
+}
 
 static CHANSERV_FUNC(cmd_levels)
 {
@@ -3422,15 +3139,13 @@ cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *chann
     time_t limit;
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    actor = GetChannelAccess(channel->channel_info, user->handle_info);
-
     if(min_access > max_access)
     {
         reply("CSMSG_BAD_RANGE", min_access, max_access);
         return 0;
     }
 
-    if(!actor || actor->access <= max_access)
+    if((actor->access <= max_access) && !IsHelping(user))
     {
        reply("CSMSG_NO_ACCESS");
        return 0;
@@ -3770,35 +3485,6 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
        ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
        name = victim->nick;
     }
-    else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
-    {
-        struct handle_info *hi;
-        char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
-        const char *accountname = argv[1] + 1;
-
-        if(!(hi = get_handle_info(accountname)))
-        {
-            reply("MSG_HANDLE_UNKNOWN", accountname);
-            return 0;
-        }
-
-        snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
-        victims = alloca(sizeof(victims[0]) * channel->members.used);
-
-        if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
-        {
-            reply("CSMSG_MASK_PROTECTED", banmask);
-            return 0;
-        }
-
-        if((action == ACTION_KICK) && (victimCount == 0))
-        {
-            reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
-            return 0;
-        }
-
-        name = ban = strdup(banmask);
-    }
     else
     {
        if(!is_ircmask(argv[1]))
@@ -4202,23 +3888,13 @@ unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     /* may want to allow a comma delimited list of users... */
     if(!(actee = GetUserH(argv[1])))
     {
-        if(!is_ircmask(argv[1]) && *argv[1] == '*')
-        {
-            char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
-            const char *accountname = argv[1] + 1;
-
-            snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
-            mask = strdup(banmask);
-        }
-        else if(!is_ircmask(argv[1]))
-        {
-          reply("MSG_NICK_UNKNOWN", argv[1]);
-          return 0;
+       if(!is_ircmask(argv[1]))
+       {
+           reply("MSG_NICK_UNKNOWN", argv[1]);
+           return 0;
        }
-        else
-        {
-          mask = strdup(argv[1]);
-        }
+
+       mask = strdup(argv[1]);
     }
 
     /* We don't sanitize the mask here because ircu
@@ -4463,17 +4139,17 @@ static CHANSERV_FUNC(cmd_access)
         if(IsOper(target))
         {
             epithet = chanserv_conf.irc_operator_epithet;
-            type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
+            type = "IRCOp";
         }
         else if(IsNetworkHelper(target))
         {
             epithet = chanserv_conf.network_helper_epithet;
-            type = user_find_message(user, "CSMSG_UC_H_TITLE");
+            type = "network helper";
         }
         else if(IsSupportHelper(target))
         {
             epithet = chanserv_conf.support_helper_epithet;
-            type = user_find_message(user, "CSMSG_LC_H_TITLE");
+            type = "support helper";
         }
         if(epithet)
         {
@@ -4875,22 +4551,16 @@ static CHANSERV_FUNC(cmd_plist)
 
 static CHANSERV_FUNC(cmd_lamers)
 {
-    struct userNode *search_u = NULL;
     struct helpfile_table tbl;
-    unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
+    unsigned int matches = 0, timed = 0, ii;
     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
     const char *msg_never, *triggered, *expires;
     struct banData *ban, **bans; /* lamers */
 
-    if(argc < 2)
+    if(argc > 1)
+       search = argv[1];
+    else
         search = NULL;
-    else if(strchr(search = argv[1], '!'))
-    {
-        search = argv[1];
-        search_wilds = search[strcspn(search, "?*")];
-    }
-    else if(!(search_u = GetUserH(search)))
-        reply("MSG_NICK_UNKNOWN", search);
 
     reply("CSMSG_LAMERS_HEADER", channel->name);
     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
@@ -4898,16 +4568,8 @@ static CHANSERV_FUNC(cmd_lamers)
     /* lamers */
     for(ban = channel->channel_info->bans; ban; ban = ban->next)
     {
-        if(search_u)
-        {
-            if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
+       if(search && !match_ircglobs(search, ban->mask))
            continue;
-        }
-        else if(search)
-        {
-            if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
-                continue;
-        }
        bans[matches++] = ban;
        if(ban->expires)
             timed = 1;
@@ -5509,12 +5171,10 @@ static CHANSERV_FUNC(cmd_peek)
 static MODCMD_FUNC(cmd_wipeinfo)
 {
     struct handle_info *victim;
-    struct userData *ud, *actor, *real_actor;
-    unsigned int override = 0;
+    struct userData *ud, *actor;
 
     REQUIRE_PARAMS(2);
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
     if(!(victim = modcmd_get_handle_info(user, argv[1])))
         return 0;
     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
@@ -5527,13 +5187,11 @@ static MODCMD_FUNC(cmd_wipeinfo)
         reply("MSG_USER_OUTRANKED", victim->handle);
         return 0;
     }
-    if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
-        override = CMD_LOG_OVERRIDE;
     if(ud->info)
         free(ud->info);
     ud->info = NULL;
     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
-    return 1 | override;
+    return 1;
 }
 
 static void
@@ -6170,8 +5828,6 @@ chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int a
                search->flags |= CHANNEL_NODELETE;
            else if(!irccasecmp(argv[i], "suspended"))
                search->flags |= CHANNEL_SUSPENDED;
-            else if(!irccasecmp(argv[i], "unreviewed"))
-                search->flags |= CHANNEL_UNREVIEWED;
            else
            {
                reply("CSMSG_INVALID_CFLAG", argv[i]);
@@ -6314,60 +5970,6 @@ static CHANSERV_FUNC(cmd_unvisited)
     return 1;
 }
 
-static MODCMD_FUNC(chan_opt_unreviewed)
-{
-    struct chanData *cData = channel->channel_info;
-    int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
-
-    if(argc > 1)
-    {
-        int new_value;
-
-        /* The two directions can have different ACLs. */
-        if(enabled_string(argv[1]))
-            new_value = 1;
-        else if(disabled_string(argv[1]))
-            new_value = 0;
-        else
-        {
-            reply("MSG_INVALID_BINARY", argv[1]);
-            return 0;
-        }
-
-        if (new_value != value)
-        {
-            struct svccmd *subcmd;
-            char subcmd_name[32];
-
-            snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
-            subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
-            if(!subcmd)
-            {
-                reply("MSG_COMMAND_DISABLED", subcmd_name);
-                return 0;
-            }
-            else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
-                return 0;
-
-            if (new_value)
-                cData->flags |= CHANNEL_UNREVIEWED;
-            else
-            {
-                free(cData->registrar);
-                cData->registrar = strdup(user->handle_info->handle);
-                cData->flags &= ~CHANNEL_UNREVIEWED;
-            }
-            value = new_value;
-        }
-    }
-
-    if(value)
-        reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
-    else
-        reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
-    return 1;
-}
-
 static MODCMD_FUNC(chan_opt_defaulttopic)
 {
     if(argc > 1)
@@ -6483,6 +6085,20 @@ static MODCMD_FUNC(chan_opt_usergreeting)
     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
 }
 
+static MODCMD_FUNC(chan_opt_maxsetinfo)
+{
+   unsigned int charmax;
+
+   if(argc > 1) {
+     charmax = atoi(argv[1]);
+     if ((charmax > 0) && (charmax < chanserv_conf.max_userinfo_length))
+       channel->channel_info->maxsetinfo = charmax;
+   }
+
+   reply("CSMSG_SET_MAXSETINFO", channel->channel_info->maxsetinfo);
+   return 1;
+}
+
 static MODCMD_FUNC(chan_opt_modes)
 {
     struct mod_chanmode *new_modes;
@@ -6583,6 +6199,16 @@ static MODCMD_FUNC(chan_opt_nodelete)
 
 static MODCMD_FUNC(chan_opt_dynlimit)
 {
+    struct mod_chanmode change;
+
+    if (argc > 1) {
+        if (disabled_string(argv[1])) {
+            mod_chanmode_init(&change);
+            change.modes_clear |= MODE_LIMIT;
+            mod_chanmode_announce(chanserv, channel, &change);
+        }
+    }
+
     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
 }
 
@@ -6655,8 +6281,7 @@ static MODCMD_FUNC(chan_opt_defaults)
         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
         return 0;
     }
-    cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
-        | (cData->flags & CHANNEL_PRESERVED_FLAGS);
+    cData->flags = CHANNEL_DEFAULT_FLAGS;
     cData->modes = chanserv_conf.default_modes;
     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
@@ -6783,7 +6408,7 @@ channel_multiple_option(enum charOption option, struct userNode *user, struct ch
 
        if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
        {
-           reply("CSMSG_INVALID_NUMERIC", index);
+           reply("CSMSG_INVALID_NUMERIC", argv[1]);
             /* Show possible values. */
             for(index = 0; index < count; index++)
                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
@@ -6911,9 +6536,6 @@ static CHANSERV_FUNC(cmd_set)
         return 0;
     }
 
-    argv[0] = "";
-    argv[1] = buf;
-
     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
 }
 
@@ -6997,9 +6619,9 @@ static MODCMD_FUNC(user_opt_info)
     {
         size_t bp;
         infoline = unsplit_string(argv + 1, argc - 1, NULL);
-        if(strlen(infoline) > chanserv_conf.max_userinfo_length)
+        if(strlen(infoline) > channel->channel_info->maxsetinfo)
         {
-            reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
+            reply("CSMSG_INFOLINE_TOO_LONG", channel->channel_info->maxsetinfo);
             return 0;
         }
         bp = strcspn(infoline, "\001");
@@ -7080,12 +6702,9 @@ static CHANSERV_FUNC(cmd_uset)
 static CHANSERV_FUNC(cmd_giveownership)
 {
     struct handle_info *new_owner_hi;
-    struct userData *new_owner;
-    struct userData *curr_user;
-    struct userData *invoker;
+    struct userData *new_owner, *curr_user;
     struct chanData *cData = channel->channel_info;
     struct do_not_register *dnr;
-    const char *confirm;
     struct giveownership *giveownership;
     unsigned int force, override;
     unsigned short co_access, new_owner_old_access;
@@ -7120,7 +6739,7 @@ static CHANSERV_FUNC(cmd_giveownership)
         }
         curr_user = owner;
     }
-    else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
+    else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
     {
         char delay[INTERVALLEN];
         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
@@ -7143,7 +6762,7 @@ static CHANSERV_FUNC(cmd_giveownership)
     {
         if(force)
         {
-            new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL, 0);
+            new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL, 0);
         }
         else
         {
@@ -7164,17 +6783,6 @@ static CHANSERV_FUNC(cmd_giveownership)
         return 0;
     }
 
-    invoker = GetChannelUser(cData, user->handle_info);
-    if(invoker->access <= UL_OWNER)
-    {
-        confirm = make_confirmation_string(curr_user);
-        if((argc < 3) || strcmp(argv[2], confirm))
-        {
-            reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
-            return 0;
-        }
-    }
-
     new_owner_old_access = new_owner->access;
     if(new_owner->access >= UL_COOWNER)
         co_access = new_owner->access;
@@ -7221,14 +6829,12 @@ chanserv_expire_user_suspension(void *data)
 static CHANSERV_FUNC(cmd_suspend)
 {
     struct handle_info *hi;
-    struct userData *self, *real_self, *target;
-    unsigned int override = 0;
+    struct userData *self, *target;
     time_t expiry;
 
     REQUIRE_PARAMS(3);
     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
     self = GetChannelUser(channel->channel_info, user->handle_info);
-    real_self = GetChannelAccess(channel->channel_info, user->handle_info);
     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
     {
         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
@@ -7267,23 +6873,19 @@ static CHANSERV_FUNC(cmd_suspend)
         if(target->expires)
             timeq_add(target->expires, chanserv_expire_user_suspension, target);
 
-    if(!real_self || target->access >= real_self->access)
-        override = CMD_LOG_OVERRIDE;
     target->flags |= USER_SUSPENDED;
     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
-    return 1 | override;
+    return 1;
 }
 
 static CHANSERV_FUNC(cmd_unsuspend)
 {
     struct handle_info *hi;
-    struct userData *self, *real_self, *target;
-    unsigned int override = 0;
+    struct userData *self, *target;
 
     REQUIRE_PARAMS(2);
     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
     self = GetChannelUser(channel->channel_info, user->handle_info);
-    real_self = GetChannelAccess(channel->channel_info, user->handle_info);
     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
     {
         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
@@ -7299,13 +6901,11 @@ static CHANSERV_FUNC(cmd_unsuspend)
         reply("CSMSG_NOT_SUSPENDED", hi->handle);
         return 0;
     }
-    if(!real_self || target->access >= real_self->access)
-        override = CMD_LOG_OVERRIDE;
     target->flags &= ~USER_SUSPENDED;
     scan_user_presence(target, NULL);
     timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
-    return 1 | override;
+    return 1;
 }
 
 static MODCMD_FUNC(cmd_deleteme)
@@ -7425,6 +7025,316 @@ static CHANSERV_FUNC(cmd_wut)
     return 1;
 }
 
+static CHANSERV_FUNC(cmd_roulette)
+{
+    if(channel) {
+        struct chanData *cData = channel->channel_info;
+
+        if (cData) {
+            if (cData->roulette_chamber) {
+               DelUser(user, chanserv, 1, "BANG - Don't stuff bullets into a loaded gun");
+                return 1;
+            }
+        
+            send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_ROULETTE_LOADS");
+            cData->roulette_chamber = 1 + rand() % 6;
+        }
+    }
+
+    return 1;
+}
+static CHANSERV_FUNC(cmd_shoot)
+{
+    if(channel) {
+        struct chanData *cData = channel->channel_info;
+
+        if (cData->roulette_chamber <= 0) {
+            struct service *service;
+            if ((service = service_find(chanserv->nick))) {
+                reply("CSMSG_ROULETTE_NEW", service->trigger);
+            }
+            return 1;
+        }
+
+        cData->roulette_chamber--;
+
+        if (cData->roulette_chamber == 0) {
+            reply("CSMSG_ROULETTE_BANG");
+            reply("CSMSG_ROULETTE_BETTER_LUCK", user->nick);
+            DelUser(user, chanserv, 1, "BANG!!!!");
+        } else
+            reply("CSMSG_ROULETTE_CLICK");
+    }
+
+    return 1;
+}
+
+static void
+chanserv_remove_abuse(void *data)
+{
+    char *remnick = data;
+    struct userNode *user;
+    /* sometimes the clone was killed and maybe even the user took their nick back 
+     * (ie, an oper) so dont kill them here after all unless they are local. */
+    if( (user = GetUserH(remnick)) )
+       if(IsLocal(user) )
+         DelUser(user, NULL, 1, "");
+}
+
+int lamepart(struct userNode *nick) {
+    struct modeNode *mn;
+    unsigned int count, n;
+
+    for (n=count=0; n<nick->channels.used; n++) {
+        mn = nick->channels.list[n];
+        irc_svspart(chanserv, nick, mn->channel);
+    }
+
+    return 0;
+}
+
+static CHANSERV_FUNC(cmd_spin)
+{
+    if(!channel)
+        return 1;
+      
+    int type = 0, lamep = 1;
+    char *tstr;
+
+    tstr = conf_get_data("server/type", RECDB_QSTRING);
+    if(tstr) {
+        type = atoi(tstr);
+        if (type > 6)
+            lamep = 0;
+    }
+
+
+    send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL1", user->nick);
+    send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL2");
+    send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL3");
+
+    if(chanserv_conf.wheel->used < 1) {
+        /* wheel actions not defined! eek */
+        return 1;
+    }
+
+    const char *wheel = chanserv_conf.wheel->list[ (int) ( (chanserv_conf.wheel->used) * (rand() / (RAND_MAX + 1.0)) ) ];
+    if(!wheel && *wheel) 
+        return 1;
+
+/* enable this to be able to manually specify a result for testing:
+    log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
+    if(argc > 1) {
+      wheel = argv[1];
+    }
+*/
+
+    /* connection reset by peer */
+    if (!strcasecmp(wheel, "peer")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PEER");
+         if (type < 7)
+              irc_kill(chanserv, user, "Connection reset by peer");
+         else
+              irc_svsquit(chanserv, user, "Connection reset by peer");
+    }
+    /* part all channels */
+    else if (!strcasecmp(wheel, "partall")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PARTALL");
+         if (lamep)
+             lamepart(user);
+         else
+             sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
+    }
+    /* random time gline */
+    else if (!strcasecmp(wheel, "gline")) {
+         char target[IRC_NTOP_MAX_SIZE + 3];
+         int wtime = 120 + rand() % 600;
+
+         strcpy(target, "*@");
+         strcat(target, user->hostname);
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_GLINE");
+
+         gline_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1, 0);
+//         irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
+    }
+    /* random shun */
+    else if (!strcasecmp(wheel, "shun")) {
+         char target[IRC_NTOP_MAX_SIZE + 3];
+         int wtime = 120 + rand() % 600;
+
+         strcpy(target, "*@");
+         strcat(target, user->hostname);
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SHUN");
+
+         shun_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1);
+    }
+    /* absolutely nothing */
+    else if (!strcasecmp(wheel, "nothing")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NOTHING");
+    }
+    /* join random chans and part em several times */
+    else if (!strcasecmp(wheel, "randjoin")) {
+         int complete = 0;
+         int rndchans = 0;
+         int chango = 0;
+         int roundz0r = 0;
+
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_RANDJOIN");
+         while(complete != 1)  {
+            if (rndchans != 15) {
+                chango = 120 + rand() % 600;
+                sputsock("%s SJ %s #%d %d", self->numeric, user->numeric, chango, now);
+                rndchans++;
+            } else {
+                if (roundz0r != 1) {
+                     if (lamep)
+                         lamepart(user);
+                     else
+                         sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
+                     roundz0r = 1;
+                     rndchans = 0;
+                } else {
+                     if (lamep)
+                         lamepart(user);
+                     else
+                         sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
+                     complete = 1;
+                }
+            }
+        }
+    }
+    /* abuse line added to /whois */
+    else if (!strcasecmp(wheel, "abusewhois")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_ABUSEWHOIS");
+         irc_swhois(chanserv, user, "is being defecated on by services");
+    }
+    /* kick from each channel your in */
+    else if (!strcasecmp(wheel, "kickall")) {
+         unsigned int count, n;
+         struct modeNode *mn;
+
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKALL");
+
+         for (n=count=0; n<user->channels.used; n++) {
+             mn = user->channels.list[n];
+             irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
+         }
+    }
+    /* random nick change */
+    else if (!strcasecmp(wheel, "nickchange")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NICKCHANGE");
+
+         char *oldnick = NULL;
+         char *oldident = NULL;
+         char *oldhost = NULL;
+         char abusednick[NICKLEN] = "";
+         int abusednum = 1 + (int) (10000.0 * (rand() / (RAND_MAX + 1.0)));
+         struct userNode *clone;
+
+         oldnick = strdup(user->nick);
+         oldident = strdup(user->ident);
+         oldhost = strdup(user->hostname);
+
+         //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
+         while (1) {
+             snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
+             log_module(MAIN_LOG, LOG_DEBUG, "Abused Nick: %s, Client Nick: %s", abusednick, user->nick);
+             if(!GetUserH(abusednick))
+               break;
+         }
+
+         SVSNickChange(user, abusednick);
+         irc_svsnick(chanserv, user, abusednick);
+
+         clone = AddClone(oldnick, oldident, oldhost, "I got abused by the wheel of misfortune :D");
+         timeq_add(now + 300, chanserv_remove_abuse, clone->nick);
+    }
+    /* kill */
+    else if (!strcasecmp(wheel, "kill")) {
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KILL");
+
+        DelUser(user, chanserv, 1, "Reward for spinning the wheel of misfortune!");
+         //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
+    }
+    /* service ignore */
+    else if (!strcasecmp(wheel, "svsignore")) {
+         int gagged, ignoretime = 0;
+         char target[IRC_NTOP_MAX_SIZE + 13];
+
+         if(IsOper(user)) {
+            /* we cant gag opers, so just verbally abuse them */
+            send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE_OPER");
+            return 1;
+         }
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE");
+
+         strcpy(target, "*!*@");
+         strcat(target, user->hostname);
+         ignoretime = now + (1 + rand() % 120);
+
+         gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime);
+    }
+    /* kick and ban from each channel your in */
+    else if (!strcasecmp(wheel, "kickbanall")) {
+         unsigned int count, n;
+         struct modeNode *mn;
+         //char ban[IRC_NTOP_MAX_SIZE + 1];
+
+         send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKBANALL");
+
+         //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
+         for (n=count=0; n<user->channels.used; n++) {
+             struct mod_chanmode *change;
+/*             struct banData *bData; */
+            unsigned int exists;
+/*          int duration = 300; */
+            char *ban;
+
+            ban = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT|GENMASK_USENICK);
+
+            log_module(MAIN_LOG, LOG_DEBUG, "Generated ban %s", ban);
+             mn = user->channels.list[n];
+             if(mn->channel->banlist.used >= MAXBANS) {
+               reply("CSMSG_BANLIST_FULL", mn->channel->name);
+               free(ban);
+               continue;
+             }
+
+/*             bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
+
+            change = mod_chanmode_alloc(1);
+            change->args[0].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
+            change->args[0].u.member = GetUserMode(mn->channel, user);
+            change->argc = 1;
+
+            mod_chanmode_announce(chanserv, mn->channel, change);
+            mod_chanmode_free(change);
+
+            exists = ChannelBanExists(mn->channel, ban);
+            if(!exists) {
+               change = mod_chanmode_alloc(1);
+              change->args[0].mode = MODE_BAN;
+              change->args[0].u.hostmask = ban;
+              change->argc = 1;
+              mod_chanmode_announce(chanserv, mn->channel, change);
+              mod_chanmode_free(change);
+             }
+
+             if(exists) {
+               reply("CSMSG_REDUNDANT_BAN", ban, mn->channel->name);
+               free(ban);
+            }
+
+             irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
+         }
+    }
+    else {
+       send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_UNKNOWN", wheel);
+    }
+
+    return 1;
+}
+
 #ifdef lame8ball
 static CHANSERV_FUNC(cmd_8ball)
 {
@@ -8219,8 +8129,6 @@ handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
     {
        scan_user_presence(uData, mn->user);
         uData->seen = now;
-        if (uData->access >= UL_PRESENT)
-            cData->visited = now;
     }
 
     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
@@ -8529,8 +8437,6 @@ chanserv_conf_read(void)
     chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
-    str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
-    chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
     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);
@@ -8571,6 +8477,24 @@ chanserv_conf_read(void)
         chanserv_conf.default_modes = *change;
         mod_chanmode_free(change);
     }
+    free_string_list(chanserv_conf.wheel);
+    strlist = database_get_data(conf_node, "wheel", RECDB_STRING_LIST);
+    if(strlist)
+        strlist = string_list_copy(strlist);
+    else
+    {
+       static const char *list[] = {
+          "peer", "partall", "gline",  /* "shun", */
+          "nothing", "randjoin", "abusewhois", "kickall", 
+          "nickchange", "kill", "svsignore", "kickbanall", 
+          NULL};
+       unsigned int ii;
+       strlist = alloc_string_list(ArrayLength(list)-1);
+       for(ii=0; list[ii]; ii++)
+          string_list_append(strlist, strdup(list[ii]));
+    }
+    chanserv_conf.wheel = strlist;
+
     free_string_list(chanserv_conf.set_shows);
     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
     if(strlist)
@@ -8601,6 +8525,7 @@ chanserv_conf_read(void)
      * function list as invalid, so it will be initialized.
      */
     set_shows_list.used = 0;
+
     free_string_list(chanserv_conf.eightball);
     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
     if(strlist)
@@ -8615,6 +8540,7 @@ chanserv_conf_read(void)
         string_list_append(strlist, strdup("Maybe so."));
     }
     chanserv_conf.eightball = strlist;
+
     free_string_list(chanserv_conf.old_ban_names);
     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
     if(strlist)
@@ -9000,6 +8926,9 @@ chanserv_channel_read(const char *key, struct record_data *hir)
     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
     cData->topic = str ? strdup(str) : NULL;
 
+    str = database_get_data(channel, KEY_MAXSETINFO, RECDB_QSTRING);
+    cData->maxsetinfo = str ? strtoul(str, NULL, 0) : chanserv_conf.max_userinfo_length;
+
     if(!IsSuspended(cData)
        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
        && (argc = split_line(str, 0, ArrayLength(argv), argv))
@@ -9063,7 +8992,6 @@ chanserv_dnr_read(const char *key, struct record_data *hir)
 {
     const char *setter, *reason, *str;
     struct do_not_register *dnr;
-    time_t expiry;
 
     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
     if(!setter)
@@ -9077,11 +9005,7 @@ chanserv_dnr_read(const char *key, struct record_data *hir)
         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
         return;
     }
-    str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
-    expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
-    if(expiry && expiry <= now)
-        return;
-    dnr = chanserv_add_dnr(key, setter, expiry, reason);
+    dnr = chanserv_add_dnr(key, setter, reason);
     if(!dnr)
         return;
     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
@@ -9253,6 +9177,9 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
     }
     saxdb_end_record(ctx);
 
+    if (channel->maxsetinfo)
+        saxdb_write_int(ctx, KEY_MAXSETINFO, channel->maxsetinfo);
+
     if(channel->modes.modes_set || channel->modes.modes_clear)
     {
        mod_chanmode_format(&channel->modes, buf);
@@ -9318,22 +9245,14 @@ static void
 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
 {
     struct do_not_register *dnr;
-    dict_iterator_t it, next;
+    dict_iterator_t it;
 
-    for(it = dict_first(dnrs); it; it = next)
+    for(it = dict_first(dnrs); it; it = iter_next(it))
     {
-        next = iter_next(it);
         dnr = iter_data(it);
-        if(dnr->expires && dnr->expires <= now)
-        {
-            dict_remove(dnrs, iter_key(it));
-            continue;
-        }
         saxdb_start_record(ctx, dnr->chan_name, 0);
         if(dnr->set)
             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
-        if(dnr->expires)
-            saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
         saxdb_end_record(ctx);
@@ -9388,6 +9307,7 @@ chanserv_db_cleanup(void) {
     dict_delete(note_types);
     free_string_list(chanserv_conf.eightball);
     free_string_list(chanserv_conf.old_ban_names);
+    free_string_list(chanserv_conf.wheel);
     free_string_list(chanserv_conf.set_shows);
     free(set_shows_list.list);
     free(uset_shows_list.list);
@@ -9408,12 +9328,12 @@ init_chanserv(const char *nick)
 {
     struct chanNode *chan;
     unsigned int i;
+
     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
     conf_register_reload(chanserv_conf_read);
 
-    if(nick)
-    {
     reg_server_link_func(handle_server_link);
+
     reg_new_channel_func(handle_new_channel);
     reg_join_func(handle_join);
     reg_part_func(handle_part);
@@ -9421,10 +9341,8 @@ init_chanserv(const char *nick)
     reg_topic_func(handle_topic);
     reg_mode_change_func(handle_mode);
     reg_nick_change_func(handle_nick_change);
-    reg_auth_func(handle_auth);
-    }
-
 
+    reg_auth_func(handle_auth);
     reg_handle_rename_func(handle_rename);
     reg_unreg_func(handle_unreg);
 
@@ -9440,10 +9358,6 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
-    DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
-    modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
-    modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
-    modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
@@ -9466,6 +9380,7 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
+    DEFINE_COMMAND(mdelpal, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
     DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
 
     DEFINE_COMMAND(levels, 1, 0, NULL);
@@ -9496,9 +9411,6 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "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_CHANNEL_OPTION(unreviewed);
-    modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
-    modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
@@ -9553,6 +9465,9 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(roulette, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(shoot, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(spin, 1, MODCMD_REQUIRE_AUTHED, "spin", "+nolog,+toy,+acceptchan", NULL);
 
     /* Channel options */
     DEFINE_CHANNEL_OPTION(defaulttopic);
@@ -9578,6 +9493,7 @@ init_chanserv(const char *nick)
     DEFINE_CHANNEL_OPTION(ctcpreaction);
     DEFINE_CHANNEL_OPTION(bantimeout);
     DEFINE_CHANNEL_OPTION(inviteme);
+    DEFINE_CHANNEL_OPTION(maxsetinfo);
     if(off_channel > 1)
         DEFINE_CHANNEL_OPTION(offchannel);
     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
@@ -9599,7 +9515,7 @@ init_chanserv(const char *nick)
     if(nick)
     {
         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
-        chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
+        chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
         service_register(chanserv)->trigger = '!';
         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
     }
@@ -9609,9 +9525,6 @@ init_chanserv(const char *nick)
     if(chanserv_conf.channel_expire_frequency)
        timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 
-    if(chanserv_conf.dnr_expire_frequency)
-        timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
-
     if(chanserv_conf.ban_timeout_frequency)
         timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);