Check account-based alerts when someone authenticates.
[srvx.git] / src / opserv.c
index 83c7f93e6e1f9b5556b6a0de276869d313a60f2f..eea1adaaa44fe7d7f166295d04865d632b307f09 100644 (file)
@@ -151,6 +151,7 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_BADWORD_LIST", "Bad words: %s" },
     { "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" },
     { "OSMSG_GLINE_COUNT", "There are %d glines active on the network." },
+    { "OSMSG_NO_GLINE", "$b%s$b is not a known G-line." },
     { "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" },
     { "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" },
     { "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" },
@@ -195,7 +196,7 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" },
     { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" },
     { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." },
-    { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
+    { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, lastmod %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." },
@@ -265,6 +266,7 @@ static dict_t opserv_hostinfo_dict; /* data is struct opserv_hostinfo* */
 static dict_t opserv_user_alerts; /* data is struct opserv_user_alert* */
 static dict_t opserv_nick_based_alerts; /* data is struct opserv_user_alert* */
 static dict_t opserv_channel_alerts; /* data is struct opserv_user_alert* */
+static dict_t opserv_account_alerts; /* data is struct opserv_user_alert* */
 static struct module *opserv_module;
 static struct log_type *OS_LOG;
 static unsigned int new_user_flood;
@@ -758,7 +760,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, 1);
+    return gline_add(src_handle, mask, duration, reason, now, now, 1);
 }
 
 static MODCMD_FUNC(cmd_block)
@@ -766,18 +768,23 @@ static MODCMD_FUNC(cmd_block)
     struct userNode *target;
     struct gline *gline;
     char *reason;
+    unsigned long duration = 0;
+    unsigned int offset = 2;
 
     target = GetUserH(argv[1]);
     if (!target) {
-       reply("MSG_NICK_UNKNOWN", argv[1]);
-       return 0;
+        reply("MSG_NICK_UNKNOWN", argv[1]);
+        return 0;
     }
     if (IsService(target)) {
-       reply("MSG_SERVICE_IMMUNE", target->nick);
-       return 0;
+        reply("MSG_SERVICE_IMMUNE", target->nick);
+        return 0;
     }
-    reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL;
-    gline = opserv_block(target, user->handle_info->handle, reason, 0);
+    if(argc > 2 && (duration = ParseInterval(argv[2]))) {
+        offset = 3;
+    }
+    reason = (argc > offset) ? unsplit_string(argv+offset, argc-offset, NULL) : NULL;
+    gline = opserv_block(target, user->handle_info->handle, reason, duration);
     reply("OSMSG_GLINE_ISSUED", gline->target);
     return 1;
 }
@@ -802,7 +809,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, 1);
+    gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 1);
     reply("OSMSG_GLINE_ISSUED", gline->target);
     return 1;
 }
@@ -1184,11 +1191,11 @@ static MODCMD_FUNC(cmd_whois)
        if (IsWallOp(target)) buffer[bpos++] = 'w';
        if (IsOper(target)) buffer[bpos++] = 'o';
        if (IsGlobal(target)) buffer[bpos++] = 'g';
-       if (IsServNotice(target)) buffer[bpos++] = 's';
-       if (IsHelperIrcu(target)) buffer[bpos++] = 'h';
        if (IsService(target)) buffer[bpos++] = 'k';
        if (IsDeaf(target)) buffer[bpos++] = 'd';
+       if (IsNoChan(target)) buffer[bpos++] = 'n';
         if (IsHiddenHost(target)) buffer[bpos++] = 'x';
+        if (IsNoIdle(target)) buffer[bpos++] = 'I';
         if (IsGagged(target)) buffer_cat(" (gagged)");
        if (IsRegistering(target)) buffer_cat(" (registered account)");
        buffer[bpos] = 0;
@@ -1303,11 +1310,6 @@ static MODCMD_FUNC(cmd_stats_bad) {
     return 1;
 }
 
-static MODCMD_FUNC(cmd_stats_glines) {
-    reply("OSMSG_GLINE_COUNT", gline_count());
-    return 1;
-}
-
 static void
 trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) {
     unsigned int nn, pos;
@@ -1484,11 +1486,15 @@ static MODCMD_FUNC(cmd_stats_uplink) {
 }
 
 static MODCMD_FUNC(cmd_stats_uptime) {
+    extern int lines_processed;
+    extern time_t boot_time;
+    double kernel_time;
+    double user_time;
     char uptime[INTERVALLEN];
+
+#if defined(HAVE_TIMES)
+    static double clocks_per_sec;
     struct tms buf;
-    extern time_t boot_time;
-    extern int lines_processed;
-    static long clocks_per_sec;
 
     if (!clocks_per_sec) {
 #if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
@@ -1500,12 +1506,27 @@ static MODCMD_FUNC(cmd_stats_uptime) {
             clocks_per_sec = CLOCKS_PER_SEC;
         }
     }
-    intervalString(uptime, time(NULL)-boot_time, user->handle_info);
     times(&buf);
-    reply("OSMSG_UPTIME_STATS",
-          uptime, lines_processed,
-          buf.tms_utime/(double)clocks_per_sec,
-          buf.tms_stime/(double)clocks_per_sec);
+    user_time = buf.tms_utime / clocks_per_sec;
+    kernel_time = buf.tms_stime / clocks_per_sec;
+#elif defined(HAVE_GETPROCESSTIMES)
+    FILETIME times[4];
+    LARGE_INTEGER li[2];
+
+    GetProcessTimes(GetCurrentProcess(), &times[0], &times[1], &times[2], &times[3]);
+    li[0].LowPart = times[2].dwLowDateTime;
+    li[0].HighPart = times[2].dwHighDateTime;
+    kernel_time = li[0].QuadPart * 1e-7;
+    li[1].LowPart = times[3].dwLowDateTime;
+    li[1].HighPart = times[3].dwHighDateTime;
+    user_time = li[1].QuadPart * 1e-7;
+#else
+    user_time = NAN;
+    system_time = NAN;
+#endif
+
+    intervalString(uptime, time(NULL)-boot_time, user->handle_info);
+    reply("OSMSG_UPTIME_STATS", uptime, lines_processed, user_time, kernel_time);
     return 1;
 }
 
@@ -1650,7 +1671,7 @@ opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick,
            return NULL;
        }
     }
-    if ((resv = AddClone(nick, ident, host, desc))) {
+    if ((resv = AddLocalUser(nick, ident, host, desc, "+i"))) {
         dict_insert(opserv_reserved_nick_dict, resv->nick, resv);
     }
     return resv;
@@ -1780,7 +1801,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, "AUTO Excessive connections from a single host.", now, 1);
+            gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, now, 1);
         }
     }
 
@@ -1914,7 +1935,7 @@ opserv_join_check(struct modeNode *mNode)
         struct mod_chanmode change;
         mod_chanmode_init(&change);
         channel->join_flooded = 1;
-        if (opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
+       if (opserv && opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
             if (!GetUserMode(channel, opserv)) {
                 /* If we aren't in the channel, join it. */
                 change.args[0].mode = MODE_CHANOP;
@@ -2252,7 +2273,7 @@ static MODCMD_FUNC(cmd_clone)
            reply("OSMSG_NOT_A_HOSTMASK");
            return 0;
        }
-       if (!(clone = AddClone(argv[2], ident, argv[3]+i, userinfo))) {
+       if (!(clone = AddLocalUser(argv[2], ident, argv[3]+i, userinfo, "+i"))) {
             reply("OSMSG_CLONE_FAILED", argv[2]);
             return 0;
         }
@@ -2433,7 +2454,7 @@ int add_reserved(const char *key, void *data, void *extra)
        log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key);
        return 0;
     }
-    if ((reserve = AddClone(key, ident, hostname, desc))) {
+    if ((reserve = AddLocalUser(key, ident, hostname, desc, "+i"))) {
         reserve->modes |= FLAGS_PERSISTENT;
         dict_insert(extra, reserve->nick, reserve);
     }
@@ -2575,6 +2596,8 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react
         dict_insert(opserv_channel_alerts, name_dup, alert);
     if (alert->discrim->mask_nick)
         dict_insert(opserv_nick_based_alerts, name_dup, alert);
+    if (alert->discrim->accountmask || alert->discrim->authed != -1)
+        dict_insert(opserv_account_alerts, name_dup, alert);
     return alert;
 }
 
@@ -3041,7 +3064,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in
                     discrim->max_ts = now - (ParseInterval(cmp+1) - 1);
                 }
             } else {
-                discrim->min_ts = now - ParseInterval(cmp+2);
+                discrim->min_ts = now - ParseInterval(cmp);
             }
         } else if (irccasecmp(argv[i], "access") == 0) {
             const char *cmp = argv[++i];
@@ -3061,7 +3084,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in
                     discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
                 }
             } else {
-                discrim->min_level = strtoul(cmp+2, NULL, 0);
+                discrim->min_level = strtoul(cmp, NULL, 0);
             }
         } else if ((irccasecmp(argv[i], "abuse") == 0)
                    && (irccasecmp(argv[++i], "opers") == 0)) {
@@ -3463,7 +3486,8 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[])
     discrim = calloc(1, sizeof(*discrim));
     discrim->limit = 25;
     discrim->max_users = ~0;
-    discrim->max_ts = (time_t)~0;
+    /* So, time_t is frequently signed.  Fun. */
+    discrim->max_ts = (1ul << (CHAR_BIT * sizeof(time_t) - 1)) - 1;
 
     for (i = 0; i < argc; i++) {
        /* Assume all criteria require arguments. */
@@ -3491,7 +3515,7 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[])
                 else
                     discrim->min_users = strtoul(cmp+1, NULL, 0) + 1;
             } else {
-                discrim->min_users = strtoul(cmp+2, NULL, 0);
+                discrim->min_users = strtoul(cmp, NULL, 0);
             }
        } else if (!irccasecmp(argv[i], "timestamp")) {
            const char *cmp = argv[++i];
@@ -3643,11 +3667,38 @@ static void
 gtrace_print_func(struct gline *gline, void *extra)
 {
     struct gline_extra *xtra = extra;
-    char *when_text, set_text[20];
-    strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&gline->issued));
-    when_text = asctime(localtime(&gline->expires));
-    when_text[strlen(when_text)-1] = 0; /* strip lame \n */
-    send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, set_text, gline->issuer, when_text, gline->reason);
+    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);
+}
+
+static MODCMD_FUNC(cmd_stats_glines) {
+    if (argc < 2) {
+        reply("OSMSG_GLINE_COUNT", gline_count());
+        return 1;
+    } else if (argc < 3) {
+        struct gline_extra extra;
+        struct gline *gl;
+
+        extra.user = user;
+        gl = gline_find(argv[1]);
+        if (!gl)
+            reply("OSMSG_NO_GLINE", argv[1]);
+        else
+            gtrace_print_func(gl, &extra);
+        return 1;
+    } else return 0;
 }
 
 static void
@@ -3789,6 +3840,8 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han
         send_channel_notice(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle);
     else
         send_channel_notice(opserv_conf.staff_auth_channel, opserv, "%s [%s@%s] authed to %s account %s", user->nick, user->ident, user->hostname, type, user->handle_info->handle);
+
+    dict_foreach(opserv_account_alerts, alert_check_user, user);
 }
 
 static MODCMD_FUNC(cmd_log)
@@ -3924,10 +3977,11 @@ static MODCMD_FUNC(cmd_delalert)
     for (i=1; i<argc; i++) {
         dict_remove(opserv_nick_based_alerts, argv[i]);
         dict_remove(opserv_channel_alerts, argv[i]);
-       if (dict_remove(opserv_user_alerts, argv[i]))
-           reply("OSMSG_REMOVED_ALERT", argv[i]);
+        dict_remove(opserv_account_alerts, argv[i]);
+        if (dict_remove(opserv_user_alerts, argv[i]))
+            reply("OSMSG_REMOVED_ALERT", argv[i]);
         else
-           reply("OSMSG_NO_SUCH_ALERT", argv[i]);
+            reply("OSMSG_NO_SUCH_ALERT", argv[i]);
     }
     return 1;
 }
@@ -4027,6 +4081,8 @@ opserv_db_init(void) {
     dict_set_free_keys(opserv_chan_warn, free);
     dict_set_free_data(opserv_chan_warn, free);
     /* set up opserv_user_alerts */
+    dict_delete(opserv_account_alerts);
+    opserv_account_alerts = dict_new();
     dict_delete(opserv_channel_alerts);
     opserv_channel_alerts = dict_new();
     dict_delete(opserv_nick_based_alerts);
@@ -4057,6 +4113,7 @@ opserv_db_cleanup(void)
     unreg_del_user_func(opserv_user_cleanup);
     dict_delete(opserv_hostinfo_dict);
     dict_delete(opserv_nick_based_alerts);
+    dict_delete(opserv_account_alerts);
     dict_delete(opserv_channel_alerts);
     dict_delete(opserv_user_alerts);
     for (nn=0; nn<ArrayLength(level_strings); ++nn)
@@ -4073,7 +4130,7 @@ init_opserv(const char *nick)
     OS_LOG = log_register_type("OpServ", "file:opserv.log");
     if (nick) {
         const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING);
-        opserv = AddService(nick, modes ? modes : NULL, "Oper Services", NULL);
+        opserv = AddLocalUser(nick, nick, NULL, "Oper Services", modes);
     }
     conf_register_reload(opserv_conf_read);