X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/b8daef0fa02170e5c13f36a4c12447ff494a4104..921592dd1adaed54948c030be0daceb7e0c9d5b7:/src/chanserv.c diff --git a/src/chanserv.c b/src/chanserv.c index 275c1c2..6bdff9f 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -1,7 +1,7 @@ /* chanserv.c - Channel service bot * Copyright 2000-2004 srvx Development Team * - * This file is part of srvx. + * This file is part of x3. * * srvx is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,8 +42,6 @@ #define KEY_MAX_CHAN_BANS "max_chan_bans" #define KEY_NICK "nick" #define KEY_OLD_CHANSERV_NAME "old_chanserv_name" -#define KEY_MAX_SWITCH_LOAD "max_switch_load" -#define KEY_SWITCH_TIMEOUT "switch_timeout" #define KEY_8BALL_RESPONSES "8ball" #define KEY_OLD_BAN_NAMES "old_ban_names" #define KEY_REFRESH_PERIOD "refresh_period" @@ -55,6 +53,7 @@ #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet" #define KEY_NODELETE_LEVEL "nodelete_level" #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length" +#define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout" /* ChanServ database */ #define KEY_CHANNELS "channels" @@ -100,6 +99,7 @@ #define KEY_MAX "max" #define KEY_NOTES "notes" #define KEY_TOPIC_MASK "topic_mask" +#define KEY_OWNER_TRANSFER "owner_transfer" /* User data */ #define KEY_LEVEL "level" @@ -172,8 +172,9 @@ static const struct message_entry msgtab[] = { { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." }, { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." }, { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." }, + { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." }, { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." }, - { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." }, + { "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." }, @@ -195,6 +196,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_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$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." }, { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." }, { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." }, @@ -240,7 +242,8 @@ static const struct message_entry msgtab[] = { { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." }, { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." }, { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." }, - { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." }, + { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." }, + { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." }, { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." }, { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." }, { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." }, @@ -255,9 +258,11 @@ static const struct message_entry msgtab[] = { { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" }, { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" }, { "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_GIVE_OPS", "$bGiveOps $b %d" }, { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" }, { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" }, @@ -275,9 +280,12 @@ static const struct message_entry msgtab[] = { { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." }, { "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." }, { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." }, + { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." }, { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." }, + { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." }, { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." }, { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." }, { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." }, @@ -494,6 +502,7 @@ static struct unsigned int greeting_length; unsigned int refresh_period; + unsigned int giveownership_period; unsigned int max_owned; unsigned int max_chan_users; @@ -568,6 +577,7 @@ static const struct { char ch; } accessLevels[] = { { "peon", "Peon", UL_PEON, '+' }, + { "halfop", "HalfOp", UL_HALFOP, '%' }, { "op", "Op", UL_OP, '@' }, { "manager", "Manager", UL_MANAGER, '%' }, { "coowner", "Coowner", UL_COOWNER, '*' }, @@ -584,8 +594,10 @@ static const struct { 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_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 }, @@ -1078,6 +1090,7 @@ register_channel(struct chanNode *cNode, char *registrar) channel->registered = now; channel->visited = now; channel->limitAdjusted = now; + channel->ownerTransfer = now; channel->flags = CHANNEL_DEFAULT_FLAGS; for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt) channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value; @@ -1193,13 +1206,13 @@ add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t if(irccasecmp(mask + l1 - l2, old_name)) continue; new_mask = alloca(MAXLEN); - sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix); + sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix); mask = new_mask; } safestrncpy(bd->mask, mask, sizeof(bd->mask)); if(owner) safestrncpy(bd->owner, owner, sizeof(bd->owner)); - bd->reason = reason ? strdup(reason) : NULL; + bd->reason = strdup(reason); if(expires) timeq_add(expires, expire_ban, bd); @@ -1255,7 +1268,7 @@ expire_ban(void *data) { change.argc = 1; change.args[0].mode = MODE_REMOVE|MODE_BAN; - change.args[0].hostmask = bd->mask; + change.args[0].u.hostmask = bd->mask; mod_chanmode_announce(chanserv, bd->channel->channel, &change); break; } @@ -1287,9 +1300,12 @@ unregister_channel(struct chanData *channel, const char *reason) timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN); - mod_chanmode_init(&change); - change.modes_clear |= MODE_REGISTERED; - mod_chanmode_announce(chanserv, channel->channel, &change); + if(off_channel > 0) + { + mod_chanmode_init(&change); + change.modes_clear |= MODE_REGISTERED; + mod_chanmode_announce(chanserv, channel->channel, &change); + } while(channel->users) del_channel_user(channel->users, 0); @@ -1437,6 +1453,24 @@ validate_op(struct userNode *user, struct chanNode *channel, struct userNode *vi return 1; } +static int +validate_halfop(struct userNode *user, struct chanNode *channel, struct userNode *victim) +{ + struct chanData *cData = channel->channel_info; + struct userData *cs_victim; + + if((!(cs_victim = GetChannelUser(cData, victim->handle_info)) + || (cs_victim->access < cData->lvlOpts[lvlGiveHalfOps])) + && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0)) + { + send_message(user, chanserv, "CSMSG_HOPBY_LOCKED"); + return 0; + } + + return 1; +} + + static int validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim) { @@ -1455,6 +1489,24 @@ validate_deop(struct userNode *user, struct chanNode *channel, struct userNode * return 1; } +static int +validate_dehop(struct userNode *user, struct chanNode *channel, struct userNode *victim) +{ + if(IsService(victim)) + { + send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick); + return 0; + } + + if(protect_user(victim, user, channel->channel_info)) + { + send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick); + return 0; + } + + return 1; +} + static struct do_not_register * chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason) { @@ -1473,14 +1525,14 @@ chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason) } static struct dnrList -chanserv_find_dnrs(const char *chan_name, struct handle_info *handle) +chanserv_find_dnrs(const char *chan_name, const char *handle) { struct dnrList list; dict_iterator_t it; struct do_not_register *dnr; dnrList_init(&list); - if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL))) + if(handle && (dnr = dict_find(handle_dnrs, handle, NULL))) dnrList_append(&list, dnr); if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL))) dnrList_append(&list, dnr); @@ -1492,7 +1544,7 @@ chanserv_find_dnrs(const char *chan_name, struct handle_info *handle) } static unsigned int -chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle) +chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle) { struct dnrList list; struct do_not_register *dnr; @@ -1607,7 +1659,7 @@ static CHANSERV_FUNC(cmd_noregister) reply("CSMSG_DNR_SEARCH_RESULTS"); if(*target == '*') - matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1)); + matches = chanserv_show_dnrs(user, cmd, NULL, target + 1); else matches = chanserv_show_dnrs(user, cmd, target, NULL); if(!matches) @@ -1656,7 +1708,6 @@ chanserv_get_owned_count(struct handle_info *hi) static CHANSERV_FUNC(cmd_register) { - struct mod_chanmode *change; struct handle_info *handle; struct chanData *cData; struct modeNode *mn; @@ -1679,7 +1730,8 @@ 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; @@ -1729,7 +1781,7 @@ static CHANSERV_FUNC(cmd_register) if(!IsHelping(user)) reply("CSMSG_DNR_CHANNEL", chan_name); else - chanserv_show_dnrs(user, cmd, chan_name, handle); + chanserv_show_dnrs(user, cmd, chan_name, handle->handle); return 0; } @@ -1740,17 +1792,26 @@ static CHANSERV_FUNC(cmd_register) } if(new_channel) - channel = AddChannel(argv[1], now, NULL, NULL); + 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); cData->modes = chanserv_conf.default_modes; - change = mod_chanmode_dup(&cData->modes, 1); - change->args[change->argc].mode = MODE_CHANOP; - change->args[change->argc].member = AddChannelUser(chanserv, channel); - change->argc++; - mod_chanmode_announce(chanserv, channel, change); - mod_chanmode_free(change); + if(off_channel > 0) + cData->modes.modes_set |= MODE_REGISTERED; + if (IsOffChannel(cData)) + { + mod_chanmode_announce(chanserv, channel, &cData->modes); + } + else + { + struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1); + change->args[change->argc].mode = MODE_CHANOP; + change->args[change->argc].u.member = AddChannelUser(chanserv, channel); + change->argc++; + mod_chanmode_announce(chanserv, channel, change); + mod_chanmode_free(change); + } /* Initialize the channel's max user record. */ cData->max = channel->members.used; @@ -1835,6 +1896,7 @@ static CHANSERV_FUNC(cmd_unregister) static CHANSERV_FUNC(cmd_move) { + struct mod_chanmode change; struct chanNode *target; struct modeNode *mn; struct userData *uData; @@ -1870,15 +1932,16 @@ static CHANSERV_FUNC(cmd_move) if(!IsHelping(user)) reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]); else - chanserv_show_dnrs(user, cmd, argv[1], uData->handle); + chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle); return 0; } } } + mod_chanmode_init(&change); if(!(target = GetChannel(argv[1]))) { - target = AddChannel(argv[1], now, NULL, NULL); + target = AddChannel(argv[1], now, NULL, NULL, NULL); if(!IsSuspended(channel->channel_info)) AddChannelUser(chanserv, target); } @@ -1895,11 +1958,20 @@ static CHANSERV_FUNC(cmd_move) } else if(!IsSuspended(channel->channel_info)) { - struct mod_chanmode change; - mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(chanserv, target); + change.args[0].u.member = AddChannelUser(chanserv, target); + mod_chanmode_announce(chanserv, target, &change); + } + + if(off_channel > 0) + { + /* Clear MODE_REGISTERED from old channel, add it to new. */ + change.argc = 0; + change.modes_clear = MODE_REGISTERED; + mod_chanmode_announce(chanserv, channel, &change); + change.modes_clear = 0; + change.modes_set = MODE_REGISTERED; mod_chanmode_announce(chanserv, target, &change); } @@ -2161,7 +2233,7 @@ static CHANSERV_FUNC(cmd_opchan) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = GetUserMode(channel, chanserv); + change.args[0].u.member = GetUserMode(channel, chanserv); mod_chanmode_announce(chanserv, channel, &change); reply("CSMSG_OPCHAN_DONE", channel->name); return 1; @@ -2374,6 +2446,12 @@ static CHANSERV_FUNC(cmd_mdelpeon) return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd); } +static CHANSERV_FUNC(cmd_mdelhalfop) +{ + return cmd_mdel_user(user, channel, UL_HALFOP, UL_HALFOP, argv[1], cmd); +} + + static int cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration) { @@ -2441,7 +2519,7 @@ cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short m if(!max_access) { min_access = 1; - max_access = UL_OWNER; + max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1); } send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info)); return 1; @@ -2498,8 +2576,8 @@ static CHANSERV_FUNC(cmd_up) mod_chanmode_init(&change); change.argc = 1; - change.args[0].member = GetUserMode(channel, user); - if(!change.args[0].member) + change.args[0].u.member = GetUserMode(channel, user); + if(!change.args[0].u.member) { if(argc) reply("MSG_CHANNEL_ABSENT", channel->name); @@ -2518,6 +2596,11 @@ static CHANSERV_FUNC(cmd_up) change.args[0].mode = MODE_CHANOP; errmsg = "CSMSG_ALREADY_OPPED"; } + else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveHalfOps]) + { + change.args[0].mode = MODE_HALFOP; + errmsg = "CSMSG_ALREADY_HALFOPPED"; + } else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice]) { change.args[0].mode = MODE_VOICE; @@ -2529,7 +2612,7 @@ static CHANSERV_FUNC(cmd_up) reply("CSMSG_NO_ACCESS"); return 0; } - change.args[0].mode &= ~change.args[0].member->modes; + change.args[0].mode &= ~change.args[0].u.member->modes; if(!change.args[0].mode) { if(argc) @@ -2546,22 +2629,22 @@ static CHANSERV_FUNC(cmd_down) mod_chanmode_init(&change); change.argc = 1; - change.args[0].member = GetUserMode(channel, user); - if(!change.args[0].member) + change.args[0].u.member = GetUserMode(channel, user); + if(!change.args[0].u.member) { if(argc) reply("MSG_CHANNEL_ABSENT", channel->name); return 0; } - if(!change.args[0].member->modes) + if(!change.args[0].u.member->modes) { if(argc) reply("CSMSG_ALREADY_DOWN", channel->name); return 0; } - change.args[0].mode = MODE_REMOVE | change.args[0].member->modes; + change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes; modcmd_chanmode_announce(&change); return 1; } @@ -2610,8 +2693,8 @@ modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, if(!(victim = GetUserH(argv[ii]))) continue; change->args[valid].mode = mode; - change->args[valid].member = GetUserMode(channel, victim); - if(!change->args[valid].member) + change->args[valid].u.member = GetUserMode(channel, victim); + if(!change->args[valid].u.member) continue; if(validate && !validate(user, channel, victim)) continue; @@ -2635,11 +2718,21 @@ static CHANSERV_FUNC(cmd_op) return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS"); } +static CHANSERV_FUNC(cmd_hop) +{ + return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS"); +} + static CHANSERV_FUNC(cmd_deop) { return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS"); } +static CHANSERV_FUNC(cmd_dehop) +{ + return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS"); +} + static CHANSERV_FUNC(cmd_voice) { return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS"); @@ -2746,12 +2839,17 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c reply("CSMSG_MASK_PROTECTED", argv[1]); return 0; } + /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/ + if(cmd) + victimCount = 0; /* Dont deop etc ppl who match this */ +#ifdef entropy_lameness if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user)) { reply("CSMSG_LAME_MASK", argv[1]); return 0; } +#endif if((action == ACTION_KICK) && (victimCount == 0)) { @@ -2875,6 +2973,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c } else if(ban) { + /* WHAT DOES THIS DO?? -Rubin */ for(n = 0; n < chanserv_conf.old_ban_names->used; ++n) { extern const char *hidden_host_suffix; @@ -2889,7 +2988,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c if(irccasecmp(ban + l1 - l2, old_name)) continue; new_mask = malloc(MAXLEN); - sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix); + sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix); free(ban); name = ban = new_mask; } @@ -2913,13 +3012,13 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c change = mod_chanmode_alloc(victimCount + 1); for(n = 0; n < victimCount; ++n) { - change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE; - change->args[n].member = victims[n]; + change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE; + change->args[n].u.member = victims[n]; } if(!exists) { change->args[n].mode = MODE_BAN; - change->args[n++].hostmask = ban; + change->args[n++].u.hostmask = ban; } change->argc = n; if(cmd) @@ -3027,8 +3126,9 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas if(!match[ii]) continue; change->args[count].mode = MODE_REMOVE | MODE_BAN; - change->args[count++].hostmask = bans->list[ii]->ban; + change->args[count++].u.hostmask = strdup(bans->list[ii]->ban); } + assert(count == change->argc); return change; } @@ -3061,6 +3161,10 @@ unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c change = find_matching_bans(&channel->banlist, actee, mask); if(change) { + unsigned int ii; + + for(ii = 0; ii < change->argc; ++ii) + free((char*)change->args[ii].u.hostmask); modcmd_chanmode_announce(change); mod_chanmode_free(change); acted = 1; @@ -3138,9 +3242,11 @@ static CHANSERV_FUNC(cmd_unbanall) for(ii=0; iibanlist.used; ii++) { change->args[ii].mode = MODE_REMOVE | MODE_BAN; - change->args[ii].hostmask = channel->banlist.list[ii]->ban; + change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban); } modcmd_chanmode_announce(change); + for(ii = 0; ii < change->argc; ++ii) + free((char*)change->args[ii].u.hostmask); mod_chanmode_free(change); reply("CSMSG_BANS_REMOVED", channel->name); return 1; @@ -3149,6 +3255,7 @@ static CHANSERV_FUNC(cmd_unbanall) static CHANSERV_FUNC(cmd_open) { struct mod_chanmode *change; + unsigned int ii; change = find_matching_bans(&channel->banlist, user, NULL); if(!change) @@ -3159,6 +3266,8 @@ static CHANSERV_FUNC(cmd_open) change->modes_clear &= ~channel->channel_info->modes.modes_set; modcmd_chanmode_announce(change); reply("CSMSG_CHANNEL_OPENED", channel->name); + for(ii = 0; ii < change->argc; ++ii) + free((char*)change->args[ii].u.hostmask); mod_chanmode_free(change); return 1; } @@ -3206,6 +3315,8 @@ static CHANSERV_FUNC(cmd_myaccess) { if(uData->access >= cData->lvlOpts[lvlGiveOps]) string_buffer_append(&sbuf, 'o'); + else if(uData->access >= cData->lvlOpts[lvlGiveHalfOps]) + string_buffer_append(&sbuf, 'h'); else if(uData->access >= cData->lvlOpts[lvlGiveVoice]) string_buffer_append(&sbuf, 'v'); } @@ -3216,7 +3327,7 @@ static CHANSERV_FUNC(cmd_myaccess) else string_buffer_append_string(&sbuf, ")]"); string_buffer_append(&sbuf, '\0'); - send_message_type(4, user, cmd->parent->bot, sbuf.list); + send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list); } return 1; @@ -3531,9 +3642,14 @@ static CHANSERV_FUNC(cmd_olist) return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1); } +static CHANSERV_FUNC(cmd_hlist) +{ + return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1); +} + static CHANSERV_FUNC(cmd_plist) { - return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1); + return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1); } static CHANSERV_FUNC(cmd_bans) @@ -3733,7 +3849,7 @@ static CHANSERV_FUNC(cmd_mode) return 1; } - change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE); + change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED); if(!change) { reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL)); @@ -4090,7 +4206,26 @@ static CHANSERV_FUNC(cmd_resync) if(!(mn->modes & MODE_CHANOP)) { changes->args[used].mode = MODE_CHANOP; - changes->args[used++].member = mn; + changes->args[used++].u.member = mn; + } + } + else if(!cData->lvlOpts[lvlGiveHalfOps] + || (uData && uData->access >= cData->lvlOpts[lvlGiveHalfOps])) + { + if(!(mn->modes & MODE_HALFOP)) + { + changes->args[used].mode = MODE_HALFOP; + changes->args[used++].u.member = mn; + } + if(mn->modes & MODE_CHANOP) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP); + changes->args[used++].u.member = mn; + } + if(mn->modes & MODE_VOICE) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE); + changes->args[used++].u.member = mn; } } else if(!cData->lvlOpts[lvlGiveVoice] @@ -4099,12 +4234,17 @@ static CHANSERV_FUNC(cmd_resync) if(mn->modes & MODE_CHANOP) { changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE); - changes->args[used++].member = mn; + changes->args[used++].u.member = mn; + } + if(mn->modes & MODE_HALFOP) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE); + changes->args[used++].u.member = mn; } if(!(mn->modes & MODE_VOICE)) { changes->args[used].mode = MODE_VOICE; - changes->args[used++].member = mn; + changes->args[used++].u.member = mn; } } else @@ -4112,7 +4252,7 @@ static CHANSERV_FUNC(cmd_resync) if(mn->modes) { changes->args[used].mode = MODE_REMOVE | mn->modes; - changes->args[used++].member = mn; + changes->args[used++].u.member = mn; } } } @@ -4421,18 +4561,21 @@ chanserv_expire_suspension(void *data) { struct suspended *suspended = data; struct chanNode *channel; - struct mod_chanmode change; if(!suspended->expires || (now < suspended->expires)) suspended->revoked = now; channel = suspended->cData->channel; suspended->cData->channel = channel; suspended->cData->flags &= ~CHANNEL_SUSPENDED; - mod_chanmode_init(&change); - change.argc = 1; - change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(chanserv, channel); - mod_chanmode_announce(chanserv, channel, &change); + if(!IsOffChannel(suspended->cData)) + { + struct mod_chanmode change; + mod_chanmode_init(&change); + change.argc = 1; + change.args[0].mode = MODE_CHANOP; + change.args[0].u.member = AddChannelUser(chanserv, channel); + mod_chanmode_announce(chanserv, channel, &change); + } } static CHANSERV_FUNC(cmd_csuspend) @@ -4853,7 +4996,7 @@ static MODCMD_FUNC(chan_opt_modes) { memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes)); } - else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE))) + else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED))) { reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL)); return 0; @@ -4958,7 +5101,7 @@ static MODCMD_FUNC(chan_opt_offchannel) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(chanserv, channel); + change.args[0].u.member = AddChannelUser(chanserv, channel); mod_chanmode_announce(chanserv, channel, &change); } cData->flags &= ~CHANNEL_OFFCHANNEL; @@ -5049,6 +5192,13 @@ channel_level_option(enum levelOption option, struct userNode *user, struct chan return 0; } break; + case lvlGiveHalfOps: + if(value < cData->lvlOpts[lvlGiveVoice]) + { + reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]); + return 0; + } + break; case lvlGiveOps: if(value < cData->lvlOpts[lvlGiveVoice]) { @@ -5081,11 +5231,21 @@ static MODCMD_FUNC(chan_opt_enfops) return channel_level_option(lvlEnfOps, CSFUNC_ARGS); } +static MODCMD_FUNC(chan_opt_enfhalfops) +{ + return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS); +} + static MODCMD_FUNC(chan_opt_giveops) { return channel_level_option(lvlGiveOps, CSFUNC_ARGS); } +static MODCMD_FUNC(chan_opt_givehalfops) +{ + return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS); +} + static MODCMD_FUNC(chan_opt_enfmodes) { return channel_level_option(lvlEnfModes, CSFUNC_ARGS); @@ -5442,6 +5602,13 @@ static CHANSERV_FUNC(cmd_giveownership) } curr_user = owner; } + else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period))) + { + char delay[INTERVALLEN]; + intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info); + reply("CSMSG_TRANSFER_WAIT", delay, channel->name); + return 0; + } if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1]))) return 0; if(new_owner_hi == user->handle_info) @@ -5452,8 +5619,15 @@ static CHANSERV_FUNC(cmd_giveownership) new_owner = GetChannelAccess(cData, new_owner_hi); if(!new_owner) { - reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name); - return 0; + if(force) + { + new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL); + } + else + { + reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name); + return 0; + } } if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force) { @@ -5464,7 +5638,7 @@ static CHANSERV_FUNC(cmd_giveownership) if(!IsHelping(user)) reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle); else - chanserv_show_dnrs(user, cmd, NULL, new_owner_hi); + chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle); return 0; } if(new_owner->access >= UL_COOWNER) @@ -5474,6 +5648,7 @@ static CHANSERV_FUNC(cmd_giveownership) new_owner->access = UL_OWNER; if(curr_user) curr_user->access = co_access; + cData->ownerTransfer = now; reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle); sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle); global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason); @@ -5537,6 +5712,7 @@ static CHANSERV_FUNC(cmd_unsuspend) return 0; } target->flags &= ~USER_SUSPENDED; + scan_user_presence(target, NULL); reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name); return 1; } @@ -5740,7 +5916,7 @@ static CHANSERV_FUNC(cmd_calc) do_math(response, unsplit_string(argv + 1, argc - 1, NULL)); if(channel) - send_channel_message(channel, cmd->parent->bot, "%b%s%b: %s", user->nick, response); + send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response); else send_message_type(4, user, cmd->parent->bot, "%s", response); return 1; @@ -5872,7 +6048,7 @@ handle_join(struct modeNode *mNode) } change.args[0].mode = MODE_BAN; - change.args[0].hostmask = bData->mask; + change.args[0].u.hostmask = bData->mask; mod_chanmode_announce(chanserv, channel, &change); KickChannelUser(user, channel, chanserv, kick_reason); return 1; @@ -5902,6 +6078,8 @@ handle_join(struct modeNode *mNode) } else if(cData->lvlOpts[lvlGiveOps] == 0) modes |= MODE_CHANOP; + else if(cData->lvlOpts[lvlGiveHalfOps] == 0) + modes |= MODE_HALFOP; else if(cData->lvlOpts[lvlGiveVoice] == 0) modes |= MODE_VOICE; @@ -5931,6 +6109,8 @@ handle_join(struct modeNode *mNode) { if(uData->access >= cData->lvlOpts[lvlGiveOps]) modes |= MODE_CHANOP; + if(uData->access >= cData->lvlOpts[lvlGiveHalfOps]) + modes |= MODE_HALFOP; else if(uData->access >= cData->lvlOpts[lvlGiveVoice]) modes |= MODE_VOICE; } @@ -5951,10 +6131,12 @@ handle_join(struct modeNode *mNode) { if(modes) { - if(modes & MODE_CHANOP) + if(modes & MODE_CHANOP) { + modes &= ~MODE_HALFOP; modes &= ~MODE_VOICE; + } change.args[0].mode = modes; - change.args[0].member = mNode; + change.args[0].u.member = mNode; mod_chanmode_announce(chanserv, channel, &change); } if(greeting && !user->uplink->burst) @@ -6005,11 +6187,13 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) { if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps]) change.args[0].mode = MODE_CHANOP; + else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveHalfOps]) + change.args[0].mode = MODE_HALFOP; else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice]) change.args[0].mode = MODE_VOICE; else change.args[0].mode = 0; - change.args[0].member = mn; + change.args[0].u.member = mn; if(change.args[0].mode) mod_chanmode_announce(chanserv, cn, &change); } @@ -6023,8 +6207,9 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) struct chanNode *channel = user->channels.list[ii]->channel; struct banData *ban; - if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE)) - || !channel->channel_info) + if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) + || !channel->channel_info + || IsSuspended(channel->channel_info)) continue; for(jj = 0; jj < channel->banlist.used; ++jj) if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1)) @@ -6037,7 +6222,7 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) if(!user_matches_glob(user, ban->mask, 1)) continue; change.args[0].mode = MODE_BAN; - change.args[0].hostmask = ban->mask; + change.args[0].u.hostmask = ban->mask; mod_chanmode_announce(chanserv, channel, &change); sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason); KickChannelUser(user, channel, chanserv, kick_reason); @@ -6171,7 +6356,7 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch { if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP)) { - const struct userNode *victim = change->args[ii].member->user; + const struct userNode *victim = change->args[ii].u.member->user; if(!protect_user(victim, user, channel->channel_info)) continue; if(!bounce) @@ -6179,36 +6364,36 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch if(!deopped) { bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP; - bounce->args[bnc].member = GetUserMode(channel, user); - if(bounce->args[bnc].member) + bounce->args[bnc].u.member = GetUserMode(channel, user); + if(bounce->args[bnc].u.member) bnc++; deopped = 1; } bounce->args[bnc].mode = MODE_CHANOP; - bounce->args[bnc].member = change->args[ii].member; + bounce->args[bnc].u.member = change->args[ii].u.member; bnc++; send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick); } else if(change->args[ii].mode & MODE_CHANOP) { - const struct userNode *victim = change->args[ii].member->user; + const struct userNode *victim = change->args[ii].u.member->user; if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim)) continue; if(!bounce) bounce = mod_chanmode_alloc(change->argc + 1 - ii); bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP; - bounce->args[bnc].member = change->args[ii].member; + bounce->args[bnc].u.member = change->args[ii].u.member; bnc++; } else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN) { - const char *ban = change->args[ii].hostmask; + const char *ban = change->args[ii].u.hostmask; if(!bad_channel_ban(channel, user, ban, NULL, NULL)) continue; if(!bounce) bounce = mod_chanmode_alloc(change->argc + 1 - ii); bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN; - bounce->args[bnc].hostmask = ban; + bounce->args[bnc].u.hostmask = strdup(ban); bnc++; send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban); } @@ -6217,6 +6402,9 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch { if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear) mod_chanmode_announce(chanserv, channel, bounce); + for(ii = 0; ii < change->argc; ++ii) + if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN)) + free((char*)bounce->args[ii].u.hostmask); mod_chanmode_free(bounce); } } @@ -6237,7 +6425,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick)) { channel = user->channels.list[ii]->channel; /* Need not check for bans if they're opped or voiced. */ - if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE)) + if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) continue; /* Need not check for bans unless channel registration is active. */ if(!channel->channel_info || IsSuspended(channel->channel_info)) @@ -6254,7 +6442,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick)) { if(!user_matches_glob(user, bData->mask, 1)) continue; - change.args[0].hostmask = bData->mask; + change.args[0].u.hostmask = bData->mask; mod_chanmode_announce(chanserv, channel, &change); sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason); KickChannelUser(user, channel, chanserv, kick_reason); @@ -6337,7 +6525,7 @@ chanserv_conf_read(void) const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING); if(!str2) str2 = "+nt"; - chan = AddChannel(strlist->list[ii], now, str2, NULL); + chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL); LockChannel(chan); channelList_append(&chanserv_conf.support_channels, chan); } @@ -6348,7 +6536,7 @@ chanserv_conf_read(void) str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING); if(!str2) str2 = "+nt"; - chan = AddChannel(str, now, str2, NULL); + chan = AddChannel(str, now, str2, NULL, NULL); LockChannel(chan); channelList_append(&chanserv_conf.support_channels, chan); } @@ -6357,7 +6545,7 @@ chanserv_conf_read(void) str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING); chanserv_conf.info_delay = str ? ParseInterval(str) : 180; str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING); - chanserv_conf.greeting_length = str ? atoi(str) : 120; + chanserv_conf.greeting_length = str ? atoi(str) : 200; str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING); chanserv_conf.adjust_threshold = str ? atoi(str) : 15; str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING); @@ -6379,6 +6567,8 @@ chanserv_conf_read(void) NickChange(chanserv, str, 0); str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING); chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60; + str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING); + chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0; str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING); chanserv_conf.ctcp_short_ban_duration = str ? str : "3m"; str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING); @@ -6411,8 +6601,8 @@ chanserv_conf_read(void) /* free form text */ "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes", /* options based on user level */ - "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps", - "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers", + "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps", + "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers", /* multiple choice options */ "CtcpReaction", "Protect", "Toys", "TopicRefresh", /* binary options */ @@ -6452,10 +6642,8 @@ chanserv_conf_read(void) else strlist = alloc_string_list(2); chanserv_conf.old_ban_names = strlist; - /* the variable itself is actually declared in proto-common.c; this is equally - * parse issue. */ str = database_get_data(conf_node, "off_channel", RECDB_QSTRING); - off_channel = (str && enabled_string(str)) ? 1 : 0; + off_channel = str ? atoi(str) : 0; } static void @@ -6570,6 +6758,8 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan) s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING); owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING); reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING); + if (!reason || !owner) + return; set_time = set ? (time_t)strtoul(set, NULL, 0) : now; triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0; @@ -6580,7 +6770,7 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan) else expires_time = 0; - if(expires_time && (expires_time < now)) + if(!reason || (expires_time && (expires_time < now))) return; bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason); @@ -6623,7 +6813,7 @@ chanserv_channel_read(const char *key, struct record_data *hir) str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING); if(!str) str = ""; - cNode = AddChannel(key, now, NULL, NULL); + cNode = AddChannel(key, now, NULL, NULL, NULL); if(!cNode) { log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key); @@ -6690,6 +6880,7 @@ chanserv_channel_read(const char *key, struct record_data *hir) case 'n': lvl = UL_OWNER+1; break; case 'o': lvl = UL_OP; break; case 'p': lvl = UL_PEON; break; + case 'h': lvl = UL_HALFOP; break; case 'w': lvl = UL_OWNER; break; default: lvl = 0; break; } @@ -6707,14 +6898,14 @@ chanserv_channel_read(const char *key, struct record_data *hir) /* We could use suspended->expires and suspended->revoked to * set the CHANNEL_SUSPENDED flag, but we don't. */ } - else if(IsSuspended(cData)) + else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING))) { suspended = calloc(1, sizeof(*suspended)); suspended->issued = 0; suspended->revoked = 0; + suspended->suspender = strdup(str); str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING); suspended->expires = str ? atoi(str) : 0; - suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)); str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING); suspended->reason = strdup(str ? str : "No reason"); suspended->previous = NULL; @@ -6722,7 +6913,10 @@ chanserv_channel_read(const char *key, struct record_data *hir) suspended->cData = cData; } else + { + cData->flags &= ~CHANNEL_SUSPENDED; suspended = NULL; /* to squelch a warning */ + } if(IsSuspended(cData)) { if(suspended->expires > now) @@ -6736,7 +6930,7 @@ chanserv_channel_read(const char *key, struct record_data *hir) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(chanserv, cNode); + change.args[0].u.member = AddChannelUser(chanserv, cNode); mod_chanmode_announce(chanserv, cNode, &change); } @@ -6744,6 +6938,8 @@ chanserv_channel_read(const char *key, struct record_data *hir) cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now; str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING); cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now; + str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING); + cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0; str = database_get_data(channel, KEY_MAX, RECDB_QSTRING); cData->max = str ? atoi(str) : 0; str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING); @@ -6760,7 +6956,8 @@ chanserv_channel_read(const char *key, struct record_data *hir) && (argc = split_line(str, 0, ArrayLength(argv), argv)) && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) { cData->modes = *modes; - cData->modes.modes_set |= MODE_REGISTERED; + if(off_channel > 0) + cData->modes.modes_set |= MODE_REGISTERED; if(cData->modes.argc > 1) cData->modes.argc = 1; mod_chanmode_announce(chanserv, cNode, &cData->modes); @@ -6985,6 +7182,8 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel) saxdb_end_record(ctx); } + if(channel->ownerTransfer) + saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer); saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited); saxdb_end_record(ctx); } @@ -7146,6 +7345,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(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL); DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL); @@ -7156,8 +7356,10 @@ init_chanserv(const char *nick) DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL); DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL); DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL); + DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL); DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL); DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL); + DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL); DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL); DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL); @@ -7192,6 +7394,7 @@ init_chanserv(const char *nick) DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); + DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL); @@ -7226,7 +7429,9 @@ init_chanserv(const char *nick) DEFINE_CHANNEL_OPTION(usergreeting); DEFINE_CHANNEL_OPTION(modes); DEFINE_CHANNEL_OPTION(enfops); + DEFINE_CHANNEL_OPTION(enfhalfops); DEFINE_CHANNEL_OPTION(giveops); + DEFINE_CHANNEL_OPTION(givehalfops); DEFINE_CHANNEL_OPTION(protect); DEFINE_CHANNEL_OPTION(enfmodes); DEFINE_CHANNEL_OPTION(enftopic); @@ -7242,7 +7447,7 @@ init_chanserv(const char *nick) DEFINE_CHANNEL_OPTION(ctcpusers); DEFINE_CHANNEL_OPTION(ctcpreaction); DEFINE_CHANNEL_OPTION(inviteme); - if(off_channel) + if(off_channel > 1) DEFINE_CHANNEL_OPTION(offchannel); modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL); @@ -7261,7 +7466,8 @@ init_chanserv(const char *nick) dict_set_free_data(note_types, chanserv_deref_note_type); if(nick) { - chanserv = AddService(nick, "Channel Services", NULL); + const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING); + chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL); service_register(chanserv)->trigger = '!'; reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check); } @@ -7276,7 +7482,7 @@ init_chanserv(const char *nick) next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period; timeq_add(next_refresh, chanserv_refresh_topics, NULL); } - + reg_exit_func(chanserv_db_cleanup); message_register_table(msgtab); }