X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/258d1427c70f88404b2ae4a06df410f5da464c76..512d7958d12cef042c7324ad086cced98dc720bf:/src/opserv.c diff --git a/src/opserv.c b/src/opserv.c index 216c4d3..62a5de4 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -3,7 +3,7 @@ * * This file is part of x3. * - * srvx is free software; you can redistribute it and/or modify + * x3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. @@ -23,9 +23,11 @@ #include "global.h" #include "nickserv.h" #include "modcmd.h" +#include "modules.h" #include "opserv.h" #include "timeq.h" #include "saxdb.h" +#include "shun.h" #ifdef HAVE_SYS_TIMES_H #include @@ -36,6 +38,9 @@ #ifdef HAVE_ARPA_INET_H #include #endif +#ifdef HAVE_REGEX_H +#include +#endif #define OPSERV_CONF_NAME "services/opserv" @@ -50,6 +55,7 @@ #define KEY_NICK "nick" #define KEY_JOIN_POLICER "join_policer" #define KEY_NEW_USER_POLICER "new_user_policer" +#define KEY_AUTOJOIN_CHANNELS "autojoin_channels" #define KEY_REASON "reason" #define KEY_RESERVES "reserves" #define KEY_IDENT "username" /* for compatibility with 1.0 DBs */ @@ -74,12 +80,14 @@ #define KEY_STAFF_AUTH_CHANNEL_MODES "staff_auth_channel_modes" #define KEY_CLONE_GLINE_DURATION "clone_gline_duration" #define KEY_BLOCK_GLINE_DURATION "block_gline_duration" +#define KEY_BLOCK_SHUN_DURATION "block_shun_duration" #define KEY_ISSUER "issuer" #define KEY_ISSUED "issued" #define KEY_ADMIN_LEVEL "admin_level" +#define KEY_SILENT_LEVEL "silent_level" #define IDENT_FORMAT "%s [%s@%s/%s]" -#define IDENT_DATA(user) user->nick, user->ident, user->hostname, inet_ntoa(user->ip) +#define IDENT_DATA(user) user->nick, user->ident, user->hostname, irc_ntoa(&user->ip) #define MAX_CHANNELS_WHOIS 50 #define OSMSG_PART_REASON "%s has no reason." #define OSMSG_KICK_REQUESTED "Kick requested by %s." @@ -93,6 +101,11 @@ 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_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_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." }, @@ -118,19 +131,21 @@ 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." }, - { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" }, - { "OSMSG_WHOIS_NICK", "Nick : %s" }, - { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, - { "OSMSG_WHOIS_FAKEHOST", "Fakehost: %s" }, - { "OSMSG_WHOIS_IP", "Real IP : %s" }, - { "OSMSG_WHOIS_MODES", "Modes : +%s " }, - { "OSMSG_WHOIS_INFO", "Info : %s" }, - { "OSMSG_WHOIS_NUMERIC", "Numnick : %s" }, - { "OSMSG_WHOIS_SERVER", "Server : %s" }, - { "OSMSG_WHOIS_NICK_AGE", "Nick Age: %s" }, - { "OSMSG_WHOIS_ACCOUNT", "Account : %s" }, - { "OSMSG_WHOIS_CHANNELS", "Channels: %s" }, - { "OSMSG_WHOIS_HIDECHANS", "Channel list omitted for your sanity." }, + { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" }, + { "OSMSG_WHOIS_NICK", "Nick : %s" }, + { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, + { "OSMSG_WHOIS_FAKEHOST", "Fakehost : %s" }, + { "OSMSG_WHOIS_CRYPT_HOST", "Crypt Host : %s" }, + { "OSMSG_WHOIS_CRYPT_IP", "Crypt IP : %s" }, + { "OSMSG_WHOIS_IP", "Real IP : %s" }, + { "OSMSG_WHOIS_MODES", "Modes : +%s " }, + { "OSMSG_WHOIS_INFO", "Info : %s" }, + { "OSMSG_WHOIS_NUMERIC", "Numnick : %s" }, + { "OSMSG_WHOIS_SERVER", "Server : %s" }, + { "OSMSG_WHOIS_NICK_AGE", "Nick Age : %s" }, + { "OSMSG_WHOIS_ACCOUNT", "Account : %s" }, + { "OSMSG_WHOIS_CHANNELS", "Channels : %s" }, + { "OSMSG_WHOIS_HIDECHANS", "Channel list omitted for your sanity." }, { "OSMSG_UNBAN_DONE", "Ban(s) removed from channel %s." }, { "OSMSG_CHANNEL_VOICED", "All users on %s voiced." }, { "OSMSG_CHANNEL_DEVOICED", "All voiced users on %s de-voiced." }, @@ -157,6 +172,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_BADWORD_LIST", "Bad words: %s" }, { "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" }, { "OSMSG_GLINE_COUNT", "There are %d glines active on the network." }, + { "OSMSG_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" }, { "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" }, @@ -208,9 +224,12 @@ static const struct message_entry msgtab[] = { { "OSMSG_USER_SEARCH_COUNT_BAR", "------------ Found %4u matches -----------" }, { "OSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" }, { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" }, + { "OSMSG_SHUN_SEARCH_RESULTS", "The following shun were found:" }, { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" }, { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." }, + { "OSMSG_SSYNC_RUNNING", "Synchronizing shuns from %s." }, { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, 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." }, @@ -256,6 +275,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_UPLINK_START", "Uplink $b%s$b:" }, { "OSMSG_UPLINK_ADDRESS", "Address: %s:%d" }, { "OSMSG_STUPID_GLINE", "Gline %s? Now $bthat$b would be smooth." }, + { "OSMSG_STUPID_SHUN", "Shun %s? Now $bthat$b would be smooth." }, { "OSMSG_ACCOUNTMASK_AUTHED", "Invalid criteria: it is impossible to match an account mask but not be authed" }, { "OSMSG_CHANINFO_HEADER", "%s Information" }, { "OSMSG_CHANINFO_TIMESTAMP", "Created on: %a %b %d %H:%M:%S %Y (%s)" }, @@ -270,11 +290,15 @@ 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_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" }, { NULL, NULL } }; #define OPSERV_SYNTAX() svccmd_send_help_brief(user, opserv, cmd) +extern void add_track_user(struct userNode *user); typedef int (*discrim_search_func)(struct userNode *match, void *extra); struct userNode *opserv; @@ -293,6 +317,7 @@ static struct module *opserv_module; static struct log_type *OS_LOG; static unsigned int new_user_flood; static char *level_strings[1001]; +struct string_list *autojoin_channels; static struct { struct chanNode *debug_channel; @@ -303,10 +328,12 @@ static struct { unsigned long untrusted_max; unsigned long clone_gline_duration; unsigned long block_gline_duration; + unsigned long block_shun_duration; unsigned long purge_lock_delay; unsigned long join_flood_moderate; unsigned long join_flood_moderate_threshold; unsigned long admin_level; + unsigned long silent_level; } opserv_conf; struct trusted_host { @@ -343,14 +370,20 @@ opserv_free_hostinfo(void *data) typedef struct opservDiscrim { struct chanNode *channel; - char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *ip_mask_str, *reason, *accountmask; - unsigned long limit, ip_mask; - struct in_addr ip_addr; + char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *accountmask; + irc_in_addr_t ip_mask; + unsigned long limit; + time_t min_ts, max_ts; + regex_t regex_nick, regex_ident, regex_host, regex_info; + unsigned int has_regex_nick : 1, has_regex_ident : 1, has_regex_host : 1, has_regex_info : 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, option_log : 1; unsigned int chan_req_modes : 2, chan_no_modes : 2; int authed : 2, info_space : 2; - time_t min_ts, max_ts; + unsigned int intra_scmp : 2, intra_dcmp : 2; + unsigned int use_regex : 1; + unsigned int silent : 1; } *discrim_t; struct discrim_and_source { @@ -369,7 +402,10 @@ static int ungag_helper_func(struct userNode *match, void *extra); typedef enum { REACT_NOTICE, REACT_KILL, - REACT_GLINE +// REACT_SILENT, + REACT_GLINE, + REACT_TRACK, + REACT_SHUN } opserv_alert_reaction; struct opserv_user_alert { @@ -389,6 +425,14 @@ opserv_free_user_alert(void *data) free(alert->owner); free(alert->text_discrim); free(alert->split_discrim); + if(alert->discrim->has_regex_nick) + regfree(&alert->discrim->regex_nick); + if(alert->discrim->has_regex_ident) + regfree(&alert->discrim->regex_ident); + if(alert->discrim->has_regex_host) + regfree(&alert->discrim->regex_host); + if(alert->discrim->has_regex_info) + regfree(&alert->discrim->regex_info); free(alert->discrim->reason); free(alert->discrim); free(alert); @@ -397,6 +441,7 @@ 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) + /* A lot of these commands are very similar to what ChanServ can do, * but OpServ can do them even on channels that aren't registered. */ @@ -493,8 +538,12 @@ static MODCMD_FUNC(cmd_chaninfo) reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used); for (n=0; nmembers.used; n++) { moden = channel->members.list[n]; - if (moden->modes & MODE_CHANOP) - send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); + if (moden->modes & MODE_CHANOP) { + if (moden->oplevel >= 0) + send_message_type(4, user, cmd->parent->bot, " @%s:%d (%s@%s)", moden->user->nick, moden->oplevel, moden->user->ident, moden->user->hostname); + else + send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); + } } for (n=0; nmembers.used; n++) { moden = channel->members.list[n]; @@ -516,7 +565,7 @@ static MODCMD_FUNC(cmd_chaninfo) /* This command has been replaced by 'alert notice channel #foo' */ /* -static MODCMD_FUNC(cmd_warn) +static MODCMD_FUNC(cmd_warn) { char *reason, *message; @@ -685,6 +734,14 @@ static MODCMD_FUNC(cmd_dehopall) return 1; } +static MODCMD_FUNC(cmd_resetmax) +{ + max_clients = dict_size(clients); + max_clients_time = now; + reply("OSMSG_MAXUSERS_RESET", max_clients); + return 1; +} + static MODCMD_FUNC(cmd_rehash) { extern char *services_config; @@ -835,17 +892,16 @@ static MODCMD_FUNC(cmd_restart) } static struct gline * -opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned long duration) +opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned long duration, int silent) { - char *mask; - mask = alloca(MAXLEN); - snprintf(mask, MAXLEN, "*@%s", target->hostname); - if (!reason) { - reason = alloca(MAXLEN); - snprintf(reason, MAXLEN, "G-line requested by %s.", src_handle); - } - if (!duration) duration = opserv_conf.block_gline_duration; - return gline_add(src_handle, mask, duration, reason, now, 1); + char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' }; + irc_ntop(mask + 2, sizeof(mask) - 2, &target->ip); + if (!reason) + snprintf(reason = alloca(MAXLEN), MAXLEN, + "G-line requested by %s.", src_handle); + if (!duration) + duration = opserv_conf.block_gline_duration; + return gline_add(src_handle, mask, duration, reason, now, 1, silent ? 1 : 0); } static MODCMD_FUNC(cmd_block) @@ -864,7 +920,7 @@ static MODCMD_FUNC(cmd_block) return 0; } reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL; - gline = opserv_block(target, user->handle_info->handle, reason, 0); + gline = opserv_block(target, user->handle_info->handle, reason, 0, 0); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -889,7 +945,7 @@ static MODCMD_FUNC(cmd_gline) reply("MSG_INVALID_DURATION", argv[2]); return 0; } - gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1); + gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1, 0); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -929,6 +985,101 @@ static MODCMD_FUNC(cmd_refreshg) return 1; } +static struct shun * +opserv_shun(struct userNode *target, char *src_handle, char *reason, unsigned long duration) +{ + char *mask; + mask = alloca(MAXLEN); + snprintf(mask, MAXLEN, "*@%s", target->hostname); + if (!reason) { + reason = alloca(MAXLEN); + 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, 1); +} + +static MODCMD_FUNC(cmd_sblock) +{ + struct userNode *target; + struct shun *shun; + char *reason; + + target = GetUserH(argv[1]); + if (!target) { + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; + } + if (IsService(target)) { + reply("MSG_SERVICE_IMMUNE", target->nick); + return 0; + } + reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL; + shun = opserv_shun(target, user->handle_info->handle, reason, 0); + reply("OSMSG_SHUN_ISSUED", shun->target); + return 1; +} + +static MODCMD_FUNC(cmd_shun) +{ + unsigned long duration; + char *reason; + struct shun *shun; + + reason = unsplit_string(argv+3, argc-3, NULL); + if (!is_shun(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) { + reply("MSG_INVALID_SHUN", argv[1]); + return 0; + } + if (!argv[1][strspn(argv[1], "#&*?@.")] && (strlen(argv[1]) < 10)) { + reply("OSMSG_STUPID_SHUN", argv[1]); + return 0; + } + duration = ParseInterval(argv[2]); + if (!duration) { + reply("MSG_INVALID_DURATION", argv[2]); + return 0; + } + shun = shun_add(user->handle_info->handle, argv[1], duration, reason, now, 1); + reply("OSMSG_SHUN_ISSUED", shun->target); + return 1; +} + +static MODCMD_FUNC(cmd_unshun) +{ + if (shun_remove(argv[1], 1)) + reply("OSMSG_SHUN_REMOVED", argv[1]); + else + reply("OSMSG_SHUN_FORCE_REMOVED", argv[1]); + return 1; +} + +static MODCMD_FUNC(cmd_refreshs) +{ + if (argc > 1) { + unsigned int count; + dict_iterator_t it; + struct server *srv; + + for (it=dict_first(servers), count=0; it; it=iter_next(it)) { + srv = iter_data(it); + if ((srv == self) || !match_ircglob(srv->name, argv[1])) + continue; + shun_refresh_server(srv); + reply("OSMSG_SHUNS_ONE_REFRESHED", srv->name); + count++; + } + if (!count) { + reply("MSG_SERVER_UNKNOWN", argv[1]); + return 0; + } + } else { + shun_refresh_all(); + reply("OSMSG_SHUNS_REFRESHED"); + } + return 1; +} + static void opserv_ison(struct userNode *tell, struct userNode *target, const char *message) { @@ -1314,7 +1465,9 @@ static MODCMD_FUNC(cmd_whois) reply("OSMSG_WHOIS_HOST", target->ident, target->hostname); if (IsFakeHost(target)) reply("OSMSG_WHOIS_FAKEHOST", target->fakehost); - reply("OSMSG_WHOIS_IP", inet_ntoa(target->ip)); + reply("OSMSG_WHOIS_CRYPT_HOST", target->crypthost); + reply("OSMSG_WHOIS_CRYPT_IP", target->cryptip); + reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip)); if (target->modes) { bpos = 0; #define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen) @@ -1453,6 +1606,11 @@ static MODCMD_FUNC(cmd_stats_glines) { 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; @@ -1672,7 +1830,10 @@ static MODCMD_FUNC(cmd_stats_alerts) { switch (alert->reaction) { case REACT_NOTICE: reaction = "notice"; break; case REACT_KILL: reaction = "kill"; break; +// case REACT_SILENT: reaction = "silent"; break; case REACT_GLINE: reaction = "gline"; break; + case REACT_TRACK: reaction = "track"; break; + case REACT_SHUN: reaction = "shun"; break; default: reaction = ""; break; } reply("OSMSG_ALERT_IS", iter_key(it), reaction, alert->owner); @@ -1755,9 +1916,9 @@ static MODCMD_FUNC(cmd_stats_memory) { send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot, "%u allocations in %u slabs totalling %u bytes.", slab_alloc_count, slab_count, slab_alloc_size); -/* send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot, + send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot, "%u big allocations totalling %u bytes.", - */ + big_alloc_count, big_alloc_size); return 1; } #endif @@ -1882,6 +2043,7 @@ opserv_new_user_check(struct userNode *user) { struct opserv_hostinfo *ohi; struct gag_entry *gag; + char addr[IRC_NTOP_MAX_SIZE]; /* Check to see if we should ignore them entirely. */ if (IsLocal(user) || IsService(user)) @@ -1893,16 +2055,17 @@ opserv_new_user_check(struct userNode *user) /* Gag them if appropriate. */ for (gag = gagList; gag; gag = gag->next) { - if (user_matches_glob(user, gag->mask, 1)) { + if (user_matches_glob(user, gag->mask, MATCH_USENICK)) { gag_helper_func(user, NULL); break; } } /* Add to host info struct */ - if (!(ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) { + irc_ntop(addr, sizeof(addr), &user->ip); + if (!(ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) { ohi = calloc(1, sizeof(*ohi)); - dict_insert(opserv_hostinfo_dict, strdup(inet_ntoa(user->ip)), ohi); + dict_insert(opserv_hostinfo_dict, strdup(addr), ohi); userList_init(&ohi->clients); } userList_append(&ohi->clients, user); @@ -1920,8 +2083,10 @@ opserv_new_user_check(struct userNode *user) } /* Only warn or G-line if there's an untrusted max and their IP is sane. */ - if (opserv_conf.untrusted_max && user->ip.s_addr && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)) { - struct trusted_host *th = dict_find(opserv_trusted_hosts, inet_ntoa(user->ip), NULL); + if (opserv_conf.untrusted_max + && irc_in_addr_is_valid(user->ip) + && !irc_in_addr_is_loopback(user->ip)) { + struct trusted_host *th = dict_find(opserv_trusted_hosts, addr, NULL); unsigned int limit = th ? th->limit : opserv_conf.untrusted_max; if (!limit) { /* 0 means unlimited hosts */ @@ -1930,9 +2095,9 @@ opserv_new_user_check(struct userNode *user) for (nn=0; nnclients.used; nn++) send_message(ohi->clients.list[nn], opserv, "OSMSG_CLONE_WARNING"); } else if (ohi->clients.used > limit) { - char target[18]; - sprintf(target, "*@%s", inet_ntoa(user->ip)); - gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, 1); + 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, 1, 1); } } @@ -1943,6 +2108,7 @@ static void opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why)) { struct opserv_hostinfo *ohi; + char addr[IRC_NTOP_MAX_SIZE]; if (IsLocal(user)) { /* Try to remove it from the reserved nick dict without @@ -1951,9 +2117,11 @@ opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), dict_remove(opserv_reserved_nick_dict, user->nick); return; } - if ((ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) { + irc_ntop(addr, sizeof(addr), &user->ip); + if ((ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) { userList_remove(&ohi->clients, user); - if (ohi->clients.used == 0) dict_remove(opserv_hostinfo_dict, inet_ntoa(user->ip)); + if (ohi->clients.used == 0) + dict_remove(opserv_hostinfo_dict, addr); } } @@ -2281,7 +2449,7 @@ static MODCMD_FUNC(cmd_addtrust) { unsigned long interval; char *reason, *tmp; - struct in_addr tmpaddr; + irc_in_addr_t tmpaddr; unsigned int count; if (dict_find(opserv_trusted_hosts, argv[1], NULL)) { @@ -2289,7 +2457,7 @@ static MODCMD_FUNC(cmd_addtrust) return 0; } - if (!inet_aton(argv[1], &tmpaddr)) { + if (!irc_pton(&tmpaddr, NULL, argv[1])) { reply("OSMSG_BAD_IP", argv[1]); return 0; } @@ -2585,8 +2753,10 @@ opserv_define_func(const char *name, modcmd_func_t *func, int min_level, int req int add_reserved(const char *key, void *data, void *extra) { + struct chanNode *chan; struct record_data *rd = data; const char *ident, *hostname, *desc; + unsigned int i; struct userNode *reserve; ident = database_get_data(rd->d.object, KEY_IDENT, RECDB_QSTRING); if (!ident) { @@ -2607,6 +2777,14 @@ int add_reserved(const char *key, void *data, void *extra) reserve->modes |= FLAGS_PERSISTENT; dict_insert(extra, reserve->nick, reserve); } + + if (autojoin_channels && reserve) { + for (i = 0; i < autojoin_channels->used; i++) { + chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL); + AddChannelUser(reserve, chan)->modes |= MODE_VOICE; + } + } + return 0; } @@ -2625,17 +2803,14 @@ foreach_matching_user(const char *hostmask, discrim_search_func func, void *extr discrim->max_channels = INT_MAX; discrim->authed = -1; discrim->info_space = -1; + discrim->intra_scmp = 0; + discrim->intra_dcmp = 0; + discrim->use_regex = 0; + discrim->silent = 0; dupmask = strdup(hostmask); if (split_ircmask(dupmask, &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) { - if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "0123456789.?*")]) { - if (!parse_ipmask(discrim->mask_host, &discrim->ip_addr, &discrim->ip_mask)) { - log_module(OS_LOG, LOG_ERROR, "Couldn't parse %s as an IP mask!", discrim->mask_host); - free(discrim); - free(dupmask); - return 0; - } - discrim->mask_host = 0; - } + if (!irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, discrim->mask_host)) + discrim->ip_mask_bits = 0; matched = opserv_discrim_search(discrim, func, extra); } else { log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask); @@ -2789,8 +2964,16 @@ add_user_alert(const char *key, void *data, UNUSED_ARG(void *extra)) reaction = REACT_NOTICE; else if (!irccasecmp(react, "kill")) reaction = REACT_KILL; + /* + else if (!irccasecmp(react, "silent")) + reaction = REACT_SILENT; + */ else if (!irccasecmp(react, "gline")) reaction = REACT_GLINE; + else if (!irccasecmp(react, "track")) + reaction = REACT_TRACK; + else if (!irccasecmp(react, "shun")) + reaction = REACT_SHUN; else { log_module(OS_LOG, LOG_ERROR, "Invalid reaction %s for alert %s.", react, key); return 0; @@ -2977,7 +3160,10 @@ opserv_saxdb_write(struct saxdb_context *ctx) switch (alert->reaction) { case REACT_NOTICE: reaction = "notice"; break; case REACT_KILL: reaction = "kill"; break; +// case REACT_SILENT: reaction = "silent"; break; case REACT_GLINE: reaction = "gline"; break; + case REACT_TRACK: reaction = "track"; break; + case REACT_SHUN: reaction = "shun"; break; default: reaction = NULL; log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s (while writing database).", alert->reaction, iter_key(it)); @@ -3092,6 +3278,10 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int discrim->max_channels = INT_MAX; discrim->authed = -1; discrim->info_space = -1; + discrim->intra_dcmp = 0; + discrim->intra_scmp = 0; + discrim->use_regex = 0; + discrim->silent = 0; for (i=0; iintra_dcmp = 1; + else if (!strcasecmp(tmp, "ident")) + discrim->intra_dcmp = 2; + else if (!strcasecmp(tmp, "info")) + discrim->intra_dcmp = 3; + } + } if (irccasecmp(argv[i], "mask") == 0) { if (!is_ircmask(argv[++i])) { send_message(user, bot, "OSMSG_INVALID_IRCMASK", argv[i]); @@ -3116,18 +3318,33 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int goto fail; } } else if (irccasecmp(argv[i], "nick") == 0) { - discrim->mask_nick = argv[++i]; + i++; + if (discrim->intra_dcmp > 0) + discrim->intra_scmp = 1; + else + discrim->mask_nick = argv[i]; } else if (irccasecmp(argv[i], "ident") == 0) { - discrim->mask_ident = argv[++i]; + i++; + if (discrim->intra_dcmp > 0) + discrim->intra_scmp = 2; + else + discrim->mask_ident = argv[i]; } else if (irccasecmp(argv[i], "host") == 0) { discrim->mask_host = argv[++i]; } else if (irccasecmp(argv[i], "info") == 0) { - discrim->mask_info = argv[++i]; + i++; + if (discrim->intra_dcmp > 0) + discrim->intra_scmp = 3; + else + discrim->mask_info = argv[i]; } else if (irccasecmp(argv[i], "server") == 0) { discrim->server = argv[++i]; } else if (irccasecmp(argv[i], "ip") == 0) { - j = parse_ipmask(argv[++i], &discrim->ip_addr, &discrim->ip_mask); - if (!j) discrim->ip_mask_str = argv[i]; + j = irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, argv[++i]); + if (!j) { + send_message(user, opserv, "OSMSG_BAD_IP", argv[i]); + goto fail; + } } else if (irccasecmp(argv[i], "account") == 0) { if (discrim->authed == 0) { send_message(user, bot, "OSMSG_ACCOUNTMASK_AUTHED"); @@ -3161,6 +3378,28 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int send_message(user, bot, "MSG_INVALID_BINARY", argv[i]); goto fail; } + } else if (irccasecmp(argv[i], "regex") == 0) { + i++; + if (true_string(argv[i])) { + discrim->use_regex = 1; + } else if (false_string(argv[i])) { + discrim->use_regex = 0; + } else { + send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]); + goto fail; + } + } else if (irccasecmp(argv[i], "silent") == 0) { + i++; + if(!oper_has_access(user, opserv, opserv_conf.silent_level, 0)) { + goto fail; + } else if (true_string(argv[i])) { + discrim->silent = 1; + } else if (false_string(argv[i])) { + discrim->silent = 0; + } else { + send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]); + goto fail; + } } else if (irccasecmp(argv[i], "duration") == 0) { discrim->duration = ParseInterval(argv[++i]); } else if (irccasecmp(argv[i], "channel") == 0) { @@ -3274,16 +3513,91 @@ opserv_discrim_create(struct userNode *user, struct userNode *bot, unsigned int if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "*.")]) { discrim->mask_host = 0; } + + if(discrim->use_regex) + { + if(discrim->mask_nick) + { + int err = regcomp(&discrim->regex_nick, discrim->mask_nick, REG_EXTENDED|REG_ICASE|REG_NOSUB); + discrim->has_regex_nick = !err; + if(err) + { + char buff[256]; + buff[regerror(err, &discrim->regex_nick, buff, sizeof(buff))] = 0; + + send_message(user, opserv, "OSMSG_INVALID_REGEX", discrim->mask_nick, buff, err); + goto regfail; + } + } + + if(discrim->mask_ident) + { + int err = regcomp(&discrim->regex_ident, discrim->mask_ident, REG_EXTENDED|REG_ICASE|REG_NOSUB); + discrim->has_regex_ident = !err; + if(err) + { + char buff[256]; + buff[regerror(err, &discrim->regex_ident, buff, sizeof(buff))] = 0; + + send_message(user, opserv, "OSMSG_INVALID_REGEX", discrim->mask_ident, buff, err); + goto regfail; + } + } + + if(discrim->mask_host) + { + int err = regcomp(&discrim->regex_host, discrim->mask_host, REG_EXTENDED|REG_ICASE|REG_NOSUB); + discrim->has_regex_host = !err; + if(err) + { + char buff[256]; + buff[regerror(err, &discrim->regex_host, buff, sizeof(buff))] = 0; + + send_message(user, opserv, "OSMSG_INVALID_REGEX", discrim->mask_host, buff, err); + goto regfail; + } + } + + if(discrim->mask_info) + { + int err = regcomp(&discrim->regex_info, discrim->mask_info, REG_EXTENDED|REG_ICASE|REG_NOSUB); + discrim->has_regex_info = !err; + if(err) + { + char buff[256]; + buff[regerror(err, &discrim->regex_info, buff, sizeof(buff))] = 0; + + send_message(user, opserv, "OSMSG_INVALID_REGEX", discrim->mask_info, buff, err); + goto regfail; + } + } + } + return discrim; + fail: free(discrim); return NULL; + + regfail: + if(discrim->has_regex_nick) + regfree(&discrim->regex_nick); + if(discrim->has_regex_ident) + regfree(&discrim->regex_ident); + if(discrim->has_regex_host) + regfree(&discrim->regex_host); + if(discrim->has_regex_info) + regfree(&discrim->regex_info); + + free(discrim); + return NULL; } static int discrim_match(discrim_t discrim, struct userNode *user) { unsigned int access; + char *scmp=NULL, *dcmp=NULL; if ((user->timestamp < discrim->min_ts) || (user->timestamp > discrim->max_ts) @@ -3293,27 +3607,68 @@ discrim_match(discrim_t discrim, struct userNode *user) || (discrim->authed == 1 && !user->handle_info) || (discrim->info_space == 0 && user->info[0] == ' ') || (discrim->info_space == 1 && user->info[0] != ' ') - || (discrim->mask_nick && !match_ircglob(user->nick, discrim->mask_nick)) - || (discrim->mask_ident && !match_ircglob(user->ident, discrim->mask_ident)) - || (discrim->mask_host && !match_ircglob(user->hostname, discrim->mask_host)) - || (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info)) || (discrim->server && !match_ircglob(user->uplink->name, discrim->server)) || (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask))) - || (discrim->ip_mask && !MATCH_IPMASK(user->ip, discrim->ip_addr, discrim->ip_mask))) { + || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits)) + ) + return 0; + + if (discrim->channel && !GetUserMode(discrim->channel, user)) return 0; + + if(discrim->use_regex) + { + if((discrim->has_regex_nick && regexec(&discrim->regex_nick, user->nick, 0, 0, 0)) + || (discrim->has_regex_ident && regexec(&discrim->regex_ident, user->ident, 0, 0, 0)) + || (discrim->has_regex_host && regexec(&discrim->regex_host, user->hostname, 0, 0, 0)) + || (discrim->has_regex_info && regexec(&discrim->regex_info, user->info, 0, 0, 0))) { + return 0; + } + } + else + { + if ((discrim->mask_nick && !match_ircglob(user->nick, discrim->mask_nick)) + || (discrim->mask_ident && !match_ircglob(user->ident, discrim->mask_ident)) + || (discrim->mask_host && !match_ircglob(user->hostname, discrim->mask_host)) + || (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info))) { + return 0; + } + } + + if ((discrim->intra_scmp > 0 && discrim->intra_dcmp > 0)) { + switch(discrim->intra_scmp) { + case 1: scmp=user->nick; break; + case 2: scmp=user->ident; break; + case 3: + scmp=user->info; + if (discrim->info_space == 1) scmp++; + break; + } + switch(discrim->intra_dcmp) { + case 1: dcmp=user->nick; break; + case 2: dcmp=user->ident; break; + case 3: /* When checking INFO, and info_space is enabled + * ignore the first character in a search + * XXX: Should we ignore ALL leading whitespace? + * Also, what about ignoring ~ in ident? + */ + dcmp=user->info; + if (discrim->info_space == 1) dcmp++; + break; + } + if (irccasecmp(scmp,dcmp)) + return 0; } - if (discrim->channel && !GetUserMode(discrim->channel, user)) return 0; + access = user->handle_info ? user->handle_info->opserv_level : 0; if ((access < discrim->min_level) || (access > discrim->max_level)) { return 0; } - if (discrim->ip_mask_str) { - if (!match_ircglob(inet_ntoa(user->ip), discrim->ip_mask_str)) return 0; - } if (discrim->min_clones > 1) { - struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL); - if (!ohi || (ohi->clients.used < discrim->min_clones)) return 0; + struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&user->ip), NULL); + if (!ohi || (ohi->clients.used < discrim->min_clones)) + return 0; } return 1; } @@ -3340,8 +3695,8 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) userList_append(&matched, mn->user); } } - } else if (discrim->ip_mask_str && !discrim->ip_mask_str[strcspn(discrim->ip_mask_str, "?*")]) { - struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, discrim->ip_mask_str, NULL); + } else if (discrim->ip_mask_bits == 128) { + struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&discrim->ip_mask), NULL); if (!ohi) { userList_clean(&matched); return 0; @@ -3420,7 +3775,19 @@ 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)) { - opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration); + opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration, das->discrim->silent); + } + + return 0; +} + +static int +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)) { + opserv_shun(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration); } return 0; @@ -3486,29 +3853,42 @@ static int trace_domains_func(struct userNode *match, void *extra) { struct discrim_and_source *das = extra; + irc_in_addr_t ip; unsigned long *count; unsigned int depth; char *hostname; + char ipmask[IRC_NTOP_MASK_MAX_SIZE]; - if (!match->hostname[strspn(match->hostname, "0123456789.")]) { - char ipmask[16]; - unsigned long matchip = ntohl(match->ip.s_addr); - /* raw IP address.. use up to first three octets of IP */ - switch (das->discrim->domain_depth) { - default: - snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255); - break; - case 2: - snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255); - break; - case 1: - snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255); - break; - } + if (irc_pton(&ip, NULL, match->hostname)) { + if (irc_in_addr_is_ipv4(ip)) { + unsigned long matchip = ntohl(ip.in6_32[3]); + /* raw IP address.. use up to first three octets of IP */ + switch (das->discrim->domain_depth) { + default: + snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255); + break; + case 2: + snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255); + break; + case 1: + snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255); + break; + } + } else if (irc_in_addr_is_ipv6(ip)) { + switch (das->discrim->domain_depth) { + case 1: depth = 16; goto ipv6_pfx; + case 2: depth = 24; goto ipv6_pfx; + case 3: depth = 32; goto ipv6_pfx; + default: depth = das->discrim->domain_depth; + ipv6_pfx: + irc_ntop_mask(ipmask, sizeof(ipmask), &ip, depth); + } + } else safestrncpy(ipmask, match->hostname, sizeof(ipmask)); + ipmask[sizeof(ipmask) - 1] = '\0'; hostname = ipmask; } else { hostname = match->hostname + strlen(match->hostname); - for (depth=das->discrim->domain_depth; + for (depth=das->discrim->domain_depth; depth && (hostname > match->hostname); depth--) { hostname--; @@ -3557,6 +3937,8 @@ static MODCMD_FUNC(cmd_trace) action = trace_domains_func; else if (!irccasecmp(argv[1], "gline")) action = trace_gline_func; + else if (!irccasecmp(argv[1], "shun")) + action = trace_shun_func; else if (!irccasecmp(argv[1], "kill")) action = trace_kill_func; else if (!irccasecmp(argv[1], "gag")) @@ -3589,6 +3971,8 @@ static MODCMD_FUNC(cmd_trace) das.discrim->limit = INT_MAX; else if ((action == trace_gline_func) && !das.discrim->duration) das.discrim->duration = opserv_conf.block_gline_duration; + else if ((action == trace_shun_func) && !das.discrim->duration) + das.discrim->duration = opserv_conf.block_shun_duration; else if (action == trace_domains_func) { das.dict = dict_new(); dict_set_free_data(das.dict, free); @@ -3614,6 +3998,16 @@ static MODCMD_FUNC(cmd_trace) if (das.discrim->channel) UnlockChannel(das.discrim->channel); free(das.discrim->reason); + + if(das.discrim->has_regex_nick) + regfree(&das.discrim->regex_nick); + if(das.discrim->has_regex_ident) + regfree(&das.discrim->regex_ident); + if(das.discrim->has_regex_host) + regfree(&das.discrim->regex_host); + if(das.discrim->has_regex_info) + regfree(&das.discrim->regex_info); + free(das.discrim); dict_delete(das.dict); return 1; @@ -3817,6 +4211,23 @@ static MODCMD_FUNC(cmd_gsync) return 1; } +static MODCMD_FUNC(cmd_ssync) +{ + struct server *src; + if (argc > 1) { + src = GetServerH(argv[1]); + if (!src) { + reply("MSG_SERVER_UNKNOWN", argv[1]); + return 0; + } + } else { + src = self->uplink; + } + irc_stats(cmd->parent->bot, src, 'S'); + reply("OSMSG_SSYNC_RUNNING", src->name); + return 1; +} + struct gline_extra { struct userNode *user; struct string_list *glines; @@ -3899,6 +4310,88 @@ static MODCMD_FUNC(cmd_gtrace) return 1; } +struct shun_extra { + struct userNode *user; + struct string_list *shuns; + struct userNode *bot; +}; + +static void +strace_print_func(struct shun *shun, void *extra) +{ + struct shun_extra *xtra = extra; + 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 +strace_count_func(UNUSED_ARG(struct shun *shun), UNUSED_ARG(void *extra)) +{ +} + +static void +strace_unshun_func(struct shun *shun, void *extra) +{ + struct shun_extra *xtra = extra; + string_list_append(xtra->shuns, strdup(shun->target)); +} + +static MODCMD_FUNC(cmd_strace) +{ + struct shun_discrim *discrim; + shun_search_func action; + unsigned int matches, nn; + struct shun_extra extra; + struct svccmd *subcmd; + char buf[MAXLEN]; + + if (!irccasecmp(argv[1], "print")) + action = strace_print_func; + else if (!irccasecmp(argv[1], "count")) + action = strace_count_func; + else if (!irccasecmp(argv[1], "unshun")) + action = strace_unshun_func; + else { + reply("OSMSG_BAD_ACTION", argv[1]); + return 0; + } + sprintf(buf, "%s %s", argv[0], argv[0]); + if ((subcmd = dict_find(opserv_service->commands, buf, NULL)) + && !svccmd_can_invoke(user, opserv_service->bot, subcmd, channel, SVCCMD_NOISY)) { + return 0; + } + + discrim = shun_discrim_create(user, cmd->parent->bot, argc-2, argv+2); + if (!discrim) + return 0; + + if (action == strace_print_func) + reply("OSMSG_SHUN_SEARCH_RESULTS"); + else if (action == strace_count_func) + discrim->limit = INT_MAX; + + extra.user = user; + extra.shuns = alloc_string_list(4); + extra.bot = cmd->parent->bot; + matches = shun_discrim_search(discrim, action, &extra); + + if (action == strace_unshun_func) + for (nn=0; nnused; nn++) + shun_remove(extra.shuns->list[nn], 1); + free_string_list(extra.shuns); + + if (matches) + reply("MSG_MATCH_COUNT", matches); + else + reply("MSG_NO_MATCHES"); + free(discrim->alt_target_mask); + free(discrim); + return 1; +} + static int alert_check_user(const char *key, void *data, void *extra) { @@ -3924,8 +4417,16 @@ alert_check_user(const char *key, void *data, void *extra) case REACT_KILL: DelUser(user, opserv, 1, alert->discrim->reason); return 1; +/* + case REACT_SILENT: + opserv_block(user, alert->owner, alert->discrim->reason, alert->discrim->duration, 1); + return 1; +*/ case REACT_GLINE: - opserv_block(user, alert->owner, alert->discrim->reason, alert->discrim->duration); + opserv_block(user, alert->owner, alert->discrim->reason, alert->discrim->duration, alert->discrim->silent); + return 1; + case REACT_SHUN: + opserv_shun(user, alert->owner, alert->discrim->reason, alert->discrim->duration); return 1; default: log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key); @@ -3933,6 +4434,12 @@ alert_check_user(const char *key, void *data, void *extra) case REACT_NOTICE: 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_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; } return 0; } @@ -3945,7 +4452,7 @@ opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick)) /* Gag them if appropriate (and only if). */ user->modes &= ~FLAGS_GAGGED; for (gag = gagList; gag; gag = gag->next) { - if (user_matches_glob(user, gag->mask, 1)) { + if (user_matches_glob(user, gag->mask, MATCH_USENICK)) { gag_helper_func(user, NULL); break; } @@ -3970,7 +4477,7 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han else return; - if (user->ip.s_addr) + if (irc_in_addr_is_valid(user->ip)) 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); @@ -4090,8 +4597,21 @@ static MODCMD_FUNC(cmd_addalert) reaction = REACT_NOTICE; else if (!irccasecmp(argv[2], "kill")) reaction = REACT_KILL; +/* + else if (!irccasecmp(argv[2], "silent")) + reaction = REACT_SILENT; +*/ else if (!irccasecmp(argv[2], "gline")) reaction = REACT_GLINE; + else if (!irccasecmp(argv[2], "track")) { +#ifndef HAVE_TRACK + send_message(user, opserv, "OSMSG_TRACK_DISABLED"); + return 0; +#else + reaction = REACT_TRACK; +#endif + } else if (!irccasecmp(argv[2], "shun")) + reaction = REACT_SHUN; else { reply("OSMSG_UNKNOWN_REACTION", argv[2]); return 0; @@ -4120,6 +4640,8 @@ static MODCMD_FUNC(cmd_delalert) static void opserv_conf_read(void) { + struct chanNode *chan; + unsigned int i; struct record_data *rd; dict_t conf_node, child; const char *str, *str2; @@ -4166,6 +4688,9 @@ opserv_conf_read(void) str = database_get_data(conf_node, KEY_ADMIN_LEVEL, RECDB_QSTRING); opserv_conf.admin_level = str ? strtoul(str, NULL, 0): 800; + str = database_get_data(conf_node, KEY_SILENT_LEVEL, RECDB_QSTRING); + opserv_conf.silent_level = str ? strtoul(str, NULL, 0): 700; + str = database_get_data(conf_node, KEY_UNTRUSTED_MAX, RECDB_QSTRING); opserv_conf.untrusted_max = str ? strtoul(str, NULL, 0) : 5; str = database_get_data(conf_node, KEY_PURGE_LOCK_DELAY, RECDB_QSTRING); @@ -4177,11 +4702,28 @@ opserv_conf_read(void) str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING); if (opserv && str) NickChange(opserv, str, 0); + str = database_get_data(conf_node, KEY_CLONE_GLINE_DURATION, RECDB_QSTRING); opserv_conf.clone_gline_duration = str ? ParseInterval(str) : 3600; str = database_get_data(conf_node, KEY_BLOCK_GLINE_DURATION, RECDB_QSTRING); opserv_conf.block_gline_duration = str ? ParseInterval(str) : 3600; + free_string_list(autojoin_channels); + autojoin_channels = database_get_data(conf_node, KEY_AUTOJOIN_CHANNELS, RECDB_STRING_LIST); + + if(autojoin_channels) + autojoin_channels = string_list_copy(autojoin_channels); + + if (autojoin_channels && opserv) { + for (i = 0; i < autojoin_channels->used; i++) { + chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL); + AddChannelUser(opserv, chan)->modes |= MODE_CHANOP; + } + } + + str = database_get_data(conf_node, KEY_BLOCK_SHUN_DURATION, RECDB_QSTRING); + opserv_conf.block_shun_duration = str ? ParseInterval(str) : 3600; + if (!opserv_conf.join_policer_params) opserv_conf.join_policer_params = policer_params_new(); policer_params_set(opserv_conf.join_policer_params, "size", "20"); @@ -4281,7 +4823,10 @@ init_opserv(const char *nick) opserv_define_func("ACCESS", cmd_access, 0, 0, 0); opserv_define_func("ADDALERT", cmd_addalert, 800, 0, 4); opserv_define_func("ADDALERT NOTICE", NULL, 0, 0, 0); + opserv_define_func("ADDALERT SILENT", NULL, 900, 0, 0); opserv_define_func("ADDALERT GLINE", NULL, 900, 0, 0); + opserv_define_func("ADDALERT SHUN", NULL, 900, 0, 0); + opserv_define_func("ADDALERT TRACK", NULL, 900, 0, 0); opserv_define_func("ADDALERT KILL", NULL, 900, 0, 0); opserv_define_func("ADDBAD", cmd_addbad, 800, 0, 2); opserv_define_func("ADDEXEMPT", cmd_addexempt, 800, 0, 2); @@ -4314,6 +4859,12 @@ init_opserv(const char *nick) opserv_define_func("GTRACE", cmd_gtrace, 100, 0, 3); opserv_define_func("GTRACE COUNT", NULL, 0, 0, 0); opserv_define_func("GTRACE PRINT", NULL, 0, 0, 0); + opserv_define_func("SBLOCK", cmd_sblock, 100, 0, 2); + opserv_define_func("SHUN", cmd_shun, 600, 0, 4); + opserv_define_func("SSYNC", cmd_ssync, 600, 0, 0); + opserv_define_func("STRACE", cmd_strace, 100, 0, 3); + opserv_define_func("STRACE COUNT", NULL, 0, 0, 0); + opserv_define_func("STRACE PRINT", NULL, 0, 0, 0); opserv_define_func("INVITE", cmd_invite, 100, 2, 0); opserv_define_func("INVITEME", cmd_inviteme, 100, 0, 0); opserv_define_func("JOIN", cmd_join, 601, 0, 2); @@ -4334,8 +4885,10 @@ init_opserv(const char *nick) opserv_define_func("RAW", cmd_raw, 999, 0, 2); opserv_define_func("RECONNECT", cmd_reconnect, 900, 0, 0); opserv_define_func("REFRESHG", cmd_refreshg, 600, 0, 0); + opserv_define_func("REFRESHS", cmd_refreshs, 600, 0, 0); opserv_define_func("REHASH", cmd_rehash, 900, 0, 0); opserv_define_func("REOPEN", cmd_reopen, 900, 0, 0); + opserv_define_func("RESETMAX", cmd_resetmax, 900, 0, 0); opserv_define_func("RESERVE", cmd_reserve, 800, 0, 5); opserv_define_func("RESTART", cmd_restart, 900, 0, 2); opserv_define_func("SET", cmd_set, 900, 0, 3); @@ -4344,6 +4897,7 @@ init_opserv(const char *nick) opserv_define_func("STATS BAD", cmd_stats_bad, 0, 0, 0); opserv_define_func("STATS GAGS", cmd_stats_gags, 0, 0, 0); opserv_define_func("STATS GLINES", cmd_stats_glines, 0, 0, 0); + opserv_define_func("STATS SHUNS", cmd_stats_shuns, 0, 0, 0); opserv_define_func("STATS LINKS", cmd_stats_links, 0, 0, 0); opserv_define_func("STATS MAX", cmd_stats_max, 0, 0, 0); opserv_define_func("STATS NETWORK", cmd_stats_network, 0, 0, 0); @@ -4362,12 +4916,15 @@ init_opserv(const char *nick) opserv_define_func("TRACE COUNT", NULL, 0, 0, 0); opserv_define_func("TRACE DOMAINS", NULL, 0, 0, 0); opserv_define_func("TRACE GLINE", NULL, 600, 0, 0); + opserv_define_func("TRACE SHUN", NULL, 600, 0, 0); opserv_define_func("TRACE GAG", NULL, 600, 0, 0); opserv_define_func("TRACE KILL", NULL, 600, 0, 0); opserv_define_func("UNBAN", cmd_unban, 100, 2, 2); opserv_define_func("UNGAG", cmd_ungag, 600, 0, 2); opserv_define_func("UNGLINE", cmd_ungline, 600, 0, 2); modcmd_register(opserv_module, "GTRACE UNGLINE", NULL, 0, 0, "template", "ungline", NULL); + opserv_define_func("UNSHUN", cmd_unshun, 600, 0, 2); + modcmd_register(opserv_module, "GTRACE UNSHUN", NULL, 0, 0, "template", "unshun", NULL); opserv_define_func("UNJUPE", cmd_unjupe, 900, 0, 2); opserv_define_func("UNRESERVE", cmd_unreserve, 800, 0, 2); /* opserv_define_func("UNWARN", cmd_unwarn, 800, 0, 0); */