]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/chanserv.c
added extern type to comply with gcc10 change
[irc/evilnet/x3.git] / src / chanserv.c
index 9bc91ac4378eb8bfad60d64de6b666745073c5ca..5931cd0fc731b9654e409ba6bd5d5cdf5bb52b34 100644 (file)
 #include "ioset.h"
 #include "modcmd.h"
 #include "opserv.h" /* for opserv_bad_channel() */
+#include "nickserv.h" /* for oper_outranks() */
 #include "saxdb.h"
 #include "shun.h"
 #include "spamserv.h"
 #include "timeq.h"
 
-#define CHANSERV_CONF_NAME     "services/chanserv"
+#define CHANSERV_CONF_NAME  "services/chanserv"
 
 /* ChanServ options */
-#define KEY_SUPPORT_CHANNEL     "support_channel"
-#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
-#define KEY_DB_BACKUP_FREQ     "db_backup_freq"
-#define KEY_INFO_DELAY         "info_delay"
-#define KEY_MAX_GREETLEN               "max_greetlen"
-#define KEY_ADJUST_THRESHOLD           "adjust_threshold"
-#define KEY_ADJUST_DELAY       "adjust_delay"
-#define KEY_CHAN_EXPIRE_FREQ   "chan_expire_freq"
-#define KEY_CHAN_EXPIRE_DELAY  "chan_expire_delay"
-#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_NICK               "nick"
-#define KEY_OLD_CHANSERV_NAME  "old_chanserv_name"
-#define KEY_8BALL_RESPONSES     "8ball"
-#define KEY_OLD_BAN_NAMES       "old_ban_names"
-#define KEY_REFRESH_PERIOD      "refresh_period"
+#define KEY_SUPPORT_CHANNEL         "support_channel"
+#define KEY_SUPPORT_CHANNEL_MODES   "support_channel_modes"
+#define KEY_DB_BACKUP_FREQ          "db_backup_freq"
+#define KEY_INFO_DELAY              "info_delay"
+#define KEY_MAX_GREETLEN            "max_greetlen"
+#define KEY_ADJUST_THRESHOLD        "adjust_threshold"
+#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_NICK                    "nick"
+#define KEY_OLD_CHANSERV_NAME       "old_chanserv_name"
+#define KEY_8BALL_RESPONSES         "8ball"
+#define KEY_OLD_BAN_NAMES           "old_ban_names"
+#define KEY_REFRESH_PERIOD          "refresh_period"
 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
 #define KEY_CTCP_LONG_BAN_DURATION  "ctcp_long_ban_duration"
 #define KEY_MAX_OWNED               "max_owned"
 #define KEY_IRC_OPERATOR_EPITHET    "irc_operator_epithet"
 #define KEY_NETWORK_HELPER_EPITHET  "network_helper_epithet"
 #define KEY_SUPPORT_HELPER_EPITHET  "support_helper_epithet"
-#define KEY_NODELETE_LEVEL      "nodelete_level"
-#define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
-#define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
+#define KEY_NODELETE_LEVEL          "nodelete_level"
+#define KEY_MAX_USERINFO_LENGTH     "max_userinfo_length"
+#define KEY_GIVEOWNERSHIP_PERIOD    "giveownership_timeout"
+#define KEY_VALID_CHANNEL_REGEX     "valid_channel_regex"
 
 /* ChanServ database */
-#define KEY_VERSION_CONTROL     "version_control"
-#define KEY_CHANNELS           "channels"
-#define KEY_NOTE_TYPES          "note_types"
+#define KEY_VERSION_CONTROL         "version_control"
+#define KEY_CHANNELS                "channels"
+#define KEY_NOTE_TYPES              "note_types"
 
 /* version control paramiter */
-#define KEY_VERSION_NUMBER      "version_number"
+#define KEY_VERSION_NUMBER          "version_number"
 
 /* Note type parameters */
-#define KEY_NOTE_OPSERV_ACCESS  "opserv_access"
-#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
-#define KEY_NOTE_SETTER_ACCESS  "setter_access"
-#define KEY_NOTE_VISIBILITY     "visibility"
-#define KEY_NOTE_VIS_PRIVILEGED "privileged"
-#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
-#define KEY_NOTE_VIS_ALL        "all"
-#define KEY_NOTE_MAX_LENGTH     "max_length"
-#define KEY_NOTE_SETTER         "setter"
-#define KEY_NOTE_NOTE           "note"
+#define KEY_NOTE_OPSERV_ACCESS      "opserv_access"
+#define KEY_NOTE_CHANNEL_ACCESS     "channel_access"
+#define KEY_NOTE_SETTER_ACCESS      "setter_access"
+#define KEY_NOTE_VISIBILITY         "visibility"
+#define KEY_NOTE_VIS_PRIVILEGED     "privileged"
+#define KEY_NOTE_VIS_CHANNEL_USERS  "channel_users"
+#define KEY_NOTE_VIS_ALL            "all"
+#define KEY_NOTE_MAX_LENGTH         "max_length"
+#define KEY_NOTE_SETTER             "setter"
+#define KEY_NOTE_NOTE               "note"
 
 /* Do-not-register channels */
-#define KEY_DNR                 "dnr"
-#define KEY_DNR_SET             "set"
-#define KEY_DNR_SETTER          "setter"
-#define KEY_DNR_REASON          "reason"
+#define KEY_DNR                     "dnr"
+#define KEY_DNR_SET                 "set"
+#define KEY_DNR_SETTER              "setter"
+#define KEY_DNR_REASON              "reason"
 
 /* Channel data */
-#define KEY_REGISTERED         "registered"
-#define KEY_REGISTRAR          "registrar"
-#define KEY_SUSPENDED           "suspended"
-#define KEY_PREVIOUS            "previous"
-#define KEY_SUSPENDER          "suspender"
-#define KEY_ISSUED              "issued"
-#define KEY_REVOKED             "revoked"
-#define KEY_SUSPEND_EXPIRES     "suspend_expires"
-#define KEY_SUSPEND_REASON      "suspend_reason"
-#define KEY_GIVEOWNERSHIP       "giveownership"
-#define KEY_STAFF_ISSUER        "staff_issuer"
-#define KEY_OLD_OWNER           "old_owner"
-#define KEY_TARGET              "target"
-#define KEY_TARGET_ACCESS       "target_access"
-#define KEY_VISITED            "visited"
-#define KEY_TOPIC              "topic"
-#define KEY_GREETING           "greeting"
-#define KEY_USER_GREETING      "user_greeting"
-#define KEY_MODES              "modes"
-#define KEY_FLAGS              "flags"
-#define KEY_OPTIONS             "options"
-#define KEY_USERS              "users"
-#define KEY_BANS               "bans" /* for lamers */
-#define KEY_MAX                        "max"
-#define KEY_NOTES               "notes"
-#define KEY_TOPIC_MASK          "topic_mask"
-#define KEY_OWNER_TRANSFER      "owner_transfer"
-#define KEY_MAXSETINFO          "maxsetinfo"
+#define KEY_REGISTERED              "registered"
+#define KEY_REGISTRAR               "registrar"
+#define KEY_SUSPENDED               "suspended"
+#define KEY_PREVIOUS                "previous"
+#define KEY_SUSPENDER               "suspender"
+#define KEY_ISSUED                  "issued"
+#define KEY_REVOKED                 "revoked"
+#define KEY_SUSPEND_EXPIRES         "suspend_expires"
+#define KEY_SUSPEND_REASON          "suspend_reason"
+#define KEY_GIVEOWNERSHIP           "giveownership"
+#define KEY_STAFF_ISSUER            "staff_issuer"
+#define KEY_OLD_OWNER               "old_owner"
+#define KEY_TARGET                  "target"
+#define KEY_TARGET_ACCESS           "target_access"
+#define KEY_VISITED                 "visited"
+#define KEY_TOPIC                   "topic"
+#define KEY_GREETING                "greeting"
+#define KEY_USER_GREETING           "user_greeting"
+#define KEY_MODES                   "modes"
+#define KEY_FLAGS                   "flags"
+#define KEY_OPTIONS                 "options"
+#define KEY_USERS                   "users"
+#define KEY_BANS                    "bans" /* for lamers */
+#define KEY_MAX                     "max"
+#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_INFO               "info"
-#define KEY_SEEN               "seen"
-#define KEY_ACCESSEXPIRY       "accessexpiry"
-#define KEY_CLVLEXPIRY         "clvlexpiry"
-#define KEY_LASTLEVEL          "lastlevel"
+#define KEY_LEVEL                   "level"
+#define KEY_INFO                    "info"
+#define KEY_SEEN                    "seen"
+#define KEY_ACCESSEXPIRY            "accessexpiry"
+#define KEY_CLVLEXPIRY              "clvlexpiry"
+#define KEY_LASTLEVEL               "lastlevel"
 
 /* Ban data */
-#define KEY_OWNER              "owner"
-#define KEY_REASON             "reason"
-#define KEY_SET                        "set"
-#define KEY_DURATION           "duration"
-#define KEY_EXPIRES             "expires"
-#define KEY_TRIGGERED          "triggered"
+#define KEY_OWNER                   "owner"
+#define KEY_REASON                  "reason"
+#define KEY_SET                     "set"
+#define KEY_DURATION                "duration"
+#define KEY_EXPIRES                 "expires"
+#define KEY_TRIGGERED               "triggered"
 
-#define KEY_GOD_TIMEOUT         "god_timeout"
+#define KEY_GOD_TIMEOUT             "god_timeout"
 
-#define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL)
+#define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
+#define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
 
 /* Administrative messages */
@@ -154,8 +158,9 @@ static const struct message_entry msgtab[] = {
 /* Do-not-register channels */
     { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
     { "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", "$b%s$b is do-not-register (by $b%s$b): %s" },
+    { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s 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." },
@@ -163,6 +168,8 @@ 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." },
@@ -198,7 +205,9 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
     { "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_OUT_OF_CHANNEL", "For some reason I don't seem to be 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." },
@@ -215,7 +224,7 @@ static const struct message_entry msgtab[] = {
     { "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%s$b, not %s." },
     { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
-    { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
+    { "CSMSG_ADDUSER_PENDING", "I have sent %s a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
     { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
     { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
     { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" },             /* Remove after testing? */
@@ -231,6 +240,7 @@ static const struct message_entry msgtab[] = {
     { "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_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
+    { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
     { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
 
 /* Ban management */
@@ -247,10 +257,12 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
     { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
     { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
+    { "CSMSG_NO_EXTBANS", "$b%s$b is an extended ban, which are not allowed." },
     { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
     { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
     { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
     { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
+    { "CSMSG_BAD_BAN", "The given ban $b%s$b is invalid." },
 
     { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
 
@@ -285,6 +297,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_SET_USERGREETING",  "$bUserGreeting$b %s" },
     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
     { "CSMSG_SET_NODELETE",      "$bNoDelete    $b %s" },
+    { "CSMSG_SET_UNREVIEWED",    "$bUnreviewed  $b %s" },
     { "CSMSG_SET_DYNLIMIT",      "$bDynLimit    $b %s - +l joinflood protection." },
     { "CSMSG_SET_OFFCHANNEL",    "$bOffChannel  $b %s" },
     { "CSMSG_SET_USERINFO",      "$bUserInfo    $b %d - and above userinfos are shown." },
@@ -324,7 +337,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)" },
@@ -370,7 +383,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
     { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
     { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
-    { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
+    { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
     { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
     { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
 
@@ -429,7 +442,10 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
     { "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_SMURF_TARGET", "%s %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 */
@@ -504,6 +520,7 @@ 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." },
@@ -524,7 +541,7 @@ static const struct message_entry msgtab[] = {
     { "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 .roulette to start a new round" } ,
+    { "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" } ,
@@ -557,29 +574,29 @@ static const struct message_entry msgtab[] = {
 };
 
 /* eject_user and unban_user flags */
-#define ACTION_KICK            0x0001
-#define ACTION_BAN             0x0002
-#define ACTION_ADD_LAMER       0x0004
-#define ACTION_ADD_TIMED_LAMER         0x0008
-#define ACTION_UNBAN           0x0010
-#define ACTION_DEL_LAMER       0x0020
+#define ACTION_KICK     0x0001
+#define ACTION_BAN      0x0002
+#define ACTION_ADD_LAMER    0x0004
+#define ACTION_ADD_TIMED_LAMER  0x0008
+#define ACTION_UNBAN        0x0010
+#define ACTION_DEL_LAMER    0x0020
 
 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
-#define MODELEN                        40 + KEYLEN
-#define PADLEN                 21
-#define ACCESSLEN              10
+#define MODELEN         40 + KEYLEN
+#define PADLEN          21
+#define ACCESSLEN       10
 
-#define CSFUNC_ARGS            user, channel, argc, argv, cmd
+#define CSFUNC_ARGS     user, channel, argc, argv, cmd
 
 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
-#define CHANSERV_SYNTAX()      svccmd_send_help_brief(user, chanserv, cmd)
-#define REQUIRE_PARAMS(N)      if(argc < (N)) {            \
-       reply("MSG_MISSING_PARAMS", argv[0]); \
-       CHANSERV_SYNTAX(); \
-       return 0; }
+#define CHANSERV_SYNTAX()   svccmd_send_help_brief(user, chanserv, cmd)
+#define REQUIRE_PARAMS(N)   if(argc < (N)) {            \
+    reply("MSG_MISSING_PARAMS", argv[0]); \
+    CHANSERV_SYNTAX(); \
+    return 0; }
 
 DECLARE_LIST(dnrList, struct do_not_register *);
-DEFINE_LIST(dnrList, struct do_not_register *);
+DEFINE_LIST(dnrList, struct do_not_register *)
 
 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
 
@@ -598,26 +615,30 @@ static struct
     struct channelList  support_channels;
     struct mod_chanmode default_modes;
 
-    unsigned long      db_backup_frequency;
-    unsigned long      channel_expire_frequency;
+    unsigned long   db_backup_frequency;
+    unsigned long   channel_expire_frequency;
     unsigned long       ban_timeout_frequency;
+    unsigned long   dnr_expire_frequency;
 
-    long               info_delay;
-    unsigned int       adjust_delay;
-    long               channel_expire_delay;
+    long        info_delay;
+    unsigned int    adjust_delay;
+    long        channel_expire_delay;
     unsigned int        nodelete_level;
 
-    unsigned int       adjust_threshold;
-    int                        join_flood_threshold;
+    unsigned int    adjust_threshold;
+    int         join_flood_threshold;
 
-    unsigned int       greeting_length;
+    unsigned int    greeting_length;
     unsigned int        refresh_period;
     unsigned int        giveownership_period;
 
     unsigned int        max_owned;
-    unsigned int       max_chan_users;
-    unsigned int       max_chan_bans; /* lamers */
+    unsigned int    max_chan_users;
+    unsigned int    max_chan_bans; /* lamers */
     unsigned int        max_userinfo_length;
+    unsigned int        valid_channel_regex_set : 1;
+
+    regex_t             valid_channel_regex;
 
     struct string_list  *set_shows;
     struct string_list  *eightball;
@@ -658,7 +679,7 @@ enum note_visible_type
     NOTE_VIS_PRIVILEGED
 };
 
-struct io_fd *socket_io_fd;
+extern struct io_fd *socket_io_fd;
 extern struct cManagerNode cManager;
 
 struct note_type
@@ -690,6 +711,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, '@' },
@@ -839,7 +861,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;
@@ -877,52 +899,52 @@ _GetChannelUser(struct chanData *channel, struct handle_info *handle, int overri
     if(override && HANDLE_FLAGGED(handle, HELPING)
        && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
     {
-       for(uData = helperList;
-           uData && uData->handle != handle;
-           uData = uData->next);
+    for(uData = helperList;
+        uData && uData->handle != handle;
+        uData = uData->next);
 
-       if(!uData)
-       {
-           uData = calloc(1, sizeof(struct userData));
-           uData->handle = handle;
+    if(!uData)
+    {
+        uData = calloc(1, sizeof(struct userData));
+        uData->handle = handle;
 
-           uData->access = UL_HELPER;
-           uData->seen = 0;
+        uData->access = UL_HELPER;
+        uData->seen = 0;
 
-           uData->info = NULL;
+        uData->info = NULL;
 
-           uData->prev = NULL;
-           uData->next = helperList;
-           if(helperList)
-               helperList->prev = uData;
-           helperList = uData;
-       }
+        uData->prev = NULL;
+        uData->next = helperList;
+        if(helperList)
+        helperList->prev = uData;
+        helperList = uData;
+    }
 
-       head = &helperList;
+    head = &helperList;
     }
     else
     {
-       for(uData = channel->users; uData; uData = uData->next)
+    for(uData = channel->users; uData; uData = uData->next)
             if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
                 break;
 
-       head = &(channel->users);
+    head = &(channel->users);
     }
 
     if(uData && (uData != *head))
     {
-       /* Shuffle the user to the head of whatever list he was in. */
-       if(uData->next)
+    /* Shuffle the user to the head of whatever list he was in. */
+    if(uData->next)
             uData->next->prev = uData->prev;
-       if(uData->prev)
+    if(uData->prev)
             uData->prev->next = uData->next;
 
-       uData->prev = NULL;
-       uData->next = *head;
+    uData->prev = NULL;
+    uData->next = *head;
 
-       if(*head)
-           (**head).prev = uData;
-       *head = uData;
+    if(*head)
+        (**head).prev = uData;
+    *head = uData;
     }
 
     return uData;
@@ -973,7 +995,7 @@ scan_user_presence(struct userData *uData, struct userNode *user)
 }
 
 static void
-chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
+chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice), UNUSED_ARG(void *extra))
 {
     unsigned int eflags, argc;
     char *argv[4];
@@ -988,15 +1010,15 @@ chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text,
     /* We dont punish people we know -Rubin 
      *    * Figure out the minimum level needed to CTCP the channel *
      *
-     *     if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
-     *         return;
+     *      if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
+     *      return;
      */
     /* If they are a user of the channel, they are exempt */
     if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
-           return;
+        return;
     /* We need to enforce against them; do so. */
     eflags = 0;
-    argv[0] = text;
+    argv[0] = (char*)text;
     argv[1] = user->nick;
     argc = 2;
     if(GetUserMode(channel, user))
@@ -1038,7 +1060,7 @@ chanserv_deref_note_type(void *data)
     struct note_type *ntype = data;
 
     if(--ntype->refs > 0)
-       return;
+    return;
     free(ntype);
 }
 
@@ -1061,7 +1083,7 @@ chanserv_truncate_notes(struct note_type *ntype)
         note = dict_find(cData->notes, ntype->name, NULL);
         if(!note)
             continue;
-       if(strlen(note->note) <= ntype->max_length)
+    if(strlen(note->note) <= ntype->max_length)
             continue;
         dict_remove2(cData->notes, ntype->name, 1);
         note = realloc(note, size);
@@ -1106,7 +1128,7 @@ static MODCMD_FUNC(cmd_createnote) {
     if((ntype = dict_find(note_types, argv[1], NULL)))
         existed = 1;
     else
-       ntype = chanserv_create_note_type(argv[arg]);
+    ntype = chanserv_create_note_type(argv[arg]);
     if(!irccasecmp(argv[++arg], "privileged"))
     {
         arg++;
@@ -1119,7 +1141,7 @@ static MODCMD_FUNC(cmd_createnote) {
         if(!ulvl)
         {
             reply("CSMSG_INVALID_ACCESS", argv[arg]);
-           goto fail;
+        goto fail;
         }
         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
         ntype->set_access.min_ulevel = ulvl;
@@ -1131,7 +1153,7 @@ static MODCMD_FUNC(cmd_createnote) {
     else
     {
         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
-       goto fail;
+    goto fail;
     }
 
     if(!irccasecmp(argv[++arg], "privileged"))
@@ -1142,23 +1164,23 @@ static MODCMD_FUNC(cmd_createnote) {
         ntype->visible_type = NOTE_VIS_ALL;
     else {
         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
-       goto fail;
+    goto fail;
     }
 
     if((arg+1) >= argc) {
         reply("MSG_MISSING_PARAMS", argv[0]);
-       goto fail;
+    goto fail;
     }
     max_length = strtoul(argv[++arg], NULL, 0);
     if(max_length < 20 || max_length > 450)
     {
         reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
-       goto fail;
+    goto fail;
     }
     if(existed && (max_length < ntype->max_length))
     {
-       ntype->max_length = max_length;
-       chanserv_truncate_notes(ntype);
+    ntype->max_length = max_length;
+    chanserv_truncate_notes(ntype);
     }
     ntype->max_length = max_length;
 
@@ -1170,7 +1192,7 @@ static MODCMD_FUNC(cmd_createnote) {
 
 fail:
     if(!existed)
-       dict_remove(note_types, ntype->name);
+    dict_remove(note_types, ntype->name);
     return 0;
 }
 
@@ -1287,7 +1309,7 @@ register_channel(struct chanNode *cNode, char *registrar)
     channel->next = channelList;
 
     if(channelList)
-       channelList->prev = channel;
+    channelList->prev = channel;
     channelList = channel;
     registered_channels++;
 
@@ -1299,18 +1321,18 @@ register_channel(struct chanNode *cNode, char *registrar)
 }
 
 static struct userData*
-add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info, time_t accessexpiry)
+add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, time_t seen, const char *info, time_t accessexpiry)
 {
     struct userData *ud;
 
-    if(access > UL_OWNER)
-       return NULL;
+    if(access_level > UL_OWNER)
+    return NULL;
 
     ud = calloc(1, sizeof(*ud));
     ud->channel = channel;
     ud->handle = handle;
     ud->seen = seen;
-    ud->access = access;
+    ud->access = access_level;
     ud->info = info ? strdup(info) : NULL;
     ud->accessexpiry = accessexpiry ? accessexpiry : 0;
     ud->clvlexpiry = 0;
@@ -1319,7 +1341,7 @@ add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned
     ud->prev = NULL;
     ud->next = channel->users;
     if(channel->users)
-       channel->users->prev = ud;
+    channel->users->prev = ud;
     channel->users = ud;
 
     channel->userCount++;
@@ -1627,12 +1649,12 @@ add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t
     bd->reason = strdup(reason);
 
     if(expires)
-       timeq_add(expires, expire_ban, bd);
+    timeq_add(expires, expire_ban, bd);
 
     bd->prev = NULL;
     bd->next = channel->bans; /* lamers */
     if(channel->bans)
-       channel->bans->prev = bd;
+    channel->bans->prev = bd;
     channel->bans = bd;
     channel->banCount++;
     banCount++;
@@ -1655,7 +1677,7 @@ del_channel_ban(struct banData *ban)
         ban->next->prev = ban->prev;
 
     if(ban->expires)
-       timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
+    timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
 
     if(ban->reason)
         free(ban->reason);
@@ -1677,7 +1699,7 @@ expire_ban(void *data) /* lamer.. */
         for(ii=0; ii<bans.used; ii++)
         {
             if(!strcmp(bans.list[ii]->ban, bd->mask))
-           {
+        {
                 change.argc = 1;
                 change.args[0].mode = MODE_REMOVE|MODE_BAN;
                 change.args[0].u.hostmask = bd->mask;
@@ -1709,7 +1731,7 @@ unregister_channel(struct chanData *channel, const char *reason)
     */
 
     if(!channel)
-       return;
+    return;
 
     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
 
@@ -1723,10 +1745,10 @@ unregister_channel(struct chanData *channel, const char *reason)
     wipe_adduser_pending(channel->channel, NULL);
 
     while(channel->users)
-       del_channel_user(channel->users, 0);
+    del_channel_user(channel->users, 0);
 
     while(channel->bans)
-       del_channel_ban(channel->bans);
+    del_channel_ban(channel->bans);
 
     free(channel->topic);
     free(channel->registrar);
@@ -1744,8 +1766,8 @@ unregister_channel(struct chanData *channel, const char *reason)
 
     if(channel->suspended)
     {
-       struct chanNode *cNode = channel->channel;
-       struct suspended *suspended, *next_suspended;
+    struct chanNode *cNode = channel->channel;
+    struct suspended *suspended, *next_suspended;
 
         for(suspended = channel->suspended; suspended; suspended = next_suspended)
         {
@@ -1757,8 +1779,8 @@ unregister_channel(struct chanData *channel, const char *reason)
             free(suspended);
         }
 
-       if(cNode)
-           cNode->channel_info = NULL;
+    if(cNode)
+        cNode->channel_info = NULL;
     }
     channel->channel->channel_info = NULL;
 
@@ -1784,7 +1806,7 @@ expire_channels(UNUSED_ARG(void *data))
 
     for(channel = channelList; channel; channel = next)
     {
-       next = channel->next;
+    next = channel->next;
 
         /* See if the channel can be expired. */
         if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
@@ -1805,7 +1827,39 @@ expire_channels(UNUSED_ARG(void *data))
     }
 
     if(chanserv_conf.channel_expire_frequency)
-       timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
+    timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
+}
+
+static void
+expire_dnrs(UNUSED_ARG(void *data))
+{
+    dict_iterator_t it, next;
+    struct do_not_register *dnr;
+
+    for(it = dict_first(handle_dnrs); it; it = next)
+    {
+        dnr = iter_data(it);
+        next = iter_next(it);
+        if(dnr->expires && dnr->expires <= now)
+            dict_remove(handle_dnrs, dnr->chan_name + 1);
+    }
+    for(it = dict_first(plain_dnrs); it; it = next)
+    {
+        dnr = iter_data(it);
+        next = iter_next(it);
+        if(dnr->expires && dnr->expires <= now)
+            dict_remove(plain_dnrs, dnr->chan_name + 1);
+    }
+    for(it = dict_first(mask_dnrs); it; it = next)
+    {
+        dnr = iter_data(it);
+        next = iter_next(it);
+        if(dnr->expires && dnr->expires <= now)
+            dict_remove(mask_dnrs, dnr->chan_name + 1);
+    }
+
+    if(chanserv_conf.dnr_expire_frequency)
+        timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
 }
 
 static int
@@ -1833,7 +1887,7 @@ protect_user(const struct userNode *victim, const struct userNode *aggressor, st
         return 1;
 
     if(protect == 'n')
-       return 0;
+    return 0;
 
     if(protect != 'a' && !cs_victim)
         return 0;
@@ -1857,14 +1911,14 @@ protect_user(const struct userNode *victim, const struct userNode *aggressor, st
     switch(protect)
     {
     case 'l':
-       if(cs_victim->access > cs_aggressor->access)
+    if(cs_victim->access > cs_aggressor->access)
             return 1;
-       break;
+    break;
     case 'a':
     case 'e':
-       if(cs_victim->access >= cs_aggressor->access)
+    if(cs_victim->access >= cs_aggressor->access)
             return 1;
-       break;
+    break;
     }
 
     return 0;
@@ -1881,10 +1935,10 @@ validate_op(struct svccmd *cmd, struct userNode *user, struct chanNode *channel,
        && !check_user_level(channel, user, lvlEnfOps, 0, 0))
     {
         if(cmd)
-           reply("CSMSG_OPBY_LOCKED");
+        reply("CSMSG_OPBY_LOCKED");
         else
             send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
-       return 0;
+    return 0;
     }
 
     return 1;
@@ -1913,14 +1967,14 @@ validate_deop(struct svccmd *cmd, struct userNode *user, struct chanNode *channe
 {
     if(IsService(victim))
     {
-       reply("MSG_SERVICE_IMMUNE", victim->nick);
-       return 0;
+    reply("MSG_SERVICE_IMMUNE", victim->nick);
+    return 0;
     }
 
     if(protect_user(victim, user, channel->channel_info, false))
     {
-       reply("CSMSG_USER_PROTECTED", victim->nick);
-       return 0;
+    reply("CSMSG_USER_PROTECTED", victim->nick);
+    return 0;
     }
 
     return 1;
@@ -1945,13 +1999,14 @@ 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, const char *reason)
+chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, 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, "*?"))
@@ -1962,44 +2017,78 @@ chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
 }
 
 static struct dnrList
-chanserv_find_dnrs(const char *chan_name, const char *handle)
+chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
 {
     struct dnrList list;
-    dict_iterator_t it;
+    dict_iterator_t it, next;
     struct do_not_register *dnr;
 
     dnrList_init(&list);
+
     if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
-        dnrList_append(&list, dnr);
+    {
+        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)))
-        dnrList_append(&list, dnr);
+    {
+        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; it = iter_next(it))
-            if(match_ircglob(chan_name, iter_key(it)))
-                dnrList_append(&list, iter_data(it));
+    {
+        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);
+        }
+    }
     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);
+    list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
     for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
-    {
-        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);
-    }
+        dnr_print_func(list.list[ii], user);
     if(ii < list.used)
         reply("CSMSG_MORE_DNRS", list.used - ii);
     free(list.list);
@@ -2009,65 +2098,50 @@ 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;
-    dict_iterator_t it;
 
-    if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
-        return dnr;
-    if(chan_name)
+    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;
+
+    for(it = dict_first(dict); it; it = next)
     {
-        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);
+        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++;
     }
-    return NULL;
+
+    return matches;
 }
 
 static CHANSERV_FUNC(cmd_noregister)
 {
     const char *target;
-    struct do_not_register *dnr;
-    char buf[INTERVALLEN];
+    const char *reason;
+    time_t expiry, duration;
     unsigned int matches;
 
     if(argc < 2)
     {
-        dict_iterator_t it;
-
         reply("CSMSG_DNR_SEARCH_RESULTS");
-        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++;
-        }
-
+        matches = send_dnrs(user, handle_dnrs);
+        matches += send_dnrs(user, plain_dnrs);
+        matches += send_dnrs(user, mask_dnrs);
         if(matches)
             reply("MSG_MATCH_COUNT", matches);
         else
@@ -2085,13 +2159,30 @@ static CHANSERV_FUNC(cmd_noregister)
 
     if(argc > 2)
     {
-        const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
+        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;
+        }
+
+        reason = unsplit_string(argv + 3, argc - 3, NULL);
+
         if((*target == '*') && !get_handle_info(target + 1))
         {
             reply("MSG_HANDLE_UNKNOWN", target + 1);
             return 0;
         }
-        chanserv_add_dnr(target, user->handle_info->handle, reason);
+        chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
         reply("CSMSG_NOREGISTER_CHANNEL", target);
         return 1;
     }
@@ -2112,26 +2203,264 @@ static CHANSERV_FUNC(cmd_allowregister)
 {
     const char *chan_name = argv[1];
 
-    if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
+    if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
+       || dict_remove(plain_dnrs, chan_name)
+       || dict_remove(mask_dnrs, chan_name))
     {
-        dict_remove(handle_dnrs, chan_name+1);
         reply("CSMSG_DNR_REMOVED", chan_name);
+        return 1;
     }
-    else if(dict_find(plain_dnrs, chan_name, NULL))
+    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 < search->min_expires)
+             || (search->max_expires
+                 && ((dnr->expires == 0)
+                     || (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->limit = 50;
+
+    for(ii=0; ii<argc; ++ii)
     {
-        dict_remove(plain_dnrs, chan_name);
-        reply("CSMSG_DNR_REMOVED", chan_name);
+        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;
+        }
     }
-    else if(dict_find(mask_dnrs, chan_name, NULL))
+    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)
     {
-        dict_remove(mask_dnrs, chan_name);
-        reply("CSMSG_DNR_REMOVED", chan_name);
+        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);
+    }
+    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)
+    {
+        reply("CSMSG_DNR_BAD_ACTION", argv[1]);
+        return 0;
     }
+    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_NO_SUCH_DNR", chan_name);
+        reply("CSMSG_DNR_BAD_ACTION", argv[1]);
         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;
 }
 
@@ -2212,13 +2541,13 @@ static CHANSERV_FUNC(cmd_register)
 
     if(argc >= (new_channel+2))
     {
-       if(!IsHelping(user))
-       {
-           reply("CSMSG_PROXY_FORBIDDEN");
-           return 0;
-       }
+    if(!IsHelping(user))
+    {
+        reply("CSMSG_PROXY_FORBIDDEN");
+        return 0;
+    }
 
-       if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
+    if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
             return 0;
         force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
         dnr = chanserv_is_dnr(chan_name, handle);
@@ -2270,8 +2599,21 @@ static CHANSERV_FUNC(cmd_register)
     }
     */
 
+    if (chanserv_conf.valid_channel_regex_set) {
+        int err = regexec(&chanserv_conf.valid_channel_regex, chan_name, 0, 0, 0);
+        if (err) {
+            char buff[256];
+            buff[regerror(err, &chanserv_conf.valid_channel_regex, buff, sizeof(buff))] = 0;
+            log_module(CS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
+        }
+        if(err == REG_NOMATCH) {
+            reply("CSMSG_ILLEGAL_CHANNEL", chan_name);
+            return 0;
+        }
+    }
+
     if(new_channel)
-        channel = AddChannel(argv[1], now, NULL, NULL, NULL);
+        channel = AddChannel(chan_name, now, NULL, NULL, NULL);
 
     cData = register_channel(channel, user->handle_info->handle);
     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL);
@@ -2359,11 +2701,11 @@ static CHANSERV_FUNC(cmd_unregister)
             return 0;
         }
         confirm_string = make_confirmation_string(uData);
-       if((argc < 2) || strcmp(argv[1], confirm_string))
-       {
-           reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
-           return 0;
-       }
+    if((argc < 2) || strcmp(argv[1], confirm_string))
+    {
+        reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
+        return 0;
+    }
     }
 
     sprintf(reason, "unregistered by %s.", user->handle_info->handle);
@@ -2415,8 +2757,8 @@ static CHANSERV_FUNC(cmd_move)
 
     if(IsProtected(channel->channel_info))
     {
-       reply("CSMSG_MOVE_NODELETE", channel->name);
-       return 0;
+    reply("CSMSG_MOVE_NODELETE", channel->name);
+    return 0;
     }
 
     if(!IsChannelName(argv[1]))
@@ -2455,8 +2797,8 @@ static CHANSERV_FUNC(cmd_move)
     }
     else if(target->channel_info)
     {
-       reply("CSMSG_ALREADY_REGGED", target->name);
-       return 0;
+    reply("CSMSG_ALREADY_REGGED", target->name);
+    return 0;
     }
     else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
             && !IsHelping(user))
@@ -2517,7 +2859,7 @@ merge_users(struct chanData *source, struct chanData *target)
 
     /* Insert the source's users into the scratch area. */
     for(suData = source->users; suData; suData = suData->next)
-       dict_insert(merge, suData->handle->handle, suData);
+        dict_insert(merge, suData->handle->handle, suData);
 
     /* Iterate through the target's users, looking for
        users common to both channels. The lower access is
@@ -2525,50 +2867,50 @@ merge_users(struct chanData *source, struct chanData *target)
        list. */
     for(tuData = target->users; tuData; tuData = next)
     {
-       struct userData *choice;
+        struct userData *choice;
 
-       next = tuData->next;
+        next = tuData->next;
 
-       /* If a source user exists with the same handle as a target
-          channel's user, resolve the conflict by removing one. */
-       suData = dict_find(merge, tuData->handle->handle, NULL);
-       if(!suData)
-           continue;
+        /* If a source user exists with the same handle as a target
+           channel's user, resolve the conflict by removing one. */
+        suData = dict_find(merge, tuData->handle->handle, NULL);
+        if(!suData)
+            continue;
 
-       /* Pick the data we want to keep. */
-        /* If the access is the same, use the later seen time. */
-       if(suData->access == tuData->access)
-           choice = (suData->seen > tuData->seen) ? suData : tuData;
-       else /* Otherwise, keep the higher access level. */
-           choice = (suData->access > tuData->access) ? suData : tuData;
+        /* Pick the data we want to keep. */
+            /* If the access is the same, use the later seen time. */
+        if(suData->access == tuData->access)
+            choice = (suData->seen > tuData->seen) ? suData : tuData;
+        else /* Otherwise, keep the higher access level. */
+            choice = (suData->access > tuData->access) ? suData : tuData;
 
-       /* Remove the user that wasn't picked. */
-       if(choice == tuData)
-       {
-           dict_remove(merge, suData->handle->handle);
-           del_channel_user(suData, 0);
-       }
-       else
-           del_channel_user(tuData, 0);
+        /* Remove the user that wasn't picked. */
+        if(choice == tuData)
+        {
+            dict_remove(merge, suData->handle->handle);
+            del_channel_user(suData, 0);
+        }
+        else
+            del_channel_user(tuData, 0);
     }
 
     /* Move the remaining users to the target channel. */
     for(it = dict_first(merge); it; it = iter_next(it))
     {
-       suData = iter_data(it);
+        suData = iter_data(it);
 
-       /* Insert the user into the target channel's linked list. */
-       suData->prev = NULL;
-       suData->next = target->users;
+        /* Insert the user into the target channel's linked list. */
+        suData->prev = NULL;
+        suData->next = target->users;
         suData->channel = target;
 
-       if(target->users)
-           target->users->prev = suData;
-       target->users = suData;
+        if(target->users)
+            target->users->prev = suData;
+        target->users = suData;
 
-       /* Update the user counts for the target channel; the
-          source counts are left alone. */
-       target->userCount++;
+        /* Update the user counts for the target channel; the
+       source counts are left alone. */
+        target->userCount++;
     }
 
     /* Possible to assert (source->users == NULL) here. */
@@ -2588,72 +2930,72 @@ merge_bans(struct chanData *source, struct chanData *target)
     /* Perform a totally expensive O(n*m) merge, ick. */
     for(sbData = source->bans; sbData; sbData = sNext)
     {
-       /* Flag to track whether the ban's been moved
-          to the destination yet. */
-       int moved = 0;
-
-       /* Possible to assert (sbData->prev == NULL) here. */
-       sNext = sbData->next;
-
-       for(tbData = tFront; tbData; tbData = tNext)
-       {
-           tNext = tbData->next;
-
-           /* Perform two comparisons between each source
-              and target ban, conflicts are resolved by
-              keeping the broader ban and copying the later
-              expiration and triggered time. */
-           if(match_ircglobs(tbData->mask, sbData->mask))
-           {
-               /* There is a broader ban in the target channel that
-                  overrides one in the source channel; remove the 
-                  source ban and break. */
-               if(sbData->expires > tbData->expires)
-                   tbData->expires = sbData->expires;
-               if(sbData->triggered > tbData->triggered)
-                   tbData->triggered = sbData->triggered;
-               del_channel_ban(sbData);
-               break;
-           }
-           else if(match_ircglobs(sbData->mask, tbData->mask))
-           {
-               /* There is a broader ban in the source channel that
-                  overrides one in the target channel; remove the
-                  target ban, fall through and move the source over. */
-               if(tbData->expires > sbData->expires)
-                   sbData->expires = tbData->expires;
-               if(tbData->triggered > sbData->triggered)
-                   sbData->triggered = tbData->triggered;
-               if(tbData == tFront)
-                   tFront = tNext;
-               del_channel_ban(tbData);
-           }
-
-           /* Source bans can override multiple target bans, so
-              we allow a source to run through this loop multiple
-              times, but we can only move it once. */
-           if(moved)
-               continue;
-           moved = 1;
-
-           /* Remove the source ban from the source ban list. */
-           if(sbData->next)
-               sbData->next->prev = sbData->prev;
-
-           /* Modify the source ban's associated channel. */
-           sbData->channel = target;
-
-           /* Insert the ban into the target channel's linked list. */
-           sbData->prev = NULL;
-           sbData->next = target->bans;
-
-           if(target->bans)
-               target->bans->prev = sbData;
-           target->bans = sbData;
-
-           /* Update the user counts for the target channel. */
-           target->banCount++;
-       }
+        /* Flag to track whether the ban's been moved
+           to the destination yet. */
+        int moved = 0;
+
+        /* Possible to assert (sbData->prev == NULL) here. */
+        sNext = sbData->next;
+
+        for(tbData = tFront; tbData; tbData = tNext)
+        {
+            tNext = tbData->next;
+
+            /* Perform two comparisons between each source
+               and target ban, conflicts are resolved by
+               keeping the broader ban and copying the later
+               expiration and triggered time. */
+            if(match_ircglobs(tbData->mask, sbData->mask))
+            {
+                /* There is a broader ban in the target channel that
+                   overrides one in the source channel; remove the 
+                   source ban and break. */
+                if(sbData->expires > tbData->expires)
+                    tbData->expires = sbData->expires;
+                if(sbData->triggered > tbData->triggered)
+                    tbData->triggered = sbData->triggered;
+                del_channel_ban(sbData);
+                break;
+            }
+            else if(match_ircglobs(sbData->mask, tbData->mask))
+            {
+                /* There is a broader ban in the source channel that
+                   overrides one in the target channel; remove the
+                   target ban, fall through and move the source over. */
+                if(tbData->expires > sbData->expires)
+                    sbData->expires = tbData->expires;
+                if(tbData->triggered > sbData->triggered)
+                    sbData->triggered = tbData->triggered;
+                if(tbData == tFront)
+                    tFront = tNext;
+                del_channel_ban(tbData);
+            }
+
+            /* Source bans can override multiple target bans, so
+               we allow a source to run through this loop multiple
+               times, but we can only move it once. */
+            if(moved)
+            continue;
+            moved = 1;
+
+            /* Remove the source ban from the source ban list. */
+            if(sbData->next)
+            sbData->next->prev = sbData->prev;
+
+            /* Modify the source ban's associated channel. */
+            sbData->channel = target;
+
+            /* Insert the ban into the target channel's linked list. */
+            sbData->prev = NULL;
+            sbData->next = target->bans;
+
+            if(target->bans)
+            target->bans->prev = sbData;
+            target->bans = sbData;
+
+            /* Update the user counts for the target channel. */
+            target->banCount++;
+        }
     }
 
     /* Possible to assert (source->bans == NULL) here. */
@@ -2668,7 +3010,7 @@ merge_data(struct chanData *source, struct chanData *target)
      * Do not touch last_refresh, ban count or user counts.
      */
     if(source->visited > target->visited)
-       target->visited = source->visited;
+    target->visited = source->visited;
     if(source->registered < target->registered)
         target->registered = source->registered;
     if(source->ownerTransfer > target->ownerTransfer)
@@ -2692,6 +3034,7 @@ static CHANSERV_FUNC(cmd_merge)
     struct userData *target_user;
     struct chanNode *target;
     char reason[MAXLEN];
+    int nodelete = 0;
 
     REQUIRE_PARAMS(2);
 
@@ -2703,6 +3046,11 @@ static CHANSERV_FUNC(cmd_merge)
         return 0;
     }
 
+    if (argc > 2) {
+        if (!irccasecmp("nodelete", argv[2]))
+            nodelete = 1;
+    }
+
     if(!target->channel_info)
     {
         reply("CSMSG_NOT_REGISTERED", target->name);
@@ -2738,7 +3086,8 @@ static CHANSERV_FUNC(cmd_merge)
     merge_channel(channel->channel_info, target->channel_info);
     spamserv_cs_move_merge(user, channel, target, 0);
     sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
-    unregister_channel(channel->channel_info, reason);
+    if (!nodelete)
+        unregister_channel(channel->channel_info, reason);
     reply("CSMSG_MERGE_SUCCESS", target->name);
     return 1;
 }
@@ -2751,11 +3100,20 @@ 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;
     change.args[0].mode = MODE_CHANOP;
     change.args[0].u.member = GetUserMode(channel, chanserv);
+    if(!change.args[0].u.member)
+    {
+        reply("CSMSG_OUT_OF_CHANNEL", channel->name);
+        return 0;
+    }
     mod_chanmode_announce(chanserv, channel, &change);
     reply("CSMSG_OPCHAN_DONE", channel->name);
     return 1;
@@ -2764,35 +3122,42 @@ static CHANSERV_FUNC(cmd_opchan)
 static CHANSERV_FUNC(cmd_adduser)
 {
     struct userData *actee;
-    struct userData *actor;
-    struct handle_info *handle;
-    unsigned short access;
+    struct userData *actor, *real_actor;
+    struct handle_info *handle = NULL;
+    //struct adduserPending *tmp;
+    unsigned short access_level, override = 0;
 
     REQUIRE_PARAMS(3);
 
     if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
     {
-       reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
-       return 0;
+        reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
+        return 0;
     }
 
-    access = user_level_from_name(argv[2], UL_OWNER);
-    if(!access)
+    access_level = user_level_from_name(argv[2], UL_OWNER);
+    if(!access_level)
     {
-       reply("CSMSG_INVALID_ACCESS", argv[2]);
-       return 0;
+        reply("CSMSG_INVALID_ACCESS", argv[2]);
+        return 0;
     }
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-    if(actor->access <= access)
+    real_actor = GetTrueChannelAccess(channel->channel_info, user->handle_info);
+
+    if(actor->access <= access_level)
     {
-       reply("CSMSG_NO_BUMP_ACCESS");
-       return 0;
+        reply("CSMSG_NO_BUMP_ACCESS");
+        return 0;
     }
 
+    /* Trying to add someone with equal/more access */
+    if (!real_actor || real_actor->access <= access_level)
+        override = CMD_LOG_OVERRIDE;
+
     if(!(handle = modcmd_get_handle_info(user, argv[1])))
     {
-        // 'kevin must first authenticate with AuthServ.' is sent to user
+        /* 'kevin must first authenticate with AuthServ'. is sent to user */
         struct userNode *unode;
         unode = GetUserH(argv[1]); /* find user struct by nick */
         if(unode)
@@ -2802,8 +3167,8 @@ static CHANSERV_FUNC(cmd_adduser)
             }
             else {
                 if(IsInChannel(channel, unode)) {
-                   reply("CSMSG_ADDUSER_PENDING");
-                   add_adduser_pending(channel, unode, access);
+                   reply("CSMSG_ADDUSER_PENDING", unode->nick);
+                   add_adduser_pending(channel, unode, access_level);
                    send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
                 }
                 /* this results in user must auth AND not in chan errors. too confusing..
@@ -2818,8 +3183,8 @@ static CHANSERV_FUNC(cmd_adduser)
 
     if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
     {
-       reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
-       return 0;
+        reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
+        return 0;
     }
 
     time_t accessexpiry = 0;
@@ -2829,61 +3194,62 @@ static CHANSERV_FUNC(cmd_adduser)
             accessexpiry = now + duration;
     }
 
-    actee = add_channel_user(channel->channel_info, handle, access, 0, NULL, accessexpiry);
+    actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL, accessexpiry);
     scan_user_presence(actee, NULL);
 
     if (duration > 0)
         timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
 
-    reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
-    return 1;
+    reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access_level), access_level);
+    return 1 | override;
 }
 
 static CHANSERV_FUNC(cmd_clvl)
 {
     struct handle_info *handle;
     struct userData *victim;
-    struct userData *actor;
-    unsigned short new_access;
+    struct userData *actor, *real_actor;
+    unsigned short new_access, override = 0;
     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;
 
     if(handle == user->handle_info && !privileged)
     {
-       reply("CSMSG_NO_SELF_CLVL");
-       return 0;
+        reply("CSMSG_NO_SELF_CLVL");
+        return 0;
     }
 
     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
     {
-       reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
-       return 0;
+        reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
+        return 0;
     }
 
     if(actor->access <= victim->access && !privileged)
     {
-       reply("MSG_USER_OUTRANKED", handle->handle);
-       return 0;
+        reply("MSG_USER_OUTRANKED", handle->handle);
+        return 0;
     }
 
     new_access = user_level_from_name(argv[2], UL_OWNER);
 
     if(!new_access)
     {
-       reply("CSMSG_INVALID_ACCESS", argv[2]);
-       return 0;
+        reply("CSMSG_INVALID_ACCESS", argv[2]);
+        return 0;
     }
 
     if(new_access >= actor->access && !privileged)
     {
-       reply("CSMSG_NO_BUMP_ACCESS");
-       return 0;
+        reply("CSMSG_NO_BUMP_ACCESS");
+        return 0;
     }
 
     time_t clvlexpiry = 0;
@@ -2904,72 +3270,90 @@ 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;
+    return 1 | override;
 }
 
 static CHANSERV_FUNC(cmd_deluser)
 {
     struct handle_info *handle;
     struct userData *victim;
-    struct userData *actor;
-    unsigned short access;
+    struct userData *actor, *real_actor;
+    unsigned short access_level, override = 0;
+    unsigned short access_level_user = 0;
     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;
 
     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
     {
-       reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
-       return 0;
+        reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
+        return 0;
     }
 
     if(argc > 2)
     {
-        access = user_level_from_name(argv[1], UL_OWNER);
+        access_level = user_level_from_name(argv[1], UL_OWNER);
         char *useraccess = user_level_name_from_level(victim->access);
-        if(!access)
+        access_level_user = user_level_from_name(useraccess, UL_OWNER);
+        if(!access_level)
         {
             reply("CSMSG_INVALID_ACCESS", argv[1]);
             return 0;
         }
-       if(strcasecmp(argv[1], useraccess))
-       {
-           reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
-           return 0;
-       }
+        if(access_level != access_level_user)
+        {
+            reply("CSMSG_INCORRECT_ACCESS", handle->handle, useraccess, argv[1]);
+            return 0;
+        }
     }
     else
     {
-        access = victim->access;
+        access_level = victim->access;
     }
 
     if((actor->access <= victim->access) && !IsHelping(user))
     {
-       reply("MSG_USER_OUTRANKED", victim->handle->handle);
-       return 0;
+        reply("MSG_USER_OUTRANKED", victim->handle->handle);
+        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);
+    reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
     free(chan_name);
-    return 1;
+    return 1 | override;
 }
 
 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, *uData, *next;
+    struct userData *actor, *real_actor, *uData, *next;
+    unsigned int override = 0;
 
     actor = GetChannelUser(channel->channel_info, user->handle_info);
-
+    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
     if(min_access > max_access)
     {
         reply("CSMSG_BAD_RANGE", min_access, max_access);
@@ -2978,22 +3362,27 @@ cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short mi
 
     if((actor->access <= max_access) && !IsHelping(user))
     {
-       reply("CSMSG_NO_ACCESS");
-       return 0;
+    reply("CSMSG_NO_ACCESS");
+    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;
+        next = uData->next;
 
-       if((uData->access >= min_access)
-           && (uData->access <= max_access)
-           && match_ircglob(uData->handle->handle, mask))
-           del_channel_user(uData, 1);
+        if((uData->access >= min_access) 
+               && (uData->access <= max_access)
+               && match_ircglob(uData->handle->handle, mask))
+        {
+            del_channel_user(uData, 1);
+        }
     }
 
     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
-    return 1;
+    return 1 | override;
 }
 
 static CHANSERV_FUNC(cmd_mdelowner)
@@ -3026,6 +3415,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)
 {
@@ -3106,7 +3499,7 @@ cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channe
     limit = now - duration;
     for(bData = channel->channel_info->bans; bData; bData = next)
     {
-       next = bData->next;
+        next = bData->next;
 
         if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
             continue;
@@ -3128,36 +3521,36 @@ cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *chann
     unsigned int count;
     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->access <= max_access) && !IsHelping(user))
+    if(!actor || actor->access <= max_access)
     {
-       reply("CSMSG_NO_ACCESS");
-       return 0;
+        reply("CSMSG_NO_ACCESS");
+        return 0;
     }
 
     count = 0;
     limit = now - duration;
     for(uData = channel->channel_info->users; uData; uData = next)
     {
-       next = uData->next;
+        next = uData->next;
 
-       if((uData->seen > limit)
+        if((uData->seen > limit)
            || uData->present
            || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
-           continue;
+        continue;
 
-       if(((uData->access >= min_access) && (uData->access <= max_access))
-           || (!max_access && (uData->access < actor->access)))
-       {
-           del_channel_user(uData, 1);
-           count++;
-       }
+        if(((uData->access >= min_access) && (uData->access <= max_access))
+               || (!max_access && (uData->access < actor->access)))
+        {
+            del_channel_user(uData, 1);
+            count++;
+        }
     }
 
     if(!max_access)
@@ -3181,29 +3574,29 @@ static CHANSERV_FUNC(cmd_trim)
     duration = ParseInterval(argv[2]);
     if(duration < 60)
     {
-       reply("CSMSG_CANNOT_TRIM");
-       return 0;
+        reply("CSMSG_CANNOT_TRIM");
+        return 0;
     }
 
     if(!irccasecmp(argv[1], "lamers"))
     {
-       cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
-       return 1;
+        cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
+        return 1;
     }
     else if(!irccasecmp(argv[1], "users"))
     {
-       cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
-       return 1;
+        cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
+        return 1;
     }
     else if(parse_level_range(&min_level, &max_level, argv[1]))
     {
-       cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
-       return 1;
+        cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
+        return 1;
     }
     else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
     {
-       cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
-       return 1;
+        cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
+        return 1;
     }
     else
     {
@@ -3227,7 +3620,7 @@ static CHANSERV_FUNC(cmd_up)
     {
         if(argc)
             reply("MSG_CHANNEL_ABSENT", channel->name);
-       return 0;
+        return 0;
     }
 
     uData = GetChannelAccess(channel->channel_info, user->handle_info);
@@ -3280,14 +3673,14 @@ static CHANSERV_FUNC(cmd_down)
     {
         if(argc)
             reply("MSG_CHANNEL_ABSENT", channel->name);
-       return 0;
+    return 0;
     }
 
     if(!change.args[0].u.member->modes)
     {
         if(argc)
             reply("CSMSG_ALREADY_DOWN", channel->name);
-       return 0;
+    return 0;
     }
 
     change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
@@ -3301,12 +3694,12 @@ static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel),
 
     for(cList = user->handle_info->channels; cList; cList = cList->u_next)
     {
-       if(IsSuspended(cList->channel)
-           || IsUserSuspended(cList)
-           || !GetUserMode(cList->channel->channel, user))
-           continue;
+        if(IsSuspended(cList->channel)
+               || IsUserSuspended(cList)
+               || !GetUserMode(cList->channel->channel, user))
+            continue;
 
-       mcmd(user, cList->channel->channel, 0, NULL, cmd);
+        mcmd(user, cList->channel->channel, 0, NULL, cmd);
     }
 
     return 1;
@@ -3336,20 +3729,20 @@ modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc,
 
     for(ii=valid=0; ++ii < argc; )
     {
-       if(!(victim = GetUserH(argv[ii])))
+        if(!(victim = GetUserH(argv[ii])))
             continue;
         change->args[valid].mode = mode;
         change->args[valid].u.member = GetUserMode(channel, victim);
         if(!change->args[valid].u.member)
             continue;
         if(validate && !validate(cmd, user, channel, victim))
-           continue;
+        continue;
         valid++;
     }
 
     change->argc = valid;
     if(valid < (argc-1))
-       reply("CSMSG_PROCESS_FAILED");
+    reply("CSMSG_PROCESS_FAILED");
     if(valid)
     {
         modcmd_chanmode_announce(change);
@@ -3393,6 +3786,7 @@ static int
 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
 {
     unsigned int ii;
+    int b = 0;
 
     if(victimCount)
         *victimCount = 0;
@@ -3403,7 +3797,10 @@ bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban
         if(IsService(mn->user))
             continue;
 
-        if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
+        b = user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE, 0);
+        if (b == -1)
+            return -1;
+        else if (b == 0)
             continue;
 
         if(protect_user(mn->user, user, channel->channel_info, false))
@@ -3415,12 +3812,20 @@ bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban
     return 0;
 }
 
+int is_extban(char *b) {
+    if(*b == '~') {
+        return 1;
+    }
+    return 0;
+}
+
 static int
 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
 {
     struct userNode *victim;
     struct modeNode **victims;
     unsigned int offset, n, victimCount, duration = 0;
+    int b = 0;
     char *reason = "Bye.", *ban, *name;
     char interval[INTERVALLEN];
 
@@ -3428,7 +3833,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     REQUIRE_PARAMS(offset);
     if(argc > offset)
     {
-       reason = unsplit_string(argv + offset, argc - offset, NULL);
+        reason = unsplit_string(argv + offset, argc - offset, NULL);
         if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
         {
             /* Truncate the reason to a length of TOPICLEN, as
@@ -3447,51 +3852,89 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
          * want to allow those (e.g.  unbans) in that case.  If we add
          * some other ejection action for in-channel users, change
          * this too. */
-       victimCount = victims[0] ? 1 : 0;
+        victimCount = victims[0] ? 1 : 0;
 
-       if(IsService(victim))
-       {
-           if(cmd)
-               reply("MSG_SERVICE_IMMUNE", victim->nick);
-           return 0;
-       }
+        if(IsService(victim))
+        {
+            if(cmd)
+                reply("MSG_SERVICE_IMMUNE", victim->nick);
+            return 0;
+        }
 
         if((action == ACTION_KICK) && !victimCount)
         {
-           if(cmd)
-               reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
+            if(cmd)
+                reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
             return 0;
         }
 
-       if(protect_user(victim, user, channel->channel_info, false))
-       {
-           // This translates to  send_message(user, cmd->parent->bot, ...)
-           // if user is x3 (ctcp action) cmd is null and segfault.
-           if(cmd)
-               reply("CSMSG_USER_PROTECTED", victim->nick);
-           return 0;
-       }
+        if(protect_user(victim, user, channel->channel_info, false))
+        {
+            // This translates to  send_message(user, cmd->parent->bot, ...)
+            // if user is x3 (ctcp action) cmd is null and segfault.
+            if(cmd)
+                reply("CSMSG_USER_PROTECTED", victim->nick);
+            return 0;
+        }
 
-       ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
-       name = victim->nick;
+        ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
+        name = victim->nick;
     }
-    else
+    else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
     {
-       if(!is_ircmask(argv[1]))
-       {
-           if(cmd)
-              reply("MSG_NICK_UNKNOWN", argv[1]);
-           return 0;
-       }
+        struct handle_info *hi;
+        char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
+        const char *accountname = argv[1] + 1;
 
-       victims = alloca(sizeof(victims[0]) * channel->members.used);
+        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);
+
+        b = bad_channel_ban(channel, user, banmask, &victimCount, victims);
+        if(b == 1)
+        {
+            reply("CSMSG_MASK_PROTECTED", banmask);
+            return 0;
+        }
+        else if(b == -1)
+        {
+            reply("CSMSG_BAD_BAN", banmask);
+            return 0;
+        }
 
-        if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
+        if((action == ACTION_KICK) && (victimCount == 0))
         {
-           if(cmd)
-               reply("CSMSG_MASK_PROTECTED", argv[1]);
-           return 0;
-       }
+            reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
+            return 0;
+        }
+
+        name = ban = strdup(banmask);
+    }
+    else
+    {
+        if(!is_ircmask(argv[1]))
+        {
+            if(cmd)
+               reply("MSG_NICK_UNKNOWN", argv[1]);
+            return 0;
+        }
+
+        victims = alloca(sizeof(victims[0]) * channel->members.used);
+
+        b = bad_channel_ban(channel, user, argv[1], &victimCount, victims);
+        if(cmd && (b == 1)) {
+            reply("CSMSG_MASK_PROTECTED", argv[1]);
+            return 0;
+        }
+        else if(cmd && (b == -1)) {
+            reply("CSMSG_BAD_BAN", argv[1]);
+            return 0;
+        }
 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!? 
 //      if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user)) 
                                                                            And, ^^^^^^^^^ BAH! 
@@ -3500,133 +3943,141 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
    some creativity, but its not x3's job to be the ban censor anyway.  */
         if(is_overmask(argv[1]))
         {
-           if(cmd)
+            if(cmd)
                 reply("CSMSG_LAME_MASK", argv[1]);
             return 0;
         }
+        //TODO: We have no support to do protection etc etc so for now we dont let you use x3 to set extended bans.
+        if(is_extban(argv[1]))
+        {
+            if(cmd)
+                reply("CSMSG_NO_EXTBANS", argv[1]);
+            return 0;
+        }
 
         if((action == ACTION_KICK) && (victimCount == 0))
         {
-           if(cmd)
+            if(cmd)
                 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
             return 0;
         }
 
-       name = ban = strdup(argv[1]);
+        name = ban = strdup(argv[1]);
     }
 
     /* Truncate the ban in place if necessary; we must ensure
        that 'ban' is a valid ban mask before sanitizing it. */
-    sanitize_ircmask(ban);
+    if (*ban != '~')
+        sanitize_ircmask(ban);
 
     if(action & ACTION_ADD_LAMER)
     {
-       struct banData *bData, *next;
-
-       if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
-       {
-           if(cmd)
-               reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
-           free(ban);
-           return 0;
-       }
-
-       if(action & ACTION_ADD_TIMED_LAMER)
-       {
-           duration = ParseInterval(argv[2]);
-
-           if(duration < 15)
-           {
-               if(cmd)
-                   reply("CSMSG_DURATION_TOO_LOW");
-               free(ban);
-               return 0;
-           }
-           else if(duration > (86400 * 365 * 2))
-           {
-               if(cmd)
-                   reply("CSMSG_DURATION_TOO_HIGH");
-               free(ban);
-               return 0;
-           }
-       }
+        struct banData *bData, *next;
+
+        if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
+        {
+            if(cmd)
+                reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
+            free(ban);
+            return 0;
+        }
+
+        if(action & ACTION_ADD_TIMED_LAMER)
+        {
+            duration = ParseInterval(argv[2]);
+
+            if(duration < 15)
+            {
+                if(cmd)
+                    reply("CSMSG_DURATION_TOO_LOW");
+                free(ban);
+                return 0;
+            }
+            else if(duration > (86400 * 365 * 2))
+            {
+                if(cmd)
+                    reply("CSMSG_DURATION_TOO_HIGH");
+                free(ban);
+                return 0;
+            }
+        }
 
         /* lamers... */
-       for(bData = channel->channel_info->bans; bData; bData = next)
-       {
-           if(match_ircglobs(bData->mask, ban))
-           {
-               int exact = !irccasecmp(bData->mask, ban);
-
-               /* The ban is redundant; there is already a ban
-                  with the same effect in place. */
-               if(exact)
-               {
-                   if(bData->reason)
+        for(bData = channel->channel_info->bans; bData; bData = next)
+        {
+            if(match_ircglobs(bData->mask, ban))
+            {
+                int exact = !irccasecmp(bData->mask, ban);
+
+                /* The ban is redundant; there is already a ban
+                   with the same effect in place. */
+                if(exact)
+                {
+                    if(bData->reason)
                         free(bData->reason);
-                   bData->reason = strdup(reason);
+                    bData->reason = strdup(reason);
                     safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
                     if(cmd)
                         reply("CSMSG_REASON_CHANGE", ban);
-                   if(!bData->expires)
+                    if(!bData->expires)
                         goto post_add_ban;
-               }
-               if(exact && bData->expires)
-               {
-                   int reset = 0;
-
-                   /* If the ban matches an existing one exactly,
-                      extend the expiration time if the provided
-                      duration is longer. */
-                   if(duration && ((time_t)(now + duration) > bData->expires))
-                   {
-                       bData->expires = now + duration;
-                       reset = 1;
-                   }
-                   else if(!duration)
-                   {
-                       bData->expires = 0;
-                       reset = 1;
-                   }
-
-                   if(reset)
-                   {
-                       /* Delete the expiration timeq entry and
-                          requeue if necessary. */
-                       timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
-
-                       if(bData->expires)
-                           timeq_add(bData->expires, expire_ban, bData);
+                }
+                if(exact && bData->expires)
+                {
+                    int reset = 0;
+
+                    /* If the ban matches an existing one exactly,
+                       extend the expiration time if the provided
+                       duration is longer. */
+                    if(duration && ((time_t)(now + duration) > bData->expires))
+                    {
+                        bData->expires = now + duration;
+                        reset = 1;
+                    }
+                    else if(!duration)
+                    {
+                        bData->expires = 0;
+                        reset = 1;
+                    }
+
+                    if(reset)
+                    {
+                        /* Delete the expiration timeq entry and
+                           requeue if necessary. */
+                        timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
+
+                        if(bData->expires)
+                            timeq_add(bData->expires, expire_ban, bData);
 
                         if(!cmd)
                         {
-                            /* automated kickban, dont reply */
+                                /* automated kickban, dont reply */
                         }
-                       else if(duration)
-                           reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
-                       else
-                           reply("CSMSG_LAMER_ADDED", name, channel->name);
-
-                       goto post_add_ban;
-                   }
-               }
+                        else if(duration)
+                            reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
+                        else
+                            reply("CSMSG_LAMER_ADDED", name, channel->name);
+
+                        goto post_add_ban;
+                    }
+                }
                 if(cmd)
                     reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
 
-               free(ban);
-               return 0;
-           }
+                free(ban);
+                return 0;
+            }
 
-           next = bData->next;
-           if(match_ircglobs(ban, bData->mask))
-           {
-               /* The ban we are adding makes previously existing
-                  bans redundant; silently remove them. */
-               del_channel_ban(bData);
-           }
-       }
+            next = bData->next;
+            if(match_ircglobs(ban, bData->mask))
+            {
+                /* The ban we are adding makes previously existing
+                   bans redundant; silently remove them. */
+                del_channel_ban(bData);
+            }
+        }
 
-       bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
+        bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
         free(ban);
         name = ban = strdup(bData->mask);
     }
@@ -3653,19 +4104,19 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
         }
     }
 
-  post_add_ban:
+    post_add_ban:
     if(action & ACTION_BAN)
     {
-       unsigned int exists;
+        unsigned int exists;
         struct mod_chanmode *change;
 
-       if(channel->banlist.used >= MAXBANS)
-       {
+        if(channel->banlist.used >= MAXBANS)
+        {
             if(cmd)
                 reply("CSMSG_BANLIST_FULL", channel->name);
-           free(ban);
-           return 0;
-       }
+            free(ban);
+            return 0;
+        }
 
         exists = ChannelBanExists(channel, ban);
         change = mod_chanmode_alloc(victimCount + 1);
@@ -3687,7 +4138,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
         mod_chanmode_free(change);
 
         if(exists && (action == ACTION_BAN))
-       {
+        {
             if(cmd)
                 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
             free(ban);
@@ -3698,21 +4149,24 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     if(action & ACTION_ADD_LAMER)
     {
         char kick_reason[MAXLEN];
-       sprintf(kick_reason, "(%s) %s", user->nick, reason);
+        sprintf(kick_reason, "(%s) %s", user->nick, reason);
 
-       for(n = 0; n < victimCount; n++) {
-            if(!protect_user(victims[n]->user, user, channel->channel_info, true)) {
-               KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
+        for(n = 0; n < victimCount; n++) 
+        {
+            if(!protect_user(victims[n]->user, user, channel->channel_info, true)) 
+            {
+                KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
             }
         }
     }
     else if(action & ACTION_KICK)
     {
         char kick_reason[MAXLEN];
-       sprintf(kick_reason, "(%s) %s", user->nick, reason);
+        sprintf(kick_reason, "(%s) %s", user->nick, reason);
 
-       for(n = 0; n < victimCount; n++) {
-           KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
+        for(n = 0; n < victimCount; n++) 
+        {
+            KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
         }
     }
 
@@ -3722,17 +4176,17 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     }
     else if(action & ACTION_ADD_LAMER)
     {
-       if(duration)
-           reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
-       else
-           reply("CSMSG_LAMER_ADDED", name, channel->name);
+        if(duration)
+            reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
+        else
+            reply("CSMSG_LAMER_ADDED", name, channel->name);
     }
     else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
-       reply("CSMSG_KICK_BAN_DONE", name, channel->name);
+        reply("CSMSG_KICK_BAN_DONE", name, channel->name);
     else if(action & ACTION_BAN)
-       reply("CSMSG_BAN_DONE", name, channel->name);
+        reply("CSMSG_BAN_DONE", name, channel->name);
     else if(action & ACTION_KICK && victimCount)
-       reply("CSMSG_KICK_DONE", name, channel->name);
+        reply("CSMSG_KICK_DONE", name, channel->name);
 
     free(ban);
     return 1;
@@ -3776,7 +4230,7 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas
         for(ii = count = 0; ii < bans->used; ++ii)
         {
             match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
-                                          MATCH_USENICK | MATCH_VISIBLE);
+                                          MATCH_USENICK | MATCH_VISIBLE, 0);
             if(match[ii])
                 count++;
         }
@@ -3827,7 +4281,8 @@ void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
         count = 0;
         /* First find out how many bans were going to unset */
         for (jj=0; jj < channel->channel->banlist.used; ++jj) {
-            if(channel->channel->banlist.list[jj]->set < bantimeout)
+            //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see below
+            if(channel->channel->banlist.list[jj]->set < bantimeout && !is_extban(channel->channel->banlist.list[jj]->ban))
                 count++;
         }
         if(count > 0) {
@@ -3837,7 +4292,8 @@ void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
             /* Walk over every ban in this channel.. */
             for (jj=0; jj < channel->channel->banlist.used; ++jj) {
                 bn = channel->channel->banlist.list[jj];
-                if (bn->set < bantimeout) {
+                //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see above
+                if (bn->set < bantimeout && !is_extban(bn->ban)) {
                     log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
 
                     /* Add this ban to the mode change */
@@ -3878,13 +4334,23 @@ 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]))
-       {
-           reply("MSG_NICK_UNKNOWN", argv[1]);
-           return 0;
-       }
+        if(!is_ircmask(argv[1]) && *argv[1] == '*')
+        {
+            char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
+            const char *accountname = argv[1] + 1;
 
-       mask = strdup(argv[1]);
+            snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
+            mask = strdup(banmask);
+        }
+        else if(!is_ircmask(argv[1]))
+        {
+            reply("MSG_NICK_UNKNOWN", argv[1]);
+            return 0;
+        }
+        else
+        {
+            mask = strdup(argv[1]);
+        }
     }
 
     /* We don't sanitize the mask here because ircu
@@ -3907,30 +4373,30 @@ unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
 
     if(action & ACTION_DEL_LAMER)
     {
-       struct banData *ban, *next;
-
-       ban = channel->channel_info->bans; /* lamers */
-       while(ban)
-       {
-           if(actee)
-               for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
-                    ban = ban->next);
-           else
-               for( ; ban && !match_ircglobs(mask, ban->mask);
-                    ban = ban->next);
-           if(!ban)
-                break;
-           next = ban->next;
-           del_channel_ban(ban);
-           ban = next;
-           acted = 1;
-       }
+        struct banData *ban, *next;
+
+        ban = channel->channel_info->bans; /* lamers */
+        while(ban)
+        {
+            if(actee)
+                   for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE, 0);
+                 ban = ban->next);
+            else
+            for( ; ban && !match_ircglobs(mask, ban->mask);
+                 ban = ban->next);
+            if(!ban)
+                    break;
+            next = ban->next;
+            del_channel_ban(ban);
+            ban = next;
+            acted = 1;
+        }
     }
 
     if(!acted)
-       reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
+        reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
     else
-       reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
+        reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
     if(mask)
         free(mask);
     return 1;
@@ -3955,7 +4421,7 @@ static CHANSERV_FUNC(cmd_unbanme)
 
     /* remove permanent bans if the user has the proper access. */
     if(uData->access >= UL_MANAGER)
-       flags |= ACTION_DEL_LAMER;
+    flags |= ACTION_DEL_LAMER;
 
     argv[1] = user->nick;
     return unban_user(user, channel, 2, argv, cmd, flags);
@@ -3968,10 +4434,11 @@ static CHANSERV_FUNC(cmd_unbanall)
 
     if(!channel->banlist.used)
     {
-       reply("CSMSG_NO_BANS", channel->name);
-       return 0;
+        reply("CSMSG_NO_BANS", channel->name);
+        return 0;
     }
 
+    // TODO: dont remove some kinds of extended bans such as ~c
     change = mod_chanmode_alloc(channel->banlist.used);
     for(ii=0; ii<channel->banlist.used; ii++)
     {
@@ -4014,13 +4481,17 @@ static CHANSERV_FUNC(cmd_myaccess)
 
     if(argc < 2)
         target_handle = user->handle_info;
-    else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
-        return 0;
-    else if(!IsHelping(user) && target_handle != user->handle_info)
+    else if(!IsStaff(user))
     {
         reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
         return 0;
     }
+    else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
+        return 0;
+
+    if(!oper_outranks(user, target_handle))
+        return 0;
+
     if(!target_handle->channels)
     {
         reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
@@ -4040,7 +4511,7 @@ static CHANSERV_FUNC(cmd_myaccess)
             continue;
         sbuf.used = 0;
         string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
-        if(uData->flags == USER_AUTO_OP)
+        if(uData->flags != 0)
             string_buffer_append(&sbuf, ',');
         if(IsUserSuspended(uData))
             string_buffer_append(&sbuf, 's');
@@ -4078,7 +4549,7 @@ static CHANSERV_FUNC(cmd_access)
 
     if(argc < 2)
     {
-       target = user;
+    target = user;
         target_handle = target->handle_info;
     }
     else if((target = GetUserH(argv[1])))
@@ -4103,8 +4574,8 @@ static CHANSERV_FUNC(cmd_access)
 
     if(target == chanserv)
     {
-       reply("CSMSG_IS_CHANSERV");
-       return 1;
+        reply("CSMSG_IS_CHANSERV");
+        return 1;
     }
 
     if(!target_handle)
@@ -4114,13 +4585,13 @@ static CHANSERV_FUNC(cmd_access)
             reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
             return 0;
         }
-       if(target != user)
-       {
-           reply("MSG_USER_AUTHENTICATE", target->nick);
-           return 0;
-       }
-        reply("MSG_AUTHENTICATE");
-        return 0;
+        if(target != user)
+        {
+            reply("MSG_USER_AUTHENTICATE", target->nick);
+            return 0;
+        }
+            reply("MSG_AUTHENTICATE");
+            return 0;
     }
 
     if(target)
@@ -4129,17 +4600,17 @@ static CHANSERV_FUNC(cmd_access)
         if(IsOper(target))
         {
             epithet = chanserv_conf.irc_operator_epithet;
-            type = "IRCOp";
+            type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
         }
         else if(IsNetworkHelper(target))
         {
             epithet = chanserv_conf.network_helper_epithet;
-            type = "network helper";
+            type = user_find_message(user, "CSMSG_UC_H_TITLE");
         }
         else if(IsSupportHelper(target))
         {
             epithet = chanserv_conf.support_helper_epithet;
-            type = "support helper";
+            type = user_find_message(user, "CSMSG_LC_H_TITLE");
         }
         if(epithet)
         {
@@ -4373,11 +4844,11 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     matches = 0;
     for(uData = channel->channel_info->users; uData; uData = uData->next)
     {
-       if((uData->access < lowest)
-           || (uData->access > highest)
-           || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
-           continue;
-       lData.users[matches++] = uData;
+        if((uData->access < lowest)
+               || (uData->access > highest)
+               || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
+            continue;
+        lData.users[matches++] = uData;
     }
     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
 
@@ -4419,9 +4890,10 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     ary[i++] = "Expiry";
     for(matches = 1; matches < lData.table.length; ++matches)
     {
-        struct userData *uData = lData.users[matches-1];
         char seen[INTERVALLEN];
 
+
+        uData = lData.users[matches-1];
         i = 0;
         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
         lData.table.contents[matches] = ary;
@@ -4443,7 +4915,7 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
            break;
             }
         }
-     else {
+        else {
            ary[i++] = user_level_name_from_level(uData->access);
         }
         ary[i++] = uData->handle->handle;
@@ -4474,7 +4946,8 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
                 intervalString(delay, diff, user->handle_info);
             }
             ary[i++] = delay;
-        } else
+        } 
+        else
             ary[i++] = "Never";
     }
 
@@ -4541,16 +5014,22 @@ 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, ii;
+    unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
     const char *msg_never, *triggered, *expires;
     struct banData *ban, **bans; /* lamers */
 
-    if(argc > 1)
-       search = argv[1];
-    else
+    if(argc < 2)
         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 */
@@ -4558,10 +5037,18 @@ static CHANSERV_FUNC(cmd_lamers)
     /* lamers */
     for(ban = channel->channel_info->bans; ban; ban = ban->next)
     {
-       if(search && !match_ircglobs(search, ban->mask))
-           continue;
-       bans[matches++] = ban;
-       if(ban->expires)
+        if(search_u)
+        {
+            if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE, 0))
+                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;
     }
 
@@ -4584,28 +5071,28 @@ static CHANSERV_FUNC(cmd_lamers)
     if(!matches)
     {
         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
-/*     reply("MSG_NONE"); */
+        /*  reply("MSG_NONE"); */
         free(tbl.contents[0]);
         free(tbl.contents);
-       return 0;
+        return 0;
     }
 
     msg_never = user_find_message(user, "MSG_NEVER");
     for(ii = 0; ii < matches; )
     {
-       ban = bans[ii];
+        ban = bans[ii];
 
-       if(!timed)
-           expires = "";
-       else if(ban->expires)
-           expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
-       else
-           expires = msg_never;
+        if(!timed)
+            expires = "";
+        else if(ban->expires)
+            expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
+        else
+            expires = msg_never;
 
-       if(ban->triggered)
-           triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
-       else
-           triggered = msg_never;
+        if(ban->triggered)
+            triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
+        else
+            triggered = msg_never;
 
         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
         tbl.contents[ii][0] = ban->mask;
@@ -4774,18 +5261,20 @@ static CHANSERV_FUNC(cmd_mode)
     
     if(argc < 2)
     {
-        if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
+        if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) 
+        {
             reply("CSMSG_DEFCON_NO_MODE_CHANGE");
             return 0;
         }
 
         change = &channel->channel_info->modes;
-       if(change->modes_set || change->modes_clear) {
+        if(change->modes_set || change->modes_clear) {
             modcmd_chanmode_announce(change);
             reply("CSMSG_DEFAULTED_MODES", channel->name);
-       } else
-           reply("CSMSG_NO_MODES", channel->name);
-       return 1;
+        } 
+        else
+            reply("CSMSG_NO_MODES", channel->name);
+        return 1;
     }
 
     uData = GetChannelUser(channel->channel_info, user->handle_info);
@@ -4799,8 +5288,8 @@ static CHANSERV_FUNC(cmd_mode)
 
     if(!change)
     {
-       reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
-       return 0;
+        reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
+        return 0;
     }
 
     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
@@ -4820,15 +5309,15 @@ static CHANSERV_FUNC(cmd_mode)
 
 static CHANSERV_FUNC(cmd_invite)
 {
-    struct userData *uData;
+    //struct userData *uData;
     struct userNode *invite;
 
-    uData = GetChannelUser(channel->channel_info, user->handle_info);
+    //uData = GetChannelUser(channel->channel_info, user->handle_info);
 
     if(argc > 1)
     {
         if(!(invite = GetUserH(argv[1])))
-       {
+        {
             reply("MSG_NICK_UNKNOWN", argv[1]);
             return 0;
         }
@@ -4838,8 +5327,8 @@ static CHANSERV_FUNC(cmd_invite)
 
     if(GetUserMode(channel, invite))
     {
-       reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
-       return 0;
+        reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
+        return 0;
     }
 
     if(user != invite)
@@ -4855,17 +5344,19 @@ static CHANSERV_FUNC(cmd_invite)
 
     if (invite->handle_info && invite->handle_info->ignores->used && (argc > 1)) {
         unsigned int i;
-        for (i=0; i < invite->handle_info->ignores->used; i++) {
-            if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK)) {
-              reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
-              return 0;
+        for (i=0; i < invite->handle_info->ignores->used; i++) 
+        {
+            if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK, 0)) 
+            {
+                reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
+                return 0;
             }
         }
     }
 
     irc_invite(chanserv, invite, channel);
     if(argc > 1)
-       reply("CSMSG_INVITED_USER", argv[1], channel->name);
+        reply("CSMSG_INVITED_USER", argv[1], channel->name);
 
     return 1;
 }
@@ -4874,8 +5365,8 @@ static CHANSERV_FUNC(cmd_inviteme)
 {
     if(GetUserMode(channel, user))
     {
-       reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
-       return 0;
+    reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
+    return 0;
     }
     if(channel->channel_info
        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
@@ -4902,43 +5393,43 @@ show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended
     combo = (suspended->issued ? 4 : 0)
         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
     switch(combo) {
-    case 0: /* no issue time, indefinite expiration */
-        reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
-        break;
-    case 1: /* no issue time, expires in future */
-        intervalString(buf1, suspended->expires-now, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
-        break;
-    case 2: /* no issue time, expired */
-        intervalString(buf1, now-suspended->expires, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
-        break;
-    case 3: /* no issue time, revoked */
-        intervalString(buf1, now-suspended->revoked, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
-        break;
-    case 4: /* issue time set, indefinite expiration */
-        intervalString(buf1, now-suspended->issued, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
-        break;
-    case 5: /* issue time set, expires in future */
-        intervalString(buf1, now-suspended->issued, user->handle_info);
-        intervalString(buf2, suspended->expires-now, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
-        break;
-    case 6: /* issue time set, expired */
-        intervalString(buf1, now-suspended->issued, user->handle_info);
-        intervalString(buf2, now-suspended->expires, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
-        break;
-    case 7: /* issue time set, revoked */
-        intervalString(buf1, now-suspended->issued, user->handle_info);
-        intervalString(buf2, now-suspended->revoked, user->handle_info);
-        reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
-        break;
-    default:
-        log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
-        return;
+        case 0: /* no issue time, indefinite expiration */
+            reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
+            break;
+        case 1: /* no issue time, expires in future */
+            intervalString(buf1, suspended->expires-now, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
+            break;
+        case 2: /* no issue time, expired */
+            intervalString(buf1, now-suspended->expires, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
+            break;
+        case 3: /* no issue time, revoked */
+            intervalString(buf1, now-suspended->revoked, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
+            break;
+        case 4: /* issue time set, indefinite expiration */
+            intervalString(buf1, now-suspended->issued, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
+            break;
+        case 5: /* issue time set, expires in future */
+            intervalString(buf1, now-suspended->issued, user->handle_info);
+            intervalString(buf2, suspended->expires-now, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
+            break;
+        case 6: /* issue time set, expired */
+            intervalString(buf1, now-suspended->issued, user->handle_info);
+            intervalString(buf2, now-suspended->expires, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
+            break;
+        case 7: /* issue time set, revoked */
+            intervalString(buf1, now-suspended->issued, user->handle_info);
+            intervalString(buf2, now-suspended->revoked, user->handle_info);
+            reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
+            break;
+        default:
+            log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
+            return;
     }
 }
 
@@ -4986,8 +5477,8 @@ static CHANSERV_FUNC(cmd_info)
     if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
     {
         mod_chanmode_format(&cData->modes, modes);
-       reply("CSMSG_CHANNEL_TOPIC", cData->topic);
-       reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
+        reply("CSMSG_CHANNEL_TOPIC", cData->topic);
+        reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
     }
 
     for(it = dict_first(cData->notes); it; it = iter_next(it))
@@ -5011,9 +5502,8 @@ static CHANSERV_FUNC(cmd_info)
     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
 
     privileged = IsStaff(user);
-    /* if(privileged) */
-        reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
-    if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData->registrar)
+    reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
+    if(cData->registrar)
         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
 
     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
@@ -5054,7 +5544,7 @@ static CHANSERV_FUNC(cmd_netinfo)
     reply("CSMSG_NETWORK_INFO");
     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
     reply("CSMSG_NETWORK_USERS", dict_size(clients));
-    reply("CSMSG_NETWORK_OPERS", curr_opers.used);
+    reply("CSMSG_NETWORK_OPERS", count_opers);
     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
     reply("CSMSG_NETWORK_LAMERS", banCount);
     reply("CSMSG_NETWORK_CHANUSERS", userCount);
@@ -5080,7 +5570,7 @@ send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
         user = list->list[nn];
         if(user->modes & skip_flags)
             continue;
-        if(IsBot(user))
+        if(IsBot(user) || IsHideOper(user))
             continue;
         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
         if(IsAway(user))
@@ -5099,14 +5589,14 @@ send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
 static CHANSERV_FUNC(cmd_ircops)
 {
     reply("CSMSG_STAFF_OPERS");
-    send_staff_list(user, &curr_opers, FLAGS_SERVICE);
+    send_staff_list(user, &curr_opers, FLAGS_SERVICE | FLAGS_BOT);
     return 1;
 }
 
 static CHANSERV_FUNC(cmd_helpers)
 {
     reply("CSMSG_STAFF_HELPERS");
-    send_staff_list(user, &curr_helpers, FLAGS_OPER);
+    send_staff_list(user, &curr_helpers, FLAGS_OPER | FLAGS_SERVICE | FLAGS_BOT);
     return 1;
 }
 
@@ -5140,9 +5630,9 @@ static CHANSERV_FUNC(cmd_peek)
     table.contents = alloca(channel->members.used*sizeof(*table.contents));
     for(n = 0; n < channel->members.used; n++)
     {
-       mn = channel->members.list[n];
-       if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
-            continue;
+        mn = channel->members.list[n];
+        if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
+                continue;
         table.contents[table.length] = alloca(sizeof(**table.contents));
         table.contents[table.length][0] = mn->user->nick;
         table.length++;
@@ -5161,10 +5651,12 @@ static CHANSERV_FUNC(cmd_peek)
 static MODCMD_FUNC(cmd_wipeinfo)
 {
     struct handle_info *victim;
-    struct userData *ud, *actor;
+    struct userData *ud, *actor, *real_actor;
+    unsigned int override = 0;
 
     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)))
@@ -5177,11 +5669,13 @@ 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;
+    return 1 | override;
 }
 
 static void
@@ -5215,7 +5709,18 @@ resync_channel(struct chanNode *channel)
         }
         else /* Give various userlevels their modes.. */
         {
-            if(uData && uData->access >= UL_OP )
+            /* If the user has autoop/autovoice disabled then ignore them */
+            if(uData && !IsUserAutoOp(uData))
+              continue;
+            if(uData && uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
+            {
+                if(!(mn->modes & MODE_VOICE))
+                {
+                    changes->args[used].mode = MODE_VOICE;
+                    changes->args[used++].u.member = mn;
+                }
+            }
+            else if(uData && uData->access >= UL_OP )
             {
                 if(!(mn->modes & MODE_CHANOP))
                 {
@@ -5348,24 +5853,24 @@ static CHANSERV_FUNC(cmd_seen)
 
     if(!irccasecmp(argv[1], chanserv->nick))
     {
-       reply("CSMSG_IS_CHANSERV");
-       return 1;
+        reply("CSMSG_IS_CHANSERV");
+        return 1;
     }
 
     if(!(handle = get_handle_info(argv[1])))
     {
-       reply("MSG_HANDLE_UNKNOWN", argv[1]);
-       return 0;
+        reply("MSG_HANDLE_UNKNOWN", argv[1]);
+        return 0;
     }
 
     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
     {
-       reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
-       return 0;
+        reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
+        return 0;
     }
 
     if(uData->present)
-       reply("CSMSG_USER_PRESENT", handle->handle);
+    reply("CSMSG_USER_PRESENT", handle->handle);
     else if(uData->seen)
         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
     else
@@ -5412,9 +5917,9 @@ note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, str
 {
     switch(ntype->visible_type)
     {
-    case NOTE_VIS_ALL: return 1;
-    case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
-    case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
+        case NOTE_VIS_ALL: return 1;
+        case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
+        case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
     }
 }
 
@@ -5584,9 +6089,9 @@ static CHANSERV_FUNC(cmd_events)
         reply("CSMSG_BAR");
     matches = log_entry_search(&discrim, log_report_entry, &report);
     if(matches)
-       reply("MSG_MATCH_COUNT", matches);
+        reply("MSG_MATCH_COUNT", matches);
     else
-       reply("MSG_NO_MATCHES");
+        reply("MSG_NO_MATCHES");
     return 1;
 }
 
@@ -5599,6 +6104,23 @@ static CHANSERV_FUNC(cmd_say)
         msg = unsplit_string(argv + 1, argc - 1, NULL);
         send_channel_message(channel, cmd->parent->bot, "%s", msg);
     }
+    else if(*argv[1] == '*' && argv[1][1] != '\0')
+    {
+        struct handle_info *hi;
+        struct userNode *authed;
+
+        REQUIRE_PARAMS(3);
+        msg = unsplit_string(argv + 2, argc - 2, NULL);
+
+        if (!(hi = get_handle_info(argv[1] + 1)))
+        {
+            reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
+            return 0;
+        }
+
+        for (authed = hi->users; authed; authed = authed->next_authed)
+            send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
+    }
     else if(GetUserH(argv[1]))
     {
         REQUIRE_PARAMS(3);
@@ -5623,6 +6145,23 @@ static CHANSERV_FUNC(cmd_emote)
         msg = unsplit_string(argv + 1, argc - 1, NULL);
         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
     }
+    else if(*argv[1] == '*' && argv[1][1] != '\0')
+    {
+        struct handle_info *hi;
+        struct userNode *authed;
+
+        REQUIRE_PARAMS(3);
+        msg = unsplit_string(argv + 2, argc - 2, NULL);
+
+        if (!(hi = get_handle_info(argv[1] + 1)))
+        {
+            reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
+            return 0;
+        }
+
+        for (authed = hi->users; authed; authed = authed->next_authed)
+            send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
+    }
     else if(GetUserH(argv[1]))
     {
         msg = unsplit_string(argv + 2, argc - 2, NULL);
@@ -5655,17 +6194,32 @@ chanserv_expire_suspension(void *data)
 {
     struct suspended *suspended = data;
     struct chanNode *channel;
+    unsigned int ii;
 
+    /* Update the channel registration data structure. */
     if(!suspended->expires || (now < suspended->expires))
         suspended->revoked = now;
     channel = suspended->cData->channel;
     suspended->cData->channel = channel;
     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
+
+    /* If appropriate, re-join ChanServ to the channel. */
     if(!IsOffChannel(suspended->cData))
     {
         spamserv_cs_suspend(channel, 0, 0, NULL);
         ss_cs_join_channel(channel, 1);
     }
+
+    /* Mark everyone currently in the channel as present. */
+    for(ii = 0; ii < channel->members.used; ++ii)
+    {
+        struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
+        if(uData)
+        {
+            uData->present = 1;
+            uData->seen = now;
+        }
+    }
 }
 
 static CHANSERV_FUNC(cmd_csuspend)
@@ -5687,9 +6241,9 @@ static CHANSERV_FUNC(cmd_csuspend)
         argv[1]++;
     else if(IsSuspended(channel->channel_info))
     {
-       reply("CSMSG_ALREADY_SUSPENDED", channel->name);
+        reply("CSMSG_ALREADY_SUSPENDED", channel->name);
         show_suspension_info(cmd, user, channel->channel_info->suspended);
-       return 0;
+        return 0;
     }
 
     if(!strcmp(argv[1], "0"))
@@ -5796,47 +6350,49 @@ chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int a
 
     for(i = 0; i < argc; i++)
     {
-       /* Assume all criteria require arguments. */
-       if(i == (argc - 1))
-       {
-           reply("MSG_MISSING_PARAMS", argv[i]);
+        /* Assume all criteria require arguments. */
+        if(i == (argc - 1))
+        {
+            reply("MSG_MISSING_PARAMS", argv[i]);
+                goto fail;
+        }
+
+        if(!irccasecmp(argv[i], "name"))
+            search->name = argv[++i];
+        else if(!irccasecmp(argv[i], "registrar"))
+            search->registrar = argv[++i];
+        else if(!irccasecmp(argv[i], "unvisited"))
+            search->unvisited = ParseInterval(argv[++i]);
+        else if(!irccasecmp(argv[i], "registered"))
+            search->registered = ParseInterval(argv[++i]);
+        else if(!irccasecmp(argv[i], "flags"))
+        {
+            i++;
+            if(!irccasecmp(argv[i], "nodelete"))
+                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]);
+                goto fail;
+            }
+        }
+        else if(!irccasecmp(argv[i], "limit"))
+            search->limit = strtoul(argv[++i], NULL, 10);
+        else
+        {
+            reply("MSG_INVALID_CRITERIA", argv[i]);
             goto fail;
-       }
-
-       if(!irccasecmp(argv[i], "name"))
-           search->name = argv[++i];
-       else if(!irccasecmp(argv[i], "registrar"))
-           search->registrar = argv[++i];
-       else if(!irccasecmp(argv[i], "unvisited"))
-           search->unvisited = ParseInterval(argv[++i]);
-       else if(!irccasecmp(argv[i], "registered"))
-           search->registered = ParseInterval(argv[++i]);
-       else if(!irccasecmp(argv[i], "flags"))
-       {
-           i++;
-           if(!irccasecmp(argv[i], "nodelete"))
-               search->flags |= CHANNEL_NODELETE;
-           else if(!irccasecmp(argv[i], "suspended"))
-               search->flags |= CHANNEL_SUSPENDED;
-           else
-           {
-               reply("CSMSG_INVALID_CFLAG", argv[i]);
-               goto fail;
-           }
-       }
-       else if(!irccasecmp(argv[i], "limit"))
-           search->limit = strtoul(argv[++i], NULL, 10);
-       else
-       {
-           reply("MSG_INVALID_CRITERIA", argv[i]);
-           goto fail;
-       }
+        }
     }
 
     if(search->name && !strcmp(search->name, "*"))
-       search->name = 0;
+    search->name = 0;
     if(search->registrar && !strcmp(search->registrar, "*"))
-       search->registrar = 0;
+    search->registrar = 0;
 
     return search;
   fail:
@@ -5854,7 +6410,7 @@ chanserv_channel_match(struct chanData *channel, search_t search)
        (search->unvisited && (now - channel->visited) < search->unvisited) ||
        (search->registered && (now - channel->registered) > search->registered) ||
        (search->flags && ((search->flags & channel->flags) != search->flags)))
-       return 0;
+    return 0;
 
     return 1;
 }
@@ -5867,10 +6423,10 @@ chanserv_channel_search(search_t search, channel_search_func smf, void *data)
 
     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
     {
-       if(!chanserv_channel_match(channel, search))
+        if(!chanserv_channel_match(channel, search))
             continue;
-       matches++;
-       smf(channel, data);
+        matches++;
+        smf(channel, data);
     }
 
     return matches;
@@ -5896,13 +6452,13 @@ static CHANSERV_FUNC(cmd_search)
     REQUIRE_PARAMS(3);
 
     if(!irccasecmp(argv[1], "count"))
-       action = search_count;
+        action = search_count;
     else if(!irccasecmp(argv[1], "print"))
-       action = search_print;
+        action = search_print;
     else
     {
-       reply("CSMSG_ACTION_INVALID", argv[1]);
-       return 0;
+        reply("CSMSG_ACTION_INVALID", argv[1]);
+        return 0;
     }
 
     search = chanserv_search_create(cmd, user, argc - 2, argv + 2);
@@ -5910,11 +6466,11 @@ static CHANSERV_FUNC(cmd_search)
         return 0;
 
     if(action == search_count)
-       search->limit = INT_MAX;
+    search->limit = INT_MAX;
 
     if(action == search_print)
     {
-           reply("CSMSG_CHANNEL_SEARCH_RESULTS");
+        reply("CSMSG_CHANNEL_SEARCH_RESULTS");
         if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
             reply("CSMSG_BAR");
     }
@@ -5922,9 +6478,9 @@ static CHANSERV_FUNC(cmd_search)
     matches = chanserv_channel_search(search, action, user);
 
     if(matches)
-       reply("MSG_MATCH_COUNT", matches);
+        reply("MSG_MATCH_COUNT", matches);
     else
-       reply("MSG_NO_MATCHES");
+        reply("MSG_NO_MATCHES");
 
     free(search);
     return 1;
@@ -5939,8 +6495,8 @@ static CHANSERV_FUNC(cmd_unvisited)
 
     if(argc > 1)
     {
-       interval = ParseInterval(argv[1]);
-       if(argc > 2)
+        interval = ParseInterval(argv[1]);
+        if(argc > 2)
             limit = atoi(argv[2]);
     }
 
@@ -5949,14 +6505,68 @@ static CHANSERV_FUNC(cmd_unvisited)
 
     for(cData = channelList; cData && matches < limit; cData = cData->next)
     {
-       if((now - cData->visited) < interval)
+        if((now - cData->visited) < interval)
             continue;
 
-       intervalString(buffer, now - cData->visited, user->handle_info);
-       reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
-       matches++;
+        intervalString(buffer, now - cData->visited, user->handle_info);
+        reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
+        matches++;
+    }
+
+    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;
 }
 
@@ -5972,20 +6582,20 @@ static MODCMD_FUNC(chan_opt_defaulttopic)
             return 0;
         }
 
-       topic = unsplit_string(argv+1, argc-1, NULL);
+        topic = unsplit_string(argv+1, argc-1, NULL);
 
         free(channel->channel_info->topic);
-       if(topic[0] == '*' && topic[1] == 0)
-       {
+        if(topic[0] == '*' && topic[1] == 0)
+        {
             topic = channel->channel_info->topic = NULL;
-       }
-       else
-       {
-           topic = channel->channel_info->topic = strdup(topic);
+        }
+        else
+        {
+            topic = channel->channel_info->topic = strdup(topic);
             if(channel->channel_info->topic_mask
                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
-       }
+        }
         SetChannelTopic(channel, chanserv, user, topic ? topic : "", 1);
     }
 
@@ -6009,22 +6619,22 @@ static MODCMD_FUNC(chan_opt_topicmask)
             return 0;
         }
 
-       mask = unsplit_string(argv+1, argc-1, NULL);
+        mask = unsplit_string(argv+1, argc-1, NULL);
 
         if(cData->topic_mask)
             free(cData->topic_mask);
-       if(mask[0] == '*' && mask[1] == 0)
-       {
-           cData->topic_mask = 0;
-       }
-       else
-       {
+        if(mask[0] == '*' && mask[1] == 0)
+        {
+            cData->topic_mask = 0;
+        }
+        else
+        {
             cData->topic_mask = strdup(mask);
             if(!cData->topic)
                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
             else if(!match_ircglob(cData->topic, cData->topic_mask))
                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
-       }
+        }
     }
 
     if(channel->channel_info->topic_mask)
@@ -6042,18 +6652,18 @@ int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, cha
         char *previous;
 
         previous = *data;
-       if(greeting[0] == '*' && greeting[1] == 0)
-           *data = NULL;
-       else
-       {
-           unsigned int length = strlen(greeting);
-           if(length > chanserv_conf.greeting_length)
-           {
-               reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
-               return 0;
-           }
-           *data = strdup(greeting);
-       }
+        if(greeting[0] == '*' && greeting[1] == 0)
+            *data = NULL;
+        else
+        {
+            unsigned int length = strlen(greeting);
+            if(length > chanserv_conf.greeting_length)
+            {
+                reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
+                return 0;
+            }
+            *data = strdup(greeting);
+        }
         if(previous)
             free(previous);
     }
@@ -6079,9 +6689,10 @@ static MODCMD_FUNC(chan_opt_maxsetinfo)
 {
    unsigned int charmax;
 
-   if(argc > 1) {
+   if(argc > 1) 
+   {
      charmax = atoi(argv[1]);
-     if ((charmax > 0) && (charmax < chanserv_conf.max_userinfo_length))
+     if ((charmax > 0) && (charmax <= chanserv_conf.max_userinfo_length))
        channel->channel_info->maxsetinfo = charmax;
    }
 
@@ -6106,12 +6717,12 @@ static MODCMD_FUNC(chan_opt_modes)
             reply("CSMSG_NO_ACCESS");
             return 0;
         }
-       if(argv[1][0] == '*' && argv[1][1] == 0)
-       {
-            memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
-       }
+        if(argv[1][0] == '*' && argv[1][1] == 0)
+        {
+                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, 0)))
-       {
+        {
             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
             return 0;
         }
@@ -6146,27 +6757,27 @@ channel_binary_option(char *name, unsigned long mask, struct userNode *user, str
 
     if(argc > 1)
     {
-       /* Set flag according to value. */
-       if(enabled_string(argv[1]))
-       {
-           cData->flags |= mask;
-           value = 1;
-       }
-       else if(disabled_string(argv[1]))
-       {
-           cData->flags &= ~mask;
-           value = 0;
-       }
-       else
-       {
-           reply("MSG_INVALID_BINARY", argv[1]);
-           return 0;
-       }
+        /* Set flag according to value. */
+        if(enabled_string(argv[1]))
+        {
+            cData->flags |= mask;
+            value = 1;
+        }
+        else if(disabled_string(argv[1]))
+        {
+            cData->flags &= ~mask;
+            value = 0;
+        }
+        else
+        {
+            reply("MSG_INVALID_BINARY", argv[1]);
+            return 0;
+        }
     }
     else
     {
-       /* Find current option value. */
-       value = (cData->flags & mask) ? 1 : 0;
+        /* Find current option value. */
+        value = (cData->flags & mask) ? 1 : 0;
     }
 
     if(value)
@@ -6180,8 +6791,8 @@ static MODCMD_FUNC(chan_opt_nodelete)
 {
     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
     {
-       reply("MSG_SETTING_PRIVILEGED", argv[0]);
-       return 0;
+        reply("MSG_SETTING_PRIVILEGED", argv[0]);
+        return 0;
     }
 
     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
@@ -6189,6 +6800,18 @@ 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);
 }
 
@@ -6199,38 +6822,38 @@ static MODCMD_FUNC(chan_opt_offchannel)
 
     if(argc > 1)
     {
-       /* Set flag according to value. */
-       if(enabled_string(argv[1]))
-       {
-            if(!IsOffChannel(cData))
-                DelChannelUser(chanserv, channel, "Going off-channel.", 0);
-           cData->flags |= CHANNEL_OFFCHANNEL;
-           value = 1;
-       }
-       else if(disabled_string(argv[1]))
-       {
-            if(IsOffChannel(cData))
-            {
-                struct mod_chanmode change;
-                mod_chanmode_init(&change);
-                change.argc = 1;
-                change.args[0].mode = MODE_CHANOP;
-                change.args[0].u.member = AddChannelUser(chanserv, channel);
-                mod_chanmode_announce(chanserv, channel, &change);
-            }
-           cData->flags &= ~CHANNEL_OFFCHANNEL;
-           value = 0;
-       }
-       else
-       {
-           reply("MSG_INVALID_BINARY", argv[1]);
-           return 0;
-       }
+        /* Set flag according to value. */
+        if(enabled_string(argv[1]))
+        {
+                if(!IsOffChannel(cData))
+                    DelChannelUser(chanserv, channel, "Going off-channel.", 0);
+            cData->flags |= CHANNEL_OFFCHANNEL;
+            value = 1;
+        }
+        else if(disabled_string(argv[1]))
+        {
+                if(IsOffChannel(cData))
+                {
+                    struct mod_chanmode change;
+                    mod_chanmode_init(&change);
+                    change.argc = 1;
+                    change.args[0].mode = MODE_CHANOP;
+                    change.args[0].u.member = AddChannelUser(chanserv, channel);
+                    mod_chanmode_announce(chanserv, channel, &change);
+                }
+            cData->flags &= ~CHANNEL_OFFCHANNEL;
+            value = 0;
+        }
+        else
+        {
+            reply("MSG_INVALID_BINARY", argv[1]);
+            return 0;
+        }
     }
     else
     {
-       /* Find current option value. */
-       value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
+        /* Find current option value. */
+        value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
     }
 
     if(value)
@@ -6261,7 +6884,8 @@ static MODCMD_FUNC(chan_opt_defaults)
         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
         return 0;
     }
-    cData->flags = CHANNEL_DEFAULT_FLAGS;
+    cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
+        | (cData->flags & CHANNEL_PRESERVED_FLAGS);
     cData->modes = chanserv_conf.default_modes;
     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
@@ -6287,8 +6911,8 @@ channel_level_option(enum levelOption option, struct userNode *user, struct chan
         }
         value = user_level_from_name(argv[1], UL_OWNER+1);
         if(!value && strcmp(argv[1], "0"))
-       {
-           reply("CSMSG_INVALID_ACCESS", argv[1]);
+        {
+            reply("CSMSG_INVALID_ACCESS", argv[1]);
             return 0;
         }
         uData = GetChannelUser(cData, user->handle_info);
@@ -6380,31 +7004,31 @@ static int
 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
 {
     struct chanData *cData = channel->channel_info;
-    int count = charOptions[option].count, index;
+    int count = charOptions[option].count, idx;
 
     if(argc > 1)
     {
-        index = atoi(argv[1]);
+        idx = atoi(argv[1]);
 
-       if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
-       {
-           reply("CSMSG_INVALID_NUMERIC", index);
+    if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
+    {
+        reply("CSMSG_INVALID_NUMERIC", idx);
             /* 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));
-           return 0;
-       }
+            for(idx = 0; idx < count; idx++)
+                reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
+        return 0;
+    }
 
-       cData->chOpts[option] = charOptions[option].values[index].value;
+    cData->chOpts[option] = charOptions[option].values[idx].value;
     }
     else
     {
-       /* Find current option value. */
+    /* Find current option value. */
       find_value:
-       for(index = 0;
-            (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
-            index++);
-        if(index == count)
+    for(idx = 0;
+            (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
+            idx++);
+        if(idx == count)
         {
             /* Somehow, the option value is corrupt; reset it to the default. */
             cData->chOpts[option] = charOptions[option].default_value;
@@ -6412,7 +7036,7 @@ channel_multiple_option(enum charOption option, struct userNode *user, struct ch
         }
     }
 
-    reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
+    reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
     return 1;
 }
 
@@ -6454,7 +7078,7 @@ static MODCMD_FUNC(chan_opt_resync)
 static struct svccmd_list set_shows_list;
 
 static void
-handle_svccmd_unbind(struct svccmd *target) {
+handle_svccmd_unbind(struct svccmd *target, UNUSED_ARG(void *extra)) {
     unsigned int ii;
     for(ii=0; ii<set_shows_list.used; ++ii)
         if(target == set_shows_list.list[ii])
@@ -6491,7 +7115,7 @@ static CHANSERV_FUNC(cmd_set)
 
     if(argc < 2)
     {
-           reply("CSMSG_CHANNEL_OPTIONS", channel->name);
+        reply("CSMSG_CHANNEL_OPTIONS", channel->name);
         if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
             reply("CSMSG_BAR");
         for(ii = 0; ii < set_shows_list.used; ii++)
@@ -6500,7 +7124,7 @@ static CHANSERV_FUNC(cmd_set)
             subcmd->command->func(user, channel, 1, argv+1, subcmd);
         }
         reply("CSMSG_CHANNEL_OPTIONS_END");
-       return 1;
+    return 1;
     }
 
     sprintf(buf, "%s %s", argv[0], argv[1]);
@@ -6516,6 +7140,8 @@ static CHANSERV_FUNC(cmd_set)
         return 0;
     }
 
+    argv[0] = "";
+    argv[1] = buf;
     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
 }
 
@@ -6533,7 +7159,7 @@ user_binary_option(char *name, unsigned long mask, struct userNode *user, struct
 
     if(argc < 2)
     {
-       /* Just show current option value. */
+    /* Just show current option value. */
     }
     else if(enabled_string(argv[1]))
     {
@@ -6571,6 +7197,10 @@ static MODCMD_FUNC(user_opt_autoop)
 
 static MODCMD_FUNC(user_opt_autoinvite)
 {
+    if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
+    {
+        reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
+    }
     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
 }
 
@@ -6588,11 +7218,11 @@ static MODCMD_FUNC(user_opt_info)
 
     if(!uData)
     {
-       /* If they got past the command restrictions (which require access)
+        /* If they got past the command restrictions (which require access)
          * but fail this test, we have some fool with security override on.
          */
-       reply("CSMSG_NOT_USER", channel->name);
-       return 0;
+        reply("CSMSG_NOT_USER", channel->name);
+        return 0;
     }
 
     if(argc > 1)
@@ -6661,11 +7291,11 @@ static CHANSERV_FUNC(cmd_uset)
 
     if(argc < 2)
     {
-       /* Do this so options are presented in a consistent order. */
-       reply("CSMSG_USER_OPTIONS");
+    /* Do this so options are presented in a consistent order. */
+    reply("CSMSG_USER_OPTIONS");
         for(ii = 0; ii < uset_shows_list.used; ii++)
             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
-       return 1;
+    return 1;
     }
 
     sprintf(buf, "%s %s", argv[0], argv[1]);
@@ -6682,10 +7312,13 @@ static CHANSERV_FUNC(cmd_uset)
 static CHANSERV_FUNC(cmd_giveownership)
 {
     struct handle_info *new_owner_hi;
-    struct userData *new_owner, *curr_user;
+    struct userData *new_owner;
+    struct userData *curr_user;
+    struct userData *invoker;
     struct chanData *cData = channel->channel_info;
     struct do_not_register *dnr;
     struct giveownership *giveownership;
+    const char *confirm;
     unsigned int force, override;
     unsigned short co_access, new_owner_old_access;
     char transfer_reason[MAXLEN];
@@ -6719,7 +7352,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);
@@ -6742,7 +7375,7 @@ static CHANSERV_FUNC(cmd_giveownership)
     {
         if(force)
         {
-            new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL, 0);
+            new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL, 0);
         }
         else
         {
@@ -6763,6 +7396,17 @@ 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;
@@ -6802,25 +7446,27 @@ chanserv_expire_user_suspension(void *data)
 {
     struct userData *target = data;
 
-       target->expires = 0;
-       target->flags &= ~USER_SUSPENDED;
+    target->expires = 0;
+    target->flags &= ~USER_SUSPENDED;
 }
 
 static CHANSERV_FUNC(cmd_suspend)
 {
     struct handle_info *hi;
-    struct userData *self, *target;
+    struct userData *actor, *real_actor, *target;
+    unsigned int override = 0;
     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);
+    actor = GetChannelUser(channel->channel_info, user->handle_info);
+    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
     {
         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
         return 0;
     }
-    if(target->access >= self->access)
+    if(target->access >= actor->access)
     {
         reply("MSG_USER_OUTRANKED", hi->handle);
         return 0;
@@ -6848,30 +7494,35 @@ static CHANSERV_FUNC(cmd_suspend)
         expiry = now + duration;
     }
 
-        target->expires = expiry;
+    target->expires = expiry;
 
-        if(target->expires)
-            timeq_add(target->expires, chanserv_expire_user_suspension, target);
+    if(target->expires)
+        timeq_add(target->expires, chanserv_expire_user_suspension, target);
 
+    if(!real_actor || target->access >= real_actor->access)
+        override = CMD_LOG_OVERRIDE;
     target->flags |= USER_SUSPENDED;
     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
-    return 1;
+    return 1 | override;
 }
 
 static CHANSERV_FUNC(cmd_unsuspend)
 {
     struct handle_info *hi;
-    struct userData *self, *target;
+    struct userData *actor = NULL, *real_actor = NULL, *target;
+    unsigned int override = 0;
 
     REQUIRE_PARAMS(2);
-    if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
-    self = GetChannelUser(channel->channel_info, user->handle_info);
+    if(!(hi = modcmd_get_handle_info(user, argv[1]))) 
+        return 0;
+    actor = GetChannelUser(channel->channel_info, user->handle_info);
+    real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
     {
         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
         return 0;
     }
-    if(target->access >= self->access)
+    if(target->access >= actor->access)
     {
         reply("MSG_USER_OUTRANKED", hi->handle);
         return 0;
@@ -6881,11 +7532,13 @@ static CHANSERV_FUNC(cmd_unsuspend)
         reply("CSMSG_NOT_SUSPENDED", hi->handle);
         return 0;
     }
+    if(!real_actor || target->access >= real_actor->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;
+    return 1 | override;
 }
 
 static MODCMD_FUNC(cmd_deleteme)
@@ -6924,7 +7577,7 @@ static MODCMD_FUNC(cmd_deleteme)
 static void
 chanserv_refresh_topics(UNUSED_ARG(void *data))
 {
-    unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
+    unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
     struct chanData *cData;
     char opt;
 
@@ -6947,17 +7600,20 @@ chanserv_refresh_topics(UNUSED_ARG(void *data))
 static void
 chanserv_auto_resync(UNUSED_ARG(void *data))
 {
-    unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
+    unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
     struct chanData *cData;
     char opt;
 
     for(cData = channelList; cData; cData = cData->next)
     {
-        if(IsSuspended(cData)) continue;
+        if(IsSuspended(cData)) 
+            continue;
         opt = cData->chOpts[chResync];
-        if(opt == 'n') continue;
-        if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
-               resync_channel(cData->channel);
+        if(opt == 'n') 
+            continue;
+        if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) 
+            continue;
+        resync_channel(cData->channel);
         cData->last_resync = refresh_num;
     }
     timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
@@ -7007,12 +7663,15 @@ static CHANSERV_FUNC(cmd_wut)
 
 static CHANSERV_FUNC(cmd_roulette)
 {
-    if(channel) {
+    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");
+        if (cData) 
+        {
+            if (cData->roulette_chamber) 
+            {
+                DelUser(user, chanserv, 1, "BANG - Don't stuff bullets into a loaded gun");
                 return 1;
             }
         
@@ -7025,21 +7684,29 @@ static CHANSERV_FUNC(cmd_roulette)
 }
 static CHANSERV_FUNC(cmd_shoot)
 {
-    if(channel) {
+    if(channel) 
+    {
         struct chanData *cData = channel->channel_info;
 
-        if (cData->roulette_chamber <= 0) {
-            reply("CSMSG_ROULETTE_NEW");
+        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) {
+        if (cData->roulette_chamber == 0) 
+        {
             reply("CSMSG_ROULETTE_BANG");
             reply("CSMSG_ROULETTE_BETTER_LUCK", user->nick);
             DelUser(user, chanserv, 1, "BANG!!!!");
-        } else
+        } 
+        else
             reply("CSMSG_ROULETTE_CLICK");
     }
 
@@ -7062,7 +7729,8 @@ int lamepart(struct userNode *nick) {
     struct modeNode *mn;
     unsigned int count, n;
 
-    for (n=count=0; n<nick->channels.used; n++) {
+    for (n=count=0; n<nick->channels.used; n++) 
+    {
         mn = nick->channels.list[n];
         irc_svspart(chanserv, nick, mn->channel);
     }
@@ -7075,7 +7743,7 @@ static CHANSERV_FUNC(cmd_spin)
     if(!channel)
         return 1;
       
-    int type, lamep = 1;
+    int type = 0, lamep = 1;
     char *tstr;
 
     tstr = conf_get_data("server/type", RECDB_QSTRING);
@@ -7090,7 +7758,8 @@ static CHANSERV_FUNC(cmd_spin)
     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) {
+    if(chanserv_conf.wheel->used < 1) 
+    {
         /* wheel actions not defined! eek */
         return 1;
     }
@@ -7099,15 +7768,9 @@ static CHANSERV_FUNC(cmd_spin)
     if(!wheel && *wheel) 
         return 1;
 
-/*
-    log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
-* enable this to be able to manually specify a result for testing:
-    if(argc > 1) {
-      wheel = argv[1];
-    }
-*/
     /* connection reset by peer */
-    if (!strcasecmp(wheel, "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");
@@ -7115,7 +7778,8 @@ static CHANSERV_FUNC(cmd_spin)
               irc_svsquit(chanserv, user, "Connection reset by peer");
     }
     /* part all channels */
-    else if (!strcasecmp(wheel, "partall")) {
+    else if (!strcasecmp(wheel, "partall")) 
+    {
          send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PARTALL");
          if (lamep)
              lamepart(user);
@@ -7123,8 +7787,9 @@ static CHANSERV_FUNC(cmd_spin)
              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];
+    else if (!strcasecmp(wheel, "gline")) 
+    {
+         char target[HOSTLEN + 3];
          int wtime = 120 + rand() % 600;
 
          strcpy(target, "*@");
@@ -7132,11 +7797,11 @@ static CHANSERV_FUNC(cmd_spin)
          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];
+    else if (!strcasecmp(wheel, "shun")) 
+    {
+         char target[HOSTLEN + 3];
          int wtime = 120 + rand() % 600;
 
          strcpy(target, "*@");
@@ -7146,31 +7811,40 @@ static CHANSERV_FUNC(cmd_spin)
          shun_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1);
     }
     /* absolutely nothing */
-    else if (!strcasecmp(wheel, "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")) {
+    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) {
+         while(complete != 1)  
+         {
+            if (rndchans != 15) 
+            {
                 chango = 120 + rand() % 600;
-                sputsock("%s SJ %s #%d %ld", self->numeric, user->numeric, chango, now);
+                sputsock("%s SJ %s #%d "FMT_TIME_T, self->numeric, user->numeric, chango, now);
                 rndchans++;
-            } else {
-                if (roundz0r != 1) {
+            } 
+            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 {
+                } 
+                else 
+                {
                      if (lamep)
                          lamepart(user);
                      else
@@ -7181,12 +7855,14 @@ static CHANSERV_FUNC(cmd_spin)
         }
     }
     /* abuse line added to /whois */
-    else if (!strcasecmp(wheel, "abusewhois")) {
+    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")) {
+    else if (!strcasecmp(wheel, "kickall")) 
+    {
          unsigned int count, n;
          struct modeNode *mn;
 
@@ -7198,22 +7874,22 @@ static CHANSERV_FUNC(cmd_spin)
          }
     }
     /* random nick change */
-    else if (!strcasecmp(wheel, "nickchange")) {
+    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) {
+         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))
@@ -7222,21 +7898,23 @@ static CHANSERV_FUNC(cmd_spin)
 
          SVSNickChange(user, abusednick);
          irc_svsnick(chanserv, user, abusednick);
-
-         clone = AddClone(oldnick, oldident, oldhost, "I got abused by the wheel of misfortune :D");
+         clone = AddLocalUser(oldnick, oldident, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
          timeq_add(now + 300, chanserv_remove_abuse, clone->nick);
     }
     /* kill */
-    else if (!strcasecmp(wheel, "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!");
+         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];
+    else if (!strcasecmp(wheel, "svsignore")) 
+    {
+         //int gagged;
+         int ignoretime = 0;
+         char target[HOSTLEN + 13];
 
          if(IsOper(user)) {
             /* we cant gag opers, so just verbally abuse them */
@@ -7249,51 +7927,67 @@ static CHANSERV_FUNC(cmd_spin)
          strcat(target, user->hostname);
          ignoretime = now + (1 + rand() % 120);
 
-         gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime);
+         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")) {
+    else if (!strcasecmp(wheel, "kickbanall")) 
+    {
          unsigned int count, n;
          struct modeNode *mn;
-         //char ban[IRC_NTOP_MAX_SIZE + 1];
+         //char ban[HOSTLEN + 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++) {
+         for (n=count=0; n<user->channels.used; n++) 
+         {
              struct mod_chanmode *change;
-            unsigned int exists;
-            char *ban;
-            ban = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT|GENMASK_USENICK);
-            log_module(MAIN_LOG, LOG_DEBUG, "Generated ban %s", ban);
+/*           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;
+             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);
              }
 
-            exists = ChannelBanExists(mn->channel, ban);
-            change = mod_chanmode_alloc(2);
-            change->args[0].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
-            change->args[0].u.member = GetUserMode(mn->channel, user);
-            change->argc = 1;
-            if(!exists) {
-              change->args[1].mode = MODE_BAN;
-              change->args[1].u.hostmask = ban;
-              change->argc = 2;
-            }
-            mod_chanmode_announce(chanserv, mn->channel, change);
-            mod_chanmode_free(change);
-            if(exists) {
-               reply("CSMSG_REDUNDANT_BAN", ban, mn->channel->name);
-               free(ban);
-            }
+             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 {
+    else 
+    {
        send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_UNKNOWN", wheel);
     }
 
@@ -7330,13 +8024,14 @@ void eightball(char *outcome, int method, unsigned int seed)
    int answer = 0;
 
 #define NUMOFCOLORS 18
-   char ballcolors[50][50] = {"blue", "red", "green", "yellow",
+   char ballcolors[50][50] = {
+        "blue", "red", "green", "yellow",
         "white", "black", "grey", "brown",
-        "yellow", "pink", "purple", "orange", "teal", "burgandy",
+        "yellow", "pink", "purple", "orange", "teal", "burgundy",
         "fuchsia","turquoise","magenta", "cyan"};
 #define NUMOFLOCATIONS 50
    char balllocations[50][55] = { 
-       "Locke's house", "Oregon", "California", "Indiana", "Canada",
+        "Locke's house", "Oregon", "California", "Indiana", "Canada",
         "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
         "the Caribbean", "the Everglades", "your head", "your pants", "your school",
         "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
@@ -7344,13 +8039,13 @@ void eightball(char *outcome, int method, unsigned int seed)
         "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
         "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
         "your bra", "your hair", "your bed", "the couch", "the wall", 
-       "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands", 
-       "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
+        "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands", 
+        "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
 #define NUMOFPREPS 15
    char ballpreps[50][50] = { 
-       "Near", "Somewhere near", "In", "In", "In", 
-       "In", "Hiding in", "Under", "Next to", "Over", 
-       "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
+        "Near", "Somewhere near", "In", "In", "In", 
+        "In", "Hiding in", "Under", "Next to", "Over", 
+        "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
 #define NUMOFNUMS 34
    char ballnums[50][50] = { 
         "A hundred", "A thousand", "A few", "42",
@@ -7387,13 +8082,13 @@ void eightball(char *outcome, int method, unsigned int seed)
         case 4: strcpy(tmp, "A gross kind of mucky %s.");
                 break;
         case 5: strcpy(tmp, "Brilliant whiteish %s.");
-                       break;
+                break;
         case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
                 break;
         case 10: strcpy(tmp, "Solid %s.");
                 break;
         case 11: strcpy(tmp, "Transparent %s.");
-                       break;
+                break;
         default: strcpy(outcome, "An invalid random number was generated.");
                 return;
       }
@@ -7417,30 +8112,30 @@ void eightball(char *outcome, int method, unsigned int seed)
 
 static CHANSERV_FUNC(cmd_8ball)
 {
-  char *word1, *word2, *word3;
-  static char eb[MAXLEN];
-  unsigned int accum, i, j;
+    char *word1, *word2, *word3;
+    static char eb[MAXLEN];
+    unsigned int accum, i, j;
 
-  REQUIRE_PARAMS(2);
-  accum = 0;
-  for(i=1; i<argc; i++)
-    for(j=0; argv[i][j]; j++)
-      accum = (accum << 5) - accum + toupper(argv[i][j]);
+    REQUIRE_PARAMS(2);
+    accum = 0;
+    for(i=1; i<argc; i++)
+        for(j=0; argv[i][j]; j++)
+            accum = (accum << 5) - accum + toupper(argv[i][j]);
 
-  accum += time(NULL)/3600;
-  word1 = argv[1];
-  word2 = argc>2?argv[2]:"";
-  word3 = argc>3?argv[3]:"";
+    accum += time(NULL)/3600;
+    word1 = argv[1];
+    word2 = argc>2?argv[2]:"";
+    word3 = argc>3?argv[3]:"";
 
 /*** COLOR *****/
-  if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
-     eightball(eb, 1, accum);
-  else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
-     eightball(eb, 1, accum);
-  else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
-     eightball(eb, 1, accum);
+    if((word2) && strcasecmp(word1, "what") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
+        eightball(eb, 1, accum);
+    else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
+        eightball(eb, 1, accum);
+    else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
+        eightball(eb, 1, accum);
 /*** LOCATION *****/
-  else if(
+    else if(
            (
              word2 &&
              (
@@ -7454,24 +8149,24 @@ static CHANSERV_FUNC(cmd_8ball)
          )
      eightball(eb, 2, accum);
 /*** NUMBER *****/
-  else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
-     eightball(eb, 3, accum);
+    else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
+        eightball(eb, 3, accum);
 /*** GENERIC *****/
-  else
-  {
-     /* Generic 8ball question.. so pull from x3.conf srvx style */
-           const char *resp;
-
-           resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
-           if(channel)
-           {
-               char response[MAXLEN];
-               sprintf(response, "\002%s\002: %s", user->nick, resp);
-               irc_privmsg(cmd->parent->bot, channel->name, response);
-           }
-           else
-               send_message_type(4, user, cmd->parent->bot, "%s", resp);
-           return 1;
+    else
+    {
+        /* Generic 8ball question.. so pull from x3.conf srvx style */
+        const char *resp;
+
+        resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
+        if(channel)
+        {
+            char response[MAXLEN];
+            sprintf(response, "\002%s\002: %s", user->nick, resp);
+            irc_privmsg(cmd->parent->bot, channel->name, response);
+        }
+        else
+            send_message_type(4, user, cmd->parent->bot, "%s", resp);
+        return 1;
   }
 
   if(channel)
@@ -7612,7 +8307,7 @@ chanserv_adjust_limit(void *data)
 }
 
 static void
-handle_new_channel(struct chanNode *channel)
+handle_new_channel(struct chanNode *channel, UNUSED_ARG(void *extra))
 {
     struct chanData *cData;
 
@@ -7640,7 +8335,7 @@ trace_check_bans(struct userNode *user, struct chanNode *chan)
     if (chan->channel_info) {
         for(bData = chan->channel_info->bans; bData; bData = bData->next) {
 
-            if(!user_matches_glob(user, bData->mask, MATCH_USENICK))
+            if(!user_matches_glob(user, bData->mask, MATCH_USENICK, 0))
                 continue;
 
             if(bData)
@@ -7672,7 +8367,7 @@ check_bans(struct userNode *user, const char *channel)
     {
         /* Not joining through a ban. */
         for(bData = cData->bans;
-            bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+            bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
             bData = bData->next);
 
         if(bData)
@@ -7714,7 +8409,7 @@ channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
    unsigned int ii;
    for(ii = 0; ii < channel->exemptlist.used; ii++)
    {
-       if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK))
+       if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK, 0))
            return true;
    }
    return false;
@@ -7724,7 +8419,7 @@ channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
 /* Welcome to my worst nightmare. Warning: Read (or modify)
    the code below at your own risk. */
 static int
-handle_join(struct modeNode *mNode)
+handle_join(struct modeNode *mNode, UNUSED_ARG(void *extra))
 {
     struct mod_chanmode change;
     struct userNode *user = mNode->user;
@@ -7759,7 +8454,7 @@ handle_join(struct modeNode *mNode)
         unsigned int ii;
         for(ii = 0; ii < channel->banlist.used; ii++)
         {
-            if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
+            if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK, 0))
             {
                 /* Riding a netburst.  Naughty. */
                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
@@ -7790,7 +8485,7 @@ handle_join(struct modeNode *mNode)
         {
             /* Not joining through a ban. */
             for(bData = cData->bans;
-                bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+                bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
                 bData = bData->next);
 
             if(bData)
@@ -7955,14 +8650,14 @@ chanserv_autojoin_channels(struct userNode *user)
 }
 
 static void
-handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
+handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra))
 {
     struct mod_chanmode change;
     struct userData *channel;
     unsigned int ii, jj, i;
 
     if(!user->handle_info)
-       return;
+    return;
 
     mod_chanmode_init(&change);
     change.argc = 1;
@@ -7989,12 +8684,14 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
             continue;
         }
 
-       if(channel->access >= UL_PRESENT)
-           channel->channel->visited = now;
+    if(channel->access >= UL_PRESENT)
+        channel->channel->visited = now;
 
         if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
         {
-            if(channel->access >= UL_OP )
+            if (channel->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
+                change.args[0].mode = MODE_VOICE;
+            else if(channel->access >= UL_OP )
                 change.args[0].mode = MODE_CHANOP;
             else if(channel->access >= UL_HALFOP )
                 change.args[0].mode = MODE_HALFOP;
@@ -8007,36 +8704,36 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
                 mod_chanmode_announce(chanserv, cn, &change);
         }
 
-       channel->seen = now;
-       channel->present = 1;
+        channel->seen = now;
+        channel->present = 1;
     }
 
     for(ii = 0; ii < user->channels.used; ++ii)
     {
-        struct chanNode *channel = user->channels.list[ii]->channel;
+        struct chanNode *chan = user->channels.list[ii]->channel;
         struct banData *ban;
 
         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
-           || !channel->channel_info
-           || IsSuspended(channel->channel_info))
+           || !chan->channel_info
+           || IsSuspended(chan->channel_info))
             continue;
-        if(protect_user(user, chanserv, channel->channel_info, true))
+        if(protect_user(user, chanserv, chan->channel_info, true))
             continue;
-        for(jj = 0; jj < channel->banlist.used; ++jj)
-            if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
+        for(jj = 0; jj < chan->banlist.used; ++jj)
+            if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK, 0))
                 break;
-        if(jj < channel->banlist.used)
+        if(jj < chan->banlist.used)
             continue;
-        for(ban = channel->channel_info->bans; ban; ban = ban->next)
+        for(ban = chan->channel_info->bans; ban; ban = ban->next)
         {
             char kick_reason[MAXLEN];
-            if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
+            if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE, 0))
                 continue;
             change.args[0].mode = MODE_BAN;
             change.args[0].u.hostmask = ban->mask;
-            mod_chanmode_announce(chanserv, channel, &change);
+            mod_chanmode_announce(chanserv, chan, &change);
             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
-            KickChannelUser(user, channel, chanserv, kick_reason);
+            KickChannelUser(user, chan, chanserv, kick_reason);
             ban->triggered = now;
             break;
         }
@@ -8061,16 +8758,16 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
     }
 
     if (user->handle_info->epithet)
-      irc_swhois(chanserv, user, user->handle_info->epithet);
+        irc_swhois(chanserv, user, user->handle_info->epithet);
 
-   /* process autojoin channels 5 seconds later as this sometimes 
-      happens before autohide */
-//   timeq_add(now + 5, chanserv_autojoin_channels, user);
+    /* process autojoin channels 5 seconds later as this sometimes 
+       happens before autohide */
+    //timeq_add(now + 5, chanserv_autojoin_channels, user);
     chanserv_autojoin_channels(user);
 }
 
 static void
-handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
+handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason), UNUSED_ARG(void *extra))
 {
     struct chanData *cData;
     struct userData *uData;
@@ -8081,30 +8778,37 @@ handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
 
     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
     {
-       /* Allow for a bit of padding so that the limit doesn't
-          track the user count exactly, which could get annoying. */
-       if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
-       {
-           timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
-           timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
-       }
+        /* Allow for a bit of padding so that the limit doesn't
+           track the user count exactly, which could get annoying. */
+        if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
+        {
+            timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
+            timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
+        }
     }
 
     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
     {
-       scan_user_presence(uData, mn->user);
+        scan_user_presence(uData, mn->user);
         uData->seen = now;
+        if (uData->access >= UL_PRESENT)
+            cData->visited = now; 
     }
 
     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
     {
-        unsigned int ii, jj;
-        for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
+        unsigned int ii;
+        for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) 
         {
-            for(jj = 0; jj < mn->user->channels.used; ++jj)
-                if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
-                    break;
-            if(jj < mn->user->channels.used)
+            struct chanNode *channel;
+            struct userNode *exclude;
+            /* When looking at the channel that is being /part'ed, we
+             * have to skip over the client that is leaving.  For
+             * other channels, we must not do that.
+             */
+            channel = chanserv_conf.support_channels.list[ii];
+            exclude = (channel == mn->channel) ? mn->user : NULL;
+            if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
                 break;
         }
         if(ii == chanserv_conf.support_channels.used)
@@ -8113,7 +8817,7 @@ handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
 }
 
 static void
-handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
+handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel, UNUSED_ARG(void *extra))
 {
     struct userData *uData;
 
@@ -8125,7 +8829,7 @@ handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *c
     if(protect_user(victim, kicker, channel->channel_info, false))
     {
         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
-       KickChannelUser(kicker, channel, chanserv, reason);
+        KickChannelUser(kicker, channel, chanserv, reason);
     }
 
     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
@@ -8133,7 +8837,7 @@ handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *c
 }
 
 static int
-handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
+handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic, UNUSED_ARG(void *extra))
 {
     struct chanData *cData;
 
@@ -8176,7 +8880,7 @@ handle_topic(struct userNode *user, struct chanNode *channel, const char *old_to
 }
 
 static void
-handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
+handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change, UNUSED_ARG(void *extra))
 {
     struct mod_chanmode *bounce = NULL;
     unsigned int bnc, ii;
@@ -8229,7 +8933,7 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch
         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
         {
             const char *ban = change->args[ii].u.hostmask;
-            if(!bad_channel_ban(channel, user, ban, NULL, NULL))
+            if(bad_channel_ban(channel, user, ban, NULL, NULL) != 1)
                 continue;
             if(!bounce)
                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
@@ -8251,7 +8955,7 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch
 }
 
 static void
-handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
+handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick), UNUSED_ARG(void *extra))
 {
     struct chanNode *channel;
     struct banData *bData;
@@ -8275,7 +8979,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
             continue;
         /* Look for a matching ban already on the channel. */
         for(jj = 0; jj < channel->banlist.used; ++jj)
-            if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
+            if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK, 0))
                 break;
         /* Need not act if we found one. */
         if(jj < channel->banlist.used)
@@ -8286,7 +8990,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
         /* Look for a matching ban in this channel. */
         for(bData = channel->channel_info->bans; bData; bData = bData->next)
         {
-            if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
+            if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE, 0))
                 continue;
             change.args[0].u.hostmask = bData->mask;
             mod_chanmode_announce(chanserv, channel, &change);
@@ -8298,7 +9002,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
     }
 }
 
-static void handle_rename(struct handle_info *handle, const char *old_handle)
+static void handle_rename(struct handle_info *handle, const char *old_handle, UNUSED_ARG(void *extra))
 {
     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
 
@@ -8311,7 +9015,7 @@ static void handle_rename(struct handle_info *handle, const char *old_handle)
 }
 
 static void
-handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
+handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle, UNUSED_ARG(void *extra))
 {
     struct userNode *h_user;
 
@@ -8325,8 +9029,8 @@ handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
     }
 }
 
-static void
-handle_server_link(UNUSED_ARG(struct server *server))
+static int 
+handle_server_link(UNUSED_ARG(struct server *server), UNUSED_ARG(void *extra))
 {
     struct chanData *cData;
 
@@ -8343,6 +9047,7 @@ handle_server_link(UNUSED_ARG(struct server *server))
             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
         }
     }
+    return 0;
 }
 
 static void
@@ -8358,8 +9063,8 @@ chanserv_conf_read(void)
 
     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
     {
-       log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
-       return;
+        log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
+        return;
     }
     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
         UnlockChannel(chanserv_conf.support_channels.list[ii]);
@@ -8402,6 +9107,8 @@ 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);
@@ -8442,6 +9149,17 @@ chanserv_conf_read(void)
         chanserv_conf.default_modes = *change;
         mod_chanmode_free(change);
     }
+    str = database_get_data(conf_node, KEY_VALID_CHANNEL_REGEX, RECDB_QSTRING);
+    if (chanserv_conf.valid_channel_regex_set)
+        regfree(&chanserv_conf.valid_channel_regex);
+    if (str) {
+        int err = regcomp(&chanserv_conf.valid_channel_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
+        chanserv_conf.valid_channel_regex_set = !err;
+        if (err) log_module(CS_LOG, LOG_ERROR, "Bad valid_channel_regex (error %d)", err);
+    } 
+    else {
+        chanserv_conf.valid_channel_regex_set = 0;
+    }
     free_string_list(chanserv_conf.wheel);
     strlist = database_get_data(conf_node, "wheel", RECDB_STRING_LIST);
     if(strlist)
@@ -8451,7 +9169,8 @@ chanserv_conf_read(void)
        static const char *list[] = {
           "peer", "partall", "gline",  /* "shun", */
           "nothing", "randjoin", "abusewhois", "kickall", 
-          "nickchange", "kill", "svsignore", "kickbanall" };
+          "nickchange", "kill", "svsignore", "kickbanall", 
+          NULL};
        unsigned int ii;
        strlist = alloc_string_list(ArrayLength(list)-1);
        for(ii=0; list[ii]; ii++)
@@ -8478,7 +9197,6 @@ chanserv_conf_read(void)
             /* delimiter */
             NULL
         };
-        unsigned int ii;
         strlist = alloc_string_list(ArrayLength(list)-1);
         for(ii=0; list[ii]; ii++)
             string_list_append(strlist, strdup(list[ii]));
@@ -8579,19 +9297,19 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
     struct userData *uData;
     char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
     time_t last_seen;
-    unsigned short access, lastaccess = 0;
+    unsigned short access_level, lastaccess = 0;
 
     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
     {
-       log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
-       return;
+        log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
+        return;
     }
 
-    access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
-    if(access > UL_OWNER)
+    access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
+    if(access_level > UL_OWNER)
     {
-       log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
-       return;
+        log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
+        return;
     }
 
     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
@@ -8611,7 +9329,7 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
         return;
     }
 
-    uData = add_channel_user(chan, handle, access, last_seen, inf, 0);
+    uData = add_channel_user(chan, handle, access_level, last_seen, inf, 0);
     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
     uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
 
@@ -8649,14 +9367,14 @@ 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;
+    //struct banData *bData;
     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
     time_t set_time, triggered_time, expires_time;
 
     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
     {
-       log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
-       return;
+        log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
+        return;
     }
 
     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
@@ -8680,7 +9398,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 *
@@ -8756,7 +9474,7 @@ chanserv_channel_read(const char *key, struct record_data *hir)
     if(!cData)
     {
         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
-       return 0;
+        return 0;
     }
 
     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
@@ -8794,7 +9512,7 @@ chanserv_channel_read(const char *key, struct record_data *hir)
         enum charOption chOpt;
         unsigned int count;
 
-       cData->flags = base64toint(str, 5);
+        cData->flags = base64toint(str, 5);
         count = strlen(str += 5);
         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
         {
@@ -8808,14 +9526,14 @@ chanserv_channel_read(const char *key, struct record_data *hir)
             }
             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
             {
-            case 'c': lvl = UL_COOWNER; break;
-            case 'm': lvl = UL_MANAGER; break;
-            case 'n': lvl = UL_OWNER+1; break;
-            case 'o': lvl = UL_OP; break;
-            case 'p': lvl = UL_PEON; break;
-            case 'h': lvl = UL_HALFOP; break;
-            case 'w': lvl = UL_OWNER; break;
-            default: lvl = 0; break;
+                case 'c': lvl = UL_COOWNER; break;
+                case 'm': lvl = UL_MANAGER; break;
+                case 'n': lvl = UL_OWNER+1; break;
+                case 'o': lvl = UL_OP; break;
+                case 'p': lvl = UL_PEON; break;
+                case 'h': lvl = UL_HALFOP; break;
+                case 'w': lvl = UL_OWNER; break;
+                default: lvl = 0; break;
             }
             cData->lvlOpts[lvlOpt] = lvl;
         }
@@ -8896,9 +9614,10 @@ 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, 0))) {
+       && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) 
+    {
         cData->modes = *modes;
-       if(off_channel > 0)
+        if(off_channel > 0)
           cData->modes.modes_set |= MODE_REGISTERED;
         if(cData->modes.argc > 1)
             cData->modes.argc = 1;
@@ -8908,12 +9627,12 @@ chanserv_channel_read(const char *key, struct record_data *hir)
 
     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
     for(it = dict_first(obj); it; it = iter_next(it))
-       user_read_helper(iter_key(it), iter_data(it), cData);
+    user_read_helper(iter_key(it), iter_data(it), cData);
 
     if(!cData->users && !IsProtected(cData))
     {
         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
-       unregister_channel(cData, "has empty user list.");
+        unregister_channel(cData, "has empty user list.");
         return 0;
     }
 
@@ -8956,6 +9675,7 @@ 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)
@@ -8969,7 +9689,11 @@ chanserv_dnr_read(const char *key, struct record_data *hir)
         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
         return;
     }
-    dnr = chanserv_add_dnr(key, setter, reason);
+    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);
     if(!dnr)
         return;
     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
@@ -9004,8 +9728,8 @@ chanserv_saxdb_read(struct dict *database)
             chanserv_note_type_read(iter_key(it), iter_data(it));
 
     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
-       for(it = dict_first(section); it; it = iter_next(it))
-           chanserv_channel_read(iter_key(it), iter_data(it));
+    for(it = dict_first(section); it; it = iter_next(it))
+        chanserv_channel_read(iter_key(it), iter_data(it));
 
     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
         for(it = dict_first(section); it; it = iter_next(it))
@@ -9033,7 +9757,7 @@ chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
         if(uData->expires)
             saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
-       if(uData->info)
+        if(uData->info)
             saxdb_write_string(ctx, KEY_INFO, uData->info);
         saxdb_end_record(ctx);
     }
@@ -9146,7 +9870,7 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
 
     if(channel->modes.modes_set || channel->modes.modes_clear)
     {
-       mod_chanmode_format(&channel->modes, buf);
+        mod_chanmode_format(&channel->modes, buf);
         saxdb_write_string(ctx, KEY_MODES, buf);
     }
 
@@ -9160,7 +9884,7 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
         saxdb_start_record(ctx, KEY_NOTES, 1);
         for(it = dict_first(channel->notes); it; it = iter_next(it))
         {
-           struct note *note = iter_data(it);
+        struct note *note = iter_data(it);
             saxdb_start_record(ctx, iter_key(it), 0);
             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
@@ -9209,14 +9933,22 @@ static void
 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
 {
     struct do_not_register *dnr;
-    dict_iterator_t it;
+    dict_iterator_t it, next;
 
-    for(it = dict_first(dnrs); it; it = iter_next(it))
+    for(it = dict_first(dnrs); it; it = next)
     {
+        next = iter_next(it);
         dnr = iter_data(it);
+        if(dnr->expires && dnr->expires <= now)
+            continue;
         saxdb_start_record(ctx, dnr->chan_name, 0);
         if(dnr->set)
             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
+        if(dnr->expires)
+        {
+            dict_remove(dnrs, iter_key(it));
+            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);
@@ -9231,7 +9963,7 @@ chanserv_saxdb_write(struct saxdb_context *ctx)
 
     /* Version Control*/
     saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
-      saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
+    saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
     saxdb_end_record(ctx);
 
     /* Notes */
@@ -9257,9 +9989,9 @@ chanserv_saxdb_write(struct saxdb_context *ctx)
 }
 
 static void
-chanserv_db_cleanup(void) {
+chanserv_db_cleanup(UNUSED_ARG(void *extra)) {
     unsigned int ii;
-    unreg_part_func(handle_part);
+    unreg_part_func(handle_part, NULL);
     while(channelList)
         unregister_channel(channelList, "terminating.");
     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
@@ -9283,7 +10015,11 @@ chanserv_db_cleanup(void) {
     }
 }
 
-#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
+#if defined(GCC_VARMACROS)
+# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
+#elif defined(C99_VARMACROS)
+# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
+#endif
 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
 
@@ -9296,19 +10032,20 @@ init_chanserv(const char *nick)
     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
     conf_register_reload(chanserv_conf_read);
 
-    reg_server_link_func(handle_server_link);
-
-    reg_new_channel_func(handle_new_channel);
-    reg_join_func(handle_join);
-    reg_part_func(handle_part);
-    reg_kick_func(handle_kick);
-    reg_topic_func(handle_topic);
-    reg_mode_change_func(handle_mode);
-    reg_nick_change_func(handle_nick_change);
+    if (nick) {
+        reg_server_link_func(handle_server_link, NULL);
+        reg_new_channel_func(handle_new_channel, NULL);
+        reg_join_func(handle_join, NULL);
+        reg_part_func(handle_part, NULL);
+        reg_kick_func(handle_kick, NULL);
+        reg_topic_func(handle_topic, NULL);
+        reg_mode_change_func(handle_mode, NULL);
+        reg_nick_change_func(handle_nick_change, NULL);
+        reg_auth_func(handle_auth, NULL);
+    }
 
-    reg_auth_func(handle_auth);
-    reg_handle_rename_func(handle_rename);
-    reg_unreg_func(handle_unreg);
+    reg_handle_rename_func(handle_rename, NULL);
+    reg_unreg_func(handle_unreg, NULL);
 
     handle_dnrs = dict_new();
     dict_set_free_data(handle_dnrs, free);
@@ -9317,14 +10054,18 @@ init_chanserv(const char *nick)
     mask_dnrs = dict_new();
     dict_set_free_data(mask_dnrs, free);
 
-    reg_svccmd_unbind_func(handle_svccmd_unbind);
+    reg_svccmd_unbind_func(handle_svccmd_unbind, NULL);
     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
-    DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
+    DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+channel", 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);
+    DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
+    DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
 
@@ -9344,6 +10085,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);
@@ -9456,6 +10198,9 @@ init_chanserv(const char *nick)
     DEFINE_CHANNEL_OPTION(ctcpreaction);
     DEFINE_CHANNEL_OPTION(bantimeout);
     DEFINE_CHANNEL_OPTION(inviteme);
+    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_CHANNEL_OPTION(maxsetinfo);
     if(off_channel > 1)
         DEFINE_CHANNEL_OPTION(offchannel);
@@ -9478,15 +10223,18 @@ init_chanserv(const char *nick)
     if(nick)
     {
         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
-        chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
+        chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
         service_register(chanserv)->trigger = '!';
-        reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
+        reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check, NULL);
     }
 
     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
 
     if(chanserv_conf.channel_expire_frequency)
-       timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
+    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);
@@ -9506,7 +10254,7 @@ init_chanserv(const char *nick)
         }    
     }
 
-    reg_exit_func(chanserv_db_cleanup);
+    reg_exit_func(chanserv_db_cleanup, NULL);
     message_register_table(msgtab);
 }