X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/c99dcaf6a0f9c7798580fc9f315d7b368c9f972c..4cb36ef05734bc040784d67794a3376b0e3a5ec2:/src/chanserv.c diff --git a/src/chanserv.c b/src/chanserv.c index 2419e33..b39dd9b 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -5,7 +5,7 @@ * * x3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -21,9 +21,12 @@ #include "chanserv.h" #include "conf.h" #include "global.h" +#include "gline.h" +#include "ioset.h" #include "modcmd.h" #include "opserv.h" /* for opserv_bad_channel() */ #include "saxdb.h" +#include "shun.h" #include "spamserv.h" #include "timeq.h" @@ -111,11 +114,15 @@ #define KEY_NOTES "notes" #define KEY_TOPIC_MASK "topic_mask" #define KEY_OWNER_TRANSFER "owner_transfer" +#define KEY_MAXSETINFO "maxsetinfo" /* User data */ #define KEY_LEVEL "level" #define KEY_INFO "info" #define KEY_SEEN "seen" +#define KEY_ACCESSEXPIRY "accessexpiry" +#define KEY_CLVLEXPIRY "clvlexpiry" +#define KEY_LASTLEVEL "lastlevel" /* Ban data */ #define KEY_OWNER "owner" @@ -192,6 +199,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." }, { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." }, { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." }, + { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." }, /* Removing yourself from a channel. */ { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." }, @@ -199,6 +207,8 @@ static const struct message_entry msgtab[] = { { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." }, /* User management */ + { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." }, + { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." }, { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." }, { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." }, { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." }, @@ -217,6 +227,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." }, { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." }, + { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." }, { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." }, { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." }, { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." }, @@ -230,7 +241,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." }, { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." }, { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." }, - { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." }, + { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." }, { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." }, { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." }, { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." }, @@ -268,7 +279,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." }, { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." }, { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." }, - { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" }, + { "CSMSG_INVALID_NUMERIC", "$b%s$b is not a valid choice. Choose one:" }, { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" }, { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" }, { "CSMSG_SET_GREETING", "$bGreeting $b %s" }, @@ -293,6 +304,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" }, { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" }, { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" }, + { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." }, { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" }, { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" }, @@ -313,7 +325,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." }, { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." }, - { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." }, + { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." }, { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." }, { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." }, { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" }, @@ -512,6 +524,30 @@ static const struct message_entry msgtab[] = { { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." }, { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" }, { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" }, + { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" }, + { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } , + { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" }, + { "CSMSG_ROULETTE_BANG", "Bang!!!" } , + { "CSMSG_ROULETTE_CLICK", "Click" } , + + { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } , + { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } , + { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } , + + { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } , + { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } , + { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } , + { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } , + { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } , + { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } , + { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } , + { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } , + { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } , + { "CSMSG_SPIN_KILL", "Kill: /kill" } , + { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } , + { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } , + { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } , + { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." }, /* Other things */ { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" }, @@ -587,6 +623,7 @@ static struct struct string_list *set_shows; struct string_list *eightball; struct string_list *old_ban_names; + struct string_list *wheel; const char *ctcp_short_ban_duration; const char *ctcp_long_ban_duration; @@ -622,6 +659,9 @@ enum note_visible_type NOTE_VIS_PRIVILEGED }; +struct io_fd *socket_io_fd; +extern struct cManagerNode cManager; + struct note_type { enum note_access_type set_access_type; @@ -651,6 +691,7 @@ static const struct { unsigned short level; char ch; } accessLevels[] = { /* MUST be orderd less to most! */ + { "pal", "Pal", UL_PEON, '+' }, { "peon", "Peon", UL_PEON, '+' }, { "halfop", "HalfOp", UL_HALFOP, '%' }, { "op", "Op", UL_OP, '@' }, @@ -756,6 +797,28 @@ unsigned int chanserv_read_version = 0; /* db version control */ #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0) #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1) +void sputsock(const char *text, ...) PRINTF_LIKE(1, 2); + +void +sputsock(const char *text, ...) +{ + va_list arg_list; + char buffer[MAXLEN]; + int pos; + + if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return; + buffer[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + buffer[pos] = 0; + log_replay(MAIN_LOG, true, buffer); + buffer[pos++] = '\n'; + buffer[pos] = 0; + ioset_write(socket_io_fd, buffer, pos); +} + unsigned short user_level_from_name(const char *name, unsigned short clamp_level) { @@ -778,7 +841,7 @@ user_level_name_from_level(int level) highest = "None"; if(level >= 1) - highest = "Peon"; + highest = "Pal"; for(ii = 0; (ii < ArrayLength(accessLevels)); ii++) if(level >= accessLevels[ii].level) highest = accessLevels[ii].title; @@ -1238,7 +1301,7 @@ register_channel(struct chanNode *cNode, char *registrar) } static struct userData* -add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info) +add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info, time_t accessexpiry) { struct userData *ud; @@ -1251,6 +1314,9 @@ add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned ud->seen = seen; ud->access = access; ud->info = info ? strdup(info) : NULL; + ud->accessexpiry = accessexpiry ? accessexpiry : 0; + ud->clvlexpiry = 0; + ud->lastaccess = 0; ud->prev = NULL; ud->next = channel->users; @@ -1273,6 +1339,110 @@ add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned static void unregister_channel(struct chanData *channel, const char *reason); +static void +chanserv_expire_tempuser(void *data) +{ + struct userData *uData = data; + char *handle; + + if (data) { + handle = strdup(uData->handle->handle); + if (uData->accessexpiry > 0) { + if (uData->present) { + struct userNode *user, *next_un = NULL; + struct handle_info *hi; + + hi = get_handle_info(handle); + for (user = hi->users; user; user = next_un) { + struct mod_chanmode *change; + struct modeNode *mn; + unsigned int count = 0; + + send_message(user, chanserv, "CSMSG_AUTO_DELETED", chanserv->nick, uData->channel->channel->name); + if (!(mn = GetUserMode(uData->channel->channel, user)) || !(mn->modes & MODE_CHANOP)) { + next_un = user->next_authed; + continue; + } + + change = mod_chanmode_alloc(2); + change->args[count].mode = MODE_REMOVE | MODE_CHANOP; + change->args[count++].u.member = mn; + + if (count) { + change->argc = count; + mod_chanmode_announce(chanserv, uData->channel->channel, change); + } + mod_chanmode_free(change); + next_un = user->next_authed; + } + } + del_channel_user(uData, 1); + } + } +} + +static void +chanserv_expire_tempclvl(void *data) +{ + struct userData *uData = data; + char *handle; + + if (data) { + handle = strdup(uData->handle->handle); + if (uData->clvlexpiry > 0) { + int changemodes = 0; + unsigned int mode = 0; + + if (((uData->lastaccess == UL_PEON) || (uData->lastaccess == UL_HALFOP)) && (uData->access >= UL_OP)) { + changemodes = 1; + mode = MODE_REMOVE | MODE_CHANOP; + } else if ((uData->lastaccess == UL_PEON) && (uData->access == UL_HALFOP)) { + changemodes = 1; + mode = MODE_REMOVE | MODE_HALFOP; + } else + changemodes = 0; + + if (uData->present) { + struct userNode *user, *next_un = NULL; + struct handle_info *hi; + + hi = get_handle_info(handle); + for (user = hi->users; user; user = next_un) { + struct mod_chanmode *change; + struct modeNode *mn; + unsigned int count = 0; + + send_message(user, chanserv, "CSMSG_CLVL_EXPIRED", uData->channel->channel->name); + if (!(mn = GetUserMode(uData->channel->channel, user))) { + next_un = user->next_authed; + continue; + } + + if (changemodes == 0) { + next_un = user->next_authed; + continue; + } + + change = mod_chanmode_alloc(2); + change->args[count].mode = mode; + change->args[count++].u.member = mn; + + if (count) { + change->argc = count; + mod_chanmode_announce(chanserv, uData->channel->channel, change); + } + mod_chanmode_free(change); + next_un = user->next_authed; + } + } + + uData->access = uData->lastaccess; + uData->lastaccess = 0; + uData->clvlexpiry = 0; + } + } +} + void del_channel_user(struct userData *user, int do_gc) { @@ -1281,6 +1451,9 @@ del_channel_user(struct userData *user, int do_gc) channel->userCount--; userCount--; + timeq_del(0, chanserv_expire_tempuser, user, TIMEQ_IGNORE_WHEN); + timeq_del(0, chanserv_expire_tempclvl, user, TIMEQ_IGNORE_WHEN); + if(user->prev) user->prev->next = user->next; else @@ -1391,7 +1564,7 @@ process_adduser_pending(struct userNode *user) } else { - actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL); + actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL, 0); scan_user_presence(actee, NULL); } del_adduser_pending(ap); @@ -2103,7 +2276,7 @@ static CHANSERV_FUNC(cmd_register) channel = AddChannel(argv[1], now, NULL, NULL, NULL); cData = register_channel(channel, user->handle_info->handle); - scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL); + scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL); cData->modes = chanserv_conf.default_modes; if(off_channel > 0) cData->modes.modes_set |= MODE_REGISTERED; @@ -2123,6 +2296,7 @@ static CHANSERV_FUNC(cmd_register) /* Initialize the channel's max user record. */ cData->max = channel->members.used; + cData->maxsetinfo = chanserv_conf.max_userinfo_length; if(handle != user->handle_info) reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name); @@ -2579,6 +2753,10 @@ static CHANSERV_FUNC(cmd_opchan) reply("CSMSG_ALREADY_OPCHANNED", channel->name); return 0; } + if(!IsInChannel(channel,chanserv)) { + reply("CSMSG_NOT_IN_CHANNEL", channel->name); + return 0; + } channel->channel_info->may_opchan = 0; mod_chanmode_init(&change); change.argc = 1; @@ -2650,8 +2828,19 @@ static CHANSERV_FUNC(cmd_adduser) return 0; } - actee = add_channel_user(channel->channel_info, handle, access, 0, NULL); + time_t accessexpiry = 0; + unsigned int duration = 0; + if (argc > 3) { + if ((duration = ParseInterval(argv[3]))) + accessexpiry = now + duration; + } + + actee = add_channel_user(channel->channel_info, handle, access, 0, NULL, accessexpiry); scan_user_presence(actee, NULL); + + if (duration > 0) + timeq_add(accessexpiry, chanserv_expire_tempuser, actee); + reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access); return 1; } @@ -2703,6 +2892,24 @@ static CHANSERV_FUNC(cmd_clvl) return 0; } + time_t clvlexpiry = 0; + unsigned int duration = 0; + if (argc > 3) { + if ((duration = ParseInterval(argv[3]))) + clvlexpiry = now + duration; + } + + if (duration > 0) { + if (victim->accessexpiry > 0) { + reply("CSMSG_NO_BUMP_EXPIRY"); + return 0; + } + + victim->clvlexpiry = clvlexpiry; + victim->lastaccess = victim->access; + timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim); + } + victim->access = new_access; reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name); return 1; @@ -2732,12 +2939,13 @@ static CHANSERV_FUNC(cmd_deluser) if(argc > 2) { access = user_level_from_name(argv[1], UL_OWNER); + char *useraccess = user_level_name_from_level(victim->access); if(!access) { reply("CSMSG_INVALID_ACCESS", argv[1]); return 0; } - if(access != victim->access) + if(strcasecmp(argv[1], useraccess)) { reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]); return 0; @@ -2824,6 +3032,10 @@ static CHANSERV_FUNC(cmd_mdelpeon) return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd); } +static CHANSERV_FUNC(cmd_mdelpal) +{ + return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd); +} static CHANSERV_FUNC(cmd_levels) { @@ -4184,9 +4396,9 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents)); if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED) - lData.table.width = 5; /* with level = 5 */ + lData.table.width = 6; /* with level = 6 */ else - lData.table.width = 4; /* without = 4 */ + lData.table.width = 5; /* without = 5 */ ary = malloc(lData.table.width*sizeof(**lData.table.contents)); lData.table.contents[0] = ary; if(user->handle_info) { @@ -4214,6 +4426,7 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg ary[i] = "Last Seen"; seen_index = i++; ary[i++] = "Status"; + ary[i++] = "Expiry"; for(matches = 1; matches < lData.table.length; ++matches) { struct userData *uData = lData.users[matches-1]; @@ -4258,7 +4471,23 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg ary[i++] = "Vacation"; else ary[i++] = "Normal"; + + if ((uData->accessexpiry > 0) || (uData->clvlexpiry > 0)) { + char delay[INTERVALLEN]; + time_t diff; + + if (uData->accessexpiry > 0) { + diff = uData->accessexpiry - now; + intervalString(delay, diff, user->handle_info); + } else { + diff = uData->clvlexpiry - now; + intervalString(delay, diff, user->handle_info); + } + ary[i++] = delay; + } else + ary[i++] = "Never"; } + send_list(&lData); for(matches = 1; matches < lData.table.length; ++matches) { @@ -5856,6 +6085,20 @@ static MODCMD_FUNC(chan_opt_usergreeting) return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting); } +static MODCMD_FUNC(chan_opt_maxsetinfo) +{ + unsigned int charmax; + + if(argc > 1) { + charmax = atoi(argv[1]); + if ((charmax > 0) && (charmax < chanserv_conf.max_userinfo_length)) + channel->channel_info->maxsetinfo = charmax; + } + + reply("CSMSG_SET_MAXSETINFO", channel->channel_info->maxsetinfo); + return 1; +} + static MODCMD_FUNC(chan_opt_modes) { struct mod_chanmode *new_modes; @@ -5956,6 +6199,16 @@ static MODCMD_FUNC(chan_opt_nodelete) static MODCMD_FUNC(chan_opt_dynlimit) { + struct mod_chanmode change; + + if (argc > 1) { + if (disabled_string(argv[1])) { + mod_chanmode_init(&change); + change.modes_clear |= MODE_LIMIT; + mod_chanmode_announce(chanserv, channel, &change); + } + } + CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT); } @@ -6155,7 +6408,7 @@ channel_multiple_option(enum charOption option, struct userNode *user, struct ch if(!isdigit(argv[1][0]) || (index < 0) || (index >= count)) { - reply("CSMSG_INVALID_NUMERIC", index); + reply("CSMSG_INVALID_NUMERIC", argv[1]); /* Show possible values. */ for(index = 0; index < count; index++) reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name)); @@ -6366,9 +6619,9 @@ static MODCMD_FUNC(user_opt_info) { size_t bp; infoline = unsplit_string(argv + 1, argc - 1, NULL); - if(strlen(infoline) > chanserv_conf.max_userinfo_length) + if(strlen(infoline) > channel->channel_info->maxsetinfo) { - reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length); + reply("CSMSG_INFOLINE_TOO_LONG", channel->channel_info->maxsetinfo); return 0; } bp = strcspn(infoline, "\001"); @@ -6509,7 +6762,7 @@ static CHANSERV_FUNC(cmd_giveownership) { if(force) { - new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL); + new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL, 0); } else { @@ -6542,8 +6795,8 @@ static CHANSERV_FUNC(cmd_giveownership) giveownership = calloc(1, sizeof(*giveownership)); giveownership->issued = now; - giveownership->old_owner = curr_user->handle->handle; - giveownership->target = new_owner_hi->handle; + giveownership->old_owner = strdup(curr_user->handle->handle); + giveownership->target = strdup(new_owner_hi->handle); giveownership->target_access = new_owner_old_access; if(override) { @@ -6772,6 +7025,316 @@ static CHANSERV_FUNC(cmd_wut) return 1; } +static CHANSERV_FUNC(cmd_roulette) +{ + if(channel) { + struct chanData *cData = channel->channel_info; + + if (cData) { + if (cData->roulette_chamber) { + DelUser(user, chanserv, 1, "BANG - Don't stuff bullets into a loaded gun"); + return 1; + } + + send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_ROULETTE_LOADS"); + cData->roulette_chamber = 1 + rand() % 6; + } + } + + return 1; +} +static CHANSERV_FUNC(cmd_shoot) +{ + if(channel) { + struct chanData *cData = channel->channel_info; + + if (cData->roulette_chamber <= 0) { + struct service *service; + if ((service = service_find(chanserv->nick))) { + reply("CSMSG_ROULETTE_NEW", service->trigger); + } + return 1; + } + + cData->roulette_chamber--; + + if (cData->roulette_chamber == 0) { + reply("CSMSG_ROULETTE_BANG"); + reply("CSMSG_ROULETTE_BETTER_LUCK", user->nick); + DelUser(user, chanserv, 1, "BANG!!!!"); + } else + reply("CSMSG_ROULETTE_CLICK"); + } + + return 1; +} + +static void +chanserv_remove_abuse(void *data) +{ + char *remnick = data; + struct userNode *user; + /* sometimes the clone was killed and maybe even the user took their nick back + * (ie, an oper) so dont kill them here after all unless they are local. */ + if( (user = GetUserH(remnick)) ) + if(IsLocal(user) ) + DelUser(user, NULL, 1, ""); +} + +int lamepart(struct userNode *nick) { + struct modeNode *mn; + unsigned int count, n; + + for (n=count=0; nchannels.used; n++) { + mn = nick->channels.list[n]; + irc_svspart(chanserv, nick, mn->channel); + } + + return 0; +} + +static CHANSERV_FUNC(cmd_spin) +{ + if(!channel) + return 1; + + int type = 0, lamep = 1; + char *tstr; + + tstr = conf_get_data("server/type", RECDB_QSTRING); + if(tstr) { + type = atoi(tstr); + if (type > 6) + lamep = 0; + } + + + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL1", user->nick); + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL2"); + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL3"); + + if(chanserv_conf.wheel->used < 1) { + /* wheel actions not defined! eek */ + return 1; + } + + const char *wheel = chanserv_conf.wheel->list[ (int) ( (chanserv_conf.wheel->used) * (rand() / (RAND_MAX + 1.0)) ) ]; + if(!wheel && *wheel) + return 1; + +/* enable this to be able to manually specify a result for testing: + log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel); + if(argc > 1) { + wheel = argv[1]; + } +*/ + + /* connection reset by peer */ + if (!strcasecmp(wheel, "peer")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PEER"); + if (type < 7) + irc_kill(chanserv, user, "Connection reset by peer"); + else + irc_svsquit(chanserv, user, "Connection reset by peer"); + } + /* part all channels */ + else if (!strcasecmp(wheel, "partall")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PARTALL"); + if (lamep) + lamepart(user); + else + sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now); + } + /* random time gline */ + else if (!strcasecmp(wheel, "gline")) { + char target[IRC_NTOP_MAX_SIZE + 3]; + int wtime = 120 + rand() % 600; + + strcpy(target, "*@"); + strcat(target, user->hostname); + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_GLINE"); + + gline_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1, 0); +// irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!"); + } + /* random shun */ + else if (!strcasecmp(wheel, "shun")) { + char target[IRC_NTOP_MAX_SIZE + 3]; + int wtime = 120 + rand() % 600; + + strcpy(target, "*@"); + strcat(target, user->hostname); + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SHUN"); + + shun_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1); + } + /* absolutely nothing */ + else if (!strcasecmp(wheel, "nothing")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NOTHING"); + } + /* join random chans and part em several times */ + else if (!strcasecmp(wheel, "randjoin")) { + int complete = 0; + int rndchans = 0; + int chango = 0; + int roundz0r = 0; + + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_RANDJOIN"); + while(complete != 1) { + if (rndchans != 15) { + chango = 120 + rand() % 600; + sputsock("%s SJ %s #%d %d", self->numeric, user->numeric, chango, now); + rndchans++; + } else { + if (roundz0r != 1) { + if (lamep) + lamepart(user); + else + sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now); + roundz0r = 1; + rndchans = 0; + } else { + if (lamep) + lamepart(user); + else + sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now); + complete = 1; + } + } + } + } + /* abuse line added to /whois */ + else if (!strcasecmp(wheel, "abusewhois")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_ABUSEWHOIS"); + irc_swhois(chanserv, user, "is being defecated on by services"); + } + /* kick from each channel your in */ + else if (!strcasecmp(wheel, "kickall")) { + unsigned int count, n; + struct modeNode *mn; + + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKALL"); + + for (n=count=0; nchannels.used; n++) { + mn = user->channels.list[n]; + irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!"); + } + } + /* random nick change */ + else if (!strcasecmp(wheel, "nickchange")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NICKCHANGE"); + + char *oldnick = NULL; + char *oldident = NULL; + char *oldhost = NULL; + char abusednick[NICKLEN] = ""; + int abusednum = 1 + (int) (10000.0 * (rand() / (RAND_MAX + 1.0))); + struct userNode *clone; + + oldnick = strdup(user->nick); + oldident = strdup(user->ident); + oldhost = strdup(user->hostname); + + //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120)); + while (1) { + snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120)); + log_module(MAIN_LOG, LOG_DEBUG, "Abused Nick: %s, Client Nick: %s", abusednick, user->nick); + if(!GetUserH(abusednick)) + break; + } + + SVSNickChange(user, abusednick); + irc_svsnick(chanserv, user, abusednick); + + clone = AddClone(oldnick, oldident, oldhost, "I got abused by the wheel of misfortune :D"); + timeq_add(now + 300, chanserv_remove_abuse, clone->nick); + } + /* kill */ + else if (!strcasecmp(wheel, "kill")) { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KILL"); + + DelUser(user, chanserv, 1, "Reward for spinning the wheel of misfortune!"); + //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!"); + } + /* service ignore */ + else if (!strcasecmp(wheel, "svsignore")) { + int gagged, ignoretime = 0; + char target[IRC_NTOP_MAX_SIZE + 13]; + + if(IsOper(user)) { + /* we cant gag opers, so just verbally abuse them */ + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE_OPER"); + return 1; + } + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE"); + + strcpy(target, "*!*@"); + strcat(target, user->hostname); + ignoretime = now + (1 + rand() % 120); + + gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime); + } + /* kick and ban from each channel your in */ + else if (!strcasecmp(wheel, "kickbanall")) { + unsigned int count, n; + struct modeNode *mn; + //char ban[IRC_NTOP_MAX_SIZE + 1]; + + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKBANALL"); + + //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname); + for (n=count=0; nchannels.used; n++) { + struct mod_chanmode *change; +/* struct banData *bData; */ + unsigned int exists; +/* int duration = 300; */ + char *ban; + + ban = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT|GENMASK_USENICK); + + log_module(MAIN_LOG, LOG_DEBUG, "Generated ban %s", ban); + mn = user->channels.list[n]; + if(mn->channel->banlist.used >= MAXBANS) { + reply("CSMSG_BANLIST_FULL", mn->channel->name); + free(ban); + continue; + } + +/* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */ + + change = mod_chanmode_alloc(1); + change->args[0].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE; + change->args[0].u.member = GetUserMode(mn->channel, user); + change->argc = 1; + + mod_chanmode_announce(chanserv, mn->channel, change); + mod_chanmode_free(change); + + exists = ChannelBanExists(mn->channel, ban); + if(!exists) { + change = mod_chanmode_alloc(1); + change->args[0].mode = MODE_BAN; + change->args[0].u.hostmask = ban; + change->argc = 1; + mod_chanmode_announce(chanserv, mn->channel, change); + mod_chanmode_free(change); + } + + if(exists) { + reply("CSMSG_REDUNDANT_BAN", ban, mn->channel->name); + free(ban); + } + + irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!"); + } + } + else { + send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_UNKNOWN", wheel); + } + + return 1; +} + #ifdef lame8ball static CHANSERV_FUNC(cmd_8ball) { @@ -7099,7 +7662,32 @@ handle_new_channel(struct chanNode *channel) } int -check_bans(struct userNode *user, char *channel) +trace_check_bans(struct userNode *user, struct chanNode *chan) +{ + struct banData *bData; + struct mod_chanmode *change; + + change = find_matching_bans(&chan->banlist, user, NULL); + if (change) + return 1; + + /* lamer list */ + if (chan->channel_info) { + for(bData = chan->channel_info->bans; bData; bData = bData->next) { + + if(!user_matches_glob(user, bData->mask, MATCH_USENICK)) + continue; + + if(bData) + return 1; + } + } + + return 0; +} + +int +check_bans(struct userNode *user, const char *channel) { struct chanNode *chan; struct mod_chanmode change; @@ -7112,6 +7700,9 @@ check_bans(struct userNode *user, char *channel) if(!(cData = chan->channel_info)) return 0; + mod_chanmode_init(&change); + change.argc = 1; + if(chan->banlist.used < MAXBANS) { /* Not joining through a ban. */ @@ -7152,6 +7743,18 @@ check_bans(struct userNode *user, char *channel) return 0; } +int +channel_user_is_exempt(struct userNode *user, struct chanNode *channel) +{ + unsigned int ii; + for(ii = 0; ii < channel->exemptlist.used; ii++) + { + if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK)) + return true; + } + return false; +} + /* Welcome to my worst nightmare. Warning: Read (or modify) the code below at your own risk. */ @@ -7168,7 +7771,7 @@ handle_join(struct modeNode *mNode) unsigned int modes = 0, info = 0; char *greeting; - if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info)) + if(IsLocal(user) || !channel || !channel->channel_info || IsSuspended(channel->channel_info)) return 0; cData = channel->channel_info; @@ -7211,13 +7814,12 @@ handle_join(struct modeNode *mNode) } - mod_chanmode_init(&change); change.argc = 1; /* TODO: maybe only people above inviteme level? -Rubin */ /* We don't kick people with access */ - if(!uData) + if(!uData && !channel_user_is_exempt(user, channel)) { if(channel->banlist.used < MAXBANS) { @@ -7875,6 +8477,24 @@ chanserv_conf_read(void) chanserv_conf.default_modes = *change; mod_chanmode_free(change); } + free_string_list(chanserv_conf.wheel); + strlist = database_get_data(conf_node, "wheel", RECDB_STRING_LIST); + if(strlist) + strlist = string_list_copy(strlist); + else + { + static const char *list[] = { + "peer", "partall", "gline", /* "shun", */ + "nothing", "randjoin", "abusewhois", "kickall", + "nickchange", "kill", "svsignore", "kickbanall", + NULL}; + unsigned int ii; + strlist = alloc_string_list(ArrayLength(list)-1); + for(ii=0; list[ii]; ii++) + string_list_append(strlist, strdup(list[ii])); + } + chanserv_conf.wheel = strlist; + free_string_list(chanserv_conf.set_shows); strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST); if(strlist) @@ -7905,6 +8525,7 @@ chanserv_conf_read(void) * function list as invalid, so it will be initialized. */ set_shows_list.used = 0; + free_string_list(chanserv_conf.eightball); strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST); if(strlist) @@ -7919,6 +8540,7 @@ chanserv_conf_read(void) string_list_append(strlist, strdup("Maybe so.")); } chanserv_conf.eightball = strlist; + free_string_list(chanserv_conf.old_ban_names); strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST); if(strlist) @@ -7991,9 +8613,9 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) { struct handle_info *handle; struct userData *uData; - char *seen, *inf, *flags, *expires; + char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc; time_t last_seen; - unsigned short access; + unsigned short access, lastaccess = 0; if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object)) { @@ -8013,6 +8635,11 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now; flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING); expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING); + accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING); + clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING); + lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING); + lastaccess = lstacc ? atoi(lstacc) : 0; + handle = get_handle_info(key); if(!handle) { @@ -8020,10 +8647,20 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) return; } - uData = add_channel_user(chan, handle, access, last_seen, inf); + uData = add_channel_user(chan, handle, access, last_seen, inf, 0); uData->flags = flags ? strtoul(flags, NULL, 0) : 0; uData->expires = expires ? strtoul(expires, NULL, 0) : 0; + uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0; + if (uData->accessexpiry > 0) + timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData); + + uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0; + if (uData->clvlexpiry > 0) + timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData); + + uData->lastaccess = lastaccess; + if((uData->flags & USER_SUSPENDED) && uData->expires) { if(uData->expires > now) @@ -8289,6 +8926,9 @@ chanserv_channel_read(const char *key, struct record_data *hir) str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING); cData->topic = str ? strdup(str) : NULL; + str = database_get_data(channel, KEY_MAXSETINFO, RECDB_QSTRING); + cData->maxsetinfo = str ? strtoul(str, NULL, 0) : chanserv_conf.max_userinfo_length; + if(!IsSuspended(cData) && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING)) && (argc = split_line(str, 0, ArrayLength(argv), argv)) @@ -8422,6 +9062,9 @@ chanserv_write_users(struct saxdb_context *ctx, struct userData *uData) saxdb_start_record(ctx, uData->handle->handle, 0); saxdb_write_int(ctx, KEY_LEVEL, uData->access); saxdb_write_int(ctx, KEY_SEEN, uData->seen); + saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry); + saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry); + saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess); if(uData->flags) saxdb_write_int(ctx, KEY_FLAGS, uData->flags); if(uData->expires) @@ -8534,6 +9177,9 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel) } saxdb_end_record(ctx); + if (channel->maxsetinfo) + saxdb_write_int(ctx, KEY_MAXSETINFO, channel->maxsetinfo); + if(channel->modes.modes_set || channel->modes.modes_clear) { mod_chanmode_format(&channel->modes, buf); @@ -8661,6 +9307,7 @@ chanserv_db_cleanup(void) { dict_delete(note_types); free_string_list(chanserv_conf.eightball); free_string_list(chanserv_conf.old_ban_names); + free_string_list(chanserv_conf.wheel); free_string_list(chanserv_conf.set_shows); free(set_shows_list.list); free(uset_shows_list.list); @@ -8681,6 +9328,7 @@ init_chanserv(const char *nick) { struct chanNode *chan; unsigned int i; + CS_LOG = log_register_type("ChanServ", "file:chanserv.log"); conf_register_reload(chanserv_conf_read); @@ -8732,6 +9380,7 @@ init_chanserv(const char *nick) DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL); DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); + DEFINE_COMMAND(mdelpal, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(levels, 1, 0, NULL); @@ -8816,6 +9465,9 @@ init_chanserv(const char *nick) DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL); DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL); DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL); + DEFINE_COMMAND(roulette, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL); + DEFINE_COMMAND(shoot, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL); + DEFINE_COMMAND(spin, 1, MODCMD_REQUIRE_AUTHED, "spin", "+nolog,+toy,+acceptchan", NULL); /* Channel options */ DEFINE_CHANNEL_OPTION(defaulttopic); @@ -8841,6 +9493,7 @@ init_chanserv(const char *nick) DEFINE_CHANNEL_OPTION(ctcpreaction); DEFINE_CHANNEL_OPTION(bantimeout); DEFINE_CHANNEL_OPTION(inviteme); + DEFINE_CHANNEL_OPTION(maxsetinfo); if(off_channel > 1) DEFINE_CHANNEL_OPTION(offchannel); modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL); @@ -8893,3 +9546,4 @@ init_chanserv(const char *nick) reg_exit_func(chanserv_db_cleanup); message_register_table(msgtab); } +