X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/ff3b058ac51e9caf5cf1fd310b8a401a97a85582..82089e3fd2b8c69de9286ff4d6cc9d845670ab7e:/src/opserv.c diff --git a/src/opserv.c b/src/opserv.c index b3f3b97..fad20c3 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" @@ -144,17 +146,16 @@ static const struct message_entry msgtab[] = { { "OSMSG_NEED_CHANNEL", "You must specify a channel for $b%s$b." }, { "OSMSG_INVALID_IRCMASK", "$b%s$b is an invalid IRC hostmask." }, { "OSMSG_ADDED_BAN", "I have banned $b%s$b from $b%s$b." }, + { "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_SBLOCK_OPER" , "Shuning $b%s$b (*@%s) would also hit the IRC operator $b%s$b." }, + { "OSMSG_NO_GLINE_CMD", "The GLINE command is not bound so you can only block with the default duration." }, + { "OSMSG_NO_SHUN_CMD", "The SHUN command is not bound so you can only block with the default duration." }, { "OSMSG_SHUN_ISSUED", "Shun issued for $b%s$b." }, { "OSMSG_SHUN_REMOVED", "Shun removed for $b%s$b." }, { "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 +171,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 +187,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,9 +244,9 @@ 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_NO_GLINE", "$b%s$b is not a known G-line." }, + { "OSMSG_NO_SHUN", "$b%s$b is not a known Shun" }, { "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)" }, @@ -301,8 +305,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 +324,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" }, @@ -408,10 +414,10 @@ static const struct message_entry msgtab[] = { { "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" }, { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" }, { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" }, + { "OSMSG_TRACE_MAX_CHANNELS", "You may not use the 'channel' criterion more than %d times." }, { "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" }, @@ -467,8 +473,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; @@ -539,7 +545,7 @@ opserv_free_waiting_connection(void *data) 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; + char *mask_nick, *mask_ident, *mask_host, *mask_info, *mask_version, *server, *reason, *accountmask, *chantarget, *mark, *mask_mark, *modes, *notice_target; irc_in_addr_t ip_mask; unsigned long limit; time_t min_ts, max_ts; @@ -568,6 +574,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 +593,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 */ @@ -614,11 +623,15 @@ opserv_free_user_alert(void *data) free(alert); } -#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) - - +#if defined(GCC_VARMACROS) +# define opserv_debug(ARGS...) do { if (opserv_conf.debug_channel) send_channel_notice(opserv_conf.debug_channel, opserv, ARGS); } while (0) +# define opserv_alert(ARGS...) do { if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel, opserv, ARGS); } while (0) +# define opserv_custom_alert(CHAN, ARGS...) do { if (CHAN) send_target_message(4, (CHAN), opserv, ARGS); else if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel, opserv, ARGS); } while (0) +#elif defined(C99_VARMACROS) +# define opserv_debug(...) do { if (opserv_conf.debug_channel) send_channel_notice(opserv_conf.debug_channel, opserv, __VA_ARGS__); } while (0) +# define opserv_alert(...) do { if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel, opserv, __VA_ARGS__); } while (0) +# define opserv_custom_alert(chan, ...) do { if (chan) send_target_message(4, chan, opserv, __VA_ARGS__); else if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel, opserv, __VA_ARGS__); } while (0) +#endif char *defconReverseModes(const char *modes) { @@ -1291,7 +1304,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) @@ -1317,14 +1330,12 @@ static MODCMD_FUNC(cmd_block) 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; } @@ -1365,7 +1376,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 +1506,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) @@ -1503,6 +1514,10 @@ static MODCMD_FUNC(cmd_sblock) struct userNode *target; struct shun *shun; char *reason; + unsigned long duration = 0; + unsigned int offset = 2; + unsigned int nn; + struct svccmd *shun_cmd; target = GetUserH(argv[1]); if (!target) { @@ -1513,8 +1528,32 @@ static MODCMD_FUNC(cmd_sblock) 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_SBLOCK_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_shun_duration) { + // We require more access when the duration is not the default block duration. + shun_cmd = dict_find(cmd->parent->commands, "shun", NULL); + if(!shun_cmd) + { + reply("OSMSG_NO_SHUN_CMD"); + return 0; + } + if(!svccmd_can_invoke(user, cmd->parent->bot, shun_cmd, channel, SVCCMD_NOISY)) + return 0; + } reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL; - shun = opserv_shun(target, user->handle_info->handle, reason, 0); + shun = opserv_shun(target, user->handle_info->handle, reason, duration); reply("OSMSG_SHUN_ISSUED", shun->target); return 1; } @@ -1539,7 +1578,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 +1708,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,6 +1716,23 @@ 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; @@ -1684,8 +1740,8 @@ static MODCMD_FUNC(cmd_join) if (!channel) { if((argc < 2) || !IsChannelName(argv[1])) { - reply("MSG_NOT_CHANNEL_NAME"); - return 0; + reply("MSG_NOT_CHANNEL_NAME"); + return 0; } channel = AddChannel(argv[1], now, NULL, NULL, NULL); @@ -1701,13 +1757,12 @@ 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) +static MODCMD_FUNC(cmd_kick) { struct userNode *target; char *reason; @@ -1727,15 +1782,11 @@ static MODCMD_FUNC(cmd_forcekick) 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); + KickChannelUser(target, channel, cmd->parent->bot, reason); return 1; } -static MODCMD_FUNC(cmd_kick) +static MODCMD_FUNC(cmd_forcekick) { struct userNode *target; char *reason; @@ -1755,7 +1806,11 @@ static MODCMD_FUNC(cmd_kick) reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); return 0; } - KickChannelUser(target, channel, cmd->parent->bot, reason); + if (IsLocal(target)) { + reply("OSMSG_FORCEKICK_LOCAL", target->nick); + return 0; + } + irc_kick(cmd->parent->bot, target, channel, reason); return 1; } @@ -1878,28 +1933,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; } @@ -1908,19 +1964,13 @@ static MODCMD_FUNC(cmd_part) { char *reason; - if (!IsChannelName(argv[1])) { - reply("MSG_NOT_CHANNEL_NAME"); + if (!GetUserMode(channel, cmd->parent->bot)) { + reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name); return 0; } - if ((channel = GetChannel(argv[1]))) { - if (!GetUserMode(channel, cmd->parent->bot)) { - reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name); - return 0; - } - reason = (argc < 3) ? "Leaving." : unsplit_string(argv+2, argc-2, NULL); - reply("OSMSG_LEAVING", channel->name); - DelChannelUser(cmd->parent->bot, channel, reason, 0); - } + reason = (argc < 2) ? "Leaving." : unsplit_string(argv+1, argc-1, NULL); + reply("OSMSG_LEAVING", channel->name); + DelChannelUser(cmd->parent->bot, channel, reason, 0); return 1; } @@ -2123,20 +2173,8 @@ static MODCMD_FUNC(cmd_whois) 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'; - if (IsServNotice(target)) buffer[bpos++] = 's'; - 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 (IsGagged(target)) buffer_cat(" (gagged)"); if (IsRegistering(target)) buffer_cat(" (registered account)"); buffer[bpos] = 0; @@ -2351,7 +2389,7 @@ static MODCMD_FUNC(cmd_stats_network2) { #endif tbl.contents[nn][1] = buffer; ofs = strlen(buffer) + 1; - intervalString(buffer + ofs, now - server->link, user->handle_info); + intervalString(buffer + ofs, now - server->link_time, user->handle_info); if (server->self_burst) strcat(buffer + ofs, " Bursting"); tbl.contents[nn][2] = buffer + ofs; @@ -2436,15 +2474,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 +2490,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 +2503,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 +2531,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; @@ -2724,11 +2753,11 @@ opserv_new_user_check(struct userNode *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; + return 0; /* 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; } @@ -2756,7 +2785,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 +2794,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 +2820,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); } } @@ -2891,17 +2920,17 @@ static void opserv_notice_handler(struct userNode *user, struct userNode *bot, const char *text, UNUSED_ARG(int server_qualified)) { char *cmd; - char *tmpline; + char *textb; - tmpline = strdup(text); + textb = 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, " "); + textb++; + cmd = mysep(&textb, " "); if(cmd && !irccasecmp(cmd, "VERSION")) { - const char *version = mysep(&tmpline, "\n"); + char *version = mysep(&textb, "\n"); if(!version) version = ""; /* opserv_debug("Opserv got CTCP VERSION Notice from %s: %s", user->nick, version); */ @@ -2924,7 +2953,7 @@ opserv_join_check(struct modeNode *mNode) dict_foreach(opserv_channel_alerts, alert_check_user, user); - if (channel->bad_channel) { + 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); if (channel->name[0] != '#') DelUser(user, opserv, 1, "OSMSG_ILLEGAL_KILL_REASON"); @@ -2994,7 +3023,6 @@ opserv_add_bad_word(struct svccmd *cmd, struct userNode *user, const char *new_b reply("OSMSG_BAD_NUKING", orig_bad); string_list_delete(opserv_bad_words, bad_idx); bad_idx--; - free(orig_bad); } return 1; } @@ -3128,7 +3156,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 +3229,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 +3276,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; } } @@ -3625,7 +3653,7 @@ void routing_karma_timer(void *data) { routing_change_karma(rps, iter_key(it), KARMA_ENTROPE); /* give an additonal +KARMA_RELIABLE to servers that * have been linked at least KARMA_TIMER seconds. */ - if(server && (server->link < (now - KARMA_TIMER) ) ) { + if(server && (server->link_time < (now - KARMA_TIMER) ) ) { routing_change_karma(rps, iter_key(it), KARMA_RELIABLE); } } @@ -3747,7 +3775,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.. */ @@ -4152,17 +4180,17 @@ static MODCMD_FUNC(cmd_addbad) /* Scan for existing channels that match the new bad word. */ if (!bad_found) { for (it = dict_first(channels); it; it = iter_next(it)) { - struct chanNode *channel = iter_data(it); + struct chanNode *chan = iter_data(it); - if (!opserv_bad_channel(channel->name)) + if (!opserv_bad_channel(chan->name)) continue; - channel->bad_channel = 1; - if (channel->name[0] == '#') - opserv_shutdown_channel(channel, "OSMSG_ILLEGAL_REASON"); + chan->bad_channel = 1; + if (chan->name[0] == '#') + opserv_shutdown_channel(chan, "OSMSG_ILLEGAL_REASON"); else { unsigned int nn; - for (nn=0; nnmembers.used; nn++) { - struct userNode *user = channel->members.list[nn]->user; + for (nn=0; nnmembers.used; nn++) { + struct userNode *user = chan->members.list[nn]->user; DelUser(user, cmd->parent->bot, 1, "OSMSG_ILLEGAL_KILL_REASON"); } } @@ -4323,7 +4351,7 @@ static MODCMD_FUNC(cmd_edittrust) return 0; } count = strtoul(argv[2], &tmp, 10); - if (!count || *tmp) { + if (*tmp != '\0') { reply("OSMSG_BAD_NUMBER", argv[2]); return 0; } @@ -4402,7 +4430,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 = AddLocalUser(argv[2], ident, argv[3]+i, userinfo, "+i"))) { reply("OSMSG_CLONE_FAILED", argv[2]); return 0; } @@ -4719,7 +4747,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 +4761,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) || @@ -4760,8 +4790,11 @@ 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); + 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 +4819,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 +4831,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 +4864,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 +5134,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; @@ -5367,56 +5411,56 @@ 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; - } + } 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; - 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; - 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; - break; - case '@': - discrim->chan_req_modes[discrim->channel_count] |= MODE_CHANOP; - break; - case '\0': - send_message(user, bot, "MSG_NOT_CHANNEL_NAME"); - 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; + 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; + 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; + break; + case '@': + discrim->chan_req_modes[discrim->channel_count] |= MODE_CHANOP; + break; + case '\0': + send_message(user, opserv, "MSG_NOT_CHANNEL_NAME"); + goto fail; } - 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))) { - /* 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); - } + } + 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))) { + /* secretly "allow_channel" now means "if a channel name is + * specified, require that it currently exist" */ + if (allow_channel) { + send_message(user, opserv, "MSG_CHANNEL_UNKNOWN", argv[i]); + goto fail; + } else { + discrim->channels[discrim->channel_count] = AddChannel(argv[i]+j, now, NULL, NULL, NULL); } - LockChannel(discrim->channels[discrim->channel_count]); - discrim->channel_count++; - } 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) { - discrim->limit = strtoul(argv[++i], NULL, 10); + } + LockChannel(discrim->channels[discrim->channel_count]); + discrim->channel_count++; + } 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) { + discrim->limit = strtoul(argv[++i], NULL, 10); } else if (irccasecmp(argv[i], "reason") == 0) { discrim->reason = strdup(unsplit_string(argv+i+1, argc-i-1, NULL)); i = argc; @@ -5477,6 +5521,8 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int 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 +5549,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 +5563,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 +5577,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 +5591,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 +5605,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 +5641,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 level, i; char *scmp=NULL, *dcmp=NULL; if ((user->timestamp < discrim->min_ts) @@ -5613,9 +5659,9 @@ 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; + for(i = 0; i < discrim->channel_count; i++) + if (!GetUserMode(discrim->channels[i], user)) + return 0; if(discrim->use_regex) { @@ -5663,9 +5709,57 @@ discrim_match(discrim_t discrim, struct userNode *user) return 0; } - access = user->handle_info ? user->handle_info->opserv_level : 0; - if ((access < discrim->min_level) - || (access > discrim->max_level)) { + 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 '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; + } + + level = user->handle_info ? user->handle_info->opserv_level : 0; + if ((level < discrim->min_level) + || (level > discrim->max_level)) { return 0; } if (discrim->min_clones > 1) { @@ -5687,15 +5781,14 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) if (discrim->channel_count) { for (nn=0; (nn < discrim->channels[0]->members.used) - && (matched.used < discrim->limit); - nn++) { + && (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)) { continue; } - if ((match = discrim_match(discrim, mn->user))) { unsigned int i; @@ -5711,7 +5804,7 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) } if (match) - userList_append(&matched, mn->user); + userList_append(&matched, mn->user); } } } else if (discrim->ip_mask_bits == 128) { @@ -5773,6 +5866,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)) { @@ -5786,9 +5903,9 @@ is_oper_victim(struct userNode *user, struct userNode *target, int match_opers, unsigned int nn; is_victim = !(IsService(target) - || (!match_opers && IsOper(target)) - || (target->handle_info - && target->handle_info->opserv_level > user->handle_info->opserv_level)); + || (!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) @@ -5813,6 +5930,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)) { opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration, das->discrim->silent); } @@ -5837,7 +5955,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, 1)) { char *reason; if (das->discrim->reason) { reason = das->discrim->reason; @@ -6059,6 +6177,10 @@ 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")) @@ -6393,19 +6515,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, ""); 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) { @@ -6502,19 +6619,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, ""); 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) { @@ -6523,14 +6635,14 @@ static MODCMD_FUNC(cmd_stats_shuns) { return 1; } else if (argc < 3) { struct shun_extra extra; - struct shun *sh; + struct shun *gl; extra.user = user; - sh = shun_find(argv[1]); - if (!sh) + gl = shun_find(argv[1]); + if (!gl) reply("OSMSG_NO_SHUN", argv[1]); else - strace_print_func(sh, &extra); + strace_print_func(gl, &extra); return 1; } else return 0; } @@ -6624,6 +6736,8 @@ alert_check_user(const char *key, void *data, void *extra) 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) { @@ -6662,7 +6776,7 @@ alert_check_user(const char *key, void *data, void *extra) 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,15 +6784,23 @@ 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) { - 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; } @@ -6707,8 +6829,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 +6934,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 +6969,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 +6982,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