X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/1bcd5f19c2dc83addddf93dfa9a465dd7642586b..5a1daaaba65bb61921c1274fd8672cb91bae4268:/src/chanserv.c diff --git a/src/chanserv.c b/src/chanserv.c index daf8687..9f6b80c 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -38,6 +38,7 @@ #define KEY_ADJUST_DELAY "adjust_delay" #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq" #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay" +#define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq" #define KEY_MAX_CHAN_USERS "max_chan_users" #define KEY_MAX_CHAN_BANS "max_chan_bans" #define KEY_NICK "nick" @@ -129,9 +130,11 @@ static const struct message_entry msgtab[] = { { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." }, { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." }, { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." }, - { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." }, + { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." }, { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." }, - { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." }, + { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." }, + { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." }, + { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." }, /* Do-not-register channels */ { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." }, @@ -150,7 +153,7 @@ static const struct message_entry msgtab[] = { { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." }, { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." }, { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." }, - { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." }, + { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." }, /* Channel moving */ { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." }, @@ -268,36 +271,37 @@ static const struct message_entry msgtab[] = { { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" }, { "CSMSG_SET_MODES", "$bModes $b %s" }, { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" }, - { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" }, + { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." }, { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" }, - { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" }, + { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." }, /* { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" }, { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" }, */ { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" }, - { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" }, - { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" }, - { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" }, + { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." }, + { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." }, + { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." }, /* { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" }, */ - { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" }, - { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" }, - { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" }, - { "CSMSG_SET_SETTERS", "$bSetters $b %d" }, - { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" }, + { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." }, + { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." }, + { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." }, + { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." }, { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" }, { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" }, { "CSMSG_SET_TOYS", "$bToys $b %d - %s" }, { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" }, { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" }, + { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" }, { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" }, { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" }, { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" }, { "CSMSG_USET_INFO", "$bInfo $b %s" }, { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." }, + { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */ { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." }, { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." }, { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." }, @@ -317,15 +321,25 @@ static const struct message_entry msgtab[] = { { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." }, { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." }, { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." }, + { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." }, { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." }, { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." }, { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." }, { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." }, + + { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" }, { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" }, { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" }, { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" }, { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" }, + + { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."}, + { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."}, + { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."}, + { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."}, + { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."}, + { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."}, { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." }, { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" }, @@ -459,7 +473,7 @@ static const struct message_entry msgtab[] = { /* User settings */ { "CSMSG_USER_OPTIONS", "User Options:" }, - { "CSMSG_USER_PROTECTED", "That user is protected." }, +// { "CSMSG_USER_PROTECTED", "That user is protected." }, /* Toys */ { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" }, @@ -475,6 +489,7 @@ static const struct message_entry msgtab[] = { /* Other things */ { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" }, + { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" }, { NULL, NULL } }; @@ -520,6 +535,7 @@ static struct unsigned long db_backup_frequency; unsigned long channel_expire_frequency; + unsigned long ban_timeout_frequency; long info_delay; unsigned int adjust_delay; @@ -614,6 +630,7 @@ static const struct { { "helper", "BUG:", UL_HELPER, 'X' } }; +/* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */ static const struct { char *format_name; char *db_name; @@ -622,16 +639,16 @@ static const struct { unsigned int old_flag; unsigned short flag_value; } levelOptions[] = { - { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 }, - { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL, 0 }, - { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 }, +// { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 }, +// { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL, 0 }, +// { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 }, // these 3 need removed, but causes segs if its still in the db.. { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 }, { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 }, { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 }, { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 }, { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 }, { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 }, - { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 }, +// { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 }, { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 }, { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 }, { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 } @@ -660,10 +677,18 @@ struct charOptionValues { { '3', "CSMSG_TOPICREFRESH_12_HOURS" }, { '4', "CSMSG_TOPICREFRESH_24_HOURS" } }, ctcpReactionValues[] = { + { 'n', "CSMSG_CTCPREACTION_NONE" }, { 'k', "CSMSG_CTCPREACTION_KICK" }, { 'b', "CSMSG_CTCPREACTION_KICKBAN" }, { 't', "CSMSG_CTCPREACTION_SHORTBAN" }, { 'T', "CSMSG_CTCPREACTION_LONGBAN" } +}, banTimeoutValues[] = { + { '0', "CSMSG_BANTIMEOUT_NONE" }, + { '1', "CSMSG_BANTIMEOUT_10M" }, + { '2', "CSMSG_BANTIMEOUT_2H" }, + { '3', "CSMSG_BANTIMEOUT_4H" }, + { '4', "CSMSG_BANTIMEOUT_1D" }, + { '5', "CSMSG_BANTIMEOUT_1W" } }; static const struct { @@ -678,7 +703,8 @@ static const struct { { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues }, { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues }, { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues }, - { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues } + { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }, + { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues } }; struct userData *helperList; @@ -861,9 +887,15 @@ chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, || IsService(user) || !ircncasecmp(text, "ACTION ", 7)) return; - /* Figure out the minimum level needed to CTCP the channel */ - if(check_user_level(channel, user, lvlCTCPUsers, 1, 0)) - return; + /* We dont punish people we know -Rubin + * * Figure out the minimum level needed to CTCP the channel * + * + * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0)) + * return; + */ + /* If they are a user of the channel, they are exempt */ + if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0)) + return; /* We need to enforce against them; do so. */ eflags = 0; argv[0] = text; @@ -872,7 +904,10 @@ chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, if(GetUserMode(channel, user)) eflags |= ACTION_KICK; switch(channel->channel_info->chOpts[chCTCPReaction]) { - default: case 'k': /* just do the kick */ break; + default: case 'n': return; + case 'k': + eflags |= ACTION_KICK; + break; case 'b': eflags |= ACTION_BAN; break; @@ -1887,6 +1922,8 @@ static CHANSERV_FUNC(cmd_register) char *chan_name; unsigned int new_channel, force=0; struct do_not_register *dnr; + unsigned int n; + if(channel) { @@ -1902,8 +1939,7 @@ static CHANSERV_FUNC(cmd_register) return 0; } - if(!IsHelping(user) - && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP))) + if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP))) { reply("CSMSG_MUST_BE_OPPED", channel->name); return 0; @@ -1948,11 +1984,36 @@ static CHANSERV_FUNC(cmd_register) return 0; force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force"); dnr = chanserv_is_dnr(chan_name, handle); + + /* Check if they are over the limit.. */ + if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force) + { + reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned); + return 0; + } + } else { - handle = user->handle_info; + handle = user->handle_info; dnr = chanserv_is_dnr(chan_name, handle); + /* Check if they are over the limit.. */ + if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force) + { + reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned); + return 0; + } + /* Check if another service is in the channel */ + if(channel) + for(n = 0; n < channel->members.used; n++) + { + mn = channel->members.list[n]; + if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user)) + { + reply("CSMSG_ANOTHER_SERVICE"); + return 0; + } + } } if(dnr && !force) { @@ -1963,11 +2024,13 @@ static CHANSERV_FUNC(cmd_register) return 0; } + /* now handled above for message specilization * if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force) { reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned); return 0; } + */ if(new_channel) channel = AddChannel(argv[1], now, NULL, NULL, NULL); @@ -2059,7 +2122,7 @@ static CHANSERV_FUNC(cmd_unregister) confirm_string = make_confirmation_string(uData); if((argc < 2) || strcmp(argv[1], confirm_string)) { - reply("CSMSG_CONFIRM_UNREG", confirm_string); + reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string); return 0; } } @@ -3007,19 +3070,24 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(IsService(victim)) { - reply("MSG_SERVICE_IMMUNE", victim->nick); + if(cmd) + reply("MSG_SERVICE_IMMUNE", victim->nick); return 0; } if((action == ACTION_KICK) && !victimCount) { - reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name); + if(cmd) + reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name); return 0; } if(protect_user(victim, user, channel->channel_info)) { - reply("CSMSG_USER_PROTECTED", victim->nick); + // This translates to send_message(user, cmd->parent->bot, ...) + // if user is x3 (ctcp action) cmd is null and segfault. + if(cmd) + reply("CSMSG_USER_PROTECTED", victim->nick); return 0; } @@ -3030,7 +3098,8 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c { if(!is_ircmask(argv[1])) { - reply("MSG_NICK_UNKNOWN", argv[1]); + if(cmd) + reply("MSG_NICK_UNKNOWN", argv[1]); return 0; } @@ -3038,7 +3107,8 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(bad_channel_ban(channel, user, argv[1], &victimCount, victims)) { - reply("CSMSG_MASK_PROTECTED", argv[1]); + if(cmd) + reply("CSMSG_MASK_PROTECTED", argv[1]); return 0; } /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/ @@ -3048,14 +3118,16 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c #ifdef entropy_lameness if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user)) { - reply("CSMSG_LAME_MASK", argv[1]); + if(cmd) + reply("CSMSG_LAME_MASK", argv[1]); return 0; } #endif if((action == ACTION_KICK) && (victimCount == 0)) { - reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]); + if(cmd) + reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]); return 0; } @@ -3072,7 +3144,8 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */ { - reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */ + if(cmd) + reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */ free(ban); return 0; } @@ -3083,13 +3156,15 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(duration < 15) { - reply("CSMSG_DURATION_TOO_LOW"); + if(cmd) + reply("CSMSG_DURATION_TOO_LOW"); free(ban); return 0; } else if(duration > (86400 * 365 * 2)) { - reply("CSMSG_DURATION_TOO_HIGH"); + if(cmd) + reply("CSMSG_DURATION_TOO_HIGH"); free(ban); return 0; } @@ -3144,7 +3219,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(!cmd) { - /* automated kickban */ + /* automated kickban, dont reply */ } else if(duration) reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info)); @@ -3335,6 +3410,68 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas return change; } +void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */ +{ + unsigned int jj, ii, count; + struct banNode *bn; + struct chanData *channel; + time_t bantimeout; + struct mod_chanmode *change; + + log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans"); + /* Walk through every channel */ + for(channel = channelList; channel; channel = channel->next) { + switch(channel->chOpts[chBanTimeout]) + { + default: case '0': continue; /* Dont remove bans in this chan */ + case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */ + case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */ + case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */ + case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */ + case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */ + } + count = 0; + /* First find out how many bans were going to unset */ + for (jj=0; jj < channel->channel->banlist.used; ++jj) { + if(channel->channel->banlist.list[jj]->set < bantimeout) + count++; + } + if(count > 0) { + /* At least one ban, so setup a removal */ + change = mod_chanmode_alloc(count); + ii = 0; + /* Walk over every ban in this channel.. */ + for (jj=0; jj < channel->channel->banlist.used; ++jj) { + bn = channel->channel->banlist.list[jj]; + if (bn->set < bantimeout) { + log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name); + + /* Add this ban to the mode change */ + change->args[ii].mode = MODE_REMOVE | MODE_BAN; + change->args[ii].u.hostmask = strdup(bn->ban); + ii++; + /* Pull this ban out of the list */ + banList_remove(&(channel->channel->banlist), bn); + jj--; + free(bn); + } + } + /* Send the modes to IRC */ + mod_chanmode_announce(chanserv, channel->channel, change); + + /* free memory from strdup above */ + for(ii = 0; ii < count; ++ii) + free((char*)change->args[ii].u.hostmask); + + mod_chanmode_free(change); + } + } + /* Set this function to run again */ + if(chanserv_conf.ban_timeout_frequency) + timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL); +} + + static int unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action) { @@ -3658,7 +3795,7 @@ static CHANSERV_FUNC(cmd_access) return 1; } -/* This is never used... */ +/* This is never used... static void zoot_list(struct listData *list) { @@ -3708,6 +3845,7 @@ zoot_list(struct listData *list) } } } +*/ static void def_list(struct listData *list) @@ -3717,12 +3855,13 @@ def_list(struct listData *list) send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search); else send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest)); - table_send(list->bot, list->user->nick, 0, NULL, list->table); if(list->table.length == 1) { msg = user_find_message(list->user, "MSG_NONE"); send_message_type(4, list->user, list->bot, " %s", msg); } + else + table_send(list->bot, list->user->nick, 0, NULL, list->table); } static int @@ -3755,7 +3894,7 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg lData.search = (argc > 1) ? argv[1] : NULL; send_list = def_list; /* What does the following line do exactly?? */ - (void)zoot_list; /* since it doesn't show user levels */ + /*(void)zoot_list; ** since it doesn't show user levels */ /* this does nothing!! -rubin if(user->handle_info) @@ -4478,7 +4617,7 @@ static CHANSERV_FUNC(cmd_resync) { if(mn->modes & MODE_CHANOP) { - changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP); + changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; changes->args[used++].u.member = mn; } if(!(mn->modes & MODE_HALFOP)) @@ -4486,22 +4625,24 @@ static CHANSERV_FUNC(cmd_resync) changes->args[used].mode = MODE_HALFOP; changes->args[used++].u.member = mn; } + /* why cant halfops keep voice if(mn->modes & MODE_VOICE) { changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE); changes->args[used++].u.member = mn; } + */ } else if(uData && uData->access >= UL_PEON /* cData->lvlOpts[lvlGiveVoice]*/) { if(mn->modes & MODE_CHANOP) { - changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP); + changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; changes->args[used++].u.member = mn; } if(mn->modes & MODE_HALFOP) { - changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP); + changes->args[used].mode = MODE_REMOVE | MODE_HALFOP; changes->args[used++].u.member = mn; } if(!(mn->modes & MODE_VOICE)) @@ -4730,6 +4871,23 @@ static CHANSERV_FUNC(cmd_delnote) return 1; } +static CHANSERV_FUNC(cmd_last) +{ + int numoflines; + + REQUIRE_PARAMS(1); + + numoflines = (argc > 1) ? atoi(argv[1]) : 10; + + if(numoflines < 1 || numoflines > 200) + { + reply("CSMSG_LAST_INVALID"); + return 0; + } + ShowLog(user, channel, "*", "*", "*", "*", numoflines); + return 1; +} + static CHANSERV_FUNC(cmd_events) { struct logSearch discrim; @@ -5535,11 +5693,6 @@ static MODCMD_FUNC(chan_opt_setters) return channel_level_option(lvlSetters, CSFUNC_ARGS); } -static MODCMD_FUNC(chan_opt_ctcpusers) -{ - return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS); -} - static MODCMD_FUNC(chan_opt_userinfo) { return channel_level_option(lvlUserInfo, CSFUNC_ARGS); @@ -5562,6 +5715,19 @@ static MODCMD_FUNC(chan_opt_inviteme) return channel_level_option(lvlInviteMe, CSFUNC_ARGS); } +/* TODO: Make look like this when no args are + * given: + * -X3- ------------------------------- + * -X3- BanTimeout: Bans are removed: + * -X3- ----- * indicates current ----- + * -X3- 0: [*] Never. + * -X3- 1: [ ] After 10 minutes. + * -X3- 2: [ ] After 2 hours. + * -X3- 3: [ ] After 4 hours. + * -X3- 4: [ ] After 24 hours. + * -X3- 5: [ ] After one week. + * -X3- ------------- End ------------- + */ static int channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd) { @@ -5622,6 +5788,11 @@ static MODCMD_FUNC(chan_opt_ctcpreaction) return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS); } +static MODCMD_FUNC(chan_opt_bantimeout) +{ + return channel_multiple_option(chBanTimeout, CSFUNC_ARGS); +} + static MODCMD_FUNC(chan_opt_topicrefresh) { return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS); @@ -6094,6 +6265,7 @@ static CHANSERV_FUNC(cmd_wut) return 1; } +#ifdef lame8ball static CHANSERV_FUNC(cmd_8ball) { unsigned int i, j, accum; @@ -6116,6 +6288,169 @@ static CHANSERV_FUNC(cmd_8ball) return 1; } +#else /* Use cool 8ball instead */ + +void eightball(char *outcome, int method, unsigned int seed) +{ + int answer = 0; + +#define NUMOFCOLORS 18 + char ballcolors[50][50] = {"blue", "red", "green", "yellow", + "white", "black", "grey", "brown", + "yellow", "pink", "purple", "orange", "teal", "burgandy", + "fuchsia","turquoise","magenta", "cyan"}; +#define NUMOFLOCATIONS 50 + char balllocations[50][55] = { + "Locke's house", "Oregon", "California", "Indiana", "Canada", + "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima", + "the Caribbean", "the Everglades", "your head", "your pants", "your school", + "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU", + "Locke's cat", "the closet", "the washroom", "the lake", "Spain", + "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter", + "Uranus", "Pluto", "a dark place", "your undies", "your shirt", + "your bra", "your hair", "your bed", "the couch", "the wall", + "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands", + "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" }; +#define NUMOFPREPS 15 + char ballpreps[50][50] = { + "Near", "Somewhere near", "In", "In", "In", + "In", "Hiding in", "Under", "Next to", "Over", + "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"}; +#define NUMOFNUMS 34 + char ballnums[50][50] = { + "A hundred", "A thousand", "A few", "42", + "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten", + }; +#define NUMOFMULTS 8 + char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""}; + + /* Method: + * 0: normal (Not used in x3) + * 1: color + * 2: where is + * 3: how many + */ + + srand(seed); + if (method == 1) /* A Color */ + { + char tmp[MAXLEN]; + + answer = (rand() % 12); /* Make sure this is the # of entries */ + switch(answer) + { + case 0: strcpy(tmp, "Very bright %s, I'd say."); + break; + case 1: strcpy(tmp, "Sort of a light %s color."); + break; + case 2: strcpy(tmp, "Dark and dreary %s."); + break; + case 3: strcpy(tmp, "Quite a pale shade of %s."); + break; + case 4: strcpy(tmp, "A gross kind of mucky %s."); + break; + case 5: strcpy(tmp, "Brilliant whiteish %s."); + break; + case 6: case 7: case 8: case 9: strcpy(tmp, "%s."); + break; + case 10: strcpy(tmp, "Solid %s."); + break; + case 11: strcpy(tmp, "Transparent %s."); + break; + default: strcpy(outcome, "An invalid random number was generated."); + return; + } + sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]); + return; + } + else if (method == 2) /* Location */ + { + sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]); + } + else if (method == 3) /* Number of ___ */ + { + sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]); + } + else + { + //Debug(DBGWARNING, "Error in 8ball."); + } + return; +} + +static CHANSERV_FUNC(cmd_8ball) +{ + char *word1, *word2, *word3; + static char eb[MAXLEN]; + unsigned int accum, i, j; + + REQUIRE_PARAMS(2); + accum = 0; + for(i=1; i2?argv[2]:""; + word3 = argc>3?argv[3]:""; + +/*** COLOR *****/ + if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0) + eightball(eb, 1, accum); + else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0) + eightball(eb, 1, accum); + else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0) + eightball(eb, 1, accum); +/*** LOCATION *****/ + else if( + ( + word2 && + ( + (strcasecmp(word1, "where") == 0) && + (strcasecmp(word2, "is") == 0) + ) + ) || + ( + strcasecmp(word1, "where's") == 0 + ) + ) + eightball(eb, 2, accum); +/*** NUMBER *****/ + else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0) + eightball(eb, 3, accum); +/*** GENERIC *****/ + else + { + /* Generic 8ball question.. so pull from x3.conf srvx style */ + const char *resp; + + resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used]; + if(channel) + { + char response[MAXLEN]; + sprintf(response, "\002%s\002: %s", user->nick, resp); + irc_privmsg(cmd->parent->bot, channel->name, response); + } + else + send_message_type(4, user, cmd->parent->bot, "%s", resp); + return 1; + } + + if(channel) + { + char response[MAXLEN]; + sprintf(response, "\002%s\002: %s", user->nick, eb); + irc_privmsg(cmd->parent->bot, channel->name, response); + } + else + send_message_type(4, user, cmd->parent->bot, "%s", eb); + return 1; +} +#endif + static CHANSERV_FUNC(cmd_d) { unsigned long sides, count, modifier, ii, total; @@ -6575,7 +6910,7 @@ handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *c if(protect_user(victim, kicker, channel->channel_info)) { - const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED"); + const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK"); KickChannelUser(kicker, channel, chanserv, reason); } @@ -6844,6 +7179,8 @@ chanserv_conf_read(void) chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30; str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING); chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400; + str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING); + chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600; str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING); chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30; str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING); @@ -6894,11 +7231,11 @@ chanserv_conf_read(void) "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes", /* options based on user level */ "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps", - "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers", + "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", /*"CtcpUsers", */ /* multiple choice options */ "CtcpReaction", "Protect", "Toys", "TopicRefresh", /* binary options */ - "DynLimit", "NoDelete", + "DynLimit", "NoDelete", "BanTimeout", /* delimiter */ NULL }; @@ -7704,6 +8041,7 @@ init_chanserv(const char *nick) DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL); + DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL); DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL); DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL); @@ -7776,8 +8114,8 @@ init_chanserv(const char *nick) DEFINE_CHANNEL_OPTION(toys); DEFINE_CHANNEL_OPTION(setters); DEFINE_CHANNEL_OPTION(topicrefresh); - DEFINE_CHANNEL_OPTION(ctcpusers); DEFINE_CHANNEL_OPTION(ctcpreaction); + DEFINE_CHANNEL_OPTION(bantimeout); DEFINE_CHANNEL_OPTION(inviteme); if(off_channel > 1) DEFINE_CHANNEL_OPTION(offchannel); @@ -7809,6 +8147,9 @@ init_chanserv(const char *nick) if(chanserv_conf.channel_expire_frequency) timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL); + if(chanserv_conf.ban_timeout_frequency) + timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL); + if(chanserv_conf.refresh_period) { time_t next_refresh;