]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/opserv.c
Added tempshun support to OpServ trace and addalert
[irc/evilnet/x3.git] / src / opserv.c
index e3aacab5a6565bb2eecb6ea88a2b4a076a8bd012..44808812f0f8e9c1c145f1d0f06f8b896199f006 100644 (file)
@@ -182,12 +182,14 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_KICKALL_DONE", "I have cleared out %s." },
     { "OSMSG_LEAVING", "Leaving $b%s$b." },
     { "OSMSG_MARK_INVALID", "Sorry, marks must contain only letters, numbers, and dashes ('-')." },
+    { "OSMSG_MARK_NOTMARKED", "Action not mark but mark supplied. (Did you mean marked?)" },
     { "OSMSG_MODE_SET", "I have set the modes for $b%s$b." },
     { "OSMSG_OP_DONE", "Opped the requested lusers." },
     { "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." },
     { "OSMSG_HOP_DONE", "Halfopped the requested lusers." },
     { "OSMSG_HOPALL_DONE", "Halfopped everyone on $b%s$b." },
     { "OMSG_BAD_SVSNICK", "$b%s$b is an invalid nickname." },
+    { "OSMSG_BAD_SVSCMDTARGET", "$b%s$b is an invalid target for %s." },
 
     { "OSMSG_WHOIS_IDENT",      "%s (%s@%s) from %d.%d.%d.%d" },
     { "OSMSG_WHOIS_NICK",       "Nick         : %s" },
@@ -216,7 +218,9 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_WHOIS_CHANNELS",   "Channels     : %s" },
     { "OSMSG_WHOIS_HIDECHANS",  "Channel list omitted for your sanity." },
     { "OSMSG_WHOIS_VERSION",    "Version      : %s" },  
-    { "OSMSG_WHOIS_MARK",       "Mark         : %s" },  
+    { "OSMSG_WHOIS_SSLFP",      "SSL f/print  : %s" },
+    { "OSMSG_WHOIS_MARK",       "Mark         : %s" },
+    { "OSMSG_WHOIS_MARKS",      "Marks        : %s" },
     { "OSMSG_WHOIS_NO_NOTICE",  "No_notices   : %s" },
     { "OSMSG_UNBAN_DONE", "Ban(s) removed from channel %s." },
     { "OSMSG_CHANNEL_VOICED", "All users on %s voiced." },
@@ -305,8 +309,8 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" },
     { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." },
     { "OSMSG_SSYNC_RUNNING", "Synchronizing shuns from %s." },
-    { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, lastmod %s, expires %s): %s" },
-    { "OSMSG_STRACE_FORMAT", "%s (issued %s by %s, lastmod %s, expires %s): %s" },
+    { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
+    { "OSMSG_STRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
     { "OSMSG_GAG_APPLIED", "Gagged $b%s$b, affecting %d users." },
     { "OSMSG_GAG_ADDED", "Gagged $b%s$b." },
     { "OSMSG_REDUNDANT_GAG", "Gag $b%s$b is redundant." },
@@ -570,7 +574,7 @@ struct discrim_and_source {
     unsigned int disp_limit;
 };
 
-static discrim_t opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel);
+static discrim_t opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel, const char *action);
 static unsigned int opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data);
 static int gag_helper_func(struct userNode *match, void *extra);
 static int ungag_helper_func(struct userNode *match, void *extra);
@@ -582,10 +586,13 @@ typedef enum {
     REACT_GLINE,
     REACT_TRACK,
     REACT_SHUN,
+    REACT_TEMPSHUN,
     REACT_SVSJOIN,
     REACT_SVSPART,
     REACT_VERSION,
-    REACT_MARK
+    REACT_MARK,
+    REACT_NOTICEUSER,
+    REACT_MSGUSER
 } opserv_alert_reaction;
 
 struct opserv_user_alert {
@@ -1304,7 +1311,7 @@ opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned l
                  "G-line requested by %s.", src_handle);
     if (!duration)
         duration = opserv_conf.block_gline_duration;
-    return gline_add(src_handle, mask, duration, reason, now, now, 1, silent ? 1 : 0);
+    return gline_add(src_handle, mask, duration, reason, now, 1, silent ? 1 : 0);
 }
 
 static MODCMD_FUNC(cmd_block)
@@ -1376,7 +1383,7 @@ static MODCMD_FUNC(cmd_gline)
         reply("MSG_INVALID_DURATION", argv[2]);
         return 0;
     }
-    gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 1, 0);
+    gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1, 0);
     reply("OSMSG_GLINE_ISSUED", gline->target);
     return 1;
 }
@@ -1457,6 +1464,22 @@ opserv_svsjoin(struct userNode *target, UNUSED_ARG(char *src_handle), UNUSED_ARG
             return; /* channel is invite only */
         }
 
+        if (!IsOper(target) && (channel->modes & MODE_OPERSONLY)) {
+            return; /* user is not oper and channel is opers only */
+        }
+
+        if (!IsAdmin(target) && (channel->modes & MODE_ADMINSONLY)) {
+            return; /* user is not admin and channel is admin only */
+        }
+
+        if (target->handle_info && (channel->modes & MODE_REGONLY)) {
+            return; /* user is not authed and channel is authed only users */
+        }
+
+        if (!IsSSL(target) && (channel->modes & MODE_SSLONLY)) {
+            return; /* user is not SSL and channel is SSL only */
+        }
+
         if (channel->limit > 0) {
             if (channel->members.used >= channel->limit) {
                 return; /* channel is invite on */
@@ -1506,7 +1529,13 @@ opserv_shun(struct userNode *target, char *src_handle, char *reason, unsigned lo
         snprintf(reason, MAXLEN, "Shun requested by %s.", src_handle);
     }
     if (!duration) duration = opserv_conf.block_shun_duration;
-    return shun_add(src_handle, mask, duration, reason, now, now, 1);
+    return shun_add(src_handle, mask, duration, reason, now, 1);
+}
+
+static void
+opserv_tempshun(struct userNode *target, char *src_handle, char *reason)
+{
+    irc_tempshun(opserv, target, 0, reason);
 }
 
 static MODCMD_FUNC(cmd_sblock)
@@ -1565,7 +1594,7 @@ static MODCMD_FUNC(cmd_shun)
     struct shun *shun;
 
     reason = unsplit_string(argv+3, argc-3, NULL);
-    if (!is_shun(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) {
+    if (!is_shun(argv[1])) {
         reply("MSG_INVALID_SHUN", argv[1]);
         return 0;
     }
@@ -1578,7 +1607,7 @@ static MODCMD_FUNC(cmd_shun)
         reply("MSG_INVALID_DURATION", argv[2]);
         return 0;
     }
-    shun = shun_add(user->handle_info->handle, argv[1], duration, reason, now, now, 1);
+    shun = shun_add(user->handle_info->handle, argv[1], duration, reason, now, 1);
     reply("OSMSG_SHUN_ISSUED", shun->target);
     return 1;
 }
@@ -1704,6 +1733,11 @@ static MODCMD_FUNC(cmd_svsjoin)
        return 0;
     }
 
+    if (IsLocal(target)) {
+       reply("OSMSG_BAD_SVSCMDTARGET", argv[1], "SVSJOIN");
+       return 0;
+    }
+
     if (!(channel = GetChannel(argv[2]))) {
        channel = AddChannel(argv[2], now, NULL, NULL, NULL);
     }
@@ -1725,6 +1759,12 @@ static MODCMD_FUNC(cmd_svsnick)
        reply("MSG_NICK_UNKNOWN", argv[1]);
        return 0;
     }
+
+    if (IsLocal(target)) {
+       reply("OSMSG_BAD_SVSCMDTARGET", argv[1], "SVSNICK");
+       return 0;
+    }
+
     if(!is_valid_nick(argv[2])) {
        reply("OMSG_BAD_SVSNICK", argv[2]);
        return 0;
@@ -1950,6 +1990,11 @@ static MODCMD_FUNC(cmd_svspart)
        return 0;
     }
 
+    if (IsLocal(target)) {
+       reply("OSMSG_BAD_SVSCMDTARGET", argv[1], "SVSPART");
+       return 0;
+    }
+
     if (!GetUserMode(target_channel, target)) {
         reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, target_channel->name);
         return 0;
@@ -2167,31 +2212,39 @@ static MODCMD_FUNC(cmd_whois)
     if(target->version_reply) {
         reply("OSMSG_WHOIS_VERSION", target->version_reply);
     }
+    if(target->sslfp) {
+        reply("OSMSG_WHOIS_SSLFP", target->sslfp);
+    }
     if(target->mark) {
         reply("OSMSG_WHOIS_MARK", target->mark);
     }
+    if(target->marks) {
+        char markbuf[MAXLEN] = "";
+        unsigned int ii = 0;
+
+        string_list_sort(user->marks);
+
+        for (ii=0; ii<user->marks->used; ii++)
+        {
+            if (markbuf[0] && strlen(markbuf) + strlen(user->marks->list[ii]) + 4 > 70) {
+                reply("OSMSG_WHOIS_MARKS", markbuf);
+                memset(&markbuf, 0, MAXLEN);
+            }
+
+            if (markbuf[0])
+                strcat(markbuf, ", ");
+            strcat(markbuf, user->marks->list[ii]);
+        }
+
+        if (markbuf[0])
+            reply("OSMSG_WHOIS_MARKS", markbuf);
+    }
+
     reply("OSMSG_WHOIS_NO_NOTICE", target->no_notice ? "YES":"NO");
   
     if (target->modes) {
-        bpos = 0;
+        bpos = irc_user_modes(target, buffer, sizeof(buffer));
 #define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen)
-        if (IsInvisible(target)) buffer[bpos++] = 'i';
-        if (IsWallOp(target)) buffer[bpos++] = 'w';
-        if (IsOper(target)) buffer[bpos++] = 'o';
-        if (IsGlobal(target)) buffer[bpos++] = 'g';
-
-        // sethost - reed/apples
-        // if (IsHelperIrcu(target)) buffer[bpos++] = 'h';
-        if (IsSetHost(target)) buffer[bpos++] = 'h';
-
-        if (IsService(target)) buffer[bpos++] = 'k';
-        if (IsDeaf(target)) buffer[bpos++] = 'd';
-        if (target->handle_info) buffer[bpos++] = 'r';
-        if (IsHiddenHost(target)) buffer[bpos++] = 'x';
-       if (IsBotM(target)) buffer[bpos++] = 'B';
-       if (IsHideChans(target)) buffer[bpos++] = 'n';
-       if (IsHideIdle(target)) buffer[bpos++] = 'I';
-       if (IsXtraOp(target)) buffer[bpos++] = 'X';
         if (IsGagged(target)) buffer_cat(" (gagged)");
         if (IsRegistering(target)) buffer_cat(" (registered account)");
         buffer[bpos] = 0;
@@ -2541,10 +2594,13 @@ static MODCMD_FUNC(cmd_stats_alerts) {
         case REACT_GLINE: reaction = "gline"; break;
         case REACT_TRACK: reaction = "track"; break;
         case REACT_SHUN: reaction = "shun"; break;
+        case REACT_TEMPSHUN: reaction = "tempshun"; break;
         case REACT_SVSJOIN: reaction = "svsjoin"; break;
         case REACT_SVSPART: reaction = "svspart"; break;
         case REACT_VERSION: reaction = "version"; break;
         case REACT_MARK: reaction = "mark"; break;
+        case REACT_NOTICEUSER: reaction = "noticeuser"; break;
+        case REACT_MSGUSER: reaction = "msguser"; break;
         default: reaction = "<unknown>"; break;
         }
         reply("OSMSG_ALERT_IS", iter_key(it), reaction, alert->owner);
@@ -2758,7 +2814,7 @@ opserv_part_channel(void *data)
 static int alert_check_user(const char *key, void *data, void *extra);
 
 static int
-opserv_new_user_check(struct userNode *user)
+opserv_new_user_check(struct userNode *user, UNUSED_ARG(void *extra))
 {
     struct opserv_hostinfo *ohi;
     struct gag_entry *gag;
@@ -2774,7 +2830,7 @@ opserv_new_user_check(struct userNode *user)
 
     /* Gag them if appropriate. */
     for (gag = gagList; gag; gag = gag->next) {
-        if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
+        if (user_matches_glob(user, gag->mask, MATCH_USENICK, 0)) {
             gag_helper_func(user, NULL);
             break;
         }
@@ -2811,9 +2867,9 @@ opserv_new_user_check(struct userNode *user)
 
         strcpy(target + 2, user->hostname);
         if (checkDefCon(DEFCON_GLINE_NEW_CLIENTS))
-            gline_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, now, 1, 0);
+            gline_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, 1, 0);
         else if (checkDefCon(DEFCON_SHUN_NEW_CLIENTS))
-            shun_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, now, 1);
+            shun_add(opserv->nick, target, DefConGlineExpire, DefConGlineReason, now, 1);
           
         return 0;
     }
@@ -2837,7 +2893,7 @@ opserv_new_user_check(struct userNode *user)
         } else if (ohi->clients.used > limit) {
             char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
             strcpy(target + 2, addr);
-            gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "Excessive connections from a single host.", now, now, 1, 1);
+            gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "Excessive connections from a single host.", now, 1, 1);
         }
     }
 
@@ -2845,7 +2901,7 @@ opserv_new_user_check(struct userNode *user)
 }
 
 static void
-opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
+opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why), UNUSED_ARG(void *extra))
 {
     struct opserv_hostinfo *ohi;
     char addr[IRC_NTOP_MAX_SIZE];
@@ -2909,7 +2965,7 @@ opserv_shutdown_channel(struct chanNode *channel, const char *reason)
 }
 
 static void
-opserv_channel_check(struct chanNode *newchan)
+opserv_channel_check(struct chanNode *newchan, UNUSED_ARG(void *extra))
 {
     /*char *warning; */
 
@@ -2928,7 +2984,7 @@ opserv_channel_check(struct chanNode *newchan)
 }
 
 static void
-opserv_channel_delete(struct chanNode *chan)
+opserv_channel_delete(struct chanNode *chan, UNUSED_ARG(void *extra))
 {
     timeq_del(0, opserv_part_channel, chan, TIMEQ_IGNORE_WHEN);
 }
@@ -2959,7 +3015,7 @@ opserv_notice_handler(struct userNode *user, struct userNode *bot, const char *t
 }
 
 static int
-opserv_join_check(struct modeNode *mNode)
+opserv_join_check(struct modeNode *mNode, UNUSED_ARG(void *extra))
 {
     struct userNode *user = mNode->user;
     struct chanNode *channel = mNode->channel;
@@ -2968,7 +3024,9 @@ opserv_join_check(struct modeNode *mNode)
     if (IsService(user))
         return 0;
 
-    dict_foreach(opserv_channel_alerts, alert_check_user, user);
+    /* Check for alerts, and stop if we find one that kills them. */
+    if (dict_foreach(opserv_user_alerts, alert_check_user, user))
+        return 1;
 
     if (opserv && channel->bad_channel) {
         opserv_debug("Found $b%s$b in bad-word channel $b%s$b; removing the user.", user->nick, channel->name);
@@ -4764,7 +4822,7 @@ add_gag_helper(const char *key, void *data, UNUSED_ARG(void *extra))
 }
 
 static struct opserv_user_alert *
-opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_reaction reaction, const char *text_discrim, int last, int expire)
+opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_reaction reaction, const char *text_discrim, int last, int expire, const char *action)
 {
     unsigned int wordc;
     char *wordv[MAXNUMPARAMS], *discrim_copy;
@@ -4781,7 +4839,7 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react
     alert->last = last;
     discrim_copy = strdup(text_discrim); /* save a copy of the discrim */
     wordc = split_line(discrim_copy, false, ArrayLength(wordv), wordv);
-    alert->discrim = opserv_discrim_create(req, opserv, wordc, wordv, 0);
+    alert->discrim = opserv_discrim_create(req, opserv, wordc, wordv, 0, action);
     alert->expire = expire;
     /* Check for missing required criteria or broken records */
     if (!alert->discrim || (reaction==REACT_SVSJOIN && !alert->discrim->chantarget) ||
@@ -4869,6 +4927,8 @@ add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra))
         reaction = REACT_TRACK;
     else if (!irccasecmp(react, "shun"))
         reaction = REACT_SHUN;
+    else if (!irccasecmp(react, "tempshun"))
+        reaction = REACT_TEMPSHUN;
     else if (!irccasecmp(react, "svsjoin"))
         reaction = REACT_SVSJOIN;
     else if (!irccasecmp(react, "svspart"))
@@ -4877,11 +4937,15 @@ add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra))
         reaction = REACT_VERSION;
     else if (!irccasecmp(react, "mark"))
         reaction = REACT_MARK;
+    else if (!irccasecmp(react, "noticeuser"))
+        reaction = REACT_NOTICEUSER;
+    else if (!irccasecmp(react, "msguser"))
+        reaction = REACT_MSGUSER;
     else {
         log_module(OS_LOG, LOG_ERROR, "Invalid reaction %s for alert %s.", react, key);
         return 0;
     }
-    alert = opserv_add_user_alert(opserv, key, reaction, discrim, last, expire);
+    alert = opserv_add_user_alert(opserv, key, reaction, discrim, last, expire, react);
     if (!alert) {
         log_module(OS_LOG, LOG_ERROR, "Unable to create alert %s from database.", key);
         return 0;
@@ -5160,10 +5224,13 @@ opserv_saxdb_write(struct saxdb_context *ctx)
             case REACT_GLINE: reaction = "gline"; break;
             case REACT_TRACK: reaction = "track"; break;
             case REACT_SHUN: reaction = "shun"; break;
+            case REACT_TEMPSHUN: reaction = "tempshun"; break;
             case REACT_SVSJOIN: reaction = "svsjoin"; break;
             case REACT_SVSPART: reaction = "svspart"; break;
             case REACT_VERSION: reaction = "version"; break;
             case REACT_MARK: reaction = "mark"; break;
+            case REACT_NOTICEUSER: reaction = "noticeuser"; break;
+            case REACT_MSGUSER: reaction = "msguser"; break;
             default:
                 reaction = NULL;
                 log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s (while writing database).", alert->reaction, iter_key(it));
@@ -5265,7 +5332,7 @@ static MODCMD_FUNC(cmd_settime)
 }
 
 static discrim_t
-opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel)
+opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int argc, char *argv[], int allow_channel, const char *action)
 {
     unsigned int i, j;
     discrim_t discrim;
@@ -5373,6 +5440,10 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int
             goto fail;
         }
     } else if (irccasecmp(argv[i], "mark") == 0) {
+        if (irccasecmp(action, "mark")) {
+            send_message(user, bot, "OSMSG_MARK_NOTMARKED");
+            goto fail;
+        }
         if(!is_valid_mark(argv[i+1])) {
             send_message(user, bot, "OSMSG_MARK_INVALID");
             goto fail;
@@ -5660,6 +5731,20 @@ discrim_match(discrim_t discrim, struct userNode *user)
 {
     unsigned int level, i;
     char *scmp=NULL, *dcmp=NULL;
+    int markmatched = 0;
+
+    if (discrim->mask_mark)
+    {
+        unsigned int ii = 0;
+
+        if (user->mark && match_ircglob(user->mark, discrim->mask_mark))
+            markmatched = 1;
+
+        if (user->marks)
+            for (ii=0; ii<user->marks->used; ii++)
+                if (match_ircglob(user->marks->list[ii], discrim->mask_mark))
+                    markmatched = 1;
+    }
 
     if ((user->timestamp < discrim->min_ts)
         || (user->timestamp > discrim->max_ts)
@@ -5670,7 +5755,7 @@ discrim_match(discrim_t discrim, struct userNode *user)
         || (discrim->info_space == 0 && user->info[0] == ' ')
         || (discrim->info_space == 1 && user->info[0] != ' ')
         || (discrim->server && !match_ircglob(user->uplink->name, discrim->server))
-        || (discrim->mask_mark && (!user->mark || !match_ircglob(user->mark, discrim->mask_mark)))
+        || (discrim->mask_mark && !markmatched)
         || (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask)))
         || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits))
         )
@@ -5728,46 +5813,80 @@ discrim_match(discrim_t discrim, struct userNode *user)
 
     if (discrim->modes) {
            unsigned int ii, matches = 0;
+        unsigned int negate = 0;
         for (ii = 0; ii < strlen(discrim->modes); ii++) {
             switch(discrim->modes[ii]) {
+                case '+':
+                    negate = 0;
+                    matches++;
+                    break;
+                case '-':
+                    negate = 1;
+                    matches++;
+                    break;
                 case 'O':
-                    if(IsOper(user)) matches++;
+                    if(IsOper(user) != 0 != negate) matches++;
                     break;
                 case 'o':
-                    if(IsOper(user)) matches++;
+                    if(IsOper(user) != 0 != negate) matches++;
                     break;
                 case 'i':
-                    if(IsInvisible(user)) matches++;
+                    if(IsInvisible(user) != 0 != negate) matches++;
                     break;
                 case 'w':
-                    if(IsWallOp(user)) matches++;
+                    if(IsWallOp(user) != 0 != negate) matches++;
                     break;
                 case 'd':
-                    if(IsDeaf(user)) matches++;
+                    if(IsDeaf(user) != 0 != negate) matches++;
                     break;
                 case 'k':
-                    if(IsService(user)) matches++;
+                    if(IsService(user) != 0 != negate) matches++;
                     break;
                 case 'g':
-                    if(IsGlobal(user)) matches++;
+                    if(IsGlobal(user) != 0 != negate) matches++;
                     break;
                 case 'h':
-                    if(IsSetHost(user)) matches++;
+                    if(IsSetHost(user) != 0 != negate) matches++;
                     break;
                 case 'B':
-                    if(IsBotM(user)) matches++;
+                    if(IsBotM(user) != 0 != negate) matches++;
                     break;
+                case 'p':
                 case 'n':
-                    if(IsHideChans(user)) matches++;
+                    if(IsHideChans(user) != 0 != negate) matches++;
                     break;
                 case 'I':
-                    if(IsHideIdle(user)) matches++;
+                    if(IsHideIdle(user) != 0 != negate) matches++;
                     break;
                 case 'X':
-                    if(IsXtraOp(user)) matches++;
+                    if(IsXtraOp(user) != 0 != negate) matches++;
                     break;
                 case 'x':
-                    if(IsHiddenHost(user)) matches++;
+                    if(IsHiddenHost(user) != 0 != negate) matches++;
+                    break;
+                case 'a':
+                    if(IsAdmin(user) != 0 != negate) matches++;
+                    break;
+                case 'z':
+                    if(IsSSL(user) != 0 != negate) matches++;
+                    break;
+                case 'D':
+                    if(IsPrivDeaf(user) != 0 != negate) matches++;
+                    break;
+                case 'R':
+                    if(IsAccountOnly(user) != 0 != negate) matches++;
+                    break;
+                case 'W':
+                    if(IsWhoisNotice(user) != 0 != negate) matches++;
+                    break;
+                case 'H':
+                    if(IsHideOper(user) != 0 != negate) matches++;
+                    break;
+                case 'L':
+                    if(IsHideOper(user) != 0 != negate) matches++;
+                    break;
+                case 'q':
+                    if(IsCommonChansOnly(user) != 0 != negate) matches++;
                     break;
             }
         }
@@ -5883,6 +6002,30 @@ trace_print_func(struct userNode *match, void *extra)
     return 0;
 }
 
+static int
+trace_privmsg_func(struct userNode *match, void *extra)
+{
+    struct discrim_and_source *das = extra;
+    char *reason;
+    if (das->discrim->reason) {
+        reason = das->discrim->reason;
+        irc_privmsg_user(opserv, match, reason);
+    }
+    return 0;
+}
+
+static int
+trace_notice_func(struct userNode *match, void *extra)
+{
+    struct discrim_and_source *das = extra;
+    char *reason;
+    if (das->discrim->reason) {
+        reason = das->discrim->reason;
+        irc_notice_user(opserv, match, reason);
+    }
+    return 0;
+}
+
 static int
 trace_count_func(UNUSED_ARG(struct userNode *match), UNUSED_ARG(void *extra))
 {
@@ -5943,6 +6086,18 @@ trace_shun_func(struct userNode *match, void *extra)
     return 0;
 }
 
+static int
+trace_tempshun_func(struct userNode *match, void *extra)
+{
+  struct discrim_and_source *das = extra;
+
+  if (is_oper_victim(das->source, match, das->discrim->match_opers, 1) && is_trust_victim(match, das->discrim->match_trusted)) {
+    opserv_tempshun(match, das->source->handle_info->handle, das->discrim->reason);
+  }
+
+  return 0;
+}
+
 static int
 trace_kill_func(struct userNode *match, void *extra)
 {
@@ -6170,12 +6325,18 @@ static MODCMD_FUNC(cmd_trace)
         action = trace_print_func;
     else if (!irccasecmp(argv[1], "count"))
         action = trace_count_func;
+    else if (!irccasecmp(argv[1], "privmsg"))
+        action = trace_privmsg_func;
+    else if (!irccasecmp(argv[1], "notice"))
+        action = trace_notice_func;
     else if (!irccasecmp(argv[1], "domains"))
         action = trace_domains_func;
     else if (!irccasecmp(argv[1], "gline"))
         action = trace_gline_func;
     else if (!irccasecmp(argv[1], "shun"))
         action = trace_shun_func;
+    else if (!irccasecmp(argv[1], "tempshun"))
+        action = trace_tempshun_func;
     else if (!irccasecmp(argv[1], "kill"))
         action = trace_kill_func;
     else if (!irccasecmp(argv[1], "gag"))
@@ -6201,7 +6362,7 @@ static MODCMD_FUNC(cmd_trace)
     das.dict = NULL;
     das.source = user;
     das.destination = cmd->parent->bot;
-    das.discrim = opserv_discrim_create(user, cmd->parent->bot, argc-2, argv+2, 1);
+    das.discrim = opserv_discrim_create(user, cmd->parent->bot, argc-2, argv+2, 1, argv[1]);
     if (!das.discrim)
         return 0;
 
@@ -6504,19 +6665,14 @@ gtrace_print_func(struct gline *gline, void *extra)
 {
     struct gline_extra *xtra = extra;
     char issued[INTERVALLEN];
-    char lastmod[INTERVALLEN];
     char expires[INTERVALLEN];
 
     intervalString(issued, now - gline->issued, xtra->user->handle_info);
-    if (gline->lastmod)
-        intervalString(lastmod, now - gline->lastmod, xtra->user->handle_info);
-    else
-        strcpy(lastmod, "<unknown>");
     if (gline->expires)
         intervalString(expires, gline->expires - now, xtra->user->handle_info);
     else
         strcpy(expires, "never");
-    send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason);
+    send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, expires, gline->reason);
 }
 
 static MODCMD_FUNC(cmd_stats_glines) {
@@ -6613,19 +6769,14 @@ strace_print_func(struct shun *shun, void *extra)
 {
     struct shun_extra *xtra = extra;
     char issued[INTERVALLEN];
-    char lastmod[INTERVALLEN];
     char expires[INTERVALLEN];
 
     intervalString(issued, now - shun->issued, xtra->user->handle_info);
-    if (shun->lastmod)
-        intervalString(lastmod, now - shun->lastmod, xtra->user->handle_info);
-    else
-        strcpy(lastmod, "<unknown>");
     if (shun->expires)
         intervalString(expires, shun->expires - now, xtra->user->handle_info);
     else
         strcpy(expires, "never");
-    send_message(xtra->user, opserv, "OSMSG_STRACE_FORMAT", shun->target, issued, shun->issuer, lastmod, expires, shun->reason);
+    send_message(xtra->user, opserv, "OSMSG_STRACE_FORMAT", shun->target, issued, shun->issuer, expires, shun->reason);
 }
 
 static MODCMD_FUNC(cmd_stats_shuns) {
@@ -6749,6 +6900,9 @@ alert_check_user(const char *key, void *data, void *extra)
     case REACT_SHUN:
         opserv_shun(user, alert->owner, alert->discrim->reason, alert->discrim->duration);
         return 1;
+    case REACT_TEMPSHUN:
+        opserv_tempshun(user, alert->owner, alert->discrim->reason);
+        return 1;
     case REACT_SVSJOIN:
         opserv_svsjoin(user, alert->owner, alert->discrim->reason, alert->discrim->chantarget, alert->discrim->checkrestrictions);
         break;
@@ -6779,18 +6933,24 @@ alert_check_user(const char *key, void *data, void *extra)
        add_track_user(user);
 #endif
        break;
+    case REACT_NOTICEUSER:
+        irc_notice_user(opserv, user, alert->discrim->reason);
+        break;
+    case REACT_MSGUSER:
+        irc_privmsg_user(opserv, user, alert->discrim->reason);
+        break;
     }
     return 0;
 }
 
 static void
-opserv_alert_check_account(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
+opserv_alert_check_account(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra))
 {
     dict_foreach(opserv_account_based_alerts, alert_check_user, user);
 }
 
 static void
-opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
+opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick), UNUSED_ARG(void *extra))
 {
     struct gag_entry *gag;
 
@@ -6799,7 +6959,7 @@ opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
     /* Gag them if appropriate (and only if). */
     user->modes &= ~FLAGS_GAGGED;
     for (gag = gagList; gag; gag = gag->next) {
-        if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
+        if (user_matches_glob(user, gag->mask, MATCH_USENICK, 0)) {
             gag_helper_func(user, NULL);
             break;
         }
@@ -6807,7 +6967,7 @@ opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
 }
 
 static void
-opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
+opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra))
 {
     const char *type;
 
@@ -6956,6 +7116,8 @@ static MODCMD_FUNC(cmd_addalert)
 #endif
     } else if (!irccasecmp(argv[2], "shun"))
         reaction = REACT_SHUN;
+    else if(!irccasecmp(argv[2], "tempshun"))
+        reaction = REACT_TEMPSHUN;
     else if(!irccasecmp(argv[2], "svsjoin")) 
         reaction = REACT_SVSJOIN;
     else if(!irccasecmp(argv[2], "svspart")) 
@@ -6964,6 +7126,10 @@ static MODCMD_FUNC(cmd_addalert)
         reaction = REACT_VERSION;
     else if(!irccasecmp(argv[2], "mark"))
         reaction = REACT_MARK;
+    else if(!irccasecmp(argv[2], "noticeuser"))
+        reaction = REACT_NOTICEUSER;
+    else if(!irccasecmp(argv[2], "msguser"))
+        reaction = REACT_MSGUSER;
     else {
         reply("OSMSG_UNKNOWN_REACTION", argv[2]);
         return 0;
@@ -6973,7 +7139,7 @@ static MODCMD_FUNC(cmd_addalert)
         expire = now + ParseInterval(argv[4]);
 
     if (!svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)
-        || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + (expire ? 5 : 3), argc - (expire ? 5 : 3), NULL), 0, expire)) {
+        || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + (expire ? 5 : 3), argc - (expire ? 5 : 3), NULL), 0, expire, argv[2])) {
         reply("OSMSG_ALERT_ADD_FAILED");
         return 0;
     }
@@ -7210,7 +7376,7 @@ opserv_db_init(void) {
 }
 
 static void
-opserv_db_cleanup(void)
+opserv_db_cleanup(UNUSED_ARG(void* extra))
 {
     unsigned int nn;
 
@@ -7219,7 +7385,7 @@ opserv_db_cleanup(void)
     free_string_list(opserv_bad_words);
     dict_delete(opserv_exempt_channels);
     dict_delete(opserv_trusted_hosts);
-    unreg_del_user_func(opserv_user_cleanup);
+    unreg_del_user_func(opserv_user_cleanup, NULL);
     dict_delete(opserv_hostinfo_dict);
     dict_delete(opserv_nick_based_alerts);
     dict_delete(opserv_account_based_alerts);
@@ -7248,9 +7414,12 @@ init_opserv(const char *nick)
     opserv_define_func("ACCESS", cmd_access, 0, 0, 0);
     opserv_define_func("ADDALERT", cmd_addalert, 800, 0, 4);
     opserv_define_func("ADDALERT NOTICE", NULL, 0, 0, 0);
+    opserv_define_func("ADDALERT NOTICEUSER", NULL, 0, 0, 0);
+    opserv_define_func("ADDALERT MSGUSER", NULL, 0, 0, 0);
     opserv_define_func("ADDALERT SILENT", NULL, 900, 0, 0);
     opserv_define_func("ADDALERT GLINE", NULL, 900, 0, 0);
     opserv_define_func("ADDALERT SHUN", NULL, 900, 0, 0);
+    opserv_define_func("ADDALERT TEMPSHUN", NULL, 900, 0, 0);
     opserv_define_func("ADDALERT TRACK", NULL, 900, 0, 0);
     opserv_define_func("ADDALERT KILL", NULL, 900, 0, 0);
     opserv_define_func("ADDALERT SVSJOIN", NULL, 999, 0, 0);
@@ -7359,9 +7528,12 @@ init_opserv(const char *nick)
     opserv_define_func("TRACE", cmd_trace, 100, 0, 3);
     opserv_define_func("TRACE PRINT", NULL, 0, 0, 0);
     opserv_define_func("TRACE COUNT", NULL, 0, 0, 0);
+    opserv_define_func("TRACE PRIVMSG", NULL, 0, 0, 0);
+    opserv_define_func("TRACE NOTICE", NULL, 0, 0, 0);
     opserv_define_func("TRACE DOMAINS", NULL, 0, 0, 0);
     opserv_define_func("TRACE GLINE", NULL, 600, 0, 0);
     opserv_define_func("TRACE SHUN", NULL, 600, 0, 0);
+    opserv_define_func("TRACE TEMPSHUN", NULL, 600, 0, 0);
     opserv_define_func("TRACE GAG", NULL, 600, 0, 0);
     opserv_define_func("TRACE KILL", NULL, 600, 0, 0);
     opserv_define_func("TRACE VERSION", NULL, 999, 0, 0);
@@ -7390,14 +7562,14 @@ init_opserv(const char *nick)
     opserv_waiting_connections = dict_new();
     dict_set_free_data(opserv_waiting_connections, opserv_free_waiting_connection);
 
-    reg_new_user_func(opserv_new_user_check);
-    reg_nick_change_func(opserv_alert_check_nick);
-    reg_del_user_func(opserv_user_cleanup);
-    reg_new_channel_func(opserv_channel_check); 
-    reg_del_channel_func(opserv_channel_delete);
-    reg_join_func(opserv_join_check);
-    reg_auth_func(opserv_staff_alert);
-    reg_auth_func(opserv_alert_check_account);
+    reg_new_user_func(opserv_new_user_check, NULL);
+    reg_nick_change_func(opserv_alert_check_nick, NULL);
+    reg_del_user_func(opserv_user_cleanup, NULL);
+    reg_new_channel_func(opserv_channel_check, NULL); 
+    reg_del_channel_func(opserv_channel_delete, NULL);
+    reg_join_func(opserv_join_check, NULL);
+    reg_auth_func(opserv_staff_alert, NULL);
+    reg_auth_func(opserv_alert_check_account, NULL);
     reg_notice_func(opserv, opserv_notice_handler);
 
     opserv_db_init();
@@ -7415,6 +7587,6 @@ init_opserv(const char *nick)
     /* start the karma timer, using the saved one if available */
     routing_karma_timer(dict_find(opserv_routing_plan_options, "KARMA_TIMER", NULL));
 
-    reg_exit_func(opserv_db_cleanup);
+    reg_exit_func(opserv_db_cleanup, NULL);
     message_register_table(msgtab);
 }