X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fopserv.c;h=d358d57637b4c59f10a51e819e99ee32115d2e6c;hb=52ad451848654edc3f544e7e52eb773b156da9ff;hp=a5d8c4ee53463a94faee8ce3e9cde5263dd170c6;hpb=84cd7ed43a9dec662b1e3fed5850fe85a7f8a452;p=srvx.git diff --git a/src/opserv.c b/src/opserv.c index a5d8c4e..d358d57 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -22,6 +22,7 @@ #include "gline.h" #include "global.h" #include "nickserv.h" +#include "chanserv.h" #include "modcmd.h" #include "opserv.h" #include "timeq.h" @@ -72,14 +73,20 @@ #define KEY_EXPIRES "expires" #define KEY_STAFF_AUTH_CHANNEL "staff_auth_channel" #define KEY_STAFF_AUTH_CHANNEL_MODES "staff_auth_channel_modes" +#define KEY_STAFF_AUTH_FORCE_OPS "staff_auth_force_opers" #define KEY_CLONE_GLINE_DURATION "clone_gline_duration" #define KEY_BLOCK_GLINE_DURATION "block_gline_duration" #define KEY_ISSUER "issuer" #define KEY_ISSUED "issued" - -#define IDENT_FORMAT "%s [%s@%s/%s]" -#define IDENT_DATA(user) user->nick, user->ident, user->hostname, inet_ntoa(user->ip) -#define MAX_CHANNELS_WHOIS 50 +#define KEY_DEVNULL_CLASSES "classes" +#define KEY_DEVNULL_NAME "class" +#define KEY_DEVNULL_MODE "modes" +#define KEY_DEVNULL_MAXCHAN "chanlimit" +#define KEY_DEVNULL_MAXSENDQ "sendq" + +#define IDENT_FORMAT "%s [%s@%s/%s]" +#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." #define OSMSG_KILL_REQUESTED "Kill requested by %s." @@ -91,6 +98,9 @@ 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_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_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." }, @@ -113,17 +123,19 @@ static const struct message_entry msgtab[] = { { "OSMSG_OP_DONE", "Opped the requested lusers." }, { "OSMSG_OPALL_DONE", "Opped 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_NICK", "Nick : %s" }, + { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, + { "OSMSG_WHOIS_FAKEHOST", "Fakehost : %s" }, + { "OSMSG_WHOIS_FAKEIDENT", "Fakeident: %s" }, + { "OSMSG_WHOIS_FAKEIDENTHOST", "Fakehost : %s@%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." }, @@ -151,6 +163,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_BADWORD_LIST", "Bad words: %s" }, { "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" }, { "OSMSG_GLINE_COUNT", "There are %d glines active on the network." }, + { "OSMSG_NO_GLINE", "$b%s$b is not a known G-line." }, { "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" }, { "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" }, { "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" }, @@ -195,7 +208,9 @@ static const struct message_entry msgtab[] = { { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" }, { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" }, { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." }, - { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" }, + { "OSMSG_GTRACE_FORMAT", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, expires %5$s, lifetime %7$s): %6$s" }, + { "OSMSG_GTRACE_FOREVER", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, never expires, lifetime %7$s): %6$s" }, + { "OSMSG_GTRACE_EXPIRED", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, expired %5$s ago, lifetime %7$s): %6$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." }, @@ -247,6 +262,45 @@ 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_FORCEKICK_LOCAL", "You cannot kick $b%s$b forcefully." }, + { "OSMSG_SVSNONICK", "$b%s$b is not a valid nick." }, + { "OSMSG_SVSNICKUSED", "$b%s$b is an already used nickname." }, + { "OSMSG_SVSNICK", "You have renamed $b%s$b to $b%s$b." }, + { "OSMSG_SVSJOIN", "$b%s$b joined $b%s$b." }, + { "OSMSG_SVSMODE", "You have set mode $b%s$b for $b%s$b." }, + { "OSMSG_SIMUL", "You have simuled $b%s$b: %s" }, + { "OSMSG_DEVNULL_USER" , "[%s] %s %s" }, + { "OSMSG_DEVNULL_MATCH" , "%d Users found." }, + { "OSMSG_DEVNULL_CLASS" , "%s is not a valid DevNull class." }, + { "OSMSG_DEVNULL_ADDED", "Added %s to DevNull list (class: %s)" }, + { "OSMSG_DEVNULL_DELETED", "Deleted %s from DevNull list (class: %s)" }, + { "OSMSG_DEVNULL_NOTADDED", "User %s is not listed on DevNull list." }, + { "OSMSG_DEVNULL_ACTION", "Unrecognized trace action $b%s$b" }, + { "OSMSG_DEVNULL_FOUND", "DevNull Class %s is already existing." }, + { "OSMSG_DEVNULL_NOTFOUND", "can't find DevNull class %s." }, + { "OSMSG_DEVNULL_ADDED", "DevNull Class %s added." }, + { "OSMSG_DEVNULL_REMOVED", "DevNull Class %s removed." }, + { "OSMSG_DEVNULL_SET", "Settings for DevNull Class %s" }, + { "OSMSG_DEVNULL_SET_A", "ChanLimit: %s" }, + { "OSMSG_DEVNULL_SET_A_i", "ChanLimit: %i" }, + { "OSMSG_DEVNULL_SET_B", "UnlimitTarget: %s" }, + { "OSMSG_DEVNULL_SET_C", "Flood: %s" }, + { "OSMSG_DEVNULL_SET_E", "ChanHide: %s" }, + { "OSMSG_DEVNULL_SET_F", "IdleHide: %s" }, + { "OSMSG_DEVNULL_SET_G", "ChServMode: %s" }, + { "OSMSG_DEVNULL_SET_H", "XtraOpMode: %s" }, + { "OSMSG_DEVNULL_SET_I", "NetServMode: %s" }, + { "OSMSG_DEVNULL_SET_J", "SeeIdle: %s" }, + { "OSMSG_DEVNULL_SET_K", "ForceIdleHide: %s" }, + { "OSMSG_DEVNULL_SET_L", "OverrideCC: %s" }, + { "OSMSG_DEVNULL_SET_M", "OverrideNoAmsg: %s" }, + { "OSMSG_DEVNULL_SET_N", "MaxSendQ: %s" }, + { "OSMSG_DEVNULL_SET_N_i", "MaxSendQ: %i" }, + { "OSMSG_DEVNULL_SET_OPME", "OpMe: %s" }, + { "OSMSG_DEVNULL_SET_DONE", "Done." }, + { "OSMSG_DEVNULL_RENAMED", "Devnull class %s renamed to %s" }, + { "OSMSG_DEVNULL_SET_INVALID", "Invalid Option for setting %s" }, { NULL, NULL } }; @@ -261,19 +315,24 @@ static dict_t opserv_reserved_nick_dict; /* data is struct userNode* */ static struct string_list *opserv_bad_words; static dict_t opserv_exempt_channels; /* data is not used */ static dict_t opserv_trusted_hosts; /* data is struct trusted_host* */ +static dict_t opserv_devnull_classes; /* data is struct devnull_class* */ static dict_t opserv_hostinfo_dict; /* data is struct opserv_hostinfo* */ static dict_t opserv_user_alerts; /* data is struct opserv_user_alert* */ static dict_t opserv_nick_based_alerts; /* data is struct opserv_user_alert* */ static dict_t opserv_channel_alerts; /* data is struct opserv_user_alert* */ +static dict_t opserv_account_alerts; /* data is struct opserv_user_alert* */ static struct module *opserv_module; static struct log_type *OS_LOG; static unsigned int new_user_flood; +const char *devnull_modes = DEVNULL_MODES; static char *level_strings[1001]; +static char devnull_inverse_modes[256]; static struct { struct chanNode *debug_channel; struct chanNode *alert_channel; struct chanNode *staff_auth_channel; + int staff_auth_force; struct policer_params *join_policer_params; struct policer new_user_policer; unsigned long untrusted_max; @@ -289,15 +348,15 @@ struct trusted_host { char *issuer; char *reason; unsigned long limit; - time_t issued; - time_t expires; + unsigned long issued; + unsigned long expires; }; struct gag_entry { char *mask; char *owner; char *reason; - time_t expires; + unsigned long expires; struct gag_entry *next; }; @@ -316,16 +375,20 @@ opserv_free_hostinfo(void *data) free(ohi); } +#define DISCRIM_MAX_CHANS 20 + 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; + struct chanNode *channels[DISCRIM_MAX_CHANS]; + unsigned int channel_count; + char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *notice_target, *accountmask; + irc_in_addr_t ip_mask; + unsigned long limit; + unsigned long min_ts, max_ts; unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels; - unsigned int match_opers : 1, option_log : 1; - unsigned int chan_req_modes : 2, chan_no_modes : 2; + unsigned char ip_mask_bits; + unsigned int match_opers : 1, match_trusted : 1, option_log : 1; + unsigned int chan_req_modes[DISCRIM_MAX_CHANS], chan_no_modes[DISCRIM_MAX_CHANS]; int authed : 2, info_space : 2; - time_t min_ts, max_ts; } *discrim_t; struct discrim_and_source { @@ -358,8 +421,9 @@ static void opserv_free_user_alert(void *data) { struct opserv_user_alert *alert = data; - if (alert->discrim->channel) - UnlockChannel(alert->discrim->channel); + unsigned int i; + for(i = 0; i < alert->discrim->channel_count; i++) + UnlockChannel(alert->discrim->channels[i]); free(alert->owner); free(alert->text_discrim); free(alert->split_discrim); @@ -368,8 +432,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) +#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 /* 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. @@ -406,8 +477,8 @@ static MODCMD_FUNC(cmd_ban) else if ((victim = GetUserH(argv[1]))) change.args[0].u.hostmask = generate_hostmask(victim, 0); else { - reply("OSMSG_INVALID_IRCMASK", argv[1]); - return 0; + reply("OSMSG_INVALID_IRCMASK", argv[1]); + return 0; } modcmd_chanmode_announce(&change); reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name); @@ -421,11 +492,15 @@ static MODCMD_FUNC(cmd_chaninfo) const char *fmt; struct banNode *ban; struct modeNode *moden; + struct modeNode **members; + time_t feh; unsigned int n; + int show_oplevels; reply("OSMSG_CHANINFO_HEADER", channel->name); fmt = user_find_message(user, "OSMSG_CHANINFO_TIMESTAMP"); - strftime(buffer, sizeof(buffer), fmt, gmtime(&channel->timestamp)); + feh = channel->timestamp; + strftime(buffer, sizeof(buffer), fmt, gmtime(&feh)); send_message_type(4, user, cmd->parent->bot, "%s", buffer); irc_make_chanmode(channel, buffer); if (channel->bad_channel) @@ -434,20 +509,22 @@ static MODCMD_FUNC(cmd_chaninfo) reply("OSMSG_CHANINFO_MODES", buffer); if (channel->topic_time) { fmt = user_find_message(user, "OSMSG_CHANINFO_TOPIC"); - strftime(buffer, sizeof(buffer), fmt, gmtime(&channel->topic_time)); + feh = channel->topic_time; + strftime(buffer, sizeof(buffer), fmt, gmtime(&feh)); send_message_type(4, user, cmd->parent->bot, buffer, channel->topic_nick, channel->topic); } else { - irc_fetchtopic(cmd->parent->bot, channel->name); - reply("OSMSG_CHANINFO_TOPIC_UNKNOWN"); + irc_fetchtopic(cmd->parent->bot, channel->name); + reply("OSMSG_CHANINFO_TOPIC_UNKNOWN"); } if (channel->banlist.used) { - reply("OSMSG_CHANINFO_BAN_COUNT", channel->banlist.used); + reply("OSMSG_CHANINFO_BAN_COUNT", channel->banlist.used); fmt = user_find_message(user, "OSMSG_CHANINFO_BAN"); - for (n = 0; n < channel->banlist.used; n++) { - ban = channel->banlist.list[n]; - strftime(buffer, sizeof(buffer), fmt, localtime(&ban->set)); - send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who); - } + for (n = 0; n < channel->banlist.used; n++) { + ban = channel->banlist.list[n]; + feh = ban->set; + strftime(buffer, sizeof(buffer), fmt, localtime(&feh)); + send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who); + } } if ((argc < 2) && (channel->members.used >= 50)) { /* early out unless they ask for users */ @@ -455,31 +532,40 @@ static MODCMD_FUNC(cmd_chaninfo) return 1; } reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used); + + /* Create and sort the members array. */ + members = alloca(channel->members.used * sizeof(members[0])); + for (n=0; nmembers.used; n++) + members[n] = channel->members.list[n]; + qsort(members, channel->members.used, sizeof(members[0]), modeNode_sort); + + /* Display the array. */ + show_oplevels = (channel->members.used != 0) + && (members[0]->modes & MODE_CHANOP) + && (members[0]->oplevel < MAXOPLEVEL); 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); - } - for (n=0; nmembers.used; n++) { - moden = channel->members.list[n]; - if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == MODE_VOICE) + moden = members[n]; + if (moden->modes & MODE_CHANOP) { + if (show_oplevels) + 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); + } else if (moden->modes & MODE_VOICE) 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]; - if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == 0) + else send_message_type(4, user, cmd->parent->bot, " %s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); } + return 1; } -static MODCMD_FUNC(cmd_warn) +static MODCMD_FUNC(cmd_warn) { char *reason, *message; if (!IsChannelName(argv[1])) { - reply("OSMSG_NEED_CHANNEL", argv[0]); - return 0; + reply("OSMSG_NEED_CHANNEL", argv[0]); + return 0; } reason = dict_find(opserv_chan_warn, argv[1], NULL); if (reason) { @@ -504,7 +590,7 @@ static MODCMD_FUNC(cmd_unwarn) { if ((argc < 2) || !IsChannelName(argv[1])) { reply("OSMSG_NEED_CHANNEL", argv[0]); - return 0; + return 0; } if (!dict_remove(opserv_chan_warn, argv[1])) { reply("OSMSG_WARN_NOEXIST", argv[1]); @@ -537,11 +623,11 @@ static MODCMD_FUNC(cmd_clearmodes) struct mod_chanmode change; if (!channel->modes) { - reply("OSMSG_NO_CHANNEL_MODES", channel->name); + reply("OSMSG_NO_CHANNEL_MODES", channel->name); return 0; } mod_chanmode_init(&change); - change.modes_clear = channel->modes; + change.modes_clear = channel->modes & ~MODE_REGISTERED; modcmd_chanmode_announce(&change); reply("OSMSG_CLEARMODES_DONE", channel->name); return 1; @@ -556,7 +642,7 @@ static MODCMD_FUNC(cmd_deop) for (arg = 1, count = 0; arg < argc; ++arg) { struct userNode *victim = GetUserH(argv[arg]); struct modeNode *mn; - if (!victim || IsService(victim) + if (!victim || IsService(victim) || !(mn = GetUserMode(channel, victim)) || !(mn->modes & MODE_CHANOP)) continue; @@ -579,8 +665,8 @@ static MODCMD_FUNC(cmd_deopall) change = mod_chanmode_alloc(channel->members.used); for (ii = count = 0; ii < channel->members.used; ++ii) { - struct modeNode *mn = channel->members.list[ii]; - if (IsService(mn->user) || !(mn->modes & MODE_CHANOP)) + struct modeNode *mn = channel->members.list[ii]; + if (IsService(mn->user) || !(mn->modes & MODE_CHANOP)) continue; change->args[count].mode = MODE_REMOVE | MODE_CHANOP; change->args[count++].u.member = mn; @@ -599,9 +685,9 @@ static MODCMD_FUNC(cmd_rehash) extern char *services_config; if (conf_read(services_config)) - reply("OSMSG_REHASH_COMPLETE"); + reply("OSMSG_REHASH_COMPLETE"); else - reply("OSMSG_REHASH_FAILED"); + reply("OSMSG_REHASH_FAILED"); return 1; } @@ -686,14 +772,14 @@ static MODCMD_FUNC(cmd_jump) target = unsplit_string(argv+1, argc-1, NULL); if (!strcmp(cManager.uplink->name, target)) { - reply("OSMSG_CURRENT_UPLINK", cManager.uplink->name); - return 0; + reply("OSMSG_CURRENT_UPLINK", cManager.uplink->name); + return 0; } uplink = uplink_find(target); if (!uplink) { - reply("OSMSG_INVALID_UPLINK", target); - return 0; + reply("OSMSG_INVALID_UPLINK", target); + return 0; } if (uplink->flags & UPLINK_UNAVAILABLE) { reply("OSMSG_UPLINK_DISABLED", uplink->name); @@ -746,15 +832,14 @@ static MODCMD_FUNC(cmd_restart) static struct gline * opserv_block(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, "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, now, 0, 1); } static MODCMD_FUNC(cmd_block) @@ -762,18 +847,48 @@ static MODCMD_FUNC(cmd_block) struct userNode *target; struct gline *gline; char *reason; + unsigned long duration = 0; + unsigned int offset = 2; + unsigned int nn; + struct svccmd *gline_cmd; target = GetUserH(argv[1]); if (!target) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; } if (IsService(target)) { - reply("MSG_SERVICE_IMMUNE", target->nick); - return 0; + reply("MSG_SERVICE_IMMUNE", target->nick); + return 0; + } + if (dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)) { + reply("OSMSG_BLOCK_TRUSTED", target->nick); + return 0; + } + + for(nn = 0; nn < curr_opers.used; nn++) { + if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0) { + reply("OSMSG_BLOCK_OPER", target->nick, irc_ntoa(&target->ip), curr_opers.list[nn]->nick); + return 0; + } + } + + if(argc > 2 && (duration = ParseInterval(argv[2]))) { + offset = 3; + } + if(duration && duration != opserv_conf.block_gline_duration) { + /* We require more access when the duration is not the default block duration. */ + gline_cmd = dict_find(cmd->parent->commands, "gline", NULL); + if(!gline_cmd) + { + reply("OSMSG_NO_GLINE_CMD"); + return 0; + } + if(!svccmd_can_invoke(user, cmd->parent->bot, gline_cmd, channel, SVCCMD_NOISY)) + return 0; } - reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL; - gline = opserv_block(target, user->handle_info->handle, reason, 0); + reason = (argc > offset) ? unsplit_string(argv+offset, argc-offset, NULL) : NULL; + gline = opserv_block(target, user->handle_info->handle, reason, duration); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -786,8 +901,8 @@ static MODCMD_FUNC(cmd_gline) reason = unsplit_string(argv+3, argc-3, NULL); if (!is_gline(argv[1]) && !IsChannelName(argv[1]) && (argv[1][0] != '&')) { - reply("MSG_INVALID_GLINE", argv[1]); - return 0; + reply("MSG_INVALID_GLINE", argv[1]); + return 0; } if (!argv[1][strspn(argv[1], "#&*?@.")] && (strlen(argv[1]) < 10)) { reply("OSMSG_STUPID_GLINE", argv[1]); @@ -798,7 +913,7 @@ static MODCMD_FUNC(cmd_gline) reply("MSG_INVALID_DURATION", argv[2]); return 0; } - gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1); + gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 0, 1); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -849,24 +964,24 @@ opserv_ison(struct userNode *tell, struct userNode *target, const char *message) if (!maxlen) maxlen = MAX_LINE_SIZE; for (n=count=0; nchannels.used; n++) { - mn = target->channels.list[n]; - here_len = strlen(mn->channel->name); - if ((count + here_len + 4) > maxlen) { - buff[count] = 0; + mn = target->channels.list[n]; + here_len = strlen(mn->channel->name); + if ((count + here_len + 4) > maxlen) { + buff[count] = 0; send_message(tell, opserv, message, buff); - count = 0; - } - if (mn->modes & MODE_CHANOP) + count = 0; + } + if (mn->modes & MODE_CHANOP) buff[count++] = '@'; - if (mn->modes & MODE_VOICE) + if (mn->modes & MODE_VOICE) buff[count++] = '+'; - memcpy(buff+count, mn->channel->name, here_len); - count += here_len; - buff[count++] = ' '; + memcpy(buff+count, mn->channel->name, here_len); + count += here_len; + buff[count++] = ' '; } if (count) { - buff[count] = 0; - send_message(tell, opserv, message, buff); + buff[count] = 0; + send_message(tell, opserv, message, buff); } } @@ -875,17 +990,17 @@ static MODCMD_FUNC(cmd_inviteme) struct userNode *target; if (argc < 2) { - target = user; + target = user; } else { - target = GetUserH(argv[1]); - if (!target) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; - } + target = GetUserH(argv[1]); + if (!target) { + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; + } } if (opserv_conf.debug_channel == NULL) { - reply("OSMSG_NO_DEBUG_CHANNEL"); - return 0; + reply("OSMSG_NO_DEBUG_CHANNEL"); + return 0; } if (GetUserMode(opserv_conf.debug_channel, user)) { reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name); @@ -893,7 +1008,7 @@ static MODCMD_FUNC(cmd_inviteme) } irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel); if (target != user) - reply("OSMSG_INVITE_DONE", target->nick, opserv_conf.debug_channel->name); + reply("OSMSG_INVITE_DONE", target->nick, opserv_conf.debug_channel->name); return 1; } @@ -911,10 +1026,13 @@ static MODCMD_FUNC(cmd_join) { struct userNode *bot = cmd->parent->bot; - if (!IsChannelName(argv[1])) { - reply("MSG_NOT_CHANNEL_NAME"); - return 0; - } else if (!(channel = GetChannel(argv[1]))) { + if (!channel) { + if((argc < 2) || !IsChannelName(argv[1])) + { + reply("MSG_NOT_CHANNEL_NAME"); + return 0; + } + channel = AddChannel(argv[1], now, NULL, NULL); AddChannelUser(bot, channel)->modes |= MODE_CHANOP; } else if (GetUserMode(channel, bot)) { @@ -928,6 +1046,7 @@ 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; @@ -939,24 +1058,56 @@ static MODCMD_FUNC(cmd_kick) char *reason; if (argc < 3) { - reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); - sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); + reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); + sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); } else { - reason = unsplit_string(argv+2, argc-2, NULL); + reason = unsplit_string(argv+2, argc-2, NULL); } target = GetUserH(argv[1]); if (!target) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; } if (!GetUserMode(channel, target)) { - reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); - return 0; + reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); + return 0; + } + if (IsService(target)) { + reply("MSG_SERVICE_IMMUNE", target->nick); + return 0; } KickChannelUser(target, channel, cmd->parent->bot, reason); return 1; } +static MODCMD_FUNC(cmd_forcekick) +{ + struct userNode *target; + char *reason; + + if (argc < 3) { + reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); + sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); + } else { + reason = unsplit_string(argv+2, argc-2, NULL); + } + target = GetUserH(argv[1]); + if (!target) { + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; + } + if (!GetUserMode(channel, target)) { + reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); + return 0; + } + if (IsLocal(target)) { + reply("OSMSG_FORCEKICK_LOCAL", target->nick); + return 0; + } + irc_kick(cmd->parent->bot, target, channel, reason); + return 1; +} + static MODCMD_FUNC(cmd_kickall) { unsigned int limit, n, inchan; @@ -974,20 +1125,20 @@ static MODCMD_FUNC(cmd_kickall) modcmd_chanmode_announce(&change); } if (argc < 2) { - reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); - sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); + reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); + sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); } else { - reason = unsplit_string(argv+1, argc-1, NULL); + reason = unsplit_string(argv+1, argc-1, NULL); } limit = user->handle_info->opserv_level; for (n=channel->members.used; n>0;) { - mn = channel->members.list[--n]; - if (IsService(mn->user) - || (mn->user->handle_info - && (mn->user->handle_info->opserv_level >= limit))) { - continue; - } - KickChannelUser(mn->user, channel, bot, reason); + mn = channel->members.list[--n]; + if (IsService(mn->user) + || (mn->user->handle_info + && (mn->user->handle_info->opserv_level >= limit))) { + continue; + } + KickChannelUser(mn->user, channel, bot, reason); } if (!inchan) DelChannelUser(bot, channel, "My work here is done", 0); @@ -1003,19 +1154,19 @@ static MODCMD_FUNC(cmd_kickban) char *mask; if (argc == 2) { - reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); - sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); + reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); + sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); } else { - reason = unsplit_string(argv+2, argc-2, NULL); + reason = unsplit_string(argv+2, argc-2, NULL); } target = GetUserH(argv[1]); if (!target) { - reply("MSG_NICK_UNKNOWN", argv[1]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[1]); + return 0; } if (!GetUserMode(channel, target)) { - reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); - return 0; + reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name); + return 0; } mod_chanmode_init(&change); change.argc = 1; @@ -1051,51 +1202,45 @@ static MODCMD_FUNC(cmd_kickbanall) modcmd_chanmode_announce(change); mod_chanmode_free(change); if (argc < 2) { - reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); - sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); + reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); + sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); } else { - reason = unsplit_string(argv+1, argc-1, NULL); + reason = unsplit_string(argv+1, argc-1, NULL); } /* now kick them */ limit = user->handle_info->opserv_level; for (n=channel->members.used; n>0; ) { - mn = channel->members.list[--n]; - if (IsService(mn->user) - || (mn->user->handle_info - && (mn->user->handle_info->opserv_level >= limit))) { - continue; - } - KickChannelUser(mn->user, channel, bot, reason); + mn = channel->members.list[--n]; + if (IsService(mn->user) + || (mn->user->handle_info + && (mn->user->handle_info->opserv_level >= limit))) { + continue; + } + KickChannelUser(mn->user, channel, bot, reason); } if (!inchan) DelChannelUser(bot, channel, "My work here is done", 0); reply("OSMSG_KICKALL_DONE", channel->name); - return 1; + return 1; } 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; } static MODCMD_FUNC(cmd_mode) { - if (!modcmd_chanmode(argv+1, argc-1, MCP_ALLOW_OVB|MCP_KEY_FREE|MC_ANNOUNCE)) { + if (!modcmd_chanmode(argv+1, argc-1, MCP_ALLOW_OVB|MCP_KEY_FREE|MC_ANNOUNCE|MCP_OPERMODE)) { reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL)); return 0; } @@ -1137,15 +1282,15 @@ static MODCMD_FUNC(cmd_opall) change = mod_chanmode_alloc(channel->members.used); for (ii = count = 0; ii < channel->members.used; ++ii) { - struct modeNode *mn = channel->members.list[ii]; - if (mn->modes & MODE_CHANOP) + struct modeNode *mn = channel->members.list[ii]; + if (mn->modes & MODE_CHANOP) continue; change->args[count].mode = MODE_CHANOP; change->args[count++].u.member = mn; } if (count) { change->argc = count; - modcmd_chanmode_announce(change); + modcmd_chanmode_announce(change); } mod_chanmode_free(change); reply("OSMSG_OPALL_DONE", channel->name); @@ -1170,25 +1315,20 @@ static MODCMD_FUNC(cmd_whois) } reply("OSMSG_WHOIS_NICK", target->nick); reply("OSMSG_WHOIS_HOST", target->ident, target->hostname); - if (IsFakeHost(target)) + if (IsFakeIdent(target) && IsFakeHost(target)) + reply("OSMSG_WHOIS_FAKEIDENTHOST", target->fakeident, target->fakehost); + else if (IsFakeIdent(target)) + reply("OSMSG_WHOIS_FAKEIDENT", target->fakeident); + else if (IsFakeHost(target)) reply("OSMSG_WHOIS_FAKEHOST", target->fakehost); - reply("OSMSG_WHOIS_IP", inet_ntoa(target->ip)); + reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip)); 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 (IsHelperIrcu(target)) buffer[bpos++] = 'h'; - if (IsService(target)) buffer[bpos++] = 'k'; - if (IsDeaf(target)) buffer[bpos++] = 'd'; - if (IsHiddenHost(target)) buffer[bpos++] = 'x'; if (IsGagged(target)) buffer_cat(" (gagged)"); - if (IsRegistering(target)) buffer_cat(" (registered account)"); - buffer[bpos] = 0; - if (bpos > 0) + if (IsRegistering(target)) buffer_cat(" (registered account)"); + buffer[bpos] = 0; + if (bpos > 0) reply("OSMSG_WHOIS_MODES", buffer); } reply("OSMSG_WHOIS_INFO", target->info); @@ -1199,10 +1339,10 @@ static MODCMD_FUNC(cmd_whois) reply("OSMSG_WHOIS_ACCOUNT", (target->handle_info ? target->handle_info->handle : "Not authenticated")); intervalString(buffer, now - target->timestamp, user->handle_info); reply("OSMSG_WHOIS_NICK_AGE", buffer); - if (target->channels.used <= MAX_CHANNELS_WHOIS) - opserv_ison(user, target, "OSMSG_WHOIS_CHANNELS"); + if (target->channels.used <= MAX_CHANNELS_WHOIS || HANDLE_FLAGGED(user->handle_info, BOT)) + opserv_ison(user, target, "OSMSG_WHOIS_CHANNELS"); else - reply("OSMSG_WHOIS_HIDECHANS"); + reply("OSMSG_WHOIS_HIDECHANS"); return 1; } @@ -1225,15 +1365,15 @@ static MODCMD_FUNC(cmd_voiceall) change = mod_chanmode_alloc(channel->members.used); for (ii = count = 0; ii < channel->members.used; ++ii) { - struct modeNode *mn = channel->members.list[ii]; - if (mn->modes & (MODE_CHANOP|MODE_VOICE)) + struct modeNode *mn = channel->members.list[ii]; + if (mn->modes & (MODE_CHANOP|MODE_VOICE)) continue; change->args[count].mode = MODE_VOICE; change->args[count++].u.member = mn; } if (count) { change->argc = count; - modcmd_chanmode_announce(change); + modcmd_chanmode_announce(change); } mod_chanmode_free(change); reply("OSMSG_CHANNEL_VOICED", channel->name); @@ -1247,15 +1387,15 @@ static MODCMD_FUNC(cmd_devoiceall) change = mod_chanmode_alloc(channel->members.used); for (ii = count = 0; ii < channel->members.used; ++ii) { - struct modeNode *mn = channel->members.list[ii]; - if (!(mn->modes & MODE_VOICE)) + struct modeNode *mn = channel->members.list[ii]; + if (!(mn->modes & MODE_VOICE)) continue; change->args[count].mode = MODE_REMOVE | MODE_VOICE; change->args[count++].u.member = mn; } if (count) { change->argc = count; - modcmd_chanmode_announce(change); + modcmd_chanmode_announce(change); } mod_chanmode_free(change); reply("OSMSG_CHANNEL_DEVOICED", channel->name); @@ -1299,11 +1439,6 @@ static MODCMD_FUNC(cmd_stats_bad) { return 1; } -static MODCMD_FUNC(cmd_stats_glines) { - reply("OSMSG_GLINE_COUNT", gline_count()); - return 1; -} - static void trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) { unsigned int nn, pos; @@ -1332,7 +1467,9 @@ static MODCMD_FUNC(cmd_stats_links) { static MODCMD_FUNC(cmd_stats_max) { - reply("OSMSG_MAX_CLIENTS", max_clients, asctime(localtime(&max_clients_time))); + time_t feh; + feh = max_clients_time; + reply("OSMSG_MAX_CLIENTS", max_clients, asctime(localtime(&feh))); return 1; } @@ -1399,7 +1536,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; @@ -1480,11 +1617,15 @@ static MODCMD_FUNC(cmd_stats_uplink) { } static MODCMD_FUNC(cmd_stats_uptime) { + extern int lines_processed; + extern unsigned long 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) @@ -1496,12 +1637,27 @@ static MODCMD_FUNC(cmd_stats_uptime) { clocks_per_sec = CLOCKS_PER_SEC; } } - intervalString(uptime, time(NULL)-boot_time, user->handle_info); times(&buf); - reply("OSMSG_UPTIME_STATS", - uptime, lines_processed, - buf.tms_utime/(double)clocks_per_sec, - buf.tms_stime/(double)clocks_per_sec); + user_time = buf.tms_utime / clocks_per_sec; + kernel_time = buf.tms_stime / clocks_per_sec; +#elif defined(HAVE_GETPROCESSTIMES) + FILETIME times[4]; + LARGE_INTEGER li[2]; + + GetProcessTimes(GetCurrentProcess(), ×[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); return 1; } @@ -1530,7 +1686,7 @@ static MODCMD_FUNC(cmd_stats_gags) { unsigned int nn; if (!gagList) { - reply("OSMSG_NO_GAGS"); + reply("OSMSG_NO_GAGS"); return 1; } for (nn=0, gag=gagList; gag; nn++, gag=gag->next) ; @@ -1610,10 +1766,10 @@ static MODCMD_FUNC(cmd_dump) safestrncpy(linedup, original, sizeof(linedup)); /* assume it's only valid IRC if we can parse it */ if (parse_line(linedup, 1)) { - irc_raw(original); - reply("OSMSG_LINE_DUMPED"); + irc_raw(original); + reply("OSMSG_LINE_DUMPED"); } else - reply("OSMSG_RAW_PARSE_ERROR"); + reply("OSMSG_RAW_PARSE_ERROR"); return 1; } @@ -1636,17 +1792,17 @@ opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick, { struct userNode *resv = GetUserH(nick); if (resv) { - if (IsService(resv)) { - reply("MSG_SERVICE_IMMUNE", resv->nick); - return NULL; - } - if (resv->handle_info - && resv->handle_info->opserv_level > user->handle_info->opserv_level) { - reply("OSMSG_LEVEL_TOO_LOW"); - return NULL; - } - } - if ((resv = AddClone(nick, ident, host, desc))) { + if (IsService(resv)) { + reply("MSG_SERVICE_IMMUNE", resv->nick); + return NULL; + } + if (resv->handle_info + && resv->handle_info->opserv_level > user->handle_info->opserv_level) { + reply("OSMSG_LEVEL_TOO_LOW"); + return NULL; + } + } + if ((resv = AddLocalUser(nick, ident, host, desc, "+i"))) { dict_insert(opserv_reserved_nick_dict, resv->nick, resv); } return resv; @@ -1658,11 +1814,11 @@ static MODCMD_FUNC(cmd_collide) resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL)); if (resv) { - reply("OSMSG_COLLIDED_NICK", resv->nick); - return 1; + reply("OSMSG_COLLIDED_NICK", resv->nick); + return 1; } else { reply("OSMSG_CLONE_FAILED", argv[1]); - return 0; + return 0; } } @@ -1672,12 +1828,12 @@ static MODCMD_FUNC(cmd_reserve) resv = opserv_add_reserve(cmd, user, argv[1], argv[2], argv[3], unsplit_string(argv+4, argc-4, NULL)); if (resv) { - resv->modes |= FLAGS_PERSISTENT; - reply("OSMSG_RESERVED_NICK", resv->nick); - return 1; + resv->modes |= FLAGS_PERSISTENT; + reply("OSMSG_RESERVED_NICK", resv->nick); + return 1; } else { reply("OSMSG_CLONE_FAILED", argv[1]); - return 0; + return 0; } } @@ -1703,9 +1859,9 @@ free_reserve(char *nick) static MODCMD_FUNC(cmd_unreserve) { if (free_reserve(argv[1])) - reply("OSMSG_NICK_UNRESERVED", argv[1]); + reply("OSMSG_NICK_UNRESERVED", argv[1]); else - reply("OSMSG_NOT_RESERVED", argv[1]); + reply("OSMSG_NOT_RESERVED", argv[1]); return 1; } @@ -1717,32 +1873,34 @@ opserv_part_channel(void *data) static int alert_check_user(const char *key, void *data, void *extra); -static int +static void 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)) - return 0; + return; /* 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; /* 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); @@ -1760,8 +1918,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 */ @@ -1770,19 +1930,18 @@ 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, "AUTO Excessive connections from a single host.", now, now, 0, 1); } } - - return 0; } 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 @@ -1791,9 +1950,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); } } @@ -1801,9 +1962,10 @@ int opserv_bad_channel(const char *name) { unsigned int found; + int present; - dict_find(opserv_exempt_channels, name, &found); - if (found) + dict_find(opserv_exempt_channels, name, &present); + if (present) return 0; if (gline_find(name)) @@ -1876,7 +2038,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"); @@ -1902,7 +2064,7 @@ opserv_join_check(struct modeNode *mNode) struct mod_chanmode change; mod_chanmode_init(&change); channel->join_flooded = 1; - if (opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) { + if (opserv && opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) { if (!GetUserMode(channel, opserv)) { /* If we aren't in the channel, join it. */ change.args[0].mode = MODE_CHANOP; @@ -1945,7 +2107,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; } @@ -1990,18 +2151,18 @@ 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; - DelUser(user, cmd->parent->bot, 1, "OSMSG_ILLEGAL_KILL_REASON"); + for (nn = 0; nn < chan->members.used; nn++) { + struct userNode *victim = chan->members.list[nn]->user; + DelUser(victim, cmd->parent->bot, 1, "OSMSG_ILLEGAL_KILL_REASON"); } } } @@ -2086,7 +2247,7 @@ opserv_expire_trusted_host(void *data) } static void -opserv_add_trusted_host(const char *ipaddr, unsigned int limit, const char *issuer, time_t issued, time_t expires, const char *reason) +opserv_add_trusted_host(const char *ipaddr, unsigned int limit, const char *issuer, unsigned long issued, unsigned long expires, const char *reason) { struct trusted_host *th; th = calloc(1, sizeof(*th)); @@ -2117,7 +2278,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)) { @@ -2125,7 +2286,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; } @@ -2161,7 +2322,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; } @@ -2218,92 +2379,92 @@ static MODCMD_FUNC(cmd_clone) char *userinfo; char ident[USERLEN+1]; - if (argc < 5) { - reply("MSG_MISSING_PARAMS", argv[1]); - OPSERV_SYNTAX(); - return 0; - } - if (clone) { - reply("OSMSG_CLONE_EXISTS", argv[2]); - return 0; - } - userinfo = unsplit_string(argv+4, argc-4, NULL); - for (i=0; argv[3][i] && (inick); - return 1; + return 1; } if (!clone) { - reply("MSG_NICK_UNKNOWN", argv[2]); - return 0; + reply("MSG_NICK_UNKNOWN", argv[2]); + return 0; } if (clone->uplink != self || IsService(clone)) { - reply("OSMSG_NOT_A_CLONE", clone->nick); - return 0; + reply("OSMSG_NOT_A_CLONE", clone->nick); + return 0; } if (!irccasecmp(argv[1], "REMOVE")) { - const char *reason; - if (argc > 3) { - reason = unsplit_string(argv+3, argc-3, NULL); - } else { - char *tmp; - tmp = alloca(strlen(clone->nick) + strlen(OSMSG_PART_REASON)); - sprintf(tmp, OSMSG_PART_REASON, clone->nick); - reason = tmp; - } - DelUser(clone, NULL, 1, reason); - reply("OSMSG_CLONE_REMOVED", argv[2]); - return 1; + const char *reason; + if (argc > 3) { + reason = unsplit_string(argv+3, argc-3, NULL); + } else { + char *tmp; + tmp = alloca(strlen(clone->nick) + strlen(OSMSG_PART_REASON)); + sprintf(tmp, OSMSG_PART_REASON, clone->nick); + reason = tmp; + } + DelUser(clone, NULL, 1, reason); + reply("OSMSG_CLONE_REMOVED", argv[2]); + return 1; } if (argc < 4) { - reply("MSG_MISSING_PARAMS", argv[1]); - OPSERV_SYNTAX(); - return 0; + reply("MSG_MISSING_PARAMS", argv[1]); + OPSERV_SYNTAX(); + return 0; } channel = GetChannel(argv[3]); if (!irccasecmp(argv[1], "JOIN")) { - if (!channel - && !(channel = AddChannel(argv[3], now, NULL, NULL))) { - reply("MSG_CHANNEL_UNKNOWN", argv[3]); - return 0; - } - AddChannelUser(clone, channel); - reply("OSMSG_CLONE_JOINED", clone->nick, channel->name); - return 1; + if (!channel + && !(channel = AddChannel(argv[3], now, NULL, NULL))) { + reply("MSG_CHANNEL_UNKNOWN", argv[3]); + return 0; + } + AddChannelUser(clone, channel); + reply("OSMSG_CLONE_JOINED", clone->nick, channel->name); + return 1; } if (!irccasecmp(argv[1], "PART")) { - if (!channel) { - reply("MSG_CHANNEL_UNKNOWN", argv[3]); - return 0; - } - if (!GetUserMode(channel, clone)) { - reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name); - return 0; - } - reply("OSMSG_CLONE_PARTED", clone->nick, channel->name); - DelChannelUser(clone, channel, "Leaving.", 0); - return 1; + if (!channel) { + reply("MSG_CHANNEL_UNKNOWN", argv[3]); + return 0; + } + if (!GetUserMode(channel, clone)) { + reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name); + return 0; + } + reply("OSMSG_CLONE_PARTED", clone->nick, channel->name); + DelChannelUser(clone, channel, "Leaving.", 0); + return 1; } if (!irccasecmp(argv[1], "OP")) { struct mod_chanmode change; - if (!channel) { - reply("MSG_CHANNEL_UNKNOWN", argv[3]); - return 0; - } + if (!channel) { + reply("MSG_CHANNEL_UNKNOWN", argv[3]); + return 0; + } mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; @@ -2311,21 +2472,21 @@ static MODCMD_FUNC(cmd_clone) if (!change.args[0].u.member) { reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name); return 0; - } + } modcmd_chanmode_announce(&change); - reply("OSMSG_OPS_GIVEN", channel->name, clone->nick); - return 1; + reply("OSMSG_OPS_GIVEN", channel->name, clone->nick); + return 1; } if (argc < 5) { - reply("MSG_MISSING_PARAMS", argv[1]); - OPSERV_SYNTAX(); - return 0; + reply("MSG_MISSING_PARAMS", argv[1]); + OPSERV_SYNTAX(); + return 0; } if (!irccasecmp(argv[1], "SAY")) { - char *text = unsplit_string(argv+4, argc-4, NULL); - irc_privmsg(clone, argv[3], text); - reply("OSMSG_CLONE_SAID", clone->nick, argv[3]); - return 1; + char *text = unsplit_string(argv+4, argc-4, NULL); + irc_privmsg(clone, argv[3], text); + reply("OSMSG_CLONE_SAID", clone->nick, argv[3]); + return 1; } reply("OSMSG_UNKNOWN_SUBCOMMAND", argv[1], argv[0]); return 0; @@ -2408,20 +2569,20 @@ int add_reserved(const char *key, void *data, void *extra) struct userNode *reserve; ident = database_get_data(rd->d.object, KEY_IDENT, RECDB_QSTRING); if (!ident) { - log_module(OS_LOG, LOG_ERROR, "Missing ident for reserve of %s", key); - return 0; + log_module(OS_LOG, LOG_ERROR, "Missing ident for reserve of %s", key); + return 0; } hostname = database_get_data(rd->d.object, KEY_HOSTNAME, RECDB_QSTRING); if (!hostname) { - log_module(OS_LOG, LOG_ERROR, "Missing hostname for reserve of %s", key); - return 0; + log_module(OS_LOG, LOG_ERROR, "Missing hostname for reserve of %s", key); + return 0; } desc = database_get_data(rd->d.object, KEY_DESC, RECDB_QSTRING); if (!desc) { - log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key); - return 0; + log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key); + return 0; } - if ((reserve = AddClone(key, ident, hostname, desc))) { + if ((reserve = AddLocalUser(key, ident, hostname, desc, "+i"))) { reserve->modes |= FLAGS_PERSISTENT; dict_insert(extra, reserve->nick, reserve); } @@ -2438,25 +2599,18 @@ foreach_matching_user(const char *hostmask, discrim_search_func func, void *extr if (!self->uplink) return 0; discrim = calloc(1, sizeof(*discrim)); discrim->limit = dict_size(clients); - discrim->max_level = ~0; - discrim->max_ts = now; + discrim->max_level = UINT_MAX; + discrim->max_ts = ULONG_MAX; discrim->max_channels = INT_MAX; discrim->authed = -1; discrim->info_space = -1; 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); + log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask); matched = 0; } free(discrim); @@ -2496,7 +2650,7 @@ gag_expire(void *data) } unsigned int -gag_create(const char *mask, const char *owner, const char *reason, time_t expires) +gag_create(const char *mask, const char *owner, const char *reason, unsigned long expires) { struct gag_entry *gag; @@ -2520,7 +2674,7 @@ add_gag_helper(const char *key, void *data, UNUSED_ARG(void *extra)) { struct record_data *rd = data; char *owner, *reason, *expstr; - time_t expires; + unsigned long expires; owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING); reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING); @@ -2540,8 +2694,8 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react char *name_dup; if (dict_find(opserv_user_alerts, name, NULL)) { - send_message(req, opserv, "OSMSG_ALERT_EXISTS", name); - return NULL; + send_message(req, opserv, "OSMSG_ALERT_EXISTS", name); + return NULL; } alert = malloc(sizeof(*alert)); alert->owner = strdup(req->handle_info ? req->handle_info->handle : req->nick); @@ -2561,10 +2715,17 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react alert->discrim->reason = strdup(name); alert->reaction = reaction; dict_insert(opserv_user_alerts, name_dup, alert); - if (alert->discrim->channel) + /* Stick the alert into the appropriate additional alert dict(s). + * For channel alerts, we only use channels and min_channels; + * max_channels would have to be checked on /part, which we do not + * yet do, and which seems of questionable value. + */ + if (alert->discrim->channel_count || alert->discrim->min_channels) dict_insert(opserv_channel_alerts, name_dup, alert); - else if (alert->discrim->mask_nick) + if (alert->discrim->mask_nick) dict_insert(opserv_nick_based_alerts, name_dup, alert); + if (alert->discrim->accountmask || alert->discrim->authed != -1) + dict_insert(opserv_account_alerts, name_dup, alert); return alert; } @@ -2622,7 +2783,7 @@ trusted_host_read(const char *host, void *data, UNUSED_ARG(void *extra)) { struct record_data *rd = data; const char *limit, *str, *reason, *issuer; - time_t issued, expires; + unsigned long issued, expires; if (rd->type == RECDB_QSTRING) { /* old style host by itself */ @@ -2650,6 +2811,57 @@ trusted_host_read(const char *host, void *data, UNUSED_ARG(void *extra)) return 0; } +static void +opserv_add_devnull_class(const char *name, unsigned long modes, unsigned long maxchan, unsigned long maxsendq) +{ + struct devnull_class *th; + th = calloc(1, sizeof(*th)); + if (!th) + return; + th->name = strdup(name); + th->modes = modes; + th->maxchan = maxchan; + th->maxsendq = maxsendq; + dict_insert(opserv_devnull_classes, th->name, th); +} + +static void +free_devnull_class(void *data) +{ + struct devnull_class *th = data; + free(th->name); + free(th); +} + +static int +devnull_class_read(const char *name, void *data, UNUSED_ARG(void *extra)) +{ + struct record_data *rd = data; + const char *nameb = name, *str; + unsigned long modes, maxchan, maxsendq; + unsigned int ii; + + if (rd->type == RECDB_OBJECT) { + dict_t obj = GET_RECORD_OBJECT(rd); + /* new style structure */ + nameb = database_get_data(obj, KEY_DEVNULL_NAME, RECDB_QSTRING); + str = database_get_data(obj, KEY_DEVNULL_MODE, RECDB_QSTRING); + modes = 0; + if (str) { + for (ii=0; str[ii]; ii++) + modes |= 1 << (devnull_inverse_modes[(unsigned char)str[ii]] - 1); + } + str = database_get_data(obj, KEY_DEVNULL_MAXCHAN, RECDB_QSTRING); + maxchan = str ? ParseInterval(str) : 0; + str = database_get_data(obj, KEY_DEVNULL_MAXSENDQ, RECDB_QSTRING); + maxsendq = str ? ParseInterval(str) : 0; + } else + return 0; + + opserv_add_devnull_class(nameb, modes, maxchan, maxsendq); + return 0; +} + static int opserv_saxdb_read(struct dict *conf_db) { @@ -2694,6 +2906,8 @@ opserv_saxdb_read(struct dict *conf_db) } if ((object = database_get_data(conf_db, KEY_TRUSTED_HOSTS, RECDB_OBJECT))) dict_foreach(object, trusted_host_read, opserv_trusted_hosts); + if ((object = database_get_data(conf_db, KEY_DEVNULL_CLASSES, RECDB_OBJECT))) + dict_foreach(object, devnull_class_read, opserv_devnull_classes); if ((object = database_get_data(conf_db, KEY_GAGS, RECDB_OBJECT))) dict_foreach(object, add_gag_helper, NULL); if ((object = database_get_data(conf_db, KEY_ALERTS, RECDB_OBJECT))) @@ -2751,6 +2965,28 @@ opserv_saxdb_write(struct saxdb_context *ctx) } saxdb_end_record(ctx); } + /* devnull_classes */ + if (dict_size(opserv_devnull_classes)) { + saxdb_start_record(ctx, KEY_DEVNULL_CLASSES, 1); + for (it = dict_first(opserv_devnull_classes); it; it = iter_next(it)) { + struct devnull_class *th = iter_data(it); + saxdb_start_record(ctx, iter_key(it), 0); + if (th->name) saxdb_write_string(ctx, KEY_DEVNULL_NAME, th->name); + if (th->modes) { + int ii, flen; + char flags[50]; + for (ii=flen=0; devnull_modes[ii]; ++ii) + if (th->modes & (1 << ii)) + flags[flen++] = devnull_modes[ii]; + flags[flen] = 0; + saxdb_write_string(ctx, KEY_DEVNULL_MODE, flags); + } + if (th->maxchan) saxdb_write_int(ctx, KEY_DEVNULL_MAXCHAN, th->maxchan); + if (th->maxsendq) saxdb_write_int(ctx, KEY_DEVNULL_MAXSENDQ, th->maxsendq); + saxdb_end_record(ctx); + } + saxdb_end_record(ctx); + } /* gags */ if (gagList) { struct gag_entry *gag; @@ -2817,29 +3053,29 @@ static MODCMD_FUNC(cmd_query) char *nodename; if (argc < 2) { - reply("OSMSG_OPTION_ROOT"); - conf_enum_root(query_keys_helper, user); - return 1; + reply("OSMSG_OPTION_ROOT"); + conf_enum_root(query_keys_helper, user); + return 1; } nodename = unsplit_string(argv+1, argc-1, NULL); if (!(rd = conf_get_node(nodename))) { - reply("OSMSG_UNKNOWN_OPTION", nodename); - return 0; + reply("OSMSG_UNKNOWN_OPTION", nodename); + return 0; } if (rd->type == RECDB_QSTRING) - reply("OSMSG_OPTION_IS", nodename, rd->d.qstring); + reply("OSMSG_OPTION_IS", nodename, rd->d.qstring); else if (rd->type == RECDB_STRING_LIST) { - reply("OSMSG_OPTION_LIST", nodename); - if (rd->d.slist->used) - for (i=0; id.slist->used; i++) - send_message_type(4, user, cmd->parent->bot, "$b%s$b", rd->d.slist->list[i]); - else - reply("OSMSG_OPTION_LIST_EMPTY"); + reply("OSMSG_OPTION_LIST", nodename); + if (rd->d.slist->used) + for (i=0; id.slist->used; i++) + send_message_type(4, user, cmd->parent->bot, "$b%s$b", rd->d.slist->list[i]); + else + reply("OSMSG_OPTION_LIST_EMPTY"); } else if (rd->type == RECDB_OBJECT) { - reply("OSMSG_OPTION_KEYS", nodename); - dict_foreach(rd->d.object, query_keys_helper, user); + reply("OSMSG_OPTION_KEYS", nodename); + dict_foreach(rd->d.object, query_keys_helper, user); } return 1; @@ -2855,13 +3091,13 @@ static MODCMD_FUNC(cmd_set) invent a syntax for it. -Zoot */ if (!(rd = conf_get_node(argv[1]))) { - reply("OSMSG_SET_NOT_SET", argv[1]); - return 0; + reply("OSMSG_SET_NOT_SET", argv[1]); + return 0; } if (rd->type != RECDB_QSTRING) { - reply("OSMSG_SET_BAD_TYPE", argv[1]); - return 0; + reply("OSMSG_SET_BAD_TYPE", argv[1]); + return 0; } free(rd->d.qstring); @@ -2874,7 +3110,7 @@ static MODCMD_FUNC(cmd_set) static MODCMD_FUNC(cmd_settime) { const char *srv_name_mask = "*"; - time_t new_time = now; + unsigned long new_time = now; if (argc > 1) srv_name_mask = argv[1]; @@ -2893,8 +3129,8 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in discrim = calloc(1, sizeof(*discrim)); discrim->limit = 250; - discrim->max_level = ~0; - discrim->max_ts = INT_MAX; + discrim->max_level = UINT_MAX; + discrim->max_ts = ULONG_MAX; discrim->domain_depth = 2; discrim->max_channels = INT_MAX; discrim->authed = -1; @@ -2910,106 +3146,122 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in send_message(user, opserv, "MSG_MISSING_PARAMS", argv[i]); goto fail; } - if (irccasecmp(argv[i], "mask") == 0) { - if (!is_ircmask(argv[++i])) { - send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]); - goto fail; - } - if (!split_ircmask(argv[i], + if (irccasecmp(argv[i], "mask") == 0) { + if (!is_ircmask(argv[++i])) { + send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]); + goto fail; + } + if (!split_ircmask(argv[i], &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) { - send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]); - goto fail; - } - } else if (irccasecmp(argv[i], "nick") == 0) { - discrim->mask_nick = argv[++i]; - } else if (irccasecmp(argv[i], "ident") == 0) { - 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]; - } 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]; - } else if (irccasecmp(argv[i], "account") == 0) { - if (discrim->authed == 0) { - send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED"); - goto fail; - } - discrim->accountmask = argv[++i]; - discrim->authed = 1; - } else if (irccasecmp(argv[i], "authed") == 0) { - i++; /* true_string and false_string are macros! */ - if (true_string(argv[i])) { - discrim->authed = 1; - } else if (false_string(argv[i])) { - if (discrim->accountmask) { + send_message(user, opserv, "OSMSG_INVALID_IRCMASK", argv[i]); + goto fail; + } + } else if (irccasecmp(argv[i], "nick") == 0) { + discrim->mask_nick = argv[++i]; + } else if (irccasecmp(argv[i], "ident") == 0) { + 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]; + } else if (irccasecmp(argv[i], "server") == 0) { + discrim->server = argv[++i]; + } else if (irccasecmp(argv[i], "ip") == 0) { + 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, opserv, "OSMSG_ACCOUNTMASK_AUTHED"); goto fail; } - discrim->authed = 0; - } else { - send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]); - goto fail; - } - } else if (irccasecmp(argv[i], "info_space") == 0) { - /* XXX: A hack because you can't check explicitly for a space through - * any other means */ - i++; - if (true_string(argv[i])) { - discrim->info_space = 1; - } else if (false_string(argv[i])) { - discrim->info_space = 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) { + discrim->accountmask = argv[++i]; + discrim->authed = 1; + } else if (irccasecmp(argv[i], "authed") == 0) { + i++; /* true_string and false_string are macros! */ + if (true_string(argv[i])) { + discrim->authed = 1; + } else if (false_string(argv[i])) { + if (discrim->accountmask) { + send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED"); + goto fail; + } + discrim->authed = 0; + } else { + send_message(user, opserv, "MSG_INVALID_BINARY", argv[i]); + goto fail; + } + } else if (irccasecmp(argv[i], "info_space") == 0) { + /* XXX: A hack because you can't check explicitly for a space through + * any other means */ + i++; + if (true_string(argv[i])) { + discrim->info_space = 1; + } else if (false_string(argv[i])) { + discrim->info_space = 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) { + 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 |= MODE_CHANOP | MODE_VOICE; + discrim->chan_no_modes[discrim->channel_count] |= MODE_CHANOP | MODE_VOICE; break; case '+': - discrim->chan_req_modes |= MODE_VOICE; - discrim->chan_no_modes |= MODE_CHANOP; + discrim->chan_req_modes[discrim->channel_count] |= MODE_VOICE; + discrim->chan_no_modes[discrim->channel_count] |= MODE_CHANOP; break; case '@': - discrim->chan_req_modes |= MODE_CHANOP; + 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->chan_req_modes; - if (!(discrim->channel = GetChannel(argv[i]+j))) { + 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->channel = AddChannel(argv[i]+j, now, NULL, NULL); + discrim->channels[discrim->channel_count] = AddChannel(argv[i]+j, now, NULL, NULL); } - } - LockChannel(discrim->channel); + } + 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], "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; + } else if (irccasecmp(argv[i], "notice_target") == 0 || irccasecmp(argv[i], "target") == 0) { + if (!IsChannelName(argv[i + 1])) { + send_message(user, opserv, "MSG_NOT_CHANNEL_NAME"); + goto fail; + } + discrim->notice_target = argv[++i]; } else if (irccasecmp(argv[i], "last") == 0) { discrim->min_ts = now - ParseInterval(argv[++i]); } else if ((irccasecmp(argv[i], "linked") == 0) @@ -3028,7 +3280,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in discrim->max_ts = now - (ParseInterval(cmp+1) - 1); } } else { - discrim->min_ts = now - ParseInterval(cmp+2); + discrim->min_ts = now - ParseInterval(cmp); } } else if (irccasecmp(argv[i], "access") == 0) { const char *cmp = argv[++i]; @@ -3048,11 +3300,15 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in discrim->min_level = strtoul(cmp+1, NULL, 0) + 1; } } else { - discrim->min_level = strtoul(cmp+2, NULL, 0); + discrim->min_level = strtoul(cmp, NULL, 0); + } + } else if (irccasecmp(argv[i], "abuse") == 0) { + const char *abuse_what = argv[++i]; + if (irccasecmp(abuse_what, "opers") == 0) { + discrim->match_opers = 1; + } else if (irccasecmp(abuse_what, "trusted") == 0) { + discrim->match_trusted = 1; } - } else if ((irccasecmp(argv[i], "abuse") == 0) - && (irccasecmp(argv[++i], "opers") == 0)) { - discrim->match_opers = 1; } else if (irccasecmp(argv[i], "depth") == 0) { discrim->domain_depth = strtoul(argv[++i], NULL, 0); } else if (irccasecmp(argv[i], "clones") == 0) { @@ -3064,13 +3320,13 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in } if (discrim->mask_nick && !strcmp(discrim->mask_nick, "*")) { - discrim->mask_nick = 0; + discrim->mask_nick = 0; } if (discrim->mask_ident && !strcmp(discrim->mask_ident, "*")) { discrim->mask_ident = 0; } if (discrim->mask_info && !strcmp(discrim->mask_info, "*")) { - discrim->mask_info = 0; + discrim->mask_info = 0; } if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "*.")]) { discrim->mask_host = 0; @@ -3084,7 +3340,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in static int discrim_match(discrim_t discrim, struct userNode *user) { - unsigned int access; + unsigned int level, i; if ((user->timestamp < discrim->min_ts) || (user->timestamp > discrim->max_ts) @@ -3100,21 +3356,21 @@ discrim_match(discrim_t discrim, struct userNode *user) || (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; - access = user->handle_info ? user->handle_info->opserv_level : 0; - if ((access < discrim->min_level) - || (access > discrim->max_level)) { + for(i = 0; i < discrim->channel_count; i++) + if (!GetUserMode(discrim->channels[i], user)) + 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->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; } @@ -3122,27 +3378,43 @@ discrim_match(discrim_t discrim, struct userNode *user) static unsigned int opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) { - unsigned int nn, count; + unsigned int nn, count, match; struct userList matched; userList_init(&matched); /* Try most optimized search methods first */ - if (discrim->channel) { - for (nn=0; - (nn < discrim->channel->members.used) - && (matched.used < discrim->limit); - nn++) { - struct modeNode *mn = discrim->channel->members.list[nn]; - if (((mn->modes & discrim->chan_req_modes) != discrim->chan_req_modes) - || ((mn->modes & discrim->chan_no_modes) != 0)) { + if (discrim->channel_count) + { + for (nn=0; (nn < discrim->channels[0]->members.used) + && (matched.used < discrim->limit); + nn++) { + struct modeNode *mn = discrim->channels[0]->members.list[nn]; + + if (((mn->modes & discrim->chan_req_modes[0]) != discrim->chan_req_modes[0]) + || ((mn->modes & discrim->chan_no_modes[0]) != 0)) { continue; } - if (discrim_match(discrim, mn->user)) { - userList_append(&matched, mn->user); + + if ((match = discrim_match(discrim, mn->user))) + { + unsigned int i; + + for (i = 1; i < discrim->channel_count; i++) { + struct modeNode *mn2 = GetUserMode(discrim->channels[i], mn->user); + + if (((mn2->modes & discrim->chan_req_modes[i]) != discrim->chan_req_modes[i]) + || ((mn2->modes & discrim->chan_no_modes[i]) != 0)) { + match = 0; + break; + } + } + + if (match) + 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; @@ -3175,10 +3447,10 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) log_module(OS_LOG, LOG_INFO, " %s!%s@%s", user->nick, user->ident, user->hostname); } if (dsf(user, data)) { - /* If a search function returns true, it ran into a - problem. Stop going through the list. */ - break; - } + /* If a search function returns true, it ran into a + problem. Stop going through the list. */ + break; + } } if (discrim->option_log) { log_module(OS_LOG, LOG_INFO, "End of matching users."); @@ -3207,12 +3479,32 @@ trace_count_func(UNUSED_ARG(struct userNode *match), UNUSED_ARG(void *extra)) } static int -is_oper_victim(struct userNode *user, struct userNode *target, int match_opers) +is_oper_victim(struct userNode *user, struct userNode *target, int match_opers, int check_ip) +{ + unsigned char is_victim; + unsigned int nn; + + is_victim = !(IsService(target) + || (!match_opers && IsOper(target)) + || (target->handle_info + && target->handle_info->opserv_level > user->handle_info->opserv_level)); + + /* If we don't need an ip check or want to hit opers or the the "cheap" check already disqualified the target, we are done. */ + if (!check_ip || match_opers || !is_victim) + return is_victim; + + for(nn = 0; nn < curr_opers.used; nn++) { + if(memcmp(&curr_opers.list[nn]->ip, &target->ip, sizeof(irc_in_addr_t)) == 0) + return 0; + } + + return 1; +} + +static int +is_trust_victim(struct userNode *target, int match_trusted) { - return !(IsService(target) - || (!match_opers && IsOper(target)) - || (target->handle_info - && target->handle_info->opserv_level > user->handle_info->opserv_level)); + return (match_trusted || !dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)); } static int @@ -3220,7 +3512,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)) { + 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); } @@ -3232,8 +3524,8 @@ 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)) { - char *reason; + if (is_oper_victim(das->source, match, das->discrim->match_opers, 0) && is_trust_victim(match, das->discrim->match_trusted)) { + char *reason; if (das->discrim->reason) { reason = das->discrim->reason; } else { @@ -3262,7 +3554,7 @@ trace_gag_func(struct userNode *match, void *extra) { struct discrim_and_source *das = extra; - if (is_oper_victim(das->source, match, das->discrim->match_opers)) { + if (is_oper_victim(das->source, match, das->discrim->match_opers, 1) && is_trust_victim(match, das->discrim->match_trusted)) { char *reason, *mask; int masksize; if (das->discrim->reason) { @@ -3271,10 +3563,10 @@ trace_gag_func(struct userNode *match, void *extra) reason = alloca(strlen(OSMSG_GAG_REQUESTED)+strlen(das->source->nick)+1); sprintf(reason, OSMSG_GAG_REQUESTED, das->source->nick); } - masksize = 5+strlen(match->hostname); - mask = alloca(masksize); + masksize = 5+strlen(match->hostname); + mask = alloca(masksize); snprintf(mask, masksize, "*!*@%s", match->hostname); - if (!is_gagged(mask)) { + if (!is_gagged(mask)) { gag_create(mask, das->source->handle_info->handle, reason, das->discrim->duration ? (now + das->discrim->duration) : 0); } @@ -3287,29 +3579,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--; @@ -3339,13 +3644,13 @@ static MODCMD_FUNC(cmd_trace) { struct discrim_and_source das; discrim_search_func action; - unsigned int matches; + unsigned int matches, i; struct svccmd *subcmd; char buf[MAXLEN]; sprintf(buf, "trace %s", argv[1]); if (!(subcmd = dict_find(cmd->parent->commands, buf, NULL))) { - reply("OSMSG_BAD_ACTION", argv[1]); + reply("OSMSG_BAD_ACTION", argv[1]); return 0; } if (!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY)) @@ -3363,8 +3668,8 @@ static MODCMD_FUNC(cmd_trace) else if (!irccasecmp(argv[1], "gag")) action = trace_gag_func; else { - reply("OSMSG_BAD_ACTION", argv[1]); - return 0; + reply("OSMSG_BAD_ACTION", argv[1]); + return 0; } if (user->handle_info->opserv_level < subcmd->min_opserv_level) { @@ -3379,9 +3684,9 @@ static MODCMD_FUNC(cmd_trace) return 0; if (action == trace_print_func) - reply("OSMSG_USER_SEARCH_RESULTS"); + reply("OSMSG_USER_SEARCH_RESULTS"); else if (action == trace_count_func) - das.discrim->limit = INT_MAX; + 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_domains_func) { @@ -3397,12 +3702,12 @@ static MODCMD_FUNC(cmd_trace) dict_foreach(das.dict, opserv_show_hostinfo, &das); if (matches) - reply("MSG_MATCH_COUNT", matches); + reply("MSG_MATCH_COUNT", matches); else - reply("MSG_NO_MATCHES"); + reply("MSG_NO_MATCHES"); - if (das.discrim->channel) - UnlockChannel(das.discrim->channel); + for (i = 0; i < das.discrim->channel_count; i++) + UnlockChannel(das.discrim->channels[i]); free(das.discrim->reason); free(das.discrim); dict_delete(das.dict); @@ -3415,18 +3720,18 @@ typedef struct channel_discrim { char *name, *topic; unsigned int min_users, max_users; - time_t min_ts, max_ts; + unsigned long min_ts, max_ts; unsigned int limit; } *cdiscrim_t; static cdiscrim_t opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]); static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_func dsf, void *data); -static time_t +static unsigned long smart_parse_time(const char *str) { /* If an interval-style string is given, treat as time before now. * If it's all digits, treat directly as a Unix timestamp. */ - return str[strspn(str, "0123456789")] ? (time_t)(now - ParseInterval(str)) : (time_t)atoi(str); + return str[strspn(str, "0123456789")] ? (now - ParseInterval(str)) : strtoul(str, NULL, 0); } static cdiscrim_t @@ -3437,20 +3742,22 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]) discrim = calloc(1, sizeof(*discrim)); discrim->limit = 25; + discrim->max_users = UINT_MAX; + discrim->max_ts = ULONG_MAX; for (i = 0; i < argc; i++) { - /* Assume all criteria require arguments. */ - if (i == (argc - 1)) { - send_message(user, opserv, "MSG_MISSING_PARAMS", argv[i]); - return NULL; - } - - if (!irccasecmp(argv[i], "name")) - discrim->name = argv[++i]; - else if (!irccasecmp(argv[i], "topic")) - discrim->topic = argv[++i]; - else if (!irccasecmp(argv[i], "users")) { - const char *cmp = argv[++i]; + /* Assume all criteria require arguments. */ + if (i == (argc - 1)) { + send_message(user, opserv, "MSG_MISSING_PARAMS", argv[i]); + return NULL; + } + + if (!irccasecmp(argv[i], "name")) + discrim->name = argv[++i]; + else if (!irccasecmp(argv[i], "topic")) + discrim->topic = argv[++i]; + else if (!irccasecmp(argv[i], "users")) { + const char *cmp = argv[++i]; if (cmp[0] == '<') { if (cmp[1] == '=') discrim->max_users = strtoul(cmp+2, NULL, 0); @@ -3464,10 +3771,10 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]) else discrim->min_users = strtoul(cmp+1, NULL, 0) + 1; } else { - discrim->min_users = strtoul(cmp+2, NULL, 0); + discrim->min_users = strtoul(cmp, NULL, 0); } - } else if (!irccasecmp(argv[i], "timestamp")) { - const char *cmp = argv[++i]; + } else if (!irccasecmp(argv[i], "timestamp")) { + const char *cmp = argv[++i]; if (cmp[0] == '<') { if (cmp[1] == '=') discrim->max_ts = smart_parse_time(cmp+2); @@ -3483,18 +3790,18 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]) } else { discrim->min_ts = smart_parse_time(cmp); } - } else if (!irccasecmp(argv[i], "limit")) { - discrim->limit = strtoul(argv[++i], NULL, 10); - } else { - send_message(user, opserv, "MSG_INVALID_CRITERIA", argv[i]); - goto fail; - } + } else if (!irccasecmp(argv[i], "limit")) { + discrim->limit = strtoul(argv[++i], NULL, 10); + } else { + send_message(user, opserv, "MSG_INVALID_CRITERIA", argv[i]); + goto fail; + } } if (discrim->name && !strcmp(discrim->name, "*")) - discrim->name = 0; + discrim->name = 0; if (discrim->topic && !strcmp(discrim->topic, "*")) - discrim->topic = 0; + discrim->topic = 0; return discrim; fail: @@ -3507,11 +3814,11 @@ cdiscrim_match(cdiscrim_t discrim, struct chanNode *chan) { if ((discrim->name && !match_ircglob(chan->name, discrim->name)) || (discrim->topic && !match_ircglob(chan->topic, discrim->topic)) || - (discrim->min_users && chan->members.used < discrim->min_users) || - (discrim->max_users && chan->members.used > discrim->max_users) || - (discrim->min_ts && chan->timestamp < discrim->min_ts) || - (discrim->max_ts && chan->timestamp > discrim->max_ts)) { - return 0; + (chan->members.used < discrim->min_users) || + (chan->members.used > discrim->max_users) || + (chan->timestamp < discrim->min_ts) || + (chan->timestamp > discrim->max_ts)) { + return 0; } return 1; } @@ -3522,15 +3829,15 @@ static unsigned int opserv_cdiscrim_search(cdiscrim_t discrim, cdiscrim_search_f dict_iterator_t it, next; for (it = dict_first(channels); it && count < discrim->limit ; it = next) { - struct chanNode *chan = iter_data(it); + struct chanNode *chan = iter_data(it); - /* Hold on to the next channel in case we decide to - add actions that destructively modify the channel. */ - next = iter_next(it); - if ((chan->members.used > 0) && cdiscrim_match(discrim, chan)) { - dsf(chan, data); - count++; - } + /* Hold on to the next channel in case we decide to + add actions that destructively modify the channel. */ + next = iter_next(it); + if ((chan->members.used > 0) && cdiscrim_match(discrim, chan)) { + dsf(chan, data); + count++; + } } return count; @@ -3556,12 +3863,12 @@ static MODCMD_FUNC(cmd_csearch) char buf[MAXLEN]; if (!irccasecmp(argv[1], "count")) - action = channel_count; + action = channel_count; else if (!irccasecmp(argv[1], "print")) - action = channel_print; + action = channel_print; else { - reply("OSMSG_BAD_ACTION", argv[1]); - return 0; + reply("OSMSG_BAD_ACTION", argv[1]); + return 0; } sprintf(buf, "%s %s", argv[0], argv[0]); @@ -3572,19 +3879,19 @@ static MODCMD_FUNC(cmd_csearch) discrim = opserv_cdiscrim_create(user, argc - 2, argv + 2); if (!discrim) - return 0; + return 0; if (action == channel_print) - reply("OSMSG_CHANNEL_SEARCH_RESULTS"); + reply("OSMSG_CHANNEL_SEARCH_RESULTS"); else if (action == channel_count) - discrim->limit = INT_MAX; + discrim->limit = INT_MAX; matches = opserv_cdiscrim_search(discrim, action, user); if (matches) - reply("MSG_MATCH_COUNT", matches); + reply("MSG_MATCH_COUNT", matches); else - reply("MSG_NO_MATCHES"); + reply("MSG_NO_MATCHES"); free(discrim); return 1; @@ -3616,11 +3923,44 @@ static void gtrace_print_func(struct gline *gline, void *extra) { struct gline_extra *xtra = extra; - char *when_text, set_text[20]; - strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&gline->issued)); - when_text = asctime(localtime(&gline->expires)); - when_text[strlen(when_text)-1] = 0; /* strip lame \n */ - send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, set_text, gline->issuer, when_text, gline->reason); + char issued[INTERVALLEN]; + char lastmod[INTERVALLEN]; + char expires[INTERVALLEN]; + char lifetime[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, ""); + intervalString(lifetime, gline->lifetime - now, xtra->user->handle_info); + if (!gline->expires) { + send_message(xtra->user, opserv, "OSMSG_GTRACE_FOREVER", gline->target, issued, gline->issuer, lastmod, NULL, gline->reason, lifetime); + } else if (gline->expires < now) { + intervalString(expires, now - gline->expires, xtra->user->handle_info); + send_message(xtra->user, opserv, "OSMSG_GTRACE_EXPIRED", gline->target, issued, gline->issuer, lastmod, expires, gline->reason, lifetime); + } else { /* must be in the future */ + intervalString(expires, gline->expires - now, xtra->user->handle_info); + send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason, lifetime); + } +} + +static MODCMD_FUNC(cmd_stats_glines) { + if (argc < 2) { + reply("OSMSG_GLINE_COUNT", gline_count()); + return 1; + } else if (argc < 3) { + struct gline_extra extra; + struct gline *gl; + + extra.user = user; + gl = gline_find(argv[1]); + if (!gl) + reply("OSMSG_NO_GLINE", argv[1]); + else + gtrace_print_func(gl, &extra); + return 1; + } else return 0; } static void @@ -3702,6 +4042,11 @@ alert_check_user(const char *key, void *data, void *extra) return 0; } + if ((alert->reaction != REACT_NOTICE) + && !is_trust_victim(user, alert->discrim->match_trusted)) { + return 0; + } + /* The user matches the alert criteria, so trigger the reaction. */ if (alert->discrim->option_log) log_module(OS_LOG, LOG_INFO, "Alert %s triggered by user %s!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); @@ -3719,7 +4064,7 @@ alert_check_user(const char *key, void *data, void *extra) log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key); /* fall through to REACT_NOTICE case */ case REACT_NOTICE: - opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); + opserv_custom_alert(alert->discrim->notice_target, "Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason); break; } return 0; @@ -3733,7 +4078,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; } @@ -3749,6 +4094,8 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han || user->uplink->burst || !user->handle_info) return; + else if (IsBot(user)) + return; else if (user->handle_info->opserv_level) type = "OPER"; else if (IsNetworkHelper(user)) @@ -3758,10 +4105,17 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han else return; - if (user->ip.s_addr) - send_channel_notice(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle); + if (irc_in_addr_is_valid(user->ip)) + send_channel_message(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); + send_channel_message(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); +} + +static void +opserv_auth_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) +{ + if (!user->uplink->burst && user->handle_info) + dict_foreach(opserv_account_alerts, alert_check_user, user); } static MODCMD_FUNC(cmd_log) @@ -3780,9 +4134,9 @@ static MODCMD_FUNC(cmd_log) matches = log_entry_search(discrim, log_report_entry, &report); if (matches) - reply("MSG_MATCH_COUNT", matches); + reply("MSG_MATCH_COUNT", matches); else - reply("MSG_NO_MATCHES"); + reply("MSG_NO_MATCHES"); free(discrim); return 1; @@ -3807,26 +4161,26 @@ static MODCMD_FUNC(cmd_gag) reason = unsplit_string(argv + 3, argc - 3, NULL); if (!is_ircmask(argv[1])) { - reply("OSMSG_INVALID_IRCMASK", argv[1]); + reply("OSMSG_INVALID_IRCMASK", argv[1]); return 0; } for (gag = gagList; gag; gag = gag->next) - if (match_ircglobs(gag->mask, argv[1])) + if (match_ircglobs(gag->mask, argv[1])) break; if (gag) { - reply("OSMSG_REDUNDANT_GAG", argv[1]); - return 0; + reply("OSMSG_REDUNDANT_GAG", argv[1]); + return 0; } duration = ParseInterval(argv[2]); gagged = gag_create(argv[1], user->handle_info->handle, reason, (duration?now+duration:0)); if (gagged) - reply("OSMSG_GAG_APPLIED", argv[1], gagged); + reply("OSMSG_GAG_APPLIED", argv[1], gagged); else - reply("OSMSG_GAG_ADDED", argv[1]); + reply("OSMSG_GAG_ADDED", argv[1]); return 1; } @@ -3843,21 +4197,21 @@ static MODCMD_FUNC(cmd_ungag) unsigned int ungagged; for (gag = gagList; gag; gag = gag->next) - if (!strcmp(gag->mask, argv[1])) + if (!strcmp(gag->mask, argv[1])) break; if (!gag) { - reply("OSMSG_GAG_NOT_FOUND", argv[1]); - return 0; + reply("OSMSG_GAG_NOT_FOUND", argv[1]); + return 0; } timeq_del(gag->expires, gag_expire, gag, 0); ungagged = gag_free(gag); if (ungagged) - reply("OSMSG_UNGAG_APPLIED", argv[1], ungagged); + reply("OSMSG_UNGAG_APPLIED", argv[1], ungagged); else - reply("OSMSG_UNGAG_ADDED", argv[1]); + reply("OSMSG_UNGAG_ADDED", argv[1]); return 1; } @@ -3871,8 +4225,8 @@ static MODCMD_FUNC(cmd_addalert) name = argv[1]; sprintf(buf, "addalert %s", argv[2]); if (!(subcmd = dict_find(cmd->parent->commands, buf, NULL))) { - reply("OSMSG_UNKNOWN_REACTION", argv[2]); - return 0; + reply("OSMSG_UNKNOWN_REACTION", argv[2]); + return 0; } if (!irccasecmp(argv[2], "notice")) reaction = REACT_NOTICE; @@ -3881,8 +4235,8 @@ static MODCMD_FUNC(cmd_addalert) else if (!irccasecmp(argv[2], "gline")) reaction = REACT_GLINE; else { - reply("OSMSG_UNKNOWN_REACTION", argv[2]); - return 0; + reply("OSMSG_UNKNOWN_REACTION", argv[2]); + return 0; } if (!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY) || !opserv_add_user_alert(user, name, reaction, unsplit_string(argv + 3, argc - 3, NULL))) @@ -3897,11 +4251,590 @@ static MODCMD_FUNC(cmd_delalert) for (i=1; iparent->bot, user->nick, 0, NULL, tbl); + reply("MSG_NONE"); + free(tbl.contents[0]); + free(tbl.contents); + return 0; + } + for (it = dict_first(opserv_devnull_classes); it; it = iter_next(it)) { + struct devnull_class *th = iter_data(it); + tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0])); + tbl.contents[ii][0] = th->name; + if(DEVNULL_FLAGGED(th, MODE_A)) { + tbl.contents[ii][1] = strtab(th->maxchan); + } else { + tbl.contents[ii][1] = off; + } + if(DEVNULL_FLAGGED(th, MODE_B)) { + tbl.contents[ii][2] = on; + } else { + tbl.contents[ii][2] = off; + } + if(DEVNULL_FLAGGED(th, MODE_D)) { + tbl.contents[ii][3] = on; + } else if(DEVNULL_FLAGGED(th, MODE_C)) { + tbl.contents[ii][3] = half; + } else { + tbl.contents[ii][3] = off; + } + if(DEVNULL_FLAGGED(th, MODE_E)) { + tbl.contents[ii][4] = on; + } else { + tbl.contents[ii][4] = off; + } + if(DEVNULL_FLAGGED(th, MODE_F)) { + tbl.contents[ii][5] = on; + } else { + tbl.contents[ii][5] = off; + } + if(DEVNULL_FLAGGED(th, MODE_J)) { + tbl.contents[ii][6] = on; + } else { + tbl.contents[ii][6] = off; + } + if(DEVNULL_FLAGGED(th, MODE_K)) { + tbl.contents[ii][7] = on; + } else { + tbl.contents[ii][7] = off; + } + if(DEVNULL_FLAGGED(th, MODE_L)) { + tbl.contents[ii][8] = on; + } else { + tbl.contents[ii][8] = off; + } + if(DEVNULL_FLAGGED(th, MODE_M)) { + tbl.contents[ii][9] = on; + } else { + tbl.contents[ii][9] = off; + } + if(DEVNULL_FLAGGED(th, MODE_G)) { + tbl.contents[ii][10] = on; + } else { + tbl.contents[ii][10] = off; + } + if(DEVNULL_FLAGGED(th, MODE_I)) { + tbl.contents[ii][11] = on; + } else { + tbl.contents[ii][11] = off; + } + if(DEVNULL_FLAGGED(th, MODE_H)) { + tbl.contents[ii][12] = on; + } else { + tbl.contents[ii][12] = off; + } + if(DEVNULL_FLAGGED(th, MODE_N)) { + tbl.contents[ii][13] = on; + } else { + tbl.contents[ii][13] = off; + } + if(DEVNULL_FLAGGED(th, MODE_OPME)) { + tbl.contents[ii][14] = on; + } else { + tbl.contents[ii][14] = off; + } + } + table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); + for(ii = 1; ii < tbl.length; ++ii) + { + free(tbl.contents[ii]); + } + free(tbl.contents[0]); + free(tbl.contents); + send_message_type(4, user, opserv,"-"); + /* + tbl.contents[0][1] = "MC"; + tbl.contents[0][2] = "UT"; + tbl.contents[0][3] = "FL"; + tbl.contents[0][4] = "CH"; + tbl.contents[0][5] = "IH"; + tbl.contents[0][6] = "SI"; + tbl.contents[0][7] = "IH²"; + tbl.contents[0][8] = "oC"; + tbl.contents[0][9] = "oM"; + tbl.contents[0][10] = "+k"; + tbl.contents[0][11] = "+S"; + tbl.contents[0][12] = "+X"; + tbl.contents[0][13] = "MaxQ"; + */ + send_message_type(4, user, opserv,"MC = MaxChan"); + send_message_type(4, user, opserv,"UT = UnlimitTargets"); + send_message_type(4, user, opserv,"FL = Flood (1 = 1/2 flood, X = unlimited)"); + send_message_type(4, user, opserv,"CH = Channel Hide (mode +n)"); + send_message_type(4, user, opserv,"IH = Idle Hide (mode +I)"); + send_message_type(4, user, opserv,"SI = See Idle"); + send_message_type(4, user, opserv,"IH² = Idle Hide (override SI)"); + send_message_type(4, user, opserv,"oC = Color override (mode +c)"); + send_message_type(4, user, opserv,"oM = AMsg override"); + send_message_type(4, user, opserv,"+k = ChanServ Mode (mode +k)"); + send_message_type(4, user, opserv,"+S = NetServ Mode (mode +S)"); + send_message_type(4, user, opserv,"+X = XtraOp Mode (mode +X)"); + send_message_type(4, user, opserv,"OpMe = ChanServ opme command"); + return 1; +} + +static MODCMD_FUNC(cmd_adddevnull) +{ + + if (dict_find(opserv_devnull_classes, argv[1], NULL)) { + reply("OSMSG_DEVNULL_FOUND", argv[1]); + return 0; + } + + opserv_add_devnull_class(argv[1], 0, 0, 0); + reply("OSMSG_DEVNULL_ADDED",argv[1]); + return 1; +} + + +static MODCMD_FUNC(cmd_deldevnull) +{ + unsigned int n; + + for (n=1; nname); + dict_remove(opserv_devnull_classes, argv[n]); + reply("OSMSG_DEVNULL_REMOVED",argv[n]); + } + return 1; +} + +static MODCMD_FUNC(cmd_renamedevnull) +{ + struct devnull_class *th; + if (dict_find(opserv_devnull_classes, argv[2], NULL)) { + reply("OSMSG_DEVNULL_FOUND", argv[2]); + return 0; + } + if ((th = dict_find(opserv_devnull_classes, argv[1], NULL))) { + opserv_add_devnull_class(argv[2], th->modes, th->maxchan, th->maxsendq); + nickserv_devnull_rename(th->name,argv[2]); + dict_remove(opserv_devnull_classes, argv[1]); + reply("OSMSG_DEVNULL_RENAMED",argv[1],argv[2]); + } else { + reply("OSMSG_DEVNULL_NOTFOUND", argv[1]); + return 0; + } + return 1; +} + +static MODCMD_FUNC(cmd_setdevnull) +{ + struct devnull_class *th; + unsigned int offset=0; + if ((th = dict_find(opserv_devnull_classes, argv[offset+1], NULL))) { + if (argc > 3) { + unsigned int ii; + for( ii = 0; argv[offset+2][ ii ]; ii++) + argv[offset+2][ ii ] = toupper( argv[offset+2][ ii ] ); + for( ii = 0; argv[offset+3][ ii ]; ii++) + argv[offset+3][ ii ] = toupper( argv[offset+3][ ii ] ); + if(!strcmp("CHANLIMIT",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_A); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + DEVNULL_SET_FLAG(th, MODE_A); + th->maxchan = strtoul(argv[offset+3], NULL, 0); + reply("OSMSG_DEVNULL_SET_DONE"); + } + } + else if(!strcmp("UNLIMITTARGET",argv[offset+2]) || !strcmp("UNLIMITEDTARGET",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_B); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_B); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("FLOOD",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_C); + DEVNULL_CLEAR_FLAG(th, MODE_D); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_D); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("HALF",argv[offset+3]) || !strcmp("2",argv[offset+3]) || !strcmp("1/2",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_C); + DEVNULL_CLEAR_FLAG(th, MODE_D); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("CHANHIDE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_E); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_E); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("IDLEHIDE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_F); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_F); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("CHSERVMODE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_G); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_G); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("XTRAOPMODE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_H); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_H); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("NETSERVMODE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_I); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_I); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("SEEIDLE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_J); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_J); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("FORCEIDLEHIDE",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_K); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_K); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("OVERRIDECC",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_L); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_L); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("OVERRIDENOAMSG",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_M); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_M); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } + else if(!strcmp("MAXSENDQ",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_N); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + DEVNULL_SET_FLAG(th, MODE_N); + th->maxsendq = strtoul(argv[offset+3], NULL, 0); + reply("OSMSG_DEVNULL_SET_DONE"); + } + } + else if(!strcmp("OPME",argv[offset+2])) { + if (!strcmp("OFF",argv[offset+3]) || !strcmp("0",argv[offset+3])) { + DEVNULL_CLEAR_FLAG(th, MODE_OPME); + reply("OSMSG_DEVNULL_SET_DONE"); + } else if (!strcmp("ON",argv[offset+3]) || !strcmp("1",argv[offset+3])) { + DEVNULL_SET_FLAG(th, MODE_OPME); + reply("OSMSG_DEVNULL_SET_DONE"); + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+3]); + } + } else { + reply("OSMSG_DEVNULL_SET_INVALID", argv[offset+2]); + } + + } else { + reply("OSMSG_DEVNULL_SET", th->name); + if(DEVNULL_FLAGGED(th, MODE_A)) { + reply("OSMSG_DEVNULL_SET_A_i", th->maxchan); + } else { + reply("OSMSG_DEVNULL_SET_A", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_B)) { + reply("OSMSG_DEVNULL_SET_B", "on"); + } else { + reply("OSMSG_DEVNULL_SET_B", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_D)) { + reply("OSMSG_DEVNULL_SET_C", "on"); + } else if(DEVNULL_FLAGGED(th, MODE_C)) { + reply("OSMSG_DEVNULL_SET_C", "half"); + } else { + reply("OSMSG_DEVNULL_SET_C", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_E)) { + reply("OSMSG_DEVNULL_SET_E", "on"); + } else { + reply("OSMSG_DEVNULL_SET_E", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_F)) { + reply("OSMSG_DEVNULL_SET_F", "on"); + } else { + reply("OSMSG_DEVNULL_SET_F", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_G)) { + reply("OSMSG_DEVNULL_SET_G", "on"); + } else { + reply("OSMSG_DEVNULL_SET_G", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_H)) { + reply("OSMSG_DEVNULL_SET_H", "on"); + } else { + reply("OSMSG_DEVNULL_SET_H", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_I)) { + reply("OSMSG_DEVNULL_SET_I", "on"); + } else { + reply("OSMSG_DEVNULL_SET_I", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_J)) { + reply("OSMSG_DEVNULL_SET_J", "on"); + } else { + reply("OSMSG_DEVNULL_SET_J", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_K)) { + reply("OSMSG_DEVNULL_SET_K", "on"); + } else { + reply("OSMSG_DEVNULL_SET_K", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_L)) { + reply("OSMSG_DEVNULL_SET_L", "on"); + } else { + reply("OSMSG_DEVNULL_SET_L", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_M)) { + reply("OSMSG_DEVNULL_SET_M", "on"); + } else { + reply("OSMSG_DEVNULL_SET_M", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_N)) { + reply("OSMSG_DEVNULL_SET_N_i", th->maxsendq); + } else { + reply("OSMSG_DEVNULL_SET_N", "off"); + } + if(DEVNULL_FLAGGED(th, MODE_OPME)) { + reply("OSMSG_DEVNULL_SET_OPME", "on"); + } else { + reply("OSMSG_DEVNULL_SET_OPME", "off"); + } + } + } else { + reply("OSMSG_DEVNULL_NOTFOUND", argv[offset+1]); + return 0; + } + return 1; +} + +int devnull_check(const char *name) { + if (dict_find(opserv_devnull_classes, name, NULL)) { + return 1; + } + return 0; +} + +struct devnull_class* + devnull_get(const char *name) { + return dict_find(opserv_devnull_classes, name, NULL); +} + +void operpart(struct chanNode *chan, struct userNode *user) +{ + if(opserv_conf.alert_channel && opserv_conf.staff_auth_force > 0 && + !(irccasecmp(chan->name,opserv_conf.alert_channel->name))) { + struct mod_chanmode *change; + change = find_matching_bans(&chan->banlist, user, NULL); //don't join them if they're banned (exceptions from forced join) + if(change) + return; + irc_svsjoin(opserv,user,chan); + } +} + +void operadd(struct userNode *user) +{ + if(opserv_conf.alert_channel && opserv_conf.staff_auth_force > 0) + irc_svsjoin(opserv,user,opserv_conf.alert_channel); +} + +void operdel(struct userNode *user) +{ + if(opserv_conf.alert_channel && opserv_conf.staff_auth_force == 2) + irc_kick(opserv, user, opserv_conf.alert_channel, "mode -o"); +} + +static MODCMD_FUNC(cmd_svsjoin) +{ + struct userNode *target; + if(!(target=GetUserH(argv[1]))) { + reply("OSMSG_SVSNONICK", argv[1]); + return 0; + } + if(!IsChannelName(argv[2])) + { + reply("MSG_NOT_CHANNEL_NAME"); + return 0; + } + irc_svsjoinchan(opserv,target,argv[2]); + reply("OSMSG_SVSJOIN",target->nick,argv[2]); + return 1; +} + +static MODCMD_FUNC(cmd_svsnick) +{ + struct userNode *target; + if(!(target=GetUserH(argv[1]))) { + reply("OSMSG_SVSNONICK", argv[1]); + return 0; + } + if(GetUserH(argv[2])) + { + reply("OSMSG_SVSNICKUSED",argv[2]); + return 0; + } + irc_svsnick(opserv,target,argv[2]); + reply("OSMSG_SVSNICK",target->nick,argv[2]); + return 1; +} + +static MODCMD_FUNC(cmd_svsmode) +{ + struct userNode *target; + char *modestr; + if(!(target=GetUserH(argv[1]))) { + reply("OSMSG_SVSNONICK", argv[1]); + return 0; } + modestr = unsplit_string(argv + 2, argc - 2, NULL); + irc_svsmode(opserv,target,modestr); + reply("OSMSG_SVSMODE",modestr,target->nick); + return 1; +} + +static MODCMD_FUNC(cmd_simul) +{ + struct userNode *target; + char *line; + if(argc > 2) { + if(!(target=GetUserH(argv[1]))) { + reply("OSMSG_SVSNONICK", argv[1]); + return 0; + } + line = unsplit_string(argv + 2, argc - 2, NULL); + irc_simul(target,line); + reply("OSMSG_SIMUL",target->nick,line); + return 1; + } + return 0; +} + +static MODCMD_FUNC(cmd_relay) +{ + struct userNode *target; + char *line; + if(!(target=GetUserH(argv[1]))) { + reply("OSMSG_SVSNONICK", argv[1]); + return 0; + } + line = unsplit_string(argv + 2, argc - 2, NULL); + char sendline[512]; + if(channel) + sprintf(sendline, "relay %s %s :%s",user->nick,channel->name,line); + else + sprintf(sendline, "relay %s query :%s",user->nick,line); + irc_privmsg(opserv,target->numeric,sendline); return 1; } @@ -3916,8 +4849,8 @@ opserv_conf_read(void) rd = conf_get_node(OPSERV_CONF_NAME); if (!rd || rd->type != RECDB_OBJECT) { - log_module(OS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", OPSERV_CONF_NAME); - return; + log_module(OS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", OPSERV_CONF_NAME); + return; } conf_node = rd->d.object; str = database_get_data(conf_node, KEY_DEBUG_CHANNEL, RECDB_QSTRING); @@ -3925,20 +4858,20 @@ opserv_conf_read(void) str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING); if (!str2) str2 = "+tinms"; - opserv_conf.debug_channel = AddChannel(str, now, str2, NULL); + opserv_conf.debug_channel = AddChannel(str, now, str2, NULL); AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP; } else { - opserv_conf.debug_channel = NULL; + opserv_conf.debug_channel = NULL; } str = database_get_data(conf_node, KEY_ALERT_CHANNEL, RECDB_QSTRING); if (opserv && str) { str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING); if (!str2) str2 = "+tns"; - opserv_conf.alert_channel = AddChannel(str, now, str2, NULL); + opserv_conf.alert_channel = AddChannel(str, now, str2, NULL); AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP; } else { - opserv_conf.alert_channel = NULL; + opserv_conf.alert_channel = NULL; } str = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL, RECDB_QSTRING); if (opserv && str) { @@ -3947,8 +4880,11 @@ opserv_conf_read(void) str2 = "+timns"; opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL); AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP; + str2 = database_get_data(conf_node, KEY_STAFF_AUTH_FORCE_OPS, RECDB_QSTRING); + opserv_conf.staff_auth_force = str2 ? atoi(str2) : 0; } else { opserv_conf.staff_auth_channel = NULL; + opserv_conf.staff_auth_force = 0; } str = database_get_data(conf_node, KEY_UNTRUSTED_MAX, RECDB_QSTRING); opserv_conf.untrusted_max = str ? strtoul(str, NULL, 0) : 5; @@ -3971,7 +4907,7 @@ opserv_conf_read(void) policer_params_set(opserv_conf.join_policer_params, "size", "20"); policer_params_set(opserv_conf.join_policer_params, "drain-rate", "1"); if ((child = database_get_data(conf_node, KEY_JOIN_POLICER, RECDB_OBJECT))) - dict_foreach(child, set_policer_param, opserv_conf.join_policer_params); + dict_foreach(child, set_policer_param, opserv_conf.join_policer_params); for (it = dict_first(channels); it; it = iter_next(it)) { struct chanNode *cNode = iter_data(it); @@ -3985,7 +4921,7 @@ opserv_conf_read(void) policer_params_set(pp, "size", "200"); policer_params_set(pp, "drain-rate", "3"); if ((child = database_get_data(conf_node, KEY_NEW_USER_POLICER, RECDB_OBJECT))) - dict_foreach(child, set_policer_param, pp); + dict_foreach(child, set_policer_param, pp); } static void @@ -3994,12 +4930,18 @@ opserv_db_init(void) { dict_delete(opserv_trusted_hosts); opserv_trusted_hosts = dict_new(); dict_set_free_data(opserv_trusted_hosts, free_trusted_host); + /* set up opserv_devnull_classes dict */ + dict_delete(opserv_devnull_classes); + opserv_devnull_classes = dict_new(); + dict_set_free_data(opserv_devnull_classes, free_devnull_class); /* set up opserv_chan_warn dict */ dict_delete(opserv_chan_warn); opserv_chan_warn = dict_new(); dict_set_free_keys(opserv_chan_warn, free); dict_set_free_data(opserv_chan_warn, free); /* set up opserv_user_alerts */ + dict_delete(opserv_account_alerts); + opserv_account_alerts = dict_new(); dict_delete(opserv_channel_alerts); opserv_channel_alerts = dict_new(); dict_delete(opserv_nick_based_alerts); @@ -4027,9 +4969,11 @@ opserv_db_cleanup(void) free_string_list(opserv_bad_words); dict_delete(opserv_exempt_channels); dict_delete(opserv_trusted_hosts); + dict_delete(opserv_devnull_classes); unreg_del_user_func(opserv_user_cleanup); dict_delete(opserv_hostinfo_dict); dict_delete(opserv_nick_based_alerts); + dict_delete(opserv_account_alerts); dict_delete(opserv_channel_alerts); dict_delete(opserv_user_alerts); for (nn=0; nn