anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

cs_set.cpp (41155B)

      1 /* ChanServ core functions
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "module.h"
     13 #include "modules/cs_mode.h"
     14 
     15 class CommandCSSet : public Command
     16 {
     17  public:
     18 	CommandCSSet(Module *creator) : Command(creator, "chanserv/set", 2, 3)
     19 	{
     20 		this->SetDesc(_("Set channel options and information"));
     21 		this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037"));
     22 	}
     23 
     24 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     25 	{
     26 		this->OnSyntaxError(source, "");
     27 	}
     28 
     29 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
     30 	{
     31 		this->SendSyntax(source);
     32 		source.Reply(" ");
     33 		source.Reply(_("Allows the channel founder to set various channel options\n"
     34 			"and other information.\n"
     35 			" \n"
     36 			"Available options:"));
     37 		Anope::string this_name = source.command;
     38 		bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
     39 		     hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
     40 		for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
     41 		{
     42 			const Anope::string &c_name = it->first;
     43 			const CommandInfo &info = it->second;
     44 			if (c_name.find_ci(this_name + " ") == 0)
     45 			{
     46 				if (info.hide)
     47 					continue;
     48 
     49 				ServiceReference<Command> c("Command", info.name);
     50 
     51 				// XXX dup
     52 				if (!c)
     53 					continue;
     54 				else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
     55 					continue;
     56 				else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
     57 					continue;
     58 
     59 				source.command = it->first;
     60 				c->OnServHelp(source);
     61 			}
     62 		}
     63 		source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information on a\n"
     64 				"particular option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
     65 		return true;
     66 	}
     67 };
     68 
     69 class CommandCSSetAutoOp : public Command
     70 {
     71  public:
     72 	CommandCSSetAutoOp(Module *creator, const Anope::string &cname = "chanserv/set/autoop") : Command(creator, cname, 2, 2)
     73 	{
     74 		this->SetDesc(_("Should services automatically give status to users"));
     75 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
     76 	}
     77 
     78 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     79 	{
     80 		if (Anope::ReadOnly)
     81 		{
     82 			source.Reply(READ_ONLY_MODE);
     83 			return;
     84 		}
     85 
     86 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
     87 		if (ci == NULL)
     88 		{
     89 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
     90 			return;
     91 		}
     92 
     93 		EventReturn MOD_RESULT;
     94 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
     95 		if (MOD_RESULT == EVENT_STOP)
     96 			return;
     97 
     98 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
     99 		{
    100 			source.Reply(ACCESS_DENIED);
    101 			return;
    102 		}
    103 
    104 		if (params[1].equals_ci("ON"))
    105 		{
    106 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable autoop";
    107 			ci->Shrink<bool>("NOAUTOOP");
    108 			source.Reply(_("Services will now automatically give modes to users in \002%s\002."), ci->name.c_str());
    109 		}
    110 		else if (params[1].equals_ci("OFF"))
    111 		{
    112 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable autoop";
    113 			ci->Extend<bool>("NOAUTOOP");
    114 			source.Reply(_("Services will no longer automatically give modes to users in \002%s\002."), ci->name.c_str());
    115 		}
    116 		else
    117 			this->OnSyntaxError(source, "AUTOOP");
    118 	}
    119 
    120 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    121 	{
    122 		this->SendSyntax(source);
    123 		source.Reply(" ");
    124 		source.Reply(_("Enables or disables %s's autoop feature for a\n"
    125 			"channel. When disabled, users who join the channel will\n"
    126 			"not automatically gain any status from %s."), source.service->nick.c_str(),
    127 			source.service->nick.c_str(), this->name.c_str());
    128 		return true;
    129 	}
    130 };
    131 
    132 class CommandCSSetBanType : public Command
    133 {
    134  public:
    135 	CommandCSSetBanType(Module *creator, const Anope::string &cname = "chanserv/set/bantype") : Command(creator, cname, 2, 2)
    136 	{
    137 		this->SetDesc(_("Set how Services make bans on the channel"));
    138 		this->SetSyntax(_("\037channel\037 \037bantype\037"));
    139 	}
    140 
    141 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    142 	{
    143 		if (Anope::ReadOnly)
    144 		{
    145 			source.Reply(READ_ONLY_MODE);
    146 			return;
    147 		}
    148 
    149 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    150 		if (ci == NULL)
    151 		{
    152 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    153 			return;
    154 		}
    155 
    156 		EventReturn MOD_RESULT;
    157 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    158 		if (MOD_RESULT == EVENT_STOP)
    159 			return;
    160 
    161 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    162 		{
    163 			source.Reply(ACCESS_DENIED);
    164 			return;
    165 		}
    166 
    167 		try
    168 		{
    169 			int16_t new_type = convertTo<int16_t>(params[1]);
    170 			if (new_type < 0 || new_type > 3)
    171 				throw ConvertException("Invalid range");
    172 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
    173 			ci->bantype = new_type;
    174 			source.Reply(_("Ban type for channel %s is now #%d."), ci->name.c_str(), ci->bantype);
    175 		}
    176 		catch (const ConvertException &)
    177 		{
    178 			source.Reply(_("\002%s\002 is not a valid ban type."), params[1].c_str());
    179 		}
    180 	}
    181 
    182 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    183 	{
    184 		this->SendSyntax(source);
    185 		source.Reply(" ");
    186 		source.Reply(_("Sets the ban type that will be used by services whenever\n"
    187 				"they need to ban someone from your channel.\n"
    188 				" \n"
    189 				"Bantype is a number between 0 and 3 that means:\n"
    190 				" \n"
    191 				"0: ban in the form *!user@host\n"
    192 				"1: ban in the form *!*user@host\n"
    193 				"2: ban in the form *!*@host\n"
    194 				"3: ban in the form *!*user@*.domain"), this->name.c_str());
    195 		return true;
    196 	}
    197 };
    198 
    199 class CommandCSSetDescription : public Command
    200 {
    201  public:
    202 	CommandCSSetDescription(Module *creator, const Anope::string &cname = "chanserv/set/description") : Command(creator, cname, 1, 2)
    203 	{
    204 		this->SetDesc(_("Set the channel description"));
    205 		this->SetSyntax(_("\037channel\037 [\037description\037]"));
    206 	}
    207 
    208 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    209 	{
    210 		if (Anope::ReadOnly)
    211 		{
    212 			source.Reply(READ_ONLY_MODE);
    213 			return;
    214 		}
    215 
    216 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    217 		const Anope::string &param = params.size() > 1 ? params[1] : "";
    218 		if (ci == NULL)
    219 		{
    220 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    221 			return;
    222 		}
    223 
    224 		EventReturn MOD_RESULT;
    225 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
    226 		if (MOD_RESULT == EVENT_STOP)
    227 			return;
    228 
    229 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    230 		{
    231 			source.Reply(ACCESS_DENIED);
    232 			return;
    233 		}
    234 
    235 		if (!param.empty())
    236 		{
    237 			ci->desc = param;
    238 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the description to " << ci->desc;
    239 			source.Reply(_("Description of %s changed to \002%s\002."), ci->name.c_str(), ci->desc.c_str());
    240 		}
    241 		else
    242 		{
    243 			ci->desc.clear();
    244 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset the description";
    245 			source.Reply(_("Description of %s unset."), ci->name.c_str());
    246 		}
    247 
    248 		return;
    249 	}
    250 
    251 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    252 	{
    253 		this->SendSyntax(source);
    254 		source.Reply(" ");
    255 		source.Reply(_("Sets the description for the channel, which shows up with\n"
    256 				"the \002LIST\002 and \002INFO\002 commands."), this->name.c_str());
    257 		return true;
    258 	}
    259 };
    260 
    261 class CommandCSSetFounder : public Command
    262 {
    263  public:
    264 	CommandCSSetFounder(Module *creator, const Anope::string &cname = "chanserv/set/founder") : Command(creator, cname, 2, 2)
    265 	{
    266 		this->SetDesc(_("Set the founder of a channel"));
    267 		this->SetSyntax(_("\037channel\037 \037nick\037"));
    268 	}
    269 
    270 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    271 	{
    272 		if (Anope::ReadOnly)
    273 		{
    274 			source.Reply(READ_ONLY_MODE);
    275 			return;
    276 		}
    277 
    278 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    279 		if (ci == NULL)
    280 		{
    281 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    282 			return;
    283 		}
    284 
    285 		EventReturn MOD_RESULT;
    286 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    287 		if (MOD_RESULT == EVENT_STOP)
    288 			return;
    289 
    290 		if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    291 		{
    292 			source.Reply(ACCESS_DENIED);
    293 			return;
    294 		}
    295 
    296 		const NickAlias *na = NickAlias::Find(params[1]);
    297 		if (!na)
    298 		{
    299 			source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str());
    300 			return;
    301 		}
    302 
    303 		NickCore *nc = na->nc;
    304 		unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
    305 		if (max_reg && nc->channelcount >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
    306 		{
    307 			source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str());
    308 			return;
    309 		}
    310 
    311 		Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the founder from " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << " to " << nc->display;
    312 
    313 		ci->SetFounder(nc);
    314 
    315 		source.Reply(_("Founder of \002%s\002 changed to \002%s\002."), ci->name.c_str(), na->nick.c_str());
    316 
    317 		return;
    318 	}
    319 
    320 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    321 	{
    322 		this->SendSyntax(source);
    323 		source.Reply(" ");
    324 		source.Reply(_("Changes the founder of a channel. The new nickname must\n"
    325 				"be a registered one."), this->name.c_str());
    326 		return true;
    327 	}
    328 };
    329 
    330 class CommandCSSetKeepModes : public Command
    331 {
    332  public:
    333 	CommandCSSetKeepModes(Module *creator, const Anope::string &cname = "chanserv/set/keepmodes") :  Command(creator, cname, 2, 2)
    334 	{
    335 		this->SetDesc(_("Retain modes when channel is not in use"));
    336 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    337 	}
    338 
    339 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    340 	{
    341 		if (Anope::ReadOnly)
    342 		{
    343 			source.Reply(READ_ONLY_MODE);
    344 			return;
    345 		}
    346 
    347 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    348 		if (ci == NULL)
    349 		{
    350 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    351 			return;
    352 		}
    353 
    354 		EventReturn MOD_RESULT;
    355 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    356 		if (MOD_RESULT == EVENT_STOP)
    357 			return;
    358 
    359 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    360 		{
    361 			source.Reply(ACCESS_DENIED);
    362 			return;
    363 		}
    364 
    365 		if (params[1].equals_ci("ON"))
    366 		{
    367 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keep modes";
    368 			ci->Extend<bool>("CS_KEEP_MODES");
    369 			source.Reply(_("Keep modes for %s is now \002on\002."), ci->name.c_str());
    370 			if (ci->c)
    371 				ci->last_modes = ci->c->GetModes();
    372 		}
    373 		else if (params[1].equals_ci("OFF"))
    374 		{
    375 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keep modes";
    376 			ci->Shrink<bool>("CS_KEEP_MODES");
    377 			source.Reply(_("Keep modes for %s is now \002off\002."), ci->name.c_str());
    378 			ci->last_modes.clear();
    379 		}
    380 		else
    381 			this->OnSyntaxError(source, "KEEPMODES");
    382 	}
    383 
    384 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    385 	{
    386 		this->SendSyntax(source);
    387 		source.Reply(" ");
    388 		source.Reply(_("Enables or disables keepmodes for the given channel. If keep\n"
    389 				"modes is enabled, services will remember modes set on the channel\n"
    390 				"and attempt to re-set them the next time the channel is created."));
    391 		return true;
    392 	}
    393 };
    394 
    395 class CommandCSSetPeace : public Command
    396 {
    397  public:
    398 	CommandCSSetPeace(Module *creator, const Anope::string &cname = "chanserv/set/peace") : Command(creator, cname, 2, 2)
    399 	{
    400 		this->SetDesc(_("Regulate the use of critical commands"));
    401 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    402 	}
    403 
    404 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    405 	{
    406 		if (Anope::ReadOnly)
    407 		{
    408 			source.Reply(READ_ONLY_MODE);
    409 			return;
    410 		}
    411 
    412 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    413 		if (ci == NULL)
    414 		{
    415 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    416 			return;
    417 		}
    418 
    419 		EventReturn MOD_RESULT;
    420 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    421 		if (MOD_RESULT == EVENT_STOP)
    422 			return;
    423 
    424 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    425 		{
    426 			source.Reply(ACCESS_DENIED);
    427 			return;
    428 		}
    429 
    430 		if (params[1].equals_ci("ON"))
    431 		{
    432 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable peace";
    433 			ci->Extend<bool>("PEACE");
    434 			source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str());
    435 		}
    436 		else if (params[1].equals_ci("OFF"))
    437 		{
    438 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable peace";
    439 			ci->Shrink<bool>("PEACE");
    440 			source.Reply(_("Peace option for %s is now \002off\002."), ci->name.c_str());
    441 		}
    442 		else
    443 			this->OnSyntaxError(source, "PEACE");
    444 
    445 		return;
    446 	}
    447 
    448 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    449 	{
    450 		this->SendSyntax(source);
    451 		source.Reply(" ");
    452 		source.Reply(_("Enables or disables the \002peace\002 option for a channel.\n"
    453 				"When \002peace\002 is set, a user won't be able to kick,\n"
    454 				"ban or remove a channel status of a user that has\n"
    455 				"a level superior or equal to his via %s commands."), source.service->nick.c_str());
    456 		return true;
    457 	}
    458 };
    459 
    460 inline static Anope::string BotModes()
    461 {
    462 	return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
    463 		Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
    464 	);
    465 }
    466 
    467 class CommandCSSetPersist : public Command
    468 {
    469  public:
    470 	CommandCSSetPersist(Module *creator, const Anope::string &cname = "chanserv/set/persist") : Command(creator, cname, 2, 2)
    471 	{
    472 		this->SetDesc(_("Set the channel as permanent"));
    473 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    474 	}
    475 
    476 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    477 	{
    478 		if (Anope::ReadOnly)
    479 		{
    480 			source.Reply(READ_ONLY_MODE);
    481 			return;
    482 		}
    483 
    484 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    485 		if (ci == NULL)
    486 		{
    487 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    488 			return;
    489 		}
    490 
    491 		EventReturn MOD_RESULT;
    492 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    493 		if (MOD_RESULT == EVENT_STOP)
    494 			return;
    495 
    496 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    497 		{
    498 			source.Reply(ACCESS_DENIED);
    499 			return;
    500 		}
    501 
    502 		ChannelMode *cm = ModeManager::FindChannelModeByName("PERM");
    503 
    504 		if (params[1].equals_ci("ON"))
    505 		{
    506 			if (!ci->HasExt("PERSIST"))
    507 			{
    508 				ci->Extend<bool>("PERSIST");
    509 
    510 				/* Set the perm mode */
    511 				if (cm)
    512 				{
    513 					if (ci->c && !ci->c->HasMode("PERM"))
    514 						ci->c->SetMode(NULL, cm);
    515 					/* Add it to the channels mlock */
    516 					ModeLocks *ml = ci->Require<ModeLocks>("modelocks");
    517 					if (ml)
    518 						ml->SetMLock(cm, true, "", source.GetNick());
    519 				}
    520 				/* No botserv bot, no channel mode, give them ChanServ.
    521 				 * Yes, this works fine with no BotServ.
    522 				 */
    523 				else if (!ci->bi)
    524 				{
    525 					BotInfo *ChanServ = Config->GetClient("ChanServ");
    526 					if (!ChanServ)
    527 					{
    528 						source.Reply(_("ChanServ is required to enable persist on this network."));
    529 						return;
    530 					}
    531 
    532 					ChanServ->Assign(NULL, ci);
    533 					if (ci->c && !ci->c->FindUser(ChanServ))
    534 					{
    535 						ChannelStatus status(BotModes());
    536 						ChanServ->Join(ci->c, &status);
    537 					}
    538 				}
    539 			}
    540 
    541 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable persist";
    542 			source.Reply(_("Channel \002%s\002 is now persistent."), ci->name.c_str());
    543 		}
    544 		else if (params[1].equals_ci("OFF"))
    545 		{
    546 			if (ci->HasExt("PERSIST"))
    547 			{
    548 				ci->Shrink<bool>("PERSIST");
    549 
    550 				BotInfo *ChanServ = Config->GetClient("ChanServ"),
    551 					*BotServ = Config->GetClient("BotServ");
    552 
    553 				/* Unset perm mode */
    554 				if (cm)
    555 				{
    556 					if (ci->c && ci->c->HasMode("PERM"))
    557 						ci->c->RemoveMode(NULL, cm);
    558 					/* Remove from mlock */
    559 					ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
    560 					if (ml)
    561 						ml->RemoveMLock(cm, true);
    562 				}
    563 				/* No channel mode, no BotServ, but using ChanServ as the botserv bot
    564 				 * which was assigned when persist was set on
    565 				 */
    566 				else if (!cm && !BotServ && ci->bi)
    567 				{
    568 					if (!ChanServ)
    569 					{
    570 						source.Reply(_("ChanServ is required to enable persist on this network."));
    571 						return;
    572 					}
    573 
    574 					/* Unassign bot */
    575 					ChanServ->UnAssign(NULL, ci);
    576 				}
    577 			}
    578 
    579 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable persist";
    580 			source.Reply(_("Channel \002%s\002 is no longer persistent."), ci->name.c_str());
    581 		}
    582 		else
    583 			this->OnSyntaxError(source, "PERSIST");
    584 	}
    585 
    586 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    587 	{
    588 		BotInfo *BotServ = Config->GetClient("BotServ");
    589 		BotInfo *ChanServ = Config->GetClient("ChanServ");
    590 		this->SendSyntax(source);
    591 		source.Reply(" ");
    592 		source.Reply(_("Enables or disables the persistent channel setting.\n"
    593 				"When persistent is set, the service bot will remain\n"
    594 				"in the channel when it has emptied of users.\n"
    595 				" \n"
    596 				"If your IRCd does not have a permanent (persistent) channel\n"
    597 				"mode you must have a service bot in your channel to\n"
    598 				"set persist on, and it can not be unassigned while persist\n"
    599 				"is on.\n"
    600 				" \n"
    601 				"If this network does not have %s enabled and does\n"
    602 				"not have a permanent channel mode, %s will\n"
    603 				"join your channel when you set persist on (and leave when\n"
    604 				"it has been set off).\n"
    605 				" \n"
    606 				"If your IRCd has a permanent (persistent) channel mode\n"
    607 				"and it is set or unset (for any reason, including MODE LOCK),\n"
    608 				"persist is automatically set and unset for the channel as well.\n"
    609 				"Additionally, services will set or unset this mode when you\n"
    610 				"set persist on or off."), BotServ ? BotServ->nick.c_str() : "BotServ",
    611 				ChanServ ? ChanServ->nick.c_str() : "ChanServ");
    612 		return true;
    613 	}
    614 };
    615 
    616 class CommandCSSetRestricted : public Command
    617 {
    618  public:
    619 	CommandCSSetRestricted(Module *creator, const Anope::string &cname = "chanserv/set/restricted") : Command(creator, cname, 2, 2)
    620 	{
    621 		this->SetDesc(_("Restrict access to the channel"));
    622 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    623 	}
    624 
    625 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    626 	{
    627 		if (Anope::ReadOnly)
    628 		{
    629 			source.Reply(READ_ONLY_MODE);
    630 			return;
    631 		}
    632 
    633 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    634 		if (ci == NULL)
    635 		{
    636 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    637 			return;
    638 		}
    639 
    640 		EventReturn MOD_RESULT;
    641 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    642 		if (MOD_RESULT == EVENT_STOP)
    643 			return;
    644 
    645 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    646 		{
    647 			source.Reply(ACCESS_DENIED);
    648 			return;
    649 		}
    650 
    651 		if (params[1].equals_ci("ON"))
    652 		{
    653 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable restricted";
    654 			ci->Extend<bool>("RESTRICTED");
    655 			source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str());
    656 		}
    657 		else if (params[1].equals_ci("OFF"))
    658 		{
    659 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable restricted";
    660 			ci->Shrink<bool>("RESTRICTED");
    661 			source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str());
    662 		}
    663 		else
    664 			this->OnSyntaxError(source, "RESTRICTED");
    665 	}
    666 
    667 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    668 	{
    669 		this->SendSyntax(source);
    670 		source.Reply(" ");
    671 		source.Reply(_("Enables or disables the \002restricted access\002 option for a\n"
    672 				"channel. When \002restricted access\002 is set, users not on the access list will\n"
    673 				"instead be kicked and banned from the channel."));
    674 		return true;
    675 	}
    676 };
    677 
    678 class CommandCSSetSecure : public Command
    679 {
    680  public:
    681 	CommandCSSetSecure(Module *creator, const Anope::string &cname = "chanserv/set/secure") : Command(creator, cname, 2, 2)
    682 	{
    683 		this->SetDesc(_("Activate security features"));
    684 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    685 	}
    686 
    687 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    688 	{
    689 		if (Anope::ReadOnly)
    690 		{
    691 			source.Reply(READ_ONLY_MODE);
    692 			return;
    693 		}
    694 
    695 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    696 		if (ci == NULL)
    697 		{
    698 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    699 			return;
    700 		}
    701 
    702 		EventReturn MOD_RESULT;
    703 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    704 		if (MOD_RESULT == EVENT_STOP)
    705 			return;
    706 
    707 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    708 		{
    709 			source.Reply(ACCESS_DENIED);
    710 			return;
    711 		}
    712 
    713 		if (params[1].equals_ci("ON"))
    714 		{
    715 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure";
    716 			ci->Extend<bool>("CS_SECURE");
    717 			source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str());
    718 		}
    719 		else if (params[1].equals_ci("OFF"))
    720 		{
    721 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure";
    722 			ci->Shrink<bool>("CS_SECURE");
    723 			source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str());
    724 		}
    725 		else
    726 			this->OnSyntaxError(source, "SECURE");
    727 	}
    728 
    729 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    730 	{
    731 		this->SendSyntax(source);
    732 		source.Reply(" ");
    733 		source.Reply(_("Enables or disables security features for a\n"
    734 			"channel. When \002SECURE\002 is set, only users who have\n"
    735 			"identified to services, and are not only recognized, will be\n"
    736 			"given access to channels from account-based access entries."));
    737 		return true;
    738 	}
    739 };
    740 
    741 class CommandCSSetSecureFounder : public Command
    742 {
    743  public:
    744 	CommandCSSetSecureFounder(Module *creator, const Anope::string &cname = "chanserv/set/securefounder") : Command(creator, cname, 2, 2)
    745 	{
    746 		this->SetDesc(_("Stricter control of channel founder status"));
    747 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    748 	}
    749 
    750 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    751 	{
    752 		if (Anope::ReadOnly)
    753 		{
    754 			source.Reply(READ_ONLY_MODE);
    755 			return;
    756 		}
    757 
    758 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    759 		if (ci == NULL)
    760 		{
    761 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    762 			return;
    763 		}
    764 
    765 		EventReturn MOD_RESULT;
    766 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    767 		if (MOD_RESULT == EVENT_STOP)
    768 			return;
    769 
    770 		if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    771 		{
    772 			source.Reply(ACCESS_DENIED);
    773 			return;
    774 		}
    775 
    776 		if (params[1].equals_ci("ON"))
    777 		{
    778 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure founder";
    779 			ci->Extend<bool>("SECUREFOUNDER");
    780 			source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str());
    781 		}
    782 		else if (params[1].equals_ci("OFF"))
    783 		{
    784 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure founder";
    785 			ci->Shrink<bool>("SECUREFOUNDER");
    786 			source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str());
    787 		}
    788 		else
    789 			this->OnSyntaxError(source, "SECUREFOUNDER");
    790 	}
    791 
    792 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    793 	{
    794 		this->SendSyntax(source);
    795 		source.Reply(" ");
    796 		source.Reply(_("Enables or disables the \002secure founder\002 option for a channel.\n"
    797 			"When \002secure founder\002 is set, only the real founder will be\n"
    798 			"able to drop the channel, change its founder and its successor,\n"
    799 			"and not those who have founder level access through\n"
    800 			"the access/qop command."));
    801 		return true;
    802 	}
    803 };
    804 
    805 class CommandCSSetSecureOps : public Command
    806 {
    807  public:
    808 	CommandCSSetSecureOps(Module *creator, const Anope::string &cname = "chanserv/set/secureops") : Command(creator, cname, 2, 2)
    809 	{
    810 		this->SetDesc(_("Stricter control of chanop status"));
    811 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
    812 	}
    813 
    814 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    815 	{
    816 		if (Anope::ReadOnly)
    817 		{
    818 			source.Reply(READ_ONLY_MODE);
    819 			return;
    820 		}
    821 
    822 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    823 		if (ci == NULL)
    824 		{
    825 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    826 			return;
    827 		}
    828 
    829 		EventReturn MOD_RESULT;
    830 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    831 		if (MOD_RESULT == EVENT_STOP)
    832 			return;
    833 
    834 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    835 		{
    836 			source.Reply(ACCESS_DENIED);
    837 			return;
    838 		}
    839 
    840 		if (params[1].equals_ci("ON"))
    841 		{
    842 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure ops";
    843 			ci->Extend<bool>("SECUREOPS");
    844 			source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str());
    845 		}
    846 		else if (params[1].equals_ci("OFF"))
    847 		{
    848 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure ops";
    849 			ci->Shrink<bool>("SECUREOPS");
    850 			source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str());
    851 		}
    852 		else
    853 			this->OnSyntaxError(source, "SECUREOPS");
    854 	}
    855 
    856 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    857 	{
    858 		this->SendSyntax(source);
    859 		source.Reply(" ");
    860 		source.Reply(_("Enables or disables the \002secure ops\002 option for a channel.\n"
    861 				"When \002secure ops\002 is set, users who are not on the access list\n"
    862 				"will not be allowed channel operator status."));
    863 		return true;
    864 	}
    865 };
    866 
    867 class CommandCSSetSignKick : public Command
    868 {
    869  public:
    870 	CommandCSSetSignKick(Module *creator, const Anope::string &cname = "chanserv/set/signkick") : Command(creator, cname, 2, 2)
    871 	{
    872 		this->SetDesc(_("Sign kicks that are done with the KICK command"));
    873 		this->SetSyntax(_("\037channel\037 {ON | LEVEL | OFF}"));
    874 	}
    875 
    876 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    877 	{
    878 		if (Anope::ReadOnly)
    879 		{
    880 			source.Reply(READ_ONLY_MODE);
    881 			return;
    882 		}
    883 
    884 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    885 		if (ci == NULL)
    886 		{
    887 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    888 			return;
    889 		}
    890 
    891 		EventReturn MOD_RESULT;
    892 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
    893 		if (MOD_RESULT == EVENT_STOP)
    894 			return;
    895 
    896 		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    897 		{
    898 			source.Reply(ACCESS_DENIED);
    899 			return;
    900 		}
    901 
    902 		if (params[1].equals_ci("ON"))
    903 		{
    904 			ci->Extend<bool>("SIGNKICK");
    905 			ci->Shrink<bool>("SIGNKICK_LEVEL");
    906 			source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str());
    907 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick";
    908 		}
    909 		else if (params[1].equals_ci("LEVEL"))
    910 		{
    911 			ci->Extend<bool>("SIGNKICK_LEVEL");
    912 			ci->Shrink<bool>("SIGNKICK");
    913 			source.Reply(_("Signed kick option for %s is now \002on\002, but depends of the\n"
    914 				"level of the user that is using the command."), ci->name.c_str());
    915 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick level";
    916 		}
    917 		else if (params[1].equals_ci("OFF"))
    918 		{
    919 			ci->Shrink<bool>("SIGNKICK");
    920 			ci->Shrink<bool>("SIGNKICK_LEVEL");
    921 			source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str());
    922 			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable sign kick";
    923 		}
    924 		else
    925 			this->OnSyntaxError(source, "SIGNKICK");
    926 	}
    927 
    928 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    929 	{
    930 		this->SendSyntax(source);
    931 		source.Reply(" ");
    932 		source.Reply(_("Enables or disables signed kicks for a\n"
    933 				"channel.  When \002SIGNKICK\002 is set, kicks issued with\n"
    934 				"the \002KICK\002 command will have the nick that used the\n"
    935 				"command in their reason.\n"
    936 				" \n"
    937 				"If you use \002LEVEL\002, those who have a level that is superior\n"
    938 				"or equal to the SIGNKICK level on the channel won't have their\n"
    939 				"kicks signed."));
    940 		return true;
    941 	}
    942 };
    943 
    944 class CommandCSSetSuccessor : public Command
    945 {
    946  public:
    947 	CommandCSSetSuccessor(Module *creator, const Anope::string &cname = "chanserv/set/successor") : Command(creator, cname, 1, 2)
    948 	{
    949 		this->SetDesc(_("Set the successor for a channel"));
    950 		this->SetSyntax(_("\037channel\037 [\037nick\037]"));
    951 	}
    952 
    953 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    954 	{
    955 		if (Anope::ReadOnly)
    956 		{
    957 			source.Reply(READ_ONLY_MODE);
    958 			return;
    959 		}
    960 
    961 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    962 		const Anope::string &param = params.size() > 1 ? params[1] : "";
    963 		if (ci == NULL)
    964 		{
    965 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    966 			return;
    967 		}
    968 
    969 		EventReturn MOD_RESULT;
    970 		FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
    971 		if (MOD_RESULT == EVENT_STOP)
    972 			return;
    973 
    974 		if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
    975 		{
    976 			source.Reply(ACCESS_DENIED);
    977 			return;
    978 		}
    979 
    980 		NickCore *nc;
    981 
    982 		if (!param.empty())
    983 		{
    984 			const NickAlias *na = NickAlias::Find(param);
    985 
    986 			if (!na)
    987 			{
    988 				source.Reply(NICK_X_NOT_REGISTERED, param.c_str());
    989 				return;
    990 			}
    991 			if (na->nc == ci->GetFounder())
    992 			{
    993 				source.Reply(_("%s cannot be the successor on channel %s as they are the founder."), na->nick.c_str(), ci->name.c_str());
    994 				return;
    995 			}
    996 			nc = na->nc;
    997 		}
    998 		else
    999 			nc = NULL;
   1000 
   1001 		Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the successor from " << (ci->GetSuccessor() ? ci->GetSuccessor()->display : "(none)") << " to " << (nc ? nc->display : "(none)");
   1002 
   1003 		ci->SetSuccessor(nc);
   1004 
   1005 		if (nc)
   1006 			source.Reply(_("Successor for \002%s\002 changed to \002%s\002."), ci->name.c_str(), nc->display.c_str());
   1007 		else
   1008 			source.Reply(_("Successor for \002%s\002 unset."), ci->name.c_str());
   1009 
   1010 		return;
   1011 	}
   1012 
   1013 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
   1014 	{
   1015 		this->SendSyntax(source);
   1016 		source.Reply(" ");
   1017 		source.Reply(_("Changes the successor of a channel. If the founder's\n"
   1018 				"nickname expires or is dropped while the channel is still\n"
   1019 				"registered, the successor will become the new founder of the\n"
   1020 				"channel. The successor's nickname must be a registered one.\n"
   1021 				"If there's no successor set, then the first nickname on the\n"
   1022 				"access list (with the highest access, if applicable) will\n"
   1023 				"become the new founder, but if the access list is empty, the\n"
   1024 				"channel will be dropped."));
   1025 		unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
   1026 		if (max_reg)
   1027 		{
   1028 			source.Reply(" ");
   1029 			source.Reply(_("Note, however, if the successor already has too many\n"
   1030 				"channels registered (%d), they will not be able to\n"
   1031 				"become the new founder and it will be as if the\n"
   1032 				"channel had no successor set."), max_reg);
   1033 		}
   1034 		return true;
   1035 	}
   1036 };
   1037 
   1038 class CommandCSSetNoexpire : public Command
   1039 {
   1040  public:
   1041 	CommandCSSetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2)
   1042 	{
   1043 		this->SetDesc(_("Prevent the channel from expiring"));
   1044 		this->SetSyntax(_("\037channel\037 {ON | OFF}"));
   1045 	}
   1046 
   1047 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
   1048 	{
   1049 		if (Anope::ReadOnly)
   1050 		{
   1051 			source.Reply(READ_ONLY_MODE);
   1052 			return;
   1053 		}
   1054 
   1055 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
   1056 		if (ci == NULL)
   1057 		{
   1058 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
   1059 			return;
   1060 		}
   1061 
   1062 		if (source.permission.empty() && !source.AccessFor(ci).HasPriv("SET"))
   1063 		{
   1064 			source.Reply(ACCESS_DENIED);
   1065 			return;
   1066 		}
   1067 
   1068 		if (params[1].equals_ci("ON"))
   1069 		{
   1070 			Log(LOG_ADMIN, source, this, ci) << "to enable noexpire";
   1071 			ci->Extend<bool>("CS_NO_EXPIRE");
   1072 			source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str());
   1073 		}
   1074 		else if (params[1].equals_ci("OFF"))
   1075 		{
   1076 			Log(LOG_ADMIN, source, this, ci) << "to disable noexpire";
   1077 			ci->Shrink<bool>("CS_NO_EXPIRE");
   1078 			source.Reply(_("Channel %s \002will\002 expire."), ci->name.c_str());
   1079 		}
   1080 		else
   1081 			this->OnSyntaxError(source, "NOEXPIRE");
   1082 
   1083 		return;
   1084 	}
   1085 
   1086 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
   1087 	{
   1088 		this->SendSyntax(source);
   1089 		source.Reply(" ");
   1090 		source.Reply(_("Sets whether the given channel will expire.  Setting this\n"
   1091 				"to ON prevents the channel from expiring."));
   1092 		return true;
   1093 	}
   1094 };
   1095 
   1096 class CSSet : public Module
   1097 {
   1098 	SerializableExtensibleItem<bool> noautoop, peace, securefounder,
   1099 		restricted, secure, secureops, signkick, signkick_level, noexpire,
   1100 		persist;
   1101 
   1102 	struct KeepModes : SerializableExtensibleItem<bool>
   1103 	{
   1104 		KeepModes(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
   1105 
   1106 		void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
   1107 		{
   1108 			SerializableExtensibleItem<bool>::ExtensibleSerialize(e, s, data);
   1109 
   1110 			if (s->GetSerializableType()->GetName() != "ChannelInfo")
   1111 				return;
   1112 
   1113 			const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(s);
   1114 			Anope::string modes;
   1115 			for (Channel::ModeList::const_iterator it = ci->last_modes.begin(); it != ci->last_modes.end(); ++it)
   1116 			{
   1117 				if (!modes.empty())
   1118 					modes += " ";
   1119 				modes += it->first;
   1120 				if (!it->second.empty())
   1121 					modes += "," + it->second;
   1122 			}
   1123 			data["last_modes"] << modes;
   1124 		}
   1125 
   1126 		void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
   1127 		{
   1128 			SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
   1129 
   1130 			if (s->GetSerializableType()->GetName() != "ChannelInfo")
   1131 				return;
   1132 
   1133 			ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
   1134 			Anope::string modes;
   1135 			data["last_modes"] >> modes;
   1136 			ci->last_modes.clear();
   1137 			for (spacesepstream sep(modes); sep.GetToken(modes);)
   1138 			{
   1139 				size_t c = modes.find(',');
   1140 				if (c == Anope::string::npos)
   1141 					ci->last_modes.insert(std::make_pair(modes, ""));
   1142 				else
   1143 					ci->last_modes.insert(std::make_pair(modes.substr(0, c), modes.substr(c + 1)));
   1144 			}
   1145 		}
   1146 	} keep_modes;
   1147 
   1148 	CommandCSSet commandcsset;
   1149 	CommandCSSetAutoOp commandcssetautoop;
   1150 	CommandCSSetBanType commandcssetbantype;
   1151 	CommandCSSetDescription commandcssetdescription;
   1152 	CommandCSSetFounder commandcssetfounder;
   1153 	CommandCSSetKeepModes commandcssetkeepmodes;
   1154 	CommandCSSetPeace commandcssetpeace;
   1155 	CommandCSSetPersist commandcssetpersist;
   1156 	CommandCSSetRestricted commandcssetrestricted;
   1157 	CommandCSSetSecure commandcssetsecure;
   1158 	CommandCSSetSecureFounder commandcssetsecurefounder;
   1159 	CommandCSSetSecureOps commandcssetsecureops;
   1160 	CommandCSSetSignKick commandcssetsignkick;
   1161 	CommandCSSetSuccessor commandcssetsuccessor;
   1162 	CommandCSSetNoexpire commandcssetnoexpire;
   1163 
   1164 	ExtensibleRef<bool> inhabit;
   1165 
   1166 	bool persist_lower_ts;
   1167 
   1168  public:
   1169 	CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
   1170 		noautoop(this, "NOAUTOOP"), peace(this, "PEACE"),
   1171 		securefounder(this, "SECUREFOUNDER"), restricted(this, "RESTRICTED"),
   1172 		secure(this, "CS_SECURE"), secureops(this, "SECUREOPS"), signkick(this, "SIGNKICK"),
   1173 		signkick_level(this, "SIGNKICK_LEVEL"), noexpire(this, "CS_NO_EXPIRE"),
   1174 		persist(this, "PERSIST"),
   1175 		keep_modes(this, "CS_KEEP_MODES"),
   1176 
   1177 		commandcsset(this), commandcssetautoop(this), commandcssetbantype(this),
   1178 		commandcssetdescription(this), commandcssetfounder(this), commandcssetkeepmodes(this),
   1179 		commandcssetpeace(this), commandcssetpersist(this), commandcssetrestricted(this),
   1180 		commandcssetsecure(this), commandcssetsecurefounder(this), commandcssetsecureops(this), commandcssetsignkick(this),
   1181 		commandcssetsuccessor(this), commandcssetnoexpire(this),
   1182 
   1183 		inhabit("inhabit")
   1184 	{
   1185 	}
   1186 
   1187 	void OnReload(Configuration::Conf *conf) anope_override
   1188 	{
   1189 		persist_lower_ts = conf->GetModule(this)->Get<bool>("persist_lower_ts");
   1190 	}
   1191 
   1192 	void OnCreateChan(ChannelInfo *ci) anope_override
   1193 	{
   1194 		ci->bantype = Config->GetModule(this)->Get<int>("defbantype", "2");
   1195 	}
   1196 
   1197 	void OnChannelSync(Channel *c) anope_override
   1198 	{
   1199 		if (c->ci && keep_modes.HasExt(c->ci))
   1200 		{
   1201 			Channel::ModeList ml = c->ci->last_modes;
   1202 			for (Channel::ModeList::iterator it = ml.begin(); it != ml.end(); ++it)
   1203 				c->SetMode(c->ci->WhoSends(), it->first, it->second);
   1204 		}
   1205 	}
   1206 
   1207 	EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
   1208 	{
   1209 		if (!c->ci || !restricted.HasExt(c->ci) || c->MatchesList(u, "EXCEPT"))
   1210 			return EVENT_CONTINUE;
   1211 
   1212 		if (c->ci->AccessFor(u).empty() && (!c->ci->GetFounder() || u->Account() != c->ci->GetFounder()))
   1213 			return EVENT_STOP;
   1214 
   1215 		return EVENT_CONTINUE;
   1216 	}
   1217 
   1218 	void OnDelChan(ChannelInfo *ci) anope_override
   1219 	{
   1220 		if (ci->c && persist.HasExt(ci))
   1221 			ci->c->RemoveMode(ci->WhoSends(), "PERM", "", false);
   1222 		persist.Unset(ci);
   1223 	}
   1224 
   1225 	EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
   1226 	{
   1227 		if (c->ci)
   1228 		{
   1229 			/* Channel mode +P or so was set, mark this channel as persistent */
   1230 			if (mode->name == "PERM")
   1231 				persist.Set(c->ci, true);
   1232 
   1233 			if (mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
   1234 				c->ci->last_modes = c->GetModes();
   1235 		}
   1236 
   1237 		return EVENT_CONTINUE;
   1238 	}
   1239 
   1240 	EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
   1241 	{
   1242 		if (mode->name == "PERM")
   1243 		{
   1244 			if (c->ci)
   1245 				persist.Unset(c->ci);
   1246 		}
   1247 
   1248 		if (c->ci && mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
   1249 			c->ci->last_modes = c->GetModes();
   1250 
   1251 		return EVENT_CONTINUE;
   1252 	}
   1253 
   1254 	void OnJoinChannel(User *u, Channel *c) anope_override
   1255 	{
   1256 		if (u->server != Me && persist_lower_ts && c->ci && persist.HasExt(c->ci) && c->creation_time > c->ci->time_registered)
   1257 		{
   1258 			Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered;
   1259 			c->creation_time = c->ci->time_registered;
   1260 			IRCD->SendChannel(c);
   1261 			c->Reset();
   1262 		}
   1263 	}
   1264 
   1265 	void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
   1266 	{
   1267 		if (chan->ci)
   1268 		{
   1269 			if (noautoop.HasExt(chan->ci))
   1270 				give_modes = false;
   1271 			if (secureops.HasExt(chan->ci) && !user->HasPriv("chanserv/administration"))
   1272 				// This overrides what chanserv does because it is loaded after chanserv
   1273 				take_modes = true;
   1274 		}
   1275 	}
   1276 
   1277 	void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override
   1278 	{
   1279 		if (noexpire.HasExt(ci))
   1280 			expire = false;
   1281 	}
   1282 
   1283 	void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
   1284 	{
   1285 		if (!show_all)
   1286 			return;
   1287 
   1288 		if (peace.HasExt(ci))
   1289 			info.AddOption(_("Peace"));
   1290 		if (restricted.HasExt(ci))
   1291 			info.AddOption(_("Restricted access"));
   1292 		if (secure.HasExt(ci))
   1293 			info.AddOption(_("Security"));
   1294 		if (securefounder.HasExt(ci))
   1295 			info.AddOption(_("Secure founder"));
   1296 		if (secureops.HasExt(ci))
   1297 			info.AddOption(_("Secure ops"));
   1298 		if (signkick.HasExt(ci) || signkick_level.HasExt(ci))
   1299 			info.AddOption(_("Signed kicks"));
   1300 		if (persist.HasExt(ci))
   1301 			info.AddOption(_("Persistent"));
   1302 		if (noexpire.HasExt(ci))
   1303 			info.AddOption(_("No expire"));
   1304 		if (keep_modes.HasExt(ci))
   1305 			info.AddOption(_("Keep modes"));
   1306 		if (noautoop.HasExt(ci))
   1307 			info.AddOption(_("No auto-op"));
   1308 	}
   1309 };
   1310 
   1311 MODULE_INIT(CSSet)