X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/ff3b058ac51e9caf5cf1fd310b8a401a97a85582..ef5e03051fd86d66844d61f3fcc1c7a82957826a:/src/opserv.c diff --git a/src/opserv.c b/src/opserv.c index b3f3b97..9f1539e 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -76,6 +76,8 @@ #define KEY_WARN "chanwarn" #define KEY_MAX "max" #define KEY_TIME "time" +#define KEY_LAST "last" +#define KEY_EXPIRE "expire" #define KEY_MAX_CLIENTS "max_clients" #define KEY_LIMIT "limit" #define KEY_EXPIRES "expires" @@ -149,12 +151,6 @@ static const struct message_entry msgtab[] = { { "OSMSG_SHUN_FORCE_REMOVED", "Unknown/expired Shun removed for $b%s$b." }, { "OSMSG_SHUN_ONE_REFRESHED", "All Shuns resent to $b%s$b." }, { "OSMSG_SHUN_REFRESHED", "All Shuns refreshed." }, - { "OSMSG_NO_GLINE_CMD", "The GLINE command is not bound so you can only block with the default duration." }, - { "OSMSG_BLOCK_TRUSTED", "$b%s$b is on a trusted ip. If you really want to G-line him, use the GLINE command." }, - { "OSMSG_BLOCK_OPER" , "G-lining $b%s$b (*@%s) would also hit the IRC operator $b%s$b." }, - { "OSMSG_NO_SHUN_CMD", "The SHUN command is not bound so you can only block with the default duration." }, - { "OSMSG_SHUN_TRUSTED", "$b%s$b is on a trusted ip. If you really want to Shun him, use the SHUN command." }, - { "OSMSG_SHUN_OPER" , "Shunning $b%s$b (*@%s) would also hit the IRC operator $b%s$b." }, { "OSMSG_GLINE_ISSUED", "G-line issued for $b%s$b." }, { "OSMSG_GLINE_REMOVED", "G-line removed for $b%s$b." }, { "OSMSG_GLINE_FORCE_REMOVED", "Unknown/expired G-line removed for $b%s$b." }, @@ -170,6 +166,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_NO_DEBUG_CHANNEL", "No debug channel has been configured." }, { "OSMSG_INVITE_DONE", "Invited $b%s$b to $b%s$b." }, { "OSMSG_ALREADY_THERE", "You are already in $b%s$b." }, + { "OSMSG_USER_ALREADY_THERE", "%s is already in $b%s$b." }, { "OSMSG_NOT_THERE", "You not in $b%s$b." }, { "OSMSG_JOIN_DONE", "I have joined $b%s$b." }, { "OSMSG_MARK_SET", "Set the MARK." }, @@ -185,6 +182,8 @@ static const struct message_entry msgtab[] = { { "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_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" }, { "OSMSG_WHOIS_NICK", "Nick : %s" }, { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, @@ -240,8 +239,6 @@ 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_NO_SHUN", "$b%s$b is not a known Shun." }, { "OSMSG_SHUN_COUNT", "There are %d shuns active on the network." }, { "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" }, { "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" }, @@ -301,8 +298,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_STRACE_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." }, @@ -320,8 +317,10 @@ static const struct message_entry msgtab[] = { { "OSMSG_ALERTS_LIST", "$bCurrent $O alerts matching '$b%s$b'$b" }, { "OSMSG_ALERTS_BAR", "----------------------------------------------" }, { "OSMSG_ALERTS_HEADER", "Name Action (by Oper)" }, - { "OSMSG_ALERTS_DESC", " Criteria: %s" }, + { "OSMSG_ALERTS_DESC", " $uCriteria$u: %s" }, + { "OSMSG_ALERTS_LAST", " $uTriggered$u: %s" }, { "OSMSG_ALERT_IS", "$b%-20s$b %-6s (by %s)" }, + { "OSMSG_ALERT_EXPIRE", " $uExpires:$u: %s" }, { "OSMSG_ALERT_END", "----------------End of Alerts-----------------" }, /* routing messages */ { "OSMSG_ROUTINGPLAN", "$bRouting Plan(s)$b" }, @@ -411,8 +410,6 @@ static const struct message_entry msgtab[] = { { "OSMSG_INVALID_REGEX", "Invalid regex: %s: %s (%d)" }, { "OSMSG_TRACK_DISABLED", "Tracking is not currently compiled into X3" }, { "OSMSG_MAXUSERS_RESET", "Max clients has been reset to $b%d$b" }, - { "OSMSG_TRACE_MAX_CHANNELS", "You may not use the 'channel' criterion more than %d times." }, - { "OSMSG_FORCEKICK_LOCAL", "You cannot kick $b%s$b forcefully." }, { "OSMSG_DEFCON_INVALID", "DefCon level %d is invalid, please choose a value between 1 and 5" }, { "OSMSG_DEFCON_ALLOWING_ALL", "DefCon is at level 5 and allowing everything" }, @@ -467,8 +464,8 @@ static dict_t opserv_waiting_connections; /* data is struct waitingConnection */ 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_account_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; @@ -534,12 +531,9 @@ opserv_free_waiting_connection(void *data) free(wc); } -#define DISCRIM_MAX_CHANS 20 - typedef struct opservDiscrim { - struct chanNode *channels[DISCRIM_MAX_CHANS]; - unsigned int channel_count; - char *mask_nick, *mask_ident, *mask_host, *mask_info, *mask_version, *server, *reason, *accountmask, *chantarget, *mark, *mask_mark, *notice_target; + struct chanNode *channel; + char *mask_nick, *mask_ident, *mask_host, *mask_info, *mask_version, *server, *reason, *accountmask, *chantarget, *mark, *mask_mark, *modes; irc_in_addr_t ip_mask; unsigned long limit; time_t min_ts, max_ts; @@ -547,8 +541,8 @@ typedef struct opservDiscrim { unsigned int has_regex_nick : 1, has_regex_ident : 1, has_regex_host : 1, has_regex_info : 1, has_regex_version : 1; unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels; unsigned char ip_mask_bits; - unsigned int match_opers : 1, match_trusted : 1, option_log : 1; - unsigned int chan_req_modes[DISCRIM_MAX_CHANS], chan_no_modes[DISCRIM_MAX_CHANS]; + unsigned int match_opers : 1, option_log : 1; + unsigned int chan_req_modes : 2, chan_no_modes : 2; int authed : 2, info_space : 2; unsigned int intra_scmp : 2, intra_dcmp : 2; unsigned int use_regex : 1; @@ -568,6 +562,7 @@ static discrim_t opserv_discrim_create(struct userNode *user, struct userNode *b 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); +static void alert_expire(void* name); typedef enum { REACT_NOTICE, @@ -586,6 +581,8 @@ struct opserv_user_alert { char *text_discrim, *split_discrim; discrim_t discrim; opserv_alert_reaction reaction; + int last; + time_t expire; }; /* funny type to make it acceptible to dict_set_free_data, far below */ @@ -593,9 +590,8 @@ static void opserv_free_user_alert(void *data) { struct opserv_user_alert *alert = data; - unsigned int i; - for(i = 0; i < alert->discrim->channel_count; i++) - UnlockChannel(alert->discrim->channels[i]); + if (alert->discrim->channel) + UnlockChannel(alert->discrim->channel); free(alert->owner); free(alert->text_discrim); free(alert->split_discrim); @@ -616,8 +612,6 @@ opserv_free_user_alert(void *data) #define opserv_debug(format...) do { if (opserv_conf.debug_channel) send_channel_notice(opserv_conf.debug_channel , opserv , ## format); } while (0) #define opserv_alert(format...) do { if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel , opserv , ## format); } while (0) -#define opserv_custom_alert(chan, format...) do { if (chan) send_target_message(4 , chan , opserv , ## format); else if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel , opserv , ## format); } while (0) - char *defconReverseModes(const char *modes) @@ -1291,7 +1285,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) @@ -1299,10 +1293,6 @@ static MODCMD_FUNC(cmd_block) struct userNode *target; struct gline *gline; char *reason; - unsigned long duration = 0; - unsigned int offset = 2; - unsigned int nn; - struct svccmd *gline_cmd; target = GetUserH(argv[1]); if (!target) { @@ -1313,34 +1303,8 @@ static MODCMD_FUNC(cmd_block) reply("MSG_SERVICE_IMMUNE", target->nick); return 0; } - if (dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)) { - reply("OSMSG_BLOCK_TRUSTED", target->nick); - return 0; - } - - for(nn = 0; nn < curr_opers.used; nn++) { - if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0) { - reply("OSMSG_BLOCK_OPER", target->nick, irc_ntoa(&target->ip), curr_opers.list[nn]->nick); - return 0; - } - } - - if(argc > 2 && (duration = ParseInterval(argv[2]))) { - offset = 3; - } - if(duration && duration != opserv_conf.block_gline_duration) { - /* We require more access when the duration is not the default block duration. */ - gline_cmd = dict_find(cmd->parent->commands, "gline", NULL); - if(!gline_cmd) - { - reply("OSMSG_NO_GLINE_CMD"); - return 0; - } - if(!svccmd_can_invoke(user, cmd->parent->bot, gline_cmd, channel, SVCCMD_NOISY)) - return 0; - } - reason = (argc > offset) ? unsplit_string(argv+offset, argc-offset, NULL) : NULL; - gline = opserv_block(target, user->handle_info->handle, reason, duration, 0); + reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL; + gline = opserv_block(target, user->handle_info->handle, reason, 0, 0); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -1365,7 +1329,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; } @@ -1495,7 +1459,7 @@ 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 MODCMD_FUNC(cmd_sblock) @@ -1539,7 +1503,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; } @@ -1669,7 +1633,7 @@ static MODCMD_FUNC(cmd_svsjoin) channel = AddChannel(argv[2], now, NULL, NULL, NULL); } if (GetUserMode(channel, target)) { - reply("OSMSG_ALREADY_THERE", channel->name); + reply("OSMSG_USER_ALREADY_THERE", target->nick, channel->name); return 0; } irc_svsjoin(opserv, target, channel); @@ -1677,17 +1641,31 @@ static MODCMD_FUNC(cmd_svsjoin) return 1; } +static MODCMD_FUNC(cmd_svsnick) +{ + struct userNode *target; + + target = GetUserH(argv[1]); + if (!target) { + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; + } + if(!is_valid_nick(argv[2])) { + reply("OMSG_BAD_SVSNICK", argv[2]); + return 0; + } + irc_svsnick(opserv, target, argv[2]); + return 1; +} + static MODCMD_FUNC(cmd_join) { struct userNode *bot = cmd->parent->bot; - if (!channel) { - if((argc < 2) || !IsChannelName(argv[1])) - { + if (!IsChannelName(argv[1])) { reply("MSG_NOT_CHANNEL_NAME"); return 0; - } - + } else if (!(channel = GetChannel(argv[1]))) { channel = AddChannel(argv[1], now, NULL, NULL, NULL); AddChannelUser(bot, channel)->modes |= MODE_CHANOP; } else if (GetUserMode(channel, bot)) { @@ -1701,40 +1679,11 @@ static MODCMD_FUNC(cmd_join) change.args[0].u.member = AddChannelUser(bot, channel); modcmd_chanmode_announce(&change); } - irc_fetchtopic(bot, channel->name); reply("OSMSG_JOIN_DONE", channel->name); return 1; } -static MODCMD_FUNC(cmd_forcekick) -{ - struct userNode *target; - char *reason; - - if (argc < 3) { - reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); - sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); - } else { - reason = unsplit_string(argv+2, argc-2, NULL); - } - target = GetUserH(argv[1]); - if (!target) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; - } - if (!GetUserMode(channel, target)) { - reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); - return 0; - } - if (IsLocal(target)) { - reply("OSMSG_FORCEKICK_LOCAL", target->nick); - return 0; - } - irc_kick(cmd->parent->bot, target, channel, reason); - return 1; -} - static MODCMD_FUNC(cmd_kick) { struct userNode *target; @@ -1878,28 +1827,29 @@ static MODCMD_FUNC(cmd_kickbanall) static MODCMD_FUNC(cmd_svspart) { struct userNode *target; + struct chanNode *target_channel; if(!IsChannelName(argv[2])) { reply("MSG_NOT_CHANNEL_NAME"); return 0; } + if(!(target_channel = GetChannel(argv[2]))) + { + reply("MSG_INVALID_CHANNEL"); + return 0; + } target = GetUserH(argv[1]); if (!target) { reply("MSG_NICK_UNKNOWN", argv[1]); return 0; } - if (!(channel = GetChannel(argv[2]))) { - reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name); - return 0; - } - - if (!GetUserMode(channel, target)) { - reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name); + if (!GetUserMode(target_channel, target)) { + reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, target_channel->name); return 0; } - irc_svspart(opserv, target, channel); + irc_svspart(opserv, target, target_channel); reply("OSMSG_SVSPART_SENT"); return 1; } @@ -2130,13 +2080,19 @@ static MODCMD_FUNC(cmd_whois) if (IsOper(target)) buffer[bpos++] = 'o'; if (IsGlobal(target)) buffer[bpos++] = 'g'; if (IsServNotice(target)) buffer[bpos++] = 's'; + + // 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 (IsNoChan(target)) buffer[bpos++] = 'n'; - if (IsNoIdle(target)) buffer[bpos++] = 'I'; + 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; @@ -2256,6 +2212,16 @@ static MODCMD_FUNC(cmd_stats_bad) { return 1; } +static MODCMD_FUNC(cmd_stats_glines) { + reply("OSMSG_GLINE_COUNT", gline_count()); + return 1; +} + +static MODCMD_FUNC(cmd_stats_shuns) { + reply("OSMSG_SHUN_COUNT", shun_count()); + return 1; +} + static void trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) { unsigned int nn, pos; @@ -2436,15 +2402,11 @@ 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) @@ -2456,27 +2418,12 @@ static MODCMD_FUNC(cmd_stats_uptime) { clocks_per_sec = CLOCKS_PER_SEC; } } - times(&buf); - 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(), ×[0], ×[1], ×[2], ×[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); + times(&buf); + reply("OSMSG_UPTIME_STATS", + uptime, lines_processed, + buf.tms_utime/(double)clocks_per_sec, + buf.tms_stime/(double)clocks_per_sec); return 1; } @@ -2484,6 +2431,8 @@ static MODCMD_FUNC(cmd_stats_alerts) { dict_iterator_t it; struct opserv_user_alert *alert; const char *reaction; + char t_buffer[INTERVALLEN]; + char expire_buffer[30]; char *m = NULL; if(argc > 1) @@ -2510,7 +2459,15 @@ static MODCMD_FUNC(cmd_stats_alerts) { default: reaction = ""; break; } reply("OSMSG_ALERT_IS", iter_key(it), reaction, alert->owner); + if (alert->expire) { + strftime(expire_buffer, sizeof(expire_buffer), "%Y-%m-%d %H:%M:%S %z", localtime(&alert->expire)); + reply("OSMSG_ALERT_EXPIRE", expire_buffer); + } reply("OSMSG_ALERTS_DESC", alert->text_discrim); + if (alert->last > 0) + reply("OSMSG_ALERTS_LAST", intervalString(t_buffer, now - alert->last, user->handle_info)); + else + reply("OSMSG_ALERTS_LAST", "Never"); } reply("OSMSG_ALERT_END"); return 1; @@ -2640,7 +2597,7 @@ opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick, return NULL; } } - if ((resv = AddLocalUser(nick, ident, host, desc, "+i"))) { + if ((resv = AddClone(nick, ident, host, desc))) { dict_insert(opserv_reserved_nick_dict, resv->nick, resv); } return resv; @@ -2756,7 +2713,7 @@ opserv_new_user_check(struct userNode *user) } if (checkDefCon(DEFCON_NO_NEW_CLIENTS)) { - irc_kill(opserv, user, DefConGlineReason); + DelUser(user, opserv, 1, DefConGlineReason); return 0; } @@ -2765,9 +2722,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; } @@ -2791,7 +2748,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); } } @@ -2888,20 +2845,16 @@ opserv_channel_delete(struct chanNode *chan) } static void -opserv_notice_handler(struct userNode *user, struct userNode *bot, const char *text, UNUSED_ARG(int server_qualified)) +opserv_notice_handler(struct userNode *user, struct userNode *bot, char *text, UNUSED_ARG(int server_qualified)) { char *cmd; - char *tmpline; - - tmpline = strdup(text); - /* if its a version reply, do an alert check (only alerts with version=something) */ if(bot == opserv) { if(text[0] == '\001') { text++; - cmd = mysep(&tmpline, " "); + cmd = mysep(&text, " "); if(cmd && !irccasecmp(cmd, "VERSION")) { - const char *version = mysep(&tmpline, "\n"); + char *version = mysep(&text, "\n"); if(!version) version = ""; /* opserv_debug("Opserv got CTCP VERSION Notice from %s: %s", user->nick, version); */ @@ -2950,7 +2903,7 @@ opserv_join_check(struct modeNode *mNode) struct mod_chanmode change; mod_chanmode_init(&change); channel->join_flooded = 1; - if (opserv && opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) { + if (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; @@ -3128,7 +3081,7 @@ rank_outside_rec(struct route *route, char *server, int count) return(max + 1); } else { - log_module(MAIN_LOG, LOG_ERROR, "routing struct rank_outsideness() couldnt find %s", server); + log_module(MAIN_LOG, LOG_WARNING, "routing struct rank_outsideness() couldnt find %s", server); return 0; } } @@ -3201,7 +3154,7 @@ change_route_uplinks(struct route *route) strcpy(lastserver, servicename); rptr = find_routeList_server(route, self->uplink->name); if(!rptr) { - log_module(MAIN_LOG, LOG_ERROR, "Cannot convert routing map to center: My uplink is not on the map! Marking map as uncentered."); + log_module(MAIN_LOG, LOG_WARNING, "Cannot convert routing map to center: My uplink is not on the map! Marking map as uncentered."); route->centered = false; return false; } @@ -3248,7 +3201,7 @@ activate_routing(struct svccmd *cmd, struct userNode *user, char *plan_name) /* since it doesnt exist, remove the active setting */ dict_remove(opserv_routing_plan_options, plan_name); } - log_module(MAIN_LOG, LOG_ERROR, "activate_routing() couldnt find active routing plan!"); + log_module(MAIN_LOG, LOG_WARNING, "activate_routing() couldnt find active routing plan!"); return 0; } } @@ -3747,7 +3700,7 @@ routing_handle_connect_failure(struct server *source, char *server, char *messag char *active; struct routingPlan *rp; struct routingPlanServer *rps; - log_module(MAIN_LOG, LOG_ERROR, "Failed to connect %s to %s: %s", server, source->name, message); + log_module(MAIN_LOG, LOG_WARNING, "Failed to connect %s to %s: %s", server, source->name, message); /* remove the waiting connection n timeq */ routing_delete_connect_timer(server); /* check if routing is active.. */ @@ -4402,7 +4355,7 @@ static MODCMD_FUNC(cmd_clone) reply("OSMSG_NOT_A_HOSTMASK"); return 0; } - if (!(clone = AddLocalUser(argv[2], ident, argv[3]+i, userinfo, "+i"))) { + if (!(clone = AddClone(argv[2], ident, argv[3]+i, userinfo))) { reply("OSMSG_CLONE_FAILED", argv[2]); return 0; } @@ -4603,7 +4556,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 = AddLocalUser(key, ident, hostname, desc, "+i"))) { + if ((reserve = AddClone(key, ident, hostname, desc))) { reserve->modes |= FLAGS_PERSISTENT; dict_insert(extra, reserve->nick, reserve); } @@ -4719,7 +4672,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) +opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_reaction reaction, const char *text_discrim, int last, int expire) { unsigned int wordc; char *wordv[MAXNUMPARAMS], *discrim_copy; @@ -4733,9 +4686,11 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react alert = malloc(sizeof(*alert)); alert->owner = strdup(req->handle_info ? req->handle_info->handle : req->nick); alert->text_discrim = strdup(text_discrim); + 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->expire = expire; /* Check for missing required criteria or broken records */ if (!alert->discrim || (reaction==REACT_SVSJOIN && !alert->discrim->chantarget) || (reaction==REACT_SVSPART && !alert->discrim->chantarget) || @@ -4756,12 +4711,15 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react * max_channels would have to be checked on /part, which we do not * yet do, and which seems of questionable value. */ - if (alert->discrim->channel_count || alert->discrim->min_channels) + if (alert->discrim->channel || alert->discrim->min_channels) 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); + if (alert->discrim->accountmask) + dict_insert(opserv_account_based_alerts, name_dup, alert); + + if (alert->expire) + timeq_add(alert->expire, alert_expire, (void*)name_dup); return alert; } @@ -4786,6 +4744,8 @@ static int add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra)) { dict_t alert_dict; + char *str; + int last = 0, expire = 0; const char *discrim, *react, *owner; opserv_alert_reaction reaction; struct opserv_user_alert *alert; @@ -4796,6 +4756,13 @@ add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra)) } discrim = database_get_data(alert_dict, KEY_DISCRIM, RECDB_QSTRING); react = database_get_data(alert_dict, KEY_REACTION, RECDB_QSTRING); + str = database_get_data(alert_dict, KEY_LAST, RECDB_QSTRING); + if (str) + last = atoi(str); + str = database_get_data(alert_dict, KEY_EXPIRE, RECDB_QSTRING); + if (str) + expire = atoi(str); + if (!react || !irccasecmp(react, "notice")) reaction = REACT_NOTICE; else if (!irccasecmp(react, "kill")) @@ -4822,7 +4789,7 @@ add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra)) 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); + alert = opserv_add_user_alert(opserv, key, reaction, discrim, last, expire); if (!alert) { log_module(OS_LOG, LOG_ERROR, "Unable to create alert %s from database.", key); return 0; @@ -5092,6 +5059,8 @@ opserv_saxdb_write(struct saxdb_context *ctx) saxdb_start_record(ctx, iter_key(it), 0); saxdb_write_string(ctx, KEY_DISCRIM, alert->text_discrim); saxdb_write_string(ctx, KEY_OWNER, alert->owner); + saxdb_write_int(ctx, KEY_LAST, alert->last); + saxdb_write_int(ctx, KEY_EXPIRE, alert->expire); switch (alert->reaction) { case REACT_NOTICE: reaction = "notice"; break; case REACT_KILL: reaction = "kill"; break; @@ -5368,31 +5337,25 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int } else if (irccasecmp(argv[i], "duration") == 0) { discrim->duration = ParseInterval(argv[++i]); } else if (irccasecmp(argv[i], "channel") == 0) { - if(discrim->channel_count == DISCRIM_MAX_CHANS) - { - send_message(user, opserv, "OSMSG_TRACE_MAX_CHANNELS", DISCRIM_MAX_CHANS); - goto fail; - } - for (j=0, i++; ; j++) { switch (argv[i][j]) { case '#': goto find_channel; case '-': - discrim->chan_no_modes[discrim->channel_count] |= MODE_CHANOP | MODE_VOICE; + discrim->chan_no_modes |= MODE_CHANOP | MODE_HALFOP | MODE_VOICE; break; case '+': - discrim->chan_req_modes[discrim->channel_count] |= MODE_VOICE; - discrim->chan_no_modes[discrim->channel_count] |= MODE_CHANOP; - discrim->chan_no_modes[discrim->channel_count] |= MODE_HALFOP; + discrim->chan_req_modes |= MODE_VOICE; + discrim->chan_no_modes |= MODE_CHANOP; + discrim->chan_no_modes |= MODE_HALFOP; break; case '%': - discrim->chan_req_modes[discrim->channel_count] |= MODE_HALFOP; - discrim->chan_no_modes[discrim->channel_count] |= MODE_CHANOP; - discrim->chan_no_modes[discrim->channel_count] |= MODE_VOICE; + discrim->chan_req_modes |= MODE_HALFOP; + discrim->chan_no_modes |= MODE_CHANOP; + discrim->chan_no_modes |= MODE_VOICE; break; case '@': - discrim->chan_req_modes[discrim->channel_count] |= MODE_CHANOP; + discrim->chan_req_modes |= MODE_CHANOP; break; case '\0': send_message(user, bot, "MSG_NOT_CHANNEL_NAME"); @@ -5400,19 +5363,18 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int } } find_channel: - discrim->chan_no_modes[discrim->channel_count] &= ~discrim->chan_req_modes[discrim->channel_count]; - if (!(discrim->channels[discrim->channel_count] = GetChannel(argv[i]+j))) { + discrim->chan_no_modes &= ~discrim->chan_req_modes; + if (!(discrim->channel = GetChannel(argv[i]+j))) { /* secretly "allow_channel" now means "if a channel name is * specified, require that it currently exist" */ if (allow_channel) { send_message(user, bot, "MSG_CHANNEL_UNKNOWN", argv[i]); goto fail; } else { - discrim->channels[discrim->channel_count] = AddChannel(argv[i]+j, now, NULL, NULL, NULL); + discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL, NULL); } } - LockChannel(discrim->channels[discrim->channel_count]); - discrim->channel_count++; + LockChannel(discrim->channel); } else if (irccasecmp(argv[i], "numchannels") == 0) { discrim->min_channels = discrim->max_channels = strtoul(argv[++i], NULL, 10); } else if (irccasecmp(argv[i], "limit") == 0) { @@ -5420,12 +5382,6 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int } else if (irccasecmp(argv[i], "reason") == 0) { discrim->reason = strdup(unsplit_string(argv+i+1, argc-i-1, NULL)); i = argc; - } else if (irccasecmp(argv[i], "notice_target") == 0 || irccasecmp(argv[i], "target") == 0) { - if (!IsChannelName(argv[i + 1])) { - send_message(user, opserv, "MSG_NOT_CHANNEL_NAME"); - goto fail; - } - discrim->notice_target = argv[++i]; } else if (irccasecmp(argv[i], "last") == 0) { discrim->min_ts = now - ParseInterval(argv[++i]); } else if ((irccasecmp(argv[i], "linked") == 0) @@ -5444,7 +5400,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int discrim->max_ts = now - (ParseInterval(cmp+1) - 1); } } else { - discrim->min_ts = now - ParseInterval(cmp); + discrim->min_ts = now - ParseInterval(cmp+2); } } else if (irccasecmp(argv[i], "access") == 0) { const char *cmp = argv[++i]; @@ -5464,19 +5420,17 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int discrim->min_level = strtoul(cmp+1, NULL, 0) + 1; } } else { - discrim->min_level = strtoul(cmp, NULL, 0); - } - } else if (irccasecmp(argv[i], "abuse") == 0) { - const char *abuse_what = argv[++i]; - if (irccasecmp(abuse_what, "opers") == 0) { - discrim->match_opers = 1; - } else if (irccasecmp(abuse_what, "trusted") == 0) { - discrim->match_trusted = 1; + discrim->min_level = strtoul(cmp+2, NULL, 0); } + } else if ((irccasecmp(argv[i], "abuse") == 0) + && (irccasecmp(argv[++i], "opers") == 0)) { + discrim->match_opers = 1; } else if (irccasecmp(argv[i], "depth") == 0) { discrim->domain_depth = strtoul(argv[++i], NULL, 0); } else if (irccasecmp(argv[i], "clones") == 0) { discrim->min_clones = strtoul(argv[++i], NULL, 0); + } else if (irccasecmp(argv[i], "modes") == 0) { + discrim->modes = argv[++i]; } else { send_message(user, bot, "MSG_INVALID_CRITERIA", argv[i]); goto fail; @@ -5503,7 +5457,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int { if(discrim->mask_nick) { - int err = regcomp(&discrim->regex_nick, discrim->mask_nick, REG_EXTENDED|REG_ICASE|REG_NOSUB); + int err = regcomp(&discrim->regex_nick, discrim->mask_nick, REG_EXTENDED|REG_NOSUB); discrim->has_regex_nick = !err; if(err) { @@ -5517,7 +5471,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int if(discrim->mask_ident) { - int err = regcomp(&discrim->regex_ident, discrim->mask_ident, REG_EXTENDED|REG_ICASE|REG_NOSUB); + int err = regcomp(&discrim->regex_ident, discrim->mask_ident, REG_EXTENDED|REG_NOSUB); discrim->has_regex_ident = !err; if(err) { @@ -5531,7 +5485,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int if(discrim->mask_host) { - int err = regcomp(&discrim->regex_host, discrim->mask_host, REG_EXTENDED|REG_ICASE|REG_NOSUB); + int err = regcomp(&discrim->regex_host, discrim->mask_host, REG_EXTENDED|REG_NOSUB); discrim->has_regex_host = !err; if(err) { @@ -5545,7 +5499,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int if(discrim->mask_info) { - int err = regcomp(&discrim->regex_info, discrim->mask_info, REG_EXTENDED|REG_ICASE|REG_NOSUB); + int err = regcomp(&discrim->regex_info, discrim->mask_info, REG_EXTENDED|REG_NOSUB); discrim->has_regex_info = !err; if(err) { @@ -5559,7 +5513,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int if(discrim->mask_version) { - int err = regcomp(&discrim->regex_version, discrim->mask_version, REG_EXTENDED|REG_ICASE|REG_NOSUB); + int err = regcomp(&discrim->regex_version, discrim->mask_version, REG_EXTENDED|REG_NOSUB); discrim->has_regex_version = !err; if(err) { @@ -5595,7 +5549,7 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int static int discrim_match(discrim_t discrim, struct userNode *user) { - unsigned int access, i; + unsigned int access; char *scmp=NULL, *dcmp=NULL; if ((user->timestamp < discrim->min_ts) @@ -5613,9 +5567,8 @@ discrim_match(discrim_t discrim, struct userNode *user) ) return 0; - for(i = 0; i < discrim->channel_count; i++) - if (!GetUserMode(discrim->channels[i], user)) - return 0; + if (discrim->channel && !GetUserMode(discrim->channel, user)) + return 0; if(discrim->use_regex) { @@ -5663,6 +5616,57 @@ discrim_match(discrim_t discrim, struct userNode *user) return 0; } + if (discrim->modes) { + unsigned int ii, matches = 0; + for (ii = 0; ii < strlen(discrim->modes); ii++) { + switch(discrim->modes[ii]) { + case 'O': + if(IsOper(user)) matches++; + break; + case 'o': + if(IsOper(user)) matches++; + break; + case 'i': + if(IsInvisible(user)) matches++; + break; + case 'w': + if(IsWallOp(user)) matches++; + break; + case 's': + if(IsServNotice(user)) matches++; + break; + case 'd': + if(IsDeaf(user)) matches++; + break; + case 'k': + if(IsService(user)) matches++; + break; + case 'g': + if(IsGlobal(user)) matches++; + break; + case 'h': + if(IsSetHost(user)) matches++; + break; + case 'B': + if(IsBotM(user)) matches++; + break; + case 'n': + if(IsHideChans(user)) matches++; + break; + case 'I': + if(IsHideIdle(user)) matches++; + break; + case 'X': + if(IsXtraOp(user)) matches++; + break; + case 'x': + if(IsHiddenHost(user)) matches++; + break; + } + } + if (matches != strlen(discrim->modes)) return 0; + } + access = user->handle_info ? user->handle_info->opserv_level : 0; if ((access < discrim->min_level) || (access > discrim->max_level)) { @@ -5679,38 +5683,22 @@ discrim_match(discrim_t discrim, struct userNode *user) static unsigned int opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) { - unsigned int nn, count, match; + unsigned int nn, count; struct userList matched; userList_init(&matched); /* Try most optimized search methods first */ - if (discrim->channel_count) - { - for (nn=0; (nn < discrim->channels[0]->members.used) + if (discrim->channel) { + for (nn=0; + (nn < discrim->channel->members.used) && (matched.used < discrim->limit); nn++) { - struct modeNode *mn = discrim->channels[0]->members.list[nn]; - - if (((mn->modes & discrim->chan_req_modes[0]) != discrim->chan_req_modes[0]) - || ((mn->modes & discrim->chan_no_modes[0]) != 0)) { + struct modeNode *mn = discrim->channel->members.list[nn]; + if (((mn->modes & discrim->chan_req_modes) != discrim->chan_req_modes) + || ((mn->modes & discrim->chan_no_modes) != 0)) { continue; } - - if ((match = discrim_match(discrim, mn->user))) - { - unsigned int i; - - for (i = 1; i < discrim->channel_count; i++) { - struct modeNode *mn2 = GetUserMode(discrim->channels[i], mn->user); - - if (((mn2->modes & discrim->chan_req_modes[i]) != discrim->chan_req_modes[i]) - || ((mn2->modes & discrim->chan_no_modes[i]) != 0)) { - match = 0; - break; - } - } - - if (match) + if (discrim_match(discrim, mn->user)) { userList_append(&matched, mn->user); } } @@ -5780,32 +5768,12 @@ trace_count_func(UNUSED_ARG(struct userNode *match), UNUSED_ARG(void *extra)) } static int -is_oper_victim(struct userNode *user, struct userNode *target, int match_opers, int check_ip) +is_oper_victim(struct userNode *user, struct userNode *target, int match_opers) { - unsigned char is_victim; - unsigned int nn; - - is_victim = !(IsService(target) + return !(IsService(target) || (!match_opers && IsOper(target)) || (target->handle_info && target->handle_info->opserv_level > user->handle_info->opserv_level)); - - /* If we don't need an ip check or want to hit opers or the the "cheap" check already disqualified the target, we are done. */ - if (!check_ip || match_opers || !is_victim) - return is_victim; - - for(nn = 0; nn < curr_opers.used; nn++) { - if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0) - return 0; - } - - return 1; -} - -static int -is_trust_victim(struct userNode *target, int match_trusted) -{ - return (match_trusted || !dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)); } static int @@ -5813,7 +5781,7 @@ trace_gline_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)) { + if (is_oper_victim(das->source, match, das->discrim->match_opers)) { opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration, das->discrim->silent); } @@ -5825,7 +5793,7 @@ trace_shun_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)) { + if (is_oper_victim(das->source, match, das->discrim->match_opers)) { opserv_shun(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration); } @@ -5837,7 +5805,7 @@ trace_kill_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)) { + if (is_oper_victim(das->source, match, das->discrim->match_opers)) { char *reason; if (das->discrim->reason) { reason = das->discrim->reason; @@ -5953,7 +5921,7 @@ trace_gag_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)) { + if (is_oper_victim(das->source, match, das->discrim->match_opers)) { char *reason, *mask; int masksize; if (das->discrim->reason) { @@ -6043,7 +6011,7 @@ static MODCMD_FUNC(cmd_trace) { struct discrim_and_source das; discrim_search_func action; - unsigned int matches, i; + unsigned int matches; struct svccmd *subcmd; char buf[MAXLEN]; int ret = 1; @@ -6144,8 +6112,8 @@ static MODCMD_FUNC(cmd_trace) reply("MSG_NO_MATCHES"); } - for (i = 0; i < das.discrim->channel_count; i++) - UnlockChannel(das.discrim->channels[i]); + if (das.discrim->channel) + UnlockChannel(das.discrim->channel); free(das.discrim->reason); if(das.discrim->has_regex_nick) @@ -6192,9 +6160,6 @@ opserv_cdiscrim_create(struct userNode *user, struct userNode *bot, unsigned int discrim = calloc(1, sizeof(*discrim)); discrim->limit = 25; - discrim->max_users = ~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. */ @@ -6222,7 +6187,7 @@ opserv_cdiscrim_create(struct userNode *user, struct userNode *bot, unsigned int else discrim->min_users = strtoul(cmp+1, NULL, 0) + 1; } else { - discrim->min_users = strtoul(cmp, NULL, 0); + discrim->min_users = strtoul(cmp+2, NULL, 0); } } else if (!irccasecmp(argv[i], "timestamp")) { const char *cmp = argv[++i]; @@ -6265,10 +6230,10 @@ cdiscrim_match(cdiscrim_t discrim, struct chanNode *chan) { if ((discrim->name && !match_ircglob(chan->name, discrim->name)) || (discrim->topic && !match_ircglob(chan->topic, discrim->topic)) || - (chan->members.used < discrim->min_users) || - (chan->members.used > discrim->max_users) || - (chan->timestamp < discrim->min_ts) || - (chan->timestamp > discrim->max_ts)) { + (discrim->min_users && chan->members.used < discrim->min_users) || + (discrim->max_users && chan->members.used > discrim->max_users) || + (discrim->min_ts && chan->timestamp < discrim->min_ts) || + (discrim->max_ts && chan->timestamp > discrim->max_ts)) { return 0; } return 1; @@ -6392,38 +6357,11 @@ static void 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, ""); - 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; + 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, xtra->bot, "OSMSG_GTRACE_FORMAT", gline->target, set_text, gline->issuer, when_text, gline->reason); } static void @@ -6501,38 +6439,11 @@ static void 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, ""); - 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); -} - -static MODCMD_FUNC(cmd_stats_shuns) { - if (argc < 2) { - reply("OSMSG_SHUN_COUNT", shun_count()); - return 1; - } else if (argc < 3) { - struct shun_extra extra; - struct shun *sh; - - extra.user = user; - sh = shun_find(argv[1]); - if (!sh) - reply("OSMSG_NO_SHUN", argv[1]); - else - strace_print_func(sh, &extra); - return 1; - } else return 0; + char *when_text, set_text[20]; + strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&shun->issued)); + when_text = asctime(localtime(&shun->expires)); + when_text[strlen(when_text)-1] = 0; /* strip lame \n */ + send_message(xtra->user, xtra->bot, "OSMSG_STRACE_FORMAT", shun->target, set_text, shun->issuer, when_text, shun->reason); } static void @@ -6615,15 +6526,12 @@ alert_check_user(const char *key, void *data, void *extra) return 0; } - if ((alert->reaction != REACT_NOTICE) - && !is_trust_victim(user, alert->discrim->match_trusted)) { - return 0; - } - /* The user matches the alert criteria, so trigger the reaction. */ if (alert->discrim->option_log) log_module(OS_LOG, LOG_INFO, "Alert %s triggered by user %s!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); + alert->last = now; + /* Return 1 to halt alert matching, such as when killing the user that triggered the alert. */ switch (alert->reaction) { @@ -6658,11 +6566,11 @@ alert_check_user(const char *key, void *data, void *extra) log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key); /* fall through to REACT_NOTICE case */ case REACT_NOTICE: - opserv_custom_alert(alert->discrim->notice_target, "Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); + opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); break; case REACT_TRACK: #ifdef HAVE_TRACK - opserv_custom_alert(alert->discrim->notice_target, "Alert $b%s$b triggered by user $b%s$b!%s@%s (%s). (Tracking)", key, user->nick, user->ident, user->hostname, alert->discrim->reason); + opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s) (Tracking).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); add_track_user(user); #endif break; @@ -6670,11 +6578,19 @@ alert_check_user(const char *key, void *data, void *extra) return 0; } +static void +opserv_alert_check_account(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) +{ + 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)) { struct gag_entry *gag; - dict_foreach(opserv_nick_based_alerts, alert_check_user, user); + + dict_foreach(opserv_nick_based_alerts, alert_check_user, user); + /* Gag them if appropriate (and only if). */ user->modes &= ~FLAGS_GAGGED; for (gag = gagList; gag; gag = gag->next) { @@ -6707,8 +6623,6 @@ 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) @@ -6814,6 +6728,7 @@ static MODCMD_FUNC(cmd_addalert) struct svccmd *subcmd; const char *name; char buf[MAXLEN]; + int expire = 0; name = argv[1]; sprintf(buf, "addalert %s", argv[2]); @@ -6848,8 +6763,12 @@ static MODCMD_FUNC(cmd_addalert) reply("OSMSG_UNKNOWN_REACTION", argv[2]); return 0; } + + if (argc >= 4 && !irccasecmp(argv[3], "expire")) + 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 + 3, argc - 3, NULL))) { + || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + (expire ? 5 : 3), argc - (expire ? 5 : 3), NULL), 0, expire)) { reply("OSMSG_ALERT_ADD_FAILED"); return 0; } @@ -6857,14 +6776,30 @@ static MODCMD_FUNC(cmd_addalert) return 1; } +static int delete_alert(char const* name) +{ + dict_remove(opserv_nick_based_alerts, (char const*)name); + dict_remove(opserv_channel_alerts, (char const*)name); + dict_remove(opserv_account_based_alerts, (char const*)name); + return dict_remove(opserv_user_alerts, (char const*)name); +} + +static void alert_expire(void* name) +{ + int present = 0; + struct opserv_user_alert* alert = NULL; + + alert = dict_find(opserv_user_alerts, (char const*)name, &present); + + if (present && alert && alert->expire > 0 && alert->expire <= now) + delete_alert(name); +} + static MODCMD_FUNC(cmd_delalert) { unsigned int i; for (i=1; i